mapchete-eo 2025.8.3__py2.py3-none-any.whl → 2025.10.1__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 -1
- mapchete_eo/array/convert.py +7 -1
- mapchete_eo/base.py +37 -2
- mapchete_eo/io/items.py +5 -3
- mapchete_eo/io/levelled_cubes.py +66 -35
- mapchete_eo/io/products.py +15 -3
- mapchete_eo/platforms/sentinel2/metadata_parser.py +0 -2
- mapchete_eo/platforms/sentinel2/product.py +5 -5
- mapchete_eo/processes/merge_rasters.py +7 -3
- mapchete_eo/product.py +0 -1
- mapchete_eo/search/base.py +21 -8
- mapchete_eo/search/stac_search.py +22 -5
- mapchete_eo/search/stac_static.py +17 -4
- mapchete_eo/search/utm_search.py +5 -1
- mapchete_eo/sort.py +17 -1
- {mapchete_eo-2025.8.3.dist-info → mapchete_eo-2025.10.1.dist-info}/METADATA +2 -2
- {mapchete_eo-2025.8.3.dist-info → mapchete_eo-2025.10.1.dist-info}/RECORD +20 -20
- {mapchete_eo-2025.8.3.dist-info → mapchete_eo-2025.10.1.dist-info}/WHEEL +0 -0
- {mapchete_eo-2025.8.3.dist-info → mapchete_eo-2025.10.1.dist-info}/entry_points.txt +0 -0
- {mapchete_eo-2025.8.3.dist-info → mapchete_eo-2025.10.1.dist-info}/licenses/LICENSE +0 -0
mapchete_eo/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "2025.
|
|
1
|
+
__version__ = "2025.10.1"
|
mapchete_eo/array/convert.py
CHANGED
|
@@ -2,6 +2,7 @@ from typing import List, Optional, Union
|
|
|
2
2
|
|
|
3
3
|
import numpy as np
|
|
4
4
|
import numpy.ma as ma
|
|
5
|
+
from numpy.typing import DTypeLike
|
|
5
6
|
import xarray as xr
|
|
6
7
|
from mapchete.types import NodataVal
|
|
7
8
|
|
|
@@ -19,7 +20,9 @@ _NUMPY_FLOAT_DTYPES = [
|
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
def to_masked_array(
|
|
22
|
-
xarr: Union[xr.Dataset, xr.DataArray],
|
|
23
|
+
xarr: Union[xr.Dataset, xr.DataArray],
|
|
24
|
+
copy: bool = False,
|
|
25
|
+
out_dtype: Optional[DTypeLike] = None,
|
|
23
26
|
) -> ma.MaskedArray:
|
|
24
27
|
"""Convert xr.DataArray to ma.MaskedArray."""
|
|
25
28
|
if isinstance(xarr, xr.Dataset):
|
|
@@ -31,6 +34,9 @@ def to_masked_array(
|
|
|
31
34
|
"Cannot create masked_array because DataArray fill value is None"
|
|
32
35
|
)
|
|
33
36
|
|
|
37
|
+
if out_dtype:
|
|
38
|
+
xarr = xarr.astype(out_dtype, copy=False)
|
|
39
|
+
|
|
34
40
|
if xarr.dtype in _NUMPY_FLOAT_DTYPES:
|
|
35
41
|
return ma.masked_values(xarr, fill_value, copy=copy, shrink=False)
|
|
36
42
|
else:
|
mapchete_eo/base.py
CHANGED
|
@@ -5,6 +5,8 @@ from functools import cached_property
|
|
|
5
5
|
from typing import Any, Callable, List, Optional, Type, Union
|
|
6
6
|
|
|
7
7
|
import croniter
|
|
8
|
+
from mapchete import Bounds
|
|
9
|
+
import numpy as np
|
|
8
10
|
import numpy.ma as ma
|
|
9
11
|
import xarray as xr
|
|
10
12
|
from dateutil.tz import tzutc
|
|
@@ -17,6 +19,8 @@ from mapchete.tile import BufferedTile
|
|
|
17
19
|
from mapchete.types import MPathLike, NodataVal, NodataVals
|
|
18
20
|
from pydantic import BaseModel
|
|
19
21
|
from rasterio.enums import Resampling
|
|
22
|
+
from rasterio.features import geometry_mask
|
|
23
|
+
from shapely.geometry import mapping
|
|
20
24
|
from shapely.geometry.base import BaseGeometry
|
|
21
25
|
|
|
22
26
|
from mapchete_eo.archives.base import Archive
|
|
@@ -61,6 +65,7 @@ class EODataCube(base.InputTile):
|
|
|
61
65
|
eo_bands: dict
|
|
62
66
|
time: List[TimeRange]
|
|
63
67
|
area: BaseGeometry
|
|
68
|
+
area_pixelbuffer: int = 0
|
|
64
69
|
|
|
65
70
|
def __init__(
|
|
66
71
|
self,
|
|
@@ -366,6 +371,29 @@ class EODataCube(base.InputTile):
|
|
|
366
371
|
nodatavals=nodatavals,
|
|
367
372
|
merge_products_by=merge_products_by,
|
|
368
373
|
merge_method=merge_method,
|
|
374
|
+
read_mask=self.get_read_mask(),
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
def get_read_mask(self) -> np.ndarray:
|
|
378
|
+
"""
|
|
379
|
+
Determine read mask according to input area.
|
|
380
|
+
|
|
381
|
+
This will generate a numpy array where pixel overlapping the input area
|
|
382
|
+
are set True and thus will get filled by the read function. Pixel outside
|
|
383
|
+
of the area are not considered for reading.
|
|
384
|
+
|
|
385
|
+
On staged reading, i.e. first checking the product masks to assess valid
|
|
386
|
+
pixels, this will avoid reading product bands in cases the product only covers
|
|
387
|
+
pixels outside of the intended reading area.
|
|
388
|
+
"""
|
|
389
|
+
area = self.area.buffer(self.area_pixelbuffer * self.tile.pixel_x_size)
|
|
390
|
+
if area.is_empty:
|
|
391
|
+
return np.zeros((self.tile.shape), dtype=bool)
|
|
392
|
+
return geometry_mask(
|
|
393
|
+
geometries=[mapping(area)],
|
|
394
|
+
out_shape=self.tile.shape,
|
|
395
|
+
transform=self.tile.transform,
|
|
396
|
+
invert=True,
|
|
369
397
|
)
|
|
370
398
|
|
|
371
399
|
|
|
@@ -436,8 +464,15 @@ class InputData(base.InputData):
|
|
|
436
464
|
process_area = input_params["delimiters"]["effective_area"]
|
|
437
465
|
if self.params.area:
|
|
438
466
|
# read area parameter and intersect with effective area
|
|
439
|
-
configured_area, configured_area_crs = guess_geometry(
|
|
440
|
-
|
|
467
|
+
configured_area, configured_area_crs = guess_geometry(
|
|
468
|
+
self.params.area,
|
|
469
|
+
bounds=Bounds.from_inp(
|
|
470
|
+
input_params.get("delimiters", {}).get("bounds"),
|
|
471
|
+
crs=getattr(input_params.get("pyramid"), "crs"),
|
|
472
|
+
),
|
|
473
|
+
raise_if_empty=False,
|
|
474
|
+
)
|
|
475
|
+
process_area = process_area.intersection(
|
|
441
476
|
reproject_geometry(
|
|
442
477
|
configured_area,
|
|
443
478
|
src_crs=configured_area_crs or self.crs,
|
mapchete_eo/io/items.py
CHANGED
|
@@ -56,7 +56,7 @@ def item_to_np_array(
|
|
|
56
56
|
return out
|
|
57
57
|
|
|
58
58
|
|
|
59
|
-
def expand_params(param, length):
|
|
59
|
+
def expand_params(param: Any, length: int) -> List[Any]:
|
|
60
60
|
"""
|
|
61
61
|
Expand parameters if they are not a list.
|
|
62
62
|
"""
|
|
@@ -104,8 +104,10 @@ def get_item_property(
|
|
|
104
104
|
| ``collection`` | The collection ID of an Item's collection. |
|
|
105
105
|
+--------------------+--------------------------------------------------------+
|
|
106
106
|
"""
|
|
107
|
-
if property
|
|
108
|
-
|
|
107
|
+
if property == "id":
|
|
108
|
+
return item.id
|
|
109
|
+
elif property in ["year", "month", "day", "date", "datetime"]:
|
|
110
|
+
if item.datetime is None: # pragma: no cover
|
|
109
111
|
raise ValueError(
|
|
110
112
|
f"STAC item has no datetime attached, thus cannot get property {property}"
|
|
111
113
|
)
|
mapchete_eo/io/levelled_cubes.py
CHANGED
|
@@ -40,27 +40,50 @@ def read_levelled_cube_to_np_array(
|
|
|
40
40
|
raise_empty: bool = True,
|
|
41
41
|
out_dtype: DTypeLike = np.uint16,
|
|
42
42
|
out_fill_value: NodataVal = 0,
|
|
43
|
+
read_mask: Optional[np.ndarray] = None,
|
|
43
44
|
) -> ma.MaskedArray:
|
|
44
45
|
"""
|
|
45
46
|
Read products as slices into a cube by filling up nodata gaps with next slice.
|
|
47
|
+
|
|
48
|
+
If a read_mask is provided, only the pixels marked True are considered to be read.
|
|
46
49
|
"""
|
|
47
|
-
if len(products) == 0:
|
|
50
|
+
if len(products) == 0: # pragma: no cover
|
|
48
51
|
raise NoSourceProducts("no products to read")
|
|
49
|
-
|
|
50
52
|
bands = assets or eo_bands
|
|
51
|
-
if bands is None:
|
|
53
|
+
if bands is None: # pragma: no cover
|
|
52
54
|
raise ValueError("either assets or eo_bands have to be set")
|
|
53
|
-
|
|
54
55
|
out_shape = (target_height, len(bands), *grid.shape)
|
|
56
|
+
|
|
57
|
+
# 2D read_mask shape
|
|
58
|
+
if read_mask is None:
|
|
59
|
+
read_mask = np.ones(grid.shape, dtype=bool)
|
|
60
|
+
elif read_mask.ndim != 2: # pragma: no cover
|
|
61
|
+
raise ValueError(
|
|
62
|
+
"read_mask must be 2-dimensional, not %s-dimensional",
|
|
63
|
+
read_mask.ndim,
|
|
64
|
+
)
|
|
55
65
|
out: ma.MaskedArray = ma.masked_array(
|
|
56
|
-
data=np.
|
|
57
|
-
mask=np.ones(out_shape, dtype=
|
|
66
|
+
data=np.full(out_shape, out_fill_value, dtype=out_dtype),
|
|
67
|
+
mask=np.ones(out_shape, dtype=bool),
|
|
58
68
|
fill_value=out_fill_value,
|
|
59
69
|
)
|
|
70
|
+
|
|
71
|
+
if not read_mask.any():
|
|
72
|
+
logger.debug("nothing to read")
|
|
73
|
+
return out
|
|
74
|
+
|
|
75
|
+
# extrude mask to match each layer
|
|
76
|
+
layer_read_mask = np.stack([read_mask for _ in bands])
|
|
77
|
+
|
|
78
|
+
def _cube_read_mask() -> np.ndarray:
|
|
79
|
+
# This is only needed for debug output, thus there is no need to materialize always
|
|
80
|
+
return np.stack([layer_read_mask for _ in range(target_height)])
|
|
81
|
+
|
|
60
82
|
logger.debug(
|
|
61
|
-
"empty cube with shape %s has %s",
|
|
83
|
+
"empty cube with shape %s has %s and %s pixels to be filled",
|
|
62
84
|
out.shape,
|
|
63
85
|
pretty_bytes(out.size * out.itemsize),
|
|
86
|
+
_cube_read_mask().sum(),
|
|
64
87
|
)
|
|
65
88
|
|
|
66
89
|
logger.debug("sort products into slices ...")
|
|
@@ -76,25 +99,25 @@ def read_levelled_cube_to_np_array(
|
|
|
76
99
|
slices_read_count, slices_skip_count = 0, 0
|
|
77
100
|
|
|
78
101
|
# pick slices one by one
|
|
79
|
-
for slice_count,
|
|
102
|
+
for slice_count, slice_ in enumerate(slices, 1):
|
|
80
103
|
# all filled up? let's get outta here!
|
|
81
104
|
if not out.mask.any():
|
|
82
|
-
logger.debug("cube
|
|
105
|
+
logger.debug("cube has no pixels to be filled, quitting!")
|
|
83
106
|
break
|
|
84
107
|
|
|
85
108
|
# generate 2D mask of holes to be filled in output cube
|
|
86
|
-
cube_nodata_mask = out.mask.any(axis=0).any(axis=0)
|
|
109
|
+
cube_nodata_mask = np.logical_and(out.mask.any(axis=0).any(axis=0), read_mask)
|
|
87
110
|
|
|
88
111
|
# read slice
|
|
89
112
|
try:
|
|
90
113
|
logger.debug(
|
|
91
114
|
"see if slice %s %s has some of the %s unmasked pixels for cube",
|
|
92
115
|
slice_count,
|
|
93
|
-
|
|
116
|
+
slice_,
|
|
94
117
|
cube_nodata_mask.sum(),
|
|
95
118
|
)
|
|
96
|
-
with
|
|
97
|
-
slice_array =
|
|
119
|
+
with slice_.cached():
|
|
120
|
+
slice_array = slice_.read(
|
|
98
121
|
merge_method=merge_method,
|
|
99
122
|
product_read_kwargs=dict(
|
|
100
123
|
product_read_kwargs,
|
|
@@ -104,17 +127,18 @@ def read_levelled_cube_to_np_array(
|
|
|
104
127
|
resampling=resampling,
|
|
105
128
|
nodatavals=nodatavals,
|
|
106
129
|
raise_empty=raise_empty,
|
|
107
|
-
|
|
130
|
+
read_mask=cube_nodata_mask.copy(),
|
|
131
|
+
out_dtype=out_dtype,
|
|
108
132
|
),
|
|
109
133
|
)
|
|
110
134
|
slices_read_count += 1
|
|
111
135
|
except (EmptySliceException, CorruptedSlice) as exc:
|
|
112
|
-
logger.debug("skipped slice %s: %s",
|
|
136
|
+
logger.debug("skipped slice %s: %s", slice_, str(exc))
|
|
113
137
|
slices_skip_count += 1
|
|
114
138
|
continue
|
|
115
139
|
|
|
116
140
|
# if slice was not empty, fill pixels into cube
|
|
117
|
-
logger.debug("add slice %s array to cube",
|
|
141
|
+
logger.debug("add slice %s array to cube", slice_)
|
|
118
142
|
|
|
119
143
|
# iterate through layers of cube
|
|
120
144
|
for layer_index in range(target_height):
|
|
@@ -124,34 +148,35 @@ def read_levelled_cube_to_np_array(
|
|
|
124
148
|
continue
|
|
125
149
|
|
|
126
150
|
# determine empty patches of current layer
|
|
127
|
-
empty_patches = out[layer_index].mask
|
|
128
|
-
|
|
151
|
+
empty_patches = np.logical_and(out[layer_index].mask, layer_read_mask)
|
|
152
|
+
remaining_pixels_for_layer = (~slice_array[empty_patches].mask).sum()
|
|
129
153
|
|
|
130
154
|
# when slice has nothing to offer for this layer, skip
|
|
131
|
-
if
|
|
155
|
+
if remaining_pixels_for_layer == 0:
|
|
132
156
|
logger.debug(
|
|
133
157
|
"layer %s: slice has no pixels for this layer, jump to next",
|
|
134
158
|
layer_index,
|
|
135
159
|
)
|
|
136
160
|
continue
|
|
137
161
|
|
|
162
|
+
# insert slice data into empty patches of layer
|
|
138
163
|
logger.debug(
|
|
139
164
|
"layer %s: fill with %s pixels ...",
|
|
140
165
|
layer_index,
|
|
141
|
-
|
|
166
|
+
remaining_pixels_for_layer,
|
|
142
167
|
)
|
|
143
|
-
# insert slice data into empty patches of layer
|
|
144
168
|
out[layer_index][empty_patches] = slice_array[empty_patches]
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
percent_full = round(
|
|
148
|
-
100 * ((total_pixels - masked_pixels) / total_pixels), 2
|
|
149
|
-
)
|
|
169
|
+
|
|
170
|
+
# report on layer fill status
|
|
150
171
|
logger.debug(
|
|
151
|
-
"layer %s: %s
|
|
172
|
+
"layer %s: %s",
|
|
152
173
|
layer_index,
|
|
153
|
-
|
|
154
|
-
|
|
174
|
+
_percent_full(
|
|
175
|
+
remaining=np.logical_and(
|
|
176
|
+
out[layer_index].mask, layer_read_mask
|
|
177
|
+
).sum(),
|
|
178
|
+
total=layer_read_mask.sum(),
|
|
179
|
+
),
|
|
155
180
|
)
|
|
156
181
|
|
|
157
182
|
# remove slice values which were just inserted for next layer
|
|
@@ -161,13 +186,13 @@ def read_levelled_cube_to_np_array(
|
|
|
161
186
|
logger.debug("slice fully inserted into cube, skipping")
|
|
162
187
|
break
|
|
163
188
|
|
|
164
|
-
|
|
165
|
-
total_pixels = out.size
|
|
166
|
-
percent_full = round(100 * ((total_pixels - masked_pixels) / total_pixels), 2)
|
|
189
|
+
# report on layer fill status
|
|
167
190
|
logger.debug(
|
|
168
|
-
"cube is %s
|
|
169
|
-
|
|
170
|
-
|
|
191
|
+
"cube is %s",
|
|
192
|
+
_percent_full(
|
|
193
|
+
remaining=np.logical_and(out.mask, _cube_read_mask()).sum(),
|
|
194
|
+
total=_cube_read_mask().sum(),
|
|
195
|
+
),
|
|
171
196
|
)
|
|
172
197
|
|
|
173
198
|
logger.debug(
|
|
@@ -197,6 +222,7 @@ def read_levelled_cube_to_xarray(
|
|
|
197
222
|
band_axis_name: str = "bands",
|
|
198
223
|
x_axis_name: str = "x",
|
|
199
224
|
y_axis_name: str = "y",
|
|
225
|
+
read_mask: Optional[np.ndarray] = None,
|
|
200
226
|
) -> xr.Dataset:
|
|
201
227
|
"""
|
|
202
228
|
Read products as slices into a cube by filling up nodata gaps with next slice.
|
|
@@ -218,6 +244,7 @@ def read_levelled_cube_to_xarray(
|
|
|
218
244
|
sort=sort,
|
|
219
245
|
product_read_kwargs=product_read_kwargs,
|
|
220
246
|
raise_empty=raise_empty,
|
|
247
|
+
read_mask=read_mask,
|
|
221
248
|
),
|
|
222
249
|
slice_names=[f"layer-{ii}" for ii in range(target_height)],
|
|
223
250
|
band_names=variables,
|
|
@@ -226,3 +253,7 @@ def read_levelled_cube_to_xarray(
|
|
|
226
253
|
x_axis_name=x_axis_name,
|
|
227
254
|
y_axis_name=y_axis_name,
|
|
228
255
|
)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def _percent_full(remaining: int, total: int, ndigits: int = 2) -> str:
|
|
259
|
+
return f"{round(100 * (total - remaining) / total, ndigits=ndigits)}% full ({remaining} remaining emtpy pixels)"
|
mapchete_eo/io/products.py
CHANGED
|
@@ -10,6 +10,7 @@ from typing import Any, Dict, Generator, Iterator, List, Optional, Sequence
|
|
|
10
10
|
from mapchete import Timer
|
|
11
11
|
import numpy as np
|
|
12
12
|
import numpy.ma as ma
|
|
13
|
+
from numpy.typing import DTypeLike
|
|
13
14
|
import xarray as xr
|
|
14
15
|
from mapchete.config import get_hash
|
|
15
16
|
from mapchete.geometry import to_shape
|
|
@@ -49,11 +50,13 @@ def products_to_np_array(
|
|
|
49
50
|
sort: Optional[SortMethodConfig] = None,
|
|
50
51
|
product_read_kwargs: dict = {},
|
|
51
52
|
raise_empty: bool = True,
|
|
53
|
+
out_dtype: Optional[DTypeLike] = None,
|
|
54
|
+
read_mask: Optional[np.ndarray] = None,
|
|
52
55
|
) -> ma.MaskedArray:
|
|
53
56
|
"""Read grid window of EOProducts and merge into a 4D xarray."""
|
|
54
57
|
return ma.stack(
|
|
55
58
|
[
|
|
56
|
-
to_masked_array(s)
|
|
59
|
+
to_masked_array(s, out_dtype=out_dtype)
|
|
57
60
|
for s in generate_slice_dataarrays(
|
|
58
61
|
products=products,
|
|
59
62
|
assets=assets,
|
|
@@ -66,6 +69,7 @@ def products_to_np_array(
|
|
|
66
69
|
sort=sort,
|
|
67
70
|
product_read_kwargs=product_read_kwargs,
|
|
68
71
|
raise_empty=raise_empty,
|
|
72
|
+
read_mask=read_mask,
|
|
69
73
|
)
|
|
70
74
|
]
|
|
71
75
|
)
|
|
@@ -87,6 +91,7 @@ def products_to_xarray(
|
|
|
87
91
|
sort: Optional[SortMethodConfig] = None,
|
|
88
92
|
raise_empty: bool = True,
|
|
89
93
|
product_read_kwargs: dict = {},
|
|
94
|
+
read_mask: Optional[np.ndarray] = None,
|
|
90
95
|
) -> xr.Dataset:
|
|
91
96
|
"""Read grid window of EOProducts and merge into a 4D xarray."""
|
|
92
97
|
data_vars = [
|
|
@@ -103,6 +108,7 @@ def products_to_xarray(
|
|
|
103
108
|
sort=sort,
|
|
104
109
|
product_read_kwargs=product_read_kwargs,
|
|
105
110
|
raise_empty=raise_empty,
|
|
111
|
+
read_mask=read_mask,
|
|
106
112
|
)
|
|
107
113
|
]
|
|
108
114
|
if merge_products_by and merge_products_by not in ["date", "datetime"]:
|
|
@@ -322,8 +328,11 @@ def merge_products(
|
|
|
322
328
|
valid_arrays = [a for a in arrays if not ma.getmaskarray(a).all()]
|
|
323
329
|
|
|
324
330
|
if valid_arrays:
|
|
325
|
-
|
|
326
|
-
|
|
331
|
+
out_dtype = out.dtype
|
|
332
|
+
out_fill_value = out.fill_value
|
|
333
|
+
stacked = ma.stack(valid_arrays, dtype=out_dtype)
|
|
334
|
+
out = stacked.mean(axis=0, dtype=out_dtype).astype(out_dtype, copy=False)
|
|
335
|
+
out.set_fill_value(out_fill_value)
|
|
327
336
|
else:
|
|
328
337
|
# All arrays were fully masked — return fully masked output
|
|
329
338
|
out = ma.masked_all(out.shape, dtype=out.dtype)
|
|
@@ -351,10 +360,12 @@ def generate_slice_dataarrays(
|
|
|
351
360
|
sort: Optional[SortMethodConfig] = None,
|
|
352
361
|
product_read_kwargs: dict = {},
|
|
353
362
|
raise_empty: bool = True,
|
|
363
|
+
read_mask: Optional[np.ndarray] = None,
|
|
354
364
|
) -> Iterator[xr.DataArray]:
|
|
355
365
|
"""
|
|
356
366
|
Yield products or merged products into slices as DataArrays.
|
|
357
367
|
"""
|
|
368
|
+
|
|
358
369
|
if len(products) == 0:
|
|
359
370
|
raise NoSourceProducts("no products to read")
|
|
360
371
|
|
|
@@ -396,6 +407,7 @@ def generate_slice_dataarrays(
|
|
|
396
407
|
resampling=resampling,
|
|
397
408
|
nodatavals=nodatavals,
|
|
398
409
|
raise_empty=raise_empty,
|
|
410
|
+
read_mask=read_mask,
|
|
399
411
|
),
|
|
400
412
|
raise_empty=raise_empty,
|
|
401
413
|
),
|
|
@@ -161,10 +161,8 @@ class S2Metadata:
|
|
|
161
161
|
return f"<S2Metadata id={self.product_id}, processing_baseline={self.processing_baseline}>"
|
|
162
162
|
|
|
163
163
|
def clear_cached_data(self):
|
|
164
|
-
logger.debug("clear S2Metadata internal caches")
|
|
165
164
|
self._cache = dict(viewing_incidence_angles=dict(), detector_footprints=dict())
|
|
166
165
|
if self._cached_xml_root is not None:
|
|
167
|
-
logger.debug("clear S2Metadata xml cache")
|
|
168
166
|
self._cached_xml_root.clear()
|
|
169
167
|
self._cached_xml_root = None
|
|
170
168
|
self.path_mapper.clear_cached_data()
|
|
@@ -195,7 +195,6 @@ class S2Product(EOProduct, EOProductProtocol):
|
|
|
195
195
|
return f"<S2Product product_id={self.id}>"
|
|
196
196
|
|
|
197
197
|
def clear_cached_data(self):
|
|
198
|
-
logger.debug("clear S2Product caches")
|
|
199
198
|
if self._metadata is not None:
|
|
200
199
|
self._metadata.clear_cached_data()
|
|
201
200
|
self._metadata = None
|
|
@@ -215,7 +214,7 @@ class S2Product(EOProduct, EOProductProtocol):
|
|
|
215
214
|
mask_config: MaskConfig = MaskConfig(),
|
|
216
215
|
brdf_config: Optional[BRDFConfig] = None,
|
|
217
216
|
fill_value: int = 0,
|
|
218
|
-
|
|
217
|
+
read_mask: Optional[np.ndarray] = None,
|
|
219
218
|
**kwargs,
|
|
220
219
|
) -> ma.MaskedArray:
|
|
221
220
|
assets = assets or []
|
|
@@ -228,7 +227,9 @@ class S2Product(EOProduct, EOProductProtocol):
|
|
|
228
227
|
count = len(assets)
|
|
229
228
|
if isinstance(grid, Resolution):
|
|
230
229
|
grid = self.metadata.grid(grid)
|
|
231
|
-
mask = self.get_mask(
|
|
230
|
+
mask = self.get_mask(
|
|
231
|
+
grid, mask_config, target_mask=None if read_mask is None else ~read_mask
|
|
232
|
+
).data
|
|
232
233
|
if nodatavals is None:
|
|
233
234
|
nodatavals = fill_value
|
|
234
235
|
elif fill_value is None and nodatavals is not None:
|
|
@@ -464,13 +465,12 @@ class S2Product(EOProduct, EOProductProtocol):
|
|
|
464
465
|
if isinstance(grid, Resolution)
|
|
465
466
|
else Grid.from_obj(grid)
|
|
466
467
|
)
|
|
467
|
-
|
|
468
468
|
if target_mask is None:
|
|
469
469
|
target_mask = np.zeros(shape=grid.shape, dtype=bool)
|
|
470
470
|
else:
|
|
471
471
|
if target_mask.shape != grid.shape:
|
|
472
472
|
raise ValueError("a target mask must have the same shape as the grid")
|
|
473
|
-
logger.debug("got custom target mask to start with: %s", target_mask)
|
|
473
|
+
logger.debug("got custom target mask to start with: %s", target_mask.shape)
|
|
474
474
|
|
|
475
475
|
def _check_full(arr):
|
|
476
476
|
# ATTENTION: target_mask and out have to be combined *after* mask was buffered!
|
|
@@ -181,15 +181,19 @@ def gradient_merge(
|
|
|
181
181
|
# footprint coverage)
|
|
182
182
|
# set 1 to 0:
|
|
183
183
|
gradient_1band[gradient_1band == 1] = 0
|
|
184
|
-
logger.debug(
|
|
184
|
+
logger.debug(
|
|
185
|
+
f"gradient_1band; min: {np.min(gradient_1band)}, max: {np.max(gradient_1band)}"
|
|
186
|
+
)
|
|
185
187
|
|
|
186
188
|
# extrude array to match number of raster bands
|
|
187
189
|
gradient_8bit = np.stack([gradient_1band for _ in range(raster.shape[0])])
|
|
188
|
-
logger.debug(
|
|
190
|
+
logger.debug(
|
|
191
|
+
f"gradient_8bit; min: {np.min(gradient_8bit)}, max: {np.max(gradient_8bit)}"
|
|
192
|
+
)
|
|
189
193
|
|
|
190
194
|
# scale gradient from 0 to 1
|
|
191
195
|
gradient = gradient_8bit / 255
|
|
192
|
-
logger.debug(f"gradient: {gradient}")
|
|
196
|
+
logger.debug(f"gradient; min: {np.min(gradient)} , max: {np.max(gradient)}")
|
|
193
197
|
|
|
194
198
|
# now only apply the gradient where out and raster have values
|
|
195
199
|
# otherwise pick the remaining existing value or keep a masked
|
mapchete_eo/product.py
CHANGED
mapchete_eo/search/base.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from functools import cached_property
|
|
1
2
|
import json
|
|
2
3
|
import logging
|
|
3
4
|
from abc import ABC, abstractmethod
|
|
@@ -48,13 +49,25 @@ class CatalogSearcher(ABC):
|
|
|
48
49
|
This class serves as a bridge between an Archive and a catalog implementation.
|
|
49
50
|
"""
|
|
50
51
|
|
|
51
|
-
eo_bands: List[str]
|
|
52
|
-
id: str
|
|
53
|
-
description: str
|
|
54
|
-
stac_extensions: List[str]
|
|
55
52
|
collections: List[str]
|
|
56
53
|
config_cls: Type[BaseModel]
|
|
57
54
|
|
|
55
|
+
@abstractmethod
|
|
56
|
+
@cached_property
|
|
57
|
+
def eo_bands(self) -> List[str]: ...
|
|
58
|
+
|
|
59
|
+
@abstractmethod
|
|
60
|
+
@cached_property
|
|
61
|
+
def id(self) -> str: ...
|
|
62
|
+
|
|
63
|
+
@abstractmethod
|
|
64
|
+
@cached_property
|
|
65
|
+
def description(self) -> str: ...
|
|
66
|
+
|
|
67
|
+
@abstractmethod
|
|
68
|
+
@cached_property
|
|
69
|
+
def stac_extensions(self) -> List[str]: ...
|
|
70
|
+
|
|
58
71
|
@abstractmethod
|
|
59
72
|
def search(
|
|
60
73
|
self,
|
|
@@ -66,10 +79,10 @@ class CatalogSearcher(ABC):
|
|
|
66
79
|
|
|
67
80
|
|
|
68
81
|
class StaticCatalogWriterMixin(CatalogSearcher):
|
|
69
|
-
client: Client
|
|
70
|
-
id: str
|
|
71
|
-
description: str
|
|
72
|
-
stac_extensions: List[str]
|
|
82
|
+
# client: Client
|
|
83
|
+
# id: str
|
|
84
|
+
# description: str
|
|
85
|
+
# stac_extensions: List[str]
|
|
73
86
|
|
|
74
87
|
@abstractmethod
|
|
75
88
|
def get_collections(self) -> List[Collection]: # pragma: no cover
|
|
@@ -36,17 +36,34 @@ class STACSearchCatalog(StaticCatalogWriterMixin, CatalogSearcher):
|
|
|
36
36
|
stac_item_modifiers: Optional[List[Callable[[Item], Item]]] = None,
|
|
37
37
|
endpoint: Optional[MPathLike] = None,
|
|
38
38
|
):
|
|
39
|
+
if endpoint is not None:
|
|
40
|
+
self.endpoint = endpoint
|
|
39
41
|
if collections:
|
|
40
42
|
self.collections = collections
|
|
41
43
|
else: # pragma: no cover
|
|
42
44
|
raise ValueError("collections must be given")
|
|
43
|
-
self.client = Client.open(endpoint or self.endpoint)
|
|
44
|
-
self.id = self.client.id
|
|
45
|
-
self.description = self.client.description
|
|
46
|
-
self.stac_extensions = self.client.stac_extensions
|
|
47
|
-
self.eo_bands = self._eo_bands()
|
|
48
45
|
self.stac_item_modifiers = stac_item_modifiers
|
|
49
46
|
|
|
47
|
+
@cached_property
|
|
48
|
+
def client(self) -> Client:
|
|
49
|
+
return Client.open(self.endpoint)
|
|
50
|
+
|
|
51
|
+
@cached_property
|
|
52
|
+
def eo_bands(self) -> List[str]:
|
|
53
|
+
return self._eo_bands()
|
|
54
|
+
|
|
55
|
+
@cached_property
|
|
56
|
+
def id(self) -> str:
|
|
57
|
+
return self.client.id
|
|
58
|
+
|
|
59
|
+
@cached_property
|
|
60
|
+
def description(self) -> str:
|
|
61
|
+
return self.client.description
|
|
62
|
+
|
|
63
|
+
@cached_property
|
|
64
|
+
def stac_extensions(self) -> List[str]:
|
|
65
|
+
return self.client.stac_extensions
|
|
66
|
+
|
|
50
67
|
def search(
|
|
51
68
|
self,
|
|
52
69
|
time: Optional[Union[TimeRange, List[TimeRange]]] = None,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from functools import cached_property
|
|
1
2
|
import logging
|
|
2
3
|
import warnings
|
|
3
4
|
from typing import Any, Callable, Dict, Generator, List, Optional, Union
|
|
@@ -37,13 +38,25 @@ class STACStaticCatalog(StaticCatalogWriterMixin, CatalogSearcher):
|
|
|
37
38
|
stac_item_modifiers: Optional[List[Callable[[Item], Item]]] = None,
|
|
38
39
|
):
|
|
39
40
|
self.client = Client.from_file(str(baseurl), stac_io=FSSpecStacIO())
|
|
40
|
-
self.id = self.client.id
|
|
41
|
-
self.description = self.client.description
|
|
42
|
-
self.stac_extensions = self.client.stac_extensions
|
|
43
41
|
self.collections = [c.id for c in self.client.get_children()]
|
|
44
|
-
self.eo_bands = self._eo_bands()
|
|
45
42
|
self.stac_item_modifiers = stac_item_modifiers
|
|
46
43
|
|
|
44
|
+
@cached_property
|
|
45
|
+
def eo_bands(self) -> List[str]:
|
|
46
|
+
return self._eo_bands()
|
|
47
|
+
|
|
48
|
+
@cached_property
|
|
49
|
+
def id(self) -> str:
|
|
50
|
+
return self.client.id
|
|
51
|
+
|
|
52
|
+
@cached_property
|
|
53
|
+
def description(self) -> str:
|
|
54
|
+
return self.client.description
|
|
55
|
+
|
|
56
|
+
@cached_property
|
|
57
|
+
def stac_extensions(self) -> List[str]:
|
|
58
|
+
return self.client.stac_extensions
|
|
59
|
+
|
|
47
60
|
def search(
|
|
48
61
|
self,
|
|
49
62
|
time: Optional[Union[TimeRange, List[TimeRange]]] = None,
|
mapchete_eo/search/utm_search.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import datetime
|
|
2
|
+
from functools import cached_property
|
|
2
3
|
import logging
|
|
3
4
|
from typing import Any, Callable, Dict, Generator, List, Optional, Set, Union
|
|
4
5
|
|
|
@@ -51,9 +52,12 @@ class UTMSearchCatalog(StaticCatalogWriterMixin, CatalogSearcher):
|
|
|
51
52
|
if len(collections) == 0: # pragma: no cover
|
|
52
53
|
raise ValueError("no collections provided")
|
|
53
54
|
self.collections = collections
|
|
54
|
-
self.eo_bands = self._eo_bands()
|
|
55
55
|
self.stac_item_modifiers = stac_item_modifiers
|
|
56
56
|
|
|
57
|
+
@cached_property
|
|
58
|
+
def eo_bands(self) -> List[str]: # pragma: no cover
|
|
59
|
+
return self._eo_bands()
|
|
60
|
+
|
|
57
61
|
def search(
|
|
58
62
|
self,
|
|
59
63
|
time: Optional[Union[TimeRange, List[TimeRange]]] = None,
|
mapchete_eo/sort.py
CHANGED
|
@@ -5,7 +5,9 @@ This module holds all code required to sort products or slices.
|
|
|
5
5
|
from typing import Callable, List, Optional
|
|
6
6
|
|
|
7
7
|
from pydantic import BaseModel
|
|
8
|
+
from pystac import Item
|
|
8
9
|
|
|
10
|
+
from mapchete_eo.io.items import get_item_property
|
|
9
11
|
from mapchete_eo.protocols import DateTimeProtocol
|
|
10
12
|
from mapchete_eo.time import timedelta, to_datetime
|
|
11
13
|
from mapchete_eo.types import DateTimeLike
|
|
@@ -22,7 +24,7 @@ def sort_objects_by_target_date(
|
|
|
22
24
|
**kwargs,
|
|
23
25
|
) -> List[DateTimeProtocol]:
|
|
24
26
|
"""
|
|
25
|
-
Return sorted list of
|
|
27
|
+
Return sorted list of objects according to their distance to the target_date.
|
|
26
28
|
|
|
27
29
|
Default for target date is the middle between the objects start date and end date.
|
|
28
30
|
"""
|
|
@@ -46,3 +48,17 @@ class TargetDateSort(SortMethodConfig):
|
|
|
46
48
|
func: Callable = sort_objects_by_target_date
|
|
47
49
|
target_date: Optional[DateTimeLike] = None
|
|
48
50
|
reverse: bool = False
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def sort_objects_by_cloud_cover(
|
|
54
|
+
objects: List[Item], reverse: bool = False
|
|
55
|
+
) -> List[Item]:
|
|
56
|
+
if len(objects) == 0: # pragma: no cover
|
|
57
|
+
return objects
|
|
58
|
+
objects.sort(key=lambda x: get_item_property(x, "eo:cloud_cover"), reverse=reverse)
|
|
59
|
+
return objects
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class CloudCoverSort(SortMethodConfig):
|
|
63
|
+
func: Callable = sort_objects_by_cloud_cover
|
|
64
|
+
reverse: bool = False
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mapchete-eo
|
|
3
|
-
Version: 2025.
|
|
3
|
+
Version: 2025.10.1
|
|
4
4
|
Summary: mapchete EO data reader
|
|
5
5
|
Project-URL: Homepage, https://gitlab.eox.at/maps/mapchete_eo
|
|
6
6
|
Author-email: Joachim Ungar <joachim.ungar@eox.at>, Petr Sevcik <petr.sevcik@eox.at>
|
|
@@ -17,7 +17,7 @@ Classifier: Topic :: Scientific/Engineering :: GIS
|
|
|
17
17
|
Requires-Dist: click
|
|
18
18
|
Requires-Dist: croniter
|
|
19
19
|
Requires-Dist: lxml
|
|
20
|
-
Requires-Dist: mapchete[complete]>=2025.
|
|
20
|
+
Requires-Dist: mapchete[complete]>=2025.10.0
|
|
21
21
|
Requires-Dist: opencv-python-headless
|
|
22
22
|
Requires-Dist: pillow
|
|
23
23
|
Requires-Dist: pydantic
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
mapchete_eo/__init__.py,sha256=
|
|
2
|
-
mapchete_eo/base.py,sha256
|
|
1
|
+
mapchete_eo/__init__.py,sha256=r2vzraIeM6daquybaheJf__iYb9hpxuEfM5uy2Ygulg,26
|
|
2
|
+
mapchete_eo/base.py,sha256=G4DFwU9AjzWLfF_8hWPKmjsBhpwtJxQ_Z31-uZjAeFc,20294
|
|
3
3
|
mapchete_eo/blacklist.txt,sha256=6KhBY0jNjXgRpKRvKJoOTswvNUoP56IrIcNeCYnd2qo,17471
|
|
4
4
|
mapchete_eo/eostac.py,sha256=5K08Mr4wm-VOXTEvTB6DQHX7rbFLYw8mjW1wlpPltC0,574
|
|
5
5
|
mapchete_eo/exceptions.py,sha256=ul7_9o6_SfJXQPsDOQqRBhh09xv2t4AwHl6YJ7yWN5s,2150
|
|
6
6
|
mapchete_eo/geometry.py,sha256=NiLeXSnp2c_AsBin8XUFzlr_ndqHbKos-dHQSDApIro,9420
|
|
7
7
|
mapchete_eo/known_catalogs.py,sha256=dlxImeNwaUVuss-sGddixTcxjcrznyR-XWKfkX9S5PA,1274
|
|
8
|
-
mapchete_eo/product.py,sha256=
|
|
8
|
+
mapchete_eo/product.py,sha256=_KULHdTWbUw0TkRfpm8xvD2qukhuMmm9ov7cJz9aY18,9523
|
|
9
9
|
mapchete_eo/protocols.py,sha256=_WxiHPgErDjiJ0dzWJiWjHm0ZgoDlG16oEw1X1fzUXc,1512
|
|
10
10
|
mapchete_eo/settings.py,sha256=-i4UPX0KJ7NWGpGzOUkhzCABOMz2xc-q3sVLBFT4JAM,696
|
|
11
|
-
mapchete_eo/sort.py,sha256=
|
|
11
|
+
mapchete_eo/sort.py,sha256=puj3YBTL-AikC--CZwvfYCKVQWy7URMUzobf2rm9FGU,1817
|
|
12
12
|
mapchete_eo/time.py,sha256=mCIrn6C-B7CDoFIIM4u8pG15nVV8KWQixF2fuhdDTdE,1799
|
|
13
13
|
mapchete_eo/types.py,sha256=yIHKZHlGCSerJzDBS2AH7yYLun4rY3rZZ73SCKiN26U,1814
|
|
14
14
|
mapchete_eo/archives/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -16,7 +16,7 @@ mapchete_eo/archives/base.py,sha256=32NCFg46p95Uv3rO-M0Y-W6iCH4BzLtfUHpHfyzzrC4,
|
|
|
16
16
|
mapchete_eo/array/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
mapchete_eo/array/buffer.py,sha256=GeeM7RVQ-Z_SKlkaeztWUrxEsa_yck-nXwXRIM3w0_I,483
|
|
18
18
|
mapchete_eo/array/color.py,sha256=ArJ3-0qjJJGAlRQodLios6JwD0uHLSRRb9BwfB31v-U,810
|
|
19
|
-
mapchete_eo/array/convert.py,sha256=
|
|
19
|
+
mapchete_eo/array/convert.py,sha256=oimklMfyS2XxQLd73zQ2cr9aE-39z1UIt6ry_2ZQI10,5390
|
|
20
20
|
mapchete_eo/cli/__init__.py,sha256=SRRilPKUtwsUCi1GdMNPxjIrCM-XyYM2hK1T9vvKUI8,946
|
|
21
21
|
mapchete_eo/cli/bounds.py,sha256=QcOXOXMcnd8ecwiCFVWukTLUFIkpj9SbB3ot-GA4heA,551
|
|
22
22
|
mapchete_eo/cli/options_arguments.py,sha256=6rnnABT8WU6DF6vvy7lGDj5-OpMMo1Vb36fL1oan56c,7344
|
|
@@ -40,10 +40,10 @@ mapchete_eo/image_operations/linear_normalization.py,sha256=-eQX3WLCUYWUv-um3s1u
|
|
|
40
40
|
mapchete_eo/image_operations/sigmoidal.py,sha256=IKU8F89HhQJWGUVmIrnkuhqzn_ztlGrTf8xXZaVQWzU,3575
|
|
41
41
|
mapchete_eo/io/__init__.py,sha256=1-1g4cESZZREvyumEUABZhDwgVuSxtxdqbNlLuVKlow,950
|
|
42
42
|
mapchete_eo/io/assets.py,sha256=8OrqvrCzJOY0pExh_vKRL7Bz_RGk6M_LfayQqxHo8ag,17014
|
|
43
|
-
mapchete_eo/io/items.py,sha256=
|
|
44
|
-
mapchete_eo/io/levelled_cubes.py,sha256=
|
|
43
|
+
mapchete_eo/io/items.py,sha256=7l6A5E6fDNOAx7cjvu3VDP1WlD-ufEUBzt1uBBRD80Q,5750
|
|
44
|
+
mapchete_eo/io/levelled_cubes.py,sha256=ZF7BLn9MHnJCCDjAoR9D7MNbHdusjJ9EACdM7rKNlyM,9018
|
|
45
45
|
mapchete_eo/io/path.py,sha256=y5aYr-dE-0BafgF1_RCSXCvbF3taCI-EOz0R58rNxO8,4413
|
|
46
|
-
mapchete_eo/io/products.py,sha256=
|
|
46
|
+
mapchete_eo/io/products.py,sha256=2IdReyvVNY06BHUgdb7EwePqrUL0bL070c6Dnv6RhB0,14627
|
|
47
47
|
mapchete_eo/io/profiles.py,sha256=l9YiXbKYs674xz6NLy5EAq3fBEvHTVSf_gopXD-CuSY,935
|
|
48
48
|
mapchete_eo/platforms/sentinel2/__init__.py,sha256=zAyBzOhwKSIyNJyfEpyY6G-PxPDhLvOCky8aA2PsW_k,390
|
|
49
49
|
mapchete_eo/platforms/sentinel2/archives.py,sha256=uhIY0EUta45Ka94ky35NZ33Hxi0qPA2yIu21ad1z6CQ,5830
|
|
@@ -51,10 +51,10 @@ mapchete_eo/platforms/sentinel2/bandpass_adjustment.py,sha256=DA0cQtjr8UH7r_kizA
|
|
|
51
51
|
mapchete_eo/platforms/sentinel2/config.py,sha256=Mn_uw2bdR6gcIWPS_istY6E5HaN6JLuxQO05HtEovNU,5974
|
|
52
52
|
mapchete_eo/platforms/sentinel2/driver.py,sha256=ny9Rfnvi07NeB29RwimqBIF7BbCxPPRfZO_rfDJyWzI,2776
|
|
53
53
|
mapchete_eo/platforms/sentinel2/masks.py,sha256=6ig8sQhXkm1u6Bwbe7n8ewW8gTdbVJp-iaDCk780Qxg,11220
|
|
54
|
-
mapchete_eo/platforms/sentinel2/metadata_parser.py,sha256=
|
|
54
|
+
mapchete_eo/platforms/sentinel2/metadata_parser.py,sha256=czh-2M_r79sWFbZETk_-0eOyvonbhaY7Hkb3tRTeF5A,26723
|
|
55
55
|
mapchete_eo/platforms/sentinel2/preprocessing_tasks.py,sha256=eJh1wSDsQkEKtQ6G6vmVkFBoBl5nZUV5vNHQM4RifQg,772
|
|
56
56
|
mapchete_eo/platforms/sentinel2/processing_baseline.py,sha256=B2-t5H7xC54g2aIvcdBSRQPHygfqccbKEhXSgClAADY,5085
|
|
57
|
-
mapchete_eo/platforms/sentinel2/product.py,sha256=
|
|
57
|
+
mapchete_eo/platforms/sentinel2/product.py,sha256=9grX6TGCryVxS8TH6Lzc-yca-8zKzCx_WD1r3_bPK9E,25376
|
|
58
58
|
mapchete_eo/platforms/sentinel2/types.py,sha256=Cd0bmDT1mLETuSJYEAoF8Kl4-QOPv2fFrwgR8xHTka0,2009
|
|
59
59
|
mapchete_eo/platforms/sentinel2/brdf/__init__.py,sha256=W7zAJZ5F5Xla7j4ua2opRANUbcD3jZbhTwhW5_cpOUI,242
|
|
60
60
|
mapchete_eo/platforms/sentinel2/brdf/config.py,sha256=v3WCEu4r-tEPQgWBEkYNAPLgCQobvYtQ2YiDeAdImMk,1133
|
|
@@ -73,16 +73,16 @@ mapchete_eo/processes/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
|
|
|
73
73
|
mapchete_eo/processes/config.py,sha256=WorMzHJzJaZPh-aEyEEwcXN08ALxn6zAhVMrjkDUWDU,1745
|
|
74
74
|
mapchete_eo/processes/dtype_scale.py,sha256=7hJlIe2DsE6Kmk1c3PLwLr8rnn_4_9S6Cz7bmRuzP9M,4176
|
|
75
75
|
mapchete_eo/processes/eo_to_xarray.py,sha256=Gcp4qju2C9S8KeUnVY5f3nAsrdckPhGRguzsgRtWBFs,514
|
|
76
|
-
mapchete_eo/processes/merge_rasters.py,sha256=
|
|
76
|
+
mapchete_eo/processes/merge_rasters.py,sha256=jSXUI8pEWXbQWrM9iMtrH4ACH2VkCaBUH9CQg4z6zLA,8486
|
|
77
77
|
mapchete_eo/search/__init__.py,sha256=71VuR1BFFbFiV9rae5qXt0RLtwWk1DP_CYbZflMZDWA,516
|
|
78
|
-
mapchete_eo/search/base.py,sha256=
|
|
78
|
+
mapchete_eo/search/base.py,sha256=LQfCwsZW5YFZx5pvyNSpfDbcObB-UGypjfrvL-3VUZE,8883
|
|
79
79
|
mapchete_eo/search/config.py,sha256=0idsjXt2T8scerlw-PzNVc0u8JxpOxHgrEO2asSRtNE,1122
|
|
80
80
|
mapchete_eo/search/s2_mgrs.py,sha256=5LWl9c7WzFvXODr9f8m37cv-8yyf-LvnN0-TSSPcXKM,10868
|
|
81
|
-
mapchete_eo/search/stac_search.py,sha256=
|
|
82
|
-
mapchete_eo/search/stac_static.py,sha256=
|
|
83
|
-
mapchete_eo/search/utm_search.py,sha256=
|
|
84
|
-
mapchete_eo-2025.
|
|
85
|
-
mapchete_eo-2025.
|
|
86
|
-
mapchete_eo-2025.
|
|
87
|
-
mapchete_eo-2025.
|
|
88
|
-
mapchete_eo-2025.
|
|
81
|
+
mapchete_eo/search/stac_search.py,sha256=TXsCSMi3PBst80_vexXv0pvY5DNcD8sgtsw3zOQtoRA,9844
|
|
82
|
+
mapchete_eo/search/stac_static.py,sha256=3PA1uUIQy0bHywFA3_hmyn65pjlWEqeCW4Gx2DdZMZI,8839
|
|
83
|
+
mapchete_eo/search/utm_search.py,sha256=__HZCw5no9_8Rb6kO1pCfCEUS_ZwmgrVwlcyE78SY6k,9754
|
|
84
|
+
mapchete_eo-2025.10.1.dist-info/METADATA,sha256=edkh71VZo0bygu4yQ44RIeR9CnshfudaH-HOx76lg7w,3237
|
|
85
|
+
mapchete_eo-2025.10.1.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
|
|
86
|
+
mapchete_eo-2025.10.1.dist-info/entry_points.txt,sha256=ewk6R4FGdAclOnUpikhlPZGWI40MWeksVIIwu4jVebk,324
|
|
87
|
+
mapchete_eo-2025.10.1.dist-info/licenses/LICENSE,sha256=TC5JwvBnFrUgsSQSCDFPc3cqlbth2N0q8MWrhY1EVd0,1089
|
|
88
|
+
mapchete_eo-2025.10.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|