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