Skip to main content

utiles_core/
tile_like.rs

1use crate::bbox::WebBBox;
2#[cfg(feature = "pmtiles")]
3use crate::pmtiles;
4use crate::{
5    BBox, LngLat, Tile, TileZBox, flipy, neighbors, neighbors_wrap_x, xyz2rmid,
6};
7
8/// Trait def for tile-like objects/structs/things/whatevers
9pub trait TileLike {
10    /// x coordinate (column)
11    fn x(&self) -> u32;
12
13    /// y coordinate (row -- flipped for TMS)
14    fn y(&self) -> u32;
15
16    /// z coordinate (zoom level)
17    fn z(&self) -> u8;
18
19    /// zoom level
20    #[must_use]
21    fn zoom(&self) -> u8 {
22        self.z()
23    }
24
25    /// x coordinate
26    fn yflip(&self) -> u32 {
27        flipy(self.y(), self.z())
28    }
29
30    /// both bc I keep forgetting which is which
31    fn flipy(&self) -> u32 {
32        flipy(self.y(), self.z())
33    }
34
35    fn yup(&self) -> u32 {
36        flipy(self.y(), self.z())
37    }
38
39    fn xyz_str_fslash(&self) -> String {
40        format!("{}/{}/{}", self.x(), self.y(), self.z())
41    }
42
43    fn zxy_str_fslash(&self) -> String {
44        format!("{}/{}/{}", self.z(), self.x(), self.y())
45    }
46
47    fn xyz_str_sep(&self, sep: &str) -> String {
48        format!("{}{}{}{}{}", self.x(), sep, self.y(), sep, self.z())
49    }
50
51    fn zxy_str_sep(&self, sep: &str) -> String {
52        format!("{}{}{}{}{}", self.z(), sep, self.x(), sep, self.y())
53    }
54
55    /// Return Tile struct
56    #[must_use]
57    fn tile(&self) -> Tile {
58        Tile::new(self.x(), self.y(), self.z())
59    }
60
61    /// Return if the tile is valid (x, y is in bounds for zoom level z)
62    #[must_use]
63    fn valid(&self) -> bool {
64        crate::valid(self.x(), self.y(), self.z())
65    }
66
67    /// Return the ul (upper left) corner of the tile
68    #[must_use]
69    fn ul(&self) -> LngLat {
70        crate::ul(self.x(), self.y(), self.z())
71    }
72
73    /// Return the ur (upper right) corner of the tile
74    #[must_use]
75    fn ur(&self) -> LngLat {
76        crate::ul(self.x() + 1, self.y(), self.z())
77    }
78
79    /// Return the lr (lower right) corner of the tile
80    #[must_use]
81    fn lr(&self) -> LngLat {
82        crate::ul(self.x() + 1, self.y() + 1, self.z())
83    }
84
85    /// Return the ll (lower left) corner of the tile
86    #[must_use]
87    fn ll(&self) -> LngLat {
88        crate::ul(self.x(), self.y() + 1, self.z())
89    }
90
91    /// Return the quadkey for the tile
92    #[must_use]
93    fn quadkey(&self) -> String {
94        crate::xyz2quadkey(self.x(), self.y(), self.z())
95    }
96
97    /// Return the quadkey for the tile (alias for quadkey)
98    #[must_use]
99    fn qk(&self) -> String {
100        crate::xyz2quadkey(self.x(), self.y(), self.z())
101    }
102
103    /// Return the pmtile-id for the tile
104    #[cfg(feature = "pmtiles")]
105    #[must_use]
106    fn pmtileid(&self) -> u64 {
107        pmtiles::xyz2pmid(self.x(), self.y(), self.z())
108    }
109
110    /// Return the pmtile id
111    #[cfg(feature = "pmtiles")]
112    #[must_use]
113    fn pmid(&self) -> u64 {
114        self.pmtileid()
115    }
116
117    /// Return the row major id for the tile
118    #[must_use]
119    fn row_major_id(&self) -> u64 {
120        xyz2rmid(self.x(), self.y(), self.z())
121    }
122
123    /// Return the row major id for the tile (alias for `row_major_id`)
124    #[must_use]
125    fn rmid(&self) -> u64 {
126        self.row_major_id()
127    }
128
129    /// Return the geo-bbox tuple for the tile (west, south, east, north)
130    #[must_use]
131    fn bbox(&self) -> (f64, f64, f64, f64) {
132        let ul = self.ul();
133        let lr = self.lr();
134        (ul.lng(), lr.lat(), lr.lng(), ul.lat())
135    }
136
137    #[must_use]
138    fn geobbox(&self) -> BBox {
139        let ul = self.ul();
140        let lr = self.lr();
141        BBox::new(ul.lng(), lr.lat(), lr.lng(), ul.lat())
142    }
143
144    #[must_use]
145    fn webbbox(&self) -> WebBBox {
146        self.geobbox().into()
147    }
148
149    #[must_use]
150    fn bbox_string(&self) -> String {
151        let (w, s, e, n) = self.bbox();
152        format!("[{w},{s},{e},{n}]")
153    }
154
155    /// Return the center of the tile as a `LngLat`
156    #[must_use]
157    fn center(&self) -> LngLat {
158        let ul = self.ul();
159        let lr = self.lr();
160        LngLat::new(
161            f64::midpoint(ul.lng(), lr.lng()),
162            f64::midpoint(ul.lat(), lr.lat()),
163        )
164    }
165
166    /// Return json array string for tile with spaces after commas
167    #[must_use]
168    fn json_arr(&self) -> String {
169        format!("[{}, {}, {}]", self.x(), self.y(), self.z())
170    }
171
172    /// Return json array string for tile with no spaces after commas
173    #[must_use]
174    fn json_arr_min(&self) -> String {
175        format!("[{},{},{}]", self.x(), self.y(), self.z())
176    }
177
178    /// Return json object string for tile
179    #[must_use]
180    fn json_obj(&self) -> String {
181        format!(
182            "{{\"x\":{}, \"y\":{}, \"z\":{}}}",
183            self.x(),
184            self.y(),
185            self.z()
186        )
187    }
188
189    /// Return json object string for tile
190    #[must_use]
191    fn json(&self) -> String {
192        self.json_obj()
193    }
194
195    /// Return tuple string for tile `(x, y, z)`
196    #[must_use]
197    fn tuple_string(&self) -> String {
198        format!("({}, {}, {})", self.x(), self.y(), self.z())
199    }
200
201    /// Return sql `WHERE` clause for querying mbtiles (y is up)
202    #[must_use]
203    fn mbtiles_sql_where(&self) -> String {
204        // classic mbtiles sqlite query:
205        // 'SELECT tile_data FROM tiles WHERE zoom_level = ? AND tile_column = ? AND tile_row = ?',
206
207        // flip y for tms (default for mbtiles)
208        format!(
209            "(zoom_level = {} AND tile_column = {} AND tile_row = {})",
210            self.z(),
211            self.x(),
212            flipy(self.y(), self.z())
213        )
214    }
215
216    /// return zbox for tile-like
217    #[must_use]
218    fn zbox(&self) -> TileZBox {
219        TileZBox::from_tile(self)
220    }
221
222    /// Return children-zbox for tile-like at optional depth (default 1)
223    #[must_use]
224    fn children_zbox(&self, depth: Option<u8>) -> TileZBox {
225        let d = depth.unwrap_or(1);
226        let target_zoom = self.z() + d;
227        let mut zbox = TileZBox::from_tile(self);
228        while zbox.z() < target_zoom {
229            zbox = zbox.zoom_in();
230        }
231        zbox
232    }
233
234    /// Return neighbor tiles for tile-like optionally wrapping x
235    #[must_use]
236    fn neighbors(&self, wrapx: bool) -> Vec<Tile> {
237        if wrapx {
238            neighbors_wrap_x(self.x(), self.y(), self.z())
239        } else {
240            neighbors(self.x(), self.y(), self.z())
241        }
242    }
243}