Skip to content

Commit

Permalink
Much cleanup, num_traits new dependency
Browse files Browse the repository at this point in the history
  • Loading branch information
busstoptaktik committed Mar 20, 2024
1 parent 1e27657 commit 80120f5
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 183 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ default-run = "kp"
[dependencies]
# Library functionality
uuid = { version = "1", features = ["v4"] }
num-traits = { version = "0.2" }

# Command line program helpers
clap = { version = "4.3.11", features = ["derive"], optional = true }
Expand Down
243 changes: 72 additions & 171 deletions src/coordinate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,229 +105,130 @@ pub trait CoordinateSet: CoordinateMetadata {
}
}

// An experiment with an extended version of Kyle Barron's CoordTrait<DIMENSION, MEASURE> PR
//-----------------------------------------------------------------------
// 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.
//-----------------------------------------------------------------------

// The next 8 lines are mostly copied from georust/geo/geo-types/src/lib.rs
// Although I added ToPrimitive in the first line
use core::fmt::Debug;
use num_traits::{Float, Num, NumCast, ToPrimitive};
pub trait CoordinateType: Num + Copy + NumCast + PartialOrd + Debug {}
impl<T: Num + Copy + NumCast + PartialOrd + Debug> CoordinateType for T {}
pub trait CoordNum: CoordinateType + Debug {}
impl<T: CoordinateType + Debug> CoordNum for T {}
pub trait CoordFloat: CoordNum + Float {}
impl<T: CoordNum + Float> CoordFloat for T {}

