Skip to main content

grib/grid/
latlon.rs

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) -> &param_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            // grid point definition extracted from
112            // testdata/CMC_glb_TMP_ISBL_1_latlon.24x.24_2021051800_P000.grib2
113            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}