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