Source code for geoips.utils.types.datatree_helpers
# # # This source code is subject to the license referenced at
# # # https://github.com/NRLMMD-GEOIPS.
"""Neutral helpers for flattening ``xr.DataTree`` nodes into mutable Datasets.
These helpers previously lived as duplicated methods on plugin base classes
(``BaseClassPlugin._to_mutable_dataset``) and inside
``geoips.utils.types.script_datatree``. They have one home now so the
interface-level plugin classes (algorithms, coverage_checkers,
filename_formatters) can share a single implementation without reaching back
into the base plugin class or the scripting module.
The canonical flattener is **recursive** and runs
``normalize_geoips_dataset_coords`` on the result, matching the behavior the
legacy plugin ``_pre_call`` overrides relied on before this refactor.
"""
from __future__ import annotations
import xarray as xr
from geoips.xarray_utils.coords import normalize_geoips_dataset_coords
[docs]def has_data_vars(node):
"""Return whether *node* or any descendant contains data variables.
Parameters
----------
node : xarray.DataTree
DataTree node to inspect.
Returns
-------
bool
``True`` if *node* itself carries data variables, or any of its
descendant nodes do.
"""
if node.ds is not None and bool(node.ds.data_vars):
return True
return any(has_data_vars(child) for child in node.children.values())
[docs]def to_mutable_dataset(node):
"""Return a mutable ``xr.Dataset`` flattening *node* and its descendants.
``DataTree.ds`` returns an immutable ``DatasetView``. Plugins that write
back into the dataset (e.g. ``xobj[product_name] = ...``) need a mutable
``xr.Dataset``. This helper recursively collects every descendant that
carries data variables, merges them into a single mutable Dataset,
propagates node attributes, and applies
``normalize_geoips_dataset_coords`` so the result matches the GeoIPS
coordinate convention legacy ``call()`` methods expect.
Parameters
----------
node : xarray.DataTree
DataTree node to flatten. May be a reader-style multi-level tree
(root attrs-only, sub-children holding each variable group) or a
single-level multi-child tree.
Returns
-------
xarray.Dataset
A mutable, coord-normalized Dataset merging all data-bearing
descendants of *node*. ``attrs`` are merged with node attrs taking
precedence.
"""
datasets = []
if node.ds is not None and bool(node.ds.data_vars):
datasets.append(node.to_dataset())
for child in node.children.values():
if has_data_vars(child):
datasets.append(to_mutable_dataset(child))
if not datasets:
dataset = xr.Dataset(attrs=dict(node.attrs))
elif len(datasets) == 1:
dataset = datasets[0].copy(deep=True)
else:
dataset = xr.merge(datasets).copy(deep=True)
dataset.attrs = {**dict(node.attrs), **dict(dataset.attrs)}
return normalize_geoips_dataset_coords(dataset)