use crate::error::Error;
use crate::mgrs::Mgrs;
use crate::precision::Precision;
use crate::utmups::{
central_meridian, standard_zone, UtmUps, UTM_FALSE_EASTING, UTM_FALSE_NORTHING,
};
use crate::{polar, tm};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct LatLon {
lat: f64,
lon: f64,
}
impl LatLon {
pub fn new(lat: f64, lon: f64) -> Result<LatLon, Error> {
if !lat.is_finite() || !(-90.0..=90.0).contains(&lat) {
return Err(Error::LatOutOfRange { found: lat });
}
if !lon.is_finite() {
return Err(Error::LonOutOfRange { found: lon });
}
Ok(LatLon {
lat,
lon: normalize_lon(lon),
})
}
pub(crate) fn new_normalized(lat: f64, lon: f64) -> LatLon {
LatLon {
lat: lat.clamp(-90.0, 90.0),
lon: normalize_lon(lon),
}
}
pub fn lat(self) -> f64 {
self.lat
}
pub fn lon(self) -> f64 {
self.lon
}
pub fn to_utmups(self) -> UtmUps {
let (zone, north, easting, northing) = self.utmups_parts();
UtmUps::raw(zone, north, easting, northing)
}
pub(crate) fn utmups_parts(self) -> (u8, bool, f64, f64) {
let zone = standard_zone(self.lat, self.lon);
if zone == 0 {
let north = self.lat >= 0.0;
let (e, n) = polar::forward(north, self.lat, self.lon);
(0, north, e, n)
} else {
let north = self.lat >= 0.0;
let (x, y) = tm::forward(self.lat, self.lon - central_meridian(zone));
let fold = if north { 0.0 } else { UTM_FALSE_NORTHING };
(zone, north, UTM_FALSE_EASTING + x, fold + y)
}
}
pub fn to_mgrs(self, precision: Precision) -> Mgrs {
crate::mgrs::from_latlon(self, precision)
}
}
fn normalize_lon(lon: f64) -> f64 {
if (-180.0..180.0).contains(&lon) {
return lon;
}
let r = lon.rem_euclid(360.0); if r >= 180.0 {
r - 360.0
} else {
r
}
}