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 codetables::{
12 CodeTable3_1, CodeTable4_0, CodeTable4_1, CodeTable4_2, CodeTable4_3, CodeTable5_0, Lookup,
13 },
14 datatypes::*,
15 error::*,
16 grid::GridPointIterator,
17 parser::Grib2SubmessageIndexStream,
18 reader::{Grib2Read, Grib2SectionStream, SeekableGrib2Reader, SECT8_ES_SIZE},
19 GridPointIndexIterator, TemporalRawInfo,
20};
21
22#[derive(Default, Debug, Clone, PartialEq, Eq)]
23pub struct SectionInfo {
24 pub num: u8,
25 pub offset: usize,
26 pub size: usize,
27 pub body: Option<SectionBody>,
28}
29
30impl SectionInfo {
31 pub fn get_tmpl_code(&self) -> Option<TemplateInfo> {
32 let tmpl_num = self.body.as_ref()?.get_tmpl_num()?;
33 Some(TemplateInfo(self.num, tmpl_num))
34 }
35
36 pub(crate) fn new_8(offset: usize) -> Self {
37 Self {
38 num: 8,
39 offset,
40 size: SECT8_ES_SIZE,
41 body: None,
42 }
43 }
44}
45
46#[derive(Debug, Clone, PartialEq, Eq)]
47pub enum SectionBody {
48 Section0(Indicator),
49 Section1(Identification),
50 Section2(LocalUse),
51 Section3(GridDefinition),
52 Section4(ProdDefinition),
53 Section5(ReprDefinition),
54 Section6(BitMap),
55 Section7,
56}
57
58impl SectionBody {
59 fn get_tmpl_num(&self) -> Option<u16> {
60 match self {
61 Self::Section3(s) => Some(s.grid_tmpl_num()),
62 Self::Section4(s) => Some(s.prod_tmpl_num()),
63 Self::Section5(s) => Some(s.repr_tmpl_num()),
64 _ => None,
65 }
66 }
67}
68
69#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
70pub struct TemplateInfo(pub u8, pub u16);
71
72impl TemplateInfo {
73 pub fn describe(&self) -> Option<String> {
74 match self.0 {
75 3 => Some(CodeTable3_1.lookup(usize::from(self.1)).to_string()),
76 4 => Some(CodeTable4_0.lookup(usize::from(self.1)).to_string()),
77 5 => Some(CodeTable5_0.lookup(usize::from(self.1)).to_string()),
78 _ => None,
79 }
80 }
81}
82
83impl Display for TemplateInfo {
84 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
85 write!(f, "{}.{}", self.0, self.1)
86 }
87}
88
89/// Reads a [`Grib2`] instance from an I/O stream of GRIB2.
90///
91/// # Examples
92///
93/// ```
94/// fn main() -> Result<(), Box<dyn std::error::Error>> {
95/// let f = std::fs::File::open(
96/// "testdata/icon_global_icosahedral_single-level_2021112018_000_TOT_PREC.grib2",
97/// )?;
98/// let f = std::io::BufReader::new(f);
99/// let result = grib::from_reader(f);
100///
101/// assert!(result.is_ok());
102/// let grib2 = result?;
103/// assert_eq!(grib2.len(), 1);
104/// Ok(())
105/// }
106/// ```
107pub fn from_reader<SR: Read + Seek>(
108 reader: SR,
109) -> Result<Grib2<SeekableGrib2Reader<SR>>, GribError> {
110 Grib2::<SeekableGrib2Reader<SR>>::read_with_seekable(reader)
111}
112
113/// Reads a [`Grib2`] instance from bytes of GRIB2.
114///
115/// # Examples
116///
117/// You can use this method to create a reader from a slice, i.e., a borrowed
118/// sequence of bytes:
119///
120/// ```
121/// use std::io::Read;
122///
123/// fn main() -> Result<(), Box<dyn std::error::Error>> {
124/// let f = std::fs::File::open(
125/// "testdata/icon_global_icosahedral_single-level_2021112018_000_TOT_PREC.grib2",
126/// )?;
127/// let mut f = std::io::BufReader::new(f);
128/// let mut buf = Vec::new();
129/// f.read_to_end(&mut buf).unwrap();
130/// let result = grib::from_bytes(&buf);
131///
132/// assert!(result.is_ok());
133/// let grib2 = result?;
134/// assert_eq!(grib2.len(), 1);
135/// Ok(())
136/// }
137/// ```
138///
139/// Also, you can use this method to create a reader from an owned sequence of
140/// bytes:
141///
142/// ```
143/// use std::io::Read;
144///
145/// fn main() -> Result<(), Box<dyn std::error::Error>> {
146/// let f = std::fs::File::open(
147/// "testdata/icon_global_icosahedral_single-level_2021112018_000_TOT_PREC.grib2",
148/// )?;
149/// let mut f = std::io::BufReader::new(f);
150/// let mut buf = Vec::new();
151/// f.read_to_end(&mut buf).unwrap();
152/// let result = grib::from_bytes(buf);
153///
154/// assert!(result.is_ok());
155/// let grib2 = result?;
156/// assert_eq!(grib2.len(), 1);
157/// Ok(())
158/// }
159/// ```
160pub fn from_bytes<T>(bytes: T) -> Result<Grib2<SeekableGrib2Reader<Cursor<T>>>, GribError>
161where
162 T: AsRef<[u8]>,
163{
164 let reader = Cursor::new(bytes);
165 Grib2::<SeekableGrib2Reader<Cursor<T>>>::read_with_seekable(reader)
166}
167
168pub struct Grib2<R> {
169 reader: RefCell<R>,
170 sections: Box<[SectionInfo]>,
171 submessages: Vec<Grib2SubmessageIndex>,
172}
173
174impl<R> Grib2<R> {
175 /// Returns the length of submessages in the data.
176 ///
177 /// # Examples
178 ///
179 /// ```
180 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
181 /// let f = std::fs::File::open(
182 /// "testdata/icon_global_icosahedral_single-level_2021112018_000_TOT_PREC.grib2",
183 /// )?;
184 /// let f = std::io::BufReader::new(f);
185 /// let grib2 = grib::from_reader(f)?;
186 ///
187 /// assert_eq!(grib2.len(), 1);
188 /// Ok(())
189 /// }
190 /// ```
191 pub fn len(&self) -> usize {
192 self.submessages.len()
193 }
194
195 /// Returns `true` if `self` has zero submessages.
196 #[inline]
197 pub fn is_empty(&self) -> bool {
198 self.len() == 0
199 }
200
201 /// Returns an iterator over submessages in the data.
202 ///
203 /// # Examples
204 ///
205 /// ```
206 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
207 /// let f = std::fs::File::open(
208 /// "testdata/icon_global_icosahedral_single-level_2021112018_000_TOT_PREC.grib2",
209 /// )?;
210 /// let f = std::io::BufReader::new(f);
211 /// let grib2 = grib::from_reader(f)?;
212 ///
213 /// let mut iter = grib2.iter();
214 /// let first = iter.next();
215 /// assert!(first.is_some());
216 ///
217 /// let first = first.unwrap();
218 /// let (message_index, _) = first;
219 /// assert_eq!(message_index, (0, 0));
220 ///
221 /// let second = iter.next();
222 /// assert!(second.is_none());
223 /// Ok(())
224 /// }
225 /// ```
226 #[inline]
227 pub fn iter(&self) -> SubmessageIterator<'_, R> {
228 self.into_iter()
229 }
230
231 /// Returns an iterator over submessages in the data.
232 ///
233 /// This is an alias to [`Grib2::iter()`].
234 pub fn submessages(&self) -> SubmessageIterator<'_, R> {
235 self.into_iter()
236 }
237
238 /// Returns an iterator over sections in the data.
239 ///
240 /// # Examples
241 ///
242 /// ```
243 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
244 /// let f = std::fs::File::open(
245 /// "testdata/icon_global_icosahedral_single-level_2021112018_000_TOT_PREC.grib2",
246 /// )?;
247 /// let f = std::io::BufReader::new(f);
248 /// let grib2 = grib::from_reader(f)?;
249 ///
250 /// let mut iter = grib2.sections();
251 /// let first = iter.next();
252 /// assert!(first.is_some());
253 ///
254 /// let first = first.unwrap();
255 /// assert_eq!(first.num, 0);
256 ///
257 /// let tenth = iter.nth(9);
258 /// assert!(tenth.is_none());
259 /// Ok(())
260 /// }
261 /// ```
262 pub fn sections(&self) -> std::slice::Iter<'_, SectionInfo> {
263 self.sections.iter()
264 }
265}
266
267impl<R: Grib2Read> Grib2<R> {
268 pub fn read(r: R) -> Result<Self, GribError> {
269 let mut sect_stream = Grib2SectionStream::new(r);
270 let mut cacher = Vec::new();
271 let parser = Grib2SubmessageIndexStream::new(sect_stream.by_ref()).with_cacher(&mut cacher);
272 let submessages = parser.collect::<Result<Vec<_>, _>>()?;
273 Ok(Self {
274 reader: RefCell::new(sect_stream.into_reader()),
275 sections: cacher.into_boxed_slice(),
276 submessages,
277 })
278 }
279
280 pub fn read_with_seekable<SR: Read + Seek>(
281 r: SR,
282 ) -> Result<Grib2<SeekableGrib2Reader<SR>>, GribError> {
283 let r = SeekableGrib2Reader::new(r);
284 Grib2::<SeekableGrib2Reader<SR>>::read(r)
285 }
286
287 pub fn list_templates(&self) -> Vec<TemplateInfo> {
288 get_templates(&self.sections)
289 }
290}
291
292impl<'a, R: 'a> IntoIterator for &'a Grib2<R> {
293 type Item = (MessageIndex, SubMessage<'a, R>);
294 type IntoIter = SubmessageIterator<'a, R>;
295
296 fn into_iter(self) -> Self::IntoIter {
297 Self::IntoIter::new(self)
298 }
299}
300
301fn get_templates(sects: &[SectionInfo]) -> Vec<TemplateInfo> {
302 let uniq: HashSet<_> = sects.iter().filter_map(|s| s.get_tmpl_code()).collect();
303 let mut vec: Vec<_> = uniq.into_iter().collect();
304 vec.sort_unstable();
305 vec
306}
307
308/// An iterator over submessages in the GRIB data.
309///
310/// This `struct` is created by the [`iter`] method on [`Grib2`]. See its
311/// documentation for more.
312///
313/// [`iter`]: Grib2::iter
314#[derive(Clone)]
315pub struct SubmessageIterator<'a, R> {
316 context: &'a Grib2<R>,
317 pos: usize,
318}
319
320impl<'a, R> SubmessageIterator<'a, R> {
321 fn new(context: &'a Grib2<R>) -> Self {
322 Self { context, pos: 0 }
323 }
324
325 fn new_submessage_section(&self, index: usize) -> Option<SubMessageSection<'a>> {
326 Some(SubMessageSection::new(
327 index,
328 self.context.sections.get(index)?,
329 ))
330 }
331}
332
333impl<'a, R> Iterator for SubmessageIterator<'a, R> {
334 type Item = (MessageIndex, SubMessage<'a, R>);
335
336 fn next(&mut self) -> Option<Self::Item> {
337 let submessage_index = self.context.submessages.get(self.pos)?;
338 self.pos += 1;
339
340 Some((
341 submessage_index.message_index(),
342 SubMessage(
343 self.new_submessage_section(submessage_index.0)?,
344 self.new_submessage_section(submessage_index.1)?,
345 submessage_index
346 .2
347 .and_then(|i| self.new_submessage_section(i)),
348 self.new_submessage_section(submessage_index.3)?,
349 self.new_submessage_section(submessage_index.4)?,
350 self.new_submessage_section(submessage_index.5)?,
351 self.new_submessage_section(submessage_index.6)?,
352 self.new_submessage_section(submessage_index.7)?,
353 self.new_submessage_section(submessage_index.8)?,
354 self.context.reader.borrow_mut(),
355 ),
356 ))
357 }
358
359 fn size_hint(&self) -> (usize, Option<usize>) {
360 let size = self.context.submessages.len() - self.pos;
361 (size, Some(size))
362 }
363
364 fn nth(&mut self, n: usize) -> Option<Self::Item> {
365 self.pos = n;
366 self.next()
367 }
368}
369
370impl<'a, R> IntoIterator for &'a SubmessageIterator<'a, R> {
371 type Item = (MessageIndex, SubMessage<'a, R>);
372 type IntoIter = SubmessageIterator<'a, R>;
373
374 fn into_iter(self) -> Self::IntoIter {
375 SubmessageIterator {
376 context: self.context,
377 pos: self.pos,
378 }
379 }
380}
381
382pub struct SubMessage<'a, R>(
383 pub SubMessageSection<'a>,
384 pub SubMessageSection<'a>,
385 pub Option<SubMessageSection<'a>>,
386 pub SubMessageSection<'a>,
387 pub SubMessageSection<'a>,
388 pub SubMessageSection<'a>,
389 pub SubMessageSection<'a>,
390 pub SubMessageSection<'a>,
391 pub SubMessageSection<'a>,
392 pub(crate) RefMut<'a, R>,
393);
394
395impl<R> SubMessage<'_, R> {
396 /// Returns the product's parameter.
397 ///
398 /// In the context of GRIB products, parameters refer to weather elements
399 /// such as air temperature, air pressure, and humidity, and other physical
400 /// quantities.
401 ///
402 /// # Examples
403 ///
404 /// ```
405 /// use std::{
406 /// fs::File,
407 /// io::{BufReader, Read},
408 /// };
409 ///
410 /// use grib::codetables::NCEP;
411 ///
412 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
413 /// let mut buf = Vec::new();
414 ///
415 /// let f = File::open("testdata/gdas.t12z.pgrb2.0p25.f000.0-10.xz")?;
416 /// let f = BufReader::new(f);
417 /// let mut f = xz2::bufread::XzDecoder::new(f);
418 /// f.read_to_end(&mut buf)?;
419 ///
420 /// let f = std::io::Cursor::new(buf);
421 /// let grib2 = grib::from_reader(f)?;
422 ///
423 /// let mut iter = grib2.iter();
424 /// let (_, message) = iter.next().ok_or_else(|| "first message is not found")?;
425 ///
426 /// let param = message.parameter();
427 /// assert_eq!(
428 /// param,
429 /// Some(grib::Parameter {
430 /// discipline: 0,
431 /// centre: 7,
432 /// master_ver: 2,
433 /// local_ver: 1,
434 /// category: 3,
435 /// num: 1
436 /// })
437 /// );
438 /// let param = param.unwrap();
439 /// assert_eq!(
440 /// param.description(),
441 /// Some("Pressure reduced to MSL".to_owned())
442 /// );
443 /// assert!(param.is_identical_to(NCEP::PRMSL));
444 /// Ok(())
445 /// }
446 /// ```
447 pub fn parameter(&self) -> Option<Parameter> {
448 let discipline = self.indicator().discipline;
449 let ident = self.identification();
450 let centre = ident.centre_id();
451 let master_ver = ident.master_table_version();
452 let local_ver = ident.local_table_version();
453 let prod_def = self.prod_def();
454 let category = prod_def.parameter_category()?;
455 let num = prod_def.parameter_number()?;
456 Some(Parameter {
457 discipline,
458 centre,
459 master_ver,
460 local_ver,
461 category,
462 num,
463 })
464 }
465
466 pub fn indicator(&self) -> &Indicator {
467 // panics should not happen if data is correct
468 match self.0.body.body.as_ref().unwrap() {
469 SectionBody::Section0(data) => data,
470 _ => panic!("something unexpected happened"),
471 }
472 }
473
474 pub fn identification(&self) -> &Identification {
475 // panics should not happen if data is correct
476 match self.1.body.body.as_ref().unwrap() {
477 SectionBody::Section1(data) => data,
478 _ => panic!("something unexpected happened"),
479 }
480 }
481
482 pub fn grid_def(&self) -> &GridDefinition {
483 // panics should not happen if data is correct
484 match self.3.body.body.as_ref().unwrap() {
485 SectionBody::Section3(data) => data,
486 _ => panic!("something unexpected happened"),
487 }
488 }
489
490 pub fn prod_def(&self) -> &ProdDefinition {
491 // panics should not happen if data is correct
492 match self.4.body.body.as_ref().unwrap() {
493 SectionBody::Section4(data) => data,
494 _ => panic!("something unexpected happened"),
495 }
496 }
497
498 pub fn repr_def(&self) -> &ReprDefinition {
499 // panics should not happen if data is correct
500 match self.5.body.body.as_ref().unwrap() {
501 SectionBody::Section5(data) => data,
502 _ => panic!("something unexpected happened"),
503 }
504 }
505
506 pub fn describe(&self) -> String {
507 let category = self.prod_def().parameter_category();
508 let forecast_time = self
509 .prod_def()
510 .forecast_time()
511 .map(|ft| ft.describe())
512 .unwrap_or((String::new(), String::new()));
513 let fixed_surfaces_info = self
514 .prod_def()
515 .fixed_surfaces()
516 .map(|(first, second)| (first.describe(), second.describe()))
517 .map(|(first, second)| (first.0, first.1, first.2, second.0, second.1, second.2))
518 .unwrap_or((
519 String::new(),
520 String::new(),
521 String::new(),
522 String::new(),
523 String::new(),
524 String::new(),
525 ));
526
527 format!(
528 "\
529Grid: {}
530 Number of points: {}
531Product: {}
532 Parameter Category: {}
533 Parameter: {}
534 Generating Proceess: {}
535 Forecast Time: {}
536 Forecast Time Unit: {}
537 1st Fixed Surface Type: {}
538 1st Scale Factor: {}
539 1st Scaled Value: {}
540 2nd Fixed Surface Type: {}
541 2nd Scale Factor: {}
542 2nd Scaled Value: {}
543Data Representation: {}
544 Number of represented values: {}
545",
546 self.3.describe().unwrap_or_default(),
547 self.grid_def().num_points(),
548 self.4.describe().unwrap_or_default(),
549 category
550 .map(|v| CodeTable4_1::new(self.indicator().discipline)
551 .lookup(usize::from(v))
552 .to_string())
553 .unwrap_or_default(),
554 self.prod_def()
555 .parameter_number()
556 .zip(category)
557 .map(|(n, c)| CodeTable4_2::new(self.indicator().discipline, c)
558 .lookup(usize::from(n))
559 .to_string())
560 .unwrap_or_default(),
561 self.prod_def()
562 .generating_process()
563 .map(|v| CodeTable4_3.lookup(usize::from(v)).to_string())
564 .unwrap_or_default(),
565 forecast_time.1,
566 forecast_time.0,
567 fixed_surfaces_info.0,
568 fixed_surfaces_info.1,
569 fixed_surfaces_info.2,
570 fixed_surfaces_info.3,
571 fixed_surfaces_info.4,
572 fixed_surfaces_info.5,
573 self.5.describe().unwrap_or_default(),
574 self.repr_def().num_points(),
575 )
576 }
577
578 /// Returns time-related raw information associated with the submessage.
579 ///
580 /// # Examples
581 ///
582 /// ```
583 /// use std::{
584 /// fs::File,
585 /// io::{BufReader, Read},
586 /// };
587 ///
588 /// use grib::{
589 /// codetables::grib2::{Table1_2, Table4_4},
590 /// Code, ForecastTime, TemporalRawInfo, UtcDateTime,
591 /// };
592 ///
593 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
594 /// let f = File::open(
595 /// "testdata/Z__C_RJTD_20160822020000_NOWC_GPV_Ggis10km_Pphw10_FH0000-0100_grib2.bin",
596 /// )?;
597 /// let f = BufReader::new(f);
598 /// let grib2 = grib::from_reader(f)?;
599 ///
600 /// let mut iter = grib2.iter();
601 ///
602 /// {
603 /// let (_, message) = iter.next().ok_or_else(|| "first message is not found")?;
604 /// let actual = message.temporal_raw_info();
605 /// let expected = TemporalRawInfo {
606 /// ref_time_significance: Code::Name(Table1_2::Analysis),
607 /// ref_time_unchecked: UtcDateTime::new(2016, 8, 22, 2, 0, 0),
608 /// forecast_time_diff: Some(ForecastTime {
609 /// unit: Code::Name(Table4_4::Minute),
610 /// value: 0,
611 /// }),
612 /// };
613 /// assert_eq!(actual, expected);
614 /// }
615 ///
616 /// {
617 /// let (_, message) = iter.next().ok_or_else(|| "second message is not found")?;
618 /// let actual = message.temporal_raw_info();
619 /// let expected = TemporalRawInfo {
620 /// ref_time_significance: Code::Name(Table1_2::Analysis),
621 /// ref_time_unchecked: UtcDateTime::new(2016, 8, 22, 2, 0, 0),
622 /// forecast_time_diff: Some(ForecastTime {
623 /// unit: Code::Name(Table4_4::Minute),
624 /// value: 10,
625 /// }),
626 /// };
627 /// assert_eq!(actual, expected);
628 /// }
629 ///
630 /// Ok(())
631 /// }
632 /// ```
633 pub fn temporal_raw_info(&self) -> TemporalRawInfo {
634 let ref_time_significance = self.identification().ref_time_significance();
635 let ref_time_unchecked = self.identification().ref_time_unchecked();
636 let forecast_time = self.prod_def().forecast_time();
637 TemporalRawInfo::new(ref_time_significance, ref_time_unchecked, forecast_time)
638 }
639
640 #[cfg(feature = "time-calculation")]
641 /// Returns time-related calculated information associated with the
642 /// submessage.
643 ///
644 /// # Examples
645 ///
646 /// ```
647 /// use std::{
648 /// fs::File,
649 /// io::{BufReader, Read},
650 /// };
651 ///
652 /// use chrono::{TimeZone, Utc};
653 /// use grib::{
654 /// codetables::grib2::{Table1_2, Table4_4},
655 /// Code, ForecastTime, TemporalInfo, UtcDateTime,
656 /// };
657 ///
658 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
659 /// let f = File::open(
660 /// "testdata/Z__C_RJTD_20160822020000_NOWC_GPV_Ggis10km_Pphw10_FH0000-0100_grib2.bin",
661 /// )?;
662 /// let f = BufReader::new(f);
663 /// let grib2 = grib::from_reader(f)?;
664 ///
665 /// let mut iter = grib2.iter();
666 ///
667 /// {
668 /// let (_, message) = iter.next().ok_or_else(|| "first message is not found")?;
669 /// let actual = message.temporal_info();
670 /// let expected = TemporalInfo {
671 /// ref_time: Some(Utc.with_ymd_and_hms(2016, 8, 22, 2, 0, 0).unwrap()),
672 /// forecast_time_target: Some(Utc.with_ymd_and_hms(2016, 8, 22, 2, 0, 0).unwrap()),
673 /// };
674 /// assert_eq!(actual, expected);
675 /// }
676 ///
677 /// {
678 /// let (_, message) = iter.next().ok_or_else(|| "second message is not found")?;
679 /// let actual = message.temporal_info();
680 /// let expected = TemporalInfo {
681 /// ref_time: Some(Utc.with_ymd_and_hms(2016, 8, 22, 2, 0, 0).unwrap()),
682 /// forecast_time_target: Some(Utc.with_ymd_and_hms(2016, 8, 22, 2, 10, 0).unwrap()),
683 /// };
684 /// assert_eq!(actual, expected);
685 /// }
686 ///
687 /// Ok(())
688 /// }
689 /// ```
690 pub fn temporal_info(&self) -> TemporalInfo {
691 let raw_info = self.temporal_raw_info();
692 TemporalInfo::from(&raw_info)
693 }
694
695 /// Returns the shape of the grid, i.e. a tuple of the number of grids in
696 /// the i and j directions.
697 ///
698 /// # Examples
699 ///
700 /// ```
701 /// use std::{
702 /// fs::File,
703 /// io::{BufReader, Read},
704 /// };
705 ///
706 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
707 /// let mut buf = Vec::new();
708 ///
709 /// let f = File::open("testdata/gdas.t12z.pgrb2.0p25.f000.0-10.xz")?;
710 /// let f = BufReader::new(f);
711 /// let mut f = xz2::bufread::XzDecoder::new(f);
712 /// f.read_to_end(&mut buf)?;
713 ///
714 /// let f = std::io::Cursor::new(buf);
715 /// let grib2 = grib::from_reader(f)?;
716 ///
717 /// let mut iter = grib2.iter();
718 /// let (_, message) = iter.next().ok_or_else(|| "first message is not found")?;
719 ///
720 /// let shape = message.grid_shape()?;
721 /// assert_eq!(shape, (1440, 721));
722 /// Ok(())
723 /// }
724 /// ```
725 pub fn grid_shape(&self) -> Result<(usize, usize), GribError> {
726 let grid_def = self.grid_def();
727 let shape = GridDefinitionTemplateValues::try_from(grid_def)?.grid_shape();
728 Ok(shape)
729 }
730
731 /// Computes and returns an iterator over `(i, j)` of grid points.
732 ///
733 /// The order of items is the same as the order of the grid point values,
734 /// defined by the scanning mode ([`ScanningMode`](`crate::ScanningMode`))
735 /// in the data.
736 ///
737 /// This iterator allows users to perform their own coordinate calculations
738 /// for unsupported grid systems and map the results to grid point values.
739 ///
740 /// # Examples
741 ///
742 /// ```
743 /// use std::{
744 /// fs::File,
745 /// io::{BufReader, Read},
746 /// };
747 ///
748 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
749 /// let mut buf = Vec::new();
750 ///
751 /// let f = File::open("testdata/gdas.t12z.pgrb2.0p25.f000.0-10.xz")?;
752 /// let f = BufReader::new(f);
753 /// let mut f = xz2::bufread::XzDecoder::new(f);
754 /// f.read_to_end(&mut buf)?;
755 ///
756 /// let f = std::io::Cursor::new(buf);
757 /// let grib2 = grib::from_reader(f)?;
758 ///
759 /// let mut iter = grib2.iter();
760 /// let (_, message) = iter.next().ok_or_else(|| "first message is not found")?;
761 ///
762 /// let mut latlons = message.ij()?;
763 /// assert_eq!(latlons.next(), Some((0, 0)));
764 /// assert_eq!(latlons.next(), Some((1, 0)));
765 /// Ok(())
766 /// }
767 /// ```
768 pub fn ij(&self) -> Result<GridPointIndexIterator, GribError> {
769 let grid_def = self.grid_def();
770 let num_defined = grid_def.num_points() as usize;
771 let ij = GridDefinitionTemplateValues::try_from(grid_def)?.ij()?;
772 let (num_decoded, _) = ij.size_hint();
773 if num_defined == num_decoded {
774 Ok(ij)
775 } else {
776 Err(GribError::InvalidValueError(format!(
777 "number of grid points does not match: {num_defined} (defined) vs {num_decoded} (decoded)"
778 )))
779 }
780 }
781
782 /// Computes and returns an iterator over latitudes and longitudes of grid
783 /// points.
784 ///
785 /// The order of lat/lon data of grid points is the same as the order of the
786 /// grid point values, defined by the scanning mode
787 /// ([`ScanningMode`](`crate::ScanningMode`)) in the data.
788 ///
789 /// # Examples
790 ///
791 /// ```
792 /// use std::{
793 /// fs::File,
794 /// io::{BufReader, Read},
795 /// };
796 ///
797 /// fn main() -> Result<(), Box<dyn std::error::Error>> {
798 /// let mut buf = Vec::new();
799 ///
800 /// let f = File::open("testdata/gdas.t12z.pgrb2.0p25.f000.0-10.xz")?;
801 /// let f = BufReader::new(f);
802 /// let mut f = xz2::bufread::XzDecoder::new(f);
803 /// f.read_to_end(&mut buf)?;
804 ///
805 /// let f = std::io::Cursor::new(buf);
806 /// let grib2 = grib::from_reader(f)?;
807 ///
808 /// let mut iter = grib2.iter();
809 /// let (_, message) = iter.next().ok_or_else(|| "first message is not found")?;
810 ///
811 /// let mut latlons = message.latlons()?;
812 /// assert_eq!(latlons.next(), Some((90.0, 0.0)));
813 /// assert_eq!(latlons.next(), Some((90.0, 0.25000003)));
814 /// Ok(())
815 /// }
816 /// ```
817 pub fn latlons(&self) -> Result<GridPointIterator, GribError> {
818 let grid_def = self.grid_def();
819 let num_defined = grid_def.num_points() as usize;
820 let latlons = GridDefinitionTemplateValues::try_from(grid_def)?.latlons()?;
821 let (num_decoded, _) = latlons.size_hint();
822 if num_defined == num_decoded {
823 Ok(latlons)
824 } else {
825 Err(GribError::InvalidValueError(format!(
826 "number of grid points does not match: {num_defined} (defined) vs {num_decoded} (decoded)"
827 )))
828 }
829 }
830}
831
832pub struct SubMessageSection<'a> {
833 pub index: usize,
834 pub body: &'a SectionInfo,
835}
836
837impl<'a> SubMessageSection<'a> {
838 pub fn new(index: usize, body: &'a SectionInfo) -> Self {
839 Self { index, body }
840 }
841
842 pub fn template_code(&self) -> Option<TemplateInfo> {
843 self.body.get_tmpl_code()
844 }
845
846 pub fn describe(&self) -> Option<String> {
847 self.template_code().and_then(|code| code.describe())
848 }
849}
850
851#[cfg(test)]
852mod tests {
853 use std::{fs::File, io::BufReader};
854
855 use super::*;
856
857 macro_rules! sect_placeholder {
858 ($num:expr) => {{
859 SectionInfo {
860 num: $num,
861 offset: 0,
862 size: 0,
863 body: None,
864 }
865 }};
866 }
867
868 #[test]
869 fn context_from_buf_reader() {
870 let f = File::open(
871 "testdata/icon_global_icosahedral_single-level_2021112018_000_TOT_PREC.grib2",
872 )
873 .unwrap();
874 let f = BufReader::new(f);
875 let result = from_reader(f);
876 assert!(result.is_ok())
877 }
878
879 #[test]
880 fn context_from_bytes() {
881 let f = File::open(
882 "testdata/icon_global_icosahedral_single-level_2021112018_000_TOT_PREC.grib2",
883 )
884 .unwrap();
885 let mut f = BufReader::new(f);
886 let mut buf = Vec::new();
887 f.read_to_end(&mut buf).unwrap();
888 let result = from_bytes(&buf);
889 assert!(result.is_ok())
890 }
891
892 #[test]
893 fn get_tmpl_code_normal() {
894 let sect = SectionInfo {
895 num: 5,
896 offset: 8902,
897 size: 23,
898 body: Some(SectionBody::Section5(
899 ReprDefinition::from_payload(
900 vec![0x00, 0x01, 0x50, 0x00, 0x00, 0xc8].into_boxed_slice(),
901 )
902 .unwrap(),
903 )),
904 };
905
906 assert_eq!(sect.get_tmpl_code(), Some(TemplateInfo(5, 200)));
907 }
908
909 #[test]
910 fn get_templates_normal() {
911 let sects = vec![
912 sect_placeholder!(0),
913 sect_placeholder!(1),
914 SectionInfo {
915 num: 3,
916 offset: 0,
917 size: 0,
918 body: Some(SectionBody::Section3(
919 GridDefinition::from_payload(vec![0; 9].into_boxed_slice()).unwrap(),
920 )),
921 },
922 SectionInfo {
923 num: 4,
924 offset: 0,
925 size: 0,
926 body: Some(SectionBody::Section4(
927 ProdDefinition::from_payload(vec![0; 4].into_boxed_slice()).unwrap(),
928 )),
929 },
930 SectionInfo {
931 num: 5,
932 offset: 0,
933 size: 0,
934 body: Some(SectionBody::Section5(
935 ReprDefinition::from_payload(vec![0; 6].into_boxed_slice()).unwrap(),
936 )),
937 },
938 sect_placeholder!(6),
939 sect_placeholder!(7),
940 SectionInfo {
941 num: 3,
942 offset: 0,
943 size: 0,
944 body: Some(SectionBody::Section3(
945 GridDefinition::from_payload(
946 vec![0, 0, 0, 0, 0, 0, 0, 0, 1].into_boxed_slice(),
947 )
948 .unwrap(),
949 )),
950 },
951 SectionInfo {
952 num: 4,
953 offset: 0,
954 size: 0,
955 body: Some(SectionBody::Section4(
956 ProdDefinition::from_payload(vec![0; 4].into_boxed_slice()).unwrap(),
957 )),
958 },
959 SectionInfo {
960 num: 5,
961 offset: 0,
962 size: 0,
963 body: Some(SectionBody::Section5(
964 ReprDefinition::from_payload(vec![0; 6].into_boxed_slice()).unwrap(),
965 )),
966 },
967 sect_placeholder!(6),
968 sect_placeholder!(7),
969 sect_placeholder!(8),
970 ]
971 .into_boxed_slice();
972
973 assert_eq!(
974 get_templates(§s),
975 vec![
976 TemplateInfo(3, 0),
977 TemplateInfo(3, 1),
978 TemplateInfo(4, 0),
979 TemplateInfo(5, 0),
980 ]
981 );
982 }
983
984 macro_rules! test_submessage_iterator {
985 ($((
986 $name:ident,
987 $xz_compressed_input:expr,
988 $nth:expr,
989 $expected_index:expr,
990 $expected_section_indices:expr,
991 ),)*) => ($(
992 #[test]
993 fn $name() -> Result<(), Box<dyn std::error::Error>> {
994 let mut buf = Vec::new();
995
996 let f = File::open($xz_compressed_input)?;
997 let f = BufReader::new(f);
998 let mut f = xz2::bufread::XzDecoder::new(f);
999 f.read_to_end(&mut buf)?;
1000
1001 let f = Cursor::new(buf);
1002 let grib2 = crate::from_reader(f)?;
1003 let mut iter = grib2.iter();
1004
1005 let (actual_index, message) = iter.nth($nth).ok_or_else(|| "item not available")?;
1006 assert_eq!(actual_index, $expected_index);
1007 let actual_section_indices = get_section_indices(message);
1008 assert_eq!(actual_section_indices, $expected_section_indices);
1009
1010 Ok(())
1011 }
1012 )*);
1013 }
1014
1015 test_submessage_iterator! {
1016 (
1017 item_0_from_submessage_iterator_for_single_message_data_with_multiple_submessages,
1018 "testdata/Z__C_RJTD_20190304000000_MSM_GUID_Rjp_P-all_FH03-39_Toorg_grib2.bin.xz",
1019 0,
1020 (0, 0),
1021 (0, 1, None, 2, 3, 4, 5, 6, 0),
1022 ),
1023 (
1024 item_1_from_submessage_iterator_for_single_message_data_with_multiple_submessages,
1025 "testdata/Z__C_RJTD_20190304000000_MSM_GUID_Rjp_P-all_FH03-39_Toorg_grib2.bin.xz",
1026 1,
1027 (0, 1),
1028 (0, 1, None, 2, 7, 8, 9, 10, 0),
1029 ),
1030 (
1031 item_0_from_submessage_iterator_for_multi_message_data,
1032 "testdata/gdas.t12z.pgrb2.0p25.f000.0-10.xz",
1033 0,
1034 (0, 0),
1035 (0, 1, None, 2, 3, 4, 5, 6, 7),
1036 ),
1037 (
1038 item_1_from_submessage_iterator_for_multi_message_data,
1039 "testdata/gdas.t12z.pgrb2.0p25.f000.0-10.xz",
1040 1,
1041 (1, 0),
1042 (8, 9, None, 10, 11, 12, 13, 14, 15),
1043 ),
1044 }
1045
1046 fn get_section_indices<R>(
1047 submessage: SubMessage<'_, R>,
1048 ) -> (
1049 usize,
1050 usize,
1051 Option<usize>,
1052 usize,
1053 usize,
1054 usize,
1055 usize,
1056 usize,
1057 usize,
1058 ) {
1059 (
1060 submessage.0.index,
1061 submessage.1.index,
1062 submessage.2.map(|s| s.index),
1063 submessage.3.index,
1064 submessage.4.index,
1065 submessage.5.index,
1066 submessage.6.index,
1067 submessage.7.index,
1068 submessage.8.index,
1069 )
1070 }
1071}