mapchete-eo 2025.7.0__py2.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.
- mapchete_eo/__init__.py +1 -0
- mapchete_eo/archives/__init__.py +0 -0
- mapchete_eo/archives/base.py +65 -0
- mapchete_eo/array/__init__.py +0 -0
- mapchete_eo/array/buffer.py +16 -0
- mapchete_eo/array/color.py +29 -0
- mapchete_eo/array/convert.py +157 -0
- mapchete_eo/base.py +528 -0
- mapchete_eo/blacklist.txt +175 -0
- mapchete_eo/cli/__init__.py +30 -0
- mapchete_eo/cli/bounds.py +22 -0
- mapchete_eo/cli/options_arguments.py +243 -0
- mapchete_eo/cli/s2_brdf.py +77 -0
- mapchete_eo/cli/s2_cat_results.py +146 -0
- mapchete_eo/cli/s2_find_broken_products.py +93 -0
- mapchete_eo/cli/s2_jp2_static_catalog.py +166 -0
- mapchete_eo/cli/s2_mask.py +71 -0
- mapchete_eo/cli/s2_mgrs.py +45 -0
- mapchete_eo/cli/s2_rgb.py +114 -0
- mapchete_eo/cli/s2_verify.py +129 -0
- mapchete_eo/cli/static_catalog.py +123 -0
- mapchete_eo/eostac.py +30 -0
- mapchete_eo/exceptions.py +87 -0
- mapchete_eo/geometry.py +271 -0
- mapchete_eo/image_operations/__init__.py +12 -0
- mapchete_eo/image_operations/color_correction.py +136 -0
- mapchete_eo/image_operations/compositing.py +247 -0
- mapchete_eo/image_operations/dtype_scale.py +43 -0
- mapchete_eo/image_operations/fillnodata.py +130 -0
- mapchete_eo/image_operations/filters.py +319 -0
- mapchete_eo/image_operations/linear_normalization.py +81 -0
- mapchete_eo/image_operations/sigmoidal.py +114 -0
- mapchete_eo/io/__init__.py +37 -0
- mapchete_eo/io/assets.py +492 -0
- mapchete_eo/io/items.py +147 -0
- mapchete_eo/io/levelled_cubes.py +228 -0
- mapchete_eo/io/path.py +144 -0
- mapchete_eo/io/products.py +413 -0
- mapchete_eo/io/profiles.py +45 -0
- mapchete_eo/known_catalogs.py +42 -0
- mapchete_eo/platforms/sentinel2/__init__.py +17 -0
- mapchete_eo/platforms/sentinel2/archives.py +190 -0
- mapchete_eo/platforms/sentinel2/bandpass_adjustment.py +104 -0
- mapchete_eo/platforms/sentinel2/brdf/__init__.py +8 -0
- mapchete_eo/platforms/sentinel2/brdf/config.py +32 -0
- mapchete_eo/platforms/sentinel2/brdf/correction.py +260 -0
- mapchete_eo/platforms/sentinel2/brdf/hls.py +251 -0
- mapchete_eo/platforms/sentinel2/brdf/models.py +44 -0
- mapchete_eo/platforms/sentinel2/brdf/protocols.py +27 -0
- mapchete_eo/platforms/sentinel2/brdf/ross_thick.py +136 -0
- mapchete_eo/platforms/sentinel2/brdf/sun_angle_arrays.py +76 -0
- mapchete_eo/platforms/sentinel2/config.py +181 -0
- mapchete_eo/platforms/sentinel2/driver.py +78 -0
- mapchete_eo/platforms/sentinel2/masks.py +325 -0
- mapchete_eo/platforms/sentinel2/metadata_parser.py +734 -0
- mapchete_eo/platforms/sentinel2/path_mappers/__init__.py +29 -0
- mapchete_eo/platforms/sentinel2/path_mappers/base.py +56 -0
- mapchete_eo/platforms/sentinel2/path_mappers/earthsearch.py +34 -0
- mapchete_eo/platforms/sentinel2/path_mappers/metadata_xml.py +135 -0
- mapchete_eo/platforms/sentinel2/path_mappers/sinergise.py +105 -0
- mapchete_eo/platforms/sentinel2/preprocessing_tasks.py +26 -0
- mapchete_eo/platforms/sentinel2/processing_baseline.py +160 -0
- mapchete_eo/platforms/sentinel2/product.py +669 -0
- mapchete_eo/platforms/sentinel2/types.py +109 -0
- mapchete_eo/processes/__init__.py +0 -0
- mapchete_eo/processes/config.py +51 -0
- mapchete_eo/processes/dtype_scale.py +112 -0
- mapchete_eo/processes/eo_to_xarray.py +19 -0
- mapchete_eo/processes/merge_rasters.py +235 -0
- mapchete_eo/product.py +278 -0
- mapchete_eo/protocols.py +56 -0
- mapchete_eo/search/__init__.py +14 -0
- mapchete_eo/search/base.py +222 -0
- mapchete_eo/search/config.py +42 -0
- mapchete_eo/search/s2_mgrs.py +314 -0
- mapchete_eo/search/stac_search.py +251 -0
- mapchete_eo/search/stac_static.py +236 -0
- mapchete_eo/search/utm_search.py +251 -0
- mapchete_eo/settings.py +24 -0
- mapchete_eo/sort.py +48 -0
- mapchete_eo/time.py +53 -0
- mapchete_eo/types.py +73 -0
- mapchete_eo-2025.7.0.dist-info/METADATA +38 -0
- mapchete_eo-2025.7.0.dist-info/RECORD +87 -0
- mapchete_eo-2025.7.0.dist-info/WHEEL +5 -0
- mapchete_eo-2025.7.0.dist-info/entry_points.txt +11 -0
- mapchete_eo-2025.7.0.dist-info/licenses/LICENSE +21 -0
mapchete_eo/base.py
ADDED
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
from functools import cached_property
|
|
5
|
+
from typing import Any, Callable, List, Optional, Type, Union
|
|
6
|
+
|
|
7
|
+
import croniter
|
|
8
|
+
import numpy.ma as ma
|
|
9
|
+
import xarray as xr
|
|
10
|
+
from dateutil.tz import tzutc
|
|
11
|
+
from mapchete.config.parse import guess_geometry
|
|
12
|
+
from mapchete.formats import base
|
|
13
|
+
from mapchete.geometry import reproject_geometry
|
|
14
|
+
from mapchete.io.vector import IndexedFeatures
|
|
15
|
+
from mapchete.path import MPath
|
|
16
|
+
from mapchete.tile import BufferedTile
|
|
17
|
+
from mapchete.types import MPathLike, NodataVal, NodataVals
|
|
18
|
+
from pydantic import BaseModel
|
|
19
|
+
from rasterio.enums import Resampling
|
|
20
|
+
from shapely.geometry.base import BaseGeometry
|
|
21
|
+
|
|
22
|
+
from mapchete_eo.archives.base import Archive
|
|
23
|
+
from mapchete_eo.exceptions import CorruptedProductMetadata, PreprocessingNotFinished
|
|
24
|
+
from mapchete_eo.io import (
|
|
25
|
+
products_to_np_array,
|
|
26
|
+
products_to_xarray,
|
|
27
|
+
read_levelled_cube_to_np_array,
|
|
28
|
+
read_levelled_cube_to_xarray,
|
|
29
|
+
)
|
|
30
|
+
from mapchete_eo.product import EOProduct
|
|
31
|
+
from mapchete_eo.protocols import EOProductProtocol
|
|
32
|
+
from mapchete_eo.search.stac_static import STACStaticCatalog
|
|
33
|
+
from mapchete_eo.settings import mapchete_eo_settings
|
|
34
|
+
from mapchete_eo.sort import SortMethodConfig, TargetDateSort
|
|
35
|
+
from mapchete_eo.time import to_datetime
|
|
36
|
+
from mapchete_eo.types import DateTimeLike, MergeMethod, TimeRange
|
|
37
|
+
|
|
38
|
+
logger = logging.getLogger(__name__)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class BaseDriverConfig(BaseModel):
|
|
42
|
+
format: str
|
|
43
|
+
time: Union[TimeRange, List[TimeRange]]
|
|
44
|
+
cat_baseurl: Optional[str] = None
|
|
45
|
+
cache: Optional[Any] = None
|
|
46
|
+
footprint_buffer: float = 0
|
|
47
|
+
area: Optional[Union[MPathLike, dict, type[BaseGeometry]]] = None
|
|
48
|
+
preprocessing_tasks: bool = False
|
|
49
|
+
archive: Optional[Type[Archive]] = None
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class EODataCube(base.InputTile):
|
|
53
|
+
"""Target Tile representation of input data."""
|
|
54
|
+
|
|
55
|
+
default_read_merge_method: MergeMethod = MergeMethod.first
|
|
56
|
+
default_read_merge_products_by: Optional[str] = None
|
|
57
|
+
default_read_nodataval: NodataVal = None
|
|
58
|
+
default_read_resampling: Resampling = Resampling.nearest
|
|
59
|
+
|
|
60
|
+
tile: BufferedTile
|
|
61
|
+
eo_bands: dict
|
|
62
|
+
time: List[TimeRange]
|
|
63
|
+
area: BaseGeometry
|
|
64
|
+
|
|
65
|
+
def __init__(
|
|
66
|
+
self,
|
|
67
|
+
tile: BufferedTile,
|
|
68
|
+
products: Optional[List[EOProductProtocol]],
|
|
69
|
+
eo_bands: dict,
|
|
70
|
+
time: List[TimeRange],
|
|
71
|
+
input_key: Optional[str] = None,
|
|
72
|
+
area: Optional[BaseGeometry] = None,
|
|
73
|
+
**kwargs,
|
|
74
|
+
) -> None:
|
|
75
|
+
"""Initialize."""
|
|
76
|
+
self.tile = tile
|
|
77
|
+
self._products = products
|
|
78
|
+
self.eo_bands = eo_bands
|
|
79
|
+
self.time = time
|
|
80
|
+
self.input_key = input_key
|
|
81
|
+
self.area = tile.bbox if area is None else area
|
|
82
|
+
|
|
83
|
+
@cached_property
|
|
84
|
+
def products(self) -> IndexedFeatures[EOProductProtocol]:
|
|
85
|
+
# during task graph processing, the products have to be fetched as preprocessing task results
|
|
86
|
+
if self._products is None: # pragma: no cover
|
|
87
|
+
return IndexedFeatures(
|
|
88
|
+
[
|
|
89
|
+
item
|
|
90
|
+
for item in self.preprocessing_tasks_results.values()
|
|
91
|
+
if not isinstance(item, CorruptedProductMetadata)
|
|
92
|
+
],
|
|
93
|
+
crs=self.tile.crs,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# just return the prouducts as is
|
|
97
|
+
return IndexedFeatures(
|
|
98
|
+
[
|
|
99
|
+
item
|
|
100
|
+
for item in self._products
|
|
101
|
+
if not isinstance(item, CorruptedProductMetadata)
|
|
102
|
+
],
|
|
103
|
+
crs=self.tile.crs,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
def read(
|
|
107
|
+
self,
|
|
108
|
+
assets: Optional[List[str]] = None,
|
|
109
|
+
eo_bands: Optional[List[str]] = None,
|
|
110
|
+
start_time: Optional[DateTimeLike] = None,
|
|
111
|
+
end_time: Optional[DateTimeLike] = None,
|
|
112
|
+
timestamps: Optional[List[DateTimeLike]] = None,
|
|
113
|
+
time_pattern: Optional[str] = None,
|
|
114
|
+
resampling: Optional[Union[Resampling, str]] = None,
|
|
115
|
+
merge_products_by: Optional[str] = None,
|
|
116
|
+
merge_method: Optional[MergeMethod] = None,
|
|
117
|
+
sort: Optional[SortMethodConfig] = None,
|
|
118
|
+
nodatavals: NodataVals = None,
|
|
119
|
+
raise_empty: bool = True,
|
|
120
|
+
**kwargs,
|
|
121
|
+
) -> xr.Dataset:
|
|
122
|
+
"""
|
|
123
|
+
Read reprojected & resampled input data.
|
|
124
|
+
|
|
125
|
+
Returns
|
|
126
|
+
-------
|
|
127
|
+
data : xarray.Dataset
|
|
128
|
+
"""
|
|
129
|
+
return products_to_xarray(
|
|
130
|
+
products=self.filter_products(
|
|
131
|
+
start_time=start_time,
|
|
132
|
+
end_time=end_time,
|
|
133
|
+
timestamps=timestamps,
|
|
134
|
+
time_pattern=time_pattern,
|
|
135
|
+
),
|
|
136
|
+
eo_bands=eo_bands,
|
|
137
|
+
assets=assets,
|
|
138
|
+
grid=self.tile,
|
|
139
|
+
raise_empty=raise_empty,
|
|
140
|
+
product_read_kwargs=kwargs,
|
|
141
|
+
sort=sort,
|
|
142
|
+
**self.default_read_values(
|
|
143
|
+
merge_products_by=merge_products_by,
|
|
144
|
+
merge_method=merge_method,
|
|
145
|
+
resampling=resampling,
|
|
146
|
+
nodatavals=nodatavals,
|
|
147
|
+
),
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def read_np_array(
|
|
151
|
+
self,
|
|
152
|
+
assets: Optional[List[str]] = None,
|
|
153
|
+
eo_bands: Optional[List[str]] = None,
|
|
154
|
+
start_time: Optional[DateTimeLike] = None,
|
|
155
|
+
end_time: Optional[DateTimeLike] = None,
|
|
156
|
+
timestamps: Optional[List[DateTimeLike]] = None,
|
|
157
|
+
time_pattern: Optional[str] = None,
|
|
158
|
+
resampling: Optional[Union[Resampling, str]] = None,
|
|
159
|
+
merge_products_by: Optional[str] = None,
|
|
160
|
+
merge_method: Optional[MergeMethod] = None,
|
|
161
|
+
sort: Optional[SortMethodConfig] = None,
|
|
162
|
+
nodatavals: NodataVals = None,
|
|
163
|
+
raise_empty: bool = True,
|
|
164
|
+
**kwargs,
|
|
165
|
+
) -> ma.MaskedArray:
|
|
166
|
+
return products_to_np_array(
|
|
167
|
+
products=self.filter_products(
|
|
168
|
+
start_time=start_time,
|
|
169
|
+
end_time=end_time,
|
|
170
|
+
timestamps=timestamps,
|
|
171
|
+
time_pattern=time_pattern,
|
|
172
|
+
),
|
|
173
|
+
eo_bands=eo_bands,
|
|
174
|
+
assets=assets,
|
|
175
|
+
grid=self.tile,
|
|
176
|
+
product_read_kwargs=kwargs,
|
|
177
|
+
raise_empty=raise_empty,
|
|
178
|
+
sort=sort,
|
|
179
|
+
**self.default_read_values(
|
|
180
|
+
merge_products_by=merge_products_by,
|
|
181
|
+
merge_method=merge_method,
|
|
182
|
+
resampling=resampling,
|
|
183
|
+
nodatavals=nodatavals,
|
|
184
|
+
),
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
def read_levelled(
|
|
188
|
+
self,
|
|
189
|
+
target_height: int,
|
|
190
|
+
assets: Optional[List[str]] = None,
|
|
191
|
+
eo_bands: Optional[List[str]] = None,
|
|
192
|
+
start_time: Optional[DateTimeLike] = None,
|
|
193
|
+
end_time: Optional[DateTimeLike] = None,
|
|
194
|
+
timestamps: Optional[List[DateTimeLike]] = None,
|
|
195
|
+
time_pattern: Optional[str] = None,
|
|
196
|
+
resampling: Optional[Union[Resampling, str]] = None,
|
|
197
|
+
nodatavals: NodataVals = None,
|
|
198
|
+
merge_products_by: Optional[str] = None,
|
|
199
|
+
merge_method: Optional[MergeMethod] = None,
|
|
200
|
+
sort: SortMethodConfig = TargetDateSort(),
|
|
201
|
+
raise_empty: bool = True,
|
|
202
|
+
slice_axis_name: str = "layers",
|
|
203
|
+
band_axis_name: str = "bands",
|
|
204
|
+
x_axis_name: str = "x",
|
|
205
|
+
y_axis_name: str = "y",
|
|
206
|
+
**kwargs,
|
|
207
|
+
) -> xr.Dataset:
|
|
208
|
+
return read_levelled_cube_to_xarray(
|
|
209
|
+
products=self.filter_products(
|
|
210
|
+
start_time=start_time,
|
|
211
|
+
end_time=end_time,
|
|
212
|
+
timestamps=timestamps,
|
|
213
|
+
time_pattern=time_pattern,
|
|
214
|
+
),
|
|
215
|
+
target_height=target_height,
|
|
216
|
+
assets=assets,
|
|
217
|
+
eo_bands=eo_bands,
|
|
218
|
+
grid=self.tile,
|
|
219
|
+
raise_empty=raise_empty,
|
|
220
|
+
product_read_kwargs=kwargs,
|
|
221
|
+
slice_axis_name=slice_axis_name,
|
|
222
|
+
band_axis_name=band_axis_name,
|
|
223
|
+
x_axis_name=x_axis_name,
|
|
224
|
+
y_axis_name=y_axis_name,
|
|
225
|
+
sort=sort,
|
|
226
|
+
**self.default_read_values(
|
|
227
|
+
merge_products_by=merge_products_by,
|
|
228
|
+
merge_method=merge_method,
|
|
229
|
+
resampling=resampling,
|
|
230
|
+
nodatavals=nodatavals,
|
|
231
|
+
),
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
def read_levelled_np_array(
|
|
235
|
+
self,
|
|
236
|
+
target_height: int,
|
|
237
|
+
assets: Optional[List[str]] = None,
|
|
238
|
+
eo_bands: Optional[List[str]] = None,
|
|
239
|
+
start_time: Optional[DateTimeLike] = None,
|
|
240
|
+
end_time: Optional[DateTimeLike] = None,
|
|
241
|
+
timestamps: Optional[List[DateTimeLike]] = None,
|
|
242
|
+
time_pattern: Optional[str] = None,
|
|
243
|
+
resampling: Optional[Union[Resampling, str]] = None,
|
|
244
|
+
nodatavals: NodataVals = None,
|
|
245
|
+
merge_products_by: Optional[str] = None,
|
|
246
|
+
merge_method: Optional[MergeMethod] = None,
|
|
247
|
+
sort: SortMethodConfig = TargetDateSort(),
|
|
248
|
+
raise_empty: bool = True,
|
|
249
|
+
**kwargs,
|
|
250
|
+
) -> ma.MaskedArray:
|
|
251
|
+
return read_levelled_cube_to_np_array(
|
|
252
|
+
products=self.filter_products(
|
|
253
|
+
start_time=start_time,
|
|
254
|
+
end_time=end_time,
|
|
255
|
+
timestamps=timestamps,
|
|
256
|
+
time_pattern=time_pattern,
|
|
257
|
+
),
|
|
258
|
+
target_height=target_height,
|
|
259
|
+
assets=assets,
|
|
260
|
+
eo_bands=eo_bands,
|
|
261
|
+
grid=self.tile,
|
|
262
|
+
raise_empty=raise_empty,
|
|
263
|
+
product_read_kwargs=kwargs,
|
|
264
|
+
sort=sort,
|
|
265
|
+
**self.default_read_values(
|
|
266
|
+
merge_products_by=merge_products_by,
|
|
267
|
+
merge_method=merge_method,
|
|
268
|
+
resampling=resampling,
|
|
269
|
+
nodatavals=nodatavals,
|
|
270
|
+
),
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
def read_masks(
|
|
274
|
+
self,
|
|
275
|
+
start_time: Optional[DateTimeLike] = None,
|
|
276
|
+
end_time: Optional[DateTimeLike] = None,
|
|
277
|
+
timestamps: Optional[List[DateTimeLike]] = None,
|
|
278
|
+
time_pattern: Optional[str] = None,
|
|
279
|
+
nodatavals: NodataVals = None,
|
|
280
|
+
**kwargs,
|
|
281
|
+
):
|
|
282
|
+
from mapchete_eo.platforms.sentinel2.masks import read_masks
|
|
283
|
+
|
|
284
|
+
return read_masks(
|
|
285
|
+
products=self.filter_products(
|
|
286
|
+
start_time=start_time,
|
|
287
|
+
end_time=end_time,
|
|
288
|
+
timestamps=timestamps,
|
|
289
|
+
time_pattern=time_pattern,
|
|
290
|
+
),
|
|
291
|
+
grid=self.tile,
|
|
292
|
+
nodatavals=nodatavals,
|
|
293
|
+
product_read_kwargs=kwargs,
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
def filter_products(
|
|
297
|
+
self,
|
|
298
|
+
start_time: Optional[DateTimeLike] = None,
|
|
299
|
+
end_time: Optional[DateTimeLike] = None,
|
|
300
|
+
timestamps: Optional[List[DateTimeLike]] = None,
|
|
301
|
+
time_pattern: Optional[str] = None,
|
|
302
|
+
):
|
|
303
|
+
"""
|
|
304
|
+
Return a filtered list of input products.
|
|
305
|
+
"""
|
|
306
|
+
if any([start_time, end_time, timestamps]):
|
|
307
|
+
raise NotImplementedError("time subsets are not yet implemented")
|
|
308
|
+
|
|
309
|
+
if time_pattern:
|
|
310
|
+
# filter products by time pattern
|
|
311
|
+
tz = tzutc()
|
|
312
|
+
coord_time = [
|
|
313
|
+
t.replace(tzinfo=tz)
|
|
314
|
+
for t in croniter.croniter_range(
|
|
315
|
+
to_datetime(self.start_time),
|
|
316
|
+
to_datetime(self.end_time),
|
|
317
|
+
time_pattern,
|
|
318
|
+
)
|
|
319
|
+
]
|
|
320
|
+
return [
|
|
321
|
+
product
|
|
322
|
+
for product in self.products
|
|
323
|
+
if product.item.datetime in coord_time
|
|
324
|
+
]
|
|
325
|
+
else:
|
|
326
|
+
return self.products
|
|
327
|
+
|
|
328
|
+
def is_empty(self) -> bool: # pragma: no cover
|
|
329
|
+
"""
|
|
330
|
+
Check if there is data within this tile.
|
|
331
|
+
|
|
332
|
+
Returns
|
|
333
|
+
-------
|
|
334
|
+
is empty : bool
|
|
335
|
+
"""
|
|
336
|
+
return len(self.products) == 0
|
|
337
|
+
|
|
338
|
+
def default_read_values(
|
|
339
|
+
self,
|
|
340
|
+
resampling: Optional[Union[Resampling, str]] = None,
|
|
341
|
+
nodatavals: NodataVals = None,
|
|
342
|
+
merge_products_by: Optional[str] = None,
|
|
343
|
+
merge_method: Optional[MergeMethod] = None,
|
|
344
|
+
) -> dict:
|
|
345
|
+
"""Provide proper read values depending on user input and defaults."""
|
|
346
|
+
if nodatavals is None:
|
|
347
|
+
nodatavals = self.default_read_nodataval
|
|
348
|
+
merge_products_by = merge_products_by or self.default_read_merge_products_by
|
|
349
|
+
merge_method = merge_method or self.default_read_merge_method
|
|
350
|
+
if resampling is None:
|
|
351
|
+
resampling = self.default_read_resampling
|
|
352
|
+
else:
|
|
353
|
+
resampling = (
|
|
354
|
+
resampling
|
|
355
|
+
if isinstance(resampling, Resampling)
|
|
356
|
+
else Resampling[resampling]
|
|
357
|
+
)
|
|
358
|
+
return dict(
|
|
359
|
+
resampling=resampling,
|
|
360
|
+
nodatavals=nodatavals,
|
|
361
|
+
merge_products_by=merge_products_by,
|
|
362
|
+
merge_method=merge_method,
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
class InputData(base.InputData):
|
|
367
|
+
default_preprocessing_task: Callable = staticmethod(EOProduct.from_stac_item)
|
|
368
|
+
driver_config_model: Type[BaseDriverConfig] = BaseDriverConfig
|
|
369
|
+
params: BaseDriverConfig
|
|
370
|
+
archive: Archive
|
|
371
|
+
time: Union[TimeRange, List[TimeRange]]
|
|
372
|
+
area: BaseGeometry
|
|
373
|
+
_products: Optional[IndexedFeatures] = None
|
|
374
|
+
|
|
375
|
+
def __init__(
|
|
376
|
+
self,
|
|
377
|
+
input_params: dict,
|
|
378
|
+
readonly: bool = False,
|
|
379
|
+
input_key: Optional[str] = None,
|
|
380
|
+
standalone: bool = False,
|
|
381
|
+
**kwargs,
|
|
382
|
+
) -> None:
|
|
383
|
+
"""Initialize."""
|
|
384
|
+
super().__init__(input_params, **kwargs)
|
|
385
|
+
self.readonly = readonly
|
|
386
|
+
self.input_key = input_key
|
|
387
|
+
self.standalone = standalone
|
|
388
|
+
|
|
389
|
+
self.params = self.driver_config_model(**input_params["abstract"])
|
|
390
|
+
# we have to make sure, the cache path is absolute
|
|
391
|
+
# not quite fond of this solution
|
|
392
|
+
if self.params.cache:
|
|
393
|
+
self.params.cache.path = MPath.from_inp(
|
|
394
|
+
self.params.cache.dict()
|
|
395
|
+
).absolute_path(base_dir=input_params.get("conf_dir"))
|
|
396
|
+
self.area = self._init_area(input_params)
|
|
397
|
+
self.time = self.params.time
|
|
398
|
+
if self.readonly: # pragma: no cover
|
|
399
|
+
return
|
|
400
|
+
|
|
401
|
+
self.set_archive(base_dir=input_params["conf_dir"])
|
|
402
|
+
|
|
403
|
+
# don't use preprocessing tasks for Sentinel-2 products:
|
|
404
|
+
if self.params.preprocessing_tasks or self.params.cache is not None:
|
|
405
|
+
for item in self.archive.items():
|
|
406
|
+
self.add_preprocessing_task(
|
|
407
|
+
self.default_preprocessing_task,
|
|
408
|
+
fargs=(item,),
|
|
409
|
+
fkwargs=dict(cache_config=self.params.cache, cache_all=True),
|
|
410
|
+
key=item.id,
|
|
411
|
+
geometry=reproject_geometry(
|
|
412
|
+
item.geometry,
|
|
413
|
+
src_crs=mapchete_eo_settings.default_catalog_crs,
|
|
414
|
+
dst_crs=self.crs,
|
|
415
|
+
),
|
|
416
|
+
)
|
|
417
|
+
else:
|
|
418
|
+
logger.debug("do preprocessing tasks now rather than later")
|
|
419
|
+
self._products = IndexedFeatures(
|
|
420
|
+
[
|
|
421
|
+
self.default_preprocessing_task(
|
|
422
|
+
item, cache_config=self.params.cache, cache_all=True
|
|
423
|
+
)
|
|
424
|
+
for item in self.archive.items()
|
|
425
|
+
]
|
|
426
|
+
)
|
|
427
|
+
|
|
428
|
+
def _init_area(self, input_params: dict) -> BaseGeometry:
|
|
429
|
+
"""Returns valid driver area for this process."""
|
|
430
|
+
process_area = input_params["delimiters"]["effective_area"]
|
|
431
|
+
if self.params.area:
|
|
432
|
+
# read area parameter and intersect with effective area
|
|
433
|
+
configured_area, configured_area_crs = guess_geometry(self.params.area)
|
|
434
|
+
return process_area.intersection(
|
|
435
|
+
reproject_geometry(
|
|
436
|
+
configured_area,
|
|
437
|
+
src_crs=configured_area_crs or self.crs,
|
|
438
|
+
dst_crs=self.crs,
|
|
439
|
+
)
|
|
440
|
+
)
|
|
441
|
+
return process_area
|
|
442
|
+
|
|
443
|
+
def set_archive(self, base_dir: MPath):
|
|
444
|
+
# this only works with some static archive:
|
|
445
|
+
if self.params.cat_baseurl:
|
|
446
|
+
self.archive = Archive(
|
|
447
|
+
catalog=STACStaticCatalog(
|
|
448
|
+
baseurl=MPath(self.params.cat_baseurl).absolute_path(
|
|
449
|
+
base_dir=base_dir
|
|
450
|
+
),
|
|
451
|
+
),
|
|
452
|
+
area=self.bbox(mapchete_eo_settings.default_catalog_crs),
|
|
453
|
+
time=self.time,
|
|
454
|
+
)
|
|
455
|
+
else:
|
|
456
|
+
raise NotImplementedError()
|
|
457
|
+
|
|
458
|
+
def bbox(self, out_crs: Optional[str] = None) -> BaseGeometry:
|
|
459
|
+
"""Return data bounding box."""
|
|
460
|
+
return reproject_geometry(
|
|
461
|
+
self.area,
|
|
462
|
+
src_crs=self.pyramid.crs,
|
|
463
|
+
dst_crs=self.pyramid.crs if out_crs is None else out_crs,
|
|
464
|
+
segmentize_on_clip=True,
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
@cached_property
|
|
468
|
+
def products(self) -> IndexedFeatures:
|
|
469
|
+
"""Hold preprocessed S2Products in an IndexedFeatures container."""
|
|
470
|
+
|
|
471
|
+
# nothing to index here
|
|
472
|
+
if self.readonly:
|
|
473
|
+
return IndexedFeatures([])
|
|
474
|
+
|
|
475
|
+
elif self._products is not None:
|
|
476
|
+
return self._products
|
|
477
|
+
|
|
478
|
+
# TODO: copied it from mapchete_satellite, not yet sure which use case this is
|
|
479
|
+
elif self.standalone:
|
|
480
|
+
raise NotImplementedError()
|
|
481
|
+
|
|
482
|
+
# if preprocessing tasks are ready, index them for further use
|
|
483
|
+
elif self.preprocessing_tasks_results:
|
|
484
|
+
return IndexedFeatures(
|
|
485
|
+
[
|
|
486
|
+
self.get_preprocessing_task_result(item.id)
|
|
487
|
+
for item in self.archive.items()
|
|
488
|
+
if not isinstance(item, CorruptedProductMetadata)
|
|
489
|
+
],
|
|
490
|
+
crs=self.crs,
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
elif not self.preprocessing_tasks:
|
|
494
|
+
return IndexedFeatures([])
|
|
495
|
+
|
|
496
|
+
# this happens on task graph execution when preprocessing task results are not ready
|
|
497
|
+
else: # pragma: no cover
|
|
498
|
+
raise PreprocessingNotFinished(
|
|
499
|
+
f"products are not ready yet because {len(self.preprocessing_tasks)} preprocessing task(s) were not executed."
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
def open(self, tile, **kwargs) -> EODataCube:
|
|
503
|
+
"""
|
|
504
|
+
Return InputTile object.
|
|
505
|
+
"""
|
|
506
|
+
try:
|
|
507
|
+
tile_products = self.products.filter(
|
|
508
|
+
reproject_geometry(
|
|
509
|
+
tile.bbox,
|
|
510
|
+
src_crs=tile.crs,
|
|
511
|
+
dst_crs=mapchete_eo_settings.default_catalog_crs,
|
|
512
|
+
).bounds
|
|
513
|
+
)
|
|
514
|
+
except PreprocessingNotFinished: # pragma: no cover
|
|
515
|
+
tile_products = None
|
|
516
|
+
return self.input_tile_cls(
|
|
517
|
+
tile,
|
|
518
|
+
products=tile_products,
|
|
519
|
+
eo_bands=self.archive.catalog.eo_bands,
|
|
520
|
+
time=self.time,
|
|
521
|
+
# passing on the input key is essential so dependent preprocessing tasks can be found!
|
|
522
|
+
input_key=self.input_key,
|
|
523
|
+
area=self.area.intersection(tile.bbox),
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
def cleanup(self):
|
|
527
|
+
for product in self.products:
|
|
528
|
+
product.clear_cached_data()
|