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