diff --git a/docs/source/data-publishing/ogcapi-tiles.rst b/docs/source/data-publishing/ogcapi-tiles.rst
index 3c0f74e..c3a40b6 100644
--- a/docs/source/data-publishing/ogcapi-tiles.rst
+++ b/docs/source/data-publishing/ogcapi-tiles.rst
@@ -46,7 +46,6 @@ This code block shows how to configure pygeoapi to read Mapbox vector tiles gene
data: tests/data/tiles/ne_110m_lakes # local directory tree
# data: http://localhost:9000/ne_110m_lakes/{z}/{x}/{y}.pbf # tiles stored on a MinIO bucket
options:
- metadata_format: default # default | tilejson
zoom:
min: 0
max: 5
@@ -77,7 +76,6 @@ This code block shows how to configure pygeoapi to read Mapbox vector tiles from
# if you don't use precision 0, you will be requesting for aggregations which are not supported in the
# free version of elastic
options:
- metadata_format: default # default | tilejson
zoom:
min: 0
max: 5
diff --git a/pygeoapi/api.py b/pygeoapi/api.py
index 84c36d6..93fbee7 100644
--- a/pygeoapi/api.py
+++ b/pygeoapi/api.py
@@ -74,6 +74,7 @@ from pygeoapi.plugin import load_plugin, PLUGINS
from pygeoapi.provider.base import (
ProviderGenericError, ProviderConnectionError, ProviderNotFoundError,
ProviderTypeError)
+from pygeoapi.models.provider.base import TilesMetadataFormat
from pygeoapi.models.cql import CQLModel
from pygeoapi.util import (dategetter, RequestedProcessExecutionMode,
@@ -85,8 +86,6 @@ from pygeoapi.util import (dategetter, RequestedProcessExecutionMode,
get_crs_from_uri, get_supported_crs_list,
CrsTransformSpec, transform_bbox)
-from pygeoapi.models.provider.base import TilesMetadataFormat
-
LOGGER = logging.getLogger(__name__)
#: Return headers for requests (e.g:X-Powered-By)
@@ -2682,15 +2681,12 @@ class API:
tiles['tilesets'].append(tile_matrix)
- metadata_format = p.options['metadata_format']
-
if request.format == F_HTML: # render
tiles['id'] = dataset
tiles['title'] = l10n.translate(
self.config['resources'][dataset]['title'], SYSTEM_LOCALE)
tiles['tilesets'] = [
scheme.tileMatrixSet for scheme in p.get_tiling_schemes()]
- tiles['format'] = metadata_format
tiles['bounds'] = \
self.config['resources'][dataset]['extents']['spatial']['bbox']
tiles['minzoom'] = p.options['zoom']['min']
@@ -2785,7 +2781,7 @@ class API:
:returns: tuple of headers, status code, content
"""
- if not request.is_valid():
+ if not request.is_valid([TilesMetadataFormat.TILEJSON]):
return self.get_format_exception(request)
headers = request.get_response_headers(**self.api_headers)
@@ -2821,47 +2817,30 @@ class API:
return self.get_exception(HTTPStatus.NOT_FOUND, headers,
request.format, 'NotFound', msg)
- metadata_format = TilesMetadataFormat[
- str(p.options['metadata_format']).upper()]
-
# Set response language to requested provider locale
# (if it supports language) and/or otherwise the requested pygeoapi
# locale (or fallback default locale)
l10n.set_response_language(headers, prv_locale, request.locale)
- if request.format == F_HTML: # render
- tiles_metadata = p.get_metadata(
- dataset=dataset, server_url=self.base_url,
- layer=p.get_layer(), tileset=matrix_id,
- metadata_format=TilesMetadataFormat.TILEJSON,
- language=prv_locale)
- metadata = dict()
- metadata['metadata'] = tiles_metadata
- metadata['id'] = dataset
- metadata['title'] = l10n.translate(
- self.config['resources'][dataset]['title'], request.locale)
- metadata['tileset'] = matrix_id
- metadata['format'] = metadata_format.value
- metadata['collections_path'] = self.get_collections_url()
+ tiles_metadata = p.get_metadata(
+ dataset=dataset, server_url=self.base_url,
+ layer=p.get_layer(), tileset=matrix_id,
+ metadata_format=request._format, title=l10n.translate(
+ self.config['resources'][dataset]['title'],
+ request.locale),
+ description=l10n.translate(
+ self.config['resources'][dataset]['description'],
+ request.locale),
+ language=prv_locale)
+ if request.format == F_HTML: # render
content = render_j2_template(self.tpl_config,
'collections/tiles/metadata.html',
- metadata, request.locale)
+ tiles_metadata, request.locale)
return headers, HTTPStatus.OK, content
else:
- tiles_metadata = p.get_metadata(
- dataset=dataset, server_url=self.base_url,
- layer=p.get_layer(), tileset=matrix_id,
- metadata_format=metadata_format, title=l10n.translate(
- self.config['resources'][dataset]['title'],
- request.locale),
- description=l10n.translate(
- self.config['resources'][dataset]['description'],
- request.locale),
- language=prv_locale)
-
- return headers, HTTPStatus.OK, tiles_metadata
+ return headers, HTTPStatus.OK, tiles_metadata
@gzip
@pre_process
diff --git a/pygeoapi/models/provider/base.py b/pygeoapi/models/provider/base.py
index 39d18fa..727e467 100644
--- a/pygeoapi/models/provider/base.py
+++ b/pygeoapi/models/provider/base.py
@@ -38,13 +38,14 @@ from typing import List, Optional
from pydantic import BaseModel
-class TilesMetadataFormat(Enum):
+class TilesMetadataFormat(str, Enum):
# Tile Set Metadata
- DEFAULT = "Tile Set Metadata"
+ JSON = "JSON"
+ JSONLD = "JSONLD"
# TileJSON 3.0
- TILEJSON = "TileJSON"
- # Custom JSON
- CUSTOMJSON = "Custom"
+ TILEJSON = "TILEJSON"
+ # HTML (default)
+ HTML = "HTML"
# Tile Set Metadata Enums
diff --git a/pygeoapi/provider/base_mvt.py b/pygeoapi/provider/base_mvt.py
index 796d9c4..23ef01c 100644
--- a/pygeoapi/provider/base_mvt.py
+++ b/pygeoapi/provider/base_mvt.py
@@ -29,19 +29,13 @@
#
# =================================================================
-import json
import logging
-import requests
-from pathlib import Path
from urllib.parse import urlparse
from pygeoapi.provider.tile import BaseTileProvider
-from pygeoapi.provider.base import ProviderConnectionError
from pygeoapi.models.provider.base import (
- TileMatrixSetEnum, TilesMetadataFormat, TileSetMetadata, LinkType,
- GeospatialDataType)
-from pygeoapi.models.provider.mvt import MVTTilesJson
-from pygeoapi.util import is_url, url_join
+ TileMatrixSetEnum, TilesMetadataFormat)
+from pygeoapi.util import url_join
LOGGER = logging.getLogger(__name__)
@@ -125,11 +119,10 @@ class BaseMVTProvider(BaseTileProvider):
raise NotImplementedError()
- def get_metadata(self, dataset, server_url, layer=None,
- tileset=None, metadata_format=None, title=None,
- description=None, keywords=None, **kwargs):
+ def get_html_metadata(self, dataset, server_url, layer, tileset,
+ title, description, keywords, **kwargs):
"""
- Gets tile metadata
+ Gets tile metadata informations in html format
:param dataset: dataset name
:param server_url: server base url
@@ -137,88 +130,91 @@ class BaseMVTProvider(BaseTileProvider):
:param tileset: mvt tileset name
:param metadata_format: format for metadata,
enum TilesMetadataFormat
+ :param title: title name
+ :param description: description name
+ :param keywords: keywords list
:returns: `dict` of JSON metadata
"""
- if is_url(self.data):
- url = urlparse(self.data)
- base_url = f'{url.scheme}://{url.netloc}'
- if metadata_format == TilesMetadataFormat.TILEJSON:
- with requests.Session() as session:
- session.get(base_url)
- resp = session.get(f'{base_url}/{layer}/metadata.json')
- resp.raise_for_status()
- metadata_json_content = resp.json()
+ raise NotImplementedError()
+
+ def get_default_metadata(self, dataset, server_url, layer, tileset,
+ title, description, keywords, **kwargs):
+ """
+ Gets tile metadata in default Tile Set Metadata format
+
+ :param dataset: dataset name
+ :param server_url: server base url
+ :param layer: mvt tile layer name
+ :param tileset: mvt tileset name
+ :param metadata_format: format for metadata,
+ enum TilesMetadataFormat
+ :param title: title name
+ :param description: description name
+ :param keywords: keywords list
+
+ :returns: `dict` of JSON metadata
+ """
+ raise NotImplementedError()
+
+ def get_vendor_metadata(self, dataset, server_url, layer, tileset,
+ title, description, keywords, **kwargs):
+ """
+ Gets tile metadata in Tilejson format
+
+ :param dataset: dataset name
+ :param server_url: server base url
+ :param layer: mvt tile layer name
+ :param tileset: mvt tileset name
+ :param metadata_format: format for metadata,
+ enum TilesMetadataFormat
+ :param title: title name
+ :param description: description name
+ :param keywords: keywords list
+
+ :returns: `dict` of JSON metadata
+ """
+
+ raise NotImplementedError()
+
+ def get_metadata(self, dataset, server_url, layer=None,
+ tileset=None, metadata_format=None, title=None,
+ description=None, keywords=None, **kwargs):
+ """
+ Gets tiles metadata
+
+ :param dataset: dataset name
+ :param server_url: server base url
+ :param layer: mvt tile layer name
+ :param tileset: mvt tileset name
+ :param metadata_format: format for metadata,
+ enum TilesMetadataFormat
+ :param title: title name
+ :param description: description name
+ :param keywords: keywords list
+
+ :returns: `dict` of JSON metadata
+ """
+
+ if metadata_format.upper() == TilesMetadataFormat.JSON:
+ return self.get_default_metadata(dataset, server_url, layer,
+ tileset, title, description,
+ keywords, **kwargs)
+ elif metadata_format.upper() == TilesMetadataFormat.TILEJSON:
+ return self.get_vendor_metadata(dataset, server_url, layer,
+ tileset, title, description,
+ keywords, **kwargs)
+ elif metadata_format.upper() == TilesMetadataFormat.HTML:
+ return self.get_html_metadata(dataset, server_url, layer,
+ tileset, title, description,
+ keywords, **kwargs)
+ elif metadata_format.upper() == TilesMetadataFormat.JSONLD:
+ return self.get_default_metadata(dataset, server_url, layer,
+ tileset, title, description,
+ keywords, **kwargs)
else:
- if not isinstance(self.service_metadata_url, Path):
- msg = f'Wrong data path configuration: {self.service_metadata_url}' # noqa
- LOGGER.error(msg)
- raise ProviderConnectionError(msg)
-
- if self.service_metadata_url.exists():
- with open(self.service_metadata_url, 'r') as md_file:
- metadata_json_content = json.loads(md_file.read())
-
- service_url = url_join(
- server_url,
- f'collections/{dataset}/tiles/{tileset}/{{tileMatrix}}/{{tileRow}}/{{tileCol}}?f=mvt') # noqa
-
- content = {}
- if metadata_format == TilesMetadataFormat.TILEJSON:
- if 'metadata_json_content' in locals():
- content = MVTTilesJson(**metadata_json_content)
- content.tiles = service_url
- content.vector_layers = json.loads(
- metadata_json_content["json"])["vector_layers"]
- return content.dict()
- else:
- msg = f'No tiles metadata json available: {self.service_metadata_url}' # noqa
- LOGGER.error(msg)
- raise ProviderConnectionError(msg)
- elif metadata_format == TilesMetadataFormat.CUSTOMJSON:
- if 'metadata_json_content' in locals():
- content = metadata_json_content
- if 'json' in metadata_json_content:
- content['json'] = json.loads(metadata_json_content['json'])
- return content
- else:
- msg = f'No custom JSON for tiles metadata available: {self.service_metadata_url}' # noqa
- LOGGER.error(msg)
- raise ProviderConnectionError(msg)
- else:
- tiling_schemes = self.get_tiling_schemes()
- # Default values
- tileMatrixSetURI = tiling_schemes[0].tileMatrixSetURI
- crs = tiling_schemes[0].crs
- # Checking the selected matrix in configured tiling_schemes
- for schema in tiling_schemes:
- if (schema.tileMatrixSet == tileset):
- crs = schema.crs
- tileMatrixSetURI = schema.tileMatrixSetURI
-
- content = TileSetMetadata(title=title, description=description,
- keywords=keywords, crs=crs,
- tileMatrixSetURI=tileMatrixSetURI)
-
- links = []
- service_url_link_type = "application/vnd.mapbox-vector-tile"
- service_url_link_title = f'{tileset} vector tiles for {layer}'
- service_url_link = LinkType(href=service_url, rel="item",
- type=service_url_link_type,
- title=service_url_link_title)
- links.append(service_url_link)
-
- content.links = links
-
- if 'metadata_json_content' in locals():
- vector_layers = json.loads(
- metadata_json_content["json"])["vector_layers"]
- layers = []
- for vector_layer in vector_layers:
- layers.append(GeospatialDataType(id=vector_layer['id']))
- content.layers = layers
- return content.dict(exclude_none=True)
+ raise NotImplementedError(f"_{metadata_format.upper()}_ is not supported") # noqa
def get_tms_links(self):
"""
diff --git a/pygeoapi/provider/mvt_elastic.py b/pygeoapi/provider/mvt_elastic.py
index 626742f..78914e9 100644
--- a/pygeoapi/provider/mvt_elastic.py
+++ b/pygeoapi/provider/mvt_elastic.py
@@ -33,6 +33,8 @@ from urllib.parse import urlparse
from pygeoapi.provider.base_mvt import BaseMVTProvider
from pygeoapi.provider.base import ProviderConnectionError
+from pygeoapi.models.provider.base import (
+ TileSetMetadata, LinkType)
from pygeoapi.util import is_url, url_join
LOGGER = logging.getLogger(__name__)
@@ -170,22 +172,63 @@ class MVTElasticProvider(BaseMVTProvider):
LOGGER.error(msg)
raise ProviderConnectionError(msg)
- def get_metadata(self, dataset, server_url, layer=None,
- tileset=None, metadata_format=None, title=None,
- description=None, keywords=None, **kwargs):
+ def get_html_metadata(self, dataset, server_url, layer, tileset,
+ title, description, keywords, **kwargs):
+
+ service_url = url_join(
+ server_url,
+ f'collections/{dataset}/tiles/{tileset}/{{tileMatrix}}/{{tileRow}}/{{tileCol}}?f=mvt') # noqa
+ metadata_url = url_join(
+ server_url,
+ f'collections/{dataset}/tiles/{tileset}/metadata')
+
+ metadata = dict()
+ metadata['id'] = dataset
+ metadata['title'] = title
+ metadata['tileset'] = tileset
+ metadata['collections_path'] = service_url
+ metadata['json_url'] = f'{metadata_url}?f=json'
+
+ return metadata
+
+ def get_default_metadata(self, dataset, server_url, layer, tileset,
+ title, description, keywords, **kwargs):
+
+ service_url = url_join(
+ server_url,
+ f'collections/{dataset}/tiles/{tileset}/{{tileMatrix}}/{{tileRow}}/{{tileCol}}?f=mvt') # noqa
+
+ content = {}
+ tiling_schemes = self.get_tiling_schemes()
+ # Default values
+ tileMatrixSetURI = tiling_schemes[0].tileMatrixSetURI
+ crs = tiling_schemes[0].crs
+ # Checking the selected matrix in configured tiling_schemes
+ for schema in tiling_schemes:
+ if (schema.tileMatrixSet == tileset):
+ crs = schema.crs
+ tileMatrixSetURI = schema.tileMatrixSetURI
+
+ content = TileSetMetadata(title=title, description=description,
+ keywords=keywords, crs=crs,
+ tileMatrixSetURI=tileMatrixSetURI)
+
+ links = []
+ service_url_link_type = "application/vnd.mapbox-vector-tile"
+ service_url_link_title = f'{tileset} vector tiles for {layer}'
+ service_url_link = LinkType(href=service_url, rel="item",
+ type=service_url_link_type,
+ title=service_url_link_title)
+ links.append(service_url_link)
+
+ content.links = links
+
+ return content.dict(exclude_none=True)
+
+ def get_vendor_metadata(self, dataset, server_url, layer, tileset,
+ title, description, keywords, **kwargs):
"""
- Gets tile metadata
-
- :param dataset: dataset name
- :param server_url: server base url
- :param layer: mvt tile layer name
- :param tileset: mvt tileset name
- :param metadata_format: format for metadata,
- enum TilesMetadataFormat
-
- :returns: `dict` of JSON metadata
+ Gets tile metadata in tilejson format
"""
-
- return super().get_metadata(dataset, server_url, layer,
- tileset, metadata_format, title,
- description, keywords, **kwargs)
+ LOGGER.debug("Get tilejson metadata")
+ return ""
diff --git a/pygeoapi/provider/mvt_tippecanoe.py b/pygeoapi/provider/mvt_tippecanoe.py
index 88b315c..fd8a83e 100644
--- a/pygeoapi/provider/mvt_tippecanoe.py
+++ b/pygeoapi/provider/mvt_tippecanoe.py
@@ -27,14 +27,19 @@
#
# =================================================================
+import json
import logging
from pathlib import Path
from urllib.parse import urlparse
from pygeoapi.provider.tile import (
ProviderTileNotFoundError)
-from pygeoapi.provider.base_mvt import BaseMVTProvider
from pygeoapi.provider.base import ProviderConnectionError
+from pygeoapi.provider.base_mvt import BaseMVTProvider
+from pygeoapi.models.provider.base import (
+ TileSetMetadata, LinkType)
+from pygeoapi.models.provider.mvt import MVTTilesJson
+
from pygeoapi.util import is_url, url_join
LOGGER = logging.getLogger(__name__)
@@ -169,22 +174,102 @@ class MVTTippecanoeProvider(BaseMVTProvider):
except FileNotFoundError as err:
raise ProviderTileNotFoundError(err)
- def get_metadata(self, dataset, server_url, layer=None,
- tileset=None, metadata_format=None, title=None,
- description=None, keywords=None, **kwargs):
+ def get_html_metadata(self, dataset, server_url, layer, tileset,
+ title, description, keywords, **kwargs):
+
+ service_url = url_join(
+ server_url,
+ f'collections/{dataset}/tiles/{tileset}/{{tileMatrix}}/{{tileRow}}/{{tileCol}}?f=mvt') # noqa
+ metadata_url = url_join(
+ server_url,
+ f'collections/{dataset}/tiles/{tileset}/metadata')
+
+ metadata = dict()
+ metadata['id'] = dataset
+ metadata['title'] = title
+ metadata['tileset'] = tileset
+ metadata['collections_path'] = service_url
+ metadata['json_url'] = f'{metadata_url}?f=json'
+ # Some providers may not implement tilejson metadata
+ metadata['tilejson_url'] = f'{metadata_url}?f=tilejson'
+
+ if not isinstance(self.service_metadata_url, Path):
+ msg = f'Wrong data path configuration: {self.service_metadata_url}' # noqa
+ LOGGER.warning(msg)
+ elif self.service_metadata_url.exists():
+ with open(self.service_metadata_url, 'r') as md_file:
+ metadata_json_content = json.loads(md_file.read())
+ if 'metadata_json_content' in locals():
+ content = MVTTilesJson(**metadata_json_content)
+ content.tiles = service_url
+ content.vector_layers = json.loads(
+ metadata_json_content["json"])["vector_layers"]
+ metadata['metadata'] = content.model_dump()
+
+ return metadata
+
+ def get_default_metadata(self, dataset, server_url, layer, tileset,
+ title, description, keywords, **kwargs):
+
+ service_url = url_join(
+ server_url,
+ f'collections/{dataset}/tiles/{tileset}/{{tileMatrix}}/{{tileRow}}/{{tileCol}}?f=mvt') # noqa
+
+ content = {}
+ tiling_schemes = self.get_tiling_schemes()
+ # Default values
+ tileMatrixSetURI = tiling_schemes[0].tileMatrixSetURI
+ crs = tiling_schemes[0].crs
+ # Checking the selected matrix in configured tiling_schemes
+ for schema in tiling_schemes:
+ if (schema.tileMatrixSet == tileset):
+ crs = schema.crs
+ tileMatrixSetURI = schema.tileMatrixSetURI
+
+ content = TileSetMetadata(title=title, description=description,
+ keywords=keywords, crs=crs,
+ tileMatrixSetURI=tileMatrixSetURI)
+
+ links = []
+ service_url_link_type = "application/vnd.mapbox-vector-tile"
+ service_url_link_title = f'{tileset} vector tiles for {layer}'
+ service_url_link = LinkType(href=service_url, rel="item",
+ type=service_url_link_type,
+ title=service_url_link_title)
+ links.append(service_url_link)
+
+ content.links = links
+
+ return content.model_dump(exclude_none=True)
+
+ def get_vendor_metadata(self, dataset, server_url, layer, tileset,
+ title, description, keywords, **kwargs):
"""
- Gets tile metadata
-
- :param dataset: dataset name
- :param server_url: server base url
- :param layer: mvt tile layer name
- :param tileset: mvt tileset name
- :param metadata_format: format for metadata,
- enum TilesMetadataFormat
-
- :returns: `dict` of JSON metadata
+ Gets tile metadata in tilejson format
"""
- return super().get_metadata(dataset, server_url, layer,
- tileset, metadata_format, title,
- description, keywords, **kwargs)
+ if not isinstance(self.service_metadata_url, Path):
+ msg = f'Wrong data path configuration: {self.service_metadata_url}' # noqa
+ LOGGER.error(msg)
+ raise ProviderConnectionError(msg)
+
+ if self.service_metadata_url.exists():
+ with open(self.service_metadata_url, 'r') as md_file:
+ metadata_json_content = json.loads(md_file.read())
+
+ service_url = url_join(
+ server_url,
+ f'collections/{dataset}/tiles/{tileset}/{{tileMatrix}}/{{tileRow}}/{{tileCol}}?f=mvt') # noqa
+
+ content = {}
+
+ if 'metadata_json_content' in locals():
+ content = MVTTilesJson(**metadata_json_content)
+ content.tiles = service_url
+ content.vector_layers = json.loads(
+ metadata_json_content["json"])["vector_layers"]
+ return content.model_dump()
+ else:
+ msg = f'No tiles metadata json available: {self.service_metadata_url}' # noqa
+ LOGGER.error(msg)
+ raise ProviderConnectionError(msg)
diff --git a/pygeoapi/templates/collections/tiles/index.html b/pygeoapi/templates/collections/tiles/index.html
index 639e8bd..8a789c4 100644
--- a/pygeoapi/templates/collections/tiles/index.html
+++ b/pygeoapi/templates/collections/tiles/index.html
@@ -39,25 +39,23 @@
{{ data['description'] }}
+- {% for kw in data['keywords'] %} - {{ kw }} - {% endfor %} + (View + {% if data['tilejson_url'] %} + {% trans %}TileJSON{% endtrans %} or + {% endif %} + {% trans %}JSON{% endtrans %} representation)
-+
{{ data['metadata'][key] }}){{ data['metadata'][key] }})