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