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