eotdl 2023.11.2.post5__py3-none-any.whl → 2023.11.3.post2__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/__init__.py +1 -1
- eotdl/access/__init__.py +6 -3
- eotdl/access/airbus/__init__.py +5 -1
- eotdl/access/airbus/client.py +356 -338
- eotdl/access/airbus/parameters.py +19 -4
- eotdl/access/airbus/utils.py +26 -21
- eotdl/access/download.py +30 -14
- eotdl/access/search.py +17 -6
- eotdl/access/sentinelhub/__init__.py +5 -1
- eotdl/access/sentinelhub/client.py +57 -54
- eotdl/access/sentinelhub/evalscripts.py +38 -39
- eotdl/access/sentinelhub/parameters.py +43 -23
- eotdl/access/sentinelhub/utils.py +38 -28
- eotdl/auth/errors.py +2 -1
- eotdl/commands/auth.py +3 -3
- eotdl/curation/__init__.py +5 -1
- eotdl/curation/stac/__init__.py +5 -1
- eotdl/curation/stac/assets.py +55 -32
- eotdl/curation/stac/dataframe.py +20 -14
- eotdl/curation/stac/dataframe_bck.py +2 -2
- eotdl/curation/stac/dataframe_labeling.py +15 -12
- eotdl/curation/stac/extensions/__init__.py +6 -2
- eotdl/curation/stac/extensions/base.py +8 -4
- eotdl/curation/stac/extensions/dem.py +6 -3
- eotdl/curation/stac/extensions/eo.py +10 -6
- eotdl/curation/stac/extensions/label/__init__.py +5 -1
- eotdl/curation/stac/extensions/label/base.py +40 -26
- eotdl/curation/stac/extensions/label/image_name_labeler.py +64 -43
- eotdl/curation/stac/extensions/label/scaneo.py +59 -56
- eotdl/curation/stac/extensions/ml_dataset.py +154 -56
- eotdl/curation/stac/extensions/projection.py +11 -9
- eotdl/curation/stac/extensions/raster.py +22 -14
- eotdl/curation/stac/extensions/sar.py +12 -7
- eotdl/curation/stac/extent.py +67 -40
- eotdl/curation/stac/parsers.py +18 -10
- eotdl/curation/stac/stac.py +81 -62
- eotdl/datasets/__init__.py +1 -1
- eotdl/datasets/download.py +42 -55
- eotdl/datasets/ingest.py +68 -11
- eotdl/files/__init__.py +1 -1
- eotdl/files/ingest.py +3 -1
- eotdl/models/download.py +1 -1
- eotdl/repos/AuthAPIRepo.py +0 -1
- eotdl/repos/DatasetsAPIRepo.py +22 -146
- eotdl/repos/FilesAPIRepo.py +7 -92
- eotdl/repos/ModelsAPIRepo.py +0 -1
- eotdl/tools/__init__.py +5 -1
- eotdl/tools/geo_utils.py +78 -48
- eotdl/tools/metadata.py +13 -11
- eotdl/tools/paths.py +14 -14
- eotdl/tools/stac.py +36 -31
- eotdl/tools/time_utils.py +53 -26
- eotdl/tools/tools.py +84 -50
- {eotdl-2023.11.2.post5.dist-info → eotdl-2023.11.3.post2.dist-info}/METADATA +5 -3
- eotdl-2023.11.3.post2.dist-info/RECORD +84 -0
- eotdl-2023.11.2.post5.dist-info/RECORD +0 -84
- {eotdl-2023.11.2.post5.dist-info → eotdl-2023.11.3.post2.dist-info}/WHEEL +0 -0
- {eotdl-2023.11.2.post5.dist-info → eotdl-2023.11.3.post2.dist-info}/entry_points.txt +0 -0
@@ -1,5 +1,9 @@
|
|
1
1
|
"""Implements the :stac-ext:`Machine Learning Dataset Extension <ml-dataset>`."""
|
2
2
|
|
3
|
+
from typing import Any, Dict, List, Optional, Generic, TypeVar, Union, Set
|
4
|
+
from shutil import rmtree
|
5
|
+
from os.path import dirname, exists
|
6
|
+
|
3
7
|
import traceback
|
4
8
|
import json
|
5
9
|
import random
|
@@ -10,11 +14,8 @@ from tqdm import tqdm
|
|
10
14
|
from pystac.extensions.base import ExtensionManagementMixin, PropertiesExtension
|
11
15
|
from pystac.extensions.label import LabelExtension
|
12
16
|
from pystac import STACValidationError
|
13
|
-
from shutil import rmtree
|
14
|
-
from os.path import dirname, exists
|
15
17
|
from pystac.cache import ResolvedObjectCache
|
16
18
|
from pystac.extensions.hooks import ExtensionHooks
|
17
|
-
from typing import Any, Dict, List, Optional, Generic, TypeVar, Union, Set
|
18
19
|
from ....tools import make_links_relative_to_path
|
19
20
|
|
20
21
|
T = TypeVar("T", pystac.Item, pystac.Collection, pystac.Catalog)
|
@@ -74,74 +75,129 @@ class MLDatasetExtension(
|
|
74
75
|
self._resolved_objects = ResolvedObjectCache()
|
75
76
|
|
76
77
|
def apply(self, name: str = None) -> None:
|
78
|
+
"""
|
79
|
+
Applies the :stac-ext:`Machine Learning Dataset Extension <ml-dataset>` to the extended
|
80
|
+
:class:`~pystac.Catalog`.
|
81
|
+
"""
|
77
82
|
self.name = name
|
78
83
|
|
79
84
|
@property
|
80
85
|
def name(self) -> str:
|
86
|
+
"""
|
87
|
+
Name of the ML Dataset.
|
88
|
+
"""
|
81
89
|
return self.extra_fields[f"{PREFIX}name"]
|
82
90
|
|
83
91
|
@name.setter
|
84
92
|
def name(self, v: str) -> None:
|
93
|
+
"""
|
94
|
+
Set the name of the ML Dataset.
|
95
|
+
"""
|
85
96
|
self.extra_fields[f"{PREFIX}name"] = v
|
86
97
|
|
87
98
|
@property
|
88
99
|
def tasks(self) -> List:
|
100
|
+
"""
|
101
|
+
Tasks of the ML Dataset.
|
102
|
+
"""
|
89
103
|
return self.extra_fields[f"{PREFIX}tasks"]
|
90
104
|
|
91
105
|
@tasks.setter
|
92
106
|
def tasks(self, v: Union[list, tuple]) -> None:
|
107
|
+
"""
|
108
|
+
Set the tasks of the ML Dataset.
|
109
|
+
"""
|
93
110
|
self.extra_fields[f"{PREFIX}tasks"] = v
|
94
111
|
|
95
112
|
@property
|
96
113
|
def type(self) -> str:
|
114
|
+
"""
|
115
|
+
Type of the ML Dataset.
|
116
|
+
"""
|
97
117
|
return self.extra_fields[f"{PREFIX}type"]
|
98
118
|
|
99
119
|
@type.setter
|
100
120
|
def type(self, v: str) -> None:
|
121
|
+
"""
|
122
|
+
Set the type of the ML Dataset.
|
123
|
+
"""
|
101
124
|
self.extra_fields[f"{PREFIX}type"] = v
|
102
125
|
|
103
126
|
@property
|
104
127
|
def inputs_type(self) -> str:
|
128
|
+
"""
|
129
|
+
Inputs type of the ML Dataset.
|
130
|
+
"""
|
105
131
|
return self.extra_fields[f"{PREFIX}inputs-type"]
|
106
132
|
|
107
133
|
@inputs_type.setter
|
108
134
|
def inputs_type(self, v: str) -> None:
|
135
|
+
"""
|
136
|
+
Set the inputs type of the ML Dataset.
|
137
|
+
"""
|
109
138
|
self.extra_fields[f"{PREFIX}inputs-type"] = v
|
110
139
|
|
111
140
|
@property
|
112
141
|
def annotations_type(self) -> str:
|
142
|
+
"""
|
143
|
+
Annotations type of the ML Dataset.
|
144
|
+
"""
|
113
145
|
return self.extra_fields[f"{PREFIX}annotations-type"]
|
114
146
|
|
115
147
|
@annotations_type.setter
|
116
148
|
def annotations_type(self, v: str) -> None:
|
149
|
+
"""
|
150
|
+
Set the annotations type of the ML Dataset.
|
151
|
+
"""
|
117
152
|
self.extra_fields[f"{PREFIX}annotations-type"] = v
|
118
153
|
|
119
154
|
@property
|
120
155
|
def splits(self) -> List[str]:
|
121
|
-
|
156
|
+
"""
|
157
|
+
Splits of the ML Dataset.
|
158
|
+
"""
|
159
|
+
return self.extra_fields[f"{PREFIX}splits"]
|
122
160
|
|
123
161
|
@splits.setter
|
124
162
|
def splits(self, v: dict) -> None:
|
163
|
+
"""
|
164
|
+
Set the splits of the ML Dataset.
|
165
|
+
"""
|
125
166
|
self.extra_fields[f"{PREFIX}splits"] = v
|
126
167
|
|
127
168
|
@property
|
128
169
|
def quality_metrics(self) -> List[dict]:
|
129
|
-
|
170
|
+
"""
|
171
|
+
Quality metrics of the ML Dataset.
|
172
|
+
"""
|
173
|
+
return self.extra_fields[f"{PREFIX}quality-metrics"]
|
130
174
|
|
131
175
|
@quality_metrics.setter
|
132
176
|
def quality_metrics(self, v: dict) -> None:
|
177
|
+
"""
|
178
|
+
Set the quality metrics of the ML Dataset.
|
179
|
+
"""
|
133
180
|
self.extra_fields[f"{PREFIX}quality-metrics"] = v
|
134
181
|
|
135
182
|
@property
|
136
183
|
def version(self) -> str:
|
137
|
-
|
184
|
+
"""
|
185
|
+
Version of the ML Dataset.
|
186
|
+
"""
|
187
|
+
return self.extra_fields[f"{PREFIX}version"]
|
138
188
|
|
139
189
|
@version.setter
|
140
190
|
def version(self, v: str) -> None:
|
191
|
+
"""
|
192
|
+
Set the version of the ML Dataset.
|
193
|
+
"""
|
141
194
|
self.extra_fields[f"{PREFIX}version"] = v
|
142
195
|
|
143
196
|
@classmethod
|
144
197
|
def get_schema_uri(cls) -> str:
|
198
|
+
"""
|
199
|
+
Get the JSON Schema URI that validates the extended object.
|
200
|
+
"""
|
145
201
|
return SCHEMA_URI
|
146
202
|
|
147
203
|
def add_metric(self, metric: dict) -> None:
|
@@ -150,11 +206,11 @@ class MLDatasetExtension(
|
|
150
206
|
Args:
|
151
207
|
metric : The metric to add.
|
152
208
|
"""
|
153
|
-
if not self.extra_fields.get(f
|
154
|
-
self.extra_fields[f
|
209
|
+
if not self.extra_fields.get(f"{PREFIX}quality-metrics"):
|
210
|
+
self.extra_fields[f"{PREFIX}quality-metrics"] = []
|
155
211
|
|
156
|
-
if metric not in self.extra_fields[f
|
157
|
-
self.extra_fields[f
|
212
|
+
if metric not in self.extra_fields[f"{PREFIX}quality-metrics"]:
|
213
|
+
self.extra_fields[f"{PREFIX}quality-metrics"].append(metric)
|
158
214
|
|
159
215
|
def add_metrics(self, metrics: List[dict]) -> None:
|
160
216
|
"""Add a list of metrics to this object's set of metrics.
|
@@ -204,23 +260,34 @@ class CollectionMLDatasetExtension(MLDatasetExtension[pystac.Collection]):
|
|
204
260
|
self.properties[f"{PREFIX}split-items"] = []
|
205
261
|
|
206
262
|
def __repr__(self) -> str:
|
207
|
-
return "<CollectionMLDatasetExtension Item id={
|
263
|
+
return f"<CollectionMLDatasetExtension Item id={self.collection.id}>"
|
208
264
|
|
209
265
|
@property
|
210
266
|
def splits(self) -> List[dict]:
|
211
|
-
|
267
|
+
"""
|
268
|
+
Splits of the ML Dataset.
|
269
|
+
"""
|
270
|
+
return self.extra_fields[f"{PREFIX}splits"]
|
212
271
|
|
213
272
|
@splits.setter
|
214
273
|
def splits(self, v: dict) -> None:
|
274
|
+
"""
|
275
|
+
Set the splits of the ML Dataset.
|
276
|
+
"""
|
215
277
|
self.properties[f"{PREFIX}split-items"] = v
|
216
278
|
|
217
279
|
def add_split(self, v: dict) -> None:
|
280
|
+
"""
|
281
|
+
Add a split to the ML Dataset.
|
282
|
+
"""
|
218
283
|
self.properties[f"{PREFIX}split-items"].append(v)
|
219
284
|
|
220
285
|
def create_and_add_split(
|
221
286
|
self, split_data: List[pystac.Item], split_type: str
|
222
287
|
) -> None:
|
223
|
-
"""
|
288
|
+
"""
|
289
|
+
Create and add a split to the ML Dataset.
|
290
|
+
"""
|
224
291
|
items_ids = [item.id for item in split_data]
|
225
292
|
items_ids.sort()
|
226
293
|
|
@@ -238,7 +305,6 @@ class CollectionMLDatasetExtension(MLDatasetExtension[pystac.Collection]):
|
|
238
305
|
item_ml.split = split_type
|
239
306
|
|
240
307
|
|
241
|
-
|
242
308
|
class ItemMLDatasetExtension(MLDatasetExtension[pystac.Item]):
|
243
309
|
"""A concrete implementation of :class:`MLDatasetExtension` on an
|
244
310
|
:class:`~pystac.Item` that extends the properties of the Item to include properties
|
@@ -257,22 +323,32 @@ class ItemMLDatasetExtension(MLDatasetExtension[pystac.Item]):
|
|
257
323
|
|
258
324
|
@property
|
259
325
|
def split(self) -> str:
|
260
|
-
|
326
|
+
"""
|
327
|
+
Split of the ML Dataset.
|
328
|
+
"""
|
329
|
+
return self.properties[f"{PREFIX}split"]
|
261
330
|
|
262
331
|
@split.setter
|
263
332
|
def split(self, v: str) -> None:
|
333
|
+
"""
|
334
|
+
Set the split of the ML Dataset.
|
335
|
+
"""
|
264
336
|
self.properties[f"{PREFIX}split"] = v
|
265
337
|
|
266
338
|
def __repr__(self) -> str:
|
267
|
-
return "<ItemMLDatasetExtension Item id={
|
339
|
+
return f"<ItemMLDatasetExtension Item id={self.item.id}>"
|
268
340
|
|
269
341
|
|
270
342
|
class MLDatasetQualityMetrics:
|
271
|
-
"""
|
343
|
+
"""
|
344
|
+
ML Dataset Quality Metrics
|
345
|
+
"""
|
272
346
|
|
273
347
|
@classmethod
|
274
|
-
def calculate(
|
275
|
-
"""
|
348
|
+
def calculate(cls, catalog: Union[pystac.Catalog, str]) -> None:
|
349
|
+
"""
|
350
|
+
Calculate the quality metrics of the catalog
|
351
|
+
"""
|
276
352
|
if isinstance(catalog, str):
|
277
353
|
catalog = MLDatasetExtension(pystac.read_file(catalog))
|
278
354
|
elif isinstance(catalog, pystac.Catalog):
|
@@ -284,15 +360,15 @@ class MLDatasetQualityMetrics:
|
|
284
360
|
)
|
285
361
|
|
286
362
|
try:
|
287
|
-
catalog.add_metric(
|
288
|
-
catalog.add_metric(
|
289
|
-
except AttributeError:
|
363
|
+
catalog.add_metric(cls._search_spatial_duplicates(catalog))
|
364
|
+
catalog.add_metric(cls._get_classes_balance(catalog))
|
365
|
+
except AttributeError as exc:
|
290
366
|
raise pystac.ExtensionNotImplemented(
|
291
|
-
f"The catalog does not have the required properties or the ML-Dataset extension to calculate the metrics"
|
367
|
+
f"The catalog does not have the required properties or the ML-Dataset extension to calculate the metrics: {exc}"
|
292
368
|
)
|
293
369
|
finally:
|
294
370
|
catalog.make_all_asset_hrefs_relative()
|
295
|
-
|
371
|
+
|
296
372
|
try:
|
297
373
|
print("Validating and saving...")
|
298
374
|
catalog.validate()
|
@@ -303,26 +379,32 @@ class MLDatasetQualityMetrics:
|
|
303
379
|
catalog.set_root(catalog)
|
304
380
|
catalog.normalize_and_save(root_href=destination)
|
305
381
|
print("Success!")
|
306
|
-
except STACValidationError
|
382
|
+
except STACValidationError:
|
307
383
|
# Return full callback
|
308
384
|
traceback.print_exc()
|
309
385
|
|
310
386
|
@staticmethod
|
311
387
|
def _search_spatial_duplicates(catalog: pystac.Catalog):
|
312
|
-
"""
|
388
|
+
"""
|
389
|
+
Search for spatial duplicates in the catalog
|
390
|
+
"""
|
313
391
|
items = list(
|
314
392
|
set(
|
315
|
-
[
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
393
|
+
[
|
394
|
+
item
|
395
|
+
for item in tqdm(
|
396
|
+
catalog.get_items(recursive=True),
|
397
|
+
desc="Looking for spatial duplicates...",
|
398
|
+
)
|
399
|
+
if not LabelExtension.has_extension(item)
|
400
|
+
]
|
320
401
|
)
|
402
|
+
)
|
321
403
|
|
322
404
|
# Initialize the spatial duplicates dict
|
323
405
|
spatial_duplicates = {"name": "spatial-duplicates", "values": [], "total": 0}
|
324
406
|
|
325
|
-
items_bboxes =
|
407
|
+
items_bboxes = {}
|
326
408
|
for item in items:
|
327
409
|
# Get the item bounding box
|
328
410
|
bbox = str(item.bbox)
|
@@ -340,12 +422,15 @@ class MLDatasetQualityMetrics:
|
|
340
422
|
|
341
423
|
@staticmethod
|
342
424
|
def _get_classes_balance(catalog: pystac.Catalog) -> dict:
|
343
|
-
"""
|
425
|
+
"""
|
426
|
+
Get the classes balance of the catalog
|
427
|
+
"""
|
344
428
|
|
345
429
|
def get_label_properties(items: List[pystac.Item]) -> List:
|
346
430
|
"""
|
431
|
+
Get the label properties of the catalog
|
347
432
|
"""
|
348
|
-
label_properties =
|
433
|
+
label_properties = []
|
349
434
|
for label in items:
|
350
435
|
label_ext = LabelExtension.ext(label)
|
351
436
|
for prop in label_ext.label_properties:
|
@@ -353,30 +438,36 @@ class MLDatasetQualityMetrics:
|
|
353
438
|
label_properties.append(prop)
|
354
439
|
|
355
440
|
return label_properties
|
356
|
-
|
441
|
+
|
357
442
|
catalog.make_all_asset_hrefs_absolute()
|
358
443
|
|
359
444
|
labels = list(
|
360
445
|
set(
|
361
|
-
[
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
446
|
+
[
|
447
|
+
item
|
448
|
+
for item in tqdm(
|
449
|
+
catalog.get_items(recursive=True),
|
450
|
+
desc="Calculating classes balance...",
|
451
|
+
)
|
452
|
+
if LabelExtension.has_extension(item)
|
453
|
+
]
|
366
454
|
)
|
455
|
+
)
|
367
456
|
|
368
457
|
# Initialize the classes balance dict
|
369
458
|
classes_balance = {"name": "classes-balance", "values": []}
|
370
459
|
label_properties = get_label_properties(labels)
|
371
460
|
|
372
|
-
for
|
373
|
-
property_balance = {"name":
|
374
|
-
properties =
|
461
|
+
for prop in label_properties:
|
462
|
+
property_balance = {"name": prop, "values": []}
|
463
|
+
properties = {}
|
375
464
|
for label in labels:
|
465
|
+
if 'labels' not in label.assets:
|
466
|
+
continue
|
376
467
|
asset_path = label.assets["labels"].href
|
377
468
|
# Open the linked geoJSON to obtain the label properties
|
378
469
|
try:
|
379
|
-
with open(asset_path) as f:
|
470
|
+
with open(asset_path, mode="r", encoding="utf-8") as f:
|
380
471
|
label_data = json.load(f)
|
381
472
|
except FileNotFoundError:
|
382
473
|
raise FileNotFoundError(
|
@@ -384,8 +475,8 @@ class MLDatasetQualityMetrics:
|
|
384
475
|
)
|
385
476
|
# Get the property
|
386
477
|
for feature in label_data["features"]:
|
387
|
-
if
|
388
|
-
property_value = feature["properties"][
|
478
|
+
if prop in feature["properties"]:
|
479
|
+
property_value = feature["properties"][prop]
|
389
480
|
else:
|
390
481
|
if feature["properties"]["labels"]:
|
391
482
|
property_value = feature["properties"]["labels"][0]
|
@@ -394,7 +485,7 @@ class MLDatasetQualityMetrics:
|
|
394
485
|
if property_value not in properties:
|
395
486
|
properties[property_value] = 0
|
396
487
|
properties[property_value] += 1
|
397
|
-
|
488
|
+
|
398
489
|
# Create the property balance dict
|
399
490
|
total_labels = sum(properties.values())
|
400
491
|
for key, value in properties.items():
|
@@ -414,6 +505,9 @@ class MLDatasetQualityMetrics:
|
|
414
505
|
|
415
506
|
|
416
507
|
class MLDatasetExtensionHooks(ExtensionHooks):
|
508
|
+
"""
|
509
|
+
ML Dataset Extension Hooks
|
510
|
+
"""
|
417
511
|
schema_uri: str = SCHEMA_URI
|
418
512
|
prev_extension_ids: Set[str] = set()
|
419
513
|
stac_object_types = {
|
@@ -480,15 +574,19 @@ def add_ml_extension(
|
|
480
574
|
|
481
575
|
# Normalize the ref on the same folder
|
482
576
|
if destination:
|
483
|
-
catalog_ml_dataset = make_links_relative_to_path(
|
577
|
+
catalog_ml_dataset = make_links_relative_to_path(
|
578
|
+
destination, catalog_ml_dataset
|
579
|
+
)
|
484
580
|
|
485
581
|
try:
|
486
582
|
print("Validating and saving...")
|
487
583
|
catalog_ml_dataset.validate()
|
488
|
-
rmtree(destination) if exists(
|
489
|
-
|
490
|
-
|
491
|
-
|
584
|
+
rmtree(destination) if exists(
|
585
|
+
destination
|
586
|
+
) else None # Remove the old catalog and replace it with the new one
|
587
|
+
catalog_ml_dataset.normalize_and_save(
|
588
|
+
root_href=destination, catalog_type=catalog_type
|
589
|
+
)
|
492
590
|
print("Success!")
|
493
591
|
except STACValidationError:
|
494
592
|
# Return full callback
|
@@ -500,7 +598,7 @@ def make_splits(
|
|
500
598
|
splits_names: Optional[List[str]] = ("Training", "Validation", "Test"),
|
501
599
|
splits_proportions: Optional[List[int]] = (80, 10, 10),
|
502
600
|
verbose: Optional[bool] = True,
|
503
|
-
**kwargs,
|
601
|
+
**kwargs: Optional[dict],
|
504
602
|
) -> None:
|
505
603
|
"""
|
506
604
|
Makes the splits of the labels collection.
|
@@ -514,7 +612,7 @@ def make_splits(
|
|
514
612
|
raise ValueError("The sum of the splits must be 100")
|
515
613
|
|
516
614
|
# Get all items in the labels collection
|
517
|
-
items =
|
615
|
+
items = list(labels_collection.get_items(recursive=True))
|
518
616
|
|
519
617
|
# Calculate indices to split the items
|
520
618
|
length = len(items)
|
@@ -536,9 +634,9 @@ def make_splits(
|
|
536
634
|
|
537
635
|
# Split the items
|
538
636
|
train_items = items[:idx_train]
|
539
|
-
test_items = items[idx_train
|
637
|
+
test_items = items[idx_train: idx_train + idx_test]
|
540
638
|
if val_size:
|
541
|
-
val_items = items[idx_train + idx_test
|
639
|
+
val_items = items[idx_train + idx_test: idx_train + idx_test + idx_val]
|
542
640
|
|
543
641
|
# Create the splits in the collection
|
544
642
|
labels_collection = MLDatasetExtension.ext(labels_collection, add_if_missing=True)
|
@@ -1,24 +1,26 @@
|
|
1
|
-
|
1
|
+
"""
|
2
2
|
Module for projection STAC extensions object
|
3
|
-
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import Union
|
4
6
|
|
5
7
|
import pystac
|
6
8
|
import pandas as pd
|
7
9
|
import rasterio
|
8
10
|
|
9
|
-
from typing import Union
|
10
|
-
from .base import STACExtensionObject
|
11
11
|
from pystac.extensions.projection import ProjectionExtension
|
12
|
-
|
12
|
+
from .base import STACExtensionObject
|
13
13
|
|
14
14
|
|
15
15
|
class ProjExtensionObject(STACExtensionObject):
|
16
|
+
"""
|
17
|
+
Projection extension object
|
18
|
+
"""
|
16
19
|
def __init__(self) -> None:
|
17
20
|
super().__init__()
|
18
21
|
|
19
22
|
def add_extension_to_object(
|
20
|
-
self, obj: Union[pystac.Item, pystac.Asset],
|
21
|
-
obj_info: pd.DataFrame
|
23
|
+
self, obj: Union[pystac.Item, pystac.Asset], obj_info: pd.DataFrame
|
22
24
|
) -> Union[pystac.Item, pystac.Asset]:
|
23
25
|
"""
|
24
26
|
Add the extension to the given object
|
@@ -31,12 +33,12 @@ class ProjExtensionObject(STACExtensionObject):
|
|
31
33
|
return obj
|
32
34
|
elif isinstance(obj, pystac.Item):
|
33
35
|
proj_ext = ProjectionExtension.ext(obj, add_if_missing=True)
|
34
|
-
ds = rasterio.open(obj_info[
|
36
|
+
ds = rasterio.open(obj_info["image"].values[0])
|
35
37
|
# Assume all the bands have the same projection
|
36
38
|
proj_ext.apply(
|
37
39
|
epsg=ds.crs.to_epsg(),
|
38
40
|
transform=ds.transform,
|
39
41
|
shape=ds.shape,
|
40
|
-
|
42
|
+
)
|
41
43
|
|
42
44
|
return obj
|
@@ -1,23 +1,28 @@
|
|
1
|
-
|
1
|
+
"""
|
2
2
|
Module for raster STAC extensions object
|
3
|
-
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import Union, Optional
|
4
6
|
|
5
7
|
import pystac
|
6
8
|
import rasterio
|
7
9
|
import pandas as pd
|
8
10
|
|
9
11
|
from pystac.extensions.raster import RasterExtension, RasterBand
|
10
|
-
from typing import Union, Optional
|
11
12
|
from .base import STACExtensionObject
|
12
13
|
|
13
14
|
|
14
15
|
class RasterExtensionObject(STACExtensionObject):
|
16
|
+
"""
|
17
|
+
Raster extension object
|
18
|
+
"""
|
15
19
|
def __init__(self) -> None:
|
16
20
|
super().__init__()
|
17
21
|
|
18
22
|
def add_extension_to_object(
|
19
|
-
self,
|
20
|
-
|
23
|
+
self,
|
24
|
+
obj: Union[pystac.Item, pystac.Asset],
|
25
|
+
obj_info: Optional[pd.DataFrame] = None,
|
21
26
|
) -> Union[pystac.Item, pystac.Asset]:
|
22
27
|
"""
|
23
28
|
Add the extension to the given object
|
@@ -30,16 +35,19 @@ class RasterExtensionObject(STACExtensionObject):
|
|
30
35
|
else:
|
31
36
|
raster_ext = RasterExtension.ext(obj, add_if_missing=True)
|
32
37
|
src = rasterio.open(obj.href)
|
33
|
-
bands =
|
38
|
+
bands = []
|
34
39
|
for band in src.indexes:
|
35
|
-
bands.append(
|
36
|
-
|
37
|
-
|
38
|
-
spatial_resolution=src.res) if src.nodatavals else RasterBand.create(
|
40
|
+
bands.append(
|
41
|
+
RasterBand.create(
|
42
|
+
nodata=src.nodatavals[band - 1],
|
39
43
|
data_type=src.dtypes[band - 1],
|
40
|
-
spatial_resolution=src.res
|
44
|
+
spatial_resolution=src.res,
|
45
|
+
)
|
46
|
+
if src.nodatavals
|
47
|
+
else RasterBand.create(
|
48
|
+
data_type=src.dtypes[band - 1], spatial_resolution=src.res
|
49
|
+
)
|
50
|
+
)
|
41
51
|
raster_ext.apply(bands=bands)
|
42
|
-
|
43
|
-
return obj
|
44
|
-
|
45
52
|
|
53
|
+
return obj
|
@@ -1,26 +1,31 @@
|
|
1
|
-
|
1
|
+
"""
|
2
2
|
Module for SAR STAC extensions object
|
3
|
-
|
3
|
+
"""
|
4
|
+
|
5
|
+
from typing import Optional, Union
|
4
6
|
|
5
7
|
import pystac
|
6
8
|
import pandas as pd
|
7
9
|
|
8
|
-
from .base import STACExtensionObject
|
9
|
-
|
10
|
-
from typing import Optional, Union
|
11
10
|
from pystac.extensions.sar import SarExtension
|
12
11
|
from pystac.extensions.sar import FrequencyBand, Polarization
|
13
12
|
|
13
|
+
from .base import STACExtensionObject
|
14
|
+
|
14
15
|
|
15
16
|
class SarExtensionObject(STACExtensionObject):
|
17
|
+
"""
|
18
|
+
SAR extension object
|
19
|
+
"""
|
16
20
|
def __init__(self) -> None:
|
17
21
|
super().__init__()
|
18
22
|
self.polarizations = [Polarization.VV, Polarization.VH]
|
19
23
|
self.polarizations_dict = {"VV": Polarization.VV, "VH": Polarization.VH}
|
20
24
|
|
21
25
|
def add_extension_to_object(
|
22
|
-
self,
|
23
|
-
|
26
|
+
self,
|
27
|
+
obj: Union[pystac.Item, pystac.Asset],
|
28
|
+
obj_info: Optional[pd.DataFrame] = None,
|
24
29
|
) -> Union[pystac.Item, pystac.Asset]:
|
25
30
|
"""
|
26
31
|
Add the extension to the given object
|