diff --git a/xcube/webapi/datasets/__init__.py b/xcube/webapi/datasets/__init__.py index 5510c38db..a577fa4da 100644 --- a/xcube/webapi/datasets/__init__.py +++ b/xcube/webapi/datasets/__init__.py @@ -19,5 +19,10 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -# noinspection PyUnresolvedReferences +from .routes import PATH_PARAM_DATASET_ID +from .routes import PATH_PARAM_VAR_NAME +from .routes import QUERY_PARAM_CBAR +from .routes import QUERY_PARAM_CRS +from .routes import QUERY_PARAM_VMAX +from .routes import QUERY_PARAM_VMIN from .routes import api diff --git a/xcube/webapi/datasets/controllers.py b/xcube/webapi/datasets/controllers.py index b139fc830..454a019bf 100644 --- a/xcube/webapi/datasets/controllers.py +++ b/xcube/webapi/datasets/controllers.py @@ -51,7 +51,6 @@ _CRS84 = pyproj.CRS.from_string('CRS84') - def find_dataset_places(ctx: DatasetsContext, place_group_id: str, ds_id: str, diff --git a/xcube/webapi/datasets/routes.py b/xcube/webapi/datasets/routes.py index 717bf7496..290dc1ce6 100644 --- a/xcube/webapi/datasets/routes.py +++ b/xcube/webapi/datasets/routes.py @@ -30,8 +30,67 @@ from .controllers import get_dataset_place_group from .controllers import get_datasets from .controllers import get_legend +from ..places import PATH_PARAM_PLACE_GROUP_ID from ...util.assertions import assert_true +PATH_PARAM_DATASET_ID = { + "name": "datasetId", + "in": "path", + "description": "Dataset identifier", + "schema": {"type": "string"} +} + +PATH_PARAM_VAR_NAME = { + "name": "varName", + "in": "path", + "description": "Variable name", + "schema": {"type": "string"} +} + +QUERY_PARAM_CRS = { + "name": "crs", + "in": "query", + "description": "The tile grid's spatial CRS", + "schema": { + "type": "string", + "enum": ["EPSG:3857", "CRS84"], + "default": "CRS84" + } +} + +QUERY_PARAM_VMIN = { + "name": "vmin", + "in": "query", + "description": "Minimum value of variable" + " for color mapping", + "schema": { + "type": "number", + "default": 0 + } +} + +QUERY_PARAM_VMAX = { + "name": "vmax", + "in": "query", + "description": "Maximum value of variable" + " for color mapping", + "schema": { + "type": "number", + "default": 1 + } +} + +QUERY_PARAM_CBAR = { + "name": "cbar", + "in": "query", + "description": "Name of the (matplotlib) color bar" + " for color mapping", + "schema": { + "type": "string", + "default": "bone" + } +} + @api.route('/datasets') class DatasetsHandler(ApiHandler[DatasetsContext]): @@ -39,7 +98,7 @@ class DatasetsHandler(ApiHandler[DatasetsContext]): @api.operation( operation_id="getDatasets", - summary="Get the published datasets", + summary="Get all datasets.", parameters=[ { "name": "details", @@ -86,7 +145,8 @@ def get(self): class DatasetHandler(ApiHandler[DatasetsContext]): # noinspection PyPep8Naming @api.operation(operation_id='getDataset', - summary='Get dataset details') + summary='Get the details of a dataset.', + parameters=[PATH_PARAM_DATASET_ID]) async def get(self, datasetId: str): granted_scopes = self.ctx.auth_ctx.get_granted_scopes( self.request.headers @@ -104,7 +164,8 @@ class DatasetCoordsHandler(ApiHandler[DatasetsContext]): # noinspection PyPep8Naming @api.operation(operation_id='getDatasetCoordinates', - summary='Get dataset coordinates') + summary='Get the coordinates for a dimension of a dataset.', + parameters=[PATH_PARAM_DATASET_ID]) async def get(self, datasetId: str, dimName: str): result = get_dataset_coordinates(self.ctx, datasetId, dimName) self.response.set_header('Content-Type', 'application/json') @@ -117,7 +178,11 @@ class DatasetPlaceGroupHandler(ApiHandler[DatasetsContext]): """Get places for given dataset and place group.""" @api.operation(operation_id='getDatasetPlaceGroup', - summary='Get places for given dataset and place group') + summary='Get places for given dataset and place group.', + parameters=[ + PATH_PARAM_DATASET_ID, + PATH_PARAM_PLACE_GROUP_ID + ]) def get(self, datasetId: str, placeGroupId: str): response = get_dataset_place_group(self.ctx, datasetId, @@ -132,7 +197,11 @@ class PlacesForDatasetHandler(ApiHandler[DatasetsContext]): @api.operation(operation_id='findPlacesForDataset', tags=['places'], summary='Find places in place group for' - ' bounding box of given dataset') + ' bounding box of given dataset.', + parameters=[ + PATH_PARAM_PLACE_GROUP_ID, + PATH_PARAM_DATASET_ID + ]) def get(self, placeGroupId: str, datasetId: str): query_expr = self.request.get_query_arg("query", default=None) comb_op = self.request.get_query_arg("comb", default="and") @@ -160,11 +229,40 @@ async def get(self, datasetId: str, varName: str): await self.response.finish(legend) +LEGEND_PARAMETERS = [ + PATH_PARAM_DATASET_ID, + PATH_PARAM_VAR_NAME, + QUERY_PARAM_CBAR, + QUERY_PARAM_VMIN, + QUERY_PARAM_VMAX, + { + "name": "width", + "in": "query", + "description": "Width of the legend in pixels", + "schema": { + "type": "number", + "default": 256 + } + }, + { + "name": "height", + "in": "query", + "description": "Height of the legend in pixels", + "schema": { + "type": "number", + "default": 16 + } + }, +] + + # noinspection PyPep8Naming @api.route('/datasets/{datasetId}/vars/{varName}/legend.png') class OldLegendHandler(LegendHandler): @api.operation(operation_id='getLegendForVariable', - summary='Deprecated.') + summary='Get the legend as PNG used for the tiles' + ' for given variable. Deprecated!', + parameters=LEGEND_PARAMETERS) async def get(self, datasetId: str, varName: str): await super().get(datasetId, varName) @@ -175,14 +273,14 @@ class NewLegendHandler(LegendHandler): @api.operation(operation_id='getLegendForTiles', tags=["tiles"], summary='Get the legend as PNG used for the tiles' - ' for given variable.') + ' for given variable.', + parameters=LEGEND_PARAMETERS) async def get(self, datasetId: str, varName: str): await super().get(datasetId, varName) # TODO (forman): move as endpoint "styles/colorbars" into API "styles" - @api.route('/colorbars') class StylesColorBarsHandler(ApiHandler): """Get available color bars.""" diff --git a/xcube/webapi/ows/wmts/routes.py b/xcube/webapi/ows/wmts/routes.py index d600c297f..42a8a84da 100644 --- a/xcube/webapi/ows/wmts/routes.py +++ b/xcube/webapi/ows/wmts/routes.py @@ -29,10 +29,59 @@ from .controllers import WMTS_WEB_MERCATOR_TMS_ID from .controllers import get_crs_name_from_tms_id from .controllers import get_wmts_capabilities_xml +from ...datasets import PATH_PARAM_DATASET_ID +from ...datasets import PATH_PARAM_VAR_NAME +from ...datasets import QUERY_PARAM_CBAR +from ...datasets import QUERY_PARAM_CRS +from ...datasets import QUERY_PARAM_VMAX +from ...datasets import QUERY_PARAM_VMIN +from ...tiles import PATH_PARAM_X +from ...tiles import PATH_PARAM_Y +from ...tiles import PATH_PARAM_Z +from ...tiles import QUERY_PARAM_RETINA +from ...tiles import QUERY_PARAM_TIME from ...tiles.controllers import compute_ml_dataset_tile _VALID_WMTS_TMS_IDS = (WMTS_CRS84_TMS_ID, WMTS_WEB_MERCATOR_TMS_ID) +PATH_PARAM_TMS_ID = { + "name": "tmsId", + "in": "query", + "description": "Tile matrix set identifier", + "schema": { + "type": "string", + "enum": [WMTS_CRS84_TMS_ID, WMTS_WEB_MERCATOR_TMS_ID], + } +} + +TILE_PARAMETERS = [ + PATH_PARAM_DATASET_ID, + PATH_PARAM_VAR_NAME, + PATH_PARAM_Z, + PATH_PARAM_Y, + PATH_PARAM_X, + QUERY_PARAM_CRS, + QUERY_PARAM_VMIN, + QUERY_PARAM_VMAX, + QUERY_PARAM_CBAR, + QUERY_PARAM_TIME, + QUERY_PARAM_RETINA, +] + +TMS_TILE_PARAMETERS = [ + PATH_PARAM_DATASET_ID, + PATH_PARAM_VAR_NAME, + PATH_PARAM_Z, + PATH_PARAM_Y, + PATH_PARAM_X, + QUERY_PARAM_CRS, + QUERY_PARAM_VMIN, + QUERY_PARAM_VMAX, + QUERY_PARAM_CBAR, + QUERY_PARAM_TIME, + QUERY_PARAM_RETINA, +] + @api.route('/wmts/1.0.0/WMTSCapabilities.xml') class WmtsCapabilitiesXmlHandler(ApiHandler[WmtsContext]): @@ -56,7 +105,8 @@ class WmtsCapabilitiesXmlForTmsHandler(ApiHandler[WmtsContext]): # noinspection PyPep8Naming @api.operation(operationId='getWmtsTmsCapabilities', summary='Gets the WMTS capabilities' - ' for tile matrix set as XML document') + ' for tile matrix set as XML document', + parameters=[PATH_PARAM_TMS_ID]) async def get(self, tmsId): self.request.make_query_lower_case() _assert_valid_tms_id(tmsId) @@ -75,7 +125,8 @@ async def get(self, tmsId): class WmtsImageTileHandler(ApiHandler[WmtsContext]): # noinspection PyPep8Naming @api.operation(operationId='getWmtsImageTile', - summary='Gets a WMTS image tile in PNG format') + summary='Gets a WMTS image tile in PNG format.', + parameters=TILE_PARAMETERS) async def get(self, datasetId: str, varName: str, @@ -105,7 +156,8 @@ class WmtsImageTileForTmsHandler(ApiHandler[WmtsContext]): # noinspection PyPep8Naming @api.operation(operationId='getWmtsTmsImageTile', summary='Gets a WMTS image tile' - ' for given tile matrix set in PNG format') + ' for given tile matrix set in PNG format', + parameters=TMS_TILE_PARAMETERS) async def get(self, datasetId: str, varName: str, @@ -131,7 +183,8 @@ async def get(self, @api.route('/wmts/kvp') class WmtsKvpHandler(ApiHandler[WmtsContext]): @api.operation(operationId='invokeWmtsMethodFromKvp', - summary='Invokes the WMTS by key-value pairs') + summary='Invokes the WMTS by key-value pairs', + parameters=TILE_PARAMETERS) async def get(self): self.request.make_query_lower_case() service = self.request.get_query_arg('service') diff --git a/xcube/webapi/places/__init__.py b/xcube/webapi/places/__init__.py index cdbb48c9a..8b9fd8d49 100644 --- a/xcube/webapi/places/__init__.py +++ b/xcube/webapi/places/__init__.py @@ -20,4 +20,5 @@ # DEALINGS IN THE SOFTWARE. from .context import PlacesContext +from .routes import PATH_PARAM_PLACE_GROUP_ID from .routes import api diff --git a/xcube/webapi/places/context.py b/xcube/webapi/places/context.py index 62c9fe3cf..603298d50 100644 --- a/xcube/webapi/places/context.py +++ b/xcube/webapi/places/context.py @@ -1,5 +1,5 @@ # The MIT License (MIT) -# Copyright (c) 2022 by the xcube team and contributors +# Copyright (c) 2023 by the xcube team and contributors # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), diff --git a/xcube/webapi/places/routes.py b/xcube/webapi/places/routes.py index 60d066719..cbaccd111 100644 --- a/xcube/webapi/places/routes.py +++ b/xcube/webapi/places/routes.py @@ -24,13 +24,22 @@ from .context import PlacesContext from .controllers import find_places +PATH_PARAM_PLACE_GROUP_ID = { + "name": "placeGroupId", + "in": "path", + "description": "Place group identifier", + "schema": {"type": "string"} +} + @api.route('/places') class PlaceGroupsHandler(ApiHandler[PlacesContext]): - @api.operation(operationId='getPlaceGroups') + @api.operation(operationId='getPlaceGroups', + summary='Get place groups including all places.') def get(self): place_groups = self.ctx.get_global_place_groups( - self.request.reverse_base_url) + self.request.reverse_base_url + ) self.response.finish({"placeGroups": place_groups}) @@ -42,6 +51,7 @@ class FindPlacesHandler(ApiHandler): @api.operation(operationId='findPlacesInPlaceGroup', summary='Find places in a given place group.', parameters=[ + PATH_PARAM_PLACE_GROUP_ID, { "name": "geom", "in": "query", @@ -79,6 +89,7 @@ def get(self, placeGroupId: str): @api.operation(operationId='findPlacesInPlaceGroup', summary='Find places in a given place group' ' for a GeoJSON object.', + parameters=[PATH_PARAM_PLACE_GROUP_ID], description='The request body must be a GeoJSON object.') def post(self, placeGroupId: str): # Not implemented yet: diff --git a/xcube/webapi/tiles/__init__.py b/xcube/webapi/tiles/__init__.py index b26372509..b4b0d1341 100644 --- a/xcube/webapi/tiles/__init__.py +++ b/xcube/webapi/tiles/__init__.py @@ -19,6 +19,10 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER # DEALINGS IN THE SOFTWARE. -# noinspection PyUnresolvedReferences +from .routes import PATH_PARAM_X +from .routes import PATH_PARAM_Y +from .routes import PATH_PARAM_Z +from .routes import QUERY_PARAM_TIME +from .routes import QUERY_PARAM_FORMAT +from .routes import QUERY_PARAM_RETINA from .routes import api - diff --git a/xcube/webapi/tiles/routes.py b/xcube/webapi/tiles/routes.py index 1b0a0fed6..3f9e78e14 100644 --- a/xcube/webapi/tiles/routes.py +++ b/xcube/webapi/tiles/routes.py @@ -23,107 +23,85 @@ from .api import api from .context import TilesContext from .controllers import compute_ml_dataset_tile +from ..datasets import PATH_PARAM_DATASET_ID +from ..datasets import PATH_PARAM_VAR_NAME +from ..datasets import QUERY_PARAM_CBAR +from ..datasets import QUERY_PARAM_CRS +from ..datasets import QUERY_PARAM_VMAX +from ..datasets import QUERY_PARAM_VMIN + +PATH_PARAM_X = { + "name": "x", + "in": "path", + "description": "The tile grid's x-coordinate", + "schema": { + "type": "integer", + } +} + +PATH_PARAM_Y = { + "name": "y", + "in": "path", + "description": "The tile grid's y-coordinate", + "schema": { + "type": "integer", + } +} + +PATH_PARAM_Z = { + "name": "z", + "in": "path", + "description": "The tile grid's z-coordinate", + "schema": { + "type": "integer", + } +} + +QUERY_PARAM_TIME = { + "name": "time", + "in": "query", + "description": "Optional time coordinate using format" + " \"YYYY-MM-DD hh:mm:ss\"", + "schema": { + "type": "string", + "format": "datetime" + } +} + +QUERY_PARAM_FORMAT = { + "name": "format", + "in": "query", + "description": "Image format", + "schema": { + "type": "string", + "enum": ["png", "image/png"], + "default": "png" + } +} + +QUERY_PARAM_RETINA = { + "name": "retina", + "in": "query", + "description": "Returns tiles of size" + " 512 instead of 256", + "schema": { + "type": "boolean" + } +} TILE_PARAMETERS = [ - { - "name": "datasetId", - "in": "path", - "description": "Dataset identifier", - "schema": { - "type": "string", - } - }, - { - "name": "varName", - "in": "path", - "description": "Name of variable in dataset", - "schema": { - "type": "string", - } - }, - { - "name": "z", - "in": "path", - "description": "The tile grid's z-coordinate", - "schema": { - "type": "integer", - } - }, - { - "name": "y", - "in": "path", - "description": "The tile grid's y-coordinate", - "schema": { - "type": "integer", - } - }, - { - "name": "x", - "in": "path", - "description": "The tile grid's x-coordinate", - "schema": { - "type": "integer", - } - }, - { - "name": "crs", - "in": "query", - "description": "The tile grid's spatial CRS", - "schema": { - "type": "string", - "enum": ["EPSG:3857", "CRS84"], - "default": "CRS84" - } - }, - { - "name": "vmin", - "in": "query", - "description": "Minimum value of variable" - " for color mapping", - "schema": { - "type": "number", - "default": 0 - } - }, - { - "name": "vmax", - "in": "query", - "description": "Maximum value of variable" - " for color mapping", - "schema": { - "type": "number", - "default": 1 - } - }, - { - "name": "cbar", - "in": "query", - "description": "Name of the color bar" - " for color mapping", - "schema": { - "type": "string", - "default": "bone" - } - }, - { - "name": "format", - "in": "query", - "description": "Image format", - "schema": { - "type": "string", - "enum": ["png", "image/png"], - "default": "png" - } - }, - { - "name": "retina", - "in": "query", - "description": "Returns tiles of size" - " 512 instead of 256", - "schema": { - "type": "boolean" - } - }, + PATH_PARAM_DATASET_ID, + PATH_PARAM_VAR_NAME, + PATH_PARAM_Z, + PATH_PARAM_Y, + PATH_PARAM_X, + QUERY_PARAM_CRS, + QUERY_PARAM_VMIN, + QUERY_PARAM_VMAX, + QUERY_PARAM_CBAR, + QUERY_PARAM_TIME, + QUERY_PARAM_FORMAT, + QUERY_PARAM_RETINA, ] diff --git a/xcube/webapi/timeseries/routes.py b/xcube/webapi/timeseries/routes.py index 85334045a..9f229975b 100644 --- a/xcube/webapi/timeseries/routes.py +++ b/xcube/webapi/timeseries/routes.py @@ -25,6 +25,8 @@ from .api import api from .context import TimeSeriesContext from .controllers import get_time_series +from ..datasets import PATH_PARAM_DATASET_ID +from ..datasets import PATH_PARAM_VAR_NAME # noinspection PyPep8Naming @@ -34,22 +36,8 @@ class TimeseriesHandler(ApiHandler[TimeSeriesContext]): summary="Get the time-series for a variable" " and given GeoJSON object.", parameters=[ - { - "name": "datasetId", - "in": "path", - "description": "Dataset identifier", - "schema": { - "type": "string", - } - }, - { - "name": "varName", - "in": "path", - "description": "Name of variable in dataset", - "schema": { - "type": "string", - } - }, + PATH_PARAM_DATASET_ID, + PATH_PARAM_VAR_NAME, { "name": "aggMethods", "in": "query",