1use grib_template_helpers::TryFromSlice;
2use helpers::RegularGridIterator;
3
4pub use self::{gaussian::compute_gaussian_latitudes, rotated_ll::Unrotate};
5use crate::{
6 GribError, GridDefinition,
7 def::grib2::template::{
8 Template3_0, Template3_1, Template3_20, Template3_30, Template3_40, param_set::ScanningMode,
9 },
10};
11
12#[derive(Debug, PartialEq)]
13pub enum GridDefinitionTemplateValues {
14 Template0(Template3_0),
15 Template1(Template3_1),
16 Template20(Template3_20),
17 Template30(Template3_30),
18 Template40(Template3_40),
19}
20
21impl GridShortName for GridDefinitionTemplateValues {
22 fn short_name(&self) -> &'static str {
23 match self {
24 Self::Template0(def) => def.lat_lon.short_name(),
25 Self::Template1(def) => def.short_name(),
26 Self::Template20(def) => def.short_name(),
27 Self::Template30(def) => def.short_name(),
28 Self::Template40(def) => def.gaussian.short_name(),
29 }
30 }
31}
32
33impl GridPointIndex for GridDefinitionTemplateValues {
34 fn grid_shape(&self) -> (usize, usize) {
35 match self {
36 Self::Template0(def) => def.lat_lon.grid_shape(),
37 Self::Template1(def) => def.grid_shape(),
38 Self::Template20(def) => def.grid_shape(),
39 Self::Template30(def) => def.grid_shape(),
40 Self::Template40(def) => def.gaussian.grid_shape(),
41 }
42 }
43
44 fn scanning_mode(&self) -> &ScanningMode {
45 match self {
46 Self::Template0(def) => def.lat_lon.scanning_mode(),
47 Self::Template1(def) => def.scanning_mode(),
48 Self::Template20(def) => def.scanning_mode(),
49 Self::Template30(def) => def.scanning_mode(),
50 Self::Template40(def) => def.gaussian.scanning_mode(),
51 }
52 }
53}
54
55impl LatLons for GridDefinitionTemplateValues {
56 type Iter<'a>
57 = GridPointLatLons
58 where
59 Self: 'a;
60
61 fn latlons<'a>(&'a self) -> Result<Self::Iter<'a>, GribError> {
62 let iter = match self {
63 Self::Template0(def) => GridPointLatLons::from(def.lat_lon.latlons()?),
64 Self::Template1(def) => GridPointLatLons::from(def.latlons()?),
65 #[cfg(feature = "gridpoints-proj")]
66 Self::Template20(def) => GridPointLatLons::from(def.latlons()?),
67 #[cfg(feature = "gridpoints-proj")]
68 Self::Template30(def) => GridPointLatLons::from(def.latlons()?),
69 Self::Template40(def) => GridPointLatLons::from(def.gaussian.latlons()?),
70 #[cfg(not(feature = "gridpoints-proj"))]
71 _ => {
72 return Err(GribError::NotSupported(
73 "lat/lon computation support for the template is dropped in this build"
74 .to_owned(),
75 ));
76 }
77 };
78 Ok(iter)
79 }
80}
81
82impl TryFrom<&GridDefinition> for GridDefinitionTemplateValues {
83 type Error = GribError;
84
85 fn try_from(value: &GridDefinition) -> Result<Self, Self::Error> {
86 let buf = &value.payload[9..];
102 let mut pos = 0;
103 let num = value.grid_tmpl_num();
104 let template = match num {
105 0 => GridDefinitionTemplateValues::Template0(
106 Template3_0::try_from_slice(buf, &mut pos)
107 .map_err(|e| GribError::Unknown(e.to_owned()))?,
108 ),
109 1 => GridDefinitionTemplateValues::Template1(
110 Template3_1::try_from_slice(buf, &mut pos)
111 .map_err(|e| GribError::Unknown(e.to_owned()))?,
112 ),
113 20 => GridDefinitionTemplateValues::Template20(
114 Template3_20::try_from_slice(buf, &mut pos)
115 .map_err(|e| GribError::Unknown(e.to_owned()))?,
116 ),
117 30 => GridDefinitionTemplateValues::Template30(
118 Template3_30::try_from_slice(buf, &mut pos)
119 .map_err(|e| GribError::Unknown(e.to_owned()))?,
120 ),
121 40 => GridDefinitionTemplateValues::Template40(
122 Template3_40::try_from_slice(buf, &mut pos)
123 .map_err(|e| GribError::Unknown(e.to_owned()))?,
124 ),
125 _ => {
126 return Err(GribError::NotSupported(format!(
127 "lat/lon computation support for the template {num} is dropped in this build"
128 )));
129 }
130 };
131 if buf.len() > pos {
132 return Err(GribError::NotSupported(
133 "template with list of number of points".to_owned(),
134 ));
135 }
136 Ok(template)
137 }
138}
139
140pub trait GridShortName {
142 fn short_name(&self) -> &'static str;
151}
152
153pub trait LatLons {
156 type Iter<'a>: Iterator<Item = (f32, f32)>
157 where
158 Self: 'a;
159
160 fn latlons<'a>(&'a self) -> Result<Self::Iter<'a>, GribError>;
167}
168
169#[derive(Clone)]
177pub struct GridPointLatLons(LatLonsWrapper);
178
179impl Iterator for GridPointLatLons {
180 type Item = (f32, f32);
181
182 fn next(&mut self) -> Option<Self::Item> {
183 match self {
184 Self(LatLonsWrapper::SigR(iter)) => iter.next(),
185 Self(LatLonsWrapper::SigUR(iter)) => iter.next(),
186 Self(LatLonsWrapper::SigIf(iter)) => iter.next(),
187 }
188 }
189
190 fn size_hint(&self) -> (usize, Option<usize>) {
191 match self {
192 Self(LatLonsWrapper::SigR(iter)) => iter.size_hint(),
193 Self(LatLonsWrapper::SigUR(iter)) => iter.size_hint(),
194 Self(LatLonsWrapper::SigIf(iter)) => iter.size_hint(),
195 }
196 }
197}
198
199impl From<RegularGridIterator> for GridPointLatLons {
200 fn from(value: RegularGridIterator) -> Self {
201 Self(LatLonsWrapper::SigR(value))
202 }
203}
204
205impl From<Unrotate<RegularGridIterator>> for GridPointLatLons {
206 fn from(value: Unrotate<RegularGridIterator>) -> Self {
207 Self(LatLonsWrapper::SigUR(value))
208 }
209}
210
211impl From<std::vec::IntoIter<(f32, f32)>> for GridPointLatLons {
212 fn from(value: std::vec::IntoIter<(f32, f32)>) -> Self {
213 Self(LatLonsWrapper::SigIf(value))
214 }
215}
216
217#[derive(Clone)]
218enum LatLonsWrapper {
219 SigR(RegularGridIterator),
220 SigUR(Unrotate<RegularGridIterator>),
221 SigIf(std::vec::IntoIter<(f32, f32)>),
222}
223
224pub trait GridPointIndex {
265 fn grid_shape(&self) -> (usize, usize);
268
269 fn scanning_mode(&self) -> &ScanningMode;
271
272 fn ij(&self) -> Result<GridPointIndexIterator, GribError> {
274 GridPointIndexIterator::new(self.grid_shape(), *self.scanning_mode())
275 }
276}
277
278#[derive(Clone)]
283pub struct GridPointIndexIterator {
284 major_len: usize,
285 minor_len: usize,
286 scanning_mode: ScanningMode,
287 major_pos: usize,
288 minor_pos: usize,
289 increments: bool,
290}
291
292impl GridPointIndexIterator {
293 pub(crate) fn new(
294 (i_len, j_len): (usize, usize),
295 scanning_mode: ScanningMode,
296 ) -> Result<Self, GribError> {
297 if scanning_mode.has_unsupported_flags() {
298 let ScanningMode(mode) = scanning_mode;
299 return Err(GribError::NotSupported(format!("scanning mode {mode}")));
300 }
301
302 let (major_len, minor_len) = if scanning_mode.is_consecutive_for_i() {
303 (j_len, i_len)
304 } else {
305 (i_len, j_len)
306 };
307
308 Ok(Self {
309 major_len,
310 minor_len,
311 scanning_mode,
312 minor_pos: 0,
313 major_pos: 0,
314 increments: true,
315 })
316 }
317}
318
319impl Iterator for GridPointIndexIterator {
320 type Item = (usize, usize);
321
322 fn next(&mut self) -> Option<Self::Item> {
323 if self.major_pos == self.major_len {
324 return None;
325 }
326
327 let minor = if self.increments {
328 self.minor_pos
329 } else {
330 self.minor_len - self.minor_pos - 1
331 };
332 let major = self.major_pos;
333
334 self.minor_pos += 1;
335 if self.minor_pos == self.minor_len {
336 self.major_pos += 1;
337 self.minor_pos = 0;
338 if self.scanning_mode.scans_alternating_rows() {
339 self.increments = !self.increments;
340 }
341 }
342
343 if self.scanning_mode.is_consecutive_for_i() {
344 Some((minor, major))
345 } else {
346 Some((major, minor))
347 }
348 }
349
350 fn size_hint(&self) -> (usize, Option<usize>) {
351 let len = (self.major_len - self.major_pos) * self.minor_len - self.minor_pos;
352 (len, Some(len))
353 }
354}
355
356#[cfg(test)]
357mod tests {
358 use super::*;
359 use crate::def::grib2::template::param_set::EarthShape;
360
361 #[test]
362 fn grid_definition_template_0() {
363 let data = GridDefinition::from_payload(
367 vec![
368 0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff, 0xff, 0xff, 0xff,
369 0xff, 0x01, 0x03, 0xcd, 0x39, 0xfa, 0x01, 0x03, 0xc9, 0xf6, 0xa3, 0x00, 0x00, 0x01,
370 0x00, 0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x02,
371 0xdb, 0xc9, 0x3d, 0x07, 0x09, 0x7d, 0xa4, 0x30, 0x01, 0x31, 0xcf, 0xc3, 0x08, 0xef,
372 0xdd, 0x5c, 0x00, 0x01, 0xe8, 0x48, 0x00, 0x01, 0x45, 0x85, 0x00,
373 ]
374 .into_boxed_slice(),
375 )
376 .unwrap();
377
378 let actual = GridDefinitionTemplateValues::try_from(&data).unwrap();
379 let expected = GridDefinitionTemplateValues::Template0(Template3_0 {
380 earth: EarthShape {
381 shape: 4,
382 spherical_earth_radius_scale_factor: 0xff,
383 spherical_earth_radius_scaled_value: 0xffffffff,
384 major_axis_scale_factor: 1,
385 major_axis_scaled_value: 63781370,
386 minor_axis_scale_factor: 1,
387 minor_axis_scaled_value: 63567523,
388 },
389 lat_lon: crate::def::grib2::template::param_set::LatLonGrid {
390 grid: crate::def::grib2::template::param_set::Grid {
391 ni: 256,
392 nj: 336,
393 initial_production_domain_basic_angle: 0,
394 basic_angle_subdivisions: 0xffffffff,
395 first_point_lat: 47958333,
396 first_point_lon: 118062500,
397 resolution_and_component_flags:
398 crate::def::grib2::template::param_set::ResolutionAndComponentFlags(
399 0b00110000,
400 ),
401 last_point_lat: 20041667,
402 last_point_lon: 149937500,
403 },
404 i_direction_inc: 125000,
405 j_direction_inc: 83333,
406 scanning_mode: crate::def::grib2::template::param_set::ScanningMode(0b00000000),
407 },
408 });
409 assert_eq!(actual, expected);
410 }
411}
412
413mod earth;
414mod flags;
415mod gaussian;
416mod helpers;
417mod lambert;
418mod latlon;
419mod polar_stereographic;
420mod rotated_ll;