Skip to content

Commit 80120f5

Browse files
committed
Much cleanup, num_traits new dependency
1 parent 1e27657 commit 80120f5

File tree

3 files changed

+81
-183
lines changed

3 files changed

+81
-183
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ default-run = "kp"
1818
[dependencies]
1919
# Library functionality
2020
uuid = { version = "1", features = ["v4"] }
21+
num-traits = { version = "0.2" }
2122

2223
# Command line program helpers
2324
clap = { version = "4.3.11", features = ["derive"], optional = true }

src/coordinate/mod.rs

+72-171
Original file line numberDiff line numberDiff line change
@@ -105,229 +105,130 @@ pub trait CoordinateSet: CoordinateMetadata {
105105
}
106106
}
107107

108-
// An experiment with an extended version of Kyle Barron's CoordTrait<DIMENSION, MEASURE> PR
108+
//-----------------------------------------------------------------------
109+
// An experiment with an extended version of Kyle Barron's CoordTrait PR
109110
// over at https://github.com/georust/geo/pull/1157
110-
111-
pub trait CoordNum {}
112-
impl CoordNum for f32 {}
113-
impl CoordNum for f64 {}
114-
115-
/// A trait for accessing data from a generic Coord.
111+
//-----------------------------------------------------------------------
112+
113+
// The next 8 lines are mostly copied from georust/geo/geo-types/src/lib.rs
114+
// Although I added ToPrimitive in the first line
115+
use core::fmt::Debug;
116+
use num_traits::{Float, Num, NumCast, ToPrimitive};
117+
pub trait CoordinateType: Num + Copy + NumCast + PartialOrd + Debug {}
118+
impl<T: Num + Copy + NumCast + PartialOrd + Debug> CoordinateType for T {}
119+
pub trait CoordNum: CoordinateType + Debug {}
120+
impl<T: CoordinateType + Debug> CoordNum for T {}
121+
pub trait CoordFloat: CoordNum + Float {}
122+
impl<T: CoordNum + Float> CoordFloat for T {}
123+
124+
// And here is Kyle Barron's CoordTrait from https://github.com/georust/geo/pull/1157
125+
// extended with z(), t(), m(), and associated consts DIMENSION and MEASURE
116126
pub trait CoordTrait {
117127
type T: CoordNum;
118128
const DIMENSION: usize;
119129
const MEASURE: bool;
120130

121-
// Required implementations
122-
123131
/// Accessors for the coordinate tuple components
124132
fn x(&self) -> Self::T;
125133
fn y(&self) -> Self::T;
126134
fn z(&self) -> Self::T;
127135
fn t(&self) -> Self::T;
128136
fn m(&self) -> Self::T;
129137

130-
/// Accessors for the coordinate tuple components converted to f64
131-
fn x_as_f64(&self) -> f64;
132-
fn y_as_f64(&self) -> f64;
133-
fn z_as_f64(&self) -> f64;
134-
fn t_as_f64(&self) -> f64;
135-
fn m_as_f64(&self) -> f64;
136-
137-
// Provided implementations
138-
139138
/// Returns a tuple that contains the two first components of the coord.
140-
fn xy(&self) -> (Self::T, Self::T) {
139+
fn x_y(&self) -> (Self::T, Self::T) {
141140
(self.x(), self.y())
142141
}
142+
}
143143

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

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

154-
/// Returns a tuple that contains the two first components of the coord converted to f64.
155159
fn xy_as_f64(&self) -> (f64, f64) {
156160
(self.x_as_f64(), self.y_as_f64())
157161
}
158162

159-
/// Returns a tuple that contains the three first components of the coord converted to f64.
160163
fn xyz_as_f64(&self) -> (f64, f64, f64) {
161164
(self.x_as_f64(), self.y_as_f64(), self.z_as_f64())
162165
}
163166

164-
/// Returns a tuple that contains the three first components of the coord converted to f64.
165167
fn xyzt_as_f64(&self) -> (f64, f64, f64, f64) {
166-
(
167-
self.x_as_f64(),
168-
self.y_as_f64(),
169-
self.z_as_f64(),
170-
self.t_as_f64(),
171-
)
168+
(self.x_as_f64(), self.y_as_f64(), self.z_as_f64(), self.t_as_f64())
172169
}
173170
}
174171

