Skip to main content

grib/
context.rs

1use std::{
2    cell::{RefCell, RefMut},
3    collections::HashSet,
4    fmt::{self, Display, Formatter},
5    io::{Cursor, Read, Seek},
6};
7
8use grib_template_helpers::{Dump as _, TryFromSlice as _};
9
10#[cfg(feature = "time-calculation")]
11use crate::TemporalInfo;
12use crate::{
13    GridDefinitionTemplateValues, GridPointIndex, GridPointIndexIterator, LatLons, TemporalRawInfo,
14    codetables::{
15        CodeTable3_1, CodeTable4_0, CodeTable4_1, CodeTable4_2, CodeTable4_3, CodeTable5_0, Lookup,
16    },
17    datatypes::*,
18    def::grib2::{Section1, Section3, Section4, Section5, Section6, SectionHeader},
19    error::*,
20    grid::GridPointLatLons,
21    parser::Grib2SubmessageIndexStream,
22    reader::{Grib2Read, Grib2SectionStream, SECT8_ES_SIZE, SeekableGrib2Reader},
23};
24
25#[derive(Default, Debug, Clone, PartialEq, Eq)]
26pub struct SectionInfo {
27    pub num: u8,
28    pub offset: usize,
29    pub size: usize,
30    pub body: Option<SectionBody>,
31}
32
33impl SectionInfo {
34    pub fn get_tmpl_code(&self) -> Option<TemplateInfo> {
35        let tmpl_num = self.body.as_ref()?.get_tmpl_num()?;
36        Some(TemplateInfo(self.num, tmpl_num))
37    }
38
39    pub(crate) fn new_8(offset: usize) -> Self {
40        Self {
41            num: 8,
42            offset,
43            size: SECT8_ES_SIZE,
44            body: None,
45        }
46    }
47}
48
49#[derive(Debug, Clone, PartialEq, Eq)]
50pub enum SectionBody {
51    Section0(Indicator),
52    Section1(Identification),
53    Section2(LocalUse),
54    Section3(GridDefinition),
55    Section4(ProdDefinition),
56    Section5(ReprDefinition),
57    Section6(BitMap),
58    Section7,
59}
60
61impl SectionBody {
62    fn get_tmpl_num(&self) -> Option<u16> {
63        match self {
64            Self::Section3(s) => Some(s.grid_tmpl_num()),
65            Self::Section4(s) => Some(s.prod_tmpl_num()),
66            Self::Section5(s) => Some(s.repr_tmpl_num()),
67            _ => None,
68        }
69    }
70}
71
72#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
73pub struct TemplateInfo(pub u8, pub u16);
74
75impl TemplateInfo {
76    pub fn describe(&self) -> Option<String> {
77        match self.0 {
78            3 => Some(CodeTable3_1.lookup(usize::from(self.1)).to_string()),
79            4 => Some(CodeTable4_0.lookup(usize::from(self.1)).to_string()),
80            5 => Some(CodeTable5_0.lookup(usize::from(self.1)).to_string()),
81            _ => None,
82        }
83    }
84}
85
86impl Display for TemplateInfo {
87    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
88        write!(f, "{}.{}", self.0, self.1)
89    }
90}
91
92/// Reads a [`Grib2`] instance from an I/O stream of GRIB2.
93///
94/// # Examples
95///
96/// ```
97/// fn main() -> Result<(), Box<dyn std::error::Error>> {
98///     let f = std::fs::File::open(
99///         "testdata/icon_global_icosahedral_single-level_2021112018_000_TOT_PREC.grib2",
100///     )?;
101///     let f = std::io::BufReader::new(f);
102///     let result = grib::from_reader(f);
103///
104///     assert!(result.is_ok());
105///     let grib2 = result?;
106///     assert_eq!(grib2.len(), 1);
107///     Ok(())
108/// }
109/// ```
110pub fn from_reader<SR: Read + Seek>(
111    reader: SR,
112) -> Result<Grib2<SeekableGrib2Reader<SR>>, GribError> {
113    Grib2::<SeekableGrib2Reader<SR>>::read_with_seekable(reader)
114}
115
116/// Reads a [`Grib2`] instance from bytes of GRIB2.
117///
118/// # Examples
119///
120/// You can use this method to create a reader from a slice, i.e., a borrowed
121/// sequence of bytes:
122///
123/// ```
124/// use std::io::Read;
125///
126/// fn main() -> Result<(), Box<dyn std::error::Error>> {
127///     let f = std::fs::File::open(
128///         "testdata/icon_global_icosahedral_single-level_2021112018_000_TOT_PREC.grib2",
129///     )?;
130///     let mut f = std::io::BufReader::new(f);
131///     let mut buf = Vec::new();
132///     f.read_to_end(&mut buf).unwrap();
133///     let result = grib::from_bytes(&buf);
134///
135///     assert!(result.is_ok());
136///     let grib2 = result?;
137///     assert_eq!(grib2.len(), 1);
138///     Ok(())
139/// }
140/// ```
141///
142/// Also, you can use this method to create a reader from an owned sequence of
143/// bytes:
144///
145/// ```
146/// use std::io::Read;
147///
148/// fn main() -> Result<(), Box<dyn std::error::Error>> {
149///     let f = std::fs::File::open(
150///         "testdata/icon_global_icosahedral_single-level_2021112018_000_TOT_PREC.grib2",
151///     )?;
152///     let mut f = std::io::BufReader::new(f);
153///     let mut buf = Vec::new();
154///     f.read_to_end(&mut buf).unwrap();
155///     let result = grib::from_bytes(buf);
156///
157///     assert!(result.is_ok());
158///     let grib2 = result?;
159///     assert_eq!(grib2.len(), 1);
160///     Ok(())
161/// }
162/// ```
163pub fn from_bytes<T>(bytes: T) -> Result<Grib2<SeekableGrib2Reader<Cursor<T>>>, GribError>
164where
165    T: AsRef<[u8]>,
166{
167    let reader = Cursor::new(bytes);
168    Grib2::<SeekableGrib2Reader<Cursor<T>>>::read_with_seekable(reader)
169}
170
171pub struct Grib2<R> {
172    reader: RefCell<R>,
173    sections: Box<[SectionInfo]>,
174    submessages: Vec<Grib2SubmessageIndex>,
175}
176
177impl<R> Grib2<R> {
178    /// Returns the length of submessages in the data.
179    ///
180    /// # Examples
181    ///
182    /// ```
183    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
184    ///     let f = std::fs::File::open(
185    ///         "testdata/icon_global_icosahedral_single-level_2021112018_000_TOT_PREC.grib2",
186    ///     )?;
187    ///     let f = std::io::BufReader::new(f);
188    ///     let grib2 = grib::from_reader(f)?;
189    ///
190    ///     assert_eq!(grib2.len(), 1);
191    ///     Ok(())
192    /// }
193    /// ```
194    pub fn len(&self) -> usize {
195        self.submessages.len()
196    }
197
198    /// Returns `true` if `self` has zero submessages.
199    #[inline]
200    pub fn is_empty(&self) -> bool {
201        self.len() == 0
202    }
203
204    /// Returns an iterator over submessages in the data.
205    ///
206    /// # Examples
207    ///
208    /// ```
209    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
210    ///     let f = std::fs::File::open(
211    ///         "testdata/icon_global_icosahedral_single-level_2021112018_000_TOT_PREC.grib2",
212    ///     )?;
213    ///     let f = std::io::BufReader::new(f);
214    ///     let grib2 = grib::from_reader(f)?;
215    ///
216    ///     let mut iter = grib2.iter();
217    ///     let first = iter.next();
218    ///     assert!(first.is_some());
219    ///
220    ///     let first = first.unwrap();
221    ///     let (message_index, _) = first;
222    ///     assert_eq!(message_index, (0, 0));
223    ///
224    ///     let second = iter.next();
225    ///     assert!(second.is_none());
226    ///     Ok(())
227    /// }
228    /// ```
229    #[inline]
230    pub fn iter(&self) -> SubmessageIterator<'_, R> {
231        self.into_iter()
232    }
233
234    /// Returns an iterator over submessages in the data.
235    ///
236    /// This is an alias to [`Grib2::iter()`].
237    pub fn submessages(&self) -> SubmessageIterator<'_, R> {
238        self.into_iter()
239    }
240
241    /// Returns an iterator over sections in the data.
242    ///
243    /// # Examples
244    ///
245    /// ```
246    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
247    ///     let f = std::fs::File::open(
248    ///         "testdata/icon_global_icosahedral_single-level_2021112018_000_TOT_PREC.grib2",
249    ///     )?;
250    ///     let f = std::io::BufReader::new(f);
251    ///     let grib2 = grib::from_reader(f)?;
252    ///
253    ///     let mut iter = grib2.sections();
254    ///     let first = iter.next();
255    ///     assert!(first.is_some());
256    ///
257    ///     let first = first.unwrap();
258    ///     assert_eq!(first.num, 0);
259    ///
260    ///     let tenth = iter.nth(9);
261    ///     assert!(tenth.is_none());
262    ///     Ok(())
263    /// }
264    /// ```
265    pub fn sections(&self) -> std::slice::Iter<'_, SectionInfo> {
266        self.sections.iter()
267    }
268}
269
270impl<R: Grib2Read> Grib2<R> {
271    pub fn read(r: R) -> Result<Self, GribError> {
272        let mut sect_stream = Grib2SectionStream::new(r);
273        let mut cacher = Vec::new();
274        let parser = Grib2SubmessageIndexStream::new(sect_stream.by_ref()).with_cacher(&mut cacher);
275        let submessages = parser.collect::<Result<Vec<_>, _>>()?;
276        Ok(Self {
277            reader: RefCell::new(sect_stream.into_reader()),
278            sections: cacher.into_boxed_slice(),
279            submessages,
280        })
281    }
282
283    pub fn read_with_seekable<SR: Read + Seek>(
284        r: SR,
285    ) -> Result<Grib2<SeekableGrib2Reader<SR>>, GribError> {
286        let r = SeekableGrib2Reader::new(r);
287        Grib2::<SeekableGrib2Reader<SR>>::read(r)
288    }
289
290    pub fn list_templates(&self) -> Vec<TemplateInfo> {
291        get_templates(&self.sections)
292    }
293}
294
295impl<'a, R: 'a> IntoIterator for &'a Grib2<R> {
296    type Item = (MessageIndex, SubMessage<'a, R>);
297    type IntoIter = SubmessageIterator<'a, R>;
298
299    fn into_iter(self) -> Self::IntoIter {
300        Self::IntoIter::new(self)
301    }
302}
303
304fn get_templates(sects: &[SectionInfo]) -> Vec<TemplateInfo> {
305    let uniq: HashSet<_> = sects.iter().filter_map(|s| s.get_tmpl_code()).collect();
306    let mut vec: Vec<_> = uniq.into_iter().collect();
307    vec.sort_unstable();
308    vec
309}
310
311/// An iterator over submessages in the GRIB data.
312///
313/// This `struct` is created by the [`iter`] method on [`Grib2`]. See its
314/// documentation for more.
315///
316/// [`iter`]: Grib2::iter
317#[derive(Clone)]
318pub struct SubmessageIterator<'a, R> {
319    context: &'a Grib2<R>,
320    pos: usize,
321}
322
323impl<'a, R> SubmessageIterator<'a, R> {
324    fn new(context: &'a Grib2<R>) -> Self {
325        Self { context, pos: 0 }
326    }
327
328    fn new_submessage_section(&self, index: usize) -> Option<SubMessageSection<'a>> {
329        Some(SubMessageSection::new(
330            index,
331            self.context.sections.get(index)?,
332        ))
333    }
334}
335
336impl<'a, R> Iterator for SubmessageIterator<'a, R> {
337    type Item = (MessageIndex, SubMessage<'a, R>);
338
339    fn next(&mut self) -> Option<Self::Item> {
340        let submessage_index = self.context.submessages.get(self.pos)?;
341        self.pos += 1;
342
343        Some((
344            submessage_index.message_index(),
345            SubMessage(
346                self.new_submessage_section(submessage_index.0)?,
347                self.new_submessage_section(submessage_index.1)?,
348                submessage_index
349                    .2
350                    .and_then(|i| self.new_submessage_section(i)),
351                self.new_submessage_section(submessage_index.3)?,
352                self.new_submessage_section(submessage_index.4)?,
353                self.new_submessage_section(submessage_index.5)?,
354                self.new_submessage_section(submessage_index.6)?,
355                self.new_submessage_section(submessage_index.7)?,
356                self.new_submessage_section(submessage_index.8)?,
357                self.context.reader.borrow_mut(),
358            ),
359        ))
360    }
361
362    fn size_hint(&self) -> (usize, Option<usize>) {
363        let size = self.context.submessages.len() - self.pos;
364        (size, Some(size))
365    }
366
367    fn nth(&mut self, n: usize) -> Option<Self::Item> {
368        self.pos = n;
369        self.next()
370    }
371}
372
373impl<'a, R> IntoIterator for &'a SubmessageIterator<'a, R> {
374    type Item = (MessageIndex, SubMessage<'a, R>);
375    type IntoIter = SubmessageIterator<'a, R>;
376
377    fn into_iter(self) -> Self::IntoIter {
378        SubmessageIterator {
379            context: self.context,
380            pos: self.pos,
381        }
382    }
383}
384
385pub struct SubMessage<'a, R>(
386    pub SubMessageSection<'a>,
387    pub SubMessageSection<'a>,
388    pub Option<SubMessageSection<'a>>,
389    pub SubMessageSection<'a>,
390    pub SubMessageSection<'a>,
391    pub SubMessageSection<'a>,
392    pub SubMessageSection<'a>,
393    pub SubMessageSection<'a>,
394    pub SubMessageSection<'a>,
395    pub(crate) RefMut<'a, R>,
396);
397
398impl<R> SubMessage<'_, R> {
399    /// Returns the product's parameter.
400    ///
401    /// In the context of GRIB products, parameters refer to weather elements
402    /// such as air temperature, air pressure, and humidity, and other physical
403    /// quantities.
404    ///
405    /// # Examples
406    ///
407    /// ```
408    /// use std::{
409    ///     fs::File,
410    ///     io::{BufReader, Read},
411    /// };
412    ///
413    /// use grib::codetables::NCEP;
414    ///
415    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
416    ///     let mut buf = Vec::new();
417    ///
418    ///     let f = File::open("testdata/gdas.t12z.pgrb2.0p25.f000.0-10.xz")?;
419    ///     let f = BufReader::new(f);
420    ///     let mut f = xz2::bufread::XzDecoder::new(f);
421    ///     f.read_to_end(&mut buf)?;
422    ///
423    ///     let f = std::io::Cursor::new(buf);
424    ///     let grib2 = grib::from_reader(f)?;
425    ///
426    ///     let mut iter = grib2.iter();
427    ///     let (_, message) = iter.next().ok_or_else(|| "first message is not found")?;
428    ///
429    ///     let param = message.parameter();
430    ///     assert_eq!(
431    ///         param,
432    ///         Some(grib::Parameter {
433    ///             discipline: 0,
434    ///             centre: 7,
435    ///             master_ver: 2,
436    ///             local_ver: 1,
437    ///             category: 3,
438    ///             num: 1
439    ///         })
440    ///     );
441    ///     let param = param.unwrap();
442    ///     assert_eq!(
443    ///         param.description(),
444    ///         Some("Pressure reduced to MSL".to_owned())
445    ///     );
446    ///     assert!(param.is_identical_to(NCEP::PRMSL));
447    ///     Ok(())
448    /// }
449    /// ```
450    pub fn parameter(&self) -> Option<Parameter> {
451        let discipline = self.indicator().discipline;
452        let ident = self.identification();
453        let centre = ident.centre_id();
454        let master_ver = ident.master_table_version();
455        let local_ver = ident.local_table_version();
456        let prod_def = self.prod_def();
457        let category = prod_def.parameter_category()?;
458        let num = prod_def.parameter_number()?;
459        Some(Parameter {
460            discipline,
461            centre,
462            master_ver,
463            local_ver,
464            category,
465            num,
466        })
467    }
468
469    pub fn indicator(&self) -> &Indicator {
470        // panics should not happen if data is correct
471        match self.0.body.body.as_ref().unwrap() {
472            SectionBody::Section0(data) => data,
473            _ => panic!("something unexpected happened"),
474        }
475    }
476
477    pub fn identification(&self) -> &Identification {
478        // panics should not happen if data is correct
479        match self.1.body.body.as_ref().unwrap() {
480            SectionBody::Section1(data) => data,
481            _ => panic!("something unexpected happened"),
482        }
483    }
484
485    pub fn grid_def(&self) -> &GridDefinition {
486        // panics should not happen if data is correct
487        match self.3.body.body.as_ref().unwrap() {
488            SectionBody::Section3(data) => data,
489            _ => panic!("something unexpected happened"),
490        }
491    }
492
493    pub fn prod_def(&self) -> &ProdDefinition {
494        // panics should not happen if data is correct
495        match self.4.body.body.as_ref().unwrap() {
496            SectionBody::Section4(data) => data,
497            _ => panic!("something unexpected happened"),
498        }
499    }
500
501    pub fn repr_def(&self) -> &ReprDefinition {
502        // panics should not happen if data is correct
503        match self.5.body.body.as_ref().unwrap() {
504            SectionBody::Section5(data) => data,
505            _ => panic!("something unexpected happened"),
506        }
507    }
508
509    pub fn describe(&self) -> String {
510        let category = self.prod_def().parameter_category();
511        let forecast_time = self
512            .prod_def()
513            .forecast_time()
514            .map(|ft| ft.describe())
515            .unwrap_or((String::new(), String::new()));
516        let fixed_surfaces_info = self
517            .prod_def()
518            .fixed_surfaces()
519            .map(|(first, second)| (first.describe(), second.describe()))
520            .map(|(first, second)| (first.0, first.1, first.2, second.0, second.1, second.2))
521            .unwrap_or((
522                String::new(),
523                String::new(),
524                String::new(),
525                String::new(),
526                String::new(),
527                String::new(),
528            ));
529
530        format!(
531            "\
532Grid:                                   {}
533  Number of points:                     {}
534Product:                                {}
535  Parameter Category:                   {}
536  Parameter:                            {}
537  Generating Proceess:                  {}
538  Forecast Time:                        {}
539  Forecast Time Unit:                   {}
540  1st Fixed Surface Type:               {}
541  1st Scale Factor:                     {}
542  1st Scaled Value:                     {}
543  2nd Fixed Surface Type:               {}
544  2nd Scale Factor:                     {}
545  2nd Scaled Value:                     {}
546Data Representation:                    {}
547  Number of represented values:         {}
548",
549            self.3.describe().unwrap_or_default(),
550            self.grid_def().num_points(),
551            self.4.describe().unwrap_or_default(),
552            category
553                .map(|v| CodeTable4_1::new(self.indicator().discipline)
554                    .lookup(usize::from(v))
555                    .to_string())
556                .unwrap_or_default(),
557            self.prod_def()
558                .parameter_number()
559                .zip(category)
560                .map(|(n, c)| CodeTable4_2::new(self.indicator().discipline, c)
561                    .lookup(usize::from(n))
562                    .to_string())
563                .unwrap_or_default(),
564            self.prod_def()
565                .generating_process()
566                .map(|v| CodeTable4_3.lookup(usize::from(v)).to_string())
567                .unwrap_or_default(),
568            forecast_time.1,
569            forecast_time.0,
570            fixed_surfaces_info.0,
571            fixed_surfaces_info.1,
572            fixed_surfaces_info.2,
573            fixed_surfaces_info.3,
574            fixed_surfaces_info.4,
575            fixed_surfaces_info.5,
576            self.5.describe().unwrap_or_default(),
577            self.repr_def().num_points(),
578        )
579    }
580
581    /// Provides access to the parameters in Section 1.
582    ///
583    /// # Examples
584    /// ```
585    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
586    ///     let f = std::fs::File::open(
587    ///         "testdata/Z__C_RJTD_20160822020000_NOWC_GPV_Ggis10km_Pphw10_FH0000-0100_grib2.bin",
588    ///     )?;
589    ///     let f = std::io::BufReader::new(f);
590    ///     let grib2 = grib::from_reader(f)?;
591    ///     let (_index, first_submessage) = grib2.iter().next().unwrap();
592    ///
593    ///     let actual = first_submessage.section1();
594    ///     let expected = Ok(grib::def::grib2::Section1 {
595    ///         header: grib::def::grib2::SectionHeader {
596    ///             len: 21,
597    ///             sect_num: 1,
598    ///         },
599    ///         payload: grib::def::grib2::Section1Payload {
600    ///             centre_id: 34,
601    ///             subcentre_id: 0,
602    ///             master_table_version: 5,
603    ///             local_table_version: 1,
604    ///             ref_time_significance: 0,
605    ///             ref_time: grib::def::grib2::RefTime {
606    ///                 year: 2016,
607    ///                 month: 8,
608    ///                 day: 22,
609    ///                 hour: 2,
610    ///                 minute: 0,
611    ///                 second: 0,
612    ///             },
613    ///             prod_status: 0,
614    ///             data_type: 2,
615    ///             optional: None,
616    ///         },
617    ///     });
618    ///     assert_eq!(actual, expected);
619    ///
620    ///     Ok(())
621    /// }
622    /// ```
623    pub fn section1(&self) -> Result<Section1, GribError> {
624        let Identification { payload } = self.identification();
625        let mut pos = 0;
626        let payload = crate::def::grib2::Section1Payload::try_from_slice(payload, &mut pos)
627            .map_err(|e| GribError::Unknown(e.to_owned()))?;
628
629        let SectionInfo { num, size, .. } = self.1.body;
630        Ok(Section1 {
631            header: SectionHeader {
632                len: *size as u32,
633                sect_num: *num,
634            },
635            payload,
636        })
637    }
638
639    /// Provides access to the parameters in Section 3.
640    ///
641    /// # Examples
642    /// ```
643    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
644    ///     let f = std::fs::File::open(
645    ///         "testdata/Z__C_RJTD_20160822020000_NOWC_GPV_Ggis10km_Pphw10_FH0000-0100_grib2.bin",
646    ///     )?;
647    ///     let f = std::io::BufReader::new(f);
648    ///     let grib2 = grib::from_reader(f)?;
649    ///     let (_index, first_submessage) = grib2.iter().next().unwrap();
650    ///
651    ///     let actual = first_submessage.section3();
652    ///     let expected = Ok(grib::def::grib2::Section3 {
653    ///         header: grib::def::grib2::SectionHeader {
654    ///             len: 72,
655    ///             sect_num: 3,
656    ///         },
657    ///         payload: grib::def::grib2::Section3Payload {
658    ///             grid_def_source: 0,
659    ///             num_points: 86016,
660    ///             num_point_list_octets: 0,
661    ///             point_list_interpretation: 0,
662    ///             template_num: 0,
663    ///             template: grib::def::grib2::GridDefinitionTemplate::_3_0(grib::def::grib2::template::Template3_0 {
664    ///                 earth: grib::def::grib2::template::param_set::EarthShape {
665    ///                     shape: 4,
666    ///                     spherical_earth_radius_scale_factor: 0xff,
667    ///                     spherical_earth_radius_scaled_value: 0xffffffff,
668    ///                     major_axis_scale_factor: 1,
669    ///                     major_axis_scaled_value: 63781370,
670    ///                     minor_axis_scale_factor: 1,
671    ///                     minor_axis_scaled_value: 63567523,
672    ///                 },
673    ///                 lat_lon: grib::def::grib2::template::param_set::LatLonGrid {
674    ///                     grid: grib::def::grib2::template::param_set::Grid {
675    ///                         ni: 256,
676    ///                         nj: 336,
677    ///                         initial_production_domain_basic_angle: 0,
678    ///                         basic_angle_subdivisions: 0xffffffff,
679    ///                         first_point_lat: 47958333,
680    ///                         first_point_lon: 118062500,
681    ///                         resolution_and_component_flags: grib::def::grib2::template::param_set::ResolutionAndComponentFlags(0b00110000),
682    ///                         last_point_lat: 20041667,
683    ///                         last_point_lon: 149937500,
684    ///                     },
685    ///                     i_direction_inc: 125000,
686    ///                     j_direction_inc: 83333,
687    ///                     scanning_mode: grib::def::grib2::template::param_set::ScanningMode(0b00000000),
688    ///                 },
689    ///             }),
690    ///         },
691    ///     });
692    ///     assert_eq!(actual, expected);
693    ///
694    ///     Ok(())
695    /// }
696    /// ```
697    pub fn section3(&self) -> Result<Section3, GribError> {
698        let GridDefinition { payload } = self.grid_def();
699        let mut pos = 0;
700        let payload = crate::def::grib2::Section3Payload::try_from_slice(payload, &mut pos)
701            .map_err(|e| GribError::Unknown(e.to_owned()))?;
702
703        let SectionInfo { num, size, .. } = self.3.body;
704        Ok(Section3 {
705            header: SectionHeader {
706                len: *size as u32,
707                sect_num: *num,
708            },
709            payload,
710        })
711    }
712
713    /// Provides access to the parameters in Section 4.
714    ///
715    /// # Examples
716    /// ```
717    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
718    ///     let f = std::fs::File::open(
719    ///         "testdata/Z__C_RJTD_20160822020000_NOWC_GPV_Ggis10km_Pphw10_FH0000-0100_grib2.bin",
720    ///     )?;
721    ///     let f = std::io::BufReader::new(f);
722    ///     let grib2 = grib::from_reader(f)?;
723    ///     let (_index, first_submessage) = grib2.iter().next().unwrap();
724    ///
725    ///     let actual = first_submessage.section4();
726    ///     let expected = Ok(grib::def::grib2::Section4 {
727    ///         header: grib::def::grib2::SectionHeader {
728    ///             len: 34,
729    ///             sect_num: 4,
730    ///         },
731    ///         payload: grib::def::grib2::Section4Payload {
732    ///             num_coord_values: 0,
733    ///             template_num: 0,
734    ///         },
735    ///     });
736    ///     assert_eq!(actual, expected);
737    ///
738    ///     Ok(())
739    /// }
740    /// ```
741    pub fn section4(&self) -> Result<Section4, GribError> {
742        let ProdDefinition { payload }: &ProdDefinition = self.prod_def();
743        let mut pos = 0;
744        let payload = crate::def::grib2::Section4Payload::try_from_slice(payload, &mut pos)
745            .map_err(|e| GribError::Unknown(e.to_owned()))?;
746
747        let SectionInfo { num, size, .. } = self.4.body;
748        Ok(Section4 {
749            header: SectionHeader {
750                len: *size as u32,
751                sect_num: *num,
752            },
753            payload,
754        })
755    }
756
757    /// Provides access to the parameters in Section 5.
758    ///
759    /// # Examples
760    /// ```
761    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
762    ///     let f = std::fs::File::open(
763    ///         "testdata/Z__C_RJTD_20160822020000_NOWC_GPV_Ggis10km_Pphw10_FH0000-0100_grib2.bin",
764    ///     )?;
765    ///     let f = std::io::BufReader::new(f);
766    ///     let grib2 = grib::from_reader(f)?;
767    ///     let (_index, first_submessage) = grib2.iter().next().unwrap();
768    ///
769    ///     let actual = first_submessage.section5();
770    ///     let expected = Ok(grib::def::grib2::Section5 {
771    ///         header: grib::def::grib2::SectionHeader {
772    ///             len: 23,
773    ///             sect_num: 5,
774    ///         },
775    ///         payload: grib::def::grib2::Section5Payload {
776    ///             num_encoded_points: 86016,
777    ///             template_num: 200,
778    ///             template: grib::def::grib2::DataRepresentationTemplate::_5_200(
779    ///                 grib::def::grib2::template::Template5_200 {
780    ///                     num_bits: 8,
781    ///                     max_val: 3,
782    ///                     max_level: 3,
783    ///                     dec: 0,
784    ///                     level_vals: vec![1, 2, 3],
785    ///                 },
786    ///             ),
787    ///         },
788    ///     });
789    ///     assert_eq!(actual, expected);
790    ///
791    ///     Ok(())
792    /// }
793    /// ```
794    pub fn section5(&self) -> Result<Section5, GribError> {
795        let ReprDefinition { payload } = self.repr_def();
796        let mut pos = 0;
797        let payload = crate::def::grib2::Section5Payload::try_from_slice(payload, &mut pos)
798            .map_err(|e| GribError::Unknown(e.to_owned()))?;
799
800        let SectionInfo { num, size, .. } = self.5.body;
801        Ok(Section5 {
802            header: SectionHeader {
803                len: *size as u32,
804                sect_num: *num,
805            },
806            payload,
807        })
808    }
809
810    /// Provides access to the parameters in Section 6.
811    ///
812    /// # Examples
813    /// ```
814    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
815    ///     let f = std::fs::File::open(
816    ///         "testdata/Z__C_RJTD_20160822020000_NOWC_GPV_Ggis10km_Pphw10_FH0000-0100_grib2.bin",
817    ///     )?;
818    ///     let f = std::io::BufReader::new(f);
819    ///     let grib2 = grib::from_reader(f)?;
820    ///     let (_index, first_submessage) = grib2.iter().next().unwrap();
821    ///
822    ///     let actual = first_submessage.section6();
823    ///     let expected = Ok(grib::def::grib2::Section6 {
824    ///         header: grib::def::grib2::SectionHeader {
825    ///             len: 6,
826    ///             sect_num: 6,
827    ///         },
828    ///         payload: grib::def::grib2::Section6Payload {
829    ///             bitmap_indicator: 255,
830    ///         },
831    ///     });
832    ///     assert_eq!(actual, expected);
833    ///
834    ///     Ok(())
835    /// }
836    /// ```
837    pub fn section6(&self) -> Result<Section6, GribError> {
838        // panics should not happen if data is correct
839        let BitMap { bitmap_indicator } = match self.6.body.body.as_ref().unwrap() {
840            SectionBody::Section6(data) => data,
841            _ => panic!("something unexpected happened"),
842        };
843        let payload = crate::def::grib2::Section6Payload {
844            bitmap_indicator: *bitmap_indicator,
845        };
846
847        let SectionInfo { num, size, .. } = self.6.body;
848        Ok(Section6 {
849            header: SectionHeader {
850                len: *size as u32,
851                sect_num: *num,
852            },
853            payload,
854        })
855    }
856
857    /// Dumps the GRIB2 submessage.
858    ///
859    /// # Examples
860    /// ```
861    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
862    ///     let f = std::fs::File::open(
863    ///         "testdata/Z__C_RJTD_20160822020000_NOWC_GPV_Ggis10km_Pphw10_FH0000-0100_grib2.bin",
864    ///     )?;
865    ///     let f = std::io::BufReader::new(f);
866    ///     let grib2 = grib::from_reader(f)?;
867    ///     let (_index, first_submessage) = grib2.iter().next().unwrap();
868    ///
869    ///     let mut buf = std::io::Cursor::new(Vec::with_capacity(1024));
870    ///     first_submessage.dump(&mut buf)?;
871    ///     let expected = "\
872    /// ##  SUBMESSAGE (total_length = 10321)
873    /// ###  SECTION 0: INDICATOR SECTION (length = 16)
874    /// ###  SECTION 1: IDENTIFICATION SECTION (length = 21)
875    /// 1-4       header.len = 21  // Length of section in octets (nn).
876    /// 5         header.sect_num = 1  // Number of section.
877    /// 6-7       payload.centre_id = 34  // Identification of originating/generating centre (see Common Code table C-11).
878    /// 8-9       payload.subcentre_id = 0  // Identification of originating/generating subcentre (allocated by originating/generating centre).
879    /// 10        payload.master_table_version = 5  // GRIB master table version number (see Common Code table C-0 and Note 1).
880    /// 11        payload.local_table_version = 1  // Version number of GRIB Local tables used to augment Master tables (see Code table 1.1 and Note 2).
881    /// 12        payload.ref_time_significance = 0  // Significance of reference time (see Code table 1.2).
882    /// 13-14     payload.ref_time.year = 2016  // Year (4 digits).
883    /// 15        payload.ref_time.month = 8  // Month.
884    /// 16        payload.ref_time.day = 22  // Day.
885    /// 17        payload.ref_time.hour = 2  // Hour.
886    /// 18        payload.ref_time.minute = 0  // Minute.
887    /// 19        payload.ref_time.second = 0  // Second.
888    /// 20        payload.prod_status = 0  // Production status of processed data in this GRIB message (see Code table 1.3).
889    /// 21        payload.data_type = 2  // Type of processed data in this GRIB message (see Code table 1.4).
890    /// ###  SECTION 3: GRID DEFINITION SECTION (length = 72)
891    /// 1-4       header.len = 72  // Length of section in octets (nn).
892    /// 5         header.sect_num = 3  // Number of section.
893    /// 6         payload.grid_def_source = 0  // Source of grid definition (see Code table 3.0 and Note 1).
894    /// 7-10      payload.num_points = 86016  // Number of data points.
895    /// 11        payload.num_point_list_octets = 0  // Number of octets for optional list of numbers (see Note 2).
896    /// 12        payload.point_list_interpretation = 0  // Interpretation of list of numbers (see Code table 3.11).
897    /// 13-14     payload.template_num = 0  // Grid definition template number (= N) (see Code table 3.1).
898    /// 15        payload.template.earth.shape = 4  // Shape of the Earth (see Code table 3.2).
899    /// 16        payload.template.earth.spherical_earth_radius_scale_factor = 255  // Scale factor of radius of spherical Earth.
900    /// 17-20     payload.template.earth.spherical_earth_radius_scaled_value = 4294967295  // Scaled value of radius of spherical Earth.
901    /// 21        payload.template.earth.major_axis_scale_factor = 1  // Scale factor of major axis of oblate spheroid Earth.
902    /// 22-25     payload.template.earth.major_axis_scaled_value = 63781370  // Scaled value of major axis of oblate spheroid Earth.
903    /// 26        payload.template.earth.minor_axis_scale_factor = 1  // Scale factor of minor axis of oblate spheroid Earth.
904    /// 27-30     payload.template.earth.minor_axis_scaled_value = 63567523  // Scaled value of minor axis of oblate spheroid Earth.
905    /// 31-34     payload.template.lat_lon.grid.ni = 256  // Ni - number of points along a parallel.
906    /// 35-38     payload.template.lat_lon.grid.nj = 336  // Nj - number of points along a meridian.
907    /// 39-42     payload.template.lat_lon.grid.initial_production_domain_basic_angle = 0  // Basic angle of the initial production domain (see Note 1).
908    /// 43-46     payload.template.lat_lon.grid.basic_angle_subdivisions = 4294967295  // Subdivisions of basic angle used to define extreme longitudes and latitudes, and direction increments (see Note 1).
909    /// 47-50     payload.template.lat_lon.grid.first_point_lat = 47958333  // La1 - latitude of first grid point (see Note 1).
910    /// 51-54     payload.template.lat_lon.grid.first_point_lon = 118062500  // Lo1 - longitude of first grid point (see Note 1).
911    /// 55        payload.template.lat_lon.grid.resolution_and_component_flags = 0b00110000  // Resolution and component flags (see Flag table 3.3).
912    /// 56-59     payload.template.lat_lon.grid.last_point_lat = 20041667  // La2 - latitude of last grid point (see Note 1).
913    /// 60-63     payload.template.lat_lon.grid.last_point_lon = 149937500  // Lo2 - longitude of last grid point (see Note 1).
914    /// 64-67     payload.template.lat_lon.i_direction_inc = 125000  // Di - i direction increment (see Notes 1 and 5).
915    /// 68-71     payload.template.lat_lon.j_direction_inc = 83333  // Dj - j direction increment (see Notes 1 and 5).
916    /// 72        payload.template.lat_lon.scanning_mode = 0b00000000  // Scanning mode (flags - see Flag table 3.4).
917    /// ###  SECTION 4: PRODUCT DEFINITION SECTION (length = 34)
918    /// 1-4       header.len = 34  // Length of section in octets (nn).
919    /// 5         header.sect_num = 4  // Number of section.
920    /// 6-7       payload.num_coord_values = 0  // Number of coordinate values after template or number of information according to 3D vertical coordinate GRIB2 message (see Notes 1 and 5).
921    /// 8-9       payload.template_num = 0  // Product definition template number (see Code table 4.0).
922    /// ###  SECTION 5: DATA REPRESENTATION SECTION (length = 23)
923    /// 1-4       header.len = 23  // Length of section in octets (nn).
924    /// 5         header.sect_num = 5  // Number of section.
925    /// 6-9       payload.num_encoded_points = 86016  // Number of data points where one or more values are specified in Section 7 when a bit map is present, total number of data points when a bit map is absent.
926    /// 10-11     payload.template_num = 200  // Data representation template number (see Code table 5.0).
927    /// 12        payload.template.num_bits = 8  // Number of bits used for each packed value in the run length packing with level value.
928    /// 13-14     payload.template.max_val = 3  // MV - maximum value within the levels that are used in the packing.
929    /// 15-16     payload.template.max_level = 3  // MVL - maximum value of level (predefined).
930    /// 17        payload.template.dec = 0  // Decimal scale factor of representative value of each level.
931    /// 18-23     payload.template.level_vals = [1, 2, 3]  // List of MVL scaled representative values of each level from lv=1 to MVL.
932    /// ###  SECTION 6: BIT-MAP SECTION (length = 6)
933    /// 1-4       header.len = 6  // Length of section in octets (nn).
934    /// 5         header.sect_num = 6  // Number of section.
935    /// 6         payload.bitmap_indicator = 255  // Bit-map indicator (see Code table 6.0 and the Note).
936    /// ###  SECTION 7: DATA SECTION (length = 1391)
937    /// ###  SECTION 8: END SECTION (length = 4)
938    /// ";
939    ///     assert_eq!(String::from_utf8_lossy(buf.get_ref()), expected);
940    ///
941    ///     Ok(())
942    /// }
943    /// ```
944    pub fn dump<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
945        let write_heading =
946            |writer: &mut W, sect: &SectionInfo, sect_name: &str| -> Result<(), std::io::Error> {
947                let SectionInfo { num, size, .. } = sect;
948                let sect_name = sect_name.to_ascii_uppercase();
949                writeln!(writer, "##  SECTION {num}: {sect_name} (length = {size})")
950            };
951
952        macro_rules! write_section {
953            ($sect:expr) => {{
954                let mut pos = 1;
955                match $sect {
956                    Ok(s) => s.dump(None, &mut pos, writer)?,
957                    Err(e) => writeln!(writer, "error: {}", e)?,
958                }
959            }};
960        }
961
962        let total_length = self.indicator().total_length;
963        writeln!(writer, "#  SUBMESSAGE (total_length = {total_length})")?;
964        write_heading(writer, self.0.body, "indicator section")?;
965        write_heading(writer, self.1.body, "identification section")?;
966        write_section!(self.section1());
967        if let Some(sect) = &self.2 {
968            write_heading(writer, sect.body, "local use section")?;
969        }
970        write_heading(writer, self.3.body, "grid definition section")?;
971        write_section!(self.section3());
972        write_heading(writer, self.4.body, "product definition section")?;
973        write_section!(self.section4());
974        write_heading(writer, self.5.body, "data representation section")?;
975        write_section!(self.section5());
976        write_heading(writer, self.6.body, "bit-map section")?;
977        write_section!(self.section6());
978        write_heading(writer, self.7.body, "data section")?;
979
980        // Since `self.8.body` might be dummy, we don't use that Section 8 data.
981        writeln!(writer, "##  SECTION 8: END SECTION (length = 4)")?;
982
983        Ok(())
984    }
985
986    /// Returns time-related raw information associated with the submessage.
987    ///
988    /// # Examples
989    ///
990    /// ```
991    /// use std::{
992    ///     fs::File,
993    ///     io::{BufReader, Read},
994    /// };
995    ///
996    /// use grib::{
997    ///     Code, ForecastTime, TemporalRawInfo,
998    ///     codetables::grib2::{Table1_2, Table4_4},
999    ///     def::grib2::RefTime,
1000    /// };
1001    ///
1002    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
1003    ///     let f = File::open(
1004    ///         "testdata/Z__C_RJTD_20160822020000_NOWC_GPV_Ggis10km_Pphw10_FH0000-0100_grib2.bin",
1005    ///     )?;
1006    ///     let f = BufReader::new(f);
1007    ///     let grib2 = grib::from_reader(f)?;
1008    ///
1009    ///     let mut iter = grib2.iter();
1010    ///
1011    ///     {
1012    ///         let (_, message) = iter.next().ok_or_else(|| "first message is not found")?;
1013    ///         let actual = message.temporal_raw_info();
1014    ///         let expected = TemporalRawInfo {
1015    ///             ref_time_significance: Code::Name(Table1_2::Analysis),
1016    ///             ref_time_unchecked: RefTime::new(2016, 8, 22, 2, 0, 0),
1017    ///             forecast_time_diff: Some(ForecastTime {
1018    ///                 unit: Code::Name(Table4_4::Minute),
1019    ///                 value: 0,
1020    ///             }),
1021    ///         };
1022    ///         assert_eq!(actual, expected);
1023    ///     }
1024    ///
1025    ///     {
1026    ///         let (_, message) = iter.next().ok_or_else(|| "second message is not found")?;
1027    ///         let actual = message.temporal_raw_info();
1028    ///         let expected = TemporalRawInfo {
1029    ///             ref_time_significance: Code::Name(Table1_2::Analysis),
1030    ///             ref_time_unchecked: RefTime::new(2016, 8, 22, 2, 0, 0),
1031    ///             forecast_time_diff: Some(ForecastTime {
1032    ///                 unit: Code::Name(Table4_4::Minute),
1033    ///                 value: 10,
1034    ///             }),
1035    ///         };
1036    ///         assert_eq!(actual, expected);
1037    ///     }
1038    ///
1039    ///     Ok(())
1040    /// }
1041    /// ```
1042    pub fn temporal_raw_info(&self) -> TemporalRawInfo {
1043        let ref_time_significance = self.identification().ref_time_significance();
1044        let ref_time_unchecked = self.identification().ref_time_unchecked();
1045        let forecast_time = self.prod_def().forecast_time();
1046        TemporalRawInfo::new(ref_time_significance, ref_time_unchecked, forecast_time)
1047    }
1048
1049    #[cfg(feature = "time-calculation")]
1050    #[cfg_attr(docsrs, doc(cfg(feature = "time-calculation")))]
1051    /// Returns time-related calculated information associated with the
1052    /// submessage.
1053    ///
1054    /// # Examples
1055    ///
1056    /// ```
1057    /// use std::{
1058    ///     fs::File,
1059    ///     io::{BufReader, Read},
1060    /// };
1061    ///
1062    /// use chrono::{TimeZone, Utc};
1063    /// use grib::{
1064    ///     Code, ForecastTime, TemporalInfo,
1065    ///     codetables::grib2::{Table1_2, Table4_4},
1066    /// };
1067    ///
1068    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
1069    ///     let f = File::open(
1070    ///         "testdata/Z__C_RJTD_20160822020000_NOWC_GPV_Ggis10km_Pphw10_FH0000-0100_grib2.bin",
1071    ///     )?;
1072    ///     let f = BufReader::new(f);
1073    ///     let grib2 = grib::from_reader(f)?;
1074    ///
1075    ///     let mut iter = grib2.iter();
1076    ///
1077    ///     {
1078    ///         let (_, message) = iter.next().ok_or_else(|| "first message is not found")?;
1079    ///         let actual = message.temporal_info();
1080    ///         let expected = TemporalInfo {
1081    ///             ref_time: Some(Utc.with_ymd_and_hms(2016, 8, 22, 2, 0, 0).unwrap()),
1082    ///             forecast_time_target: Some(Utc.with_ymd_and_hms(2016, 8, 22, 2, 0, 0).unwrap()),
1083    ///         };
1084    ///         assert_eq!(actual, expected);
1085    ///     }
1086    ///
1087    ///     {
1088    ///         let (_, message) = iter.next().ok_or_else(|| "second message is not found")?;
1089    ///         let actual = message.temporal_info();
1090    ///         let expected = TemporalInfo {
1091    ///             ref_time: Some(Utc.with_ymd_and_hms(2016, 8, 22, 2, 0, 0).unwrap()),
1092    ///             forecast_time_target: Some(Utc.with_ymd_and_hms(2016, 8, 22, 2, 10, 0).unwrap()),
1093    ///         };
1094    ///         assert_eq!(actual, expected);
1095    ///     }
1096    ///
1097    ///     Ok(())
1098    /// }
1099    /// ```
1100    pub fn temporal_info(&self) -> TemporalInfo {
1101        let raw_info = self.temporal_raw_info();
1102        TemporalInfo::from(&raw_info)
1103    }
1104
1105    /// Returns the shape of the grid, i.e. a tuple of the number of grids in
1106    /// the i and j directions.
1107    ///
1108    /// # Examples
1109    ///
1110    /// ```
1111    /// use std::{
1112    ///     fs::File,
1113    ///     io::{BufReader, Read},
1114    /// };
1115    ///
1116    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
1117    ///     let mut buf = Vec::new();
1118    ///
1119    ///     let f = File::open("testdata/gdas.t12z.pgrb2.0p25.f000.0-10.xz")?;
1120    ///     let f = BufReader::new(f);
1121    ///     let mut f = xz2::bufread::XzDecoder::new(f);
1122    ///     f.read_to_end(&mut buf)?;
1123    ///
1124    ///     let f = std::io::Cursor::new(buf);
1125    ///     let grib2 = grib::from_reader(f)?;
1126    ///
1127    ///     let mut iter = grib2.iter();
1128    ///     let (_, message) = iter.next().ok_or_else(|| "first message is not found")?;
1129    ///
1130    ///     let shape = message.grid_shape()?;
1131    ///     assert_eq!(shape, (1440, 721));
1132    ///     Ok(())
1133    /// }
1134    /// ```
1135    pub fn grid_shape(&self) -> Result<(usize, usize), GribError> {
1136        let grid_def = self.grid_def();
1137        let shape = GridDefinitionTemplateValues::try_from(grid_def)?.grid_shape();
1138        Ok(shape)
1139    }
1140
1141    /// Computes and returns an iterator over `(i, j)` of grid points.
1142    ///
1143    /// The order of items is the same as the order of the grid point values,
1144    /// defined by the scanning mode
1145    /// ([`ScanningMode`](`crate::def::grib2::template::param_set::ScanningMode`))
1146    /// in the data.
1147    ///
1148    /// This iterator allows users to perform their own coordinate calculations
1149    /// for unsupported grid systems and map the results to grid point values.
1150    ///
1151    /// # Examples
1152    ///
1153    /// ```
1154    /// use std::{
1155    ///     fs::File,
1156    ///     io::{BufReader, Read},
1157    /// };
1158    ///
1159    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
1160    ///     let mut buf = Vec::new();
1161    ///
1162    ///     let f = File::open("testdata/gdas.t12z.pgrb2.0p25.f000.0-10.xz")?;
1163    ///     let f = BufReader::new(f);
1164    ///     let mut f = xz2::bufread::XzDecoder::new(f);
1165    ///     f.read_to_end(&mut buf)?;
1166    ///
1167    ///     let f = std::io::Cursor::new(buf);
1168    ///     let grib2 = grib::from_reader(f)?;
1169    ///
1170    ///     let mut iter = grib2.iter();
1171    ///     let (_, message) = iter.next().ok_or_else(|| "first message is not found")?;
1172    ///
1173    ///     let mut latlons = message.ij()?;
1174    ///     assert_eq!(latlons.next(), Some((0, 0)));
1175    ///     assert_eq!(latlons.next(), Some((1, 0)));
1176    ///     Ok(())
1177    /// }
1178    /// ```
1179    pub fn ij(&self) -> Result<GridPointIndexIterator, GribError> {
1180        let grid_def = self.grid_def();
1181        let num_defined = grid_def.num_points() as usize;
1182        let ij = GridDefinitionTemplateValues::try_from(grid_def)?.ij()?;
1183        let (num_decoded, _) = ij.size_hint();
1184        if num_defined == num_decoded {
1185            Ok(ij)
1186        } else {
1187            Err(GribError::InvalidValueError(format!(
1188                "number of grid points does not match: {num_defined} (defined) vs {num_decoded} (decoded)"
1189            )))
1190        }
1191    }
1192}
1193
1194impl<'s, R> LatLons for SubMessage<'s, R> {
1195    type Iter<'a>
1196        = GridPointLatLons
1197    where
1198        Self: 'a;
1199
1200    /// Computes and returns an iterator over latitudes and longitudes of grid
1201    /// points in degrees. [Read more](`crate::LatLons::latlons`)
1202    ///
1203    /// # Examples
1204    ///
1205    /// ```
1206    /// use std::{
1207    ///     fs::File,
1208    ///     io::{BufReader, Read},
1209    /// };
1210    ///
1211    /// use grib::LatLons;
1212    ///
1213    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
1214    ///     let mut buf = Vec::new();
1215    ///
1216    ///     let f = File::open("testdata/gdas.t12z.pgrb2.0p25.f000.0-10.xz")?;
1217    ///     let f = BufReader::new(f);
1218    ///     let mut f = xz2::bufread::XzDecoder::new(f);
1219    ///     f.read_to_end(&mut buf)?;
1220    ///
1221    ///     let f = std::io::Cursor::new(buf);
1222    ///     let grib2 = grib::from_reader(f)?;
1223    ///
1224    ///     let mut iter = grib2.iter();
1225    ///     let (_, message) = iter.next().ok_or_else(|| "first message is not found")?;
1226    ///
1227    ///     let mut latlons = message.latlons()?;
1228    ///     assert_eq!(latlons.next(), Some((90.0, 0.0)));
1229    ///     assert_eq!(latlons.next(), Some((90.0, 0.25)));
1230    ///     Ok(())
1231    /// }
1232    /// ```
1233    fn latlons_unchecked<'a>(&'a self) -> Result<Self::Iter<'a>, GribError> {
1234        let grid_def = self.grid_def();
1235        let num_defined = grid_def.num_points() as usize;
1236        let latlons = GridDefinitionTemplateValues::try_from(grid_def)?.latlons_unchecked()?;
1237        let (num_decoded, _) = latlons.size_hint();
1238        if num_defined == num_decoded {
1239            Ok(latlons)
1240        } else {
1241            Err(GribError::InvalidValueError(format!(
1242                "number of grid points does not match: {num_defined} (defined) vs {num_decoded} (decoded)"
1243            )))
1244        }
1245    }
1246}
1247
1248pub struct SubMessageSection<'a> {
1249    pub index: usize,
1250    pub body: &'a SectionInfo,
1251}
1252
1253impl<'a> SubMessageSection<'a> {
1254    pub fn new(index: usize, body: &'a SectionInfo) -> Self {
1255        Self { index, body }
1256    }
1257
1258    pub fn template_code(&self) -> Option<TemplateInfo> {
1259        self.body.get_tmpl_code()
1260    }
1261
1262    pub fn describe(&self) -> Option<String> {
1263        self.template_code().and_then(|code| code.describe())
1264    }
1265}
1266
1267#[cfg(test)]
1268mod tests {
1269    use std::{fs::File, io::BufReader};
1270
1271    use super::*;
1272
1273    macro_rules! sect_placeholder {
1274        ($num:expr) => {{
1275            SectionInfo {
1276                num: $num,
1277                offset: 0,
1278                size: 0,
1279                body: None,
1280            }
1281        }};
1282    }
1283
1284    #[test]
1285    fn context_from_buf_reader() {
1286        let f = File::open(
1287            "testdata/icon_global_icosahedral_single-level_2021112018_000_TOT_PREC.grib2",
1288        )
1289        .unwrap();
1290        let f = BufReader::new(f);
1291        let result = from_reader(f);
1292        assert!(result.is_ok())
1293    }
1294
1295    #[test]
1296    fn context_from_bytes() {
1297        let f = File::open(
1298            "testdata/icon_global_icosahedral_single-level_2021112018_000_TOT_PREC.grib2",
1299        )
1300        .unwrap();
1301        let mut f = BufReader::new(f);
1302        let mut buf = Vec::new();
1303        f.read_to_end(&mut buf).unwrap();
1304        let result = from_bytes(&buf);
1305        assert!(result.is_ok())
1306    }
1307
1308    #[test]
1309    fn get_tmpl_code_normal() {
1310        let sect = SectionInfo {
1311            num: 5,
1312            offset: 8902,
1313            size: 23,
1314            body: Some(SectionBody::Section5(
1315                ReprDefinition::from_payload(
1316                    vec![0x00, 0x01, 0x50, 0x00, 0x00, 0xc8].into_boxed_slice(),
1317                )
1318                .unwrap(),
1319            )),
1320        };
1321
1322        assert_eq!(sect.get_tmpl_code(), Some(TemplateInfo(5, 200)));
1323    }
1324
1325    #[test]
1326    fn get_templates_normal() {
1327        let sects = vec![
1328            sect_placeholder!(0),
1329            sect_placeholder!(1),
1330            SectionInfo {
1331                num: 3,
1332                offset: 0,
1333                size: 0,
1334                body: Some(SectionBody::Section3(
1335                    GridDefinition::from_payload(vec![0; 9].into_boxed_slice()).unwrap(),
1336                )),
1337            },
1338            SectionInfo {
1339                num: 4,
1340                offset: 0,
1341                size: 0,
1342                body: Some(SectionBody::Section4(
1343                    ProdDefinition::from_payload(vec![0; 4].into_boxed_slice()).unwrap(),
1344                )),
1345            },
1346            SectionInfo {
1347                num: 5,
1348                offset: 0,
1349                size: 0,
1350                body: Some(SectionBody::Section5(
1351                    ReprDefinition::from_payload(vec![0; 6].into_boxed_slice()).unwrap(),
1352                )),
1353            },
1354            sect_placeholder!(6),
1355            sect_placeholder!(7),
1356            SectionInfo {
1357                num: 3,
1358                offset: 0,
1359                size: 0,
1360                body: Some(SectionBody::Section3(
1361                    GridDefinition::from_payload(
1362                        vec![0, 0, 0, 0, 0, 0, 0, 0, 1].into_boxed_slice(),
1363                    )
1364                    .unwrap(),
1365                )),
1366            },
1367            SectionInfo {
1368                num: 4,
1369                offset: 0,
1370                size: 0,
1371                body: Some(SectionBody::Section4(
1372                    ProdDefinition::from_payload(vec![0; 4].into_boxed_slice()).unwrap(),
1373                )),
1374            },
1375            SectionInfo {
1376                num: 5,
1377                offset: 0,
1378                size: 0,
1379                body: Some(SectionBody::Section5(
1380                    ReprDefinition::from_payload(vec![0; 6].into_boxed_slice()).unwrap(),
1381                )),
1382            },
1383            sect_placeholder!(6),
1384            sect_placeholder!(7),
1385            sect_placeholder!(8),
1386        ]
1387        .into_boxed_slice();
1388
1389        assert_eq!(
1390            get_templates(&sects),
1391            vec![
1392                TemplateInfo(3, 0),
1393                TemplateInfo(3, 1),
1394                TemplateInfo(4, 0),
1395                TemplateInfo(5, 0),
1396            ]
1397        );
1398    }
1399
1400    macro_rules! test_submessage_iterator {
1401        ($((
1402            $name:ident,
1403            $xz_compressed_input:expr,
1404            $nth:expr,
1405            $expected_index:expr,
1406            $expected_section_indices:expr,
1407        ),)*) => ($(
1408            #[test]
1409            fn $name() -> Result<(), Box<dyn std::error::Error>> {
1410                let mut buf = Vec::new();
1411
1412                let f = File::open($xz_compressed_input)?;
1413                let f = BufReader::new(f);
1414                let mut f = xz2::bufread::XzDecoder::new(f);
1415                f.read_to_end(&mut buf)?;
1416
1417                let f = Cursor::new(buf);
1418                let grib2 = crate::from_reader(f)?;
1419                let mut iter = grib2.iter();
1420
1421                let (actual_index, message) = iter.nth($nth).ok_or_else(|| "item not available")?;
1422                assert_eq!(actual_index, $expected_index);
1423                let actual_section_indices = get_section_indices(message);
1424                assert_eq!(actual_section_indices, $expected_section_indices);
1425
1426                Ok(())
1427            }
1428        )*);
1429    }
1430
1431    test_submessage_iterator! {
1432        (
1433            item_0_from_submessage_iterator_for_single_message_data_with_multiple_submessages,
1434            "testdata/Z__C_RJTD_20190304000000_MSM_GUID_Rjp_P-all_FH03-39_Toorg_grib2.bin.xz",
1435            0,
1436            (0, 0),
1437            (0, 1, None, 2, 3, 4, 5, 6, 0),
1438        ),
1439        (
1440            item_1_from_submessage_iterator_for_single_message_data_with_multiple_submessages,
1441            "testdata/Z__C_RJTD_20190304000000_MSM_GUID_Rjp_P-all_FH03-39_Toorg_grib2.bin.xz",
1442            1,
1443            (0, 1),
1444            (0, 1, None, 2, 7, 8, 9, 10, 0),
1445        ),
1446        (
1447            item_0_from_submessage_iterator_for_multi_message_data,
1448            "testdata/gdas.t12z.pgrb2.0p25.f000.0-10.xz",
1449            0,
1450            (0, 0),
1451            (0, 1, None, 2, 3, 4, 5, 6, 7),
1452        ),
1453        (
1454            item_1_from_submessage_iterator_for_multi_message_data,
1455            "testdata/gdas.t12z.pgrb2.0p25.f000.0-10.xz",
1456            1,
1457            (1, 0),
1458            (8, 9, None, 10, 11, 12, 13, 14, 15),
1459        ),
1460    }
1461
1462    fn get_section_indices<R>(
1463        submessage: SubMessage<'_, R>,
1464    ) -> (
1465        usize,
1466        usize,
1467        Option<usize>,
1468        usize,
1469        usize,
1470        usize,
1471        usize,
1472        usize,
1473        usize,
1474    ) {
1475        (
1476            submessage.0.index,
1477            submessage.1.index,
1478            submessage.2.map(|s| s.index),
1479            submessage.3.index,
1480            submessage.4.index,
1481            submessage.5.index,
1482            submessage.6.index,
1483            submessage.7.index,
1484            submessage.8.index,
1485        )
1486    }
1487}