cf-xarray 0.9.1__tar.gz → 0.9.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/.github/workflows/ci.yaml +2 -2
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/.github/workflows/pypi.yaml +2 -2
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/.pre-commit-config.yaml +6 -6
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/PKG-INFO +2 -2
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/__init__.py +2 -0
- cf_xarray-0.9.3/cf_xarray/_version.py +1 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/accessor.py +118 -38
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/coding.py +1 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/criteria.py +9 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/datasets.py +29 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/formatting.py +16 -3
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/geometry.py +242 -21
- cf_xarray-0.9.3/cf_xarray/tests/conftest.py +55 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/tests/test_accessor.py +25 -3
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/tests/test_geometry.py +34 -52
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/tests/test_units.py +5 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/units.py +47 -51
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray.egg-info/PKG-INFO +2 -2
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray.egg-info/SOURCES.txt +1 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray.egg-info/requires.txt +1 -1
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/api.rst +11 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/bounds.md +3 -2
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/coding.md +4 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/dsg.md +5 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/flags.md +4 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/geometry.md +76 -22
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/grid_mappings.md +4 -3
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/parametricz.md +6 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/selecting.md +9 -1
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/sgrid_ugrid.md +5 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/units.md +6 -2
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/pyproject.toml +1 -1
- cf_xarray-0.9.1/cf_xarray/_version.py +0 -1
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/.binder/environment.yml +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/.deepsource.toml +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/.github/dependabot.yml +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/.github/workflows/parse_logs.py +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/.github/workflows/testpypi-release.yaml +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/.github/workflows/upstream-dev-ci.yaml +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/.gitignore +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/.readthedocs.yml +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/.tributors +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/CITATION.cff +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/LICENSE +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/README.rst +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/helpers.py +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/options.py +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/py.typed +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/scripts/make_doc.py +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/scripts/print_versions.py +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/sgrid.py +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/tests/__init__.py +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/tests/test_coding.py +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/tests/test_helpers.py +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/tests/test_options.py +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/tests/test_scripts.py +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray/utils.py +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray.egg-info/dependency_links.txt +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/cf_xarray.egg-info/top_level.txt +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/ci/doc.yml +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/ci/environment-no-optional-deps.yml +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/ci/environment.yml +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/ci/upstream-dev-env.yml +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/codecov.yml +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/2D_bounds_averaged.png +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/2D_bounds_error.png +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/2D_bounds_nonunique.png +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/Makefile +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/_static/dataset-diagram-logo.tex +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/_static/full-logo.png +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/_static/logo.png +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/_static/logo.svg +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/_static/rich-repr-example.png +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/_static/style.css +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/cartopy_rotated_pole.png +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/conf.py +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/contributing.rst +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/coord_axes.md +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/custom-criteria.md +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/examples/introduction.ipynb +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/faq.md +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/howtouse.md +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/index.rst +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/make.bat +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/plotting.md +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/provenance.md +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/quickstart.md +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/roadmap.rst +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/doc/whats-new.rst +0 -0
- {cf_xarray-0.9.1 → cf_xarray-0.9.3}/setup.cfg +0 -0
|
@@ -50,7 +50,7 @@ jobs:
|
|
|
50
50
|
run: |
|
|
51
51
|
pytest -n auto --cov=./ --cov-report=xml
|
|
52
52
|
- name: Upload code coverage to Codecov
|
|
53
|
-
uses: codecov/codecov-action@v4.
|
|
53
|
+
uses: codecov/codecov-action@v4.5.0
|
|
54
54
|
with:
|
|
55
55
|
file: ./coverage.xml
|
|
56
56
|
flags: unittests
|
|
@@ -114,7 +114,7 @@ jobs:
|
|
|
114
114
|
run: |
|
|
115
115
|
python -m mypy --install-types --non-interactive --cobertura-xml-report mypy_report cf_xarray/
|
|
116
116
|
- name: Upload mypy coverage to Codecov
|
|
117
|
-
uses: codecov/codecov-action@v4.
|
|
117
|
+
uses: codecov/codecov-action@v4.5.0
|
|
118
118
|
with:
|
|
119
119
|
file: mypy_report/cobertura.xml
|
|
120
120
|
flags: mypy
|
|
@@ -72,7 +72,7 @@ jobs:
|
|
|
72
72
|
|
|
73
73
|
- name: Publish package to TestPyPI
|
|
74
74
|
if: github.event_name == 'push'
|
|
75
|
-
uses: pypa/gh-action-pypi-publish@v1.
|
|
75
|
+
uses: pypa/gh-action-pypi-publish@v1.9.0
|
|
76
76
|
with:
|
|
77
77
|
password: ${{ secrets.TESTPYPI_TOKEN }}
|
|
78
78
|
repository_url: https://test.pypi.org/legacy/
|
|
@@ -96,6 +96,6 @@ jobs:
|
|
|
96
96
|
name: releases
|
|
97
97
|
path: dist
|
|
98
98
|
- name: Publish package to PyPI
|
|
99
|
-
uses: pypa/gh-action-pypi-publish@v1.
|
|
99
|
+
uses: pypa/gh-action-pypi-publish@v1.9.0
|
|
100
100
|
with:
|
|
101
101
|
verbose: true
|
|
@@ -3,20 +3,20 @@ ci:
|
|
|
3
3
|
|
|
4
4
|
repos:
|
|
5
5
|
- repo: https://github.com/asottile/pyupgrade
|
|
6
|
-
rev: v3.
|
|
6
|
+
rev: v3.16.0
|
|
7
7
|
hooks:
|
|
8
8
|
- id: pyupgrade
|
|
9
9
|
args: ["--py39-plus"]
|
|
10
10
|
|
|
11
11
|
- repo: https://github.com/astral-sh/ruff-pre-commit
|
|
12
12
|
# Ruff version.
|
|
13
|
-
rev: 'v0.
|
|
13
|
+
rev: 'v0.5.0'
|
|
14
14
|
hooks:
|
|
15
15
|
- id: ruff
|
|
16
16
|
args: ["--show-fixes", "--fix"]
|
|
17
17
|
|
|
18
18
|
- repo: https://github.com/psf/black-pre-commit-mirror
|
|
19
|
-
rev:
|
|
19
|
+
rev: 24.4.2
|
|
20
20
|
hooks:
|
|
21
21
|
- id: black
|
|
22
22
|
|
|
@@ -36,7 +36,7 @@ repos:
|
|
|
36
36
|
- mdformat-myst
|
|
37
37
|
|
|
38
38
|
- repo: https://github.com/nbQA-dev/nbQA
|
|
39
|
-
rev: 1.
|
|
39
|
+
rev: 1.8.5
|
|
40
40
|
hooks:
|
|
41
41
|
- id: nbqa-black
|
|
42
42
|
- id: nbqa-ruff
|
|
@@ -47,7 +47,7 @@ repos:
|
|
|
47
47
|
additional_dependencies: [mdformat==0.7.17]
|
|
48
48
|
|
|
49
49
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
50
|
-
rev: v4.
|
|
50
|
+
rev: v4.6.0
|
|
51
51
|
hooks:
|
|
52
52
|
- id: trailing-whitespace
|
|
53
53
|
- id: end-of-file-fixer
|
|
@@ -67,7 +67,7 @@ repos:
|
|
|
67
67
|
- id: validate-cff
|
|
68
68
|
|
|
69
69
|
- repo: https://github.com/abravalheri/validate-pyproject
|
|
70
|
-
rev: v0.
|
|
70
|
+
rev: v0.18
|
|
71
71
|
hooks:
|
|
72
72
|
- id: validate-pyproject
|
|
73
73
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: cf_xarray
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.3
|
|
4
4
|
Summary: A convenience wrapper for using CF attributes on xarray objects
|
|
5
5
|
License: Apache License
|
|
6
6
|
Version 2.0, January 2004
|
|
@@ -224,7 +224,7 @@ License-File: LICENSE
|
|
|
224
224
|
Requires-Dist: xarray
|
|
225
225
|
Provides-Extra: all
|
|
226
226
|
Requires-Dist: matplotlib; extra == "all"
|
|
227
|
-
Requires-Dist: pint; extra == "all"
|
|
227
|
+
Requires-Dist: pint!=0.24.0,>=0.18; extra == "all"
|
|
228
228
|
Requires-Dist: shapely; extra == "all"
|
|
229
229
|
Requires-Dist: regex; extra == "all"
|
|
230
230
|
Requires-Dist: rich; extra == "all"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.9.3"
|
|
@@ -28,8 +28,10 @@ from xarray.core.weighted import Weighted
|
|
|
28
28
|
from . import sgrid
|
|
29
29
|
from .criteria import (
|
|
30
30
|
_DSG_ROLES,
|
|
31
|
+
_GEOMETRY_TYPES,
|
|
31
32
|
cf_role_criteria,
|
|
32
33
|
coordinate_criteria,
|
|
34
|
+
geometry_var_criteria,
|
|
33
35
|
grid_mapping_var_criteria,
|
|
34
36
|
regex,
|
|
35
37
|
)
|
|
@@ -39,6 +41,7 @@ from .formatting import (
|
|
|
39
41
|
_format_data_vars,
|
|
40
42
|
_format_dsg_roles,
|
|
41
43
|
_format_flags,
|
|
44
|
+
_format_geometries,
|
|
42
45
|
_format_sgrid,
|
|
43
46
|
_maybe_panel,
|
|
44
47
|
)
|
|
@@ -198,7 +201,9 @@ def _get_groupby_time_accessor(
|
|
|
198
201
|
|
|
199
202
|
|
|
200
203
|
def _get_custom_criteria(
|
|
201
|
-
obj: DataArray | Dataset,
|
|
204
|
+
obj: DataArray | Dataset,
|
|
205
|
+
key: Hashable,
|
|
206
|
+
criteria: Iterable[Mapping] | Mapping | None = None,
|
|
202
207
|
) -> list[Hashable]:
|
|
203
208
|
"""
|
|
204
209
|
Translate from axis, coord, or custom name to variable name.
|
|
@@ -227,18 +232,16 @@ def _get_custom_criteria(
|
|
|
227
232
|
except ImportError:
|
|
228
233
|
from re import match as regex_match # type: ignore[no-redef]
|
|
229
234
|
|
|
230
|
-
if isinstance(obj, DataArray):
|
|
231
|
-
obj = obj._to_temp_dataset()
|
|
232
|
-
variables = obj._variables
|
|
233
|
-
|
|
234
235
|
if criteria is None:
|
|
235
236
|
if not OPTIONS["custom_criteria"]:
|
|
236
237
|
return []
|
|
237
238
|
criteria = OPTIONS["custom_criteria"]
|
|
238
239
|
|
|
239
|
-
if
|
|
240
|
-
|
|
240
|
+
if isinstance(obj, DataArray):
|
|
241
|
+
obj = obj._to_temp_dataset()
|
|
242
|
+
variables = obj._variables
|
|
241
243
|
|
|
244
|
+
criteria_iter = always_iterable(criteria, allowed=(tuple, list, set))
|
|
242
245
|
criteria_map = ChainMap(*criteria_iter)
|
|
243
246
|
results: set = set()
|
|
244
247
|
if key in criteria_map:
|
|
@@ -367,6 +370,21 @@ def _get_measure(obj: DataArray | Dataset, key: str) -> list[str]:
|
|
|
367
370
|
return list(results)
|
|
368
371
|
|
|
369
372
|
|
|
373
|
+
def _parse_related_geometry_vars(attrs: Mapping) -> tuple[Hashable]:
|
|
374
|
+
names = itertools.chain(
|
|
375
|
+
*[
|
|
376
|
+
attrs.get(attr, "").split(" ")
|
|
377
|
+
for attr in [
|
|
378
|
+
"interior_ring",
|
|
379
|
+
"node_coordinates",
|
|
380
|
+
"node_count",
|
|
381
|
+
"part_node_count",
|
|
382
|
+
]
|
|
383
|
+
]
|
|
384
|
+
)
|
|
385
|
+
return tuple(n for n in names if n)
|
|
386
|
+
|
|
387
|
+
|
|
370
388
|
def _get_bounds(obj: DataArray | Dataset, key: Hashable) -> list[Hashable]:
|
|
371
389
|
"""
|
|
372
390
|
Translate from key (either CF key or variable name) to its bounds' variable names.
|
|
@@ -470,8 +488,14 @@ def _get_all(obj: DataArray | Dataset, key: Hashable) -> list[Hashable]:
|
|
|
470
488
|
"""
|
|
471
489
|
all_mappers: tuple[Mapper] = (
|
|
472
490
|
_get_custom_criteria,
|
|
473
|
-
functools.partial(
|
|
474
|
-
|
|
491
|
+
functools.partial(
|
|
492
|
+
_get_custom_criteria,
|
|
493
|
+
criteria=(
|
|
494
|
+
cf_role_criteria,
|
|
495
|
+
grid_mapping_var_criteria,
|
|
496
|
+
geometry_var_criteria,
|
|
497
|
+
),
|
|
498
|
+
), # type: ignore[assignment]
|
|
475
499
|
_get_axis_coord,
|
|
476
500
|
_get_measure,
|
|
477
501
|
_get_grid_mapping_name,
|
|
@@ -721,8 +745,7 @@ def _getitem(
|
|
|
721
745
|
accessor: CFAccessor,
|
|
722
746
|
key: Hashable,
|
|
723
747
|
skip: list[Literal["coords", "measures"]] | None = None,
|
|
724
|
-
) -> DataArray:
|
|
725
|
-
...
|
|
748
|
+
) -> DataArray: ...
|
|
726
749
|
|
|
727
750
|
|
|
728
751
|
@overload
|
|
@@ -730,8 +753,7 @@ def _getitem(
|
|
|
730
753
|
accessor: CFAccessor,
|
|
731
754
|
key: Iterable[Hashable],
|
|
732
755
|
skip: list[Literal["coords", "measures"]] | None = None,
|
|
733
|
-
) -> Dataset:
|
|
734
|
-
...
|
|
756
|
+
) -> Dataset: ...
|
|
735
757
|
|
|
736
758
|
|
|
737
759
|
def _getitem(
|
|
@@ -823,6 +845,23 @@ def _getitem(
|
|
|
823
845
|
successful[k] = bool(grid_mapping)
|
|
824
846
|
if grid_mapping:
|
|
825
847
|
varnames.extend(grid_mapping)
|
|
848
|
+
elif "geometries" not in skip and (k == "geometry" or k in _GEOMETRY_TYPES):
|
|
849
|
+
geometries = _get_all(obj, k)
|
|
850
|
+
if geometries and k in _GEOMETRY_TYPES:
|
|
851
|
+
new = itertools.chain(
|
|
852
|
+
_parse_related_geometry_vars(
|
|
853
|
+
ChainMap(obj[g].attrs, obj[g].encoding)
|
|
854
|
+
)
|
|
855
|
+
for g in geometries
|
|
856
|
+
)
|
|
857
|
+
geometries.extend(*new)
|
|
858
|
+
if len(geometries) > 1 and scalar_key:
|
|
859
|
+
raise ValueError(
|
|
860
|
+
f"CF geometries must be represented by an Xarray Dataset. To request a Dataset in return please pass `[{k!r}]` instead."
|
|
861
|
+
)
|
|
862
|
+
successful[k] = bool(geometries)
|
|
863
|
+
if geometries:
|
|
864
|
+
varnames.extend(geometries)
|
|
826
865
|
elif k in custom_criteria or k in cf_role_criteria:
|
|
827
866
|
names = _get_all(obj, k)
|
|
828
867
|
check_results(names, k)
|
|
@@ -1561,8 +1600,7 @@ class CFAccessor:
|
|
|
1561
1600
|
_format_flags(self, rich), title="Flag Variable", rich=rich
|
|
1562
1601
|
)
|
|
1563
1602
|
|
|
1564
|
-
roles
|
|
1565
|
-
if roles:
|
|
1603
|
+
if roles := self.cf_roles:
|
|
1566
1604
|
if any(role in roles for role in _DSG_ROLES):
|
|
1567
1605
|
yield _maybe_panel(
|
|
1568
1606
|
_format_dsg_roles(self, dims, rich),
|
|
@@ -1578,6 +1616,13 @@ class CFAccessor:
|
|
|
1578
1616
|
rich=rich,
|
|
1579
1617
|
)
|
|
1580
1618
|
|
|
1619
|
+
if self.geometries:
|
|
1620
|
+
yield _maybe_panel(
|
|
1621
|
+
_format_geometries(self, dims, rich),
|
|
1622
|
+
title="Geometries",
|
|
1623
|
+
rich=rich,
|
|
1624
|
+
)
|
|
1625
|
+
|
|
1581
1626
|
yield _maybe_panel(
|
|
1582
1627
|
_format_coordinates(self, dims, coords, rich),
|
|
1583
1628
|
title="Coordinates",
|
|
@@ -1757,12 +1802,42 @@ class CFAccessor:
|
|
|
1757
1802
|
|
|
1758
1803
|
vardict: dict[str, list[Hashable]] = {}
|
|
1759
1804
|
for k, v in variables.items():
|
|
1760
|
-
|
|
1761
|
-
|
|
1805
|
+
attrs_or_encoding = ChainMap(v.attrs, v.encoding)
|
|
1806
|
+
if role := attrs_or_encoding.get("cf_role", None):
|
|
1762
1807
|
vardict[role] = vardict.setdefault(role, []) + [k]
|
|
1763
1808
|
|
|
1764
1809
|
return {role_: sort_maybe_hashable(v) for role_, v in vardict.items()}
|
|
1765
1810
|
|
|
1811
|
+
@property
|
|
1812
|
+
def geometries(self) -> dict[str, list[Hashable]]:
|
|
1813
|
+
"""
|
|
1814
|
+
Mapping geometry type names to variable names.
|
|
1815
|
+
|
|
1816
|
+
Returns
|
|
1817
|
+
-------
|
|
1818
|
+
dict
|
|
1819
|
+
Dictionary mapping geometry names to variable names.
|
|
1820
|
+
|
|
1821
|
+
References
|
|
1822
|
+
----------
|
|
1823
|
+
Please refer to the CF conventions document : http://cfconventions.org/Data/cf-conventions/cf-conventions-1.8/cf-conventions.html#coordinates-metadata
|
|
1824
|
+
"""
|
|
1825
|
+
vardict: dict[str, list[Hashable]] = {}
|
|
1826
|
+
|
|
1827
|
+
if isinstance(self._obj, Dataset):
|
|
1828
|
+
variables = self._obj._variables
|
|
1829
|
+
elif isinstance(self._obj, DataArray):
|
|
1830
|
+
variables = {"_": self._obj._variable}
|
|
1831
|
+
|
|
1832
|
+
for v in variables.values():
|
|
1833
|
+
attrs_or_encoding = ChainMap(v.attrs, v.encoding)
|
|
1834
|
+
if geometry := attrs_or_encoding.get("geometry", None):
|
|
1835
|
+
gtype = self._obj[geometry].attrs["geometry_type"]
|
|
1836
|
+
vardict.setdefault(gtype, [])
|
|
1837
|
+
if geometry not in vardict[gtype]:
|
|
1838
|
+
vardict[gtype] += [geometry]
|
|
1839
|
+
return {type_: sort_maybe_hashable(v) for type_, v in vardict.items()}
|
|
1840
|
+
|
|
1766
1841
|
def get_associated_variable_names(
|
|
1767
1842
|
self, name: Hashable, skip_bounds: bool = False, error: bool = True
|
|
1768
1843
|
) -> dict[str, list[Hashable]]:
|
|
@@ -1797,15 +1872,15 @@ class CFAccessor:
|
|
|
1797
1872
|
"bounds",
|
|
1798
1873
|
"grid_mapping",
|
|
1799
1874
|
"grid",
|
|
1875
|
+
"geometry",
|
|
1800
1876
|
]
|
|
1801
1877
|
|
|
1802
1878
|
coords: dict[str, list[Hashable]] = {k: [] for k in keys}
|
|
1803
1879
|
attrs_or_encoding = ChainMap(self._obj[name].attrs, self._obj[name].encoding)
|
|
1804
1880
|
|
|
1805
|
-
coordinates = attrs_or_encoding.get("coordinates", None)
|
|
1806
1881
|
# Handles case where the coordinates attribute is None
|
|
1807
1882
|
# This is used to tell xarray to not write a coordinates attribute
|
|
1808
|
-
if coordinates:
|
|
1883
|
+
if coordinates := attrs_or_encoding.get("coordinates", None):
|
|
1809
1884
|
coords["coordinates"] = coordinates.split(" ")
|
|
1810
1885
|
|
|
1811
1886
|
if "cell_measures" in attrs_or_encoding:
|
|
@@ -1824,27 +1899,32 @@ class CFAccessor:
|
|
|
1824
1899
|
)
|
|
1825
1900
|
coords["cell_measures"] = []
|
|
1826
1901
|
|
|
1827
|
-
if (
|
|
1828
|
-
|
|
1829
|
-
and "ancillary_variables" in attrs_or_encoding
|
|
1902
|
+
if isinstance(self._obj, Dataset) and (
|
|
1903
|
+
anc := attrs_or_encoding.get("ancillary_variables", None)
|
|
1830
1904
|
):
|
|
1831
|
-
coords["ancillary_variables"] =
|
|
1832
|
-
"ancillary_variables"
|
|
1833
|
-
].split(" ")
|
|
1905
|
+
coords["ancillary_variables"] = anc.split(" ")
|
|
1834
1906
|
|
|
1835
1907
|
if not skip_bounds:
|
|
1836
|
-
if "bounds"
|
|
1837
|
-
coords["bounds"] = [
|
|
1908
|
+
if bounds := attrs_or_encoding.get("bounds", None):
|
|
1909
|
+
coords["bounds"] = [bounds]
|
|
1838
1910
|
for dim in self._obj[name].dims:
|
|
1839
|
-
dbounds
|
|
1840
|
-
if dbounds:
|
|
1911
|
+
if dbounds := self._obj[dim].attrs.get("bounds", None):
|
|
1841
1912
|
coords["bounds"].append(dbounds)
|
|
1842
1913
|
|
|
1843
|
-
|
|
1844
|
-
|
|
1914
|
+
for attrname in ["grid", "grid_mapping"]:
|
|
1915
|
+
if maybe := attrs_or_encoding.get(attrname, None):
|
|
1916
|
+
coords[attrname] = [maybe]
|
|
1845
1917
|
|
|
1846
|
-
|
|
1847
|
-
|
|
1918
|
+
more: Sequence[Hashable] = ()
|
|
1919
|
+
if geometry_var := attrs_or_encoding.get("geometry", None):
|
|
1920
|
+
coords["geometry"] = [geometry_var]
|
|
1921
|
+
_attrs = ChainMap(
|
|
1922
|
+
self._obj[geometry_var].attrs, self._obj[geometry_var].encoding
|
|
1923
|
+
)
|
|
1924
|
+
more = _parse_related_geometry_vars(_attrs)
|
|
1925
|
+
elif "geometry_type" in attrs_or_encoding:
|
|
1926
|
+
more = _parse_related_geometry_vars(attrs_or_encoding)
|
|
1927
|
+
coords["geometry"].extend(more)
|
|
1848
1928
|
|
|
1849
1929
|
allvars = itertools.chain(*coords.values())
|
|
1850
1930
|
missing = set(allvars) - set(self._maybe_to_dataset()._variables)
|
|
@@ -2276,8 +2356,8 @@ class CFDatasetAccessor(CFAccessor):
|
|
|
2276
2356
|
References
|
|
2277
2357
|
----------
|
|
2278
2358
|
Please refer to the CF conventions document :
|
|
2279
|
-
1. http://cfconventions.org/Data/cf-conventions/cf-conventions-1.
|
|
2280
|
-
2. http://cfconventions.org/Data/cf-conventions/cf-conventions-1.
|
|
2359
|
+
1. http://cfconventions.org/Data/cf-conventions/cf-conventions-1.11/cf-conventions.html#parametric-vertical-coordinate
|
|
2360
|
+
2. http://cfconventions.org/Data/cf-conventions/cf-conventions-1.11/cf-conventions.html#parametric-v-coord.
|
|
2281
2361
|
|
|
2282
2362
|
Examples
|
|
2283
2363
|
--------
|
|
@@ -2553,7 +2633,7 @@ class CFDatasetAccessor(CFAccessor):
|
|
|
2553
2633
|
|
|
2554
2634
|
References
|
|
2555
2635
|
----------
|
|
2556
|
-
Please refer to the CF conventions document : http://cfconventions.org/Data/cf-conventions/cf-conventions-1.
|
|
2636
|
+
Please refer to the CF conventions document : http://cfconventions.org/Data/cf-conventions/cf-conventions-1.11/cf-conventions.html#cell-boundaries.
|
|
2557
2637
|
"""
|
|
2558
2638
|
if keys is None:
|
|
2559
2639
|
coords = tuple(self.keys())
|
|
@@ -2762,8 +2842,8 @@ class CFDataArrayAccessor(CFAccessor):
|
|
|
2762
2842
|
References
|
|
2763
2843
|
----------
|
|
2764
2844
|
Please refer to the CF conventions document :
|
|
2765
|
-
1. http://cfconventions.org/Data/cf-conventions/cf-conventions-1.
|
|
2766
|
-
2. http://cfconventions.org/Data/cf-conventions/cf-conventions-1.
|
|
2845
|
+
1. http://cfconventions.org/Data/cf-conventions/cf-conventions-1.11/cf-conventions.html#parametric-vertical-coordinate
|
|
2846
|
+
2. http://cfconventions.org/Data/cf-conventions/cf-conventions-1.11/cf-conventions.html#parametric-v-coord.
|
|
2767
2847
|
|
|
2768
2848
|
Examples
|
|
2769
2849
|
--------
|
|
@@ -12,7 +12,10 @@ except ImportError:
|
|
|
12
12
|
from collections.abc import Mapping, MutableMapping
|
|
13
13
|
from typing import Any
|
|
14
14
|
|
|
15
|
+
#: CF Roles understood by cf-xarray
|
|
15
16
|
_DSG_ROLES = ["timeseries_id", "profile_id", "trajectory_id"]
|
|
17
|
+
#: Geometry types understood by cf-xarray
|
|
18
|
+
_GEOMETRY_TYPES = ("line", "point", "polygon")
|
|
16
19
|
|
|
17
20
|
cf_role_criteria: Mapping[str, Mapping[str, str]] = {
|
|
18
21
|
k: {"cf_role": k}
|
|
@@ -31,6 +34,12 @@ grid_mapping_var_criteria: Mapping[str, Mapping[str, Any]] = {
|
|
|
31
34
|
"grid_mapping": {"grid_mapping_name": re.compile(".")}
|
|
32
35
|
}
|
|
33
36
|
|
|
37
|
+
# A geometry container is anything with a geometry_type attribute
|
|
38
|
+
geometry_var_criteria: Mapping[str, Mapping[str, Any]] = {
|
|
39
|
+
"geometry": {"geometry_type": re.compile(".")},
|
|
40
|
+
**{k: {"geometry_type": k} for k in _GEOMETRY_TYPES},
|
|
41
|
+
}
|
|
42
|
+
|
|
34
43
|
coordinate_criteria: MutableMapping[str, MutableMapping[str, tuple]] = {
|
|
35
44
|
"latitude": {
|
|
36
45
|
"standard_name": ("latitude",),
|
|
@@ -748,3 +748,32 @@ sgrid_delft3["grid"] = xr.DataArray(
|
|
|
748
748
|
node_coordinates="node_lon node_lat node_elevation",
|
|
749
749
|
),
|
|
750
750
|
)
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
def point_dataset():
|
|
754
|
+
from shapely.geometry import MultiPoint, Point
|
|
755
|
+
|
|
756
|
+
da = xr.DataArray(
|
|
757
|
+
[
|
|
758
|
+
MultiPoint([(1.0, 2.0), (2.0, 3.0)]),
|
|
759
|
+
Point(3.0, 4.0),
|
|
760
|
+
Point(4.0, 5.0),
|
|
761
|
+
Point(3.0, 4.0),
|
|
762
|
+
],
|
|
763
|
+
dims=("index",),
|
|
764
|
+
name="geometry",
|
|
765
|
+
)
|
|
766
|
+
ds = da.to_dataset()
|
|
767
|
+
return ds
|
|
768
|
+
|
|
769
|
+
|
|
770
|
+
def encoded_point_dataset():
|
|
771
|
+
from .geometry import encode_geometries
|
|
772
|
+
|
|
773
|
+
ds = encode_geometries(point_dataset())
|
|
774
|
+
ds["data"] = (
|
|
775
|
+
"index",
|
|
776
|
+
np.arange(ds.sizes["index"]),
|
|
777
|
+
{"geometry": "geometry_container"},
|
|
778
|
+
)
|
|
779
|
+
return ds
|
|
@@ -110,9 +110,11 @@ def _print_rows(subtitle: str, rows: list[str], rich: bool):
|
|
|
110
110
|
|
|
111
111
|
# Add subtitle to the first row, align other rows
|
|
112
112
|
rows = [
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
113
|
+
(
|
|
114
|
+
_format_subtitle(subtitle, rich=rich) + row
|
|
115
|
+
if i == 0
|
|
116
|
+
else len(subtitle) * " " + row
|
|
117
|
+
)
|
|
116
118
|
for i, row in enumerate(rows)
|
|
117
119
|
]
|
|
118
120
|
|
|
@@ -293,6 +295,17 @@ def _format_dsg_roles(accessor, dims, rich):
|
|
|
293
295
|
)
|
|
294
296
|
|
|
295
297
|
|
|
298
|
+
def _format_geometries(accessor, dims, rich):
|
|
299
|
+
yield make_text_section(
|
|
300
|
+
accessor,
|
|
301
|
+
"CF Geometries",
|
|
302
|
+
"geometries",
|
|
303
|
+
dims=dims,
|
|
304
|
+
# valid_keys=_DSG_ROLES,
|
|
305
|
+
rich=rich,
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
|
|
296
309
|
def _format_coordinates(accessor, dims, coords, rich):
|
|
297
310
|
from .accessor import _AXIS_NAMES, _CELL_MEASURES, _COORD_NAMES
|
|
298
311
|
|