gistools/readers/grib2/sections/_4/
templates.rs

1use crate::{
2    parsers::Reader,
3    readers::{
4        Grib2Sections, Grib2Table4_3, Grib2Table4_4, Grib2Table4_6, Grib2Table4_7, Grib2TableA,
5        TableCategory, TypeAndUnit, grib2_lookup_table4_1, grib2_lookup_table4_5,
6        grib2_lookup_table42,
7    },
8    util::Date,
9};
10use alloc::string::String;
11
12/// Returns a template generator for the given template number
13///
14/// ## Parameters
15/// - `template`: the template number to generate
16/// - `reader`: the byte data to read
17/// - sections`: the sections of the GRIB2 message that have been parsed so far
18///
19/// ## Returns
20/// Generated template data
21#[derive(Debug, Clone, PartialEq)]
22pub enum Grib2ProductDefinition {
23    /// Analysis or forecast at a horizontal level or in a horizontal layer at a point in time.
24    Grib2Template40(Grib2Template40),
25    /// Individual ensemble forecast, control and perturbed, at a horizontal level or in a
26    /// horizontal layer at a point in time.
27    Grib2Template41(Grib2Template41),
28    /// Derived forecast, based on all ensemble members at a horizontal level or in a horizontal
29    /// layer at a point in time.
30    Grib2Template42(Grib2Template42),
31}
32impl Grib2ProductDefinition {
33    /// Create a new instance of Grib2ProductDefinition
34    pub fn new<T: Reader>(template: u16, reader: &T, sections: &Grib2Sections) -> Self {
35        match template {
36            0 => Grib2ProductDefinition::Grib2Template40(Grib2Template40::new(reader, sections)),
37            1 => Grib2ProductDefinition::Grib2Template41(Grib2Template41::new(reader, sections)),
38            2 => Grib2ProductDefinition::Grib2Template42(Grib2Template42::new(reader, sections)),
39            _ => panic!("Template 4.{template} not defined"),
40        }
41    }
42
43    /// Get the values
44    pub fn values(&self) -> &TableCategory {
45        match self {
46            Grib2ProductDefinition::Grib2Template40(template) => &template.values,
47            Grib2ProductDefinition::Grib2Template41(template) => &template.values,
48            Grib2ProductDefinition::Grib2Template42(template) => &template.values,
49        }
50    }
51}
52
53/// PRODUCT DEFINITION TEMPLATE 4.0
54///
55/// Analysis or forecast at a horizontal level or in
56/// a horizontal layer at a point in time.
57///
58/// [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp4-0.shtml)
59#[derive(Debug, Clone, PartialEq)]
60pub struct Grib2Template40 {
61    /// table accessed category
62    pub category: String,
63    /// Paramater
64    pub values: TableCategory,
65    /// Parameter category (see Code [Table 4.1](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-1.shtml))
66    pub parameter_category: u8,
67    /// Parameter number (see Code [Table 4.2](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-2.shtml))
68    pub parameter_number: u8,
69    /// Type of generating process (see Code [Table 4.3](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-3.shtml))
70    pub gen_process_type: Grib2Table4_3,
71    /// Background generating process identifier (defined by originating centre)
72    pub background_gen_process: u8,
73    /// Analysis or forecast generating process identifier (see Code [ON388 Table A](https://www.nco.ncep.noaa.gov/pmb/docs/on388/tablea.html))
74    pub forecast_gen_process: Grib2TableA,
75    /// Hours after reference time data cutoff (see Notes)
76    pub hours_after_ref_time: u16,
77    /// Minutes after reference time data cutoff (see Notes)
78    pub min_after_ref_time: u8,
79    /// Indicator of unit of time range (see Code [Table 4.4](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-4.shtml))
80    pub unit_of_time_range_indicator: Grib2Table4_4,
81    /// Forecast time in units defined by octet 18
82    pub forecast_time: Date,
83    /// First fixed surface
84    pub surface1: TypeAndUnit, // grib2_lookup_table4_5
85    /// Type of first fixed surface (see Code [Table 4.5](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-5.shtml))
86    pub surface1_type: u8,
87    /// Scale factor of first fixed surface
88    pub surface1_scale: u8,
89    /// Scaled value of first fixed surface
90    pub surface1_value: u32,
91    /// Second fixed surface
92    pub surface2: TypeAndUnit, // grib2_lookup_table4_5
93    /// Type of second fixed surface (see Code [Table 4.5](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-5.shtml))
94    pub surface2_type: u8,
95    /// Scale factor of second fixed surface
96    pub surface2_scale: u8,
97    /// Scaled value of second fixed surface
98    pub surface2_value: u32,
99}
100impl Grib2Template40 {
101    /// Create a new instance of Grib2ProductDefinition
102    ///
103    /// ## Parameters
104    /// - `section`: the byte data to read
105    /// - `sections`: the sections of the GRIB2 message that have been parsed so far
106    ///
107    /// ## Returns
108    /// The parsed template
109    pub fn new<T: Reader>(reader: &T, sections: &Grib2Sections) -> Self {
110        let discipline = sections.indicator.as_ref().map(|d| u8::from(d.discipline)).unwrap_or(0);
111        let ref_time = sections.identification.as_ref().map(|i| i.ref_time).unwrap_or_default();
112        let parameter_category = reader.uint8(Some(9));
113        let parameter_number = reader.uint8(Some(10));
114        let gen_process_type = reader.uint8(Some(11));
115        let background_gen_process = reader.uint8(Some(12));
116        let forecast_gen_process = reader.uint8(Some(13));
117        let hours_after_ref_time = reader.uint16_be(Some(14));
118        let min_after_ref_time = reader.uint8(Some(16));
119        let unit_of_time_range_indicator: Grib2Table4_4 = reader.uint8(Some(17)).into();
120        let forecast_time = reader.uint32_be(Some(18));
121        let surface1_type = reader.uint8(Some(22));
122        let surface1_scale = reader.uint8(Some(23));
123        let surface1_value = reader.uint32_be(Some(24));
124        let surface2_type = reader.uint8(Some(28));
125        let surface2_scale = reader.uint8(Some(29));
126        let surface2_value = reader.uint32_be(Some(30));
127        let category = grib2_lookup_table4_1(discipline, parameter_category);
128        let values = grib2_lookup_table42(discipline, parameter_category)(parameter_number);
129        let surface1 = grib2_lookup_table4_5(surface1_type);
130        let surface2 = grib2_lookup_table4_5(surface2_type);
131
132        Self {
133            category,
134            values,
135            parameter_category,
136            parameter_number,
137            gen_process_type: gen_process_type.into(),
138            background_gen_process,
139            forecast_gen_process: forecast_gen_process.into(),
140            hours_after_ref_time,
141            min_after_ref_time,
142            unit_of_time_range_indicator,
143            forecast_time: calculate_forecast_time(
144                &ref_time,
145                forecast_time as i64,
146                &unit_of_time_range_indicator,
147            ),
148            surface1,
149            surface1_type,
150            surface1_scale,
151            surface1_value,
152            surface2,
153            surface2_type,
154            surface2_scale,
155            surface2_value,
156        }
157    }
158}
159
160/// PRODUCT DEFINITION TEMPLATE 4.1
161///
162/// Individual ensemble forecast, control and perturbed, at a horizontal
163/// level or in a horizontal layer at a point in time.
164///
165/// [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp4-1.shtml)
166#[derive(Debug, Clone, PartialEq)]
167pub struct Grib2Template41 {
168    /// table accessed category
169    pub category: String,
170    /// Paramater
171    pub values: TableCategory,
172    /// Parameter category (see Code [Table 4.1](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-1.shtml))
173    pub parameter_category: u8,
174    /// Parameter number (see Code [Table 4.2](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-2.shtml))
175    pub parameter_number: u8,
176    /// Type of generating process (see Code [Table 4.3](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-3.shtml))
177    pub gen_process_type: Grib2Table4_3,
178    /// Background generating process identifier (defined by originating centre)
179    pub background_gen_process: u8,
180    /// Forecast generating process identifier (see Code [ON388 Table A](https://www.nco.ncep.noaa.gov/pmb/docs/on388/tablea.html))
181    pub forecast_gen_process: Grib2TableA,
182    /// Hours after reference time data cutoff (see Notes)
183    pub hours_after_ref_time: u16,
184    /// Minutes after reference time data cutoff (see Notes)
185    pub min_after_ref_time: u8,
186    /// Indicator of unit of time range (see Code [Table 4.4](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-4.shtml))
187    pub unit_of_time_range_indicator: Grib2Table4_4,
188    /// Forecast time in units defined by octet 18
189    pub forecast_time: Date,
190    /// First fixed surface
191    pub surface1: TypeAndUnit,
192    /// Type of first fixed surface (see Code [Table 4.5](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-5.shtml), result stored in `surface1`)
193    pub surface1_type: u8,
194    /// Scale factor of first fixed surface
195    pub surface1_scale: u8,
196    /// Scaled value of first fixed surface
197    pub surface1_value: u32,
198    /// Second fixed surface
199    pub surface2: TypeAndUnit,
200    /// Type of second fixed surface (see Code [Table 4.5](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-5.shtml) result stored in `surface2`)
201    pub surface2_type: u8,
202    /// Scale factor of second fixed surface
203    pub surface2_scale: u8,
204    /// Scaled value of second fixed surface
205    pub surface2_value: u32,
206    /// Type of ensemble forecast (see Code [Table 4.6](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-6.shtml))
207    pub ensemble_forecast_type: Grib2Table4_6,
208    /// Perturbation number
209    pub perturbation_number: u8,
210    /// Number of forecasts in ensemble
211    pub num_forecasts_in_ensemble: u8,
212}
213impl Grib2Template41 {
214    /// Create a new instance of Grib2ProductDefinition
215    ///
216    /// ## Parameters
217    /// - `section`: the byte data to read
218    /// - `sections`: the sections of the GRIB2 message that have been parsed so far
219    ///
220    /// ## Returns
221    /// The parsed template
222    pub fn new<T: Reader>(reader: &T, sections: &Grib2Sections) -> Self {
223        let discipline = sections.indicator.as_ref().map(|d| u8::from(d.discipline)).unwrap_or(0);
224        let ref_time = sections.identification.as_ref().map(|i| i.ref_time).unwrap_or_default();
225        let parameter_category = reader.uint8(Some(9));
226        let parameter_number = reader.uint8(Some(10));
227        let gen_process_type = reader.uint8(Some(11));
228        let background_gen_process = reader.uint8(Some(12));
229        let forecast_gen_process = reader.uint8(Some(13));
230        let hours_after_ref_time = reader.uint16_be(Some(14));
231        let min_after_ref_time = reader.uint8(Some(16));
232        let unit_of_time_range_indicator = reader.uint8(Some(17));
233        let forecast_time = reader.uint32_be(Some(18));
234        let surface1_type = reader.uint8(Some(22));
235        let surface1_scale = reader.uint8(Some(23));
236        let surface1_value = reader.uint32_be(Some(24));
237        let surface2_type = reader.uint8(Some(28));
238        let surface2_scale = reader.uint8(Some(29));
239        let surface2_value = reader.uint32_be(Some(30));
240        let ensemble_forecast_type = reader.uint8(Some(34));
241        let perturbation_number = reader.uint8(Some(35));
242        let num_forecasts_in_ensemble = reader.uint8(Some(36));
243        let category = grib2_lookup_table4_1(discipline, parameter_category);
244        let values = grib2_lookup_table42(discipline, parameter_category)(parameter_number);
245        let surface1 = grib2_lookup_table4_5(surface1_type);
246        let surface2 = grib2_lookup_table4_5(surface2_type);
247        let unit_of_time_range_indicator = unit_of_time_range_indicator.into();
248
249        Self {
250            category,
251            values,
252            parameter_category,
253            parameter_number,
254            gen_process_type: gen_process_type.into(),
255            background_gen_process,
256            forecast_gen_process: forecast_gen_process.into(),
257            hours_after_ref_time,
258            min_after_ref_time,
259            unit_of_time_range_indicator,
260            forecast_time: calculate_forecast_time(
261                &ref_time,
262                forecast_time as i64,
263                &unit_of_time_range_indicator,
264            ),
265            surface1,
266            surface1_type,
267            surface1_scale,
268            surface1_value,
269            surface2,
270            surface2_type,
271            surface2_scale,
272            surface2_value,
273            ensemble_forecast_type: ensemble_forecast_type.into(),
274            perturbation_number,
275            num_forecasts_in_ensemble,
276        }
277    }
278}
279
280/// PRODUCT DEFINITION TEMPLATE 4.2
281///
282/// Derived forecast, based on all ensemble members at a horizontal
283/// level or in a horizontal layer at a point in time.
284///
285/// [Read more...](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_temp4-2.shtml)
286#[derive(Debug, Clone, PartialEq)]
287pub struct Grib2Template42 {
288    /// table accessed category
289    pub category: String,
290    /// Paramater
291    pub values: TableCategory,
292    /// Parameter category (see Code [Table 4.1](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-1.shtml)) */
293    pub parameter_category: u8,
294    /// Parameter number (see Code [Table 4.2](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-2.shtml)) */
295    pub parameter_number: u8,
296    /// Type of generating process (see Code [Table 4.3](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-3.shtml)) */
297    pub gen_process_type: Grib2Table4_3,
298    /// Background generating process identifier (defined by originating centre) */
299    pub background_gen_process: u8,
300    /// Forecast generating process identifier (see Code [ON388 Table A](https://www.nco.ncep.noaa.gov/pmb/docs/on388/tablea.html)) */
301    pub forecast_gen_process: Grib2TableA,
302    /// Hours after reference time data cutoff (see Notes) */
303    pub hours_after_ref_time: u16,
304    /// Minutes after reference time data cutoff */
305    pub min_after_ref_time: u8,
306    /// Indicator of unit of time range (see Code [Table 4.4](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-4.shtml)) */
307    pub unit_of_time_range_indicator: Grib2Table4_4,
308    /// Forecast time in units defined by octet 18 */
309    pub forecast_time: Date,
310    /// First fixed surface */
311    pub surface1: TypeAndUnit,
312    /// Type of first fixed surface (see Code [Table 4.5](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-5.shtml)) */
313    pub surface1_type: u8,
314    /// Scale factor of first fixed surface */
315    pub surface1_scale: u8,
316    /// Scaled value of first fixed surface */
317    pub surface1_value: u32,
318    /// Second fixed surface */
319    pub surface2: TypeAndUnit,
320    /// Type of second fixed surface (see Code [Table 4.5](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-5.shtml)) */
321    pub surface2_type: u8,
322    /// Scale factor of second fixed surface */
323    pub surface2_scale: u8,
324    /// Scaled value of second fixed surface */
325    pub surface2_value: u32,
326    /// Derived forecast type (see Code [Table 4.7](https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-7.shtml)) */
327    pub derived_forecast_type: Grib2Table4_7,
328    /// Number of forecasts in the ensemble */
329    pub num_forecasts_in_ensemble: u8,
330}
331impl Grib2Template42 {
332    /// Create a new instance of Grib2ProductDefinition
333    ///
334    /// ## Parameters
335    /// - `section`: the byte data to read
336    /// - `sections`: the sections of the GRIB2 message that have been parsed so far
337    ///
338    /// ## Returns
339    /// The parsed template
340    pub fn new<T: Reader>(reader: &T, sections: &Grib2Sections) -> Self {
341        let discipline = sections.indicator.as_ref().map(|d| u8::from(d.discipline)).unwrap_or(0);
342        let ref_time = sections.identification.as_ref().map(|i| i.ref_time).unwrap_or_default();
343        let parameter_category = reader.uint8(Some(9));
344        let parameter_number = reader.uint8(Some(10));
345        let gen_process_type = reader.uint8(Some(11));
346        let background_gen_process = reader.uint8(Some(12));
347        let forecast_gen_process = reader.uint8(Some(13));
348        let hours_after_ref_time = reader.uint16_be(Some(14));
349        let min_after_ref_time = reader.uint8(Some(16));
350        let unit_of_time_range_indicator = reader.uint8(Some(17));
351        let forecast_time = reader.uint32_be(Some(18));
352        let surface1_type = reader.uint8(Some(22));
353        let surface1_scale = reader.uint8(Some(23));
354        let surface1_value = reader.uint32_be(Some(24));
355        let surface2_type = reader.uint8(Some(28));
356        let surface2_scale = reader.uint8(Some(29));
357        let surface2_value = reader.uint32_be(Some(30));
358        let derived_forecast_type = reader.uint8(Some(34));
359        let num_forecasts_in_ensemble = reader.uint8(Some(35));
360        let category = grib2_lookup_table4_1(discipline, parameter_category);
361        let values = grib2_lookup_table42(discipline, parameter_category)(parameter_number);
362        let surface1 = grib2_lookup_table4_5(surface1_type);
363        let surface2 = grib2_lookup_table4_5(surface2_type);
364        let unit_of_time_range_indicator = unit_of_time_range_indicator.into();
365
366        Self {
367            category,
368            values,
369            parameter_category,
370            parameter_number,
371            gen_process_type: gen_process_type.into(),
372            background_gen_process,
373            forecast_gen_process: forecast_gen_process.into(),
374            hours_after_ref_time,
375            min_after_ref_time,
376            unit_of_time_range_indicator,
377            forecast_time: calculate_forecast_time(
378                &ref_time,
379                forecast_time as i64,
380                &unit_of_time_range_indicator,
381            ),
382            surface1,
383            surface1_type,
384            surface1_scale,
385            surface1_value,
386            surface2,
387            surface2_type,
388            surface2_scale,
389            surface2_value,
390            derived_forecast_type: derived_forecast_type.into(),
391            num_forecasts_in_ensemble,
392        }
393    }
394}
395
396/// Calculate Forecast Time
397///
398/// ## Parameters
399/// - `ref_time`: Reference time of GRIB Packet
400/// - `offset`: Number of units to offset the ref time by
401/// - `unit_of_time`: unit of time of offset
402///
403/// ## Returns
404/// The forecast time
405pub fn calculate_forecast_time(ref_time: &Date, offset: i64, unit_of_time: &Grib2Table4_4) -> Date {
406    match unit_of_time {
407        Grib2Table4_4::Hour => Date::from_time(ref_time.get_time() + offset * 1000 * 60 * 60),
408        _ => {
409            panic!("Unable to calculate foercast time for unit: {}", unit_of_time);
410        }
411    }
412}