#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Ellipsoid {
pub a: f64,
pub f: f64,
pub b: f64,
pub e2: f64,
}
impl Ellipsoid {
#[inline]
pub const fn from_a_inv_f(a: f64, inv_f: f64) -> Self {
let f = 1.0 / inv_f;
let b = a * (1.0 - f);
let e2 = 2.0 * f - f * f;
Self { a, f, b, e2 }
}
pub const WGS84: Self = Self::from_a_inv_f(6_378_137.0, 298.257_223_563);
pub const GRS80: Self = Self::from_a_inv_f(6_378_137.0, 298.257_222_101);
#[inline]
pub fn second_eccentricity_squared(&self) -> f64 {
(self.a * self.a - self.b * self.b) / (self.b * self.b)
}
#[inline]
pub fn radius_of_curvature_n(&self, lat_rad: f64) -> f64 {
let sin_lat = lat_rad.sin();
self.a / (1.0 - self.e2 * sin_lat * sin_lat).sqrt()
}
#[inline]
pub fn is_sphere(&self) -> bool {
self.f == 0.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn wgs84_constants() {
let e = Ellipsoid::WGS84;
assert!((e.a - 6_378_137.0).abs() < 1e-6);
assert!((e.b - 6_356_752.314_245_179).abs() < 1e-3);
assert!((e.f - 1.0 / 298.257_223_563).abs() < 1e-15);
assert!((e.e2 - 0.006_694_379_990_141_316).abs() < 1e-12);
}
#[test]
fn second_eccentricity() {
let e = Ellipsoid::WGS84;
let ep2 = e.second_eccentricity_squared();
assert!((ep2 - 0.006_739_496_742_276_434).abs() < 1e-12);
}
#[test]
fn grs80_constants() {
let e = Ellipsoid::GRS80;
assert!((e.a - 6_378_137.0).abs() < 1e-6);
assert!((e.f - 1.0 / 298.257_222_101).abs() < 1e-15);
assert!(e.f != Ellipsoid::WGS84.f);
assert!((e.f - Ellipsoid::WGS84.f).abs() < 1e-8);
}
#[test]
fn custom_ellipsoid_roundtrip() {
let e = Ellipsoid::from_a_inv_f(6_400_000.0, 300.0);
assert!((e.a - 6_400_000.0).abs() < 1e-6);
assert!((e.f - 1.0 / 300.0).abs() < 1e-15);
assert!((e.b - e.a * (1.0 - e.f)).abs() < 1e-6);
assert!((e.e2 - (2.0 * e.f - e.f * e.f)).abs() < 1e-15);
}
#[test]
fn radius_n_at_equator() {
let e = Ellipsoid::WGS84;
let n = e.radius_of_curvature_n(0.0);
assert!((n - e.a).abs() < 1e-6);
}
#[test]
fn radius_n_at_pole() {
let e = Ellipsoid::WGS84;
let n = e.radius_of_curvature_n(std::f64::consts::FRAC_PI_2);
let expected = e.a * e.a / e.b;
assert!((n - expected).abs() < 1e-3);
}
#[test]
fn wgs84_is_not_sphere() {
assert!(!Ellipsoid::WGS84.is_sphere());
}
#[test]
fn perfect_sphere() {
let s = Ellipsoid::from_a_inv_f(6_371_000.0, f64::INFINITY);
assert!(s.is_sphere());
assert!((s.a - s.b).abs() < 1e-6);
assert!(s.e2.abs() < 1e-15);
}
}