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.
Files changed (87) hide show
  1. mapchete_eo/__init__.py +1 -0
  2. mapchete_eo/archives/__init__.py +0 -0
  3. mapchete_eo/archives/base.py +65 -0
  4. mapchete_eo/array/__init__.py +0 -0
  5. mapchete_eo/array/buffer.py +16 -0
  6. mapchete_eo/array/color.py +29 -0
  7. mapchete_eo/array/convert.py +157 -0
  8. mapchete_eo/base.py +528 -0
  9. mapchete_eo/blacklist.txt +175 -0
  10. mapchete_eo/cli/__init__.py +30 -0
  11. mapchete_eo/cli/bounds.py +22 -0
  12. mapchete_eo/cli/options_arguments.py +243 -0
  13. mapchete_eo/cli/s2_brdf.py +77 -0
  14. mapchete_eo/cli/s2_cat_results.py +146 -0
  15. mapchete_eo/cli/s2_find_broken_products.py +93 -0
  16. mapchete_eo/cli/s2_jp2_static_catalog.py +166 -0
  17. mapchete_eo/cli/s2_mask.py +71 -0
  18. mapchete_eo/cli/s2_mgrs.py +45 -0
  19. mapchete_eo/cli/s2_rgb.py +114 -0
  20. mapchete_eo/cli/s2_verify.py +129 -0
  21. mapchete_eo/cli/static_catalog.py +123 -0
  22. mapchete_eo/eostac.py +30 -0
  23. mapchete_eo/exceptions.py +87 -0
  24. mapchete_eo/geometry.py +271 -0
  25. mapchete_eo/image_operations/__init__.py +12 -0
  26. mapchete_eo/image_operations/color_correction.py +136 -0
  27. mapchete_eo/image_operations/compositing.py +247 -0
  28. mapchete_eo/image_operations/dtype_scale.py +43 -0
  29. mapchete_eo/image_operations/fillnodata.py +130 -0
  30. mapchete_eo/image_operations/filters.py +319 -0
  31. mapchete_eo/image_operations/linear_normalization.py +81 -0
  32. mapchete_eo/image_operations/sigmoidal.py +114 -0
  33. mapchete_eo/io/__init__.py +37 -0
  34. mapchete_eo/io/assets.py +492 -0
  35. mapchete_eo/io/items.py +147 -0
  36. mapchete_eo/io/levelled_cubes.py +228 -0
  37. mapchete_eo/io/path.py +144 -0
  38. mapchete_eo/io/products.py +413 -0
  39. mapchete_eo/io/profiles.py +45 -0
  40. mapchete_eo/known_catalogs.py +42 -0
  41. mapchete_eo/platforms/sentinel2/__init__.py +17 -0
  42. mapchete_eo/platforms/sentinel2/archives.py +190 -0
  43. mapchete_eo/platforms/sentinel2/bandpass_adjustment.py +104 -0
  44. mapchete_eo/platforms/sentinel2/brdf/__init__.py +8 -0
  45. mapchete_eo/platforms/sentinel2/brdf/config.py +32 -0
  46. mapchete_eo/platforms/sentinel2/brdf/correction.py +260 -0
  47. mapchete_eo/platforms/sentinel2/brdf/hls.py +251 -0
  48. mapchete_eo/platforms/sentinel2/brdf/models.py +44 -0
  49. mapchete_eo/platforms/sentinel2/brdf/protocols.py +27 -0
  50. mapchete_eo/platforms/sentinel2/brdf/ross_thick.py +136 -0
  51. mapchete_eo/platforms/sentinel2/brdf/sun_angle_arrays.py +76 -0
  52. mapchete_eo/platforms/sentinel2/config.py +181 -0
  53. mapchete_eo/platforms/sentinel2/driver.py +78 -0
  54. mapchete_eo/platforms/sentinel2/masks.py +325 -0
  55. mapchete_eo/platforms/sentinel2/metadata_parser.py +734 -0
  56. mapchete_eo/platforms/sentinel2/path_mappers/__init__.py +29 -0
  57. mapchete_eo/platforms/sentinel2/path_mappers/base.py +56 -0
  58. mapchete_eo/platforms/sentinel2/path_mappers/earthsearch.py +34 -0
  59. mapchete_eo/platforms/sentinel2/path_mappers/metadata_xml.py +135 -0
  60. mapchete_eo/platforms/sentinel2/path_mappers/sinergise.py +105 -0
  61. mapchete_eo/platforms/sentinel2/preprocessing_tasks.py +26 -0
  62. mapchete_eo/platforms/sentinel2/processing_baseline.py +160 -0
  63. mapchete_eo/platforms/sentinel2/product.py +669 -0
  64. mapchete_eo/platforms/sentinel2/types.py +109 -0
  65. mapchete_eo/processes/__init__.py +0 -0
  66. mapchete_eo/processes/config.py +51 -0
  67. mapchete_eo/processes/dtype_scale.py +112 -0
  68. mapchete_eo/processes/eo_to_xarray.py +19 -0
  69. mapchete_eo/processes/merge_rasters.py +235 -0
  70. mapchete_eo/product.py +278 -0
  71. mapchete_eo/protocols.py +56 -0
  72. mapchete_eo/search/__init__.py +14 -0
  73. mapchete_eo/search/base.py +222 -0
  74. mapchete_eo/search/config.py +42 -0
  75. mapchete_eo/search/s2_mgrs.py +314 -0
  76. mapchete_eo/search/stac_search.py +251 -0
  77. mapchete_eo/search/stac_static.py +236 -0
  78. mapchete_eo/search/utm_search.py +251 -0
  79. mapchete_eo/settings.py +24 -0
  80. mapchete_eo/sort.py +48 -0
  81. mapchete_eo/time.py +53 -0
  82. mapchete_eo/types.py +73 -0
  83. mapchete_eo-2025.7.0.dist-info/METADATA +38 -0
  84. mapchete_eo-2025.7.0.dist-info/RECORD +87 -0
  85. mapchete_eo-2025.7.0.dist-info/WHEEL +5 -0
  86. mapchete_eo-2025.7.0.dist-info/entry_points.txt +11 -0
  87. mapchete_eo-2025.7.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,27 @@
