eodash_catalog 0.0.11__py3-none-any.whl → 0.0.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of eodash_catalog might be problematic. Click here for more details.
- eodash_catalog/__about__.py +1 -1
- eodash_catalog/endpoints.py +250 -173
- eodash_catalog/generate_indicators.py +105 -74
- eodash_catalog/sh_endpoint.py +3 -1
- eodash_catalog/stac_handling.py +127 -101
- eodash_catalog/thumbnails.py +14 -12
- eodash_catalog/utils.py +14 -12
- {eodash_catalog-0.0.11.dist-info → eodash_catalog-0.0.13.dist-info}/METADATA +1 -1
- eodash_catalog-0.0.13.dist-info/RECORD +14 -0
- eodash_catalog-0.0.11.dist-info/RECORD +0 -14
- {eodash_catalog-0.0.11.dist-info → eodash_catalog-0.0.13.dist-info}/WHEEL +0 -0
- {eodash_catalog-0.0.11.dist-info → eodash_catalog-0.0.13.dist-info}/entry_points.txt +0 -0
- {eodash_catalog-0.0.11.dist-info → eodash_catalog-0.0.13.dist-info}/licenses/LICENSE.txt +0 -0
eodash_catalog/endpoints.py
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
+
import importlib
|
|
1
2
|
import json
|
|
2
3
|
import os
|
|
4
|
+
import sys
|
|
3
5
|
import uuid
|
|
6
|
+
from collections.abc import Callable
|
|
4
7
|
from datetime import datetime, timedelta
|
|
5
8
|
from itertools import groupby
|
|
6
9
|
from operator import itemgetter
|
|
@@ -14,7 +17,8 @@ from eodash_catalog.sh_endpoint import get_SH_token
|
|
|
14
17
|
from eodash_catalog.stac_handling import (
|
|
15
18
|
add_collection_information,
|
|
16
19
|
add_example_info,
|
|
17
|
-
|
|
20
|
+
get_collection_times_from_config,
|
|
21
|
+
get_or_create_collection,
|
|
18
22
|
)
|
|
19
23
|
from eodash_catalog.thumbnails import generate_thumbnail
|
|
20
24
|
from eodash_catalog.utils import (
|
|
@@ -26,31 +30,33 @@ from eodash_catalog.utils import (
|
|
|
26
30
|
|
|
27
31
|
|
|
28
32
|
def process_STAC_Datacube_Endpoint(
|
|
29
|
-
|
|
33
|
+
catalog_config: dict, endpoint_config: dict, collection_config: dict, catalog: Catalog
|
|
30
34
|
) -> Collection:
|
|
31
|
-
collection
|
|
32
|
-
catalog,
|
|
35
|
+
collection = get_or_create_collection(
|
|
36
|
+
catalog, collection_config["Name"], collection_config, catalog_config, endpoint_config
|
|
33
37
|
)
|
|
34
|
-
add_visualization_info(collection,
|
|
38
|
+
add_visualization_info(collection, collection_config, endpoint_config)
|
|
35
39
|
|
|
36
|
-
stac_endpoint_url =
|
|
37
|
-
if
|
|
38
|
-
stac_endpoint_url = stac_endpoint_url +
|
|
40
|
+
stac_endpoint_url = endpoint_config["EndPoint"]
|
|
41
|
+
if endpoint_config.get("Name") == "xcube":
|
|
42
|
+
stac_endpoint_url = stac_endpoint_url + endpoint_config.get("StacEndpoint", "")
|
|
39
43
|
# assuming /search not implemented
|
|
40
44
|
api = Client.open(stac_endpoint_url)
|
|
41
|
-
collection_id =
|
|
45
|
+
collection_id = endpoint_config.get("CollectionId", "datacubes")
|
|
42
46
|
coll = api.get_collection(collection_id)
|
|
43
47
|
if not coll:
|
|
44
|
-
raise ValueError(f"Collection {collection_id} not found in endpoint {
|
|
45
|
-
item_id =
|
|
48
|
+
raise ValueError(f"Collection {collection_id} not found in endpoint {endpoint_config}")
|
|
49
|
+
item_id = endpoint_config.get("DatacubeId", "")
|
|
46
50
|
item = coll.get_item(item_id)
|
|
47
51
|
if not item:
|
|
48
52
|
raise ValueError(f"Item {item_id} not found in collection {coll}")
|
|
49
53
|
# slice a datacube along temporal axis to individual items, selectively adding properties
|
|
50
54
|
dimensions = item.properties.get("cube:dimensions", {})
|
|
51
55
|
variables = item.properties.get("cube:variables", {})
|
|
52
|
-
if
|
|
53
|
-
raise Exception(
|
|
56
|
+
if endpoint_config.get("Variable") not in variables:
|
|
57
|
+
raise Exception(
|
|
58
|
+
f'Variable {endpoint_config.get("Variable")} not found in datacube {variables}'
|
|
59
|
+
)
|
|
54
60
|
time_dimension = "time"
|
|
55
61
|
for k, v in dimensions.items():
|
|
56
62
|
if v.get("type") == "temporal":
|
|
@@ -75,29 +81,34 @@ def process_STAC_Datacube_Endpoint(
|
|
|
75
81
|
else:
|
|
76
82
|
link.extra_fields["start_datetime"] = item.properties["start_datetime"]
|
|
77
83
|
link.extra_fields["end_datetime"] = item.properties["end_datetime"]
|
|
78
|
-
unit = variables.get(
|
|
79
|
-
if unit and "yAxis" not in
|
|
80
|
-
|
|
84
|
+
unit = variables.get(endpoint_config.get("Variable")).get("unit")
|
|
85
|
+
if unit and "yAxis" not in collection_config:
|
|
86
|
+
collection_config["yAxis"] = unit
|
|
81
87
|
collection.update_extent_from_items()
|
|
82
88
|
|
|
83
|
-
add_collection_information(
|
|
89
|
+
add_collection_information(catalog_config, collection, collection_config)
|
|
84
90
|
|
|
85
91
|
return collection
|
|
86
92
|
|
|
87
93
|
|
|
88
94
|
def handle_STAC_based_endpoint(
|
|
89
|
-
|
|
95
|
+
catalog_config: dict,
|
|
96
|
+
endpoint_config: dict,
|
|
97
|
+
collection_config: dict,
|
|
98
|
+
catalog: Catalog,
|
|
99
|
+
options: Options,
|
|
100
|
+
headers=None,
|
|
90
101
|
) -> Collection:
|
|
91
|
-
if "Locations" in
|
|
92
|
-
root_collection
|
|
93
|
-
catalog,
|
|
102
|
+
if "Locations" in collection_config:
|
|
103
|
+
root_collection = get_or_create_collection(
|
|
104
|
+
catalog, collection_config["Name"], collection_config, catalog_config, endpoint_config
|
|
94
105
|
)
|
|
95
|
-
for location in
|
|
106
|
+
for location in collection_config["Locations"]:
|
|
96
107
|
if "FilterDates" in location:
|
|
97
108
|
collection = process_STACAPI_Endpoint(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
109
|
+
catalog_config=catalog_config,
|
|
110
|
+
endpoint_config=endpoint_config,
|
|
111
|
+
collection_config=collection_config,
|
|
101
112
|
catalog=catalog,
|
|
102
113
|
options=options,
|
|
103
114
|
headers=headers,
|
|
@@ -107,9 +118,9 @@ def handle_STAC_based_endpoint(
|
|
|
107
118
|
)
|
|
108
119
|
else:
|
|
109
120
|
collection = process_STACAPI_Endpoint(
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
121
|
+
catalog_config=catalog_config,
|
|
122
|
+
endpoint_config=endpoint_config,
|
|
123
|
+
collection_config=collection_config,
|
|
113
124
|
catalog=catalog,
|
|
114
125
|
options=options,
|
|
115
126
|
headers=headers,
|
|
@@ -128,12 +139,12 @@ def handle_STAC_based_endpoint(
|
|
|
128
139
|
collection.description = location["Name"]
|
|
129
140
|
# TODO: should we remove all assets from sub collections?
|
|
130
141
|
link = root_collection.add_child(collection)
|
|
131
|
-
latlng = f
|
|
142
|
+
latlng = f'{location["Point"][1]},{location["Point"][0]}'
|
|
132
143
|
# Add extra properties we need
|
|
133
144
|
link.extra_fields["id"] = location["Identifier"]
|
|
134
145
|
link.extra_fields["latlng"] = latlng
|
|
135
146
|
link.extra_fields["name"] = location["Name"]
|
|
136
|
-
add_example_info(collection,
|
|
147
|
+
add_example_info(collection, collection_config, endpoint_config, catalog_config)
|
|
137
148
|
if "OverwriteBBox" in location:
|
|
138
149
|
collection.extent.spatial = SpatialExtent(
|
|
139
150
|
[
|
|
@@ -146,34 +157,34 @@ def handle_STAC_based_endpoint(
|
|
|
146
157
|
if isinstance(c_child, Collection):
|
|
147
158
|
root_collection.extent.spatial.bboxes.append(c_child.extent.spatial.bboxes[0])
|
|
148
159
|
else:
|
|
149
|
-
if "Bbox" in
|
|
160
|
+
if "Bbox" in endpoint_config:
|
|
150
161
|
root_collection = process_STACAPI_Endpoint(
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
162
|
+
catalog_config=catalog_config,
|
|
163
|
+
endpoint_config=endpoint_config,
|
|
164
|
+
collection_config=collection_config,
|
|
154
165
|
catalog=catalog,
|
|
155
166
|
options=options,
|
|
156
167
|
headers=headers,
|
|
157
|
-
bbox=",".join(map(str,
|
|
168
|
+
bbox=",".join(map(str, endpoint_config["Bbox"])),
|
|
158
169
|
)
|
|
159
170
|
else:
|
|
160
171
|
root_collection = process_STACAPI_Endpoint(
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
172
|
+
catalog_config=catalog_config,
|
|
173
|
+
endpoint_config=endpoint_config,
|
|
174
|
+
collection_config=collection_config,
|
|
164
175
|
catalog=catalog,
|
|
165
176
|
options=options,
|
|
166
177
|
headers=headers,
|
|
167
178
|
)
|
|
168
179
|
|
|
169
|
-
add_example_info(root_collection,
|
|
180
|
+
add_example_info(root_collection, collection_config, endpoint_config, catalog_config)
|
|
170
181
|
return root_collection
|
|
171
182
|
|
|
172
183
|
|
|
173
184
|
def process_STACAPI_Endpoint(
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
185
|
+
catalog_config: dict,
|
|
186
|
+
endpoint_config: dict,
|
|
187
|
+
collection_config: dict,
|
|
177
188
|
catalog: Catalog,
|
|
178
189
|
options: Options,
|
|
179
190
|
headers: dict[str, str] | None = None,
|
|
@@ -183,15 +194,15 @@ def process_STACAPI_Endpoint(
|
|
|
183
194
|
) -> Collection:
|
|
184
195
|
if headers is None:
|
|
185
196
|
headers = {}
|
|
186
|
-
collection
|
|
187
|
-
catalog,
|
|
197
|
+
collection = get_or_create_collection(
|
|
198
|
+
catalog, endpoint_config["CollectionId"], collection_config, catalog_config, endpoint_config
|
|
188
199
|
)
|
|
189
200
|
|
|
190
|
-
api = Client.open(
|
|
201
|
+
api = Client.open(endpoint_config["EndPoint"], headers=headers)
|
|
191
202
|
if bbox is None:
|
|
192
203
|
bbox = [-180, -90, 180, 90]
|
|
193
204
|
results = api.search(
|
|
194
|
-
collections=[
|
|
205
|
+
collections=[endpoint_config["CollectionId"]],
|
|
195
206
|
bbox=bbox,
|
|
196
207
|
datetime=["1900-01-01T00:00:00Z", "3000-01-01T00:00:00Z"],
|
|
197
208
|
)
|
|
@@ -210,24 +221,28 @@ def process_STACAPI_Endpoint(
|
|
|
210
221
|
link = collection.add_item(item)
|
|
211
222
|
if options.tn:
|
|
212
223
|
if "cog_default" in item.assets:
|
|
213
|
-
generate_thumbnail(
|
|
224
|
+
generate_thumbnail(
|
|
225
|
+
item, collection_config, endpoint_config, item.assets["cog_default"].href
|
|
226
|
+
)
|
|
214
227
|
else:
|
|
215
|
-
generate_thumbnail(item,
|
|
228
|
+
generate_thumbnail(item, collection_config, endpoint_config)
|
|
216
229
|
# Check if we can create visualization link
|
|
217
|
-
if "Assets" in
|
|
218
|
-
add_visualization_info(item,
|
|
230
|
+
if "Assets" in endpoint_config:
|
|
231
|
+
add_visualization_info(item, collection_config, endpoint_config, item.id)
|
|
219
232
|
link.extra_fields["item"] = item.id
|
|
220
233
|
elif "cog_default" in item.assets:
|
|
221
|
-
add_visualization_info(
|
|
234
|
+
add_visualization_info(
|
|
235
|
+
item, collection_config, endpoint_config, item.assets["cog_default"].href
|
|
236
|
+
)
|
|
222
237
|
link.extra_fields["cog_href"] = item.assets["cog_default"].href
|
|
223
238
|
elif item_datetime:
|
|
224
239
|
time_string = item_datetime.isoformat()[:-6] + "Z"
|
|
225
|
-
add_visualization_info(item,
|
|
240
|
+
add_visualization_info(item, collection_config, endpoint_config, time=time_string)
|
|
226
241
|
elif "start_datetime" in item.properties and "end_datetime" in item.properties:
|
|
227
242
|
add_visualization_info(
|
|
228
243
|
item,
|
|
229
|
-
|
|
230
|
-
|
|
244
|
+
collection_config,
|
|
245
|
+
endpoint_config,
|
|
231
246
|
time="{}/{}".format(
|
|
232
247
|
item.properties["start_datetime"], item.properties["end_datetime"]
|
|
233
248
|
),
|
|
@@ -240,7 +255,7 @@ def process_STACAPI_Endpoint(
|
|
|
240
255
|
# it is possible for datetime to be null, if it is start and end datetime have to exist
|
|
241
256
|
if item_datetime:
|
|
242
257
|
iso_time = item_datetime.isoformat()[:-6] + "Z"
|
|
243
|
-
if
|
|
258
|
+
if endpoint_config["Name"] == "Sentinel Hub":
|
|
244
259
|
# for SH WMS we only save the date (no time)
|
|
245
260
|
link.extra_fields["datetime"] = iso_date
|
|
246
261
|
else:
|
|
@@ -252,14 +267,14 @@ def process_STACAPI_Endpoint(
|
|
|
252
267
|
collection.update_extent_from_items()
|
|
253
268
|
|
|
254
269
|
# replace SH identifier with catalog identifier
|
|
255
|
-
collection.id =
|
|
256
|
-
add_collection_information(
|
|
270
|
+
collection.id = collection_config["Name"]
|
|
271
|
+
add_collection_information(catalog_config, collection, collection_config)
|
|
257
272
|
|
|
258
273
|
# Check if we need to overwrite the bbox after update from items
|
|
259
|
-
if "OverwriteBBox" in
|
|
274
|
+
if "OverwriteBBox" in endpoint_config:
|
|
260
275
|
collection.extent.spatial = SpatialExtent(
|
|
261
276
|
[
|
|
262
|
-
|
|
277
|
+
endpoint_config["OverwriteBBox"],
|
|
263
278
|
]
|
|
264
279
|
)
|
|
265
280
|
|
|
@@ -267,52 +282,59 @@ def process_STACAPI_Endpoint(
|
|
|
267
282
|
|
|
268
283
|
|
|
269
284
|
def handle_VEDA_endpoint(
|
|
270
|
-
|
|
285
|
+
catalog_config: dict,
|
|
286
|
+
endpoint_config: dict,
|
|
287
|
+
collection_config: dict,
|
|
288
|
+
catalog: Catalog,
|
|
289
|
+
options: Options,
|
|
271
290
|
) -> Collection:
|
|
272
|
-
collection = handle_STAC_based_endpoint(
|
|
291
|
+
collection = handle_STAC_based_endpoint(
|
|
292
|
+
catalog_config, endpoint_config, collection_config, catalog, options
|
|
293
|
+
)
|
|
273
294
|
return collection
|
|
274
295
|
|
|
275
296
|
|
|
276
297
|
def handle_collection_only(
|
|
277
|
-
|
|
298
|
+
catalog_config: dict, endpoint_config: dict, collection_config: dict, catalog: Catalog
|
|
278
299
|
) -> Collection:
|
|
279
|
-
collection
|
|
280
|
-
catalog,
|
|
300
|
+
collection = get_or_create_collection(
|
|
301
|
+
catalog, collection_config["Name"], collection_config, catalog_config, endpoint_config
|
|
281
302
|
)
|
|
282
|
-
|
|
303
|
+
times = get_collection_times_from_config(endpoint_config)
|
|
304
|
+
if len(times) > 0 and not endpoint_config.get("Disable_Items"):
|
|
283
305
|
for t in times:
|
|
284
306
|
item = Item(
|
|
285
307
|
id=t,
|
|
286
|
-
bbox=
|
|
308
|
+
bbox=endpoint_config.get("OverwriteBBox"),
|
|
287
309
|
properties={},
|
|
288
310
|
geometry=None,
|
|
289
311
|
datetime=parser.isoparse(t),
|
|
290
312
|
)
|
|
291
313
|
link = collection.add_item(item)
|
|
292
314
|
link.extra_fields["datetime"] = t
|
|
293
|
-
add_collection_information(
|
|
315
|
+
add_collection_information(catalog_config, collection, collection_config)
|
|
294
316
|
return collection
|
|
295
317
|
|
|
296
318
|
|
|
297
319
|
def handle_SH_WMS_endpoint(
|
|
298
|
-
|
|
320
|
+
catalog_config: dict, endpoint_config: dict, collection_config: dict, catalog: Catalog
|
|
299
321
|
) -> Collection:
|
|
300
322
|
# create collection and subcollections (based on locations)
|
|
301
|
-
if "Locations" in
|
|
302
|
-
root_collection
|
|
303
|
-
catalog,
|
|
323
|
+
if "Locations" in collection_config:
|
|
324
|
+
root_collection = get_or_create_collection(
|
|
325
|
+
catalog, collection_config["Name"], collection_config, catalog_config, endpoint_config
|
|
304
326
|
)
|
|
305
|
-
for location in
|
|
327
|
+
for location in collection_config["Locations"]:
|
|
306
328
|
# create and populate location collections based on times
|
|
307
329
|
# TODO: Should we add some new description per location?
|
|
308
330
|
location_config = {
|
|
309
331
|
"Title": location["Name"],
|
|
310
332
|
"Description": "",
|
|
311
333
|
}
|
|
312
|
-
collection
|
|
313
|
-
catalog, location["Identifier"], location_config,
|
|
334
|
+
collection = get_or_create_collection(
|
|
335
|
+
catalog, location["Identifier"], location_config, catalog_config, endpoint_config
|
|
314
336
|
)
|
|
315
|
-
collection.extra_fields["endpointtype"] =
|
|
337
|
+
collection.extra_fields["endpointtype"] = endpoint_config["Name"]
|
|
316
338
|
for time in location["Times"]:
|
|
317
339
|
item = Item(
|
|
318
340
|
id=time,
|
|
@@ -324,7 +346,7 @@ def handle_SH_WMS_endpoint(
|
|
|
324
346
|
"https://stac-extensions.github.io/web-map-links/v1.1.0/schema.json",
|
|
325
347
|
],
|
|
326
348
|
)
|
|
327
|
-
add_visualization_info(item,
|
|
349
|
+
add_visualization_info(item, collection_config, endpoint_config, time=time)
|
|
328
350
|
item_link = collection.add_item(item)
|
|
329
351
|
item_link.extra_fields["datetime"] = time
|
|
330
352
|
|
|
@@ -336,7 +358,7 @@ def handle_SH_WMS_endpoint(
|
|
|
336
358
|
link.extra_fields["country"] = location["Country"]
|
|
337
359
|
link.extra_fields["city"] = location["Name"]
|
|
338
360
|
collection.update_extent_from_items()
|
|
339
|
-
add_visualization_info(collection,
|
|
361
|
+
add_visualization_info(collection, collection_config, endpoint_config)
|
|
340
362
|
|
|
341
363
|
root_collection.update_extent_from_items()
|
|
342
364
|
# Add bbox extents from children
|
|
@@ -346,30 +368,34 @@ def handle_SH_WMS_endpoint(
|
|
|
346
368
|
return root_collection
|
|
347
369
|
|
|
348
370
|
|
|
349
|
-
def handle_xcube_endpoint(
|
|
371
|
+
def handle_xcube_endpoint(
|
|
372
|
+
catalog_config: dict, endpoint_config: dict, collection_config: dict, catalog: Catalog
|
|
373
|
+
) -> Collection:
|
|
350
374
|
collection = process_STAC_Datacube_Endpoint(
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
375
|
+
catalog_config=catalog_config,
|
|
376
|
+
endpoint_config=endpoint_config,
|
|
377
|
+
collection_config=collection_config,
|
|
354
378
|
catalog=catalog,
|
|
355
379
|
)
|
|
356
380
|
|
|
357
|
-
add_example_info(collection,
|
|
381
|
+
add_example_info(collection, collection_config, endpoint_config, catalog_config)
|
|
358
382
|
return collection
|
|
359
383
|
|
|
360
384
|
|
|
361
|
-
def handle_GeoDB_endpoint(
|
|
362
|
-
|
|
363
|
-
|
|
385
|
+
def handle_GeoDB_endpoint(
|
|
386
|
+
catalog_config: dict, endpoint_config: dict, collection_config: dict, catalog: Catalog
|
|
387
|
+
) -> Collection:
|
|
388
|
+
collection = get_or_create_collection(
|
|
389
|
+
catalog, endpoint_config["CollectionId"], collection_config, catalog_config, endpoint_config
|
|
364
390
|
)
|
|
365
391
|
select = "?select=aoi,aoi_id,country,city,time"
|
|
366
392
|
url = (
|
|
367
|
-
|
|
368
|
-
+
|
|
369
|
-
+ "_{}".format(
|
|
393
|
+
endpoint_config["EndPoint"]
|
|
394
|
+
+ endpoint_config["Database"]
|
|
395
|
+
+ "_{}".format(endpoint_config["CollectionId"])
|
|
370
396
|
+ select
|
|
371
397
|
)
|
|
372
|
-
if additional_query_parameters :=
|
|
398
|
+
if additional_query_parameters := endpoint_config.get("AdditionalQueryString"):
|
|
373
399
|
url += f"&{additional_query_parameters}"
|
|
374
400
|
response = json.loads(requests.get(url).text)
|
|
375
401
|
|
|
@@ -384,7 +410,7 @@ def handle_GeoDB_endpoint(config: dict, endpoint: dict, data: dict, catalog: Cat
|
|
|
384
410
|
unique_values = next(iter({v["aoi_id"]: v for v in values}.values()))
|
|
385
411
|
country = unique_values["country"]
|
|
386
412
|
city = unique_values["city"]
|
|
387
|
-
IdKey =
|
|
413
|
+
IdKey = endpoint_config.get("IdKey", "city")
|
|
388
414
|
IdValue = unique_values[IdKey]
|
|
389
415
|
if country not in countries:
|
|
390
416
|
countries.append(country)
|
|
@@ -422,20 +448,20 @@ def handle_GeoDB_endpoint(config: dict, endpoint: dict, data: dict, catalog: Cat
|
|
|
422
448
|
link.extra_fields["country"] = country
|
|
423
449
|
link.extra_fields["city"] = city
|
|
424
450
|
|
|
425
|
-
if "yAxis" not in
|
|
451
|
+
if "yAxis" not in collection_config:
|
|
426
452
|
# fetch yAxis and store it to data, preventing need to save it per dataset in yml
|
|
427
453
|
select = "?select=y_axis&limit=1"
|
|
428
454
|
url = (
|
|
429
|
-
|
|
430
|
-
+
|
|
431
|
-
+ "_{}".format(
|
|
455
|
+
endpoint_config["EndPoint"]
|
|
456
|
+
+ endpoint_config["Database"]
|
|
457
|
+
+ "_{}".format(endpoint_config["CollectionId"])
|
|
432
458
|
+ select
|
|
433
459
|
)
|
|
434
460
|
response = json.loads(requests.get(url).text)
|
|
435
461
|
yAxis = response[0]["y_axis"]
|
|
436
|
-
|
|
437
|
-
add_collection_information(
|
|
438
|
-
add_example_info(collection,
|
|
462
|
+
collection_config["yAxis"] = yAxis
|
|
463
|
+
add_collection_information(catalog_config, collection, collection_config)
|
|
464
|
+
add_example_info(collection, collection_config, endpoint_config, catalog_config)
|
|
439
465
|
|
|
440
466
|
collection.update_extent_from_items()
|
|
441
467
|
collection.summaries = Summaries(
|
|
@@ -448,37 +474,50 @@ def handle_GeoDB_endpoint(config: dict, endpoint: dict, data: dict, catalog: Cat
|
|
|
448
474
|
|
|
449
475
|
|
|
450
476
|
def handle_SH_endpoint(
|
|
451
|
-
|
|
477
|
+
catalog_config: dict,
|
|
478
|
+
endpoint_config: dict,
|
|
479
|
+
collection_config: dict,
|
|
480
|
+
catalog: Catalog,
|
|
481
|
+
options: Options,
|
|
452
482
|
) -> Collection:
|
|
453
483
|
token = get_SH_token()
|
|
454
484
|
headers = {"Authorization": f"Bearer {token}"}
|
|
455
|
-
|
|
485
|
+
endpoint_config["EndPoint"] = "https://services.sentinel-hub.com/api/v1/catalog/1.0.0/"
|
|
456
486
|
# Overwrite collection id with type, such as ZARR or BYOC
|
|
457
|
-
if "Type" in
|
|
458
|
-
|
|
459
|
-
|
|
487
|
+
if "Type" in endpoint_config:
|
|
488
|
+
endpoint_config["CollectionId"] = (
|
|
489
|
+
endpoint_config["Type"] + "-" + endpoint_config["CollectionId"]
|
|
490
|
+
)
|
|
491
|
+
collection = handle_STAC_based_endpoint(
|
|
492
|
+
catalog_config, endpoint_config, collection_config, catalog, options, headers
|
|
493
|
+
)
|
|
460
494
|
return collection
|
|
461
495
|
|
|
462
496
|
|
|
463
497
|
def handle_WMS_endpoint(
|
|
464
|
-
|
|
498
|
+
catalog_config: dict,
|
|
499
|
+
endpoint_config: dict,
|
|
500
|
+
collection_config: dict,
|
|
501
|
+
catalog: Catalog,
|
|
502
|
+
wmts: bool = False,
|
|
465
503
|
) -> Collection:
|
|
466
|
-
collection
|
|
467
|
-
catalog,
|
|
504
|
+
collection = get_or_create_collection(
|
|
505
|
+
catalog, collection_config["Name"], collection_config, catalog_config, endpoint_config
|
|
468
506
|
)
|
|
507
|
+
times = get_collection_times_from_config(endpoint_config)
|
|
469
508
|
spatial_extent = collection.extent.spatial.to_dict().get("bbox", [-180, -90, 180, 90])[0]
|
|
470
|
-
if
|
|
509
|
+
if endpoint_config.get("Type") != "OverwriteTimes" or not endpoint_config.get("OverwriteBBox"):
|
|
471
510
|
# some endpoints allow "narrowed-down" capabilities per-layer, which we utilize to not
|
|
472
511
|
# have to process full service capabilities XML
|
|
473
|
-
capabilities_url =
|
|
512
|
+
capabilities_url = endpoint_config["EndPoint"]
|
|
474
513
|
spatial_extent, times = retrieveExtentFromWMSWMTS(
|
|
475
514
|
capabilities_url,
|
|
476
|
-
|
|
477
|
-
version=
|
|
515
|
+
endpoint_config["LayerId"],
|
|
516
|
+
version=endpoint_config.get("Version", "1.1.1"),
|
|
478
517
|
wmts=wmts,
|
|
479
518
|
)
|
|
480
519
|
# Create an item per time to allow visualization in stac clients
|
|
481
|
-
if len(times) > 0 and not
|
|
520
|
+
if len(times) > 0 and not endpoint_config.get("Disable_Items"):
|
|
482
521
|
for t in times:
|
|
483
522
|
item = Item(
|
|
484
523
|
id=t,
|
|
@@ -490,33 +529,33 @@ def handle_WMS_endpoint(
|
|
|
490
529
|
"https://stac-extensions.github.io/web-map-links/v1.1.0/schema.json",
|
|
491
530
|
],
|
|
492
531
|
)
|
|
493
|
-
add_visualization_info(item,
|
|
532
|
+
add_visualization_info(item, collection_config, endpoint_config, time=t)
|
|
494
533
|
link = collection.add_item(item)
|
|
495
534
|
link.extra_fields["datetime"] = t
|
|
496
535
|
collection.update_extent_from_items()
|
|
497
536
|
|
|
498
537
|
# Check if we should overwrite bbox
|
|
499
|
-
if "OverwriteBBox" in
|
|
538
|
+
if "OverwriteBBox" in endpoint_config:
|
|
500
539
|
collection.extent.spatial = SpatialExtent(
|
|
501
540
|
[
|
|
502
|
-
|
|
541
|
+
endpoint_config["OverwriteBBox"],
|
|
503
542
|
]
|
|
504
543
|
)
|
|
505
|
-
add_collection_information(
|
|
544
|
+
add_collection_information(catalog_config, collection, collection_config)
|
|
506
545
|
return collection
|
|
507
546
|
|
|
508
547
|
|
|
509
|
-
def generate_veda_tiles_link(
|
|
510
|
-
collection = "collection={}".format(
|
|
548
|
+
def generate_veda_tiles_link(endpoint_config: dict, item: str | None) -> str:
|
|
549
|
+
collection = "collection={}".format(endpoint_config["CollectionId"])
|
|
511
550
|
assets = ""
|
|
512
|
-
for asset in
|
|
551
|
+
for asset in endpoint_config["Assets"]:
|
|
513
552
|
assets += f"&assets={asset}"
|
|
514
553
|
color_formula = ""
|
|
515
|
-
if "ColorFormula" in
|
|
516
|
-
color_formula = "&color_formula={}".format(
|
|
554
|
+
if "ColorFormula" in endpoint_config:
|
|
555
|
+
color_formula = "&color_formula={}".format(endpoint_config["ColorFormula"])
|
|
517
556
|
no_data = ""
|
|
518
|
-
if "NoData" in
|
|
519
|
-
no_data = "&no_data={}".format(
|
|
557
|
+
if "NoData" in endpoint_config:
|
|
558
|
+
no_data = "&no_data={}".format(endpoint_config["NoData"])
|
|
520
559
|
item = f"&item={item}" if item else ""
|
|
521
560
|
target_url = f"https://staging-raster.delta-backend.com/stac/tiles/WebMercatorQuad/{{z}}/{{x}}/{{y}}?{collection}{item}{assets}{color_formula}{no_data}"
|
|
522
561
|
return target_url
|
|
@@ -524,66 +563,68 @@ def generate_veda_tiles_link(endpoint: dict, item: str | None) -> str:
|
|
|
524
563
|
|
|
525
564
|
def add_visualization_info(
|
|
526
565
|
stac_object: Collection | Item,
|
|
527
|
-
|
|
528
|
-
|
|
566
|
+
collection_config: dict,
|
|
567
|
+
endpoint_config: dict,
|
|
529
568
|
file_url: str | None = None,
|
|
530
569
|
time: str | None = None,
|
|
531
570
|
) -> None:
|
|
532
571
|
# add extension reference
|
|
533
|
-
if
|
|
572
|
+
if endpoint_config["Name"] == "Sentinel Hub" or endpoint_config["Name"] == "Sentinel Hub WMS":
|
|
534
573
|
instanceId = os.getenv("SH_INSTANCE_ID")
|
|
535
|
-
if "InstanceId" in
|
|
536
|
-
instanceId =
|
|
574
|
+
if "InstanceId" in endpoint_config:
|
|
575
|
+
instanceId = endpoint_config["InstanceId"]
|
|
537
576
|
extra_fields: dict[str, list[str] | dict[str, str]] = {
|
|
538
|
-
"wms:layers": [
|
|
577
|
+
"wms:layers": [endpoint_config["LayerId"]],
|
|
539
578
|
"role": ["data"],
|
|
540
579
|
}
|
|
541
580
|
if time is not None:
|
|
542
|
-
if
|
|
581
|
+
if endpoint_config["Name"] == "Sentinel Hub WMS":
|
|
543
582
|
# SH WMS for public collections needs time interval, we use full day here
|
|
544
583
|
datetime_object = datetime.strptime(time, "%Y-%m-%d")
|
|
545
584
|
start = datetime_object.isoformat()
|
|
546
585
|
end = (datetime_object + timedelta(days=1) - timedelta(milliseconds=1)).isoformat()
|
|
547
586
|
time_interval = f"{start}/{end}"
|
|
548
587
|
extra_fields["wms:dimensions"] = {"TIME": time_interval}
|
|
549
|
-
if
|
|
588
|
+
if endpoint_config["Name"] == "Sentinel Hub":
|
|
550
589
|
extra_fields["wms:dimensions"] = {"TIME": time}
|
|
551
590
|
stac_object.add_link(
|
|
552
591
|
Link(
|
|
553
592
|
rel="wms",
|
|
554
593
|
target=f"https://services.sentinel-hub.com/ogc/wms/{instanceId}",
|
|
555
|
-
media_type=(
|
|
556
|
-
title=
|
|
594
|
+
media_type=(endpoint_config.get("MimeType", "image/png")),
|
|
595
|
+
title=collection_config["Name"],
|
|
557
596
|
extra_fields=extra_fields,
|
|
558
597
|
)
|
|
559
598
|
)
|
|
560
|
-
elif
|
|
599
|
+
elif endpoint_config["Name"] == "WMS":
|
|
561
600
|
extra_fields = {
|
|
562
|
-
"wms:layers": [
|
|
601
|
+
"wms:layers": [endpoint_config["LayerId"]],
|
|
563
602
|
"role": ["data"],
|
|
564
603
|
}
|
|
565
604
|
if time is not None:
|
|
566
605
|
extra_fields["wms:dimensions"] = {
|
|
567
606
|
"TIME": time,
|
|
568
607
|
}
|
|
569
|
-
if "Styles" in
|
|
570
|
-
extra_fields["wms:styles"] =
|
|
608
|
+
if "Styles" in endpoint_config:
|
|
609
|
+
extra_fields["wms:styles"] = endpoint_config["Styles"]
|
|
571
610
|
media_type = "image/jpeg"
|
|
572
|
-
if "MediaType" in
|
|
573
|
-
media_type =
|
|
611
|
+
if "MediaType" in endpoint_config:
|
|
612
|
+
media_type = endpoint_config["MediaType"]
|
|
574
613
|
stac_object.add_link(
|
|
575
614
|
Link(
|
|
576
615
|
rel="wms",
|
|
577
|
-
target=
|
|
616
|
+
target=endpoint_config["EndPoint"],
|
|
578
617
|
media_type=media_type,
|
|
579
|
-
title=
|
|
618
|
+
title=collection_config["Name"],
|
|
580
619
|
extra_fields=extra_fields,
|
|
581
620
|
)
|
|
582
621
|
)
|
|
583
|
-
elif
|
|
584
|
-
target_url = "{}".format(
|
|
622
|
+
elif endpoint_config["Name"] == "JAXA_WMTS_PALSAR":
|
|
623
|
+
target_url = "{}".format(endpoint_config.get("EndPoint"))
|
|
585
624
|
# custom time just for this special case as a default for collection wmts
|
|
586
|
-
extra_fields = {
|
|
625
|
+
extra_fields = {
|
|
626
|
+
"wmts:layer": endpoint_config.get("LayerId", "").replace("{time}", time or "2017")
|
|
627
|
+
}
|
|
587
628
|
stac_object.add_link(
|
|
588
629
|
Link(
|
|
589
630
|
rel="wmts",
|
|
@@ -593,23 +634,23 @@ def add_visualization_info(
|
|
|
593
634
|
extra_fields=extra_fields,
|
|
594
635
|
)
|
|
595
636
|
)
|
|
596
|
-
elif
|
|
597
|
-
if
|
|
637
|
+
elif endpoint_config["Name"] == "xcube":
|
|
638
|
+
if endpoint_config["Type"] == "zarr":
|
|
598
639
|
# either preset ColormapName of left as a template
|
|
599
|
-
cbar =
|
|
640
|
+
cbar = endpoint_config.get("ColormapName", "{cbar}")
|
|
600
641
|
# either preset Rescale of left as a template
|
|
601
642
|
vmin = "{vmin}"
|
|
602
643
|
vmax = "{vmax}"
|
|
603
|
-
if "Rescale" in
|
|
604
|
-
vmin =
|
|
605
|
-
vmax =
|
|
606
|
-
crs =
|
|
644
|
+
if "Rescale" in endpoint_config:
|
|
645
|
+
vmin = endpoint_config["Rescale"][0]
|
|
646
|
+
vmax = endpoint_config["Rescale"][1]
|
|
647
|
+
crs = endpoint_config.get("Crs", "EPSG:3857")
|
|
607
648
|
target_url = (
|
|
608
649
|
"{}/tiles/{}/{}/{{z}}/{{y}}/{{x}}" "?crs={}&time={{time}}&vmin={}&vmax={}&cbar={}"
|
|
609
650
|
).format(
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
651
|
+
endpoint_config["EndPoint"],
|
|
652
|
+
endpoint_config["DatacubeId"],
|
|
653
|
+
endpoint_config["Variable"],
|
|
613
654
|
crs,
|
|
614
655
|
vmin,
|
|
615
656
|
vmax,
|
|
@@ -623,16 +664,16 @@ def add_visualization_info(
|
|
|
623
664
|
title="xcube tiles",
|
|
624
665
|
)
|
|
625
666
|
)
|
|
626
|
-
elif
|
|
627
|
-
target_url = "{}".format(
|
|
667
|
+
elif endpoint_config["Type"] == "WMTSCapabilities":
|
|
668
|
+
target_url = "{}".format(endpoint_config.get("EndPoint"))
|
|
628
669
|
extra_fields = {
|
|
629
|
-
"wmts:layer":
|
|
670
|
+
"wmts:layer": endpoint_config.get("LayerId", ""),
|
|
630
671
|
"role": ["data"],
|
|
631
672
|
}
|
|
632
673
|
dimensions = {}
|
|
633
674
|
if time is not None:
|
|
634
675
|
dimensions["time"] = time
|
|
635
|
-
if dimensions_config :=
|
|
676
|
+
if dimensions_config := endpoint_config.get("Dimensions", {}):
|
|
636
677
|
for key, value in dimensions_config.items():
|
|
637
678
|
dimensions[key] = value
|
|
638
679
|
if dimensions != {}:
|
|
@@ -646,44 +687,80 @@ def add_visualization_info(
|
|
|
646
687
|
extra_fields=extra_fields,
|
|
647
688
|
)
|
|
648
689
|
)
|
|
649
|
-
elif
|
|
650
|
-
if
|
|
651
|
-
target_url = generate_veda_cog_link(
|
|
652
|
-
elif
|
|
653
|
-
target_url = generate_veda_tiles_link(
|
|
690
|
+
elif endpoint_config["Name"] == "VEDA":
|
|
691
|
+
if endpoint_config["Type"] == "cog":
|
|
692
|
+
target_url = generate_veda_cog_link(endpoint_config, file_url)
|
|
693
|
+
elif endpoint_config["Type"] == "tiles":
|
|
694
|
+
target_url = generate_veda_tiles_link(endpoint_config, file_url)
|
|
654
695
|
if target_url:
|
|
655
696
|
stac_object.add_link(
|
|
656
697
|
Link(
|
|
657
698
|
rel="xyz",
|
|
658
699
|
target=target_url,
|
|
659
700
|
media_type="image/png",
|
|
660
|
-
title=
|
|
701
|
+
title=collection_config["Name"],
|
|
661
702
|
)
|
|
662
703
|
)
|
|
663
|
-
elif
|
|
704
|
+
elif endpoint_config["Name"] == "GeoDB Vector Tiles":
|
|
664
705
|
# `${geoserverUrl}${config.layerName}@EPSG%3A${projString}@pbf/{z}/{x}/{-y}.pbf`,
|
|
665
706
|
# 'geodb_debd884d-92f9-4979-87b6-eadef1139394:GTIF_AT_Gemeinden_3857'
|
|
666
707
|
target_url = "{}{}:{}_{}@EPSG:3857@pbf/{{z}}/{{x}}/{{-y}}.pbf".format(
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
708
|
+
endpoint_config["EndPoint"],
|
|
709
|
+
endpoint_config["Instance"],
|
|
710
|
+
endpoint_config["Database"],
|
|
711
|
+
endpoint_config["CollectionId"],
|
|
671
712
|
)
|
|
672
713
|
stac_object.add_link(
|
|
673
714
|
Link(
|
|
674
715
|
rel="xyz",
|
|
675
716
|
target=target_url,
|
|
676
717
|
media_type="application/pbf",
|
|
677
|
-
title=
|
|
718
|
+
title=collection_config["Name"],
|
|
678
719
|
extra_fields={
|
|
679
|
-
"description":
|
|
680
|
-
"parameters":
|
|
681
|
-
"matchKey":
|
|
682
|
-
"timeKey":
|
|
683
|
-
"source":
|
|
720
|
+
"description": collection_config["Title"],
|
|
721
|
+
"parameters": endpoint_config["Parameters"],
|
|
722
|
+
"matchKey": endpoint_config["MatchKey"],
|
|
723
|
+
"timeKey": endpoint_config["TimeKey"],
|
|
724
|
+
"source": endpoint_config["Source"],
|
|
684
725
|
"role": ["data"],
|
|
685
726
|
},
|
|
686
727
|
)
|
|
687
728
|
)
|
|
688
729
|
else:
|
|
689
730
|
print("Visualization endpoint not supported")
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
def handle_custom_endpoint(
|
|
734
|
+
catalog_config: dict,
|
|
735
|
+
endpoint_config: dict,
|
|
736
|
+
collection_config: dict,
|
|
737
|
+
catalog: Catalog,
|
|
738
|
+
) -> Collection:
|
|
739
|
+
collection = get_or_create_collection(
|
|
740
|
+
catalog, collection_config["Name"], collection_config, catalog_config, endpoint_config
|
|
741
|
+
)
|
|
742
|
+
# invoke 3rd party code and return
|
|
743
|
+
function_path = endpoint_config["Python_Function_Location"]
|
|
744
|
+
module_name, _, func_name = function_path.rpartition(".")
|
|
745
|
+
# add current working directory to sys path
|
|
746
|
+
sys.path.append(os.getcwd())
|
|
747
|
+
try:
|
|
748
|
+
# import configured function
|
|
749
|
+
imported_function: Callable[[Collection, dict, dict, dict], Collection] = getattr(
|
|
750
|
+
importlib.import_module(module_name), func_name
|
|
751
|
+
)
|
|
752
|
+
except ModuleNotFoundError as e:
|
|
753
|
+
print(
|
|
754
|
+
f"""function {func_name} from module {module_name} can not be imported.
|
|
755
|
+
Check if you are specifying relative path inside the
|
|
756
|
+
catalog repository or catalog generator repository."""
|
|
757
|
+
)
|
|
758
|
+
raise e
|
|
759
|
+
# execture the custom handler
|
|
760
|
+
collection = imported_function(
|
|
761
|
+
collection,
|
|
762
|
+
catalog_config,
|
|
763
|
+
endpoint_config,
|
|
764
|
+
collection_config,
|
|
765
|
+
)
|
|
766
|
+
return collection
|