use std::f64::consts::PI;
use std::str::FromStr;
use num::cast::AsPrimitive;
use num::Integer;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ZoomLv {
Lv0,
Lv1,
Lv2,
Lv3,
Lv4,
Lv5,
Lv6,
Lv7,
Lv8,
Lv9,
Lv10,
Lv11,
Lv12,
Lv13,
Lv14,
Lv15,
Lv16,
Lv17,
Lv18,
Lv19,
Lv20,
Lv21,
Lv22,
Lv23,
Lv24,
}
impl ZoomLv {
pub fn parse<T: Integer + AsPrimitive<u8>>(num: T) -> Result<Self, ()> {
match num.as_() {
0 => Ok(Self::Lv0),
1 => Ok(Self::Lv1),
2 => Ok(Self::Lv2),
3 => Ok(Self::Lv3),
4 => Ok(Self::Lv4),
5 => Ok(Self::Lv5),
6 => Ok(Self::Lv6),
7 => Ok(Self::Lv7),
8 => Ok(Self::Lv8),
9 => Ok(Self::Lv9),
10 => Ok(Self::Lv10),
11 => Ok(Self::Lv11),
12 => Ok(Self::Lv12),
13 => Ok(Self::Lv13),
14 => Ok(Self::Lv14),
15 => Ok(Self::Lv15),
16 => Ok(Self::Lv16),
17 => Ok(Self::Lv17),
18 => Ok(Self::Lv18),
19 => Ok(Self::Lv19),
20 => Ok(Self::Lv20),
21 => Ok(Self::Lv21),
22 => Ok(Self::Lv22),
23 => Ok(Self::Lv23),
24 => Ok(Self::Lv24),
_ => Err(()),
}
}
}
impl TryFrom<u8> for ZoomLv
{
type Error = ();
fn try_from(value: u8) -> Result<Self, Self::Error> {
Self::parse(value)
}
}
impl TryFrom<u16> for ZoomLv
{
type Error = ();
fn try_from(value: u16) -> Result<Self, Self::Error> {
Self::parse(value)
}
}
impl TryFrom<u32> for ZoomLv
{
type Error = ();
fn try_from(value: u32) -> Result<Self, Self::Error> {
Self::parse(value)
}
}
impl TryFrom<u64> for ZoomLv
{
type Error = ();
fn try_from(value: u64) -> Result<Self, Self::Error> {
Self::parse(value)
}
}
impl TryFrom<usize> for ZoomLv
{
type Error = ();
fn try_from(value: usize) -> Result<Self, Self::Error> {
Self::parse(value)
}
}
impl TryFrom<i8> for ZoomLv
{
type Error = ();
fn try_from(value: i8) -> Result<Self, Self::Error> {
Self::parse(value)
}
}
impl TryFrom<i16> for ZoomLv
{
type Error = ();
fn try_from(value: i16) -> Result<Self, Self::Error> {
Self::parse(value)
}
}
impl TryFrom<i32> for ZoomLv
{
type Error = ();
fn try_from(value: i32) -> Result<Self, Self::Error> {
Self::parse(value)
}
}
impl TryFrom<i64> for ZoomLv
{
type Error = ();
fn try_from(value: i64) -> Result<Self, Self::Error> {
Self::parse(value)
}
}
impl TryFrom<isize> for ZoomLv
{
type Error = ();
fn try_from(value: isize) -> Result<Self, Self::Error> {
Self::parse(value)
}
}
impl FromStr for ZoomLv {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.parse::<u8>() {
Ok(num) => Self::parse(num),
Err(_) => Err(()),
}
}
}
pub fn ll2pixel(ll: (f64, f64), zoom: ZoomLv) -> (u32, u32) {
let (long, lat) = ll;
const L: f64 = 85.05112878;
let x = (2_f64.powf(zoom as i32 as f64 + 7.)) * (long / PI + 1.);
let y = (2_f64.powf(zoom as i32 as f64 + 7.) / PI)
* (-(lat.sin().atanh()) + (L * PI / 180.).sin().atanh());
(x as u32, y as u32)
}
pub fn pixel2ll(pixel: (u32, u32), zoom: ZoomLv) -> (f64, f64) {
let (x, y) = pixel;
const L: f64 = 85.05112878;
let long = PI * (x as f64 / 2_f64.powf(zoom as i32 as f64 + 7.) - 1.);
let lat = ((-PI * y as f64 / (2_f64.powf(zoom as i32 as f64 + 7.))
+ (PI * L / 180.).sin().atanh())
.tanh())
.asin();
(long, lat)
}
pub fn pixel_resolution(lat: f64, zoom: ZoomLv) -> f64 {
156543.04 * lat.cos() / 2_f64.powf(zoom as i32 as f64)
}
pub fn pixel2tile(pixel: (u32, u32)) -> (u32, u32) {
let (x, y) = pixel;
(x / 256, y / 256)
}
#[cfg(test)]
mod tests {
use close_to::assert_close_to;
use super::*;
#[test]
fn ll2pixel_works() {
let (x, y) = ll2pixel(
(139.7649308_f64.to_radians(), (35.6812405_f64).to_radians()),
ZoomLv::Lv21,
);
assert_eq!((x, y), (476868027, 211407949));
}
#[test]
fn pixel2ll_works() {
let (long, lat) = pixel2ll((476868027, 211407949), ZoomLv::Lv21);
println!("{}, {}", long.to_degrees(), lat.to_degrees());
assert_close_to(139.7649308_f64.to_radians(), long, 5);
assert_close_to(35.6812405_f64.to_radians(), lat, 5);
}
#[test]
fn pixel_resolution_works() {
let equator_length_m = 40075_f64 * 1000_f64;
let zoom_lv = ZoomLv::Lv17;
let resolution = pixel_resolution(0_f64.to_radians(), zoom_lv);
assert_close_to(
resolution,
equator_length_m / (2_f64.powf(zoom_lv as i32 as f64) * 256.),
5,
);
}
}