diff --git a/CHANGES.md b/CHANGES.md index f7cc0b5..1000b37 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -39,6 +39,9 @@ Note: Minor version `0.X.0` update might break the API, It's recommended to pin ) -> None: ``` * fix URL in HTML templates when behind proxy +* remove **deprecated** tiles endpoint with default TileMatrixSet +* renamed tilejson endpoint from `/collections/{collectionId}/{tileMatrixSetId}/tilejson.json` to `/collections/{collectionId}/tiles/{tileMatrixSetId}/tilejson.json` **breaking change** +* renamed stylejson endpoint from `/collections/{collectionId}/{tileMatrixSetId}/style.json` to `/collections/{collectionId}/tiles/{tileMatrixSetId}/style.json` **breaking change** ## [0.11.0] - TBD diff --git a/tests/routes/test_geography.py b/tests/routes/test_geography.py index cde0bdc..2f08162 100644 --- a/tests/routes/test_geography.py +++ b/tests/routes/test_geography.py @@ -24,7 +24,9 @@ def test_geography_column(app): assert body["numberReturned"] == 6 assert body["features"][0]["geometry"]["type"] == "Polygon" - response = app.get("/collections/public.my_data_geo/tilejson.json") + response = app.get( + "/collections/public.my_data_geo/tiles/WebMercatorQuad/tilejson.json" + ) assert response.status_code == 200 resp_json = response.json() assert resp_json["name"] == "public.my_data_geo" @@ -34,7 +36,7 @@ def test_geography_column(app): resp_json["bounds"], [-47.5356, 74.8049, -8.97407, 81.8555] ) - response = app.get("/collections/public.my_data_geo/tiles/5/11/5") + response = app.get("/collections/public.my_data_geo/tiles/WebMercatorQuad/5/11/5") assert response.status_code == 200 decoded = mapbox_vector_tile.decode(response.content) assert len(decoded["default"]["features"]) diff --git a/tests/routes/test_tiles.py b/tests/routes/test_tiles.py index 0e84c30..b96a5c4 100644 --- a/tests/routes/test_tiles.py +++ b/tests/routes/test_tiles.py @@ -8,7 +8,9 @@ def test_tilejson(app): """Test TileJSON endpoint.""" - response = app.get("/collections/public.landsat_wrs/tilejson.json") + response = app.get( + "/collections/public.landsat_wrs/tiles/WebMercatorQuad/tilejson.json" + ) assert response.status_code == 200 resp_json = response.json() @@ -22,7 +24,9 @@ def test_tilejson(app): resp_json["bounds"], [-180.0, -82.6401, 180.0, 82.6401], decimal=4 ) - response = app.get("/collections/public.landsat_wrs/WGS1984Quad/tilejson.json") + response = app.get( + "/collections/public.landsat_wrs/tiles/WGS1984Quad/tilejson.json" + ) assert response.status_code == 200 resp_json = response.json() @@ -38,7 +42,7 @@ def test_tilejson(app): ) response = app.get( - "/collections/public.landsat_wrs/tilejson.json?minzoom=1&maxzoom=2" + "/collections/public.landsat_wrs/tiles/WebMercatorQuad/tilejson.json?minzoom=1&maxzoom=2" ) assert response.status_code == 200 @@ -48,7 +52,7 @@ def test_tilejson(app): assert resp_json["maxzoom"] == 2 response = app.get( - "/collections/public.landsat_wrs/tilejson.json?minzoom=1&maxzoom=2&limit=1000" + "/collections/public.landsat_wrs/tiles/WebMercatorQuad/tilejson.json?minzoom=1&maxzoom=2&limit=1000" ) assert response.status_code == 200 @@ -59,7 +63,9 @@ def test_tilejson(app): assert "?limit=1000" in resp_json["tiles"][0] # Make sure that a non-4326 collection still returns the bounds in 4326 - response = app.get("/collections/public.minnesota/tilejson.json") + response = app.get( + "/collections/public.minnesota/tiles/WebMercatorQuad/tilejson.json" + ) assert response.status_code == 200 resp_json = response.json() @@ -77,13 +83,15 @@ def test_tile(app): mvt_settings.set_mvt_layername = False name = "landsat_wrs" - response = app.get(f"/collections/public.{name}/tiles/0/0/0") + response = app.get(f"/collections/public.{name}/tiles/WebMercatorQuad/0/0/0") assert response.status_code == 200 decoded = mapbox_vector_tile.decode(response.content) assert "default" in decoded.keys() assert len(decoded["default"]["features"]) == 10000 - response = app.get(f"/collections/public.{name}/tiles/0/0/0?limit=1000") + response = app.get( + f"/collections/public.{name}/tiles/WebMercatorQuad/0/0/0?limit=1000" + ) assert response.status_code == 200 decoded = mapbox_vector_tile.decode(response.content) assert len(decoded["default"]["features"]) == 1000 @@ -92,7 +100,7 @@ def test_tile(app): ) response = app.get( - f"/collections/public.{name}/tiles/0/0/0?limit=1&properties=pr,row,path" + f"/collections/public.{name}/tiles/WebMercatorQuad/0/0/0?limit=1&properties=pr,row,path" ) assert response.status_code == 200 decoded = mapbox_vector_tile.decode(response.content) @@ -100,13 +108,17 @@ def test_tile(app): decoded["default"]["features"][0]["properties"] ) - response = app.get(f"/collections/public.{name}/tiles/0/0/0?geom-column=geom") + response = app.get( + f"/collections/public.{name}/tiles/WebMercatorQuad/0/0/0?geom-column=geom" + ) assert response.status_code == 200 decoded = mapbox_vector_tile.decode(response.content) assert len(decoded["default"]["features"]) == 10000 # invalid geometry column name - response = app.get(f"/collections/public.{name}/tiles/0/0/0?geom-column=the_geom") + response = app.get( + f"/collections/public.{name}/tiles/WebMercatorQuad/0/0/0?geom-column=the_geom" + ) assert response.status_code == 404 mvt_settings.set_mvt_layername = init_value @@ -118,7 +130,7 @@ def test_tile_custom_name(app): mvt_settings.set_mvt_layername = True name = "landsat_wrs" - response = app.get(f"/collections/public.{name}/tiles/0/0/0") + response = app.get(f"/collections/public.{name}/tiles/WebMercatorQuad/0/0/0") assert response.status_code == 200 decoded = mapbox_vector_tile.decode(response.content) assert name in decoded.keys() @@ -178,7 +190,9 @@ def test_tile_tms_custom_name(app): def test_stylejson(app): """Test StyleJSON endpoint.""" - response = app.get("/collections/public.landsat_wrs/style.json") + response = app.get( + "/collections/public.landsat_wrs/tiles/WebMercatorQuad/style.json" + ) assert response.status_code == 200 resp_json = response.json() @@ -197,7 +211,7 @@ def test_stylejson(app): np.around(source["bounds"], 4), [-180.0, -82.6401, 180.0, 82.6401] ) - response = app.get("/collections/public.landsat_wrs/WGS1984Quad/style.json") + response = app.get("/collections/public.landsat_wrs/tiles/WGS1984Quad/style.json") assert response.status_code == 200 resp_json = response.json() @@ -217,7 +231,9 @@ def test_stylejson(app): np.around(source["bounds"], 4), [-180.0, -82.6401, 180.0, 82.6401] ) - response = app.get("/collections/public.landsat_wrs/style.json?minzoom=1&maxzoom=2") + response = app.get( + "/collections/public.landsat_wrs/tiles/WebMercatorQuad/style.json?minzoom=1&maxzoom=2" + ) assert response.status_code == 200 resp_json = response.json() @@ -227,5 +243,7 @@ def test_stylejson(app): assert "minzoom" not in source["tiles"][0] assert "maxzoom" not in source["tiles"][0] - response = app.get("/collections/public.landsat/style.json?geom-column=centroid") + response = app.get( + "/collections/public.landsat/tiles/WebMercatorQuad/style.json?geom-column=centroid" + ) assert response.status_code == 200 diff --git a/tests/test_factories.py b/tests/test_factories.py index c23ec73..7f91ee7 100644 --- a/tests/test_factories.py +++ b/tests/test_factories.py @@ -116,7 +116,7 @@ def test_tiles_factory(): endpoints = OGCTilesFactory() assert endpoints.with_common assert endpoints.title == "OGC API" - assert len(endpoints.router.routes) == 15 + assert len(endpoints.router.routes) == 10 assert len(endpoints.conforms_to) == 5 app = FastAPI() @@ -150,7 +150,7 @@ def test_tiles_factory(): assert endpoints.router_prefix == "/map" assert endpoints.with_common assert endpoints.title == "OGC Tiles API" - assert len(endpoints.router.routes) == 15 + assert len(endpoints.router.routes) == 10 app = FastAPI() app.include_router(endpoints.router, prefix="/map") @@ -180,7 +180,7 @@ def test_tiles_factory(): endpoints = OGCTilesFactory(title="OGC Tiles API", with_common=False) assert not endpoints.with_common assert endpoints.title == "OGC Tiles API" - assert len(endpoints.router.routes) == 13 + assert len(endpoints.router.routes) == 8 assert len(endpoints.conforms_to) == 5 app = FastAPI() @@ -203,7 +203,7 @@ def test_endpoints_factory(): endpoints = Endpoints() assert endpoints.with_common assert endpoints.title == "OGC API" - assert len(endpoints.router.routes) == 20 + assert len(endpoints.router.routes) == 15 assert len(endpoints.conforms_to) == 11 # 5 from tiles + 6 from features app = FastAPI() @@ -244,7 +244,7 @@ def test_endpoints_factory(): assert endpoints.router_prefix == "/ogc" assert endpoints.with_common assert endpoints.title == "OGC Full API" - assert len(endpoints.router.routes) == 20 + assert len(endpoints.router.routes) == 15 assert not endpoints.ogc_features.with_common assert endpoints.ogc_features.router_prefix == "/ogc" assert not endpoints.ogc_tiles.with_common @@ -288,7 +288,7 @@ def test_endpoints_factory(): endpoints = Endpoints(title="Tiles and Features API", with_common=False) assert not endpoints.with_common assert endpoints.title == "Tiles and Features API" - assert len(endpoints.router.routes) == 18 # 11 from tiles + 5 from features + assert len(endpoints.router.routes) == 13 # 8 from tiles + 5 from features assert len(endpoints.conforms_to) == 11 # 4 from tiles + 6 from features app = FastAPI() diff --git a/tests/test_sql_functions.py b/tests/test_sql_functions.py index 13801cc..4253f53 100644 --- a/tests/test_sql_functions.py +++ b/tests/test_sql_functions.py @@ -138,14 +138,18 @@ def test_items_function(app_functions): def test_tiles_functions(app_functions): """Test Tiles endpoint.""" - response = app_functions.get("/collections/pg_temp.landsat_centroids/tilejson.json") + response = app_functions.get( + "/collections/pg_temp.landsat_centroids/tiles/WebMercatorQuad/tilejson.json" + ) assert response.status_code == 200 body = response.json() assert body["name"] == "pg_temp.landsat_centroids" assert body["minzoom"] == 5 assert body["maxzoom"] == 12 - response = app_functions.get("/collections/pg_temp.hexagons/tilejson.json") + response = app_functions.get( + "/collections/pg_temp.hexagons/tiles/WebMercatorQuad/tilejson.json" + ) assert response.status_code == 200 body = response.json() assert body["name"] == "pg_temp.hexagons" @@ -153,7 +157,7 @@ def test_tiles_functions(app_functions): assert body["maxzoom"] == 12 response = app_functions.get( - "/collections/pg_temp.hexagons/tilejson.json?minzoom=1&maxzoom=2&size=4" + "/collections/pg_temp.hexagons/tiles/WebMercatorQuad/tilejson.json?minzoom=1&maxzoom=2&size=4" ) assert response.status_code == 200 body = response.json() @@ -193,43 +197,57 @@ def test_tiles_functions(app_functions): # tiles # Check default's function are used - response = app_functions.get("/collections/pg_temp.squares/tiles/3/3/3") + response = app_functions.get( + "/collections/pg_temp.squares/tiles/WebMercatorQuad/3/3/3" + ) assert response.status_code == 200 decoded = mapbox_vector_tile.decode(response.content) assert len(decoded["default"]["features"]) == 25 # Check default's function are used - response = app_functions.get("/collections/pg_temp.squares/tiles/3/3/3?size=2") + response = app_functions.get( + "/collections/pg_temp.squares/tiles/WebMercatorQuad/3/3/3?size=2" + ) assert response.status_code == 200 decoded = mapbox_vector_tile.decode(response.content) assert len(decoded["default"]["features"]) == 483 # Check any geometry input column will work - response = app_functions.get("/collections/pg_temp.hexagons/tiles/3/3/3") + response = app_functions.get( + "/collections/pg_temp.hexagons/tiles/WebMercatorQuad/3/3/3" + ) assert response.status_code == 200 decoded = mapbox_vector_tile.decode(response.content) assert len(decoded["default"]["features"]) == 12 - response = app_functions.get("/collections/pg_temp.hexagons_g/tiles/3/3/3") + response = app_functions.get( + "/collections/pg_temp.hexagons_g/tiles/WebMercatorQuad/3/3/3" + ) assert response.status_code == 200 decoded = mapbox_vector_tile.decode(response.content) assert len(decoded["default"]["features"]) == 12 # Check function with x/y/z input - response = app_functions.get("/collections/pg_temp.landsat/tiles/0/0/0?p=13") + response = app_functions.get( + "/collections/pg_temp.landsat/tiles/WebMercatorQuad/0/0/0?p=13" + ) assert response.status_code == 200 decoded = mapbox_vector_tile.decode(response.content) assert len(decoded["default"]["features"]) == 104 assert decoded["default"]["features"][0]["properties"]["grid_path"] == 13 # No features with p=0 - response = app_functions.get("/collections/pg_temp.landsat/tiles/0/0/0?p=0") + response = app_functions.get( + "/collections/pg_temp.landsat/tiles/WebMercatorQuad/0/0/0?p=0" + ) assert response.status_code == 200 decoded = mapbox_vector_tile.decode(response.content) assert not decoded # default p=0 so it should return nothing - response = app_functions.get("/collections/pg_temp.landsat/tiles/0/0/0") + response = app_functions.get( + "/collections/pg_temp.landsat/tiles/WebMercatorQuad/0/0/0" + ) assert response.status_code == 200 decoded = mapbox_vector_tile.decode(response.content) assert not decoded diff --git a/tipg/factory.py b/tipg/factory.py index 29e0b3c..1155094 100644 --- a/tipg/factory.py +++ b/tipg/factory.py @@ -1192,6 +1192,7 @@ def links(self, request: Request) -> List[model.Link]: request, "collection_get_tile", collectionId="{collectionId}", + tileMatrixSetId="{tileMatrixSetId}", z="{z}", x="{x}", y="{y}", @@ -1606,22 +1607,16 @@ def _tile_routes(self): operation_id=".collection.vector.getTileTms", tags=["OGC Tiles API"], ) - @self.router.get( - "/collections/{collectionId}/tiles/{z}/{x}/{y}", - response_class=Response, - responses={200: {"content": {MediaType.mvt.value: {}}}}, - operation_id=".collection.vector.getTile", - tags=["OGC Tiles API"], - deprecated=True, - ) async def collection_get_tile( request: Request, collection: Annotated[Collection, Depends(self.collection_dependency)], - tile: Annotated[Tile, Depends(TileParams)], tileMatrixSetId: Annotated[ Literal[tuple(self.supported_tms.list())], - f"Identifier selecting one of the TileMatrixSetId supported (default: '{tms_settings.default_tms}')", - ] = tms_settings.default_tms, + Path( + description="Identifier selecting one of the TileMatrixSetId supported." + ), + ], + tile: Annotated[Tile, Depends(TileParams)], ids_filter: Annotated[Optional[List[str]], Depends(ids_query)] = None, bbox_filter: Annotated[Optional[List[float]], Depends(bbox_query)] = None, datetime_filter: Annotated[ @@ -1680,7 +1675,7 @@ def _tilejson_routes(self): # ADDITIONAL ENDPOINTS NOT IN OGC Tiles API (tilejson, style.json, viewer) # ############################################################################ @self.router.get( - "/collections/{collectionId}/{tileMatrixSetId}/tilejson.json", + "/collections/{collectionId}/tiles/{tileMatrixSetId}/tilejson.json", response_model=model.TileJSON, responses={200: {"description": "Return a tilejson"}}, response_model_exclude_none=True, @@ -1688,23 +1683,15 @@ def _tilejson_routes(self): operation_id=".collection.vector.getTileJSONTms", tags=["OGC Tiles API"], ) - @self.router.get( - "/collections/{collectionId}/tilejson.json", - response_model=model.TileJSON, - responses={200: {"description": "Return a tilejson"}}, - response_model_exclude_none=True, - response_class=ORJSONResponse, - operation_id=".collection.vector.getTileJSON", - tags=["OGC Tiles API"], - deprecated=True, - ) async def collection_tilejson( request: Request, collection: Annotated[Collection, Depends(self.collection_dependency)], tileMatrixSetId: Annotated[ Literal[tuple(self.supported_tms.list())], - f"Identifier selecting one of the TileMatrixSetId supported (default: '{tms_settings.default_tms}')", - ] = tms_settings.default_tms, + Path( + description="Identifier selecting one of the TileMatrixSetId supported." + ), + ], minzoom: Annotated[ Optional[int], Query(description="Overwrite default minzoom."), @@ -1782,7 +1769,7 @@ async def collection_tilejson( def _stylejson_routes(self): @self.router.get( - "/collections/{collectionId}/{tileMatrixSetId}/style.json", + "/collections/{collectionId}/tiles/{tileMatrixSetId}/style.json", response_model=model.StyleJSON, responses={200: {"description": "Return a tilejson"}}, response_model_exclude_none=True, @@ -1790,23 +1777,15 @@ def _stylejson_routes(self): operation_id=".collection.vector.getStyleJSONTms", tags=["OGC Tiles API"], ) - @self.router.get( - "/collections/{collectionId}/style.json", - response_model=model.StyleJSON, - responses={200: {"description": "Return a StyleJSON"}}, - response_model_exclude_none=True, - response_class=ORJSONResponse, - operation_id=".collection.vector.getStyleJSON", - tags=["OGC Tiles API"], - deprecated=True, - ) async def collection_stylejson( request: Request, collection: Annotated[Collection, Depends(self.collection_dependency)], tileMatrixSetId: Annotated[ Literal[tuple(self.supported_tms.list())], - f"Identifier selecting one of the TileMatrixSetId supported (default: '{tms_settings.default_tms}')", - ] = tms_settings.default_tms, + Path( + description="Identifier selecting one of the TileMatrixSetId supported." + ), + ], geom_column: Annotated[ Optional[str], Query( @@ -1921,20 +1900,6 @@ async def collection_stylejson( if self.with_viewer: - @self.router.get( - "/collections/{collectionId}/{tileMatrixSetId}/viewer", - response_class=HTMLResponse, - operation_id=".collection.vector.viewerTms", - deprecated=True, - tags=["Map Viewer"], - ) - @self.router.get( - "/collections/{collectionId}/viewer", - response_class=HTMLResponse, - operation_id=".collection.vector.viewer", - deprecated=True, - tags=["Map Viewer"], - ) @self.router.get( "/collections/{collectionId}/tiles/{tileMatrixSetId}/viewer", response_class=HTMLResponse, @@ -1946,8 +1911,10 @@ def viewer_endpoint( collection: Annotated[Collection, Depends(self.collection_dependency)], tileMatrixSetId: Annotated[ Literal[tuple(self.supported_tms.list())], - f"Identifier selecting one of the TileMatrixSetId supported (default: '{tms_settings.default_tms}')", - ] = tms_settings.default_tms, + Path( + description="Identifier selecting one of the TileMatrixSetId supported." + ), + ], minzoom: Annotated[ Optional[int], Query(description="Overwrite default minzoom."),