Skip to content

Commit b960c1d

Browse files
Added a plugin for processing level3 Land Cover product. (#151)
* Added a plugin for land cover level 3 and a unit test for this level. * Add the level3 unit test * Applied formatting. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Added comments on classes and passed cultivated classification as is. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent cc2b79a commit b960c1d

File tree

3 files changed

+141
-0
lines changed

3 files changed

+141
-0
lines changed

odc/stats/plugins/_registry.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ def import_all():
4040
# TODO: make that more automatic
4141
modules = [
4242
"odc.stats.plugins.lc_treelite_cultivated.py",
43+
"odc.stats.plugins.lc_level3",
4344
"odc.stats.plugins.lc_treelite_woody",
4445
"odc.stats.plugins.lc_tf_urban",
4546
"odc.stats.plugins.lc_veg_class_a1",

odc/stats/plugins/lc_level3.py

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
"""
2+
Land Cover Level3 classification
3+
"""
4+
5+
from typing import Tuple
6+
import xarray as xr
7+
from ._registry import StatsPluginInterface, register
8+
9+
NODATA = 255
10+
11+
12+
class StatsLccsLevel3(StatsPluginInterface):
13+
NAME = "ga_ls_lccs_level3"
14+
SHORT_NAME = NAME
15+
VERSION = "0.0.1"
16+
PRODUCT_FAMILY = "lccs"
17+
18+
@property
19+
def measurements(self) -> Tuple[str, ...]:
20+
_measurements = ["level3_class"]
21+
return _measurements
22+
23+
def reduce(self, xx: xr.Dataset) -> xr.Dataset:
24+
25+
l34_dss = xx.classes_l3_l4
26+
urban_dss = xx.urban_classes
27+
cultivated_dss = xx.cultivated_class
28+
29+
# Cultivated pipeline applies a mask which feeds only terrestrial veg (110) to the model
30+
# Just exclude no data (255) and apply the cultivated results
31+
cultivated_mask = cultivated_dss != int(NODATA)
32+
l34_cultivated_masked = xr.where(cultivated_mask, cultivated_dss, l34_dss)
33+
34+
# Urban is classified on l3/4 surface output (210)
35+
urban_mask = l34_dss == 210
36+
l34_urban_cultivated_masked = xr.where(
37+
urban_mask, urban_dss, l34_cultivated_masked
38+
)
39+
40+
attrs = xx.attrs.copy()
41+
attrs["nodata"] = NODATA
42+
l34_urban_cultivated_masked = l34_urban_cultivated_masked.squeeze(dim=["spec"])
43+
dims = l34_urban_cultivated_masked.dims
44+
45+
data_vars = {
46+
"level3_class": xr.DataArray(
47+
l34_urban_cultivated_masked.data, dims=dims, attrs=attrs
48+
)
49+
}
50+
51+
coords = dict((dim, xx.coords[dim]) for dim in dims)
52+
level3 = xr.Dataset(data_vars=data_vars, coords=coords, attrs=attrs)
53+
54+
return level3
55+
56+
57+
register("lccs_level3", StatsLccsLevel3)

tests/test_lc_level3.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import numpy as np
2+
import pandas as pd
3+
import xarray as xr
4+
5+
from odc.stats.plugins.lc_level3 import StatsLccsLevel3
6+
import pytest
7+
8+
expected_l3_classes = [
9+
[111, 112, 215],
10+
[124, 112, 215],
11+
[221, 215, 216],
12+
[223, 255, 223],
13+
]
14+
15+
16+
@pytest.fixture(scope="module")
17+
def image_groups():
18+
l34 = np.array(
19+
[
20+
[
21+
[110, 110, 210],
22+
[124, 110, 210],
23+
[221, 210, 210],
24+
[223, 255, 223],
25+
]
26+
],
27+
dtype="int",
28+
)
29+
30+
urban = np.array(
31+
[
32+
[
33+
[215, 215, 215],
34+
[216, 216, 215],
35+
[116, 215, 216],
36+
[216, 216, 216],
37+
]
38+
],
39+
dtype="int",
40+
)
41+
42+
cultivated = np.array(
43+
[
44+
[
45+
[111, 112, 255],
46+
[255, 112, 255],
47+
[255, 255, 255],
48+
[255, 255, 255],
49+
]
50+
],
51+
dtype="int",
52+
)
53+
54+
tuples = [
55+
(np.datetime64("2000-01-01T00"), np.datetime64("2000-01-01")),
56+
]
57+
index = pd.MultiIndex.from_tuples(tuples, names=["time", "solar_day"])
58+
coords = {
59+
"x": np.linspace(10, 20, l34.shape[2]),
60+
"y": np.linspace(0, 5, l34.shape[1]),
61+
"spec": index,
62+
}
63+
64+
data_vars = {
65+
"classes_l3_l4": xr.DataArray(
66+
l34, dims=("spec", "y", "x"), attrs={"nodata": 255}
67+
),
68+
"urban_classes": xr.DataArray(
69+
urban, dims=("spec", "y", "x"), attrs={"nodata": 255}
70+
),
71+
"cultivated_class": xr.DataArray(
72+
cultivated, dims=("spec", "y", "x"), attrs={"nodata": 255}
73+
),
74+
}
75+
xx = xr.Dataset(data_vars=data_vars, coords=coords)
76+
return xx
77+
78+
79+
def test_urban_class(image_groups):
80+
81+
lc_level3 = StatsLccsLevel3()
82+
level3_classes = lc_level3.reduce(image_groups)
83+
assert (level3_classes.level3_class.values == expected_l3_classes).all()

0 commit comments

Comments
 (0)