grib/datatypes/
product_attributes.rs1use std::fmt::{self, Display, Formatter};
2
3use crate::codetables::{grib2::*, *};
4
5#[derive(Debug, PartialEq, Eq)]
16pub struct Parameter {
17 pub discipline: u8,
19 pub centre: u16,
21 pub master_ver: u8,
23 pub local_ver: u8,
25 pub category: u8,
27 pub num: u8,
29}
30
31impl Parameter {
32 pub fn description(&self) -> Option<String> {
49 CodeTable4_2::new(self.discipline, self.category)
50 .lookup(usize::from(self.num))
51 .description()
52 }
53
54 pub fn is_identical_to<'a, T>(&'a self, code: T) -> bool
74 where
75 T: TryFrom<&'a Self>,
76 T: PartialEq,
77 {
78 let self_ = T::try_from(self);
79 self_.is_ok_and(|v| v == code)
80 }
81
82 pub(crate) fn as_u32(&self) -> u32 {
83 (u32::from(self.discipline) << 16) + (u32::from(self.category) << 8) + u32::from(self.num)
84 }
85}
86
87#[derive(Debug, PartialEq, Eq)]
88pub struct ForecastTime {
89 pub unit: Code<grib2::Table4_4, u8>,
90 pub value: u32,
91}
92
93impl ForecastTime {
94 pub fn new(unit: Code<grib2::Table4_4, u8>, value: u32) -> Self {
95 Self { unit, value }
96 }
97
98 pub fn from_numbers(unit: u8, value: u32) -> Self {
99 let unit = Table4_4::try_from(unit).into();
100 Self { unit, value }
101 }
102
103 pub fn describe(&self) -> (String, String) {
104 let unit = match &self.unit {
105 Name(unit) => format!("{unit:#?}"),
106 Num(num) => format!("code {num:#?}"),
107 };
108 let value = self.value.to_string();
109 (unit, value)
110 }
111}
112
113impl Display for ForecastTime {
114 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
115 write!(f, "{}", self.value)?;
116
117 match &self.unit {
118 Name(unit) => {
119 if let Some(expr) = unit.short_expr() {
120 write!(f, " [{expr}]")?;
121 }
122 }
123 Num(num) => {
124 write!(f, " [unit: {num}]")?;
125 }
126 }
127
128 Ok(())
129 }
130}
131
132#[derive(Debug, PartialEq, Eq)]
133pub struct FixedSurface {
134 pub surface_type: u8,
136 pub scale_factor: i8,
137 pub scaled_value: i32,
138}
139
140impl FixedSurface {
141 pub fn new(surface_type: u8, scale_factor: i8, scaled_value: i32) -> Self {
142 Self {
143 surface_type,
144 scale_factor,
145 scaled_value,
146 }
147 }
148
149 pub fn value(&self) -> f64 {
150 if self.value_is_nan() {
151 f64::NAN
152 } else {
153 let factor: f64 = 10_f64.powi(-i32::from(self.scale_factor));
154 f64::from(self.scaled_value) * factor
155 }
156 }
157
158 pub fn unit(&self) -> Option<&str> {
166 let unit = match self.surface_type {
169 11 => "m",
170 12 => "m",
171 13 => "%",
172 18 => "Pa",
173 20 => "K",
174 21 => "kg m-3",
175 22 => "kg m-3",
176 23 => "Bq m-3",
177 24 => "Bq m-3",
178 25 => "dBZ",
179 26 => "m",
180 27 => "m",
181 30 => "m",
182 100 => "Pa",
183 102 => "m",
184 103 => "m",
185 104 => r#""sigma" value"#,
186 106 => "m",
187 107 => "K",
188 108 => "Pa",
189 109 => "K m2 kg-1 s-1",
190 114 => "Numeric",
191 117 => "m",
192 151 => "Numeric",
193 152 => "Numeric",
194 160 => "m",
195 161 => "m",
196 168 => "Numeric",
197 169 => "kg m-3",
198 170 => "K",
199 171 => "m2 s-1",
200 _ => return None,
201 };
202 Some(unit)
203 }
204
205 pub fn scale_factor_is_nan(&self) -> bool {
207 self.scale_factor == i8::MIN + 1
210 }
211
212 pub fn value_is_nan(&self) -> bool {
214 self.scaled_value == i32::MIN + 1
217 }
218
219 pub fn describe(&self) -> (String, String, String) {
220 let stype = CodeTable4_5
221 .lookup(usize::from(self.surface_type))
222 .to_string();
223 let scale_factor = if self.scale_factor_is_nan() {
224 "Missing".to_owned()
225 } else {
226 self.scale_factor.to_string()
227 };
228 let scaled_value = if self.value_is_nan() {
229 "Missing".to_owned()
230 } else {
231 self.scaled_value.to_string()
232 };
233 (stype, scale_factor, scaled_value)
234 }
235}