|
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 (
|
@@ -1445,16 +1446,19 @@ def differentiate(
|
1445 | 1446 | self, coord, *xr_args, positive_upward: bool = False, **xr_kwargs
|
1446 | 1447 | ):
|
1447 | 1448 | """
|
| 1449 | + Differentiate an xarray object. |
| 1450 | +
|
1448 | 1451 | Parameters
|
1449 | 1452 | ----------
|
1450 |
| - xr_args, xr_kwargs are passed directly to the underlying xarray function. |
1451 |
| - The following are added by cf_xarray: |
1452 |
| -
|
1453 | 1453 | positive_upward: optional, bool
|
1454 | 1454 | Change sign of the derivative based on the ``"positive"`` attribute of ``coord``
|
1455 | 1455 | so that positive values indicate increasing upward.
|
1456 | 1456 | If ``positive=="down"``, then multiplied by -1.
|
1457 | 1457 |
|
| 1458 | + Notes |
| 1459 | + ----- |
| 1460 | + ``xr_args``, ``xr_kwargs`` are passed directly to the underlying xarray function. |
| 1461 | +
|
1458 | 1462 | See Also
|
1459 | 1463 | --------
|
1460 | 1464 | DataArray.cf.differentiate
|
@@ -1515,6 +1519,16 @@ def __getitem__(self, key: Union[str, List[str]]) -> Union[DataArray, Dataset]:
|
1515 | 1519 | """
|
1516 | 1520 | return _getitem(self, key)
|
1517 | 1521 |
|
| 1522 | + @property |
| 1523 | + def formula_terms(self) -> Dict[str, Dict[str, str]]: |
| 1524 | + """ |
| 1525 | + Property that returns a dictionary |
| 1526 | + {parametric_coord_name: {standard_term_name: variable_name}} |
| 1527 | + """ |
| 1528 | + return { |
| 1529 | + dim: self._obj[dim].cf.formula_terms for dim in _get_dims(self._obj, "Z") |
| 1530 | + } |
| 1531 | + |
1518 | 1532 | @property
|
1519 | 1533 | def bounds(self) -> Dict[str, List[str]]:
|
1520 | 1534 | """
|
@@ -1719,36 +1733,29 @@ def decode_vertical_coords(self, prefix="z"):
|
1719 | 1733 | .. warning::
|
1720 | 1734 | Very lightly tested. Please double check the results.
|
1721 | 1735 | """
|
1722 |
| - import re |
1723 |
| - |
1724 | 1736 | ds = self._obj
|
1725 |
| - dims = _get_dims(ds, "Z") |
1726 | 1737 |
|
1727 | 1738 | requirements = {
|
1728 | 1739 | "ocean_s_coordinate_g1": {"depth_c", "depth", "s", "C", "eta"},
|
1729 | 1740 | "ocean_s_coordinate_g2": {"depth_c", "depth", "s", "C", "eta"},
|
1730 | 1741 | }
|
1731 | 1742 |
|
1732 |
| - for dim in dims: |
| 1743 | + allterms = self.formula_terms |
| 1744 | + for dim in allterms: |
1733 | 1745 | suffix = dim.split("_")
|
1734 | 1746 | zname = f"{prefix}_" + "_".join(suffix[1:])
|
1735 | 1747 |
|
1736 |
| - if ( |
1737 |
| - "formula_terms" not in ds[dim].attrs |
1738 |
| - or "standard_name" not in ds[dim].attrs |
1739 |
| - ): |
| 1748 | + if "standard_name" not in ds[dim].attrs: |
1740 | 1749 | continue
|
1741 |
| - |
1742 |
| - formula_terms = ds[dim].attrs["formula_terms"] |
1743 | 1750 | stdname = ds[dim].attrs["standard_name"]
|
1744 | 1751 |
|
1745 | 1752 | # map "standard" formula term names to actual variable names
|
1746 | 1753 | terms = {}
|
1747 |
| - for mapping in re.sub(": ", ":", formula_terms).split(" "): |
1748 |
| - key, value = mapping.split(":") |
| 1754 | + for key, value in allterms[dim].items(): |
1749 | 1755 | if value not in ds:
|
1750 | 1756 | raise KeyError(
|
1751 |
| - f"Variable {value!r} is required to decode coordinate for {dim} but it is absent in the Dataset." |
| 1757 | + f"Variable {value!r} is required to decode coordinate for {dim!r}" |
| 1758 | + " but it is absent in the Dataset." |
1752 | 1759 | )
|
1753 | 1760 | terms[key] = ds[value]
|
1754 | 1761 |
|
@@ -1776,12 +1783,30 @@ def decode_vertical_coords(self, prefix="z"):
|
1776 | 1783 |
|
1777 | 1784 | else:
|
1778 | 1785 | raise NotImplementedError(
|
1779 |
| - f"Coordinate function for {stdname} not implemented yet. Contributions welcome!" |
| 1786 | + f"Coordinate function for {stdname!r} not implemented yet. Contributions welcome!" |
1780 | 1787 | )
|
1781 | 1788 |
|
1782 | 1789 |
|
1783 | 1790 | @xr.register_dataarray_accessor("cf")
|
1784 | 1791 | class CFDataArrayAccessor(CFAccessor):
|
| 1792 | + @property |
| 1793 | + def formula_terms(self) -> Dict[str, str]: |
| 1794 | + """ |
| 1795 | + Property that returns a dictionary |
| 1796 | + {parametric_coord_name: {standard_term_name: variable_name}} |
| 1797 | + """ |
| 1798 | + da = self._obj |
| 1799 | + if "formula_terms" not in da.attrs: |
| 1800 | + var = da[_single(_get_dims)(da, "Z")[0]] |
| 1801 | + else: |
| 1802 | + var = da |
| 1803 | + terms = {} |
| 1804 | + formula_terms = var.attrs.get("formula_terms", "") |
| 1805 | + for mapping in re.sub(r"\s*:\s*", ":", formula_terms).split(): |
| 1806 | + key, value = mapping.split(":") |
| 1807 | + terms[key] = value |
| 1808 | + return terms |
| 1809 | + |
1785 | 1810 | def __getitem__(self, key: Union[str, List[str]]) -> DataArray:
|
1786 | 1811 | """
|
1787 | 1812 | Index into a DataArray making use of CF attributes.
|
|
0 commit comments