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