use std::f64;
pub fn get_code(lng: f64, lat: f64, precision: usize) -> u64 {
let lng = dec2code(lng, precision);
let lat = dec2code(lat, precision);
magic_bits(lng, lat)
}
fn dec2code(dec: f64, precision: usize) -> u32 {
let mut code = 0;
let val = if dec < 0.0 { -dec } else { dec };
let g = if dec < 0.0 { 1 } else { 0 };
let d = val.trunc() as u32;
let dm = round((val - d as f64) * 60.0, 6);
let m = dm.trunc() as u32;
let seconds = round((dm - m as f64) * 60.0, 4);
let s = seconds.trunc() as u32;
let dot_seconds = (seconds - s as f64) * 2048.0;
let s11 = dot_seconds.round() as u32;
code = (g << 31) | (d << 23) | (m << 17) | (s << 11) | s11;
code >>= 32 - precision;
code <<= 32 - precision;
code
}
fn magic_bits(lng: u32, lat: u32) -> u64 {
split_by_bits(lng) | (split_by_bits(lat) << 1)
}
fn split_by_bits(a: u32) -> u64 {
let mut x = a as u64;
x = (x | x << 32) & 0x00000000FFFFFFFF;
x = (x | x << 16) & 0x0000FFFF0000FFFF;
x = (x | x << 8) & 0x00FF00FF00FF00FF;
x = (x | x << 4) & 0x0F0F0F0F0F0F0F0F;
x = (x | x << 2) & 0x3333333333333333;
x = (x | x << 1) & 0x5555555555555555;
x
}
fn round(x: f64, y: i32) -> f64 {
let mul = 10f64.powi(y);
if x >= 0.0 {
(x * mul + 0.5).trunc() / mul
} else {
(x * mul - 0.5).trunc() / mul
}
}
pub fn to_string(code: u64, level: usize) -> String {
let mut str_out = String::from("G");
let level = level - 1;
for i in (31 - level..=31).rev() {
let v = (code >> (i * 2)) & 0x3;
str_out.push_str(&v.to_string());
if i > 32 - level {
match i {
23 | 17 => str_out.push('-'),
11 => str_out.push('.'),
_ => (),
}
}
}
str_out
}