Skip to main content

cf-cm-tree

Generic and light-weight package to assist CF-compliant dataset creation.

GeoZarr note. cf-cm-tree is a CF-conventions helper. It does not implement the GeoZarr conventions (geo-proj, spatial, multiscales). For spec-compliant GeoZarr metadata use zarr-cm (primitives, zero-dep) or geozarr-toolkit (Pydantic models + CLI). The CF helpers in this package remain useful for variable-level CF metadata (standard_name, _FillValue, etc.) and are complementary to GeoZarr.

Installation

pip install cf-cm-tree

Usage and examples

cf-cm-tree contains all user-facing classes at its root and a utils module, which collects some useful helper functions.

Coordinates

In terms of multi-dimensional dataset hierarchy, the CFCoordinate class is at the lowest level. It defines some mandatory attributes like name and standard_name, and optional attributes like long_name or units. Certain attribute values are validated during class initialisation to ensure that they are CF compliant, e.g., axis always needs to have a single uppercase letter.

from pprint import pprint
from cf_cm_tree import CFCoordinate

cf_coord = CFCoordinate(name="z", standard_name="z_coordinate", axis="Z", units="m")

Besides directly accessing the class attributes, CFCoordinate has the attrs property, which allows to retrieve CF compliant metadata attributes as a dictionary.

pprint(cf_coord.attrs)
{'axis': 'Z', 'standard_name': 'z_coordinate', 'units': 'm'}

There are already some pre-defined coordinate classes available, e.g., CFXCoordinate, CFYCoordinate, CFLonCoordinate, CFLatCoordinate, and CFTimeCoordinate.

from cf_cm_tree import CFXCoordinate

cf_xcoord = CFXCoordinate(name="x")
pprint(cf_xcoord.attrs)
{'axis': 'X',
 'long_name': 'x coordinate of projection',
 'standard_name': 'projection_x_coordinate',
 'units': 'meters'}

Data variables

There are two types of data variables, CFDataVariable and CFFlagVariable. CFDataVariable defines (CF) attributes for data variables representing a physical quantity and CFFlagVariable for boolean or bitwise data flags. Here is an example with CFDataVariable:

from cf_cm_tree import CFDataVariable

cf_dvar = CFDataVariable(
        name="dem",
        standard_name="digital_elevation_model",
        scale_factor=2.0,
        add_offset=0,
        fill_value=-9999,
        units="m",
    )
pprint(cf_dvar.attrs)
{'_FillValue': -9999,
 'add_offset': 0,
 'scale_factor': 2.0,
 'standard_name': 'digital_elevation_model',
 'units': 'm'}

and here with CFFlagVariable:

from cf_cm_tree import CFFlagVariable

cf_fvar = CFFlagVariable(
        name="qflag",
        standard_name="quality_flag",
        flag_values=[1 << 0, 1 << 1, 1 << 2],
        flag_meanings=[
            "processing_successfull",
            "retrieval_successful",
            "quality_good",
        ],
    )
pprint(cf_fvar.attrs)
{'_FillValue': 255,
 'flag_meanings': 'processing_successfull retrieval_successful quality_good',
 'flag_values': [1, 2, 4],
 'standard_name': 'quality_flag'}

Each data variable can hold a set of coordinates with unique names. Coordinates can be attached to a data variable either during initialisation or at a later stage. Below is an example:

from cf_cm_tree import CFXCoordinate, CFYCoordinate, CFTimeCoordinate

cf_xcoord = CFXCoordinate(name="x")
cf_ycoord = CFYCoordinate(name="y")
cf_dvar = CFDataVariable(
        name="temp",
        standard_name="temperature",
        fill_value=-9999,
        units="degrees_celsius",
        cf_coords=[cf_xcoord, cf_ycoord]
    )
print(len(cf_dvar))
pprint(cf_dvar.coordinates)
2
{'x': CFXCoordinate(name='x', standard_name='projection_x_coordinate', long_name='x coordinate of projection', axis='X', units='meters', other_attrs={}),
 'y': CFYCoordinate(name='y', standard_name='projection_y_coordinate', long_name='y coordinate of projection', axis='Y', units='meters', other_attrs={})}
cf_tcoord = CFTimeCoordinate(name="t", units="days since 1990-1-1 0:0:0")
cf_dvar = cf_dvar + cf_tcoord
print(len(cf_dvar))
pprint(cf_dvar.coordinates)
3
{'t': CFTimeCoordinate(name='t', standard_name='time', long_name=None, axis='T', units='days since 1990-1-1 0:0:0', other_attrs={}),
 'x': CFXCoordinate(name='x', standard_name='projection_x_coordinate', long_name='x coordinate of projection', axis='X', units='meters', other_attrs={}),
 'y': CFYCoordinate(name='y', standard_name='projection_y_coordinate', long_name='y coordinate of projection', axis='Y', units='meters', other_attrs={})}

Attention: be aware that the + operator overwrites the initial instance!

Dataset

The CFDataset is at the highest level of a multi-dimensional dataset hierarchy. It has some mandatory global attributes like title and source and can store several CF data variables.

