From ab71728f74dd06575e1d5647cdb6de67a29bddb1 Mon Sep 17 00:00:00 2001 From: Henri Kynsilehto Date: Wed, 26 Nov 2025 15:40:05 +0200 Subject: [PATCH] Add CRS parameter support to EDR API --- pygeoapi/api/environmental_data_retrieval.py | 27 ++++++++++++++++--- .../api/test_environmental_data_retrieval.py | 20 ++++++++++++++ 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/pygeoapi/api/environmental_data_retrieval.py b/pygeoapi/api/environmental_data_retrieval.py index 1c2550740..a601a9919 100644 --- a/pygeoapi/api/environmental_data_retrieval.py +++ b/pygeoapi/api/environmental_data_retrieval.py @@ -43,11 +43,13 @@ from typing import Tuple import urllib +from pyproj.exceptions import CRSError from shapely.errors import ShapelyError from shapely.wkt import loads as shapely_loads from pygeoapi import l10n from pygeoapi.api import evaluate_limit +from pygeoapi.crs import (create_crs_transform_spec, set_content_crs_header) from pygeoapi.plugin import load_plugin, PLUGINS from pygeoapi.provider.base import ( ProviderGenericError, ProviderItemNotFoundError) @@ -265,8 +267,9 @@ def get_collection_edr_query(api: API, request: APIRequest, LOGGER.debug('Loading provider') try: - p = load_plugin('provider', get_provider_by_type( - collections[dataset]['providers'], 'edr')) + provider_def = get_provider_by_type( + collections[dataset]['providers'], 'edr') + p = load_plugin('provider', provider_def) except ProviderGenericError as err: return api.get_exception( err.http_status_code, headers, request.format, @@ -284,6 +287,21 @@ def get_collection_edr_query(api: API, request: APIRequest, HTTPStatus.BAD_REQUEST, headers, request.format, 'InvalidParameterValue', msg) + crs_transform_spec = None + query_crs_uri = request.params.get('crs') + if query_crs_uri is not None: + LOGGER.debug('Processing crs parameter') + try: + crs_transform_spec = create_crs_transform_spec( + provider_def, query_crs_uri + ) + except (ValueError, CRSError) as err: + msg = str(err) + return api.get_exception( + HTTPStatus.BAD_REQUEST, headers, request.format, + 'InvalidParameterValue', msg) + set_content_crs_header(headers, provider_def, query_crs_uri) + LOGGER.debug('Processing query parameters') LOGGER.debug('Processing datetime parameter') @@ -377,7 +395,8 @@ def get_collection_edr_query(api: API, request: APIRequest, within=within, within_units=within_units, limit=limit, - location_id=location_id + location_id=location_id, + crs_transform_spec=crs_transform_spec ) try: @@ -507,6 +526,7 @@ def get_oas_30(cfg: dict, locale: str) -> tuple[list[dict[str, str]], dict[str, {'$ref': f"{OPENAPI_YAML['oapif-1']}#/components/parameters/datetime"}, # noqa {'$ref': f"{OPENAPI_YAML['oaedr']}/parameters/parameter-name.yaml"}, # noqa {'$ref': f"{OPENAPI_YAML['oaedr']}/parameters/z.yaml"}, # noqa + {'$ref': '#/components/parameters/crs'}, {'$ref': '#/components/parameters/f'} ], 'responses': { @@ -590,6 +610,7 @@ def get_oas_30(cfg: dict, locale: str) -> tuple[list[dict[str, str]], dict[str, {'$ref': f"{OPENAPI_YAML['oaedr']}/parameters/locationId.yaml"}, # noqa {'$ref': f"{OPENAPI_YAML['oapif-1']}#/components/parameters/datetime"}, # noqa {'$ref': f"{OPENAPI_YAML['oaedr']}/parameters/parameter-name.yaml"}, # noqa + {'$ref': '#/components/parameters/crs'}, {'$ref': '#/components/parameters/f'} ], 'responses': { diff --git a/tests/api/test_environmental_data_retrieval.py b/tests/api/test_environmental_data_retrieval.py index 230218499..8b7d665e1 100644 --- a/tests/api/test_environmental_data_retrieval.py +++ b/tests/api/test_environmental_data_retrieval.py @@ -252,3 +252,23 @@ def test_get_collection_edr_query(config, api_): rsp_headers, code, response = get_collection_edr_query( api_, req, 'usgs-prism', None, 'cube') assert code == HTTPStatus.OK + + +def test_get_collection_edr_query_crs(config, api_): + # Invalid CRS query parameter (not a URI) + req = mock_api_request({'coords': 'POINT(11 11)', 'crs': '4326'}) + rsp_headers, code, response = get_collection_edr_query( + api_, req, 'icoads-sst', None, 'position') + assert code == HTTPStatus.BAD_REQUEST + + # Valid CRS parameter (default CRS) + req = mock_api_request({ + 'coords': 'POINT(11 11)', + 'crs': 'http://www.opengis.net/def/crs/OGC/1.3/CRS84' + }) + rsp_headers, code, response = get_collection_edr_query( + api_, req, 'icoads-sst', None, 'position') + assert code == HTTPStatus.OK + assert 'Content-Crs' in rsp_headers + expected_crs = '' + assert rsp_headers['Content-Crs'] == expected_crs