apsis-rs/crates/physics/apsis-geodesy/src/ellipsoid.rs

137 lines
4.8 KiB
Rust

//! Reference ellipsoids.
/// A biaxial reference ellipsoid: an ellipse of revolution about its minor
/// axis.
///
/// The shape is fixed by two numbers. This crate stores the semi-major axis
/// and the inverse flattening, which are the two defining parameters of the
/// WGS 84 ellipsoid in the standard. Every other quantity is derived, so
/// there is no risk of the parameters disagreeing with each other.
///
/// Lengths are in meters and angles are in radians.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Ellipsoid {
semi_major_axis: f64,
inverse_flattening: f64,
}
impl Ellipsoid {
/// Creates an ellipsoid from its semi-major axis and inverse
/// flattening.
///
/// Lengths are in meters. The inverse flattening must be positive and
/// finite; a value of infinity describes a sphere.
pub const fn new(semi_major_axis: f64, inverse_flattening: f64) -> Self {
Self {
semi_major_axis,
inverse_flattening,
}
}
/// Returns the semi-major axis, in meters.
pub const fn semi_major_axis(self) -> f64 {
self.semi_major_axis
}
/// Returns the inverse flattening.
pub const fn inverse_flattening(self) -> f64 {
self.inverse_flattening
}
/// Returns the flattening.
///
/// This is zero for a sphere.
pub fn flattening(self) -> f64 {
1.0 / self.inverse_flattening
}
/// Returns the semi-minor axis, in meters.
pub fn semi_minor_axis(self) -> f64 {
self.semi_major_axis * (1.0 - self.flattening())
}
/// Returns the first eccentricity squared, a dimensionless number in
/// `[0, 1)`.
///
/// It is `2f - f^2` for flattening `f`.
pub fn eccentricity_squared(self) -> f64 {
let flattening = self.flattening();
2.0 * flattening - flattening * flattening
}
/// Returns the first eccentricity, a dimensionless number in `[0, 1)`.
pub fn eccentricity(self) -> f64 {
self.eccentricity_squared().sqrt()
}
/// Returns the second eccentricity squared, a dimensionless number.
///
/// It is `e^2 / (1 - e^2)`, which appears in the closed form for
/// converting a geodetic position to Cartesian coordinates.
pub fn second_eccentricity_squared(self) -> f64 {
let squared = self.eccentricity_squared();
squared / (1.0 - squared)
}
/// Returns the polar radius of curvature, in meters.
///
/// This is the radius of curvature in the meridian at the pole, equal
/// to `a / sqrt(1 - e^2)`.
pub fn polar_radius_of_curvature(self) -> f64 {
self.semi_major_axis / (1.0 - self.eccentricity_squared()).sqrt()
}
/// Returns the radius of curvature in the prime vertical at the given
/// geodetic latitude, in meters.
///
/// This is the distance from the surface to the polar axis measured
/// along the ellipsoid normal, and it grows from the semi-major axis at
/// the equator to [`Ellipsoid::polar_radius_of_curvature`] at the pole.
///
/// The latitude is in radians.
pub fn prime_vertical_radius(self, latitude: f64) -> f64 {
let sin = latitude.sin();
self.semi_major_axis / (1.0 - self.eccentricity_squared() * sin * sin).sqrt()
}
/// Returns the radius of curvature in the meridian at the given
/// geodetic latitude, in meters.
///
/// The latitude is in radians.
pub fn meridian_radius(self, latitude: f64) -> f64 {
let sin = latitude.sin();
let squared = self.eccentricity_squared();
let denominator = 1.0 - squared * sin * sin;
self.semi_major_axis * (1.0 - squared) / denominator.powf(1.5)
}
/// Returns the geocentric latitude corresponding to a geodetic
/// latitude, in radians.
///
/// The geocentric latitude is measured from the equatorial plane to the
/// radius vector, while the geodetic latitude is measured to the
/// ellipsoid normal. The geocentric value is the smaller of the two
/// away from the equator and the poles.
pub fn geocentric_latitude(self, geodetic_latitude: f64) -> f64 {
let squared = self.eccentricity_squared();
let tangent = (1.0 - squared) * geodetic_latitude.tan();
tangent.atan()
}
}
/// The WGS 84 reference ellipsoid.
///
/// The two defining parameters are the semi-major axis `a = 6 378 137 m` and
/// the inverse flattening `1 / f = 298.257 223 563`.
///
/// Source: NIMA TR8350.2, third edition, 2000.
pub const WGS84: Ellipsoid = Ellipsoid::new(6_378_137.0, 298.257_223_563);
/// The Earth gravitational parameter for WGS 84, in cubic meters per second
/// squared.
///
/// This is held here rather than in `apsis-core` so that a caller working
/// with the ellipsoid can use the matching gravity field. The value is
/// `3.986 004 418e14 m^3/s^2`.
pub const WGS84_GRAVITATIONAL_PARAMETER: f64 = 3.986_004_418e14;