1use std::slice::Iter;
2
3use crate::{
4 TryFromSlice, codetables::SUPPORTED_PROD_DEF_TEMPLATE_NUMBERS, datatypes::*, error::*,
5 helpers::read_as,
6};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct Indicator {
10 pub discipline: u8,
12 pub total_length: u64,
14}
15
16impl Indicator {
17 pub(crate) fn from_slice(slice: &[u8]) -> Result<Self, ParseError> {
18 let mut pos = 0;
19 let sect = crate::def::grib2::Section0::try_from_slice(slice, &mut pos)
20 .map_err(|_e| ParseError::UnexpectedEndOfData(0))?;
21 if sect.edition_num != 2 {
22 return Err(ParseError::GRIBVersionMismatch(sect.edition_num));
23 }
24
25 Ok(Self {
26 discipline: sect.discipline,
27 total_length: sect.total_len,
28 })
29 }
30}
31
32#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct Identification {
34 pub(crate) payload: Box<[u8]>,
35}
36
37impl Identification {
38 pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
39 let size = slice.len();
40 if size < 16 {
41 Err(BuildError::SectionSizeTooSmall(size))
42 } else {
43 Ok(Self { payload: slice })
44 }
45 }
46
47 pub fn iter(&self) -> Iter<'_, u8> {
48 self.payload.iter()
49 }
50
51 #[inline]
54 pub fn centre_id(&self) -> u16 {
55 let payload = &self.payload;
56 read_as!(u16, payload, 0)
57 }
58
59 #[inline]
62 pub fn subcentre_id(&self) -> u16 {
63 let payload = &self.payload;
64 read_as!(u16, payload, 2)
65 }
66
67 #[inline]
69 pub fn master_table_version(&self) -> u8 {
70 self.payload[4]
71 }
72
73 #[inline]
75 pub fn local_table_version(&self) -> u8 {
76 self.payload[5]
77 }
78
79 #[inline]
81 pub fn ref_time_significance(&self) -> u8 {
82 self.payload[6]
83 }
84
85 pub fn ref_time_unchecked(&self) -> crate::def::grib2::template::param_set::DateTime {
91 let mut pos = 7;
92 crate::def::grib2::template::param_set::DateTime::try_from_slice(&self.payload, &mut pos)
93 .unwrap()
94 }
95
96 #[inline]
99 pub fn prod_status(&self) -> u8 {
100 self.payload[14]
101 }
102
103 #[inline]
105 pub fn data_type(&self) -> u8 {
106 self.payload[15]
107 }
108}
109
110#[derive(Debug, Clone, PartialEq, Eq)]
111pub struct LocalUse {
112 payload: Box<[u8]>,
113}
114
115impl LocalUse {
116 pub fn from_payload(slice: Box<[u8]>) -> Self {
117 Self { payload: slice }
118 }
119
120 pub fn iter(&self) -> Iter<'_, u8> {
121 self.payload.iter()
122 }
123}
124
125#[derive(Debug, Clone, PartialEq, Eq, Hash)]
126pub struct GridDefinition {
127 pub(crate) payload: Box<[u8]>,
128}
129
130impl GridDefinition {
131 pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
132 let size = slice.len();
133 if size < 9 {
134 Err(BuildError::SectionSizeTooSmall(size))
135 } else {
136 Ok(Self { payload: slice })
137 }
138 }
139
140 pub fn iter(&self) -> Iter<'_, u8> {
141 self.payload.iter()
142 }
143
144 pub fn num_points(&self) -> u32 {
146 let payload = &self.payload;
147 read_as!(u32, payload, 1)
148 }
149
150 pub fn grid_tmpl_num(&self) -> u16 {
152 let payload = &self.payload;
153 read_as!(u16, payload, 7)
154 }
155}
156
157const START_OF_PROD_TEMPLATE: usize = 4;
158
159#[derive(Debug, Clone, PartialEq, Eq, Hash)]
160pub struct ProdDefinition {
161 pub(crate) payload: Box<[u8]>,
162}
163
164impl ProdDefinition {
165 pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
166 let size = slice.len();
167 if size < START_OF_PROD_TEMPLATE {
168 Err(BuildError::SectionSizeTooSmall(size))
169 } else {
170 Ok(Self { payload: slice })
171 }
172 }
173
174 pub fn iter(&self) -> Iter<'_, u8> {
175 self.payload.iter()
176 }
177
178 pub fn num_coordinates(&self) -> u16 {
180 let payload = &self.payload;
181 read_as!(u16, payload, 0)
182 }
183
184 pub fn prod_tmpl_num(&self) -> u16 {
186 let payload = &self.payload;
187 read_as!(u16, payload, 2)
188 }
189
190 pub(crate) fn template_supported(&self) -> bool {
195 SUPPORTED_PROD_DEF_TEMPLATE_NUMBERS.contains(&self.prod_tmpl_num())
196 }
197
198 pub fn parameter_category(&self) -> Option<u8> {
201 if self.template_supported() {
202 self.payload.get(START_OF_PROD_TEMPLATE).copied()
203 } else {
204 None
205 }
206 }
207
208 pub fn parameter_number(&self) -> Option<u8> {
211 if self.template_supported() {
212 self.payload.get(START_OF_PROD_TEMPLATE + 1).copied()
213 } else {
214 None
215 }
216 }
217
218 pub fn generating_process(&self) -> Option<u8> {
221 if self.template_supported() {
222 let index = match self.prod_tmpl_num() {
223 0..=39 => Some(2),
224 40..=43 => Some(4),
225 44..=46 => Some(15),
226 47 => Some(2),
227 48..=49 => Some(26),
228 51 => Some(2),
229 55..=56 => Some(8),
231 59 => Some(8),
233 60..=61 => Some(2),
234 62..=63 => Some(8),
235 70..=73 => Some(7),
237 76..=79 => Some(5),
238 80..=81 => Some(27),
239 82 => Some(16),
240 83 => Some(2),
241 84 => Some(16),
242 85 => Some(15),
243 86..=91 => Some(2),
244 254 => Some(2),
245 1000..=1101 => Some(2),
246 _ => None,
247 }?;
248 self.payload.get(START_OF_PROD_TEMPLATE + index).copied()
249 } else {
250 None
251 }
252 }
253
254 pub fn forecast_time(&self) -> Option<ForecastTime> {
258 if self.template_supported() {
259 let unit_index = match self.prod_tmpl_num() {
260 0..=15 => Some(8),
261 32..=34 => Some(8),
262 40..=43 => Some(10),
263 44..=47 => Some(21),
264 48..=49 => Some(32),
265 51 => Some(8),
266 55..=56 => Some(14),
268 59 => Some(14),
270 60..=61 => Some(8),
271 62..=63 => Some(14),
272 70..=73 => Some(13),
274 76..=79 => Some(11),
275 80..=81 => Some(33),
276 82..=84 => Some(22),
277 85 => Some(21),
278 86..=87 => Some(8),
279 88 => Some(26),
280 91 => Some(8),
281 1000..=1101 => Some(8),
282 _ => None,
283 }?;
284 let unit_index = START_OF_PROD_TEMPLATE + unit_index;
285 let unit = self.payload.get(unit_index).copied();
286 let start = unit_index + 1;
287 let end = unit_index + 5;
288 let time = u32::from_be_bytes(self.payload[start..end].try_into().unwrap());
289 unit.map(|v| ForecastTime::from_numbers(v, time))
290 } else {
291 None
292 }
293 }
294
295 pub fn fixed_surfaces(&self) -> Option<(FixedSurface, FixedSurface)> {
297 if self.template_supported() {
298 let index = match self.prod_tmpl_num() {
299 0..=15 => Some(13),
300 40..=43 => Some(15),
301 44 => Some(24),
302 45..=47 => Some(26),
303 48..=49 => Some(37),
304 51 => Some(13),
305 55..=56 => Some(19),
307 59 => Some(19),
309 60..=61 => Some(13),
310 62..=63 => Some(19),
311 70..=73 => Some(18),
313 76..=79 => Some(16),
314 80..=81 => Some(38),
315 82..=84 => Some(27),
316 85 => Some(26),
317 86..=87 => Some(13),
318 88 => Some(5),
319 91 => Some(13),
320 1100..=1101 => Some(13),
321 _ => None,
322 }?;
323
324 let mut pos = START_OF_PROD_TEMPLATE + index;
325 let first_surface = FixedSurface::try_from_slice(&self.payload, &mut pos).ok();
326 let second_surface = FixedSurface::try_from_slice(&self.payload, &mut pos).ok();
327 first_surface.zip(second_surface)
328 } else {
329 None
330 }
331 }
332}
333
334#[derive(Debug, Clone, PartialEq, Eq, Hash)]
335pub struct ReprDefinition {
336 pub(crate) payload: Box<[u8]>,
337}
338
339impl ReprDefinition {
340 pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
341 let size = slice.len();
342 if size < 6 {
343 Err(BuildError::SectionSizeTooSmall(size))
344 } else {
345 Ok(Self { payload: slice })
346 }
347 }
348
349 pub fn iter(&self) -> Iter<'_, u8> {
350 self.payload.iter()
351 }
352
353 pub fn num_points(&self) -> u32 {
357 let payload = &self.payload;
358 read_as!(u32, payload, 0)
359 }
360
361 pub fn repr_tmpl_num(&self) -> u16 {
363 let payload = &self.payload;
364 read_as!(u16, payload, 4)
365 }
366}
367
368#[derive(Debug, Clone, PartialEq, Eq, Hash)]
369pub struct BitMap {
370 pub bitmap_indicator: u8,
372}
373
374#[cfg(test)]
375mod tests {
376 use super::*;
377
378 #[test]
379 fn prod_definition_parameters() {
380 let data = ProdDefinition::from_payload(
381 vec![
382 0, 0, 0, 0, 193, 0, 2, 153, 255, 0, 0, 0, 0, 0, 0, 0, 40, 1, 255, 255, 255, 255,
383 255, 255, 255, 255, 255, 255, 255,
384 ]
385 .into_boxed_slice(),
386 )
387 .unwrap();
388
389 assert_eq!(data.parameter_category(), Some(193));
390 assert_eq!(data.parameter_number(), Some(0));
391 assert_eq!(
392 data.forecast_time(),
393 Some(ForecastTime::from_numbers(0, 40))
394 );
395 assert_eq!(
396 data.fixed_surfaces(),
397 Some((
398 FixedSurface::new(1, -127, -2147483647),
399 FixedSurface::new(255, -127, -2147483647)
400 ))
401 );
402 }
403}