// And here is Kyle Barron's CoordTrait from https://github.com/georust/geo/pull/1157
// extended with z(), t(), m(), and associated consts DIMENSION and MEASURE
pub trait CoordTrait {
type T: CoordNum;
const DIMENSION: usize;
const MEASURE: bool;

// Required implementations

/// Accessors for the coordinate tuple components
fn x(&self) -> Self::T;
fn y(&self) -> Self::T;
fn z(&self) -> Self::T;
fn t(&self) -> Self::T;
fn m(&self) -> Self::T;

/// Accessors for the coordinate tuple components converted to f64
fn x_as_f64(&self) -> f64;
fn y_as_f64(&self) -> f64;
fn z_as_f64(&self) -> f64;
fn t_as_f64(&self) -> f64;
fn m_as_f64(&self) -> f64;

// Provided implementations

/// Returns a tuple that contains the two first components of the coord.
fn xy(&self) -> (Self::T, Self::T) {
fn x_y(&self) -> (Self::T, Self::T) {
(self.x(), self.y())
}
}

/// Returns a tuple that contains the three first components of the coord.
fn xyz(&self) -> (Self::T, Self::T, Self::T) {
(self.x(), self.y(), self.z())
}
// The CoordTuples trait is blanket-implemented for anything that
// CoordTrait is implemented for
impl<C: CoordTrait> CoordTuples for C {}

/// Returns a tuple that contains the three first components of the coord.
fn xyzt(&self) -> (Self::T, Self::T, Self::T, Self::T) {
(self.x(), self.y(), self.z(), self.t())
}
// And here the actual implementation, which takes any CoordTrait implementing
// data type, and lets us access the contents as geodesy-compatible f64 tuples
#[rustfmt::skip]
pub trait CoordTuples: CoordTrait {
/// Accessors for the coordinate tuple components converted to f64
fn x_as_f64(&self) -> f64 { self.x().to_f64().unwrap_or(f64::NAN) }
fn y_as_f64(&self) -> f64 { self.y().to_f64().unwrap_or(f64::NAN) }
fn z_as_f64(&self) -> f64 { self.z().to_f64().unwrap_or(f64::NAN) }
fn t_as_f64(&self) -> f64 { self.t().to_f64().unwrap_or(f64::NAN) }
fn m_as_f64(&self) -> f64 { self.m().to_f64().unwrap_or(f64::NAN) }

/// Returns a tuple that contains the two first components of the coord converted to f64.
fn xy_as_f64(&self) -> (f64, f64) {
(self.x_as_f64(), self.y_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.x_as_f64(), self.y_as_f64(), self.z_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.x_as_f64(),
self.y_as_f64(),
self.z_as_f64(),
self.t_as_f64(),
)
(self.x_as_f64(), self.y_as_f64(), self.z_as_f64(), self.t_as_f64())
}
}

// We must still implement the foundational CoordTrait trait for
// the Geodesy data types Coor2D, Coor32, Coor3D, Coor4D

#[rustfmt::skip]
impl CoordTrait for Coor2D {
type T = f64;
const DIMENSION: usize = 2;
const MEASURE: bool = false;

/// Accessors for the coordinate tuple components
fn x(&self) -> Self::T {
self.0[0]
}
fn y(&self) -> Self::T {
self.0[1]
}
fn z(&self) -> Self::T {
f64::NAN
}
fn t(&self) -> Self::T {
f64::NAN
}
fn m(&self) -> Self::T {
f64::NAN
}

/// Accessors for the coordinate tuple components converted to f64
fn x_as_f64(&self) -> f64 {
self.0[0]
}
fn y_as_f64(&self) -> f64 {
self.0[1]
}
fn z_as_f64(&self) -> f64 {
f64::NAN
}
fn t_as_f64(&self) -> f64 {
f64::NAN
}
fn m_as_f64(&self) -> f64 {
f64::NAN
}
fn x(&self) -> Self::T { self.0[0] }
fn y(&self) -> Self::T { self.0[1] }
fn z(&self) -> Self::T { f64::NAN }
fn t(&self) -> Self::T { f64::NAN }
fn m(&self) -> Self::T { f64::NAN }
}

#[rustfmt::skip]
impl CoordTrait for Coor32 {
type T = f32;
const DIMENSION: usize = 2;
const MEASURE: bool = false;

/// Accessors for the coordinate tuple components
fn x(&self) -> Self::T {
self.0[0]
}
fn y(&self) -> Self::T {
self.0[1]
}
fn z(&self) -> Self::T {
f32::NAN
}
fn t(&self) -> Self::T {
f32::NAN
}
fn m(&self) -> Self::T {
f32::NAN
}

/// Accessors for the coordinate tuple components converted to f64
fn x_as_f64(&self) -> f64 {
self.0[0] as f64
}
fn y_as_f64(&self) -> f64 {
self.0[1] as f64
}
fn z_as_f64(&self) -> f64 {
f64::NAN
}
fn t_as_f64(&self) -> f64 {
f64::NAN
}
fn m_as_f64(&self) -> f64 {
f64::NAN
}
fn x(&self) -> Self::T { self.0[0] }
fn y(&self) -> Self::T { self.0[1] }
fn z(&self) -> Self::T { f32::NAN }
fn t(&self) -> Self::T { f32::NAN }
fn m(&self) -> Self::T { f32::NAN }
}

#[rustfmt::skip]
impl CoordTrait for Coor3D {
type T = f64;
const DIMENSION: usize = 3;
const MEASURE: bool = false;

/// Accessors for the coordinate tuple components
fn x(&self) -> Self::T {
self.0[0]
}
fn y(&self) -> Self::T {
self.0[1]
}
fn z(&self) -> Self::T {
self.0[2]
}
fn t(&self) -> Self::T {
f64::NAN
}
fn m(&self) -> Self::T {
f64::NAN
}

/// Accessors for the coordinate tuple components converted to f64
fn x_as_f64(&self) -> f64 {
self.0[0]
}
fn y_as_f64(&self) -> f64 {
self.0[1]
}
fn z_as_f64(&self) -> f64 {
self.0[2]
}
fn t_as_f64(&self) -> f64 {
f64::NAN
}
fn m_as_f64(&self) -> f64 {
f64::NAN
}
fn x(&self) -> Self::T { self.0[0] }
fn y(&self) -> Self::T { self.0[1] }
fn z(&self) -> Self::T { self.0[2] }
fn t(&self) -> Self::T { f64::NAN }
fn m(&self) -> Self::T { f64::NAN }
}

#[rustfmt::skip]
impl CoordTrait for Coor4D {
type T = f64;
const DIMENSION: usize = 4;
const MEASURE: bool = false;
fn x(&self) -> Self::T { self.0[0] }
fn y(&self) -> Self::T { self.0[1] }
fn z(&self) -> Self::T { self.0[2] }
fn t(&self) -> Self::T { self.0[3] }
fn m(&self) -> Self::T { f64::NAN }
}

/// Accessors for the coordinate tuple components
fn x(&self) -> Self::T {
self.0[0]
}
fn y(&self) -> Self::T {
self.0[1]
}
fn z(&self) -> Self::T {
self.0[2]
}
fn t(&self) -> Self::T {
self.0[3]
}
fn m(&self) -> Self::T {
f64::NAN
}

/// Accessors for the coordinate tuple components converted to f64
fn x_as_f64(&self) -> f64 {
self.0[0]
}
fn y_as_f64(&self) -> f64 {
self.0[1]
}
fn z_as_f64(&self) -> f64 {
self.0[2]
}
fn t_as_f64(&self) -> f64 {
self.0[3]
}
fn m_as_f64(&self) -> f64 {
f64::NAN
}
// And let's also implement it for a plain 2D f64 tuple
#[rustfmt::skip]
impl CoordTrait for (f64, f64) {
type T = f64;
const DIMENSION: usize = 2;
const MEASURE: bool = false;
fn x(&self) -> Self::T { self.0 }
fn y(&self) -> Self::T { self.1 }
fn z(&self) -> Self::T { f64::NAN }
fn t(&self) -> Self::T { f64::NAN }
fn m(&self) -> Self::T { f64::NAN }
}
20 changes: 8 additions & 12 deletions src/ellipsoid/geodesics.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::coordinate::CoordTrait;
use num_traits::AsPrimitive;

use crate::coordinate::{CoordTrait, CoordTuples};

// Now using an extended version of Kyle Barron's CoordTrait, cf. src/coordinate/mod.rs

Expand All @@ -18,10 +20,7 @@ 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<T>(&self, from: &T, azimuth: f64, distance: f64) -> Coor4D
where
T: CoordTrait,
{
pub fn geodesic_fwd<G: CoordTuples>(&self, from: &G, azimuth: f64, distance: f64) -> Coor4D {
// Coordinates of the point of origin, P1
let (L1, B1) = from.xy_as_f64();

Expand Down Expand Up @@ -99,10 +98,7 @@ 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<T>(&self, from: &T, to: &T) -> Coor4D
where
T: CoordTrait,
{
pub fn geodesic_inv<G: CoordTrait>(&self, from: &G, to: &G) -> Coor4D {
let (L1, B1) = from.xy_as_f64();
let (L2, B2) = to.xy_as_f64();
let B = B2 - B1;
Expand Down Expand Up @@ -204,11 +200,11 @@ impl Ellipsoid {
/// }
/// ```
#[must_use]
pub fn distance<T>(&self, from: &T, to: &T) -> f64
pub fn distance<G: CoordTrait>(&self, from: &G, to: &G) -> f64
where
T: CoordTrait,
G::T: AsPrimitive<f64>,
{
self.geodesic_inv::<T>(from, to)[2]
self.geodesic_inv::<G>(from, to)[2]
}
}

Expand Down

0 comments on commit 80120f5

Please sign in to comment.