1use super::helpers::{RegularGridIterator, evenly_spaced_degrees, evenly_spaced_longitudes};
2use crate::{GridPointIndex, LatLons, def::grib2::template::param_set, error::GribError};
3
4impl crate::GridShortName for param_set::LatLonGrid {
5 fn short_name(&self) -> &'static str {
6 "regular_ll"
7 }
8}
9
10impl GridPointIndex for param_set::LatLonGrid {
11 fn grid_shape(&self) -> (usize, usize) {
12 (self.grid.ni as usize, self.grid.nj as usize)
13 }
14
15 fn scanning_mode(&self) -> ¶m_set::ScanningMode {
16 &self.scanning_mode
17 }
18}
19
20impl LatLons for param_set::LatLonGrid {
21 type Iter<'a> = RegularGridIterator;
22
23 fn latlons<'a>(&'a self) -> Result<Self::Iter<'a>, GribError> {
24 if !self.is_consistent_for_j() {
25 return Err(GribError::InvalidValueError(
26 "Latitudes for first/last grid points are not consistent with scanning mode"
27 .to_owned(),
28 ));
29 }
30
31 let ij = self.ij()?;
32 let lat = evenly_spaced_degrees(
33 self.grid.first_point_lat as f32,
34 self.grid.last_point_lat as f32,
35 (self.grid.nj - 1) as usize,
36 );
37 let lon = evenly_spaced_longitudes(
38 self.grid.first_point_lon,
39 self.grid.last_point_lon,
40 (self.grid.ni - 1) as usize,
41 self.scanning_mode,
42 );
43
44 let iter = RegularGridIterator::new(lat, lon, ij);
45 Ok(iter)
46 }
47}
48
49impl param_set::LatLonGrid {
50 pub(crate) fn is_consistent_for_j(&self) -> bool {
51 let lat_diff = self.grid.last_point_lat - self.grid.first_point_lat;
52 !((lat_diff > 0) ^ self.scanning_mode.scans_positively_for_j())
53 }
54}
55
56#[cfg(test)]
57mod tests {
58 use super::*;
59
60 macro_rules! test_lat_lon_calculation_for_inconsistent_longitude_definitions {
61 ($((
62 $name:ident,
63 $grid:expr,
64 $scanning_mode:expr,
65 $expected_head:expr,
66 $expected_tail:expr
67 ),)*) => ($(
68 #[test]
69 fn $name() {
70 let grid = param_set::LatLonGrid {
71 grid: $grid,
72 i_direction_inc: 0xffffffff,
73 j_direction_inc: 0xffffffff,
74 scanning_mode: $scanning_mode,
75 };
76 let latlons = grid.latlons();
77 assert!(latlons.is_ok());
78
79 let latlons = latlons.unwrap();
80 let actual = latlons.clone().take(3).collect::<Vec<_>>();
81 let expected = $expected_head;
82 assert_eq!(actual, expected);
83
84 let (len, _) = latlons.size_hint();
85 let actual = latlons.skip(len - 3).collect::<Vec<_>>();
86 let expected = $expected_tail;
87 assert_eq!(actual, expected);
88 }
89 )*);
90 }
91
92 test_lat_lon_calculation_for_inconsistent_longitude_definitions! {
93 (
94 lat_lon_calculation_for_increasing_longitudes_and_positive_direction_scan,
95 param_set::Grid {
96 ni: 1500,
97 nj: 751,
98 initial_production_domain_basic_angle: 0,
99 basic_angle_subdivisions: 0xffffffff,
100 first_point_lat: -90000000,
101 first_point_lon: 0,
102 resolution_and_component_flags: param_set::ResolutionAndComponentFlags(0b00110000),
103 last_point_lat: 90000000,
104 last_point_lon: 359760000,
105 },
106 param_set::ScanningMode(0b01000000),
107 vec![(-90.0, 0.0), (-90.0, 0.24), (-90.0, 0.48)],
108 vec![(90.0, 359.28), (90.0, 359.52), (90.0, 359.76)]
109 ),
110 (
111 lat_lon_calculation_for_decreasing_longitudes_and_positive_direction_scan,
114 param_set::Grid {
115 ni: 1500,
116 nj: 751,
117 initial_production_domain_basic_angle: 0,
118 basic_angle_subdivisions: 0xffffffff,
119 first_point_lat: -90000000,
120 first_point_lon: 180000000,
121 resolution_and_component_flags: param_set::ResolutionAndComponentFlags(0b00110000),
122 last_point_lat: 90000000,
123 last_point_lon: 179760000,
124 },
125 param_set::ScanningMode(0b01000000),
126 vec![(-90.0, 180.0), (-90.0, 180.24), (-90.0, 180.48)],
127 vec![(90.0, 179.28003), (90.0, 179.52002), (90.0, 179.76001)]
128 ),
129 (
130 lat_lon_calculation_for_decreasing_longitudes_and_negative_direction_scan,
131 param_set::Grid {
132 ni: 1500,
133 nj: 751,
134 initial_production_domain_basic_angle: 0,
135 basic_angle_subdivisions: 0xffffffff,
136 first_point_lat: -90000000,
137 first_point_lon: 359760000,
138 resolution_and_component_flags: param_set::ResolutionAndComponentFlags(0b00110000),
139 last_point_lat: 90000000,
140 last_point_lon: 0,
141 },
142 param_set::ScanningMode(0b11000000),
143 vec![(-90.0, 359.76), (-90.0, 359.52), (-90.0, 359.28)],
144 vec![(90.0, 0.48), (90.0, 0.24), (90.0, 0.0)]
145 ),
146 (
147 lat_lon_calculation_for_increasing_longitudes_and_negative_direction_scan,
148 param_set::Grid {
149 ni: 1500,
150 nj: 751,
151 initial_production_domain_basic_angle: 0,
152 basic_angle_subdivisions: 0xffffffff,
153 first_point_lat: -90000000,
154 first_point_lon: 179760000,
155 resolution_and_component_flags: param_set::ResolutionAndComponentFlags(0b00110000),
156 last_point_lat: 90000000,
157 last_point_lon: 180000000,
158 },
159 param_set::ScanningMode(0b11000000),
160 vec![(-90.0, 179.76001), (-90.0, 179.52002), (-90.0, 179.28003)],
161 vec![(90.0, 180.48), (90.0, 180.24), (90.0, 180.0)]
162 ),
163 }
164
165 macro_rules! test_consistencies_between_lat_lon_and_scanning_mode {
166 ($((
167 $name:ident,
168 $first_point_lat:expr,
169 $first_point_lon:expr,
170 $last_point_lat:expr,
171 $last_point_lon:expr,
172 $scanning_mode:expr,
173 $expected_for_j:expr
174 ),)*) => ($(
175 #[test]
176 fn $name() {
177 let grid = param_set::LatLonGrid {
178 grid: param_set::Grid {
179 ni: 1,
180 nj: 1,
181 initial_production_domain_basic_angle: 0,
182 basic_angle_subdivisions: 0xffffffff,
183 first_point_lat: $first_point_lat,
184 first_point_lon: $first_point_lon,
185 resolution_and_component_flags:
186 param_set::ResolutionAndComponentFlags(0b00110000),
187 last_point_lat: $last_point_lat,
188 last_point_lon: $last_point_lon,
189 },
190 i_direction_inc: 0xffffffff,
191 j_direction_inc: 0xffffffff,
192 scanning_mode: param_set::ScanningMode($scanning_mode),
193 };
194 assert_eq!(grid.is_consistent_for_j(), $expected_for_j);
195 }
196 )*);
197 }
198
199 test_consistencies_between_lat_lon_and_scanning_mode! {
200 (
201 consistency_between_lat_decrease_and_scanning_mode_0b00000000,
202 37_000_000,
203 140_000_000,
204 36_000_000,
205 141_000_000,
206 0b00000000,
207 true
208 ),
209 (
210 consistency_between_lat_decrease_and_scanning_mode_0b01000000,
211 37_000_000,
212 140_000_000,
213 36_000_000,
214 141_000_000,
215 0b01000000,
216 false
217 ),
218 (
219 consistency_between_lat_increase_and_scanning_mode_0b00000000,
220 36_000_000,
221 140_000_000,
222 37_000_000,
223 141_000_000,
224 0b00000000,
225 false
226 ),
227 (
228 consistency_between_lat_increase_and_scanning_mode_0b01000000,
229 36_000_000,
230 140_000_000,
231 37_000_000,
232 141_000_000,
233 0b01000000,
234 true
235 ),
236 }
237}