Skip to content

Commit d6c03fe

Browse files
committed
Started work on s3 pre-processing, some tests
1 parent 3917c1a commit d6c03fe

File tree

2 files changed

+127
-1
lines changed

2 files changed

+127
-1
lines changed

efast/openeo/preprocessing/s3.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
from datetime import datetime
2+
import openeo
3+
from openeo import processes, UDF
4+
5+
from efast.constants import S3L2SYNClassificationAerosolFlags, S3L2SYNCloudFlags
6+
from efast.openeo.preprocessing.general import _extract_bit_mask
7+
8+
from . import extract_mask
9+
10+
DATE_FORMAT = "%Y-%m-%d"
11+
12+
13+
def extract_clear_land_mask(s3_cube: openeo.DataCube):
14+
CLOUD_FLAGS_COMBINED = (
15+
S3L2SYNCloudFlags.CLOUD
16+
| S3L2SYNCloudFlags.CLOUD_AMBIGUOUS
17+
| S3L2SYNCloudFlags.CLOUD_MARGIN
18+
)
19+
cloud_mask = (s3_cube.band("CLOUD_flags") & CLOUD_FLAGS_COMBINED.value) != 0
20+
land_mask = (
21+
s3_cube.band("SYN_flags") & S3L2SYNClassificationAerosolFlags.SYN_land.value
22+
) != 0
23+
return ((cloud_mask == 0) & land_mask) != 0
24+
25+
26+
# TODO add land mask, broken
27+
# TODO debug as a batch job, to access logs
28+
def _extract_clear_land_mask(s3_cube: openeo.DataCube) -> openeo.DataCube:
29+
mask = _extract_bit_mask(
30+
s3_cube,
31+
{
32+
"CLOUD_flags": (
33+
S3L2SYNCloudFlags.CLOUD
34+
| S3L2SYNCloudFlags.CLOUD_AMBIGUOUS
35+
| S3L2SYNCloudFlags.CLOUD_MARGIN
36+
).value
37+
},
38+
invert=True,
39+
) # | extract_bit_mask(
40+
# s3_cube,
41+
# {
42+
# "SYN_flags": S3L2SYNClassificationAerosolFlags.SYN_land.value,
43+
# },
44+
# )
45+
return mask
46+
47+
48+
def composite(cube):
49+
# What we want: sliding window computation
50+
# distance_to_cloud_score = compute_distance_to_cloud_score(distance_to_cloud) # add normalization
51+
# merged = distance_to_cloud_score.merge_cubes(cube)
52+
# merged.apply_neighbourhood(process=produce_weighted_composite)
53+
pass
54+
55+
56+
# TODO rename D to something more useful
57+
def compute_distance_to_cloud_score(
58+
distance_to_cloud: processes.ProcessBuilder, D: float
59+
) -> processes.ProcessBuilder:
60+
score = (distance_to_cloud - 1) / D
61+
return processes.clip(score, 0.0, 1.0)
62+
63+
64+
def compute_time_weighted_cube(
65+
unweighted_cube: processes.ProcessBuilder, target_date: datetime
66+
) -> processes.ProcessBuilder:
67+
udf_compute_temporal_score = UDF.from_file(
68+
"efast/openeo/udf/compute_temporal_score",
69+
context={"target_date": target_date.strftime(DATE_FORMAT)},
70+
)
71+
return unweighted_cube.apply_dimension(
72+
process=udf_compute_temporal_score, dimension="t"
73+
)
74+
75+
76+
def produce_weighted_composite(
77+
s3_cube: processes.ProcessBuilder,
78+
distance_to_cloud_score: processes.ProcessBuilder,
79+
target_date: datetime,
80+
) -> processes.ProcessBuilder:
81+
"""
82+
:param distance_to_cloud_score:
83+
"""
84+
# input1: all relevant Sentinel-3 images to produce a single output composite
85+
# input2: distance to cloud scores for all Sentinel-3 observations to avoid
86+
# recomputing them for each composite
87+
88+
pass

tests/test_preprocessing_openeo.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@
1818

1919
from openeo.udf import execute_local_udf
2020

21+
from efast.constants import S3L2SYNClassificationAerosolFlags
2122
from efast.openeo import preprocessing
2223
# TODO this should live somewhere else
2324
from efast.openeo.preprocessing import connect
2425
from efast import s2_processing, s3_processing
26+
from efast.openeo.preprocessing.s3 import extract_clear_land_mask
2527

2628

2729
TEST_DATA_ROOT = Path(__file__).parent.parent / "test_data"
@@ -40,8 +42,9 @@
4042

4143
SKIP_LOCAL = True
4244
DOWNLOAD_INTERMEDIATE_RESULTS = True
43-
SMALL_AREA = True
45+
SMALL_AREA = False
4446

47+
VISUAL_OUTPUT_PATH = Path(__file__).parent.parent / "visual_test_results"
4548

4649
@contextmanager
4750
def create_temp_dir_and_copy_files(
@@ -316,3 +319,38 @@ def apply_datacube(cube: XarrayDataCube, context: dict) -> XarrayDataCube:
316319
#return XarrayDataCube(xr.DataArray(array, dims=["t", "x", "y", "bands"]))
317320
return XarrayDataCube(xr.DataArray(array, dims=["bands", "x", "y"]))
318321
""")
322+
323+
def test_extract_clear_land_mask_s3():
324+
out_path = VISUAL_OUTPUT_PATH / "extract_clear_land_mask"
325+
out_path.mkdir(exist_ok=True, parents=True)
326+
conn = connect()
327+
conn = conn.authenticate_oidc()
328+
bounds = {
329+
"west": 399960.0,
330+
"south": 1590240.0,
331+
"east": 509760.0,
332+
"north": 1700040.0,
333+
"crs": 32628,
334+
}
335+
if SMALL_AREA:
336+
dist = 3600
337+
bounds["east"] = bounds["west"] + dist
338+
bounds["north"] = bounds["south"] + dist
339+
340+
bands = [
341+
"Syn_Oa17_reflectance",
342+
"CLOUD_flags",
343+
"SYN_flags",
344+
]
345+
test_area = preprocessing.TestArea(
346+
bbox=bounds, s3_bands=bands, temporal_extent=(TEST_DATE_DASH, TEST_DATE_DASH)
347+
)
348+
cube = test_area.get_s3_cube(conn)
349+
350+
351+
print("Downloading input")
352+
#cube.download(out_path / "input.nc")
353+
mask = extract_clear_land_mask(cube)
354+
print("Downloading mask")
355+
mask.download(out_path / "mask.nc")
356+
print("Done")

0 commit comments

Comments
 (0)