Skip to content

Commit a78997b

Browse files
committed
RFC5: apply distance limiting
1 parent 6302a5f commit a78997b

5 files changed

Lines changed: 94 additions & 8 deletions

File tree

pygeoapi/api/__init__.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,3 +1332,53 @@ def evaluate_limit(requested: Union[None, int], server_limits: dict,
13321332
else:
13331333
LOGGER.debug('limit requested')
13341334
return min(requested2, max_)
1335+
1336+
1337+
def evaluate_limit_distance(server_limits: dict, collection_limits: dict,
1338+
request_bbox: list = []) -> bool:
1339+
"""
1340+
Helper function to evaluate limit parameter
1341+
1342+
:param server_limits: `dict` of server limits
1343+
:param collection_limits: `dict` of collection limits
1344+
:param request_bbox: `list` bbox of request
1345+
1346+
:returns: `bool` successful distance check
1347+
"""
1348+
1349+
exceed_msg = None
1350+
1351+
effective_limits = ChainMap(collection_limits, server_limits)
1352+
1353+
on_exceed = effective_limits.get('on_exceed', 'throttle')
1354+
max_distance_x = effective_limits.get('max_distance_x')
1355+
max_distance_y = effective_limits.get('max_distance_y')
1356+
max_distance_units = effective_limits.get('max_distance_units')
1357+
1358+
LOGGER.debug(f'On exceed: {on_exceed}')
1359+
LOGGER.debug(f'Maximum distance x: {max_distance_x}')
1360+
LOGGER.debug(f'Maximum distance y: {max_distance_y}')
1361+
LOGGER.debug(f'Maximum distance units: {max_distance_units}')
1362+
1363+
# TODO: assess distance units
1364+
1365+
if not request_bbox:
1366+
LOGGER.debug('no bbox requested')
1367+
return True
1368+
1369+
if max_distance_x is not None:
1370+
requested_distance_x = abs(request_bbox[2] - request_bbox[0])
1371+
if requested_distance_x > max_distance_x:
1372+
exceed_msg = 'Maximum distance x exceeded'
1373+
1374+
if max_distance_y is not None:
1375+
requested_distance_y = abs(request_bbox[3] - request_bbox[1])
1376+
if requested_distance_y > max_distance_y:
1377+
exceed_msg = 'Maximum distance y exceeded'
1378+
1379+
if exceed_msg is not None:
1380+
LOGGER.warning(exceed_msg)
1381+
if on_exceed == 'error':
1382+
raise ValueError(exceed_msg)
1383+
1384+
return True

pygeoapi/api/coverages.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@
5151
from pygeoapi.util import filter_dict_by_key_value, to_json
5252

5353
from . import (
54-
APIRequest, API, SYSTEM_LOCALE, validate_bbox, validate_datetime,
55-
validate_subset
54+
APIRequest, API, SYSTEM_LOCALE, evaluate_limit_distance, validate_bbox,
55+
validate_datetime, validate_subset
5656
)
5757

5858
LOGGER = logging.getLogger(__name__)
@@ -112,6 +112,11 @@ def get_collection_coverage(
112112
else:
113113
try:
114114
bbox = validate_bbox(bbox)
115+
server_limits = api.config['server'].get('limits', {})
116+
collection_limits = api.config['resources'][dataset].get('limits', {}) # noqa
117+
118+
_ = evaluate_limit_distance(request.params.get('bbox', []),
119+
server_limits, collection_limits)
115120
except ValueError as err:
116121
msg = str(err)
117122
return api.get_exception(

pygeoapi/api/environmental_data_retrieval.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
from shapely.wkt import loads as shapely_loads
4949

5050
from pygeoapi import l10n
51-
from pygeoapi.api import evaluate_limit
51+
from pygeoapi.api import evaluate_limit, evaluate_limit_distance
5252
from pygeoapi.formats import F_COVERAGEJSON, F_HTML, F_JSON, F_JSONLD
5353
from pygeoapi.formatter.base import FormatterSerializationError
5454
from pygeoapi.crs import (create_crs_transform_spec, set_content_crs_header)
@@ -345,6 +345,11 @@ def get_collection_edr_query(api: API, request: APIRequest,
345345
LOGGER.debug('Processing cube bbox')
346346
try:
347347
bbox = validate_bbox(request.params.get('bbox'))
348+
server_limits = api.config['server'].get('limits', {})
349+
collection_limits = api.config['resources'][dataset].get('limits', {}) # noqa
350+
351+
_ = evaluate_limit_distance(request.params.get('bbox', []),
352+
server_limits, collection_limits)
348353
if not bbox and query_type == 'cube':
349354
raise ValueError('bbox parameter required by cube queries')
350355
except ValueError as err:

pygeoapi/api/itemtypes.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
from pyproj.exceptions import CRSError
4949

5050
from pygeoapi import l10n
51-
from pygeoapi.api import evaluate_limit
51+
from pygeoapi.api import evaluate_limit, evaluate_limit_distance
5252
from pygeoapi.api.pubsub import publish_message
5353
from pygeoapi.crs import (DEFAULT_CRS, DEFAULT_STORAGE_CRS,
5454
create_crs_transform_spec, get_supported_crs_list,
@@ -287,9 +287,13 @@ def get_collection_items(
287287
'level (RFC5)')
288288
LOGGER.warning(msg)
289289
try:
290+
server_limits = api.config['server'].get('limits', {})
291+
collection_limits = collections[dataset].get('limits', {})
292+
290293
limit = evaluate_limit(request.params.get('limit'),
291-
api.config['server'].get('limits', {}),
292-
collections[dataset].get('limits', {}))
294+
server_limits, collection_limits)
295+
_ = evaluate_limit_distance(request.params.get('bbox', []),
296+
server_limits, collection_limits)
293297
except ValueError as err:
294298
return api.get_exception(
295299
HTTPStatus.BAD_REQUEST, headers, request.format,

tests/api/test_api.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@
4242

4343
from pygeoapi.api import (
4444
API, APIRequest, CONFORMANCE_CLASSES, __version__, validate_bbox,
45-
validate_datetime, evaluate_limit, validate_subset, landing_page, openapi_,
46-
conformance, describe_collections, get_collection_schema)
45+
validate_datetime, evaluate_limit, evaluate_limit_distance,
46+
validate_subset, landing_page, openapi_, conformance, describe_collections,
47+
get_collection_schema)
4748

4849
from pygeoapi.formats import FORMAT_TYPES, F_GZIP, F_JSON, F_JSONLD, F_HTML
4950
from pygeoapi.util import yaml_load, get_api_rules, get_base_url
@@ -1023,3 +1024,24 @@ def test_evaluate_limit():
10231024

10241025
assert evaluate_limit(None, server, collection) == 10
10251026
assert evaluate_limit('40', server, collection) == 3
1027+
1028+
1029+
def test_evaluate_limit_distance():
1030+
collection = {
1031+
'on_exceed': 'error',
1032+
'max_distance_x': 10,
1033+
'max_distance_y': 10
1034+
}
1035+
1036+
with pytest.raises(ValueError):
1037+
assert evaluate_limit_distance(
1038+
{}, collection, request_bbox=[-180, -90, 180, 90])
1039+
1040+
assert evaluate_limit_distance({}, collection,
1041+
request_bbox=[-142, 42, -140, 44])
1042+
1043+
assert evaluate_limit_distance({}, {}, request_bbox=[-180, -90, 180, 90])
1044+
1045+
collection['on_exceed'] = 'throttle'
1046+
assert evaluate_limit_distance(
1047+
{}, collection, request_bbox=[-180, -90, 180, 90])

0 commit comments

Comments
 (0)