1use std::slice::Iter;
2
3use crate::{
4 codetables::SUPPORTED_PROD_DEF_TEMPLATE_NUMBERS,
5 datatypes::*,
6 error::*,
7 helpers::{GribInt, read_as},
8};
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub struct Indicator {
12 pub discipline: u8,
14 pub total_length: u64,
16}
17
18impl Indicator {
19 pub(crate) fn from_slice(slice: &[u8]) -> Result<Self, ParseError> {
20 let discipline = slice[6];
21 let version = slice[7];
22 if version != 2 {
23 return Err(ParseError::GRIBVersionMismatch(version));
24 }
25
26 let total_length = read_as!(u64, slice, 8);
27
28 Ok(Self {
29 discipline,
30 total_length,
31 })
32 }
33}
34
35#[derive(Debug, Clone, PartialEq, Eq)]
36pub struct Identification {
37 pub(crate) payload: Box<[u8]>,
38}
39
40impl Identification {
41 pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
42 let size = slice.len();
43 if size < 16 {
44 Err(BuildError::SectionSizeTooSmall(size))
45 } else {
46 Ok(Self { payload: slice })
47 }
48 }
49
50 pub fn iter(&self) -> Iter<'_, u8> {
51 self.payload.iter()
52 }
53
54 #[inline]
57 pub fn centre_id(&self) -> u16 {
58 let payload = &self.payload;
59 read_as!(u16, payload, 0)
60 }
61
62 #[inline]
65 pub fn subcentre_id(&self) -> u16 {
66 let payload = &self.payload;
67 read_as!(u16, payload, 2)
68 }
69
70 #[inline]
72 pub fn master_table_version(&self) -> u8 {
73 self.payload[4]
74 }
75
76 #[inline]
78 pub fn local_table_version(&self) -> u8 {
79 self.payload[5]
80 }
81
82 #[inline]
84 pub fn ref_time_significance(&self) -> u8 {
85 self.payload[6]
86 }
87
88 pub fn ref_time_unchecked(&self) -> crate::def::grib2::RefTime {
94 let payload = &self.payload;
95 crate::def::grib2::RefTime::new(
96 read_as!(u16, payload, 7),
97 payload[9],
98 payload[10],
99 payload[11],
100 payload[12],
101 payload[13],
102 )
103 }
104
105 #[inline]
108 pub fn prod_status(&self) -> u8 {
109 self.payload[14]
110 }
111
112 #[inline]
114 pub fn data_type(&self) -> u8 {
115 self.payload[15]
116 }
117}
118
119#[derive(Debug, Clone, PartialEq, Eq)]
120pub struct LocalUse {
121 payload: Box<[u8]>,
122}
123
124impl LocalUse {
125 pub fn from_payload(slice: Box<[u8]>) -> Self {
126 Self { payload: slice }
127 }
128
129 pub fn iter(&self) -> Iter<'_, u8> {
130 self.payload.iter()
131 }
132}
133
134#[derive(Debug, Clone, PartialEq, Eq, Hash)]
135pub struct GridDefinition {
136 pub(crate) payload: Box<[u8]>,
137}
138
139impl GridDefinition {
140 pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
141 let size = slice.len();
142 if size < 9 {
143 Err(BuildError::SectionSizeTooSmall(size))
144 } else {
145 Ok(Self { payload: slice })
146 }
147 }
148
149 pub fn iter(&self) -> Iter<'_, u8> {
150 self.payload.iter()
151 }
152
153 pub fn num_points(&self) -> u32 {
155 let payload = &self.payload;
156 read_as!(u32, payload, 1)
157 }
158
159 pub fn grid_tmpl_num(&self) -> u16 {
161 let payload = &self.payload;
162 read_as!(u16, payload, 7)
163 }
164}
165
166const START_OF_PROD_TEMPLATE: usize = 4;
167
168#[derive(Debug, Clone, PartialEq, Eq, Hash)]
169pub struct ProdDefinition {
170 pub(crate) payload: Box<[u8]>,
171}
172
173impl ProdDefinition {
174 pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
175 let size = slice.len();
176 if size < START_OF_PROD_TEMPLATE {
177 Err(BuildError::SectionSizeTooSmall(size))
178 } else {
179 Ok(Self { payload: slice })
180 }
181 }
182
183 pub fn iter(&self) -> Iter<'_, u8> {
184 self.payload.iter()
185 }
186
187 pub fn num_coordinates(&self) -> u16 {
189 let payload = &self.payload;
190 read_as!(u16, payload, 0)
191 }
192
193 pub fn prod_tmpl_num(&self) -> u16 {
195 let payload = &self.payload;
196 read_as!(u16, payload, 2)
197 }
198
199 pub(crate) fn template_supported(&self) -> bool {
204 SUPPORTED_PROD_DEF_TEMPLATE_NUMBERS.contains(&self.prod_tmpl_num())
205 }
206
207 pub fn parameter_category(&self) -> Option<u8> {
210 if self.template_supported() {
211 self.payload.get(START_OF_PROD_TEMPLATE).copied()
212 } else {
213 None
214 }
215 }
216
217 pub fn parameter_number(&self) -> Option<u8> {
220 if self.template_supported() {
221 self.payload.get(START_OF_PROD_TEMPLATE + 1).copied()
222 } else {
223 None
224 }
225 }
226
227 pub fn generating_process(&self) -> Option<u8> {
230 if self.template_supported() {
231 let index = match self.prod_tmpl_num() {
232 0..=39 => Some(2),
233 40..=43 => Some(4),
234 44..=46 => Some(15),
235 47 => Some(2),
236 48..=49 => Some(26),
237 51 => Some(2),
238 55..=56 => Some(8),
240 59 => Some(8),
242 60..=61 => Some(2),
243 62..=63 => Some(8),
244 70..=73 => Some(7),
246 76..=79 => Some(5),
247 80..=81 => Some(27),
248 82 => Some(16),
249 83 => Some(2),
250 84 => Some(16),
251 85 => Some(15),
252 86..=91 => Some(2),
253 254 => Some(2),
254 1000..=1101 => Some(2),
255 _ => None,
256 }?;
257 self.payload.get(START_OF_PROD_TEMPLATE + index).copied()
258 } else {
259 None
260 }
261 }
262
263 pub fn forecast_time(&self) -> Option<ForecastTime> {
267 if self.template_supported() {
268 let unit_index = match self.prod_tmpl_num() {
269 0..=15 => Some(8),
270 32..=34 => Some(8),
271 40..=43 => Some(10),
272 44..=47 => Some(21),
273 48..=49 => Some(32),
274 51 => Some(8),
275 55..=56 => Some(14),
277 59 => Some(14),
279 60..=61 => Some(8),
280 62..=63 => Some(14),
281 70..=73 => Some(13),
283 76..=79 => Some(11),
284 80..=81 => Some(33),
285 82..=84 => Some(22),
286 85 => Some(21),
287 86..=87 => Some(8),
288 88 => Some(26),
289 91 => Some(8),
290 1000..=1101 => Some(8),
291 _ => None,
292 }?;
293 let unit_index = START_OF_PROD_TEMPLATE + unit_index;
294 let unit = self.payload.get(unit_index).copied();
295 let start = unit_index + 1;
296 let end = unit_index + 5;
297 let time = u32::from_be_bytes(self.payload[start..end].try_into().unwrap());
298 unit.map(|v| ForecastTime::from_numbers(v, time))
299 } else {
300 None
301 }
302 }
303
304 pub fn fixed_surfaces(&self) -> Option<(FixedSurface, FixedSurface)> {
306 if self.template_supported() {
307 let index = match self.prod_tmpl_num() {
308 0..=15 => Some(13),
309 40..=43 => Some(15),
310 44 => Some(24),
311 45..=47 => Some(26),
312 48..=49 => Some(37),
313 51 => Some(13),
314 55..=56 => Some(19),
316 59 => Some(19),
318 60..=61 => Some(13),
319 62..=63 => Some(19),
320 70..=73 => Some(18),
322 76..=79 => Some(16),
323 80..=81 => Some(38),
324 82..=84 => Some(27),
325 85 => Some(26),
326 86..=87 => Some(13),
327 88 => Some(5),
328 91 => Some(13),
329 1100..=1101 => Some(13),
330 _ => None,
331 }?;
332
333 let first_surface = self.read_surface_from(index);
334 let second_surface = self.read_surface_from(index + 6);
335 first_surface.zip(second_surface)
336 } else {
337 None
338 }
339 }
340
341 fn read_surface_from(&self, index: usize) -> Option<FixedSurface> {
342 let index = START_OF_PROD_TEMPLATE + index;
343 let surface_type = self.payload.get(index).copied();
344 let scale_factor = self.payload.get(index + 1).map(|v| (*v).as_grib_int());
345 let start = index + 2;
346 let end = index + 6;
347 let scaled_value =
348 u32::from_be_bytes(self.payload[start..end].try_into().unwrap()).as_grib_int();
349 surface_type
350 .zip(scale_factor)
351 .map(|(stype, factor)| FixedSurface::new(stype, factor, scaled_value))
352 }
353}
354
355#[derive(Debug, Clone, PartialEq, Eq, Hash)]
356pub struct ReprDefinition {
357 pub(crate) payload: Box<[u8]>,
358}
359
360impl ReprDefinition {
361 pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
362 let size = slice.len();
363 if size < 6 {
364 Err(BuildError::SectionSizeTooSmall(size))
365 } else {
366 Ok(Self { payload: slice })
367 }
368 }
369
370 pub fn iter(&self) -> Iter<'_, u8> {
371 self.payload.iter()
372 }
373
374 pub fn num_points(&self) -> u32 {
378 let payload = &self.payload;
379 read_as!(u32, payload, 0)
380 }
381
382 pub fn repr_tmpl_num(&self) -> u16 {
384 let payload = &self.payload;
385 read_as!(u16, payload, 4)
386 }
387}
388
389#[derive(Debug, Clone, PartialEq, Eq, Hash)]
390pub struct BitMap {
391 pub bitmap_indicator: u8,
393}
394
395#[cfg(test)]
396mod tests {
397 use super::*;
398
399 #[test]
400 fn prod_definition_parameters() {
401 let data = ProdDefinition::from_payload(
402 vec![
403 0, 0, 0, 0, 193, 0, 2, 153, 255, 0, 0, 0, 0, 0, 0, 0, 40, 1, 255, 255, 255, 255,
404 255, 255, 255, 255, 255, 255, 255,
405 ]
406 .into_boxed_slice(),
407 )
408 .unwrap();
409
410 assert_eq!(data.parameter_category(), Some(193));
411 assert_eq!(data.parameter_number(), Some(0));
412 assert_eq!(
413 data.forecast_time(),
414 Some(ForecastTime::from_numbers(0, 40))
415 );
416 assert_eq!(
417 data.fixed_surfaces(),
418 Some((
419 FixedSurface::new(1, -127, -2147483647),
420 FixedSurface::new(255, -127, -2147483647)
421 ))
422 );
423 }
424}