Skip to content

Commit 5582bac

Browse files
A CoordTrait experiment (#103)
Essentially: - Traitify coordinate access - Add dim() as required impl for `CoordinateSet` In detail: - A CoordTrait experiment - Simplified, and added dimension+measure - Trait impl of ISO-19111 coordinate tuple - Angular conversions in CoordinateTuple - Extend and use CoordinateTuple trait. dim in CoordinateSet - Prefix unchecked to postfix - xy/set_xy for CoordinateSet. Tests via tmerc/merc
1 parent 5ed8773 commit 5582bac

27 files changed

+704
-612
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 }

examples/00-transformations.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,7 @@ fn main() -> anyhow::Result<()> {
3737
// But since a coordinate tuple is really just an array of double
3838
// precision numbers, you may also generate it directly using plain
3939
// Rust syntax. Note that Coor2D, like f64, provides the to_radians
40-
// method. So compared to cph_raw above, we can use a slightly more
41-
// compact notation.
40+
// method, but it operates in place, so we need two steps in this case.
4241
let cph_direct = Coor2D([12., 55.]).to_radians();
4342
// The three versions of Copenhagen coordinates should be identical.
4443
assert_eq!(cph, cph_raw);
@@ -79,8 +78,8 @@ fn main() -> anyhow::Result<()> {
7978
// Note the use of `to_geo`, which transforms lon/lat in radians
8079
// to lat/lon in degrees. It is defined for Coor2D as well as for
8180
// arrays, vectors and slices of Coor2D
82-
for coord in data.to_geo() {
83-
println!(" {:?}", coord);
81+
for coord in data {
82+
println!(" {:?}", coord.to_geo());
8483
}
8584

8685
// To geo again, but using slices - in two different ways

examples/01-geometric_geodesy.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ fn main() -> Result<(), Error> {
4646
// surface of the ellipsoid. Let's compute the distance and
4747
// azimuth between the approximate locations of the airports
4848
// of Copenhagen (CPH) and Paris (CDG).
49-
let CPH = Coor4D::geo(55., 12., 0., 0.);
50-
let CDG = Coor4D::geo(49., 2., 0., 0.);
49+
let CPH = Coor2D::geo(55., 12.);
50+
let CDG = Coor2D::geo(49., 2.);
5151

5252
// By historical convention the "from A to B" situation is considered
5353
// the inverse sense of the geodesic problem - hence `geodesic_inv`:

examples/02-user_defined_macros.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,9 @@ fn main() -> anyhow::Result<()> {
4646

4747
// data.to_geo() transforms all elements in data from the internal GIS
4848
// format (lon/lat in radians) to lat/lon in degrees.
49-
data.to_geo();
5049
println!("Back to ed50:");
5150
for coord in data {
52-
println!(" {:?}", coord);
51+
println!(" {:?}", coord.to_geo());
5352
}
5453

5554
Ok(())

examples/06-user_defined_coordinate_types_and_containers.rs

+3
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ impl CoordinateSet for AbscissaCollection {
7575
fn len(&self) -> usize {
7676
4
7777
}
78+
fn dim(&self) -> usize {
79+
1
80+
}
7881
}
7982

8083
fn main() -> Result<(), anyhow::Error> {

examples/07-examples_from_ruminations.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,8 @@ fn rumination_000() -> Result<(), anyhow::Error> {
6161

6262
// [6] And go back, i.e. utm -> geo
6363
ctx.apply(utm32, Inv, &mut data)?;
64-
data.to_geo();
6564
for coord in data {
66-
println!("{:?}", coord);
65+
println!("{:?}", coord.to_geo());
6766
}
6867

6968
Ok(())

examples/08-user_defined_operators_using_proj.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ pub fn proj_constructor(parameters: &RawParameters, _ctx: &dyn Context) -> Resul
173173
fn main() -> anyhow::Result<()> {
174174
let mut prv = geodesy::Minimal::new();
175175
prv.register_op("proj", OpConstructor(proj_constructor));
176+
let e = Ellipsoid::default();
176177

177178
// Check that we can access the `proj` binary - if not, just ignore
178179
if Command::new("proj").stderr(Stdio::piped()).spawn().is_err() {
@@ -195,7 +196,7 @@ fn main() -> anyhow::Result<()> {
195196
println!("projected: {:?}", geo[0]);
196197

197198
ctx.apply(op, Inv, &mut geo)?;
198-
assert!(rtp[0].default_ellps_dist(&geo[0]) < 1e-5);
199+
assert!(e.distance(&rtp[0], &geo[0]) < 1e-5);
199200
println!("roundtrip: {:?}", geo[0].to_degrees());
200201

201202
// Inverted invocation - note "proj inv ..."
@@ -208,7 +209,7 @@ fn main() -> anyhow::Result<()> {
208209

209210
// Now, we get the inverse utm projection when calling the operator in the Fwd direction
210211
ctx.apply(op, Fwd, &mut utm)?;
211-
assert!(geo[0].default_ellps_dist(&utm[0]) < 1e-5);
212+
assert!(e.distance(&utm[0], &geo[0]) < 1e-5);
212213
// ...and roundtrip back to utm
213214
ctx.apply(op, Inv, &mut utm)?;
214215
assert!(rtp[0].hypot2(&utm[0]) < 1e-5);

src/context/minimal.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -143,16 +143,16 @@ mod tests {
143143
assert_eq!(steps[2], "addone inv");
144144

145145
let mut data = some_basic_coor2dinates();
146-
assert_eq!(data[0][0], 55.);
147-
assert_eq!(data[1][0], 59.);
146+
assert_eq!(data[0].x(), 55.);
147+
assert_eq!(data[1].x(), 59.);
148148

149149
assert_eq!(2, ctx.apply(op, Fwd, &mut data)?);
150-
assert_eq!(data[0][0], 56.);
151-
assert_eq!(data[1][0], 60.);
150+
assert_eq!(data[0].x(), 56.);
151+
assert_eq!(data[1].x(), 60.);
152152

153153
ctx.apply(op, Inv, &mut data)?;
154-
assert_eq!(data[0][0], 55.);
155-
assert_eq!(data[1][0], 59.);
154+
assert_eq!(data[0].x(), 55.);
155+
assert_eq!(data[1].x(), 59.);
156156

157157
let params = ctx.params(op, 1)?;
158158
let ellps = params.ellps(0);
@@ -168,8 +168,8 @@ mod tests {
168168
let op = ctx.op("geo:in | utm zone=32 | neu:out")?;
169169

170170
let mut data = some_basic_coor2dinates();
171-
assert_eq!(data[0][0], 55.);
172-
assert_eq!(data[1][0], 59.);
171+
assert_eq!(data[0].x(), 55.);
172+
assert_eq!(data[1].x(), 59.);
173173

174174
ctx.apply(op, Fwd, &mut data)?;
175175
let expected = [6098907.825005002, 691875.6321396609];

src/context/plain.rs

+12-12
Original file line numberDiff line numberDiff line change
@@ -312,16 +312,16 @@ mod tests {
312312

313313
// ...and it works as expected?
314314
let mut data = some_basic_coor2dinates();
315-
assert_eq!(data[0][0], 55.);
316-
assert_eq!(data[1][0], 59.);
315+
assert_eq!(data[0].x(), 55.);
316+
assert_eq!(data[1].x(), 59.);
317317

318318
ctx.apply(op, Fwd, &mut data)?;
319-
assert_eq!(data[0][0], 56.);
320-
assert_eq!(data[1][0], 60.);
319+
assert_eq!(data[0].x(), 56.);
320+
assert_eq!(data[1].x(), 60.);
321321

322322
ctx.apply(op, Inv, &mut data)?;
323-
assert_eq!(data[0][0], 55.);
324-
assert_eq!(data[1][0], 59.);
323+
assert_eq!(data[0].x(), 55.);
324+
assert_eq!(data[1].x(), 59.);
325325

326326
// Now test that the look-up functionality works in general
327327

@@ -344,8 +344,8 @@ mod tests {
344344
let mut data = some_basic_coor2dinates();
345345

346346
ctx.apply(op, Fwd, &mut data)?;
347-
assert_eq!(data[0][0], 57.);
348-
assert_eq!(data[1][0], 61.);
347+
assert_eq!(data[0].x(), 57.);
348+
assert_eq!(data[1].x(), 61.);
349349

350350
// 3 Console tests from stupid.md
351351
let op = ctx.op("stupid:bad");
@@ -354,14 +354,14 @@ mod tests {
354354
let op = ctx.op("stupid:addthree")?;
355355
let mut data = some_basic_coor2dinates();
356356
ctx.apply(op, Fwd, &mut data)?;
357-
assert_eq!(data[0][0], 58.);
358-
assert_eq!(data[1][0], 62.);
357+
assert_eq!(data[0].x(), 58.);
358+
assert_eq!(data[1].x(), 62.);
359359

360360
let op = ctx.op("stupid:addthree_one_by_one")?;
361361
let mut data = some_basic_coor2dinates();
362362
ctx.apply(op, Fwd, &mut data)?;
363-
assert_eq!(data[0][0], 58.);
364-
assert_eq!(data[1][0], 62.);
363+
assert_eq!(data[0].x(), 58.);
364+
assert_eq!(data[1].x(), 62.);
365365

366366
// Make sure we can access "sigil-less runtime defined resources"
367367
ctx.register_resource("foo", "bar");

src/coordinate/coor2d.rs

+9-87
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
use super::*;
22
use crate::math::angular;
3-
use std::ops::{Index, IndexMut};
43

54
/// Generic 2D Coordinate tuple, with no fixed interpretation of the elements
65
#[derive(Debug, Default, PartialEq, Copy, Clone)]
76
pub struct Coor2D(pub [f64; 2]);
87

9-
// ----- O P E R A T O R T R A I T S -------------------------------------------------
8+
use std::ops::{Index, IndexMut};
109

1110
impl Index<usize> for Coor2D {
1211
type Output = f64;
@@ -21,34 +20,6 @@ impl IndexMut<usize> for Coor2D {
2120
}
2221
}
2322

24-
// ----- A N G U L A R U N I T S -------------------------------------------
25-
26-
impl AngularUnits for Coor2D {
27-
/// Transform the elements of a `Coor2D` from degrees to radians
28-
#[must_use]
29-
fn to_radians(self) -> Self {
30-
Coor2D([self[0].to_radians(), self[1].to_radians()])
31-
}
32-
33-
/// Transform the elements of a `Coor2D` from radians to degrees
34-
#[must_use]
35-
fn to_degrees(self) -> Self {
36-
Coor2D([self[0].to_degrees(), self[1].to_degrees()])
37-
}
38-
39-
/// Transform the elements of a `Coor2D` from radians to seconds of arc.
40-
#[must_use]
41-
fn to_arcsec(self) -> Self {
42-
Coor2D([self[0].to_degrees() * 3600., self[1].to_degrees() * 3600.])
43-
}
44-
45-
/// Transform the internal lon/lat-in-radians to lat/lon-in-degrees
46-
#[must_use]
47-
fn to_geo(self) -> Self {
48-
Coor2D([self[1].to_degrees(), self[0].to_degrees()])
49-
}
50-
}
51-
5223
// ----- C O N S T R U C T O R S ---------------------------------------------
5324

5425
/// Constructors
@@ -127,62 +98,13 @@ impl Coor2D {
12798
/// Multiply by a scalar
12899
#[must_use]
129100
pub fn scale(&self, factor: f64) -> Coor2D {
130-
Coor2D([self[0] * factor, self[1] * factor])
101+
Coor2D([self.x() * factor, self.y() * factor])
131102
}
132103

133104
/// Scalar product
134105
#[must_use]
135106
pub fn dot(&self, other: Coor2D) -> f64 {
136-
self[0] * other[0] + self[1] * other[1]
137-
}
138-
}
139-
140-
// ----- D I S T A N C E S ---------------------------------------------------
141-
142-
impl Coor2D {
143-
/// Euclidean distance between two points in the 2D plane.
144-
///
145-
/// Primarily used to compute the distance between two projected points
146-
/// in their projected plane. Typically, this distance will differ from
147-
/// the actual distance in the real world.
148-
///
149-
/// # See also:
150-
///
151-
/// [`distance`](crate::ellipsoid::Ellipsoid::distance)
152-
///
153-
/// # Examples
154-
///
155-
/// ```
156-
/// use geodesy::prelude::*;
157-
/// let t = 1000 as f64;
158-
/// let p0 = Coor2D::origin();
159-
/// let p1 = Coor2D::raw(t, t);
160-
/// assert_eq!(p0.hypot2(&p1), t.hypot(t));
161-
/// ```
162-
#[must_use]
163-
pub fn hypot2(&self, other: &Self) -> f64 {
164-
(self[0] - other[0]).hypot(self[1] - other[1])
165-
}
166-
167-
/// The Geodesic distance on the default ellipsoid. Mostly a shortcut
168-
/// for test authoring
169-
pub fn default_ellps_dist(&self, other: &Self) -> f64 {
170-
Ellipsoid::default().distance(
171-
&Coor4D([self[0], self[1], 0., 0.]),
172-
&Coor4D([other[0], other[1], 0., 0.]),
173-
)
174-
}
175-
}
176-
177-
impl From<Coor2D> for Coor4D {
178-
fn from(c: Coor2D) -> Self {
179-
Coor4D([c[0], c[1], 0.0, 0.0])
180-
}
181-
}
182-
183-
impl From<Coor4D> for Coor2D {
184-
fn from(xyzt: Coor4D) -> Self {
185-
Coor2D([xyzt[0], xyzt[1]])
107+
self.x() * other.x() + self.y() * other.y()
186108
}
187109
}
188110

@@ -193,28 +115,28 @@ mod tests {
193115
use super::*;
194116
#[test]
195117
fn distances() {
118+
let e = Ellipsoid::default();
196119
let lat = angular::dms_to_dd(55, 30, 36.);
197120
let lon = angular::dms_to_dd(12, 45, 36.);
198121
let dms = Coor2D::geo(lat, lon);
199122
let geo = Coor2D::geo(55.51, 12.76);
200-
assert!(geo.default_ellps_dist(&dms) < 1e-10);
123+
assert!(e.distance(&geo, &dms) < 1e-10);
201124
}
202125

203126
#[test]
204127
fn coor2d() {
205128
let c = Coor2D::raw(12., 55.).to_radians();
206129
let d = Coor2D::gis(12., 55.);
207130
assert_eq!(c, d);
208-
assert_eq!(d[0], 12f64.to_radians());
209-
let e = d.to_degrees();
210-
assert_eq!(e[0], c.to_degrees()[0]);
131+
assert_eq!(d.x(), 12f64.to_radians());
132+
assert_eq!(d.x().to_degrees(), c.x().to_degrees());
211133
}
212134

213135
#[test]
214136
fn array() {
215137
let b = Coor2D::raw(7., 8.);
216-
let c = [b[0], b[1], f64::NAN, f64::NAN];
217-
assert_eq!(b[0], c[0]);
138+
let c = [b.x(), b.y(), f64::NAN, f64::NAN];
139+
assert_eq!(b.x(), c[0]);
218140
}
219141

220142
#[test]

0 commit comments

Comments
 (0)