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