eodag 3.10.0__py3-none-any.whl → 4.0.0a1__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 (68) hide show
  1. eodag/api/core.py +378 -419
  2. eodag/api/product/__init__.py +3 -3
  3. eodag/api/product/_product.py +68 -40
  4. eodag/api/product/drivers/__init__.py +3 -5
  5. eodag/api/product/drivers/base.py +1 -18
  6. eodag/api/product/metadata_mapping.py +151 -215
  7. eodag/api/search_result.py +13 -7
  8. eodag/cli.py +72 -395
  9. eodag/config.py +46 -50
  10. eodag/plugins/apis/base.py +2 -2
  11. eodag/plugins/apis/ecmwf.py +20 -21
  12. eodag/plugins/apis/usgs.py +37 -33
  13. eodag/plugins/authentication/base.py +1 -3
  14. eodag/plugins/crunch/filter_date.py +3 -3
  15. eodag/plugins/crunch/filter_latest_intersect.py +2 -2
  16. eodag/plugins/crunch/filter_latest_tpl_name.py +1 -1
  17. eodag/plugins/download/aws.py +45 -41
  18. eodag/plugins/download/base.py +13 -14
  19. eodag/plugins/download/http.py +65 -65
  20. eodag/plugins/manager.py +28 -29
  21. eodag/plugins/search/__init__.py +3 -4
  22. eodag/plugins/search/base.py +128 -77
  23. eodag/plugins/search/build_search_result.py +105 -107
  24. eodag/plugins/search/cop_marine.py +44 -47
  25. eodag/plugins/search/csw.py +33 -33
  26. eodag/plugins/search/qssearch.py +335 -354
  27. eodag/plugins/search/stac_list_assets.py +1 -1
  28. eodag/plugins/search/static_stac_search.py +31 -31
  29. eodag/resources/{product_types.yml → collections.yml} +2353 -2429
  30. eodag/resources/ext_collections.json +1 -1
  31. eodag/resources/providers.yml +2427 -2719
  32. eodag/resources/stac_provider.yml +46 -90
  33. eodag/types/queryables.py +55 -91
  34. eodag/types/search_args.py +1 -1
  35. eodag/utils/__init__.py +94 -21
  36. eodag/utils/exceptions.py +6 -6
  37. eodag/utils/free_text_search.py +3 -3
  38. {eodag-3.10.0.dist-info → eodag-4.0.0a1.dist-info}/METADATA +10 -87
  39. eodag-4.0.0a1.dist-info/RECORD +92 -0
  40. {eodag-3.10.0.dist-info → eodag-4.0.0a1.dist-info}/entry_points.txt +0 -4
  41. eodag/plugins/authentication/oauth.py +0 -60
  42. eodag/plugins/download/creodias_s3.py +0 -71
  43. eodag/plugins/download/s3rest.py +0 -351
  44. eodag/plugins/search/data_request_search.py +0 -565
  45. eodag/resources/stac.yml +0 -294
  46. eodag/resources/stac_api.yml +0 -2105
  47. eodag/rest/__init__.py +0 -24
  48. eodag/rest/cache.py +0 -70
  49. eodag/rest/config.py +0 -67
  50. eodag/rest/constants.py +0 -26
  51. eodag/rest/core.py +0 -764
  52. eodag/rest/errors.py +0 -210
  53. eodag/rest/server.py +0 -604
  54. eodag/rest/server.wsgi +0 -6
  55. eodag/rest/stac.py +0 -1032
  56. eodag/rest/templates/README +0 -1
  57. eodag/rest/types/__init__.py +0 -18
  58. eodag/rest/types/collections_search.py +0 -44
  59. eodag/rest/types/eodag_search.py +0 -386
  60. eodag/rest/types/queryables.py +0 -174
  61. eodag/rest/types/stac_search.py +0 -272
  62. eodag/rest/utils/__init__.py +0 -207
  63. eodag/rest/utils/cql_evaluate.py +0 -119
  64. eodag/rest/utils/rfc3339.py +0 -64
  65. eodag-3.10.0.dist-info/RECORD +0 -116
  66. {eodag-3.10.0.dist-info → eodag-4.0.0a1.dist-info}/WHEEL +0 -0
  67. {eodag-3.10.0.dist-info → eodag-4.0.0a1.dist-info}/licenses/LICENSE +0 -0
  68. {eodag-3.10.0.dist-info → eodag-4.0.0a1.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
@@ -51,9 +51,10 @@ from eodag.utils import (
51
51
  DEFAULT_STREAM_REQUESTS_TIMEOUT,
52
52
  USER_AGENT,
53
53
  ProgressCallback,
54
+ format_string,
54
55
  get_geometry_from_various,
55
56
  )
56
- from eodag.utils.exceptions import DownloadError, MisconfiguredError
57
+ from eodag.utils.exceptions import DownloadError, MisconfiguredError, ValidationError
57
58
  from eodag.utils.repr import dict_to_html_table
58
59
 
59
60
  if TYPE_CHECKING:
@@ -104,8 +105,8 @@ class EOProduct:
104
105
  provider: str
105
106
  #: The metadata of the product
106
107
  properties: dict[str, Any]
107
- #: The product type
108
- product_type: Optional[str]
108
+ #: The collection
109
+ collection: Optional[str]
109
110
  #: The geometry of the product
110
111
  geometry: BaseGeometry
111
112
  #: The intersection between the product's geometry and the search area.
@@ -127,8 +128,12 @@ class EOProduct:
127
128
  self, provider: str, properties: dict[str, Any], **kwargs: Any
128
129
  ) -> None:
