use super::{S1Angle, S2CellId, S2Point};
use core::{
cmp::Ordering,
ops::{Add, Div, Mul, Neg, Sub},
};
use libm::{asin, atan2, cos, fmin, sin, sqrt};
use s2json::{GetM, GetXY, MValue, NewXY, VectorPoint};
#[derive(Clone, PartialEq, Debug)]
pub struct LonLat<M: Clone + Default = MValue>(pub VectorPoint<M>);
impl GetXY for LonLat {
fn x(&self) -> f64 {
self.0.x
}
fn y(&self) -> f64 {
self.0.y
}
}
impl<M: Clone + Default> GetM<M> for LonLat<M> {
fn m(&self) -> Option<&M> {
self.0.m.as_ref()
}
}
impl<M: Clone + Default> NewXY for LonLat<M> {
fn new_xy(x: f64, y: f64) -> LonLat<M> {
LonLat(VectorPoint::new(x, y, None, None))
}
}
impl<M: Clone + Default> LonLat<M> {
pub fn new(lon: f64, lat: f64, m: Option<M>) -> LonLat<M> {
LonLat(VectorPoint::new(lon, lat, None, m))
}
pub fn take(&mut self) -> VectorPoint<M> {
core::mem::take(&mut self.0)
}
#[inline]
pub fn lon(&self) -> f64 {
self.0.x
}
#[inline]
pub fn lat(&self) -> f64 {
self.0.y
}
pub fn from_s2cellid(cellid: S2CellId) -> Self {
let p = cellid.to_point();
Self::from_s2_point(&p)
}
pub fn from_s2_point(p: &S2Point) -> Self {
let lon_rad = atan2(p.y + 0.0, p.x + 0.0);
let lat_rad = atan2(p.z, sqrt(p.x * p.x + p.y * p.y));
let ll = LonLat::new((lon_rad).to_degrees(), (lat_rad).to_degrees(), None);
debug_assert!(ll.is_valid(), "from_s2_point() called on invalid LonLat");
ll
}
pub fn normalize(&mut self) {
let mut lon = self.lon();
let mut lat = self.lat();
lon = ((((lon + 180.) % 360.) + 360.) % 360.) - 180.;
lat = lat.clamp(-90., 90.);
self.0.x = lon;
self.0.y = lat;
}
pub fn coords(self) -> (f64, f64) {
(self.lon(), self.lat())
}
pub fn to_angles(&self) -> (S1Angle, S1Angle) {
(S1Angle::from_degrees(self.lon()), S1Angle::from_degrees(self.lat()))
}
pub fn is_valid(&self) -> bool {
let lat = self.lat();
let lon = self.lon();
(-90.0..=90.0).contains(&lat) && (-180.0..=180.0).contains(&lon)
}
pub fn to_point(&self) -> S2Point {
debug_assert!(self.is_valid(), "to_point() called on invalid LonLat");
let lon: f64 = (self.lon()).to_radians();
let lat: f64 = (self.lat()).to_radians();
S2Point::new(
cos(lat) * cos(lon), cos(lat) * sin(lon), sin(lat), )
}
pub fn to_point_gl(&self) -> S2Point {
debug_assert!(self.is_valid(), "to_point_gl() called on invalid LonLat");
let lon: f64 = (self.lon()).to_radians();
let lat: f64 = (self.lat()).to_radians();
S2Point::new(
cos(lat) * sin(lon), sin(lat), cos(lat) * cos(lon), )
}
pub fn get_distance<M2: Clone + Default>(&self, b: &LonLat<M2>) -> f64 {
debug_assert!(self.is_valid(), "get_bearing() called on invalid LonLat (self)");
debug_assert!(b.is_valid(), "get_bearing() called on invalid LonLat (b)");
let lat1 = self.lat().to_radians();
let lat2 = b.lat().to_radians();
let lon1 = self.lon().to_radians();
let lon2 = b.lon().to_radians();
let dlat = sin(0.5 * (lat2 - lat1));
let dlon = sin(0.5 * (lon2 - lon1));
let x = dlat * dlat + dlon * dlon * cos(lat1) * cos(lat2);
2. * asin(sqrt(fmin(1., x)))
}
pub fn get_bearing<M2: Clone + Default>(&self, b: &LonLat<M2>) -> f64 {
debug_assert!(self.is_valid(), "get_bearing() called on invalid LonLat (self)");
debug_assert!(b.is_valid(), "get_bearing() called on invalid LonLat (self)");
let lat1 = self.lat().to_radians();
let lat2 = b.lat().to_radians();
let lon1 = self.lon().to_radians();
let lon2 = b.lon().to_radians();
let y = sin(lon2 - lon1) * cos(lat2);
let x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(lon2 - lon1);
(atan2(y, x).to_degrees() + 360.) % 360.
}
}
impl<M: Clone + Default> From<S2CellId> for LonLat<M> {
fn from(c: S2CellId) -> Self {
LonLat::from_s2cellid(c)
}
}
impl<M: Clone + Default> From<&S2Point> for LonLat<M> {
fn from(p: &S2Point) -> Self {
LonLat::from_s2_point(p)
}
}
impl<M1: Clone + Default, M2: Clone + Default> Add<LonLat<M2>> for LonLat<M1> {
type Output = LonLat<M1>;
fn add(self, rhs: LonLat<M2>) -> Self::Output {
LonLat::new(self.lon() + rhs.lon(), self.lat() + rhs.lat(), self.0.m)
}
}
impl<M1: Clone + Default, M2: Clone + Default> Sub<LonLat<M2>> for LonLat<M1> {
type Output = LonLat<M1>;
fn sub(self, rhs: LonLat<M2>) -> Self::Output {
LonLat::new(self.lon() - rhs.lon(), self.lat() - rhs.lat(), self.0.m)
}
}
impl<M1: Clone + Default, M2: Clone + Default> Mul<LonLat<M2>> for LonLat<M1> {
type Output = LonLat<M1>;
fn mul(self, rhs: LonLat<M2>) -> Self::Output {
LonLat::new(self.lon() * rhs.lon(), self.lat() * rhs.lat(), self.0.m)
}
}
impl<M1: Clone + Default, M2: Clone + Default> Div<LonLat<M2>> for LonLat<M1> {
type Output = LonLat<M1>;
fn div(self, rhs: LonLat<M2>) -> Self::Output {
LonLat::new(self.lon() / rhs.lon(), self.lat() / rhs.lat(), self.0.m)
}
}
impl Neg for LonLat {
type Output = LonLat;
fn neg(self) -> Self::Output {
LonLat::new(-self.lon(), -self.lat(), self.0.m)
}
}
impl Eq for LonLat {}
impl Ord for LonLat {
fn cmp(&self, other: &Self) -> Ordering {
match self.lon().partial_cmp(&other.lon()) {
Some(Ordering::Equal) => {}
other => return other.unwrap(), }
match self.lat().partial_cmp(&other.lat()) {
Some(order) => order,
None => Ordering::Equal, }
}
}
impl PartialOrd for LonLat {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}