172+
// We must still implement the foundational CoordTrait trait for
173+
// the Geodesy data types Coor2D, Coor32, Coor3D, Coor4D
174+
175+
#[rustfmt::skip]
175176
impl CoordTrait for Coor2D {
176177
type T = f64;
177178
const DIMENSION: usize = 2;
178179
const MEASURE: bool = false;
179-
180-
/// Accessors for the coordinate tuple components
181-
fn x(&self) -> Self::T {
182-
self.0[0]
183-
}
184-
fn y(&self) -> Self::T {
185-
self.0[1]
186-
}
187-
fn z(&self) -> Self::T {
188-
f64::NAN
189-
}
190-
fn t(&self) -> Self::T {
191-
f64::NAN
192-
}
193-
fn m(&self) -> Self::T {
194-
f64::NAN
195-
}
196-
197-
/// Accessors for the coordinate tuple components converted to f64
198-
fn x_as_f64(&self) -> f64 {
199-
self.0[0]
200-
}
201-
fn y_as_f64(&self) -> f64 {
202-
self.0[1]
203-
}
204-
fn z_as_f64(&self) -> f64 {
205-
f64::NAN
206-
}
207-
fn t_as_f64(&self) -> f64 {
208-
f64::NAN
209-
}
210-
fn m_as_f64(&self) -> f64 {
211-
f64::NAN
212-
}
180+
fn x(&self) -> Self::T { self.0[0] }
181+
fn y(&self) -> Self::T { self.0[1] }
182+
fn z(&self) -> Self::T { f64::NAN }
183+
fn t(&self) -> Self::T { f64::NAN }
184+
fn m(&self) -> Self::T { f64::NAN }
213185
}
214186

187+
#[rustfmt::skip]
215188
impl CoordTrait for Coor32 {
216189
type T = f32;
217190
const DIMENSION: usize = 2;
218191
const MEASURE: bool = false;
219-
220-
/// Accessors for the coordinate tuple components
221-
fn x(&self) -> Self::T {
222-
self.0[0]
223-
}
224-
fn y(&self) -> Self::T {
225-
self.0[1]
226-
}
227-
fn z(&self) -> Self::T {
228-
f32::NAN
229-
}
230-
fn t(&self) -> Self::T {
231-
f32::NAN
232-
}
233-
fn m(&self) -> Self::T {
234-
f32::NAN
235-
}
236-
237-
/// Accessors for the coordinate tuple components converted to f64
238-
fn x_as_f64(&self) -> f64 {
239-
self.0[0] as f64
240-
}
241-
fn y_as_f64(&self) -> f64 {
242-
self.0[1] as f64
243-
}
244-
fn z_as_f64(&self) -> f64 {
245-
f64::NAN
246-
}
247-
fn t_as_f64(&self) -> f64 {
248-
f64::NAN
249-
}
250-
fn m_as_f64(&self) -> f64 {
251-
f64::NAN
252-
}
192+
fn x(&self) -> Self::T { self.0[0] }
193+
fn y(&self) -> Self::T { self.0[1] }
194+
fn z(&self) -> Self::T { f32::NAN }
195+
fn t(&self) -> Self::T { f32::NAN }
196+
fn m(&self) -> Self::T { f32::NAN }
253197
}
254198

199+
#[rustfmt::skip]
255200
impl CoordTrait for Coor3D {
256201
type T = f64;
257202
const DIMENSION: usize = 3;
258203
const MEASURE: bool = false;
259-
260-
/// Accessors for the coordinate tuple components
261-
fn x(&self) -> Self::T {
262-
self.0[0]
263-
}
264-
fn y(&self) -> Self::T {
265-
self.0[1]
266-
}
267-
fn z(&self) -> Self::T {
268-
self.0[2]
269-
}
270-
fn t(&self) -> Self::T {
271-
f64::NAN
272-
}
273-
fn m(&self) -> Self::T {
274-
f64::NAN
275-
}
276-
277-
/// Accessors for the coordinate tuple components converted to f64
278-
fn x_as_f64(&self) -> f64 {
279-
self.0[0]
280-
}
281-
fn y_as_f64(&self) -> f64 {
282-
self.0[1]
283-
}
284-
fn z_as_f64(&self) -> f64 {
285-
self.0[2]
286-
}
287-
fn t_as_f64(&self) -> f64 {
288-
f64::NAN
289-
}
290-
fn m_as_f64(&self) -> f64 {
291-
f64::NAN
292-
}
204+
fn x(&self) -> Self::T { self.0[0] }
205+
fn y(&self) -> Self::T { self.0[1] }
206+
fn z(&self) -> Self::T { self.0[2] }
207+
fn t(&self) -> Self::T { f64::NAN }
208+
fn m(&self) -> Self::T { f64::NAN }
293209
}
294210