129
130
  self.provider = provider
130
- self.product_type = kwargs.get("productType")
131
- self.location = self.remote_location = properties.get("downloadLink", "")
131
+ self.collection = (
132
+ kwargs.get("collection")
133
+ or properties.pop("collection", None)
134
+ or properties.get("_collection")
135
+ )
136
+ self.location = self.remote_location = properties.get("eodag:download_link", "")
132
137
  self.assets = AssetsDict(self)
133
138
  self.properties = {
134
139
  key: value
@@ -136,19 +141,32 @@ class EOProduct:
136
141
  if key != "geometry"
137
142
  and value != NOT_MAPPED
138
143
  and NOT_AVAILABLE not in str(value)
144
+ and not key.startswith("_")
145
+ }
146
+ common_stac_properties = {
147
+ key: self.properties[key]
148
+ for key in sorted(self.properties)
149
+ if ":" not in key
150
+ }
151
+ extensions_stac_properties = {
152
+ key: self.properties[key] for key in sorted(self.properties) if ":" in key
139
153
  }
154
+ self.properties = common_stac_properties | extensions_stac_properties
155
+
140
156
  if "geometry" not in properties or (
141
157
  (
142
158
  properties["geometry"] == NOT_AVAILABLE
143
159
  or properties["geometry"] == NOT_MAPPED
144
160
  )
145
- and "defaultGeometry" not in properties
161
+ and "eodag:default_geometry" not in properties
146
162
  ):
147
163
  raise MisconfiguredError(
148
164
  f"No geometry available to build EOProduct(id={properties.get('id')}, provider={provider})"
149
165
  )
150
166
  elif not properties["geometry"] or properties["geometry"] == NOT_AVAILABLE:
151
- product_geometry = properties.pop("defaultGeometry", DEFAULT_GEOMETRY)
167
+ product_geometry = properties.pop(
168
+ "eodag:default_geometry", DEFAULT_GEOMETRY
169
+ )
152
170
  else:
153
171
  product_geometry = properties["geometry"]
154
172
 
@@ -192,9 +210,9 @@ class EOProduct:
192
210
  "id": self.properties["id"],
193
211
  "assets": self.assets.as_dict(),
