From 8fdbda2a23a9d176e2c332ac6fbaee1a946967ff Mon Sep 17 00:00:00 2001 From: Thomas Knudsen Date: Tue, 19 Mar 2024 17:42:31 +0100 Subject: [PATCH] A CoordTrait experiment --- src/coordinate/mod.rs | 237 +++++++++++++++++++++++++++++++++++++ src/ellipsoid/geodesics.rs | 39 +++--- 2 files changed, 261 insertions(+), 15 deletions(-) diff --git a/src/coordinate/mod.rs b/src/coordinate/mod.rs index 7ce29bdc..25b327c6 100644 --- a/src/coordinate/mod.rs +++ b/src/coordinate/mod.rs @@ -104,3 +104,240 @@ pub trait CoordinateSet: CoordinateMetadata { } } } + +// An experiment with an extended version of Kyle Barron's CoordTrait PR +// over at https://github.com/georust/geo/pull/1157 + +pub trait CoordNum {} +impl CoordNum for f32 {} +impl CoordNum for f64 {} + +/// A trait for accessing data from a generic Coord. +pub trait CoordTrait { + type T: CoordNum; + + /// Accessors for the coordinate tuple components + fn first(&self) -> Self::T; + fn second(&self) -> Self::T; + fn third(&self) -> Self::T; + fn fourth(&self) -> Self::T; + fn measure(&self) -> Self::T; + + /// Accessors for the coordinate tuple components + fn first_as_f64(&self) -> f64; + fn second_as_f64(&self) -> f64; + fn third_as_f64(&self) -> f64; + fn fourth_as_f64(&self) -> f64; + fn measure_as_f64(&self) -> f64; + + /// x component of this coord + fn x(&self) -> Self::T { + self.first() + } + + /// y component of this coord + fn y(&self) -> Self::T { + self.second() + } + + /// z component of this coord + fn z(&self) -> Self::T { + self.third() + } + + /// t component of this coord + fn t(&self) -> Self::T { + self.fourth() + } + + /// Returns a tuple that contains the two first components of the coord. + fn xy(&self) -> (Self::T, Self::T) { + (self.first(), self.second()) + } + + /// Returns a tuple that contains the three first components of the coord. + fn xyz(&self) -> (Self::T, Self::T, Self::T) { + (self.first(), self.second(), self.third()) + } + + /// Returns a tuple that contains the three first components of the coord. + fn xyzt(&self) -> (Self::T, Self::T, Self::T, Self::T) { + (self.first(), self.second(), self.third(), self.fourth()) + } + + /// Returns a tuple that contains the two first components of the coord converted to f64. + fn xy_as_f64(&self) -> (f64, f64) { + (self.first_as_f64(), self.second_as_f64()) + } + + /// Returns a tuple that contains the three first components of the coord converted to f64. + fn xyz_as_f64(&self) -> (f64, f64, f64) { + ( + self.first_as_f64(), + self.second_as_f64(), + self.third_as_f64(), + ) + } + + /// Returns a tuple that contains the three first components of the coord converted to f64. + fn xyzt_as_f64(&self) -> (f64, f64, f64, f64) { + ( + self.first_as_f64(), + self.second_as_f64(), + self.third_as_f64(), + self.fourth_as_f64(), + ) + } +} + +impl CoordTrait for Coor2D { + type T = f64; + + /// Accessors for the coordinate tuple components + fn first(&self) -> Self::T { + self.0[0] + } + fn second(&self) -> Self::T { + self.0[1] + } + fn third(&self) -> Self::T { + f64::NAN + } + fn fourth(&self) -> Self::T { + f64::NAN + } + fn measure(&self) -> Self::T { + f64::NAN + } + + /// Accessors for the coordinate tuple components + fn first_as_f64(&self) -> f64 { + self.0[0] + } + fn second_as_f64(&self) -> f64 { + self.0[1] + } + fn third_as_f64(&self) -> f64 { + f64::NAN + } + fn fourth_as_f64(&self) -> f64 { + f64::NAN + } + fn measure_as_f64(&self) -> f64 { + f64::NAN + } +} + +impl CoordTrait for Coor32 { + type T = f32; + + /// Accessors for the coordinate tuple components + fn first(&self) -> Self::T { + self.0[0] + } + fn second(&self) -> Self::T { + self.0[1] + } + fn third(&self) -> Self::T { + f32::NAN + } + fn fourth(&self) -> Self::T { + f32::NAN + } + fn measure(&self) -> Self::T { + f32::NAN + } + + /// Accessors for the coordinate tuple components + fn first_as_f64(&self) -> f64 { + self.0[0] as f64 + } + fn second_as_f64(&self) -> f64 { + self.0[1] as f64 + } + fn third_as_f64(&self) -> f64 { + f64::NAN + } + fn fourth_as_f64(&self) -> f64 { + f64::NAN + } + fn measure_as_f64(&self) -> f64 { + f64::NAN + } +} + +impl CoordTrait for Coor3D { + type T = f64; + + /// Accessors for the coordinate tuple components + fn first(&self) -> Self::T { + self.0[0] + } + fn second(&self) -> Self::T { + self.0[1] + } + fn third(&self) -> Self::T { + self.0[2] + } + fn fourth(&self) -> Self::T { + f64::NAN + } + fn measure(&self) -> Self::T { + f64::NAN + } + + /// Accessors for the coordinate tuple components + fn first_as_f64(&self) -> f64 { + self.0[0] + } + fn second_as_f64(&self) -> f64 { + self.0[1] + } + fn third_as_f64(&self) -> f64 { + self.0[2] + } + fn fourth_as_f64(&self) -> f64 { + f64::NAN + } + fn measure_as_f64(&self) -> f64 { + f64::NAN + } +} + +impl CoordTrait for Coor4D { + type T = f64; + + /// Accessors for the coordinate tuple components + fn first(&self) -> Self::T { + self.0[0] + } + fn second(&self) -> Self::T { + self.0[1] + } + fn third(&self) -> Self::T { + self.0[2] + } + fn fourth(&self) -> Self::T { + self.0[3] + } + fn measure(&self) -> Self::T { + f64::NAN + } + + /// Accessors for the coordinate tuple components + fn first_as_f64(&self) -> f64 { + self.0[0] + } + fn second_as_f64(&self) -> f64 { + self.0[1] + } + fn third_as_f64(&self) -> f64 { + self.0[2] + } + fn fourth_as_f64(&self) -> f64 { + self.0[3] + } + fn measure_as_f64(&self) -> f64 { + f64::NAN + } +} diff --git a/src/ellipsoid/geodesics.rs b/src/ellipsoid/geodesics.rs index 102bdf33..c298bb16 100644 --- a/src/ellipsoid/geodesics.rs +++ b/src/ellipsoid/geodesics.rs @@ -1,3 +1,7 @@ +use crate::coordinate::CoordTrait; + +// Now using an extended version of Kyle Barron's CoordTrait, cf. src/coordinate/mod.rs + use super::*; // ----- Geodesics ------------------------------------------------------------- @@ -14,10 +18,12 @@ impl Ellipsoid { /// Federico Dolce and Michael Kirk, provides a Rust implementation of Karney's algorithm. #[must_use] #[allow(non_snake_case)] - pub fn geodesic_fwd(&self, from: &Coor4D, azimuth: f64, distance: f64) -> Coor4D { + pub fn geodesic_fwd(&self, from: &T, azimuth: f64, distance: f64) -> Coor4D + where + T: CoordTrait, + { // Coordinates of the point of origin, P1 - let B1 = from[1]; - let L1 = from[0]; + let (L1, B1) = from.xy_as_f64(); // The latitude of P1 projected onto the auxiliary sphere let U1 = self.latitude_geographic_to_reduced(B1); @@ -93,13 +99,13 @@ impl Ellipsoid { /// See [`geodesic_fwd`](crate::Ellipsoid::geodesic_fwd) #[must_use] #[allow(non_snake_case)] // So we can use the mathematical notation from the original text - pub fn geodesic_inv(&self, from: &Coor4D, to: &Coor4D) -> Coor4D { - let B1 = from[1]; - let B2 = to[1]; + pub fn geodesic_inv(&self, from: &T, to: &T) -> Coor4D + where + T: CoordTrait, + { + let (L1, B1) = from.xy_as_f64(); + let (L2, B2) = to.xy_as_f64(); let B = B2 - B1; - - let L1 = from[0]; - let L2 = to[0]; let L = L2 - L1; // Below the micrometer level, we don't care about directions @@ -191,14 +197,17 @@ impl Ellipsoid { /// // Compute the distance between Copenhagen and Paris /// use geodesy::prelude::*; /// if let Ok(ellps) = Ellipsoid::named("GRS80") { - /// let p0 = Coor4D::geo(55., 12., 0., 0.); - /// let p1 = Coor4D::geo(49., 2., 0., 0.); + /// let p0 = Coor2D::geo(55., 12.); + /// let p1 = Coor2D::geo(49., 2.); /// let d = ellps.distance(&p0, &p1); /// assert!((d - 956_066.231_959).abs() < 1e-5); /// } /// ``` #[must_use] - pub fn distance(&self, from: &Coor4D, to: &Coor4D) -> f64 { + pub fn distance(&self, from: &T, to: &T) -> f64 + where + T: CoordTrait, + { self.geodesic_inv(from, to)[2] } } @@ -216,8 +225,8 @@ mod tests { // Copenhagen (Denmark)--Paris (France) // Expect distance good to 0.01 mm, azimuths to a nanodegree - let p1 = Coor4D::gis(12., 55., 0., 0.); - let p2 = Coor4D::gis(2., 49., 0., 0.); + let p1 = Coor2D::gis(12., 55.); + let p2 = Coor2D::gis(2., 49.); let d = ellps.geodesic_inv(&p1, &p2); assert!((d[0].to_degrees() - (-130.15406042072)).abs() < 1e-9); @@ -231,7 +240,7 @@ mod tests { // Copenhagen (Denmark)--Rabat (Morocco) // Expect distance good to 0.1 mm, azimuths to a nanodegree - let p2 = Coor4D::gis(7., 34., 0., 0.); + let p2 = Coor2D::gis(7., 34.); let d = ellps.geodesic_inv(&p1, &p2); assert!((d[0].to_degrees() - (-168.48914418666)).abs() < 1e-9);