Skip to content

Commit 1758f1c

Browse files
committed
print metadata in YAML format and fix parsing of attribute names; update README examples with better coverages and formatting for YAML output
1 parent da90ba5 commit 1758f1c

File tree

6 files changed

+269
-128
lines changed

6 files changed

+269
-128
lines changed

README.md

Lines changed: 152 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,10 @@ object. Optionally, a username and a password may be specified, if the endpoint
3030
```python
3131
from wcs.service import WebCoverageService
3232

33-
wcs_endpoint = "https://ows.rasdaman.org/rasdaman/ows"
33+
wcs_endpoint = "https://fairicube.rasdaman.com/rasdaman/ows"
3434
service = WebCoverageService(wcs_endpoint)
35+
36+
# or with credentials:
3537
# service = WebCoverageService(wcs_endpoint,
3638
# username=..., password=...)
3739
```
@@ -44,88 +46,94 @@ with basic information such as a WGS 84 bounding box and a native bounding box:
4446
coverages = service.list_coverages()
4547
```
4648

47-
Let's inspect a single coverage with name `AvgLandTemp`:
49+
Let's print information of a single coverage with name `dominant_leaf_type_20m`:
4850

4951
```python
50-
avg_land_temp = coverages['AvgLandTemp']
52+
cov = coverages['dominant_leaf_type_20m']
5153

52-
# print all information
54+
print(cov)
55+
```
56+
```yaml
57+
dominant_leaf_type_20m:
58+
subtype: ReferenceableGridCoverage
59+
native CRS: OGC:AnsiDate+EPSG:3035
60+
geo bbox:
61+
time:
62+
min: "2012-01-01"
63+
max: "2015-01-01"
64+
crs: OGC:AnsiDate
65+
Y:
66+
min: 900000
67+
max: 5500000
68+
crs: EPSG:3035
69+
X:
70+
min: 900000
71+
max: 7400000
72+
crs: EPSG:3035
73+
lon/lat bbox:
74+
Lon:
75+
min: -56.50514190170437
76+
max: 72.9061049341568
77+
crs: EPSG:4326
78+
Lat:
79+
min: 24.28417068794856
80+
max: 72.66326966834436
81+
crs: EPSG:4326
82+
size in bytes: 113000000001
83+
additional params:
84+
title: Dominant Leaf Type (2012-2015)
85+
sizeInBytesWithPyramidLevels: "113000000001"
86+
```
5387
54-
print(avg_land_temp)
55-
56-
# AvgLandTemp:
57-
# subtype: ReferenceableGridCoverage
58-
# native CRS: OGC:AnsiDate+EPSG:4326
59-
# geo bbox:
60-
# ansi:
61-
# min: "2000-02-01"
62-
# max: "2015-06-01"
63-
# crs: OGC:AnsiDate
64-
# Lat:
65-
# min: -90
66-
# max: 90
67-
# crs: EPSG:4326
68-
# Lon:
69-
# min: -180
70-
# max: 180
71-
# crs: EPSG:4326
72-
# lon/lat bbox:
73-
# Lon:
74-
# min: -180
75-
# max: 180
76-
# crs: EPSG:4326
77-
# Lat:
78-
# min: -90
79-
# max: 90
80-
# crs: EPSG:4326
81-
# size in bytes: 4809618404
88+
Examples for extracting individual details of the coverage:
8289
90+
```python
8391
# coverage subtype
8492

85-
print(avg_land_temp.subtype)
93+
print(cov.subtype)
8694

8795
# ReferenceableGridCoverage
8896

8997
# coverage bounding box, containing the CRS and axes
9098

91-
bbox = avg_land_temp.bbox
99+
bbox = cov.bbox
92100

93101
# full coverage crs identifier
94102

95103
print(bbox.crs)
96104

97105
# https://www.opengis.net/def/crs-compound?
98-
# 1=https://www.opengis.net/def/crs/OGC/0/AnsiDate&
99-
# 2=https://www.opengis.net/def/crs/EPSG/0/4326
106+
# 1=https://www.opengis.net/def/crs/OGC/0/AnsiDate?axis-label="time"&
107+
# 2=https://www.opengis.net/def/crs/EPSG/0/3035
100108

101109
# coverage crs identifier in shorthand notation
102110

103111
from wcs.model import Crs
112+
104113
print(Crs.to_short_notation(bbox.crs))
105114

106-
# OGC:AnsiDate+EPSG:4326
115+
# OGC:AnsiDate+EPSG:3035
107116

108117
# get information for the first axis; as it is a temporal axis,
109118
# the lower_bound and upper_bound are datetime.datetime objects.
110119

111-
axis = bbox.ansi
120+
axis = bbox.time
112121

