Skip to content

Commit

Permalink
A CoordTrait experiment
Browse files Browse the repository at this point in the history
  • Loading branch information
busstoptaktik committed Mar 19, 2024
1 parent 5ed8773 commit 8fdbda2
Show file tree
Hide file tree
Showing 2 changed files with 261 additions and 15 deletions.
237 changes: 237 additions & 0 deletions src/coordinate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
39 changes: 24 additions & 15 deletions src/ellipsoid/geodesics.rs
Original file line number Diff line number Diff line change
@@ -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 -------------------------------------------------------------
Expand All @@ -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<T>(&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);
Expand Down Expand Up @@ -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<T>(&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
Expand Down Expand Up @@ -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<T>(&self, from: &T, to: &T) -> f64
where
T: CoordTrait,
{
self.geodesic_inv(from, to)[2]
}
}
Expand All @@ -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);
Expand All @@ -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);
Expand Down

0 comments on commit 8fdbda2

Please sign in to comment.