use std::f64::consts::FRAC_PI_2;
use super::super::Customf64;
use super::super::TWICE_PI;
pub trait Proj: Default {
fn set_center(&mut self, lon: f64, lat: f64);
fn proj(&self, lon: f64, lat: f64) -> Option<(f64, f64)>;
fn unproj(&self, x: f64, y: f64) -> Option<(f64, f64)>;
}
fn normalize_lonlat(lon: &mut f64, lat: &mut f64) {
if *lon < 0.0 || TWICE_PI <= *lon || *lat < -FRAC_PI_2 || FRAC_PI_2 < *lat {
let (sin_l, cos_l) = (*lon).sin_cos();
let (sin_b, cos_b) = (*lat).sin_cos();
let x = cos_b * cos_l;
let y = cos_b * sin_l;
let z = sin_b;
*lon = y.atan2(x);
if *lon < 0.0_f64 {
*lon += TWICE_PI;
}
*lat = z.atan2((x.pow2() + y.pow2()).sqrt());
}
}
#[derive(Debug)]
pub struct ProjSIN {
center_lon: f64,
center_lat: f64,
pub cos_center_lat: f64,
pub sin_center_lat: f64,
}
impl ProjSIN {
pub fn new(lon: f64, lat: f64) -> ProjSIN {
let mut proj: ProjSIN = Default::default();
proj.set_center(lon, lat);
proj
}
pub fn forced_proj_and_distance(&self, lon: f64, lat: f64) -> ((f64, f64), f64, bool) {
let (sin_lat, cos_lat) = lat.sin_cos();
let dlon = lon - self.center_lon;
let (sin_dlon, cos_dlon) = dlon.sin_cos();
(
(
cos_lat * sin_dlon,
self.cos_center_lat * sin_lat - self.sin_center_lat * cos_lat * cos_dlon,
),
(self.sin_center_lat * sin_lat + self.cos_center_lat * cos_lat * cos_dlon).acos(),
self.sin_center_lat * sin_lat + self.cos_center_lat * cos_lat * cos_dlon > 0.0,
)
}
}
impl Default for ProjSIN {
fn default() -> Self {
ProjSIN {
center_lon: 0.0,
center_lat: 0.0,
cos_center_lat: 1.0,
sin_center_lat: 0.0,
}
}
}
impl Proj for ProjSIN {
fn set_center(&mut self, lon: f64, lat: f64) {
self.center_lon = lon;
self.center_lat = lat;
normalize_lonlat(&mut self.center_lon, &mut self.center_lat);
let (sin_lat, cos_lat) = self.center_lat.sin_cos();
self.cos_center_lat = cos_lat;
self.sin_center_lat = sin_lat;
}
fn proj(&self, lon: f64, lat: f64) -> Option<(f64, f64)> {
let (sin_lat, cos_lat) = lat.sin_cos();
let dlon = lon - self.center_lon;
let (sin_dlon, cos_dlon) = dlon.sin_cos();
if self.sin_center_lat * sin_lat + self.cos_center_lat * cos_lat * cos_dlon > 0.0 {
Some((
cos_lat * sin_dlon,
self.cos_center_lat * sin_lat - self.sin_center_lat * cos_lat * cos_dlon,
))
} else {
None
}
}
fn unproj(&self, x: f64, y: f64) -> Option<(f64, f64)> {
let rho2 = x.pow2() + y.pow2();
if rho2 < 1.0 {
let z = (1.0 - rho2).sqrt(); let lat = (self.sin_center_lat * z + y * self.cos_center_lat).asin();
let lon = self.cos_center_lat * z - y * self.sin_center_lat;
let lon = self.center_lon + x.atan2(lon);
Some((
if lon < 0.0 {
lon + TWICE_PI
} else if lon > TWICE_PI {
lon - TWICE_PI
} else {
lon
},
lat,
))
} else {
None
}
}
}