113122
# note that these are all equivalent:
114-
# axis = bbox['ansi']
115-
# axis = bbox.0
123+
# axis = bbox['time']
116124
# axis = bbox[0]
117125

118126
name = axis.name
119127
lower_bound = axis.low
120128
upper_bound = axis.high
121129
print(f'{name}({lower_bound} - {upper_bound})')
122-
# ansi(2000-02-01 00:00:00+00:00 - 2015-06-01 00:00:00+00:00)
130+
# time(2012-01-01 00:00:00+00:00 - 2015-01-01 00:00:00+00:00)
123131

124132
# get size in bytes if available
125133

126-
if avg_land_temp.size_bytes is not None:
127-
print(avg_land_temp.size_bytes)
128-
# 4809618404
134+
if cov.size_bytes is not None:
135+
print(cov.size_bytes)
136+
# 113000000001
129137
```
130138

131139
## Full Coverage Information
@@ -139,64 +147,94 @@ More detailed information can be retrieved with the
139147
object:
140148

141149
```python
142-
full_avg_land_temp = service.list_full_info('AvgLandTemp')
150+
cov = service.list_full_info('dominant_leaf_type_20m')
143151

144152
# print all information
145153

146-
print(full_avg_land_temp)
147-
148-
# AvgLandTemp:
149-
# native CRS: OGC:AnsiDate+EPSG:4326
150-
# geo bbox:
151-
# ansi:
152-
# min: "2000-02-01"
153-
# max: "2015-06-01"
154-
# crs: OGC:AnsiDate
155-
# uom: d
156-
# type: irregular
157-
# coefficients: ["2000-02-01", "2000-03-01", ...
158-
# "2015-05-01", "2015-06-01"]
159-
# Lat:
160-
# min: -90
161-
# max: 90
162-
# crs: EPSG:4326
163-
# uom: degree
164-
# resolution: -0.1
165-
# type: regular
166-
# Lon:
167-
# min: -180
168-
# max: 180
169-
# crs: EPSG:4326
170-
# uom: degree
171-
# resolution: 0.1
172-
# type: regular
173-
# grid bbox:
174-
# i:
175-
# min: 0
176-
# max: 184
177-
# resolution: 1
178-
# type: regular
179-
# j:
180-
# min: 0
181-
# max: 1799
182-
# resolution: 1
183-
# type: regular
184-
# k:
185-
# min: 0
186-
# max: 3599
187-
# resolution: 1
188-
# type: regular
189-
# range type fields:
190-
# Gray:
191-
# type: Quantity
192-
# label: Gray
193-
# definition: http://www.opengis.net/def/dataType/OGC/0/float32
194-
# nil values: 99999
195-
# uom: 10^0
196-
# metadata:
197-
# {
198-
# "covMetadata": null
199-
# }
154+
print(cov)
155+
```
156+
```yaml
157+
dominant_leaf_type_20m:
158+
native CRS: OGC:AnsiDate+EPSG:3035
159+
geo bbox:
160+
time:
161+
min: 2012-01-01
162+
max: 2015-01-01
163+
crs: OGC:AnsiDate
164+
uom: d
165+
type: irregular
166+
coefficients:
167+
- 2012-01-01
168+
- 2015-01-01
169+
Y:
170+
min: 900000
171+
max: 5500000
172+
crs: EPSG:3035
173+
uom: metre
174+
resolution: -20
175+
type: regular
176+
X:
177+
min: 900000
178+
max: 7400000
179+
crs: EPSG:3035
180+
uom: metre
181+
resolution: 20
182+
type: regular
183+
grid bbox:
184+
i:
185+
min: 0
186+
max: 1
187+
resolution: 1
188+
type: regular
189+
j:
190+
min: -125000
191+
max: 104999
192+
resolution: 1
193+
type: regular
194+
k:
195+
min: 0
196+
max: 324999
197+
resolution: 1
198+
type: regular
199+
range type fields:
200+
dlt:
201+
type: Category
202+
label: dominant leaf type map of Europe
203+
description: >
204+
raster coding (thematic pixel values):
205+
0: all non-tree covered areas;
206+
1: broadleaved trees;
207+
2: coniferous trees;
208+
254: unclassifiable (no satellite image available, or clouds, shadows, or snow);
209+
255: outside area
210+
definition: https://land.copernicus.eu/en/technical-library/hrl-forest-2012-2015/@@download/file
211+
nil values: 250
212+
metadata:
213+
covMetadata:
214+
axes:
215+
time:
216+
areasOfValidity:
217+
area:
218+
-
219+
"@start": 2011-01-01T00:00:00.000Z
220+
"@end": 2013-12-31T23:59:59.999Z
221+
-
222+
"@start": 2014-01-01T00:00:00.000Z
223+
"@end": 2016-12-31T23:59:59.999Z
224+
rasdamanCoverageMetadata:
225+
catalog:
226+
title: Dominant Leaf Type (2012-2015)
227+
thumbnail: https://fairicube.rasdaman.com/rasdaman/ows/coverage/thumbnail?COVERAGEID=dominant_leaf_type_20m
228+
description: Provides at pan-European level in the spatial resolution of 20 m information on the dominant leaf type (broadleaved or coniferous).
229+
provenance:
230+
"@sourceUrl": https://land.copernicus.eu/en/products/high-resolution-layer-dominant-leaf-type
231+
"@providerName": Copernicus
232+
"@termsUrl": https://land.copernicus.eu/en/data-policy
233+
ourTerms: https://fairicube.rasdaman.com/#terms
234+
fairicubeMetadata:
235+
"@role": https://codelists.fairicube.eu/metadata/MetadataCatalogLink
236+
"@title": Metadata in the FAIRiCUBE Catalog
237+
"@href": https://stacapi.eoxhub.fairicube.eu/collections/index/items/dominant_leaf_type_20m
200238
```
201239
202240
In addition to the geo `bbox` in native CRS, the
@@ -207,7 +245,7 @@ type of
207245
object, except its `crs` attribute is `None`.
208246

209247
```python
210-
print(full_avg_land_temp.grid_bbox)
248+
print(cov.grid_bbox)
211249
```
212250

213251
The `range_type` attribute indicates the structure of the cell values
@@ -220,30 +258,33 @@ coverage. Check the documentation of
220258
for full details.
221259

222260
```python
223-
range_type = full_avg_land_temp.range_type
261+
range_type = cov.range_type
224262
all_fields = range_type.fields
225-
field = range_type.Gray
263+
dlt = range_type.dlt
226264
227265
# note that these are all equivalent:
228-
# field = range_type['Gray']
229-
# field = range_type.0
266+
# field = range_type['dlt']
230267
# field = range_type[0]
231268
232269
# get all properties of the field
233270
234-
label = field.label
235-
description = field.description
236-
definition = field.definition
237-
nil_values = field.nil_values
238-
if field.is_quantity:
239-
uom = field.uom
271+
label = dlt.label
272+
description = dlt.description
273+
definition = dlt.definition
274+
nil_values = dlt.nil_values
275+
if dlt.is_quantity:
276+
uom = dlt.uom
240277
else:
241-
codespace = field.codespace
278+
codespace = dlt.codespace
242279
```
243280

244281
Finally, any coverage metadata is available from the `metadata` attribute,
245282
which is a nested dict mirroring the XML structure in the *DescribeCoverage* document.
283+
E.g. to get the link to the FAIRiCUBE catalog entry for this coverage:
246284

285+
```python
286+
catalog_link = cov.metadata['fairicubeMetadata']['@href']
287+
```
247288

248289
# Contributing
249290

tests/test_service.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,18 @@ def test_list_coverages():
4747
assert cov.bbox.ansi.crs == 'https://www.opengis.net/def/crs/OGC/0/AnsiDate'
4848

4949

50+
def test_list_coverages_only_local():
51+
service = WebCoverageService("https://fairicube.rasdaman.com/rasdaman/ows")
52+
coverages = service.list_coverages(only_local=True)
53+
assert all(cov.is_local() for k, cov in coverages.items())
54+
55+
56+
def test_list_coverages_all():
57+
service = WebCoverageService("https://fairicube.rasdaman.com/rasdaman/ows")
58+
coverages = service.list_coverages()
59+
assert not all(cov.is_local() for k, cov in coverages.items())
60+
61+
5062
def test_list_full_info():
5163
service = WebCoverageService("https://ows.rasdaman.org/rasdaman/ows")
5264
cov = service.list_full_info('AvgLandTemp')
@@ -124,9 +136,15 @@ def test_list_full_info():
124136
nil values: 99999
125137
uom: 10^0
126138
metadata:
127-
{
128-
"covMetadata": null
129-
}'''
139+
covMetadata: None
140+
'''
130141
assert str(cov) == expected
131142
subset = cov.bbox.ansi["2006-08-01" : "2007-01-01"]
132143
assert len(subset) == 6
144+
assert cov.is_local()
145+
146+
def test_list_full_info2():
147+
service = WebCoverageService("https://fairicube.rasdaman.com/rasdaman/ows")
148+
cov = service.list_full_info('dominant_leaf_type_20m')
149+
catalog_link = cov.metadata['fairicubeMetadata']['@href']
150+
assert catalog_link == "https://stacapi.eoxhub.fairicube.eu/collections/index/items/dominant_leaf_type_20m"

wcs/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
"""
66
__title__ = 'wcs'
77
__author__ = 'rasdaman team'
8-
__version__ = "0.2.2"
8+
__version__ = "0.2.3"

0 commit comments

Comments
 (0)