Skip to content

Commit

Permalink
add items/item depencencies
Browse files Browse the repository at this point in the history
  • Loading branch information
vincentsarago committed Oct 31, 2023
1 parent a284292 commit 4cd8639
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 150 deletions.
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

1 comment on commit 4cd8639

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'TiPg Benchmarks'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.30.

Benchmark suite Current: 4cd8639 Previous: a284292 Ratio
tests/benchmarks.py::test_benchmark_collections[json-1] 218.17449027849244 iter/sec (stddev: 0.0012080961964354335) 389.8174657849523 iter/sec (stddev: 0.0001346513331121) 1.79
tests/benchmarks.py::test_benchmark_collections[json-10] 141.6977098041463 iter/sec (stddev: 0.001885074909774726) 235.81724379810439 iter/sec (stddev: 0.0001314043223951821) 1.66
tests/benchmarks.py::test_benchmark_collections[html-1] 219.83424278258306 iter/sec (stddev: 0.001437689013235457) 386.9907532429826 iter/sec (stddev: 0.00012887457842136315) 1.76
tests/benchmarks.py::test_benchmark_collections[html-10] 140.8749498825268 iter/sec (stddev: 0.000945956898324737) 226.57826331125347 iter/sec (stddev: 0.00016911503432068357) 1.61
tests/benchmarks.py::test_benchmark_collection[json] 294.86058588521485 iter/sec (stddev: 0.00030912728993643724) 497.36579735757243 iter/sec (stddev: 0.00012218872577341353) 1.69
tests/benchmarks.py::test_benchmark_collection[html] 283.85523348740077 iter/sec (stddev: 0.0007220129425748756) 474.5252825568808 iter/sec (stddev: 0.00012230952747538322) 1.67
tests/benchmarks.py::test_benchmark_queryables 343.77602291301 iter/sec (stddev: 0.00048622758568530217) 560.1728217671863 iter/sec (stddev: 0.00009868798011117214) 1.63
tests/benchmarks.py::test_benchmark_items[geojson-1] 103.98236999028411 iter/sec (stddev: 0.0008794826175333024) 171.72340015031662 iter/sec (stddev: 0.0001975764018515539) 1.65
tests/benchmarks.py::test_benchmark_items[geojson-10] 92.30454547452304 iter/sec (stddev: 0.0015242390399558415) 150.89509342585401 iter/sec (stddev: 0.0005174120591189715) 1.63
tests/benchmarks.py::test_benchmark_items[geojson-50] 65.03897022399762 iter/sec (stddev: 0.0014222348598595667) 92.37633529510073 iter/sec (stddev: 0.004799516181177598) 1.42
tests/benchmarks.py::test_benchmark_items[geojson-100] 44.56621951889656 iter/sec (stddev: 0.012838397954430732) 65.4199256164811 iter/sec (stddev: 0.005496999397562707) 1.47
tests/benchmarks.py::test_benchmark_items[geojson-200] 29.501769553544815 iter/sec (stddev: 0.012822291147388273) 40.93348370299044 iter/sec (stddev: 0.006767118864512801) 1.39
tests/benchmarks.py::test_benchmark_items[csv-1] 100.38307511846098 iter/sec (stddev: 0.002709775088755286) 164.99236600027038 iter/sec (stddev: 0.0001625180883885496) 1.64
tests/benchmarks.py::test_benchmark_items[csv-10] 71.62969217974369 iter/sec (stddev: 0.002205595572500069) 131.78887347828973 iter/sec (stddev: 0.0003126913356476361) 1.84
tests/benchmarks.py::test_benchmark_items[csv-50] 37.91350582878962 iter/sec (stddev: 0.0016602625724890625) 71.00074025478915 iter/sec (stddev: 0.0004241045785840394) 1.87
tests/benchmarks.py::test_benchmark_items[csv-100] 23.42911717170071 iter/sec (stddev: 0.007037673222364735) 44.912298902681094 iter/sec (stddev: 0.0004133500660856742) 1.92
tests/benchmarks.py::test_benchmark_items[csv-200] 13.295687734724245 iter/sec (stddev: 0.010473543469190853) 25.38712730341501 iter/sec (stddev: 0.0015488591510809668) 1.91
tests/benchmarks.py::test_benchmark_items[csv-250] 11.221889353840709 iter/sec (stddev: 0.009016648968119488) 21.20231606184937 iter/sec (stddev: 0.0009630622441218977) 1.89
tests/benchmarks.py::test_benchmark_items[html-1] 99.15846456770889 iter/sec (stddev: 0.002347229145944913) 161.2583038976123 iter/sec (stddev: 0.00015107003155602982) 1.63
tests/benchmarks.py::test_benchmark_items[html-10] 82.80465425724122 iter/sec (stddev: 0.0023761846467990014) 131.41922491054981 iter/sec (stddev: 0.00038299977072516665) 1.59
tests/benchmarks.py::test_benchmark_items[html-50] 45.309949763957846 iter/sec (stddev: 0.011377877445539999) 71.18975022809582 iter/sec (stddev: 0.0060196890867636015) 1.57
tests/benchmarks.py::test_benchmark_items[html-100] 27.41068479918186 iter/sec (stddev: 0.027758702665916308) 50.012686611030425 iter/sec (stddev: 0.00017634477829479224) 1.82
tests/benchmarks.py::test_benchmark_items[html-200] 15.540421752390143 iter/sec (stddev: 0.03573905146983195) 25.74513671846712 iter/sec (stddev: 0.015851254043987614) 1.66
tests/benchmarks.py::test_benchmark_items[html-250] 13.704055002925916 iter/sec (stddev: 0.03611240281639115) 19.446041680188895 iter/sec (stddev: 0.02196395036197749) 1.42
tests/benchmarks.py::test_benchmark_item[geojson-NewfoundlandandLabrador] 1.5607069018895499 iter/sec (stddev: 0.026137200511787785) 2.451994014029415 iter/sec (stddev: 0.009399100052411144) 1.57
tests/benchmarks.py::test_benchmark_item[geojson-Saskatchewan] 36.39180963685581 iter/sec (stddev: 0.023988642316204793) 50.561971751322865 iter/sec (stddev: 0.01543572328009663) 1.39
tests/benchmarks.py::test_benchmark_item[html-NewfoundlandandLabrador] 0.649568195813314 iter/sec (stddev: 0.09954551706431072) 0.9127396645615509 iter/sec (stddev: 0.039552382563250214) 1.41
tests/benchmarks.py::test_benchmark_item[html-Saskatchewan] 12.364589252321895 iter/sec (stddev: 0.05371056416562014) 20.11363481067079 iter/sec (stddev: 0.030565646770264313) 1.63
tests/benchmarks.py::test_benchmark_tile[0/0/0-WGS1984Quad] 6.978369510901955 iter/sec (stddev: 0.01574465093101298) 9.071946454832053 iter/sec (stddev: 0.007972027740308708) 1.30
tests/benchmarks.py::test_benchmark_tile[4/8/5-WGS1984Quad] 129.84576598901157 iter/sec (stddev: 0.001755601860580967) 197.51129716783083 iter/sec (stddev: 0.00011718955652817509) 1.52
tests/benchmarks.py::test_benchmark_tile[4/8/5-WebMercatorQuad] 98.21865170874219 iter/sec (stddev: 0.000990751830177583) 132.2249070318908 iter/sec (stddev: 0.0002176113631555468) 1.35
tests/benchmarks.py::test_benchmark_tile[6/33/25-WGS1984Quad] 163.77414897855775 iter/sec (stddev: 0.000955493497521207) 253.74316349195954 iter/sec (stddev: 0.00010642252659784684) 1.55
tests/benchmarks.py::test_benchmark_tile[6/33/25-WebMercatorQuad] 146.61237734136915 iter/sec (stddev: 0.0019162542785118109) 232.08461437687794 iter/sec (stddev: 0.0002772635786773613) 1.58
tests/benchmarks.py::test_benchmark_tilematrixset_endpoints[/tileMatrixSets] 334.1964321643578 iter/sec (stddev: 0.0015004437625633317) 504.3077044734148 iter/sec (stddev: 0.0028841331379532574) 1.51
tests/benchmarks.py::test_benchmark_tilematrixset_endpoints[/tileMatrixSets/WGS1984Quad] 443.6848724820769 iter/sec (stddev: 0.004105934191698231) 714.4151473564792 iter/sec (stddev: 0.00011123610818711978) 1.61
tests/benchmarks.py::test_benchmark_tilematrixset_endpoints[/tileMatrixSets/WebMercatorQuad] 495.9972463664904 iter/sec (stddev: 0.0004081857980586652) 708.851850379112 iter/sec (stddev: 0.00007234887368660502) 1.43
tests/benchmarks.py::test_benchmark_tilematrixset_endpoints[/collections/public.landsat_wrs/tiles] 37.02183998307772 iter/sec (stddev: 0.0013581990726481646) 51.44201801933845 iter/sec (stddev: 0.0002227153087666646) 1.39
tests/benchmarks.py::test_benchmark_tilematrixset_endpoints[/collections/public.landsat_wrs/tiles/WGS1984Quad] 162.58409125598413 iter/sec (stddev: 0.0018844028624617093) 230.92654791968474 iter/sec (stddev: 0.00007775541167608782) 1.42
tests/benchmarks.py::test_benchmark_tilematrixset_endpoints[/collections/public.landsat_wrs/tiles/WebMercatorQuad] 167.79644072347773 iter/sec (stddev: 0.0013485356254824962) 224.97888641484812 iter/sec (stddev: 0.00008029404899892052) 1.34

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.