1use crate::error::{Error, Result};
4use crate::util::grib_i16;
5
6#[derive(Debug, Clone, PartialEq)]
8pub enum DataRepresentation {
9 SimplePacking(SimplePackingParams),
11 ComplexPacking(ComplexPackingParams),
13 Jpeg2000Packing(Jpeg2000PackingParams),
15 PngPacking(PngPackingParams),
17 Unsupported(u16),
19}
20
21#[derive(Debug, Clone, PartialEq)]
23pub struct SimplePackingParams {
24 pub encoded_values: usize,
25 pub reference_value: f32,
26 pub binary_scale: i16,
27 pub decimal_scale: i16,
28 pub bits_per_value: u8,
29 pub original_field_type: u8,
30}
31
32#[derive(Debug, Clone, PartialEq)]
34pub struct ImagePackingParams {
35 pub encoded_values: usize,
36 pub reference_value: f32,
37 pub binary_scale: i16,
38 pub decimal_scale: i16,
39 pub bits_per_value: u8,
40 pub original_field_type: u8,
41}
42
43#[derive(Debug, Clone, PartialEq)]
45pub struct Jpeg2000PackingParams {
46 pub packing: ImagePackingParams,
47 pub compression_type: u8,
48 pub target_compression_ratio: u8,
49}
50
51#[derive(Debug, Clone, PartialEq)]
53pub struct PngPackingParams {
54 pub packing: ImagePackingParams,
55}
56
57#[derive(Debug, Clone, PartialEq)]
59pub struct ComplexPackingParams {
60 pub encoded_values: usize,
61 pub reference_value: f32,
62 pub binary_scale: i16,
63 pub decimal_scale: i16,
64 pub group_reference_bits: u8,
65 pub original_field_type: u8,
66 pub group_splitting_method: u8,
67 pub missing_value_management: u8,
68 pub primary_missing_substitute: u32,
69 pub secondary_missing_substitute: u32,
70 pub num_groups: usize,
71 pub group_width_reference: u8,
72 pub group_width_bits: u8,
73 pub group_length_reference: u32,
74 pub group_length_increment: u8,
75 pub true_length_last_group: u32,
76 pub scaled_group_length_bits: u8,
77 pub spatial_differencing: Option<SpatialDifferencingParams>,
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
82pub struct SpatialDifferencingParams {
83 pub order: u8,
84 pub descriptor_octets: u8,
85}
86
87impl DataRepresentation {
88 pub fn parse(section_bytes: &[u8]) -> Result<Self> {
89 if section_bytes.len() < 11 {
90 return Err(Error::InvalidSection {
91 section: 5,
92 reason: format!("expected at least 11 bytes, got {}", section_bytes.len()),
93 });
94 }
95 if section_bytes[4] != 5 {
96 return Err(Error::InvalidSection {
97 section: section_bytes[4],
98 reason: "not a data representation section".into(),
99 });
100 }
101
102 let template = u16::from_be_bytes(section_bytes[9..11].try_into().unwrap());
103 match template {
104 0 => parse_simple_packing(section_bytes),
105 2 => parse_complex_packing(section_bytes, false),
106 3 => parse_complex_packing(section_bytes, true),
107 40 => parse_jpeg2000_packing(section_bytes),
108 41 => parse_png_packing(section_bytes),
109 _ => Ok(Self::Unsupported(template)),
110 }
111 }
112
113 pub fn encoded_values(&self) -> Option<usize> {
114 match self {
115 Self::SimplePacking(params) => Some(params.encoded_values),
116 Self::ComplexPacking(params) => Some(params.encoded_values),
117 Self::Jpeg2000Packing(params) => Some(params.packing.encoded_values),
118 Self::PngPacking(params) => Some(params.packing.encoded_values),
119 Self::Unsupported(_) => None,
120 }
121 }
122}
123
124fn parse_simple_packing(data: &[u8]) -> Result<DataRepresentation> {
125 if data.len() < 21 {
126 return Err(Error::InvalidSection {
127 section: 5,
128 reason: format!("template 5.0 requires 21 bytes, got {}", data.len()),
129 });
130 }
131
132 let encoded_values = u32::from_be_bytes(data[5..9].try_into().unwrap()) as usize;
133 let reference_value = f32::from_be_bytes(data[11..15].try_into().unwrap());
134 let binary_scale = grib_i16(&data[15..17]).unwrap();
135 let decimal_scale = grib_i16(&data[17..19]).unwrap();
136 let bits_per_value = data[19];
137 let original_field_type = data[20];
138
139 Ok(DataRepresentation::SimplePacking(SimplePackingParams {
140 encoded_values,
141 reference_value,
142 binary_scale,
143 decimal_scale,
144 bits_per_value,
145 original_field_type,
146 }))
147}
148
149fn parse_image_packing_base(
150 data: &[u8],
151 template: u16,
152 required: usize,
153) -> Result<ImagePackingParams> {
154 if data.len() < required {
155 return Err(Error::InvalidSection {
156 section: 5,
157 reason: format!(
158 "template 5.{template} requires {required} bytes, got {}",
159 data.len()
160 ),
161 });
162 }
163
164 Ok(ImagePackingParams {
165 encoded_values: u32::from_be_bytes(data[5..9].try_into().unwrap()) as usize,
166 reference_value: f32::from_be_bytes(data[11..15].try_into().unwrap()),
167 binary_scale: grib_i16(&data[15..17]).unwrap(),
168 decimal_scale: grib_i16(&data[17..19]).unwrap(),
169 bits_per_value: data[19],
170 original_field_type: data[20],
171 })
172}
173
174fn parse_jpeg2000_packing(data: &[u8]) -> Result<DataRepresentation> {
175 Ok(DataRepresentation::Jpeg2000Packing(Jpeg2000PackingParams {
176 packing: parse_image_packing_base(data, 40, 23)?,
177 compression_type: data[21],
178 target_compression_ratio: data[22],
179 }))
180}
181
182fn parse_png_packing(data: &[u8]) -> Result<DataRepresentation> {
183 Ok(DataRepresentation::PngPacking(PngPackingParams {
184 packing: parse_image_packing_base(data, 41, 21)?,
185 }))
186}
187
188fn parse_complex_packing(
189 data: &[u8],
190 with_spatial_differencing: bool,
191) -> Result<DataRepresentation> {
192 let required = if with_spatial_differencing { 49 } else { 47 };
193 if data.len() < required {
194 return Err(Error::InvalidSection {
195 section: 5,
196 reason: format!(
197 "template 5.{} requires {required} bytes, got {}",
198 if with_spatial_differencing { 3 } else { 2 },
199 data.len()
200 ),
201 });
202 }
203
204 let group_splitting_method = data[21];
205 if group_splitting_method != 1 {
206 return Err(Error::UnsupportedGroupSplittingMethod(
207 group_splitting_method,
208 ));
209 }
210
211 let missing_value_management = data[22];
212 if missing_value_management > 2 {
213 return Err(Error::UnsupportedMissingValueManagement(
214 missing_value_management,
215 ));
216 }
217
218 let spatial_differencing = if with_spatial_differencing {
219 let order = data[47];
220 if !matches!(order, 1 | 2) {
221 return Err(Error::UnsupportedSpatialDifferencingOrder(order));
222 }
223 Some(SpatialDifferencingParams {
224 order,
225 descriptor_octets: data[48],
226 })
227 } else {
228 None
229 };
230
231 Ok(DataRepresentation::ComplexPacking(ComplexPackingParams {
232 encoded_values: u32::from_be_bytes(data[5..9].try_into().unwrap()) as usize,
233 reference_value: f32::from_be_bytes(data[11..15].try_into().unwrap()),
234 binary_scale: grib_i16(&data[15..17]).unwrap(),
235 decimal_scale: grib_i16(&data[17..19]).unwrap(),
236 group_reference_bits: data[19],
237 original_field_type: data[20],
238 group_splitting_method,
239 missing_value_management,
240 primary_missing_substitute: u32::from_be_bytes(data[23..27].try_into().unwrap()),
241 secondary_missing_substitute: u32::from_be_bytes(data[27..31].try_into().unwrap()),
242 num_groups: u32::from_be_bytes(data[31..35].try_into().unwrap()) as usize,
243 group_width_reference: data[35],
244 group_width_bits: data[36],
245 group_length_reference: u32::from_be_bytes(data[37..41].try_into().unwrap()),
246 group_length_increment: data[41],
247 true_length_last_group: u32::from_be_bytes(data[42..46].try_into().unwrap()),
248 scaled_group_length_bits: data[46],
249 spatial_differencing,
250 }))
251}
252
253#[cfg(test)]
254mod tests {
255 use super::{
256 DataRepresentation, ImagePackingParams, Jpeg2000PackingParams, PngPackingParams,
257 SimplePackingParams,
258 };
259 use crate::util::encode_grib_i16;
260
261 #[test]
262 fn parses_simple_packing_template() {
263 let mut section = vec![0u8; 21];
264 section[..4].copy_from_slice(&(21u32).to_be_bytes());
265 section[4] = 5;
266 section[5..9].copy_from_slice(&3u32.to_be_bytes());
267 section[9..11].copy_from_slice(&0u16.to_be_bytes());
268 section[11..15].copy_from_slice(&10.0f32.to_be_bytes());
269 section[15..17].copy_from_slice(&2i16.to_be_bytes());
270 section[17..19].copy_from_slice(&1i16.to_be_bytes());
271 section[19] = 8;
272 section[20] = 0;
273
274 assert_eq!(
275 DataRepresentation::parse(§ion).unwrap(),
276 DataRepresentation::SimplePacking(SimplePackingParams {
277 encoded_values: 3,
278 reference_value: 10.0,
279 binary_scale: 2,
280 decimal_scale: 1,
281 bits_per_value: 8,
282 original_field_type: 0,
283 })
284 );
285 }
286
287 #[test]
288 fn parses_jpeg2000_packing_template() {
289 let mut section = vec![0u8; 23];
290 section[..4].copy_from_slice(&(23u32).to_be_bytes());
291 section[4] = 5;
292 section[5..9].copy_from_slice(&4u32.to_be_bytes());
293 section[9..11].copy_from_slice(&40u16.to_be_bytes());
294 section[11..15].copy_from_slice(&3.5f32.to_be_bytes());
295 section[15..17].copy_from_slice(&encode_grib_i16(-1).unwrap());
296 section[17..19].copy_from_slice(&2i16.to_be_bytes());
297 section[19] = 12;
298 section[20] = 0;
299 section[21] = 1;
300 section[22] = 255;
301
302 assert_eq!(
303 DataRepresentation::parse(§ion).unwrap(),
304 DataRepresentation::Jpeg2000Packing(Jpeg2000PackingParams {
305 packing: ImagePackingParams {
306 encoded_values: 4,
307 reference_value: 3.5,
308 binary_scale: -1,
309 decimal_scale: 2,
310 bits_per_value: 12,
311 original_field_type: 0,
312 },
313 compression_type: 1,
314 target_compression_ratio: 255,
315 })
316 );
317 }
318
319 #[test]
320 fn parses_png_packing_template() {
321 let mut section = vec![0u8; 21];
322 section[..4].copy_from_slice(&(21u32).to_be_bytes());
323 section[4] = 5;
324 section[5..9].copy_from_slice(&6u32.to_be_bytes());
325 section[9..11].copy_from_slice(&41u16.to_be_bytes());
326 section[11..15].copy_from_slice(&1.25f32.to_be_bytes());
327 section[15..17].copy_from_slice(&1i16.to_be_bytes());
328 section[17..19].copy_from_slice(&encode_grib_i16(-2).unwrap());
329 section[19] = 16;
330 section[20] = 1;
331
332 assert_eq!(
333 DataRepresentation::parse(§ion).unwrap(),
334 DataRepresentation::PngPacking(PngPackingParams {
335 packing: ImagePackingParams {
336 encoded_values: 6,
337 reference_value: 1.25,
338 binary_scale: 1,
339 decimal_scale: -2,
340 bits_per_value: 16,
341 original_field_type: 1,
342 },
343 })
344 );
345 }
346}