use std::fmt;
use crate::error::Error;
use crate::geom::{Transform, Vec2};
#[derive(Debug, Clone, Copy)]
pub struct BBox {
north_west: Vec2,
south_east: Vec2,
}
#[derive(Debug, Clone, Copy)]
pub struct TileId {
x: u32,
y: u32,
z: u32,
}
pub struct MapGrid {
srid: i32,
bbox: BBox,
}
impl TileId {
pub fn x(&self) -> u32 {
self.x
}
pub fn y(&self) -> u32 {
self.y
}
pub fn z(&self) -> u32 {
self.z
}
}
impl fmt::Display for TileId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}/{}/{}", self.z, self.x, self.y)
}
}
impl BBox {
pub fn new(north_west: Vec2, south_east: Vec2) -> Self {
BBox { north_west, south_east }
}
pub fn x_min(&self) -> f64 {
self.north_west.x.min(self.south_east.x)
}
pub fn x_max(&self) -> f64 {
self.north_west.x.max(self.south_east.x)
}
pub fn y_min(&self) -> f64 {
self.north_west.y.min(self.south_east.y)
}
pub fn y_max(&self) -> f64 {
self.north_west.y.max(self.south_east.y)
}
fn x_span(&self) -> f64 {
self.south_east.x - self.north_west.x
}
fn y_span(&self) -> f64 {
self.south_east.y - self.north_west.y
}
}
const SCALE: [f64; 32] = [
1.0 / (1 << 0) as f64, 1.0 / (1 << 1) as f64,
1.0 / (1 << 2) as f64, 1.0 / (1 << 3) as f64,
1.0 / (1 << 4) as f64, 1.0 / (1 << 5) as f64,
1.0 / (1 << 6) as f64, 1.0 / (1 << 7) as f64,
1.0 / (1 << 8) as f64, 1.0 / (1 << 9) as f64,
1.0 / (1 << 10) as f64, 1.0 / (1 << 11) as f64,
1.0 / (1 << 12) as f64, 1.0 / (1 << 13) as f64,
1.0 / (1 << 14) as f64, 1.0 / (1 << 15) as f64,
1.0 / (1 << 16) as f64, 1.0 / (1 << 17) as f64,
1.0 / (1 << 18) as f64, 1.0 / (1 << 19) as f64,
1.0 / (1 << 20) as f64, 1.0 / (1 << 21) as f64,
1.0 / (1 << 22) as f64, 1.0 / (1 << 23) as f64,
1.0 / (1 << 24) as f64, 1.0 / (1 << 25) as f64,
1.0 / (1 << 26) as f64, 1.0 / (1 << 27) as f64,
1.0 / (1 << 28) as f64, 1.0 / (1 << 29) as f64,
1.0 / (1 << 30) as f64, 1.0 / (1 << 31) as f64,
];
impl TileId {
pub fn new(x: u32, y: u32, z: u32) -> Result<Self, Error> {
TileId::check_valid(x, y, z)?;
Ok(TileId { x, y, z })
}
fn check_valid(x: u32, y: u32, z: u32) -> Result<(), Error> {
if z > 31 {
return Err(Error::InvalidTid());
}
let s = 1 << z;
if x < s && y < s {
Ok(())
} else {
Err(Error::InvalidTid())
}
}
}
impl MapGrid {
pub fn new(srid: i32, bbox: BBox) -> Self {
MapGrid { srid, bbox }
}
pub fn new_web_mercator() -> Self {
const HALF_SIZE_M: f64 = 20037508.3427892480;
let srid = 3857;
let north_west = Vec2::new(-HALF_SIZE_M, HALF_SIZE_M);
let south_east = Vec2::new(HALF_SIZE_M, -HALF_SIZE_M);
let bbox = BBox::new(north_west, south_east);
MapGrid::new(srid, bbox)
}
pub fn srid(&self) -> i32 {
self.srid
}
pub fn bbox(&self) -> BBox {
self.bbox.clone()
}
pub fn tile_bbox(&self, tid: TileId) -> BBox {
let tz = SCALE[tid.z as usize];
let sx = self.bbox.x_span() * tz;
let sy = self.bbox.y_span() * tz;
let tx = self.bbox.north_west.x;
let ty = self.bbox.north_west.y;
let t = Transform::new_scale(sx, sy).translate(tx, ty);
let tidx = tid.x as f64;
let tidy = tid.y as f64;
let north_west = t * Vec2::new(tidx, tidy);
let south_east = t * Vec2::new(tidx + 1.0, tidy + 1.0);
BBox::new(north_west, south_east)
}
pub fn tile_transform(&self, tid: TileId) -> Transform {
let tx = self.bbox.north_west.x;
let ty = self.bbox.north_west.y;
let tz = (1 << tid.z) as f64;
let sx = tz / self.bbox.x_span();
let sy = tz / self.bbox.y_span();
Transform::new_translate(-tx, -ty)
.scale(sx, sy)
.translate(-(tid.x as f64), -(tid.y as f64))
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_tile_bbox() {
let g = MapGrid::new_web_mercator();
let tid = TileId::new(0, 0, 0).unwrap();
let b = g.tile_bbox(tid);
assert_eq!(b.north_west,
Vec2::new(-20037508.3427892480, 20037508.3427892480));
assert_eq!(b.south_east,
Vec2::new(20037508.3427892480, -20037508.3427892480));
let tid = TileId::new(0, 0, 1).unwrap();
let b = g.tile_bbox(tid);
assert_eq!(b.north_west,
Vec2::new(-20037508.3427892480, 20037508.3427892480));
assert_eq!(b.south_east,
Vec2::new(0.0, 0.0));
let tid = TileId::new(1, 1, 1).unwrap();
let b = g.tile_bbox(tid);
assert_eq!(b.north_west,
Vec2::new(0.0, 0.0));
assert_eq!(b.south_east,
Vec2::new(20037508.3427892480, -20037508.3427892480));
let tid = TileId::new(246, 368, 10).unwrap();
let b = g.tile_bbox(tid);
assert_eq!(b.north_west,
Vec2::new(-10410111.756214727, 5635549.221409475));
assert_eq!(b.south_east,
Vec2::new(-10370975.997732716, 5596413.462927466));
}
#[test]
fn test_tile_transform() {
let g = MapGrid::new_web_mercator();
let tid = TileId::new(0, 0, 0).unwrap();
let t = g.tile_transform(tid);
assert_eq!(Vec2::new(0.0, 0.0),
t * Vec2::new(-20037508.3427892480, 20037508.3427892480));
assert_eq!(Vec2::new(1.0, 1.0),
t * Vec2::new(20037508.3427892480, -20037508.3427892480));
let tid = TileId::new(0, 0, 1).unwrap();
let t = g.tile_transform(tid);
assert_eq!(Vec2::new(0.0, 0.0),
t * Vec2::new(-20037508.3427892480, 20037508.3427892480));
assert_eq!(Vec2::new(1.0, 1.0),
t * Vec2::new(0.0, 0.0));
let tid = TileId::new(1, 1, 1).unwrap();
let t = g.tile_transform(tid);
assert_eq!(Vec2::new(0.0, 0.0),
t * Vec2::new(0.0, 0.0));
assert_eq!(Vec2::new(1.0, 1.0),
t * Vec2::new(20037508.3427892480, -20037508.3427892480));
let tid = TileId::new(246, 368, 10).unwrap();
let t = g.tile_transform(tid);
assert_eq!(Vec2::new(0.0, 0.0),
t * Vec2::new(-10410111.756214727, 5635549.221409475));
assert_eq!(Vec2::new(1.0, 0.9999999999999716),
t * Vec2::new(-10370975.997732716, 5596413.462927466));
}
}