Skip to content

Commit a504ed5

Browse files
authored
Add water frequency to A1 module. (#155)
* Added water frequency range to a1 module * Apply specific data types. * Change the output type of water seasonality band to int and ensure no data is 255.
1 parent 9d4049b commit a504ed5

File tree

2 files changed

+90
-6
lines changed

2 files changed

+90
-6
lines changed

odc/stats/plugins/lc_veg_class_a1.py

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def __init__(
7171
saltpan_threshold: Optional[int] = None,
7272
water_threshold: Optional[float] = None,
7373
veg_threshold: Optional[int] = None,
74+
water_seasonality_threshold: Optional[float] = None,
7475
**kwargs,
7576
):
7677
super().__init__(**kwargs)
@@ -83,11 +84,14 @@ def __init__(
8384
)
8485
self.water_threshold = water_threshold if water_threshold is not None else 0.2
8586
self.veg_threshold = veg_threshold if veg_threshold is not None else 2
87+
self.water_seasonality_threshold = (
88+
water_seasonality_threshold if water_seasonality_threshold else 0.25
89+
)
8690
self.output_classes = output_classes
8791

8892
@property
8993
def measurements(self) -> Tuple[str, ...]:
90-
_measurements = ["classes_l3_l4"]
94+
_measurements = ["classes_l3_l4", "water_seasonality"]
9195
return _measurements
9296

9397
def native_transform(self, xx):
@@ -193,6 +197,7 @@ def l3_class(self, xx: xr.Dataset):
193197
# issues:
194198
# - nodata information from non-indexed datasets missing
195199

200+
# Mask nans with NODATA
196201
l3_mask = expr_eval(
197202
"where((a!=a)|(b>=nodata), nodata, e)",
198203
{
@@ -205,17 +210,51 @@ def l3_class(self, xx: xr.Dataset):
205210
**{"nodata": NODATA},
206211
)
207212

208-
return l3_mask
213+
# Now add the water frequency
214+
# Divide water frequency into following classes:
215+
# 0 --> 0
216+
# (0,0.25] --> 1
217+
# (0.25,1] --> 2
209218

210-
def reduce(self, xx: xr.Dataset) -> xr.Dataset:
211-
l3_mask = self.l3_class(xx)
219+
water_seasonality = expr_eval(
220+
"where((a > 0) & (a <= wt), 1, a)",
221+
{"a": xx["frequency"].data},
222+
name="mark_wo_fq",
223+
dtype="float32",
224+
**{"wt": self.water_seasonality_threshold},
225+
)
226+
227+
water_seasonality = expr_eval(
228+
"where((a > wt) & (a <= 1), 2, b)",
229+
{"a": xx["frequency"].data, "b": water_seasonality},
230+
name="mark_wo_fq",
231+
dtype="float32",
232+
**{"wt": self.water_seasonality_threshold},
233+
)
234+
235+
water_seasonality = expr_eval(
236+
"where((a != a), nodata, a)",
237+
{
238+
"a": water_seasonality,
239+
},
240+
name="mark_nodata",
241+
dtype="uint8",
242+
**{"nodata": NODATA},
243+
)
244+
245+
return l3_mask, water_seasonality
212246

247+
def reduce(self, xx: xr.Dataset) -> xr.Dataset:
248+
l3_mask, water_seasonality = self.l3_class(xx)
213249
attrs = xx.attrs.copy()
214250
attrs["nodata"] = int(NODATA)
215251
data_vars = {
216252
"classes_l3_l4": xr.DataArray(
217253
l3_mask[0], dims=xx["veg_frequency"].dims[1:], attrs=attrs
218-
)
254+
),
255+
"water_seasonality": xr.DataArray(
256+
water_seasonality[0], dims=xx["veg_frequency"].dims[1:], attrs=attrs
257+
),
219258
}
220259
coords = dict((dim, xx.coords[dim]) for dim in xx["veg_frequency"].dims[1:])
221260
return xr.Dataset(data_vars=data_vars, coords=coords, attrs=xx.attrs)

tests/test_landcover_plugin_a1.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,55 @@ def test_l3_classes(dataset):
149149
dtype="uint8",
150150
)
151151

152-
res = stats_l3.l3_class(dataset)
152+
res, water_seasonality = stats_l3.l3_class(dataset)
153153
assert (res == expected_res).all()
154154

155155

156+
def test_l4_water_seasonality(dataset):
157+
stats_l3 = StatsVegClassL1(
158+
output_classes={
159+
"aquatic_veg": 124,
160+
"terrestrial_veg": 110,
161+
"water": 221,
162+
"intertidal": 223,
163+
"surface": 210,
164+
},
165+
optional_bands=["canopy_cover_class", "elevation"],
166+
)
167+
168+
wo_fq = np.array(
169+
[
170+
[
171+
[0.0, 0.021, 0.152, 255],
172+
[0.249, 0.273, 0.252, 0.0375],
173+
[0.302, 0, 0.789, 0.078],
174+
[0.021, 0.243, 255, 0.255],
175+
]
176+
],
177+
dtype="float32",
178+
)
179+
wo_fq = da.from_array(wo_fq, chunks=(1, -1, -1))
180+
181+
dataset["frequency"] = xr.DataArray(
182+
wo_fq, dims=("spec", "y", "x"), attrs={"nodata": np.nan}
183+
)
184+
185+
expected_water_seasonality = np.array(
186+
[
187+
[
188+
[0, 1, 1, 255],
189+
[1, 2, 2, 1],
190+
[2, 0, 2, 1],
191+
[1, 1, 255, 2],
192+
]
193+
],
194+
dtype="float32",
195+
)
196+
197+
res, water_seasonality = stats_l3.l3_class(dataset)
198+
assert np.allclose(water_seasonality, expected_water_seasonality)
199+
200+
156201
def test_reduce(dataset):
157202
stats_l3 = StatsVegClassL1(
158203
output_classes={

0 commit comments

Comments
 (0)