tile_grid/
tms_iterator.rs

1//
2// Copyright (c) Pirmin Kalberer. All rights reserved.
3// Licensed under the MIT License. See LICENSE file in the project root for full license information.
4//
5
6//! TMS iterators
7
8use crate::{MinMax, Xyz};
9
10pub trait TileIterator: Iterator<Item = Xyz> {}
11
12/// Level-by-level iterator
13pub struct XyzIterator {
14    z: u8,
15    x: u64,
16    y: u64,
17    z_min: u8,
18    z_max: u8,
19    limits: Vec<MinMax>,
20    finished: bool,
21}
22
23impl XyzIterator {
24    pub(crate) fn new(z_min: u8, z_max: u8, limits: Vec<MinMax>) -> XyzIterator {
25        // "Empty" iterator for invalid parameters
26        const EMPTY_ITER: XyzIterator = XyzIterator {
27            z: 0,
28            x: 0,
29            y: 0,
30            z_min: 0,
31            z_max: 0,
32            limits: Vec::new(),
33            finished: true,
34        };
35        if z_min <= z_max {
36            if let Some(limit) = &limits.first() {
37                let z_max = std::cmp::min(z_max, z_min + limits.len().saturating_sub(1) as u8);
38                XyzIterator {
39                    z: z_min,
40                    x: limit.x_min,
41                    y: limit.y_min,
42                    z_min,
43                    z_max,
44                    limits,
45                    finished: false,
46                }
47            } else {
48                EMPTY_ITER
49            }
50        } else {
51            EMPTY_ITER
52        }
53    }
54}
55
56impl Iterator for XyzIterator {
57    type Item = Xyz;
58
59    fn next(&mut self) -> Option<Self::Item> {
60        if self.finished {
61            return None;
62        }
63        let current = Xyz::new(self.x, self.y, self.z);
64        let limit = &self.limits[(self.z - self.z_min) as usize];
65        if self.y < limit.y_max {
66            self.y += 1;
67        } else if self.x < limit.x_max {
68            self.x += 1;
69            self.y = limit.y_min;
70        } else if self.z < self.z_max {
71            self.z += 1;
72            let limit = &self.limits[(self.z - self.z_min) as usize];
73            self.x = limit.x_min;
74            self.y = limit.y_min;
75        } else {
76            self.finished = true;
77        }
78        Some(current)
79    }
80}
81
82impl TileIterator for XyzIterator {}
83
84#[cfg(test)]
85mod test {
86    use crate::{tms, Xyz};
87
88    #[test]
89    fn test_mercator_iter() {
90        let tms = tms().lookup("WebMercatorQuad").unwrap();
91        let griditer = tms.xyz_iterator(&tms.xy_bbox(), 0, 2);
92        let cells = griditer.collect::<Vec<_>>();
93        assert_eq!(
94            cells,
95            vec![
96                Xyz::new(0, 0, 0),
97                Xyz::new(0, 0, 1),
98                Xyz::new(0, 1, 1),
99                Xyz::new(1, 0, 1),
100                Xyz::new(1, 1, 1),
101                Xyz::new(0, 0, 2),
102                Xyz::new(0, 1, 2),
103                Xyz::new(0, 2, 2),
104                Xyz::new(0, 3, 2),
105                Xyz::new(1, 0, 2),
106                Xyz::new(1, 1, 2),
107                Xyz::new(1, 2, 2),
108                Xyz::new(1, 3, 2),
109                Xyz::new(2, 0, 2),
110                Xyz::new(2, 1, 2),
111                Xyz::new(2, 2, 2),
112                Xyz::new(2, 3, 2),
113                Xyz::new(3, 0, 2),
114                Xyz::new(3, 1, 2),
115                Xyz::new(3, 2, 2),
116                Xyz::new(3, 3, 2)
117            ]
118        );
119
120        let griditer = tms.xyz_iterator(&tms.xy_bbox(), 1, 2);
121        let cells = griditer.collect::<Vec<_>>();
122        assert_eq!(
123            cells,
124            vec![
125                Xyz::new(0, 0, 1),
126                Xyz::new(0, 1, 1),
127                Xyz::new(1, 0, 1),
128                Xyz::new(1, 1, 1),
129                Xyz::new(0, 0, 2),
130                Xyz::new(0, 1, 2),
131                Xyz::new(0, 2, 2),
132                Xyz::new(0, 3, 2),
133                Xyz::new(1, 0, 2),
134                Xyz::new(1, 1, 2),
135                Xyz::new(1, 2, 2),
136                Xyz::new(1, 3, 2),
137                Xyz::new(2, 0, 2),
138                Xyz::new(2, 1, 2),
139                Xyz::new(2, 2, 2),
140                Xyz::new(2, 3, 2),
141                Xyz::new(3, 0, 2),
142                Xyz::new(3, 1, 2),
143                Xyz::new(3, 2, 2),
144                Xyz::new(3, 3, 2)
145            ]
146        );
147
148        let griditer = tms.xyz_iterator(&tms.xy_bbox(), 0, 0);
149        let cells = griditer.collect::<Vec<_>>();
150        assert_eq!(cells, vec![Xyz::new(0, 0, 0)]);
151    }
152
153    #[test]
154    fn invalid_iters() {
155        let tms = tms().lookup("WebMercatorQuad").unwrap();
156
157        let griditer = tms.xyz_iterator(&tms.xy_bbox(), 3, 2);
158        assert_eq!(griditer.count(), 0);
159
160        // z_min >= (z_max - z_min)
161        // Did panic in earlier versions
162        let griditer = tms.xyz_iterator(&tms.xy_bbox(), 2, 3);
163        assert_eq!(griditer.count(), 80);
164    }
165}