eodash_catalog 0.0.12__py3-none-any.whl → 0.0.14__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.

@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2024-present Daniel Santillan <daniel.santillan@eox.at>
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
- __version__ = "0.0.12"
4
+ __version__ = "0.0.14"
@@ -10,19 +10,21 @@ from operator import itemgetter
10
10
 
11
11
  import requests
12
12
  from dateutil import parser
13
- from pystac import Catalog, Collection, Item, Link, SpatialExtent, Summaries
13
+ from pystac import Asset, Catalog, Collection, Item, Link, SpatialExtent, Summaries
14
14
  from pystac_client import Client
15
15
 
16
16
  from eodash_catalog.sh_endpoint import get_SH_token
17
17
  from eodash_catalog.stac_handling import (
18
18
  add_collection_information,
19
19
  add_example_info,
20
+ add_projection_info,
20
21
  get_collection_times_from_config,
21
22
  get_or_create_collection,
22
23
  )
23
24
  from eodash_catalog.thumbnails import generate_thumbnail
24
25
  from eodash_catalog.utils import (
25
26
  Options,
27
+ create_geojson_from_bbox,
26
28
  create_geojson_point,
27
29
  generate_veda_cog_link,
28
30
  retrieveExtentFromWMSWMTS,
@@ -263,7 +265,10 @@ def process_STACAPI_Endpoint(
263
265
  else:
264
266
  link.extra_fields["start_datetime"] = item.properties["start_datetime"]
265
267
  link.extra_fields["end_datetime"] = item.properties["end_datetime"]
266
-
268
+ add_projection_info(
269
+ endpoint_config,
270
+ item,
271
+ )
267
272
  collection.update_extent_from_items()
268
273
 
269
274
  # replace SH identifier with catalog identifier
@@ -346,6 +351,7 @@ def handle_SH_WMS_endpoint(
346
351
  "https://stac-extensions.github.io/web-map-links/v1.1.0/schema.json",
347
352
  ],
348
353
  )
354
+ add_projection_info(endpoint_config, item)
349
355
  add_visualization_info(item, collection_config, endpoint_config, time=time)
350
356
  item_link = collection.add_item(item)
351
357
  item_link.extra_fields["datetime"] = time
@@ -529,6 +535,7 @@ def handle_WMS_endpoint(
529
535
  "https://stac-extensions.github.io/web-map-links/v1.1.0/schema.json",
530
536
  ],
531
537
  )
538
+ add_projection_info(endpoint_config, item)
532
539
  add_visualization_info(item, collection_config, endpoint_config, time=t)
533
540
  link = collection.add_item(item)
534
541
  link.extra_fields["datetime"] = t
@@ -557,7 +564,7 @@ def generate_veda_tiles_link(endpoint_config: dict, item: str | None) -> str:
557
564
  if "NoData" in endpoint_config:
558
565
  no_data = "&no_data={}".format(endpoint_config["NoData"])
559
566
  item = f"&item={item}" if item else ""
560
- target_url = f"https://staging-raster.delta-backend.com/stac/tiles/WebMercatorQuad/{{z}}/{{x}}/{{y}}?{collection}{item}{assets}{color_formula}{no_data}"
567
+ target_url = f"https://openveda.cloud/stac/tiles/WebMercatorQuad/{{z}}/{{x}}/{{y}}?{collection}{item}{assets}{color_formula}{no_data}"
561
568
  return target_url
562
569
 
563
570
 
