Skip to main content

rustyhdf5_netcdf4/
lib.rs

1//! NetCDF-4 read support built on rustyhdf5.
2//!
3//! NetCDF-4 files are HDF5 files following specific conventions. This crate
4//! provides a high-level API for reading NetCDF-4 metadata (dimensions,
5//! variables, CF attributes, groups) from HDF5 files written by netCDF4-python,
6//! nco, CDO, or any compliant NetCDF-4 writer.
7//!
8//! # Example
9//!
10//! ```no_run
11//! use rustyhdf5_netcdf4::NetCDF4File;
12//!
13//! let file = NetCDF4File::open("data.nc").unwrap();
14//! for dim in file.dimensions().unwrap() {
15//!     println!("{}: {} (unlimited={})", dim.name, dim.size, dim.is_unlimited);
16//! }
17//! for mut var in file.variables().unwrap() {
18//!     println!("{}: {:?}", var.name(), var.shape().unwrap());
19//!     let cf = var.cf_attributes().unwrap();
20//!     if let Some(units) = &cf.units {
21//!         println!("  units: {units}");
22//!     }
23//! }
24//! ```
25
26pub mod cf;
27pub mod dimension;
28pub mod error;
29pub mod group;
30pub mod types;
31pub mod variable;
32
33pub use cf::{CfAttributes, FillValue};
34pub use dimension::Dimension;
35pub use error::Error;
36pub use group::NetCDF4Group;
37pub use rustyhdf5::AttrValue;
38pub use types::NcType;
39pub use variable::Variable;
40
41use std::collections::HashMap;
42
43/// A NetCDF-4 file reader.
44///
45/// Wraps a rustyhdf5 File and provides NetCDF-4 semantics: dimensions,
46/// variables with CF attributes, groups, and type mapping.
47pub struct NetCDF4File {
48    hdf5: rustyhdf5::File,
49}
50
51impl NetCDF4File {
52    /// Open a NetCDF-4 file from a filesystem path.
53    pub fn open<P: AsRef<std::path::Path>>(path: P) -> Result<Self, Error> {
54        let hdf5 = rustyhdf5::File::open(path)?;
55        Ok(Self { hdf5 })
56    }
57
58    /// Open a NetCDF-4 file from in-memory bytes.
59    pub fn from_bytes(data: Vec<u8>) -> Result<Self, Error> {
60        let hdf5 = rustyhdf5::File::from_bytes(data)?;
61        Ok(Self { hdf5 })
62    }
63
64    /// Get the _NCProperties root attribute, if present.
65    ///
66    /// This attribute is written by standard NetCDF-4 libraries and contains
67    /// provenance information (library version, HDF5 version, etc.).
68    pub fn nc_properties(&self) -> Result<Option<String>, Error> {
69        let attrs = self.hdf5.root().attrs()?;
70        match attrs.get("_NCProperties") {
71            Some(AttrValue::String(s)) => Ok(Some(s.clone())),
72            _ => Ok(None),
73        }
74    }
75
76    /// List dimensions defined in the root group.
77    pub fn dimensions(&self) -> Result<Vec<Dimension>, Error> {
78        dimension::extract_dimensions_from_datasets(&self.hdf5.root(), &self.hdf5)
79    }
80
81    /// List all variables in the root group.
82    pub fn variables(&self) -> Result<Vec<Variable<'_>>, Error> {
83        let dims = self.dimensions()?;
84        variable::build_variables(&self.hdf5.root(), &dims)
85    }
86
87    /// Get a specific variable by name from the root group.
88    pub fn variable(&self, name: &str) -> Result<Variable<'_>, Error> {
89        let dims = self.dimensions()?;
90        let ds = self
91            .hdf5
92            .dataset(name)
93            .map_err(|_| Error::VariableNotFound(name.to_string()))?;
94        let shape = ds.shape()?;
95        let var_dims = variable::match_dimensions_to_variable(&shape, &dims);
96        Ok(Variable::new(name.to_string(), ds, var_dims))
97    }
98
99    /// Read all global (root group) attributes.
100    pub fn global_attrs(&self) -> Result<HashMap<String, AttrValue>, Error> {
101        Ok(self.hdf5.root().attrs()?)
102    }
103
104    /// List subgroup names in the root group.
105    pub fn group_names(&self) -> Result<Vec<String>, Error> {
106        Ok(self.hdf5.root().groups()?)
107    }
108
109    /// Get a subgroup by name.
110    pub fn group(&self, name: &str) -> Result<NetCDF4Group<'_>, Error> {
111        let hdf5_group = self
112            .hdf5
113            .group(name)
114            .map_err(|_| Error::GroupNotFound(name.to_string()))?;
115        Ok(NetCDF4Group::new(
116            name.to_string(),
117            &self.hdf5,
118            hdf5_group,
119        ))
120    }
121
122    /// Access the underlying HDF5 file for advanced operations.
123    pub fn hdf5_file(&self) -> &rustyhdf5::File {
124        &self.hdf5
125    }
126}
127
128impl std::fmt::Debug for NetCDF4File {
129    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
130        f.debug_struct("NetCDF4File")
131            .field("hdf5", &self.hdf5)
132            .finish()
133    }
134}