Skip to content

Commit 055cbfa

Browse files
committed
Corrected the implementation of water seasonality and water persistence. Also made a range of other mappings at Level 4 based on the latest understanding of Collection 2
1 parent 6e12308 commit 055cbfa

File tree

10 files changed

+188
-222
lines changed

10 files changed

+188
-222
lines changed

odc/stats/plugins/l34_utils/l4_natural_aquatic.py

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,24 @@
44

55
from odc.stats._algebra import expr_eval
66

7+
NODATA = 255
78

8-
def natural_auquatic_veg(l4, veg_cover, water_seasonality):
9+
def natural_auquatic_veg(l4, veg_cover, water_season):
910

1011
# mark woody/herbaceous
1112
# mangroves -> woody
1213
# everything else -> herbaceous
14+
15+
water_seasonality = expr_eval(
16+
"where((a==a), a, nodata)",
17+
{
18+
"a": water_season,
19+
},
20+
name="mark_water_season",
21+
dtype="float32",
22+
**{"nodata": NODATA},
23+
)
24+
1325
res = expr_eval(
1426
"where((a==124), 56, a)",
1527
{
@@ -102,7 +114,7 @@ def natural_auquatic_veg(l4, veg_cover, water_seasonality):
102114
)
103115

104116
res = expr_eval(
105-
"where((a==252)&(b==10), 80, a)",
117+
"where((a==252)&(b==10), 79, a)",
106118
{
107119
"a": res,
108120
"b": veg_cover,
@@ -112,7 +124,7 @@ def natural_auquatic_veg(l4, veg_cover, water_seasonality):
112124
)
113125

114126
res = expr_eval(
115-
"where((a==251)&(b==10), 81, a)",
127+
"where((a==251)&(b==10), 80, a)",
116128
{
117129
"a": res,
118130
"b": veg_cover,
@@ -271,7 +283,7 @@ def natural_auquatic_veg(l4, veg_cover, water_seasonality):
271283
name="mark_final",
272284
dtype="uint8",
273285
)
274-
286+
275287
res = expr_eval(
276288
"where((a==251)&(b==16), 92, a)",
277289
{
@@ -282,4 +294,24 @@ def natural_auquatic_veg(l4, veg_cover, water_seasonality):
282294
dtype="uint8",
283295
)
284296

297+
# There are cases where a tile falls over water.
298+
# In these cases, the PC will have no data so we map back 251-254 to their corresponding classes
299+
res = expr_eval(
300+
"where((a>=251)&(a<=252), 57, a)",
301+
{
302+
"a": res,
303+
},
304+
name="mark_final",
305+
dtype="uint8",
306+
)
307+
res = expr_eval(
308+
"where((a>=253)&(a<=254), 58, a)",
309+
{
310+
"a": res,
311+
},
312+
name="mark_final",
313+
dtype="uint8",
314+
)
315+
316+
285317
return res

odc/stats/plugins/l34_utils/l4_veg_cover.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
def canopyco_veg_con(xx: xr.Dataset, veg_threshold):
99

10+
1011
# Mask NODATA
1112
pv_pc_50 = expr_eval(
1213
"where(a==a, a, nodata)",
@@ -88,7 +89,7 @@ def canopyco_veg_con(xx: xr.Dataset, veg_threshold):
8889

8990
# 65-100 --> 10
9091
veg_mask = expr_eval(
91-
"where((a>=m)&(a<n), 10, b)",
92+
"where((a>=m)&(a<=n), 10, b)",
9293
{
9394
"a": pv_pc_50,
9495
"b": veg_mask,

odc/stats/plugins/l34_utils/l4_water.py

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,43 +5,62 @@
55

66
def water_classification(xx, intertidal_mask, water_persistence):
77

8+
# Replace nan with nodata
89
l4 = expr_eval(
9-
"where(((a==223)|(a==221))&(b==1), 101, a)",
10-
{"a": xx.level_3_4.data, "b": water_persistence},
10+
"where((a==a), a, nodata)",
11+
{"a": xx.level_3_4.data},
12+
name="mark_water",
13+
dtype="uint8",
14+
**{"nodata": NODATA},
15+
)
16+
17+
l4 = expr_eval(
18+
"where((a==223)|(a==221), 98, a)",
19+
{"a": l4},
20+
name="mark_water",
21+
dtype="uint8"
22+
)
23+
24+
l4 = expr_eval(
25+
"where((a==98)&(b!=1), 99, a)",
26+
{"a": l4, "b": intertidal_mask},
1127
name="mark_water",
1228
dtype="uint8",
1329
)
14-
30+
1531
l4 = expr_eval(
16-
"where(((a==223)|(a==221))&(b==7), 102, a)",
17-
{"a": l4, "b": water_persistence},
32+
"where((a==98)&(b==1), 100, a)",
33+
{"a": l4, "b": intertidal_mask},
1834
name="mark_water",
1935
dtype="uint8",
2036
)
2137

2238
l4 = expr_eval(
23-
"where(((a==223)|(a==221))&(b==8), 103, a)",
39+
"where((a==99)&(b==1), 101, a)",
2440
{"a": l4, "b": water_persistence},
2541
name="mark_water",
2642
dtype="uint8",
2743
)
2844

2945
l4 = expr_eval(
30-
"where(((a==223)|(a==221))&(b==9), 104, a)",
46+
"where((a==99)&(b==7), 102, a)",
3147
{"a": l4, "b": water_persistence},
3248
name="mark_water",
3349
dtype="uint8",
3450
)
3551

3652
l4 = expr_eval(
37-
"where(((a==223)|(a==221))&(b==1), 100, a)",
38-
{"a": l4, "b": intertidal_mask},
53+
"where((a==99)&(b==8), 103, a)",
54+
{"a": l4, "b": water_persistence},
3955
name="mark_water",
4056
dtype="uint8",
4157
)
4258

4359
l4 = expr_eval(
44-
"where((a==223)|(a==221), 98, a)", {"a": l4}, name="mark_water", dtype="uint8"
60+
"where((a==99)&(b==9), 104, a)",
61+
{"a": l4, "b": water_persistence},
62+
name="mark_water",
63+
dtype="uint8",
4564
)
4665

4766
return l4

odc/stats/plugins/l34_utils/lc_water_seasonality.py

Lines changed: 0 additions & 36 deletions
This file was deleted.

odc/stats/plugins/l34_utils/utils.py

Lines changed: 0 additions & 10 deletions
This file was deleted.

odc/stats/plugins/lc_level34.py

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
from .l34_utils import (
1313
l4_water_persistence,
14-
lc_water_seasonality,
1514
l4_veg_cover,
1615
lc_level3,
1716
l4_cultivated,
@@ -27,7 +26,6 @@
2726

2827
NODATA = 255
2928

30-
3129
class StatsLccsLevel4(StatsPluginInterface):
3230
NAME = "ga_ls_lccs_Level34"
3331
SHORT_NAME = NAME
@@ -39,7 +37,6 @@ def __init__(
3937
veg_threshold: Optional[List] = None,
4038
bare_threshold: Optional[List] = None,
4139
watper_threshold: Optional[List] = None,
42-
water_seasonality_threshold: int = None,
4340
**kwargs,
4441
):
4542
super().__init__(**kwargs)
@@ -51,9 +48,7 @@ def __init__(
5148
self.watper_threshold = (
5249
watper_threshold if watper_threshold is not None else [1, 4, 7, 10]
5350
)
54-
self.water_seasonality_threshold = (
55-
water_seasonality_threshold if water_seasonality_threshold else 3
56-
)
51+
5752

5853
def fuser(self, xx):
5954
return xx
@@ -64,19 +59,15 @@ def reduce(self, xx: xr.Dataset) -> xr.Dataset:
6459
water_persistence = l4_water_persistence.water_persistence(
6560
xx, self.watper_threshold
6661
)
67-
68-
water_seasonality = lc_water_seasonality.water_seasonality(
69-
xx, self.water_seasonality_threshold
70-
)
71-
62+
7263
intertidal_mask = lc_intertidal_mask.intertidal_mask(xx)
7364

7465
# #TODO WATER (99-104)
7566
l4 = l4_water.water_classification(xx, intertidal_mask, water_persistence)
7667

7768
# Generate Level3 classes
7869
level3 = lc_level3.lc_level3(xx)
79-
70+
8071
# Vegetation cover
8172
veg_cover = l4_veg_cover.canopyco_veg_con(xx, self.veg_threshold)
8273

@@ -88,18 +79,19 @@ def reduce(self, xx: xr.Dataset) -> xr.Dataset:
8879

8980
# Apply terrestrial vegetation classes [19-36]
9081
l4 = l4_natural_veg.lc_l4_natural_veg(l4, level3, lifeform, veg_cover)
91-
82+
9283
# Bare gradation
9384
bare_gradation = l4_bare_gradation.bare_gradation(
9485
xx, self.bare_threshold, veg_cover
9586
)
96-
l4 = l4_natural_aquatic.natural_auquatic_veg(l4, veg_cover, water_seasonality)
87+
88+
l4 = l4_natural_aquatic.natural_auquatic_veg(l4, veg_cover, xx.water_season)
9789

9890
level4 = l4_surface.lc_l4_surface(l4, level3, bare_gradation)
9991

10092
level3 = level3.astype(np.uint8)
10193
level4 = level4.astype(np.uint8)
102-
94+
10395
attrs = xx.attrs.copy()
10496
attrs["nodata"] = NODATA
10597
dims = xx.level_3_4.dims[1:]
@@ -113,4 +105,4 @@ def reduce(self, xx: xr.Dataset) -> xr.Dataset:
113105
return leve34
114106

115107

116-
register("lc_l3_l4", StatsLccsLevel4)
108+
register("lc_l3_l4", StatsLccsLevel4)

tests/test_lc_l34.py

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ def image_groups():
1717
[
1818
[210, 210, 210],
1919
[210, 210, 210],
20-
[210, 210, 210],
21-
[210, 210, 210],
20+
[223, 210, 210],
21+
[221, 221, 221],
2222
]
2323
],
2424
dtype="uint8",
@@ -89,13 +89,25 @@ def image_groups():
8989
[
9090
[1, 3, 2],
9191
[4, 5, 6],
92-
[9, 2, 11],
92+
[2, 2, 11],
9393
[10, 11, 12],
9494
]
9595
],
9696
dtype="uint8",
9797
)
9898

99+
water_season = np.array(
100+
[
101+
[
102+
[1, 2, 1],
103+
[2, 1, 2],
104+
[1, 1, 2],
105+
[2, 2, 1],
106+
]
107+
],
108+
dtype="uint8",
109+
)
110+
99111
tuples = [
100112
(np.datetime64("2000-01-01T00"), np.datetime64("2000-01-01")),
101113
]
@@ -141,6 +153,11 @@ def image_groups():
141153
dims=("spec", "y", "x"),
142154
attrs={"nodata": 255},
143155
),
156+
"water_season": xr.DataArray(
157+
da.from_array(water_season, chunks=(1, -1, -1)),
158+
dims=("spec", "y", "x"),
159+
attrs={"nodata": 255},
160+
),
144161
}
145162

146163
xx = xr.Dataset(data_vars=data_vars, coords=coords)
@@ -149,9 +166,9 @@ def image_groups():
149166

150167

151168
def test_l4_classes(image_groups):
152-
expected_l3 = [[216, 216, 215], [216, 216, 216], [215, 215, 215], [215, 215, 215]]
169+
expected_l3 = [[216, 216, 215], [216, 216, 216], [220, 215, 215], [220, 220, 220]]
153170

154-
expected_l4 = [[95, 97, 93], [97, 96, 96], [93, 93, 93], [93, 93, 93]]
171+
expected_l4 = [[ 95, 97, 93], [97, 96, 96], [100, 93, 93], [101, 101, 101]]
155172
stats_l4 = StatsLccsLevel4(measurements=["level3", "level4"])
156173
ds = stats_l4.reduce(image_groups)
157174

0 commit comments

Comments
 (0)