irox_carto/geo/
ellipsoid.rsuse irox_units::units::length::Length;
use crate::geo::ellipse::Ellipse;
use crate::geo::standards::wgs84::WGS84_ELLIPSOID;
use crate::geo::EllipticalShape;
use irox_tools::cfg_feature_std;
cfg_feature_std! {
use crate::coordinate::Latitude;
use irox_units::units::compass::{Azimuth, Compass, CompassReference, RotationDirection};
}
impl From<Ellipse> for Ellipsoid {
fn from(value: Ellipse) -> Self {
let semi_major_axis = value.semi_major_axis_a();
let inverse_flattening = value.inverse_flattening();
let semi_minor_axis = value.semi_minor_axis_b();
let first_eccentricity = value.first_eccentricity();
let first_eccentricity_squared = value.first_eccentricity_squared();
let second_eccentricity = value.second_eccentricity();
let second_eccentricity_squared = value.second_eccentricity_squared();
Ellipsoid {
semi_major_axis,
inverse_flattening,
semi_minor_axis,
first_eccentricity,
first_eccentricity_squared,
second_eccentricity,
second_eccentricity_squared,
}
}
}
impl From<&Ellipsoid> for Ellipse {
fn from(value: &Ellipsoid) -> Self {
Ellipse::new(value.semi_major_axis, value.inverse_flattening)
}
}
#[derive(Debug, Copy, Clone)]
pub struct Ellipsoid {
pub(crate) semi_major_axis: Length,
pub(crate) inverse_flattening: f64,
pub(crate) semi_minor_axis: Length,
pub(crate) first_eccentricity: f64,
pub(crate) first_eccentricity_squared: f64,
pub(crate) second_eccentricity: f64,
pub(crate) second_eccentricity_squared: f64,
}
impl Default for Ellipsoid {
fn default() -> Self {
WGS84_ELLIPSOID
}
}
impl From<Ellipsoid> for EllipticalShape {
fn from(value: Ellipsoid) -> Self {
EllipticalShape::Ellipse(value.into())
}
}
impl From<&Ellipsoid> for EllipticalShape {
fn from(value: &Ellipsoid) -> Self {
EllipticalShape::Ellipse(value.into())
}
}
impl Ellipsoid {
#[must_use]
pub const fn semi_major_axis_a(&self) -> Length {
self.semi_major_axis
}
#[must_use]
pub const fn semi_minor_axis_b(&self) -> Length {
self.semi_minor_axis
}
#[must_use]
pub const fn inverse_flattening(&self) -> f64 {
self.inverse_flattening
}
#[must_use]
pub const fn first_eccentricity(&self) -> f64 {
self.first_eccentricity
}
#[must_use]
pub const fn first_eccentricity_squared(&self) -> f64 {
self.first_eccentricity_squared
}
#[must_use]
pub const fn second_eccentricity(&self) -> f64 {
self.second_eccentricity
}
#[must_use]
pub const fn second_eccentricity_sq(&self) -> f64 {
self.second_eccentricity_squared
}
#[must_use]
pub fn flattening_f(&self) -> f64 {
1.0 / self.inverse_flattening
}
#[must_use]
pub fn third_flattening_n_eta(&self) -> f64 {
let a = self.semi_major_axis;
let b = self.semi_minor_axis;
(a - b) / (a + b)
}
cfg_feature_std! {
#[must_use]
pub fn radius_curvature_meridian(&self, latitude: &Latitude) -> Length {
let upper = self.semi_major_axis * (1. - self.first_eccentricity_squared);
let sin2 = latitude.0.as_radians().value().sin().powi(2);
let lower = (1. - self.first_eccentricity_squared * sin2).powf(3. / 2.);
upper / lower
}
#[must_use]
pub fn radius_curvature_prime_vertical(&self, latitude: &Latitude) -> Length {
let sin2 = latitude.0.as_radians().value().sin().powi(2);
let lower = (1. - self.first_eccentricity_squared * sin2).sqrt();
self.semi_major_axis / lower
}
#[must_use]
pub fn radius_curvature_azimuthal(
&self,
latitude: &Latitude,
azimuth: &Compass<Azimuth>,
) -> Length {
let azimuth = azimuth.as_direction_reference(
RotationDirection::PositiveClockwise,
CompassReference::TrueNorth,
);
let az_rad = azimuth.angle().as_radians().value();
let cos2az = az_rad.cos().powi(2);
let cos2la = latitude.0.as_radians().value().cos().powi(2);
let n = self.radius_curvature_prime_vertical(latitude);
let lower = 1.0 + self.second_eccentricity_squared * cos2az * cos2la;
n / lower
}
#[must_use]
pub fn radius_curvature_average(&self, latitude: &Latitude) -> Length {
let sin2 = latitude.0.as_radians().value().sin().powi(2);
let upper = self.semi_major_axis * (1.0 - self.first_eccentricity_squared).sqrt();
let lower = 1.0 - self.first_eccentricity_squared * sin2;
upper / lower
}
}
#[must_use]
pub fn spherical_radius_equal_area_approximation(&self) -> Length {
let e2 = self.first_eccentricity_squared;
let e4 = e2 * e2;
let e6 = e4 * e2;
let offset = 1. - e2 / 6. - 17. / 360. * e4 - 67. / 3024. * e6;
self.semi_major_axis * offset
}
#[must_use]
pub fn spherical_radius_equal_volume_approximation(&self) -> Length {
let e2 = self.first_eccentricity_squared;
let e4 = e2 * e2;
let e6 = e4 * e2;
let offset = 1. - e2 / 6. - 5. / 72. * e4 - 55. / 1296. * e6;
self.semi_major_axis * offset
}
#[must_use]
pub fn as_ellipse(&self) -> Ellipse {
Ellipse::from(*self)
}
#[must_use]
pub fn as_elliptical_shape(&self) -> EllipticalShape {
EllipticalShape::Ellipse(self.as_ellipse())
}
}