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