eotdl 2023.7.19.post4__py3-none-any.whl → 2023.9.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.
- eotdl/commands/datasets.py +15 -29
- eotdl/curation/__init__.py +5 -5
- eotdl/curation/formatters.py +0 -2
- eotdl/curation/metadata.py +34 -9
- eotdl/curation/stac/assets.py +127 -0
- eotdl/curation/stac/dataframe.py +8 -4
- eotdl/curation/stac/extensions.py +295 -46
- eotdl/curation/stac/extent.py +130 -0
- eotdl/curation/stac/ml_dataset.py +509 -0
- eotdl/curation/stac/parsers.py +2 -0
- eotdl/curation/stac/stac.py +309 -286
- eotdl/curation/stac/utils.py +47 -1
- eotdl/datasets/__init__.py +2 -2
- eotdl/datasets/download.py +16 -3
- eotdl/datasets/ingest.py +21 -10
- eotdl/datasets/retrieve.py +10 -2
- eotdl/src/repos/APIRepo.py +42 -18
- eotdl/src/repos/AuthRepo.py +3 -3
- eotdl/src/usecases/auth/IsLogged.py +5 -3
- eotdl/src/usecases/datasets/DownloadDataset.py +35 -6
- eotdl/src/usecases/datasets/DownloadFileURL.py +22 -0
- eotdl/src/usecases/datasets/IngestFile.py +48 -28
- eotdl/src/usecases/datasets/IngestSTAC.py +43 -8
- eotdl/src/usecases/datasets/RetrieveDatasets.py +3 -2
- eotdl/src/usecases/datasets/__init__.py +1 -0
- eotdl/tools/sen12floods/tools.py +3 -3
- eotdl/tools/stac.py +8 -2
- {eotdl-2023.7.19.post4.dist-info → eotdl-2023.9.14.dist-info}/METADATA +2 -1
- {eotdl-2023.7.19.post4.dist-info → eotdl-2023.9.14.dist-info}/RECORD +31 -27
- {eotdl-2023.7.19.post4.dist-info → eotdl-2023.9.14.dist-info}/WHEEL +1 -1
- {eotdl-2023.7.19.post4.dist-info → eotdl-2023.9.14.dist-info}/entry_points.txt +0 -0
@@ -2,11 +2,24 @@
|
|
2
2
|
Module for STAC extensions objects
|
3
3
|
"""
|
4
4
|
|
5
|
+
import rasterio
|
6
|
+
import json
|
5
7
|
import pystac
|
8
|
+
|
9
|
+
from os.path import join, dirname
|
6
10
|
from pystac.extensions.sar import SarExtension
|
7
11
|
from pystac.extensions.sar import FrequencyBand, Polarization
|
8
12
|
from pystac.extensions.eo import Band, EOExtension
|
9
|
-
from
|
13
|
+
from pystac.extensions.label import (LabelClasses, LabelExtension, SummariesLabelExtension)
|
14
|
+
from pystac.extensions.raster import RasterExtension, RasterBand
|
15
|
+
from pystac.extensions.projection import ProjectionExtension
|
16
|
+
from typing import Union, List, Optional, Dict
|
17
|
+
|
18
|
+
import pandas as pd
|
19
|
+
from typing import List
|
20
|
+
|
21
|
+
|
22
|
+
SUPPORTED_EXTENSIONS = ('eo', 'sar', 'proj', 'raster')
|
10
23
|
|
11
24
|
|
12
25
|
class STACExtensionObject:
|
@@ -15,12 +28,14 @@ class STACExtensionObject:
|
|
15
28
|
self.properties = dict()
|
16
29
|
|
17
30
|
def add_extension_to_object(
|
18
|
-
self, obj: Union[pystac.Item, pystac.Asset]
|
31
|
+
self, obj: Union[pystac.Item, pystac.Asset],
|
32
|
+
obj_info: Optional[pd.DataFrame] = None
|
19
33
|
) -> Union[pystac.Item, pystac.Asset]:
|
20
34
|
"""
|
21
35
|
Add the extension to the given object
|
22
36
|
|
23
37
|
:param obj: object to add the extension
|
38
|
+
:param obj_info: object info from the STACDataFrame
|
24
39
|
"""
|
25
40
|
pass
|
26
41
|
|
@@ -28,22 +43,31 @@ class STACExtensionObject:
|
|
28
43
|
class SarExtensionObject(STACExtensionObject):
|
29
44
|
def __init__(self) -> None:
|
30
45
|
super().__init__()
|
31
|
-
|
46
|
+
self.polarizations = [Polarization.VV, Polarization.VH]
|
47
|
+
self.polarizations_dict = {"VV": Polarization.VV, "VH": Polarization.VH}
|
32
48
|
|
33
49
|
def add_extension_to_object(
|
34
|
-
self, obj: Union[pystac.Item, pystac.Asset]
|
50
|
+
self, obj: Union[pystac.Item, pystac.Asset],
|
51
|
+
obj_info: Optional[pd.DataFrame] = None
|
35
52
|
) -> Union[pystac.Item, pystac.Asset]:
|
36
53
|
"""
|
37
54
|
Add the extension to the given object
|
38
55
|
|
39
56
|
:param obj: object to add the extension
|
57
|
+
:param obj_info: object info from the STACDataFrame
|
40
58
|
"""
|
59
|
+
# Add SAR extension to the item
|
41
60
|
sar_ext = SarExtension.ext(obj, add_if_missing=True)
|
42
|
-
if isinstance(obj, pystac.Item)
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
polarizations =
|
61
|
+
if isinstance(obj, pystac.Item) or (
|
62
|
+
isinstance(obj, pystac.Asset)
|
63
|
+
and obj.title not in self.polarizations_dict.keys()
|
64
|
+
):
|
65
|
+
polarizations = self.polarizations
|
66
|
+
elif (
|
67
|
+
isinstance(obj, pystac.Asset)
|
68
|
+
and obj.title in self.polarizations_dict.keys()
|
69
|
+
):
|
70
|
+
polarizations = [self.polarizations_dict[obj.title]]
|
47
71
|
sar_ext.apply(
|
48
72
|
instrument_mode="EW",
|
49
73
|
polarizations=polarizations,
|
@@ -57,93 +81,102 @@ class SarExtensionObject(STACExtensionObject):
|
|
57
81
|
class EOS2ExtensionObject(STACExtensionObject):
|
58
82
|
def __init__(self) -> None:
|
59
83
|
super().__init__()
|
60
|
-
self.
|
61
|
-
Band.create(
|
62
|
-
name="
|
84
|
+
self.bands_dict = {
|
85
|
+
"B01": Band.create(
|
86
|
+
name="B01",
|
63
87
|
description="Coastal aerosol, 442.7 nm (S2A), 442.3 nm (S2B)",
|
64
88
|
common_name="coastal",
|
65
89
|
),
|
66
|
-
Band.create(
|
67
|
-
name="
|
90
|
+
"B02": Band.create(
|
91
|
+
name="B02",
|
68
92
|
description="Blue, 492.4 nm (S2A), 492.1 nm (S2B)",
|
69
93
|
common_name="blue",
|
70
94
|
),
|
71
|
-
Band.create(
|
72
|
-
name="
|
95
|
+
"B03": Band.create(
|
96
|
+
name="B03",
|
73
97
|
description="Green, 559.8 nm (S2A), 559.0 nm (S2B)",
|
74
98
|
common_name="green",
|
75
99
|
),
|
76
|
-
Band.create(
|
77
|
-
name="
|
100
|
+
"B04": Band.create(
|
101
|
+
name="B04",
|
78
102
|
description="Red, 664.6 nm (S2A), 665.0 nm (S2B)",
|
79
103
|
common_name="red",
|
80
104
|
),
|
81
|
-
Band.create(
|
82
|
-
name="
|
105
|
+
"B05": Band.create(
|
106
|
+
name="B05",
|
83
107
|
description="Vegetation red edge, 704.1 nm (S2A), 703.8 nm (S2B)",
|
84
108
|
common_name="rededge",
|
85
109
|
),
|
86
|
-
Band.create(
|
87
|
-
name="
|
110
|
+
"B06": Band.create(
|
111
|
+
name="B06",
|
88
112
|
description="Vegetation red edge, 740.5 nm (S2A), 739.1 nm (S2B)",
|
89
113
|
common_name="rededge",
|
90
114
|
),
|
91
|
-
Band.create(
|
92
|
-
name="
|
115
|
+
"B07": Band.create(
|
116
|
+
name="B07",
|
93
117
|
description="Vegetation red edge, 782.8 nm (S2A), 779.7 nm (S2B)",
|
94
118
|
common_name="rededge",
|
95
119
|
),
|
96
|
-
Band.create(
|
97
|
-
name="
|
120
|
+
"B08": Band.create(
|
121
|
+
name="B08",
|
98
122
|
description="NIR, 832.8 nm (S2A), 833.0 nm (S2B)",
|
99
123
|
common_name="nir",
|
100
124
|
),
|
101
|
-
Band.create(
|
102
|
-
name="
|
125
|
+
"B08a": Band.create(
|
126
|
+
name="B08a",
|
103
127
|
description="Narrow NIR, 864.7 nm (S2A), 864.0 nm (S2B)",
|
104
128
|
common_name="nir08",
|
105
129
|
),
|
106
|
-
Band.create(
|
107
|
-
name="
|
130
|
+
"B09": Band.create(
|
131
|
+
name="B09",
|
108
132
|
description="Water vapour, 945.1 nm (S2A), 943.2 nm (S2B)",
|
109
133
|
common_name="nir09",
|
110
134
|
),
|
111
|
-
Band.create(
|
112
|
-
name="
|
135
|
+
"B10": Band.create(
|
136
|
+
name="B10",
|
113
137
|
description="SWIR – Cirrus, 1373.5 nm (S2A), 1376.9 nm (S2B)",
|
114
138
|
common_name="cirrus",
|
115
139
|
),
|
116
|
-
Band.create(
|
117
|
-
name="
|
140
|
+
"B11": Band.create(
|
141
|
+
name="B11",
|
118
142
|
description="SWIR, 1613.7 nm (S2A), 1610.4 nm (S2B)",
|
119
143
|
common_name="swir16",
|
120
144
|
),
|
121
|
-
Band.create(
|
122
|
-
name="
|
145
|
+
"B12": Band.create(
|
146
|
+
name="B12",
|
123
147
|
description="SWIR, 2202.4 nm (S2A), 2185.7 nm (S2B)",
|
124
148
|
common_name="swir22",
|
125
149
|
),
|
126
|
-
|
150
|
+
}
|
127
151
|
|
128
152
|
def add_extension_to_object(
|
129
|
-
self, obj: Union[pystac.Item, pystac.Asset]
|
153
|
+
self, obj: Union[pystac.Item, pystac.Asset],
|
154
|
+
obj_info: pd.DataFrame
|
130
155
|
) -> Union[pystac.Item, pystac.Asset]:
|
131
156
|
"""
|
132
157
|
Add the extension to the given object
|
133
158
|
|
134
159
|
:param obj: object to add the extension
|
160
|
+
:param obj_info: object info from the STACDataFrame
|
135
161
|
"""
|
136
|
-
if isinstance(obj, pystac.Asset):
|
137
|
-
return
|
138
162
|
# Add EO extension
|
139
163
|
eo_ext = EOExtension.ext(obj, add_if_missing=True)
|
140
|
-
# Add the existing bands from the rasters assets list
|
141
|
-
eo_ext.apply(bands=self.bands)
|
142
164
|
# Add common metadata
|
143
|
-
obj.
|
144
|
-
|
145
|
-
|
146
|
-
|
165
|
+
if isinstance(obj, pystac.Item) or (
|
166
|
+
isinstance(obj, pystac.Asset) and obj.title not in self.bands_dict.keys()
|
167
|
+
):
|
168
|
+
obj.common_metadata.constellation = "Sentinel-2"
|
169
|
+
obj.common_metadata.platform = "Sentinel-2"
|
170
|
+
obj.common_metadata.instruments = ["Sentinel-2"]
|
171
|
+
obj.common_metadata.gsd = 10
|
172
|
+
# Add bands
|
173
|
+
bands = obj_info["bands"].values
|
174
|
+
bands = bands[0] if bands else None
|
175
|
+
bands_list = [self.bands_dict[band] for band in bands] if bands else None
|
176
|
+
eo_ext.apply(bands=bands_list)
|
177
|
+
|
178
|
+
elif isinstance(obj, pystac.Asset):
|
179
|
+
eo_ext.apply(bands=[self.bands_dict[obj.title]])
|
147
180
|
|
148
181
|
return obj
|
149
182
|
|
@@ -158,8 +191,224 @@ class DEMExtensionObject(STACExtensionObject):
|
|
158
191
|
super().__init__()
|
159
192
|
|
160
193
|
|
194
|
+
class LabelExtensionObject(STACExtensionObject):
|
195
|
+
def __init__(self) -> None:
|
196
|
+
super().__init__()
|
197
|
+
|
198
|
+
@classmethod
|
199
|
+
def add_extension_to_item(
|
200
|
+
self,
|
201
|
+
obj: pystac.Item,
|
202
|
+
label_names: List[str],
|
203
|
+
label_classes: List[str],
|
204
|
+
label_properties: list,
|
205
|
+
label_description: str,
|
206
|
+
label_methods: list,
|
207
|
+
label_tasks: List[str],
|
208
|
+
label_type: str
|
209
|
+
) -> Union[pystac.Item, pystac.Asset]:
|
210
|
+
"""
|
211
|
+
Add the extension to the given object
|
212
|
+
|
213
|
+
:param obj: object to add the extension
|
214
|
+
:param label_names: list of label names
|
215
|
+
:param label_classes: list of label classes of the item
|
216
|
+
:param label_classes_list: list of all possible label classes
|
217
|
+
:param label_properties: list of label properties
|
218
|
+
:param label_description: label description
|
219
|
+
:param label_methods: list of labeling methods
|
220
|
+
:param label_tasks: list of label tasks
|
221
|
+
:param label_type: label type
|
222
|
+
|
223
|
+
:return: the item with the label extension
|
224
|
+
"""
|
225
|
+
label_item = pystac.Item(id=obj.id,
|
226
|
+
geometry=obj.geometry,
|
227
|
+
bbox=obj.bbox,
|
228
|
+
properties=dict(),
|
229
|
+
datetime=obj.datetime
|
230
|
+
)
|
231
|
+
|
232
|
+
# Add the label extension to the item
|
233
|
+
LabelExtension.add_to(label_item)
|
234
|
+
|
235
|
+
# Access the label extension
|
236
|
+
label_ext = LabelExtension.ext(label_item)
|
237
|
+
|
238
|
+
# Add the label classes
|
239
|
+
for name, classes in zip(label_names, label_classes):
|
240
|
+
label_classes = LabelClasses.create(
|
241
|
+
name=name,
|
242
|
+
classes=classes,
|
243
|
+
)
|
244
|
+
label_ext.label_classes = [label_classes]
|
245
|
+
|
246
|
+
# TODO kwargs
|
247
|
+
# Add the label properties
|
248
|
+
label_ext.label_properties = label_properties
|
249
|
+
# Add the label description
|
250
|
+
label_ext.label_description = label_description
|
251
|
+
# Add the label methods
|
252
|
+
label_ext.label_methods = label_methods
|
253
|
+
# Add the label type
|
254
|
+
label_ext.label_type = label_type
|
255
|
+
# Add the label tasks
|
256
|
+
label_ext.label_tasks = label_tasks
|
257
|
+
# Add the source
|
258
|
+
label_ext.add_source(obj)
|
259
|
+
|
260
|
+
return label_item
|
261
|
+
|
262
|
+
@classmethod
|
263
|
+
def add_geojson_to_items(self,
|
264
|
+
collection: pystac.Collection,
|
265
|
+
df: pd.DataFrame
|
266
|
+
) -> None:
|
267
|
+
"""
|
268
|
+
"""
|
269
|
+
for item in collection.get_all_items():
|
270
|
+
label_type = item.properties['label:type']
|
271
|
+
file_name = 'vector_labels' if label_type == 'vector' else 'raster_labels'
|
272
|
+
geojson_path = join(dirname(item.get_self_href()), f'{file_name}.geojson')
|
273
|
+
|
274
|
+
properties = {'roles': ['labels', f'labels-{label_type}']}
|
275
|
+
|
276
|
+
# TODO depending on the tasks, there must be extra fields
|
277
|
+
# TODO https://github.com/stac-extensions/label#assets
|
278
|
+
tasks = item.properties['label:tasks']
|
279
|
+
if 'tile_regression' in tasks:
|
280
|
+
pass
|
281
|
+
elif any(task in tasks for task in ('tile_classification', 'object_detection', 'segmentation')):
|
282
|
+
pass
|
283
|
+
|
284
|
+
label_ext = LabelExtension.ext(item)
|
285
|
+
label_ext.add_geojson_labels(href=geojson_path,
|
286
|
+
title='Label',
|
287
|
+
properties=properties)
|
288
|
+
item.make_asset_hrefs_relative()
|
289
|
+
|
290
|
+
item_id = item.id
|
291
|
+
geometry = item.geometry
|
292
|
+
labels = [df[df['id'] == item_id]['label'].values[0]]
|
293
|
+
# There is data like DEM data that does not have datetime but start and end datetime
|
294
|
+
datetime = item.datetime.isoformat() if item.datetime else (item.properties.start_datetime.isoformat(),
|
295
|
+
item.properties.end_datetime.isoformat())
|
296
|
+
labels_properties = dict(zip(item.properties['label:properties'], labels))
|
297
|
+
labels_properties['datetime'] = datetime
|
298
|
+
|
299
|
+
geojson = {
|
300
|
+
"type": "FeatureCollection",
|
301
|
+
"features": [
|
302
|
+
{
|
303
|
+
"type": "Feature",
|
304
|
+
"geometry": geometry,
|
305
|
+
"properties": labels_properties,
|
306
|
+
}
|
307
|
+
],
|
308
|
+
}
|
309
|
+
|
310
|
+
with open(geojson_path, "w") as f:
|
311
|
+
json.dump(geojson, f)
|
312
|
+
|
313
|
+
@classmethod
|
314
|
+
def add_extension_to_collection(
|
315
|
+
self,
|
316
|
+
obj: pystac.Collection,
|
317
|
+
label_names: List[str],
|
318
|
+
label_classes: List[Union[list, tuple]],
|
319
|
+
label_type: str
|
320
|
+
) -> None:
|
321
|
+
"""
|
322
|
+
Add the label extension to the given collection
|
323
|
+
|
324
|
+
:param obj: object to add the extension
|
325
|
+
:param label_names: list of label names
|
326
|
+
:param label_classes: list of label classes
|
327
|
+
:param label_type: label type
|
328
|
+
"""
|
329
|
+
LabelExtension.add_to(obj)
|
330
|
+
|
331
|
+
# Add the label extension to the collection
|
332
|
+
label_ext = SummariesLabelExtension(obj)
|
333
|
+
|
334
|
+
# Add the label classes
|
335
|
+
for name, classes in zip(label_names, label_classes):
|
336
|
+
label_classes = LabelClasses.create(
|
337
|
+
name=name,
|
338
|
+
classes=classes,
|
339
|
+
)
|
340
|
+
label_ext.label_classes = [label_classes]
|
341
|
+
|
342
|
+
# Add the label type
|
343
|
+
label_ext.label_type = label_type
|
344
|
+
|
345
|
+
|
346
|
+
class RasterExtensionObject(STACExtensionObject):
|
347
|
+
def __init__(self) -> None:
|
348
|
+
super().__init__()
|
349
|
+
|
350
|
+
def add_extension_to_object(
|
351
|
+
self, obj: Union[pystac.Item, pystac.Asset],
|
352
|
+
obj_info: Optional[pd.DataFrame] = None
|
353
|
+
) -> Union[pystac.Item, pystac.Asset]:
|
354
|
+
"""
|
355
|
+
Add the extension to the given object
|
356
|
+
|
357
|
+
:param obj: object to add the extension
|
358
|
+
:param obj_info: object info from the STACDataFrame
|
359
|
+
"""
|
360
|
+
if not isinstance(obj, pystac.Asset):
|
361
|
+
return obj
|
362
|
+
else:
|
363
|
+
raster_ext = RasterExtension.ext(obj, add_if_missing=True)
|
364
|
+
src = rasterio.open(obj.href)
|
365
|
+
bands = list()
|
366
|
+
for band in src.indexes:
|
367
|
+
bands.append(RasterBand.create(
|
368
|
+
nodata=src.nodatavals[band - 1],
|
369
|
+
data_type=src.dtypes[band - 1],
|
370
|
+
spatial_resolution=src.res) if src.nodatavals else RasterBand.create(
|
371
|
+
data_type=src.dtypes[band - 1],
|
372
|
+
spatial_resolution=src.res))
|
373
|
+
raster_ext.apply(bands=bands)
|
374
|
+
|
375
|
+
return obj
|
376
|
+
|
377
|
+
|
378
|
+
class ProjExtensionObject(STACExtensionObject):
|
379
|
+
def __init__(self) -> None:
|
380
|
+
super().__init__()
|
381
|
+
|
382
|
+
def add_extension_to_object(
|
383
|
+
self, obj: Union[pystac.Item, pystac.Asset],
|
384
|
+
obj_info: pd.DataFrame
|
385
|
+
) -> Union[pystac.Item, pystac.Asset]:
|
386
|
+
"""
|
387
|
+
Add the extension to the given object
|
388
|
+
|
389
|
+
:param obj: object to add the extension
|
390
|
+
:param obj_info: object info from the STACDataFrame
|
391
|
+
"""
|
392
|
+
# Add raster extension to the item
|
393
|
+
if isinstance(obj, pystac.Asset):
|
394
|
+
return obj
|
395
|
+
elif isinstance(obj, pystac.Item):
|
396
|
+
proj_ext = ProjectionExtension.ext(obj, add_if_missing=True)
|
397
|
+
ds = rasterio.open(obj_info['image'].values[0])
|
398
|
+
# Assume all the bands have the same projection
|
399
|
+
proj_ext.apply(
|
400
|
+
epsg=ds.crs.to_epsg(),
|
401
|
+
transform=ds.transform,
|
402
|
+
shape=ds.shape,
|
403
|
+
)
|
404
|
+
|
405
|
+
return obj
|
406
|
+
|
407
|
+
|
161
408
|
type_stac_extensions_dict = {
|
162
409
|
"sar": SarExtensionObject(),
|
163
410
|
"eo": EOS2ExtensionObject(),
|
164
411
|
"dem": DEMExtensionObject(),
|
412
|
+
"raster": RasterExtensionObject(),
|
413
|
+
"proj": ProjExtensionObject()
|
165
414
|
}
|
@@ -0,0 +1,130 @@
|
|
1
|
+
'''
|
2
|
+
Module for STAC extent
|
3
|
+
'''
|
4
|
+
|
5
|
+
import pystac
|
6
|
+
from datetime import datetime
|
7
|
+
import rasterio
|
8
|
+
import json
|
9
|
+
|
10
|
+
from glob import glob
|
11
|
+
from os.path import dirname
|
12
|
+
|
13
|
+
from typing import List
|
14
|
+
|
15
|
+
|
16
|
+
def get_dem_temporal_interval() -> pystac.TemporalExtent:
|
17
|
+
"""
|
18
|
+
Get a temporal interval for DEM data
|
19
|
+
"""
|
20
|
+
min_date = datetime.strptime('2011-01-01', '%Y-%m-%d')
|
21
|
+
max_date = datetime.strptime('2015-01-07', '%Y-%m-%d')
|
22
|
+
|
23
|
+
return pystac.TemporalExtent([(min_date, max_date)])
|
24
|
+
|
25
|
+
def get_unknow_temporal_interval() -> pystac.TemporalExtent:
|
26
|
+
"""
|
27
|
+
Get an unknown temporal interval
|
28
|
+
"""
|
29
|
+
min_date = datetime.strptime('2000-01-01', '%Y-%m-%d')
|
30
|
+
max_date = datetime.strptime('2023-12-31', '%Y-%m-%d')
|
31
|
+
|
32
|
+
return pystac.TemporalExtent([(min_date, max_date)])
|
33
|
+
|
34
|
+
def get_unknow_extent() -> pystac.Extent:
|
35
|
+
"""
|
36
|
+
"""
|
37
|
+
return pystac.Extent(spatial=pystac.SpatialExtent([[0, 0, 0, 0]]),
|
38
|
+
temporal=pystac.TemporalExtent([(datetime.strptime('2000-01-01', '%Y-%m-%d'),
|
39
|
+
datetime.strptime('2023-12-31', '%Y-%m-%d')
|
40
|
+
)]))
|
41
|
+
|
42
|
+
|
43
|
+
def get_collection_extent(rasters: List[str]) -> pystac.Extent:
|
44
|
+
"""
|
45
|
+
Get the extent of a collection
|
46
|
+
|
47
|
+
:param rasters: list of rasters
|
48
|
+
"""
|
49
|
+
# Get the spatial extent of the collection
|
50
|
+
spatial_extent = get_collection_spatial_extent(rasters)
|
51
|
+
# Get the temporal interval of the collection
|
52
|
+
temporal_interval = get_collection_temporal_interval(rasters)
|
53
|
+
# Create the Extent object
|
54
|
+
extent = pystac.Extent(spatial=spatial_extent, temporal=temporal_interval)
|
55
|
+
|
56
|
+
return extent
|
57
|
+
|
58
|
+
def get_collection_spatial_extent(rasters: List[str]) -> pystac.SpatialExtent:
|
59
|
+
"""
|
60
|
+
Get the spatial extent of a collection
|
61
|
+
|
62
|
+
:param path: path to the directory
|
63
|
+
"""
|
64
|
+
# Get the bounding boxes of all the given rasters
|
65
|
+
bboxes = list()
|
66
|
+
for raster in rasters:
|
67
|
+
with rasterio.open(raster) as ds:
|
68
|
+
bounds = ds.bounds
|
69
|
+
dst_crs = 'EPSG:4326'
|
70
|
+
try:
|
71
|
+
left, bottom, right, top = rasterio.warp.transform_bounds(ds.crs, dst_crs, *bounds)
|
72
|
+
bbox = [left, bottom, right, top]
|
73
|
+
except rasterio.errors.CRSError:
|
74
|
+
spatial_extent = pystac.SpatialExtent([[0, 0, 0, 0]])
|
75
|
+
return spatial_extent
|
76
|
+
bboxes.append(bbox)
|
77
|
+
# Get the minimum and maximum values of the bounding boxes
|
78
|
+
try:
|
79
|
+
left = min([bbox[0] for bbox in bboxes])
|
80
|
+
bottom = min([bbox[1] for bbox in bboxes])
|
81
|
+
right = max([bbox[2] for bbox in bboxes])
|
82
|
+
top = max([bbox[3] for bbox in bboxes])
|
83
|
+
spatial_extent = pystac.SpatialExtent([[left, bottom, right, top]])
|
84
|
+
except ValueError:
|
85
|
+
spatial_extent = pystac.SpatialExtent([[0, 0, 0, 0]])
|
86
|
+
finally:
|
87
|
+
return spatial_extent
|
88
|
+
|
89
|
+
def get_collection_temporal_interval(rasters: List[str]) -> pystac.TemporalExtent:
|
90
|
+
"""
|
91
|
+
Get the temporal interval of a collection
|
92
|
+
|
93
|
+
:param path: path to the directory
|
94
|
+
"""
|
95
|
+
# Get all the metadata.json files in the directory of all the given rasters
|
96
|
+
metadata_json_files = list()
|
97
|
+
for raster in rasters:
|
98
|
+
metadata_json_files += glob(f'{dirname(raster)}/*.json', recursive=True)
|
99
|
+
|
100
|
+
if not metadata_json_files:
|
101
|
+
return get_unknow_temporal_interval() # If there is no metadata, set a generic temporal interval
|
102
|
+
|
103
|
+
# Get the temporal interval of every metadata.json file and the type of the data
|
104
|
+
data_types = list()
|
105
|
+
temporal_intervals = list()
|
106
|
+
for metadata_json_file in metadata_json_files:
|
107
|
+
with open(metadata_json_file, 'r') as f:
|
108
|
+
metadata = json.load(f)
|
109
|
+
# Append the temporal interval to the list as a datetime object
|
110
|
+
temporal_intervals.append(metadata['date-adquired']) if metadata['date-adquired'] else None
|
111
|
+
# Append the data type to the list
|
112
|
+
data_types.append(metadata['type']) if metadata['type'] else None
|
113
|
+
|
114
|
+
if temporal_intervals:
|
115
|
+
try:
|
116
|
+
# Get the minimum and maximum values of the temporal intervals
|
117
|
+
min_date = min([datetime.strptime(interval, '%Y-%m-%d') for interval in temporal_intervals])
|
118
|
+
max_date = max([datetime.strptime(interval, '%Y-%m-%d') for interval in temporal_intervals])
|
119
|
+
except ValueError:
|
120
|
+
min_date = datetime.strptime('2000-01-01', '%Y-%m-%d')
|
121
|
+
max_date = datetime.strptime('2023-12-31', '%Y-%m-%d')
|
122
|
+
finally:
|
123
|
+
# Create the temporal interval
|
124
|
+
return pystac.TemporalExtent([(min_date, max_date)])
|
125
|
+
else:
|
126
|
+
# Check if the collection is composed by DEM data. If not, set a generic temporal interval
|
127
|
+
if set(data_types) == {'dem'} or set(data_types) == {'DEM'} or set(data_types) == {'dem', 'DEM'}:
|
128
|
+
return get_dem_temporal_interval()
|
129
|
+
else:
|
130
|
+
return get_unknow_temporal_interval()
|