From 781dce946bde86f2c5d2ab6cc895d0cd56b97688 Mon Sep 17 00:00:00 2001 From: doublebyte1 Date: Wed, 13 May 2026 18:46:24 +0100 Subject: [PATCH] - added functions for parsing wms codes from uris and curies, and for transforming wms codes in uris --- pygeoapi/api/maps.py | 11 ++++--- pygeoapi/crs.py | 78 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 5 deletions(-) diff --git a/pygeoapi/api/maps.py b/pygeoapi/api/maps.py index 360673f18..c394b3cc5 100644 --- a/pygeoapi/api/maps.py +++ b/pygeoapi/api/maps.py @@ -44,7 +44,7 @@ import logging from typing import Tuple -from pygeoapi.crs import transform_bbox, DEFAULT_CRS +from pygeoapi.crs import transform_bbox, DEFAULT_CRS, get_crs_curie from pygeoapi.formats import F_JSON, FORMAT_TYPES from pygeoapi.openapi import get_oas_30_parameters from pygeoapi.plugin import load_plugin @@ -65,18 +65,18 @@ DEFAULT_BBOX = [-180, -90, 180, 90] # CRS84 CRS_CODES = { - '4326': 'http://www.opengis.net/def/crs/EPSG/0/4326', - '3857': 'http://www.opengis.net/def/crs/EPSG/0/3857', - 'CRS84': 'http://www.opengis.net/def/crs/OGC/1.3/CRS84', + '4326': 'http://www.opengis.net/def/crs/EPSG/0/4326',# stop supporting this + '3857': 'http://www.opengis.net/def/crs/EPSG/0/3857',# stop supporting this + 'CRS84': 'http://www.opengis.net/def/crs/OGC/1.3/CRS84',# stop supporting this 'http://www.opengis.net/def/crs/EPSG/0/4326': 'http://www.opengis.net/def/crs/EPSG/0/4326', # noqa 'http://www.opengis.net/def/crs/EPSG/0/3857': 'http://www.opengis.net/def/crs/EPSG/0/3857', # noqa 'http://www.opengis.net/def/crs/OGC/1.3/CRS84': 'http://www.opengis.net/def/crs/OGC/1.3/CRS84', # noqa 'EPSG:4326': 'http://www.opengis.net/def/crs/EPSG/0/4326', 'EPSG:3857': 'http://www.opengis.net/def/crs/EPSG/0/3857', 'CRS:84': 'http://www.opengis.net/def/crs/OGC/1.3/CRS84', + 'OGC:CRS84': 'http://www.opengis.net/def/crs/OGC/1.3/CRS84', } - def get_collection_map(api: API, request: APIRequest, dataset: str, style: str | None = None ) -> Tuple[dict, int, str]: @@ -128,6 +128,7 @@ def get_collection_map(api: API, request: APIRequest, else: query_args['crs'] = CRS_CODES.get(request.params['crs'], DEFAULT_CRS) + LOGGER.debug(get_crs_curie(request.params['crs'])) except KeyError: query_args['crs'] = DEFAULT_CRS diff --git a/pygeoapi/crs.py b/pygeoapi/crs.py index c66287b70..a8fe737e9 100644 --- a/pygeoapi/crs.py +++ b/pygeoapi/crs.py @@ -3,6 +3,7 @@ # Authors: Tom Kralidis # Just van den Broecke # +# Copyright (c) 2026 Joana Simoes # Copyright (c) 2025 Tom Kralidis # Copyright (c) 2025 Just van den Broecke # @@ -47,6 +48,7 @@ shape as geojson_to_geom, mapping as geom_to_geojson ) +from urllib.parse import urlparse LOGGER = logging.getLogger(__name__) @@ -119,6 +121,82 @@ def get_supported_crs_list( return supported_crs_list +def get_crs_uri(str) -> str: + + try: + if str.contains('epsg'): + curie_el = str.split(':') + return f'http://www.opengis.net/def/crs/EPSG/0/{curie_el[1]}' + elif str != 'CRS:84': + raise CRSError + + return 'http://www.opengis.net/def/crs/OGC/1.3/CRS84' + except CRSError as e: + return e + +def get_crs_curie(str) -> str: + """ + Get a wms compatible crs curie from a uri or a curie + + :param crs: Uniform resource identifier of the coordinate + reference system. In accordance with + https://docs.ogc.org/pol/09-048r5.html#_naming_rule + Or a safe, or unsafe curie + https://docs.ogc.org/DRAFTS/20-024.html#conventions-curies + + :raises `CRSError`: Error raised if no CRS could be identified from the + URI. + + :returns: `crs curie` matching the input crs. + """ + + try: + str = str.lower() + LOGGER.debug(str) + result = urlparse(str) + + if result.scheme not in ['http', 'https']: + raise CRSError('Invalid uri scheme') + if result.netloc is None or result.netloc != 'www.opengis.net': + raise CRSError('Invalid uri prefix') + + path_el = [p for p in result.path.split('/') if p] + + # Check if the path uri contains the relevant fragments + if len(path_el + ) != 5 or path_el[0] != 'def'or path_el[1] != 'crs' or path_el[2] not in ['epsg', 'ogc']: + raise CRSError('Invalid uri fragments') + + # We support all EPSG crs and CRS84 + if path_el[2] == 'epsg': + return f'EPSG:{path_el[4]}' + elif path_el[4] != 'crs84': + raise CRSError('Unsupported crs') + + return 'CRS:84' + + except CRSError as e: + try: + # parse safe curies + curie = str.strip('[]') + LOGGER.debug(curie) + + curie_el = curie.split(':') + LOGGER.debug(len(curie_el)) + # We support all EPSG and CRS84 + if len(curie_el + ) > 2 or (curie_el[0] != 'epsg' and curie not in ['crs:84','ogc:crs84']): + raise CRSError('Unsupported crs') + + if curie in ['crs:84','ogc:crs84']: + return 'CRS:84' + + return curie.upper() + + except CRSError as e: + return e + + def get_crs(crs: Union[str, pyproj.CRS]) -> pyproj.CRS: """