@@ -644,7 +651,10 @@ def add_visualization_info(
644
651
  if "Rescale" in endpoint_config:
645
652
  vmin = endpoint_config["Rescale"][0]
646
653
  vmax = endpoint_config["Rescale"][1]
647
- crs = endpoint_config.get("Crs", "EPSG:3857")
654
+ # depending on numerical input only
655
+ data_projection = endpoint_config.get("DataProjection", 3857)
656
+ epsg_prefix = "" if "EPSG:" in data_projection else "EPSG:"
657
+ crs = f"{epsg_prefix}{data_projection}"
648
658
  target_url = (
649
659
  "{}/tiles/{}/{}/{{z}}/{{y}}/{{x}}" "?crs={}&time={{time}}&vmin={}&vmax={}&cbar={}"
650
660
  ).format(
@@ -764,3 +774,60 @@ def handle_custom_endpoint(
764
774
  collection_config,
765
775
  )
766
776
  return collection
777
+
778
+
779
+ def handle_raw_source(
780
+ catalog_config: dict,
781
+ endpoint_config: dict,
782
+ collection_config: dict,
783
+ catalog: Catalog,
784
+ ) -> Collection:
785
+ collection = get_or_create_collection(
786
+ catalog, collection_config["Name"], collection_config, catalog_config, endpoint_config
787
+ )
788
+ if len(endpoint_config.get("TimeEntries", [])) > 0:
789
+ for time_entry in endpoint_config["TimeEntries"]:
790
+ assets = {}
791
+ media_type = "application/geo+json"
792
+ style_type = "text/vector-styles"
793
+ if endpoint_config["Name"] == "COG source":
794
+ style_type = "text/cog-styles"
795
+ media_type = "image/tiff"
796
+ for a in time_entry["Assets"]:
797
+ asset = Asset(
798
+ href=a["File"], roles=["data"], media_type=media_type, extra_fields={}
799
+ )
800
+ add_projection_info(endpoint_config, asset)
801
+ assets[a["Identifier"]] = asset
802
+ bbox = endpoint_config.get("Bbox", [-180, -85, 180, 85])
803
+ item = Item(
804
+ id=time_entry["Time"],
805
+ bbox=bbox,
806
+ properties={},
807
+ geometry=create_geojson_from_bbox(bbox),
808
+ datetime=parser.isoparse(time_entry["Time"]),
809
+ assets=assets,
810
+ extra_fields={},
811
+ )
812
+ add_projection_info(
813
+ endpoint_config,
814
+ item,
815
+ )
816
+ ep_st = endpoint_config["Style"]
817
+ style_link = Link(
818
+ rel="style",
819
+ target=ep_st
820
+ if ep_st.startswith("http")
821
+ else f"{catalog_config['assets_endpoint']}/{ep_st}",
822
+ media_type=style_type,
823
+ extra_fields={
824
+ "asset:keys": list(assets),
825
+ },
826
+ )
827
+ item.add_link(style_link)
828
+ link = collection.add_item(item)
829
+ link.extra_fields["datetime"] = time_entry["Time"]
830
+ link.extra_fields["assets"] = [a["File"] for a in time_entry["Assets"]]
831
+ add_collection_information(catalog_config, collection, collection_config)
832
+ collection.update_extent_from_items()
833
+ return collection
@@ -20,6 +20,7 @@ from eodash_catalog.endpoints import (
20
20
  handle_collection_only,
21
21
  handle_custom_endpoint,
22
22
  handle_GeoDB_endpoint,
23
+ handle_raw_source,
23
24
  handle_SH_endpoint,
24
25
  handle_SH_WMS_endpoint,
25
26
  handle_VEDA_endpoint,
@@ -30,6 +31,7 @@ from eodash_catalog.stac_handling import (
30
31
  add_base_overlay_info,
31
32
  add_collection_information,
32
33
  add_extra_fields,
34
+ add_projection_info,
33
35
  get_or_create_collection,
34
36
  )
35
37
  from eodash_catalog.utils import (
@@ -240,10 +242,15 @@ def process_collection_file(
240
242
  collection_config,
241
243
  catalog,
242
244
  )
245
+ elif resource["Name"] in ["COG source", "GeoJSON source"]:
246
+ collection = handle_raw_source(
247
+ catalog_config, resource, collection_config, catalog
248
+ )
243
249
  else:
244
250
  raise ValueError("Type of Resource is not supported")
245
251
  if collection:
246
252
  add_single_item_if_collection_empty(collection)
253
+ add_projection_info(resource, collection)
247
254
  add_to_catalog(collection, catalog, resource, collection_config)
248
255
  else:
249
256
  raise Exception(f"No collection was generated for resource {resource}")
@@ -9,6 +9,7 @@ from pystac import (
9
9
  Catalog,
10
10
  Collection,
11
11
  Extent,
12
+ Item,
12
13
  Link,
13
14
  Provider,
14
15
  SpatialExtent,
@@ -68,7 +69,7 @@ def get_or_create_collection(
68
69
  description = collection_config["Subtitle"]
69
70
  else:
70
71
  # relative path to assets was given
71
- response = requests.get(f"{catalog_config["assets_endpoint"]}/{description}")
72
+ response = requests.get(f'{catalog_config["assets_endpoint"]}/{description}')
72
73
  if response.status_code == 200:
73
74
  description = response.text
74
75
  elif "Subtitle" in collection_config:
@@ -87,36 +88,39 @@ def get_or_create_collection(
87
88
  return collection
88
89
 
89
90
 
90
- def create_web_map_link(layer: dict, role: str) -> Link:
91
+ def create_web_map_link(layer_config: dict, role: str) -> Link:
91
92
  extra_fields = {
92
93
  "roles": [role],
93
- "id": layer["id"],
94
+ "id": layer_config["id"],
94
95
  }
95
- if layer.get("default"):
96
+ if layer_config.get("default"):
96
97
  extra_fields["roles"].append("default")
97
- if layer.get("visible"):
98
+ if layer_config.get("visible"):
98
99
  extra_fields["roles"].append("visible")
99
- if "visible" in layer and not layer["visible"]:
100
+ if "visible" in layer_config and not layer_config["visible"]:
100
101
  extra_fields["roles"].append("invisible")
101
102
 
102
- match layer["protocol"]:
103
+ match layer_config["protocol"].lower():
103
104
  case "wms":
104
105
  # handle wms special config options
105
- extra_fields["wms:layers"] = layer["layers"]
106
- if "styles" in layer:
107
- extra_fields["wms:styles"] = layer["styles"]
108
- # TODO: handle wms dimensions extra_fields["wms:dimensions"]
106
+ extra_fields["wms:layers"] = layer_config["layers"]
107
+ if "styles" in layer_config:
108
+ extra_fields["wms:styles"] = layer_config["styles"]
109
+ if "dimensions" in layer_config:
110
+ extra_fields["wms:dimensions"] = layer_config["dimensions"]
109
111
  case "wmts":
110
- extra_fields["wmts:layer"] = layer["layer"]
111
- # TODO: handle wmts dimensions extra_fields["wmts:dimensions"]
112
+ extra_fields["wmts:layer"] = layer_config["layer"]
113
+ if "dimensions" in layer_config:
114
+ extra_fields["wmts:dimensions"] = layer_config["dimensions"]
112
115
 
113
116
  wml = Link(
114
- rel=layer["protocol"],
115
- target=layer["url"],
116
- media_type=layer.get("media_type", "image/png"),
117
- title=layer["name"],
117
+ rel=layer_config["protocol"],
118
+ target=layer_config["url"],
119
+ media_type=layer_config.get("media_type", "image/png"),
120
+ title=layer_config["name"],
118
121
  extra_fields=extra_fields,
119
122
  )
123
+ add_projection_info(layer_config, wml)
120
124
  return wml
121
125
 
122
126
 
@@ -273,7 +277,7 @@ def add_collection_information(
273
277
  collection.add_asset(
274
278
  "legend",
275
279
  Asset(
276
- href=f"{catalog_config["assets_endpoint"]}/{collection_config["Legend"]}",
280
+ href=f'{catalog_config["assets_endpoint"]}/{collection_config["Legend"]}',
277
281
  media_type="image/png",
278
282
  roles=["metadata"],
279
283
  ),
@@ -282,7 +286,7 @@ def add_collection_information(
282
286
  collection.add_asset(
283
287
  "story",
284
288
  Asset(
285
- href=f"{catalog_config["assets_endpoint"]}/{collection_config["Story"]}",
289
+ href=f'{catalog_config["assets_endpoint"]}/{collection_config["Story"]}',
286
290
  media_type="text/markdown",
287
291
  roles=["metadata"],
288
292
  ),
@@ -291,7 +295,7 @@ def add_collection_information(
291
295
  collection.add_asset(
292
296
  "thumbnail",
293
297
  Asset(
294
- href=f"{catalog_config["assets_endpoint"]}/{collection_config["Image"]}",
298
+ href=f'{catalog_config["assets_endpoint"]}/{collection_config["Image"]}',
295
299
  media_type="image/png",
296
300
  roles=["thumbnail"],
297
301
  ),
@@ -321,25 +325,26 @@ def add_collection_information(
321
325
  def add_base_overlay_info(
322
326
  collection: Collection, catalog_config: dict, collection_config: dict
323
327
  ) -> None:
324
- # check if default base layers defined
325
- if "default_base_layers" in catalog_config:
326
- with open(f"{catalog_config["default_base_layers"]}.yaml") as f:
328
+ # add custom baselayers specially for this indicator
329
+ if "BaseLayers" in collection_config:
330
+ for layer in collection_config["BaseLayers"]:
331
+ collection.add_link(create_web_map_link(layer, role="baselayer"))
332
+ # alternatively use default base layers defined
333
+ elif "default_base_layers" in catalog_config:
334
+ with open(f'{catalog_config["default_base_layers"]}.yaml') as f:
327
335
  base_layers = yaml.load(f, Loader=SafeLoader)
328
336
  for layer in base_layers:
329
337
  collection.add_link(create_web_map_link(layer, role="baselayer"))
338
+ # add custom overlays just for this indicator
339
+ if "OverlayLayers" in collection_config:
340
+ for layer in collection_config["OverlayLayers"]:
341
+ collection.add_link(create_web_map_link(layer, role="overlay"))
330
342
  # check if default overlay layers defined
331
- if "default_overlay_layers" in catalog_config:
343
+ elif "default_overlay_layers" in catalog_config:
332
344
  with open("{}.yaml".format(catalog_config["default_overlay_layers"])) as f:
333
345
  overlay_layers = yaml.load(f, Loader=SafeLoader)
334
346
  for layer in overlay_layers:
335
347
  collection.add_link(create_web_map_link(layer, role="overlay"))
336
- if "BaseLayers" in collection_config:
337
- for layer in collection_config["BaseLayers"]:
338
- collection.add_link(create_web_map_link(layer, role="baselayer"))
339
- if "OverlayLayers" in collection_config:
340
- for layer in collection_config["OverlayLayers"]:
341
- collection.add_link(create_web_map_link(layer, role="overlay"))
342
- # TODO: possibility to overwrite default base and overlay layers
343
348
 
344
349
 
345
350
  def add_extra_fields(stac_object: Collection | Catalog | Link, collection_config: dict) -> None:
@@ -357,8 +362,6 @@ def add_extra_fields(stac_object: Collection | Catalog | Link, collection_config
357
362
  stac_object.extra_fields["sensor"] = collection_config["Sensor"]
358
363
  if "Agency" in collection_config:
359
364
  stac_object.extra_fields["agency"] = collection_config["Agency"]
360
- if "yAxis" in collection_config:
361
- stac_object.extra_fields["yAxis"] = collection_config["yAxis"]
362
365
  if "EodashIdentifier" in collection_config:
363
366
  stac_object.extra_fields["subcode"] = collection_config["EodashIdentifier"]
364
367
  if "DataSource" in collection_config:
@@ -375,6 +378,8 @@ def add_extra_fields(stac_object: Collection | Catalog | Link, collection_config
375
378
  stac_object.extra_fields["insituSources"] = collection_config["DataSource"]["InSitu"]
376
379
  if "Other" in collection_config["DataSource"]:
377
380
  stac_object.extra_fields["otherSources"] = collection_config["DataSource"]["Other"]
381
+ if "MapProjection" in collection_config:
382
+ stac_object.extra_fields["mapProjection"] = collection_config["MapProjection"]
378
383
 
379
384
 
380
385
  def get_collection_times_from_config(endpoint_config: dict) -> list[str]:
@@ -388,3 +393,29 @@ def get_collection_times_from_config(endpoint_config: dict) -> list[str]:
388
393
  timedelta_config = endpoint_config["DateTimeInterval"].get("Timedelta", {"days": 1})
389
394
  times = generateDateIsostringsFromInterval(start, end, timedelta_config)
390
395
  return times
396
+
397
+
398
+ def add_projection_info(
399
+ endpoint_config: dict, stac_object: Item | Asset | Collection | Link
400
+ ) -> None:
401
+ if proj := endpoint_config.get("DataProjection"):
402
+ if isinstance(proj, str):
403
+ if proj.lower().startswith("epsg"):
404
+ # consider input such as "EPSG:4326"
405
+ proj = proj.lower().split("EPSG:")[1]
406
+ # consider a number only
407
+ proj = int(proj)
408
+ if isinstance(proj, int):
409
+ # only set if not existing on source stac_object
410
+ if not stac_object.extra_fields.get("proj:epsg"):
411
+ # handling EPSG code for "proj:epsg"
412
+ stac_object.extra_fields["proj:epsg"] = proj
413
+ elif isinstance(proj, dict):
414
+ # custom handling due to incompatibility of proj4js supported syntax (WKT1)
415
+ # and STAC supported syntax (projjson or WKT2)
416
+ # so we are taking over the DataProjection as is and deal with it in the eodash client
417
+ # in a non-standard compliant way
418
+ # https://github.com/proj4js/proj4js/issues/400
419
+ stac_object.extra_fields["proj4_def"] = proj
420
+ else:
421
+ raise Exception(f"Incorrect type of proj definition {proj}")
eodash_catalog/utils.py CHANGED
@@ -35,6 +35,21 @@ def create_geojson_point(lon: int | float, lat: int | float) -> dict[str, Any]:
35
35
  return {"type": "Feature", "geometry": point, "properties": {}}
36
36
 
37
37
 
38
+ def create_geojson_from_bbox(bbox: list[float | int]) -> dict:
39
+ coordinates = [
40
+ [bbox[0], bbox[1]],
41
+ [bbox[2], bbox[1]],
42
+ [bbox[2], bbox[3]],
43
+ [bbox[0], bbox[3]],
44
+ [bbox[0], bbox[1]],
45
+ ]
46
+ polygon = {"type": "Polygon", "coordinates": [coordinates]}
47
+
48
+ feature = {"type": "Feature", "geometry": polygon, "properties": {}}
49
+ feature_collection = {"type": "FeatureCollection", "features": [feature]}
50
+ return feature_collection
51
+
52
+
38
53
  def retrieveExtentFromWMSWMTS(
39
54
  capabilities_url: str, layer: str, version: str = "1.1.1", wmts: bool = False
40
55
  ) -> tuple[list[float], list[str]]:
@@ -206,7 +221,7 @@ def generate_veda_cog_link(endpoint_config: dict, file_url: str | None) -> str:
206
221
 
207
222
  file_url = f"url={file_url}&" if file_url else ""
208
223
 
209
- target_url = f"https://staging-raster.delta-backend.com/cog/tiles/WebMercatorQuad/{{z}}/{{x}}/{{y}}?{file_url}resampling_method=nearest{bidx}{colormap}{colormap_name}{rescale}"
224
+ target_url = f"https://openveda.cloud/cog/tiles/WebMercatorQuad/{{z}}/{{x}}/{{y}}?{file_url}resampling_method=nearest{bidx}{colormap}{colormap_name}{rescale}"
210
225
  return target_url
211
226
 
212
227
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: eodash_catalog
3
- Version: 0.0.12
3
+ Version: 0.0.14
4
4
  Summary: This package is intended to help create a compatible STAC catalog for the eodash dashboard client. It supports configuration of multiple endpoint types for information extraction.
5
5
  Project-URL: Documentation, https://github.com/eodash/eodash_catalog#readme
6
6
  Project-URL: Issues, https://github.com/eodash/eodash_catalog/issues
@@ -0,0 +1,14 @@
1
+ eodash_catalog/__about__.py,sha256=w0H7WN2arq5UEWqr5ssYyzDn4_yv6Mow9z2xHCP--Ng,138
2
+ eodash_catalog/__init__.py,sha256=_W_9emPYf6FUqc0P8L2SmADx6hGSd7PlQV3yRmCk5uM,115
3
+ eodash_catalog/duration.py,sha256=B6XOZfvNU7SuqpxuVtT1kNKODoOQJXDI6mocvA_U1ik,10816
4
+ eodash_catalog/endpoints.py,sha256=8D1mLSDwivK6_Ub3hHAtiVMtmtHatd1Ffxhw8rm_BqU,33307
5
+ eodash_catalog/generate_indicators.py,sha256=DlbgMuNVPGdg8roYO7raXIeTmsMRpsyN71DYnRATATI,18790
6
+ eodash_catalog/sh_endpoint.py,sha256=vELooJwk269v1DNnOzb32vil96vL_SRCio8UBlx10N0,618
7
+ eodash_catalog/stac_handling.py,sha256=BKFFhM2JhrBnEd6EbEESAIUcFncDaAb5N4aHx0uZBv4,18197
8
+ eodash_catalog/thumbnails.py,sha256=31Wk38oNQDxfhSUbMLBpHuZFhsR8v_7luYr65XQtDf0,2213
9
+ eodash_catalog/utils.py,sha256=FZIn3iwMNjdaGAlbaIHPffYkAhlIHpNU2vkMJmEcYRQ,8427
10
+ eodash_catalog-0.0.14.dist-info/METADATA,sha256=9uitoo4TTQi-DzVXTZpD3k81DCXQWKcVm9wbn6iI-eQ,3203
11
+ eodash_catalog-0.0.14.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
12
+ eodash_catalog-0.0.14.dist-info/entry_points.txt,sha256=kuUQrDG1PtYd8kPjf5XM6H_NtQd9Ozwl0jjiGtAvZSM,87
13
+ eodash_catalog-0.0.14.dist-info/licenses/LICENSE.txt,sha256=oJCW5zQxnFD-J0hGz6Zh5Lkpdk1oAndmWhseTmV224E,1107
14
+ eodash_catalog-0.0.14.dist-info/RECORD,,
@@ -1,14 +0,0 @@
1
- eodash_catalog/__about__.py,sha256=yEv4frNTeX9AbT5lO3thIJjP9R5pEQJ0AzODE6sN9tI,138
2
- eodash_catalog/__init__.py,sha256=_W_9emPYf6FUqc0P8L2SmADx6hGSd7PlQV3yRmCk5uM,115
3
- eodash_catalog/duration.py,sha256=B6XOZfvNU7SuqpxuVtT1kNKODoOQJXDI6mocvA_U1ik,10816
4
- eodash_catalog/endpoints.py,sha256=o0m0dMmfvZ2ybRnHW-am4g4vjoQhFMNbnQ_xI2kE_D8,30658
5
- eodash_catalog/generate_indicators.py,sha256=aTh7RHhUVfDjaWNH4GYiLuzC7Z8fQEfJGfckdkjwFOs,18454
6
- eodash_catalog/sh_endpoint.py,sha256=vELooJwk269v1DNnOzb32vil96vL_SRCio8UBlx10N0,618
7
- eodash_catalog/stac_handling.py,sha256=uHsAR-H3Js2pDOcIw2ApTUWAuSApnLkpjf0OUBHQ7_Q,16637
8
- eodash_catalog/thumbnails.py,sha256=31Wk38oNQDxfhSUbMLBpHuZFhsR8v_7luYr65XQtDf0,2213
9
- eodash_catalog/utils.py,sha256=JnXrXtq3bOmECPlSn86Mz35sDTOkgptz87lrISfE1Uo,7968
10
- eodash_catalog-0.0.12.dist-info/METADATA,sha256=1kYgr6SXzhIj2nE-EsoRwpw-bMJVYdz_sVLLCaZzT08,3203
11
- eodash_catalog-0.0.12.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
12
- eodash_catalog-0.0.12.dist-info/entry_points.txt,sha256=kuUQrDG1PtYd8kPjf5XM6H_NtQd9Ozwl0jjiGtAvZSM,87
13
- eodash_catalog-0.0.12.dist-info/licenses/LICENSE.txt,sha256=oJCW5zQxnFD-J0hGz6Zh5Lkpdk1oAndmWhseTmV224E,1107
14
- eodash_catalog-0.0.12.dist-info/RECORD,,