cf-xarray 0.9.2__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.2 → cf_xarray-0.9.3}/.github/workflows/ci.yaml +2 -2
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.github/workflows/pypi.yaml +2 -2
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.pre-commit-config.yaml +5 -5
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/PKG-INFO +2 -2
- cf_xarray-0.9.3/cf_xarray/_version.py +1 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/geometry.py +49 -9
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/tests/test_geometry.py +7 -7
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/tests/test_units.py +5 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/units.py +46 -51
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray.egg-info/PKG-INFO +2 -2
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray.egg-info/requires.txt +1 -1
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/units.md +2 -2
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/pyproject.toml +1 -1
- cf_xarray-0.9.2/cf_xarray/_version.py +0 -1
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.binder/environment.yml +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.deepsource.toml +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.github/dependabot.yml +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.github/workflows/parse_logs.py +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.github/workflows/testpypi-release.yaml +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.github/workflows/upstream-dev-ci.yaml +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.gitignore +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.readthedocs.yml +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/.tributors +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/CITATION.cff +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/LICENSE +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/README.rst +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/__init__.py +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/accessor.py +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/coding.py +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/criteria.py +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/datasets.py +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/formatting.py +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/helpers.py +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/options.py +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/py.typed +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/scripts/make_doc.py +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/scripts/print_versions.py +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/sgrid.py +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/tests/__init__.py +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/tests/conftest.py +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/tests/test_accessor.py +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/tests/test_coding.py +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/tests/test_helpers.py +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/tests/test_options.py +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/tests/test_scripts.py +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray/utils.py +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray.egg-info/SOURCES.txt +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray.egg-info/dependency_links.txt +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/cf_xarray.egg-info/top_level.txt +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/ci/doc.yml +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/ci/environment-no-optional-deps.yml +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/ci/environment.yml +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/ci/upstream-dev-env.yml +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/codecov.yml +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/2D_bounds_averaged.png +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/2D_bounds_error.png +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/2D_bounds_nonunique.png +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/Makefile +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/_static/dataset-diagram-logo.tex +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/_static/full-logo.png +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/_static/logo.png +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/_static/logo.svg +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/_static/rich-repr-example.png +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/_static/style.css +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/api.rst +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/bounds.md +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/cartopy_rotated_pole.png +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/coding.md +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/conf.py +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/contributing.rst +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/coord_axes.md +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/custom-criteria.md +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/dsg.md +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/examples/introduction.ipynb +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/faq.md +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/flags.md +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/geometry.md +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/grid_mappings.md +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/howtouse.md +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/index.rst +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/make.bat +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/parametricz.md +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/plotting.md +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/provenance.md +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/quickstart.md +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/roadmap.rst +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/selecting.md +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/sgrid_ugrid.md +0 -0
- {cf_xarray-0.9.2 → cf_xarray-0.9.3}/doc/whats-new.rst +0 -0
- {cf_xarray-0.9.2 → 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: 24.
|
|
19
|
+
rev: 24.4.2
|
|
20
20
|
hooks:
|
|
21
21
|
- id: black
|
|
22
22
|
|
|
@@ -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"
|
|
@@ -17,6 +17,19 @@ __all__ = [
|
|
|
17
17
|
]
|
|
18
18
|
|
|
19
19
|
|
|
20
|
+
# Useful convention language:
|
|
21
|
+
# 1. Whether linked to normal CF space-time coordinates with a nodes attribute or not, inclusion of such coordinates is
|
|
22
|
+
# recommended to maintain backward compatibility with software that has not implemented geometry capabilities.
|
|
23
|
+
# 2. The geometry node coordinate variables must each have an axis attribute whose allowable values are X, Y, and Z.
|
|
24
|
+
# 3. If a coordinates attribute is carried by the geometry container variable or its parent data variable, then those coordinate variables
|
|
25
|
+
# that have a meaningful correspondence with node coordinates are indicated as such by a nodes attribute that names the corresponding node
|
|
26
|
+
# coordinates, but only if the grid_mapping associated the geometry node variables is the same as that of the coordinate variables.
|
|
27
|
+
# If a different grid mapping is used, then the provided coordinates must not have the nodes attribute.
|
|
28
|
+
#
|
|
29
|
+
# Interpretation:
|
|
30
|
+
# 1. node coordinates are exact; the 'normal' coordinates are a reasonable value to use, if you do not know how to interpret the nodes.
|
|
31
|
+
|
|
32
|
+
|
|
20
33
|
def decode_geometries(encoded: xr.Dataset) -> xr.Dataset:
|
|
21
34
|
"""
|
|
22
35
|
Decode CF encoded geometries to a numpy object array containing shapely geometries.
|
|
@@ -282,6 +295,14 @@ def shapely_to_cf(geometries: xr.DataArray | Sequence, grid_mapping: str | None
|
|
|
282
295
|
----------
|
|
283
296
|
Please refer to the CF conventions document: http://cfconventions.org/Data/cf-conventions/cf-conventions-1.8/cf-conventions.html#geometries
|
|
284
297
|
"""
|
|
298
|
+
|
|
299
|
+
if isinstance(geometries, xr.DataArray) and grid_mapping is not None:
|
|
300
|
+
raise DeprecationWarning(
|
|
301
|
+
"Explicitly passing `grid_mapping` with DataArray of geometries is deprecated. "
|
|
302
|
+
"Please set a `grid_mapping` attribute on `geometries`, ",
|
|
303
|
+
"and set the grid mapping variable as a coordinate",
|
|
304
|
+
)
|
|
305
|
+
|
|
285
306
|
# Get all types to call the appropriate translation function.
|
|
286
307
|
types = {
|
|
287
308
|
geom.item().geom_type if isinstance(geom, xr.DataArray) else geom.geom_type
|
|
@@ -300,19 +321,38 @@ def shapely_to_cf(geometries: xr.DataArray | Sequence, grid_mapping: str | None
|
|
|
300
321
|
|
|
301
322
|
ds[GEOMETRY_CONTAINER_NAME].attrs.update(coordinates="crd_x crd_y")
|
|
302
323
|
|
|
324
|
+
if (
|
|
325
|
+
grid_mapping is None
|
|
326
|
+
and isinstance(geometries, xr.DataArray)
|
|
327
|
+
and (grid_mapping_varname := geometries.attrs.get("grid_mapping"))
|
|
328
|
+
):
|
|
329
|
+
if grid_mapping_varname in geometries.coords:
|
|
330
|
+
grid_mapping = geometries.coords[grid_mapping_varname].attrs[
|
|
331
|
+
"grid_mapping_name"
|
|
332
|
+
]
|
|
333
|
+
for name_ in ["x", "y", "crd_x", "crd_y"]:
|
|
334
|
+
ds[name_].attrs["grid_mapping"] = grid_mapping_varname
|
|
335
|
+
|
|
303
336
|
# Special treatment of selected grid mappings
|
|
304
|
-
if grid_mapping
|
|
305
|
-
# Special case for longitude_latitude grid
|
|
337
|
+
if grid_mapping in ["latitude_longitude", "rotated_latitude_longitude"]:
|
|
338
|
+
# Special case for longitude_latitude type grid mappings
|
|
306
339
|
ds = ds.rename(crd_x="lon", crd_y="lat")
|
|
307
|
-
|
|
308
|
-
|
|
340
|
+
if grid_mapping == "latitude_longitude":
|
|
341
|
+
ds.lon.attrs.update(units="degrees_east", standard_name="longitude")
|
|
342
|
+
ds.x.attrs.update(units="degrees_east", standard_name="longitude")
|
|
343
|
+
ds.lat.attrs.update(units="degrees_north", standard_name="latitude")
|
|
344
|
+
ds.y.attrs.update(units="degrees_north", standard_name="latitude")
|
|
345
|
+
elif grid_mapping == "rotated_latitude_longitude":
|
|
346
|
+
ds.lon.attrs.update(units="degrees", standard_name="grid_longitude")
|
|
347
|
+
ds.x.attrs.update(units="degrees", standard_name="grid_longitude")
|
|
348
|
+
ds.lat.attrs.update(units="degrees", standard_name="grid_latitude")
|
|
349
|
+
ds.y.attrs.update(units="degrees", standard_name="grid_latitude")
|
|
309
350
|
ds[GEOMETRY_CONTAINER_NAME].attrs.update(coordinates="lon lat")
|
|
310
|
-
ds.x.attrs.update(units="degrees_east", standard_name="longitude")
|
|
311
|
-
ds.y.attrs.update(units="degrees_north", standard_name="latitude")
|
|
312
351
|
elif grid_mapping is not None:
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
)
|
|
352
|
+
ds.crd_x.attrs.update(standard_name="projection_x_coordinate")
|
|
353
|
+
ds.x.attrs.update(standard_name="projection_x_coordinate")
|
|
354
|
+
ds.crd_y.attrs.update(standard_name="projection_y_coordinate")
|
|
355
|
+
ds.y.attrs.update(standard_name="projection_y_coordinate")
|
|
316
356
|
|
|
317
357
|
return ds
|
|
318
358
|
|
|
@@ -265,8 +265,8 @@ def test_shapely_to_cf(geometry_ds):
|
|
|
265
265
|
[
|
|
266
266
|
in_ds.drop_vars("geometry").isel(index=slice(1, None)),
|
|
267
267
|
cfxr.shapely_to_cf(
|
|
268
|
-
in_ds.geometry.isel(index=slice(1, None)),
|
|
269
|
-
grid_mapping="
|
|
268
|
+
in_ds.geometry.isel(index=slice(1, None)).data,
|
|
269
|
+
grid_mapping="latitude_longitude",
|
|
270
270
|
),
|
|
271
271
|
]
|
|
272
272
|
)
|
|
@@ -355,10 +355,11 @@ def test_shapely_to_cf_errors():
|
|
|
355
355
|
with pytest.raises(ValueError, match="Mixed geometry types are not supported"):
|
|
356
356
|
cfxr.shapely_to_cf(geoms)
|
|
357
357
|
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
)
|
|
361
|
-
|
|
358
|
+
encoded = cfxr.shapely_to_cf(
|
|
359
|
+
[Point(4, 5)], grid_mapping="albers_conical_equal_area"
|
|
360
|
+
)
|
|
361
|
+
assert encoded["x"].attrs["standard_name"] == "projection_x_coordinate"
|
|
362
|
+
assert encoded["y"].attrs["standard_name"] == "projection_y_coordinate"
|
|
362
363
|
|
|
363
364
|
|
|
364
365
|
@requires_shapely
|
|
@@ -491,7 +492,6 @@ def test_reshape_unique_geometries(geometry_ds):
|
|
|
491
492
|
|
|
492
493
|
@requires_shapely
|
|
493
494
|
def test_encode_decode(geometry_ds, polygon_geometry):
|
|
494
|
-
|
|
495
495
|
geom_dim_ds = xr.Dataset()
|
|
496
496
|
geom_dim_ds = geom_dim_ds.assign_coords(
|
|
497
497
|
xr.Coordinates(
|
|
@@ -75,11 +75,16 @@ def test_udunits_power_syntax_parse_units():
|
|
|
75
75
|
("m ** -1", "m-1"),
|
|
76
76
|
("m ** 2 / s ** 2", "m2 s-2"),
|
|
77
77
|
("m ** 3 / (kg * s ** 2)", "m3 kg-1 s-2"),
|
|
78
|
+
("", "1"),
|
|
78
79
|
),
|
|
79
80
|
)
|
|
80
81
|
def test_udunits_format(units, expected):
|
|
81
82
|
u = ureg.parse_units(units)
|
|
83
|
+
if units == "":
|
|
84
|
+
# The non-shortened dimensionless can only work with recent pint
|
|
85
|
+
pytest.importorskip("pint", minversion="0.24.1")
|
|
82
86
|
|
|
87
|
+
assert f"{u:~cf}" == expected
|
|
83
88
|
assert f"{u:cf}" == expected
|
|
84
89
|
|
|
85
90
|
|
|
@@ -4,62 +4,57 @@ import functools
|
|
|
4
4
|
import re
|
|
5
5
|
|
|
6
6
|
import pint
|
|
7
|
-
from
|
|
8
|
-
DimensionalityError,
|
|
9
|
-
UndefinedUnitError,
|
|
10
|
-
UnitStrippedWarning,
|
|
11
|
-
)
|
|
7
|
+
from packaging.version import Version
|
|
12
8
|
|
|
13
9
|
from .utils import emit_user_level_warning
|
|
14
10
|
|
|
15
|
-
# from `xclim`'s unit support module with permission of the maintainers
|
|
16
|
-
try:
|
|
17
11
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
12
|
+
@pint.register_unit_format("cf")
|
|
13
|
+
def short_formatter(unit, registry, **options):
|
|
14
|
+
"""Return a CF-compliant unit string from a `pint` unit.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
unit : pint.UnitContainer
|
|
19
|
+
Input unit.
|
|
20
|
+
registry : pint.UnitRegistry
|
|
21
|
+
The associated registry
|
|
22
|
+
**options
|
|
23
|
+
Additional options (may be ignored)
|
|
24
|
+
|
|
25
|
+
Returns
|
|
26
|
+
-------
|
|
27
|
+
out : str
|
|
28
|
+
Units following CF-Convention, using symbols.
|
|
29
|
+
"""
|
|
30
|
+
# pint 0.24.1 gives {"dimensionless": 1} for non-shortened dimensionless units
|
|
31
|
+
# CF uses "1" to denote fractions and dimensionless quantities
|
|
32
|
+
if unit == {"dimensionless": 1} or not unit:
|
|
33
|
+
return "1"
|
|
34
|
+
|
|
35
|
+
# If u is a name, get its symbol (same as pint's "~" pre-formatter)
|
|
36
|
+
# otherwise, assume a symbol (pint should have already raised on invalid units before this)
|
|
37
|
+
unit = pint.util.UnitsContainer(
|
|
38
|
+
{
|
|
39
|
+
registry._get_symbol(u) if u in registry._units else u: exp
|
|
40
|
+
for u, exp in unit.items()
|
|
41
|
+
}
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# Change in formatter signature in pint 0.24
|
|
45
|
+
if Version(pint.__version__) < Version("0.24"):
|
|
46
|
+
args = (unit.items(),)
|
|
47
|
+
else:
|
|
48
|
+
# Numerators splitted from denominators
|
|
49
|
+
args = (
|
|
50
|
+
((u, e) for u, e in unit.items() if e >= 0),
|
|
51
|
+
((u, e) for u, e in unit.items() if e < 0),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
out = pint.formatter(*args, as_ratio=False, product_fmt=" ", power_fmt="{}{}")
|
|
55
|
+
# To avoid potentiel unicode problems in netCDF. In both cases, this unit is not recognized by udunits
|
|
56
|
+
return out.replace("Δ°", "delta_deg")
|
|
60
57
|
|
|
61
|
-
except ImportError:
|
|
62
|
-
pass
|
|
63
58
|
|
|
64
59
|
# ------
|
|
65
60
|
# Reused with modification from MetPy under the terms of the BSD 3-Clause License.
|
|
@@ -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"
|
|
@@ -16,7 +16,7 @@ hide-toc: true
|
|
|
16
16
|
|
|
17
17
|
The xarray ecosystem supports unit-aware arrays using [pint](https://pint.readthedocs.io) and [pint-xarray](https://pint-xarray.readthedocs.io). Some changes are required to make these packages work well with [UDUNITS format recommended by the CF conventions](http://cfconventions.org/Data/cf-conventions/cf-conventions-1.8/cf-conventions.html#units).
|
|
18
18
|
|
|
19
|
-
`cf_xarray` makes those recommended changes when you `import cf_xarray.units`. These changes allow pint to parse and format UDUNIT units strings, and add several custom units like `degrees_north` for latitude, `psu` for ocean salinity, etc.
|
|
19
|
+
`cf_xarray` makes those recommended changes when you `import cf_xarray.units`. These changes allow pint to parse and format UDUNIT units strings, and add several custom units like `degrees_north` for latitude, `psu` for ocean salinity, etc. Be aware that pint supports some units that UDUNITS does not recognize but `cf-xarray` will not try to detect them and raise an error. For example, a temperature subtraction returns "delta_degC" units in pint, which does not exist in UDUNITS.
|
|
20
20
|
|
|
21
21
|
## Formatting units
|
|
22
22
|
|
|
@@ -27,5 +27,5 @@ from pint import application_registry as ureg
|
|
|
27
27
|
import cf_xarray.units
|
|
28
28
|
|
|
29
29
|
u = ureg.Unit("m ** 3 / s ** 2")
|
|
30
|
-
f"{u:~cf}
|
|
30
|
+
f"{u:cf}" # or {u:~cf}, both return the same short format
|
|
31
31
|
```
|
|
@@ -22,7 +22,7 @@ dependencies = [
|
|
|
22
22
|
dynamic = ["version"]
|
|
23
23
|
|
|
24
24
|
[project.optional-dependencies]
|
|
25
|
-
all = ["matplotlib", "pint", "shapely", "regex", "rich", "pooch"]
|
|
25
|
+
all = ["matplotlib", "pint >=0.18, !=0.24.0", "shapely", "regex", "rich", "pooch"]
|
|
26
26
|
|
|
27
27
|
[project.urls]
|
|
28
28
|
homepage = "https://cf-xarray.readthedocs.io"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.9.2"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|