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<(), std::io::Error> {
899 let write_heading =
900 |writer: &mut W, sect: &SectionInfo, sect_name: &str| -> Result<(), std::io::Error> {
901 let SectionInfo { num, size, .. } = sect;
902 let sect_name = sect_name.to_ascii_uppercase();
903 writeln!(writer, "## SECTION {num}: {sect_name} (length = {size})")
904 };
905
906 macro_rules! write_section {
907 ($sect:expr) => {{
908 let mut pos = 1;
909 match $sect {
910 Ok(s) => s.dump(None, &mut pos, writer)?,
911 Err(e) => writeln!(writer, "error: {}", e)?,
912 }
913 }};
914 }
915
916 let total_length = self.indicator().total_length;
917 writeln!(writer, "# SUBMESSAGE (total_length = {total_length})")?;
918 write_heading(writer, self.0.body, "indicator section")?;
919 write_heading(writer, self.1.body, "identification section")?;
920 write_section!(self.section1());
921 if let Some(sect) = &self.2 {
922 write_heading(writer, sect.body, "local use section")?;
923 }
924 write_heading(writer, self.3.body, "grid definition section")?;
925 write_section!(self.section3());
926 write_heading(writer, self.4.body, "product definition section")?;
927 write_section!(self.section4());
928 write_heading(writer, self.5.body, "data representation section")?;
929 write_section!(self.section5());
930 write_heading(writer, self.6.body, "bit-map section")?;
931 write_section!(self.section6());
932 write_heading(writer, self.7.body, "data section")?;
933
934 // Since `self.8.body` might be dummy, we don't use that Section 8 data.
935 writeln!(writer, "## SECTION 8: END SECTION (length = 4)")?;
936
937 Ok(())
938 }
939
940 /// Returns time-related raw information associated with the submessage.
941 ///
942 /// # Examples
943 ///
944 /// ```
945 /// use std::{
946 /// fs::File,
947 /// io::{BufReader, Read},
948 /// };
949 ///
950 /// use grib::{
951 /// Code, ForecastTime, TemporalRawInfo,
952 /// codetables::grib2::{Table1_2, Table4_4},
953 /// def::grib2::RefTime,
954 /// };
955 ///
956 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
957 /// let f = File::open(
958 /// "testdata/Z__C_RJTD_20160822020000_NOWC_GPV_Ggis10km_Pphw10_FH0000-0100_grib2.bin",
959 /// )?;
960 /// let f = BufReader::new(f);
961 /// let grib2 = grib::from_reader(f)?;
962 ///
963 /// let mut iter = grib2.iter();
964 ///
965 /// {
966 /// let (_, message) = iter.next().ok_or_else(|| "first message is not found")?;
967 /// let actual = message.temporal_raw_info();
968 /// let expected = TemporalRawInfo {
969 /// ref_time_significance: Code::Name(Table1_2::Analysis),
970 /// ref_time_unchecked: RefTime::new(2016, 8, 22, 2, 0, 0),
971 /// forecast_time_diff: Some(ForecastTime {
972 /// unit: Code::Name(Table4_4::Minute),
973 /// value: 0,
974 /// }),
975 /// };
976 /// assert_eq!(actual, expected);
977 /// }
978 ///
979 /// {
980 /// let (_, message) = iter.next().ok_or_else(|| "second message is not found")?;
981 /// let actual = message.temporal_raw_info();
982 /// let expected = TemporalRawInfo {
983 /// ref_time_significance: Code::Name(Table1_2::Analysis),
984 /// ref_time_unchecked: RefTime::new(2016, 8, 22, 2, 0, 0),
985 /// forecast_time_diff: Some(ForecastTime {
986 /// unit: Code::Name(Table4_4::Minute),
987 /// value: 10,
988 /// }),
989 /// };
990 /// assert_eq!(actual, expected);
991 /// }
992 ///
993 /// Ok(())
994 /// }
995 /// ```
996 pub fn temporal_raw_info(&self) -> TemporalRawInfo {
997 let ref_time_significance = self.identification().ref_time_significance();
998 let ref_time_unchecked = self.identification().ref_time_unchecked();
999 let forecast_time = self.prod_def().forecast_time();
1000 TemporalRawInfo::new(ref_time_significance, ref_time_unchecked, forecast_time)
1001 }
1002
1003 #[cfg(feature = "time-calculation")]
1004 #[cfg_attr(docsrs, doc(cfg(feature = "time-calculation")))]
1005 /// Returns time-related calculated information associated with the
1006 /// submessage.
1007 ///
1008 /// # Examples
1009 ///
1010 /// ```
1011 /// use std::{
1012 /// fs::File,
1013 /// io::{BufReader, Read},
1014 /// };
1015 ///
1016 /// use chrono::{TimeZone, Utc};
1017 /// use grib::{
1018 /// Code, ForecastTime, TemporalInfo,
1019 /// codetables::grib2::{Table1_2, Table4_4},
1020 /// };
1021 ///
1022 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
1023 /// let f = File::open(
1024 /// "testdata/Z__C_RJTD_20160822020000_NOWC_GPV_Ggis10km_Pphw10_FH0000-0100_grib2.bin",
1025 /// )?;
1026 /// let f = BufReader::new(f);
1027 /// let grib2 = grib::from_reader(f)?;
1028 ///
1029 /// let mut iter = grib2.iter();
1030 ///
1031 /// {
1032 /// let (_, message) = iter.next().ok_or_else(|| "first message is not found")?;
1033 /// let actual = message.temporal_info();
1034 /// let expected = TemporalInfo {
1035 /// ref_time: Some(Utc.with_ymd_and_hms(2016, 8, 22, 2, 0, 0).unwrap()),
1036 /// forecast_time_target: Some(Utc.with_ymd_and_hms(2016, 8, 22, 2, 0, 0).unwrap()),
1037 /// };
1038 /// assert_eq!(actual, expected);
1039 /// }
1040 ///
1041 /// {
1042 /// let (_, message) = iter.next().ok_or_else(|| "second message is not found")?;
1043 /// let actual = message.temporal_info();
1044 /// let expected = TemporalInfo {
1045 /// ref_time: Some(Utc.with_ymd_and_hms(2016, 8, 22, 2, 0, 0).unwrap()),
1046 /// forecast_time_target: Some(Utc.with_ymd_and_hms(2016, 8, 22, 2, 10, 0).unwrap()),
1047 /// };
1048 /// assert_eq!(actual, expected);
1049 /// }
1050 ///
1051 /// Ok(())
1052 /// }
1053 /// ```
1054 pub fn temporal_info(&self) -> TemporalInfo {
1055 let raw_info = self.temporal_raw_info();
1056 TemporalInfo::from(&raw_info)
1057 }
1058
1059 /// Returns the shape of the grid, i.e. a tuple of the number of grids in
1060 /// the i and j directions.
1061 ///
1062 /// # Examples
1063 ///
1064 /// ```
1065 /// use std::{
1066 /// fs::File,
1067 /// io::{BufReader, Read},
1068 /// };
1069 ///
1070 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
1071 /// let mut buf = Vec::new();
1072 ///
1073 /// let f = File::open("testdata/gdas.t12z.pgrb2.0p25.f000.0-10.xz")?;
1074 /// let f = BufReader::new(f);
1075 /// let mut f = xz2::bufread::XzDecoder::new(f);
1076 /// f.read_to_end(&mut buf)?;
1077 ///
1078 /// let f = std::io::Cursor::new(buf);
1079 /// let grib2 = grib::from_reader(f)?;
1080 ///
1081 /// let mut iter = grib2.iter();
1082 /// let (_, message) = iter.next().ok_or_else(|| "first message is not found")?;
1083 ///
1084 /// let shape = message.grid_shape()?;
1085 /// assert_eq!(shape, (1440, 721));
1086 /// Ok(())
1087 /// }
1088 /// ```
1089 pub fn grid_shape(&self) -> Result<(usize, usize), GribError> {
1090 let grid_def = self.grid_def();
1091 let shape = GridDefinitionTemplateValues::try_from(grid_def)?.grid_shape();
1092 Ok(shape)
1093 }
1094
1095 /// Computes and returns an iterator over `(i, j)` of grid points.
1096 ///
1097 /// The order of items is the same as the order of the grid point values,
1098 /// defined by the scanning mode ([`ScanningMode`](`crate::ScanningMode`))
1099 /// in the data.
1100 ///
1101 /// This iterator allows users to perform their own coordinate calculations
1102 /// for unsupported grid systems and map the results to grid point values.
1103 ///
1104 /// # Examples
1105 ///
1106 /// ```
1107 /// use std::{
1108 /// fs::File,
1109 /// io::{BufReader, Read},
1110 /// };
1111 ///
1112 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
1113 /// let mut buf = Vec::new();
1114 ///
1115 /// let f = File::open("testdata/gdas.t12z.pgrb2.0p25.f000.0-10.xz")?;
1116 /// let f = BufReader::new(f);
1117 /// let mut f = xz2::bufread::XzDecoder::new(f);
1118 /// f.read_to_end(&mut buf)?;
1119 ///
1120 /// let f = std::io::Cursor::new(buf);
1121 /// let grib2 = grib::from_reader(f)?;
1122 ///
1123 /// let mut iter = grib2.iter();
1124 /// let (_, message) = iter.next().ok_or_else(|| "first message is not found")?;
1125 ///
1126 /// let mut latlons = message.ij()?;
1127 /// assert_eq!(latlons.next(), Some((0, 0)));
1128 /// assert_eq!(latlons.next(), Some((1, 0)));
1129 /// Ok(())
1130 /// }
1131 /// ```
1132 pub fn ij(&self) -> Result<GridPointIndexIterator, GribError> {
1133 let grid_def = self.grid_def();
1134 let num_defined = grid_def.num_points() as usize;
1135 let ij = GridDefinitionTemplateValues::try_from(grid_def)?.ij()?;
1136 let (num_decoded, _) = ij.size_hint();
1137 if num_defined == num_decoded {
1138 Ok(ij)
1139 } else {
1140 Err(GribError::InvalidValueError(format!(
1141 "number of grid points does not match: {num_defined} (defined) vs {num_decoded} (decoded)"
1142 )))
1143 }
1144 }
1145
1146 /// Computes and returns an iterator over latitudes and longitudes of grid
1147 /// points.
1148 ///
1149 /// The order of lat/lon data of grid points is the same as the order of the
1150 /// grid point values, defined by the scanning mode
1151 /// ([`ScanningMode`](`crate::ScanningMode`)) in the data.
1152 ///
1153 /// # Examples
1154 ///
1155 /// ```
1156 /// use std::{
1157 /// fs::File,
1158 /// io::{BufReader, Read},
1159 /// };
1160 ///
1161 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
1162 /// let mut buf = Vec::new();
1163 ///
1164 /// let f = File::open("testdata/gdas.t12z.pgrb2.0p25.f000.0-10.xz")?;
1165 /// let f = BufReader::new(f);
1166 /// let mut f = xz2::bufread::XzDecoder::new(f);
1167 /// f.read_to_end(&mut buf)?;
1168 ///
1169 /// let f = std::io::Cursor::new(buf);
1170 /// let grib2 = grib::from_reader(f)?;
1171 ///
1172 /// let mut iter = grib2.iter();
1173 /// let (_, message) = iter.next().ok_or_else(|| "first message is not found")?;
1174 ///
1175 /// let mut latlons = message.latlons()?;
1176 /// assert_eq!(latlons.next(), Some((90.0, 0.0)));
1177 /// assert_eq!(latlons.next(), Some((90.0, 0.25000003)));
1178 /// Ok(())
1179 /// }
1180 /// ```
1181 pub fn latlons(&self) -> Result<GridPointIterator, GribError> {
1182 let grid_def = self.grid_def();
1183 let num_defined = grid_def.num_points() as usize;
1184 let latlons = GridDefinitionTemplateValues::try_from(grid_def)?.latlons()?;
1185 let (num_decoded, _) = latlons.size_hint();
1186 if num_defined == num_decoded {
1187 Ok(latlons)
1188 } else {
1189 Err(GribError::InvalidValueError(format!(
1190 "number of grid points does not match: {num_defined} (defined) vs {num_decoded} (decoded)"
1191 )))
1192 }
1193 }
1194}
1195
1196pub struct SubMessageSection<'a> {
1197 pub index: usize,
1198 pub body: &'a SectionInfo,
1199}
1200
1201impl<'a> SubMessageSection<'a> {
1202 pub fn new(index: usize, body: &'a SectionInfo) -> Self {
1203 Self { index, body }
1204 }
1205
1206 pub fn template_code(&self) -> Option<TemplateInfo> {
1207 self.body.get_tmpl_code()
1208 }
1209
1210 pub fn describe(&self) -> Option<String> {
1211 self.template_code().and_then(|code| code.describe())
1212 }
1213}
1214
1215#[cfg(test)]
1216mod tests {
1217 use std::{fs::File, io::BufReader};
1218
1219 use super::*;
1220
1221 macro_rules! sect_placeholder {
1222 ($num:expr) => {{
1223 SectionInfo {
1224 num: $num,
1225 offset: 0,
1226 size: 0,
1227 body: None,
1228 }
1229 }};
1230 }
1231
1232 #[test]
1233 fn context_from_buf_reader() {
1234 let f = File::open(
1235 "testdata/icon_global_icosahedral_single-level_2021112018_000_TOT_PREC.grib2",
1236 )
1237 .unwrap();
1238 let f = BufReader::new(f);
1239 let result = from_reader(f);
1240 assert!(result.is_ok())
1241 }
1242
1243 #[test]
1244 fn context_from_bytes() {
1245 let f = File::open(
1246 "testdata/icon_global_icosahedral_single-level_2021112018_000_TOT_PREC.grib2",
1247 )
1248 .unwrap();
1249 let mut f = BufReader::new(f);
1250 let mut buf = Vec::new();
1251 f.read_to_end(&mut buf).unwrap();
1252 let result = from_bytes(&buf);
1253 assert!(result.is_ok())
1254 }
1255
1256 #[test]
1257 fn get_tmpl_code_normal() {
1258 let sect = SectionInfo {
1259 num: 5,
1260 offset: 8902,
1261 size: 23,
1262 body: Some(SectionBody::Section5(
1263 ReprDefinition::from_payload(
1264 vec![0x00, 0x01, 0x50, 0x00, 0x00, 0xc8].into_boxed_slice(),
1265 )
1266 .unwrap(),
1267 )),
1268 };
1269
1270 assert_eq!(sect.get_tmpl_code(), Some(TemplateInfo(5, 200)));
1271 }
1272
1273 #[test]
1274 fn get_templates_normal() {
1275 let sects = vec![
1276 sect_placeholder!(0),
1277 sect_placeholder!(1),
1278 SectionInfo {
1279 num: 3,
1280 offset: 0,
1281 size: 0,
1282 body: Some(SectionBody::Section3(
1283 GridDefinition::from_payload(vec![0; 9].into_boxed_slice()).unwrap(),
1284 )),
1285 },
1286 SectionInfo {
1287 num: 4,
1288 offset: 0,
1289 size: 0,
1290 body: Some(SectionBody::Section4(
1291 ProdDefinition::from_payload(vec![0; 4].into_boxed_slice()).unwrap(),
1292 )),
1293 },
1294 SectionInfo {
1295 num: 5,
1296 offset: 0,
1297 size: 0,
1298 body: Some(SectionBody::Section5(
1299 ReprDefinition::from_payload(vec![0; 6].into_boxed_slice()).unwrap(),
1300 )),
1301 },
1302 sect_placeholder!(6),
1303 sect_placeholder!(7),
1304 SectionInfo {
1305 num: 3,
1306 offset: 0,
1307 size: 0,
1308 body: Some(SectionBody::Section3(
1309 GridDefinition::from_payload(
1310 vec![0, 0, 0, 0, 0, 0, 0, 0, 1].into_boxed_slice(),
1311 )
1312 .unwrap(),
1313 )),
1314 },
1315 SectionInfo {
1316 num: 4,
1317 offset: 0,
1318 size: 0,
1319 body: Some(SectionBody::Section4(
1320 ProdDefinition::from_payload(vec![0; 4].into_boxed_slice()).unwrap(),
1321 )),
1322 },
1323 SectionInfo {
1324 num: 5,
1325 offset: 0,
1326 size: 0,
1327 body: Some(SectionBody::Section5(
1328 ReprDefinition::from_payload(vec![0; 6].into_boxed_slice()).unwrap(),
1329 )),
1330 },
1331 sect_placeholder!(6),
1332 sect_placeholder!(7),
1333 sect_placeholder!(8),
1334 ]
1335 .into_boxed_slice();
1336
1337 assert_eq!(
1338 get_templates(§s),
1339 vec![
1340 TemplateInfo(3, 0),
1341 TemplateInfo(3, 1),
1342 TemplateInfo(4, 0),
1343 TemplateInfo(5, 0),
1344 ]
1345 );
1346 }
1347
1348 macro_rules! test_submessage_iterator {
1349 ($((
1350 $name:ident,
1351 $xz_compressed_input:expr,
1352 $nth:expr,
1353 $expected_index:expr,
1354 $expected_section_indices:expr,
1355 ),)*) => ($(
1356 #[test]
1357 fn $name() -> Result<(), Box<dyn std::error::Error>> {
1358 let mut buf = Vec::new();
1359
1360 let f = File::open($xz_compressed_input)?;
1361 let f = BufReader::new(f);
1362 let mut f = xz2::bufread::XzDecoder::new(f);
1363 f.read_to_end(&mut buf)?;
1364
1365 let f = Cursor::new(buf);
1366 let grib2 = crate::from_reader(f)?;
1367 let mut iter = grib2.iter();
1368
1369 let (actual_index, message) = iter.nth($nth).ok_or_else(|| "item not available")?;
1370 assert_eq!(actual_index, $expected_index);
1371 let actual_section_indices = get_section_indices(message);
1372 assert_eq!(actual_section_indices, $expected_section_indices);
1373
1374 Ok(())
1375 }
1376 )*);
1377 }
1378
1379 test_submessage_iterator! {
1380 (
1381 item_0_from_submessage_iterator_for_single_message_data_with_multiple_submessages,
1382 "testdata/Z__C_RJTD_20190304000000_MSM_GUID_Rjp_P-all_FH03-39_Toorg_grib2.bin.xz",
1383 0,
1384 (0, 0),
1385 (0, 1, None, 2, 3, 4, 5, 6, 0),
1386 ),
1387 (
1388 item_1_from_submessage_iterator_for_single_message_data_with_multiple_submessages,
1389 "testdata/Z__C_RJTD_20190304000000_MSM_GUID_Rjp_P-all_FH03-39_Toorg_grib2.bin.xz",
1390 1,
1391 (0, 1),
1392 (0, 1, None, 2, 7, 8, 9, 10, 0),
1393 ),
1394 (
1395 item_0_from_submessage_iterator_for_multi_message_data,
1396 "testdata/gdas.t12z.pgrb2.0p25.f000.0-10.xz",
1397 0,
1398 (0, 0),
1399 (0, 1, None, 2, 3, 4, 5, 6, 7),
1400 ),
1401 (
1402 item_1_from_submessage_iterator_for_multi_message_data,
1403 "testdata/gdas.t12z.pgrb2.0p25.f000.0-10.xz",
1404 1,
1405 (1, 0),
1406 (8, 9, None, 10, 11, 12, 13, 14, 15),
1407 ),
1408 }
1409
1410 fn get_section_indices<R>(
1411 submessage: SubMessage<'_, R>,
1412 ) -> (
1413 usize,
1414 usize,
1415 Option<usize>,
1416 usize,
1417 usize,
1418 usize,
1419 usize,
1420 usize,
1421 usize,
1422 ) {
1423 (
1424 submessage.0.index,
1425 submessage.1.index,
1426 submessage.2.map(|s| s.index),
1427 submessage.3.index,
1428 submessage.4.index,
1429 submessage.5.index,
1430 submessage.6.index,
1431 submessage.7.index,
1432 submessage.8.index,
1433 )
1434 }
1435}