pub const MARS_FLATTENING: f64 = 0.005_886_007_56;
pub const MARS_SEMI_MAJOR_M: f64 = crate::MARS_EQUATORIAL_RADIUS;
pub const MARS_SEMI_MINOR_M: f64 = crate::MARS_POLAR_RADIUS;
pub struct LatLon {
pub lat_deg: f64,
pub lon_deg: f64,
pub alt_m: f64,
}
pub struct Cartesian {
pub x: f64,
pub y: f64,
pub z: f64,
}
impl LatLon {
pub fn new(lat_deg: f64, lon_deg: f64, alt_m: f64) -> Self {
Self {
lat_deg,
lon_deg,
alt_m,
}
}
pub fn to_cartesian(&self) -> Cartesian {
let lat = self.lat_deg.to_radians();
let lon = self.lon_deg.to_radians();
let a = MARS_SEMI_MAJOR_M;
let e2 = 2.0 * MARS_FLATTENING - MARS_FLATTENING * MARS_FLATTENING;
let sin_lat = lat.sin();
let cos_lat = lat.cos();
let n = a / (1.0 - e2 * sin_lat * sin_lat).sqrt();
Cartesian {
x: (n + self.alt_m) * cos_lat * lon.cos(),
y: (n + self.alt_m) * cos_lat * lon.sin(),
z: (n * (1.0 - e2) + self.alt_m) * sin_lat,
}
}
pub fn distance_to(&self, other: &LatLon) -> f64 {
let r = crate::MARS_RADIUS;
let d_lat = (other.lat_deg - self.lat_deg).to_radians();
let d_lon = (other.lon_deg - self.lon_deg).to_radians();
let lat1 = self.lat_deg.to_radians();
let lat2 = other.lat_deg.to_radians();
let a = (d_lat / 2.0).sin().powi(2) + lat1.cos() * lat2.cos() * (d_lon / 2.0).sin().powi(2);
let c = 2.0 * a.sqrt().asin();
r * c
}
}
impl Cartesian {
pub fn to_latlon(&self) -> LatLon {
let a = MARS_SEMI_MAJOR_M;
let e2 = 2.0 * MARS_FLATTENING - MARS_FLATTENING * MARS_FLATTENING;
let p = (self.x * self.x + self.y * self.y).sqrt();
let lon = self.y.atan2(self.x).to_degrees();
let mut lat = (self.z / p).atan();
for _ in 0..10 {
let sin_lat = lat.sin();
let n = a / (1.0 - e2 * sin_lat * sin_lat).sqrt();
lat = (self.z + e2 * n * sin_lat).atan2(p);
}
let sin_lat = lat.sin();
let n = a / (1.0 - e2 * sin_lat * sin_lat).sqrt();
let alt = p / lat.cos() - n;
LatLon {
lat_deg: lat.to_degrees(),
lon_deg: lon,
alt_m: alt,
}
}
}