Skip to content

Commit 2fb3da9

Browse files
committed
Merge branch 'main' into forman-break_node_traversal
# Conflicts: # CHANGES.md
2 parents d4958c0 + 5647713 commit 2fb3da9

9 files changed

Lines changed: 164 additions & 20 deletions

File tree

CHANGES.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11

22
## Early development snapshots
33

4-
- Version 0.0.3 (in development)
4+
- Version 0.0.3 (08.01.2025)
55
- enhanced "simple" output format by colors and links
66
- new xcube rule "increasing-time"
7+
- new xcube rule "data-var-colors"
78
- new `RuleExit` exception to exit rule logic and
89
stop further node traversal
910

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,19 @@ there is no stable release yet. See
1717

1818
## Features
1919

20-
- Flexible validation for `xarray.Dataset` objects by configurable _rules_.
21-
- Available from _CLI_ and _Python API_.
22-
- _Custom plugins_ providing _custom rule_ sets allow addressing
20+
- Flexible validation for `xarray.Dataset` objects by configurable rules.
21+
- Available from CLI and Python API.
22+
- Custom plugins providing custom rule sets allow addressing
2323
different dataset conventions.
24-
- _Project-specific configurations_ including configuration of individual
24+
- Project-specific configurations including configuration of individual
2525
rules and file-specific settings.
2626

2727
## Inbuilt Rules
2828

2929
The following rule plugins are currently built into the code base:
3030

