openeo-gfmap 0.1.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.
- openeo_gfmap/__init__.py +23 -0
- openeo_gfmap/backend.py +122 -0
- openeo_gfmap/features/__init__.py +17 -0
- openeo_gfmap/features/feature_extractor.py +389 -0
- openeo_gfmap/fetching/__init__.py +21 -0
- openeo_gfmap/fetching/commons.py +213 -0
- openeo_gfmap/fetching/fetching.py +98 -0
- openeo_gfmap/fetching/generic.py +165 -0
- openeo_gfmap/fetching/meteo.py +126 -0
- openeo_gfmap/fetching/s1.py +195 -0
- openeo_gfmap/fetching/s2.py +236 -0
- openeo_gfmap/inference/__init__.py +3 -0
- openeo_gfmap/inference/model_inference.py +347 -0
- openeo_gfmap/manager/__init__.py +31 -0
- openeo_gfmap/manager/job_manager.py +469 -0
- openeo_gfmap/manager/job_splitters.py +144 -0
- openeo_gfmap/metadata.py +24 -0
- openeo_gfmap/preprocessing/__init__.py +22 -0
- openeo_gfmap/preprocessing/cloudmasking.py +268 -0
- openeo_gfmap/preprocessing/compositing.py +74 -0
- openeo_gfmap/preprocessing/interpolation.py +12 -0
- openeo_gfmap/preprocessing/sar.py +64 -0
- openeo_gfmap/preprocessing/scaling.py +65 -0
- openeo_gfmap/preprocessing/udf_cldmask.py +36 -0
- openeo_gfmap/preprocessing/udf_rank.py +37 -0
- openeo_gfmap/preprocessing/udf_score.py +103 -0
- openeo_gfmap/spatial.py +53 -0
- openeo_gfmap/stac/__init__.py +2 -0
- openeo_gfmap/stac/constants.py +51 -0
- openeo_gfmap/temporal.py +22 -0
- openeo_gfmap/utils/__init__.py +23 -0
- openeo_gfmap/utils/build_df.py +48 -0
- openeo_gfmap/utils/catalogue.py +248 -0
- openeo_gfmap/utils/intervals.py +64 -0
- openeo_gfmap/utils/netcdf.py +25 -0
- openeo_gfmap/utils/tile_processing.py +64 -0
- openeo_gfmap-0.1.0.dist-info/METADATA +57 -0
- openeo_gfmap-0.1.0.dist-info/RECORD +40 -0
- openeo_gfmap-0.1.0.dist-info/WHEEL +4 -0
- openeo_gfmap-0.1.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,195 @@
|
|
1
|
+
""" Collection fetching of S1 features, supporting different backends.
|
2
|
+
"""
|
3
|
+
|
4
|
+
from functools import partial
|
5
|
+
from typing import Callable
|
6
|
+
|
7
|
+
import openeo
|
8
|
+
from geojson import GeoJSON
|
9
|
+
|
10
|
+
from openeo_gfmap.backend import Backend, BackendContext
|
11
|
+
from openeo_gfmap.spatial import SpatialContext
|
12
|
+
from openeo_gfmap.temporal import TemporalContext
|
13
|
+
|
14
|
+
from .commons import (
|
15
|
+
_load_collection,
|
16
|
+
convert_band_names,
|
17
|
+
rename_bands,
|
18
|
+
resample_reproject,
|
19
|
+
)
|
20
|
+
from .fetching import CollectionFetcher, FetchType
|
21
|
+
|
22
|
+
BASE_SENTINEL1_GRD_MAPPING = {
|
23
|
+
"VH": "S1-SIGMA0-VH",
|
24
|
+
"HH": "S1-SIGMA0-HH",
|
25
|
+
"HV": "S1-SIGMA0-HV",
|
26
|
+
"VV": "S1-SIGMA0-VV",
|
27
|
+
}
|
28
|
+
|
29
|
+
|
30
|
+
def _get_s1_grd_default_fetcher(
|
31
|
+
collection_name: str, fetch_type: FetchType
|
32
|
+
) -> Callable:
|
33
|
+
"""Return a default fetcher for Sentinel-1 GRD data.
|
34
|
+
|
35
|
+
Parameters
|
36
|
+
----------
|
37
|
+
collection_name : str
|
38
|
+
The name of the sentinel1 collection to fetch as named in the backend.
|
39
|
+
fetch_type : FetchType
|
40
|
+
The type of fetching: TILE, POINT and POLYGON.
|
41
|
+
"""
|
42
|
+
|
43
|
+
def s1_grd_fetch_default(
|
44
|
+
connection: openeo.Connection,
|
45
|
+
spatial_extent: SpatialContext,
|
46
|
+
temporal_extent: TemporalContext,
|
47
|
+
bands: list,
|
48
|
+
**params,
|
49
|
+
) -> openeo.DataCube:
|
50
|
+
"""Default collection fetcher for Sentinel-1 GRD collections. The
|
51
|
+
collection values are expected to be expressed in power values.
|
52
|
+
Parameters
|
53
|
+
----------
|
54
|
+
connection: openeo.Connection
|
55
|
+
Connection to a general backend.
|
56
|
+
spatial_extent: SpatialContext
|
57
|
+
Either a GeoJSON collection or a bounding box of locations.
|
58
|
+
Performs spatial filtering if the spatial context is a GeoJSON
|
59
|
+
collection, as it implies sparse data.
|
60
|
+
temporal_extent: TemporalContexct
|
61
|
+
A time range, defined by a start and end date.
|
62
|
+
bands: list
|
63
|
+
The name of the bands to load from that collection
|
64
|
+
Returns
|
65
|
+
------
|
66
|
+
openeo.DataCube: a datacube containing the collection raw products.
|
67
|
+
"""
|
68
|
+
bands = convert_band_names(bands, BASE_SENTINEL1_GRD_MAPPING)
|
69
|
+
|
70
|
+
load_collection_parameters = params.get("load_collection", {})
|
71
|
+
|
72
|
+
cube = _load_collection(
|
73
|
+
connection,
|
74
|
+
bands,
|
75
|
+
collection_name,
|
76
|
+
spatial_extent,
|
77
|
+
temporal_extent,
|
78
|
+
fetch_type,
|
79
|
+
**load_collection_parameters,
|
80
|
+
)
|
81
|
+
|
82
|
+
if fetch_type is not FetchType.POINT and isinstance(spatial_extent, GeoJSON):
|
83
|
+
cube = cube.filter_spatial(spatial_extent)
|
84
|
+
|
85
|
+
return cube
|
86
|
+
|
87
|
+
return s1_grd_fetch_default
|
88
|
+
|
89
|
+
|
90
|
+
def _get_s1_grd_default_processor(
|
91
|
+
collection_name: str, fetch_type: FetchType, backend: Backend
|
92
|
+
) -> Callable:
|
93
|
+
"""Builds the preprocessing function from the collection name as it is stored
|
94
|
+
in the target backend.
|
95
|
+
"""
|
96
|
+
|
97
|
+
def s1_grd_default_processor(cube: openeo.DataCube, **params):
|
98
|
+
"""Default collection preprocessing method.
|
99
|
+
This method performs:
|
100
|
+
|
101
|
+
* Compute the backscatter of all the S1 products. By default, the
|
102
|
+
"sigma0-ellipsoid" method is used with "COPERNICUS_30" DEM, but those
|
103
|
+
can be changed by specifying "coefficient" and "elevation_model" in
|
104
|
+
params.
|
105
|
+
* Resampling to 10m resolution.
|
106
|
+
* Reprojection if a "target_crs" key is specified in `params`.
|
107
|
+
* Performs value rescaling to uint16.
|
108
|
+
"""
|
109
|
+
elevation_model = params.get("elevation_model", "COPERNICUS_30")
|
110
|
+
coefficient = params.get("coefficient", "sigma0-ellipsoid")
|
111
|
+
|
112
|
+
cube = cube.sar_backscatter(
|
113
|
+
elevation_model=elevation_model,
|
114
|
+
coefficient=coefficient,
|
115
|
+
local_incidence_angle=False,
|
116
|
+
)
|
117
|
+
|
118
|
+
# Reproject collection data to target CRS and resolution, if specified so.
|
119
|
+
# Can be disabled by setting target_resolution=None in the parameters
|
120
|
+
if params.get("target_resolution", True) is not None:
|
121
|
+
cube = resample_reproject(
|
122
|
+
cube,
|
123
|
+
params.get("target_resolution", 10.0),
|
124
|
+
params.get("target_crs", None),
|
125
|
+
method=params.get("resampling_method", "near"),
|
126
|
+
)
|
127
|
+
elif params.get("target_crs") is not None:
|
128
|
+
raise ValueError(
|
129
|
+
"In fetching parameters: `target_crs` specified but not `target_resolution`, which is required to perform reprojection."
|
130
|
+
)
|
131
|
+
|
132
|
+
# Harmonizing the collection band names to the default GFMAP band names
|
133
|
+
cube = rename_bands(cube, BASE_SENTINEL1_GRD_MAPPING)
|
134
|
+
|
135
|
+
return cube
|
136
|
+
|
137
|
+
return s1_grd_default_processor
|
138
|
+
|
139
|
+
|
140
|
+
SENTINEL1_GRD_BACKEND_MAP = {
|
141
|
+
Backend.TERRASCOPE: {
|
142
|
+
"default": partial(
|
143
|
+
_get_s1_grd_default_fetcher, collection_name="SENTINEL1_GRD"
|
144
|
+
),
|
145
|
+
"preprocessor": partial(
|
146
|
+
_get_s1_grd_default_processor,
|
147
|
+
collection_name="SENTINEL1_GRD",
|
148
|
+
backend=Backend.TERRASCOPE,
|
149
|
+
),
|
150
|
+
},
|
151
|
+
Backend.CDSE: {
|
152
|
+
"default": partial(
|
153
|
+
_get_s1_grd_default_fetcher, collection_name="SENTINEL1_GRD"
|
154
|
+
),
|
155
|
+
"preprocessor": partial(
|
156
|
+
_get_s1_grd_default_processor,
|
157
|
+
collection_name="SENTINEL1_GRD",
|
158
|
+
backend=Backend.CDSE,
|
159
|
+
),
|
160
|
+
},
|
161
|
+
Backend.CDSE_STAGING: {
|
162
|
+
"default": partial(
|
163
|
+
_get_s1_grd_default_fetcher, collection_name="SENTINEL1_GRD"
|
164
|
+
),
|
165
|
+
"preprocessor": partial(
|
166
|
+
_get_s1_grd_default_processor,
|
167
|
+
collection_name="SENTINEL1_GRD",
|
168
|
+
backend=Backend.CDSE_STAGING,
|
169
|
+
),
|
170
|
+
},
|
171
|
+
Backend.FED: {
|
172
|
+
"default": partial(
|
173
|
+
_get_s1_grd_default_fetcher, collection_name="SENTINEL1_GRD"
|
174
|
+
),
|
175
|
+
"preprocessor": partial(
|
176
|
+
_get_s1_grd_default_processor,
|
177
|
+
collection_name="SENTINEL1_GRD",
|
178
|
+
backend=Backend.FED,
|
179
|
+
),
|
180
|
+
},
|
181
|
+
}
|
182
|
+
|
183
|
+
|
184
|
+
def build_sentinel1_grd_extractor(
|
185
|
+
backend_context: BackendContext, bands: list, fetch_type: FetchType, **params
|
186
|
+
) -> CollectionFetcher:
|
187
|
+
"""Creates a S1 GRD collection extractor for the given backend."""
|
188
|
+
backend_functions = SENTINEL1_GRD_BACKEND_MAP.get(backend_context.backend)
|
189
|
+
|
190
|
+
fetcher, preprocessor = (
|
191
|
+
backend_functions["default"](fetch_type=fetch_type),
|
192
|
+
backend_functions["preprocessor"](fetch_type=fetch_type),
|
193
|
+
)
|
194
|
+
|
195
|
+
return CollectionFetcher(backend_context, bands, fetcher, preprocessor, **params)
|
@@ -0,0 +1,236 @@
|
|
1
|
+
""" Extraction of S2 features, supporting different backends.
|
2
|
+
"""
|
3
|
+
|
4
|
+
from functools import partial
|
5
|
+
from typing import Callable
|
6
|
+
|
7
|
+
import openeo
|
8
|
+
from geojson import GeoJSON
|
9
|
+
|
10
|
+
from openeo_gfmap.backend import Backend, BackendContext
|
11
|
+
from openeo_gfmap.metadata import FakeMetadata
|
12
|
+
from openeo_gfmap.spatial import BoundingBoxExtent, SpatialContext
|
13
|
+
from openeo_gfmap.temporal import TemporalContext
|
14
|
+
|
15
|
+
from .commons import (
|
16
|
+
_load_collection,
|
17
|
+
convert_band_names,
|
18
|
+
rename_bands,
|
19
|
+
resample_reproject,
|
20
|
+
)
|
21
|
+
from .fetching import CollectionFetcher, FetchType
|
22
|
+
|
23
|
+
BASE_SENTINEL2_L2A_MAPPING = {
|
24
|
+
"B01": "S2-L2A-B01",
|
25
|
+
"B02": "S2-L2A-B02",
|
26
|
+
"B03": "S2-L2A-B03",
|
27
|
+
"B04": "S2-L2A-B04",
|
28
|
+
"B05": "S2-L2A-B05",
|
29
|
+
"B06": "S2-L2A-B06",
|
30
|
+
"B07": "S2-L2A-B07",
|
31
|
+
"B08": "S2-L2A-B08",
|
32
|
+
"B8A": "S2-L2A-B8A",
|
33
|
+
"B09": "S2-L2A-B09",
|
34
|
+
"B11": "S2-L2A-B11",
|
35
|
+
"B12": "S2-L2A-B12",
|
36
|
+
"AOT": "S2-L2A-AOT",
|
37
|
+
"SCL": "S2-L2A-SCL",
|
38
|
+
"SNW": "S2-L2A-SNW",
|
39
|
+
}
|
40
|
+
|
41
|
+
ELEMENT84_SENTINEL2_L2A_MAPPING = {
|
42
|
+
"coastal": "S2-L2A-B01",
|
43
|
+
"blue": "S2-L2A-B02",
|
44
|
+
"green": "S2-L2A-B03",
|
45
|
+
"red": "S2-L2A-B04",
|
46
|
+
"rededge1": "S2-L2A-B05",
|
47
|
+
"rededge2": "S2-L2A-B06",
|
48
|
+
"rededge3": "S2-L2A-B07",
|
49
|
+
"nir": "S2-L2A-B08",
|
50
|
+
"nir08": "S2-L2A-B8A",
|
51
|
+
"nir09": "S2-L2A-B09",
|
52
|
+
"cirrus": "S2-L2A-B10",
|
53
|
+
"swir16": "S2-L2A-B11",
|
54
|
+
"swir22": "S2-L2A-B12",
|
55
|
+
"scl": "S2-L2A-SCL",
|
56
|
+
"aot": "S2-L2A-AOT",
|
57
|
+
}
|
58
|
+
|
59
|
+
|
60
|
+
def _get_s2_l2a_default_fetcher(
|
61
|
+
collection_name: str, fetch_type: FetchType
|
62
|
+
) -> Callable:
|
63
|
+
"""Builds the fetch function from the collection name as it stored in the
|
64
|
+
target backend.
|
65
|
+
|
66
|
+
Parameters
|
67
|
+
----------
|
68
|
+
collection_name: str
|
69
|
+
The name of the sentinel2 collection as named in the backend
|
70
|
+
point_based: bool
|
71
|
+
The type of fetching: TILE, POINT and POLYGON.
|
72
|
+
"""
|
73
|
+
|
74
|
+
def s2_l2a_fetch_default(
|
75
|
+
connection: openeo.Connection,
|
76
|
+
spatial_extent: SpatialContext,
|
77
|
+
temporal_extent: TemporalContext,
|
78
|
+
bands: list,
|
79
|
+
**params,
|
80
|
+
) -> openeo.DataCube:
|
81
|
+
"""Default collection fetcher for Sentinel_L2A collections.
|
82
|
+
Parameters
|
83
|
+
----------
|
84
|
+
connection: openeo.Connection
|
85
|
+
Connection to a general backend.
|
86
|
+
spatial_extent: SpatialContext
|
87
|
+
Either a GeoJSON collection or a bounding box of locations.
|
88
|
+
Performs spatial filtering if the spatial context is a GeoJSON
|
89
|
+
collection, as it implies sparse data.
|
90
|
+
temporal_extent: TemporalContexct
|
91
|
+
A time range, defined by a start and end date.
|
92
|
+
bands: list
|
93
|
+
The name of the bands to load from that collection
|
94
|
+
Returns
|
95
|
+
-------
|
96
|
+
openeo.DataCube: a datacube containing the collection raw products.
|
97
|
+
"""
|
98
|
+
# Rename the bands to the backend collection names
|
99
|
+
bands = convert_band_names(bands, BASE_SENTINEL2_L2A_MAPPING)
|
100
|
+
|
101
|
+
cube = _load_collection(
|
102
|
+
connection,
|
103
|
+
bands,
|
104
|
+
collection_name,
|
105
|
+
spatial_extent,
|
106
|
+
temporal_extent,
|
107
|
+
fetch_type,
|
108
|
+
**params,
|
109
|
+
)
|
110
|
+
|
111
|
+
return cube
|
112
|
+
|
113
|
+
return s2_l2a_fetch_default
|
114
|
+
|
115
|
+
|
116
|
+
def _get_s2_l2a_element84_fetcher(
|
117
|
+
collection_name: str, fetch_type: FetchType
|
118
|
+
) -> Callable:
|
119
|
+
"""Fetches the collections from the Sentinel-2 Cloud-Optimized GeoTIFFs
|
120
|
+
bucket provided by Amazon and managed by Element84.
|
121
|
+
"""
|
122
|
+
|
123
|
+
def s2_l2a_element84_fetcher(
|
124
|
+
connection: openeo.Connection,
|
125
|
+
spatial_extent: SpatialContext,
|
126
|
+
temporal_extent: TemporalContext,
|
127
|
+
bands: list,
|
128
|
+
**params,
|
129
|
+
) -> openeo.DataCube:
|
130
|
+
"""Collection fetcher on the element84 collection."""
|
131
|
+
bands = convert_band_names(bands, ELEMENT84_SENTINEL2_L2A_MAPPING)
|
132
|
+
|
133
|
+
if isinstance(spatial_extent, BoundingBoxExtent):
|
134
|
+
spatial_extent = dict(spatial_extent)
|
135
|
+
elif isinstance(spatial_extent, GeoJSON):
|
136
|
+
assert (
|
137
|
+
spatial_extent.get("crs", None) is not None
|
138
|
+
), "CRS not defined within GeoJSON collection."
|
139
|
+
spatial_extent = dict(spatial_extent)
|
140
|
+
|
141
|
+
cube = connection.load_stac(
|
142
|
+
"https://earth-search.aws.element84.com/v1/collections/sentinel-2-l2a",
|
143
|
+
spatial_extent,
|
144
|
+
temporal_extent,
|
145
|
+
bands,
|
146
|
+
)
|
147
|
+
|
148
|
+
cube.metadata = FakeMetadata(band_names=bands)
|
149
|
+
|
150
|
+
# Apply if the collection is a GeoJSON Feature collection
|
151
|
+
if isinstance(spatial_extent, GeoJSON):
|
152
|
+
cube = cube.filter_spatial(spatial_extent)
|
153
|
+
|
154
|
+
return cube
|
155
|
+
|
156
|
+
return s2_l2a_element84_fetcher
|
157
|
+
|
158
|
+
|
159
|
+
def _get_s2_l2a_default_processor(
|
160
|
+
collection_name: str, fetch_type: FetchType
|
161
|
+
) -> Callable:
|
162
|
+
"""Builds the preprocessing function from the collection name as it stored
|
163
|
+
in the target backend.
|
164
|
+
"""
|
165
|
+
|
166
|
+
def s2_l2a_default_processor(cube: openeo.DataCube, **params):
|
167
|
+
"""Default collection preprocessing method.
|
168
|
+
This method performs reprojection if specified, upsampling of bands
|
169
|
+
at 10m resolution as well as band reprojection. Finally, it converts
|
170
|
+
the type of the cube values to uint16
|
171
|
+
"""
|
172
|
+
# Reproject collection data to target CRS and resolution, if specified so.
|
173
|
+
# Can be disabled by setting target_resolution=None in the parameters
|
174
|
+
if params.get("target_resolution", True) is not None:
|
175
|
+
cube = resample_reproject(
|
176
|
+
cube,
|
177
|
+
params.get("target_resolution", 10.0),
|
178
|
+
params.get("target_crs", None),
|
179
|
+
method=params.get("resampling_method", "near"),
|
180
|
+
)
|
181
|
+
elif params.get("target_crs") is not None:
|
182
|
+
raise ValueError(
|
183
|
+
"In fetching parameters: `target_crs` specified but not `target_resolution`, which is required to perform reprojection."
|
184
|
+
)
|
185
|
+
|
186
|
+
# Harmonizing the collection band names to the default GFMAP band names
|
187
|
+
cube = rename_bands(cube, BASE_SENTINEL2_L2A_MAPPING)
|
188
|
+
|
189
|
+
# Change the data type to uint16 for optimization purposes
|
190
|
+
cube = cube.linear_scale_range(0, 65534, 0, 65534)
|
191
|
+
|
192
|
+
return cube
|
193
|
+
|
194
|
+
return s2_l2a_default_processor
|
195
|
+
|
196
|
+
|
197
|
+
SENTINEL2_L2A_BACKEND_MAP = {
|
198
|
+
Backend.TERRASCOPE: {
|
199
|
+
"fetch": partial(_get_s2_l2a_default_fetcher, collection_name="SENTINEL2_L2A"),
|
200
|
+
"preprocessor": partial(
|
201
|
+
_get_s2_l2a_default_processor, collection_name="SENTINEL2_L2A"
|
202
|
+
),
|
203
|
+
},
|
204
|
+
Backend.CDSE: {
|
205
|
+
"fetch": partial(_get_s2_l2a_default_fetcher, collection_name="SENTINEL2_L2A"),
|
206
|
+
"preprocessor": partial(
|
207
|
+
_get_s2_l2a_default_processor, collection_name="SENTINEL2_L2A"
|
208
|
+
),
|
209
|
+
},
|
210
|
+
Backend.CDSE_STAGING: {
|
211
|
+
"fetch": partial(_get_s2_l2a_default_fetcher, collection_name="SENTINEL2_L2A"),
|
212
|
+
"preprocessor": partial(
|
213
|
+
_get_s2_l2a_default_processor, collection_name="SENTINEL2_L2A"
|
214
|
+
),
|
215
|
+
},
|
216
|
+
Backend.FED: {
|
217
|
+
"fetch": partial(_get_s2_l2a_default_fetcher, collection_name="SENTINEL2_L2A"),
|
218
|
+
"preprocessor": partial(
|
219
|
+
_get_s2_l2a_default_processor, collection_name="SENTINEL2_L2A"
|
220
|
+
),
|
221
|
+
},
|
222
|
+
}
|
223
|
+
|
224
|
+
|
225
|
+
def build_sentinel2_l2a_extractor(
|
226
|
+
backend_context: BackendContext, bands: list, fetch_type: FetchType, **params
|
227
|
+
) -> CollectionFetcher:
|
228
|
+
"""Creates a S2 L2A extractor adapted to the given backend."""
|
229
|
+
backend_functions = SENTINEL2_L2A_BACKEND_MAP.get(backend_context.backend)
|
230
|
+
|
231
|
+
fetcher, preprocessor = (
|
232
|
+
backend_functions["fetch"](fetch_type=fetch_type),
|
233
|
+
backend_functions["preprocessor"](fetch_type=fetch_type),
|
234
|
+
)
|
235
|
+
|
236
|
+
return CollectionFetcher(backend_context, bands, fetcher, preprocessor, **params)
|