1use ndarray::ArrayD;
7
8use crate::types::NcVariable;
9
10#[derive(Debug, Clone)]
12pub struct MaskParams {
13 pub fill_value: Option<f64>,
14 pub missing_value: Option<f64>,
15 pub valid_min: Option<f64>,
16 pub valid_max: Option<f64>,
17}
18
19impl MaskParams {
20 pub fn from_variable(var: &NcVariable) -> Option<Self> {
24 let fill = var.attribute("_FillValue").and_then(|a| a.value.as_f64());
25 let missing = var
26 .attribute("missing_value")
27 .and_then(|a| a.value.as_f64());
28
29 let (vmin, vmax) = if let Some(range) = var
31 .attribute("valid_range")
32 .and_then(|a| a.value.as_f64_vec())
33 {
34 if range.len() >= 2 {
35 (Some(range[0]), Some(range[1]))
36 } else {
37 (None, None)
38 }
39 } else {
40 let vmin = var.attribute("valid_min").and_then(|a| a.value.as_f64());
41 let vmax = var.attribute("valid_max").and_then(|a| a.value.as_f64());
42 (vmin, vmax)
43 };
44
45 if fill.is_none() && missing.is_none() && vmin.is_none() && vmax.is_none() {
46 return None;
47 }
48
49 Some(MaskParams {
50 fill_value: fill,
51 missing_value: missing,
52 valid_min: vmin,
53 valid_max: vmax,
54 })
55 }
56
57 pub fn apply(&self, data: &mut ArrayD<f64>) {
62 data.mapv_inplace(|v| {
63 if let Some(fill) = self.fill_value {
64 if v.to_bits() == fill.to_bits() {
65 return f64::NAN;
66 }
67 }
68 if let Some(miss) = self.missing_value {
69 if v.to_bits() == miss.to_bits() {
70 return f64::NAN;
71 }
72 }
73 if let Some(vmin) = self.valid_min {
74 if v < vmin {
75 return f64::NAN;
76 }
77 }
78 if let Some(vmax) = self.valid_max {
79 if v > vmax {
80 return f64::NAN;
81 }
82 }
83 v
84 });
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91 use crate::types::{NcAttrValue, NcAttribute, NcType, NcVariable};
92 use ndarray::arr1;
93
94 fn make_var(attrs: Vec<NcAttribute>) -> NcVariable {
95 NcVariable {
96 name: "test".into(),
97 dimensions: vec![],
98 dtype: NcType::Float,
99 attributes: attrs,
100 data_offset: 0,
101 _data_size: 0,
102 is_record_var: false,
103 record_size: 0,
104 }
105 }
106
107 #[test]
108 fn test_no_mask_attrs() {
109 let var = make_var(vec![]);
110 assert!(MaskParams::from_variable(&var).is_none());
111 }
112
113 #[test]
114 fn test_fill_value() {
115 let var = make_var(vec![NcAttribute {
116 name: "_FillValue".into(),
117 value: NcAttrValue::Floats(vec![-9999.0]),
118 }]);
119 let params = MaskParams::from_variable(&var).unwrap();
120 let mut data = arr1(&[1.0, -9999.0, 3.0]).into_dyn();
121 params.apply(&mut data);
122 assert_eq!(data[[0]], 1.0);
123 assert!(data[[1]].is_nan());
124 assert_eq!(data[[2]], 3.0);
125 }
126
127 #[test]
128 fn test_missing_value() {
129 let var = make_var(vec![NcAttribute {
130 name: "missing_value".into(),
131 value: NcAttrValue::Doubles(vec![-999.0]),
132 }]);
133 let params = MaskParams::from_variable(&var).unwrap();
134 let mut data = arr1(&[-999.0, 5.0]).into_dyn();
135 params.apply(&mut data);
136 assert!(data[[0]].is_nan());
137 assert_eq!(data[[1]], 5.0);
138 }
139
140 #[test]
141 fn test_valid_range() {
142 let var = make_var(vec![NcAttribute {
143 name: "valid_range".into(),
144 value: NcAttrValue::Doubles(vec![0.0, 100.0]),
145 }]);
146 let params = MaskParams::from_variable(&var).unwrap();
147 let mut data = arr1(&[-5.0, 50.0, 150.0]).into_dyn();
148 params.apply(&mut data);
149 assert!(data[[0]].is_nan());
150 assert_eq!(data[[1]], 50.0);
151 assert!(data[[2]].is_nan());
152 }
153
154 #[test]
155 fn test_valid_min_max() {
156 let var = make_var(vec![
157 NcAttribute {
158 name: "valid_min".into(),
159 value: NcAttrValue::Doubles(vec![0.0]),
160 },
161 NcAttribute {
162 name: "valid_max".into(),
163 value: NcAttrValue::Doubles(vec![50.0]),
164 },
165 ]);
166 let params = MaskParams::from_variable(&var).unwrap();
167 let mut data = arr1(&[-1.0, 25.0, 51.0]).into_dyn();
168 params.apply(&mut data);
169 assert!(data[[0]].is_nan());
170 assert_eq!(data[[1]], 25.0);
171 assert!(data[[2]].is_nan());
172 }
173}