eodag 3.10.1__py3-none-any.whl → 4.0.0a2__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.
Files changed (75) hide show
  1. eodag/__init__.py +6 -1
  2. eodag/api/collection.py +353 -0
  3. eodag/api/core.py +606 -641
  4. eodag/api/product/__init__.py +3 -3
  5. eodag/api/product/_product.py +74 -56
  6. eodag/api/product/drivers/__init__.py +4 -46
  7. eodag/api/product/drivers/base.py +0 -28
  8. eodag/api/product/metadata_mapping.py +178 -216
  9. eodag/api/search_result.py +156 -15
  10. eodag/cli.py +83 -403
  11. eodag/config.py +81 -51
  12. eodag/plugins/apis/base.py +2 -2
  13. eodag/plugins/apis/ecmwf.py +36 -25
  14. eodag/plugins/apis/usgs.py +55 -40
  15. eodag/plugins/authentication/base.py +1 -3
  16. eodag/plugins/crunch/filter_date.py +3 -3
  17. eodag/plugins/crunch/filter_latest_intersect.py +2 -2
  18. eodag/plugins/crunch/filter_latest_tpl_name.py +1 -1
  19. eodag/plugins/download/aws.py +46 -42
  20. eodag/plugins/download/base.py +13 -14
  21. eodag/plugins/download/http.py +65 -65
  22. eodag/plugins/manager.py +28 -29
  23. eodag/plugins/search/__init__.py +6 -4
  24. eodag/plugins/search/base.py +131 -80
  25. eodag/plugins/search/build_search_result.py +245 -173
  26. eodag/plugins/search/cop_marine.py +87 -56
  27. eodag/plugins/search/csw.py +47 -37
  28. eodag/plugins/search/qssearch.py +653 -429
  29. eodag/plugins/search/stac_list_assets.py +1 -1
  30. eodag/plugins/search/static_stac_search.py +43 -44
  31. eodag/resources/{product_types.yml → collections.yml} +2594 -2453
  32. eodag/resources/ext_collections.json +1 -1
  33. eodag/resources/ext_product_types.json +1 -1
  34. eodag/resources/providers.yml +2706 -2733
  35. eodag/resources/stac_provider.yml +50 -92
  36. eodag/resources/user_conf_template.yml +9 -0
  37. eodag/types/__init__.py +2 -0
  38. eodag/types/queryables.py +70 -91
  39. eodag/types/search_args.py +1 -1
  40. eodag/utils/__init__.py +97 -21
  41. eodag/utils/dates.py +0 -12
  42. eodag/utils/exceptions.py +6 -6
  43. eodag/utils/free_text_search.py +3 -3
  44. eodag/utils/repr.py +2 -0
  45. {eodag-3.10.1.dist-info → eodag-4.0.0a2.dist-info}/METADATA +13 -99
  46. eodag-4.0.0a2.dist-info/RECORD +93 -0
  47. {eodag-3.10.1.dist-info → eodag-4.0.0a2.dist-info}/entry_points.txt +0 -4
  48. eodag/plugins/authentication/oauth.py +0 -60
  49. eodag/plugins/download/creodias_s3.py +0 -71
  50. eodag/plugins/download/s3rest.py +0 -351
  51. eodag/plugins/search/data_request_search.py +0 -565
  52. eodag/resources/stac.yml +0 -294
  53. eodag/resources/stac_api.yml +0 -2105
  54. eodag/rest/__init__.py +0 -24
  55. eodag/rest/cache.py +0 -70
  56. eodag/rest/config.py +0 -67
  57. eodag/rest/constants.py +0 -26
  58. eodag/rest/core.py +0 -764
  59. eodag/rest/errors.py +0 -210
  60. eodag/rest/server.py +0 -604
  61. eodag/rest/server.wsgi +0 -6
  62. eodag/rest/stac.py +0 -1032
  63. eodag/rest/templates/README +0 -1
  64. eodag/rest/types/__init__.py +0 -18
  65. eodag/rest/types/collections_search.py +0 -44
  66. eodag/rest/types/eodag_search.py +0 -386
  67. eodag/rest/types/queryables.py +0 -174
  68. eodag/rest/types/stac_search.py +0 -272
  69. eodag/rest/utils/__init__.py +0 -207
  70. eodag/rest/utils/cql_evaluate.py +0 -119
  71. eodag/rest/utils/rfc3339.py +0 -64
  72. eodag-3.10.1.dist-info/RECORD +0 -116
  73. {eodag-3.10.1.dist-info → eodag-4.0.0a2.dist-info}/WHEEL +0 -0
  74. {eodag-3.10.1.dist-info → eodag-4.0.0a2.dist-info}/licenses/LICENSE +0 -0
  75. {eodag-3.10.1.dist-info → eodag-4.0.0a2.dist-info}/top_level.txt +0 -0
