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::RefTime {
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            .map_err(|e| GribError::Unknown(e.to_owned()))?;
627
628        let SectionInfo { num, size, .. } = self.1.body;
629        Ok(Section1 {
630            header: SectionHeader {
631                len: *size as u32,
632                sect_num: *num,
633            },
634            payload,
635        })
636    }
637
638    /// Provides access to the parameters in Section 3.
639    ///
640    /// # Examples
641    /// ```
642    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
643    ///     let f = std::fs::File::open(
644    ///         "testdata/Z__C_RJTD_20160822020000_NOWC_GPV_Ggis10km_Pphw10_FH0000-0100_grib2.bin",
645    ///     )?;
646    ///     let f = std::io::BufReader::new(f);
647    ///     let grib2 = grib::from_reader(f)?;
648    ///     let (_index, first_submessage) = grib2.iter().next().unwrap();
649    ///
650    ///     let actual = first_submessage.section3();
651    ///     let expected = Ok(grib::def::grib2::Section3 {
652    ///         header: grib::def::grib2::SectionHeader {
653    ///             len: 72,
654    ///             sect_num: 3,
655    ///         },
656    ///         payload: grib::def::grib2::Section3Payload {
657    ///             grid_def_source: 0,
658    ///             num_points: 86016,
659    ///             num_point_list_octets: 0,
660    ///             point_list_interpretation: 0,
661    ///             template_num: 0,
662    ///             template: grib::def::grib2::GridDefinitionTemplate::_3_0(grib::def::grib2::template::Template3_0 {
663    ///                 earth: grib::def::grib2::template::param_set::EarthShape {
664    ///                     shape: 4,
665    ///                     spherical_earth_radius_scale_factor: 0xff,
666    ///                     spherical_earth_radius_scaled_value: 0xffffffff,
667    ///                     major_axis_scale_factor: 1,
668    ///                     major_axis_scaled_value: 63781370,
669    ///                     minor_axis_scale_factor: 1,
670    ///                     minor_axis_scaled_value: 63567523,
671    ///                 },
672    ///                 lat_lon: grib::def::grib2::template::param_set::LatLonGrid {
673    ///                     grid: grib::def::grib2::template::param_set::Grid {
674    ///                         ni: 256,
675    ///                         nj: 336,
676    ///                         initial_production_domain_basic_angle: 0,
677    ///                         basic_angle_subdivisions: 0xffffffff,
678    ///                         first_point_lat: 47958333,
679    ///                         first_point_lon: 118062500,
680    ///                         resolution_and_component_flags: grib::def::grib2::template::param_set::ResolutionAndComponentFlags(0b00110000),
681    ///                         last_point_lat: 20041667,
682    ///                         last_point_lon: 149937500,
683    ///                     },
684    ///                     i_direction_inc: 125000,
685    ///                     j_direction_inc: 83333,
686    ///                     scanning_mode: grib::def::grib2::template::param_set::ScanningMode(0b00000000),
687    ///                 },
688    ///             }),
689    ///         },
690    ///     });
691    ///     assert_eq!(actual, expected);
692    ///
693    ///     Ok(())
694    /// }
695    /// ```
696    pub fn section3(&self) -> Result<Section3, GribError> {
697        let GridDefinition { payload } = self.grid_def();
698        let mut pos = 0;
699        let payload = crate::def::grib2::Section3Payload::try_from_slice(payload, &mut pos)
700            .map_err(|e| GribError::Unknown(e.to_owned()))?;
701
702        let SectionInfo { num, size, .. } = self.3.body;
703        Ok(Section3 {
704            header: SectionHeader {
705                len: *size as u32,
706                sect_num: *num,
707            },
708            payload,
709        })
710    }
711
712    /// Provides access to the parameters in Section 4.
713    ///
714    /// # Examples
715    /// ```
716    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
717    ///     let f = std::fs::File::open(
718    ///         "testdata/Z__C_RJTD_20160822020000_NOWC_GPV_Ggis10km_Pphw10_FH0000-0100_grib2.bin",
719    ///     )?;
720    ///     let f = std::io::BufReader::new(f);
721    ///     let grib2 = grib::from_reader(f)?;
722    ///     let (_index, first_submessage) = grib2.iter().next().unwrap();
723    ///
724    ///     let actual = first_submessage.section4();
725    ///     let expected = Ok(grib::def::grib2::Section4 {
726    ///         header: grib::def::grib2::SectionHeader {
727    ///             len: 34,
728    ///             sect_num: 4,
729    ///         },
730    ///         payload: grib::def::grib2::Section4Payload {
731    ///             num_coord_values: 0,
732    ///             template_num: 0,
733    ///         },
734    ///     });
735    ///     assert_eq!(actual, expected);
736    ///
737    ///     Ok(())
738    /// }
739    /// ```
740    pub fn section4(&self) -> Result<Section4, GribError> {
741        let ProdDefinition { payload }: &ProdDefinition = self.prod_def();
742        let mut pos = 0;
743        let payload = crate::def::grib2::Section4Payload::try_from_slice(payload, &mut pos)
744            .map_err(|e| GribError::Unknown(e.to_owned()))?;
745
746        let SectionInfo { num, size, .. } = self.4.body;
747        Ok(Section4 {
748            header: SectionHeader {
749                len: *size as u32,
750                sect_num: *num,
751            },
752            payload,
753        })
754    }
755
756    /// Provides access to the parameters in Section 5.
757    ///
758    /// # Examples
759    /// ```
760    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
761    ///     let f = std::fs::File::open(
762    ///         "testdata/Z__C_RJTD_20160822020000_NOWC_GPV_Ggis10km_Pphw10_FH0000-0100_grib2.bin",
763    ///     )?;
764    ///     let f = std::io::BufReader::new(f);
765    ///     let grib2 = grib::from_reader(f)?;
766    ///     let (_index, first_submessage) = grib2.iter().next().unwrap();
767    ///
768    ///     let actual = first_submessage.section5();
769    ///     let expected = Ok(grib::def::grib2::Section5 {
770    ///         header: grib::def::grib2::SectionHeader {
771    ///             len: 23,
772    ///             sect_num: 5,
773    ///         },
774    ///         payload: grib::def::grib2::Section5Payload {
775    ///             num_encoded_points: 86016,
776    ///             template_num: 200,
777    ///             template: grib::def::grib2::DataRepresentationTemplate::_5_200(
778    ///                 grib::def::grib2::template::Template5_200 {
779    ///                     num_bits: 8,
780    ///                     max_val: 3,
781    ///                     max_level: 3,
782    ///                     dec: 0,
783    ///                     level_vals: vec![1, 2, 3],
784    ///                 },
785    ///             ),
786    ///         },
787    ///     });
788    ///     assert_eq!(actual, expected);
789    ///
790    ///     Ok(())
791    /// }
792    /// ```
793    pub fn section5(&self) -> Result<Section5, GribError> {
794        let ReprDefinition { payload } = self.repr_def();
795        let mut pos = 0;
796        let payload = crate::def::grib2::Section5Payload::try_from_slice(payload, &mut pos)
797            .map_err(|e| GribError::Unknown(e.to_owned()))?;
798
799        let SectionInfo { num, size, .. } = self.5.body;
800        Ok(Section5 {
801            header: SectionHeader {
802                len: *size as u32,
803                sect_num: *num,
804            },
805            payload,
806        })
807    }
808
809    /// Provides access to the parameters in Section 6.
810    ///
811    /// # Examples
812    /// ```
813    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
814    ///     let f = std::fs::File::open(
815    ///         "testdata/Z__C_RJTD_20160822020000_NOWC_GPV_Ggis10km_Pphw10_FH0000-0100_grib2.bin",
816    ///     )?;
817    ///     let f = std::io::BufReader::new(f);
818    ///     let grib2 = grib::from_reader(f)?;
819    ///     let (_index, first_submessage) = grib2.iter().next().unwrap();
820    ///
821    ///     let actual = first_submessage.section6();
822    ///     let expected = Ok(grib::def::grib2::Section6 {
823    ///         header: grib::def::grib2::SectionHeader {
824    ///             len: 6,
825    ///             sect_num: 6,
826    ///         },
827    ///         payload: grib::def::grib2::Section6Payload {
828    ///             bitmap_indicator: 255,
829    ///         },
830    ///     });
831    ///     assert_eq!(actual, expected);
832    ///
833    ///     Ok(())
834    /// }
835    /// ```
836    pub fn section6(&self) -> Result<Section6, GribError> {
837        // panics should not happen if data is correct
838        let BitMap { bitmap_indicator } = match self.6.body.body.as_ref().unwrap() {
839            SectionBody::Section6(data) => data,
840            _ => panic!("something unexpected happened"),
841        };
842        let payload = crate::def::grib2::Section6Payload {
843            bitmap_indicator: *bitmap_indicator,
844        };
845
846        let SectionInfo { num, size, .. } = self.6.body;
847        Ok(Section6 {
848            header: SectionHeader {
849                len: *size as u32,
850                sect_num: *num,
851            },
852            payload,
853        })
854    }
855
856    /// Dumps the GRIB2 submessage.
857    ///
858    /// # Examples
859    /// ```
860    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
861    ///     let f = std::fs::File::open(
862    ///         "testdata/Z__C_RJTD_20160822020000_NOWC_GPV_Ggis10km_Pphw10_FH0000-0100_grib2.bin",
863    ///     )?;
864    ///     let f = std::io::BufReader::new(f);
865    ///     let grib2 = grib::from_reader(f)?;
866    ///     let (_index, first_submessage) = grib2.iter().next().unwrap();
867    ///
868    ///     let mut buf = std::io::Cursor::new(Vec::with_capacity(10240));
869    ///     first_submessage.dump(&mut buf)?;
870    ///     let expected = "\
871    /// ##  SUBMESSAGE (total_length = 10321)
872    /// ###  SECTION 0: INDICATOR SECTION (length = 16)
873    /// ###  SECTION 1: IDENTIFICATION SECTION (length = 21)
874    /// 1-4       header.len = 21  // Length of section in octets (nn).
875    /// 5         header.sect_num = 1  // Number of section.
876    /// 6-7       payload.centre_id = 34  // Identification of originating/generating centre (see Common Code table C-11).
877    /// 8-9       payload.subcentre_id = 0  // Identification of originating/generating subcentre (allocated by originating/generating centre).
878    /// 10        payload.master_table_version = 5  // GRIB master table version number (see Common Code table C-0 and Note 1).
879    /// 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).
880    /// 12        payload.ref_time_significance = 0  // Significance of reference time (see Code table 1.2).
881    /// 13-14     payload.ref_time.year = 2016  // Year (4 digits).
882    /// 15        payload.ref_time.month = 8  // Month.
883    /// 16        payload.ref_time.day = 22  // Day.
884    /// 17        payload.ref_time.hour = 2  // Hour.
885    /// 18        payload.ref_time.minute = 0  // Minute.
886    /// 19        payload.ref_time.second = 0  // Second.
887    /// 20        payload.prod_status = 0  // Production status of processed data in this GRIB message (see Code table 1.3).
888    /// 21        payload.data_type = 2  // Type of processed data in this GRIB message (see Code table 1.4).
889    /// ###  SECTION 3: GRID DEFINITION SECTION (length = 72)
890    /// 1-4       header.len = 72  // Length of section in octets (nn).
891    /// 5         header.sect_num = 3  // Number of section.
892    /// 6         payload.grid_def_source = 0  // Source of grid definition (see Code table 3.0 and Note 1).
893    /// 7-10      payload.num_points = 86016  // Number of data points.
894    /// 11        payload.num_point_list_octets = 0  // Number of octets for optional list of numbers (see Note 2).
895    /// 12        payload.point_list_interpretation = 0  // Interpretation of list of numbers (see Code table 3.11).
896    /// 13-14     payload.template_num = 0  // Grid definition template number (= N) (see Code table 3.1).
897    /// 15        payload.template.earth.shape = 4  // Shape of the Earth (see Code table 3.2).
898    /// 16        payload.template.earth.spherical_earth_radius_scale_factor = 255  // Scale factor of radius of spherical Earth.
899    /// 17-20     payload.template.earth.spherical_earth_radius_scaled_value = 4294967295  // Scaled value of radius of spherical Earth.
900    /// 21        payload.template.earth.major_axis_scale_factor = 1  // Scale factor of major axis of oblate spheroid Earth.
901    /// 22-25     payload.template.earth.major_axis_scaled_value = 63781370  // Scaled value of major axis of oblate spheroid Earth.
902    /// 26        payload.template.earth.minor_axis_scale_factor = 1  // Scale factor of minor axis of oblate spheroid Earth.
903    /// 27-30     payload.template.earth.minor_axis_scaled_value = 63567523  // Scaled value of minor axis of oblate spheroid Earth.
904    /// 31-34     payload.template.lat_lon.grid.ni = 256  // Ni - number of points along a parallel.
905    /// 35-38     payload.template.lat_lon.grid.nj = 336  // Nj - number of points along a meridian.
906    /// 39-42     payload.template.lat_lon.grid.initial_production_domain_basic_angle = 0  // Basic angle of the initial production domain (see Note 1).
907    /// 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).
908    /// 47-50     payload.template.lat_lon.grid.first_point_lat = 47958333  // La1 - latitude of first grid point (see Note 1).
909    /// 51-54     payload.template.lat_lon.grid.first_point_lon = 118062500  // Lo1 - longitude of first grid point (see Note 1).
910    /// 55        payload.template.lat_lon.grid.resolution_and_component_flags = 0b00110000  // Resolution and component flags (see Flag table 3.3).
911    /// 56-59     payload.template.lat_lon.grid.last_point_lat = 20041667  // La2 - latitude of last grid point (see Note 1).
912    /// 60-63     payload.template.lat_lon.grid.last_point_lon = 149937500  // Lo2 - longitude of last grid point (see Note 1).
913    /// 64-67     payload.template.lat_lon.i_direction_inc = 125000  // Di - i direction increment (see Notes 1 and 5).
914    /// 68-71     payload.template.lat_lon.j_direction_inc = 83333  // Dj - j direction increment (see Notes 1 and 5).
915    /// 72        payload.template.lat_lon.scanning_mode = 0b00000000  // Scanning mode (flags - see Flag table 3.4).
916    /// ###  SECTION 4: PRODUCT DEFINITION SECTION (length = 34)
917    /// 1-4       header.len = 34  // Length of section in octets (nn).
918    /// 5         header.sect_num = 4  // Number of section.
919    /// 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).
920    /// 8-9       payload.template_num = 0  // Product definition template number (see Code table 4.0).
921    /// ###  SECTION 5: DATA REPRESENTATION SECTION (length = 23)
922    /// 1-4       header.len = 23  // Length of section in octets (nn).
923    /// 5         header.sect_num = 5  // Number of section.
924    /// 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.
925    /// 10-11     payload.template_num = 200  // Data representation template number (see Code table 5.0).
926    /// 12        payload.template.num_bits = 8  // Number of bits used for each packed value in the run length packing with level value.
927    /// 13-14     payload.template.max_val = 3  // MV - maximum value within the levels that are used in the packing.
928    /// 15-16     payload.template.max_level = 3  // MVL - maximum value of level (predefined).
929    /// 17        payload.template.dec = 0  // Decimal scale factor of representative value of each level.
930    /// 18-23     payload.template.level_vals = [1, 2, 3]  // List of MVL scaled representative values of each level from lv=1 to MVL.
931    /// ###  SECTION 6: BIT-MAP SECTION (length = 6)
932    /// 1-4       header.len = 6  // Length of section in octets (nn).
933    /// 5         header.sect_num = 6  // Number of section.
934    /// 6         payload.bitmap_indicator = 255  // Bit-map indicator (see Code table 6.0 and the Note).
935    /// ###  SECTION 7: DATA SECTION (length = 1391)
936    /// ###  SECTION 8: END SECTION (length = 4)
937    /// ";
938    ///     assert_eq!(String::from_utf8_lossy(buf.get_ref()), expected);
939    ///
940    ///     Ok(())
941    /// }
942    /// ```
943    pub fn dump<W: std::io::Write>(&self, writer: &mut W) -> Result<(), std::io::Error> {
944        let write_heading =
945            |writer: &mut W, sect: &SectionInfo, sect_name: &str| -> Result<(), std::io::Error> {
946                let SectionInfo { num, size, .. } = sect;
947                let sect_name = sect_name.to_ascii_uppercase();
948                writeln!(writer, "##  SECTION {num}: {sect_name} (length = {size})")
949            };
950
951        macro_rules! write_section {
952            ($sect:expr) => {{
953                let mut pos = 1;
954                match $sect {
955                    Ok(s) => s.dump(None, &mut pos, writer)?,
956                    Err(e) => writeln!(writer, "error: {}", e)?,
957                }
958            }};
959        }
960
961        let total_length = self.indicator().total_length;
962        writeln!(writer, "#  SUBMESSAGE (total_length = {total_length})")?;
963        write_heading(writer, self.0.body, "indicator section")?;
964        write_heading(writer, self.1.body, "identification section")?;
965        write_section!(self.section1());
966        if let Some(sect) = &self.2 {
967            write_heading(writer, sect.body, "local use section")?;
968        }
969        write_heading(writer, self.3.body, "grid definition section")?;
970        write_section!(self.section3());
971        write_heading(writer, self.4.body, "product definition section")?;
972        write_section!(self.section4());
973        write_heading(writer, self.5.body, "data representation section")?;
974        write_section!(self.section5());
975        write_heading(writer, self.6.body, "bit-map section")?;
976        write_section!(self.section6());
977        write_heading(writer, self.7.body, "data section")?;
978
979        // Since `self.8.body` might be dummy, we don't use that Section 8 data.
980        writeln!(writer, "##  SECTION 8: END SECTION (length = 4)")?;
981
982        Ok(())
983    }
984
985    /// Returns time-related raw information associated with the submessage.
986    ///
987    /// # Examples
988    ///
989    /// ```
990    /// use std::{
991    ///     fs::File,
992    ///     io::{BufReader, Read},
993    /// };
994    ///
995    /// use grib::{
996    ///     Code, ForecastTime, TemporalRawInfo,
997    ///     codetables::grib2::{Table1_2, Table4_4},
998    ///     def::grib2::RefTime,
999    /// };
1000    ///
1001    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
1002    ///     let f = File::open(
1003    ///         "testdata/Z__C_RJTD_20160822020000_NOWC_GPV_Ggis10km_Pphw10_FH0000-0100_grib2.bin",
1004    ///     )?;
1005    ///     let f = BufReader::new(f);
1006    ///     let grib2 = grib::from_reader(f)?;
1007    ///
1008    ///     let mut iter = grib2.iter();
1009    ///
1010    ///     {
1011    ///         let (_, message) = iter.next().ok_or_else(|| "first message is not found")?;
1012    ///         let actual = message.temporal_raw_info();
1013    ///         let expected = TemporalRawInfo {
1014    ///             ref_time_significance: Code::Name(Table1_2::Analysis),
1015    ///             ref_time_unchecked: RefTime::new(2016, 8, 22, 2, 0, 0),
1016    ///             forecast_time_diff: Some(ForecastTime {
1017    ///                 unit: Code::Name(Table4_4::Minute),
1018    ///                 value: 0,
1019    ///             }),
1020    ///         };
1021    ///         assert_eq!(actual, expected);
1022    ///     }
1023    ///
1024    ///     {
1025    ///         let (_, message) = iter.next().ok_or_else(|| "second message is not found")?;
1026    ///         let actual = message.temporal_raw_info();
1027    ///         let expected = TemporalRawInfo {
1028    ///             ref_time_significance: Code::Name(Table1_2::Analysis),
1029    ///             ref_time_unchecked: RefTime::new(2016, 8, 22, 2, 0, 0),
1030    ///             forecast_time_diff: Some(ForecastTime {
1031    ///                 unit: Code::Name(Table4_4::Minute),
1032    ///                 value: 10,
1033    ///             }),
1034    ///         };
1035    ///         assert_eq!(actual, expected);
1036    ///     }
1037    ///
1038    ///     Ok(())
1039    /// }
1040    /// ```
1041    pub fn temporal_raw_info(&self) -> TemporalRawInfo {
1042        let ref_time_significance = self.identification().ref_time_significance();
1043        let ref_time_unchecked = self.identification().ref_time_unchecked();
1044        let forecast_time = self.prod_def().forecast_time();
1045        TemporalRawInfo::new(ref_time_significance, ref_time_unchecked, forecast_time)
1046    }
1047
1048    #[cfg(feature = "time-calculation")]
1049    #[cfg_attr(docsrs, doc(cfg(feature = "time-calculation")))]
1050    /// Returns time-related calculated information associated with the
1051    /// submessage.
1052    ///
1053    /// # Examples
1054    ///
1055    /// ```
1056    /// use std::{
1057    ///     fs::File,
1058    ///     io::{BufReader, Read},
1059    /// };
1060    ///
1061    /// use chrono::{TimeZone, Utc};
1062    /// use grib::{
1063    ///     Code, ForecastTime, TemporalInfo,
1064    ///     codetables::grib2::{Table1_2, Table4_4},
1065    /// };
1066    ///
1067    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
1068    ///     let f = File::open(
1069    ///         "testdata/Z__C_RJTD_20160822020000_NOWC_GPV_Ggis10km_Pphw10_FH0000-0100_grib2.bin",
1070    ///     )?;
1071    ///     let f = BufReader::new(f);
1072    ///     let grib2 = grib::from_reader(f)?;
1073    ///
1074    ///     let mut iter = grib2.iter();
1075    ///
1076    ///     {
1077    ///         let (_, message) = iter.next().ok_or_else(|| "first message is not found")?;
1078    ///         let actual = message.temporal_info();
1079    ///         let expected = TemporalInfo {
1080    ///             ref_time: Some(Utc.with_ymd_and_hms(2016, 8, 22, 2, 0, 0).unwrap()),
1081    ///             forecast_time_target: Some(Utc.with_ymd_and_hms(2016, 8, 22, 2, 0, 0).unwrap()),
1082    ///         };
1083    ///         assert_eq!(actual, expected);
1084    ///     }
1085    ///
1086    ///     {
1087    ///         let (_, message) = iter.next().ok_or_else(|| "second message is not found")?;
1088    ///         let actual = message.temporal_info();
1089    ///         let expected = TemporalInfo {
1090    ///             ref_time: Some(Utc.with_ymd_and_hms(2016, 8, 22, 2, 0, 0).unwrap()),
1091    ///             forecast_time_target: Some(Utc.with_ymd_and_hms(2016, 8, 22, 2, 10, 0).unwrap()),
1092    ///         };
1093    ///         assert_eq!(actual, expected);
1094    ///     }
1095    ///
1096    ///     Ok(())
1097    /// }
1098    /// ```
1099    pub fn temporal_info(&self) -> TemporalInfo {
1100        let raw_info = self.temporal_raw_info();
1101        TemporalInfo::from(&raw_info)
1102    }
1103
1104    /// Returns the shape of the grid, i.e. a tuple of the number of grids in
1105    /// the i and j directions.
1106    ///
1107    /// # Examples
1108    ///
1109    /// ```
1110    /// use std::{
1111    ///     fs::File,
1112    ///     io::{BufReader, Read},
1113    /// };
1114    ///
1115    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
1116    ///     let mut buf = Vec::new();
1117    ///
1118    ///     let f = File::open("testdata/gdas.t12z.pgrb2.0p25.f000.0-10.xz")?;
1119    ///     let f = BufReader::new(f);
1120    ///     let mut f = xz2::bufread::XzDecoder::new(f);
1121    ///     f.read_to_end(&mut buf)?;
1122    ///
1123    ///     let f = std::io::Cursor::new(buf);
1124    ///     let grib2 = grib::from_reader(f)?;
1125    ///
1126    ///     let mut iter = grib2.iter();
1127    ///     let (_, message) = iter.next().ok_or_else(|| "first message is not found")?;
1128    ///
1129    ///     let shape = message.grid_shape()?;
1130    ///     assert_eq!(shape, (1440, 721));
1131    ///     Ok(())
1132    /// }
1133    /// ```
1134    pub fn grid_shape(&self) -> Result<(usize, usize), GribError> {
1135        let grid_def = self.grid_def();
1136        let shape = GridDefinitionTemplateValues::try_from(grid_def)?.grid_shape();
1137        Ok(shape)
1138    }
1139
1140    /// Computes and returns an iterator over `(i, j)` of grid points.
1141    ///
1142    /// The order of items is the same as the order of the grid point values,
1143    /// defined by the scanning mode
1144    /// ([`ScanningMode`](`crate::def::grib2::template::param_set::ScanningMode`))
1145    /// in the data.
1146    ///
1147    /// This iterator allows users to perform their own coordinate calculations
1148    /// for unsupported grid systems and map the results to grid point values.
1149    ///
1150    /// # Examples
1151    ///
1152    /// ```
1153    /// use std::{
1154    ///     fs::File,
1155    ///     io::{BufReader, Read},
1156    /// };
1157    ///
1158    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
1159    ///     let mut buf = Vec::new();
1160    ///
1161    ///     let f = File::open("testdata/gdas.t12z.pgrb2.0p25.f000.0-10.xz")?;
1162    ///     let f = BufReader::new(f);
1163    ///     let mut f = xz2::bufread::XzDecoder::new(f);
1164    ///     f.read_to_end(&mut buf)?;
1165    ///
1166    ///     let f = std::io::Cursor::new(buf);
1167    ///     let grib2 = grib::from_reader(f)?;
1168    ///
1169    ///     let mut iter = grib2.iter();
1170    ///     let (_, message) = iter.next().ok_or_else(|| "first message is not found")?;
1171    ///
1172    ///     let mut latlons = message.ij()?;
1173    ///     assert_eq!(latlons.next(), Some((0, 0)));
1174    ///     assert_eq!(latlons.next(), Some((1, 0)));
1175    ///     Ok(())
1176    /// }
1177    /// ```
1178    pub fn ij(&self) -> Result<GridPointIndexIterator, GribError> {
1179        let grid_def = self.grid_def();
1180        let num_defined = grid_def.num_points() as usize;
1181        let ij = GridDefinitionTemplateValues::try_from(grid_def)?.ij()?;
1182        let (num_decoded, _) = ij.size_hint();
1183        if num_defined == num_decoded {
1184            Ok(ij)
1185        } else {
1186            Err(GribError::InvalidValueError(format!(
1187                "number of grid points does not match: {num_defined} (defined) vs {num_decoded} (decoded)"
1188            )))
1189        }
1190    }
1191}
1192
1193impl<'s, R> LatLons for SubMessage<'s, R> {
1194    type Iter<'a>
1195        = GridPointLatLons
1196    where
1197        Self: 'a;
1198
1199    /// Computes and returns an iterator over latitudes and longitudes of grid
1200    /// points in degrees. [Read more](`crate::LatLons::latlons`)
1201    ///
1202    /// # Examples
1203    ///
1204    /// ```
1205    /// use std::{
1206    ///     fs::File,
1207    ///     io::{BufReader, Read},
1208    /// };
1209    ///
1210    /// use grib::LatLons;
1211    ///
1212    /// fn main() -> Result<(), Box<dyn std::error::Error>> {
1213    ///     let mut buf = Vec::new();
1214    ///
1215    ///     let f = File::open("testdata/gdas.t12z.pgrb2.0p25.f000.0-10.xz")?;
1216    ///     let f = BufReader::new(f);
1217    ///     let mut f = xz2::bufread::XzDecoder::new(f);
1218    ///     f.read_to_end(&mut buf)?;
1219    ///
1220    ///     let f = std::io::Cursor::new(buf);
1221    ///     let grib2 = grib::from_reader(f)?;
1222    ///
1223    ///     let mut iter = grib2.iter();
1224    ///     let (_, message) = iter.next().ok_or_else(|| "first message is not found")?;
1225    ///
1226    ///     let mut latlons = message.latlons()?;
1227    ///     assert_eq!(latlons.next(), Some((90.0, 0.0)));
1228    ///     assert_eq!(latlons.next(), Some((90.0, 0.25)));
1229    ///     Ok(())
1230    /// }
1231    /// ```
1232    fn latlons_unchecked<'a>(&'a self) -> Result<Self::Iter<'a>, GribError> {
1233        let grid_def = self.grid_def();
1234        let num_defined = grid_def.num_points() as usize;
1235        let latlons = GridDefinitionTemplateValues::try_from(grid_def)?.latlons_unchecked()?;
1236        let (num_decoded, _) = latlons.size_hint();
1237        if num_defined == num_decoded {
1238            Ok(latlons)
1239        } else {
1240            Err(GribError::InvalidValueError(format!(
1241                "number of grid points does not match: {num_defined} (defined) vs {num_decoded} (decoded)"
1242            )))
1243        }
1244    }
1245}
1246
1247pub struct SubMessageSection<'a> {
1248    pub index: usize,
1249    pub body: &'a SectionInfo,
1250}
1251
1252impl<'a> SubMessageSection<'a> {
1253    pub fn new(index: usize, body: &'a SectionInfo) -> Self {
1254        Self { index, body }
1255    }
1256
1257    pub fn template_code(&self) -> Option<TemplateInfo> {
1258        self.body.get_tmpl_code()
1259    }
1260
1261    pub fn describe(&self) -> Option<String> {
1262        self.template_code().and_then(|code| code.describe())
1263    }
1264}
1265
1266#[cfg(test)]
1267mod tests {
1268    use std::{fs::File, io::BufReader};
1269
1270    use super::*;
1271    use crate::test_utils::data::grib2::{DWD_ICON, JMA_MSMGUID, NOAA_GDAS_0_10};
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(DWD_ICON).unwrap();
1287        let f = BufReader::new(f);
1288        let result = from_reader(f);
1289        assert!(result.is_ok())
1290    }
1291
1292    #[test]
1293    fn context_from_bytes() {
1294        let buf = crate::test_utils::decompress_to_vec(DWD_ICON).unwrap();
1295        let result = from_bytes(&buf);
1296        assert!(result.is_ok())
1297    }
1298
1299    #[test]
1300    fn get_tmpl_code_normal() {
1301        let sect = SectionInfo {
1302            num: 5,
1303            offset: 8902,
1304            size: 23,
1305            body: Some(SectionBody::Section5(
1306                ReprDefinition::from_payload(
1307                    vec![0x00, 0x01, 0x50, 0x00, 0x00, 0xc8].into_boxed_slice(),
1308                )
1309                .unwrap(),
1310            )),
1311        };
1312
1313        assert_eq!(sect.get_tmpl_code(), Some(TemplateInfo(5, 200)));
1314    }
1315
1316    #[test]
1317    fn get_templates_normal() {
1318        let sects = vec![
1319            sect_placeholder!(0),
1320            sect_placeholder!(1),
1321            SectionInfo {
1322                num: 3,
1323                offset: 0,
1324                size: 0,
1325                body: Some(SectionBody::Section3(
1326                    GridDefinition::from_payload(vec![0; 9].into_boxed_slice()).unwrap(),
1327                )),
1328            },
1329            SectionInfo {
1330                num: 4,
1331                offset: 0,
1332                size: 0,
1333                body: Some(SectionBody::Section4(
1334                    ProdDefinition::from_payload(vec![0; 4].into_boxed_slice()).unwrap(),
1335                )),
1336            },
1337            SectionInfo {
1338                num: 5,
1339                offset: 0,
1340                size: 0,
1341                body: Some(SectionBody::Section5(
1342                    ReprDefinition::from_payload(vec![0; 6].into_boxed_slice()).unwrap(),
1343                )),
1344            },
1345            sect_placeholder!(6),
1346            sect_placeholder!(7),
1347            SectionInfo {
1348                num: 3,
1349                offset: 0,
1350                size: 0,
1351                body: Some(SectionBody::Section3(
1352                    GridDefinition::from_payload(
1353                        vec![0, 0, 0, 0, 0, 0, 0, 0, 1].into_boxed_slice(),
1354                    )
1355                    .unwrap(),
1356                )),
1357            },
1358            SectionInfo {
1359                num: 4,
1360                offset: 0,
1361                size: 0,
1362                body: Some(SectionBody::Section4(
1363                    ProdDefinition::from_payload(vec![0; 4].into_boxed_slice()).unwrap(),
1364                )),
1365            },
1366            SectionInfo {
1367                num: 5,
1368                offset: 0,
1369                size: 0,
1370                body: Some(SectionBody::Section5(
1371                    ReprDefinition::from_payload(vec![0; 6].into_boxed_slice()).unwrap(),
1372                )),
1373            },
1374            sect_placeholder!(6),
1375            sect_placeholder!(7),
1376            sect_placeholder!(8),
1377        ]
1378        .into_boxed_slice();
1379
1380        assert_eq!(
1381            get_templates(&sects),
1382            vec![
1383                TemplateInfo(3, 0),
1384                TemplateInfo(3, 1),
1385                TemplateInfo(4, 0),
1386                TemplateInfo(5, 0),
1387            ]
1388        );
1389    }
1390
1391    macro_rules! test_submessage_iterator {
1392        ($((
1393            $name:ident,
1394            $xz_compressed_input:expr,
1395            $nth:expr,
1396            $expected_index:expr,
1397            $expected_section_indices:expr,
1398        ),)*) => ($(
1399            #[test]
1400            fn $name() -> Result<(), Box<dyn std::error::Error>> {
1401                let buf = crate::test_utils::decompress_to_vec($xz_compressed_input)?;
1402
1403                let f = Cursor::new(buf);
1404                let grib2 = crate::from_reader(f)?;
1405                let mut iter = grib2.iter();
1406
1407                let (actual_index, message) = iter.nth($nth).ok_or_else(|| "item not available")?;
1408                assert_eq!(actual_index, $expected_index);
1409                let actual_section_indices = get_section_indices(message);
1410                assert_eq!(actual_section_indices, $expected_section_indices);
1411
1412                Ok(())
1413            }
1414        )*);
1415    }
1416
1417    test_submessage_iterator! {
1418        (
1419            item_0_from_submessage_iterator_for_single_message_data_with_multiple_submessages,
1420            JMA_MSMGUID,
1421            0,
1422            (0, 0),
1423            (0, 1, None, 2, 3, 4, 5, 6, 0),
1424        ),
1425        (
1426            item_1_from_submessage_iterator_for_single_message_data_with_multiple_submessages,
1427            JMA_MSMGUID,
1428            1,
1429            (0, 1),
1430            (0, 1, None, 2, 7, 8, 9, 10, 0),
1431        ),
1432        (
1433            item_0_from_submessage_iterator_for_multi_message_data,
1434            NOAA_GDAS_0_10,
1435            0,
1436            (0, 0),
1437            (0, 1, None, 2, 3, 4, 5, 6, 7),
1438        ),
1439        (
1440            item_1_from_submessage_iterator_for_multi_message_data,
1441            NOAA_GDAS_0_10,
1442            1,
1443            (1, 0),
1444            (8, 9, None, 10, 11, 12, 13, 14, 15),
1445        ),
1446    }
1447
1448    fn get_section_indices<R>(
1449        submessage: SubMessage<'_, R>,
1450    ) -> (
1451        usize,
1452        usize,
1453        Option<usize>,
1454        usize,
1455        usize,
1456        usize,
1457        usize,
1458        usize,
1459        usize,
1460    ) {
1461        (
1462            submessage.0.index,
1463            submessage.1.index,
1464            submessage.2.map(|s| s.index),
1465            submessage.3.index,
1466            submessage.4.index,
1467            submessage.5.index,
1468            submessage.6.index,
1469            submessage.7.index,
1470            submessage.8.index,
1471        )
1472    }
1473}