diff --git a/.dockerignore b/.dockerignore index 584e6c4e4..5a2addbe6 100644 --- a/.dockerignore +++ b/.dockerignore @@ -12,3 +12,4 @@ !gunicorn.conf.py !package*.json !screenshot.js +!ogcapi-tiles-bundled-filtered.json diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 2421f3a19..388d3253e 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -43,7 +43,7 @@ jobs: restore-keys: "pre-commit-${{ hashFiles('.pre-commit-config.yaml') }}\npre-commit-" - run: pre-commit run --all-files env: - SKIP: poetry-lock + SKIP: poetry-lock,jsonschema-gentypes - run: git diff && false if: failure() - name: Checks diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d8f4730f4..9362deb84 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -60,7 +60,12 @@ repos: rev: v2.2.4 hooks: - id: codespell - exclude: ^(.*/)?poetry\.lock$ + exclude: |- + (?x)^( + (.*/)?poetry\.lock + |ogcapi-tiles-bundled\.json + |ogcapi-tiles-bundled-filtered\.json + )$ args: - --ignore-words=.github/spell-ignore-words.txt - repo: https://github.com/shellcheck-py/shellcheck-py diff --git a/docker-compose.sample.override.yaml b/docker-compose.override.sample.yaml similarity index 100% rename from docker-compose.sample.override.yaml rename to docker-compose.override.sample.yaml diff --git a/ogcapi-tiles-bundled-filtered.json b/ogcapi-tiles-bundled-filtered.json new file mode 100644 index 000000000..86de94a8e --- /dev/null +++ b/ogcapi-tiles-bundled-filtered.json @@ -0,0 +1,2890 @@ +{ + "openapi": "3.0.0", + "info": { + "version": "1.0", + "title": "OGC API - Tiles", + "description": "API Definition for OGC API - Tiles - Part 1: Core", + "contact": { + "name": "Open Geospatial Consortium", + "email": "info@ogc.org" + }, + "license": { + "name": "OGC License", + "url": "http://www.opengeospatial.org/legal/" + } + }, + "servers": [ + { + "description": "OGC API - Tiles server", + "url": "http://localhost:9050/ogcapi" + } + ], + "paths": { + "/": { + "get": { + "tags": ["Landing Page"], + "operationId": "getLandingPage", + "summary": "Retrieve the OGC API landing page for this service.", + "parameters": [ + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/LandingPage" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + + "/conformance": { + "get": { + "tags": ["Conformance"], + "operationId": "getConformance", + "summary": "Retrieve the set of OGC API conformance classes that are supported by this service.", + "parameters": [ + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Conformance" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/api": { + "get": { + "tags": ["API"], + "operationId": "getAPI", + "summary": "Retrieve this API definition.", + "parameters": [ + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/API" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/api/all-collections": { + "get": { + "tags": ["API"], + "operationId": "getAPICollections", + "summary": "Retrieve the list of collections available from this API implementation & deployment.", + "parameters": [ + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Enumeration" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/api/coverage-collections": { + "get": { + "tags": ["API"], + "operationId": "getAPICoverageCollections", + "summary": "Retrieve the list of coverages collections available from this API implementation & deployment.", + "parameters": [ + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Enumeration" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/api/vectorTiles-collections": { + "get": { + "tags": ["API"], + "operationId": "getAPIVectorTileCollections", + "summary": "Retrieve the list of collections supporting vector tiles available from this API implementation & deployment.", + "parameters": [ + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Enumeration" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/api/tileMatrixSets": { + "get": { + "tags": ["API"], + "operationId": "getAPITileMatrixSets", + "summary": "Retrieve the list of shared TileMatrixSets available from this API implementation & deployment.", + "parameters": [ + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Enumeration" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/api/styles": { + "get": { + "tags": ["API"], + "operationId": "getAPIStyles", + "summary": "Retrieve the list of dataset styles available from this API implementation & deployment.", + "parameters": [ + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Enumeration" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections": { + "get": { + "tags": ["Data Collections"], + "operationId": "getCollectionsList", + "summary": "Retrieve the list of geospatial data collections available from this service.", + "parameters": [ + { + "$ref": "#/components/parameters/datetime" + }, + { + "name": "bbox", + "in": "query", + "description": "Only features that have a geometry that intersects the bounding box are selected. The bounding box is provided as four or six numbers, depending on whether the coordinate reference system includes a vertical axis (height or depth):\n* Lower left corner, coordinate axis 1 * Lower left corner, coordinate axis 2 * Minimum value, coordinate axis 3 (optional) * Upper right corner, coordinate axis 1 * Upper right corner, coordinate axis 2 * Maximum value, coordinate axis 3 (optional)\nThe coordinate reference system of the values is WGS 84 longitude/latitude (http://www.opengis.net/def/crs/OGC/1.3/CRS84) unless a different coordinate reference system is specified in the parameter `bbox-crs`.\nFor WGS 84 longitude/latitude the values are in most cases the sequence of minimum longitude, minimum latitude, maximum longitude and maximum latitude. However, in cases where the box spans the antimeridian the first value (west-most box edge) is larger than the third value (east-most box edge).\nIf the vertical axis is included, the third and the sixth number are the bottom and the top of the 3-dimensional bounding box.\nIf a feature has multiple spatial geometry properties, it is the decision of the server whether only a single spatial geometry property is used to determine the extent or all relevant geometries.", + "required": false, + "schema": { + "type": "array", + "oneOf": [ + { + "minItems": 4, + "maxItems": 4 + }, + { + "minItems": 6, + "maxItems": 6 + } + ], + "items": { + "type": "number", + "format": "double" + } + }, + "style": "form", + "explode": false + }, + { + "name": "limit", + "in": "query", + "description": "The optional limit parameter limits the number of collections that are presented in the response document.\nOnly items are counted that are on the first level of the collection in the response document. Nested objects contained within the explicitly requested items shall not be counted.\n* Minimum = 1 * Maximum = 10000 * Default = 10", + "required": false, + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 10000, + "default": 10 + }, + "style": "form", + "explode": false + }, + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/CollectionsList" + } + } + } + }, + "/collections/{collectionId}": { + "get": { + "tags": ["Data Collections"], + "operationId": "getCollection", + "summary": "Retrieve the description of a collection available from this service.", + "parameters": [ + { + "$ref": "#/components/parameters/collectionId-all" + }, + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Collection" + } + } + } + }, + "/tileMatrixSets": { + "get": { + "tags": ["Tiling Schemes"], + "summary": "Retrieve the list of available tiling schemes (tile matrix sets)", + "operationId": "getTileMatrixSetsList", + "parameters": [ + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileMatrixSetsList" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/tileMatrixSets/{tileMatrixSetId}": { + "get": { + "tags": ["Tiling Schemes"], + "summary": "Retrieve the definition of the specified tiling scheme (tile matrix set)", + "operationId": "getTileMatrixSet", + "parameters": [ + { + "$ref": "#/components/parameters/tileMatrixSetId" + }, + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileMatrixSet" + }, + "404": { + "description": "The requested tile matrix set id was not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/exception" + } + } + } + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/map/tiles": { + "get": { + "tags": ["Map Tiles"], + "summary": "Retrieve a list of all map tilesets for specified collection.", + "operationId": ".collection.map.getTileSetsList", + "parameters": [ + { + "$ref": "#/components/parameters/collectionId-all" + }, + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileSetsList" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/map/tiles/{tileMatrixSetId}": { + "get": { + "tags": ["Map Tiles"], + "summary": "Retrieve a map tile set metadata for the specified collection and tiling scheme (tile matrix set)", + "operationId": ".collection.map.getTileSet", + "parameters": [ + { + "$ref": "#/components/parameters/collectionId-all" + }, + { + "$ref": "#/components/parameters/collections" + }, + { + "$ref": "#/components/parameters/tileMatrixSetId" + }, + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileSet" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/map/tiles/{tileMatrixSetId}/{tileMatrix}/{tileRow}/{tileCol}": { + "get": { + "tags": ["Map Tiles"], + "summary": "Retrieve a map tile from the specified collection", + "operationId": ".collection.map.getTile", + "parameters": [ + { + "$ref": "#/components/parameters/tileMatrix" + }, + { + "$ref": "#/components/parameters/tileRow" + }, + { + "$ref": "#/components/parameters/tileCol" + }, + { + "$ref": "#/components/parameters/datetime" + }, + { + "$ref": "#/components/parameters/collectionId-all" + }, + { + "$ref": "#/components/parameters/collections" + }, + { + "$ref": "#/components/parameters/subset" + }, + { + "$ref": "#/components/parameters/crs" + }, + { + "$ref": "#/components/parameters/subset-crs" + }, + { + "$ref": "#/components/parameters/bgcolor" + }, + { + "$ref": "#/components/parameters/transparent" + }, + { + "$ref": "#/components/parameters/tileMatrixSetId" + }, + { + "$ref": "#/components/parameters/f-mapTile" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/MapTile" + }, + "204": { + "$ref": "#/components/responses/EmptyTile" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + } + }, + "components": { + "schemas": { + "confClasses": { + "type": "object", + "required": ["conformsTo"], + "properties": { + "conformsTo": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "link": { + "type": "object", + "required": ["href", "rel"], + "properties": { + "href": { + "type": "string", + "description": "Supplies the URI to a remote resource (or resource fragment).", + "example": "http://data.example.com/buildings/123" + }, + "rel": { + "type": "string", + "description": "The type or semantics of the relation.", + "example": "alternate" + }, + "type": { + "type": "string", + "description": "A hint indicating what the media type of the result of dereferencing the link should be.", + "example": "application/geo+json" + }, + "templated": { + "type": "boolean", + "description": "This flag set to true if the link is a URL template." + }, + "varBase": { + "description": "A base path to retrieve semantic information about the variables used in URL template.", + "type": "string", + "example": "/ogcapi/vars/" + }, + "hreflang": { + "type": "string", + "description": "A hint indicating what the language of the result of dereferencing the link should be.", + "example": "en" + }, + "title": { + "type": "string", + "description": "Used to label the destination of a link such that it can be used as a human-readable identifier.", + "example": "Trierer Strasse 70, 53115 Bonn" + }, + "length": { + "type": "integer" + } + } + }, + "landingPage": { + "type": "object", + "required": ["links"], + "properties": { + "title": { + "type": "string", + "title": "The title of the API.", + "description": "While a title is not required, implementors are strongly advised to include one.", + "example": "Buildings in Bonn" + }, + "description": { + "type": "string", + "example": "Access to data about buildings in the city of Bonn via a Web API that conforms to the OGC API Common specification." + }, + "attribution": { + "type": "string", + "title": "attribution for the API", + "description": "The `attribution` should be short and intended for presentation to a user, for example, in a corner of a map. Parts of the text can be links to other resources if additional information is needed. The string can include HTML markup." + }, + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/link" + } + } + } + }, + "exception": { + "title": "Exception Schema", + "description": "JSON schema for exceptions based on RFC 7807", + "type": "object", + "required": ["type"], + "properties": { + "type": { + "type": "string" + }, + "title": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "detail": { + "type": "string" + }, + "instance": { + "type": "string" + } + } + }, + "collections": { + "type": "object", + "required": ["links", "collections"], + "properties": { + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/link" + } + }, + "timeStamp": { + "type": "string", + "format": "date-time" + }, + "numberMatched": { + "type": "integer", + "minimum": 0, + "example": 1 + }, + "numberReturned": { + "type": "integer", + "minimum": 0, + "example": 1 + }, + "collections": { + "type": "array", + "items": { + "$ref": "#/components/schemas/collectionInfo" + } + } + } + }, + "collectionInfo": { + "type": "object", + "required": ["id", "links"], + "properties": { + "id": { + "type": "string", + "description": "identifier of the collection used, for example, in URIs", + "example": "dem" + }, + "title": { + "type": "string", + "description": "human readable title of the collection", + "example": "Digital Elevation Model" + }, + "description": { + "type": "string", + "description": "a description of the data in the collection", + "example": "A Digital Elevation Model." + }, + "links": { + "type": "array", + "example": [ + { + "href": "http://data.example.org/collections/dem?f=json", + "rel": "self", + "type": "application/json", + "title": "Digital Elevation Model" + }, + { + "href": "http://data.example.org/collections/dem?f=html", + "rel": "alternate", + "type": "application/json", + "title": "Digital Elevation Model" + }, + { + "href": "http://data.example.org/collections/dem/coverage", + "rel": "coverage", + "type": "image/tiff; application=geotiff", + "title": "Digital Elevation Model" + }, + { + "href": "http://data.example.org/collections/dem/coverage/domainset", + "rel": "domainset", + "type": "application/json", + "title": "Digital Elevation Model" + }, + { + "href": "http://data.example.org/collections/dem/coverage/rangetype", + "rel": "rangetype", + "type": "application/json", + "title": "Digital Elevation Model" + }, + { + "href": "http://data.example.org/collections/dem/coverage/metadata", + "rel": "metadata", + "type": "application/json", + "title": "Digital Elevation Model" + } + ], + "items": { + "$ref": "#/components/schemas/link" + } + }, + "extent": { + "$ref": "#/components/schemas/extent-uad" + }, + "itemType": { + "description": "indicator about the type of the items in the collection if the collection has an accessible /collections/{collectionId}/items endpoint", + "type": "string", + "default": "unknown" + }, + "crs": { + "description": "the list of coordinate reference systems supported by the API; the first item is the default coordinate reference system", + "type": "array", + "items": { + "type": "string" + }, + "default": ["http://www.opengis.net/def/crs/OGC/1.3/CRS84"], + "example": [ + "http://www.opengis.net/def/crs/OGC/1.3/CRS84", + "http://www.opengis.net/def/crs/EPSG/0/4326" + ] + }, + "dataType": { + "allOf": [ + { + "description": "Type of data represented in the collection" + }, + { + "$ref": "#/components/schemas/dataType" + } + ] + }, + "geometryDimension": { + "description": "The geometry dimension of the features shown in this layer (0: points, 1: curves, 2: surfaces, 3: solids), unspecified: mixed or unknown", + "type": "integer", + "minimum": 0, + "maximum": 3 + }, + "minScaleDenominator": { + "description": "Minimum scale denominator for usage of the collection", + "type": "number" + }, + "maxScaleDenominator": { + "description": "Maximum scale denominator for usage of the collection", + "type": "number" + }, + "minCellSize": { + "description": "Minimum cell size for usage of the collection", + "type": "number" + }, + "maxCellSize": { + "description": "Maximum cell size for usage of the collection", + "type": "number" + } + } + }, + "extent": { + "description": "The extent of the data in the collection. In the Core only spatial and temporal\nextents are specified. Extensions may add additional members to represent other\nextents, for example, thermal or pressure ranges.\n\nThe first item in the array describes the overall extent of\nthe data. All subsequent items describe more precise extents,\ne.g., to identify clusters of data.\nClients only interested in the overall extent will only need to\naccess the first item in each array.", + "type": "object", + "properties": { + "spatial": { + "description": "The spatial extent of the data in the collection.", + "type": "object", + "properties": { + "bbox": { + "description": "One or more bounding boxes that describe the spatial extent of the dataset.\nIn the Core only a single bounding box is supported.\n\nExtensions may support additional areas.\nThe first bounding box describes the overall spatial\nextent of the data. All subsequent bounding boxes describe\nmore precise bounding boxes, e.g., to identify clusters of data.\nClients only interested in the overall spatial extent will\nonly need to access the first item in each array.", + "type": "array", + "minItems": 1, + "items": { + "description": "Each bounding box is provided as four or six numbers, depending on\nwhether the coordinate reference system includes a vertical axis\n(height or depth):\n\n* Lower left corner, coordinate axis 1\n* Lower left corner, coordinate axis 2\n* Minimum value, coordinate axis 3 (optional)\n* Upper right corner, coordinate axis 1\n* Upper right corner, coordinate axis 2\n* Maximum value, coordinate axis 3 (optional)\n\nIf the value consists of four numbers, the coordinate reference system is\nWGS 84 longitude/latitude (http://www.opengis.net/def/crs/OGC/1.3/CRS84)\nunless a different coordinate reference system is specified in a parameter `bbox-crs`.\n\nIf the value consists of six numbers, the coordinate reference system is WGS 84\nlongitude/latitude/ellipsoidal height (http://www.opengis.net/def/crs/OGC/0/CRS84h)\nunless a different coordinate reference system is specified in a parameter `bbox-crs`.\n\nFor WGS 84 longitude/latitude the values are in most cases the sequence of\nminimum longitude, minimum latitude, maximum longitude and maximum latitude.\nHowever, in cases where the box spans the antimeridian the first value\n(west-most box edge) is larger than the third value (east-most box edge).\n\nIf the vertical axis is included, the third and the sixth number are\nthe bottom and the top of the 3-dimensional bounding box.\n\nIf a feature has multiple spatial geometry properties, it is the decision of the\nserver whether only a single spatial geometry property is used to determine\nthe extent or all relevant geometries.", + "type": "array", + "oneOf": [ + { + "minItems": 4, + "maxItems": 4 + }, + { + "minItems": 6, + "maxItems": 6 + } + ], + "items": { + "type": "number" + }, + "example": [-180, -90, 180, 90] + } + }, + "crs": { + "description": "Coordinate reference system of the coordinates in the spatial extent\n(property `bbox`). The default reference system is WGS 84 longitude/latitude.\nIn the Core the only other supported coordinate reference system is\nWGS 84 longitude/latitude/ellipsoidal height for coordinates with height.\nExtensions may support additional coordinate reference systems and add\nadditional enum values.", + "type": "string", + "enum": [ + "http://www.opengis.net/def/crs/OGC/1.3/CRS84", + "http://www.opengis.net/def/crs/OGC/0/CRS84h" + ], + "default": "http://www.opengis.net/def/crs/OGC/1.3/CRS84" + }, + "grid": { + "description": "Provides information about the limited availability of data within the collection organized\nas a grid (regular or irregular) along each spatial dimension.", + "type": "array", + "minItems": 2, + "maxItems": 3, + "items": { + "type": "object", + "properties": { + "coordinates": { + "description": "List of coordinates along the dimension for which data organized as an irregular grid in the collection is available\n(e.g., 2, 10, 80, 100).", + "type": "array", + "minItems": 1, + "items": { + "oneOf": [ + { + "type": "string", + "nullable": true + }, + { + "type": "number" + } + ] + }, + "example": [2, 10, 80, 100] + }, + "cellsCount": { + "description": "Number of samples available along the dimension for data organized as a regular grid.\nFor values representing the whole area of contiguous cells spanning _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_.\nFor values representing infinitely small point cells spaced by _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_ + 1.", + "type": "integer", + "example": 50 + }, + "resolution": { + "description": "Resolution of regularly gridded data along the dimension in the collection", + "oneOf": [ + { + "type": "string", + "nullable": true + }, + { + "type": "number" + } + ], + "example": 0.0006866455078 + } + } + } + } + } + }, + "temporal": { + "description": "The temporal extent of the features in the collection.", + "type": "object", + "properties": { + "interval": { + "description": "One or more time intervals that describe the temporal extent of the dataset.\nIn the Core only a single time interval is supported.\n\nExtensions may support multiple intervals.\nThe first time interval describes the overall\ntemporal extent of the data. All subsequent time intervals describe\nmore precise time intervals, e.g., to identify clusters of data.\nClients only interested in the overall extent will only need\nto access the first item in each array.", + "type": "array", + "minItems": 1, + "items": { + "description": "Begin and end times of the time interval. The timestamps are in the\ntemporal coordinate reference system specified in `trs`. By default\nthis is the Gregorian calendar.\n\nThe value `null` for start or end time is supported and indicates a half-bounded time interval.", + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "example": ["2011-11-11T12:22:11Z", null] + } + }, + "trs": { + "description": "Coordinate reference system of the coordinates in the temporal extent\n(property `interval`). The default reference system is the Gregorian calendar.\nIn the Core this is the only supported temporal coordinate reference system.\nExtensions may support additional temporal coordinate reference systems and add\nadditional enum values.", + "type": "string", + "enum": ["http://www.opengis.net/def/uom/ISO-8601/0/Gregorian"], + "default": "http://www.opengis.net/def/uom/ISO-8601/0/Gregorian" + }, + "grid": { + "type": "object", + "description": "Provides information about the limited availability of data within the collection organized as a grid (regular or irregular) along the temporal dimension.", + "properties": { + "coordinates": { + "description": "List of coordinates along the temporal dimension for which data organized as an irregular grid in the collection is available\n(e.g., \"2017-11-14T09:00Z\",\"2017-11-14T12:00Z\",\"2017-11-14T15:00Z\",\"2017-11-14T18:00Z\",\"2017-11-14T21:00Z\").", + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "nullable": true + }, + "example": [["2020-11-12T12:15Z", "2020-11-12T12:30Z", "2020-11-12T12:45Z"]] + }, + "cellsCount": { + "description": "Number of samples available along the temporal dimension for data organized as a regular grid.\nFor values representing the whole area of contiguous cells spanning _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_.\nFor values representing infinitely small point cells spaced by _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_ + 1.", + "type": "integer", + "example": 50 + }, + "resolution": { + "description": "Resolution of regularly gridded data along the temporal dimension in the collection", + "oneOf": [ + { + "type": "string", + "nullable": true + }, + { + "type": "number" + } + ], + "example": "PT1H" + } + } + } + } + } + } + }, + "extent-uad": { + "title": "Extent with Uniform Additional Dimensions Schema", + "description": "The extent module only addresses spatial and temporal extents. This module extends extent by specifying how\nintervals and crs properties can be used to specify additional geometries.", + "allOf": [ + { + "$ref": "#/components/schemas/extent" + }, + { + "type": "object", + "additionalProperties": { + "description": "The domain intervals for any additional dimensions of the extent (envelope) beyond those described in temporal and spatial.", + "type": "object", + "oneOf": [ + { + "required": ["interval", "crs"] + }, + { + "required": ["interval", "trs"] + }, + { + "required": ["interval", "vrs"] + } + ], + "properties": { + "interval": { + "description": "One or more intervals that describe the extent for this dimension of the dataset.\nThe value `null` is supported and indicates an unbounded or half-bounded interval.\nThe first interval describes the overall extent of the data for this dimension.\nAll subsequent intervals describe more precise intervals, e.g., to identify clusters of data.\nClients only interested in the overall extent will only need\nto access the first item (a pair of lower and upper bound values).", + "type": "array", + "minItems": 1, + "items": { + "description": "Lower and upper bound values of the interval. The values\nare in the coordinate reference system specified in `crs`, `trs` or `vrs`.", + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "oneOf": [ + { + "type": "string", + "nullable": true + }, + { + "type": "number" + } + ] + }, + "example": ["2011-11-11T12:22:11Z", 32.5, null] + } + }, + "crs": { + "type": "string", + "description": "generic coordinate reference system suitable for any type of dimensions" + }, + "trs": { + "type": "string", + "description": "temporal coordinate reference system (e.g. as defined by Features for 'temporal')" + }, + "vrs": { + "type": "string", + "description": "vertical coordinate reference system (e.g. as defined in EDR for 'vertical')" + }, + "grid": { + "type": "object", + "description": "Provides information about the limited availability of data within the collection organized as a grid (regular or irregular) along the dimension.", + "properties": { + "coordinates": { + "description": "List of coordinates along the temporal dimension for which data organized as an irregular grid in the collection is available\n(e.g., 2, 10, 80, 100).", + "type": "array", + "minItems": 1, + "items": { + "oneOf": [ + { + "type": "string", + "nullable": true + }, + { + "type": "number" + } + ] + }, + "example": [2, 10, 80, 100] + }, + "cellsCount": { + "description": "Number of samples available along the dimension for data organized as a regular grid.\nFor values representing the whole area of contiguous cells spanning _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_.\nFor values representing infinitely small point cells spaced by _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_ + 1.", + "type": "integer", + "example": 50 + }, + "resolution": { + "description": "Resolution of regularly gridded data along the dimension in the collection", + "oneOf": [ + { + "type": "string", + "nullable": true + }, + { + "type": "number" + } + ], + "example": ["PT1H", 0.0006866455078] + } + } + } + } + } + } + ] + }, + "crs": { + "title": "CRS", + "oneOf": [ + { + "description": "Simplification of the object into a url if the other properties are not present", + "type": "string" + }, + { + "type": "object", + "oneOf": [ + { + "required": ["uri"], + "properties": { + "uri": { + "description": "Reference to one coordinate reference system (CRS)", + "type": "string", + "format": "uri" + } + } + }, + { + "required": ["wkt"], + "properties": { + "wkt": { + "allOf": [ + { + "description": "An object defining the CRS using the JSON encoding for Well-known text representation of coordinate reference systems 2.0" + }, + { + "type": "object" + } + ] + } + } + }, + { + "required": ["referenceSystem"], + "properties": { + "referenceSystem": { + "description": "A reference system data structure as defined in the MD_ReferenceSystem of the ISO 19115", + "type": "object" + } + } + } + ] + } + ] + }, + "dataType": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": ["map", "vector", "coverage"] + } + ] + }, + "timeStamp": { + "description": "This property indicates the time and date when the response was generated using RFC 3339 notation.", + "type": "string", + "format": "date-time", + "example": "2017-08-17T08:05:32Z" + }, + "numberReturned": { + "description": "The number of features in the feature collection.\nA server may omit this information in a response, if the information\nabout the number of features is not known or difficult to compute.\nIf the value is provided, the value shall be identical to the number\nof items in the \"features\" array.", + "type": "integer", + "minimum": 0, + "example": 10 + }, + "numberMatched": { + "description": "The number of features of the feature type that match the selection\nparameters like `bbox`.", + "type": "integer", + "minimum": 0, + "example": 127 + }, + "tileSet": { + "title": "Tile Set Metadata", + "description": "A resource describing a tileset based on the OGC TileSet Metadata Standard. At least one of the 'TileMatrixSet', or a link with 'rel' http://www.opengis.net/def/rel/ogc/1.0/tiling-scheme", + "type": "object", + "required": ["dataType", "crs", "links"], + "properties": { + "title": { + "description": "A title for this tileset", + "type": "string" + }, + "description": { + "description": "Brief narrative description of this tile set", + "type": "string" + }, + "dataType": { + "allOf": [ + { + "description": "Type of data represented in the tileset" + }, + { + "$ref": "#/components/schemas/dataType" + } + ] + }, + "crs": { + "allOf": [ + { + "description": "Coordinate Reference System (CRS)" + }, + { + "$ref": "#/components/schemas/crs" + } + ] + }, + "tileMatrixSetURI": { + "description": "Reference to a Tile Matrix Set on an offical source for Tile Matrix Sets such as the OGC NA definition server (http://www.opengis.net/def/tms/). Required if the tile matrix set is registered on an open official source.", + "type": "string", + "format": "uri" + }, + "links": { + "description": "Links to related resources. Possible link 'rel' values are: 'http://www.opengis.net/def/rel/ogc/1.0/dataset' for a URL pointing to the dataset, 'item' for a URL template to get a tile; 'alternate' for a URL pointing to another representation of the TileSetMetadata (e.g a TileJSON file); 'http://www.opengis.net/def/rel/ogc/1.0/tiling-scheme' for a definition of the TileMatrixSet; 'http://www.opengis.net/def/rel/ogc/1.0/geodata' for pointing to a single collection (if the tileset represents a single collection)", + "type": "array", + "items": { + "$ref": "#/components/schemas/link" + } + }, + "tileMatrixSetLimits": { + "description": "Limits for the TileRow and TileCol values for each TileMatrix in the tileMatrixSet. If missing, there are no limits other that the ones imposed by the TileMatrixSet. If present the TileMatrices listed are limited and the rest not available at all", + "type": "array", + "items": { + "$ref": "#/components/schemas/tileMatrixLimits" + } + }, + "epoch": { + "description": "Epoch of the Coordinate Reference System (CRS)", + "type": "number" + }, + "layers": { + "minItems": 1, + "type": "array", + "items": { + "type": "object", + "required": ["id", "dataType"], + "properties": { + "title": { + "description": "Title of this tile matrix set, normally used for display to a human", + "type": "string" + }, + "description": { + "description": "Brief narrative description of this tile matrix set, normally available for display to a human", + "type": "string" + }, + "keywords": { + "description": "Unordered list of one or more commonly used or formalized word(s) or phrase(s) used to describe this layer", + "type": "string" + }, + "id": { + "description": "Unique identifier of the Layer. Implementation of 'identifier'", + "type": "string" + }, + "dataType": { + "allOf": [ + { + "description": "Type of data represented in the layer" + }, + { + "$ref": "#/components/schemas/dataType" + } + ] + }, + "geometryDimension": { + "description": "The geometry dimension of the features shown in this layer (0: points, 1: curves, 2: surfaces, 3: solids), unspecified: mixed or unknown", + "type": "integer", + "minimum": 0, + "maximum": 3 + }, + "featureType": { + "description": "Feature type identifier. Only applicable to layers of datatype 'geometries'", + "type": "string" + }, + "attribution": { + "description": "Short reference to recognize the author or provider", + "type": "string" + }, + "license": { + "description": "License applicable to the tiles", + "type": "string" + }, + "pointOfContact": { + "description": "Useful information to contact the authors or custodians for the layer (e.g. e-mail address, a physical address, phone numbers, etc)", + "type": "string" + }, + "publisher": { + "description": "Organization or individual responsible for making the layer available", + "type": "string" + }, + "theme": { + "description": "Category where the layer can be grouped", + "type": "string" + }, + "crs": { + "allOf": [ + { + "description": "Coordinate Reference System (CRS)" + }, + { + "$ref": "#/components/schemas/crs" + } + ] + }, + "epoch": { + "description": "Epoch of the Coordinate Reference System (CRS)", + "type": "number" + }, + "minScaleDenominator": { + "description": "Minimum scale denominator for usage of the layer", + "type": "number" + }, + "maxScaleDenominator": { + "description": "Maximum scale denominator for usage of the layer", + "type": "number" + }, + "minCellSize": { + "description": "Minimum cell size for usage of the layer", + "type": "number" + }, + "maxCellSize": { + "description": "Maximum cell size for usage of the layer", + "type": "number" + }, + "maxTileMatrix": { + "description": "TileMatrix identifier associated with the minScaleDenominator", + "type": "string" + }, + "minTileMatrix": { + "description": "TileMatrix identifier associated with the maxScaleDenominator", + "type": "string" + }, + "boundingBox": { + "allOf": [ + { + "description": "Minimum bounding rectangle surrounding the layer" + }, + { + "$ref": "#/components/schemas/2DBoundingBox" + } + ] + }, + "created": { + "allOf": [ + { + "description": "When the layer was first produced" + }, + { + "$ref": "#/components/schemas/timeStamp" + } + ] + }, + "updated": { + "allOf": [ + { + "description": "Last layer change/revision" + }, + { + "$ref": "#/components/schemas/timeStamp" + } + ] + }, + "style": { + "allOf": [ + { + "description": "Style used to generate the layer in the tileset" + }, + { + "$ref": "#/components/schemas/tileSet/properties/style/allOf/1" + } + ] + }, + "geoDataClasses": { + "description": "URI identifying a class of data contained in this layer (useful to determine compatibility with styles or processes)", + "type": "array", + "items": { + "type": "string" + } + }, + "propertiesSchema": { + "allOf": [ + { + "description": "Properties represented by the features in this layer. Can be the attributes of a feature dataset (datatype=geometries) or the rangeType of a coverage (datatype=coverage)" + }, + { + "description": "Attributes of the features or rangetypes of a coverage. Defined by a subset of the JSON Schema for the properties of a feature", + "type": "object", + "required": ["type", "properties"], + "properties": { + "type": { + "type": "string", + "enum": ["object"] + }, + "required": { + "description": "Implements 'multiplicity' by citing property 'name' defined as 'additionalProperties'", + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "properties": { + "type": "object", + "default": {}, + "additionalProperties": { + "description": "No property names are defined but any property name they should be described by JSON Schema. So 'additionalProperties' implements 'name'.", + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "description": { + "description": "Implements 'description'", + "type": "string" + }, + "type": { + "type": "string", + "enum": ["array", "boolean", "integer", "null", "number", "object", "string"] + }, + "enum": { + "description": "Implements 'acceptedValues'", + "type": "array", + "minItems": 1, + "items": {}, + "uniqueItems": true + }, + "format": { + "description": "Complements implementation of 'type'", + "type": "string" + }, + "contentMediaType": { + "description": "Implements 'mediaType'", + "type": "string" + }, + "maximum": { + "description": "Implements 'range'", + "type": "number" + }, + "exclusiveMaximum": { + "description": "Implements 'range'", + "type": "number" + }, + "minimum": { + "description": "Implements 'range'", + "type": "number" + }, + "exclusiveMinimum": { + "description": "Implements 'range'", + "type": "number" + }, + "pattern": { + "type": "string", + "format": "regex" + }, + "maxItems": { + "description": "Implements 'upperMultiplicity'", + "type": "integer", + "minimum": 0 + }, + "minItems": { + "description": "Implements 'lowerMultiplicity'", + "type": "integer", + "default": 0, + "minimum": 0 + }, + "observedProperty": { + "type": "string" + }, + "observedPropertyURI": { + "type": "string", + "format": "uri" + }, + "uom": { + "type": "string" + }, + "uomURI": { + "type": "string", + "format": "uri" + } + } + } + } + } + } + ] + }, + "links": { + "description": "Links related to this layer. Possible link 'rel' values are: 'geodata' for a URL pointing to the collection of geospatial data.", + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/components/schemas/link" + } + } + } + } + }, + "boundingBox": { + "allOf": [ + { + "description": "Minimum bounding rectangle surrounding the tile matrix set, in the supported CRS" + }, + { + "$ref": "#/components/schemas/2DBoundingBox" + } + ] + }, + "centerPoint": { + "allOf": [ + { + "description": "Location of a tile that nicely represents the tileset. Implementations may use this center value to set the default location or to present a representative tile in a user interface" + }, + { + "type": "object", + "required": ["coordinates"], + "properties": { + "coordinates": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "number" + } + }, + "crs": { + "allOf": [ + { + "description": "Coordinate Reference System (CRS) of the coordinates" + }, + { + "$ref": "#/components/schemas/crs" + } + ] + }, + "tileMatrix": { + "description": "TileMatrix identifier associated with the scaleDenominator", + "type": "string" + }, + "scaleDenominator": { + "description": "Scale denominator of the tile matrix selected", + "type": "number" + }, + "cellSize": { + "description": "Cell size of the tile matrix selected", + "type": "number" + } + } + } + ] + }, + "style": { + "allOf": [ + { + "description": "Style involving all layers used to generate the tileset" + }, + { + "type": "object", + "required": ["id"], + "properties": { + "id": { + "description": "An identifier for this style. Implementation of 'identifier'", + "type": "string" + }, + "title": { + "description": "A title for this style", + "type": "string" + }, + "description": { + "description": "Brief narrative description of this style", + "type": "string" + }, + "keywords": { + "description": "keywords about this style", + "type": "array", + "items": { + "type": "string" + } + }, + "links": { + "description": "Links to style related resources. Possible link 'rel' values are: 'style' for a URL pointing to the style description, 'styleSpec' for a URL pointing to the specification or standard used to define the style.", + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/components/schemas/link" + } + } + } + } + ] + }, + "attribution": { + "description": "Short reference to recognize the author or provider", + "type": "string" + }, + "license": { + "description": "License applicable to the tiles", + "type": "string" + }, + "accessConstraints": { + "description": "Restrictions on the availability of the Tile Set that the user needs to be aware of before using or redistributing the Tile Set", + "type": "string", + "default": "unclassified", + "enum": ["unclassified", "restricted", "confidential", "secret", "topSecret"] + }, + "keywords": { + "description": "keywords about this tileset", + "type": "array", + "items": { + "type": "string" + } + }, + "version": { + "description": "Version of the Tile Set. Changes if the data behind the tiles has been changed", + "type": "string" + }, + "created": { + "allOf": [ + { + "description": "When the Tile Set was first produced" + }, + { + "$ref": "#/components/schemas/timeStamp" + } + ] + }, + "updated": { + "allOf": [ + { + "description": "Last Tile Set change/revision" + }, + { + "$ref": "#/components/schemas/timeStamp" + } + ] + }, + "pointOfContact": { + "description": "Useful information to contact the authors or custodians for the Tile Set", + "type": "string" + }, + "mediaTypes": { + "description": "Media types available for the tiles", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "tileSet-item": { + "title": "Tile Set Metadata item", + "description": "A minimal tileset element for use within a list of tilesets linking to full description of those tilesets.", + "type": "object", + "required": ["dataType", "links", "crs"], + "properties": { + "title": { + "description": "A title for this tileset", + "type": "string" + }, + "dataType": { + "allOf": [ + { + "description": "Type of data represented in the tileset" + }, + { + "$ref": "#/components/schemas/dataType" + } + ] + }, + "crs": { + "allOf": [ + { + "description": "Coordinate Reference System (CRS)" + }, + { + "$ref": "#/components/schemas/crs" + } + ] + }, + "tileMatrixSetURI": { + "description": "Reference to a Tile Matrix Set on an offical source for Tile Matrix Sets such as the OGC NA definition server (http://www.opengis.net/def/tms/). Required if the tile matrix set is registered on an open official source.", + "type": "string", + "format": "uri" + }, + "links": { + "description": "Links to related resources. A 'self' link to the tileset as well as a 'http://www.opengis.net/def/rel/ogc/1.0/tiling-scheme' link to a definition of the TileMatrixSet are required.", + "type": "array", + "items": { + "$ref": "#/components/schemas/link" + } + } + } + }, + "tileMatrixSet": { + "title": "Tile Matrix Set Definition", + "description": "A definition of a tile matrix set following the Tile Matrix Set standard. For tileset metadata, such a description (in `tileMatrixSet` property) is only required for offline use, as an alternative to a link with a `http://www.opengis.net/def/rel/ogc/1.0/tiling-scheme` relation type.", + "type": "object", + "required": ["crs", "tileMatrices"], + "properties": { + "title": { + "description": "Title of this tile matrix set, normally used for display to a human", + "type": "string" + }, + "description": { + "description": "Brief narrative description of this tile matrix set, normally available for display to a human", + "type": "string" + }, + "keywords": { + "description": "Unordered list of one or more commonly used or formalized word(s) or phrase(s) used to describe this tile matrix set", + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "description": "Tile matrix set identifier. Implementation of 'identifier'", + "type": "string" + }, + "uri": { + "description": "Reference to an official source for this tileMatrixSet", + "type": "string", + "format": "uri" + }, + "orderedAxes": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "crs": { + "allOf": [ + { + "description": "Coordinate Reference System (CRS)" + }, + { + "$ref": "#/components/schemas/crs" + } + ] + }, + "wellKnownScaleSet": { + "description": "Reference to a well-known scale set", + "type": "string", + "format": "uri" + }, + "boundingBox": { + "allOf": [ + { + "description": "Minimum bounding rectangle surrounding the tile matrix set, in the supported CRS" + }, + { + "$ref": "#/components/schemas/2DBoundingBox" + } + ] + }, + "tileMatrices": { + "type": "array", + "description": "Describes scale levels and its tile matrices", + "items": { + "description": "A tile matrix, usually corresponding to a particular zoom level of a TileMatrixSet.", + "type": "object", + "required": [ + "id", + "scaleDenominator", + "cellSize", + "pointOfOrigin", + "tileWidth", + "tileHeight", + "matrixWidth", + "matrixHeight" + ], + "properties": { + "title": { + "description": "Title of this tile matrix, normally used for display to a human", + "type": "string" + }, + "description": { + "description": "Brief narrative description of this tile matrix set, normally available for display to a human", + "type": "string" + }, + "keywords": { + "description": "Unordered list of one or more commonly used or formalized word(s) or phrase(s) used to describe this dataset", + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "description": "Identifier selecting one of the scales defined in the TileMatrixSet and representing the scaleDenominator the tile. Implementation of 'identifier'", + "type": "string" + }, + "scaleDenominator": { + "description": "Scale denominator of this tile matrix", + "type": "number" + }, + "cellSize": { + "description": "Cell size of this tile matrix", + "type": "number" + }, + "cornerOfOrigin": { + "description": "The corner of the tile matrix (_topLeft_ or _bottomLeft_) used as the origin for numbering tile rows and columns. This corner is also a corner of the (0, 0) tile.", + "type": "string", + "enum": ["topLeft", "bottomLeft"], + "default": "topLeft" + }, + "pointOfOrigin": { + "allOf": [ + { + "description": "Precise position in CRS coordinates of the corner of origin (e.g. the top-left corner) for this tile matrix. This position is also a corner of the (0, 0) tile. In previous version, this was 'topLeftCorner' and 'cornerOfOrigin' did not exist." + }, + { + "$ref": "#/components/schemas/2DPoint" + } + ] + }, + "tileWidth": { + "type": "number", + "description": "Width of each tile of this tile matrix in pixels", + "format": "integer", + "minimum": 1, + "multipleOf": 1 + }, + "tileHeight": { + "type": "number", + "description": "Height of each tile of this tile matrix in pixels", + "format": "integer", + "minimum": 1, + "multipleOf": 1 + }, + "matrixHeight": { + "type": "number", + "description": "Width of the matrix (number of tiles in width)", + "format": "integer", + "minimum": 1, + "multipleOf": 1 + }, + "matrixWidth": { + "type": "number", + "description": "Height of the matrix (number of tiles in height)", + "format": "integer", + "minimum": 1, + "multipleOf": 1 + }, + "variableMatrixWidths": { + "description": "Describes the rows that has variable matrix width", + "type": "array", + "items": { + "description": "Variable Matrix Width data structure", + "type": "object", + "required": ["coalesce", "minTileRow", "maxTileRow"], + "properties": { + "coalesce": { + "description": "Number of tiles in width that coalesce in a single tile for these rows", + "type": "number", + "format": "integer", + "minimum": 2, + "multipleOf": 1 + }, + "minTileRow": { + "description": "First tile row where the coalescence factor applies for this tilematrix", + "type": "number", + "format": "integer", + "minimum": 0, + "multipleOf": 1 + }, + "maxTileRow": { + "description": "Last tile row where the coalescence factor applies for this tilematrix", + "type": "number", + "format": "integer", + "minimum": 0, + "multipleOf": 1 + } + } + } + } + } + } + } + } + }, + "tileMatrixSet-item": { + "title": "Tile Matrix Set Item", + "description": "A minimal tile matrix set element for use within a list of tile matrix sets linking to a full definition.", + "type": "object", + "required": ["links"], + "properties": { + "id": { + "description": "Optional local tile matrix set identifier, e.g. for use as unspecified `{tileMatrixSetId}` parameter. Implementation of 'identifier'", + "type": "string" + }, + "title": { + "description": "Title of this tile matrix set, normally used for display to a human", + "type": "string" + }, + "uri": { + "description": "Reference to an official source for this tileMatrixSet", + "type": "string", + "format": "uri" + }, + "crs": { + "allOf": [ + { + "description": "Coordinate Reference System (CRS)" + }, + { + "$ref": "#/components/schemas/crs" + } + ] + }, + "links": { + "description": "Links to related resources. A 'self' link to the tile matrix set definition is required.", + "type": "array", + "items": { + "$ref": "#/components/schemas/link" + } + } + } + }, + "tileMatrixLimits": { + "title": "TileMatrixLimits", + "description": "The limits for an individual tile matrix of a TileSet's TileMatrixSet, as defined in the OGC 2D TileMatrixSet and TileSet Metadata Standard", + "type": "object", + "required": ["tileMatrix", "minTileRow", "maxTileRow", "minTileCol", "maxTileCol"], + "properties": { + "tileMatrix": { + "type": "string" + }, + "minTileRow": { + "type": "integer", + "minimum": 0 + }, + "maxTileRow": { + "type": "integer", + "minimum": 0 + }, + "minTileCol": { + "type": "integer", + "minimum": 0 + }, + "maxTileCol": { + "type": "integer", + "minimum": 0 + } + } + }, + "2DPoint": { + "description": "A 2D Point in the CRS indicated elsewhere", + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "number" + } + }, + "2DBoundingBox": { + "description": "Minimum bounding rectangle surrounding a 2D resource in the CRS indicated elsewhere", + "type": "object", + "required": ["lowerLeft", "upperRight"], + "properties": { + "lowerLeft": { + "$ref": "#/components/schemas/2DPoint" + }, + "upperRight": { + "$ref": "#/components/schemas/2DPoint" + }, + "crs": { + "$ref": "#/components/schemas/crs" + }, + "orderedAxes": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "string" + } + } + } + }, + "FeatureCollection": { + "title": "GeoJSON FeatureCollection", + "type": "object", + "required": ["type", "features"], + "properties": { + "type": { + "type": "string", + "enum": ["FeatureCollection"] + }, + "features": { + "type": "array", + "items": { + "title": "GeoJSON Feature", + "type": "object", + "required": ["type", "properties", "geometry"], + "properties": { + "type": { + "type": "string", + "enum": ["Feature"] + }, + "id": { + "oneOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, + "properties": { + "type": "object", + "nullable": true + }, + "geometry": { + "oneOf": [ + { + "title": "GeoJSON Point", + "type": "object", + "nullable": true, + "required": ["type", "coordinates"], + "properties": { + "type": { + "type": "string", + "enum": ["Point"] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON LineString", + "type": "object", + "required": ["type", "coordinates"], + "properties": { + "type": { + "type": "string", + "enum": ["LineString"] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON Polygon", + "type": "object", + "required": ["type", "coordinates"], + "properties": { + "type": { + "type": "string", + "enum": ["Polygon"] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPoint", + "type": "object", + "required": ["type", "coordinates"], + "properties": { + "type": { + "type": "string", + "enum": ["MultiPoint"] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiLineString", + "type": "object", + "required": ["type", "coordinates"], + "properties": { + "type": { + "type": "string", + "enum": ["MultiLineString"] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPolygon", + "type": "object", + "required": ["type", "coordinates"], + "properties": { + "type": { + "type": "string", + "enum": ["MultiPolygon"] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + } + ] + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + "enumeration": { + "type": "object", + "required": ["type", "enum"], + "properties": { + "type": { + "type": "string", + "enum": ["enum"] + }, + "enum": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "all-collections": { + "type": "string" + }, + "vectorTiles-collections": { + "type": "string" + }, + "coverage-collections": { + "type": "string" + }, + "styles": { + "type": "string", + "enum": ["default"] + }, + "tileMatrixSets": { + "type": "string" + } + }, + "parameters": { + "f-metadata": { + "name": "f", + "in": "query", + "description": "The format of the response. If no value is provided, the accept header is used to determine the format. Accepted values are 'json' or 'html'.", + "required": false, + "schema": { + "type": "string", + "enum": ["json", "html"] + }, + "style": "form", + "explode": false + }, + "collectionId-all": { + "name": "collectionId", + "in": "path", + "description": "Local identifier of a collection", + "required": true, + "schema": { + "$ref": "#/components/schemas/all-collections" + } + }, + "collectionId-coverage": { + "name": "collectionId", + "in": "path", + "description": "Local identifier of a coverage collection", + "required": true, + "allowEmptyValue": false, + "schema": { + "$ref": "#/components/schemas/coverage-collections" + }, + "style": "simple", + "explode": false + }, + "collectionId-vectorTiles": { + "name": "collectionId", + "in": "path", + "description": "Local identifier of a vector tile collection", + "required": true, + "allowEmptyValue": false, + "schema": { + "$ref": "#/components/schemas/vectorTiles-collections" + } + }, + "collections": { + "name": "collections", + "in": "query", + "style": "form", + "description": "The collections that should be included in the response. The parameter value is a comma-separated list of collection identifiers. If the parameters is missing, some or all collections will be included. The collection will be rendered in the order specified, with the last one showing on top, unless the priority is overridden by styling rules.", + "required": false, + "explode": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/all-collections" + } + } + }, + "collections-coverage": { + "name": "collections", + "in": "query", + "style": "form", + "description": "The collections that should be included in the response. The parameter value is a comma-separated list of collection identifiers. If the parameters is missing, some or all collections will be included. The collection will be rendered in the order specified, with the last one showing on top, unless the priority is overridden by styling rules.", + "required": false, + "explode": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/coverage-collections" + } + } + }, + "collections-vectorTiles": { + "name": "collections", + "in": "query", + "style": "form", + "description": "The collections that should be included in the response. The parameter value is a comma-separated list of collection identifiers. If the parameters is missing, some or all collections will be included. The collection will be rendered in the order specified, with the last one showing on top, unless the priority is overridden by styling rules.", + "required": false, + "explode": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/vectorTiles-collections" + } + } + }, + "datetime": { + "name": "datetime", + "in": "query", + "description": "Either a date-time or an interval, half-bounded or bounded. Date and time expressions\nadhere to RFC 3339. Half-bounded intervals are expressed using double-dots.\n\nExamples:\n\n* A date-time: \"2018-02-12T23:20:50Z\"\n* A bounded interval: \"2018-02-12T00:00:00Z/2018-03-18T12:31:12Z\"\n* Half-bounded intervals: \"2018-02-12T00:00:00Z/..\" or \"../2018-03-18T12:31:12Z\"\n\nOnly features that have a temporal property that intersects the value of\n`datetime` are selected.\n\nIf a feature has multiple temporal properties, it is the decision of the\nserver whether only a single temporal property is used to determine\nthe extent or all relevant temporal properties.", + "required": false, + "schema": { + "type": "string" + }, + "style": "form", + "explode": false + }, + "subset": { + "name": "subset", + "in": "query", + "description": "Retrieve only part of the data by slicing or trimming along one or more axis\nFor trimming: {axisAbbrev}({low}:{high}) (preserves dimensionality)\n An asterisk (`*`) can be used instead of {low} or {high} to indicate the minimum/maximum value.\nFor slicing: {axisAbbrev}({value}) (reduces dimensionality)\n", + "style": "form", + "explode": false, + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "crs": { + "name": "crs", + "in": "query", + "description": "reproject the output to the given crs", + "required": false, + "style": "form", + "explode": true, + "schema": { + "type": "string" + } + }, + "subset-crs": { + "name": "subset-crs", + "in": "query", + "description": "crs for the specified subset", + "required": false, + "style": "form", + "explode": true, + "schema": { + "type": "string" + } + }, + "tileMatrix": { + "name": "tileMatrix", + "in": "path", + "description": "Identifier selecting one of the scales defined in the TileMatrixSet and representing the scaleDenominator the tile. For example,\nIreland is fully within the Tile at WebMercatorQuad tileMatrix=5, tileRow=10 and tileCol=15.", + "required": true, + "schema": { + "type": "string" + }, + "example": "5" + }, + "tileRow": { + "name": "tileRow", + "in": "path", + "description": "Row index of the tile on the selected TileMatrix. It cannot exceed the MatrixWidth-1 for the selected TileMatrix. For example, Ireland is fully within the Tile at WebMercatorQuad tileMatrix=5, tileRow=10 and tileCol=15.", + "required": true, + "schema": { + "minimum": 0, + "type": "integer" + }, + "example": 10 + }, + "tileCol": { + "name": "tileCol", + "in": "path", + "description": "Column index of the tile on the selected TileMatrix. It cannot exceed the MatrixHeight-1 for the selected TileMatrix. For example, Ireland is fully within the Tile at WebMercatorQuad tileMatrix=5, tileRow=10 and tileCol=15.", + "required": true, + "schema": { + "minimum": 0, + "type": "integer" + }, + "example": 15 + }, + "tileMatrixSetId": { + "name": "tileMatrixSetId", + "in": "path", + "description": "Identifier for a supported TileMatrixSet", + "required": true, + "allowEmptyValue": false, + "schema": { + "$ref": "#/components/schemas/tileMatrixSets" + } + }, + "f-mapTile": { + "name": "f", + "in": "query", + "description": "The format of the map tile response (e.g. png). Accepted values are 'png', 'jpg' or 'tiff' (GeoTIFF).", + "required": false, + "schema": { + "type": "string", + "enum": ["png", "jpg", "tiff"] + }, + "style": "form", + "explode": false + }, + "f-vectorTile": { + "name": "f", + "in": "query", + "description": "The format of the vector tile response (e.g. json). Accepted values are 'mvt' (Mapbox Vector Tiles) or 'json' (GeoJSON).", + "required": false, + "schema": { + "type": "string", + "enum": ["mvt", "json"] + }, + "style": "form", + "explode": false + }, + "f-coverageTile": { + "name": "f", + "in": "query", + "description": "The format of the coverage tile response (e.g. tiff). Accepted values are 'tiff' (GeoTIFF), 'netcdf', or 'png'.", + "required": false, + "schema": { + "type": "string", + "enum": ["tiff", "netcdf", "png"] + }, + "style": "form", + "explode": false + }, + "bgcolor": { + "name": "bgcolor", + "in": "query", + "description": "Web color name or hexadecimal 0x[AA]RRGGBB color value for the background color (default to 0x9C9C9C gray). If alpha is not specified, full opacity is assumed.", + "required": false, + "style": "form", + "explode": false, + "schema": { + "type": "string", + "default": "0xFFFFFF" + } + }, + "transparent": { + "name": "transparent", + "in": "query", + "description": "Background transparency of map (default=true).", + "required": false, + "style": "form", + "explode": false, + "schema": { + "type": "boolean", + "default": true + } + }, + "styleId": { + "name": "styleId", + "in": "path", + "description": "An identifier representing a specific style.", + "required": true, + "allowEmptyValue": false, + "schema": { + "$ref": "#/components/schemas/styles" + } + }, + "styleId-collection": { + "name": "styleId", + "in": "path", + "description": "An identifier representing a specific style.", + "required": true, + "allowEmptyValue": false, + "schema": { + "type": "string" + } + } + }, + "responses": { + "NotFound": { + "description": "The requested resource does not exist on the server. For example, a path parameter had an incorrect value.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/exception" + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "NotAcceptable": { + "description": "Content negotiation failed. For example, the `Accept` header submitted in the request did not support any of the media types supported by the server for the requested resource.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/exception" + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "ServerError": { + "description": "A server error occurred.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/exception" + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "InvalidParameter": { + "description": "A query parameter has an invalid value.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/exception" + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "Exception": { + "description": "An error occured.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/exception" + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "LandingPage": { + "description": "The landing page provides links to the API definition (link relation `service-desc`, in this case path `/api`),\nto the Conformance declaration (path `/conformance`, link relation `conformance`), and to the Collections of geospatial data (path `/collections`, link relation `data`).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/landingPage" + }, + "example": { + "title": "Buildings in Bonn", + "description": "Access to data about buildings in the city of Bonn via a Web API that conforms to the OGC API Tiles specification.", + "links": [ + { + "href": "http://data.example.org/", + "rel": "self", + "type": "application/json", + "title": "this document" + }, + { + "href": "http://data.example.org/api", + "rel": "service-desc", + "type": "application/vnd.oai.openapi+json;version=3.0", + "title": "the API definition" + }, + { + "href": "http://data.example.org/api.html", + "rel": "service-doc", + "type": "text/html", + "title": "the API documentation" + }, + { + "href": "http://data.example.org/conformance", + "rel": "conformance", + "type": "application/json", + "title": "OGC API conformance classes implemented by this service" + }, + { + "href": "http://data.example.org/collections", + "rel": "data", + "type": "application/json", + "title": "Information about the collections" + } + ] + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "Conformance": { + "description": "The URIs of all conformance classes supported by the server", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/confClasses" + } + ], + "example": { + "conformsTo": [ + "http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/core", + "http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/json", + "http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/html", + "http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/oas30", + "http://www.opengis.net/spec/ogcapi-common-2/1.0/conf/collections", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/core", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tileset", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tilesets-list", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/geodata-tilesets", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/dataset-tilesets", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/geodata-selection", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/jpeg", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/png", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/mvt", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/geojson", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tiff", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/netcdf" + ] + } + } + } + } + }, + "API": { + "description": "The OpenAPI definition of the API.", + "content": { + "application/vnd.oai.openapi+json;version=3.0": { + "schema": { + "type": "object" + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "Enumeration": { + "description": "An enumerated list of valid string values for API parameters.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/enumeration" + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "CollectionsList": { + "description": "The collections of (mostly geospatial) data available from this API. The dataset contains one or more collections. This resource provides information about and access to the collections. The response contains the list of collections. Each collection is accessible via one or more OGC API set of specifications, for which a link to relevant accessible resources, e.g. /collections/{collectionId}/(items, coverage, map, tiles...) is provided, with the corresponding relation type, as well as key information about the collection. This information includes:\n* a local identifier for the collection that is unique for the dataset;\n* a list of coordinate reference systems (CRS) in which data may be returned by the server. The first CRS is the default coordinate reference system (the default is always WGS 84 with axis order longitude/latitude);\n* an optional title and description for the collection;\n* an optional extent that can be used to provide an indication of the spatial and temporal extent of the collection - typically derived from the data;\n* for collections accessible via the Features or Records API, an optional indicator about the type of the items in the collection (the default value, if the indicator is not provided, is 'feature').", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/collections" + }, + "example": { + "links": [ + { + "href": "http://data.example.org/collections.json", + "rel": "self", + "type": "application/json", + "title": "this document" + }, + { + "href": "http://data.example.org/collections.html", + "rel": "alternate", + "type": "text/html", + "title": "this document as HTML" + }, + { + "href": "http://schemas.example.org/1.0/buildings.xsd", + "rel": "describedby", + "type": "application/xml", + "title": "GML application schema for Acme Corporation building data" + }, + { + "href": "http://download.example.org/buildings.gpkg", + "rel": "enclosure", + "type": "application/geopackage+sqlite3", + "title": "Bulk download (GeoPackage)", + "length": 472546 + } + ], + "collections": [ + { + "id": "buildings", + "title": "Buildings", + "description": "Buildings in the city of Bonn.", + "extent": { + "spatial": { + "bbox": [[7.01, 50.63, 7.22, 50.78]] + }, + "temporal": { + "interval": [["2010-02-15T12:34:56Z", null]] + } + }, + "links": [ + { + "href": "http://data.example.org/collections/buildings/items", + "rel": "items", + "type": "application/geo+json", + "title": "Buildings" + }, + { + "href": "http://data.example.org/collections/buildings/items.html", + "rel": "items", + "type": "text/html", + "title": "Buildings" + }, + { + "href": "https://creativecommons.org/publicdomain/zero/1.0/", + "rel": "license", + "type": "text/html", + "title": "CC0-1.0" + }, + { + "href": "https://creativecommons.org/publicdomain/zero/1.0/rdf", + "rel": "license", + "type": "application/rdf+xml", + "title": "CC0-1.0" + } + ] + } + ] + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "Collection": { + "description": "Information about a particular collection of (mostly geospatial) data available from this API. The collection is accessible via one or more OGC API set of specifications, for which a link to relevant accessible resources, e.g. /collections/{collectionId}/(items, coverage, map, tiles...) is contained in the response, with the corresponding relation type, as well as key information about the collection. This information includes:\n* a local identifier for the collection that is unique for the dataset;\n* a list of coordinate reference systems (CRS) in which data may be returned by the server. The first CRS is the default coordinate reference system (the default is always WGS 84 with axis order longitude/latitude);\n* an optional title and description for the collection;\n* an optional extent that can be used to provide an indication of the spatial and temporal extent of the collection - typically derived from the data;\n* for collections accessible via the Features or Records API, an optional indicator about the type of the items in the collection (the default value, if the indicator is not provided, is 'feature').", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/collectionInfo" + }, + "example": { + "id": "buildings", + "title": "Buildings", + "description": "Buildings in the city of Bonn.", + "extent": { + "spatial": { + "bbox": [[7.01, 50.63, 7.22, 50.78]] + }, + "temporal": { + "interval": [["2010-02-15T12:34:56Z", null]] + } + }, + "links": [ + { + "href": "http://data.example.org/collections/buildings/items", + "rel": "items", + "type": "application/geo+json", + "title": "Buildings" + }, + { + "href": "http://data.example.org/collections/buildings/items.html", + "rel": "items", + "type": "text/html", + "title": "Buildings" + }, + { + "href": "https://creativecommons.org/publicdomain/zero/1.0/", + "rel": "license", + "type": "text/html", + "title": "CC0-1.0" + }, + { + "href": "https://creativecommons.org/publicdomain/zero/1.0/rdf", + "rel": "license", + "type": "application/rdf+xml", + "title": "CC0-1.0" + } + ] + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "TileSetsList": { + "description": "List of available tilesets.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["tilesets"], + "properties": { + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/link" + } + }, + "tilesets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/tileSet-item" + } + } + } + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "TileSet": { + "description": "Description of the tileset", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/tileSet" + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "MapTile": { + "description": "A map tile image returned as a response.", + "content": { + "image/png": { + "schema": { + "type": "string", + "format": "binary" + } + }, + "image/jpeg": { + "schema": { + "type": "string", + "format": "binary" + } + }, + "image/tiff; application=geotiff": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "CoverageTile": { + "description": "A coverage tile returned as a response.", + "content": { + "application/netcdf": { + "schema": { + "type": "string", + "format": "binary" + } + }, + "image/tiff; application=geotiff": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "VectorTile": { + "description": "A vector tile returned as a response.", + "content": { + "application/vnd.mapbox-vector-tile": { + "schema": { + "type": "string", + "format": "binary" + } + }, + "application/geo+json": { + "schema": { + "allOf": [ + { + "format": "geojson-feature-collection" + }, + { + "$ref": "#/components/schemas/FeatureCollection" + } + ] + } + } + } + }, + "EmptyTile": { + "description": "No data available for this tile." + }, + "TileMatrixSetsList": { + "description": "List of tile matrix sets (tiling schemes).", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "tileMatrixSets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/tileMatrixSet-item" + } + } + } + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "TileMatrixSet": { + "description": "tile matrix set", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/tileMatrixSet" + } + } + } + } + } + } +} diff --git a/ogcapi-tiles-bundled.json b/ogcapi-tiles-bundled.json new file mode 100644 index 000000000..f9058b18e --- /dev/null +++ b/ogcapi-tiles-bundled.json @@ -0,0 +1,3909 @@ +{ + "openapi": "3.0.0", + "info": { + "version": "1.0", + "title": "OGC API - Tiles", + "description": "Example API Definition for OGC API - Tiles - Part 1: Core", + "contact": { + "name": "Open Geospatial Consortium", + "email": "info@ogc.org" + }, + "license": { + "name": "OGC License", + "url": "http://www.opengeospatial.org/legal/" + } + }, + "servers": [ + { + "description": "Example OGC API - Tiles server", + "url": "https://maps.ecere.com/ogcapi" + } + ], + "paths": { + "/": { + "get": { + "tags": ["Landing Page"], + "operationId": "getLandingPage", + "summary": "Retrieve the OGC API landing page for this service.", + "parameters": [ + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/LandingPage" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/conformance": { + "get": { + "tags": ["Conformance"], + "operationId": "getConformance", + "summary": "Retrieve the set of OGC API conformance classes that are supported by this service.", + "parameters": [ + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Conformance" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/api": { + "get": { + "tags": ["API"], + "operationId": "getAPI", + "summary": "Retrieve this API definition.", + "parameters": [ + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/API" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/api/all-collections": { + "get": { + "tags": ["API"], + "operationId": "getAPICollections", + "summary": "Retrieve the list of collections available from this API implementation & deployment.", + "parameters": [ + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Enumeration" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/api/coverage-collections": { + "get": { + "tags": ["API"], + "operationId": "getAPICoverageCollections", + "summary": "Retrieve the list of coverages collections available from this API implementation & deployment.", + "parameters": [ + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Enumeration" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/api/vectorTiles-collections": { + "get": { + "tags": ["API"], + "operationId": "getAPIVectorTileCollections", + "summary": "Retrieve the list of collections supporting vector tiles available from this API implementation & deployment.", + "parameters": [ + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Enumeration" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/api/tileMatrixSets": { + "get": { + "tags": ["API"], + "operationId": "getAPITileMatrixSets", + "summary": "Retrieve the list of shared TileMatrixSets available from this API implementation & deployment.", + "parameters": [ + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Enumeration" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/api/styles": { + "get": { + "tags": ["API"], + "operationId": "getAPIStyles", + "summary": "Retrieve the list of dataset styles available from this API implementation & deployment.", + "parameters": [ + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Enumeration" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections": { + "get": { + "tags": ["Data Collections"], + "operationId": "getCollectionsList", + "summary": "Retrieve the list of geospatial data collections available from this service.", + "parameters": [ + { + "$ref": "#/components/parameters/datetime" + }, + { + "name": "bbox", + "in": "query", + "description": "Only features that have a geometry that intersects the bounding box are selected. The bounding box is provided as four or six numbers, depending on whether the coordinate reference system includes a vertical axis (height or depth):\n* Lower left corner, coordinate axis 1 * Lower left corner, coordinate axis 2 * Minimum value, coordinate axis 3 (optional) * Upper right corner, coordinate axis 1 * Upper right corner, coordinate axis 2 * Maximum value, coordinate axis 3 (optional)\nThe coordinate reference system of the values is WGS 84 longitude/latitude (http://www.opengis.net/def/crs/OGC/1.3/CRS84) unless a different coordinate reference system is specified in the parameter `bbox-crs`.\nFor WGS 84 longitude/latitude the values are in most cases the sequence of minimum longitude, minimum latitude, maximum longitude and maximum latitude. However, in cases where the box spans the antimeridian the first value (west-most box edge) is larger than the third value (east-most box edge).\nIf the vertical axis is included, the third and the sixth number are the bottom and the top of the 3-dimensional bounding box.\nIf a feature has multiple spatial geometry properties, it is the decision of the server whether only a single spatial geometry property is used to determine the extent or all relevant geometries.", + "required": false, + "schema": { + "type": "array", + "oneOf": [ + { + "minItems": 4, + "maxItems": 4 + }, + { + "minItems": 6, + "maxItems": 6 + } + ], + "items": { + "type": "number", + "format": "double" + } + }, + "style": "form", + "explode": false + }, + { + "name": "limit", + "in": "query", + "description": "The optional limit parameter limits the number of collections that are presented in the response document.\nOnly items are counted that are on the first level of the collection in the response document. Nested objects contained within the explicitly requested items shall not be counted.\n* Minimum = 1 * Maximum = 10000 * Default = 10", + "required": false, + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 10000, + "default": 10 + }, + "style": "form", + "explode": false + }, + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/CollectionsList" + } + } + } + }, + "/collections/{collectionId}": { + "get": { + "tags": ["Data Collections"], + "operationId": "getCollection", + "summary": "Retrieve the description of a collection available from this service.", + "parameters": [ + { + "$ref": "#/components/parameters/collectionId-all" + }, + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/Collection" + } + } + } + }, + "/tileMatrixSets": { + "get": { + "tags": ["Tiling Schemes"], + "summary": "Retrieve the list of available tiling schemes (tile matrix sets)", + "operationId": "getTileMatrixSetsList", + "parameters": [ + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileMatrixSetsList" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/tileMatrixSets/{tileMatrixSetId}": { + "get": { + "tags": ["Tiling Schemes"], + "summary": "Retrieve the definition of the specified tiling scheme (tile matrix set)", + "operationId": "getTileMatrixSet", + "parameters": [ + { + "$ref": "#/components/parameters/tileMatrixSetId" + }, + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileMatrixSet" + }, + "404": { + "description": "The requested tile matrix set id was not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/exception" + } + } + } + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/tiles": { + "get": { + "tags": ["Vector Tiles"], + "summary": "Retrieve a list of available vector tilesets for the dataset", + "operationId": ".dataset.vector.getTileSetsList", + "parameters": [ + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileSetsList" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/tiles/{tileMatrixSetId}": { + "get": { + "tags": ["Vector Tiles"], + "summary": "Retrieve the vector tileset metadata for the whole dataset and the specified tiling scheme (tile matrix set)", + "operationId": ".dataset.vector.getTileSet", + "parameters": [ + { + "$ref": "#/components/parameters/collections" + }, + { + "$ref": "#/components/parameters/tileMatrixSetId" + }, + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileSet" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/tiles/{tileMatrixSetId}/{tileMatrix}/{tileRow}/{tileCol}": { + "get": { + "tags": ["Vector Tiles"], + "summary": "Retrieve a vector tile including one or more collections from the dataset.", + "operationId": ".dataset.vector.getTile", + "parameters": [ + { + "$ref": "#/components/parameters/tileMatrix" + }, + { + "$ref": "#/components/parameters/tileRow" + }, + { + "$ref": "#/components/parameters/tileCol" + }, + { + "$ref": "#/components/parameters/datetime" + }, + { + "$ref": "#/components/parameters/collections-vectorTiles" + }, + { + "$ref": "#/components/parameters/subset" + }, + { + "$ref": "#/components/parameters/crs" + }, + { + "$ref": "#/components/parameters/subset-crs" + }, + { + "$ref": "#/components/parameters/tileMatrixSetId" + }, + { + "$ref": "#/components/parameters/f-vectorTile" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VectorTile" + }, + "204": { + "$ref": "#/components/responses/EmptyTile" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/styles/{styleId}/tiles": { + "get": { + "tags": ["Vector Tiles"], + "summary": "Retrieve the list of vector tilesets intended for use with a specified style for the whole dataset", + "operationId": ".dataset.style.vector.getTileSetsList", + "parameters": [ + { + "$ref": "#/components/parameters/styleId" + }, + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileSetsList" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/styles/{styleId}/tiles/{tileMatrixSetId}": { + "get": { + "tags": ["Vector Tiles"], + "summary": "Retrieve a vector tileset intended for use with a specified style of the whole dataset for the specified tiling scheme (tile matrix set)", + "operationId": ".dataset.style.vector.getTileSet", + "parameters": [ + { + "$ref": "#/components/parameters/styleId" + }, + { + "$ref": "#/components/parameters/collections" + }, + { + "$ref": "#/components/parameters/tileMatrixSetId" + }, + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileSet" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/styles/{styleId}/tiles/{tileMatrixSetId}/{tileMatrix}/{tileRow}/{tileCol}": { + "get": { + "tags": ["Vector Tiles"], + "summary": "Retrieve vector tiles intended for use with a specified style", + "operationId": ".dataset.style.vector.getTile", + "parameters": [ + { + "$ref": "#/components/parameters/tileMatrix" + }, + { + "$ref": "#/components/parameters/tileRow" + }, + { + "$ref": "#/components/parameters/tileCol" + }, + { + "$ref": "#/components/parameters/datetime" + }, + { + "$ref": "#/components/parameters/collections-vectorTiles" + }, + { + "$ref": "#/components/parameters/subset" + }, + { + "$ref": "#/components/parameters/crs" + }, + { + "$ref": "#/components/parameters/subset-crs" + }, + { + "$ref": "#/components/parameters/styleId" + }, + { + "$ref": "#/components/parameters/tileMatrixSetId" + }, + { + "$ref": "#/components/parameters/f-vectorTile" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VectorTile" + }, + "204": { + "$ref": "#/components/responses/EmptyTile" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/tiles": { + "get": { + "tags": ["Vector Tiles"], + "summary": "Retrieve a list of available vector tilesets for the specified collection.", + "operationId": ".collection.vector.getTileSetsList", + "parameters": [ + { + "$ref": "#/components/parameters/collectionId-vectorTiles" + }, + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileSetsList" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/tiles/{tileMatrixSetId}": { + "get": { + "tags": ["Vector Tiles"], + "summary": "Retrieve the vector tileset metadata for the specified collection and tiling scheme (tile matrix set)", + "operationId": ".collection.vector.getTileSet", + "parameters": [ + { + "$ref": "#/components/parameters/collectionId-vectorTiles" + }, + { + "$ref": "#/components/parameters/collections-vectorTiles" + }, + { + "$ref": "#/components/parameters/tileMatrixSetId" + }, + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileSet" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/tiles/{tileMatrixSetId}/{tileMatrix}/{tileRow}/{tileCol}": { + "get": { + "tags": ["Vector Tiles"], + "summary": "Retrieve a vector tile from a collection.", + "operationId": ".collection.vector.getTile", + "parameters": [ + { + "$ref": "#/components/parameters/tileMatrix" + }, + { + "$ref": "#/components/parameters/tileRow" + }, + { + "$ref": "#/components/parameters/tileCol" + }, + { + "$ref": "#/components/parameters/datetime" + }, + { + "$ref": "#/components/parameters/collectionId-vectorTiles" + }, + { + "$ref": "#/components/parameters/collections-vectorTiles" + }, + { + "$ref": "#/components/parameters/subset" + }, + { + "$ref": "#/components/parameters/crs" + }, + { + "$ref": "#/components/parameters/subset-crs" + }, + { + "$ref": "#/components/parameters/tileMatrixSetId" + }, + { + "$ref": "#/components/parameters/f-vectorTile" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VectorTile" + }, + "204": { + "$ref": "#/components/responses/EmptyTile" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/styles/{styleId}/tiles": { + "get": { + "tags": ["Vector Tiles"], + "summary": "Retrieve a list of vector tilesets for the specified collection intended for use with a specified style", + "operationId": ".collection.style.vector.getTileSetsList", + "parameters": [ + { + "$ref": "#/components/parameters/collectionId-vectorTiles" + }, + { + "$ref": "#/components/parameters/f-metadata" + }, + { + "$ref": "#/components/parameters/styleId-collection" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileSetsList" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/styles/{styleId}/tiles/{tileMatrixSetId}": { + "get": { + "tags": ["Vector Tiles"], + "summary": "Retrieve the vector tileset metadata for the specified collection, style and tiling scheme (tile matrix set).", + "operationId": ".collection.style.vector.getTileSet", + "parameters": [ + { + "$ref": "#/components/parameters/collectionId-vectorTiles" + }, + { + "$ref": "#/components/parameters/collections-vectorTiles" + }, + { + "$ref": "#/components/parameters/styleId-collection" + }, + { + "$ref": "#/components/parameters/tileMatrixSetId" + }, + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileSet" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/styles/{styleId}/tiles/{tileMatrixSetId}/{tileMatrix}/{tileRow}/{tileCol}": { + "get": { + "tags": ["Vector Tiles"], + "summary": "Retrieve vector tiles for a specified collection, intended for use with a specified style", + "operationId": ".collection.style.vector.getTile", + "parameters": [ + { + "$ref": "#/components/parameters/tileMatrix" + }, + { + "$ref": "#/components/parameters/tileRow" + }, + { + "$ref": "#/components/parameters/tileCol" + }, + { + "$ref": "#/components/parameters/datetime" + }, + { + "$ref": "#/components/parameters/collectionId-vectorTiles" + }, + { + "$ref": "#/components/parameters/collections-vectorTiles" + }, + { + "$ref": "#/components/parameters/subset" + }, + { + "$ref": "#/components/parameters/crs" + }, + { + "$ref": "#/components/parameters/subset-crs" + }, + { + "$ref": "#/components/parameters/bgcolor" + }, + { + "$ref": "#/components/parameters/transparent" + }, + { + "$ref": "#/components/parameters/styleId-collection" + }, + { + "$ref": "#/components/parameters/tileMatrixSetId" + }, + { + "$ref": "#/components/parameters/f-vectorTile" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/VectorTile" + }, + "204": { + "$ref": "#/components/responses/EmptyTile" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/coverage/tiles": { + "get": { + "tags": ["Coverage Tiles"], + "summary": "Retrieve the list of available coverage tilesets for the specified collection.", + "operationId": ".collection.coverage.getTileSetsList", + "parameters": [ + { + "$ref": "#/components/parameters/collectionId-coverage" + }, + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileSetsList" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/coverage/tiles/{tileMatrixSetId}": { + "get": { + "tags": ["Coverage Tiles"], + "summary": "Retrieve the coverage tileset metadata for the specified collection and tiling scheme (tile matrix set)", + "operationId": ".collection.coverage.getTileSet", + "parameters": [ + { + "$ref": "#/components/parameters/collectionId-coverage" + }, + { + "$ref": "#/components/parameters/collections-coverage" + }, + { + "$ref": "#/components/parameters/tileMatrixSetId" + }, + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileSet" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/coverage/tiles/{tileMatrixSetId}/{tileMatrix}/{tileRow}/{tileCol}": { + "get": { + "tags": ["Coverage Tiles"], + "summary": "Retrieve coverage tiles", + "operationId": ".collection.coverage.getTile", + "parameters": [ + { + "$ref": "#/components/parameters/tileMatrix" + }, + { + "$ref": "#/components/parameters/tileRow" + }, + { + "$ref": "#/components/parameters/tileCol" + }, + { + "$ref": "#/components/parameters/datetime" + }, + { + "$ref": "#/components/parameters/collectionId-coverage" + }, + { + "$ref": "#/components/parameters/collections-coverage" + }, + { + "$ref": "#/components/parameters/subset" + }, + { + "$ref": "#/components/parameters/crs" + }, + { + "$ref": "#/components/parameters/subset-crs" + }, + { + "$ref": "#/components/parameters/tileMatrixSetId" + }, + { + "$ref": "#/components/parameters/f-coverageTile" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/CoverageTile" + }, + "204": { + "$ref": "#/components/responses/EmptyTile" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/map/tiles": { + "get": { + "tags": ["Map Tiles"], + "summary": "Retrieve the list of all default map tilesets for the whole dataset", + "operationId": ".dataset.map.getTileSetsList", + "parameters": [ + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileSetsList" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/map/tiles/{tileMatrixSetId}": { + "get": { + "tags": ["Map Tiles"], + "summary": "Retrieve a default map tileset of the whole dataset for the specified tiling scheme (tile matrix set)", + "operationId": ".dataset.map.getTileSet", + "parameters": [ + { + "$ref": "#/components/parameters/collections" + }, + { + "$ref": "#/components/parameters/tileMatrixSetId" + }, + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileSet" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/map/tiles/{tileMatrixSetId}/{tileMatrix}/{tileRow}/{tileCol}": { + "get": { + "tags": ["Map Tiles"], + "summary": "Retrieve a default map tile of the whole dataset", + "operationId": ".dataset.map.getTile", + "parameters": [ + { + "$ref": "#/components/parameters/tileMatrix" + }, + { + "$ref": "#/components/parameters/tileRow" + }, + { + "$ref": "#/components/parameters/tileCol" + }, + { + "$ref": "#/components/parameters/datetime" + }, + { + "$ref": "#/components/parameters/collections" + }, + { + "$ref": "#/components/parameters/subset" + }, + { + "$ref": "#/components/parameters/crs" + }, + { + "$ref": "#/components/parameters/subset-crs" + }, + { + "$ref": "#/components/parameters/bgcolor" + }, + { + "$ref": "#/components/parameters/transparent" + }, + { + "$ref": "#/components/parameters/tileMatrixSetId" + }, + { + "$ref": "#/components/parameters/f-mapTile" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/MapTile" + }, + "204": { + "$ref": "#/components/responses/EmptyTile" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/styles/{styleId}/map/tiles": { + "get": { + "tags": ["Map Tiles"], + "summary": "Retrieve the list of styled map tilesets for the whole dataset", + "operationId": ".dataset.style.map.getTileSetsList", + "parameters": [ + { + "$ref": "#/components/parameters/styleId" + }, + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileSetsList" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/styles/{styleId}/map/tiles/{tileMatrixSetId}": { + "get": { + "tags": ["Map Tiles"], + "summary": "Retrieve a styled map tileset of the whole dataset for the specified tiling scheme (tile matrix set)", + "operationId": ".dataset.style.map.getTileSet", + "parameters": [ + { + "$ref": "#/components/parameters/styleId" + }, + { + "$ref": "#/components/parameters/collections" + }, + { + "$ref": "#/components/parameters/tileMatrixSetId" + }, + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileSet" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/styles/{styleId}/map/tiles/{tileMatrixSetId}/{tileMatrix}/{tileRow}/{tileCol}": { + "get": { + "tags": ["Map Tiles"], + "summary": "Retrieve a styled map tiles", + "operationId": ".dataset.style.map.getTile", + "parameters": [ + { + "$ref": "#/components/parameters/tileMatrix" + }, + { + "$ref": "#/components/parameters/tileRow" + }, + { + "$ref": "#/components/parameters/tileCol" + }, + { + "$ref": "#/components/parameters/datetime" + }, + { + "$ref": "#/components/parameters/collections" + }, + { + "$ref": "#/components/parameters/subset" + }, + { + "$ref": "#/components/parameters/crs" + }, + { + "$ref": "#/components/parameters/subset-crs" + }, + { + "$ref": "#/components/parameters/bgcolor" + }, + { + "$ref": "#/components/parameters/transparent" + }, + { + "$ref": "#/components/parameters/styleId" + }, + { + "$ref": "#/components/parameters/tileMatrixSetId" + }, + { + "$ref": "#/components/parameters/f-mapTile" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/MapTile" + }, + "204": { + "$ref": "#/components/responses/EmptyTile" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/map/tiles": { + "get": { + "tags": ["Map Tiles"], + "summary": "Retrieve a list of all map tilesets for specified collection.", + "operationId": ".collection.map.getTileSetsList", + "parameters": [ + { + "$ref": "#/components/parameters/collectionId-all" + }, + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileSetsList" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/map/tiles/{tileMatrixSetId}": { + "get": { + "tags": ["Map Tiles"], + "summary": "Retrieve a map tile set metadata for the specified collection and tiling scheme (tile matrix set)", + "operationId": ".collection.map.getTileSet", + "parameters": [ + { + "$ref": "#/components/parameters/collectionId-all" + }, + { + "$ref": "#/components/parameters/collections" + }, + { + "$ref": "#/components/parameters/tileMatrixSetId" + }, + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileSet" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/map/tiles/{tileMatrixSetId}/{tileMatrix}/{tileRow}/{tileCol}": { + "get": { + "tags": ["Map Tiles"], + "summary": "Retrieve a map tile from the specified collection", + "operationId": ".collection.map.getTile", + "parameters": [ + { + "$ref": "#/components/parameters/tileMatrix" + }, + { + "$ref": "#/components/parameters/tileRow" + }, + { + "$ref": "#/components/parameters/tileCol" + }, + { + "$ref": "#/components/parameters/datetime" + }, + { + "$ref": "#/components/parameters/collectionId-all" + }, + { + "$ref": "#/components/parameters/collections" + }, + { + "$ref": "#/components/parameters/subset" + }, + { + "$ref": "#/components/parameters/crs" + }, + { + "$ref": "#/components/parameters/subset-crs" + }, + { + "$ref": "#/components/parameters/bgcolor" + }, + { + "$ref": "#/components/parameters/transparent" + }, + { + "$ref": "#/components/parameters/tileMatrixSetId" + }, + { + "$ref": "#/components/parameters/f-mapTile" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/MapTile" + }, + "204": { + "$ref": "#/components/responses/EmptyTile" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/styles/{styleId}/map/tiles": { + "get": { + "tags": ["Map Tiles"], + "summary": "Retrieve a list of styled map tilesets for the specified collection", + "operationId": ".collection.style.map.getTileSetsList", + "parameters": [ + { + "$ref": "#/components/parameters/collectionId-all" + }, + { + "$ref": "#/components/parameters/f-metadata" + }, + { + "$ref": "#/components/parameters/styleId-collection" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileSetsList" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/styles/{styleId}/map/tiles/{tileMatrixSetId}": { + "get": { + "tags": ["Map Tiles"], + "summary": "Retrieve the map tileset metadata for the specified collection, style and tiling scheme (tile matrix set).", + "operationId": ".collection.style.map.getTileSet", + "parameters": [ + { + "$ref": "#/components/parameters/collectionId-all" + }, + { + "$ref": "#/components/parameters/collections" + }, + { + "$ref": "#/components/parameters/styleId-collection" + }, + { + "$ref": "#/components/parameters/tileMatrixSetId" + }, + { + "$ref": "#/components/parameters/f-metadata" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/TileSet" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/collections/{collectionId}/styles/{styleId}/map/tiles/{tileMatrixSetId}/{tileMatrix}/{tileRow}/{tileCol}": { + "get": { + "tags": ["Map Tiles"], + "summary": "Retrieve a map tile for a specified collection and style", + "operationId": ".collection.style.map.getTile", + "parameters": [ + { + "$ref": "#/components/parameters/tileMatrix" + }, + { + "$ref": "#/components/parameters/tileRow" + }, + { + "$ref": "#/components/parameters/tileCol" + }, + { + "$ref": "#/components/parameters/datetime" + }, + { + "$ref": "#/components/parameters/collectionId-all" + }, + { + "$ref": "#/components/parameters/collections" + }, + { + "$ref": "#/components/parameters/subset" + }, + { + "$ref": "#/components/parameters/crs" + }, + { + "$ref": "#/components/parameters/subset-crs" + }, + { + "$ref": "#/components/parameters/bgcolor" + }, + { + "$ref": "#/components/parameters/transparent" + }, + { + "$ref": "#/components/parameters/styleId-collection" + }, + { + "$ref": "#/components/parameters/tileMatrixSetId" + }, + { + "$ref": "#/components/parameters/f-mapTile" + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/MapTile" + }, + "204": { + "$ref": "#/components/responses/EmptyTile" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "406": { + "$ref": "#/components/responses/NotAcceptable" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + } + }, + "components": { + "schemas": { + "confClasses": { + "type": "object", + "required": ["conformsTo"], + "properties": { + "conformsTo": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "link": { + "type": "object", + "required": ["href", "rel"], + "properties": { + "href": { + "type": "string", + "description": "Supplies the URI to a remote resource (or resource fragment).", + "example": "http://data.example.com/buildings/123" + }, + "rel": { + "type": "string", + "description": "The type or semantics of the relation.", + "example": "alternate" + }, + "type": { + "type": "string", + "description": "A hint indicating what the media type of the result of dereferencing the link should be.", + "example": "application/geo+json" + }, + "templated": { + "type": "boolean", + "description": "This flag set to true if the link is a URL template." + }, + "varBase": { + "description": "A base path to retrieve semantic information about the variables used in URL template.", + "type": "string", + "example": "/ogcapi/vars/" + }, + "hreflang": { + "type": "string", + "description": "A hint indicating what the language of the result of dereferencing the link should be.", + "example": "en" + }, + "title": { + "type": "string", + "description": "Used to label the destination of a link such that it can be used as a human-readable identifier.", + "example": "Trierer Strasse 70, 53115 Bonn" + }, + "length": { + "type": "integer" + } + } + }, + "landingPage": { + "type": "object", + "required": ["links"], + "properties": { + "title": { + "type": "string", + "title": "The title of the API.", + "description": "While a title is not required, implementors are strongly advised to include one.", + "example": "Buildings in Bonn" + }, + "description": { + "type": "string", + "example": "Access to data about buildings in the city of Bonn via a Web API that conforms to the OGC API Common specification." + }, + "attribution": { + "type": "string", + "title": "attribution for the API", + "description": "The `attribution` should be short and intended for presentation to a user, for example, in a corner of a map. Parts of the text can be links to other resources if additional information is needed. The string can include HTML markup." + }, + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/link" + } + } + } + }, + "exception": { + "title": "Exception Schema", + "description": "JSON schema for exceptions based on RFC 7807", + "type": "object", + "required": ["type"], + "properties": { + "type": { + "type": "string" + }, + "title": { + "type": "string" + }, + "status": { + "type": "integer" + }, + "detail": { + "type": "string" + }, + "instance": { + "type": "string" + } + } + }, + "collections": { + "type": "object", + "required": ["links", "collections"], + "properties": { + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/link" + } + }, + "timeStamp": { + "type": "string", + "format": "date-time" + }, + "numberMatched": { + "type": "integer", + "minimum": 0, + "example": 1 + }, + "numberReturned": { + "type": "integer", + "minimum": 0, + "example": 1 + }, + "collections": { + "type": "array", + "items": { + "$ref": "#/components/schemas/collectionInfo" + } + } + } + }, + "collectionInfo": { + "type": "object", + "required": ["id", "links"], + "properties": { + "id": { + "type": "string", + "description": "identifier of the collection used, for example, in URIs", + "example": "dem" + }, + "title": { + "type": "string", + "description": "human readable title of the collection", + "example": "Digital Elevation Model" + }, + "description": { + "type": "string", + "description": "a description of the data in the collection", + "example": "A Digital Elevation Model." + }, + "links": { + "type": "array", + "example": [ + { + "href": "http://data.example.org/collections/dem?f=json", + "rel": "self", + "type": "application/json", + "title": "Digital Elevation Model" + }, + { + "href": "http://data.example.org/collections/dem?f=html", + "rel": "alternate", + "type": "application/json", + "title": "Digital Elevation Model" + }, + { + "href": "http://data.example.org/collections/dem/coverage", + "rel": "coverage", + "type": "image/tiff; application=geotiff", + "title": "Digital Elevation Model" + }, + { + "href": "http://data.example.org/collections/dem/coverage/domainset", + "rel": "domainset", + "type": "application/json", + "title": "Digital Elevation Model" + }, + { + "href": "http://data.example.org/collections/dem/coverage/rangetype", + "rel": "rangetype", + "type": "application/json", + "title": "Digital Elevation Model" + }, + { + "href": "http://data.example.org/collections/dem/coverage/metadata", + "rel": "metadata", + "type": "application/json", + "title": "Digital Elevation Model" + } + ], + "items": { + "$ref": "#/components/schemas/link" + } + }, + "extent": { + "$ref": "#/components/schemas/extent-uad" + }, + "itemType": { + "description": "indicator about the type of the items in the collection if the collection has an accessible /collections/{collectionId}/items endpoint", + "type": "string", + "default": "unknown" + }, + "crs": { + "description": "the list of coordinate reference systems supported by the API; the first item is the default coordinate reference system", + "type": "array", + "items": { + "type": "string" + }, + "default": ["http://www.opengis.net/def/crs/OGC/1.3/CRS84"], + "example": [ + "http://www.opengis.net/def/crs/OGC/1.3/CRS84", + "http://www.opengis.net/def/crs/EPSG/0/4326" + ] + }, + "dataType": { + "allOf": [ + { + "description": "Type of data represented in the collection" + }, + { + "$ref": "#/components/schemas/dataType" + } + ] + }, + "geometryDimension": { + "description": "The geometry dimension of the features shown in this layer (0: points, 1: curves, 2: surfaces, 3: solids), unspecified: mixed or unknown", + "type": "integer", + "minimum": 0, + "maximum": 3 + }, + "minScaleDenominator": { + "description": "Minimum scale denominator for usage of the collection", + "type": "number" + }, + "maxScaleDenominator": { + "description": "Maximum scale denominator for usage of the collection", + "type": "number" + }, + "minCellSize": { + "description": "Minimum cell size for usage of the collection", + "type": "number" + }, + "maxCellSize": { + "description": "Maximum cell size for usage of the collection", + "type": "number" + } + } + }, + "extent": { + "description": "The extent of the data in the collection. In the Core only spatial and temporal\nextents are specified. Extensions may add additional members to represent other\nextents, for example, thermal or pressure ranges.\n\nThe first item in the array describes the overall extent of\nthe data. All subsequent items describe more precise extents,\ne.g., to identify clusters of data.\nClients only interested in the overall extent will only need to\naccess the first item in each array.", + "type": "object", + "properties": { + "spatial": { + "description": "The spatial extent of the data in the collection.", + "type": "object", + "properties": { + "bbox": { + "description": "One or more bounding boxes that describe the spatial extent of the dataset.\nIn the Core only a single bounding box is supported.\n\nExtensions may support additional areas.\nThe first bounding box describes the overall spatial\nextent of the data. All subsequent bounding boxes describe\nmore precise bounding boxes, e.g., to identify clusters of data.\nClients only interested in the overall spatial extent will\nonly need to access the first item in each array.", + "type": "array", + "minItems": 1, + "items": { + "description": "Each bounding box is provided as four or six numbers, depending on\nwhether the coordinate reference system includes a vertical axis\n(height or depth):\n\n* Lower left corner, coordinate axis 1\n* Lower left corner, coordinate axis 2\n* Minimum value, coordinate axis 3 (optional)\n* Upper right corner, coordinate axis 1\n* Upper right corner, coordinate axis 2\n* Maximum value, coordinate axis 3 (optional)\n\nIf the value consists of four numbers, the coordinate reference system is\nWGS 84 longitude/latitude (http://www.opengis.net/def/crs/OGC/1.3/CRS84)\nunless a different coordinate reference system is specified in a parameter `bbox-crs`.\n\nIf the value consists of six numbers, the coordinate reference system is WGS 84\nlongitude/latitude/ellipsoidal height (http://www.opengis.net/def/crs/OGC/0/CRS84h)\nunless a different coordinate reference system is specified in a parameter `bbox-crs`.\n\nFor WGS 84 longitude/latitude the values are in most cases the sequence of\nminimum longitude, minimum latitude, maximum longitude and maximum latitude.\nHowever, in cases where the box spans the antimeridian the first value\n(west-most box edge) is larger than the third value (east-most box edge).\n\nIf the vertical axis is included, the third and the sixth number are\nthe bottom and the top of the 3-dimensional bounding box.\n\nIf a feature has multiple spatial geometry properties, it is the decision of the\nserver whether only a single spatial geometry property is used to determine\nthe extent or all relevant geometries.", + "type": "array", + "oneOf": [ + { + "minItems": 4, + "maxItems": 4 + }, + { + "minItems": 6, + "maxItems": 6 + } + ], + "items": { + "type": "number" + }, + "example": [-180, -90, 180, 90] + } + }, + "crs": { + "description": "Coordinate reference system of the coordinates in the spatial extent\n(property `bbox`). The default reference system is WGS 84 longitude/latitude.\nIn the Core the only other supported coordinate reference system is\nWGS 84 longitude/latitude/ellipsoidal height for coordinates with height.\nExtensions may support additional coordinate reference systems and add\nadditional enum values.", + "type": "string", + "enum": [ + "http://www.opengis.net/def/crs/OGC/1.3/CRS84", + "http://www.opengis.net/def/crs/OGC/0/CRS84h" + ], + "default": "http://www.opengis.net/def/crs/OGC/1.3/CRS84" + }, + "grid": { + "description": "Provides information about the limited availability of data within the collection organized\nas a grid (regular or irregular) along each spatial dimension.", + "type": "array", + "minItems": 2, + "maxItems": 3, + "items": { + "type": "object", + "properties": { + "coordinates": { + "description": "List of coordinates along the dimension for which data organized as an irregular grid in the collection is available\n(e.g., 2, 10, 80, 100).", + "type": "array", + "minItems": 1, + "items": { + "oneOf": [ + { + "type": "string", + "nullable": true + }, + { + "type": "number" + } + ] + }, + "example": [2, 10, 80, 100] + }, + "cellsCount": { + "description": "Number of samples available along the dimension for data organized as a regular grid.\nFor values representing the whole area of contiguous cells spanning _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_.\nFor values representing infinitely small point cells spaced by _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_ + 1.", + "type": "integer", + "example": 50 + }, + "resolution": { + "description": "Resolution of regularly gridded data along the dimension in the collection", + "oneOf": [ + { + "type": "string", + "nullable": true + }, + { + "type": "number" + } + ], + "example": 0.0006866455078 + } + } + } + } + } + }, + "temporal": { + "description": "The temporal extent of the features in the collection.", + "type": "object", + "properties": { + "interval": { + "description": "One or more time intervals that describe the temporal extent of the dataset.\nIn the Core only a single time interval is supported.\n\nExtensions may support multiple intervals.\nThe first time interval describes the overall\ntemporal extent of the data. All subsequent time intervals describe\nmore precise time intervals, e.g., to identify clusters of data.\nClients only interested in the overall extent will only need\nto access the first item in each array.", + "type": "array", + "minItems": 1, + "items": { + "description": "Begin and end times of the time interval. The timestamps are in the\ntemporal coordinate reference system specified in `trs`. By default\nthis is the Gregorian calendar.\n\nThe value `null` for start or end time is supported and indicates a half-bounded time interval.", + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "example": ["2011-11-11T12:22:11Z", null] + } + }, + "trs": { + "description": "Coordinate reference system of the coordinates in the temporal extent\n(property `interval`). The default reference system is the Gregorian calendar.\nIn the Core this is the only supported temporal coordinate reference system.\nExtensions may support additional temporal coordinate reference systems and add\nadditional enum values.", + "type": "string", + "enum": ["http://www.opengis.net/def/uom/ISO-8601/0/Gregorian"], + "default": "http://www.opengis.net/def/uom/ISO-8601/0/Gregorian" + }, + "grid": { + "type": "object", + "description": "Provides information about the limited availability of data within the collection organized as a grid (regular or irregular) along the temporal dimension.", + "properties": { + "coordinates": { + "description": "List of coordinates along the temporal dimension for which data organized as an irregular grid in the collection is available\n(e.g., \"2017-11-14T09:00Z\",\"2017-11-14T12:00Z\",\"2017-11-14T15:00Z\",\"2017-11-14T18:00Z\",\"2017-11-14T21:00Z\").", + "type": "array", + "minItems": 1, + "items": { + "type": "string", + "nullable": true + }, + "example": [["2020-11-12T12:15Z", "2020-11-12T12:30Z", "2020-11-12T12:45Z"]] + }, + "cellsCount": { + "description": "Number of samples available along the temporal dimension for data organized as a regular grid.\nFor values representing the whole area of contiguous cells spanning _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_.\nFor values representing infinitely small point cells spaced by _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_ + 1.", + "type": "integer", + "example": 50 + }, + "resolution": { + "description": "Resolution of regularly gridded data along the temporal dimension in the collection", + "oneOf": [ + { + "type": "string", + "nullable": true + }, + { + "type": "number" + } + ], + "example": "PT1H" + } + } + } + } + } + } + }, + "extent-uad": { + "title": "Extent with Uniform Additional Dimensions Schema", + "description": "The extent module only addresses spatial and temporal extents. This module extends extent by specifying how\nintervals and crs properties can be used to specify additional geometries.", + "allOf": [ + { + "$ref": "#/components/schemas/extent" + }, + { + "type": "object", + "additionalProperties": { + "description": "The domain intervals for any additional dimensions of the extent (envelope) beyond those described in temporal and spatial.", + "type": "object", + "oneOf": [ + { + "required": ["interval", "crs"] + }, + { + "required": ["interval", "trs"] + }, + { + "required": ["interval", "vrs"] + } + ], + "properties": { + "interval": { + "description": "One or more intervals that describe the extent for this dimension of the dataset.\nThe value `null` is supported and indicates an unbounded or half-bounded interval.\nThe first interval describes the overall extent of the data for this dimension.\nAll subsequent intervals describe more precise intervals, e.g., to identify clusters of data.\nClients only interested in the overall extent will only need\nto access the first item (a pair of lower and upper bound values).", + "type": "array", + "minItems": 1, + "items": { + "description": "Lower and upper bound values of the interval. The values\nare in the coordinate reference system specified in `crs`, `trs` or `vrs`.", + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "oneOf": [ + { + "type": "string", + "nullable": true + }, + { + "type": "number" + } + ] + }, + "example": ["2011-11-11T12:22:11Z", 32.5, null] + } + }, + "crs": { + "type": "string", + "description": "generic coordinate reference system suitable for any type of dimensions" + }, + "trs": { + "type": "string", + "description": "temporal coordinate reference system (e.g. as defined by Features for 'temporal')" + }, + "vrs": { + "type": "string", + "description": "vertical coordinate reference system (e.g. as defined in EDR for 'vertical')" + }, + "grid": { + "type": "object", + "description": "Provides information about the limited availability of data within the collection organized as a grid (regular or irregular) along the dimension.", + "properties": { + "coordinates": { + "description": "List of coordinates along the temporal dimension for which data organized as an irregular grid in the collection is available\n(e.g., 2, 10, 80, 100).", + "type": "array", + "minItems": 1, + "items": { + "oneOf": [ + { + "type": "string", + "nullable": true + }, + { + "type": "number" + } + ] + }, + "example": [2, 10, 80, 100] + }, + "cellsCount": { + "description": "Number of samples available along the dimension for data organized as a regular grid.\nFor values representing the whole area of contiguous cells spanning _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_.\nFor values representing infinitely small point cells spaced by _resolution_ units along the dimension, this will be (_upperBound_ - _lowerBound_) / _resolution_ + 1.", + "type": "integer", + "example": 50 + }, + "resolution": { + "description": "Resolution of regularly gridded data along the dimension in the collection", + "oneOf": [ + { + "type": "string", + "nullable": true + }, + { + "type": "number" + } + ], + "example": ["PT1H", 0.0006866455078] + } + } + } + } + } + } + ] + }, + "crs": { + "title": "CRS", + "oneOf": [ + { + "description": "Simplification of the object into a url if the other properties are not present", + "type": "string" + }, + { + "type": "object", + "oneOf": [ + { + "required": ["uri"], + "properties": { + "uri": { + "description": "Reference to one coordinate reference system (CRS)", + "type": "string", + "format": "uri" + } + } + }, + { + "required": ["wkt"], + "properties": { + "wkt": { + "allOf": [ + { + "description": "An object defining the CRS using the JSON encoding for Well-known text representation of coordinate reference systems 2.0" + }, + { + "type": "object" + } + ] + } + } + }, + { + "required": ["referenceSystem"], + "properties": { + "referenceSystem": { + "description": "A reference system data structure as defined in the MD_ReferenceSystem of the ISO 19115", + "type": "object" + } + } + } + ] + } + ] + }, + "dataType": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "string", + "enum": ["map", "vector", "coverage"] + } + ] + }, + "timeStamp": { + "description": "This property indicates the time and date when the response was generated using RFC 3339 notation.", + "type": "string", + "format": "date-time", + "example": "2017-08-17T08:05:32Z" + }, + "numberReturned": { + "description": "The number of features in the feature collection.\nA server may omit this information in a response, if the information\nabout the number of features is not known or difficult to compute.\nIf the value is provided, the value shall be identical to the number\nof items in the \"features\" array.", + "type": "integer", + "minimum": 0, + "example": 10 + }, + "numberMatched": { + "description": "The number of features of the feature type that match the selection\nparameters like `bbox`.", + "type": "integer", + "minimum": 0, + "example": 127 + }, + "tileSet": { + "title": "Tile Set Metadata", + "description": "A resource describing a tileset based on the OGC TileSet Metadata Standard. At least one of the 'TileMatrixSet', or a link with 'rel' http://www.opengis.net/def/rel/ogc/1.0/tiling-scheme", + "type": "object", + "required": ["dataType", "crs", "links"], + "properties": { + "title": { + "description": "A title for this tileset", + "type": "string" + }, + "description": { + "description": "Brief narrative description of this tile set", + "type": "string" + }, + "dataType": { + "allOf": [ + { + "description": "Type of data represented in the tileset" + }, + { + "$ref": "#/components/schemas/dataType" + } + ] + }, + "crs": { + "allOf": [ + { + "description": "Coordinate Reference System (CRS)" + }, + { + "$ref": "#/components/schemas/crs" + } + ] + }, + "tileMatrixSetURI": { + "description": "Reference to a Tile Matrix Set on an offical source for Tile Matrix Sets such as the OGC NA definition server (http://www.opengis.net/def/tms/). Required if the tile matrix set is registered on an open official source.", + "type": "string", + "format": "uri" + }, + "links": { + "description": "Links to related resources. Possible link 'rel' values are: 'http://www.opengis.net/def/rel/ogc/1.0/dataset' for a URL pointing to the dataset, 'item' for a URL template to get a tile; 'alternate' for a URL pointing to another representation of the TileSetMetadata (e.g a TileJSON file); 'http://www.opengis.net/def/rel/ogc/1.0/tiling-scheme' for a definition of the TileMatrixSet; 'http://www.opengis.net/def/rel/ogc/1.0/geodata' for pointing to a single collection (if the tileset represents a single collection)", + "type": "array", + "items": { + "$ref": "#/components/schemas/link" + } + }, + "tileMatrixSetLimits": { + "description": "Limits for the TileRow and TileCol values for each TileMatrix in the tileMatrixSet. If missing, there are no limits other that the ones imposed by the TileMatrixSet. If present the TileMatrices listed are limited and the rest not available at all", + "type": "array", + "items": { + "$ref": "#/components/schemas/tileMatrixLimits" + } + }, + "epoch": { + "description": "Epoch of the Coordinate Reference System (CRS)", + "type": "number" + }, + "layers": { + "minItems": 1, + "type": "array", + "items": { + "type": "object", + "required": ["id", "dataType"], + "properties": { + "title": { + "description": "Title of this tile matrix set, normally used for display to a human", + "type": "string" + }, + "description": { + "description": "Brief narrative description of this tile matrix set, normally available for display to a human", + "type": "string" + }, + "keywords": { + "description": "Unordered list of one or more commonly used or formalized word(s) or phrase(s) used to describe this layer", + "type": "string" + }, + "id": { + "description": "Unique identifier of the Layer. Implementation of 'identifier'", + "type": "string" + }, + "dataType": { + "allOf": [ + { + "description": "Type of data represented in the layer" + }, + { + "$ref": "#/components/schemas/dataType" + } + ] + }, + "geometryDimension": { + "description": "The geometry dimension of the features shown in this layer (0: points, 1: curves, 2: surfaces, 3: solids), unspecified: mixed or unknown", + "type": "integer", + "minimum": 0, + "maximum": 3 + }, + "featureType": { + "description": "Feature type identifier. Only applicable to layers of datatype 'geometries'", + "type": "string" + }, + "attribution": { + "description": "Short reference to recognize the author or provider", + "type": "string" + }, + "license": { + "description": "License applicable to the tiles", + "type": "string" + }, + "pointOfContact": { + "description": "Useful information to contact the authors or custodians for the layer (e.g. e-mail address, a physical address, phone numbers, etc)", + "type": "string" + }, + "publisher": { + "description": "Organization or individual responsible for making the layer available", + "type": "string" + }, + "theme": { + "description": "Category where the layer can be grouped", + "type": "string" + }, + "crs": { + "allOf": [ + { + "description": "Coordinate Reference System (CRS)" + }, + { + "$ref": "#/components/schemas/crs" + } + ] + }, + "epoch": { + "description": "Epoch of the Coordinate Reference System (CRS)", + "type": "number" + }, + "minScaleDenominator": { + "description": "Minimum scale denominator for usage of the layer", + "type": "number" + }, + "maxScaleDenominator": { + "description": "Maximum scale denominator for usage of the layer", + "type": "number" + }, + "minCellSize": { + "description": "Minimum cell size for usage of the layer", + "type": "number" + }, + "maxCellSize": { + "description": "Maximum cell size for usage of the layer", + "type": "number" + }, + "maxTileMatrix": { + "description": "TileMatrix identifier associated with the minScaleDenominator", + "type": "string" + }, + "minTileMatrix": { + "description": "TileMatrix identifier associated with the maxScaleDenominator", + "type": "string" + }, + "boundingBox": { + "allOf": [ + { + "description": "Minimum bounding rectangle surrounding the layer" + }, + { + "$ref": "#/components/schemas/2DBoundingBox" + } + ] + }, + "created": { + "allOf": [ + { + "description": "When the layer was first produced" + }, + { + "$ref": "#/components/schemas/timeStamp" + } + ] + }, + "updated": { + "allOf": [ + { + "description": "Last layer change/revision" + }, + { + "$ref": "#/components/schemas/timeStamp" + } + ] + }, + "style": { + "allOf": [ + { + "description": "Style used to generate the layer in the tileset" + }, + { + "$ref": "#/components/schemas/tileSet/properties/style/allOf/1" + } + ] + }, + "geoDataClasses": { + "description": "URI identifying a class of data contained in this layer (useful to determine compatibility with styles or processes)", + "type": "array", + "items": { + "type": "string" + } + }, + "propertiesSchema": { + "allOf": [ + { + "description": "Properties represented by the features in this layer. Can be the attributes of a feature dataset (datatype=geometries) or the rangeType of a coverage (datatype=coverage)" + }, + { + "description": "Attributes of the features or rangetypes of a coverage. Defined by a subset of the JSON Schema for the properties of a feature", + "type": "object", + "required": ["type", "properties"], + "properties": { + "type": { + "type": "string", + "enum": ["object"] + }, + "required": { + "description": "Implements 'multiplicity' by citing property 'name' defined as 'additionalProperties'", + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "properties": { + "type": "object", + "default": {}, + "additionalProperties": { + "description": "No property names are defined but any property name they should be described by JSON Schema. So 'additionalProperties' implements 'name'.", + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "description": { + "description": "Implements 'description'", + "type": "string" + }, + "type": { + "type": "string", + "enum": ["array", "boolean", "integer", "null", "number", "object", "string"] + }, + "enum": { + "description": "Implements 'acceptedValues'", + "type": "array", + "minItems": 1, + "items": {}, + "uniqueItems": true + }, + "format": { + "description": "Complements implementation of 'type'", + "type": "string" + }, + "contentMediaType": { + "description": "Implements 'mediaType'", + "type": "string" + }, + "maximum": { + "description": "Implements 'range'", + "type": "number" + }, + "exclusiveMaximum": { + "description": "Implements 'range'", + "type": "number" + }, + "minimum": { + "description": "Implements 'range'", + "type": "number" + }, + "exclusiveMinimum": { + "description": "Implements 'range'", + "type": "number" + }, + "pattern": { + "type": "string", + "format": "regex" + }, + "maxItems": { + "description": "Implements 'upperMultiplicity'", + "type": "integer", + "minimum": 0 + }, + "minItems": { + "description": "Implements 'lowerMultiplicity'", + "type": "integer", + "default": 0, + "minimum": 0 + }, + "observedProperty": { + "type": "string" + }, + "observedPropertyURI": { + "type": "string", + "format": "uri" + }, + "uom": { + "type": "string" + }, + "uomURI": { + "type": "string", + "format": "uri" + } + } + } + } + } + } + ] + }, + "links": { + "description": "Links related to this layer. Possible link 'rel' values are: 'geodata' for a URL pointing to the collection of geospatial data.", + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/components/schemas/link" + } + } + } + } + }, + "boundingBox": { + "allOf": [ + { + "description": "Minimum bounding rectangle surrounding the tile matrix set, in the supported CRS" + }, + { + "$ref": "#/components/schemas/2DBoundingBox" + } + ] + }, + "centerPoint": { + "allOf": [ + { + "description": "Location of a tile that nicely represents the tileset. Implementations may use this center value to set the default location or to present a representative tile in a user interface" + }, + { + "type": "object", + "required": ["coordinates"], + "properties": { + "coordinates": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "number" + } + }, + "crs": { + "allOf": [ + { + "description": "Coordinate Reference System (CRS) of the coordinates" + }, + { + "$ref": "#/components/schemas/crs" + } + ] + }, + "tileMatrix": { + "description": "TileMatrix identifier associated with the scaleDenominator", + "type": "string" + }, + "scaleDenominator": { + "description": "Scale denominator of the tile matrix selected", + "type": "number" + }, + "cellSize": { + "description": "Cell size of the tile matrix selected", + "type": "number" + } + } + } + ] + }, + "style": { + "allOf": [ + { + "description": "Style involving all layers used to generate the tileset" + }, + { + "type": "object", + "required": ["id"], + "properties": { + "id": { + "description": "An identifier for this style. Implementation of 'identifier'", + "type": "string" + }, + "title": { + "description": "A title for this style", + "type": "string" + }, + "description": { + "description": "Brief narrative description of this style", + "type": "string" + }, + "keywords": { + "description": "keywords about this style", + "type": "array", + "items": { + "type": "string" + } + }, + "links": { + "description": "Links to style related resources. Possible link 'rel' values are: 'style' for a URL pointing to the style description, 'styleSpec' for a URL pointing to the specification or standard used to define the style.", + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/components/schemas/link" + } + } + } + } + ] + }, + "attribution": { + "description": "Short reference to recognize the author or provider", + "type": "string" + }, + "license": { + "description": "License applicable to the tiles", + "type": "string" + }, + "accessConstraints": { + "description": "Restrictions on the availability of the Tile Set that the user needs to be aware of before using or redistributing the Tile Set", + "type": "string", + "default": "unclassified", + "enum": ["unclassified", "restricted", "confidential", "secret", "topSecret"] + }, + "keywords": { + "description": "keywords about this tileset", + "type": "array", + "items": { + "type": "string" + } + }, + "version": { + "description": "Version of the Tile Set. Changes if the data behind the tiles has been changed", + "type": "string" + }, + "created": { + "allOf": [ + { + "description": "When the Tile Set was first produced" + }, + { + "$ref": "#/components/schemas/timeStamp" + } + ] + }, + "updated": { + "allOf": [ + { + "description": "Last Tile Set change/revision" + }, + { + "$ref": "#/components/schemas/timeStamp" + } + ] + }, + "pointOfContact": { + "description": "Useful information to contact the authors or custodians for the Tile Set", + "type": "string" + }, + "mediaTypes": { + "description": "Media types available for the tiles", + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "tileSet-item": { + "title": "Tile Set Metadata item", + "description": "A minimal tileset element for use within a list of tilesets linking to full description of those tilesets.", + "type": "object", + "required": ["dataType", "links", "crs"], + "properties": { + "title": { + "description": "A title for this tileset", + "type": "string" + }, + "dataType": { + "allOf": [ + { + "description": "Type of data represented in the tileset" + }, + { + "$ref": "#/components/schemas/dataType" + } + ] + }, + "crs": { + "allOf": [ + { + "description": "Coordinate Reference System (CRS)" + }, + { + "$ref": "#/components/schemas/crs" + } + ] + }, + "tileMatrixSetURI": { + "description": "Reference to a Tile Matrix Set on an offical source for Tile Matrix Sets such as the OGC NA definition server (http://www.opengis.net/def/tms/). Required if the tile matrix set is registered on an open official source.", + "type": "string", + "format": "uri" + }, + "links": { + "description": "Links to related resources. A 'self' link to the tileset as well as a 'http://www.opengis.net/def/rel/ogc/1.0/tiling-scheme' link to a definition of the TileMatrixSet are required.", + "type": "array", + "items": { + "$ref": "#/components/schemas/link" + } + } + } + }, + "tileMatrixSet": { + "title": "Tile Matrix Set Definition", + "description": "A definition of a tile matrix set following the Tile Matrix Set standard. For tileset metadata, such a description (in `tileMatrixSet` property) is only required for offline use, as an alternative to a link with a `http://www.opengis.net/def/rel/ogc/1.0/tiling-scheme` relation type.", + "type": "object", + "required": ["crs", "tileMatrices"], + "properties": { + "title": { + "description": "Title of this tile matrix set, normally used for display to a human", + "type": "string" + }, + "description": { + "description": "Brief narrative description of this tile matrix set, normally available for display to a human", + "type": "string" + }, + "keywords": { + "description": "Unordered list of one or more commonly used or formalized word(s) or phrase(s) used to describe this tile matrix set", + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "description": "Tile matrix set identifier. Implementation of 'identifier'", + "type": "string" + }, + "uri": { + "description": "Reference to an official source for this tileMatrixSet", + "type": "string", + "format": "uri" + }, + "orderedAxes": { + "type": "array", + "minItems": 1, + "items": { + "type": "string" + } + }, + "crs": { + "allOf": [ + { + "description": "Coordinate Reference System (CRS)" + }, + { + "$ref": "#/components/schemas/crs" + } + ] + }, + "wellKnownScaleSet": { + "description": "Reference to a well-known scale set", + "type": "string", + "format": "uri" + }, + "boundingBox": { + "allOf": [ + { + "description": "Minimum bounding rectangle surrounding the tile matrix set, in the supported CRS" + }, + { + "$ref": "#/components/schemas/2DBoundingBox" + } + ] + }, + "tileMatrices": { + "type": "array", + "description": "Describes scale levels and its tile matrices", + "items": { + "description": "A tile matrix, usually corresponding to a particular zoom level of a TileMatrixSet.", + "type": "object", + "required": [ + "id", + "scaleDenominator", + "cellSize", + "pointOfOrigin", + "tileWidth", + "tileHeight", + "matrixWidth", + "matrixHeight" + ], + "properties": { + "title": { + "description": "Title of this tile matrix, normally used for display to a human", + "type": "string" + }, + "description": { + "description": "Brief narrative description of this tile matrix set, normally available for display to a human", + "type": "string" + }, + "keywords": { + "description": "Unordered list of one or more commonly used or formalized word(s) or phrase(s) used to describe this dataset", + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "description": "Identifier selecting one of the scales defined in the TileMatrixSet and representing the scaleDenominator the tile. Implementation of 'identifier'", + "type": "string" + }, + "scaleDenominator": { + "description": "Scale denominator of this tile matrix", + "type": "number" + }, + "cellSize": { + "description": "Cell size of this tile matrix", + "type": "number" + }, + "cornerOfOrigin": { + "description": "The corner of the tile matrix (_topLeft_ or _bottomLeft_) used as the origin for numbering tile rows and columns. This corner is also a corner of the (0, 0) tile.", + "type": "string", + "enum": ["topLeft", "bottomLeft"], + "default": "topLeft" + }, + "pointOfOrigin": { + "allOf": [ + { + "description": "Precise position in CRS coordinates of the corner of origin (e.g. the top-left corner) for this tile matrix. This position is also a corner of the (0, 0) tile. In previous version, this was 'topLeftCorner' and 'cornerOfOrigin' did not exist." + }, + { + "$ref": "#/components/schemas/2DPoint" + } + ] + }, + "tileWidth": { + "type": "number", + "description": "Width of each tile of this tile matrix in pixels", + "format": "integer", + "minimum": 1, + "multipleOf": 1 + }, + "tileHeight": { + "type": "number", + "description": "Height of each tile of this tile matrix in pixels", + "format": "integer", + "minimum": 1, + "multipleOf": 1 + }, + "matrixHeight": { + "type": "number", + "description": "Width of the matrix (number of tiles in width)", + "format": "integer", + "minimum": 1, + "multipleOf": 1 + }, + "matrixWidth": { + "type": "number", + "description": "Height of the matrix (number of tiles in height)", + "format": "integer", + "minimum": 1, + "multipleOf": 1 + }, + "variableMatrixWidths": { + "description": "Describes the rows that has variable matrix width", + "type": "array", + "items": { + "description": "Variable Matrix Width data structure", + "type": "object", + "required": ["coalesce", "minTileRow", "maxTileRow"], + "properties": { + "coalesce": { + "description": "Number of tiles in width that coalesce in a single tile for these rows", + "type": "number", + "format": "integer", + "minimum": 2, + "multipleOf": 1 + }, + "minTileRow": { + "description": "First tile row where the coalescence factor applies for this tilematrix", + "type": "number", + "format": "integer", + "minimum": 0, + "multipleOf": 1 + }, + "maxTileRow": { + "description": "Last tile row where the coalescence factor applies for this tilematrix", + "type": "number", + "format": "integer", + "minimum": 0, + "multipleOf": 1 + } + } + } + } + } + } + } + } + }, + "tileMatrixSet-item": { + "title": "Tile Matrix Set Item", + "description": "A minimal tile matrix set element for use within a list of tile matrix sets linking to a full definition.", + "type": "object", + "required": ["links"], + "properties": { + "id": { + "description": "Optional local tile matrix set identifier, e.g. for use as unspecified `{tileMatrixSetId}` parameter. Implementation of 'identifier'", + "type": "string" + }, + "title": { + "description": "Title of this tile matrix set, normally used for display to a human", + "type": "string" + }, + "uri": { + "description": "Reference to an official source for this tileMatrixSet", + "type": "string", + "format": "uri" + }, + "crs": { + "allOf": [ + { + "description": "Coordinate Reference System (CRS)" + }, + { + "$ref": "#/components/schemas/crs" + } + ] + }, + "links": { + "description": "Links to related resources. A 'self' link to the tile matrix set definition is required.", + "type": "array", + "items": { + "$ref": "#/components/schemas/link" + } + } + } + }, + "tileMatrixLimits": { + "title": "TileMatrixLimits", + "description": "The limits for an individual tile matrix of a TileSet's TileMatrixSet, as defined in the OGC 2D TileMatrixSet and TileSet Metadata Standard", + "type": "object", + "required": ["tileMatrix", "minTileRow", "maxTileRow", "minTileCol", "maxTileCol"], + "properties": { + "tileMatrix": { + "type": "string" + }, + "minTileRow": { + "type": "integer", + "minimum": 0 + }, + "maxTileRow": { + "type": "integer", + "minimum": 0 + }, + "minTileCol": { + "type": "integer", + "minimum": 0 + }, + "maxTileCol": { + "type": "integer", + "minimum": 0 + } + } + }, + "2DPoint": { + "description": "A 2D Point in the CRS indicated elsewhere", + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "number" + } + }, + "2DBoundingBox": { + "description": "Minimum bounding rectangle surrounding a 2D resource in the CRS indicated elsewhere", + "type": "object", + "required": ["lowerLeft", "upperRight"], + "properties": { + "lowerLeft": { + "$ref": "#/components/schemas/2DPoint" + }, + "upperRight": { + "$ref": "#/components/schemas/2DPoint" + }, + "crs": { + "$ref": "#/components/schemas/crs" + }, + "orderedAxes": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "string" + } + } + } + }, + "FeatureCollection": { + "title": "GeoJSON FeatureCollection", + "type": "object", + "required": ["type", "features"], + "properties": { + "type": { + "type": "string", + "enum": ["FeatureCollection"] + }, + "features": { + "type": "array", + "items": { + "title": "GeoJSON Feature", + "type": "object", + "required": ["type", "properties", "geometry"], + "properties": { + "type": { + "type": "string", + "enum": ["Feature"] + }, + "id": { + "oneOf": [ + { + "type": "number" + }, + { + "type": "string" + } + ] + }, + "properties": { + "type": "object", + "nullable": true + }, + "geometry": { + "oneOf": [ + { + "title": "GeoJSON Point", + "type": "object", + "nullable": true, + "required": ["type", "coordinates"], + "properties": { + "type": { + "type": "string", + "enum": ["Point"] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON LineString", + "type": "object", + "required": ["type", "coordinates"], + "properties": { + "type": { + "type": "string", + "enum": ["LineString"] + }, + "coordinates": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON Polygon", + "type": "object", + "required": ["type", "coordinates"], + "properties": { + "type": { + "type": "string", + "enum": ["Polygon"] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPoint", + "type": "object", + "required": ["type", "coordinates"], + "properties": { + "type": { + "type": "string", + "enum": ["MultiPoint"] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiLineString", + "type": "object", + "required": ["type", "coordinates"], + "properties": { + "type": { + "type": "string", + "enum": ["MultiLineString"] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + { + "title": "GeoJSON MultiPolygon", + "type": "object", + "required": ["type", "coordinates"], + "properties": { + "type": { + "type": "string", + "enum": ["MultiPolygon"] + }, + "coordinates": { + "type": "array", + "items": { + "type": "array", + "items": { + "type": "array", + "minItems": 4, + "items": { + "type": "array", + "minItems": 2, + "items": { + "type": "number" + } + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + } + ] + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + } + }, + "bbox": { + "type": "array", + "minItems": 4, + "items": { + "type": "number" + } + } + } + }, + "enumeration": { + "type": "object", + "required": ["type", "enum"], + "properties": { + "type": { + "type": "string", + "enum": ["enum"] + }, + "enum": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "all-collections": { + "type": "string", + "enum": [ + "blueMarble", + "NaturalEarth:raster:HYP_HR_SR_OB_DR", + "NaturalEarth:cultural:ne_10m_admin_0_countries", + "NaturalEarth:physical:bathymetry", + "SRTM_ViewFinderPanorama", + "HRDEM-Ottawa", + "HRDEM-RedRiver" + ] + }, + "vectorTiles-collections": { + "type": "string", + "enum": ["NaturalEarth:cultural:ne_10m_admin_0_countries", "NaturalEarth:physical:bathymetry"] + }, + "coverage-collections": { + "type": "string", + "enum": ["SRTM_ViewFinderPanorama", "HRDEM-Ottawa", "HRDEM-RedRiver"] + }, + "styles": { + "type": "string", + "enum": ["default"] + }, + "tileMatrixSets": { + "type": "string", + "enum": ["WebMercatorQuad", "WorldCRS84Quad", "GNOSISGlobalGrid", "WorldMercatorWGS84Quad"] + } + }, + "parameters": { + "f-metadata": { + "name": "f", + "in": "query", + "description": "The format of the response. If no value is provided, the accept header is used to determine the format. Accepted values are 'json' or 'html'.", + "required": false, + "schema": { + "type": "string", + "enum": ["json", "html"] + }, + "style": "form", + "explode": false + }, + "collectionId-all": { + "name": "collectionId", + "in": "path", + "description": "Local identifier of a collection", + "required": true, + "schema": { + "$ref": "#/components/schemas/all-collections" + } + }, + "collectionId-coverage": { + "name": "collectionId", + "in": "path", + "description": "Local identifier of a coverage collection", + "required": true, + "allowEmptyValue": false, + "schema": { + "$ref": "#/components/schemas/coverage-collections" + }, + "style": "simple", + "explode": false + }, + "collectionId-vectorTiles": { + "name": "collectionId", + "in": "path", + "description": "Local identifier of a vector tile collection", + "required": true, + "allowEmptyValue": false, + "schema": { + "$ref": "#/components/schemas/vectorTiles-collections" + } + }, + "collections": { + "name": "collections", + "in": "query", + "style": "form", + "description": "The collections that should be included in the response. The parameter value is a comma-separated list of collection identifiers. If the parameters is missing, some or all collections will be included. The collection will be rendered in the order specified, with the last one showing on top, unless the priority is overridden by styling rules.", + "required": false, + "explode": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/all-collections" + } + } + }, + "collections-coverage": { + "name": "collections", + "in": "query", + "style": "form", + "description": "The collections that should be included in the response. The parameter value is a comma-separated list of collection identifiers. If the parameters is missing, some or all collections will be included. The collection will be rendered in the order specified, with the last one showing on top, unless the priority is overridden by styling rules.", + "required": false, + "explode": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/coverage-collections" + } + } + }, + "collections-vectorTiles": { + "name": "collections", + "in": "query", + "style": "form", + "description": "The collections that should be included in the response. The parameter value is a comma-separated list of collection identifiers. If the parameters is missing, some or all collections will be included. The collection will be rendered in the order specified, with the last one showing on top, unless the priority is overridden by styling rules.", + "required": false, + "explode": false, + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/vectorTiles-collections" + } + } + }, + "datetime": { + "name": "datetime", + "in": "query", + "description": "Either a date-time or an interval, half-bounded or bounded. Date and time expressions\nadhere to RFC 3339. Half-bounded intervals are expressed using double-dots.\n\nExamples:\n\n* A date-time: \"2018-02-12T23:20:50Z\"\n* A bounded interval: \"2018-02-12T00:00:00Z/2018-03-18T12:31:12Z\"\n* Half-bounded intervals: \"2018-02-12T00:00:00Z/..\" or \"../2018-03-18T12:31:12Z\"\n\nOnly features that have a temporal property that intersects the value of\n`datetime` are selected.\n\nIf a feature has multiple temporal properties, it is the decision of the\nserver whether only a single temporal property is used to determine\nthe extent or all relevant temporal properties.", + "required": false, + "schema": { + "type": "string" + }, + "style": "form", + "explode": false + }, + "subset": { + "name": "subset", + "in": "query", + "description": "Retrieve only part of the data by slicing or trimming along one or more axis\nFor trimming: {axisAbbrev}({low}:{high}) (preserves dimensionality)\n An asterisk (`*`) can be used instead of {low} or {high} to indicate the minimum/maximum value.\nFor slicing: {axisAbbrev}({value}) (reduces dimensionality)\n", + "style": "form", + "explode": false, + "required": false, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "crs": { + "name": "crs", + "in": "query", + "description": "reproject the output to the given crs", + "required": false, + "style": "form", + "explode": true, + "schema": { + "type": "string" + } + }, + "subset-crs": { + "name": "subset-crs", + "in": "query", + "description": "crs for the specified subset", + "required": false, + "style": "form", + "explode": true, + "schema": { + "type": "string" + } + }, + "tileMatrix": { + "name": "tileMatrix", + "in": "path", + "description": "Identifier selecting one of the scales defined in the TileMatrixSet and representing the scaleDenominator the tile. For example,\nIreland is fully within the Tile at WebMercatorQuad tileMatrix=5, tileRow=10 and tileCol=15.", + "required": true, + "schema": { + "type": "string" + }, + "example": "5" + }, + "tileRow": { + "name": "tileRow", + "in": "path", + "description": "Row index of the tile on the selected TileMatrix. It cannot exceed the MatrixWidth-1 for the selected TileMatrix. For example, Ireland is fully within the Tile at WebMercatorQuad tileMatrix=5, tileRow=10 and tileCol=15.", + "required": true, + "schema": { + "minimum": 0, + "type": "integer" + }, + "example": 10 + }, + "tileCol": { + "name": "tileCol", + "in": "path", + "description": "Column index of the tile on the selected TileMatrix. It cannot exceed the MatrixHeight-1 for the selected TileMatrix. For example, Ireland is fully within the Tile at WebMercatorQuad tileMatrix=5, tileRow=10 and tileCol=15.", + "required": true, + "schema": { + "minimum": 0, + "type": "integer" + }, + "example": 15 + }, + "tileMatrixSetId": { + "name": "tileMatrixSetId", + "in": "path", + "description": "Identifier for a supported TileMatrixSet", + "required": true, + "allowEmptyValue": false, + "schema": { + "$ref": "#/components/schemas/tileMatrixSets" + } + }, + "f-mapTile": { + "name": "f", + "in": "query", + "description": "The format of the map tile response (e.g. png). Accepted values are 'png', 'jpg' or 'tiff' (GeoTIFF).", + "required": false, + "schema": { + "type": "string", + "enum": ["png", "jpg", "tiff"] + }, + "style": "form", + "explode": false + }, + "f-vectorTile": { + "name": "f", + "in": "query", + "description": "The format of the vector tile response (e.g. json). Accepted values are 'mvt' (Mapbox Vector Tiles) or 'json' (GeoJSON).", + "required": false, + "schema": { + "type": "string", + "enum": ["mvt", "json"] + }, + "style": "form", + "explode": false + }, + "f-coverageTile": { + "name": "f", + "in": "query", + "description": "The format of the coverage tile response (e.g. tiff). Accepted values are 'tiff' (GeoTIFF), 'netcdf', or 'png'.", + "required": false, + "schema": { + "type": "string", + "enum": ["tiff", "netcdf", "png"] + }, + "style": "form", + "explode": false + }, + "bgcolor": { + "name": "bgcolor", + "in": "query", + "description": "Web color name or hexadecimal 0x[AA]RRGGBB color value for the background color (default to 0x9C9C9C gray). If alpha is not specified, full opacity is assumed.", + "required": false, + "style": "form", + "explode": false, + "schema": { + "type": "string", + "default": "0xFFFFFF" + } + }, + "transparent": { + "name": "transparent", + "in": "query", + "description": "Background transparency of map (default=true).", + "required": false, + "style": "form", + "explode": false, + "schema": { + "type": "boolean", + "default": true + } + }, + "styleId": { + "name": "styleId", + "in": "path", + "description": "An identifier representing a specific style.", + "required": true, + "allowEmptyValue": false, + "schema": { + "$ref": "#/components/schemas/styles" + } + }, + "styleId-collection": { + "name": "styleId", + "in": "path", + "description": "An identifier representing a specific style.", + "required": true, + "allowEmptyValue": false, + "schema": { + "type": "string" + } + } + }, + "responses": { + "NotFound": { + "description": "The requested resource does not exist on the server. For example, a path parameter had an incorrect value.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/exception" + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "NotAcceptable": { + "description": "Content negotiation failed. For example, the `Accept` header submitted in the request did not support any of the media types supported by the server for the requested resource.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/exception" + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "ServerError": { + "description": "A server error occurred.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/exception" + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "InvalidParameter": { + "description": "A query parameter has an invalid value.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/exception" + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "Exception": { + "description": "An error occured.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/exception" + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "LandingPage": { + "description": "The landing page provides links to the API definition (link relation `service-desc`, in this case path `/api`),\nto the Conformance declaration (path `/conformance`, link relation `conformance`), and to the Collections of geospatial data (path `/collections`, link relation `data`).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/landingPage" + }, + "example": { + "title": "Buildings in Bonn", + "description": "Access to data about buildings in the city of Bonn via a Web API that conforms to the OGC API Tiles specification.", + "links": [ + { + "href": "http://data.example.org/", + "rel": "self", + "type": "application/json", + "title": "this document" + }, + { + "href": "http://data.example.org/api", + "rel": "service-desc", + "type": "application/vnd.oai.openapi+json;version=3.0", + "title": "the API definition" + }, + { + "href": "http://data.example.org/api.html", + "rel": "service-doc", + "type": "text/html", + "title": "the API documentation" + }, + { + "href": "http://data.example.org/conformance", + "rel": "conformance", + "type": "application/json", + "title": "OGC API conformance classes implemented by this service" + }, + { + "href": "http://data.example.org/collections", + "rel": "data", + "type": "application/json", + "title": "Information about the collections" + } + ] + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "Conformance": { + "description": "The URIs of all conformance classes supported by the server", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/confClasses" + } + ], + "example": { + "conformsTo": [ + "http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/core", + "http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/json", + "http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/html", + "http://www.opengis.net/spec/ogcapi-common-1/1.0/conf/oas30", + "http://www.opengis.net/spec/ogcapi-common-2/1.0/conf/collections", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/core", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tileset", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tilesets-list", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/geodata-tilesets", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/dataset-tilesets", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/geodata-selection", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/jpeg", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/png", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/mvt", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/geojson", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tiff", + "http://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/netcdf" + ] + } + } + } + } + }, + "API": { + "description": "The OpenAPI definition of the API.", + "content": { + "application/vnd.oai.openapi+json;version=3.0": { + "schema": { + "type": "object" + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "Enumeration": { + "description": "An enumerated list of valid string values for API parameters.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/enumeration" + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "CollectionsList": { + "description": "The collections of (mostly geospatial) data available from this API. The dataset contains one or more collections. This resource provides information about and access to the collections. The response contains the list of collections. Each collection is accessible via one or more OGC API set of specifications, for which a link to relevant accessible resources, e.g. /collections/{collectionId}/(items, coverage, map, tiles...) is provided, with the corresponding relation type, as well as key information about the collection. This information includes:\n* a local identifier for the collection that is unique for the dataset;\n* a list of coordinate reference systems (CRS) in which data may be returned by the server. The first CRS is the default coordinate reference system (the default is always WGS 84 with axis order longitude/latitude);\n* an optional title and description for the collection;\n* an optional extent that can be used to provide an indication of the spatial and temporal extent of the collection - typically derived from the data;\n* for collections accessible via the Features or Records API, an optional indicator about the type of the items in the collection (the default value, if the indicator is not provided, is 'feature').", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/collections" + }, + "example": { + "links": [ + { + "href": "http://data.example.org/collections.json", + "rel": "self", + "type": "application/json", + "title": "this document" + }, + { + "href": "http://data.example.org/collections.html", + "rel": "alternate", + "type": "text/html", + "title": "this document as HTML" + }, + { + "href": "http://schemas.example.org/1.0/buildings.xsd", + "rel": "describedby", + "type": "application/xml", + "title": "GML application schema for Acme Corporation building data" + }, + { + "href": "http://download.example.org/buildings.gpkg", + "rel": "enclosure", + "type": "application/geopackage+sqlite3", + "title": "Bulk download (GeoPackage)", + "length": 472546 + } + ], + "collections": [ + { + "id": "buildings", + "title": "Buildings", + "description": "Buildings in the city of Bonn.", + "extent": { + "spatial": { + "bbox": [[7.01, 50.63, 7.22, 50.78]] + }, + "temporal": { + "interval": [["2010-02-15T12:34:56Z", null]] + } + }, + "links": [ + { + "href": "http://data.example.org/collections/buildings/items", + "rel": "items", + "type": "application/geo+json", + "title": "Buildings" + }, + { + "href": "http://data.example.org/collections/buildings/items.html", + "rel": "items", + "type": "text/html", + "title": "Buildings" + }, + { + "href": "https://creativecommons.org/publicdomain/zero/1.0/", + "rel": "license", + "type": "text/html", + "title": "CC0-1.0" + }, + { + "href": "https://creativecommons.org/publicdomain/zero/1.0/rdf", + "rel": "license", + "type": "application/rdf+xml", + "title": "CC0-1.0" + } + ] + } + ] + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "Collection": { + "description": "Information about a particular collection of (mostly geospatial) data available from this API. The collection is accessible via one or more OGC API set of specifications, for which a link to relevant accessible resources, e.g. /collections/{collectionId}/(items, coverage, map, tiles...) is contained in the response, with the corresponding relation type, as well as key information about the collection. This information includes:\n* a local identifier for the collection that is unique for the dataset;\n* a list of coordinate reference systems (CRS) in which data may be returned by the server. The first CRS is the default coordinate reference system (the default is always WGS 84 with axis order longitude/latitude);\n* an optional title and description for the collection;\n* an optional extent that can be used to provide an indication of the spatial and temporal extent of the collection - typically derived from the data;\n* for collections accessible via the Features or Records API, an optional indicator about the type of the items in the collection (the default value, if the indicator is not provided, is 'feature').", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/collectionInfo" + }, + "example": { + "id": "buildings", + "title": "Buildings", + "description": "Buildings in the city of Bonn.", + "extent": { + "spatial": { + "bbox": [[7.01, 50.63, 7.22, 50.78]] + }, + "temporal": { + "interval": [["2010-02-15T12:34:56Z", null]] + } + }, + "links": [ + { + "href": "http://data.example.org/collections/buildings/items", + "rel": "items", + "type": "application/geo+json", + "title": "Buildings" + }, + { + "href": "http://data.example.org/collections/buildings/items.html", + "rel": "items", + "type": "text/html", + "title": "Buildings" + }, + { + "href": "https://creativecommons.org/publicdomain/zero/1.0/", + "rel": "license", + "type": "text/html", + "title": "CC0-1.0" + }, + { + "href": "https://creativecommons.org/publicdomain/zero/1.0/rdf", + "rel": "license", + "type": "application/rdf+xml", + "title": "CC0-1.0" + } + ] + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "TileSetsList": { + "description": "List of available tilesets.", + "content": { + "application/json": { + "schema": { + "type": "object", + "required": ["tilesets"], + "properties": { + "links": { + "type": "array", + "items": { + "$ref": "#/components/schemas/link" + } + }, + "tilesets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/tileSet-item" + } + } + } + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "TileSet": { + "description": "Description of the tileset", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/tileSet" + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "MapTile": { + "description": "A map tile image returned as a response.", + "content": { + "image/png": { + "schema": { + "type": "string", + "format": "binary" + } + }, + "image/jpeg": { + "schema": { + "type": "string", + "format": "binary" + } + }, + "image/tiff; application=geotiff": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "CoverageTile": { + "description": "A coverage tile returned as a response.", + "content": { + "application/netcdf": { + "schema": { + "type": "string", + "format": "binary" + } + }, + "image/tiff; application=geotiff": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "VectorTile": { + "description": "A vector tile returned as a response.", + "content": { + "application/vnd.mapbox-vector-tile": { + "schema": { + "type": "string", + "format": "binary" + } + }, + "application/geo+json": { + "schema": { + "allOf": [ + { + "format": "geojson-feature-collection" + }, + { + "$ref": "#/components/schemas/FeatureCollection" + } + ] + } + } + } + }, + "EmptyTile": { + "description": "No data available for this tile." + }, + "TileMatrixSetsList": { + "description": "List of tile matrix sets (tiling schemes).", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "tileMatrixSets": { + "type": "array", + "items": { + "$ref": "#/components/schemas/tileMatrixSet-item" + } + } + } + } + }, + "text/html": { + "schema": { + "type": "string" + } + } + } + }, + "TileMatrixSet": { + "description": "tile matrix set", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/tileMatrixSet" + } + } + } + } + } + } +} diff --git a/poetry.lock b/poetry.lock index 95000b515..7e3eba372 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1055,6 +1055,24 @@ typing-extensions = "*" extra = ["pinyin", "romanize", "romkan"] tools = ["black", "isort", "pyupgrade"] +[[package]] +name = "jsonschema-spec" +version = "0.1.4" +description = "JSONSchema Spec with object-oriented paths" +category = "main" +optional = false +python-versions = ">=3.7.0,<4.0.0" +files = [ + {file = "jsonschema_spec-0.1.4-py3-none-any.whl", hash = "sha256:34471d8b60e1f06d174236c4d3cf9590fbf3cff1cc733b28d15cd83672bcd062"}, + {file = "jsonschema_spec-0.1.4.tar.gz", hash = "sha256:824c743197bbe2104fcc6dce114a4082bf7f7efdebf16683510cb0ec6d8d53d0"}, +] + +[package.dependencies] +jsonschema = ">=4.0.0,<4.18.0" +pathable = ">=0.4.1,<0.5.0" +PyYAML = ">=5.1" +typing-extensions = ">=4.3.0,<5.0.0" + [[package]] name = "lazy-loader" version = "0.2" @@ -1075,7 +1093,7 @@ test = ["pytest (>=7.2)", "pytest-cov (>=4.0)"] name = "lazy-object-proxy" version = "1.9.0" description = "A fast and thorough lazy object proxy." -category = "dev" +category = "main" optional = false python-versions = ">=3.7" files = [ @@ -1339,6 +1357,18 @@ files = [ {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] +[[package]] +name = "more-itertools" +version = "9.1.0" +description = "More routines for operating on iterables, beyond itertools" +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "more-itertools-9.1.0.tar.gz", hash = "sha256:cabaa341ad0389ea83c17a94566a53ae4c9d07349861ecb14dc6d0345cf9ac5d"}, + {file = "more_itertools-9.1.0-py3-none-any.whl", hash = "sha256:d2bc7f02446e86a68911e58ded76d6561eea00cddfb2a91e7019bbb586c799f3"}, +] + [[package]] name = "msal" version = "1.21.0" @@ -1566,6 +1596,77 @@ files = [ [package.dependencies] graphviz = "*" +[[package]] +name = "openapi-core" +version = "0.16.6" +description = "client-side and server-side support for the OpenAPI Specification v3" +category = "main" +optional = false +python-versions = ">=3.7.0,<4.0.0" +files = [ + {file = "openapi_core-0.16.6-py3-none-any.whl", hash = "sha256:0ce163fa5b1388cee296993fde2f1519fe6b115e3d6b2ee4eb1f5f57dc807a6c"}, + {file = "openapi_core-0.16.6.tar.gz", hash = "sha256:ed79a6adf9b0375804eac50b9b45e282b8a18fed5f6745dacc70fc1f3ea0abc3"}, +] + +[package.dependencies] +isodate = "*" +jsonschema-spec = ">=0.1.1,<0.2.0" +more-itertools = "*" +openapi-schema-validator = ">=0.3.0,<0.5" +openapi-spec-validator = ">=0.5.0,<0.6.0" +parse = "*" +pathable = ">=0.4.0,<0.5.0" +typing-extensions = ">=4.3.0,<5.0.0" +werkzeug = "*" + +[package.extras] +django = ["django (>=3.0)"] +falcon = ["falcon (>=3.0)"] +flask = ["flask"] +requests = ["requests"] + +[[package]] +name = "openapi-schema-validator" +version = "0.4.4" +description = "OpenAPI schema validation for Python" +category = "main" +optional = false +python-versions = ">=3.7.0,<4.0.0" +files = [ + {file = "openapi_schema_validator-0.4.4-py3-none-any.whl", hash = "sha256:79f37f38ef9fd5206b924ed7a6f382cea7b649b3b56383c47f1906082b7b9015"}, + {file = "openapi_schema_validator-0.4.4.tar.gz", hash = "sha256:c573e2be2c783abae56c5a1486ab716ca96e09d1c3eab56020d1dc680aa57bf8"}, +] + +[package.dependencies] +jsonschema = ">=4.0.0,<4.18.0" +rfc3339-validator = "*" + +[package.extras] +docs = ["sphinx (>=5.3.0,<6.0.0)", "sphinx-immaterial (>=0.11.0,<0.12.0)"] + +[[package]] +name = "openapi-spec-validator" +version = "0.5.6" +description = "OpenAPI 2.0 (aka Swagger) and OpenAPI 3 spec validator" +category = "main" +optional = false +python-versions = ">=3.7.0,<4.0.0" +files = [ + {file = "openapi_spec_validator-0.5.6-py3-none-any.whl", hash = "sha256:4145478f26df16059c147406eaaa59b77ff60e3461ba6edb9aa84d481ed89aaf"}, + {file = "openapi_spec_validator-0.5.6.tar.gz", hash = "sha256:1189d0618ae0678ccf6c883cc1266d381454eece6f21fcf330cc7caea5fc25eb"}, +] + +[package.dependencies] +importlib-resources = {version = ">=5.8.0,<6.0.0", markers = "python_version < \"3.9\""} +jsonschema = ">=4.0.0,<4.18.0" +jsonschema-spec = ">=0.1.1,<0.2.0" +lazy-object-proxy = ">=1.7.1,<2.0.0" +openapi-schema-validator = ">=0.4.2,<0.5.0" + +[package.extras] +docs = ["sphinx (>=5.3.0,<6.0.0)", "sphinx-immaterial (>=0.11.0,<0.12.0)"] +requests = ["requests"] + [[package]] name = "packaging" version = "23.0" @@ -1578,6 +1679,17 @@ files = [ {file = "packaging-23.0.tar.gz", hash = "sha256:b6ad297f8907de0fa2fe1ccbd26fdaf387f5f47c7275fedf8cce89f99446cf97"}, ] +[[package]] +name = "parse" +version = "1.19.0" +description = "parse() is the opposite of format()" +category = "main" +optional = false +python-versions = "*" +files = [ + {file = "parse-1.19.0.tar.gz", hash = "sha256:9ff82852bcb65d139813e2a5197627a94966245c897796760a3a2a8eb66f020b"}, +] + [[package]] name = "pastedeploy" version = "3.0.1" @@ -1595,6 +1707,18 @@ docs = ["Sphinx (>=1.7.5)", "pylons-sphinx-themes"] paste = ["Paste"] testing = ["Paste", "pytest", "pytest-cov"] +[[package]] +name = "pathable" +version = "0.4.3" +description = "Object-oriented paths" +category = "main" +optional = false +python-versions = ">=3.7.0,<4.0.0" +files = [ + {file = "pathable-0.4.3-py3-none-any.whl", hash = "sha256:cdd7b1f9d7d5c8b8d3315dbf5a86b2596053ae845f056f57d97c0eefff84da14"}, + {file = "pathable-0.4.3.tar.gz", hash = "sha256:5c869d315be50776cc8a993f3af43e0c60dc01506b399643f919034ebf4cdcab"}, +] + [[package]] name = "pbr" version = "5.11.1" @@ -2182,6 +2306,22 @@ pyramid = "*" docs = ["Sphinx (>=1.8.1)", "docutils", "pylons-sphinx-themes (>=1.0.8)", "repoze.sphinx.autointerface"] testing = ["WebTest (>=1.3.1)", "coverage", "nose"] +[[package]] +name = "pyramid-openapi3" +version = "0.16.0" +description = "Pyramid addon for OpenAPI3 validation of requests and responses." +category = "main" +optional = false +python-versions = ">=3.7,<4.0" +files = [ + {file = "pyramid_openapi3-0.16.0-py3-none-any.whl", hash = "sha256:d79c7994eb7c94df65c937c19603f0f780a8112e0e144577f75832aa0cae67b1"}, + {file = "pyramid_openapi3-0.16.0.tar.gz", hash = "sha256:f1bb0d0760664db0a498faa7189d6af0f8ffe5e2ab963c491a2ff840bf6eb6e7"}, +] + +[package.dependencies] +openapi-core = ">=0.16.1" +pyramid = ">=1.10.7" + [[package]] name = "pyramid-tm" version = "2.5" @@ -2492,6 +2632,21 @@ packaging = ">=21.3" poetry-semver = ">=0.1.0,<0.2.0" toml = ">=0.10.2,<0.11.0" +[[package]] +name = "rfc3339-validator" +version = "0.1.4" +description = "A pure python RFC3339 validator" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +files = [ + {file = "rfc3339_validator-0.1.4-py2.py3-none-any.whl", hash = "sha256:24f6ec1eda14ef823da9e36ec7113124b39c04d50a4d3d3a3c2859577e7791fa"}, + {file = "rfc3339_validator-0.1.4.tar.gz", hash = "sha256:138a2abdf93304ad60530167e51d2dfb9549521a836871b88d7f4695d0022f6b"}, +] + +[package.dependencies] +six = "*" + [[package]] name = "rich" version = "13.3.3" @@ -3366,6 +3521,24 @@ files = [ docs = ["Sphinx (>=1.7.5)", "pylons-sphinx-themes"] testing = ["coverage", "pytest (>=3.1.0)", "pytest-cov", "pytest-xdist"] +[[package]] +name = "werkzeug" +version = "2.2.3" +description = "The comprehensive WSGI web application library." +category = "main" +optional = false +python-versions = ">=3.7" +files = [ + {file = "Werkzeug-2.2.3-py3-none-any.whl", hash = "sha256:56433961bc1f12533306c624f3be5e744389ac61d722175d543e1751285da612"}, + {file = "Werkzeug-2.2.3.tar.gz", hash = "sha256:2e1ccc9417d4da358b9de6f174e3ac094391ea1d4fbef2d667865d819dfd0afe"}, +] + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog"] + [[package]] name = "wrapt" version = "1.15.0" @@ -3558,4 +3731,4 @@ test = ["zope.testing"] [metadata] lock-version = "2.0" python-versions = ">=3.8,<3.11" -content-hash = "bb72c1205582c2a9c2cf09cabf804d713028b1633be305a82cbbb5ba85b83dd0" +content-hash = "215e4f57c0286fc45f72de3620fd8ef0c6beeea669060508926de6a2561d9973" diff --git a/pyproject.toml b/pyproject.toml index f00468ab2..e16b3e715 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -72,6 +72,8 @@ jsonschema-gentypes = "1.5.0" azure-storage-blob = "12.15.0" waitress = "2.1.2" certifi = "2022.12.7" +pyramid-openapi3 = "0.16.0" +openapi-core = "0.16.6" [tool.poetry.dev-dependencies] prospector = { extras = ["with_mypy", "with_bandit", "with_pyroma"], version = "1.9.0" } diff --git a/tilecloud_chain/CONFIG.md b/tilecloud_chain/CONFIG.md index bdfb29bb2..a711a86c7 100644 --- a/tilecloud_chain/CONFIG.md +++ b/tilecloud_chain/CONFIG.md @@ -39,10 +39,14 @@ - **`headers`** _(object)_: The headers that we send to the WMS backend. Can contain additional properties. - **Additional Properties** _(string)_: The header value. - **`grid`** _(object)_: The WMTS grid definition, see https://github.com/camptocamp/tilecloud-chain/blob/master/tilecloud_chain/USAGE.rst#configure-grids. Cannot contain additional properties. + - **`title`** _(string)_: The title of the grid. + - **`description`** _(string)_: The description of the grid. + - **`keywords`** _(array)_: The keywords. + - **Items** _(string)_ - **`resolution_scale`** _(integer)_: The scale used to build a FreeTileGrid typically '2'. - **`resolutions`** _(array)_: The resolutions in pixel per meter. - **Items** _(number)_ - - **`bbox`** _(array)_: The bounding box in meter. + - **`bbox`** _(array)_: The bounding box in meter (left, bottom, right, top, of the map image) . - **Items** _(number)_ - **`srs`** _(string)_: The projection reference. - **`proj4_literal`** _(string)_: The Proj4 definition. @@ -111,6 +115,8 @@ - : Refer to _[#/definitions/cache_mbtiles](#definitions/cache_mbtiles)_. - : Refer to _[#/definitions/cache_bsddb](#definitions/cache_bsddb)_. - **`layer_title`** _(string)_: The title, use to generate the capabilities. +- **`layer_description`** _(string)_: The description, use to generate the capabilities. +- **`layer_theme`** _(string)_: Category where the layer can be grouped. - **`layer_grid`** _(string)_: The used grid name. - **`layer_bbox`** _(array)_: The bounding box where we will generate the tiles. - **Items** _(number)_ @@ -166,6 +172,8 @@ - **`layer_wms`** _(object)_: Cannot contain additional properties. - **`type`** - **`title`**: Refer to _[#/definitions/layer_title](#definitions/layer_title)_. + - **`description`**: Refer to _[#/definitions/layer_description](#definitions/layer_description)_. + - **`theme`**: Refer to _[#/definitions/layer_theme](#definitions/layer_theme)_. - **`grid`**: Refer to _[#/definitions/layer_grid](#definitions/layer_grid)_. - **`bbox`**: Refer to _[#/definitions/layer_bbox](#definitions/layer_bbox)_. - **`min_resolution_seed`**: Refer to _[#/definitions/layer_min_resolution_seed](#definitions/layer_min_resolution_seed)_. @@ -199,6 +207,8 @@ - **`layer_mapnik`** _(object)_: Cannot contain additional properties. - **`type`** - **`title`**: Refer to _[#/definitions/layer_title](#definitions/layer_title)_. + - **`description`**: Refer to _[#/definitions/layer_description](#definitions/layer_description)_. + - **`theme`**: Refer to _[#/definitions/layer_theme](#definitions/layer_theme)_. - **`grid`**: Refer to _[#/definitions/layer_grid](#definitions/layer_grid)_. - **`bbox`**: Refer to _[#/definitions/layer_bbox](#definitions/layer_bbox)_. - **`min_resolution_seed`**: Refer to _[#/definitions/layer_min_resolution_seed](#definitions/layer_min_resolution_seed)_. @@ -310,6 +320,8 @@ - **`metadata`** _(object)_: The configuration of the WMTS capabilities metadata. Cannot contain additional properties. - **`title`** _(string)_: The title. - **`abstract`** _(string)_: The abstract. + - **`attribution`** _(string)_: The attribution. + - **`license`** _(string)_: The license. - **`servicetype`** _(string)_: The service type, default is 'OGC WMTS'. Default: `"OGC WMTS"`. - **`keywords`** _(array)_: The keywords. - **Items** _(string)_ @@ -317,6 +329,7 @@ - **`access_constraints`** _(string)_: The access constraints. - **`provider`** _(object)_: The provider. Cannot contain additional properties. - **`name`** _(string)_ + - **`pointOfContact`** _(string)_: Useful information to contact the authors or custodians for the layer (e.g. e-mail address, a physical address, phone numbers, etc). - **`url`** _(string)_: The public URL. - **`contact`** _(object)_: The contact. Cannot contain additional properties. - **`name`** _(string)_ diff --git a/tilecloud_chain/__init__.py b/tilecloud_chain/__init__.py index 759672963..1eaeb582d 100644 --- a/tilecloud_chain/__init__.py +++ b/tilecloud_chain/__init__.py @@ -42,7 +42,6 @@ import botocore.client import c2cwsgiutils.setup_process import psycopg2 -import pyramid.scripts.common from c2cwsgiutils import sentry, stats from jsonschema_gentypes import validate from PIL import Image @@ -1668,17 +1667,17 @@ def get_queue_store(config: DatedConfig, daemon: bool) -> TimedTileStoreWrapper: if "redis" in config.config: # Create a Redis queue conf = config.config["redis"] - tilestore_kwargs: Dict[str, Any] = dict( - name=conf["queue"], - stop_if_empty=not daemon, - timeout=conf["timeout"], - pending_timeout=conf["pending_timeout"], - max_retries=conf["max_retries"], - max_errors_age=conf["max_errors_age"], - max_errors_nb=conf["max_errors_nb"], - connection_kwargs=conf.get("connection_kwargs", {}), - sentinel_kwargs=conf.get("sentinel_kwargs"), - ) + tilestore_kwargs: Dict[str, Any] = { + "name": conf["queue"], + "stop_if_empty": not daemon, + "timeout": conf["timeout"], + "pending_timeout": conf["pending_timeout"], + "max_retries": conf["max_retries"], + "max_errors_age": conf["max_errors_age"], + "max_errors_nb": conf["max_errors_nb"], + "connection_kwargs": conf.get("connection_kwargs", {}), + "sentinel_kwargs": conf.get("sentinel_kwargs"), + } if "socket_timeout" in conf: tilestore_kwargs["connection_kwargs"]["socket_timeout"] = conf["socket_timeout"] if "db" in conf: @@ -1708,3 +1707,8 @@ def _get_sqs_queue( sys.exit("The config hasn't any configured queue") sqs = boto3.resource("sqs", region_name=config.config["sqs"].get("region", "eu-west-1")) return sqs.get_queue_by_name(QueueName=config.config["sqs"]["queue"]) + + +def get_expires_hours(config: tilecloud_chain.DatedConfig) -> float: + """Get the expiration time in hours.""" + return config.config.get("server", {}).get("expires", tilecloud_chain.configuration.EXPIRES_DEFAULT) diff --git a/tilecloud_chain/admin/__init__.py b/tilecloud_chain/admin/__init__.py new file mode 100644 index 000000000..972f0aa7f --- /dev/null +++ b/tilecloud_chain/admin/__init__.py @@ -0,0 +1,42 @@ +# Copyright (c) 2023 by Camptocamp +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of Camptocamp nor the names of its contributors may +# be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import logging + +from pyramid.config import PHASE0_CONFIG, Configurator + +_LOG = logging.getLogger(__name__) + + +def includeme(config: Configurator) -> None: + """Include OGC API interface.""" + config.add_route("admin_slash", "/", request_method="GET") + config.add_route("admin_run", "/run", request_method="POST") + config.add_route("admin_test", "/test", request_method="GET") + + config.add_static_view(name="/static", path="/app/tilecloud_chain/static") + + config.scan("tilecloud_chain.admin.views") diff --git a/tilecloud_chain/views/admin.py b/tilecloud_chain/admin/views.py similarity index 88% rename from tilecloud_chain/views/admin.py rename to tilecloud_chain/admin/views.py index ac54c3e38..5de96b86a 100644 --- a/tilecloud_chain/views/admin.py +++ b/tilecloud_chain/admin/views.py @@ -37,13 +37,15 @@ import pyramid.httpexceptions import pyramid.request import pyramid.response -from c2cwsgiutils.auth import AuthenticationType, auth_type, auth_view, check_access, is_auth_user +import pyramid.session +from c2cwsgiutils.auth import AuthenticationType, auth_type, auth_view from pyramid.view import view_config import tilecloud_chain.server from tilecloud_chain.controller import get_status +from tilecloud_chain.server import init_tilegeneration -LOG = logging.getLogger(__name__) +_LOG = logging.getLogger(__name__) class Admin: @@ -150,7 +152,7 @@ def run(self) -> pyramid.response.Response: final_command += commands[1:] display_command = " ".join([shlex.quote(arg) for arg in final_command]) - LOG.info("Run the command `%s`", display_command) + _LOG.info("Run the command `%s`", display_command) env: Dict[str, str] = {} env.update(os.environ) env["FRONTEND"] = "noninteractive" @@ -162,7 +164,7 @@ def run(self) -> pyramid.response.Response: ) if completed_process.returncode != 0: - LOG.warning( + _LOG.warning( "The command `%s` exited with an error code: %s\nstdout:\n%s\nstderr:\n%s", display_command, completed_process.returncode, @@ -195,29 +197,6 @@ def run(self) -> pyramid.response.Response: "returncode": completed_process.returncode, } - @view_config(route_name="admin_test", renderer="tilecloud_chain:templates/openlayers.html") # type: ignore - def admin_test(self) -> Dict[str, Any]: - assert self.gene - config = self.gene.get_host_config(self.request.host) - main_config = self.gene.get_main_config() - return { - "proj4js_def": re.sub( - r"\s+", - " ", - config.config["openlayers"]["proj4js_def"], - ), - "srs": config.config["openlayers"]["srs"], - "center_x": config.config["openlayers"]["center_x"], - "center_y": config.config["openlayers"]["center_y"], - "zoom": config.config["openlayers"]["zoom"], - "http_url": urljoin( - self.request.current_route_url(), - "/" + main_config.config["server"].get("wmts_path", "wmts") + "/" - if "server" in config.config - else "/", - ), - } - def _format_output(string: str, max_length: int = 1000) -> str: result = "" @@ -261,3 +240,29 @@ def _format_output(string: str, max_length: int = 1000) -> str: if len(string) > max_length: return string[: max_length - 3] + "\n..." return string + + +@view_config(route_name="admin_test", renderer="tilecloud_chain:templates/openlayers.html") # type: ignore +def landing_page(request: pyramid.request.Request) -> pyramid.response.Response: + """Get the landing page.""" + + tilegeneration = init_tilegeneration(request.registry.settings.get("tilegeneration_configfile")) + config = tilegeneration.get_host_config(request.host) + main_config = tilegeneration.get_main_config() + return { + "proj4js_def": re.sub( + r"\s+", + " ", + config.config["openlayers"]["proj4js_def"], + ), + "srs": config.config["openlayers"]["srs"], + "center_x": config.config["openlayers"]["center_x"], + "center_y": config.config["openlayers"]["center_y"], + "zoom": config.config["openlayers"]["zoom"], + "http_url": urljoin( + request.current_route_url(), + "/" + main_config.config["server"].get("wmts_path", "wmts") + "/" + if "server" in config.config + else "/", + ), + } diff --git a/tilecloud_chain/configuration.py b/tilecloud_chain/configuration.py index d13e54c88..a4b37a3ff 100644 --- a/tilecloud_chain/configuration.py +++ b/tilecloud_chain/configuration.py @@ -619,6 +619,27 @@ class Grid(TypedDict, total=False): The WMTS grid definition, see https://github.com/camptocamp/tilecloud-chain/blob/master/tilecloud_chain/USAGE.rst#configure-grids """ + title: str + """ + Title. + + The title of the grid + """ + + description: str + """ + Description. + + The description of the grid + """ + + keywords: List[str] + """ + Keywords. + + The keywords + """ + resolution_scale: int """ Resolution scale. @@ -639,7 +660,7 @@ class Grid(TypedDict, total=False): """ Bounding box. - The bounding box in meter + The bounding box in meter (left, bottom, right, top, of the map image) minLength: 4 maxLength: 4 @@ -784,6 +805,14 @@ class LayerCost(TypedDict, total=False): """ +LayerDescription = str +""" +Layer description. + +The description, use to generate the capabilities +""" + + LayerDimensionName = str r""" Layer dimension name. @@ -919,6 +948,8 @@ class LayerMapnik(TypedDict, total=False): type: Literal["mapnik"] title: "LayerTitle" + description: "LayerDescription" + theme: "LayerTheme" grid: Required["LayerGrid"] """Required property""" @@ -1066,6 +1097,14 @@ class LayerMapnik(TypedDict, total=False): """ +LayerTheme = str +""" +Layer theme. + +Category where the layer can be grouped +""" + + LayerTitle = str """ Layer title. @@ -1079,6 +1118,8 @@ class LayerWms(TypedDict, total=False): type: Literal["wms"] title: "LayerTitle" + description: "LayerDescription" + theme: "LayerTheme" grid: Required["LayerGrid"] """Required property""" @@ -1242,6 +1283,20 @@ class Metadata(TypedDict, total=False): The abstract """ + attribution: str + """ + Attribution. + + The attribution + """ + + license: str + """ + License. + + The license + """ + servicetype: str """ Service type. @@ -1411,6 +1466,13 @@ class Provider(TypedDict, total=False): name: str """Name.""" + pointOfContact: str + """ + Point of contact. + + Useful information to contact the authors or custodians for the layer (e.g. e-mail address, a physical address, phone numbers, etc) + """ + url: str """ URL. diff --git a/tilecloud_chain/expiretiles.py b/tilecloud_chain/expiretiles.py index 587dfcaad..a1037c6ac 100644 --- a/tilecloud_chain/expiretiles.py +++ b/tilecloud_chain/expiretiles.py @@ -90,7 +90,11 @@ def main() -> None: {"schema": options.schema, "table": options.table}, ) if cursor.fetchone()[0] == 0: - cursor.execute(f'CREATE TABLE IF NOT EXISTS "{options.schema}"."{options.table}" (id serial)') + cursor.execute( + psycopg2.sql.SQL("CREATE TABLE IF NOT EXISTS {}.{} (id serial)").format( + psycopg2.sql.Identifier(options.schema), psycopg2.sql.Identifier(options.table) + ) + ) cursor.execute( "SELECT AddGeometryColumn(%(schema)s, %(table)s, %(column)s, %(srid)s, 'MULTIPOLYGON', 2)", { @@ -134,13 +138,42 @@ def main() -> None: if options.simplify > 0: geom.simplify(options.simplify) - sql_geom = f"ST_GeomFromText('{geom.wkt}', 3857)" if options.srid <= 0: - sql_geom = f"ST_GeomFromText('{geom.wkt}')" + cursor.execute( + psycopg2.sql.SQL("INSERT INTO {} ({}) VALUES (ST_GeomFromText(%(geom)s))").format( + psycopg2.sql.Identifier(options.table), + psycopg2.sql.Identifier(options.column), + ), + { + "geom": geom.wkt, + }, + ) + elif options.srid != 3857: - sql_geom = f"ST_Transform({sql_geom}, {options.srid})" + cursor.execute( + psycopg2.sql.SQL( + "INSERT INTO {} ({}) VALUES (ST_Transform(ST_GeomFromText(%(geom)s, 3857), %(srid)s))" + ).format( + psycopg2.sql.Identifier(options.table), + psycopg2.sql.Identifier(options.column), + ), + { + "geom": geom.wkt, + "srid": options.srid, + }, + ) + else: + cursor.execute( + psycopg2.sql.SQL("INSERT INTO {} ({}) VALUES (ST_GeomFromText(%(geom)s, 3857))").format( + psycopg2.sql.Identifier(options.table), + psycopg2.sql.Identifier(options.column), + ), + { + "geom": geom.wkt, + "srid": options.srid, + }, + ) - cursor.execute(f'INSERT INTO "{options.table}" ("{options.column}") VALUES ({sql_geom})') connection.commit() cursor.close() connection.close() diff --git a/tilecloud_chain/internal_mapcache.py b/tilecloud_chain/internal_mapcache.py index 7fb6de137..bd86beadf 100644 --- a/tilecloud_chain/internal_mapcache.py +++ b/tilecloud_chain/internal_mapcache.py @@ -12,14 +12,14 @@ import tilecloud_chain.configuration from tilecloud import Tile, TileCoord, TileStore -from tilecloud_chain import Run +from tilecloud_chain import Run, get_expires_hours from tilecloud_chain.generate import Generate if TYPE_CHECKING: from tilecloud_chain.server import Server MAX_GENERATION_TIME = 60 -LOG = logging.getLogger(__name__) +_LOG = logging.getLogger(__name__) lock = threading.Lock() executing_lock = threading.Lock() _generator = None @@ -69,17 +69,17 @@ def get_one(self, tile: Tile) -> Optional[Tile]: key = self._get_key(tile) data = self._slave.get(key) if data is None: - LOG.debug("Tile not found: %s/%s", tile.metadata["layer"], tile.tilecoord) + _LOG.debug("Tile not found: %s/%s", tile.metadata["layer"], tile.tilecoord) return None _decode_tile(data, tile) - LOG.debug("Tile found: %s/%s", tile.metadata["layer"], tile.tilecoord) + _LOG.debug("Tile found: %s/%s", tile.metadata["layer"], tile.tilecoord) return tile def put_one(self, tile: Tile) -> Tile: """See in superclass.""" key = self._get_key(tile) self._master.set(key, _encode_tile(tile), ex=self._expiration) - LOG.info("Tile saved: %s/%s", tile.metadata["layer"], tile.tilecoord) + _LOG.info("Tile saved: %s/%s", tile.metadata["layer"], tile.tilecoord) return tile def delete_one(self, tile: Tile) -> Tile: @@ -204,7 +204,7 @@ def fetch( generator.compute_tile(meta_tile) if meta_tile.error: - LOG.error("Tile '%s' in error: %s", meta_tile.tilecoord, meta_tile.error) + _LOG.error("Tile '%s' in error: %s", meta_tile.tilecoord, meta_tile.error) return server.error(config, 500, "Error while generate the tile, see logs for details") # Don't fetch the just generated tile @@ -212,7 +212,7 @@ def fetch( try: fetched_tile = tiles[tile.tilecoord] except KeyError: - LOG.exception( + _LOG.exception( "Try to get the tile '%s', from the available: '%s'", tile.tilecoord, ", ".join([str(e) for e in tiles.keys()]), @@ -221,9 +221,9 @@ def fetch( response_headers = { "Expires": ( - datetime.datetime.utcnow() + datetime.timedelta(hours=server.get_expires_hours(config)) + datetime.datetime.utcnow() + datetime.timedelta(hours=get_expires_hours(config)) ).isoformat(), - "Cache-Control": f"max-age={3600 * server.get_expires_hours(config)}", + "Cache-Control": f"max-age={3600 * get_expires_hours(config)}", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET", "Tile-Backend": backend, diff --git a/tilecloud_chain/ogcapi/__init__.py b/tilecloud_chain/ogcapi/__init__.py new file mode 100644 index 000000000..f83649161 --- /dev/null +++ b/tilecloud_chain/ogcapi/__init__.py @@ -0,0 +1,86 @@ +# Copyright (c) 2023 by Camptocamp +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of Camptocamp nor the names of its contributors may +# be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import logging + +import pyramid.config +from pyramid.config import PHASE0_CONFIG, Configurator + +_LOG = logging.getLogger(__name__) + + +def includeme(config: Configurator) -> None: + """Include OGC API interface.""" + + config.include("pyramid_openapi3") + apiname = "ogcapi_tiles" + config.pyramid_openapi3_spec("/app/ogcapi-tiles-bundled-filtered.json", apiname=apiname) + config.pyramid_openapi3_add_explorer(apiname=apiname) + + def add_routes() -> None: + class OgcType: + def __init__(self, val: str, config: pyramid.config.Configurator): + del config + self.val = val + + def phash(self): + return f"ogc_type = {self.val}" + + def __call__(self, context, request): + if request.params.get("f") in ["html", "json"]: + _LOG.error(request.params["f"].lower() == self.val) + return request.params["f"].lower() == self.val + _LOG.error(dict(request.headers)) + if request.headers.get("Accept", "*/*") == "*/*": + return self.val == "json" + return request.accept.best_match(["text/html", "application/json"]).split("/")[1] == self.val + + config.add_route_predicate("ogc_type", OgcType) + spec = config.registry.settings[apiname]["spec"] + for pattern in spec["paths"].keys(): + if pattern == "/": + config.add_route( + "landing_page_html", + pattern, + request_method="GET", + ogc_type="html", + ) + config.add_route( + "landing_page_json", + pattern, + request_method="GET", + ogc_type="json", + ) + else: + config.add_route( + pattern.lstrip("/").replace("/", "_").replace("{", "").replace("}", "").replace("-", "_"), + pattern, + request_method="GET", + ) + + config.action((f"{apiname}_routes",), add_routes, order=PHASE0_CONFIG) + + config.scan("tilecloud_chain.ogcapi.views") diff --git a/tilecloud_chain/ogcapi/views.py b/tilecloud_chain/ogcapi/views.py new file mode 100644 index 000000000..5cd4117a5 --- /dev/null +++ b/tilecloud_chain/ogcapi/views.py @@ -0,0 +1,603 @@ +# Copyright (c) 2023 by Camptocamp +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of Camptocamp nor the names of its contributors may +# be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +import datetime +import logging +import math +import re +from typing import Any, Dict, List, Tuple, Union, cast +from urllib.parse import urljoin + +import pyproj +import pyramid.httpexceptions +import pyramid.request +import pyramid.response +import pyramid.session +from pyramid.view import view_config + +import tilecloud_chain.configuration +from tilecloud import Tile, TileCoord +from tilecloud_chain import get_expires_hours, internal_mapcache +from tilecloud_chain.server import PyramidServer, get_max_zoom_seed, init_tilegeneration + +_LOG = logging.getLogger(__name__) + +_JSON = Union[int, float, str, None, List["_JSON"], Dict[str, "_JSON"]] +_JSONDict = Dict[str, _JSON] + + +def _copy(base: _JSONDict, sources: List[Tuple[Any, str, str]]) -> _JSONDict: + for source_, source_key, dest_key in sources: + source = cast(_JSONDict, source_) + if source_key in source: + base[dest_key] = source[source_key] + return base + + +@view_config(route_name="landing_page_html", renderer="tilecloud_chain:templates/openlayers.html") # type: ignore +def landing_page_html(request: pyramid.request.Request) -> _JSON: + """Get the landing page.""" + + tilegeneration = init_tilegeneration(request.registry.settings.get("tilegeneration_configfile")) + config = tilegeneration.get_host_config(request.host) + main_config = tilegeneration.get_main_config() + + return { + "proj4js_def": re.sub( + r"\s+", + " ", + config.config["openlayers"]["proj4js_def"], + ), + "srs": config.config["openlayers"]["srs"], + "center_x": config.config["openlayers"]["center_x"], + "center_y": config.config["openlayers"]["center_y"], + "zoom": config.config["openlayers"]["zoom"], + "http_url": urljoin( + request.current_route_url(), + "/" + main_config.config["server"].get("wmts_path", "wmts") + "/" + if "server" in config.config + else "/", + ), + } + + +@view_config(route_name="landing_page_json", renderer="fast_json") # type: ignore +def landing_page_json(request: pyramid.request.Request) -> _JSON: + """Get the landing page.""" + + return { + "title": "TileCloud-chain", + "description": "Access to the tiles via a Web API that conforms to the OGC API Tiles specification.", + "links": [ + { + "href": request.current_route_url(), + "rel": "self", + "type": "application/json", + "title": "this document", + }, + { + "href": request.route_url("api"), + "rel": "service-desc", + "type": "application/vnd.oai.openapi+json;version=3.0", + "title": "the API definition", + }, + { + "href": request.route_url("api"), + "rel": "service-doc", + "type": "text/html", + "title": "the API documentation", + }, + { + "href": request.route_url("conformance"), + "rel": "conformance", + "type": "application/json", + "title": "OGC API conformance classes implemented by this service", + }, + { + "href": request.route_url("collections"), + "rel": "data", + "type": "application/json", + "title": "Information about the collections", + }, + ], + } + + +@view_config(route_name="conformance", openapi=True, renderer="fast_json") # type: ignore +def conformance(request: pyramid.request.Request) -> _JSON: + """Retrieve information about the server implementation.""" + del request + + return { + "conformsTo": [ + "https://www.opengis.net/spec/ogcapi-common-1/1.0/conf/core", + "https://www.opengis.net/spec/ogcapi-common-1/1.0/conf/json", + "https://www.opengis.net/spec/ogcapi-common-1/1.0/conf/html", + "https://www.opengis.net/spec/ogcapi-common-1/1.0/conf/oas30", + # "https://www.opengis.net/spec/ogcapi-common-2/1.0/conf/collections", + "https://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/core", + "https://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tileset", + "https://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tilesets-list", + # "https://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/geodata-tilesets", + "https://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/dataset-tilesets", + # "https://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/geodata-selection", + "https://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/jpeg", + "https://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/png", + # "https://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/mvt", + # "https://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/geojson", + # "https://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/tiff", + # "https://www.opengis.net/spec/ogcapi-tiles-1/1.0/conf/netcdf" + ] + } + + +@view_config(route_name="api", openapi=True, renderer="fast_json") # type: ignore +def api(request: pyramid.request.Request) -> _JSON: + """Retrieve information about the API.""" + del request + return {} + + +@view_config(route_name="api_all_collections", openapi=True, renderer="fast_json") # type: ignore +def api_all_collections(request: pyramid.request.Request) -> _JSON: + """Retrieve information about all collections.""" + + tilegeneration = init_tilegeneration(request.registry.settings.get("tilegeneration_configfile")) + config = tilegeneration.get_host_config(request.host).config + return {"type": "enum", "enum": list(config.get("layers", {}).keys())} + + +@view_config(route_name="api_coverage_collections", openapi=True, renderer="fast_json") # type: ignore +def api_coverage_collections(request: pyramid.request.Request) -> _JSON: + """Retrieve information about all coverage collections.""" + del request + + return {"type": "enum", "enum": []} + + +@view_config(route_name="api_vectorTiles_collections", openapi=True, renderer="fast_json") # type: ignore +def api_vector_tiles_collections(request: pyramid.request.Request) -> _JSON: + """Retrieve information about all vector tiles collections.""" + del request + + return {"type": "enum", "enum": []} + + +@view_config(route_name="api_tileMatrixSets", openapi=True, renderer="fast_json") # type: ignore +def api_tile_matrix_sets(request: pyramid.request.Request) -> _JSON: + """Retrieve information about all tile matrix sets.""" + + tilegeneration = init_tilegeneration(request.registry.settings.get("tilegeneration_configfile")) + config = tilegeneration.get_host_config(request.host).config + return {"type": "enum", "enum": list(config.get("grids", {}).keys())} + + +@view_config(route_name="api_styles", openapi=True, renderer="fast_json") # type: ignore +def api_styles(request: pyramid.request.Request) -> _JSON: + """Retrieve information about all styles.""" + + return {"type": "enum", "enum": []} + + +def _collections_collection_id(config: tilecloud_chain.configuration.Configuration, name: str) -> _JSON: + layer = config.get("layers", {}).get(name) + assert layer is not None + matrix_set = config.get("grids", {}).get(layer["grid"]) + assert matrix_set is not None + + return _copy( + { + "id": name, + "extent": {"spatial": {"bbox": [_wgs84_bbox(_proj(matrix_set["srs"]), matrix_set["bbox"])]}}, + "grid": [{"resolution": resolution} for resolution in matrix_set["resolutions"]], + "links": [], + }, + [ + (layer, "title", "title"), + (layer, "description", "description"), + ], + ) + + +@view_config(route_name="collections", openapi=True, renderer="fast_json") # type: ignore +def collections(request: pyramid.request.Request) -> _JSON: + """Retrieve information about all collections.""" + + tilegeneration = init_tilegeneration(request.registry.settings.get("tilegeneration_configfile")) + config = tilegeneration.get_host_config(request.host).config + return { + "links": [ + { + "href": request.current_route_url(), + "rel": "self", + "type": "application/json", + "title": "this document", + }, + { + "href": request.route_url("collections"), + "rel": "alternate", + "type": "text/html", + "title": "this document as HTML", + }, + ], + "collections": [_collections_collection_id(config, name) for name in config.get("layers", {}).keys()], + } + + +@view_config(route_name="collections_collectionId", openapi=True, renderer="fast_json") # type: ignore +def collections_collection_id(request: pyramid.request.Request) -> _JSON: + """Retrieve information about a collection.""" + + tilegeneration = init_tilegeneration(request.registry.settings.get("tilegeneration_configfile")) + config = tilegeneration.get_host_config(request.host).config + + return _collections_collection_id(config, request.matchdict["collectionId"]) + + +def _tile_matrix_set_id(name: str, matrix_set: tilecloud_chain.configuration.Grid) -> _JSON: + return _copy( + { + "id": name, + "crs": matrix_set["srs"], + "links": [], + }, + [(matrix_set, "title", "title")], + ) + + +@view_config(route_name="tileMatrixSets", openapi=True, renderer="fast_json") # type: ignore +def tile_matrix_sets(request: pyramid.request.Request) -> _JSON: + """Retrieve information about all tile matrix sets.""" + + tilegeneration = init_tilegeneration(request.registry.settings.get("tilegeneration_configfile")) + config = tilegeneration.get_host_config(request.host).config + return { + "tileMatrixSets": [ + _tile_matrix_set_id(name, matrix_set) for name, matrix_set in config.get("grids", {}).items() + ] + } + + +def _proj(crs: str) -> pyproj.CRS: + return pyproj.CRS.from_string(crs) + + +def _lower_left(proj: pyproj.CRS, bbox: List[float]) -> List[float]: + if proj.axis_info[0].direction == "east": + return [bbox[0], bbox[1]] + else: + return [bbox[1], bbox[0]] + + +def _upper_right(proj: pyproj.CRS, bbox: List[float]) -> List[float]: + if proj.axis_info[0].direction == "east": + return [bbox[2], bbox[3]] + else: + return [bbox[3], bbox[2]] + + +def _top_left(proj: pyproj.CRS, bbox: List[float]) -> List[float]: + if proj.axis_info[0].direction == "east": + return [bbox[0], bbox[3]] + else: + return [bbox[3], bbox[0]] + + +def _ordered_axes(proj: pyproj.CRS) -> List[str]: + if proj.axis_info[0].direction == "east": + return ["easting", "northing"] + else: + return ["northing", "easting"] + + +def _bbox(proj: pyproj.CRS, bbox: List[Union[int, float]]) -> List[Union[int, float]]: + return bbox if proj.axis_info[0].direction == "east" else [bbox[1], bbox[0], bbox[3], bbox[2]] + + +def _wgs84_bbox(proj: pyproj.CRS, bbox: List[Union[int, float]]) -> List[Union[int, float]]: + bbox = _bbox(proj, bbox) + + transformer = pyproj.Transformer.from_crs(proj, pyproj.CRS.from_epsg(4326)) + return [*transformer.transform(bbox[0], bbox[1]), *transformer.transform(bbox[2], bbox[3])] + + +@view_config(route_name="tileMatrixSets_tileMatrixSetId", openapi=True, renderer="fast_json") # type: ignore +def tile_matrix_set_id(request: pyramid.request.Request) -> _JSON: + """Retrieve information about the tile matrix set.""" + + tilegeneration = init_tilegeneration(request.registry.settings.get("tilegeneration_configfile")) + config = tilegeneration.get_host_config(request.host).config + matrix_set = config.get("grids", {}).get(request.matchdict["tileMatrixSetId"]) + assert matrix_set is not None + proj = _proj(matrix_set["srs"]) + + return _copy( + { + "id": request.matchdict["tileMatrixSetId"], + # "uri": "string", + "orderedAxes": _ordered_axes(proj), + "crs": matrix_set["srs"], + # "wellKnownScaleSet": "string", + "boundingBox": { + "lowerLeft": _lower_left(proj, matrix_set["bbox"]), + "upperRight": _upper_right(proj, matrix_set["bbox"]), + "crs": matrix_set["srs"], + "orderedAxes": _ordered_axes(proj), + }, + "tileMatrices": [ + { + # "title": "string", + # "description": "string", + # "keywords": ["string"], + "id": str(pos), + "scaleDenominator": resolution / 0.00028, + "cellSize": 0, # What's that? + "cornerOfOrigin": "topLeft", + "pointOfOrigin": _top_left(proj, matrix_set["bbox"]), + "tileWidth": matrix_set["tile_size"], + "tileHeight": matrix_set["tile_size"], + "matrixHeight": int( + math.ceil( + (matrix_set["bbox"][2] - matrix_set["bbox"][0]) + / resolution + / matrix_set["tile_size"] + ) + ), + "matrixWidth": int( + math.ceil( + (matrix_set["bbox"][3] - matrix_set["bbox"][1]) + / resolution + / matrix_set["tile_size"] + ) + ), + # "variableMatrixWidths": [{"coalesce": 2, "minTileRow": 0, "maxTileRow": 0}], + } + for pos, resolution in enumerate(matrix_set["resolutions"]) + ], + }, + [ + (matrix_set, "title", "title"), + (matrix_set, "description", "description"), + (matrix_set, "keywords", "keywords"), + ], + ) + + +@view_config(route_name="collections_collectionId_map_tiles", openapi=True, renderer="fast_json") # type: ignore +def tiles_collection(request: pyramid.request.Request) -> _JSON: + """Retrieve the list of all default map tile sets for the whole dataset.""" + + tilegeneration = init_tilegeneration(request.registry.settings.get("tilegeneration_configfile")) + tilegeneration = init_tilegeneration(request.registry.settings.get("tilegeneration_configfile")) + config = tilegeneration.get_host_config(request.host).config + layer = config.get("layers", {}).get(request.matchdict["collectionId"]) + assert layer is not None + matrix_set = config.get("grids", {}).get(layer["grid"]) + assert matrix_set is not None + + return { + "tilesets": [ + { + "title": layer.get("grid"), + "dataType": "tiles", + "crs": matrix_set.get("srs", ""), + "tileMatrixSetURI": layer.get("grid"), + "links": [], + } + ] + } + + +@view_config( + route_name="collections_collectionId_map_tiles_tileMatrixSetId", openapi=True, renderer="fast_json" +) # type: ignore +def tiles_collection_tile_matrix_set_id(request: pyramid.request.Request) -> _JSON: + """Retrieve a default map tile set of the whole dataset for the specified tiling scheme (tile matrix set).""" + + tilegeneration = init_tilegeneration(request.registry.settings.get("tilegeneration_configfile")) + config = tilegeneration.get_host_config(request.host).config + layer = config.get("layers", {}).get(request.matchdict["collectionId"]) + assert layer is not None + matrix_set = config.get("grids", {}).get(request.matchdict["tileMatrixSetId"]) + assert matrix_set is not None + metadata = config.get("metadata", {}) + provider = config.get("provider", {}) + proj = _proj(matrix_set["srs"]) + + return _copy( + { + "title": layer.get("title", ""), + "description": layer.get("description", ""), + "dataType": "map", + "crs": matrix_set.get("srs", ""), + "tileMatrixSetURI": request.matchdict["tileMatrixSetId"], + # "tileMatrixSetLimits": [ + # { + # "tileMatrix": request.matchdict["tileMatrixSetId"], + # "minTileRow": 0, + # "maxTileRow": 0, + # "minTileCol": 0, + # "maxTileCol": 0, + # } + # ], + # "epoch": 0, + "layers": [ + _copy( + { + "id": request.matchdict["collectionId"], + "dataType": "map", + "crs": matrix_set["srs"], + # epoch": 0, + "boundingBox": { + "lowerLeft": _lower_left(proj, matrix_set["bbox"]), + "upperRight": _upper_right(proj, matrix_set["bbox"]), + "crs": matrix_set["srs"], + "orderedAxes": _ordered_axes(proj), + }, + # "style": layer["wmts_style"], + # "geoDataClasses": ["map"], + # "propertiesSchema": { + # "type": "object", + # "properties": {}, + # }, + }, + [ + (layer, "title", "title"), + (layer, "description", "description"), + # (metadata, "keywords", "keywords"), + (metadata, "attribution", "attribution"), + (metadata, "license", "license"), + (provider, "pointOfContact", "pointOfContact"), + (provider, "name", "publisher"), + (layer, "theme", "theme"), + (layer, "created", "created"), + (layer, "updated", "updated"), + ], + ) + ], + "boundingBox": { + "lowerLeft": _lower_left(proj, matrix_set["bbox"]), + "upperRight": _upper_right(proj, matrix_set["bbox"]), + "crs": matrix_set["srs"], + "orderedAxes": _ordered_axes(proj), + }, + "centerPoint": { + "coordinates": _bbox(proj, matrix_set["bbox"]), + "crs": matrix_set["srs"], + "tileMatrix": request.matchdict["tileMatrixSetId"], + # "scaleDenominator": ?, + # "cellSize": ?, + }, + "style": { + "id": layer["wmts_style"], + # "title": "", + # "description": "", + # "keywords": [], + }, + "mediaTypes": [layer["mime_type"]], + "links": [] + # "accessConstraints": "unclassified", + }, + [ + (metadata, "attribution", "attribution"), + (metadata, "license", "license"), + (metadata, "keywords", "keywords"), + (layer, "version", "version"), + (layer, "created", "created"), + (layer, "updated", "updated"), + (provider, "pointOfContact", "pointOfContact"), + ], + ) + + +_SERVER = PyramidServer() + + +@view_config( # type: ignore + route_name="collections_collectionId_map_tiles_tileMatrixSetId_tileMatrix_tileRow_tileCol", openapi=True +) +def tiles_collection_tile_matrix_set_id_row_col( + request: pyramid.request.Request, +) -> pyramid.response.Response: + """Retrieve a default map tile of the whole dataset.""" + + tilegeneration = init_tilegeneration(request.registry.settings.get("tilegeneration_configfile")) + config = tilegeneration.get_host_config(request.host) + layer = config.config.get("layers", {}).get(request.matchdict["collectionId"]) + assert layer is not None + + kwargs = {"host": request.host, "request": request} + tile = Tile( + TileCoord( + # TODO fix for matrix_identifier = resolution + int(request.matchdict["tileMatrix"]), + int(request.matchdict["tileCol"]), + int(request.matchdict["tileRow"]), + ), + metadata={}, + ) + + if tile.tilecoord.z > get_max_zoom_seed(config, request.matchdict["collectionId"]): + return internal_mapcache.fetch(config, _SERVER, tilegeneration, layer, tile, kwargs) + + layer_filter = _SERVER.get_filter(config, request.matchdict["collectionId"]) + if layer_filter: + meta_size = layer["meta_size"] + meta_tilecoord = ( + TileCoord( + # TODO fix for matrix_identifier = resolution + tile.tilecoord.z, + round(tile.tilecoord.x / meta_size * meta_size), + round(tile.tilecoord.y / meta_size * meta_size), + meta_size, + ) + if meta_size != 1 + else tile.tilecoord + ) + if not layer_filter.filter_tilecoord(config, meta_tilecoord, request.matchdict["collectionId"]): + return internal_mapcache.fetch(config, _SERVER, tilegeneration, layer, tile, kwargs) + + store = _SERVER.get_store(config, request.matchdict["collectionId"]) + if store is None: + layer_name = request.matchdict["collectionId"] + return pyramid.httpexceptions.HTTPBadRequest(f"No store found for layer '{layer_name}'") + + tile2 = store.get_one(tile) + if tile2: + if tile2.error: + return pyramid.httpexceptions.HTTPInternalServerError(tile2.error) + + assert tile2.data + assert tile2.content_type + request.response.headers.update( + { + "Content-Type": tile2.content_type, + "Expires": ( + datetime.datetime.utcnow() + datetime.timedelta(hours=get_expires_hours(config)) + ).isoformat(), + "Cache-Control": f"max-age={3600 * get_expires_hours(config)}", + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET", + "Tile-Backend": "Cache", + } + ) + if isinstance(tile2.data, memoryview): + request.response.body_file = tile2.data + else: + request.response.body = tile2.data + return request.response + else: + headers = { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET", + "Expires": ( + datetime.datetime.utcnow() + datetime.timedelta(hours=get_expires_hours(config)) + ).isoformat(), + "Cache-Control": f"max-age={3600 * get_expires_hours(config)}", + } + + return pyramid.httpexceptions.HTTPNoContent(headers=headers) diff --git a/tilecloud_chain/schema.json b/tilecloud_chain/schema.json index 2c9e53f09..e039455cf 100644 --- a/tilecloud_chain/schema.json +++ b/tilecloud_chain/schema.json @@ -20,6 +20,24 @@ "type": "object", "additionalProperties": false, "properties": { + "title": { + "title": "Title", + "description": "The title of the grid", + "type": "string" + }, + "description": { + "title": "Description", + "description": "The description of the grid", + "type": "string" + }, + "keywords": { + "title": "Keywords", + "description": "The keywords", + "type": "array", + "items": { + "type": "string" + } + }, "resolution_scale": { "title": "Resolution scale", "description": "The scale used to build a FreeTileGrid typically '2'", @@ -35,7 +53,7 @@ }, "bbox": { "title": "Bounding box", - "description": "The bounding box in meter", + "description": "The bounding box in meter (left, bottom, right, top, of the map image) ", "type": "array", "items": { "type": "number" @@ -248,6 +266,16 @@ "description": "The title, use to generate the capabilities", "type": "string" }, + "layer_description": { + "title": "Layer description", + "description": "The description, use to generate the capabilities", + "type": "string" + }, + "layer_theme": { + "title": "Layer theme", + "description": "Category where the layer can be grouped", + "type": "string" + }, "layer_grid": { "title": "Layer grid", "description": "The used grid name", @@ -543,6 +571,12 @@ "title": { "$ref": "#/definitions/layer_title" }, + "description": { + "$ref": "#/definitions/layer_description" + }, + "theme": { + "$ref": "#/definitions/layer_theme" + }, "grid": { "$ref": "#/definitions/layer_grid" }, @@ -660,6 +694,12 @@ "title": { "$ref": "#/definitions/layer_title" }, + "description": { + "$ref": "#/definitions/layer_description" + }, + "theme": { + "$ref": "#/definitions/layer_theme" + }, "grid": { "$ref": "#/definitions/layer_grid" }, @@ -1196,6 +1236,16 @@ "description": "The abstract", "type": "string" }, + "attribution": { + "title": "Attribution", + "description": "The attribution", + "type": "string" + }, + "license": { + "title": "License", + "description": "The license", + "type": "string" + }, "servicetype": { "title": "Service type", "description": "The service type, default is 'OGC WMTS'", @@ -1233,6 +1283,11 @@ "title": "Name", "type": "string" }, + "pointOfContact": { + "title": "Point of contact", + "type": "string", + "description": "Useful information to contact the authors or custodians for the layer (e.g. e-mail address, a physical address, phone numbers, etc)" + }, "url": { "title": "URL", "description": "The public URL", diff --git a/tilecloud_chain/server.py b/tilecloud_chain/server.py index 6ef602524..14f110e69 100644 --- a/tilecloud_chain/server.py +++ b/tilecloud_chain/server.py @@ -41,17 +41,19 @@ import requests from azure.core.exceptions import ResourceNotFoundError from c2cwsgiutils import health_check -from pyramid.config import Configurator +from pyramid.config import PHASE0_CONFIG, Configurator from pyramid.httpexceptions import HTTPException, exception_response from pyramid.request import Request from pyramid.router import Router from pyramid_mako import add_mako_renderer import tilecloud.store.s3 +import tilecloud_chain.admin import tilecloud_chain.configuration +import tilecloud_chain.ogcapi import tilecloud_chain.security from tilecloud import Tile, TileCoord -from tilecloud_chain import TileGeneration, controller, internal_mapcache +from tilecloud_chain import TileGeneration, controller, get_expires_hours, internal_mapcache from tilecloud_chain.controller import get_azure_client logger = logging.getLogger(__name__) @@ -59,7 +61,7 @@ tilegeneration = None -def init_tilegeneration(config_file: Optional[str]) -> None: +def init_tilegeneration(config_file: Optional[str]) -> TileGeneration: """Initialize the tile generation.""" global tilegeneration # pylint: disable=global-statement if tilegeneration is None: @@ -87,6 +89,7 @@ def init_tilegeneration(config_file: Optional[str]) -> None: multi_thread=False, maxconsecutive_errors=False, ) + return tilegeneration Response = TypeVar("Response") @@ -110,6 +113,35 @@ def __init__(self, layer_filter: Optional[tilecloud_chain.IntersectGeometryFilte self.mtime = mtime +def get_max_zoom_seed(config: tilecloud_chain.DatedConfig, layer_name: str) -> int: + """Get the max zoom to be bet in the stored cache.""" + layer = config.config["layers"][layer_name] + if "min_resolution_seed" in layer: + max_zoom_seed = -1 + for zoom, resolution in enumerate(config.config["grids"][layer["grid"]]["resolutions"]): + if resolution > layer["min_resolution_seed"]: + max_zoom_seed = zoom + return max_zoom_seed + else: + return 999999 + + +def get_static_allow_extension(config: tilecloud_chain.DatedConfig) -> List[str]: + """Get the allowed extensions in the static view.""" + return config.config["server"].get("static_allow_extension", ["jpeg", "png", "xml", "js", "html", "css"]) + + +def get_cache_name(config: tilecloud_chain.DatedConfig) -> str: + """Get the cache name.""" + return config.config["server"].get("cache", config.config["generation"]["default_cache"]) + + +def get_layers(config: tilecloud_chain.DatedConfig) -> List[str]: + """Get the layer from the config.""" + layers: List[str] = cast(List[str], config.config["layers"].keys()) + return config.config["server"].get("layers", layers) + + class Server(Generic[Response]): """The generic implementation of the WMTS server.""" @@ -128,23 +160,6 @@ def __init__(self) -> None: logger.exception("Initialization error") raise - @staticmethod - def get_expires_hours(config: tilecloud_chain.DatedConfig) -> float: - """Get the expiration time in hours.""" - return config.config.get("server", {}).get("expires", tilecloud_chain.configuration.EXPIRES_DEFAULT) - - @staticmethod - def get_static_allow_extension(config: tilecloud_chain.DatedConfig) -> List[str]: - """Get the allowed extensions in the static view.""" - return config.config["server"].get( - "static_allow_extension", ["jpeg", "png", "xml", "js", "html", "css"] - ) - - @staticmethod - def get_cache_name(config: tilecloud_chain.DatedConfig) -> str: - """Get the cache name.""" - return config.config["server"].get("cache", config.config["generation"]["default_cache"]) - def get_s3_client(self, config: tilecloud_chain.DatedConfig) -> "botocore.client.S3": """Get the AWS S3 client.""" cache_s3 = cast(tilecloud_chain.configuration.CacheS3, self.get_cache(config)) @@ -162,13 +177,7 @@ def get_s3_client(self, config: tilecloud_chain.DatedConfig) -> "botocore.client def get_cache(self, config: tilecloud_chain.DatedConfig) -> tilecloud_chain.configuration.Cache: """Get the cache from the config.""" - return config.config["caches"][self.get_cache_name(config)] - - @staticmethod - def get_layers(config: tilecloud_chain.DatedConfig) -> List[str]: - """Get the layer from the config.""" - layers: List[str] = cast(List[str], config.config["layers"].keys()) - return config.config["server"].get("layers", layers) + return config.config["caches"][get_cache_name(config)] def get_filter( self, config: tilecloud_chain.DatedConfig, layer_name: str @@ -203,19 +212,6 @@ def get_store(self, config: tilecloud_chain.DatedConfig, layer_name: str) -> til self.store_cache.setdefault(config.file, {})[layer_name] = DatedStore(store, config.mtime) return store - @staticmethod - def get_max_zoom_seed(config: tilecloud_chain.DatedConfig, layer_name: str) -> int: - """Get the max zoom to be bet in the stored cache.""" - layer = config.config["layers"][layer_name] - if "min_resolution_seed" in layer: - max_zoom_seed = -1 - for zoom, resolution in enumerate(config.config["grids"][layer["grid"]]["resolutions"]): - if resolution > layer["min_resolution_seed"]: - max_zoom_seed = zoom - return max_zoom_seed - else: - return 999999 - def _read( self, key_name: str, @@ -280,7 +276,7 @@ def _get( else: cache_filesystem = cast(tilecloud_chain.configuration.CacheFilesystem, cache) folder = cache_filesystem["folder"] or "" - if path.split(".")[-1] not in self.get_static_allow_extension(config): + if path.split(".")[-1] not in get_static_allow_extension(config): return self.error(config, 403, "Extension not allowed", **kwargs) p = os.path.join(folder, path) if not os.path.isfile(p): @@ -337,9 +333,9 @@ def serve( { "Expires": ( datetime.datetime.utcnow() - + datetime.timedelta(hours=self.get_expires_hours(config)) + + datetime.timedelta(hours=get_expires_hours(config)) ).isoformat(), - "Cache-Control": f"max-age={3600 * self.get_expires_hours(config)}", + "Cache-Control": f"max-age={3600 * get_expires_hours(config)}", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET", }, @@ -370,7 +366,7 @@ def serve( params["LAYER"] = path[1] params["STYLE"] = path[2] - if params["LAYER"] in self.get_layers(config): + if params["LAYER"] in get_layers(config): layer = cast( tilecloud_chain.configuration.LayerWms, config.config["layers"][params["LAYER"]], @@ -419,9 +415,9 @@ def serve( headers = { "Content-Type": "application/xml", "Expires": ( - datetime.datetime.utcnow() + datetime.timedelta(hours=self.get_expires_hours(config)) + datetime.datetime.utcnow() + datetime.timedelta(hours=get_expires_hours(config)) ).isoformat(), - "Cache-Control": f"max-age={3600 * self.get_expires_hours(config)}", + "Cache-Control": f"max-age={3600 * get_expires_hours(config)}", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET", } @@ -431,7 +427,7 @@ def serve( return self._get(wmtscapabilities_file, headers, config=config, **kwargs) else: body = controller.get_wmts_capabilities( - tilegeneration, self.get_cache_name(config), config=config + tilegeneration, get_cache_name(config), config=config ) assert body headers["Content-Type"] = "application/xml" @@ -448,7 +444,7 @@ def serve( return self.error(config, 400, "Not all required parameters are present", **kwargs) if not path: - if params["LAYER"] in self.get_layers(config): + if params["LAYER"] in get_layers(config): layer = cast( tilecloud_chain.configuration.LayerWms, config.config["layers"][params["LAYER"]], @@ -520,7 +516,7 @@ def serve( if params["FORMAT"] != layer["mime_type"]: return self.error(config, 400, f"Wrong Format '{params['FORMAT']}'", **kwargs) - if tile.tilecoord.z > self.get_max_zoom_seed(config, params["LAYER"]): + if tile.tilecoord.z > get_max_zoom_seed(config, params["LAYER"]): return self._map_cache(config, layer, tile, kwargs) layer_filter = self.get_filter(config, params["LAYER"]) @@ -562,10 +558,9 @@ def serve( headers={ "Content-Type": tile2.content_type, "Expires": ( - datetime.datetime.utcnow() - + datetime.timedelta(hours=self.get_expires_hours(config)) + datetime.datetime.utcnow() + datetime.timedelta(hours=get_expires_hours(config)) ).isoformat(), - "Cache-Control": f"max-age={3600 * self.get_expires_hours(config)}", + "Cache-Control": f"max-age={3600 * get_expires_hours(config)}", "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET", "Tile-Backend": "Cache", @@ -614,9 +609,9 @@ def forward( response_headers["Pragma"] = "no-cache" else: response_headers["Expires"] = ( - datetime.datetime.utcnow() + datetime.timedelta(hours=self.get_expires_hours(config)) + datetime.datetime.utcnow() + datetime.timedelta(hours=get_expires_hours(config)) ).isoformat() - response_headers["Cache-Control"] = f"max-age={3600 * self.get_expires_hours(config)}" + response_headers["Cache-Control"] = f"max-age={3600 * get_expires_hours(config)}" response_headers["Access-Control-Allow-Origin"] = "*" response_headers["Access-Control-Allow-Methods"] = "GET" return self.response(config, response.content, headers=response_headers, **kwargs) @@ -732,9 +727,9 @@ def error( headers.update( { "Expires": ( - datetime.datetime.utcnow() + datetime.timedelta(hours=self.get_expires_hours(config)) + datetime.datetime.utcnow() + datetime.timedelta(hours=get_expires_hours(config)) ).isoformat(), - "Cache-Control": f"max-age={3600 * self.get_expires_hours(config)}", + "Cache-Control": f"max-age={3600 * get_expires_hours(config)}", } ) return exception_response(code, detail=message, headers=headers) @@ -832,40 +827,21 @@ def main(global_config: Any, **settings: Any) -> Router: assert tilegeneration config.include(c2cwsgiutils.pyramid.includeme) + health_check.HealthCheck(config) add_mako_renderer(config, ".html") config.set_security_policy(tilecloud_chain.security.SecurityPolicy()) config.add_forbidden_view(forbidden) - config.add_route( - "admin", - f"/{tilegeneration.get_main_config().config['server']['admin_path']}", - request_method="GET", - ) - config.add_route( - "admin_slash", - f"/{tilegeneration.get_main_config().config['server']['admin_path']}/", - request_method="GET", - ) - config.add_route( - "admin_run", - f"/{tilegeneration.get_main_config().config['server']['admin_path']}/run", - request_method="POST", - ) - config.add_route( - "admin_test", - f"/{tilegeneration.get_main_config().config['server']['admin_path']}/test", - request_method="GET", - ) - - config.add_static_view( - name=f"/{tilegeneration.get_main_config().config['server']['admin_path']}/static", - path="/app/tilecloud_chain/static", - ) + admin_route = f"/{tilegeneration.get_main_config().config['server']['admin_path']}" + config.add_route("admin", admin_route, request_method="GET") + config.include(tilecloud_chain.admin.includeme, route_prefix=admin_route) + config.include(tilecloud_chain.ogcapi.includeme, route_prefix="/ogcapi") - config.add_route("tiles", "/*path", request_method="GET") - config.add_view(PyramidView, route_name="tiles") + def add_tiles_view() -> None: + config.add_route("tiles", "/*path", request_method="GET") + config.add_view(PyramidView, route_name="tiles") - config.scan("tilecloud_chain.views") + config.action(("add_tiles_view",), add_tiles_view, order=PHASE0_CONFIG) return config.make_wsgi_app()