use std::f64::consts::PI;
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
pub(crate) fn tile_xy(lng: f64, lat: f64, zoom: u32) -> (u32, u32) {
let n = f64::from(1u32 << zoom);
let x = ((lng / 360.0 + 0.5) * n) as u32;
let y = if lat > 85.0511 {
0
} else if lat < -85.0511 {
(n as u32) - 1
} else {
let siny = (lat * PI / 180.0).sin();
((0.5 - ((1.0 + siny) / (1.0 - siny)).ln() / (4.0 * PI)) * n) as u32
};
(x, y)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub(crate) struct TileId(pub u64);
impl TileId {
pub(crate) fn new(lng: f64, lat: f64, zoom: u32) -> Self {
let (x, y) = tile_xy(lng, lat, zoom);
Self::from_xyz(x, y, zoom as u8)
}
pub(crate) fn from_xyz(x: u32, y: u32, z: u8) -> Self {
Self(u64::from(z) << 56 | u64::from(x) << 28 | u64::from(y))
}
pub(crate) fn xyz(self) -> (u32, u32, u8) {
(
(self.0 >> 28) as u32 & 0x0FFF_FFFF,
self.0 as u32 & 0x0FFF_FFFF,
(self.0 >> 56) as u8,
)
}
#[cfg(feature = "export-geojson")]
pub(crate) fn polygon(self) -> Vec<[f64; 2]> {
let (x, y, z) = self.xyz();
let n = f64::from(1u32 << z);
let lng_min = f64::from(x) / n * 360.0 - 180.0;
let lng_max = f64::from(x + 1) / n * 360.0 - 180.0;
let lat_max = (PI * (1.0 - 2.0 * f64::from(y) / n)).sinh().atan() * 180.0 / PI;
let lat_min = (PI * (1.0 - 2.0 * f64::from(y + 1) / n)).sinh().atan() * 180.0 / PI;
vec![
[lng_min, lat_min],
[lng_max, lat_min],
[lng_max, lat_max],
[lng_min, lat_max],
[lng_min, lat_min],
]
}
pub(crate) fn shift(self, shift: u8) -> Self {
let (x, y, z) = self.xyz();
if shift > z {
return Self(0);
}
Self::from_xyz(x >> shift, y >> shift, z - shift)
}
}