eodag 3.1.0b1__py3-none-any.whl → 3.2.0__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.
- eodag/api/core.py +69 -63
- eodag/api/product/_assets.py +49 -13
- eodag/api/product/_product.py +41 -30
- eodag/api/product/drivers/__init__.py +81 -4
- eodag/api/product/drivers/base.py +65 -4
- eodag/api/product/drivers/generic.py +65 -0
- eodag/api/product/drivers/sentinel1.py +97 -0
- eodag/api/product/drivers/sentinel2.py +95 -0
- eodag/api/product/metadata_mapping.py +85 -79
- eodag/api/search_result.py +13 -23
- eodag/cli.py +4 -4
- eodag/config.py +77 -80
- eodag/plugins/apis/base.py +1 -1
- eodag/plugins/apis/ecmwf.py +12 -15
- eodag/plugins/apis/usgs.py +12 -11
- eodag/plugins/authentication/aws_auth.py +16 -13
- eodag/plugins/authentication/base.py +5 -3
- eodag/plugins/authentication/header.py +3 -3
- eodag/plugins/authentication/keycloak.py +4 -4
- eodag/plugins/authentication/oauth.py +7 -3
- eodag/plugins/authentication/openid_connect.py +20 -14
- eodag/plugins/authentication/sas_auth.py +4 -4
- eodag/plugins/authentication/token.py +7 -7
- eodag/plugins/authentication/token_exchange.py +1 -1
- eodag/plugins/base.py +4 -4
- eodag/plugins/crunch/base.py +4 -4
- eodag/plugins/crunch/filter_date.py +4 -4
- eodag/plugins/crunch/filter_latest_intersect.py +6 -6
- eodag/plugins/crunch/filter_latest_tpl_name.py +7 -7
- eodag/plugins/crunch/filter_overlap.py +4 -4
- eodag/plugins/crunch/filter_property.py +4 -4
- eodag/plugins/download/aws.py +137 -77
- eodag/plugins/download/base.py +8 -17
- eodag/plugins/download/creodias_s3.py +2 -2
- eodag/plugins/download/http.py +30 -32
- eodag/plugins/download/s3rest.py +5 -4
- eodag/plugins/manager.py +10 -20
- eodag/plugins/search/__init__.py +6 -5
- eodag/plugins/search/base.py +38 -42
- eodag/plugins/search/build_search_result.py +286 -336
- eodag/plugins/search/cop_marine.py +22 -12
- eodag/plugins/search/creodias_s3.py +8 -78
- eodag/plugins/search/csw.py +11 -11
- eodag/plugins/search/data_request_search.py +19 -18
- eodag/plugins/search/qssearch.py +84 -151
- eodag/plugins/search/stac_list_assets.py +85 -0
- eodag/plugins/search/static_stac_search.py +4 -4
- eodag/resources/ext_product_types.json +1 -1
- eodag/resources/product_types.yml +848 -398
- eodag/resources/providers.yml +1038 -1115
- eodag/resources/stac_api.yml +2 -2
- eodag/resources/user_conf_template.yml +10 -9
- eodag/rest/cache.py +2 -2
- eodag/rest/config.py +3 -3
- eodag/rest/core.py +24 -24
- eodag/rest/errors.py +5 -5
- eodag/rest/server.py +3 -11
- eodag/rest/stac.py +41 -38
- eodag/rest/types/collections_search.py +3 -3
- eodag/rest/types/eodag_search.py +23 -23
- eodag/rest/types/queryables.py +40 -28
- eodag/rest/types/stac_search.py +15 -25
- eodag/rest/utils/__init__.py +11 -21
- eodag/rest/utils/cql_evaluate.py +6 -6
- eodag/rest/utils/rfc3339.py +2 -2
- eodag/types/__init__.py +97 -29
- eodag/types/bbox.py +2 -2
- eodag/types/download_args.py +2 -2
- eodag/types/queryables.py +5 -2
- eodag/types/search_args.py +4 -4
- eodag/types/whoosh.py +1 -3
- eodag/utils/__init__.py +82 -41
- eodag/utils/exceptions.py +2 -2
- eodag/utils/import_system.py +2 -2
- eodag/utils/requests.py +2 -2
- eodag/utils/rest.py +2 -2
- eodag/utils/s3.py +231 -0
- eodag/utils/stac_reader.py +10 -10
- {eodag-3.1.0b1.dist-info → eodag-3.2.0.dist-info}/METADATA +12 -10
- eodag-3.2.0.dist-info/RECORD +113 -0
- {eodag-3.1.0b1.dist-info → eodag-3.2.0.dist-info}/WHEEL +1 -1
- {eodag-3.1.0b1.dist-info → eodag-3.2.0.dist-info}/entry_points.txt +1 -0
- eodag-3.1.0b1.dist-info/RECORD +0 -108
- {eodag-3.1.0b1.dist-info → eodag-3.2.0.dist-info/licenses}/LICENSE +0 -0
- {eodag-3.1.0b1.dist-info → eodag-3.2.0.dist-info}/top_level.txt +0 -0
|
@@ -23,19 +23,7 @@ import logging
|
|
|
23
23
|
import re
|
|
24
24
|
from datetime import datetime, timedelta
|
|
25
25
|
from string import Formatter
|
|
26
|
-
from typing import
|
|
27
|
-
TYPE_CHECKING,
|
|
28
|
-
Any,
|
|
29
|
-
AnyStr,
|
|
30
|
-
Callable,
|
|
31
|
-
Dict,
|
|
32
|
-
Iterator,
|
|
33
|
-
List,
|
|
34
|
-
Optional,
|
|
35
|
-
Tuple,
|
|
36
|
-
Union,
|
|
37
|
-
cast,
|
|
38
|
-
)
|
|
26
|
+
from typing import TYPE_CHECKING, Any, AnyStr, Callable, Iterator, Optional, Union, cast
|
|
39
27
|
|
|
40
28
|
import geojson
|
|
41
29
|
import orjson
|
|
@@ -61,6 +49,7 @@ from eodag.utils import (
|
|
|
61
49
|
get_timestamp,
|
|
62
50
|
items_recursive_apply,
|
|
63
51
|
nested_pairs2dict,
|
|
52
|
+
sanitize,
|
|
64
53
|
string_to_jsonpath,
|
|
65
54
|
update_nested_dict,
|
|
66
55
|
)
|
|
@@ -88,8 +77,8 @@ DEFAULT_GEOMETRY = "POLYGON((180 -90, 180 90, -180 90, -180 -90, 180 -90))"
|
|
|
88
77
|
|
|
89
78
|
|
|
90
79
|
def get_metadata_path(
|
|
91
|
-
map_value: Union[str,
|
|
92
|
-
) ->
|
|
80
|
+
map_value: Union[str, list[str]],
|
|
81
|
+
) -> tuple[Union[list[str], None], str]:
|
|
93
82
|
"""Return the jsonpath or xpath to the value of a EO product metadata in a provider
|
|
94
83
|
search result.
|
|
95
84
|
|
|
@@ -137,12 +126,12 @@ def get_metadata_path(
|
|
|
137
126
|
return None, path
|
|
138
127
|
|
|
139
128
|
|
|
140
|
-
def get_metadata_path_value(map_value: Union[str,
|
|
129
|
+
def get_metadata_path_value(map_value: Union[str, list[str]]) -> str:
|
|
141
130
|
"""Get raw metadata path without converter"""
|
|
142
131
|
return map_value[1] if isinstance(map_value, list) else map_value
|
|
143
132
|
|
|
144
133
|
|
|
145
|
-
def get_search_param(map_value:
|
|
134
|
+
def get_search_param(map_value: list[str]) -> str:
|
|
146
135
|
"""See :func:`~eodag.api.product.metadata_mapping.get_metadata_path`
|
|
147
136
|
|
|
148
137
|
:param map_value: The value originating from the definition of `metadata_mapping`
|
|
@@ -188,6 +177,7 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
188
177
|
- ``split_corine_id``: get the product type by splitting the product id
|
|
189
178
|
- ``to_datetime_dict``: convert a datetime string to a dictionary where values are either a string or a list
|
|
190
179
|
- ``get_ecmwf_time``: get the time of a datetime string in the ECMWF format
|
|
180
|
+
- ``sanitize``: sanitize string
|
|
191
181
|
|
|
192
182
|
:param search_param: The string to be formatted
|
|
193
183
|
:param args: (optional) Additional arguments to use in the formatting process
|
|
@@ -335,7 +325,7 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
335
325
|
return wkt_value
|
|
336
326
|
|
|
337
327
|
@staticmethod
|
|
338
|
-
def convert_to_bounds_lists(input_geom: BaseGeometry) ->
|
|
328
|
+
def convert_to_bounds_lists(input_geom: BaseGeometry) -> list[list[float]]:
|
|
339
329
|
if isinstance(input_geom, MultiPolygon):
|
|
340
330
|
geoms = [geom for geom in input_geom.geoms]
|
|
341
331
|
# sort with larger one at first (stac-browser only plots first one)
|
|
@@ -345,7 +335,7 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
345
335
|
return [list(input_geom.bounds[0:4])]
|
|
346
336
|
|
|
347
337
|
@staticmethod
|
|
348
|
-
def convert_to_bounds(input_geom_unformatted: Any) ->
|
|
338
|
+
def convert_to_bounds(input_geom_unformatted: Any) -> list[float]:
|
|
349
339
|
input_geom = get_geometry_from_various(geometry=input_geom_unformatted)
|
|
350
340
|
if isinstance(input_geom, MultiPolygon):
|
|
351
341
|
geoms = [geom for geom in input_geom.geoms]
|
|
@@ -356,16 +346,16 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
356
346
|
max_lon = -180
|
|
357
347
|
max_lat = -90
|
|
358
348
|
for geom in geoms:
|
|
359
|
-
min_lon = min(min_lon, geom.
|
|
360
|
-
min_lat = min(min_lat, geom.
|
|
361
|
-
max_lon = max(max_lon, geom.
|
|
362
|
-
max_lat = max(max_lat, geom.
|
|
349
|
+
min_lon = min(min_lon, geom.bounds[0])
|
|
350
|
+
min_lat = min(min_lat, geom.bounds[1])
|
|
351
|
+
max_lon = max(max_lon, geom.bounds[2])
|
|
352
|
+
max_lat = max(max_lat, geom.bounds[3])
|
|
363
353
|
return [min_lon, min_lat, max_lon, max_lat]
|
|
364
354
|
else:
|
|
365
355
|
return list(input_geom.bounds[0:4])
|
|
366
356
|
|
|
367
357
|
@staticmethod
|
|
368
|
-
def convert_to_nwse_bounds(input_geom: BaseGeometry) ->
|
|
358
|
+
def convert_to_nwse_bounds(input_geom: BaseGeometry) -> list[float]:
|
|
369
359
|
if isinstance(input_geom, str):
|
|
370
360
|
input_geom = shapely.wkt.loads(input_geom)
|
|
371
361
|
return list(input_geom.bounds[-1:] + input_geom.bounds[:-1])
|
|
@@ -379,8 +369,8 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
379
369
|
)
|
|
380
370
|
|
|
381
371
|
@staticmethod
|
|
382
|
-
def convert_to_geojson(
|
|
383
|
-
return geojson.dumps(
|
|
372
|
+
def convert_to_geojson(value: Any) -> str:
|
|
373
|
+
return geojson.dumps(value)
|
|
384
374
|
|
|
385
375
|
@staticmethod
|
|
386
376
|
def convert_from_ewkt(ewkt_string: str) -> Union[BaseGeometry, str]:
|
|
@@ -449,7 +439,7 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
449
439
|
else:
|
|
450
440
|
yield e
|
|
451
441
|
|
|
452
|
-
polygons_list:
|
|
442
|
+
polygons_list: list[Polygon] = []
|
|
453
443
|
for elem in flatten_elements(georss[0]):
|
|
454
444
|
coords_list = elem.text.split()
|
|
455
445
|
polygon_args = [
|
|
@@ -474,7 +464,7 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
474
464
|
@staticmethod
|
|
475
465
|
def convert_to_longitude_latitude(
|
|
476
466
|
input_geom_unformatted: Any,
|
|
477
|
-
) ->
|
|
467
|
+
) -> dict[str, float]:
|
|
478
468
|
bounds = MetadataFormatter.convert_to_bounds(input_geom_unformatted)
|
|
479
469
|
lon = (bounds[0] + bounds[2]) / 2
|
|
480
470
|
lat = (bounds[1] + bounds[3]) / 2
|
|
@@ -508,14 +498,21 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
508
498
|
return NOT_AVAILABLE
|
|
509
499
|
|
|
510
500
|
@staticmethod
|
|
511
|
-
def convert_replace_str(
|
|
501
|
+
def convert_replace_str(value: Any, args: str) -> str:
|
|
502
|
+
if isinstance(value, dict):
|
|
503
|
+
value = MetadataFormatter.convert_to_geojson(value)
|
|
504
|
+
elif not isinstance(value, str):
|
|
505
|
+
raise TypeError(
|
|
506
|
+
f"convert_replace_str expects a string or a dict (apply to_geojson). Got {type(value)}"
|
|
507
|
+
)
|
|
508
|
+
|
|
512
509
|
old, new = ast.literal_eval(args)
|
|
513
|
-
return re.sub(old, new,
|
|
510
|
+
return re.sub(old, new, value)
|
|
514
511
|
|
|
515
512
|
@staticmethod
|
|
516
513
|
def convert_recursive_sub_str(
|
|
517
|
-
input_obj: Union[
|
|
518
|
-
) -> Union[
|
|
514
|
+
input_obj: Union[dict[Any, Any], list[Any]], args: str
|
|
515
|
+
) -> Union[dict[Any, Any], list[Any]]:
|
|
519
516
|
old, new = ast.literal_eval(args)
|
|
520
517
|
return items_recursive_apply(
|
|
521
518
|
input_obj,
|
|
@@ -525,8 +522,8 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
525
522
|
|
|
526
523
|
@staticmethod
|
|
527
524
|
def convert_dict_update(
|
|
528
|
-
input_dict:
|
|
529
|
-
) ->
|
|
525
|
+
input_dict: dict[Any, Any], args: str
|
|
526
|
+
) -> dict[Any, Any]:
|
|
530
527
|
"""Converts"""
|
|
531
528
|
new_items_list = ast.literal_eval(args)
|
|
532
529
|
|
|
@@ -536,8 +533,8 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
536
533
|
|
|
537
534
|
@staticmethod
|
|
538
535
|
def convert_dict_filter(
|
|
539
|
-
input_dict:
|
|
540
|
-
) ->
|
|
536
|
+
input_dict: dict[Any, Any], jsonpath_filter_str: str
|
|
537
|
+
) -> dict[Any, Any]:
|
|
541
538
|
"""Fitlers dict items using jsonpath"""
|
|
542
539
|
|
|
543
540
|
jsonpath_filter = string_to_jsonpath(jsonpath_filter_str, force=True)
|
|
@@ -616,8 +613,8 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
616
613
|
return NOT_AVAILABLE
|
|
617
614
|
|
|
618
615
|
@staticmethod
|
|
619
|
-
def convert_split_id_into_s1_params(product_id: str) ->
|
|
620
|
-
parts:
|
|
616
|
+
def convert_split_id_into_s1_params(product_id: str) -> dict[str, str]:
|
|
617
|
+
parts: list[str] = re.split(r"_(?!_)", product_id)
|
|
621
618
|
if len(parts) < 9:
|
|
622
619
|
logger.error(
|
|
623
620
|
"id %s does not match expected Sentinel-1 id format", product_id
|
|
@@ -651,8 +648,8 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
651
648
|
return params
|
|
652
649
|
|
|
653
650
|
@staticmethod
|
|
654
|
-
def convert_split_id_into_s3_params(product_id: str) ->
|
|
655
|
-
parts:
|
|
651
|
+
def convert_split_id_into_s3_params(product_id: str) -> dict[str, str]:
|
|
652
|
+
parts: list[str] = re.split(r"_(?!_)", product_id)
|
|
656
653
|
params = {"productType": product_id[4:15]}
|
|
657
654
|
dates = re.findall("[0-9]{8}T[0-9]{6}", product_id)
|
|
658
655
|
start_date = datetime.strptime(dates[0], "%Y%m%dT%H%M%S") - timedelta(
|
|
@@ -668,8 +665,8 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
668
665
|
return params
|
|
669
666
|
|
|
670
667
|
@staticmethod
|
|
671
|
-
def convert_split_id_into_s5p_params(product_id: str) ->
|
|
672
|
-
parts:
|
|
668
|
+
def convert_split_id_into_s5p_params(product_id: str) -> dict[str, str]:
|
|
669
|
+
parts: list[str] = re.split(r"_(?!_)", product_id)
|
|
673
670
|
params = {
|
|
674
671
|
"productType": product_id[9:19],
|
|
675
672
|
"processingMode": parts[1],
|
|
@@ -686,7 +683,7 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
686
683
|
return params
|
|
687
684
|
|
|
688
685
|
@staticmethod
|
|
689
|
-
def convert_split_cop_dem_id(product_id: str) ->
|
|
686
|
+
def convert_split_cop_dem_id(product_id: str) -> list[int]:
|
|
690
687
|
parts = product_id.split("_")
|
|
691
688
|
lattitude = parts[3]
|
|
692
689
|
longitude = parts[5]
|
|
@@ -725,7 +722,7 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
725
722
|
@staticmethod
|
|
726
723
|
def convert_to_datetime_dict(
|
|
727
724
|
date: str, format: str
|
|
728
|
-
) ->
|
|
725
|
+
) -> dict[str, Union[list[str], str]]:
|
|
729
726
|
"""Convert a date (str) to a dictionary where values are in the format given in argument
|
|
730
727
|
|
|
731
728
|
date == "2021-04-21T18:27:19.123Z" and format == "list" => {
|
|
@@ -777,7 +774,7 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
777
774
|
@staticmethod
|
|
778
775
|
def convert_interval_to_datetime_dict(
|
|
779
776
|
date: str, separator: str = "/"
|
|
780
|
-
) ->
|
|
777
|
+
) -> dict[str, list[str]]:
|
|
781
778
|
"""Convert a date interval ('/' separated str) to a dictionary where values are lists
|
|
782
779
|
|
|
783
780
|
date == "2021-04-21/2021-04-22" => {
|
|
@@ -817,7 +814,7 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
817
814
|
}
|
|
818
815
|
|
|
819
816
|
@staticmethod
|
|
820
|
-
def convert_get_ecmwf_time(date: str) ->
|
|
817
|
+
def convert_get_ecmwf_time(date: str) -> list[str]:
|
|
821
818
|
"""Get the time of a date (str) in the ECMWF format (["HH:00"])
|
|
822
819
|
|
|
823
820
|
"2021-04-21T18:27:19.123Z" => ["18:00"]
|
|
@@ -828,6 +825,11 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
828
825
|
+ ":00"
|
|
829
826
|
]
|
|
830
827
|
|
|
828
|
+
@staticmethod
|
|
829
|
+
def convert_sanitize(text: str) -> str:
|
|
830
|
+
"""Sanitize string"""
|
|
831
|
+
return sanitize(text)
|
|
832
|
+
|
|
831
833
|
@staticmethod
|
|
832
834
|
def convert_get_dates_from_string(text: str, split_param="-"):
|
|
833
835
|
reg = "[0-9]{8}" + split_param + "[0-9]{8}"
|
|
@@ -861,8 +863,8 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
861
863
|
|
|
862
864
|
@staticmethod
|
|
863
865
|
def convert_assets_list_to_dict(
|
|
864
|
-
assets_list:
|
|
865
|
-
) ->
|
|
866
|
+
assets_list: list[dict[str, str]], asset_name_key: str = "title"
|
|
867
|
+
) -> dict[str, dict[str, str]]:
|
|
866
868
|
"""Convert a list of assets to a dictionary where keys represent
|
|
867
869
|
name of assets and are found among values of asset dictionaries.
|
|
868
870
|
|
|
@@ -889,8 +891,8 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
889
891
|
"asset3": {"href": "qux", "title": "qux-title", "name": "asset3"},
|
|
890
892
|
}
|
|
891
893
|
"""
|
|
892
|
-
asset_names:
|
|
893
|
-
assets_dict:
|
|
894
|
+
asset_names: list[str] = []
|
|
895
|
+
assets_dict: dict[str, dict[str, str]] = {}
|
|
894
896
|
|
|
895
897
|
for asset in assets_list:
|
|
896
898
|
asset_name = asset[asset_name_key]
|
|
@@ -899,7 +901,7 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
899
901
|
|
|
900
902
|
# we only keep the equivalent of the path basename in the case where the
|
|
901
903
|
# asset name has a path pattern and this basename is only found once
|
|
902
|
-
immutable_asset_indexes:
|
|
904
|
+
immutable_asset_indexes: list[int] = []
|
|
903
905
|
for i, asset_name in enumerate(asset_names):
|
|
904
906
|
if i in immutable_asset_indexes:
|
|
905
907
|
continue
|
|
@@ -925,10 +927,10 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
925
927
|
|
|
926
928
|
|
|
927
929
|
def properties_from_json(
|
|
928
|
-
json:
|
|
929
|
-
mapping:
|
|
930
|
-
discovery_config: Optional[
|
|
931
|
-
) ->
|
|
930
|
+
json: dict[str, Any],
|
|
931
|
+
mapping: dict[str, Any],
|
|
932
|
+
discovery_config: Optional[dict[str, Any]] = None,
|
|
933
|
+
) -> dict[str, Any]:
|
|
932
934
|
"""Extract properties from a provider json result.
|
|
933
935
|
|
|
934
936
|
:param json: The representation of a provider result as a json object
|
|
@@ -941,7 +943,7 @@ def properties_from_json(
|
|
|
941
943
|
`discovery_path` (String representation of jsonpath)
|
|
942
944
|
:returns: The metadata of the :class:`~eodag.api.product._product.EOProduct`
|
|
943
945
|
"""
|
|
944
|
-
properties:
|
|
946
|
+
properties: dict[str, Any] = {}
|
|
945
947
|
templates = {}
|
|
946
948
|
used_jsonpaths = []
|
|
947
949
|
for metadata, value in mapping.items():
|
|
@@ -1085,8 +1087,8 @@ def properties_from_xml(
|
|
|
1085
1087
|
xml_as_text: AnyStr,
|
|
1086
1088
|
mapping: Any,
|
|
1087
1089
|
empty_ns_prefix: str = "ns",
|
|
1088
|
-
discovery_config: Optional[
|
|
1089
|
-
) ->
|
|
1090
|
+
discovery_config: Optional[dict[str, Any]] = None,
|
|
1091
|
+
) -> dict[str, Any]:
|
|
1090
1092
|
"""Extract properties from a provider xml result.
|
|
1091
1093
|
|
|
1092
1094
|
:param xml_as_text: The representation of a provider result as xml
|
|
@@ -1104,7 +1106,7 @@ def properties_from_xml(
|
|
|
1104
1106
|
`discovery_path` (String representation of xpath)
|
|
1105
1107
|
:returns: the metadata of the :class:`~eodag.api.product._product.EOProduct`
|
|
1106
1108
|
"""
|
|
1107
|
-
properties:
|
|
1109
|
+
properties: dict[str, Any] = {}
|
|
1108
1110
|
templates = {}
|
|
1109
1111
|
used_xpaths = []
|
|
1110
1112
|
root = etree.XML(xml_as_text)
|
|
@@ -1232,10 +1234,10 @@ def properties_from_xml(
|
|
|
1232
1234
|
|
|
1233
1235
|
|
|
1234
1236
|
def mtd_cfg_as_conversion_and_querypath(
|
|
1235
|
-
src_dict:
|
|
1236
|
-
dest_dict:
|
|
1237
|
+
src_dict: dict[str, Any],
|
|
1238
|
+
dest_dict: dict[str, Any] = {},
|
|
1237
1239
|
result_type: str = "json",
|
|
1238
|
-
) ->
|
|
1240
|
+
) -> dict[str, Any]:
|
|
1239
1241
|
"""Metadata configuration dictionary to querypath with conversion dictionary
|
|
1240
1242
|
Transform every src_dict value from jsonpath_str to tuple `(conversion, jsonpath_object)`
|
|
1241
1243
|
or from xpath_str to tuple `(conversion, xpath_str)`
|
|
@@ -1283,8 +1285,8 @@ def mtd_cfg_as_conversion_and_querypath(
|
|
|
1283
1285
|
|
|
1284
1286
|
|
|
1285
1287
|
def format_query_params(
|
|
1286
|
-
product_type: str, config: PluginConfig, query_dict:
|
|
1287
|
-
) ->
|
|
1288
|
+
product_type: str, config: PluginConfig, query_dict: dict[str, Any]
|
|
1289
|
+
) -> dict[str, Any]:
|
|
1288
1290
|
"""format the search parameters to query parameters"""
|
|
1289
1291
|
if "raise_errors" in query_dict.keys():
|
|
1290
1292
|
del query_dict["raise_errors"]
|
|
@@ -1296,7 +1298,7 @@ def format_query_params(
|
|
|
1296
1298
|
**config.products.get(product_type, {}).get("metadata_mapping", {}),
|
|
1297
1299
|
)
|
|
1298
1300
|
|
|
1299
|
-
query_params:
|
|
1301
|
+
query_params: dict[str, Any] = {}
|
|
1300
1302
|
# Get all the search parameters that are recognised as queryables by the
|
|
1301
1303
|
# provider (they appear in the queryables dictionary)
|
|
1302
1304
|
queryables = _get_queryables(query_dict, config, product_type_metadata_mapping)
|
|
@@ -1326,8 +1328,8 @@ def format_query_params(
|
|
|
1326
1328
|
query_params[eodag_search_key] = formatted_query_param
|
|
1327
1329
|
else:
|
|
1328
1330
|
provider_search_key, provider_value = parts
|
|
1329
|
-
query_params
|
|
1330
|
-
|
|
1331
|
+
query_params[provider_search_key] = format_metadata(
|
|
1332
|
+
provider_value, product_type, **query_dict
|
|
1331
1333
|
)
|
|
1332
1334
|
else:
|
|
1333
1335
|
query_params[provider_search_key] = user_input
|
|
@@ -1386,10 +1388,10 @@ def _resolve_hashes(formatted_query_param: str) -> str:
|
|
|
1386
1388
|
|
|
1387
1389
|
|
|
1388
1390
|
def _format_free_text_search(
|
|
1389
|
-
config: PluginConfig, metadata_mapping:
|
|
1390
|
-
) ->
|
|
1391
|
+
config: PluginConfig, metadata_mapping: dict[str, Any], **kwargs: Any
|
|
1392
|
+
) -> dict[str, Any]:
|
|
1391
1393
|
"""Build the free text search parameter using the search parameters"""
|
|
1392
|
-
query_params:
|
|
1394
|
+
query_params: dict[str, Any] = {}
|
|
1393
1395
|
if not getattr(config, "free_text_search_operations", None):
|
|
1394
1396
|
return query_params
|
|
1395
1397
|
for param, operations_config in config.free_text_search_operations.items():
|
|
@@ -1428,13 +1430,13 @@ def _format_free_text_search(
|
|
|
1428
1430
|
|
|
1429
1431
|
|
|
1430
1432
|
def _get_queryables(
|
|
1431
|
-
search_params:
|
|
1433
|
+
search_params: dict[str, Any],
|
|
1432
1434
|
config: PluginConfig,
|
|
1433
|
-
metadata_mapping:
|
|
1434
|
-
) ->
|
|
1435
|
+
metadata_mapping: dict[str, Any],
|
|
1436
|
+
) -> dict[str, Any]:
|
|
1435
1437
|
"""Retrieve the metadata mappings that are query-able"""
|
|
1436
1438
|
logger.debug("Retrieving queryable metadata from metadata_mapping")
|
|
1437
|
-
queryables:
|
|
1439
|
+
queryables: dict[str, Any] = {}
|
|
1438
1440
|
for eodag_search_key, user_input in search_params.items():
|
|
1439
1441
|
if user_input is not None:
|
|
1440
1442
|
md_mapping = metadata_mapping.get(eodag_search_key, (None, NOT_MAPPED))
|
|
@@ -1481,7 +1483,7 @@ def _get_queryables(
|
|
|
1481
1483
|
|
|
1482
1484
|
|
|
1483
1485
|
def get_queryable_from_provider(
|
|
1484
|
-
provider_queryable: str, metadata_mapping:
|
|
1486
|
+
provider_queryable: str, metadata_mapping: dict[str, Union[str, list[str]]]
|
|
1485
1487
|
) -> Optional[str]:
|
|
1486
1488
|
"""Get EODAG configured queryable parameter from provider queryable parameter
|
|
1487
1489
|
|
|
@@ -1499,13 +1501,17 @@ def get_queryable_from_provider(
|
|
|
1499
1501
|
ind = mapping_values.index(provider_queryable)
|
|
1500
1502
|
return Queryables.get_queryable_from_alias(list(metadata_mapping.keys())[ind])
|
|
1501
1503
|
for param, param_conf in metadata_mapping.items():
|
|
1502
|
-
if
|
|
1504
|
+
if (
|
|
1505
|
+
isinstance(param_conf, list)
|
|
1506
|
+
and param_conf[0]
|
|
1507
|
+
and re.search(pattern, param_conf[0])
|
|
1508
|
+
):
|
|
1503
1509
|
return Queryables.get_queryable_from_alias(param)
|
|
1504
1510
|
return None
|
|
1505
1511
|
|
|
1506
1512
|
|
|
1507
1513
|
def get_provider_queryable_path(
|
|
1508
|
-
queryable: str, metadata_mapping:
|
|
1514
|
+
queryable: str, metadata_mapping: dict[str, Union[str, list[str]]]
|
|
1509
1515
|
) -> Optional[str]:
|
|
1510
1516
|
"""Get EODAG configured queryable path from its parameter
|
|
1511
1517
|
|
|
@@ -1522,8 +1528,8 @@ def get_provider_queryable_path(
|
|
|
1522
1528
|
|
|
1523
1529
|
def get_provider_queryable_key(
|
|
1524
1530
|
eodag_key: str,
|
|
1525
|
-
provider_queryables:
|
|
1526
|
-
metadata_mapping:
|
|
1531
|
+
provider_queryables: dict[str, Any],
|
|
1532
|
+
metadata_mapping: dict[str, Union[list[Any], str]],
|
|
1527
1533
|
) -> str:
|
|
1528
1534
|
"""Finds the provider queryable corresponding to the given eodag key based on the metadata mapping
|
|
1529
1535
|
|
eodag/api/search_result.py
CHANGED
|
@@ -18,17 +18,7 @@
|
|
|
18
18
|
from __future__ import annotations
|
|
19
19
|
|
|
20
20
|
from collections import UserList
|
|
21
|
-
from typing import
|
|
22
|
-
TYPE_CHECKING,
|
|
23
|
-
Annotated,
|
|
24
|
-
Any,
|
|
25
|
-
Dict,
|
|
26
|
-
Iterable,
|
|
27
|
-
List,
|
|
28
|
-
Optional,
|
|
29
|
-
Tuple,
|
|
30
|
-
Union,
|
|
31
|
-
)
|
|
21
|
+
from typing import TYPE_CHECKING, Annotated, Any, Iterable, Optional, Union
|
|
32
22
|
|
|
33
23
|
from shapely.geometry import GeometryCollection, shape
|
|
34
24
|
from typing_extensions import Doc
|
|
@@ -56,17 +46,17 @@ class SearchResult(UserList):
|
|
|
56
46
|
:ivar number_matched: Estimated total number of matching results
|
|
57
47
|
"""
|
|
58
48
|
|
|
59
|
-
data:
|
|
49
|
+
data: list[EOProduct]
|
|
60
50
|
|
|
61
51
|
errors: Annotated[
|
|
62
|
-
|
|
52
|
+
list[tuple[str, Exception]], Doc("Tuple of provider name, exception")
|
|
63
53
|
]
|
|
64
54
|
|
|
65
55
|
def __init__(
|
|
66
56
|
self,
|
|
67
|
-
products:
|
|
57
|
+
products: list[EOProduct],
|
|
68
58
|
number_matched: Optional[int] = None,
|
|
69
|
-
errors:
|
|
59
|
+
errors: list[tuple[str, Exception]] = [],
|
|
70
60
|
) -> None:
|
|
71
61
|
super().__init__(products)
|
|
72
62
|
self.number_matched = number_matched
|
|
@@ -92,7 +82,7 @@ class SearchResult(UserList):
|
|
|
92
82
|
return self.crunch(FilterDate(dict(start=start, end=end)))
|
|
93
83
|
|
|
94
84
|
def filter_latest_intersect(
|
|
95
|
-
self, geometry: Union[
|
|
85
|
+
self, geometry: Union[dict[str, Any], BaseGeometry, Any]
|
|
96
86
|
) -> SearchResult:
|
|
97
87
|
"""
|
|
98
88
|
Apply :class:`~eodag.plugins.crunch.filter_latest_intersect.FilterLatestIntersect` crunch,
|
|
@@ -148,7 +138,7 @@ class SearchResult(UserList):
|
|
|
148
138
|
return self.filter_property(storageStatus="ONLINE")
|
|
149
139
|
|
|
150
140
|
@staticmethod
|
|
151
|
-
def from_geojson(feature_collection:
|
|
141
|
+
def from_geojson(feature_collection: dict[str, Any]) -> SearchResult:
|
|
152
142
|
"""Builds an :class:`~eodag.api.search_result.SearchResult` object from its representation as geojson
|
|
153
143
|
|
|
154
144
|
:param feature_collection: A collection representing a search result.
|
|
@@ -161,7 +151,7 @@ class SearchResult(UserList):
|
|
|
161
151
|
]
|
|
162
152
|
)
|
|
163
153
|
|
|
164
|
-
def as_geojson_object(self) ->
|
|
154
|
+
def as_geojson_object(self) -> dict[str, Any]:
|
|
165
155
|
"""GeoJSON representation of SearchResult"""
|
|
166
156
|
return {
|
|
167
157
|
"type": "FeatureCollection",
|
|
@@ -182,7 +172,7 @@ class SearchResult(UserList):
|
|
|
182
172
|
return self.as_shapely_geometry_object().wkt
|
|
183
173
|
|
|
184
174
|
@property
|
|
185
|
-
def __geo_interface__(self) ->
|
|
175
|
+
def __geo_interface__(self) -> dict[str, Any]:
|
|
186
176
|
"""Implements the geo-interface protocol.
|
|
187
177
|
|
|
188
178
|
See https://gist.github.com/sgillies/2217756
|
|
@@ -230,9 +220,9 @@ class RawSearchResult(UserList):
|
|
|
230
220
|
:param results: A list of raw/unparsed search results
|
|
231
221
|
"""
|
|
232
222
|
|
|
233
|
-
data:
|
|
234
|
-
query_params:
|
|
235
|
-
product_type_def_params:
|
|
223
|
+
data: list[Any]
|
|
224
|
+
query_params: dict[str, Any]
|
|
225
|
+
product_type_def_params: dict[str, Any]
|
|
236
226
|
|
|
237
|
-
def __init__(self, results:
|
|
227
|
+
def __init__(self, results: list[Any]) -> None:
|
|
238
228
|
super(RawSearchResult, self).__init__(results)
|
eodag/cli.py
CHANGED
|
@@ -48,7 +48,7 @@ import shutil
|
|
|
48
48
|
import sys
|
|
49
49
|
import textwrap
|
|
50
50
|
from importlib.metadata import metadata
|
|
51
|
-
from typing import TYPE_CHECKING, Any,
|
|
51
|
+
from typing import TYPE_CHECKING, Any, Mapping
|
|
52
52
|
|
|
53
53
|
import click
|
|
54
54
|
|
|
@@ -104,7 +104,7 @@ class MutuallyExclusiveOption(click.Option):
|
|
|
104
104
|
super(MutuallyExclusiveOption, self).__init__(*args, **kwargs)
|
|
105
105
|
|
|
106
106
|
def handle_parse_result(
|
|
107
|
-
self, ctx: Context, opts: Mapping[str, Any], args:
|
|
107
|
+
self, ctx: Context, opts: Mapping[str, Any], args: list[str]
|
|
108
108
|
):
|
|
109
109
|
"""Raise error or use parent handle_parse_result()"""
|
|
110
110
|
if self.mutually_exclusive.intersection(opts) and self.name in opts:
|
|
@@ -359,9 +359,9 @@ def search_crunch(ctx: Context, **kwargs: Any) -> None:
|
|
|
359
359
|
count = kwargs.pop("count")
|
|
360
360
|
|
|
361
361
|
# Process inputs for crunch
|
|
362
|
-
cruncher_names:
|
|
362
|
+
cruncher_names: set[Any] = set(kwargs.pop("cruncher") or [])
|
|
363
363
|
cruncher_args = kwargs.pop("cruncher_args")
|
|
364
|
-
cruncher_args_dict:
|
|
364
|
+
cruncher_args_dict: dict[str, dict[str, Any]] = {}
|
|
365
365
|
if cruncher_args:
|
|
366
366
|
for cruncher, argname, argval in cruncher_args:
|
|
367
367
|
cruncher_args_dict.setdefault(cruncher, {}).setdefault(argname, argval)
|