rustyhdf5_netcdf4/
variable.rs1use std::collections::HashMap;
7
8use rustyhdf5::AttrValue;
9
10use crate::cf::{self, CfAttributes};
11use crate::dimension::Dimension;
12use crate::error::Error;
13use crate::types::{dtype_to_nctype, NcType};
14
15pub struct Variable<'f> {
17 name: String,
19 dataset: rustyhdf5::Dataset<'f>,
21 dims: Vec<Dimension>,
23 attrs_cache: Option<HashMap<String, AttrValue>>,
25}
26
27impl<'f> Variable<'f> {
28 pub(crate) fn new(
30 name: String,
31 dataset: rustyhdf5::Dataset<'f>,
32 dims: Vec<Dimension>,
33 ) -> Self {
34 Self {
35 name,
36 dataset,
37 dims,
38 attrs_cache: None,
39 }
40 }
41
42 pub fn name(&self) -> &str {
44 &self.name
45 }
46
47 pub fn dimensions(&self) -> &[Dimension] {
49 &self.dims
50 }
51
52 pub fn shape(&self) -> Result<Vec<u64>, Error> {
54 Ok(self.dataset.shape()?)
55 }
56
57 pub fn nc_type(&self) -> Result<NcType, Error> {
59 let dtype = self.dataset.dtype()?;
60 Ok(dtype_to_nctype(&dtype))
61 }
62
63 pub fn attrs(&mut self) -> Result<&HashMap<String, AttrValue>, Error> {
65 if self.attrs_cache.is_none() {
66 self.attrs_cache = Some(self.dataset.attrs()?);
67 }
68 Ok(self.attrs_cache.as_ref().unwrap())
69 }
70
71 pub fn cf_attributes(&mut self) -> Result<CfAttributes, Error> {
73 let attrs = self.attrs()?;
74 Ok(cf::extract_cf_attributes(attrs))
75 }
76
77 pub fn read_f64(&mut self) -> Result<Vec<f64>, Error> {
82 let raw = self.dataset.read_f64()?;
83 let cf = self.cf_attributes()?;
84 Ok(cf::apply_scale_offset(&raw, &cf))
85 }
86
87 pub fn read_raw_f64(&self) -> Result<Vec<f64>, Error> {
89 Ok(self.dataset.read_f64()?)
90 }
91
92 pub fn read_raw_f32(&self) -> Result<Vec<f32>, Error> {
94 Ok(self.dataset.read_f32()?)
95 }
96
97 pub fn read_raw_i32(&self) -> Result<Vec<i32>, Error> {
99 Ok(self.dataset.read_i32()?)
100 }
101
102 pub fn read_raw_i64(&self) -> Result<Vec<i64>, Error> {
104 Ok(self.dataset.read_i64()?)
105 }
106
107 pub fn read_raw_u64(&self) -> Result<Vec<u64>, Error> {
109 Ok(self.dataset.read_u64()?)
110 }
111
112 pub fn read_string(&self) -> Result<Vec<String>, Error> {
114 Ok(self.dataset.read_string()?)
115 }
116
117 pub fn read_raw(&self) -> Result<Vec<u8>, Error> {
119 let dtype = self.dataset.dtype()?;
124 match dtype {
125 rustyhdf5::DType::F64 => {
126 let vals = self.dataset.read_f64()?;
127 Ok(vals.iter().flat_map(|v| v.to_le_bytes()).collect())
128 }
129 rustyhdf5::DType::F32 => {
130 let vals = self.dataset.read_f32()?;
131 Ok(vals.iter().flat_map(|v| v.to_le_bytes()).collect())
132 }
133 rustyhdf5::DType::I32 => {
134 let vals = self.dataset.read_i32()?;
135 Ok(vals.iter().flat_map(|v| v.to_le_bytes()).collect())
136 }
137 rustyhdf5::DType::I64 => {
138 let vals = self.dataset.read_i64()?;
139 Ok(vals.iter().flat_map(|v| v.to_le_bytes()).collect())
140 }
141 rustyhdf5::DType::U64 => {
142 let vals = self.dataset.read_u64()?;
143 Ok(vals.iter().flat_map(|v| v.to_le_bytes()).collect())
144 }
145 _ => {
146 let vals = self.dataset.read_f64()?;
148 Ok(vals.iter().flat_map(|v| v.to_le_bytes()).collect())
149 }
150 }
151 }
152
153 pub fn is_coordinate(&self) -> bool {
155 self.dims.len() == 1 && self.dims[0].name == self.name
156 }
157}
158
159impl std::fmt::Debug for Variable<'_> {
160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
161 f.debug_struct("Variable")
162 .field("name", &self.name)
163 .field("dims", &self.dims)
164 .finish()
165 }
166}
167
168pub(crate) fn build_variables<'f>(
170 group: &rustyhdf5::Group<'f>,
171 available_dims: &[Dimension],
172) -> Result<Vec<Variable<'f>>, Error> {
173 let dataset_names = group.datasets()?;
174 let mut variables = Vec::new();
175
176 for ds_name in &dataset_names {
177 let ds = group.dataset(ds_name)?;
178 let shape = ds.shape()?;
179
180 let var_dims = match_dimensions_to_variable(&shape, available_dims);
183
184 variables.push(Variable::new(ds_name.clone(), ds, var_dims));
185 }
186
187 Ok(variables)
188}
189
190pub(crate) fn match_dimensions_to_variable(shape: &[u64], available_dims: &[Dimension]) -> Vec<Dimension> {
196 let mut result = Vec::with_capacity(shape.len());
197
198 let mut used = vec![false; available_dims.len()];
200
201 for &dim_size in shape {
202 let mut matched = false;
203
204 for (i, dim) in available_dims.iter().enumerate() {
206 if !used[i] && dim.size == dim_size {
207 result.push(dim.clone());
208 used[i] = true;
209 matched = true;
210 break;
211 }
212 }
213
214 if !matched {
215 result.push(Dimension {
217 name: format!("dim_{dim_size}"),
218 size: dim_size,
219 is_unlimited: false,
220 });
221 }
222 }
223
224 result
225}