1use std::{
2 cell::{RefCell, RefMut},
3 collections::HashSet,
4 fmt::{self, Display, Formatter},
5 io::{Cursor, Read, Seek},
6};
7
8use crate::{
9 codetables::{
10 CodeTable3_1, CodeTable4_0, CodeTable4_1, CodeTable4_2, CodeTable4_3, CodeTable5_0, Lookup,
11 },
12 datatypes::*,
13 error::*,
14 grid::GridPointIterator,
15 parser::Grib2SubmessageIndexStream,
16 reader::{Grib2Read, Grib2SectionStream, SeekableGrib2Reader, SECT8_ES_SIZE},
17 GridPointIndexIterator,
18};
19
20#[derive(Default, Debug, Clone, PartialEq, Eq)]
21pub struct SectionInfo {
22 pub num: u8,
23 pub offset: usize,
24 pub size: usize,
25 pub body: Option<SectionBody>,
26}
27
28impl SectionInfo {
29 pub fn get_tmpl_code(&self) -> Option<TemplateInfo> {
30 let tmpl_num = self.body.as_ref()?.get_tmpl_num()?;
31 Some(TemplateInfo(self.num, tmpl_num))
32 }
33
34 pub(crate) fn new_8(offset: usize) -> Self {
35 Self {
36 num: 8,
37 offset,
38 size: SECT8_ES_SIZE,
39 body: None,
40 }
41 }
42}
43
44#[derive(Debug, Clone, PartialEq, Eq)]
45pub enum SectionBody {
46 Section0(Indicator),
47 Section1(Identification),
48 Section2(LocalUse),
49 Section3(GridDefinition),
50 Section4(ProdDefinition),
51 Section5(ReprDefinition),
52 Section6(BitMap),
53 Section7,
54}
55
56impl SectionBody {
57 fn get_tmpl_num(&self) -> Option<u16> {
58 match self {
59 Self::Section3(s) => Some(s.grid_tmpl_num()),
60 Self::Section4(s) => Some(s.prod_tmpl_num()),
61 Self::Section5(s) => Some(s.repr_tmpl_num()),
62 _ => None,
63 }
64 }
65}
66
67#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
68pub struct TemplateInfo(pub u8, pub u16);
69
70impl TemplateInfo {
71 pub fn describe(&self) -> Option<String> {
72 match self.0 {
73 3 => Some(CodeTable3_1.lookup(usize::from(self.1)).to_string()),
74 4 => Some(CodeTable4_0.lookup(usize::from(self.1)).to_string()),
75 5 => Some(CodeTable5_0.lookup(usize::from(self.1)).to_string()),
76 _ => None,
77 }
78 }
79}
80
81impl Display for TemplateInfo {
82 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
83 write!(f, "{}.{}", self.0, self.1)
84 }
85}
86
87pub fn from_reader<SR: Read + Seek>(
106 reader: SR,
107) -> Result<Grib2<SeekableGrib2Reader<SR>>, GribError> {
108 Grib2::<SeekableGrib2Reader<SR>>::read_with_seekable(reader)
109}
110
111pub fn from_bytes<T>(bytes: T) -> Result<Grib2<SeekableGrib2Reader<Cursor<T>>>, GribError>
159where
160 T: AsRef<[u8]>,
161{
162 let reader = Cursor::new(bytes);
163 Grib2::<SeekableGrib2Reader<Cursor<T>>>::read_with_seekable(reader)
164}
165
166pub struct Grib2<R> {
167 reader: RefCell<R>,
168 sections: Box<[SectionInfo]>,
169 submessages: Vec<Grib2SubmessageIndex>,
170}
171
172impl<R> Grib2<R> {
173 pub fn len(&self) -> usize {
190 self.submessages.len()
191 }
192
193 #[inline]
195 pub fn is_empty(&self) -> bool {
196 self.len() == 0
197 }
198
199 #[inline]
225 pub fn iter(&self) -> SubmessageIterator<'_, R> {
226 self.into_iter()
227 }
228
229 pub fn submessages(&self) -> SubmessageIterator<'_, R> {
233 self.into_iter()
234 }
235
236 pub fn sections(&self) -> std::slice::Iter<'_, SectionInfo> {
261 self.sections.iter()
262 }
263}
264
265impl<R: Grib2Read> Grib2<R> {
266 pub fn read(r: R) -> Result<Self, GribError> {
267 let mut sect_stream = Grib2SectionStream::new(r);
268 let mut cacher = Vec::new();
269 let parser = Grib2SubmessageIndexStream::new(sect_stream.by_ref()).with_cacher(&mut cacher);
270 let submessages = parser.collect::<Result<Vec<_>, _>>()?;
271 Ok(Self {
272 reader: RefCell::new(sect_stream.into_reader()),
273 sections: cacher.into_boxed_slice(),
274 submessages,
275 })
276 }
277
278 pub fn read_with_seekable<SR: Read + Seek>(
279 r: SR,
280 ) -> Result<Grib2<SeekableGrib2Reader<SR>>, GribError> {
281 let r = SeekableGrib2Reader::new(r);
282 Grib2::<SeekableGrib2Reader<SR>>::read(r)
283 }
284
285 pub fn list_templates(&self) -> Vec<TemplateInfo> {
286 get_templates(&self.sections)
287 }
288}
289
290impl<'a, R: 'a> IntoIterator for &'a Grib2<R> {
291 type Item = (MessageIndex, SubMessage<'a, R>);
292 type IntoIter = SubmessageIterator<'a, R>;
293
294 fn into_iter(self) -> Self::IntoIter {
295 Self::IntoIter::new(self)
296 }
297}
298
299fn get_templates(sects: &[SectionInfo]) -> Vec<TemplateInfo> {
300 let uniq: HashSet<_> = sects.iter().filter_map(|s| s.get_tmpl_code()).collect();
301 let mut vec: Vec<_> = uniq.into_iter().collect();
302 vec.sort_unstable();
303 vec
304}
305
306#[derive(Clone)]
313pub struct SubmessageIterator<'a, R> {
314 context: &'a Grib2<R>,
315 pos: usize,
316}
317
318impl<'a, R> SubmessageIterator<'a, R> {
319 fn new(context: &'a Grib2<R>) -> Self {
320 Self { context, pos: 0 }
321 }
322
323 fn new_submessage_section(&self, index: usize) -> Option<SubMessageSection<'a>> {
324 Some(SubMessageSection::new(
325 index,
326 self.context.sections.get(index)?,
327 ))
328 }
329}
330
331impl<'a, R> Iterator for SubmessageIterator<'a, R> {
332 type Item = (MessageIndex, SubMessage<'a, R>);
333
334 fn next(&mut self) -> Option<Self::Item> {
335 let submessage_index = self.context.submessages.get(self.pos)?;
336 self.pos += 1;
337
338 Some((
339 submessage_index.message_index(),
340 SubMessage(
341 self.new_submessage_section(submessage_index.0)?,
342 self.new_submessage_section(submessage_index.1)?,
343 submessage_index
344 .2
345 .and_then(|i| self.new_submessage_section(i)),
346 self.new_submessage_section(submessage_index.3)?,
347 self.new_submessage_section(submessage_index.4)?,
348 self.new_submessage_section(submessage_index.5)?,
349 self.new_submessage_section(submessage_index.6)?,
350 self.new_submessage_section(submessage_index.7)?,
351 self.new_submessage_section(submessage_index.8)?,
352 self.context.reader.borrow_mut(),
353 ),
354 ))
355 }
356
357 fn size_hint(&self) -> (usize, Option<usize>) {
358 let size = self.context.submessages.len() - self.pos;
359 (size, Some(size))
360 }
361
362 fn nth(&mut self, n: usize) -> Option<Self::Item> {
363 self.pos = n;
364 self.next()
365 }
366}
367
368impl<'a, R> IntoIterator for &'a SubmessageIterator<'a, R> {
369 type Item = (MessageIndex, SubMessage<'a, R>);
370 type IntoIter = SubmessageIterator<'a, R>;
371
372 fn into_iter(self) -> Self::IntoIter {
373 SubmessageIterator {
374 context: self.context,
375 pos: self.pos,
376 }
377 }
378}
379
380pub struct SubMessage<'a, R>(
381 pub SubMessageSection<'a>,
382 pub SubMessageSection<'a>,
383 pub Option<SubMessageSection<'a>>,
384 pub SubMessageSection<'a>,
385 pub SubMessageSection<'a>,
386 pub SubMessageSection<'a>,
387 pub SubMessageSection<'a>,
388 pub SubMessageSection<'a>,
389 pub SubMessageSection<'a>,
390 pub(crate) RefMut<'a, R>,
391);
392
393impl<R> SubMessage<'_, R> {
394 pub fn parameter(&self) -> Option<Parameter> {
446 let discipline = self.indicator().discipline;
447 let ident = self.identification();
448 let centre = ident.centre_id();
449 let master_ver = ident.master_table_version();
450 let local_ver = ident.local_table_version();
451 let prod_def = self.prod_def();
452 let category = prod_def.parameter_category()?;
453 let num = prod_def.parameter_number()?;
454 Some(Parameter {
455 discipline,
456 centre,
457 master_ver,
458 local_ver,
459 category,
460 num,
461 })
462 }
463
464 pub fn indicator(&self) -> &Indicator {
465 match self.0.body.body.as_ref().unwrap() {
467 SectionBody::Section0(data) => data,
468 _ => panic!("something unexpected happened"),
469 }
470 }
471
472 fn identification(&self) -> &Identification {
473 match self.1.body.body.as_ref().unwrap() {
475 SectionBody::Section1(data) => data,
476 _ => panic!("something unexpected happened"),
477 }
478 }
479
480 pub fn grid_def(&self) -> &GridDefinition {
481 match self.3.body.body.as_ref().unwrap() {
483 SectionBody::Section3(data) => data,
484 _ => panic!("something unexpected happened"),
485 }
486 }
487
488 pub fn prod_def(&self) -> &ProdDefinition {
489 match self.4.body.body.as_ref().unwrap() {
491 SectionBody::Section4(data) => data,
492 _ => panic!("something unexpected happened"),
493 }
494 }
495
496 pub fn repr_def(&self) -> &ReprDefinition {
497 match self.5.body.body.as_ref().unwrap() {
499 SectionBody::Section5(data) => data,
500 _ => panic!("something unexpected happened"),
501 }
502 }
503
504 pub fn describe(&self) -> String {
505 let category = self.prod_def().parameter_category();
506 let forecast_time = self
507 .prod_def()
508 .forecast_time()
509 .map(|ft| ft.describe())
510 .unwrap_or((String::new(), String::new()));
511 let fixed_surfaces_info = self
512 .prod_def()
513 .fixed_surfaces()
514 .map(|(first, second)| (first.describe(), second.describe()))
515 .map(|(first, second)| (first.0, first.1, first.2, second.0, second.1, second.2))
516 .unwrap_or((
517 String::new(),
518 String::new(),
519 String::new(),
520 String::new(),
521 String::new(),
522 String::new(),
523 ));
524
525 format!(
526 "\
527Grid: {}
528 Number of points: {}
529Product: {}
530 Parameter Category: {}
531 Parameter: {}
532 Generating Proceess: {}
533 Forecast Time: {}
534 Forecast Time Unit: {}
535 1st Fixed Surface Type: {}
536 1st Scale Factor: {}
537 1st Scaled Value: {}
538 2nd Fixed Surface Type: {}
539 2nd Scale Factor: {}
540 2nd Scaled Value: {}
541Data Representation: {}
542 Number of represented values: {}
543",
544 self.3.describe().unwrap_or_default(),
545 self.grid_def().num_points(),
546 self.4.describe().unwrap_or_default(),
547 category
548 .map(|v| CodeTable4_1::new(self.indicator().discipline)
549 .lookup(usize::from(v))
550 .to_string())
551 .unwrap_or_default(),
552 self.prod_def()
553 .parameter_number()
554 .zip(category)
555 .map(|(n, c)| CodeTable4_2::new(self.indicator().discipline, c)
556 .lookup(usize::from(n))
557 .to_string())
558 .unwrap_or_default(),
559 self.prod_def()
560 .generating_process()
561 .map(|v| CodeTable4_3.lookup(usize::from(v)).to_string())
562 .unwrap_or_default(),
563 forecast_time.1,
564 forecast_time.0,
565 fixed_surfaces_info.0,
566 fixed_surfaces_info.1,
567 fixed_surfaces_info.2,
568 fixed_surfaces_info.3,
569 fixed_surfaces_info.4,
570 fixed_surfaces_info.5,
571 self.5.describe().unwrap_or_default(),
572 self.repr_def().num_points(),
573 )
574 }
575
576 pub fn grid_shape(&self) -> Result<(usize, usize), GribError> {
607 let grid_def = self.grid_def();
608 let shape = GridDefinitionTemplateValues::try_from(grid_def)?.grid_shape();
609 Ok(shape)
610 }
611
612 pub fn ij(&self) -> Result<GridPointIndexIterator, GribError> {
650 let grid_def = self.grid_def();
651 let num_defined = grid_def.num_points() as usize;
652 let ij = GridDefinitionTemplateValues::try_from(grid_def)?.ij()?;
653 let (num_decoded, _) = ij.size_hint();
654 if num_defined == num_decoded {
655 Ok(ij)
656 } else {
657 Err(GribError::InvalidValueError(format!(
658 "number of grid points does not match: {num_defined} (defined) vs {num_decoded} (decoded)"
659 )))
660 }
661 }
662
663 pub fn latlons(&self) -> Result<GridPointIterator, GribError> {
699 let grid_def = self.grid_def();
700 let num_defined = grid_def.num_points() as usize;
701 let latlons = GridDefinitionTemplateValues::try_from(grid_def)?.latlons()?;
702 let (num_decoded, _) = latlons.size_hint();
703 if num_defined == num_decoded {
704 Ok(latlons)
705 } else {
706 Err(GribError::InvalidValueError(format!(
707 "number of grid points does not match: {num_defined} (defined) vs {num_decoded} (decoded)"
708 )))
709 }
710 }
711}
712
713pub struct SubMessageSection<'a> {
714 pub index: usize,
715 pub body: &'a SectionInfo,
716}
717
718impl<'a> SubMessageSection<'a> {
719 pub fn new(index: usize, body: &'a SectionInfo) -> Self {
720 Self { index, body }
721 }
722
723 pub fn template_code(&self) -> Option<TemplateInfo> {
724 self.body.get_tmpl_code()
725 }
726
727 pub fn describe(&self) -> Option<String> {
728 self.template_code().and_then(|code| code.describe())
729 }
730}
731
732#[cfg(test)]
733mod tests {
734 use std::{fs::File, io::BufReader};
735
736 use super::*;
737
738 macro_rules! sect_placeholder {
739 ($num:expr) => {{
740 SectionInfo {
741 num: $num,
742 offset: 0,
743 size: 0,
744 body: None,
745 }
746 }};
747 }
748
749 #[test]
750 fn context_from_buf_reader() {
751 let f = File::open(
752 "testdata/icon_global_icosahedral_single-level_2021112018_000_TOT_PREC.grib2",
753 )
754 .unwrap();
755 let f = BufReader::new(f);
756 let result = from_reader(f);
757 assert!(result.is_ok())
758 }
759
760 #[test]
761 fn context_from_bytes() {
762 let f = File::open(
763 "testdata/icon_global_icosahedral_single-level_2021112018_000_TOT_PREC.grib2",
764 )
765 .unwrap();
766 let mut f = BufReader::new(f);
767 let mut buf = Vec::new();
768 f.read_to_end(&mut buf).unwrap();
769 let result = from_bytes(&buf);
770 assert!(result.is_ok())
771 }
772
773 #[test]
774 fn get_tmpl_code_normal() {
775 let sect = SectionInfo {
776 num: 5,
777 offset: 8902,
778 size: 23,
779 body: Some(SectionBody::Section5(
780 ReprDefinition::from_payload(
781 vec![0x00, 0x01, 0x50, 0x00, 0x00, 0xc8].into_boxed_slice(),
782 )
783 .unwrap(),
784 )),
785 };
786
787 assert_eq!(sect.get_tmpl_code(), Some(TemplateInfo(5, 200)));
788 }
789
790 #[test]
791 fn get_templates_normal() {
792 let sects = vec![
793 sect_placeholder!(0),
794 sect_placeholder!(1),
795 SectionInfo {
796 num: 3,
797 offset: 0,
798 size: 0,
799 body: Some(SectionBody::Section3(
800 GridDefinition::from_payload(vec![0; 9].into_boxed_slice()).unwrap(),
801 )),
802 },
803 SectionInfo {
804 num: 4,
805 offset: 0,
806 size: 0,
807 body: Some(SectionBody::Section4(
808 ProdDefinition::from_payload(vec![0; 4].into_boxed_slice()).unwrap(),
809 )),
810 },
811 SectionInfo {
812 num: 5,
813 offset: 0,
814 size: 0,
815 body: Some(SectionBody::Section5(
816 ReprDefinition::from_payload(vec![0; 6].into_boxed_slice()).unwrap(),
817 )),
818 },
819 sect_placeholder!(6),
820 sect_placeholder!(7),
821 SectionInfo {
822 num: 3,
823 offset: 0,
824 size: 0,
825 body: Some(SectionBody::Section3(
826 GridDefinition::from_payload(
827 vec![0, 0, 0, 0, 0, 0, 0, 0, 1].into_boxed_slice(),
828 )
829 .unwrap(),
830 )),
831 },
832 SectionInfo {
833 num: 4,
834 offset: 0,
835 size: 0,
836 body: Some(SectionBody::Section4(
837 ProdDefinition::from_payload(vec![0; 4].into_boxed_slice()).unwrap(),
838 )),
839 },
840 SectionInfo {
841 num: 5,
842 offset: 0,
843 size: 0,
844 body: Some(SectionBody::Section5(
845 ReprDefinition::from_payload(vec![0; 6].into_boxed_slice()).unwrap(),
846 )),
847 },
848 sect_placeholder!(6),
849 sect_placeholder!(7),
850 sect_placeholder!(8),
851 ]
852 .into_boxed_slice();
853
854 assert_eq!(
855 get_templates(§s),
856 vec![
857 TemplateInfo(3, 0),
858 TemplateInfo(3, 1),
859 TemplateInfo(4, 0),
860 TemplateInfo(5, 0),
861 ]
862 );
863 }
864
865 macro_rules! test_submessage_iterator {
866 ($((
867 $name:ident,
868 $xz_compressed_input:expr,
869 $nth:expr,
870 $expected_index:expr,
871 $expected_section_indices:expr,
872 ),)*) => ($(
873 #[test]
874 fn $name() -> Result<(), Box<dyn std::error::Error>> {
875 let mut buf = Vec::new();
876
877 let f = File::open($xz_compressed_input)?;
878 let f = BufReader::new(f);
879 let mut f = xz2::bufread::XzDecoder::new(f);
880 f.read_to_end(&mut buf)?;
881
882 let f = Cursor::new(buf);
883 let grib2 = crate::from_reader(f)?;
884 let mut iter = grib2.iter();
885
886 let (actual_index, message) = iter.nth($nth).ok_or_else(|| "item not available")?;
887 assert_eq!(actual_index, $expected_index);
888 let actual_section_indices = get_section_indices(message);
889 assert_eq!(actual_section_indices, $expected_section_indices);
890
891 Ok(())
892 }
893 )*);
894 }
895
896 test_submessage_iterator! {
897 (
898 item_0_from_submessage_iterator_for_single_message_data_with_multiple_submessages,
899 "testdata/Z__C_RJTD_20190304000000_MSM_GUID_Rjp_P-all_FH03-39_Toorg_grib2.bin.xz",
900 0,
901 (0, 0),
902 (0, 1, None, 2, 3, 4, 5, 6, 0),
903 ),
904 (
905 item_1_from_submessage_iterator_for_single_message_data_with_multiple_submessages,
906 "testdata/Z__C_RJTD_20190304000000_MSM_GUID_Rjp_P-all_FH03-39_Toorg_grib2.bin.xz",
907 1,
908 (0, 1),
909 (0, 1, None, 2, 7, 8, 9, 10, 0),
910 ),
911 (
912 item_0_from_submessage_iterator_for_multi_message_data,
913 "testdata/gdas.t12z.pgrb2.0p25.f000.0-10.xz",
914 0,
915 (0, 0),
916 (0, 1, None, 2, 3, 4, 5, 6, 7),
917 ),
918 (
919 item_1_from_submessage_iterator_for_multi_message_data,
920 "testdata/gdas.t12z.pgrb2.0p25.f000.0-10.xz",
921 1,
922 (1, 0),
923 (8, 9, None, 10, 11, 12, 13, 14, 15),
924 ),
925 }
926
927 fn get_section_indices<R>(
928 submessage: SubMessage<'_, R>,
929 ) -> (
930 usize,
931 usize,
932 Option<usize>,
933 usize,
934 usize,
935 usize,
936 usize,
937 usize,
938 usize,
939 ) {
940 (
941 submessage.0.index,
942 submessage.1.index,
943 submessage.2.map(|s| s.index),
944 submessage.3.index,
945 submessage.4.index,
946 submessage.5.index,
947 submessage.6.index,
948 submessage.7.index,
949 submessage.8.index,
950 )
951 }
952}