211+
#[rustfmt::skip]
295212
impl CoordTrait for Coor4D {
296213
type T = f64;
297214
const DIMENSION: usize = 4;
298215
const MEASURE: bool = false;
216+
fn x(&self) -> Self::T { self.0[0] }
217+
fn y(&self) -> Self::T { self.0[1] }
218+
fn z(&self) -> Self::T { self.0[2] }
219+
fn t(&self) -> Self::T { self.0[3] }
220+
fn m(&self) -> Self::T { f64::NAN }
221+
}
299222

300-
/// Accessors for the coordinate tuple components
301-
fn x(&self) -> Self::T {
302-
self.0[0]
303-
}
304-
fn y(&self) -> Self::T {
305-
self.0[1]
306-
}
307-
fn z(&self) -> Self::T {
308-
self.0[2]
309-
}
310-
fn t(&self) -> Self::T {
311-
self.0[3]
312-
}
313-
fn m(&self) -> Self::T {
314-
f64::NAN
315-
}
316-
317-
/// Accessors for the coordinate tuple components converted to f64
318-
fn x_as_f64(&self) -> f64 {
319-
self.0[0]
320-
}
321-
fn y_as_f64(&self) -> f64 {
322-
self.0[1]
323-
}
324-
fn z_as_f64(&self) -> f64 {
325-
self.0[2]
326-
}
327-
fn t_as_f64(&self) -> f64 {
328-
self.0[3]
329-
}
330-
fn m_as_f64(&self) -> f64 {
331-
f64::NAN
332-
}
223+
// And let's also implement it for a plain 2D f64 tuple
224+
#[rustfmt::skip]
225+
impl CoordTrait for (f64, f64) {
226+
type T = f64;
227+
const DIMENSION: usize = 2;
228+
const MEASURE: bool = false;
229+
fn x(&self) -> Self::T { self.0 }
230+
fn y(&self) -> Self::T { self.1 }
231+
fn z(&self) -> Self::T { f64::NAN }
232+
fn t(&self) -> Self::T { f64::NAN }
233+
fn m(&self) -> Self::T { f64::NAN }
333234
}

src/ellipsoid/geodesics.rs

+8-12
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
use crate::coordinate::CoordTrait;
1+
use num_traits::AsPrimitive;
2+
3+
use crate::coordinate::{CoordTrait, CoordTuples};
24

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

@@ -18,10 +20,7 @@ impl Ellipsoid {
1820
/// Federico Dolce and Michael Kirk, provides a Rust implementation of Karney's algorithm.
1921
#[must_use]
2022
#[allow(non_snake_case)]
21-
pub fn geodesic_fwd<T>(&self, from: &T, azimuth: f64, distance: f64) -> Coor4D
22-
where
23-
T: CoordTrait,
24-
{
23+
pub fn geodesic_fwd<G: CoordTuples>(&self, from: &G, azimuth: f64, distance: f64) -> Coor4D {
2524
// Coordinates of the point of origin, P1
2625
let (L1, B1) = from.xy_as_f64();
2726

@@ -99,10 +98,7 @@ impl Ellipsoid {
9998
/// See [`geodesic_fwd`](crate::Ellipsoid::geodesic_fwd)
10099
#[must_use]
101100
#[allow(non_snake_case)] // So we can use the mathematical notation from the original text
102-
pub fn geodesic_inv<T>(&self, from: &T, to: &T) -> Coor4D
103-
where
104-
T: CoordTrait,
105-
{
101+
pub fn geodesic_inv<G: CoordTrait>(&self, from: &G, to: &G) -> Coor4D {
106102
let (L1, B1) = from.xy_as_f64();
107103
let (L2, B2) = to.xy_as_f64();
108104
let B = B2 - B1;
@@ -204,11 +200,11 @@ impl Ellipsoid {
204200
/// }
205201
/// ```
206202
#[must_use]
207-
pub fn distance<T>(&self, from: &T, to: &T) -> f64
203+
pub fn distance<G: CoordTrait>(&self, from: &G, to: &G) -> f64
208204
where
209-
T: CoordTrait,
205+
G::T: AsPrimitive<f64>,
210206
{
211-
self.geodesic_inv::<T>(from, to)[2]
207+
self.geodesic_inv::<G>(from, to)[2]
212208
}
213209
}
214210

0 commit comments

Comments
 (0)