Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add items/item depencencies #137

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ Note: Minor version `0.X.0` update might break the API, It's recommended to pin
prev: Optional[int]
```

- add `tipg.dependencies.ItemsParams` and `tipg.dependencies.ItemParams`

### fixed

- hide map element in HTML pages when collections/items do not have spatial component (https://github.com/developmentseed/tipg/issues/132)
Expand Down Expand Up @@ -93,6 +95,7 @@ Note: Minor version `0.X.0` update might break the API, It's recommended to pin

- move `s_intersects` and `t_intersects` functions from `tipg.factory` to `tipg.dependencies`

- add `items_dependency` and `item_dependency` attributes in `OGCFeaturesFactory` class. Those new dependencies define the input for the `/items` and `/items/{itemId}` endpoints

## [0.4.4] - 2023-10-03

Expand Down
28 changes: 23 additions & 5 deletions docs/src/user_guide/factories.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,21 @@ class Factory:

collections_dependency: Callable
collection_dependency: Callable

def __init__(self, collections_dependency: Callable, collection_dependency: Callable):
items_dependency: Callable
item_dependency: Callable

def __init__(
self,
collections_dependency: Callable,
collection_dependency: Callable,
items_dependency: Callable,
item_dependency: Callable,
):
self.collections_dependency = collections_dependency
self.collection_dependency = collection_dependency
self.items_dependency = items_dependency
self.item_dependency = item_dependency

self.router = APIRouter()

self.register_routes()
Expand All @@ -35,17 +46,16 @@ class Factory:
def items(
request: Request,
collection=Depends(self.collection_dependency),
item_list=Depends(self.items_dependency),
):
item_list = collection.features(...)
...

@self.router.get("/collections/{collectionId}/items/{itemId}")
def item(
request: Request,
collection=Depends(self.collection_dependency),
itemId: str = Path(..., description="Item identifier"),
feature=Depends(self.item_dependency)
):
item_list = collection.features(ids_filter=[itemId])
...


Expand Down Expand Up @@ -76,6 +86,10 @@ app.include_router(endpoints.router, tags=["OGC Features API"])

- **collection_dependency** (Callable[..., tipg.collections.Collection]): Callable which return a Collection instance

- **items_dependency** (Callable[..., tipg.collections.ItemList]): Callable which return a ItemList dictionary

- **item_dependency** (Callable[..., tipg.collections.Feature]): Callable which return a Feature dictionary

- **with_common** (bool, optional): Create Full OGC Features API set of endpoints with OGC Common endpoints (landing `/` and conformance `/conformance`). Defaults to `True`

- **router** (fastapi.APIRouter, optional): FastAPI
Expand Down Expand Up @@ -158,6 +172,10 @@ app.include_router(endpoints.router)

- **collection_dependency** (Callable[..., tipg.collections.Collection]): Callable which return a Collection instance

- **items_dependency** (Callable[..., tipg.collections.ItemList]): Callable which return a ItemList dictionary

- **item_dependency** (Callable[..., tipg.collections.Feature]): Callable which return a Feature dictionary

- **supported_tms** (morecantile.TileMatrixSets): morecantile TileMatrixSets instance (holds a set of TileMatrixSet documents)

- **with_tiles_viewer** (bool, optional): add `/viewer` endpoint to visualize the Vector tile. Defaults to `True`
Expand Down
159 changes: 156 additions & 3 deletions tipg/dependencies.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,23 @@
from pygeofilter.parsers.cql2_text import parse as cql2_text_parser
from typing_extensions import Annotated

from tipg.collections import Catalog, Collection, CollectionList
from tipg.errors import InvalidBBox, MissingCollectionCatalog, MissingFunctionParameter
from tipg.collections import Catalog, Collection, CollectionList, Feature, ItemList
from tipg.errors import (
InvalidBBox,
MissingCollectionCatalog,
MissingFunctionParameter,
NoPrimaryKey,
NotFound,
)
from tipg.resources.enums import MediaType
from tipg.settings import TMSSettings
from tipg.settings import FeaturesSettings, TMSSettings

from fastapi import Depends, HTTPException, Path, Query

from starlette.requests import Request

tms_settings = TMSSettings()
features_settings = FeaturesSettings()

ResponseType = Literal["json", "html"]
QueryablesResponseType = Literal["schemajson", "html"]
Expand Down Expand Up @@ -505,3 +512,149 @@ def CollectionsParams(
next=offset + returned if matched - returned > offset else None,
prev=max(offset - returned, 0) if offset else None,
)


async def ItemsParams(
request: Request,
collection: Annotated[Collection, Depends(CollectionParams)],
ids_filter: Annotated[Optional[List[str]], Depends(ids_query)],
bbox_filter: Annotated[Optional[List[float]], Depends(bbox_query)],
datetime_filter: Annotated[Optional[List[str]], Depends(datetime_query)],
properties: Annotated[Optional[List[str]], Depends(properties_query)],
cql_filter: Annotated[Optional[AstType], Depends(filter_query)],
sortby: Annotated[Optional[str], Depends(sortby_query)],
geom_column: Annotated[
Optional[str],
Query(
description="Select geometry column.",
alias="geom-column",
),
] = None,
datetime_column: Annotated[
Optional[str],
Query(
description="Select datetime column.",
alias="datetime-column",
),
] = None,
limit: Annotated[
int,
Query(
ge=0,
le=features_settings.max_features_per_query,
description="Limits the number of features in the response.",
),
] = features_settings.default_features_limit,
offset: Annotated[
Optional[int],
Query(
ge=0,
description="Starts the response at an offset.",
),
] = None,
bbox_only: Annotated[
Optional[bool],
Query(
description="Only return the bounding box of the feature.",
alias="bbox-only",
),
] = None,
simplify: Annotated[
Optional[float],
Query(
description="Simplify the output geometry to given threshold in decimal degrees.",
),
] = None,
output_type: Annotated[Optional[MediaType], Depends(ItemsOutputType)] = None,
) -> ItemList:
"""Get list of Items."""
output_type = output_type or MediaType.geojson
geom_as_wkt = output_type not in [
MediaType.geojson,
MediaType.geojsonseq,
MediaType.html,
]

item_list = await collection.features(
request.app.state.pool,
ids_filter=ids_filter,
bbox_filter=bbox_filter,
datetime_filter=datetime_filter,
properties_filter=properties_filter_query(request, collection),
function_parameters=function_parameters_query(request, collection),
cql_filter=cql_filter,
sortby=sortby,
properties=properties,
limit=limit,
offset=offset,
geom=geom_column,
dt=datetime_column,
bbox_only=bbox_only,
simplify=simplify,
geom_as_wkt=geom_as_wkt,
)

return item_list


async def ItemParams(
request: Request,
collection: Annotated[Collection, Depends(CollectionParams)],
itemId: Annotated[str, Path(description="Item identifier")],
bbox_only: Annotated[
Optional[bool],
Query(
description="Only return the bounding box of the feature.",
alias="bbox-only",
),
] = None,
simplify: Annotated[
Optional[float],
Query(
description="Simplify the output geometry to given threshold in decimal degrees.",
),
] = None,
geom_column: Annotated[
Optional[str],
Query(
description="Select geometry column.",
alias="geom-column",
),
] = None,
datetime_column: Annotated[
Optional[str],
Query(
description="Select datetime column.",
alias="datetime-column",
),
] = None,
properties: Optional[List[str]] = Depends(properties_query),
output_type: Annotated[Optional[MediaType], Depends(ItemsOutputType)] = None,
) -> Feature:
"""Get Item by id."""
if collection.id_column is None:
raise NoPrimaryKey("No primary key is set on this table")

output_type = output_type or MediaType.geojson
geom_as_wkt = output_type not in [
MediaType.geojson,
MediaType.geojsonseq,
MediaType.html,
]

item_list = await collection.features(
pool=request.app.state.pool,
bbox_only=bbox_only,
simplify=simplify,
ids_filter=[itemId],
properties=properties,
function_parameters=function_parameters_query(request, collection),
geom=geom_column,
dt=datetime_column,
geom_as_wkt=geom_as_wkt,
)

if not item_list["items"]:
raise NotFound(f"Item {itemId} in Collection {collection.id} does not exist.")

return item_list["items"][0]
Loading
Loading