Skip to main content

grib/datatypes/
sections.rs

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    /// Discipline - GRIB Master Table Number (see Code Table 0.0)
13    pub discipline: u8,
14    /// Total length of GRIB message in octets (including Section 0)
15    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    /// Identification of originating/generating centre (see Common Code Table
55    /// C-1)
56    #[inline]
57    pub fn centre_id(&self) -> u16 {
58        let payload = &self.payload;
59        read_as!(u16, payload, 0)
60    }
61
62    /// Identification of originating/generating sub-centre (allocated by
63    /// originating/ generating centre)
64    #[inline]
65    pub fn subcentre_id(&self) -> u16 {
66        let payload = &self.payload;
67        read_as!(u16, payload, 2)
68    }
69
70    /// GRIB Master Tables Version Number (see Code Table 1.0)
71    #[inline]
72    pub fn master_table_version(&self) -> u8 {
73        self.payload[4]
74    }
75
76    /// GRIB Local Tables Version Number (see Code Table 1.1)
77    #[inline]
78    pub fn local_table_version(&self) -> u8 {
79        self.payload[5]
80    }
81
82    /// Significance of Reference Time (see Code Table 1.2)
83    #[inline]
84    pub fn ref_time_significance(&self) -> u8 {
85        self.payload[6]
86    }
87
88    /// Unchecked reference time of the data.
89    ///
90    /// This method returns unchecked data, so for example, if the data contains
91    /// a "date and time" such as "2000-13-32 25:61:62", it will be returned as
92    /// is.
93    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    /// Production status of processed data in this GRIB message
106    /// (see Code Table 1.3)
107    #[inline]
108    pub fn prod_status(&self) -> u8 {
109        self.payload[14]
110    }
111
112    /// Type of processed data in this GRIB message (see Code Table 1.4)
113    #[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    /// Number of data points
154    pub fn num_points(&self) -> u32 {
155        let payload = &self.payload;
156        read_as!(u32, payload, 1)
157    }
158
159    /// Grid Definition Template Number
160    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    /// Number of coordinate values after Template
188    pub fn num_coordinates(&self) -> u16 {
189        let payload = &self.payload;
190        read_as!(u16, payload, 0)
191    }
192
193    /// Product Definition Template Number
194    pub fn prod_tmpl_num(&self) -> u16 {
195        let payload = &self.payload;
196        read_as!(u16, payload, 2)
197    }
198
199    // pub(crate) templated(&self)-> Box<[u8]> {
200
201    // }
202
203    pub(crate) fn template_supported(&self) -> bool {
204        SUPPORTED_PROD_DEF_TEMPLATE_NUMBERS.contains(&self.prod_tmpl_num())
205    }
206
207    /// Use [CodeTable4_1](crate::codetables::CodeTable4_1) to get textual
208    /// representation of the returned numerical value.
209    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    /// Use [CodeTable4_2](crate::codetables::CodeTable4_2) to get textual
218    /// representation of the returned numerical value.
219    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    /// Use [CodeTable4_3](crate::codetables::CodeTable4_3) to get textual
228    /// representation of the returned numerical value.
229    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                // 53 and 54 is variable and not supported as of now
239                55..=56 => Some(8),
240                // 57 and 58 is variable and not supported as of now
241                59 => Some(8),
242                60..=61 => Some(2),
243                62..=63 => Some(8),
244                // 67 and 68 is variable and not supported as of now
245                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    /// Returns the unit and value of the forecast time wrapped by `Option`.
264    /// Use [CodeTable4_4](crate::codetables::CodeTable4_4) to get textual
265    /// representation of the unit.
266    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                // 53 and 54 is variable and not supported as of now
276                55..=56 => Some(14),
277                // 57 and 58 is variable and not supported as of now
278                59 => Some(14),
279                60..=61 => Some(8),
280                62..=63 => Some(14),
281                // 67 and 68 is variable and not supported as of now
282                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    /// Returns a tuple of two [FixedSurface], wrapped by `Option`.
305    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                // 53 and 54 is variable and not supported as of now
315                55..=56 => Some(19),
316                // 57 and 58 is variable and not supported as of now
317                59 => Some(19),
318                60..=61 => Some(13),
319                62..=63 => Some(19),
320                // 67 and 68 is variable and not supported as of now
321                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    /// Number of data points where one or more values are
375    /// specified in Section 7 when a bit map is present, total
376    /// number of data points when a bit map is absent
377    pub fn num_points(&self) -> u32 {
378        let payload = &self.payload;
379        read_as!(u32, payload, 0)
380    }
381
382    /// Data Representation Template Number
383    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    /// Bit-map indicator
392    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}