mapchete-eo 2026.2.0__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.
Files changed (89) hide show
  1. mapchete_eo/__init__.py +1 -0
  2. mapchete_eo/array/__init__.py +0 -0
  3. mapchete_eo/array/buffer.py +16 -0
  4. mapchete_eo/array/color.py +29 -0
  5. mapchete_eo/array/convert.py +163 -0
  6. mapchete_eo/base.py +653 -0
  7. mapchete_eo/blacklist.txt +175 -0
  8. mapchete_eo/cli/__init__.py +30 -0
  9. mapchete_eo/cli/bounds.py +22 -0
  10. mapchete_eo/cli/options_arguments.py +227 -0
  11. mapchete_eo/cli/s2_brdf.py +77 -0
  12. mapchete_eo/cli/s2_cat_results.py +130 -0
  13. mapchete_eo/cli/s2_find_broken_products.py +77 -0
  14. mapchete_eo/cli/s2_jp2_static_catalog.py +166 -0
  15. mapchete_eo/cli/s2_mask.py +71 -0
  16. mapchete_eo/cli/s2_mgrs.py +45 -0
  17. mapchete_eo/cli/s2_rgb.py +114 -0
  18. mapchete_eo/cli/s2_verify.py +129 -0
  19. mapchete_eo/cli/static_catalog.py +82 -0
  20. mapchete_eo/eostac.py +30 -0
  21. mapchete_eo/exceptions.py +87 -0
  22. mapchete_eo/image_operations/__init__.py +12 -0
  23. mapchete_eo/image_operations/blend_functions.py +579 -0
  24. mapchete_eo/image_operations/color_correction.py +136 -0
  25. mapchete_eo/image_operations/compositing.py +266 -0
  26. mapchete_eo/image_operations/dtype_scale.py +43 -0
  27. mapchete_eo/image_operations/fillnodata.py +130 -0
  28. mapchete_eo/image_operations/filters.py +319 -0
  29. mapchete_eo/image_operations/linear_normalization.py +81 -0
  30. mapchete_eo/image_operations/sigmoidal.py +114 -0
  31. mapchete_eo/io/__init__.py +37 -0
  32. mapchete_eo/io/assets.py +496 -0
  33. mapchete_eo/io/items.py +162 -0
  34. mapchete_eo/io/levelled_cubes.py +259 -0
  35. mapchete_eo/io/path.py +155 -0
  36. mapchete_eo/io/products.py +423 -0
  37. mapchete_eo/io/profiles.py +45 -0
  38. mapchete_eo/platforms/sentinel2/__init__.py +17 -0
  39. mapchete_eo/platforms/sentinel2/_mapper_registry.py +89 -0
  40. mapchete_eo/platforms/sentinel2/bandpass_adjustment.py +104 -0
  41. mapchete_eo/platforms/sentinel2/brdf/__init__.py +8 -0
  42. mapchete_eo/platforms/sentinel2/brdf/config.py +32 -0
  43. mapchete_eo/platforms/sentinel2/brdf/correction.py +260 -0
  44. mapchete_eo/platforms/sentinel2/brdf/hls.py +251 -0
  45. mapchete_eo/platforms/sentinel2/brdf/models.py +44 -0
  46. mapchete_eo/platforms/sentinel2/brdf/protocols.py +27 -0
  47. mapchete_eo/platforms/sentinel2/brdf/ross_thick.py +136 -0
  48. mapchete_eo/platforms/sentinel2/brdf/sun_angle_arrays.py +76 -0
  49. mapchete_eo/platforms/sentinel2/config.py +241 -0
  50. mapchete_eo/platforms/sentinel2/driver.py +43 -0
  51. mapchete_eo/platforms/sentinel2/masks.py +329 -0
  52. mapchete_eo/platforms/sentinel2/metadata_parser/__init__.py +6 -0
  53. mapchete_eo/platforms/sentinel2/metadata_parser/base.py +56 -0
  54. mapchete_eo/platforms/sentinel2/metadata_parser/default_path_mapper.py +135 -0
  55. mapchete_eo/platforms/sentinel2/metadata_parser/models.py +78 -0
  56. mapchete_eo/platforms/sentinel2/metadata_parser/s2metadata.py +639 -0
  57. mapchete_eo/platforms/sentinel2/preconfigured_sources/__init__.py +57 -0
  58. mapchete_eo/platforms/sentinel2/preconfigured_sources/guessers.py +108 -0
  59. mapchete_eo/platforms/sentinel2/preconfigured_sources/item_mappers.py +171 -0
  60. mapchete_eo/platforms/sentinel2/preconfigured_sources/metadata_xml_mappers.py +217 -0
  61. mapchete_eo/platforms/sentinel2/preprocessing_tasks.py +50 -0
  62. mapchete_eo/platforms/sentinel2/processing_baseline.py +163 -0
  63. mapchete_eo/platforms/sentinel2/product.py +747 -0
  64. mapchete_eo/platforms/sentinel2/source.py +114 -0
  65. mapchete_eo/platforms/sentinel2/types.py +114 -0
  66. mapchete_eo/processes/__init__.py +0 -0
  67. mapchete_eo/processes/config.py +51 -0
  68. mapchete_eo/processes/dtype_scale.py +112 -0
  69. mapchete_eo/processes/eo_to_xarray.py +19 -0
  70. mapchete_eo/processes/merge_rasters.py +239 -0
  71. mapchete_eo/product.py +323 -0
  72. mapchete_eo/protocols.py +61 -0
  73. mapchete_eo/search/__init__.py +14 -0
  74. mapchete_eo/search/base.py +285 -0
  75. mapchete_eo/search/config.py +113 -0
  76. mapchete_eo/search/s2_mgrs.py +313 -0
  77. mapchete_eo/search/stac_search.py +278 -0
  78. mapchete_eo/search/stac_static.py +197 -0
  79. mapchete_eo/search/utm_search.py +251 -0
  80. mapchete_eo/settings.py +25 -0
  81. mapchete_eo/sort.py +60 -0
  82. mapchete_eo/source.py +109 -0
  83. mapchete_eo/time.py +62 -0
  84. mapchete_eo/types.py +76 -0
  85. mapchete_eo-2026.2.0.dist-info/METADATA +91 -0
  86. mapchete_eo-2026.2.0.dist-info/RECORD +89 -0
  87. mapchete_eo-2026.2.0.dist-info/WHEEL +4 -0
  88. mapchete_eo-2026.2.0.dist-info/entry_points.txt +11 -0
  89. mapchete_eo-2026.2.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,136 @@
