cf-cm-tree
Generic and light-weight package to assist CF-compliant dataset creation.
GeoZarr note.
cf-cm-treeis a CF-conventions helper. It does not implement the GeoZarr conventions (geo-proj,spatial,multiscales). For spec-compliant GeoZarr metadata usezarr-cm(primitives, zero-dep) orgeozarr-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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8b9cbef1bcfada0a7f5fbc6b5566eae417d1290c9aa9ec31379f8d03cc5c2a80
|
|
| MD5 |
fd879ef0fa7e9029c4c622999852f2e9
|
|
| BLAKE2b-256 |
06790f5517e29b2b6511c480cbfc418b97bbbbfbfbdc21ad1a7255808ef4290f
|
Provenance
The following attestation bundles were made for cf_cm_tree-1.0.0.tar.gz:
Publisher:
publish.yml on eodcgmbh/cf-cm-tree
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cf_cm_tree-1.0.0.tar.gz -
Subject digest:
8b9cbef1bcfada0a7f5fbc6b5566eae417d1290c9aa9ec31379f8d03cc5c2a80 - Sigstore transparency entry: 2406030320
- Sigstore integration time:
-
Permalink:
eodcgmbh/cf-cm-tree@2f0e39e005814a71f35c7ae4a6104a5a782ef192 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/eodcgmbh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2f0e39e005814a71f35c7ae4a6104a5a782ef192 -
Trigger Event:
release
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7469304a2a8952589142923c1819a58a48c2f815bf7fca477f7baf6de9847427
|
|
| MD5 |
1b6ecdbaa7975cbf7bdafe3d3f57db78
|
|
| BLAKE2b-256 |
b9c6857c05efc28b4245a99e23e95586fef66e86afee247a91136218144c4ee7
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
cf_cm_tree-1.0.0-py3-none-any.whl -
Subject digest:
7469304a2a8952589142923c1819a58a48c2f815bf7fca477f7baf6de9847427 - Sigstore transparency entry: 2406030330
- Sigstore integration time:
-
Permalink:
eodcgmbh/cf-cm-tree@2f0e39e005814a71f35c7ae4a6104a5a782ef192 -
Branch / Tag:
refs/tags/v1.0.0 - Owner: https://github.com/eodcgmbh
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2f0e39e005814a71f35c7ae4a6104a5a782ef192 -
Trigger Event:
release
-
Statement type: