1use crate::{fns, Point2d, Tile, TileLike, UtilesCoreError, UtilesCoreResult};
4use fns::flipy;
5
6#[derive(Debug, Clone, Copy, PartialEq)]
8pub struct TileZBox {
9 pub zoom: u8,
10 pub min: Point2d<u32>,
11 pub max: Point2d<u32>,
12}
13
14#[derive(Debug)]
16pub struct TileZBoxes {
17 pub ranges: Vec<TileZBox>,
18}
19
20#[derive(Debug)]
22pub struct TileZBoxIterator {
23 range: TileZBox,
24 cur_x: u32,
25 cur_y: u32,
26}
27
28impl TileZBox {
29 #[must_use]
31 pub fn new(min_x: u32, max_x: u32, min_y: u32, max_y: u32, zoom: u8) -> Self {
32 Self {
33 zoom,
34 min: Point2d::new(min_x, min_y),
35 max: Point2d::new(max_x, max_y),
36 }
37 }
38
39 #[must_use]
41 pub fn minx(&self) -> u32 {
42 self.min.x
43 }
44
45 #[must_use]
47 pub fn maxx(&self) -> u32 {
48 self.max.x
49 }
50
51 #[must_use]
53 pub fn miny(&self) -> u32 {
54 self.min.y
55 }
56
57 #[must_use]
59 pub fn maxy(&self) -> u32 {
60 self.max.y
61 }
62
63 #[must_use]
64 pub fn dx(&self) -> u32 {
65 self.max.x - self.min.x
66 }
67
68 #[must_use]
69 pub fn width(&self) -> u32 {
70 self.dx()
71 }
72
73 #[must_use]
74 pub fn dy(&self) -> u32 {
75 self.max.y - self.min.y
76 }
77
78 #[must_use]
79 pub fn height(&self) -> u32 {
80 self.dy()
81 }
82
83 #[must_use]
84 pub fn dxdy(&self) -> (u32, u32) {
85 (self.max.x - self.min.x, self.max.y - self.min.y)
86 }
87
88 #[must_use]
90 pub fn z(&self) -> u8 {
91 self.zoom
92 }
93
94 #[must_use]
96 pub fn zoom(&self) -> u8 {
97 self.zoom
98 }
99
100 #[must_use]
102 pub fn dimensions(&self) -> (u32, u32) {
103 (self.max.x - self.min.x + 1, self.max.y - self.min.y + 1)
104 }
105
106 #[must_use]
108 pub fn length(&self) -> u64 {
109 u64::from((self.max.x - self.min.x + 1) * (self.max.y - self.min.y + 1))
110 }
111
112 #[must_use]
114 pub fn size(&self) -> u64 {
115 self.length()
116 }
117
118 #[must_use]
120 pub fn flipy(&self) -> Self {
121 Self {
122 min: Point2d::new(self.min.x, flipy(self.max.y, self.zoom)),
123 max: Point2d::new(self.max.x, flipy(self.min.y, self.zoom)),
124 zoom: self.zoom,
125 }
126 }
127
128 #[must_use]
130 pub fn contains_tile<T: TileLike>(&self, tile: &T) -> bool {
131 tile.z() == self.zoom
132 && tile.x() >= self.min.x
133 && tile.x() <= self.max.x
134 && tile.y() >= self.min.y
135 && tile.y() <= self.max.y
136 }
137
138 #[must_use]
140 pub fn mbtiles_sql_where_prefix(&self, prefix: Option<&str>) -> String {
141 let col_prefix = prefix.unwrap_or_default();
142 let miny = flipy(self.min.y, self.zoom);
145 let maxy = flipy(self.max.y, self.zoom);
146 format!(
147 "(zoom_level = {} AND {}tile_column >= {} AND {}tile_column <= {} AND {}tile_row >= {} AND {}tile_row <= {})",
148 self.zoom,
149 col_prefix, self.min.x, col_prefix, self.max.x,
150 col_prefix, maxy, col_prefix, miny
151 )
152 }
153
154 #[must_use]
156 pub fn mbtiles_sql_where(&self) -> String {
157 self.mbtiles_sql_where_prefix(None)
158 }
159
160 #[must_use]
162 pub fn from_tile<T: TileLike + ?Sized>(tile: &T) -> Self {
163 Self {
164 zoom: tile.z(),
165 min: Point2d::new(tile.x(), tile.y()),
166 max: Point2d::new(tile.x(), tile.y()),
167 }
168 }
169
170 pub fn from_tiles(tiles: &[Tile]) -> UtilesCoreResult<Self> {
177 if tiles.is_empty() {
178 return Err(UtilesCoreError::AdHoc("No tiles provided".to_string()));
179 }
180
181 let expected_zoom = tiles[0].z;
182 let mut xmin = u32::MAX;
183 let mut xmax = u32::MIN;
184 let mut ymin = u32::MAX;
185 let mut ymax = u32::MIN;
186
187 for tile in tiles {
188 if tile.z != expected_zoom {
190 return Err(UtilesCoreError::AdHoc(
191 "Not all tiles have the same zoom level".to_string(),
192 ));
193 }
194
195 let x = tile.x();
196 let y = tile.y();
197
198 if x < xmin {
200 xmin = x;
201 }
202 if x > xmax {
203 xmax = x;
204 }
205
206 if y < ymin {
208 ymin = y;
209 }
210 if y > ymax {
211 ymax = y;
212 }
213 }
214 Ok(Self {
215 zoom: expected_zoom,
216 min: Point2d::new(xmin, ymin),
217 max: Point2d::new(xmax, ymax),
218 })
219 }
220
221 #[must_use]
223 pub fn zoom_in(&self) -> Self {
224 Self {
225 zoom: self.zoom + 1,
226 min: Point2d::new(self.min.x * 2, self.min.y * 2),
227 max: Point2d::new(self.max.x * 2 + 1, self.max.y * 2 + 1),
228 }
229 }
230
231 #[must_use]
233 pub fn zoom_depth(&self, depth: u8) -> Self {
234 let target_zoom = self.zoom + depth;
235 let mut zbox = *self;
236 while zbox.zoom < target_zoom {
237 zbox = zbox.zoom_in();
238 }
239 zbox
240 }
241}
242
243impl TileZBoxIterator {
244 #[must_use]
246 pub fn new(minx: u32, maxx: u32, miny: u32, maxy: u32, zoom: u8) -> Self {
247 Self {
248 range: TileZBox::new(minx, maxx, miny, maxy, zoom),
249 cur_x: minx,
250 cur_y: miny,
251 }
252 }
253}
254
255impl From<TileZBox> for TileZBoxIterator {
256 fn from(range: TileZBox) -> Self {
257 Self::new(
258 range.minx(),
259 range.maxx(),
260 range.miny(),
261 range.maxy(),
262 range.zoom(),
263 )
264 }
265}
266
267impl Iterator for TileZBoxIterator {
268 type Item = (u32, u32, u8);
269
270 fn next(&mut self) -> Option<Self::Item> {
271 if self.cur_x > self.range.max.x {
272 self.cur_x = self.range.min.x;
273 self.cur_y += 1;
274 }
275 if self.cur_y > self.range.max.y {
276 return None;
277 }
278 let tile = (self.cur_x, self.cur_y, self.range.zoom);
279 self.cur_x += 1;
280 Some(tile)
281 }
282
283 fn size_hint(&self) -> (usize, Option<usize>) {
284 let size = ((self.range.max.x - self.range.min.x + 1)
285 * (self.range.max.y - self.range.min.y + 1)) as usize;
286 (size, Some(size))
287 }
288}
289
290impl IntoIterator for TileZBox {
291 type Item = (u32, u32, u8);
292 type IntoIter = TileZBoxIterator;
293
294 fn into_iter(self) -> Self::IntoIter {
295 TileZBoxIterator::from(self)
296 }
297}
298
299impl IntoIterator for TileZBoxes {
300 type Item = (u32, u32, u8);
301 type IntoIter = std::vec::IntoIter<Self::Item>;
302
303 fn into_iter(self) -> Self::IntoIter {
304 self.ranges
305 .into_iter()
306 .flat_map(std::iter::IntoIterator::into_iter)
307 .collect::<Vec<Self::Item>>()
308 .into_iter()
309 }
310}
311
312impl TileZBoxes {
313 #[must_use]
315 pub fn new(minx: u32, maxx: u32, miny: u32, maxy: u32, zoom: u8) -> Self {
316 Self {
317 ranges: vec![TileZBox::new(minx, maxx, miny, maxy, zoom)],
318 }
319 }
320
321 #[must_use]
323 pub fn flipy(&self) -> Self {
324 Self {
325 ranges: self.ranges.iter().map(TileZBox::flipy).collect(),
326 }
327 }
328
329 #[must_use]
331 pub fn length(&self) -> u64 {
332 self.ranges.iter().map(TileZBox::length).sum()
333 }
334
335 #[must_use]
337 pub fn mbtiles_sql_where(&self, prefix: Option<&str>) -> String {
338 self.ranges
339 .iter()
340 .map(move |r| r.mbtiles_sql_where_prefix(prefix))
341 .collect::<Vec<String>>()
342 .join(" OR ")
343 }
344}
345
346impl From<TileZBox> for TileZBoxes {
347 fn from(range: TileZBox) -> Self {
348 Self {
349 ranges: vec![range],
350 }
351 }
352}
353
354impl From<Vec<TileZBox>> for TileZBoxes {
355 fn from(ranges: Vec<TileZBox>) -> Self {
356 Self { ranges }
357 }
358}