1
+ from __future__ import annotations
2
+
3
+ import numpy as np
4
+ from numpy.typing import DTypeLike
5
+
6
+ from typing import Optional
7
+
8
+ from affine import Affine
9
+ from mapchete.io.raster import ReferencedRaster
10
+ from mapchete.types import CRSLike
11
+
12
+ from mapchete_eo.platforms.sentinel2.brdf.protocols import (
13
+ BRDFModelProtocol,
14
+ )
15
+ from mapchete_eo.platforms.sentinel2.brdf.config import L2ABandFParams, ModelParameters
16
+ from mapchete_eo.platforms.sentinel2.brdf.hls import _get_viewing_angles
17
+ from mapchete_eo.platforms.sentinel2.metadata_parser.s2metadata import S2Metadata
18
+ from mapchete_eo.platforms.sentinel2.types import L2ABand
19
+
20
+
21
+ class RossThick(BRDFModelProtocol):
22
+ """Directional model."""
23
+
24
+ sun_zenith: np.ndarray
25
+ sun_azimuth: np.ndarray
26
+ view_zenith: np.ndarray
27
+ view_azimuth: np.ndarray
28
+ f_band_params: ModelParameters
29
+ processing_dtype: DTypeLike = np.float32
30
+
31
+ transform: Affine
32
+ crs: CRSLike
33
+
34
+ def __init__(
35
+ self,
36
+ s2_metadata: S2Metadata,
37
+ band: L2ABand,
38
+ detector_id: Optional[int] = None,
39
+ processing_dtype: DTypeLike = np.float32,
40
+ ):
41
+ self.sun_zenith = s2_metadata.sun_angles.zenith.raster.data
42
+ self.sun_azimuth = s2_metadata.sun_angles.azimuth.raster.data
43
+ self.view_zenith, self.view_azimuth = _get_viewing_angles(
44
+ s2_metadata=s2_metadata, band=band, detector_id=detector_id
45
+ )
46
+ self.f_band_params = L2ABandFParams[band.name].value
47
+ self.processing_dtype = processing_dtype
48
+
49
+ self.sun_zenith_radian = np.deg2rad(self.sun_zenith)
50
+ self.sun_azimuth_radian = np.deg2rad(self.sun_azimuth)
51
+ self.view_zenith_radian = np.deg2rad(self.view_zenith)
52
+ self.view_azimuth_radian = np.deg2rad(self.view_azimuth)
53
+
54
+ self.relative_azimuth_angle_radian = np.abs(
55
+ self.view_azimuth_radian - self.sun_azimuth_radian
56
+ )
57
+
58
+ self.transform = s2_metadata.sun_angles.zenith.raster.transform
59
+ self.crs = s2_metadata.crs
60
+
61
+ def calculate(self) -> ReferencedRaster:
62
+ """
63
+ Ross-Thick BRDF model function that computes the C factor.
64
+
65
+ Parameters:
66
+ - f_iso, f_vol, f_geo: BRDF model parameters to fit.
67
+ - sza: Solar Zenith Angle (in degrees).
68
+ - vza: View Zenith Angle (in degrees).
69
+ - raa: Relative Azimuth Angle (in degrees).
70
+ - normalize: Normalize by nadir sensor with sun angles.
71
+
72
+ Returns:
73
+ - C factor according to the Ross-Thick BRDF model.
74
+ """
75
+ sza = self.sun_zenith_radian
76
+ vza = self.view_zenith_radian
77
+ raa = self.relative_azimuth_angle_radian
78
+
79
+ f_iso = self.f_band_params.f_iso
80
+ f_vol = self.f_band_params.f_vol
81
+ f_geo = self.f_band_params.f_geo
82
+
83
+ # Scale vza for fitting
84
+ vza = vza / (np.pi / 2)
85
+
86
+ def compute_kernels(vza, sza, raa):
87
+ # Cosine of view and solar zenith angles
88
+ cos_vza = np.cos(vza)
89
+ cos_sza = np.cos(sza)
90
+
91
+ # Phase angle (theta)
92
+ cos_theta = cos_vza * cos_sza + np.sin(vza) * np.sin(sza) * np.cos(raa)
93
+ theta = np.arccos(np.clip(cos_theta, -1.0, 1.0))
94
+
95
+ # Ross-Thick Kernel (K_vol)
96
+ K_vol = ((np.pi - theta) * cos_theta + np.sin(theta)) / (
97
+ cos_vza + cos_sza
98
+ ) - np.pi / 4
99
+
100
+ # Li-Sparse Kernel (K_geo)
101
+ tan_vza = np.tan(vza)
102
+ tan_sza = np.tan(sza)
103
+ D = np.sqrt(tan_vza**2 + tan_sza**2 - 2 * tan_vza * tan_sza * np.cos(raa))
104
+ cos_phase = np.clip((2 * D / (D + tan_vza + tan_sza)), -1.0, 1.0)
105
+ K_geo = (1 / np.pi) * (D - cos_phase * (tan_vza + tan_sza)) + np.arctan(D)
106
+
107
+ return K_vol, K_geo
108
+
109
+ # Calculate kernels for actual angles
110
+ K_vol, K_geo = compute_kernels(vza, sza, raa)
111
+ C_actual = f_iso + f_vol * K_vol + f_geo * K_geo
112
+
113
+ # Calculate kernels for nadir (0° view, 0° relative azimuth)
114
+ K_vol_nadir, K_geo_nadir = compute_kernels(0, sza, 0)
115
+ C_nadir = f_iso + f_vol * K_vol_nadir + f_geo * K_geo_nadir
116
+
117
+ # Normalize and return c-factors
118
+ return ReferencedRaster.from_array_like(
119
+ array_like=(C_nadir / C_actual),
120
+ transform=self.transform,
121
+ crs=self.crs,
122
+ )
123
+
124
+ @staticmethod
125
+ def from_s2metadata(
126
+ s2_metadata: S2Metadata,
127
+ band: L2ABand,
128
+ detector_id: Optional[int] = None,
129
+ processing_dtype: DTypeLike = np.float32,
130
+ ) -> RossThick:
131
+ return RossThick(
132
+ s2_metadata=s2_metadata,
133
+ band=band,
134
+ detector_id=detector_id,
135
+ processing_dtype=processing_dtype,
136
+ )
@@ -0,0 +1,76 @@
1
+ from typing import Tuple
2
+
3
+ from fiona.transform import transform
4
+ import numpy as np
5
+
6
+ from mapchete_eo.platforms.sentinel2.metadata_parser.s2metadata import S2Metadata
7
+
8
+
9
+ def get_sun_zenith_angles(s2_metadata: S2Metadata) -> np.ndarray:
10
+ _, (bottom, top) = transform(
11
+ s2_metadata.crs,
12
+ "EPSG:4326",
13
+ [s2_metadata.bounds[0], s2_metadata.bounds[2]],
14
+ [s2_metadata.bounds[1], s2_metadata.bounds[3]],
15
+ )
16
+ return get_sun_angle_array(
17
+ min_lat=bottom,
18
+ max_lat=top,
19
+ shape=s2_metadata.sun_angles.zenith.raster.data.shape,
20
+ )
21
+
22
+
23
+ def get_sun_angle_array(
24
+ min_lat: float, max_lat: float, shape: Tuple[int, int]
25
+ ) -> np.ndarray:
26
+ """
27
+ Calculate array of sun angles between latitudes.
28
+
29
+ Returns
30
+ =======
31
+ sun angle array in radians : np.ndarray
32
+ """
33
+
34
+ def _sun_angle(lat):
35
+ """
36
+ Calculate the constant sun zenith angle via 6th polynomial function see HLS.
37
+
38
+ See page 13 of:
39
+ https://hls.gsfc.nasa.gov/wp-content/uploads/2019/01/HLS.v1.4.UserGuide_draft_ver3.1.pdf
40
+ """
41
+ # constants used for sun angle calculation
42
+ # See page 13 of:
43
+ # https://hls.gsfc.nasa.gov/wp-content/uploads/2019/01/HLS.v1.4.UserGuide_draft_ver3.1.pdf
44
+ k0 = 31
45
+ k1 = -0.127
46
+ k2 = 0.0119
47
+ k3 = 2.4e-05
48
+ k4 = -9.48e-07
49
+ k5 = -1.95e-09
50
+ k6 = 6.15e-11
51
+
52
+ # Constant sun zenith angle 6th polynomial function
53
+ return (
54
+ k0
55
+ + k1 * lat
56
+ + k2 * (lat**2)
57
+ + k4 * (lat**4)
58
+ + k3 * (lat**3)
59
+ + k5 * (lat**5)
60
+ + k6 * (lat**6)
61
+ )
62
+
63
+ # return get_constant_sun_angle(min_lat, max_lat)
64
+ height, width = shape
65
+ cell_size = (max_lat - min_lat) / (height + 1)
66
+
67
+ # move by half a pixel so pixel centers are represented
68
+ top = max_lat - cell_size / 2
69
+
70
+ # generate one column of angles
71
+ angles = [_sun_angle(top - i * cell_size) for i in range(width)]
72
+
73
+ # expand column to output shape width
74
+ return np.radians(
75
+ np.array([[i for _ in range(width)] for i in angles], dtype=np.float32)
76
+ )
@@ -0,0 +1,241 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import List, Optional, Union, Dict, Any
4
+ import warnings
5
+
6
+ from mapchete.path import MPathLike
7
+ from pydantic import BaseModel, ValidationError, field_validator, model_validator
8
+
9
+ from mapchete_eo.base import BaseDriverConfig
10
+ from mapchete_eo.io.path import ProductPathGenerationMethod
11
+ from mapchete_eo.platforms.sentinel2.brdf.config import BRDFModels
12
+ from mapchete_eo.platforms.sentinel2.preconfigured_sources import (
13
+ KNOWN_SOURCES,
14
+ DEPRECATED_ARCHIVES,
15
+ )
16
+ from mapchete_eo.platforms.sentinel2.source import Sentinel2Source
17
+ from mapchete_eo.platforms.sentinel2.types import (
18
+ CloudType,
19
+ ProductQIMaskResolution,
20
+ Resolution,
21
+ SceneClassification,
22
+ )
23
+ from mapchete_eo.search.config import StacSearchConfig
24
+ from mapchete_eo.types import TimeRange
25
+
26
+
27
+ default_source = Sentinel2Source.model_validate(KNOWN_SOURCES["EarthSearch"])
28
+
29
+
30
+ class BRDFModelConfig(BaseModel):
31
+ model: BRDFModels = BRDFModels.HLS
32
+ bands: List[str] = ["blue", "green", "red", "nir"]
33
+ resolution: Resolution = Resolution["60m"]
34
+ footprints_cached_read: bool = False
35
+ log10_bands_scale: bool = False
36
+ per_detector_correction: bool = False
37
+
38
+ # This correction value is applied to `fv` (kvol) and `fr` (kgeo) in the final steps of the BRDF param
39
+ correction_weight: float = 1.0
40
+
41
+
42
+ class BRDFSCLClassConfig(BRDFModelConfig):
43
+ scl_classes: List[SceneClassification]
44
+
45
+ @field_validator("scl_classes", mode="before")
46
+ @classmethod
47
+ def to_scl_classes(cls, values: List[str]) -> List[SceneClassification]:
48
+ out = []
49
+ for value in values:
50
+ if isinstance(value, SceneClassification):
51
+ out.append(value)
52
+ elif isinstance(value, str):
53
+ out.append(SceneClassification[value])
54
+ else: # pragma: no cover
55
+ raise ValidationError("value must be mappable to SceneClassification")
56
+ return out
57
+
58
+
59
+ class BRDFConfig(BRDFModelConfig):
60
+ """
61
+ Main BRDF configuration with optional sub-configurations for certain SCL classes.
62
+
63
+ model: BRDF model
64
+ bands: list of band names
65
+ resolution: resolution BRDF is calculated on
66
+ footprints_cached_read: download and read footprints from cache or not
67
+ correction_weight: make correction stronger (>1) or weaker (<1)
68
+ scl_specific_configurations: list of parameters like above plus SCL classes it
69
+ should be applied to
70
+
71
+ e.g.
72
+ BRDFConfig(
73
+ model="HLS",
74
+ bands=["red", "green", "blue"],
75
+ resolution="60m",
76
+ footprints_cached_read=True,
77
+ correction_weight=0.9,
78
+ log10_bands_scale=True,
79
+ scl_specific_configurations=[
80
+ BRDFSCLClassConfig(
81
+ scl_classes=["water"],
82
+ model="HLS",
83
+ bands=["red", "green", "blue"],
84
+ resolution="60m",
85
+ footprints_cached_read=True,
86
+ correction_weight=1.3,
87
+ )
88
+ ]
89
+ )
90
+
91
+ """
92
+
93
+ scl_specific_configurations: Optional[List[BRDFSCLClassConfig]] = None
94
+
95
+
96
+ class CacheConfig(BaseModel):
97
+ path: MPathLike
98
+ product_path_generation_method: ProductPathGenerationMethod = (
99
+ ProductPathGenerationMethod.hash
100
+ )
101
+ intersection_percent: float = 100.0
102
+ assets: List[str] = []
103
+ assets_resolution: Resolution = Resolution.original
104
+ keep: bool = False
105
+ max_cloud_cover: float = 100.0
106
+ max_disk_usage: float = 90.0
107
+ brdf: Optional[BRDFConfig] = None
108
+ zoom: int = 13
109
+
110
+
111
+ class Sentinel2DriverConfig(BaseDriverConfig):
112
+ format: str = "Sentinel-2"
113
+ time: Union[TimeRange, List[TimeRange]]
114
+
115
+ # new
116
+ source: List[Sentinel2Source] = [default_source]
117
+
118
+ # deprecated
119
+ # for backwards compatibility, archive should be converted to
120
+ # catalog & data_archive
121
+ # archive: ArchiveClsFromString = AWSL2ACOGv1
122
+ # cat_baseurl: Optional[MPathLike] = None
123
+ search_index: Optional[MPathLike] = None
124
+
125
+ # custom params
126
+ stac_config: StacSearchConfig = StacSearchConfig()
127
+ first_granule_only: bool = False
128
+ utm_zone: Optional[int] = None
129
+ with_scl: bool = False
130
+ brdf: Optional[BRDFConfig] = None
131
+ cache: Optional[CacheConfig] = None
132
+
133
+ @model_validator(mode="before")
134
+ def deprecated_values(cls, values: Dict[str, Any]) -> Dict[str, Any]:
135
+ archive = values.pop("archive", None)
136
+ if archive:
137
+ warnings.warn(
138
+ "'archive' will be deprecated soon. Please use 'source'.",
139
+ category=DeprecationWarning,
140
+ stacklevel=2,
141
+ )
142
+ if values.get("source") is None:
143
+ values["source"] = DEPRECATED_ARCHIVES[archive]
144
+
145
+ cat_baseurl = values.pop("cat_baseurl", None)
146
+ if cat_baseurl: # pragma: no cover
147
+ warnings.warn(
148
+ "'cat_baseurl' will be deprecated soon. Please use 'catalog_type=static' in the source.",
149
+ category=DeprecationWarning,
150
+ stacklevel=2,
151
+ )
152
+ if values.get("source", []):
153
+ raise ValueError(
154
+ "deprecated cat_baseurl field found alongside sources."
155
+ )
156
+ values["source"] = [dict(collection=cat_baseurl, catalog_type="static")]
157
+
158
+ # add default source if necessary
159
+ sources = values.get("source", [])
160
+ if not sources:
161
+ values["source"] = [default_source.model_dump(exclude_none=True)]
162
+
163
+ max_cloud_cover = values.pop("max_cloud_cover", None)
164
+ if max_cloud_cover: # pragma: no cover
165
+ warnings.warn(
166
+ "'max_cloud_cover' will be deprecated soon. Please use 'eo:cloud_cover<=...' in the source 'query' field.",
167
+ category=DeprecationWarning,
168
+ stacklevel=2,
169
+ )
170
+ updated_sources = []
171
+ for source in values.get("source", []):
172
+ if source.get("query") is not None:
173
+ raise ValueError(
174
+ f"deprecated max_cloud_cover is set but also a query field is given in {source}"
175
+ )
176
+ source["query"] = f"eo:cloud_cover<={max_cloud_cover}"
177
+ updated_sources.append(source)
178
+ values["source"] = updated_sources
179
+ return values
180
+
181
+
182
+ class MaskConfig(BaseModel):
183
+ # mask by footprint geometry
184
+ footprint: bool = True
185
+ # apply buffer (in meters!) to footprint
186
+ footprint_buffer_m: float = -500
187
+ # add pixel buffer to all masks
188
+ buffer: int = 0
189
+ # mask by L1C cloud types (either opaque, cirrus or all)
190
+ l1c_cloud_type: Optional[CloudType] = None
191
+ # mask using the snow/ice mask
192
+ snow_ice: bool = False
193
+ # mask using cloud probability classification
194
+ cloud_probability_threshold: int = 100
195
+ cloud_probability_resolution: ProductQIMaskResolution = ProductQIMaskResolution[
196
+ "60m"
197
+ ]
198
+ # mask using cloud probability classification
199
+ snow_probability_threshold: int = 100
200
+ snow_probability_resolution: ProductQIMaskResolution = ProductQIMaskResolution[
201
+ "60m"
202
+ ]
203
+ # mask using one or more of the SCL classes
204
+ scl_classes: Optional[List[SceneClassification]] = None
205
+ # download masks before reading
206
+ l1c_cloud_mask_cached_read: bool = False
207
+ snow_ice_mask_cached_read: bool = False
208
+ cloud_probability_cached_read: bool = False
209
+ snow_probability_cached_read: bool = False
210
+ scl_cached_read: bool = False
211
+
212
+ @field_validator("scl_classes", mode="before")
213
+ @classmethod
214
+ def to_scl_classes(cls, values: List[str]) -> List[SceneClassification]:
215
+ if values is None:
216
+ return
217
+ out = []
218
+ for value in values:
219
+ if isinstance(value, SceneClassification):
220
+ out.append(value)
221
+ elif isinstance(value, str):
222
+ out.append(SceneClassification[value])
223
+ else: # pragma: no cover
224
+ raise ValidationError("value must be mappable to SceneClassification")
225
+ return out
226
+
227
+ @staticmethod
228
+ def parse(config: Union[dict, MaskConfig]) -> MaskConfig:
229
+ """
230
+ Make sure all values are parsed correctly
231
+ """
232
+ if isinstance(config, MaskConfig):
233
+ return config
234
+
235
+ elif isinstance(config, dict):
236
+ return MaskConfig(**config)
237
+
238
+ else: # pragma: no cover
239
+ raise TypeError(
240
+ f"mask configuration should either be a dictionary or a MaskConfig object, not {config}"
241
+ )
@@ -0,0 +1,43 @@
1
+ from typing import Optional, List, Tuple
2
+
3
+ from mapchete.types import NodataVal
4
+ from rasterio.enums import Resampling
5
+
6
+ from mapchete_eo import base
7
+ from mapchete_eo.platforms.sentinel2.config import Sentinel2DriverConfig
8
+ from mapchete_eo.platforms.sentinel2.preprocessing_tasks import parse_s2_product
9
+ from mapchete_eo.types import MergeMethod
10
+
11
+ METADATA: dict = {
12
+ "driver_name": "Sentinel-2",
13
+ "data_type": None,
14
+ "mode": "r",
15
+ "file_extensions": [],
16
+ }
17
+
18
+
19
+ class Sentinel2Cube(base.EODataCube):
20
+ """
21
+ Sentinel-2 data cube for Mapchete.
22
+ """
23
+
24
+ # Sentinel-2 driver specific default values:
25
+ default_read_merge_method: MergeMethod = MergeMethod.average
26
+ default_read_merge_products_by: Optional[str] = "s2:datastrip_id"
27
+ default_read_nodataval: NodataVal = 0
28
+ default_read_resampling: Resampling = Resampling.bilinear
29
+
30
+
31
+ Sentinel2CubeGroup = List[Tuple[str, Sentinel2Cube]]
32
+
33
+
34
+ class InputData(base.InputData):
35
+ """
36
+ Sentinel-2 driver for Mapchete.
37
+ """
38
+
39
+ # Sentinel-2 driver specific parameters:
40
+ default_preprocessing_task = staticmethod(parse_s2_product)
41
+ driver_config_model = Sentinel2DriverConfig
42
+ params: Sentinel2DriverConfig
43
+ input_tile_cls = Sentinel2Cube