Skip to content

Commit 1f4ba7b

Browse files
committed
Python binning with compatible interface
1 parent 35f941e commit 1f4ba7b

File tree

1 file changed

+42
-14
lines changed

1 file changed

+42
-14
lines changed

efast/binning.py

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,66 @@
11
from collections import namedtuple
22
from pathlib import Path
3-
from typing import Iterable, NamedTuple, Self, TypeVar
3+
from typing import Iterable, Self
44
import warnings
5-
from numpy.lib.stride_tricks import as_strided
65
from numpy.typing import NDArray
76
import xarray as xr
87
import re
98
import numpy as np
109
from scipy import stats
1110
import shapely
12-
from scipy import ndimage
1311
import cv2
12+
import rasterio
13+
from rasterio.transform import Affine
1414

15+
from efast.constants import S3L2SYNClassificationAerosolFlags, S3L2SYNCloudFlags
16+
17+
18+
FILL_VALUE = -10000 # TODO move
19+
20+
CLOUD_FLAGS_COMBINED = (
21+
S3L2SYNCloudFlags.CLOUD
22+
| S3L2SYNCloudFlags.CLOUD_AMBIGUOUS
23+
| S3L2SYNCloudFlags.CLOUD_MARGIN
24+
)
1525

1626
def binning_s3_py(
1727
download_dir,
1828
binning_dir,
1929
footprint,
2030
s3_bands=["SDR_Oa04", "SDR_Oa06", "SDR_Oa08", "SDR_Oa17"],
21-
max_zenith_angle=30,
22-
crs="EPSG:4326",
31+
**kwargs
2332
):
2433
"""
2534
TODO: "binning" might be a misnomer, as the function does more than just binning
2635
"""
36+
band_names = [*s3_bands, "CLOUD_flags", "SYN_flags"]
37+
sen3_paths = list(download_dir.glob("*.SEN3"))
38+
bbox = BBox.from_wkt(footprint)
39+
grid = create_geogrid(bbox, num_rows=66792)
40+
pixel_size = grid.lat[1] - grid.lat[0]
41+
transform = Affine.translation(grid.lon[0], grid.lat[-1]) * Affine.scale(pixel_size, pixel_size)
42+
43+
for i, sen3_path in enumerate(sen3_paths):
44+
output_path = binning_dir / (sen3_path.stem + ".tif")
45+
46+
prod = SynProduct(sen3_path, band_names=band_names)
47+
ds = prod.read_bands()
48+
49+
cloud_no_land_mask = ((ds["CLOUD_flags"] & CLOUD_FLAGS_COMBINED.value) != 0) & (ds["SYN_flags"] & S3L2SYNClassificationAerosolFlags.SYN_land.value > 0)
50+
51+
for reflectance_band in s3_bands:
52+
band_values = ds[reflectance_band].data
53+
band_values[cloud_no_land_mask] = -10000
54+
ds[reflectance_band] = (["lat", "lon"], band_values)
55+
56+
57+
binned = bin_to_grid_numpy(ds, s3_bands, grid, super_sampling=2)
58+
59+
binned[binned < 0] = np.nan
2760

28-
# read required bands of sentinel-3 product
29-
# reproject to EPSG 4326 (~300m grid), this step is likely unnecessary
30-
# bin to SEA_grid
31-
# reproject to EPSG 4326 (66xxx slices)
3261

33-
pass
62+
with rasterio.open(output_path, "w", driver="GTiff", height=binned.shape[1], width=binned.shape[2], count=len(s3_bands), dtype=binned.dtype, crs="+proj=latlong", transform=transform, nodata=np.nan) as ds:
63+
ds.write(binned[:, ::-1, :])
3464

3565

3666
def get_reflectance_filename(index: int):
@@ -220,15 +250,14 @@ def bin_to_grid_numpy(ds: xr.Dataset, bands: Iterable[str], grid: Grid,*, super_
220250
bin_idx[(bin_idx_row < 0) | (bin_idx_row > height) | (bin_idx_col < 0) | (bin_idx_col > width)] = -1
221251

222252
counts, _ = np.histogram(bin_idx, width * height, range=(0, width*height))
223-
253+
224254
binned = []
225255
means = None
226256
sampled_data = None if super_sampling == 1 else np.zeros((lat2d.shape[0] * super_sampling, lat2d.shape[1] * super_sampling), dtype=np.int16)
227257

228-
FILL_VALUE = -10000 # TODO move
229258
for band in bands:
230259
data = ds[band].data
231-
data[data == FILL_VALUE] = 0
260+
#data[data == FILL_VALUE] = 0
232261
if super_sampling != 1:
233262
super_sample(data, super_sampling, out=sampled_data)
234263
else:
@@ -253,7 +282,6 @@ def create_geogrid(bbox: BBox, num_rows: int = 66792):
253282
# the last bin is between lon[-1] and lon[0] (antimeridian)
254283
lon = np.linspace(0, 360, num=num_rows * 2 + 1, endpoint=True) - 180
255284
lon = lon[1:]
256-
# TODO return type with names to not confuse lat/lon
257285

258286
# one lat bound before first bound that is larger than the bounding box min
259287
# TODO I can calculate lat_idx_min (and the others) directly

0 commit comments

Comments
 (0)