3131
- `xrlint.plugins.core`: Implementing the rules for
32-
[tiny data](https://tutorial.xarray.dev/intermediate/data_cleaning/05.1_intro.html)
32+
[tidy data](https://tutorial.xarray.dev/intermediate/data_cleaning/05.1_intro.html)
3333
and the
3434
[CF-Conventions](https://cfconventions.org/cf-conventions/cf-conventions.html).
3535
- `xrlint.plugins.core`: Implementing the rules for

docs/cli.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@ The following are the command's options and arguments:
66
```
77
Usage: xrlint [OPTIONS] [FILES]...
88
9-
Lint the given dataset FILES.
9+
Validate the given dataset FILES.
1010
11-
Reads configuration from `./xrlint.config.py` if `--no-default-config` is
12-
not set and `--config PATH` is not provided, then validates each dataset in
13-
FILES against the configuration. The validation result is dumped to standard
14-
output if not otherwise stated by `--output-file PATH`. The output format is
15-
`simple`. Other inbuilt formats are `json` and `html` which can by setting
16-
the `--format NAME` option.
11+
Reads configuration from `xrlint.config.*` if file exists and unless `--no-
12+
default-config` is set or `--config PATH` is provided. Then validates each
13+
dataset in FILES against the configuration. The validation result is dumped
14+
to standard output if not otherwise stated by `--output-file PATH`. The
15+
output format is `simple`. Other inbuilt formats are `json` and `html` which
16+
can by setting the `--format NAME` option.
1717
1818
Options:
1919
--no-default-config Disable use of default configuration from
@@ -24,12 +24,12 @@ Options:
2424
that defines an 'export_plugin()' function.
2525
--rule SPEC Specify rules. SPEC must have format '<rule-name>:
2626
<rule-config>' (note the space character).
27-
-f, --format NAME Use a specific output format - default: simple
2827
-o, --output-file PATH Specify file to write report to
28+
-f, --format NAME Use a specific output format - default: simple
29+
--color / --no-color Force enabling/disabling of color
2930
--max-warnings COUNT Number of warnings to trigger nonzero exit code -
3031
default: 5
3132
--init Write initial configuration file and exit.
3233
--version Show the version and exit.
3334
--help Show this message and exit.
34-
3535
```

docs/index.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,20 @@ there is no stable release yet. See [to-do list](https://github.com/bcdev/xrlint
1010

1111
## Features
1212

13-
- Flexible validation for `xarray.Dataset` objects by configurable _rules_.
14-
- Available from _CLI_ and _Python API_.
15-
- _Custom plugins_ providing _custom rule_ sets allow addressing
13+
- Flexible validation for `xarray.Dataset` objects by configurable rules.
14+
- Available from CLI and Python API.
15+
- Custom plugins providing custom rule sets allow addressing
1616
different dataset conventions.
17-
- _Project-specific configurations_ including configuration of individual
17+
- Project-specific configurations including configuration of individual
1818
rules and file-specific settings.
1919

20+
2021
## Inbuilt Rules
2122

2223
The following rule plugins are currently built into the code base:
2324

2425
- `core`: Implementing the rules for
25-
[tiny data](https://tutorial.xarray.dev/intermediate/data_cleaning/05.1_intro.html)
26+
[tidy data](https://tutorial.xarray.dev/intermediate/data_cleaning/05.1_intro.html)
2627
and the
2728
[CF-Conventions](https://cfconventions.org/cf-conventions/cf-conventions.html).
2829
- `xcube`: Implementing the rules for

docs/rule-ref.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,12 +46,25 @@ Order of dimensions in spatio-temporal datacube variables should be [time, ...,
4646

4747
Contained in: `all`-:material-lightning-bolt: `recommended`-:material-lightning-bolt:
4848

49+
### :material-lightbulb: `data-var-colors`
50+
51+
Spatial data variables should encode xcube color mappings in their metadata.
52+
[More information.](https://xcube.readthedocs.io/en/latest/cubespec.html#encoding-of-colors)
53+
54+
Contained in: `all`-:material-lightning-bolt: `recommended`-:material-alert:
55+
4956
### :material-lightbulb: `grid-mapping-naming`
5057

5158
Grid mapping variables should be called 'spatial_ref' or 'crs' for compatibility with rioxarray and other packages.
5259

5360
Contained in: `all`-:material-lightning-bolt: `recommended`-:material-alert:
5461

62+
### :material-bug: `increasing-time`
63+
64+
Time coordinate labels should be monotonically increasing.
65+
66+
Contained in: `all`-:material-lightning-bolt: `recommended`-:material-lightning-bolt:
67+
5568
### :material-bug: `lat-lon-naming`
5669

5770
Latitude and longitude coordinates and dimensions should be called 'lat' and 'lon'.
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import numpy as np
2+
3+
from xrlint.plugins.xcube.rules.data_var_colors import DataVarColors
4+
5+
import xarray as xr
6+
7+
from xrlint.testing import RuleTester, RuleTest
8+
9+
10+
def make_dataset():
11+
dims = ["time", "y", "x"]
12+
n = 5
13+
return xr.Dataset(
14+
attrs=dict(title="v-data"),
15+
coords={
16+
dims[2]: xr.DataArray(
17+
np.linspace(0, 1, n), dims=dims[2], attrs={"units": "m"}
18+
),
19+
dims[1]: xr.DataArray(
20+
np.linspace(0, 1, n), dims=dims[1], attrs={"units": "m"}
21+
),
22+
dims[0]: xr.DataArray(
23+
[2010, 2011, 2012, 2013, 2014],
24+
dims=dims[0],
25+
attrs={"units": "years"},
26+
),
27+
},
28+
data_vars={
29+
"chl": xr.DataArray(
30+
np.random.random((n, n, n)),
31+
dims=dims,
32+
attrs={
33+
"units": "mg/m^-3",
34+
"color_bar_name": "plasma",
35+
"color_value_min": 0,
36+
"color_value_max": 100,
37+
"color_norm": "log",
38+
},
39+
),
40+
},
41+
)
42+
43+
44+
valid_dataset_1 = make_dataset()
45+
46+
invalid_dataset_1 = make_dataset()
47+
invalid_dataset_1.chl.attrs = {
48+
"units": "mg/m^-3",
49+
# Missing:
50+
# "color_bar_name": "plasma",
51+
# "color_value_min": 0,
52+
# "color_value_max": 100,
53+
# "color_norm": "log",
54+
}
55+
invalid_dataset_2 = make_dataset()
56+
invalid_dataset_2.chl.attrs = {
57+
"units": "mg/m^-3",
58+
"color_bar_name": "plasma",
59+
# Missing:
60+
# "color_value_min": 0,
61+
# "color_value_max": 100,
62+
# "color_norm": "log",
63+
}
64+
invalid_dataset_3 = make_dataset()
65+
invalid_dataset_3.chl.attrs = {
66+
"units": "mg/m^-3",
67+
"color_bar_name": "plasma",
68+
"color_value_min": 0,
69+
"color_value_max": 100,
70+
"color_norm": "ln", # wrong
71+
}
72+
73+
LatLonNamingTest = RuleTester.define_test(
74+
"data-var-colors",
75+
DataVarColors,
76+
valid=[
77+
RuleTest(dataset=valid_dataset_1),
78+
],
79+
invalid=[
80+
RuleTest(dataset=invalid_dataset_1),
81+
RuleTest(dataset=invalid_dataset_2),
82+
RuleTest(dataset=invalid_dataset_3),
83+
],
84+
)

tests/plugins/xcube/test_plugin.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ def test_rules_complete(self):
2121
{
2222
"any-spatial-data-var",
2323
"cube-dims-order",
24+
"data-var-colors",
2425
"grid-mapping-naming",
2526
"lat-lon-naming",
2627
"single-grid-mapping",

xrlint/plugins/xcube/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ def export_plugin() -> Plugin:
1717
"rules": {
1818
"xcube/any-spatial-data-var": "error",
1919
"xcube/cube-dims-order": "error",
20+
"xcube/data-var-colors": "warn",
2021
"xcube/grid-mapping-naming": "warn",
2122
"xcube/increasing-time": "error",
2223
"xcube/lat-lon-naming": "error",
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import numpy as np
2+
3+
from xrlint.node import DataArrayNode
4+
from xrlint.plugins.xcube.rules import plugin
5+
from xrlint.plugins.xcube.util import is_spatial_var
6+
from xrlint.rule import RuleContext
7+
from xrlint.rule import RuleOp
8+
9+
10+
@plugin.define_rule(
11+
"data-var-colors",
12+
version="1.0.0",
13+
type="suggestion",
14+
description=(
15+
"Spatial data variables should encode"
16+
" xcube color mappings in their metadata."
17+
),
18+
docs_url=(
19+
"https://xcube.readthedocs.io/en/latest/cubespec.html#encoding-of-colors"
20+
),
21+
)
22+
class DataVarColors(RuleOp):
23+
def data_array(self, ctx: RuleContext, node: DataArrayNode):
24+
array = node.data_array
25+
if not node.in_data_vars() or not is_spatial_var(array):
26+
return
27+
attrs = array.attrs
28+
color_bar_name = attrs.get("color_bar_name")
29+
if not color_bar_name:
30+
ctx.report("Missing attribute 'color_bar_name'")
31+
else:
32+
color_value_min = attrs.get("color_value_min")
33+
color_value_max = attrs.get("color_value_max")
34+
if color_value_min is None or color_value_max is None:
35+
ctx.report(
36+
"Missing both or one of 'color_value_min' and 'color_value_max'"
37+
)
38+
39+
color_norm = attrs.get("color_norm")
40+
if color_norm and color_norm not in ("lin", "log"):
41+
ctx.report(
42+
"Invalid value of attribute 'color_norm', should be 'lin' or 'log'"
43+
)

0 commit comments

Comments
 (0)