1pub mod data;
34pub mod error;
35pub mod grib1;
36pub mod grid;
37pub mod indicator;
38pub mod metadata;
39pub mod parameter;
40pub mod product;
41pub mod sections;
42mod util;
43
44pub use error::{Error, Result};
45pub use metadata::{Parameter, ReferenceTime};
46pub use product::{FixedSurface, Identification, ProductDefinition};
47
48use std::path::Path;
49
50use memmap2::Mmap;
51use ndarray::{ArrayD, IxDyn};
52
53use crate::data::{bitmap_payload as grib2_bitmap_payload, decode_field, DataRepresentation};
54use crate::grib1::{BinaryDataSection, GridDescription};
55use crate::grid::GridDefinition;
56use crate::indicator::Indicator;
57use crate::sections::{index_fields, FieldSections, SectionRef};
58
59#[cfg(feature = "rayon")]
60use rayon::prelude::*;
61
62const GRIB_MAGIC: &[u8; 4] = b"GRIB";
63
64#[derive(Debug, Clone, Copy)]
66pub struct OpenOptions {
67 pub strict: bool,
71}
72
73impl Default for OpenOptions {
74 fn default() -> Self {
75 Self { strict: true }
76 }
77}
78
79#[derive(Debug, Clone)]
81pub struct MessageMetadata {
82 pub edition: u8,
83 pub center_id: u16,
84 pub subcenter_id: u16,
85 pub discipline: Option<u8>,
86 pub reference_time: ReferenceTime,
87 pub parameter: Parameter,
88 pub grid: GridDefinition,
89 pub data_representation: DataRepresentation,
90 pub forecast_time_unit: Option<u8>,
91 pub forecast_time: Option<u32>,
92 pub message_offset: u64,
93 pub message_length: u64,
94 pub field_index_in_message: usize,
95 grib1_product: Option<grib1::ProductDefinition>,
96 grib2_identification: Option<Identification>,
97 grib2_product: Option<ProductDefinition>,
98}
99
100pub struct GribFile {
102 data: GribData,
103 messages: Vec<MessageIndex>,
104}
105
106#[derive(Clone)]
107struct MessageIndex {
108 offset: usize,
109 length: usize,
110 metadata: MessageMetadata,
111 decode_plan: DecodePlan,
112}
113
114#[derive(Clone, Copy)]
115enum DecodePlan {
116 Grib1 {
117 bitmap: Option<SectionRef>,
118 data: SectionRef,
119 },
120 Grib2(FieldSections),
121}
122
123enum GribData {
124 Mmap(Mmap),
125 Bytes(Vec<u8>),
126}
127
128impl GribData {
129 fn as_bytes(&self) -> &[u8] {
130 match self {
131 GribData::Mmap(m) => m,
132 GribData::Bytes(b) => b,
133 }
134 }
135}
136
137impl GribFile {
138 pub fn open<P: AsRef<Path>>(path: P) -> Result<Self> {
140 Self::open_with_options(path, OpenOptions::default())
141 }
142
143 pub fn open_with_options<P: AsRef<Path>>(path: P, options: OpenOptions) -> Result<Self> {
145 let file = std::fs::File::open(path.as_ref())
146 .map_err(|e| Error::Io(e, path.as_ref().display().to_string()))?;
147 let mmap = unsafe { Mmap::map(&file) }
148 .map_err(|e| Error::Io(e, path.as_ref().display().to_string()))?;
149 Self::from_data(GribData::Mmap(mmap), options)
150 }
151
152 pub fn from_bytes(data: Vec<u8>) -> Result<Self> {
154 Self::from_bytes_with_options(data, OpenOptions::default())
155 }
156
157 pub fn from_bytes_with_options(data: Vec<u8>, options: OpenOptions) -> Result<Self> {
159 Self::from_data(GribData::Bytes(data), options)
160 }
161
162 fn from_data(data: GribData, options: OpenOptions) -> Result<Self> {
163 let messages = scan_messages(data.as_bytes(), options)?;
164 if messages.is_empty() {
165 return Err(Error::NoMessages);
166 }
167 Ok(Self { data, messages })
168 }
169
170 pub fn edition(&self) -> u8 {
172 self.messages
173 .first()
174 .map(|message| message.metadata.edition)
175 .unwrap_or(0)
176 }
177
178 pub fn message_count(&self) -> usize {
180 self.messages.len()
181 }
182
183 pub fn message(&self, index: usize) -> Result<Message<'_>> {
185 let record = self
186 .messages
187 .get(index)
188 .ok_or(Error::MessageNotFound(index))?;
189 let bytes = &self.data.as_bytes()[record.offset..record.offset + record.length];
190 Ok(Message {
191 index,
192 bytes,
193 metadata: &record.metadata,
194 decode_plan: record.decode_plan,
195 })
196 }
197
198 pub fn messages(&self) -> MessageIter<'_> {
200 MessageIter {
201 file: self,
202 index: 0,
203 }
204 }
205
206 pub fn read_all_data_as_f64(&self) -> Result<Vec<ArrayD<f64>>> {
208 #[cfg(feature = "rayon")]
209 {
210 (0..self.message_count())
211 .into_par_iter()
212 .map(|index| self.message(index)?.read_data_as_f64())
213 .collect()
214 }
215 #[cfg(not(feature = "rayon"))]
216 {
217 (0..self.message_count())
218 .map(|index| self.message(index)?.read_data_as_f64())
219 .collect()
220 }
221 }
222}
223
224pub struct Message<'a> {
226 bytes: &'a [u8],
227 metadata: &'a MessageMetadata,
228 decode_plan: DecodePlan,
229 index: usize,
230}
231
232impl<'a> Message<'a> {
233 pub fn edition(&self) -> u8 {
234 self.metadata.edition
235 }
236
237 pub fn index(&self) -> usize {
238 self.index
239 }
240
241 pub fn metadata(&self) -> &MessageMetadata {
242 self.metadata
243 }
244
245 pub fn reference_time(&self) -> &ReferenceTime {
246 &self.metadata.reference_time
247 }
248
249 pub fn parameter(&self) -> &Parameter {
250 &self.metadata.parameter
251 }
252
253 pub fn center_id(&self) -> u16 {
254 self.metadata.center_id
255 }
256
257 pub fn subcenter_id(&self) -> u16 {
258 self.metadata.subcenter_id
259 }
260
261 pub fn identification(&self) -> Option<&Identification> {
262 self.metadata.grib2_identification.as_ref()
263 }
264
265 pub fn product_definition(&self) -> Option<&ProductDefinition> {
266 self.metadata.grib2_product.as_ref()
267 }
268
269 pub fn grib1_product_definition(&self) -> Option<&grib1::ProductDefinition> {
270 self.metadata.grib1_product.as_ref()
271 }
272
273 pub fn grid_definition(&self) -> &GridDefinition {
274 &self.metadata.grid
275 }
276
277 pub fn parameter_name(&self) -> &'static str {
278 self.metadata.parameter.short_name
279 }
280
281 pub fn parameter_description(&self) -> &'static str {
282 self.metadata.parameter.description
283 }
284
285 pub fn grid_shape(&self) -> (usize, usize) {
286 self.metadata.grid.shape()
287 }
288
289 pub fn latitudes(&self) -> Option<Vec<f64>> {
290 match &self.metadata.grid {
291 GridDefinition::LatLon(grid) => Some(grid.latitudes()),
292 GridDefinition::Unsupported(_) => None,
293 }
294 }
295
296 pub fn longitudes(&self) -> Option<Vec<f64>> {
297 match &self.metadata.grid {
298 GridDefinition::LatLon(grid) => Some(grid.longitudes()),
299 GridDefinition::Unsupported(_) => None,
300 }
301 }
302
303 pub fn read_data_as_f64(&self) -> Result<ArrayD<f64>> {
304 let grid = match &self.metadata.grid {
305 GridDefinition::LatLon(grid) => grid,
306 GridDefinition::Unsupported(template) => {
307 return Err(Error::UnsupportedGridTemplate(*template));
308 }
309 };
310
311 let decoded = match self.decode_plan {
312 DecodePlan::Grib2(field) => {
313 let data_section = section_bytes(self.bytes, field.data);
314 let bitmap_section = match field.bitmap {
315 Some(section) => grib2_bitmap_payload(section_bytes(self.bytes, section))?,
316 None => None,
317 };
318 decode_field(
319 data_section,
320 &self.metadata.data_representation,
321 bitmap_section,
322 self.metadata.grid.num_points(),
323 )?
324 }
325 DecodePlan::Grib1 { bitmap, data } => {
326 let bitmap_section = match bitmap {
327 Some(section) => grib1::bitmap_payload(section_bytes(self.bytes, section))?,
328 None => None,
329 };
330 let data_section = section_bytes(self.bytes, data);
331 if data_section.len() < 11 {
332 return Err(Error::InvalidSection {
333 section: 4,
334 reason: format!("expected at least 11 bytes, got {}", data_section.len()),
335 });
336 }
337 grib1::decode_simple_field(
338 &data_section[11..],
339 &self.metadata.data_representation,
340 bitmap_section,
341 self.metadata.grid.num_points(),
342 )?
343 }
344 };
345
346 let ordered = grid.reorder_for_ndarray(decoded)?;
347 ArrayD::from_shape_vec(IxDyn(&self.metadata.grid.ndarray_shape()), ordered)
348 .map_err(|e| Error::Other(format!("failed to build ndarray from decoded field: {e}")))
349 }
350
351 pub fn raw_bytes(&self) -> &[u8] {
352 self.bytes
353 }
354}
355
356pub struct MessageIter<'a> {
358 file: &'a GribFile,
359 index: usize,
360}
361
362impl<'a> Iterator for MessageIter<'a> {
363 type Item = Message<'a>;
364
365 fn next(&mut self) -> Option<Self::Item> {
366 if self.index >= self.file.message_count() {
367 return None;
368 }
369 let message = self.file.message(self.index).ok()?;
370 self.index += 1;
371 Some(message)
372 }
373}
374
375fn section_bytes(msg_bytes: &[u8], section: SectionRef) -> &[u8] {
376 &msg_bytes[section.offset..section.offset + section.length]
377}
378
379fn scan_messages(data: &[u8], options: OpenOptions) -> Result<Vec<MessageIndex>> {
380 let mut messages = Vec::new();
381 let mut pos = 0usize;
382
383 while pos + 8 <= data.len() {
384 if &data[pos..pos + 4] != GRIB_MAGIC {
385 pos += 1;
386 continue;
387 }
388
389 match try_index_message(data, pos) {
390 Ok((indexed, next_pos)) => {
391 messages.extend(indexed);
392 pos = next_pos;
393 }
394 Err(err) if !options.strict => {
395 pos += 4;
396 let _ = err;
397 }
398 Err(err) => return Err(err),
399 }
400 }
401
402 Ok(messages)
403}
404
405fn try_index_message(data: &[u8], pos: usize) -> Result<(Vec<MessageIndex>, usize)> {
406 let indicator = Indicator::parse(&data[pos..]).ok_or_else(|| {
407 Error::InvalidMessage(format!("failed to parse indicator at byte offset {pos}"))
408 })?;
409 let length = indicator.total_length as usize;
410 if length < 12 {
411 return Err(Error::InvalidMessage(format!(
412 "message at byte offset {pos} reports impossible length {length}"
413 )));
414 }
415 let end = pos
416 .checked_add(length)
417 .ok_or_else(|| Error::InvalidMessage("message length overflow".into()))?;
418 if end > data.len() {
419 return Err(Error::Truncated { offset: end as u64 });
420 }
421 if &data[end - 4..end] != b"7777" {
422 return Err(Error::InvalidMessage(format!(
423 "message at byte offset {pos} does not end with 7777"
424 )));
425 }
426
427 let message_bytes = &data[pos..end];
428 let indexed = match indicator.edition {
429 1 => index_grib1_message(message_bytes, pos)?,
430 2 => index_grib2_message(message_bytes, pos, &indicator)?,
431 other => return Err(Error::UnsupportedEdition(other)),
432 };
433
434 Ok((indexed, end))
435}
436
437fn index_grib1_message(message_bytes: &[u8], offset: usize) -> Result<Vec<MessageIndex>> {
438 let sections = grib1::parse_message_sections(message_bytes)?;
439 let grid_ref = sections.grid.ok_or_else(|| {
440 Error::InvalidSectionOrder(
441 "GRIB1 decoding requires an explicit grid definition section".into(),
442 )
443 })?;
444 let grid_description = GridDescription::parse(section_bytes(message_bytes, grid_ref))?;
445 let grid = grid_description.grid;
446
447 let bitmap_present_count = match sections.bitmap {
448 Some(bitmap) => count_bitmap_present_points(
449 grib1::bitmap_payload(section_bytes(message_bytes, bitmap))?,
450 grid.num_points(),
451 )?,
452 None => grid.num_points(),
453 };
454
455 let (_bds, data_representation) = BinaryDataSection::parse(
456 section_bytes(message_bytes, sections.data),
457 sections.product.decimal_scale,
458 bitmap_present_count,
459 )?;
460 let parameter = sections.product.parameter();
461
462 Ok(vec![MessageIndex {
463 offset,
464 length: message_bytes.len(),
465 decode_plan: DecodePlan::Grib1 {
466 bitmap: sections.bitmap,
467 data: sections.data,
468 },
469 metadata: MessageMetadata {
470 edition: 1,
471 center_id: sections.product.center_id as u16,
472 subcenter_id: sections.product.subcenter_id as u16,
473 discipline: None,
474 reference_time: sections.product.reference_time,
475 parameter,
476 grid,
477 data_representation,
478 forecast_time_unit: Some(sections.product.forecast_time_unit),
479 forecast_time: sections.product.forecast_time(),
480 message_offset: offset as u64,
481 message_length: message_bytes.len() as u64,
482 field_index_in_message: 0,
483 grib1_product: Some(sections.product),
484 grib2_identification: None,
485 grib2_product: None,
486 },
487 }])
488}
489
490fn index_grib2_message(
491 message_bytes: &[u8],
492 offset: usize,
493 indicator: &Indicator,
494) -> Result<Vec<MessageIndex>> {
495 let fields = index_fields(message_bytes)?;
496 let mut messages = Vec::with_capacity(fields.len());
497
498 for (field_index_in_message, field_sections) in fields.into_iter().enumerate() {
499 let identification =
500 Identification::parse(section_bytes(message_bytes, field_sections.identification))?;
501 let grid = GridDefinition::parse(section_bytes(message_bytes, field_sections.grid))?;
502 let product =
503 ProductDefinition::parse(section_bytes(message_bytes, field_sections.product))?;
504 let data_representation = DataRepresentation::parse(section_bytes(
505 message_bytes,
506 field_sections.data_representation,
507 ))?;
508 let parameter = Parameter::new_grib2(
509 indicator.discipline,
510 product.parameter_category,
511 product.parameter_number,
512 product.parameter_name(indicator.discipline),
513 product.parameter_description(indicator.discipline),
514 );
515
516 messages.push(MessageIndex {
517 offset,
518 length: message_bytes.len(),
519 decode_plan: DecodePlan::Grib2(field_sections),
520 metadata: MessageMetadata {
521 edition: indicator.edition,
522 center_id: identification.center_id,
523 subcenter_id: identification.subcenter_id,
524 discipline: Some(indicator.discipline),
525 reference_time: ReferenceTime {
526 year: identification.reference_year,
527 month: identification.reference_month,
528 day: identification.reference_day,
529 hour: identification.reference_hour,
530 minute: identification.reference_minute,
531 second: identification.reference_second,
532 },
533 parameter,
534 grid,
535 data_representation,
536 forecast_time_unit: product.forecast_time_unit,
537 forecast_time: product.forecast_time,
538 message_offset: offset as u64,
539 message_length: message_bytes.len() as u64,
540 field_index_in_message,
541 grib1_product: None,
542 grib2_identification: Some(identification),
543 grib2_product: Some(product),
544 },
545 });
546 }
547
548 Ok(messages)
549}
550
551fn count_bitmap_present_points(bitmap: Option<&[u8]>, num_grid_points: usize) -> Result<usize> {
552 let Some(payload) = bitmap else {
553 return Ok(0);
554 };
555
556 let mut present = 0usize;
557 for bit_index in 0..num_grid_points {
558 let byte = payload
559 .get(bit_index / 8)
560 .copied()
561 .ok_or(Error::MissingBitmap)?;
562 if ((byte >> (7 - (bit_index % 8))) & 1) != 0 {
563 present += 1;
564 }
565 }
566
567 Ok(present)
568}
569
570#[cfg(test)]
571mod tests {
572 use super::*;
573
574 fn grib_i32_bytes(value: i32) -> [u8; 4] {
575 if value >= 0 {
576 (value as u32).to_be_bytes()
577 } else {
578 ((-value) as u32 | 0x8000_0000).to_be_bytes()
579 }
580 }
581
582 fn build_indicator(total_len: usize, discipline: u8) -> Vec<u8> {
583 let mut indicator = Vec::with_capacity(16);
584 indicator.extend_from_slice(b"GRIB");
585 indicator.extend_from_slice(&[0, 0]);
586 indicator.push(discipline);
587 indicator.push(2);
588 indicator.extend_from_slice(&(total_len as u64).to_be_bytes());
589 indicator
590 }
591
592 fn build_identification() -> Vec<u8> {
593 let mut section = vec![0u8; 21];
594 section[..4].copy_from_slice(&(21u32).to_be_bytes());
595 section[4] = 1;
596 section[5..7].copy_from_slice(&7u16.to_be_bytes());
597 section[7..9].copy_from_slice(&0u16.to_be_bytes());
598 section[9] = 35;
599 section[10] = 1;
600 section[11] = 1;
601 section[12..14].copy_from_slice(&2026u16.to_be_bytes());
602 section[14] = 3;
603 section[15] = 20;
604 section[16] = 12;
605 section[17] = 0;
606 section[18] = 0;
607 section[19] = 0;
608 section[20] = 1;
609 section
610 }
611
612 fn build_grid(ni: u32, nj: u32, scanning_mode: u8) -> Vec<u8> {
613 let mut section = vec![0u8; 72];
614 section[..4].copy_from_slice(&(72u32).to_be_bytes());
615 section[4] = 3;
616 section[6..10].copy_from_slice(&(ni * nj).to_be_bytes());
617 section[12..14].copy_from_slice(&0u16.to_be_bytes());
618 section[30..34].copy_from_slice(&ni.to_be_bytes());
619 section[34..38].copy_from_slice(&nj.to_be_bytes());
620 section[46..50].copy_from_slice(&grib_i32_bytes(50_000_000));
621 section[50..54].copy_from_slice(&grib_i32_bytes(-120_000_000));
622 section[55..59].copy_from_slice(&grib_i32_bytes(49_000_000));
623 section[59..63].copy_from_slice(&grib_i32_bytes(-119_000_000));
624 section[63..67].copy_from_slice(&1_000_000u32.to_be_bytes());
625 section[67..71].copy_from_slice(&1_000_000u32.to_be_bytes());
626 section[71] = scanning_mode;
627 section
628 }
629
630 fn build_product(parameter_category: u8, parameter_number: u8) -> Vec<u8> {
631 let mut section = vec![0u8; 34];
632 section[..4].copy_from_slice(&(34u32).to_be_bytes());
633 section[4] = 4;
634 section[7..9].copy_from_slice(&0u16.to_be_bytes());
635 section[9] = parameter_category;
636 section[10] = parameter_number;
637 section[11] = 2;
638 section[17] = 1;
639 section[18..22].copy_from_slice(&0u32.to_be_bytes());
640 section[22] = 103;
641 section[23] = 0;
642 section[24..28].copy_from_slice(&850u32.to_be_bytes());
643 section[28] = 255;
644 section
645 }
646
647 fn build_simple_representation(encoded_values: usize, bits_per_value: u8) -> Vec<u8> {
648 let mut section = vec![0u8; 21];
649 section[..4].copy_from_slice(&(21u32).to_be_bytes());
650 section[4] = 5;
651 section[5..9].copy_from_slice(&(encoded_values as u32).to_be_bytes());
652 section[9..11].copy_from_slice(&0u16.to_be_bytes());
653 section[11..15].copy_from_slice(&0f32.to_be_bytes());
654 section[19] = bits_per_value;
655 section[20] = 0;
656 section
657 }
658
659 fn pack_u8_values(values: &[u8]) -> Vec<u8> {
660 values.to_vec()
661 }
662
663 fn build_bitmap(bits: &[bool]) -> Vec<u8> {
664 let payload_len = bits.len().div_ceil(8) + 1;
665 let mut section = vec![0u8; payload_len + 5];
666 section[..4].copy_from_slice(&((payload_len + 5) as u32).to_be_bytes());
667 section[4] = 6;
668 section[5] = 0;
669 for (index, bit) in bits.iter().copied().enumerate() {
670 if bit {
671 section[6 + index / 8] |= 1 << (7 - (index % 8));
672 }
673 }
674 section
675 }
676
677 fn build_data(payload: &[u8]) -> Vec<u8> {
678 let mut section = vec![0u8; payload.len() + 5];
679 section[..4].copy_from_slice(&((payload.len() + 5) as u32).to_be_bytes());
680 section[4] = 7;
681 section[5..].copy_from_slice(payload);
682 section
683 }
684
685 fn assemble_grib2_message(sections: &[Vec<u8>]) -> Vec<u8> {
686 let total_len = 16 + sections.iter().map(|section| section.len()).sum::<usize>() + 4;
687 let mut message = build_indicator(total_len, 0);
688 for section in sections {
689 message.extend_from_slice(section);
690 }
691 message.extend_from_slice(b"7777");
692 message
693 }
694
695 fn build_grib1_message(values: &[u8]) -> Vec<u8> {
696 let mut pds = vec![0u8; 28];
697 pds[..3].copy_from_slice(&[0, 0, 28]);
698 pds[3] = 2;
699 pds[4] = 7;
700 pds[5] = 255;
701 pds[6] = 0;
702 pds[7] = 0b1000_0000;
703 pds[8] = 11;
704 pds[9] = 100;
705 pds[10..12].copy_from_slice(&850u16.to_be_bytes());
706 pds[12] = 26;
707 pds[13] = 3;
708 pds[14] = 20;
709 pds[15] = 12;
710 pds[16] = 0;
711 pds[17] = 1;
712 pds[18] = 0;
713 pds[19] = 0;
714 pds[20] = 0;
715 pds[24] = 21;
716 pds[25] = 0;
717
718 let mut gds = vec![0u8; 32];
719 gds[..3].copy_from_slice(&[0, 0, 32]);
720 gds[5] = 0;
721 gds[6..8].copy_from_slice(&2u16.to_be_bytes());
722 gds[8..10].copy_from_slice(&2u16.to_be_bytes());
723 gds[10..13].copy_from_slice(&[0x01, 0x4d, 0x50]);
724 gds[13..16].copy_from_slice(&[0x81, 0xd4, 0xc0]);
725 gds[16] = 0x80;
726 gds[17..20].copy_from_slice(&[0x01, 0x49, 0x68]);
727 gds[20..23].copy_from_slice(&[0x81, 0xd0, 0xd8]);
728 gds[23..25].copy_from_slice(&1000u16.to_be_bytes());
729 gds[25..27].copy_from_slice(&1000u16.to_be_bytes());
730
731 let mut bds = vec![0u8; 11 + values.len()];
732 let len = bds.len() as u32;
733 bds[..3].copy_from_slice(&[(len >> 16) as u8, (len >> 8) as u8, len as u8]);
734 bds[3] = 0;
735 bds[10] = 8;
736 bds[11..].copy_from_slice(values);
737
738 let total_len = 8 + pds.len() + gds.len() + bds.len() + 4;
739 let mut message = Vec::new();
740 message.extend_from_slice(b"GRIB");
741 message.extend_from_slice(&[
742 ((total_len >> 16) & 0xff) as u8,
743 ((total_len >> 8) & 0xff) as u8,
744 (total_len & 0xff) as u8,
745 1,
746 ]);
747 message.extend_from_slice(&pds);
748 message.extend_from_slice(&gds);
749 message.extend_from_slice(&bds);
750 message.extend_from_slice(b"7777");
751 message
752 }
753
754 #[test]
755 fn scans_single_grib2_message() {
756 let message = assemble_grib2_message(&[
757 build_identification(),
758 build_grid(2, 2, 0),
759 build_product(0, 0),
760 build_simple_representation(4, 8),
761 build_data(&pack_u8_values(&[1, 2, 3, 4])),
762 ]);
763 let messages = scan_messages(&message, OpenOptions::default()).unwrap();
764 assert_eq!(messages.len(), 1);
765 assert_eq!(messages[0].metadata.parameter.short_name, "TMP");
766 }
767
768 #[test]
769 fn decodes_simple_grib2_message_to_ndarray() {
770 let message = assemble_grib2_message(&[
771 build_identification(),
772 build_grid(2, 2, 0),
773 build_product(0, 0),
774 build_simple_representation(4, 8),
775 build_data(&pack_u8_values(&[1, 2, 3, 4])),
776 ]);
777 let file = GribFile::from_bytes(message).unwrap();
778 let array = file.message(0).unwrap().read_data_as_f64().unwrap();
779 assert_eq!(array.shape(), &[2, 2]);
780 assert_eq!(
781 array.iter().copied().collect::<Vec<_>>(),
782 vec![1.0, 2.0, 3.0, 4.0]
783 );
784 }
785
786 #[test]
787 fn applies_bitmap_to_missing_values() {
788 let message = assemble_grib2_message(&[
789 build_identification(),
790 build_grid(2, 2, 0),
791 build_product(0, 1),
792 build_simple_representation(3, 8),
793 build_bitmap(&[true, false, true, true]),
794 build_data(&pack_u8_values(&[10, 20, 30])),
795 ]);
796 let file = GribFile::from_bytes(message).unwrap();
797 let array = file.message(0).unwrap().read_data_as_f64().unwrap();
798 let values = array.iter().copied().collect::<Vec<_>>();
799 assert_eq!(values[0], 10.0);
800 assert!(values[1].is_nan());
801 assert_eq!(values[2], 20.0);
802 assert_eq!(values[3], 30.0);
803 }
804
805 #[test]
806 fn indexes_multiple_fields_in_one_grib2_message() {
807 let message = assemble_grib2_message(&[
808 build_identification(),
809 build_grid(2, 2, 0),
810 build_product(0, 0),
811 build_simple_representation(4, 8),
812 build_data(&pack_u8_values(&[1, 2, 3, 4])),
813 build_product(0, 2),
814 build_simple_representation(4, 8),
815 build_data(&pack_u8_values(&[5, 6, 7, 8])),
816 ]);
817 let file = GribFile::from_bytes(message).unwrap();
818 assert_eq!(file.message_count(), 2);
819 assert_eq!(file.message(0).unwrap().parameter_name(), "TMP");
820 assert_eq!(file.message(1).unwrap().parameter_name(), "POT");
821 }
822
823 #[test]
824 fn decodes_simple_grib1_message_to_ndarray() {
825 let message = build_grib1_message(&[1, 2, 3, 4]);
826 let file = GribFile::from_bytes(message).unwrap();
827 assert_eq!(file.edition(), 1);
828 let field = file.message(0).unwrap();
829 assert_eq!(field.parameter_name(), "TMP");
830 assert!(field.identification().is_none());
831 assert!(field.grib1_product_definition().is_some());
832 let array = field.read_data_as_f64().unwrap();
833 assert_eq!(array.shape(), &[2, 2]);
834 assert_eq!(
835 array.iter().copied().collect::<Vec<_>>(),
836 vec![1.0, 2.0, 3.0, 4.0]
837 );
838 }
839}