Skip to content

Commit 2bdad1b

Browse files
committed
fix: restore test coverage and fix bug in categorical downsampling
fixes a drop in test coverage on this branch, and also fixes a pre-existing bug in how categorical downsampling worked, due to that code path not being tested properly.
1 parent 7088e4f commit 2bdad1b

2 files changed

Lines changed: 36 additions & 6 deletions

File tree

src/eopf_geozarr/s2_optimization/s2_multiscale.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -855,8 +855,8 @@ def create_downsampled_resolution_group(source_dataset: xr.Dataset, factor: int)
855855

856856
def subsample_2(a: xr.DataArray, axis: tuple[int, ...] | None = None) -> xr.DataArray:
857857
if axis is None:
858-
return a[((slice(None, None, 2),) * a.ndim)]
859-
indexer = [slice(None, None, 2) if i in axis else slice(None) for i in range(a.ndim)]
858+
return a[((0,) * a.ndim)]
859+
indexer = [0 if i in axis else slice(None) for i in range(a.ndim)]
860860
return a[tuple(indexer)]
861861

862862

tests/test_s2_multiscale.py

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from structlog.testing import capture_logs
1717

1818
from eopf_geozarr.s2_optimization.s2_multiscale import (
19+
_coarsen_variable,
1920
calculate_aligned_chunk_size,
2021
calculate_simple_shard_dimensions,
2122
create_downsampled_resolution_group,
@@ -219,6 +220,31 @@ def test_create_multiscale_from_datatree(
219220
assert [k for k in o_keys if expected_structure_flat[k] != observed_structure_flat[k]] == []
220221

221222

223+
# ---------------------------------------------------------------------------
224+
# _coarsen_variable
225+
# ---------------------------------------------------------------------------
226+
227+
228+
def test_coarsen_variable_classification() -> None:
229+
"""Classification variables should be downsampled via subsample."""
230+
data = np.arange(16, dtype="uint8").reshape(4, 4)
231+
var = xr.DataArray(data, dims=["y", "x"], coords={"y": np.arange(4.0), "x": np.arange(4.0)})
232+
result = _coarsen_variable("scl", var, factor=2)
233+
assert result.shape == (2, 2)
234+
assert result.dtype == np.uint8
235+
# subsample picks top-left of each 2x2 block
236+
np.testing.assert_array_equal(result.values, data[::2, ::2])
237+
238+
239+
def test_coarsen_variable_quality_mask() -> None:
240+
"""Quality mask variables should be downsampled via max."""
241+
data = np.array([[0, 1], [2, 3]], dtype="uint8")
242+
var = xr.DataArray(data, dims=["y", "x"], coords={"y": np.arange(2.0), "x": np.arange(2.0)})
243+
result = _coarsen_variable("quality_cirrus", var, factor=2)
244+
assert result.shape == (1, 1)
245+
assert result.values.item() == 3
246+
247+
222248
# ---------------------------------------------------------------------------
223249
# inject_missing_bands
224250
# ---------------------------------------------------------------------------
@@ -274,12 +300,16 @@ def test_inject_missing_bands_respects_bands_filter() -> None:
274300
def test_inject_missing_bands_skips_existing() -> None:
275301
"""Bands already present in the dataset should not be overwritten."""
276302
dt = _make_reflectance_datatree()
277-
r10m_ds = dt["measurements/reflectance/r10m"].to_dataset()
303+
r20m_ds = dt["measurements/reflectance/r20m"].to_dataset()
278304

279-
result = inject_missing_bands(r10m_ds, dt, target_resolution=10)
305+
# Pre-populate b08 with a sentinel value so we can verify it is NOT replaced.
306+
sentinel = np.full((60, 60), 999, dtype="uint16")
307+
r20m_ds["b08"] = (["y", "x"], sentinel)
308+
309+
result = inject_missing_bands(r20m_ds, dt, target_resolution=20, bands={"b08"})
280310

281-
# No bands have a native resolution finer than 10m, so nothing changes
282-
assert set(result.data_vars) == set(r10m_ds.data_vars)
311+
# b08 was already present — inject_missing_bands must leave it untouched.
312+
np.testing.assert_array_equal(result["b08"].values, sentinel)
283313

284314

285315
def test_inject_missing_bands_noop_when_no_source() -> None:

0 commit comments

Comments
 (0)