grib/datatypes/
sections.rs

1use std::slice::Iter;
2
3use crate::{
4    codetables::SUPPORTED_PROD_DEF_TEMPLATE_NUMBERS,
5    datatypes::*,
6    error::*,
7    grid::{
8        GaussianGridDefinition, GridPointIterator, LambertGridDefinition, LatLonGridDefinition,
9    },
10    helpers::{read_as, GribInt},
11    time::UtcDateTime,
12    GridPointIndexIterator, PolarStereographicGridDefinition,
13};
14
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct Indicator {
17    /// Discipline - GRIB Master Table Number (see Code Table 0.0)
18    pub discipline: u8,
19    /// Total length of GRIB message in octets (including Section 0)
20    pub total_length: u64,
21}
22
23impl Indicator {
24    pub(crate) fn from_slice(slice: &[u8]) -> Result<Self, ParseError> {
25        let discipline = slice[6];
26        let version = slice[7];
27        if version != 2 {
28            return Err(ParseError::GRIBVersionMismatch(version));
29        }
30
31        let total_length = read_as!(u64, slice, 8);
32
33        Ok(Self {
34            discipline,
35            total_length,
36        })
37    }
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct Identification {
42    payload: Box<[u8]>,
43}
44
45impl Identification {
46    pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
47        let size = slice.len();
48        if size < 16 {
49            Err(BuildError::SectionSizeTooSmall(size))
50        } else {
51            Ok(Self { payload: slice })
52        }
53    }
54
55    pub fn iter(&self) -> Iter<'_, u8> {
56        self.payload.iter()
57    }
58
59    /// Identification of originating/generating centre (see Common Code Table
60    /// C-1)
61    #[inline]
62    pub fn centre_id(&self) -> u16 {
63        let payload = &self.payload;
64        read_as!(u16, payload, 0)
65    }
66
67    /// Identification of originating/generating sub-centre (allocated by
68    /// originating/ generating centre)
69    #[inline]
70    pub fn subcentre_id(&self) -> u16 {
71        let payload = &self.payload;
72        read_as!(u16, payload, 2)
73    }
74
75    /// GRIB Master Tables Version Number (see Code Table 1.0)
76    #[inline]
77    pub fn master_table_version(&self) -> u8 {
78        self.payload[4]
79    }
80
81    /// GRIB Local Tables Version Number (see Code Table 1.1)
82    #[inline]
83    pub fn local_table_version(&self) -> u8 {
84        self.payload[5]
85    }
86
87    /// Significance of Reference Time (see Code Table 1.2)
88    #[inline]
89    pub fn ref_time_significance(&self) -> u8 {
90        self.payload[6]
91    }
92
93    /// Unchecked reference time of the data.
94    ///
95    /// This method returns unchecked data, so for example, if the data contains
96    /// a "date and time" such as "2000-13-32 25:61:62", it will be returned as
97    /// is.
98    pub fn ref_time_unchecked(&self) -> UtcDateTime {
99        let payload = &self.payload;
100        UtcDateTime::new(
101            read_as!(u16, payload, 7),
102            payload[9],
103            payload[10],
104            payload[11],
105            payload[12],
106            payload[13],
107        )
108    }
109
110    /// Production status of processed data in this GRIB message
111    /// (see Code Table 1.3)
112    #[inline]
113    pub fn prod_status(&self) -> u8 {
114        self.payload[14]
115    }
116
117    /// Type of processed data in this GRIB message (see Code Table 1.4)
118    #[inline]
119    pub fn data_type(&self) -> u8 {
120        self.payload[15]
121    }
122}
123
124#[derive(Debug, Clone, PartialEq, Eq)]
125pub struct LocalUse {
126    payload: Box<[u8]>,
127}
128
129impl LocalUse {
130    pub fn from_payload(slice: Box<[u8]>) -> Self {
131        Self { payload: slice }
132    }
133
134    pub fn iter(&self) -> Iter<'_, u8> {
135        self.payload.iter()
136    }
137}
138
139#[derive(Debug, Clone, PartialEq, Eq, Hash)]
140pub struct GridDefinition {
141    payload: Box<[u8]>,
142}
143
144impl GridDefinition {
145    pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
146        let size = slice.len();
147        if size < 9 {
148            Err(BuildError::SectionSizeTooSmall(size))
149        } else {
150            Ok(Self { payload: slice })
151        }
152    }
153
154    pub fn iter(&self) -> Iter<'_, u8> {
155        self.payload.iter()
156    }
157
158    /// Number of data points
159    pub fn num_points(&self) -> u32 {
160        let payload = &self.payload;
161        read_as!(u32, payload, 1)
162    }
163
164    /// Grid Definition Template Number
165    pub fn grid_tmpl_num(&self) -> u16 {
166        let payload = &self.payload;
167        read_as!(u16, payload, 7)
168    }
169}
170
171#[derive(Debug, PartialEq, Eq)]
172pub enum GridDefinitionTemplateValues {
173    Template0(LatLonGridDefinition),
174    Template20(PolarStereographicGridDefinition),
175    Template30(LambertGridDefinition),
176    Template40(GaussianGridDefinition),
177}
178
179impl GridDefinitionTemplateValues {
180    /// Returns the shape of the grid, i.e. a tuple of the number of grids in
181    /// the i and j directions.
182    pub fn grid_shape(&self) -> (usize, usize) {
183        match self {
184            Self::Template0(def) => def.grid_shape(),
185            Self::Template20(def) => def.grid_shape(),
186            Self::Template30(def) => def.grid_shape(),
187            Self::Template40(def) => def.grid_shape(),
188        }
189    }
190
191    /// Returns the grid type.
192    ///
193    /// The grid types are denoted as short strings based on `gridType` used in
194    /// ecCodes.
195    ///
196    /// This is provided primarily for debugging and simple notation purposes.
197    /// It is better to use enum variants instead of the string notation to
198    /// determine the grid type.
199    pub fn short_name(&self) -> &'static str {
200        match self {
201            Self::Template0(def) => def.short_name(),
202            Self::Template20(def) => def.short_name(),
203            Self::Template30(def) => def.short_name(),
204            Self::Template40(def) => def.short_name(),
205        }
206    }
207
208    /// Returns an iterator over `(i, j)` of grid points.
209    ///
210    /// Note that this is a low-level API and it is not checked that the number
211    /// of iterator iterations is consistent with the number of grid points
212    /// defined in the data.
213    pub fn ij(&self) -> Result<GridPointIndexIterator, GribError> {
214        match self {
215            Self::Template0(def) => def.ij(),
216            Self::Template20(def) => def.ij(),
217            Self::Template30(def) => def.ij(),
218            Self::Template40(def) => def.ij(),
219        }
220    }
221
222    /// Returns an iterator over latitudes and longitudes of grid points in
223    /// degrees.
224    ///
225    /// Note that this is a low-level API and it is not checked that the number
226    /// of iterator iterations is consistent with the number of grid points
227    /// defined in the data.
228    pub fn latlons(&self) -> Result<GridPointIterator, GribError> {
229        let iter = match self {
230            Self::Template0(def) => GridPointIterator::LatLon(def.latlons()?),
231            #[cfg(feature = "gridpoints-proj")]
232            Self::Template20(def) => GridPointIterator::Lambert(def.latlons()?),
233            #[cfg(feature = "gridpoints-proj")]
234            Self::Template30(def) => GridPointIterator::Lambert(def.latlons()?),
235            Self::Template40(def) => GridPointIterator::LatLon(def.latlons()?),
236            #[cfg(not(feature = "gridpoints-proj"))]
237            _ => {
238                return Err(GribError::NotSupported(
239                    "lat/lon computation support for the template is dropped in this build"
240                        .to_owned(),
241                ))
242            }
243        };
244        Ok(iter)
245    }
246}
247
248impl TryFrom<&GridDefinition> for GridDefinitionTemplateValues {
249    type Error = GribError;
250
251    fn try_from(value: &GridDefinition) -> Result<Self, Self::Error> {
252        let num = value.grid_tmpl_num();
253        match num {
254            0 => {
255                let buf = &value.payload;
256                if buf.len() > 67 {
257                    return Err(GribError::NotSupported(format!(
258                        "template {num} with list of number of points"
259                    )));
260                }
261                Ok(GridDefinitionTemplateValues::Template0(
262                    LatLonGridDefinition::from_buf(&buf[25..]),
263                ))
264            }
265            20 => {
266                let buf = &value.payload;
267                Ok(GridDefinitionTemplateValues::Template20(
268                    PolarStereographicGridDefinition::from_buf(&buf[9..]),
269                ))
270            }
271            30 => {
272                let buf = &value.payload;
273                Ok(GridDefinitionTemplateValues::Template30(
274                    LambertGridDefinition::from_buf(&buf[9..]),
275                ))
276            }
277            40 => {
278                let buf = &value.payload;
279                if buf.len() > 67 {
280                    return Err(GribError::NotSupported(format!(
281                        "template {num} with list of number of points"
282                    )));
283                }
284                Ok(GridDefinitionTemplateValues::Template40(
285                    GaussianGridDefinition::from_buf(&buf[25..]),
286                ))
287            }
288            _ => Err(GribError::NotSupported(format!("template {num}"))),
289        }
290    }
291}
292
293const START_OF_PROD_TEMPLATE: usize = 4;
294
295#[derive(Debug, Clone, PartialEq, Eq, Hash)]
296pub struct ProdDefinition {
297    payload: Box<[u8]>,
298}
299
300impl ProdDefinition {
301    pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
302        let size = slice.len();
303        if size < START_OF_PROD_TEMPLATE {
304            Err(BuildError::SectionSizeTooSmall(size))
305        } else {
306            Ok(Self { payload: slice })
307        }
308    }
309
310    pub fn iter(&self) -> Iter<'_, u8> {
311        self.payload.iter()
312    }
313
314    /// Number of coordinate values after Template
315    pub fn num_coordinates(&self) -> u16 {
316        let payload = &self.payload;
317        read_as!(u16, payload, 0)
318    }
319
320    /// Product Definition Template Number
321    pub fn prod_tmpl_num(&self) -> u16 {
322        let payload = &self.payload;
323        read_as!(u16, payload, 2)
324    }
325
326    // pub(crate) templated(&self)-> Box<[u8]> {
327
328    // }
329
330    pub(crate) fn template_supported(&self) -> bool {
331        SUPPORTED_PROD_DEF_TEMPLATE_NUMBERS.contains(&self.prod_tmpl_num())
332    }
333
334    /// Use [CodeTable4_1](crate::codetables::CodeTable4_1) to get textual
335    /// representation of the returned numerical value.
336    pub fn parameter_category(&self) -> Option<u8> {
337        if self.template_supported() {
338            self.payload.get(START_OF_PROD_TEMPLATE).copied()
339        } else {
340            None
341        }
342    }
343
344    /// Use [CodeTable4_2](crate::codetables::CodeTable4_2) to get textual
345    /// representation of the returned numerical value.
346    pub fn parameter_number(&self) -> Option<u8> {
347        if self.template_supported() {
348            self.payload.get(START_OF_PROD_TEMPLATE + 1).copied()
349        } else {
350            None
351        }
352    }
353
354    /// Use [CodeTable4_3](crate::codetables::CodeTable4_3) to get textual
355    /// representation of the returned numerical value.
356    pub fn generating_process(&self) -> Option<u8> {
357        if self.template_supported() {
358            let index = match self.prod_tmpl_num() {
359                0..=39 => Some(2),
360                40..=43 => Some(4),
361                44..=46 => Some(15),
362                47 => Some(2),
363                48..=49 => Some(26),
364                51 => Some(2),
365                // 53 and 54 is variable and not supported as of now
366                55..=56 => Some(8),
367                // 57 and 58 is variable and not supported as of now
368                59 => Some(8),
369                60..=61 => Some(2),
370                62..=63 => Some(8),
371                // 67 and 68 is variable and not supported as of now
372                70..=73 => Some(7),
373                76..=79 => Some(5),
374                80..=81 => Some(27),
375                82 => Some(16),
376                83 => Some(2),
377                84 => Some(16),
378                85 => Some(15),
379                86..=91 => Some(2),
380                254 => Some(2),
381                1000..=1101 => Some(2),
382                _ => None,
383            }?;
384            self.payload.get(START_OF_PROD_TEMPLATE + index).copied()
385        } else {
386            None
387        }
388    }
389
390    /// Returns the unit and value of the forecast time wrapped by `Option`.
391    /// Use [CodeTable4_4](crate::codetables::CodeTable4_4) to get textual
392    /// representation of the unit.
393    pub fn forecast_time(&self) -> Option<ForecastTime> {
394        if self.template_supported() {
395            let unit_index = match self.prod_tmpl_num() {
396                0..=15 => Some(8),
397                32..=34 => Some(8),
398                40..=43 => Some(10),
399                44..=47 => Some(21),
400                48..=49 => Some(32),
401                51 => Some(8),
402                // 53 and 54 is variable and not supported as of now
403                55..=56 => Some(14),
404                // 57 and 58 is variable and not supported as of now
405                59 => Some(14),
406                60..=61 => Some(8),
407                62..=63 => Some(14),
408                // 67 and 68 is variable and not supported as of now
409                70..=73 => Some(13),
410                76..=79 => Some(11),
411                80..=81 => Some(33),
412                82..=84 => Some(22),
413                85 => Some(21),
414                86..=87 => Some(8),
415                88 => Some(26),
416                91 => Some(8),
417                1000..=1101 => Some(8),
418                _ => None,
419            }?;
420            let unit_index = START_OF_PROD_TEMPLATE + unit_index;
421            let unit = self.payload.get(unit_index).copied();
422            let start = unit_index + 1;
423            let end = unit_index + 5;
424            let time = u32::from_be_bytes(self.payload[start..end].try_into().unwrap());
425            unit.map(|v| ForecastTime::from_numbers(v, time))
426        } else {
427            None
428        }
429    }
430
431    /// Returns a tuple of two [FixedSurface], wrapped by `Option`.
432    pub fn fixed_surfaces(&self) -> Option<(FixedSurface, FixedSurface)> {
433        if self.template_supported() {
434            let index = match self.prod_tmpl_num() {
435                0..=15 => Some(13),
436                40..=43 => Some(15),
437                44 => Some(24),
438                45..=47 => Some(26),
439                48..=49 => Some(37),
440                51 => Some(13),
441                // 53 and 54 is variable and not supported as of now
442                55..=56 => Some(19),
443                // 57 and 58 is variable and not supported as of now
444                59 => Some(19),
445                60..=61 => Some(13),
446                62..=63 => Some(19),
447                // 67 and 68 is variable and not supported as of now
448                70..=73 => Some(18),
449                76..=79 => Some(16),
450                80..=81 => Some(38),
451                82..=84 => Some(27),
452                85 => Some(26),
453                86..=87 => Some(13),
454                88 => Some(5),
455                91 => Some(13),
456                1100..=1101 => Some(13),
457                _ => None,
458            }?;
459
460            let first_surface = self.read_surface_from(index);
461            let second_surface = self.read_surface_from(index + 6);
462            first_surface.zip(second_surface)
463        } else {
464            None
465        }
466    }
467
468    fn read_surface_from(&self, index: usize) -> Option<FixedSurface> {
469        let index = START_OF_PROD_TEMPLATE + index;
470        let surface_type = self.payload.get(index).copied();
471        let scale_factor = self.payload.get(index + 1).map(|v| (*v).as_grib_int());
472        let start = index + 2;
473        let end = index + 6;
474        let scaled_value =
475            u32::from_be_bytes(self.payload[start..end].try_into().unwrap()).as_grib_int();
476        surface_type
477            .zip(scale_factor)
478            .map(|(stype, factor)| FixedSurface::new(stype, factor, scaled_value))
479    }
480}
481
482#[derive(Debug, Clone, PartialEq, Eq, Hash)]
483pub struct ReprDefinition {
484    payload: Box<[u8]>,
485}
486
487impl ReprDefinition {
488    pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
489        let size = slice.len();
490        if size < 6 {
491            Err(BuildError::SectionSizeTooSmall(size))
492        } else {
493            Ok(Self { payload: slice })
494        }
495    }
496
497    pub fn iter(&self) -> Iter<'_, u8> {
498        self.payload.iter()
499    }
500
501    /// Number of data points where one or more values are
502    /// specified in Section 7 when a bit map is present, total
503    /// number of data points when a bit map is absent
504    pub fn num_points(&self) -> u32 {
505        let payload = &self.payload;
506        read_as!(u32, payload, 0)
507    }
508
509    /// Data Representation Template Number
510    pub fn repr_tmpl_num(&self) -> u16 {
511        let payload = &self.payload;
512        read_as!(u16, payload, 4)
513    }
514}
515
516#[derive(Debug, Clone, PartialEq, Eq, Hash)]
517pub struct BitMap {
518    /// Bit-map indicator
519    pub bitmap_indicator: u8,
520}
521
522#[cfg(test)]
523mod tests {
524    use super::*;
525
526    #[test]
527    fn grid_definition_template_0() {
528        // data taken from submessage #0.0 of
529        // `Z__C_RJTD_20160822020000_NOWC_GPV_Ggis10km_Pphw10_FH0000-0100_grib2.bin.xz`
530        // in `testdata`
531        let data = GridDefinition::from_payload(
532            vec![
533                0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff, 0xff, 0xff, 0xff,
534                0xff, 0x01, 0x03, 0xcd, 0x39, 0xfa, 0x01, 0x03, 0xc9, 0xf6, 0xa3, 0x00, 0x00, 0x01,
535                0x00, 0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x02,
536                0xdb, 0xc9, 0x3d, 0x07, 0x09, 0x7d, 0xa4, 0x30, 0x01, 0x31, 0xcf, 0xc3, 0x08, 0xef,
537                0xdd, 0x5c, 0x00, 0x01, 0xe8, 0x48, 0x00, 0x01, 0x45, 0x85, 0x00,
538            ]
539            .into_boxed_slice(),
540        )
541        .unwrap();
542
543        let actual = GridDefinitionTemplateValues::try_from(&data).unwrap();
544        let expected = GridDefinitionTemplateValues::Template0(LatLonGridDefinition {
545            ni: 256,
546            nj: 336,
547            first_point_lat: 47958333,
548            first_point_lon: 118062500,
549            last_point_lat: 20041667,
550            last_point_lon: 149937500,
551            scanning_mode: crate::grid::ScanningMode(0b00000000),
552        });
553        assert_eq!(actual, expected);
554    }
555
556    #[test]
557    fn prod_definition_parameters() {
558        let data = ProdDefinition::from_payload(
559            vec![
560                0, 0, 0, 0, 193, 0, 2, 153, 255, 0, 0, 0, 0, 0, 0, 0, 40, 1, 255, 255, 255, 255,
561                255, 255, 255, 255, 255, 255, 255,
562            ]
563            .into_boxed_slice(),
564        )
565        .unwrap();
566
567        assert_eq!(data.parameter_category(), Some(193));
568        assert_eq!(data.parameter_number(), Some(0));
569        assert_eq!(
570            data.forecast_time(),
571            Some(ForecastTime::from_numbers(0, 40))
572        );
573        assert_eq!(
574            data.fixed_surfaces(),
575            Some((
576                FixedSurface::new(1, -127, -2147483647),
577                FixedSurface::new(255, -127, -2147483647)
578            ))
579        );
580    }
581}