use crate::error::Error;
use pointy::{Pt64, Transform64};
use std::fmt;
#[derive(Clone, Copy, Debug)]
pub struct BBox {
north_west: Pt64,
south_east: Pt64,
}
#[derive(Clone, Copy, Debug)]
pub struct TileId {
x: u32,
y: u32,
z: u32,
}
#[derive(Clone, Debug)]
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: Pt64, south_east: Pt64) -> 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 Default for MapGrid {
fn default() -> Self {
const HALF_SIZE_M: f64 = 20_037_508.342_789_248;
const WEB_MERCATOR_SRID: i32 = 3857;
let srid = WEB_MERCATOR_SRID;
let north_west = Pt64(-HALF_SIZE_M, HALF_SIZE_M);
let south_east = Pt64(HALF_SIZE_M, -HALF_SIZE_M);
let bbox = BBox::new(north_west, south_east);
MapGrid { srid, bbox }
}
}
impl MapGrid {
pub fn new(srid: i32, bbox: BBox) -> Self {
MapGrid { srid, bbox }
}
pub fn srid(&self) -> i32 {
self.srid
}
pub fn bbox(&self) -> BBox {
self.bbox
}
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 = Transform64::with_scale(sx, sy).translate(tx, ty);
let tidx = f64::from(tid.x);
let tidy = f64::from(tid.y);
let north_west = t * Pt64(tidx, tidy);
let south_east = t * Pt64(tidx + 1.0, tidy + 1.0);
BBox::new(north_west, south_east)
}
pub fn tile_transform(&self, tid: TileId) -> Transform64 {
let tx = self.bbox.north_west.x();
let ty = self.bbox.north_west.y();
let tz = f64::from(1 << tid.z);
let sx = tz / self.bbox.x_span();
let sy = tz / self.bbox.y_span();
Transform64::with_translate(-tx, -ty)
.scale(sx, sy)
.translate(-f64::from(tid.x), -f64::from(tid.y))
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_tile_bbox() {
let g = MapGrid::default();
let tid = TileId::new(0, 0, 0).unwrap();
let b = g.tile_bbox(tid);
assert_eq!(
b.north_west,
Pt64(-20037508.3427892480, 20037508.3427892480)
);
assert_eq!(
b.south_east,
Pt64(20037508.3427892480, -20037508.3427892480)
);
let tid = TileId::new(0, 0, 1).unwrap();
let b = g.tile_bbox(tid);
assert_eq!(
b.north_west,
Pt64(-20037508.3427892480, 20037508.3427892480)
);
assert_eq!(b.south_east, Pt64(0.0, 0.0));
let tid = TileId::new(1, 1, 1).unwrap();
let b = g.tile_bbox(tid);
assert_eq!(b.north_west, Pt64(0.0, 0.0));
assert_eq!(
b.south_east,
Pt64(20037508.3427892480, -20037508.3427892480)
);
let tid = TileId::new(246, 368, 10).unwrap();
let b = g.tile_bbox(tid);
assert_eq!(b.north_west, Pt64(-10410111.756214727, 5635549.221409475));
assert_eq!(b.south_east, Pt64(-10370975.997732716, 5596413.462927466));
}
#[test]
fn test_tile_transform() {
let g = MapGrid::default();
let tid = TileId::new(0, 0, 0).unwrap();
let t = g.tile_transform(tid);
assert_eq!(
Pt64(0.0, 0.0),
t * Pt64(-20037508.3427892480, 20037508.3427892480)
);
assert_eq!(
Pt64(1.0, 1.0),
t * Pt64(20037508.3427892480, -20037508.3427892480)
);
let tid = TileId::new(0, 0, 1).unwrap();
let t = g.tile_transform(tid);
assert_eq!(
Pt64(0.0, 0.0),
t * Pt64(-20037508.3427892480, 20037508.3427892480)
);
assert_eq!(Pt64(1.0, 1.0), t * Pt64(0.0, 0.0));
let tid = TileId::new(1, 1, 1).unwrap();
let t = g.tile_transform(tid);
assert_eq!(Pt64(0.0, 0.0), t * Pt64(0.0, 0.0));
assert_eq!(
Pt64(1.0, 1.0),
t * Pt64(20037508.3427892480, -20037508.3427892480)
);
let tid = TileId::new(246, 368, 10).unwrap();
let t = g.tile_transform(tid);
assert_eq!(
Pt64(0.0, 0.0),
t * Pt64(-10410111.756214727, 5635549.221409475)
);
assert_eq!(
Pt64(1.0, 0.9999999999999716),
t * Pt64(-10370975.997732716, 5596413.462927466)
);
}
}