Skip to content

Commit 3d1812c

Browse files
committed
new rule lat-lon-naming
1 parent b72db3c commit 3d1812c

File tree

3 files changed

+114
-0
lines changed

3 files changed

+114
-0
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import numpy as np
2+
3+
from xrlint.plugins.xcube.rules.lat_lon_naming import LatLonNaming
4+
5+
import xarray as xr
6+
7+
from xrlint.testing import RuleTester, RuleTest
8+
9+
10+
def make_dataset(lat_dim: str, lon_dim: str):
11+
dims = ["time", lat_dim, lon_dim]
12+
n = 3
13+
return xr.Dataset(
14+
attrs=dict(title="v-data"),
15+
coords={
16+
lon_dim: xr.DataArray(
17+
np.linspace(0, 1, n), dims=lon_dim, attrs={"units": "m"}
18+
),
19+
lat_dim: xr.DataArray(
20+
np.linspace(0, 1, n), dims=lat_dim, attrs={"units": "m"}
21+
),
22+
"time": xr.DataArray(
23+
list(range(2010, 2010 + n)), dims="time", attrs={"units": "years"}
24+
),
25+
},
26+
data_vars={
27+
"chl": xr.DataArray(
28+
np.random.random((n, n, n)), dims=dims, attrs={"units": "mg/m^-3"}
29+
),
30+
"tsm": xr.DataArray(
31+
np.random.random((n, n, n)), dims=dims, attrs={"units": "mg/m^-3"}
32+
),
33+
"avg_temp": xr.DataArray(
34+
np.random.random(n), dims=dims[0], attrs={"units": "kelvin"}
35+
),
36+
"mask": xr.DataArray(np.random.random((n, n)), dims=dims[-2:]),
37+
},
38+
)
39+
40+
41+
valid_dataset_1 = make_dataset("lat", "lon")
42+
43+
invalid_dataset_1 = make_dataset("lat", "long")
44+
invalid_dataset_2 = make_dataset("lat", "longitude")
45+
invalid_dataset_3 = make_dataset("lat", "Lon")
46+
47+
invalid_dataset_4 = make_dataset("ltd", "lon")
48+
invalid_dataset_5 = make_dataset("latitude", "lon")
49+
invalid_dataset_6 = make_dataset("Lat", "lon")
50+
51+
LatLonNamingTest = RuleTester.define_test(
52+
"lat-lon-naming",
53+
LatLonNaming,
54+
valid=[
55+
RuleTest(dataset=valid_dataset_1),
56+
],
57+
invalid=[
58+
RuleTest(dataset=invalid_dataset_1),
59+
RuleTest(dataset=invalid_dataset_2),
60+
RuleTest(dataset=invalid_dataset_3),
61+
RuleTest(dataset=invalid_dataset_4),
62+
RuleTest(dataset=invalid_dataset_5),
63+
RuleTest(dataset=invalid_dataset_6),
64+
],
65+
)

tests/plugins/xcube/test_plugin.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ def test_rules_complete(self):
1919
self.assertEqual(
2020
{
2121
"cube-dims-order",
22+
"lat-lon-naming",
2223
},
2324
set(_plugin.rules.keys()),
2425
)
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
from xrlint.node import DatasetNode
2+
from xrlint.plugins.xcube.rules import plugin
3+
from xrlint.rule import RuleOp, RuleContext
4+
5+
VALID_LON = "lon"
6+
VALID_LAT = "lat"
7+
INVALID_LONS = {"lng", "long", "longitude"}
8+
INVALID_LATS = {"ltd", "latitude"}
9+
10+
11+
@plugin.define_rule(
12+
"lat-lon-naming",
13+
version="1.0.0",
14+
description=(
15+
f"Latitude and longitude coordinates and dimensions"
16+
f" should be called {VALID_LAT!r} and {VALID_LON!r}."
17+
),
18+
)
19+
class LatLonNaming(RuleOp):
20+
def dataset(self, ctx: RuleContext, node: DatasetNode):
21+
lon_ok = _check(
22+
ctx, "variable", node.dataset.variables.keys(), INVALID_LONS, VALID_LON
23+
)
24+
lat_ok = _check(
25+
ctx, "variable", node.dataset.variables.keys(), INVALID_LATS, VALID_LAT
26+
)
27+
if lon_ok and lat_ok:
28+
# If variables have been reported,
29+
# we should not need to report (their) coordinates
30+
_check(ctx, "dimension", node.dataset.sizes.keys(), INVALID_LONS, VALID_LON)
31+
_check(ctx, "dimension", node.dataset.sizes.keys(), INVALID_LATS, VALID_LAT)
32+
33+
34+
def _check(ctx, names_name, names, invalid_names, valid_name):
35+
names = [str(n) for n in names] # xarray keys are Hashable, not str
36+
found_names = [
37+
n
38+
for n in names
39+
if (n.lower() in invalid_names) or (n.lower() == valid_name and n != valid_name)
40+
]
41+
if found_names:
42+
ctx.report(
43+
f"The {names_name} {found_names[0]!r} should be named {valid_name!r}.",
44+
suggestions=[f"Rename {names_name} to {valid_name!r}."],
45+
)
46+
return False
47+
else:
48+
return True

0 commit comments

Comments
 (0)