1
+ from __future__ import annotations
2
+ from typing import Optional, Protocol
3
+
4
+ from mapchete.io.raster import ReferencedRaster
5
+
6
+ import numpy as np
7
+ from numpy.typing import DTypeLike
8
+
9
+ from mapchete_eo.platforms.sentinel2.metadata_parser import S2Metadata
10
+ from mapchete_eo.platforms.sentinel2.types import L2ABand
11
+
12
+
13
+ class BRDFModelProtocol(Protocol):
14
+ """Defines base interface to all kind of models.
15
+
16
+ Can be sensor models, sun models or directional models!
17
+ """
18
+
19
+ def calculate(self) -> ReferencedRaster: ...
20
+
21
+ @staticmethod
22
+ def from_s2metadata(
23
+ s2_metadata: S2Metadata,
24
+ band: L2ABand,
25
+ detector_id: Optional[int] = None,
26
+ processing_dtype: DTypeLike = np.float32,
27
+ ) -> BRDFModelProtocol: ...
@@ -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 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 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,181 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import List, Optional, Union
4
+
5
+ from mapchete.path import MPathLike
6
+ from pydantic import (
7
+ BaseModel,
8
+ ValidationError,
9
+ field_validator,
10
+ )
11
+
12
+ from mapchete_eo.base import BaseDriverConfig
13
+ from mapchete_eo.io.path import ProductPathGenerationMethod
14
+ from mapchete_eo.platforms.sentinel2.archives import ArchiveClsFromString, AWSL2ACOGv1
15
+ from mapchete_eo.platforms.sentinel2.brdf.config import BRDFModels
16
+ from mapchete_eo.platforms.sentinel2.types import (
17
+ CloudType,
18
+ ProductQIMaskResolution,
19
+ Resolution,
20
+ SceneClassification,
21
+ )
22
+ from mapchete_eo.search.config import StacSearchConfig
23
+ from mapchete_eo.types import TimeRange
24
+
25
+
26
+ class BRDFModelConfig(BaseModel):
27
+ model: BRDFModels = BRDFModels.HLS
28
+ bands: List[str] = ["blue", "green", "red", "nir"]
29
+ resolution: Resolution = Resolution["60m"]
30
+ footprints_cached_read: bool = False
31
+ log10_bands_scale: bool = False
32
+ per_detector_correction: bool = False
33
+
34
+ # This correction value is applied to `fv` (kvol) and `fr` (kgeo) in the final steps of the BRDF param
35
+ correction_weight: float = 1.0
36
+
37
+
38
+ class BRDFSCLClassConfig(BRDFModelConfig):
39
+ scl_classes: List[SceneClassification]
40
+
41
+ @field_validator("scl_classes", mode="before")
42
+ @classmethod
43
+ def to_scl_classes(cls, values: List[str]) -> List[SceneClassification]:
44
+ out = []
45
+ for value in values:
46
+ if isinstance(value, SceneClassification):
47
+ out.append(value)
48
+ elif isinstance(value, str):
49
+ out.append(SceneClassification[value])
50
+ else:
51
+ raise ValidationError("value must be mappable to SceneClassification")
52
+ return out
53
+
54
+
55
+ class BRDFConfig(BRDFModelConfig):
56
+ """
57
+ Main BRDF configuration with optional sub-configurations for certain SCL classes.
58
+
59
+ model: BRDF model
60
+ bands: list of band names
61
+ resolution: resolution BRDF is calculated on
62
+ footprints_cached_read: download and read footprints from cache or not
63
+ correction_weight: make correction stronger (>1) or weaker (<1)
64
+ scl_specific_configurations: list of parameters like above plus SCL classes it
65
+ should be applied to
66
+
67
+ e.g.
68
+ BRDFConfig(
69
+ model="HLS",
70
+ bands=["red", "green", "blue"],
71
+ resolution="60m",
72
+ footprints_cached_read=True,
73
+ correction_weight=0.9,
74
+ log10_bands_scale=True,
75
+ scl_specific_configurations=[
76
+ BRDFSCLClassConfig(
77
+ scl_classes=["water"],
78
+ model="HLS",
79
+ bands=["red", "green", "blue"],
80
+ resolution="60m",
81
+ footprints_cached_read=True,
82
+ correction_weight=1.3,
83
+ )
84
+ ]
85
+ )
86
+
87
+ """
88
+
89
+ scl_specific_configurations: Optional[List[BRDFSCLClassConfig]] = None
90
+
91
+
92
+ class CacheConfig(BaseModel):
93
+ path: MPathLike
94
+ product_path_generation_method: ProductPathGenerationMethod = (
95
+ ProductPathGenerationMethod.hash
96
+ )
97
+ intersection_percent: float = 100.0
98
+ assets: List[str] = []
99
+ assets_resolution: Resolution = Resolution.original
100
+ keep: bool = False
101
+ max_cloud_cover: float = 100.0
102
+ max_disk_usage: float = 90.0
103
+ brdf: Optional[BRDFConfig] = None
104
+ zoom: int = 13
105
+
106
+
107
+ class Sentinel2DriverConfig(BaseDriverConfig):
108
+ format: str = "Sentinel-2"
109
+ time: Union[TimeRange, List[TimeRange]]
110
+ archive: ArchiveClsFromString = AWSL2ACOGv1
111
+ cat_baseurl: Optional[MPathLike] = None
112
+ search_index: Optional[MPathLike] = None
113
+ max_cloud_cover: float = 100.0
114
+ stac_config: StacSearchConfig = StacSearchConfig()
115
+ first_granule_only: bool = False
116
+ utm_zone: Optional[int] = None
117
+ with_scl: bool = False
118
+ brdf: Optional[BRDFConfig] = None
119
+ cache: Optional[CacheConfig] = None
120
+
121
+
122
+ class MaskConfig(BaseModel):
123
+ # mask by footprint geometry
124
+ footprint: bool = True
125
+ # apply buffer (in meters!) to footprint
126
+ footprint_buffer_m: float = -500
127
+ # add pixel buffer to all masks
128
+ buffer: int = 0
129
+ # mask by L1C cloud types (either opaque, cirrus or all)
130
+ l1c_cloud_type: Optional[CloudType] = None
131
+ # mask using the snow/ice mask
132
+ snow_ice: bool = False
133
+ # mask using cloud probability classification
134
+ cloud_probability_threshold: int = 100
135
+ cloud_probability_resolution: ProductQIMaskResolution = ProductQIMaskResolution[
136
+ "60m"
137
+ ]
138
+ # mask using cloud probability classification
139
+ snow_probability_threshold: int = 100
140
+ snow_probability_resolution: ProductQIMaskResolution = ProductQIMaskResolution[
141
+ "60m"
142
+ ]
143
+ # mask using one or more of the SCL classes
144
+ scl_classes: Optional[List[SceneClassification]] = None
145
+ # download masks before reading
146
+ l1c_cloud_mask_cached_read: bool = False
147
+ snow_ice_mask_cached_read: bool = False
148
+ cloud_probability_cached_read: bool = False
149
+ snow_probability_cached_read: bool = False
150
+ scl_cached_read: bool = False
151
+
152
+ @field_validator("scl_classes", mode="before")
153
+ @classmethod
154
+ def to_scl_classes(cls, values: List[str]) -> List[SceneClassification]:
155
+ if values is None:
156
+ return
157
+ out = []
158
+ for value in values:
159
+ if isinstance(value, SceneClassification):
160
+ out.append(value)
161
+ elif isinstance(value, str):
162
+ out.append(SceneClassification[value])
163
+ else:
164
+ raise ValidationError("value must be mappable to SceneClassification")
165
+ return out
166
+
167
+ @staticmethod
168
+ def parse(config: Union[dict, MaskConfig]) -> MaskConfig:
169
+ """
170
+ Make sure all values are parsed correctly
171
+ """
172
+ if isinstance(config, MaskConfig):
173
+ return config
174
+
175
+ elif isinstance(config, dict):
176
+ return MaskConfig(**config)
177
+
178
+ else:
179
+ raise TypeError(
180
+ f"mask configuration should either be a dictionary or a MaskConfig object, not {config}"
181
+ )
@@ -0,0 +1,78 @@
1
+ from typing import Optional, List, Tuple
2
+
3
+ from mapchete.geometry import reproject_geometry
4
+ from mapchete.path import MPath
5
+ from mapchete.types import NodataVal
6
+ from rasterio.enums import Resampling
7
+
8
+ from mapchete_eo import base
9
+ from mapchete_eo.archives.base import Archive
10
+ from mapchete_eo.platforms.sentinel2.config import Sentinel2DriverConfig
11
+ from mapchete_eo.platforms.sentinel2.preprocessing_tasks import parse_s2_product
12
+ from mapchete_eo.search.stac_static import STACStaticCatalog
13
+ from mapchete_eo.settings import mapchete_eo_settings
14
+ from mapchete_eo.types import MergeMethod
15
+
16
+ METADATA: dict = {
17
+ "driver_name": "Sentinel-2",
18
+ "data_type": None,
19
+ "mode": "r",
20
+ "file_extensions": [],
21
+ }
22
+
23
+
24
+ class Sentinel2Cube(base.EODataCube):
25
+ # Sentinel-2 driver specific default values:
26
+ default_read_merge_method: MergeMethod = MergeMethod.average
27
+ default_read_merge_products_by: Optional[str] = "s2:datastrip_id"
28
+ default_read_nodataval: NodataVal = 0
29
+ default_read_resampling: Resampling = Resampling.bilinear
30
+
31
+
32
+ Sentinel2CubeGroup = List[Tuple[str, Sentinel2Cube]]
33
+
34
+
35
+ class InputData(base.InputData):
36
+ """
37
+ Main driver class used by mapchete.
38
+ """
39
+
40
+ # Sentinel-2 driver specific parameters:
41
+ default_preprocessing_task = staticmethod(parse_s2_product)
42
+ driver_config_model = Sentinel2DriverConfig
43
+ params: Sentinel2DriverConfig
44
+ input_tile_cls = Sentinel2Cube
45
+
46
+ def set_archive(self, base_dir: MPath):
47
+ if self.params.cat_baseurl:
48
+ self.archive = Archive(
49
+ catalog=STACStaticCatalog(
50
+ baseurl=MPath(self.params.cat_baseurl).absolute_path(
51
+ base_dir=base_dir
52
+ ),
53
+ ),
54
+ area=self.bbox(mapchete_eo_settings.default_catalog_crs),
55
+ time=self.time,
56
+ search_kwargs=dict(max_cloud_cover=self.params.max_cloud_cover),
57
+ )
58
+ elif self.params.archive:
59
+ catalog_area = reproject_geometry(
60
+ self.area,
61
+ src_crs=self.crs,
62
+ dst_crs=mapchete_eo_settings.default_catalog_crs,
63
+ )
64
+ self.archive = self.params.archive(
65
+ time=self.time,
66
+ bounds=catalog_area.bounds,
67
+ area=catalog_area,
68
+ search_kwargs=dict(
69
+ search_index=(
70
+ MPath(self.params.search_index).absolute_path(base_dir=base_dir)
71
+ if self.params.search_index
72
+ else None
73
+ ),
74
+ max_cloud_cover=self.params.max_cloud_cover,
75
+ ),
76
+ )
77
+ else:
78
+ raise ValueError("either 'archive' or 'cat_baseurl' or both is required.")