@@ -55,8 +55,8 @@ def unregistered_product_from_item(
55
55
  # properties cleanup
56
56
  for prop in ("start_datetime", "end_datetime"):
57
57
  products[0].properties.pop(prop, None)
58
- # set product type if not already set
59
- if products[0].product_type is None:
60
- products[0].product_type = products[0].properties.get("productType")
58
+ # set collection if not already set
59
+ if products[0].collection is None:
60
+ products[0].collection = products[0].properties.get("collection")
61
61
  return products[0]
62
62
  return None
@@ -38,7 +38,8 @@ try:
38
38
  except ImportError:
39
39
  from eodag.api.product._assets import AssetsDict
40
40
 
41
- from eodag.api.product.drivers import DRIVERS, LEGACY_DRIVERS, NoDriver
41
+ from eodag.api.product.drivers import DRIVERS
42
+ from eodag.api.product.drivers.generic import GenericDriver
42
43
  from eodag.api.product.metadata_mapping import (
43
44
  DEFAULT_GEOMETRY,
44
45
  NOT_AVAILABLE,
@@ -52,9 +53,10 @@ from eodag.utils import (
52
53
  DEFAULT_STREAM_REQUESTS_TIMEOUT,
53
54
  USER_AGENT,
54
55
  ProgressCallback,
56
+ format_string,
55
57
  get_geometry_from_various,
56
58
  )
57
- from eodag.utils.exceptions import DownloadError, MisconfiguredError
59
+ from eodag.utils.exceptions import DownloadError, MisconfiguredError, ValidationError
58
60
  from eodag.utils.repr import dict_to_html_table
59
61
 
60
62
  if TYPE_CHECKING:
@@ -99,8 +101,8 @@ class EOProduct:
99
101
  provider: str
100
102
  #: The metadata of the product
101
103
  properties: dict[str, Any]
102
- #: The product type
103
- product_type: Optional[str]
104
+ #: The collection
105
+ collection: Optional[str]
104
106
  #: The geometry of the product
105
107
  geometry: BaseGeometry
106
108
  #: The intersection between the product's geometry and the search area.
@@ -122,8 +124,12 @@ class EOProduct:
122
124
  self, provider: str, properties: dict[str, Any], **kwargs: Any
123
125
  ) -> None:
124
126
  self.provider = provider
125
- self.product_type = kwargs.get("productType")
126
- self.location = self.remote_location = properties.get("downloadLink", "")
127
+ self.collection = (
128
+ kwargs.get("collection")
129
+ or properties.pop("collection", None)
130
+ or properties.get("_collection")
131
+ )
132
+ self.location = self.remote_location = properties.get("eodag:download_link", "")
127
133
  self.assets = AssetsDict(self)
128
134
  self.properties = {
129
135
  key: value
@@ -131,18 +137,31 @@ class EOProduct:
131
137
  if key != "geometry"
132
138
  and value != NOT_MAPPED
133
139
  and NOT_AVAILABLE not in str(value)
140
+ and not key.startswith("_")
134
141
  and value is not None
135
142
  }
143
+ common_stac_properties = {
144
+ key: self.properties[key]
145
+ for key in sorted(self.properties)
146
+ if ":" not in key
147
+ }
148
+ extensions_stac_properties = {
149
+ key: self.properties[key] for key in sorted(self.properties) if ":" in key
150
+ }
151
+ self.properties = common_stac_properties | extensions_stac_properties
152
+
136
153
  if "geometry" not in properties or (
137
154
  (
138
155
  properties["geometry"] == NOT_AVAILABLE
139
156
  or properties["geometry"] == NOT_MAPPED
140
157
  )
141
- and "defaultGeometry" not in properties
158
+ and "eodag:default_geometry" not in properties
142
159
  ):
143
160
  product_geometry = DEFAULT_SHAPELY_GEOMETRY
144
161
  elif not properties["geometry"] or properties["geometry"] == NOT_AVAILABLE:
145
- product_geometry = properties.pop("defaultGeometry", DEFAULT_GEOMETRY)
162
+ product_geometry = properties.pop(
163
+ "eodag:default_geometry", DEFAULT_GEOMETRY
164
+ )
146
165
  else:
147
166
  product_geometry = properties["geometry"]
148
167
 
@@ -188,9 +207,9 @@ class EOProduct:
188
207
  "id": self.properties["id"],
189
208
  "assets": self.assets.as_dict(),
190
209
  "properties": {
191
- "eodag_product_type": self.product_type,
192
- "eodag_provider": self.provider,
193
- "eodag_search_intersection": search_intersection,
210
+ "eodag:collection": self.collection,
211
+ "eodag:provider": self.provider,
212
+ "eodag:search_intersection": search_intersection,
194
213
  **{
195
214
  key: value
196
215
  for key, value in self.properties.items()
@@ -209,16 +228,22 @@ class EOProduct:
209
228
  :param feature: The representation of a :class:`~eodag.api.product._product.EOProduct`
210
229
  as a Python dict
211
230
  :returns: An instance of :class:`~eodag.api.product._product.EOProduct`
231
+ :raises: :class:`~eodag.utils.exceptions.ValidationError`
212
232
  """
213
- properties = feature["properties"]
214
- properties["geometry"] = feature["geometry"]
215
- properties["id"] = feature["id"]
216
- provider = feature["properties"]["eodag_provider"]
217
- product_type = feature["properties"]["eodag_product_type"]
218
- obj = cls(provider, properties, productType=product_type)
219
- obj.search_intersection = geometry.shape(
220
- feature["properties"]["eodag_search_intersection"]
221
- )
233
+ try:
234
+ properties = feature["properties"]
235
+ properties["geometry"] = feature["geometry"]
236
+ properties["id"] = feature["id"]
237
+ provider = properties.pop("eodag:provider")
238
+ collection = properties.pop("eodag:collection")
239
+ search_intersection = properties.pop("eodag:search_intersection")
240
+ except KeyError as e:
241
+ raise ValidationError(
242
+ "Key %s not found in geojson, make sure it comes from a serialized SearchResult"
243
+ % e.args[0]
244
+ ) from e
245
+ obj = cls(provider, properties, collection=collection)
246
+ obj.search_intersection = geometry.shape(search_intersection)
222
247
  obj.assets = AssetsDict(obj, feature.get("assets", {}))
223
248
  return obj
224
249
 
@@ -248,12 +273,12 @@ class EOProduct:
248
273
  download_plugin = plugins_manager.get_download_plugin(self)
249
274
  if len(self.assets) > 0:
250
275
  matching_url = next(iter(self.assets.values()))["href"]
251
- elif self.properties.get("storageStatus") != ONLINE_STATUS:
252
- matching_url = self.properties.get("orderLink") or self.properties.get(
253
- "downloadLink"
254
- )
276
+ elif self.properties.get("order:status") != ONLINE_STATUS:
277
+ matching_url = self.properties.get(
278
+ "eodag:order_link"
279
+ ) or self.properties.get("eodag:download_link")
255
280
  else:
256
- matching_url = self.properties.get("downloadLink")
281
+ matching_url = self.properties.get("eodag:download_link")
257
282
 
258
283
  try:
259
284
  auth_plugin = next(
@@ -420,7 +445,7 @@ class EOProduct:
420
445
  :raises HTTPError: If the HTTP request to the quicklook URL fails.
421
446
  """
422
447
  with requests.get(
423
- self.properties["quicklook"],
448
+ self.properties["eodag:quicklook"],
424
449
  stream=True,
425
450
  auth=auth,
426
451
  headers=USER_AGENT,
@@ -464,17 +489,19 @@ class EOProduct:
464
489
  def format_quicklook_address() -> None:
465
490
  """If the quicklook address is a Python format string, resolve the
466
491
  formatting with the properties of the product."""
467
- fstrmatch = re.match(r".*{.+}*.*", self.properties["quicklook"])
492
+ fstrmatch = re.match(r".*{.+}*.*", self.properties["eodag:quicklook"])
468
493
  if fstrmatch:
469
- self.properties["quicklook"].format(
470
- {
494
+ self.properties["eodag:quicklook"] = format_string(
495
+ None,
496
+ self.properties["eodag:quicklook"],
497
+ **{
471
498
  prop_key: prop_val
472
499
  for prop_key, prop_val in self.properties.items()
473
- if prop_key != "quicklook"
474
- }
500
+ if prop_key != "eodag:quicklook"
501
+ },
475
502
  )
476
503
 
477
- if self.properties.get("quicklook") is None:
504
+ if self.properties.get("eodag:quicklook") is None:
478
505
  logger.warning(
479
506
  "Missing information to retrieve quicklook for EO product: %s",
480
507
  self.properties["id"],
@@ -517,11 +544,11 @@ class EOProduct:
517
544
  # it is a HTTP URL. If not, we assume it is a base64 string, in which case
518
545
  # we just decode the content, write it into the quicklook_file and return it.
519
546
  if not (
520
- self.properties["quicklook"].startswith("http")
521
- or self.properties["quicklook"].startswith("https")
547
+ self.properties["eodag:quicklook"].startswith("http")
548
+ or self.properties["eodag:quicklook"].startswith("https")
522
549
  ):
523
550
  with open(quicklook_file, "wb") as fd:
524
- img = self.properties["quicklook"].encode("ascii")
551
+ img = self.properties["eodag:quicklook"].encode("ascii")
525
552
  fd.write(base64.b64decode(img))
526
553
  return quicklook_file
527
554
 
@@ -567,24 +594,15 @@ class EOProduct:
567
594
 
568
595
  def get_driver(self) -> DatasetDriver:
569
596
  """Get the most appropriate driver"""
570
- try:
571
- for driver_conf in DRIVERS:
572
- if all([criteria(self) for criteria in driver_conf["criteria"]]):
573
- driver = driver_conf["driver"]
574
- break
575
- # use legacy driver for deprecated get_data method usage
576
- for lecacy_conf in LEGACY_DRIVERS:
577
- if all([criteria(self) for criteria in lecacy_conf["criteria"]]):
578
- driver.legacy = lecacy_conf["driver"]
579
- break
580
- return driver
581
- except TypeError:
582
- logger.info("No driver matching")
583
- pass
584
- return NoDriver()
597
+ for driver_conf in DRIVERS:
598
+ if all([criteria(self) for criteria in driver_conf["criteria"]]):
599
+ return driver_conf["driver"]
600
+ return GenericDriver()
585
601
 
586
602
  def _repr_html_(self):
587
- thumbnail = self.properties.get("thumbnail")
603
+ thumbnail = self.properties.get("eodag:thumbnail") or self.properties.get(
604
+ "eodag:quicklook"
605
+ )
588
606
  thumbnail_html = (
589
607
  f"<img src='{thumbnail}' width=100 alt='thumbnail'/>"
590
608
  if thumbnail and not thumbnail.startswith("s3")
@@ -604,13 +622,13 @@ class EOProduct:
604
622
  <td style='text-align: left; vertical-align: top;'>
605
623
  {dict_to_html_table({
606
624
  "provider": self.provider,
607
- "product_type": self.product_type,
625
+ "collection": self.collection,
608
626
  "properties[&quot;id&quot;]": self.properties.get('id'),
609
- "properties[&quot;startTimeFromAscendingNode&quot;]": self.properties.get(
610
- 'startTimeFromAscendingNode'
627
+ "properties[&quot;start_datetime&quot;]": self.properties.get(
628
+ 'start_datetime'
611
629
  ),
612
- "properties[&quot;completionTimeFromAscendingNode&quot;]": self.properties.get(
613
- 'completionTimeFromAscendingNode'
630
+ "properties[&quot;end_datetime&quot;]": self.properties.get(
631
+ 'end_datetime'
614
632
  ),
615
633
  }, brackets=False)}
616
634
  <details><summary style='color: grey; margin-top: 10px;'>properties:&ensp;({len(
@@ -20,27 +20,11 @@ from __future__ import annotations
20
20
 
21
21
  from typing import Callable, TypedDict
22
22
 
23
- from eodag.api.product.drivers.base import DatasetDriver, NoDriver
23
+ from eodag.api.product.drivers.base import DatasetDriver
24
24
  from eodag.api.product.drivers.generic import GenericDriver
25
25
  from eodag.api.product.drivers.sentinel1 import Sentinel1Driver
26
26
  from eodag.api.product.drivers.sentinel2 import Sentinel2Driver
27
27
 
28
- try:
29
- # import from eodag-cube if installed
30
- from eodag_cube.api.product.drivers.generic import ( # pyright: ignore[reportMissingImports]; isort: skip
31
- GenericDriver as GenericDriver_cube,
32
- )
33
- from eodag_cube.api.product.drivers.sentinel2_l1c import ( # pyright: ignore[reportMissingImports]; isort: skip
34
- Sentinel2L1C as Sentinel2L1C_cube,
35
- )
36
- from eodag_cube.api.product.drivers.stac_assets import ( # pyright: ignore[reportMissingImports]; isort: skip
37
- StacAssets as StacAssets_cube,
38
- )
39
- except ImportError:
40
- GenericDriver_cube = NoDriver
41
- Sentinel2L1C_cube = NoDriver
42
- StacAssets_cube = NoDriver
43
-
44
28
 
45
29
  class DriverCriteria(TypedDict):
46
30
  """Driver criteria definition"""
@@ -56,7 +40,7 @@ DRIVERS: list[DriverCriteria] = [
56
40
  {
57
41
  "criteria": [
58
42
  lambda prod: True
59
- if (prod.product_type or "").startswith("S2_MSI_")
43
+ if (prod.collection or "").startswith("S2_MSI_")
60
44
  else False
61
45
  ],
62
46
  "driver": Sentinel2Driver(),
@@ -64,7 +48,7 @@ DRIVERS: list[DriverCriteria] = [
64
48
  {
65
49
  "criteria": [
66
50
  lambda prod: True
67
- if (prod.product_type or "").startswith("S1_SAR_")
51
+ if (prod.collection or "").startswith("S1_SAR_")
68
52
  else False
69
53
  ],
70
54
  "driver": Sentinel1Driver(),
@@ -76,31 +60,5 @@ DRIVERS: list[DriverCriteria] = [
76
60
  ]
77
61
 
78
62
 
79
- #: list of legacy drivers and their criteria
80
- LEGACY_DRIVERS: list[DriverCriteria] = [
81
- {
82
- "criteria": [
83
- lambda prod: True if len(getattr(prod, "assets", {})) > 0 else False
84
- ],
85
- "driver": StacAssets_cube(),
86
- },
87
- {
88
- "criteria": [lambda prod: True if "assets" in prod.properties else False],
89
- "driver": StacAssets_cube(),
90
- },
91
- {
92
- "criteria": [
93
- lambda prod: True
94
- if getattr(prod, "product_type") == "S2_MSI_L1C"
95
- else False
96
- ],
97
- "driver": Sentinel2L1C_cube(),
98
- },
99
- {
100
- "criteria": [lambda prod: True],
101
- "driver": GenericDriver_cube(),
102
- },
103
- ]
104
-
105
63
  # exportable content
106
- __all__ = ["DRIVERS", "DatasetDriver", "GenericDriver", "NoDriver", "Sentinel2Driver"]
64
+ __all__ = ["DRIVERS", "DatasetDriver", "GenericDriver", "Sentinel2Driver"]
@@ -21,8 +21,6 @@ import logging
21
21
  import re
22
22
  from typing import TYPE_CHECKING, Optional, TypedDict
23
23
 
24
- from eodag.utils import _deprecated
25
-
26
24
  if TYPE_CHECKING:
27
25
  from eodag.api.product import EOProduct
28
26
 
@@ -46,9 +44,6 @@ class DatasetDriver(metaclass=type):
46
44
  criteria.
47
45
  """
48
46
 
49
- #: legacy driver for deprecated :meth:`~eodag_cube.api.product._product.EOProduct.get_data` method usage
50
- legacy: DatasetDriver
51
-
52
47
  #: list of patterns to match asset keys and roles
53
48
  ASSET_KEYS_PATTERNS_ROLES: list[AssetPatterns] = []
54
49
 
@@ -81,26 +76,3 @@ class DatasetDriver(metaclass=type):
81
76
  return normalized_key or extracted_key, roles
82
77
  logger.debug(f"No key & roles could be guessed for {href}")
83
78
  return None, None
84
-
85
- @_deprecated(reason="Method used by deprecated get_data", version="3.1.0")
86
- def get_data_address(self, eo_product: EOProduct, band: str) -> str:
87
- """Retrieve the address of the dataset represented by `eo_product`.
88
-
89
- :param eo_product: The product whom underlying dataset address is to be retrieved
90
- :param band: The band to retrieve (e.g: 'B01')
91
- :returns: An address for the dataset
92
- :raises: :class:`~eodag.utils.exceptions.AddressNotFound`
93
- :raises: :class:`~eodag.utils.exceptions.UnsupportedDatasetAddressScheme`
94
-
95
- .. deprecated:: 3.1.0
96
- Method used by deprecated :meth:`~eodag_cube.api.product._product.EOProduct.get_data`
97
- """
98
- raise NotImplementedError
99
-
100
-
101
- class NoDriver(DatasetDriver):
102
- """A default :attr:`~eodag.api.product.drivers.base.DatasetDriver.legacy` driver that does not implement any of the
103
- methods it should implement, used for all product types for which the deprecated
104
- :meth:`~eodag_cube.api.product._product.EOProduct.get_data` method is not implemented. Expect a
105
- :exc:`NotImplementedError` when trying to get the data in that case.
106
- """