Skip to main content

grib/
encoder.rs

1//! GRIB2 data encoder.
2//!
3//! # The complexity of GRIB2
4//!
5//! GRIB2 is a data format specifically designed to store grid-point values
6//! related to meteorological data. Therefore, it is easy to imagine that, in
7//! addition to the values at each grid point, information such as the latitude
8//! and longitude of each point, the date and time, and meteorological elements
9//! must also be included as metadata. Furthermore, even for grid-point values
10//! represented as arrays of real numbers, GRIB2 generally does not store
11//! floating-point values as-is. Instead, the values are rounded based on their
12//! precision and then compressed and stored as an array of integers--that is,
13//! discrete numerical values.
14//!
15//! Consequently, unlike highly versatile formats such as NetCDF and HDF, which
16//! allow for the flexible serialization of large amounts of numerical data, or
17//! Gzip and Xz, which can freely compress arbitrary data, GRIB2 is a format
18//! with specific constraints. On the other hand, GRIB2 is a format whose
19//! specifications prevent situations where "a value has been obtained but its
20//! meaning is unclear." It can also be described as a format capable of
21//! "representing" meteorological data by incorporating not only raw numerical
22//! values but also the complexities of the real world, such as missing values
23//! and precision information.
24//!
25//! # Goal of this module
26//!
27//! The goal of this module is to enable users to create GRIB2 data--which
28//! has many parameters and some configurable options within its
29//! constraints--using an API that is as user-friendly as possible (although
30//! some parts are currently not user-friendly).
31//!
32//! Internally, a single GRIB2 dataset (message) is composed of a collection of
33//! blocks called "sections." However, this module's high-level API avoids using
34//! the term "section" and instead focuses on "what information needs to be
35//! configured" and "how you want to represent the meteorological data."
36//!
37//! # High-level and low-level APIs
38//!
39//! This module provides 3 structs: [`SingleGrib2Message`],
40//! [`MultiGridGrib2Message`], and [`MultiProductGrib2Message`]. By providing
41//! all the necessary information to these structs (which may be a bit of a
42//! challenge), you can create GRIB2 messages. These structs constitute a
43//! high-level API designed to accommodate the most common use cases.
44//! If your use case does not fit these scenarios, you will need to implement
45//! the low-level API [`WriteGrib2Message`] and its sub-traits:
46//! [`WriteGrib2MessageIterL1`], [`WriteGrib2MessageIterL2`], and
47//! [`WriteGrib2MessageIterL3`].
48//!
49//! # Generating data sets comprising multiple elements
50//!
51//! It is rare to have only one type of grid point value data for a given time
52//! that you want to generate and provide. As shown below, in many cases, you
53//! will likely want to generate and provide multiple types of grid point value
54//! data together.
55//!
56//! - Different meteorological elements (temperature, pressure, humidity, wind
57//!   speed in 2 directions)
58//! - Different forecast times (1 hour later, 2 hours later, 3 hours later)
59//! - Different altitude levels (1000 hPa, 925 hPa, 850 hPa)
60//!
61//! We will refer to this type of data here as "data sets comprising multiple
62//! elements." The term "element" here is used in a general sense and is not
63//! limited to meteorological elements.
64//!
65//! When representing data sets comprising multiple elements in GRIB2, there are
66//! 2 main options, or 3 when broken down in detail:
67//!
68//! - A. Include them in a single message
69//! - B. Create separate messages (but store them in a single file)
70//! - C. Create separate messages (and store them in separate files)
71//!
72//! If you wish to use option A, use [`MultiGridGrib2Message`] or
73//! [`MultiProductGrib2Message`] (or the low-level API) to include the set of
74//! multiple elements in a single message. If you want to implement options B or
75//! C, simply use [`SingleGrib2Message`] for each element. You can implement
76//! option B by writing to the same output destination consecutively, and option
77//! C by writing to separate output destinations.
78
79use std::cell::{Ref, RefCell};
80
81pub use complex::*;
82pub use grid::*;
83pub use message::*;
84pub use simple::*;
85
86use crate::def::grib2::template::param_set;
87
88/// GPV data encoder.
89pub struct GpvEncoder<'d> {
90    data: std::borrow::Cow<'d, [f64]>,
91    method: EncodingMethod,
92    encoded: RefCell<Option<GpvEncodeOutput>>,
93}
94
95impl<'d> GpvEncoder<'d> {
96    pub fn new(data: std::borrow::Cow<'d, [f64]>, method: EncodingMethod) -> Self {
97        Self {
98            data,
99            method,
100            encoded: RefCell::new(None),
101        }
102    }
103
104    pub fn get_encoded(&'_ self) -> Ref<'_, GpvEncodeOutput> {
105        if self.encoded.borrow().is_none() {
106            *self.encoded.borrow_mut() = Some(self.encode());
107        }
108
109        Ref::map(self.encoded.borrow(), |c| c.as_ref().unwrap())
110    }
111
112    fn encode(&self) -> GpvEncodeOutput {
113        let output = match &self.method {
114            EncodingMethod::SimplePacking(simple_packing_strategy) => {
115                let encoder = simple::Encoder::new(&self.data, simple_packing_strategy.clone());
116                GpvEncodeOutputInner::SimplePacking(encoder.encode())
117            }
118            EncodingMethod::ComplexPacking(
119                simple_packing_strategy,
120                complex_packing_strategy,
121                _spatial_differencing_option,
122            ) => {
123                let encoder = complex::Encoder::new(
124                    &self.data,
125                    simple_packing_strategy.clone(),
126                    complex_packing_strategy.clone(),
127                );
128                GpvEncodeOutputInner::ComplexPacking(encoder.encode())
129            }
130        };
131        GpvEncodeOutput(output)
132    }
133}
134
135impl<'d> WriteGrib2PointValues for GpvEncoder<'d> {
136    fn data_sections_len(&self) -> usize {
137        let encoded = self.get_encoded();
138        encoded.section5_len() + encoded.section6_len() + encoded.section7_len()
139    }
140
141    fn write_data_sections(&self, buf: &mut [u8]) -> Result<usize, &'static str> {
142        let encoded = self.get_encoded();
143        let mut pos = 0;
144        pos += encoded.write_section5(&mut buf[pos..])?;
145        pos += encoded.write_section6(&mut buf[pos..])?;
146        pos += encoded.write_section7(&mut buf[pos..])?;
147        Ok(pos)
148    }
149}
150
151#[derive(Debug, PartialEq, Eq, Clone)]
152#[non_exhaustive]
153pub enum EncodingMethod {
154    /// Simple packing.
155    SimplePacking(SimplePackingStrategy),
156    /// Complex packing.
157    ComplexPacking(
158        SimplePackingStrategy,
159        ComplexPackingStrategy,
160        SpatialDifferencingOption,
161    ),
162}
163
164/// Data obtained through GPV encoding. Instances are typically used to write
165/// GRIB2 data via the methods defined in [`WriteGrib2DataSections`].
166#[derive(Debug)]
167pub struct GpvEncodeOutput(GpvEncodeOutputInner);
168
169impl GpvEncodeOutput {
170    /// Returns the parameter set.
171    pub fn params(&self) -> GpvEncodeParams<'_> {
172        match &self.0 {
173            GpvEncodeOutputInner::SimplePacking(encoded) => {
174                GpvEncodeParams::SimplePacking(encoded.params())
175            }
176            GpvEncodeOutputInner::ComplexPacking(encoded) => {
177                let (simple, complex) = encoded.params();
178                GpvEncodeParams::ComplexPacking(simple, complex)
179            }
180        }
181    }
182}
183
184impl WriteGrib2DataSections for GpvEncodeOutput {
185    fn section5_len(&self) -> usize {
186        match &self.0 {
187            GpvEncodeOutputInner::SimplePacking(encoded) => encoded.section5_len(),
188            GpvEncodeOutputInner::ComplexPacking(encoded) => encoded.section5_len(),
189        }
190    }
191
192    fn write_section5(&self, buf: &mut [u8]) -> Result<usize, &'static str> {
193        match &self.0 {
194            GpvEncodeOutputInner::SimplePacking(encoded) => encoded.write_section5(buf),
195            GpvEncodeOutputInner::ComplexPacking(encoded) => encoded.write_section5(buf),
196        }
197    }
198
199    fn section6_len(&self) -> usize {
200        match &self.0 {
201            GpvEncodeOutputInner::SimplePacking(encoded) => encoded.section6_len(),
202            GpvEncodeOutputInner::ComplexPacking(encoded) => encoded.section6_len(),
203        }
204    }
205
206    fn write_section6(&self, buf: &mut [u8]) -> Result<usize, &'static str> {
207        match &self.0 {
208            GpvEncodeOutputInner::SimplePacking(encoded) => encoded.write_section6(buf),
209            GpvEncodeOutputInner::ComplexPacking(encoded) => encoded.write_section6(buf),
210        }
211    }
212
213    fn section7_len(&self) -> usize {
214        match &self.0 {
215            GpvEncodeOutputInner::SimplePacking(encoded) => encoded.section7_len(),
216            GpvEncodeOutputInner::ComplexPacking(encoded) => encoded.section7_len(),
217        }
218    }
219
220    fn write_section7(&self, buf: &mut [u8]) -> Result<usize, &'static str> {
221        match &self.0 {
222            GpvEncodeOutputInner::SimplePacking(encoded) => encoded.write_section7(buf),
223            GpvEncodeOutputInner::ComplexPacking(encoded) => encoded.write_section7(buf),
224        }
225    }
226}
227
228#[non_exhaustive]
229pub enum GpvEncodeParams<'a> {
230    SimplePacking(&'a param_set::SimplePacking),
231    ComplexPacking(&'a param_set::SimplePacking, &'a param_set::ComplexPacking),
232}
233
234#[derive(Debug)]
235enum GpvEncodeOutputInner {
236    SimplePacking(simple::Encoded),
237    ComplexPacking(complex::Encoded),
238}
239
240// Since the name `Encode` is already in use on other branches currently under
241// development, and since this trait is private, we'll continue to use that name
242// for the time being.
243trait Encode {
244    type Output;
245
246    fn encode(&self) -> Self::Output;
247}
248
249mod bitmap;
250mod complex;
251mod grid;
252mod helpers;
253mod message;
254mod simple;
255mod writer;