Skip to content

Commit de5d26e

Browse files
authored
add OGC API - EDR client (#899)
* add OGC API - EDR client * fixes
1 parent 5d6df7c commit de5d26e

6 files changed

Lines changed: 170 additions & 23 deletions

File tree

docs/source/usage.rst

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -288,8 +288,6 @@ OGC API - Records - Part 1: Core 1.0
288288

289289
>>> w.collection_item_delete('my-catalogue', identifier)
290290

291-
292-
293291
OGC API - Features - Part 4: Create, Replace, Update and Delete
294292
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
295293

@@ -300,34 +298,33 @@ OGC API - Features - Part 4: Create, Replace, Update and Delete
300298

301299
.. code-block:: python
302300
303-
import json
304-
from owslib.ogcapi.records import Records
301+
>>> import json
302+
>>> from owslib.ogcapi.records import Records
305303
306-
record_data = '/path/to/record.json'
304+
>>> record_data = '/path/to/record.json'
307305
308-
url = 'http://localhost:8000'
309-
collection_id = 'metadata:main'
306+
>>> url = 'http://localhost:8000'
307+
>>> collection_id = 'metadata:main'
310308
311-
r = Records(url)
309+
>>> r = Records(url)
312310
313-
cat = r.collection(collection_id)
311+
>>> cat = r.collection(collection_id)
314312
315-
with open(record_data) as fh:
316-
data = json.load(fh)
313+
>>> with open(record_data) as fh:
314+
... data = json.load(fh)
317315
318-
identifier = data['id']
316+
>>> identifier = data['id']
319317
320-
r.collection_item_delete(collection_id, identifier)
318+
>>> r.collection_item_delete(collection_id, identifier)
321319
322320
# insert metadata
323-
r.collection_item_create(collection_id, data)
321+
>>> r.collection_item_create(collection_id, data)
324322
325323
# update metadata
326-
r.collection_item_update(collection_id, identifier, data)
324+
>>> r.collection_item_update(collection_id, identifier, data)
327325
328326
# delete metadata
329-
r.collection_item_delete(collection_id, identifier)
330-
327+
>>> r.collection_item_delete(collection_id, identifier)
331328
332329
OGC API - Processes - Part 1: Core 1.0
333330
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -345,7 +342,6 @@ OGC API - Processes - Part 1: Core 1.0
345342
>>> hello_world['title']
346343
'Hello World'
347344
348-
349345
OGC API - Maps - Part 1: Core 1.0
350346
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
351347

@@ -358,6 +354,16 @@ OGC API - Maps - Part 1: Core 1.0
358354
>>> with open("output.png", "wb") as fh:
359355
... fh.write(data.getbuffer())
360356
357+
OGC API - Environmental Data Retrieval - Part 1: Core 1.0
358+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
359+
360+
.. code-block:: python
361+
362+
>>> from owslib.ogcapi.edr import EnvironmentalDataRetrieval
363+
>>> e = EnvironmentalDataRetrieval('http://localhost:5000')
364+
>>> icoads_sst = m.collection('icoads-sst')
365+
>>> data = e.query_data('icoads_sst', 'position', coords='POINT(-75 45)', parameter_names='SST,AIRT')
366+
361367
362368
WCS
363369
---

owslib/ogcapi/edr.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# =============================================================================
2+
# Copyright (c) 2023 Tom Kralidis
3+
#
4+
# Author: Tom Kralidis <tomkralidis@gmail.com>
5+
#
6+
# Contact email: tomkralidis@gmail.com
7+
# =============================================================================
8+
9+
from io import BytesIO
10+
import logging
11+
from typing import BinaryIO
12+
13+
from owslib.ogcapi.features import Features
14+
from owslib.util import Authentication
15+
16+
LOGGER = logging.getLogger(__name__)
17+
18+
19+
class EnvironmentalDataRetrieval(Features):
20+
"""Abstraction for OGC API - Environmental Data Retrieval"""
21+
22+
def __init__(self, url: str, json_: str = None, timeout: int = 30,
23+
headers: dict = None, auth: Authentication = None):
24+
__doc__ = Features.__doc__ # noqa
25+
super().__init__(url, json_, timeout, headers, auth)
26+
27+
def data(self) -> list:
28+
"""
29+
implements /collections filtered on EDR data resources
30+
31+
@returns: `list` of filtered collections object
32+
"""
33+
34+
datas = []
35+
collections_ = super().collections()
36+
37+
for c_ in collections_['collections']:
38+
for l_ in c_['links']:
39+
if 'data' in l_['rel']:
40+
datas.append(c_['id'])
41+
break
42+
43+
return datas
44+
45+
def query_data(self, collection_id: str,
46+
query_type: str, **kwargs: dict) -> BinaryIO:
47+
"""
48+
implements /collection/{collectionId}/coverage/
49+
50+
@type collection_id: string
51+
@param collection_id: id of collection
52+
@type query_type: string
53+
@param query_type: query type
54+
@type bbox: list
55+
@param bbox: list of minx,miny,maxx,maxy
56+
@type coords: string
57+
@param coords: well-known text geometry
58+
@type datetime_: string
59+
@type datetime_: string
60+
@param datetime_: time extent or time instant
61+
@type parameter_names: list
62+
@param parameter_names: list of parameter names
63+
64+
@returns: coverage data
65+
"""
66+
67+
kwargs_ = {}
68+
69+
if 'bbox' in kwargs:
70+
kwargs_['bbox'] = ','.join(list(map(str, kwargs['bbox'])))
71+
if 'parameter_names' in kwargs:
72+
kwargs_['parameter_names'] = ','.join(kwargs['parameter_names'])
73+
74+
query_args_map = {
75+
'coords': 'coords',
76+
'corridor_width': 'corridor-width',
77+
'corridor_height': 'corridor-height',
78+
'crs': 'crs',
79+
'cube-z': 'cube-z',
80+
'datetime_': 'datetime',
81+
'height': 'height',
82+
'height_units': 'height-units',
83+
'resolution_x': 'resolution-x',
84+
'resolution_y': 'resolution-y',
85+
'resolution_z': 'resolution-z',
86+
'width': 'width',
87+
'width_units': 'width-units',
88+
'within': 'within',
89+
'z': 'z'
90+
}
91+
92+
for key, value in query_args_map.items():
93+
if key in kwargs:
94+
kwargs_[value] = kwargs[key]
95+
96+
path = f'collections/{collection_id}/{query_type}'
97+
98+
return self._request(path=path, kwargs=kwargs_)

tests/test_ogcapi_edr_pygeoapi.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from tests.utils import service_ok
2+
3+
import pytest
4+
5+
from owslib.ogcapi.edr import EnvironmentalDataRetrieval
6+
7+
SERVICE_URL = 'https://demo.pygeoapi.io/master/'
8+
9+
10+
@pytest.mark.online
11+
@pytest.mark.skipif(not service_ok(SERVICE_URL),
12+
reason='service is unreachable')
13+
def test_ogcapi_coverages_pygeoapi():
14+
w = EnvironmentalDataRetrieval(SERVICE_URL)
15+
16+
assert w.url == SERVICE_URL
17+
assert w.url_query_string is None
18+
19+
api = w.api()
20+
assert api['components']['parameters'] is not None
21+
paths = api['paths']
22+
assert paths is not None
23+
assert paths['/collections/icoads-sst'] is not None
24+
25+
conformance = w.conformance()
26+
assert len(conformance['conformsTo']) > 1
27+
28+
collections = w.collections()
29+
assert len(collections) > 0
30+
31+
datas = w.data()
32+
assert len(datas) > 0
33+
34+
icoads = w.collection('icoads-sst')
35+
assert icoads['id'] == 'icoads-sst'
36+
assert icoads['title'] == 'International Comprehensive Ocean-Atmosphere Data Set (ICOADS)' # noqa
37+
assert icoads['description'] == 'International Comprehensive Ocean-Atmosphere Data Set (ICOADS)' # noqa
38+
39+
parameter_names = icoads['parameter_names'].keys()
40+
assert sorted(parameter_names) == ['AIRT', 'SST', 'UWND', 'VWND']
41+
42+
response = w.query_data('icoads-sst', 'position', coords='POINT(-75 45)')
43+
assert isinstance(response, dict)

tests/test_ogcapi_features_pygeoapi.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ def test_ogcapi_features_pygeoapi():
3636
assert lakes['title'] == 'Large Lakes'
3737
assert lakes['description'] == 'lakes of the world, public domain'
3838

39-
#lakes_queryables = w.collection_queryables('lakes')
40-
#assert len(lakes_queryables['queryables']) == 6
39+
# lakes_queryables = w.collection_queryables('lakes')
40+
# assert len(lakes_queryables['queryables']) == 6
4141

4242
# Minimum of limit param is 1
4343
with pytest.raises(RuntimeError):

tests/test_ogcapi_processes_pygeoapi.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def test_ogcapi_processes_pygeoapi():
2929
assert len(collections) > 0
3030

3131
processes = p.processes()
32-
assert len(processes) == 5
32+
assert len(processes) == 6
3333

3434
hello_world = p.process('hello-world')
3535
assert hello_world['id'] == 'hello-world'

tests/test_ogcapi_records_pycsw.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def test_ogcapi_records_pycsw():
2323
assert paths['/collections/{collectionId}'] is not None
2424

2525
conformance = w.conformance()
26-
assert len(conformance['conformsTo']) == 18
26+
assert len(conformance['conformsTo']) == 14
2727

2828
collections = w.collections()
2929
assert len(collections) > 0
@@ -40,7 +40,7 @@ def test_ogcapi_records_pycsw():
4040
assert isinstance(w.response, dict)
4141

4242
pycsw_cite_demo_queryables = w.collection_queryables('metadata:main')
43-
assert len(pycsw_cite_demo_queryables['properties'].keys()) == 12
43+
assert len(pycsw_cite_demo_queryables['properties'].keys()) == 13
4444

4545
# Minimum of limit param is 1
4646
with pytest.raises(RuntimeError):

0 commit comments

Comments
 (0)