1use std::slice::Iter;
2
3use crate::{
4 codetables::SUPPORTED_PROD_DEF_TEMPLATE_NUMBERS,
5 datatypes::*,
6 error::*,
7 grid::{
8 GaussianGridDefinition, GridPointIterator, LambertGridDefinition, LatLonGridDefinition,
9 },
10 helpers::{read_as, GribInt},
11 time::UtcDateTime,
12 GridPointIndexIterator, PolarStereographicGridDefinition,
13};
14
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct Indicator {
17 pub discipline: u8,
19 pub total_length: u64,
21}
22
23impl Indicator {
24 pub(crate) fn from_slice(slice: &[u8]) -> Result<Self, ParseError> {
25 let discipline = slice[6];
26 let version = slice[7];
27 if version != 2 {
28 return Err(ParseError::GRIBVersionMismatch(version));
29 }
30
31 let total_length = read_as!(u64, slice, 8);
32
33 Ok(Self {
34 discipline,
35 total_length,
36 })
37 }
38}
39
40#[derive(Debug, Clone, PartialEq, Eq)]
41pub struct Identification {
42 payload: Box<[u8]>,
43}
44
45impl Identification {
46 pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
47 let size = slice.len();
48 if size < 16 {
49 Err(BuildError::SectionSizeTooSmall(size))
50 } else {
51 Ok(Self { payload: slice })
52 }
53 }
54
55 pub fn iter(&self) -> Iter<'_, u8> {
56 self.payload.iter()
57 }
58
59 #[inline]
62 pub fn centre_id(&self) -> u16 {
63 let payload = &self.payload;
64 read_as!(u16, payload, 0)
65 }
66
67 #[inline]
70 pub fn subcentre_id(&self) -> u16 {
71 let payload = &self.payload;
72 read_as!(u16, payload, 2)
73 }
74
75 #[inline]
77 pub fn master_table_version(&self) -> u8 {
78 self.payload[4]
79 }
80
81 #[inline]
83 pub fn local_table_version(&self) -> u8 {
84 self.payload[5]
85 }
86
87 #[inline]
89 pub fn ref_time_significance(&self) -> u8 {
90 self.payload[6]
91 }
92
93 pub fn ref_time_unchecked(&self) -> UtcDateTime {
99 let payload = &self.payload;
100 UtcDateTime::new(
101 read_as!(u16, payload, 7),
102 payload[9],
103 payload[10],
104 payload[11],
105 payload[12],
106 payload[13],
107 )
108 }
109
110 #[inline]
113 pub fn prod_status(&self) -> u8 {
114 self.payload[14]
115 }
116
117 #[inline]
119 pub fn data_type(&self) -> u8 {
120 self.payload[15]
121 }
122}
123
124#[derive(Debug, Clone, PartialEq, Eq)]
125pub struct LocalUse {
126 payload: Box<[u8]>,
127}
128
129impl LocalUse {
130 pub fn from_payload(slice: Box<[u8]>) -> Self {
131 Self { payload: slice }
132 }
133
134 pub fn iter(&self) -> Iter<'_, u8> {
135 self.payload.iter()
136 }
137}
138
139#[derive(Debug, Clone, PartialEq, Eq, Hash)]
140pub struct GridDefinition {
141 payload: Box<[u8]>,
142}
143
144impl GridDefinition {
145 pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
146 let size = slice.len();
147 if size < 9 {
148 Err(BuildError::SectionSizeTooSmall(size))
149 } else {
150 Ok(Self { payload: slice })
151 }
152 }
153
154 pub fn iter(&self) -> Iter<'_, u8> {
155 self.payload.iter()
156 }
157
158 pub fn num_points(&self) -> u32 {
160 let payload = &self.payload;
161 read_as!(u32, payload, 1)
162 }
163
164 pub fn grid_tmpl_num(&self) -> u16 {
166 let payload = &self.payload;
167 read_as!(u16, payload, 7)
168 }
169}
170
171#[derive(Debug, PartialEq, Eq)]
172pub enum GridDefinitionTemplateValues {
173 Template0(LatLonGridDefinition),
174 Template20(PolarStereographicGridDefinition),
175 Template30(LambertGridDefinition),
176 Template40(GaussianGridDefinition),
177}
178
179impl GridDefinitionTemplateValues {
180 pub fn grid_shape(&self) -> (usize, usize) {
183 match self {
184 Self::Template0(def) => def.grid_shape(),
185 Self::Template20(def) => def.grid_shape(),
186 Self::Template30(def) => def.grid_shape(),
187 Self::Template40(def) => def.grid_shape(),
188 }
189 }
190
191 pub fn short_name(&self) -> &'static str {
200 match self {
201 Self::Template0(def) => def.short_name(),
202 Self::Template20(def) => def.short_name(),
203 Self::Template30(def) => def.short_name(),
204 Self::Template40(def) => def.short_name(),
205 }
206 }
207
208 pub fn ij(&self) -> Result<GridPointIndexIterator, GribError> {
214 match self {
215 Self::Template0(def) => def.ij(),
216 Self::Template20(def) => def.ij(),
217 Self::Template30(def) => def.ij(),
218 Self::Template40(def) => def.ij(),
219 }
220 }
221
222 pub fn latlons(&self) -> Result<GridPointIterator, GribError> {
229 let iter = match self {
230 Self::Template0(def) => GridPointIterator::LatLon(def.latlons()?),
231 #[cfg(feature = "gridpoints-proj")]
232 Self::Template20(def) => GridPointIterator::Lambert(def.latlons()?),
233 #[cfg(feature = "gridpoints-proj")]
234 Self::Template30(def) => GridPointIterator::Lambert(def.latlons()?),
235 Self::Template40(def) => GridPointIterator::LatLon(def.latlons()?),
236 #[cfg(not(feature = "gridpoints-proj"))]
237 _ => {
238 return Err(GribError::NotSupported(
239 "lat/lon computation support for the template is dropped in this build"
240 .to_owned(),
241 ))
242 }
243 };
244 Ok(iter)
245 }
246}
247
248impl TryFrom<&GridDefinition> for GridDefinitionTemplateValues {
249 type Error = GribError;
250
251 fn try_from(value: &GridDefinition) -> Result<Self, Self::Error> {
252 let num = value.grid_tmpl_num();
253 match num {
254 0 => {
255 let buf = &value.payload;
256 if buf.len() > 67 {
257 return Err(GribError::NotSupported(format!(
258 "template {num} with list of number of points"
259 )));
260 }
261 Ok(GridDefinitionTemplateValues::Template0(
262 LatLonGridDefinition::from_buf(&buf[25..]),
263 ))
264 }
265 20 => {
266 let buf = &value.payload;
267 Ok(GridDefinitionTemplateValues::Template20(
268 PolarStereographicGridDefinition::from_buf(&buf[9..]),
269 ))
270 }
271 30 => {
272 let buf = &value.payload;
273 Ok(GridDefinitionTemplateValues::Template30(
274 LambertGridDefinition::from_buf(&buf[9..]),
275 ))
276 }
277 40 => {
278 let buf = &value.payload;
279 if buf.len() > 67 {
280 return Err(GribError::NotSupported(format!(
281 "template {num} with list of number of points"
282 )));
283 }
284 Ok(GridDefinitionTemplateValues::Template40(
285 GaussianGridDefinition::from_buf(&buf[25..]),
286 ))
287 }
288 _ => Err(GribError::NotSupported(format!("template {num}"))),
289 }
290 }
291}
292
293const START_OF_PROD_TEMPLATE: usize = 4;
294
295#[derive(Debug, Clone, PartialEq, Eq, Hash)]
296pub struct ProdDefinition {
297 payload: Box<[u8]>,
298}
299
300impl ProdDefinition {
301 pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
302 let size = slice.len();
303 if size < START_OF_PROD_TEMPLATE {
304 Err(BuildError::SectionSizeTooSmall(size))
305 } else {
306 Ok(Self { payload: slice })
307 }
308 }
309
310 pub fn iter(&self) -> Iter<'_, u8> {
311 self.payload.iter()
312 }
313
314 pub fn num_coordinates(&self) -> u16 {
316 let payload = &self.payload;
317 read_as!(u16, payload, 0)
318 }
319
320 pub fn prod_tmpl_num(&self) -> u16 {
322 let payload = &self.payload;
323 read_as!(u16, payload, 2)
324 }
325
326 pub(crate) fn template_supported(&self) -> bool {
331 SUPPORTED_PROD_DEF_TEMPLATE_NUMBERS.contains(&self.prod_tmpl_num())
332 }
333
334 pub fn parameter_category(&self) -> Option<u8> {
337 if self.template_supported() {
338 self.payload.get(START_OF_PROD_TEMPLATE).copied()
339 } else {
340 None
341 }
342 }
343
344 pub fn parameter_number(&self) -> Option<u8> {
347 if self.template_supported() {
348 self.payload.get(START_OF_PROD_TEMPLATE + 1).copied()
349 } else {
350 None
351 }
352 }
353
354 pub fn generating_process(&self) -> Option<u8> {
357 if self.template_supported() {
358 let index = match self.prod_tmpl_num() {
359 0..=39 => Some(2),
360 40..=43 => Some(4),
361 44..=46 => Some(15),
362 47 => Some(2),
363 48..=49 => Some(26),
364 51 => Some(2),
365 55..=56 => Some(8),
367 59 => Some(8),
369 60..=61 => Some(2),
370 62..=63 => Some(8),
371 70..=73 => Some(7),
373 76..=79 => Some(5),
374 80..=81 => Some(27),
375 82 => Some(16),
376 83 => Some(2),
377 84 => Some(16),
378 85 => Some(15),
379 86..=91 => Some(2),
380 254 => Some(2),
381 1000..=1101 => Some(2),
382 _ => None,
383 }?;
384 self.payload.get(START_OF_PROD_TEMPLATE + index).copied()
385 } else {
386 None
387 }
388 }
389
390 pub fn forecast_time(&self) -> Option<ForecastTime> {
394 if self.template_supported() {
395 let unit_index = match self.prod_tmpl_num() {
396 0..=15 => Some(8),
397 32..=34 => Some(8),
398 40..=43 => Some(10),
399 44..=47 => Some(21),
400 48..=49 => Some(32),
401 51 => Some(8),
402 55..=56 => Some(14),
404 59 => Some(14),
406 60..=61 => Some(8),
407 62..=63 => Some(14),
408 70..=73 => Some(13),
410 76..=79 => Some(11),
411 80..=81 => Some(33),
412 82..=84 => Some(22),
413 85 => Some(21),
414 86..=87 => Some(8),
415 88 => Some(26),
416 91 => Some(8),
417 1000..=1101 => Some(8),
418 _ => None,
419 }?;
420 let unit_index = START_OF_PROD_TEMPLATE + unit_index;
421 let unit = self.payload.get(unit_index).copied();
422 let start = unit_index + 1;
423 let end = unit_index + 5;
424 let time = u32::from_be_bytes(self.payload[start..end].try_into().unwrap());
425 unit.map(|v| ForecastTime::from_numbers(v, time))
426 } else {
427 None
428 }
429 }
430
431 pub fn fixed_surfaces(&self) -> Option<(FixedSurface, FixedSurface)> {
433 if self.template_supported() {
434 let index = match self.prod_tmpl_num() {
435 0..=15 => Some(13),
436 40..=43 => Some(15),
437 44 => Some(24),
438 45..=47 => Some(26),
439 48..=49 => Some(37),
440 51 => Some(13),
441 55..=56 => Some(19),
443 59 => Some(19),
445 60..=61 => Some(13),
446 62..=63 => Some(19),
447 70..=73 => Some(18),
449 76..=79 => Some(16),
450 80..=81 => Some(38),
451 82..=84 => Some(27),
452 85 => Some(26),
453 86..=87 => Some(13),
454 88 => Some(5),
455 91 => Some(13),
456 1100..=1101 => Some(13),
457 _ => None,
458 }?;
459
460 let first_surface = self.read_surface_from(index);
461 let second_surface = self.read_surface_from(index + 6);
462 first_surface.zip(second_surface)
463 } else {
464 None
465 }
466 }
467
468 fn read_surface_from(&self, index: usize) -> Option<FixedSurface> {
469 let index = START_OF_PROD_TEMPLATE + index;
470 let surface_type = self.payload.get(index).copied();
471 let scale_factor = self.payload.get(index + 1).map(|v| (*v).as_grib_int());
472 let start = index + 2;
473 let end = index + 6;
474 let scaled_value =
475 u32::from_be_bytes(self.payload[start..end].try_into().unwrap()).as_grib_int();
476 surface_type
477 .zip(scale_factor)
478 .map(|(stype, factor)| FixedSurface::new(stype, factor, scaled_value))
479 }
480}
481
482#[derive(Debug, Clone, PartialEq, Eq, Hash)]
483pub struct ReprDefinition {
484 payload: Box<[u8]>,
485}
486
487impl ReprDefinition {
488 pub fn from_payload(slice: Box<[u8]>) -> Result<Self, BuildError> {
489 let size = slice.len();
490 if size < 6 {
491 Err(BuildError::SectionSizeTooSmall(size))
492 } else {
493 Ok(Self { payload: slice })
494 }
495 }
496
497 pub fn iter(&self) -> Iter<'_, u8> {
498 self.payload.iter()
499 }
500
501 pub fn num_points(&self) -> u32 {
505 let payload = &self.payload;
506 read_as!(u32, payload, 0)
507 }
508
509 pub fn repr_tmpl_num(&self) -> u16 {
511 let payload = &self.payload;
512 read_as!(u16, payload, 4)
513 }
514}
515
516#[derive(Debug, Clone, PartialEq, Eq, Hash)]
517pub struct BitMap {
518 pub bitmap_indicator: u8,
520}
521
522#[cfg(test)]
523mod tests {
524 use super::*;
525
526 #[test]
527 fn grid_definition_template_0() {
528 let data = GridDefinition::from_payload(
532 vec![
533 0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff, 0xff, 0xff, 0xff,
534 0xff, 0x01, 0x03, 0xcd, 0x39, 0xfa, 0x01, 0x03, 0xc9, 0xf6, 0xa3, 0x00, 0x00, 0x01,
535 0x00, 0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x02,
536 0xdb, 0xc9, 0x3d, 0x07, 0x09, 0x7d, 0xa4, 0x30, 0x01, 0x31, 0xcf, 0xc3, 0x08, 0xef,
537 0xdd, 0x5c, 0x00, 0x01, 0xe8, 0x48, 0x00, 0x01, 0x45, 0x85, 0x00,
538 ]
539 .into_boxed_slice(),
540 )
541 .unwrap();
542
543 let actual = GridDefinitionTemplateValues::try_from(&data).unwrap();
544 let expected = GridDefinitionTemplateValues::Template0(LatLonGridDefinition {
545 ni: 256,
546 nj: 336,
547 first_point_lat: 47958333,
548 first_point_lon: 118062500,
549 last_point_lat: 20041667,
550 last_point_lon: 149937500,
551 scanning_mode: crate::grid::ScanningMode(0b00000000),
552 });
553 assert_eq!(actual, expected);
554 }
555
556 #[test]
557 fn prod_definition_parameters() {
558 let data = ProdDefinition::from_payload(
559 vec![
560 0, 0, 0, 0, 193, 0, 2, 153, 255, 0, 0, 0, 0, 0, 0, 0, 40, 1, 255, 255, 255, 255,
561 255, 255, 255, 255, 255, 255, 255,
562 ]
563 .into_boxed_slice(),
564 )
565 .unwrap();
566
567 assert_eq!(data.parameter_category(), Some(193));
568 assert_eq!(data.parameter_number(), Some(0));
569 assert_eq!(
570 data.forecast_time(),
571 Some(ForecastTime::from_numbers(0, 40))
572 );
573 assert_eq!(
574 data.fixed_surfaces(),
575 Some((
576 FixedSurface::new(1, -127, -2147483647),
577 FixedSurface::new(255, -127, -2147483647)
578 ))
579 );
580 }
581}