Skip to main content

grib/datatypes/
sections.rs

1use std::slice::Iter;
2
3use crate::{
4    TryFromSlice, codetables::SUPPORTED_PROD_DEF_TEMPLATE_NUMBERS, datatypes::*, error::*,
5    helpers::read_as,
6};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct Indicator {
10    /// Discipline - GRIB Master Table Number (see Code Table 0.0)
11    pub discipline: u8,
12    /// Total length of GRIB message in octets (including Section 0)
13    pub total_length: u64,
14}
15
16impl Indicator {
17    pub(crate) fn from_slice(slice: &[u8]) -> Result<Self, ParseError> {
18        let mut pos = 0;
19        let sect = crate::def::grib2::Section0::try_from_slice(slice, &mut pos)
20            .map_err(|_e| ParseError::UnexpectedEndOfData(0))?;
21        if sect.edition_num != 2 {
22            return Err(ParseError::GRIBVersionMismatch(sect.edition_num));
23        }
24
25        Ok(Self {
26            discipline: sect.discipline,
27            total_length: sect.total_len,
28        })
29    }
30}
31
32#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct Identification {
34    pub(crate) payload: Box<[u8]>,
35}
36
37impl Identification {
38    pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
39        let size = slice.len();
40        if size < 16 {
41            Err(BuildError::SectionSizeTooSmall(size))
42        } else {
43            Ok(Self { payload: slice })
44        }
45    }
46
47    pub fn iter(&self) -> Iter<'_, u8> {
48        self.payload.iter()
49    }
50
51    /// Identification of originating/generating centre (see Common Code Table
52    /// C-1)
53    #[inline]
54    pub fn centre_id(&self) -> u16 {
55        let payload = &self.payload;
56        read_as!(u16, payload, 0)
57    }
58
59    /// Identification of originating/generating sub-centre (allocated by
60    /// originating/ generating centre)
61    #[inline]
62    pub fn subcentre_id(&self) -> u16 {
63        let payload = &self.payload;
64        read_as!(u16, payload, 2)
65    }
66
67    /// GRIB Master Tables Version Number (see Code Table 1.0)
68    #[inline]
69    pub fn master_table_version(&self) -> u8 {
70        self.payload[4]
71    }
72
73    /// GRIB Local Tables Version Number (see Code Table 1.1)
74    #[inline]
75    pub fn local_table_version(&self) -> u8 {
76        self.payload[5]
77    }
78
79    /// Significance of Reference Time (see Code Table 1.2)
80    #[inline]
81    pub fn ref_time_significance(&self) -> u8 {
82        self.payload[6]
83    }
84
85    /// Unchecked reference time of the data.
86    ///
87    /// This method returns unchecked data, so for example, if the data contains
88    /// a "date and time" such as "2000-13-32 25:61:62", it will be returned as
89    /// is.
90    pub fn ref_time_unchecked(&self) -> crate::def::grib2::template::param_set::DateTime {
91        let mut pos = 7;
92        crate::def::grib2::template::param_set::DateTime::try_from_slice(&self.payload, &mut pos)
93            .unwrap()
94    }
95
96    /// Production status of processed data in this GRIB message
97    /// (see Code Table 1.3)
98    #[inline]
99    pub fn prod_status(&self) -> u8 {
100        self.payload[14]
101    }
102
103    /// Type of processed data in this GRIB message (see Code Table 1.4)
104    #[inline]
105    pub fn data_type(&self) -> u8 {
106        self.payload[15]
107    }
108}
109
110#[derive(Debug, Clone, PartialEq, Eq)]
111pub struct LocalUse {
112    payload: Box<[u8]>,
113}
114
115impl LocalUse {
116    pub fn from_payload(slice: Box<[u8]>) -> Self {
117        Self { payload: slice }
118    }
119
120    pub fn iter(&self) -> Iter<'_, u8> {
121        self.payload.iter()
122    }
123}
124
125#[derive(Debug, Clone, PartialEq, Eq, Hash)]
126pub struct GridDefinition {
127    pub(crate) payload: Box<[u8]>,
128}
129
130impl GridDefinition {
131    pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
132        let size = slice.len();
133        if size < 9 {
134            Err(BuildError::SectionSizeTooSmall(size))
135        } else {
136            Ok(Self { payload: slice })
137        }
138    }
139
140    pub fn iter(&self) -> Iter<'_, u8> {
141        self.payload.iter()
142    }
143
144    /// Number of data points
145    pub fn num_points(&self) -> u32 {
146        let payload = &self.payload;
147        read_as!(u32, payload, 1)
148    }
149
150    /// Grid Definition Template Number
151    pub fn grid_tmpl_num(&self) -> u16 {
152        let payload = &self.payload;
153        read_as!(u16, payload, 7)
154    }
155}
156
157const START_OF_PROD_TEMPLATE: usize = 4;
158
159#[derive(Debug, Clone, PartialEq, Eq, Hash)]
160pub struct ProdDefinition {
161    pub(crate) payload: Box<[u8]>,
162}
163
164impl ProdDefinition {
165    pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
166        let size = slice.len();
167        if size < START_OF_PROD_TEMPLATE {
168            Err(BuildError::SectionSizeTooSmall(size))
169        } else {
170            Ok(Self { payload: slice })
171        }
172    }
173
174    pub fn iter(&self) -> Iter<'_, u8> {
175        self.payload.iter()
176    }
177
178    /// Number of coordinate values after Template
179    pub fn num_coordinates(&self) -> u16 {
180        let payload = &self.payload;
181        read_as!(u16, payload, 0)
182    }
183
184    /// Product Definition Template Number
185    pub fn prod_tmpl_num(&self) -> u16 {
186        let payload = &self.payload;
187        read_as!(u16, payload, 2)
188    }
189
190    // pub(crate) templated(&self)-> Box<[u8]> {
191
192    // }
193
194    pub(crate) fn template_supported(&self) -> bool {
195        SUPPORTED_PROD_DEF_TEMPLATE_NUMBERS.contains(&self.prod_tmpl_num())
196    }
197
198    /// Use [CodeTable4_1](crate::codetables::CodeTable4_1) to get textual
199    /// representation of the returned numerical value.
200    pub fn parameter_category(&self) -> Option<u8> {
201        if self.template_supported() {
202            self.payload.get(START_OF_PROD_TEMPLATE).copied()
203        } else {
204            None
205        }
206    }
207
208    /// Use [CodeTable4_2](crate::codetables::CodeTable4_2) to get textual
209    /// representation of the returned numerical value.
210    pub fn parameter_number(&self) -> Option<u8> {
211        if self.template_supported() {
212            self.payload.get(START_OF_PROD_TEMPLATE + 1).copied()
213        } else {
214            None
215        }
216    }
217
218    /// Use [CodeTable4_3](crate::codetables::CodeTable4_3) to get textual
219    /// representation of the returned numerical value.
220    pub fn generating_process(&self) -> Option<u8> {
221        if self.template_supported() {
222            let index = match self.prod_tmpl_num() {
223                0..=39 => Some(2),
224                40..=43 => Some(4),
225                44..=46 => Some(15),
226                47 => Some(2),
227                48..=49 => Some(26),
228                51 => Some(2),
229                // 53 and 54 is variable and not supported as of now
230                55..=56 => Some(8),
231                // 57 and 58 is variable and not supported as of now
232                59 => Some(8),
233                60..=61 => Some(2),
234                62..=63 => Some(8),
235                // 67 and 68 is variable and not supported as of now
236                70..=73 => Some(7),
237                76..=79 => Some(5),
238                80..=81 => Some(27),
239                82 => Some(16),
240                83 => Some(2),
241                84 => Some(16),
242                85 => Some(15),
243                86..=91 => Some(2),
244                254 => Some(2),
245                1000..=1101 => Some(2),
246                _ => None,
247            }?;
248            self.payload.get(START_OF_PROD_TEMPLATE + index).copied()
249        } else {
250            None
251        }
252    }
253
254    /// Returns the unit and value of the forecast time wrapped by `Option`.
255    /// Use [CodeTable4_4](crate::codetables::CodeTable4_4) to get textual
256    /// representation of the unit.
257    pub fn forecast_time(&self) -> Option<ForecastTime> {
258        if self.template_supported() {
259            let unit_index = match self.prod_tmpl_num() {
260                0..=15 => Some(8),
261                32..=34 => Some(8),
262                40..=43 => Some(10),
263                44..=47 => Some(21),
264                48..=49 => Some(32),
265                51 => Some(8),
266                // 53 and 54 is variable and not supported as of now
267                55..=56 => Some(14),
268                // 57 and 58 is variable and not supported as of now
269                59 => Some(14),
270                60..=61 => Some(8),
271                62..=63 => Some(14),
272                // 67 and 68 is variable and not supported as of now
273                70..=73 => Some(13),
274                76..=79 => Some(11),
275                80..=81 => Some(33),
276                82..=84 => Some(22),
277                85 => Some(21),
278                86..=87 => Some(8),
279                88 => Some(26),
280                91 => Some(8),
281                1000..=1101 => Some(8),
282                _ => None,
283            }?;
284            let unit_index = START_OF_PROD_TEMPLATE + unit_index;
285            let unit = self.payload.get(unit_index).copied();
286            let start = unit_index + 1;
287            let end = unit_index + 5;
288            let time = u32::from_be_bytes(self.payload[start..end].try_into().unwrap());
289            unit.map(|v| ForecastTime::from_numbers(v, time))
290        } else {
291            None
292        }
293    }
294
295    /// Returns a tuple of two [FixedSurface], wrapped by `Option`.
296    pub fn fixed_surfaces(&self) -> Option<(FixedSurface, FixedSurface)> {
297        if self.template_supported() {
298            let index = match self.prod_tmpl_num() {
299                0..=15 => Some(13),
300                40..=43 => Some(15),
301                44 => Some(24),
302                45..=47 => Some(26),
303                48..=49 => Some(37),
304                51 => Some(13),
305                // 53 and 54 is variable and not supported as of now
306                55..=56 => Some(19),
307                // 57 and 58 is variable and not supported as of now
308                59 => Some(19),
309                60..=61 => Some(13),
310                62..=63 => Some(19),
311                // 67 and 68 is variable and not supported as of now
312                70..=73 => Some(18),
313                76..=79 => Some(16),
314                80..=81 => Some(38),
315                82..=84 => Some(27),
316                85 => Some(26),
317                86..=87 => Some(13),
318                88 => Some(5),
319                91 => Some(13),
320                1100..=1101 => Some(13),
321                _ => None,
322            }?;
323
324            let mut pos = START_OF_PROD_TEMPLATE + index;
325            let first_surface = FixedSurface::try_from_slice(&self.payload, &mut pos).ok();
326            let second_surface = FixedSurface::try_from_slice(&self.payload, &mut pos).ok();
327            first_surface.zip(second_surface)
328        } else {
329            None
330        }
331    }
332}
333
334#[derive(Debug, Clone, PartialEq, Eq, Hash)]
335pub struct ReprDefinition {
336    pub(crate) payload: Box<[u8]>,
337}
338
339impl ReprDefinition {
340    pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
341        let size = slice.len();
342        if size < 6 {
343            Err(BuildError::SectionSizeTooSmall(size))
344        } else {
345            Ok(Self { payload: slice })
346        }
347    }
348
349    pub fn iter(&self) -> Iter<'_, u8> {
350        self.payload.iter()
351    }
352
353    /// Number of data points where one or more values are
354    /// specified in Section 7 when a bit map is present, total
355    /// number of data points when a bit map is absent
356    pub fn num_points(&self) -> u32 {
357        let payload = &self.payload;
358        read_as!(u32, payload, 0)
359    }
360
361    /// Data Representation Template Number
362    pub fn repr_tmpl_num(&self) -> u16 {
363        let payload = &self.payload;
364        read_as!(u16, payload, 4)
365    }
366}
367
368#[derive(Debug, Clone, PartialEq, Eq, Hash)]
369pub struct BitMap {
370    /// Bit-map indicator
371    pub bitmap_indicator: u8,
372}
373
374#[cfg(test)]
375mod tests {
376    use super::*;
377
378    #[test]
379    fn prod_definition_parameters() {
380        let data = ProdDefinition::from_payload(
381            vec![
382                0, 0, 0, 0, 193, 0, 2, 153, 255, 0, 0, 0, 0, 0, 0, 0, 40, 1, 255, 255, 255, 255,
383                255, 255, 255, 255, 255, 255, 255,
384            ]
385            .into_boxed_slice(),
386        )
387        .unwrap();
388
389        assert_eq!(data.parameter_category(), Some(193));
390        assert_eq!(data.parameter_number(), Some(0));
391        assert_eq!(
392            data.forecast_time(),
393            Some(ForecastTime::from_numbers(0, 40))
394        );
395        assert_eq!(
396            data.fixed_surfaces(),
397            Some((
398                FixedSurface::new(1, -127, -2147483647),
399                FixedSurface::new(255, -127, -2147483647)
400            ))
401        );
402    }
403}