Skip to main content

grib_core/
data.rs

1//! Data Representation Section (Section 5) shared model.
2
3use crate::error::{Error, Result};
4use crate::util::grib_i16;
5
6/// Data representation template number and parameters.
7#[derive(Debug, Clone, PartialEq)]
8pub enum DataRepresentation {
9    /// Template 5.0: Simple packing.
10    SimplePacking(SimplePackingParams),
11    /// Template 5.2/5.3: Complex packing with optional spatial differencing.
12    ComplexPacking(ComplexPackingParams),
13    /// Unsupported template.
14    Unsupported(u16),
15}
16
17/// Parameters for simple packing (Template 5.0).
18#[derive(Debug, Clone, PartialEq)]
19pub struct SimplePackingParams {
20    pub encoded_values: usize,
21    pub reference_value: f32,
22    pub binary_scale: i16,
23    pub decimal_scale: i16,
24    pub bits_per_value: u8,
25    pub original_field_type: u8,
26}
27
28/// Parameters for complex packing (Templates 5.2 and 5.3).
29#[derive(Debug, Clone, PartialEq)]
30pub struct ComplexPackingParams {
31    pub encoded_values: usize,
32    pub reference_value: f32,
33    pub binary_scale: i16,
34    pub decimal_scale: i16,
35    pub group_reference_bits: u8,
36    pub original_field_type: u8,
37    pub group_splitting_method: u8,
38    pub missing_value_management: u8,
39    pub primary_missing_substitute: u32,
40    pub secondary_missing_substitute: u32,
41    pub num_groups: usize,
42    pub group_width_reference: u8,
43    pub group_width_bits: u8,
44    pub group_length_reference: u32,
45    pub group_length_increment: u8,
46    pub true_length_last_group: u32,
47    pub scaled_group_length_bits: u8,
48    pub spatial_differencing: Option<SpatialDifferencingParams>,
49}
50
51/// Parameters specific to template 5.3 spatial differencing.
52#[derive(Debug, Clone, Copy, PartialEq, Eq)]
53pub struct SpatialDifferencingParams {
54    pub order: u8,
55    pub descriptor_octets: u8,
56}
57
58impl DataRepresentation {
59    pub fn parse(section_bytes: &[u8]) -> Result<Self> {
60        if section_bytes.len() < 11 {
61            return Err(Error::InvalidSection {
62                section: 5,
63                reason: format!("expected at least 11 bytes, got {}", section_bytes.len()),
64            });
65        }
66        if section_bytes[4] != 5 {
67            return Err(Error::InvalidSection {
68                section: section_bytes[4],
69                reason: "not a data representation section".into(),
70            });
71        }
72
73        let template = u16::from_be_bytes(section_bytes[9..11].try_into().unwrap());
74        match template {
75            0 => parse_simple_packing(section_bytes),
76            2 => parse_complex_packing(section_bytes, false),
77            3 => parse_complex_packing(section_bytes, true),
78            _ => Ok(Self::Unsupported(template)),
79        }
80    }
81
82    pub fn encoded_values(&self) -> Option<usize> {
83        match self {
84            Self::SimplePacking(params) => Some(params.encoded_values),
85            Self::ComplexPacking(params) => Some(params.encoded_values),
86            Self::Unsupported(_) => None,
87        }
88    }
89}
90
91fn parse_simple_packing(data: &[u8]) -> Result<DataRepresentation> {
92    if data.len() < 21 {
93        return Err(Error::InvalidSection {
94            section: 5,
95            reason: format!("template 5.0 requires 21 bytes, got {}", data.len()),
96        });
97    }
98
99    let encoded_values = u32::from_be_bytes(data[5..9].try_into().unwrap()) as usize;
100    let reference_value = f32::from_be_bytes(data[11..15].try_into().unwrap());
101    let binary_scale = grib_i16(&data[15..17]).unwrap();
102    let decimal_scale = grib_i16(&data[17..19]).unwrap();
103    let bits_per_value = data[19];
104    let original_field_type = data[20];
105
106    Ok(DataRepresentation::SimplePacking(SimplePackingParams {
107        encoded_values,
108        reference_value,
109        binary_scale,
110        decimal_scale,
111        bits_per_value,
112        original_field_type,
113    }))
114}
115
116fn parse_complex_packing(
117    data: &[u8],
118    with_spatial_differencing: bool,
119) -> Result<DataRepresentation> {
120    let required = if with_spatial_differencing { 49 } else { 47 };
121    if data.len() < required {
122        return Err(Error::InvalidSection {
123            section: 5,
124            reason: format!(
125                "template 5.{} requires {required} bytes, got {}",
126                if with_spatial_differencing { 3 } else { 2 },
127                data.len()
128            ),
129        });
130    }
131
132    let group_splitting_method = data[21];
133    if group_splitting_method != 1 {
134        return Err(Error::UnsupportedGroupSplittingMethod(
135            group_splitting_method,
136        ));
137    }
138
139    let missing_value_management = data[22];
140    if missing_value_management > 2 {
141        return Err(Error::UnsupportedMissingValueManagement(
142            missing_value_management,
143        ));
144    }
145
146    let spatial_differencing = if with_spatial_differencing {
147        let order = data[47];
148        if !matches!(order, 1 | 2) {
149            return Err(Error::UnsupportedSpatialDifferencingOrder(order));
150        }
151        Some(SpatialDifferencingParams {
152            order,
153            descriptor_octets: data[48],
154        })
155    } else {
156        None
157    };
158
159    Ok(DataRepresentation::ComplexPacking(ComplexPackingParams {
160        encoded_values: u32::from_be_bytes(data[5..9].try_into().unwrap()) as usize,
161        reference_value: f32::from_be_bytes(data[11..15].try_into().unwrap()),
162        binary_scale: grib_i16(&data[15..17]).unwrap(),
163        decimal_scale: grib_i16(&data[17..19]).unwrap(),
164        group_reference_bits: data[19],
165        original_field_type: data[20],
166        group_splitting_method,
167        missing_value_management,
168        primary_missing_substitute: u32::from_be_bytes(data[23..27].try_into().unwrap()),
169        secondary_missing_substitute: u32::from_be_bytes(data[27..31].try_into().unwrap()),
170        num_groups: u32::from_be_bytes(data[31..35].try_into().unwrap()) as usize,
171        group_width_reference: data[35],
172        group_width_bits: data[36],
173        group_length_reference: u32::from_be_bytes(data[37..41].try_into().unwrap()),
174        group_length_increment: data[41],
175        true_length_last_group: u32::from_be_bytes(data[42..46].try_into().unwrap()),
176        scaled_group_length_bits: data[46],
177        spatial_differencing,
178    }))
179}
180
181#[cfg(test)]
182mod tests {
183    use super::{DataRepresentation, SimplePackingParams};
184
185    #[test]
186    fn parses_simple_packing_template() {
187        let mut section = vec![0u8; 21];
188        section[..4].copy_from_slice(&(21u32).to_be_bytes());
189        section[4] = 5;
190        section[5..9].copy_from_slice(&3u32.to_be_bytes());
191        section[9..11].copy_from_slice(&0u16.to_be_bytes());
192        section[11..15].copy_from_slice(&10.0f32.to_be_bytes());
193        section[15..17].copy_from_slice(&2i16.to_be_bytes());
194        section[17..19].copy_from_slice(&1i16.to_be_bytes());
195        section[19] = 8;
196        section[20] = 0;
197
198        assert_eq!(
199            DataRepresentation::parse(&section).unwrap(),
200            DataRepresentation::SimplePacking(SimplePackingParams {
201                encoded_values: 3,
202                reference_value: 10.0,
203                binary_scale: 2,
204                decimal_scale: 1,
205                bits_per_value: 8,
206                original_field_type: 0,
207            })
208        );
209    }
210}