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