from cf_cm_tree import CFDataset

cf_ds = CFDataset(title="my dataset", source="my dataset source", cf_vars=[cf_dvar])
print(len(cf_ds))
pprint(cf_ds.attrs)
1
{'institution': 'eodc', 'source': 'my dataset source', 'title': 'my dataset', 'Conventions': 'CF-1.11'}

Also here we can now append CF data variables as we like:

cf_ds = cf_ds + cf_fvar
print(len(cf_ds))
pprint(cf_ds.variables)
2
{'qflag': CFFlagVariable(name='qflag', standard_name='quality_flag', long_name=None, fill_value=255, valid_range=None, grid_mapping=None, other_attrs={}, flag_values=[1, 2, 4], flag_masks=None, flag_meanings=['processing_successfull', 'retrieval_successful', 'quality_good']),
 'temp': CFDataVariable(name='temp', standard_name='temperature', long_name=None, fill_value=-9999, valid_range=None, grid_mapping=None, other_attrs={}, scale_factor=1.0, add_offset=0, units='degrees_celsius')}

It is also possible to combine two datasets and join their variables:

cf_ds1 = CFDataset(title="dataset1", source="source1", cf_vars=[cf_dvar])
cf_ds2 = CFDataset(title="dataset2", source="source2", cf_vars=[cf_fvar])
cf_ds1 = cf_ds1 + cf_ds2
pprint(cf_ds1.variables)
{'qflag': CFFlagVariable(name='qflag', standard_name='quality_flag', long_name=None, fill_value=255, valid_range=None, grid_mapping=None, other_attrs={}, flag_values=[1, 2, 4], flag_masks=None, flag_meanings=['processing_successfull', 'retrieval_successful', 'quality_good']),
 'temp': CFDataVariable(name='temp', standard_name='temperature', long_name=None, fill_value=-9999, valid_range=None, grid_mapping=None, other_attrs={}, scale_factor=1.0, add_offset=0, units='degrees_celsius')}

Applying metadata to an xarray dataset

The utils module provides assign_cf_metadata, which writes the CF attributes of a CFDataset (and its variables and coordinates) onto a matching xarray.Dataset in place.

import numpy as np
import xarray as xr
from cf_cm_tree.utils import assign_cf_metadata

da = xr.DataArray(
    np.zeros((2, 2, 2)),
    coords={"t": range(2), "y": range(2), "x": range(2)},
    dims=["t", "y", "x"],
)
ds = xr.Dataset({"temp": da})

new_cf_ds = CFDataset(title="my dataset", source="my dataset source", cf_vars=[cf_dvar])
ds = assign_cf_metadata(ds, new_cf_ds)
pprint(dict(ds["temp"].attrs))
pprint(dict(ds["x"].attrs))
{'_FillValue': -9999,
 'add_offset': 0,
 'scale_factor': 1.0,
 'standard_name': 'temperature',
 'units': 'degrees_celsius'}
{'axis': 'X',
 'long_name': 'x coordinate of projection',
 'standard_name': 'projection_x_coordinate',
 'units': 'meters'}

Note: dataset, variable, and coordinate names in ds must match the name given to the corresponding CFDataset/CFDataVariable/CFCoordinate instances, otherwise a KeyError is raised.

Testing

cd cf-cm-tree
pytest

Contributing

For implementing new features, or fixing bugs, we recommend to open a new branch from develop (or fork the repo). Upon completion, open a PR from the feature branch to develop, which allows the maintainers/owners to review your changes.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cf_cm_tree-1.0.0.tar.gz (7.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cf_cm_tree-1.0.0-py3-none-any.whl (7.6 kB view details)

Uploaded Python 3

File details

Details for the file cf_cm_tree-1.0.0.tar.gz.

File metadata

  • Download URL: cf_cm_tree-1.0.0.tar.gz
  • Upload date:
  • Size: 7.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for cf_cm_tree-1.0.0.tar.gz
Algorithm Hash digest
SHA256 8b9cbef1bcfada0a7f5fbc6b5566eae417d1290c9aa9ec31379f8d03cc5c2a80
MD5 fd879ef0fa7e9029c4c622999852f2e9
BLAKE2b-256 06790f5517e29b2b6511c480cbfc418b97bbbbfbfbdc21ad1a7255808ef4290f

See more details on using hashes here.

Provenance

The following attestation bundles were made for cf_cm_tree-1.0.0.tar.gz:

Publisher: publish.yml on eodcgmbh/cf-cm-tree

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file cf_cm_tree-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: cf_cm_tree-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 7.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for cf_cm_tree-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7469304a2a8952589142923c1819a58a48c2f815bf7fca477f7baf6de9847427
MD5 1b6ecdbaa7975cbf7bdafe3d3f57db78
BLAKE2b-256 b9c6857c05efc28b4245a99e23e95586fef66e86afee247a91136218144c4ee7

See more details on using hashes here.

Provenance

The following attestation bundles were made for cf_cm_tree-1.0.0-py3-none-any.whl:

Publisher: publish.yml on eodcgmbh/cf-cm-tree

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page