eodag 3.9.1__py3-none-any.whl → 3.10.1__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 +8 -2
- eodag/api/product/_product.py +9 -13
- eodag/api/product/metadata_mapping.py +7 -4
- eodag/plugins/authentication/aws_auth.py +36 -1
- eodag/plugins/authentication/base.py +17 -0
- eodag/plugins/authentication/sas_auth.py +15 -0
- eodag/plugins/crunch/filter_latest_intersect.py +1 -0
- eodag/plugins/crunch/filter_overlap.py +3 -7
- eodag/plugins/download/aws.py +1 -1
- eodag/plugins/download/creodias_s3.py +7 -0
- eodag/plugins/search/build_search_result.py +41 -14
- eodag/plugins/search/cop_marine.py +6 -0
- eodag/resources/ext_collections.json +1 -0
- eodag/resources/ext_product_types.json +1 -1
- eodag/resources/product_types.yml +81 -36
- eodag/resources/providers.yml +50 -33
- eodag/types/queryables.py +1 -0
- eodag/utils/__init__.py +44 -2
- eodag/utils/dates.py +12 -0
- {eodag-3.9.1.dist-info → eodag-3.10.1.dist-info}/METADATA +13 -2
- {eodag-3.9.1.dist-info → eodag-3.10.1.dist-info}/RECORD +25 -24
- {eodag-3.9.1.dist-info → eodag-3.10.1.dist-info}/WHEEL +0 -0
- {eodag-3.9.1.dist-info → eodag-3.10.1.dist-info}/entry_points.txt +0 -0
- {eodag-3.9.1.dist-info → eodag-3.10.1.dist-info}/licenses/LICENSE +0 -0
- {eodag-3.9.1.dist-info → eodag-3.10.1.dist-info}/top_level.txt +0 -0
eodag/api/core.py
CHANGED
|
@@ -56,7 +56,7 @@ from eodag.plugins.search import PreparedSearch
|
|
|
56
56
|
from eodag.plugins.search.build_search_result import MeteoblueSearch
|
|
57
57
|
from eodag.plugins.search.qssearch import PostJsonSearch
|
|
58
58
|
from eodag.types import model_fields_to_annotated
|
|
59
|
-
from eodag.types.queryables import CommonQueryables, QueryablesDict
|
|
59
|
+
from eodag.types.queryables import CommonQueryables, Queryables, QueryablesDict
|
|
60
60
|
from eodag.utils import (
|
|
61
61
|
DEFAULT_DOWNLOAD_TIMEOUT,
|
|
62
62
|
DEFAULT_DOWNLOAD_WAIT,
|
|
@@ -2318,8 +2318,14 @@ class EODataAccessGateway:
|
|
|
2318
2318
|
plugin.provider,
|
|
2319
2319
|
)
|
|
2320
2320
|
|
|
2321
|
+
# use queryables aliases
|
|
2322
|
+
kwargs_alias = {**kwargs}
|
|
2323
|
+
for search_param, field_info in Queryables.model_fields.items():
|
|
2324
|
+
if search_param in kwargs and field_info.alias:
|
|
2325
|
+
kwargs_alias[field_info.alias] = kwargs_alias.pop(search_param)
|
|
2326
|
+
|
|
2321
2327
|
plugin_queryables = plugin.list_queryables(
|
|
2322
|
-
|
|
2328
|
+
kwargs_alias,
|
|
2323
2329
|
available_product_types,
|
|
2324
2330
|
product_type_configs,
|
|
2325
2331
|
product_type,
|
eodag/api/product/_product.py
CHANGED
|
@@ -48,6 +48,7 @@ from eodag.api.product.metadata_mapping import (
|
|
|
48
48
|
from eodag.utils import (
|
|
49
49
|
DEFAULT_DOWNLOAD_TIMEOUT,
|
|
50
50
|
DEFAULT_DOWNLOAD_WAIT,
|
|
51
|
+
DEFAULT_SHAPELY_GEOMETRY,
|
|
51
52
|
DEFAULT_STREAM_REQUESTS_TIMEOUT,
|
|
52
53
|
USER_AGENT,
|
|
53
54
|
ProgressCallback,
|
|
@@ -67,12 +68,6 @@ if TYPE_CHECKING:
|
|
|
67
68
|
from eodag.types.download_args import DownloadConf
|
|
68
69
|
from eodag.utils import Unpack
|
|
69
70
|
|
|
70
|
-
try:
|
|
71
|
-
from shapely.errors import GEOSException
|
|
72
|
-
except ImportError:
|
|
73
|
-
# shapely < 2.0 compatibility
|
|
74
|
-
from shapely.errors import TopologicalError as GEOSException
|
|
75
|
-
|
|
76
71
|
|
|
77
72
|
logger = logging.getLogger("eodag.product")
|
|
78
73
|
|
|
@@ -136,6 +131,7 @@ class EOProduct:
|
|
|
136
131
|
if key != "geometry"
|
|
137
132
|
and value != NOT_MAPPED
|
|
138
133
|
and NOT_AVAILABLE not in str(value)
|
|
134
|
+
and value is not None
|
|
139
135
|
}
|
|
140
136
|
if "geometry" not in properties or (
|
|
141
137
|
(
|
|
@@ -144,17 +140,17 @@ class EOProduct:
|
|
|
144
140
|
)
|
|
145
141
|
and "defaultGeometry" not in properties
|
|
146
142
|
):
|
|
147
|
-
|
|
148
|
-
f"No geometry available to build EOProduct(id={properties.get('id')}, provider={provider})"
|
|
149
|
-
)
|
|
143
|
+
product_geometry = DEFAULT_SHAPELY_GEOMETRY
|
|
150
144
|
elif not properties["geometry"] or properties["geometry"] == NOT_AVAILABLE:
|
|
151
145
|
product_geometry = properties.pop("defaultGeometry", DEFAULT_GEOMETRY)
|
|
152
146
|
else:
|
|
153
147
|
product_geometry = properties["geometry"]
|
|
154
148
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
149
|
+
geometry_obj = get_geometry_from_various(geometry=product_geometry)
|
|
150
|
+
# whole world as default geometry
|
|
151
|
+
if geometry_obj is None:
|
|
152
|
+
geometry_obj = DEFAULT_SHAPELY_GEOMETRY
|
|
153
|
+
self.geometry = self.search_intersection = geometry_obj
|
|
158
154
|
|
|
159
155
|
self.search_kwargs = kwargs
|
|
160
156
|
if self.search_kwargs.get("geometry") is not None:
|
|
@@ -163,7 +159,7 @@ class EOProduct:
|
|
|
163
159
|
)
|
|
164
160
|
try:
|
|
165
161
|
self.search_intersection = self.geometry.intersection(searched_geom)
|
|
166
|
-
except
|
|
162
|
+
except ShapelyError:
|
|
167
163
|
logger.warning(
|
|
168
164
|
"Unable to intersect the requested extent: %s with the product "
|
|
169
165
|
"geometry: %s",
|
|
@@ -42,6 +42,7 @@ from shapely.ops import transform
|
|
|
42
42
|
from eodag.types.queryables import Queryables
|
|
43
43
|
from eodag.utils import (
|
|
44
44
|
DEFAULT_PROJ,
|
|
45
|
+
DEFAULT_SHAPELY_GEOMETRY,
|
|
45
46
|
_deprecated,
|
|
46
47
|
deepcopy,
|
|
47
48
|
dict_items_recursive_apply,
|
|
@@ -340,14 +341,16 @@ def format_metadata(search_param: str, *args: Any, **kwargs: Any) -> str:
|
|
|
340
341
|
@staticmethod
|
|
341
342
|
def convert_to_bounds(input_geom_unformatted: Any) -> list[float]:
|
|
342
343
|
input_geom = get_geometry_from_various(geometry=input_geom_unformatted)
|
|
344
|
+
if input_geom is None:
|
|
345
|
+
input_geom = DEFAULT_SHAPELY_GEOMETRY
|
|
343
346
|
if isinstance(input_geom, MultiPolygon):
|
|
344
347
|
geoms = [geom for geom in input_geom.geoms]
|
|
345
348
|
# sort with larger one at first (stac-browser only plots first one)
|
|
346
349
|
geoms.sort(key=lambda x: x.area, reverse=True)
|
|
347
|
-
min_lon = 180
|
|
348
|
-
min_lat = 90
|
|
349
|
-
max_lon = -180
|
|
350
|
-
max_lat = -90
|
|
350
|
+
min_lon = 180.0
|
|
351
|
+
min_lat = 90.0
|
|
352
|
+
max_lon = -180.0
|
|
353
|
+
max_lat = -90.0
|
|
351
354
|
for geom in geoms:
|
|
352
355
|
min_lon = min(min_lon, geom.bounds[0])
|
|
353
356
|
min_lat = min(min_lat, geom.bounds[1])
|
|
@@ -24,9 +24,11 @@ import boto3
|
|
|
24
24
|
from botocore.exceptions import ClientError, ProfileNotFound
|
|
25
25
|
from botocore.handlers import disable_signing
|
|
26
26
|
|
|
27
|
+
from eodag.api.product._assets import Asset
|
|
27
28
|
from eodag.plugins.authentication.base import Authentication
|
|
28
29
|
from eodag.types import S3SessionKwargs
|
|
29
|
-
from eodag.utils
|
|
30
|
+
from eodag.utils import get_bucket_name_and_prefix
|
|
31
|
+
from eodag.utils.exceptions import AuthenticationError, EodagError
|
|
30
32
|
|
|
31
33
|
if TYPE_CHECKING:
|
|
32
34
|
from mypy_boto3_s3 import S3Client, S3ServiceResource
|
|
@@ -283,3 +285,36 @@ class AwsAuth(Authentication):
|
|
|
283
285
|
"session": self.s3_session,
|
|
284
286
|
**rio_env_kwargs,
|
|
285
287
|
}
|
|
288
|
+
|
|
289
|
+
def presign_url(
|
|
290
|
+
self,
|
|
291
|
+
asset: Asset,
|
|
292
|
+
expires_in: int = 3600,
|
|
293
|
+
) -> str:
|
|
294
|
+
"""This method is used to presign a url to download an asset from S3.
|
|
295
|
+
|
|
296
|
+
:param asset: asset for which the url shall be presigned
|
|
297
|
+
:param expires_in: expiration time of the presigned url in seconds
|
|
298
|
+
:returns: presigned url
|
|
299
|
+
:raises: :class:`~eodag.utils.exceptions.EodagError`
|
|
300
|
+
:raises: :class:`NotImplementedError`
|
|
301
|
+
"""
|
|
302
|
+
if not getattr(self.config, "support_presign_url", True):
|
|
303
|
+
raise NotImplementedError(
|
|
304
|
+
f"presign_url is not supported for provider {self.provider}"
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
s3_client = self.get_s3_client()
|
|
308
|
+
bucket, prefix = get_bucket_name_and_prefix(asset["href"])
|
|
309
|
+
try:
|
|
310
|
+
presigned_url = s3_client.generate_presigned_url(
|
|
311
|
+
"get_object",
|
|
312
|
+
Params={
|
|
313
|
+
"Bucket": bucket,
|
|
314
|
+
"Key": prefix,
|
|
315
|
+
},
|
|
316
|
+
ExpiresIn=expires_in,
|
|
317
|
+
)
|
|
318
|
+
return presigned_url
|
|
319
|
+
except ClientError:
|
|
320
|
+
raise EodagError(f"Couldn't get a presigned URL for '{asset}'.")
|
|
@@ -19,6 +19,7 @@ from __future__ import annotations
|
|
|
19
19
|
|
|
20
20
|
from typing import TYPE_CHECKING, Any, Optional, Union
|
|
21
21
|
|
|
22
|
+
from eodag.api.product._assets import Asset
|
|
22
23
|
from eodag.plugins.base import PluginTopic
|
|
23
24
|
from eodag.utils.exceptions import MisconfiguredError
|
|
24
25
|
|
|
@@ -80,3 +81,19 @@ class Authentication(PluginTopic):
|
|
|
80
81
|
Authenticates with s3 and retrieves the available objects
|
|
81
82
|
"""
|
|
82
83
|
raise NotImplementedError
|
|
84
|
+
|
|
85
|
+
def presign_url(
|
|
86
|
+
self,
|
|
87
|
+
asset: Asset,
|
|
88
|
+
expires_in: int = 3600,
|
|
89
|
+
) -> str:
|
|
90
|
+
"""This method is used to presign a url to download an asset from S3.
|
|
91
|
+
|
|
92
|
+
:param asset: asset for which the url shall be presigned
|
|
93
|
+
:param expires_in: expiration time of the presigned url in seconds
|
|
94
|
+
:returns: presigned url
|
|
95
|
+
:raises: :class:`NotImplementedError`
|
|
96
|
+
"""
|
|
97
|
+
raise NotImplementedError(
|
|
98
|
+
f"presign_url is not implemented for plugin {type(self).__name__}"
|
|
99
|
+
)
|
|
@@ -25,6 +25,7 @@ from typing import TYPE_CHECKING, Optional
|
|
|
25
25
|
import requests
|
|
26
26
|
from requests.auth import AuthBase
|
|
27
27
|
|
|
28
|
+
from eodag.api.product._assets import Asset
|
|
28
29
|
from eodag.plugins.authentication.base import Authentication
|
|
29
30
|
from eodag.utils import HTTP_REQ_TIMEOUT, USER_AGENT, deepcopy, format_dict_items
|
|
30
31
|
from eodag.utils.exceptions import AuthenticationError, TimeOutError
|
|
@@ -143,3 +144,17 @@ class SASAuth(Authentication):
|
|
|
143
144
|
ssl_verify=ssl_verify,
|
|
144
145
|
matching_url=matching_url,
|
|
145
146
|
)
|
|
147
|
+
|
|
148
|
+
def presign_url(
|
|
149
|
+
self,
|
|
150
|
+
asset: Asset,
|
|
151
|
+
expires_in: int = 3600,
|
|
152
|
+
) -> str:
|
|
153
|
+
"""This method is used to presign a url to download an asset.
|
|
154
|
+
|
|
155
|
+
:param asset: asset for which the url shall be presigned
|
|
156
|
+
:param expires_in: expiration time of the presigned url in seconds
|
|
157
|
+
:returns: presigned url
|
|
158
|
+
"""
|
|
159
|
+
url = asset["href"]
|
|
160
|
+
return self.config.auth_uri.format(url=url)
|
|
@@ -70,6 +70,7 @@ class FilterLatestIntersect(Crunch):
|
|
|
70
70
|
# Warning: May crash if startTimeFromAscendingNode is not in the appropriate format
|
|
71
71
|
products.sort(key=self.sort_product_by_start_date, reverse=True)
|
|
72
72
|
filtered: list[EOProduct] = []
|
|
73
|
+
search_extent: BaseGeometry
|
|
73
74
|
add_to_filtered = filtered.append
|
|
74
75
|
footprint: Union[dict[str, Any], BaseGeometry, Any] = search_params.get(
|
|
75
76
|
"geometry"
|
|
@@ -20,15 +20,11 @@ from __future__ import annotations
|
|
|
20
20
|
import logging
|
|
21
21
|
from typing import TYPE_CHECKING, Any
|
|
22
22
|
|
|
23
|
+
from shapely.errors import ShapelyError
|
|
24
|
+
|
|
23
25
|
from eodag.plugins.crunch.base import Crunch
|
|
24
26
|
from eodag.utils import get_geometry_from_various
|
|
25
27
|
|
|
26
|
-
try:
|
|
27
|
-
from shapely.errors import GEOSException
|
|
28
|
-
except ImportError:
|
|
29
|
-
# shapely < 2.0 compatibility
|
|
30
|
-
from shapely.errors import TopologicalError as GEOSException
|
|
31
|
-
|
|
32
28
|
if TYPE_CHECKING:
|
|
33
29
|
from eodag.api.product import EOProduct
|
|
34
30
|
|
|
@@ -108,7 +104,7 @@ class FilterOverlap(Crunch):
|
|
|
108
104
|
product_geometry = product.geometry.buffer(0)
|
|
109
105
|
try:
|
|
110
106
|
intersection = search_geom.intersection(product_geometry)
|
|
111
|
-
except
|
|
107
|
+
except ShapelyError:
|
|
112
108
|
logger.debug(
|
|
113
109
|
"Product geometry still invalid. Overlap test restricted to containment"
|
|
114
110
|
)
|
eodag/plugins/download/aws.py
CHANGED
|
@@ -716,7 +716,7 @@ class AwsDownload(Download):
|
|
|
716
716
|
ignore_assets,
|
|
717
717
|
product,
|
|
718
718
|
)
|
|
719
|
-
if auth and isinstance(auth, boto3.
|
|
719
|
+
if auth and isinstance(auth, boto3.resource("s3").__class__):
|
|
720
720
|
s3_resource = auth
|
|
721
721
|
else:
|
|
722
722
|
s3_resource = boto3.resource(
|
|
@@ -15,12 +15,19 @@
|
|
|
15
15
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
16
|
# See the License for the specific language governing permissions and
|
|
17
17
|
# limitations under the License.
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
18
20
|
from typing import Optional
|
|
19
21
|
|
|
20
22
|
from eodag import EOProduct
|
|
21
23
|
from eodag.plugins.download.aws import AwsDownload
|
|
24
|
+
from eodag.utils import _deprecated
|
|
22
25
|
|
|
23
26
|
|
|
27
|
+
@_deprecated(
|
|
28
|
+
reason="Plugin that was used in creodias_s3 provider configuration, but not anymore",
|
|
29
|
+
version="3.10.0",
|
|
30
|
+
)
|
|
24
31
|
class CreodiasS3Download(AwsDownload):
|
|
25
32
|
"""
|
|
26
33
|
Download on creodias s3 from their VMs (extension of :class:`~eodag.plugins.download.aws.AwsDownload`)
|
|
@@ -57,10 +57,16 @@ from eodag.utils import (
|
|
|
57
57
|
DEFAULT_SEARCH_TIMEOUT,
|
|
58
58
|
deepcopy,
|
|
59
59
|
dict_items_recursive_sort,
|
|
60
|
+
get_geometry_from_ecmwf_area,
|
|
61
|
+
get_geometry_from_ecmwf_feature,
|
|
60
62
|
get_geometry_from_various,
|
|
61
63
|
)
|
|
62
64
|
from eodag.utils.cache import instance_cached_method
|
|
63
|
-
from eodag.utils.dates import
|
|
65
|
+
from eodag.utils.dates import (
|
|
66
|
+
COMPACT_DATE_RANGE_PATTERN,
|
|
67
|
+
DATE_RANGE_PATTERN,
|
|
68
|
+
is_range_in_range,
|
|
69
|
+
)
|
|
64
70
|
from eodag.utils.exceptions import DownloadError, NotAvailableError, ValidationError
|
|
65
71
|
from eodag.utils.requests import fetch_json
|
|
66
72
|
|
|
@@ -145,6 +151,7 @@ COP_DS_KEYWORDS = {
|
|
|
145
151
|
"aerosol_type",
|
|
146
152
|
"altitude",
|
|
147
153
|
"product_type",
|
|
154
|
+
"area",
|
|
148
155
|
"band",
|
|
149
156
|
"cdr_type",
|
|
150
157
|
"data_format",
|
|
@@ -182,6 +189,7 @@ COP_DS_KEYWORDS = {
|
|
|
182
189
|
"region",
|
|
183
190
|
"release_version",
|
|
184
191
|
"satellite",
|
|
192
|
+
"satellite_mission",
|
|
185
193
|
"sensor",
|
|
186
194
|
"sensor_and_algorithm",
|
|
187
195
|
"soil_level",
|
|
@@ -189,6 +197,7 @@ COP_DS_KEYWORDS = {
|
|
|
189
197
|
"statistic",
|
|
190
198
|
"system_version",
|
|
191
199
|
"temporal_aggregation",
|
|
200
|
+
"temporal_resolution",
|
|
192
201
|
"time_aggregation",
|
|
193
202
|
"time_reference",
|
|
194
203
|
"time_step",
|
|
@@ -607,6 +616,14 @@ class ECMWFSearch(PostJsonSearch):
|
|
|
607
616
|
# geometry
|
|
608
617
|
if "geometry" in params:
|
|
609
618
|
params["geometry"] = get_geometry_from_various(geometry=params["geometry"])
|
|
619
|
+
# ECMWF Polytope uses non-geojson structure for features
|
|
620
|
+
if "feature" in params:
|
|
621
|
+
params["geometry"] = get_geometry_from_ecmwf_feature(params["feature"])
|
|
622
|
+
params.pop("feature")
|
|
623
|
+
# bounding box in area format
|
|
624
|
+
if "area" in params:
|
|
625
|
+
params["geometry"] = get_geometry_from_ecmwf_area(params["area"])
|
|
626
|
+
params.pop("area")
|
|
610
627
|
|
|
611
628
|
return params
|
|
612
629
|
|
|
@@ -696,10 +713,13 @@ class ECMWFSearch(PostJsonSearch):
|
|
|
696
713
|
if "end" in filters:
|
|
697
714
|
filters[END] = filters.pop("end")
|
|
698
715
|
|
|
699
|
-
# extract default datetime
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
716
|
+
# extract default datetime and convert geometry
|
|
717
|
+
try:
|
|
718
|
+
processed_filters = self._preprocess_search_params(
|
|
719
|
+
deepcopy(filters), product_type
|
|
720
|
+
)
|
|
721
|
+
except Exception as e:
|
|
722
|
+
raise ValidationError(e.args[0]) from e
|
|
703
723
|
|
|
704
724
|
constraints_url = format_metadata(
|
|
705
725
|
getattr(self.config, "discover_queryables", {}).get("constraints_url", ""),
|
|
@@ -714,7 +734,7 @@ class ECMWFSearch(PostJsonSearch):
|
|
|
714
734
|
form: list[dict[str, Any]] = self._fetch_data(form_url)
|
|
715
735
|
|
|
716
736
|
formated_filters = self.format_as_provider_keyword(
|
|
717
|
-
product_type, processed_filters
|
|
737
|
+
product_type, deepcopy(processed_filters)
|
|
718
738
|
)
|
|
719
739
|
# we re-apply kwargs input to consider override of year, month, day and time.
|
|
720
740
|
for k, v in {**default_values, **kwargs}.items():
|
|
@@ -723,7 +743,6 @@ class ECMWFSearch(PostJsonSearch):
|
|
|
723
743
|
if key not in ALLOWED_KEYWORDS | {
|
|
724
744
|
START,
|
|
725
745
|
END,
|
|
726
|
-
"geom",
|
|
727
746
|
"geometry",
|
|
728
747
|
}:
|
|
729
748
|
raise ValidationError(
|
|
@@ -772,7 +791,7 @@ class ECMWFSearch(PostJsonSearch):
|
|
|
772
791
|
# To check if all keywords are queryable parameters, we check if they are in the
|
|
773
792
|
# available values or the product type config (available values calculated from the
|
|
774
793
|
# constraints might not include all queryables)
|
|
775
|
-
for keyword in
|
|
794
|
+
for keyword in processed_filters:
|
|
776
795
|
if (
|
|
777
796
|
keyword
|
|
778
797
|
not in available_values.keys()
|
|
@@ -780,7 +799,7 @@ class ECMWFSearch(PostJsonSearch):
|
|
|
780
799
|
| {
|
|
781
800
|
START,
|
|
782
801
|
END,
|
|
783
|
-
"
|
|
802
|
+
"geometry",
|
|
784
803
|
}
|
|
785
804
|
and keyword not in [f["name"] for f in form]
|
|
786
805
|
and keyword.removeprefix(ECMWF_PREFIX)
|
|
@@ -880,13 +899,21 @@ class ECMWFSearch(PostJsonSearch):
|
|
|
880
899
|
)
|
|
881
900
|
|
|
882
901
|
# We convert every single value to a list of string
|
|
883
|
-
filter_v = values if isinstance(values,
|
|
902
|
+
filter_v = list(values) if isinstance(values, tuple) else values
|
|
903
|
+
filter_v = filter_v if isinstance(filter_v, list) else [filter_v]
|
|
884
904
|
|
|
885
905
|
# We strip values of superfluous quotes (added by mapping converter to_geojson).
|
|
886
|
-
# ECMWF accept
|
|
887
|
-
# ECMWF accept
|
|
888
|
-
|
|
889
|
-
|
|
906
|
+
# ECMWF accept date ranges with /to/. We need to split it to an array
|
|
907
|
+
# ECMWF accept date ranges in format val1/val2. We need to split it to an array
|
|
908
|
+
date_regex = [
|
|
909
|
+
re.compile(p) for p in (DATE_RANGE_PATTERN, COMPACT_DATE_RANGE_PATTERN)
|
|
910
|
+
]
|
|
911
|
+
is_date = any(
|
|
912
|
+
any(r.match(v) is not None for r in date_regex) for v in filter_v
|
|
913
|
+
)
|
|
914
|
+
if is_date:
|
|
915
|
+
sep = re.compile(r"/to/|/")
|
|
916
|
+
filter_v = [i for v in filter_v for i in sep.split(str(v))]
|
|
890
917
|
|
|
891
918
|
# special handling for time 0000 converted to 0 by pre-formating with metadata_mapping
|
|
892
919
|
if keyword.split(":")[-1] == "time":
|
|
@@ -211,9 +211,12 @@ class CopMarineSearch(StaticStacSearch):
|
|
|
211
211
|
dataset_item: dict[str, Any],
|
|
212
212
|
collection_dict: dict[str, Any],
|
|
213
213
|
use_dataset_dates: bool = False,
|
|
214
|
+
product_id: Optional[str] = None,
|
|
214
215
|
) -> Optional[EOProduct]:
|
|
215
216
|
|
|
216
217
|
item_id = os.path.splitext(item_key.split("/")[-1])[0]
|
|
218
|
+
if product_id and product_id != item_id:
|
|
219
|
+
return None
|
|
217
220
|
download_url = s3_url + "/" + item_key
|
|
218
221
|
geometry = (
|
|
219
222
|
get_geometry_from_various(**dataset_item)
|
|
@@ -377,9 +380,12 @@ class CopMarineSearch(StaticStacSearch):
|
|
|
377
380
|
dataset_item,
|
|
378
381
|
collection_dict,
|
|
379
382
|
True,
|
|
383
|
+
kwargs.get("id"),
|
|
380
384
|
)
|
|
381
385
|
if product:
|
|
382
386
|
products.append(product)
|
|
387
|
+
if product and kwargs.get("id"):
|
|
388
|
+
break
|
|
383
389
|
continue
|
|
384
390
|
|
|
385
391
|
s3_client = _get_s3_client(endpoint_url)
|