|
1 | 1 | import functools
|
2 | 2 | import inspect
|
3 | 3 | import itertools
|
| 4 | +import re |
4 | 5 | import warnings
|
5 | 6 | from collections import ChainMap
|
6 | 7 | from typing import (
|
@@ -1480,6 +1481,16 @@ def __getitem__(self, key: Union[str, List[str]]) -> Union[DataArray, Dataset]:
|
1480 | 1481 | """
|
1481 | 1482 | return _getitem(self, key)
|
1482 | 1483 |
|
| 1484 | + @property |
| 1485 | + def formula_terms(self) -> Dict[str, Dict[str, str]]: |
| 1486 | + """ |
| 1487 | + Property that returns a dictionary |
| 1488 | + {parametric_coord_name: {standard_term_name: variable_name}} |
| 1489 | + """ |
| 1490 | + return { |
| 1491 | + dim: self._obj[dim].cf.formula_terms for dim in _get_dims(self._obj, "Z") |
| 1492 | + } |
| 1493 | + |
1483 | 1494 | def get_bounds(self, key: str) -> DataArray:
|
1484 | 1495 | """
|
1485 | 1496 | Get bounds variable corresponding to key.
|
@@ -1668,36 +1679,29 @@ def decode_vertical_coords(self, prefix="z"):
|
1668 | 1679 | .. warning::
|
1669 | 1680 | Very lightly tested. Please double check the results.
|
1670 | 1681 | """
|
1671 |
| - import re |
1672 |
| - |
1673 | 1682 | ds = self._obj
|
1674 |
| - dims = _get_dims(ds, "Z") |
1675 | 1683 |
|
1676 | 1684 | requirements = {
|
1677 | 1685 | "ocean_s_coordinate_g1": {"depth_c", "depth", "s", "C", "eta"},
|
1678 | 1686 | "ocean_s_coordinate_g2": {"depth_c", "depth", "s", "C", "eta"},
|
1679 | 1687 | }
|
1680 | 1688 |
|
1681 |
| - for dim in dims: |
| 1689 | + allterms = self.formula_terms |
| 1690 | + for dim in allterms: |
1682 | 1691 | suffix = dim.split("_")
|
1683 | 1692 | zname = f"{prefix}_" + "_".join(suffix[1:])
|
1684 | 1693 |
|
1685 |
| - if ( |
1686 |
| - "formula_terms" not in ds[dim].attrs |
1687 |
| - or "standard_name" not in ds[dim].attrs |
1688 |
| - ): |
| 1694 | + if "standard_name" not in ds[dim].attrs: |
1689 | 1695 | continue
|
1690 |
| - |
1691 |
| - formula_terms = ds[dim].attrs["formula_terms"] |
1692 | 1696 | stdname = ds[dim].attrs["standard_name"]
|
1693 | 1697 |
|
1694 | 1698 | # map "standard" formula term names to actual variable names
|
1695 | 1699 | terms = {}
|
1696 |
| - for mapping in re.sub(": ", ":", formula_terms).split(" "): |
1697 |
| - key, value = mapping.split(":") |
| 1700 | + for key, value in allterms[dim].items(): |
1698 | 1701 | if value not in ds:
|
1699 | 1702 | raise KeyError(
|
1700 |
| - f"Variable {value!r} is required to decode coordinate for {dim} but it is absent in the Dataset." |
| 1703 | + f"Variable {value!r} is required to decode coordinate for {dim!r}" |
| 1704 | + " but it is absent in the Dataset." |
1701 | 1705 | )
|
1702 | 1706 | terms[key] = ds[value]
|
1703 | 1707 |
|
@@ -1725,12 +1729,27 @@ def decode_vertical_coords(self, prefix="z"):
|
1725 | 1729 |
|
1726 | 1730 | else:
|
1727 | 1731 | raise NotImplementedError(
|
1728 |
| - f"Coordinate function for {stdname} not implemented yet. Contributions welcome!" |
| 1732 | + f"Coordinate function for {stdname!r} not implemented yet. Contributions welcome!" |
1729 | 1733 | )
|
1730 | 1734 |
|
1731 | 1735 |
|
1732 | 1736 | @xr.register_dataarray_accessor("cf")
|
1733 | 1737 | class CFDataArrayAccessor(CFAccessor):
|
| 1738 | + @property |
| 1739 | + def formula_terms(self) -> Dict[str, str]: |
| 1740 | + """ |
| 1741 | + Property that returns a dictionary |
| 1742 | + {parametric_coord_name: {standard_term_name: variable_name}} |
| 1743 | + """ |
| 1744 | + da = self._obj |
| 1745 | + dims = _single(_get_dims)(da, "Z")[0] |
| 1746 | + terms = {} |
| 1747 | + formula_terms = da[dims].attrs.get("formula_terms", "") |
| 1748 | + for mapping in re.sub(r"\s*:\s*", ":", formula_terms).split(): |
| 1749 | + key, value = mapping.split(":") |
| 1750 | + terms[key] = value |
| 1751 | + return terms |
| 1752 | + |
1734 | 1753 | def __getitem__(self, key: Union[str, List[str]]) -> DataArray:
|
1735 | 1754 | """
|
1736 | 1755 | Index into a DataArray making use of CF attributes.
|
|
0 commit comments