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