194
212
  "properties": {
195
- "eodag_product_type": self.product_type,
196
- "eodag_provider": self.provider,
197
- "eodag_search_intersection": search_intersection,
213
+ "eodag:collection": self.collection,
214
+ "eodag:provider": self.provider,
215
+ "eodag:search_intersection": search_intersection,
198
216
  **{
199
217
  key: value
200
218
  for key, value in self.properties.items()
@@ -213,16 +231,22 @@ class EOProduct:
213
231
  :param feature: The representation of a :class:`~eodag.api.product._product.EOProduct`
214
232
  as a Python dict
215
233
  :returns: An instance of :class:`~eodag.api.product._product.EOProduct`
234
+ :raises: :class:`~eodag.utils.exceptions.ValidationError`
216
235
  """
217
- properties = feature["properties"]
218
- properties["geometry"] = feature["geometry"]
219
- properties["id"] = feature["id"]
220
- provider = feature["properties"]["eodag_provider"]
221
- product_type = feature["properties"]["eodag_product_type"]
222
- obj = cls(provider, properties, productType=product_type)
223
- obj.search_intersection = geometry.shape(
224
- feature["properties"]["eodag_search_intersection"]
225
- )
236
+ try:
237
+ properties = feature["properties"]
238
+ properties["geometry"] = feature["geometry"]
239
+ properties["id"] = feature["id"]
240
+ provider = properties.pop("eodag:provider")
241
+ collection = properties.pop("eodag:collection")
242
+ search_intersection = properties.pop("eodag:search_intersection")
243
+ except KeyError as e:
244
+ raise ValidationError(
245
+ "Key %s not found in geojson, make sure it comes from a serialized SearchResult"
246
+ % e.args[0]
247
+ ) from e
248
+ obj = cls(provider, properties, collection=collection)
249
+ obj.search_intersection = geometry.shape(search_intersection)
226
250
  obj.assets = AssetsDict(obj, feature.get("assets", {}))
227
251
  return obj
228
252
 
@@ -252,12 +276,12 @@ class EOProduct:
252
276
  download_plugin = plugins_manager.get_download_plugin(self)
253
277
  if len(self.assets) > 0:
254
278
  matching_url = next(iter(self.assets.values()))["href"]
255
- elif self.properties.get("storageStatus") != ONLINE_STATUS:
256
- matching_url = self.properties.get("orderLink") or self.properties.get(
257
- "downloadLink"
258
- )
279
+ elif self.properties.get("order:status") != ONLINE_STATUS:
280
+ matching_url = self.properties.get(
281
+ "eodag:order_link"
282
+ ) or self.properties.get("eodag:download_link")
259
283
  else:
260
- matching_url = self.properties.get("downloadLink")
284
+ matching_url = self.properties.get("eodag:download_link")
261
285
 
262
286
  try:
263
287
  auth_plugin = next(
@@ -424,7 +448,7 @@ class EOProduct:
424
448
  :raises HTTPError: If the HTTP request to the quicklook URL fails.
425
449
  """
426
450
  with requests.get(
427
- self.properties["quicklook"],
451
+ self.properties["eodag:quicklook"],
428
452
  stream=True,
429
453
  auth=auth,
430
454
  headers=USER_AGENT,
@@ -468,17 +492,19 @@ class EOProduct:
468
492
  def format_quicklook_address() -> None:
469
493
  """If the quicklook address is a Python format string, resolve the
470
494
  formatting with the properties of the product."""
471
- fstrmatch = re.match(r".*{.+}*.*", self.properties["quicklook"])
495
+ fstrmatch = re.match(r".*{.+}*.*", self.properties["eodag:quicklook"])
472
496
  if fstrmatch:
473
- self.properties["quicklook"].format(
474
- {
497
+ self.properties["eodag:quicklook"] = format_string(
498
+ None,
499
+ self.properties["eodag:quicklook"],
500
+ **{
475
501
  prop_key: prop_val
476
502
  for prop_key, prop_val in self.properties.items()
477
- if prop_key != "quicklook"
478
- }
503
+ if prop_key != "eodag:quicklook"
504
+ },
479
505
  )
480
506
 
481
- if self.properties.get("quicklook") is None:
507
+ if self.properties.get("eodag:quicklook") is None:
482
508
  logger.warning(
483
509
  "Missing information to retrieve quicklook for EO product: %s",
484
510
  self.properties["id"],
@@ -521,11 +547,11 @@ class EOProduct:
521
547
  # it is a HTTP URL. If not, we assume it is a base64 string, in which case
522
548
  # we just decode the content, write it into the quicklook_file and return it.
523
549
  if not (
524
- self.properties["quicklook"].startswith("http")
525
- or self.properties["quicklook"].startswith("https")
550
+ self.properties["eodag:quicklook"].startswith("http")
551
+ or self.properties["eodag:quicklook"].startswith("https")
526
552
  ):
527
553
  with open(quicklook_file, "wb") as fd:
528
- img = self.properties["quicklook"].encode("ascii")
554
+ img = self.properties["eodag:quicklook"].encode("ascii")
529
555
  fd.write(base64.b64decode(img))
530
556
  return quicklook_file
531
557
 
@@ -588,7 +614,9 @@ class EOProduct:
588
614
  return NoDriver()
589
615
 
590
616
  def _repr_html_(self):
591
- thumbnail = self.properties.get("thumbnail")
617
+ thumbnail = self.properties.get("eodag:thumbnail") or self.properties.get(
618
+ "eodag:quicklook"
619
+ )
592
620
  thumbnail_html = (
593
621
  f"<img src='{thumbnail}' width=100 alt='thumbnail'/>"
594
622
  if thumbnail and not thumbnail.startswith("s3")
@@ -608,13 +636,13 @@ class EOProduct:
608
636
  <td style='text-align: left; vertical-align: top;'>
609
637
  {dict_to_html_table({
610
638
  "provider": self.provider,
611
- "product_type": self.product_type,
639
+ "collection": self.collection,
612
640
  "properties[&quot;id&quot;]": self.properties.get('id'),
613
- "properties[&quot;startTimeFromAscendingNode&quot;]": self.properties.get(
614
- 'startTimeFromAscendingNode'
641
+ "properties[&quot;start_datetime&quot;]": self.properties.get(
642
+ 'start_datetime'
615
643
  ),
616
- "properties[&quot;completionTimeFromAscendingNode&quot;]": self.properties.get(
617
- 'completionTimeFromAscendingNode'
644
+ "properties[&quot;end_datetime&quot;]": self.properties.get(
645
+ 'end_datetime'
618
646
  ),
619
647
  }, brackets=False)}
620
648
  <details><summary style='color: grey; margin-top: 10px;'>properties:&ensp;({len(
@@ -56,7 +56,7 @@ DRIVERS: list[DriverCriteria] = [
56
56
  {
57
57
  "criteria": [
58
58
  lambda prod: True
59
- if (prod.product_type or "").startswith("S2_MSI_")
59
+ if (prod.collection or "").startswith("S2_MSI_")
60
60
  else False
61
61
  ],
62
62
  "driver": Sentinel2Driver(),
@@ -64,7 +64,7 @@ DRIVERS: list[DriverCriteria] = [
64
64
  {
65
65
  "criteria": [
66
66
  lambda prod: True
67
- if (prod.product_type or "").startswith("S1_SAR_")
67
+ if (prod.collection or "").startswith("S1_SAR_")
68
68
  else False
69
69
  ],
70
70
  "driver": Sentinel1Driver(),
@@ -90,9 +90,7 @@ LEGACY_DRIVERS: list[DriverCriteria] = [
90
90
  },
91
91
  {
92
92
  "criteria": [
93
- lambda prod: True
94
- if getattr(prod, "product_type") == "S2_MSI_L1C"
95
- else False
93
+ lambda prod: True if getattr(prod, "collection") == "S2_MSI_L1C" else False
96
94
  ],
97
95
  "driver": Sentinel2L1C_cube(),
98
96
  },
@@ -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
 
@@ -82,25 +80,10 @@ class DatasetDriver(metaclass=type):
82
80
  logger.debug(f"No key & roles could be guessed for {href}")
83
81
  return None, None
84
82
 
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
83
 
101
84
  class NoDriver(DatasetDriver):
102
85
  """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
86
+ methods it should implement, used for all collections for which the deprecated
104
87
  :meth:`~eodag_cube.api.product._product.EOProduct.get_data` method is not implemented. Expect a
105
88
  :exc:`NotImplementedError` when trying to get the data in that case.
106
89
  """