diff --git a/owslib/ogcapi/coverages.py b/owslib/ogcapi/coverages.py index 1b6acfd5..a0cc360a 100644 --- a/owslib/ogcapi/coverages.py +++ b/owslib/ogcapi/coverages.py @@ -48,42 +48,84 @@ def coverage(self, collection_id: str, **kwargs: dict) -> BinaryIO: @type collection_id: string @param collection_id: id of collection - @type properties: list + @type properties: tuple | list @param properties: range subset - @type subset: list of tuples - @param subset: [(name, lower bound, upper bound)] - @type scale_size: list of tuples - @param scale_size: [(axis name, number)] + @type subset: list or dict of tuples/lists + @param subset: + [(name, lower bound, upper bound)] + [[name, lower bound, upper bound]] + {name: (lower bound, upper bound)} + {name: [lower bound, upper bound]} + @type scale_size: list of tuples or dict + @param scale_size: [(axis name, number)] | {axis name: number} @type scale_factor: int @param scale_factor: factor by which to scale the resulting coverage - @type scale_axes: list of tuples - @param scale_axes: [(axis name, number)] + @type scale_axes: list of tuples or dict + @param scale_axes: [(axis name, number)] | {axis name: number} + @type datetime: tuple | list | str + @param datetime: + tuple or list of start/end datetimes, or as 'start/end' string + start and end datetimes can be ".." for unbounded value @returns: coverage data """ kwargs_ = {} - if 'properties' in kwargs: - kwargs_['properties'] = ','.join( - [str(x) for x in kwargs['properties']]) + if isinstance(kwargs.get('properties'), (tuple, list)): + kwargs_['properties'] = ','.join([ + str(x) for x in kwargs['properties'] + ]) for p in ['scale_axes', 'scale_size']: if p in kwargs: p2 = p.replace('_', '-') + if isinstance(kwargs[p], (tuple, list)): + items = kwargs[p] + elif isinstance(kwargs[p], dict): + items = [ + (name, value) + for name, value + in kwargs[p].items() + ] + else: + continue kwargs_[p2] = [] - for s in kwargs[p2]: - val = f'{s[0]}({s[1]},{s[2]})' - kwargs_[p2].append(val) - - if 'subset' in kwargs: - subsets_list = [] - for s in kwargs['subset']: - subsets_list.append(f'{s[0]}({s[1]}:{s[2]})') - kwargs['subset'] = ','.join(subsets_list) + kwargs_[p2] = ",".join( + f'{s[0]}({s[1]})' + for s in items + ) if 'scale_factor' in kwargs: - kwargs_['scale-factor'] = int(kwargs['scale_factor']) + scale_f = float(kwargs['scale_factor']) + scale_i = int(scale_f) + if scale_i == scale_f: + kwargs_['scale-factor'] = scale_i + else: + kwargs_['scale-factor'] = scale_f + + if 'subset' in kwargs: + subset_items = [] + subset_values = kwargs['subset'] + if isinstance(subset_values, (tuple, list)): + subset_items = subset_values + elif isinstance(subset_values, dict): + subset_items = [ + (name, *values) + for name, values + in subset_values.items() + ] + if subset_items: + kwargs_['subset'] = ','.join([ + f'{s[0]}({s[1]}:{s[2]})' + for s in subset_items + ]) + + if 'datetime' in kwargs: + if isinstance(kwargs['datetime'], (tuple, list)): + kwargs_['datetime'] = '/'.join(kwargs['datetime'][:2]) + else: + kwargs_['datetime'] = str(kwargs['datetime']) path = f'collections/{collection_id}/coverage' diff --git a/tests/test_ogcapi_coverages.py b/tests/test_ogcapi_coverages.py new file mode 100644 index 00000000..4dc2ef2f --- /dev/null +++ b/tests/test_ogcapi_coverages.py @@ -0,0 +1,114 @@ +import json +import pytest + +from owslib.ogcapi.coverages import Coverages + + +class MockCoverages(Coverages): + def __init__(self, *args, **kwargs): + kwargs["json_"] = '{}' # avoid init API request + super(MockCoverages, self).__init__(*args, **kwargs) + + def _request(self, **kwargs): + json_args = json.dumps(kwargs) + return json_args.encode("utf-8") + + +@pytest.mark.parametrize( + ["kwargs", "expect"], + [ + ( + {"unknown": "dropped-param"}, + {} + ), + ( + {"properties": ["B04"]}, + {"properties": "B04"}, + ), + ( + {"properties": ["B04", "B08"]}, + {"properties": "B04,B08"}, + ), + ( + {"scale_axes": [("Lat", 1), ("Lon", 2)]}, + {"scale-axes": "Lat(1),Lon(2)"}, + ), + ( + {"scale_axes": (("Lat", 1), ("Lon", 2))}, + {"scale-axes": "Lat(1),Lon(2)"}, + ), + ( + {"scale_axes": [["Lat", 1], ["Lon", 2]]}, + {"scale-axes": "Lat(1),Lon(2)"}, + ), + ( + {"scale_axes": {"Lat": 1, "Lon": 2}}, + {"scale-axes": "Lat(1),Lon(2)"}, + ), + ( + {"scale_size": [("Lat", 100), ("Lon", 200)]}, + {"scale-size": "Lat(100),Lon(200)"}, + ), + ( + {"scale_size": (("Lat", 100), ("Lon", 200))}, + {"scale-size": "Lat(100),Lon(200)"}, + ), + ( + {"scale_size": [["Lat", 100], ["Lon", 200]]}, + {"scale-size": "Lat(100),Lon(200)"}, + ), + ( + {"scale_size": {"Lat": 100, "Lon": 200}}, + {"scale-size": "Lat(100),Lon(200)"}, + ), + ( + {"scale_factor": 1.23}, + {"scale-factor": 1.23}, + ), + ( + {"scale_factor": 2}, + {"scale-factor": 2}, + ), + ( + {"scale_factor": 0.5}, + {"scale-factor": 0.5}, + ), + ( + {"subset": {"Lat": [10, 20], "Lon": [30, 40]}}, + {"subset": "Lat(10:20),Lon(30:40)"}, + ), + ( + {"subset": {"Lat": (10, 20), "Lon": (30, 40)}}, + {"subset": "Lat(10:20),Lon(30:40)"}, + ), + ( + {"subset": [("Lat", 10, 20), ("Lon", 30, 40)]}, + {"subset": "Lat(10:20),Lon(30:40)"}, + ), + ( + {"subset": [["Lat", 10, 20], ["Lon", 30, 40]]}, + {"subset": "Lat(10:20),Lon(30:40)"}, + ), + ( + {"datetime": ("2025-01-01", "2025-01-02")}, + {"datetime": "2025-01-01/2025-01-02"}, + ), + ( + {"datetime": ["2025-01-01", "2025-01-02"]}, + {"datetime": "2025-01-01/2025-01-02"}, + ), + ( + {"datetime": "2025-01-01/2025-01-02"}, + {"datetime": "2025-01-01/2025-01-02"}, + ), + ] +) +def test_coverages_coverage_kwargs(kwargs, expect): + """ + Validate that additional keywords for coverages are parsed as intended. + """ + cov = MockCoverages("") + result = cov.coverage("test", **kwargs) + args = result.read() + params = json.loads(args) + assert params["kwargs"] == expect diff --git a/tests/test_ogcapi_records_pycsw.py b/tests/test_ogcapi_records_pycsw.py index 7a3dd41a..f89daccf 100644 --- a/tests/test_ogcapi_records_pycsw.py +++ b/tests/test_ogcapi_records_pycsw.py @@ -69,6 +69,7 @@ def test_ogcapi_records_pycsw(): assert len(pycsw_cite_demo_query['features']) == 1 +@pytest.mark.online @pytest.mark.parametrize("path, expected", [ ('collections/foo/1', 'https://demo.pycsw.org/cite/collections/foo/1'), ('collections/foo/https://example.org/11', 'https://demo.pycsw.org/cite/collections/foo/https://example.org/11') # noqa