From 4a0692e46e43e9b437be877ee09de99746eb0b4d Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Wed, 8 Jan 2025 13:26:40 -0800 Subject: [PATCH 1/9] Change line_measure associated function to instance method In itself, this adds no new functionality, but sets us up for configurable line measures - e.g. a Haversine with a custom earth radius (or non-earth radius!) or a non wgs84 Geodesic --- geo/benches/euclidean_distance.rs | 4 +- geo/benches/geodesic_distance.rs | 2 +- geo/src/algorithm/centroid.rs | 8 +- geo/src/algorithm/closest_point.rs | 2 +- geo/src/algorithm/concave_hull.rs | 16 +- geo/src/algorithm/cross_track_distance.rs | 10 +- geo/src/algorithm/densify_haversine.rs | 16 +- geo/src/algorithm/euclidean_distance.rs | 52 ++-- geo/src/algorithm/euclidean_length.rs | 8 +- geo/src/algorithm/frechet_distance.rs | 4 +- geo/src/algorithm/geodesic_area.rs | 6 +- geo/src/algorithm/geodesic_bearing.rs | 2 +- geo/src/algorithm/geodesic_destination.rs | 4 +- geo/src/algorithm/geodesic_distance.rs | 4 +- geo/src/algorithm/geodesic_intermediate.rs | 10 +- geo/src/algorithm/geodesic_length.rs | 8 +- geo/src/algorithm/hausdorff_distance.rs | 4 +- geo/src/algorithm/haversine_bearing.rs | 2 +- geo/src/algorithm/haversine_closest_point.rs | 20 +- geo/src/algorithm/haversine_destination.rs | 4 +- geo/src/algorithm/haversine_distance.rs | 4 +- geo/src/algorithm/haversine_intermediate.rs | 10 +- geo/src/algorithm/haversine_length.rs | 8 +- geo/src/algorithm/interior_point.rs | 11 +- geo/src/algorithm/line_measures/bearing.rs | 2 +- geo/src/algorithm/line_measures/densify.rs | 111 ++++++--- .../algorithm/line_measures/destination.rs | 2 +- geo/src/algorithm/line_measures/distance.rs | 2 +- .../line_measures/interpolate_point.rs | 123 +++++----- geo/src/algorithm/line_measures/length.rs | 41 ++-- .../metric_spaces/euclidean/distance.rs | 222 +++++++++--------- .../metric_spaces/euclidean/mod.rs | 21 +- .../line_measures/metric_spaces/geodesic.rs | 63 ++--- .../line_measures/metric_spaces/haversine.rs | 73 +++--- .../line_measures/metric_spaces/rhumb.rs | 73 +++--- geo/src/algorithm/linestring_segment.rs | 39 ++- geo/src/algorithm/rhumb/bearing.rs | 4 +- geo/src/algorithm/rhumb/destination.rs | 4 +- geo/src/algorithm/rhumb/distance.rs | 4 +- geo/src/algorithm/rhumb/intermediate.rs | 10 +- geo/src/algorithm/rhumb/length.rs | 6 +- geo/src/algorithm/simplify.rs | 7 +- geo/src/algorithm/triangulate_spade.rs | 7 +- geo/src/types.rs | 2 +- 44 files changed, 543 insertions(+), 492 deletions(-) diff --git a/geo/benches/euclidean_distance.rs b/geo/benches/euclidean_distance.rs index b4e2e27c3..bf65ac442 100644 --- a/geo/benches/euclidean_distance.rs +++ b/geo/benches/euclidean_distance.rs @@ -37,7 +37,7 @@ fn criterion_benchmark(c: &mut criterion::Criterion) { (x: -6.064453, y: 68.49604), ]; bencher.iter(|| { - criterion::black_box(Euclidean::distance(&poly1, &poly2)); + criterion::black_box(Euclidean.distance(&poly1, &poly2)); }); }); @@ -79,7 +79,7 @@ fn criterion_benchmark(c: &mut criterion::Criterion) { ] .convex_hull(); bencher.iter(|| { - criterion::black_box(Euclidean::distance(&poly1, &poly2)); + criterion::black_box(Euclidean.distance(&poly1, &poly2)); }); }, ); diff --git a/geo/benches/geodesic_distance.rs b/geo/benches/geodesic_distance.rs index cee6d4f19..f1f966aa6 100644 --- a/geo/benches/geodesic_distance.rs +++ b/geo/benches/geodesic_distance.rs @@ -7,7 +7,7 @@ fn criterion_benchmark(c: &mut criterion::Criterion) { let b = geo::Point::new(16.372477, 48.208810); bencher.iter(|| { - criterion::black_box(criterion::black_box(Geodesic::distance(a, b))); + criterion::black_box(criterion::black_box(Geodesic.distance(a, b))); }); }); } diff --git a/geo/src/algorithm/centroid.rs b/geo/src/algorithm/centroid.rs index 0be576341..ec507d90b 100644 --- a/geo/src/algorithm/centroid.rs +++ b/geo/src/algorithm/centroid.rs @@ -465,11 +465,9 @@ impl CentroidOperation { fn add_line(&mut self, line: &Line) { match line.dimensions() { ZeroDimensional => self.add_coord(line.start), - OneDimensional => self.add_centroid( - OneDimensional, - line.centroid().0, - line.length::(), - ), + OneDimensional => { + self.add_centroid(OneDimensional, line.centroid().0, line.length(&Euclidean)) + } _ => unreachable!("Line must be zero or one dimensional"), } } diff --git a/geo/src/algorithm/closest_point.rs b/geo/src/algorithm/closest_point.rs index a9c441973..ffee30ea7 100644 --- a/geo/src/algorithm/closest_point.rs +++ b/geo/src/algorithm/closest_point.rs @@ -52,7 +52,7 @@ impl ClosestPoint for Point { #[allow(clippy::many_single_char_names)] impl ClosestPoint for Line { fn closest_point(&self, p: &Point) -> Closest { - let line_length = self.length::(); + let line_length = self.length(&Euclidean); if line_length == F::zero() { // if we've got a zero length line, technically the entire line // is the closest point... diff --git a/geo/src/algorithm/concave_hull.rs b/geo/src/algorithm/concave_hull.rs index 321c9eb4f..4e5c0c868 100644 --- a/geo/src/algorithm/concave_hull.rs +++ b/geo/src/algorithm/concave_hull.rs @@ -116,7 +116,7 @@ where T: GeoFloat + RTreeNum, { let h = max_dist + max_dist; - let w = line.length::() + h; + let w = line.length(&Euclidean) + h; let two = T::add(T::one(), T::one()); let search_dist = T::div(T::sqrt(T::powi(w, 2) + T::powi(h, 2)), two); let centroid = line.centroid(); @@ -134,8 +134,8 @@ where let closest_point = candidates.fold(Point::new(point.x, point.y), |acc_point, candidate| { let candidate_point = Point::new(candidate.x, candidate.y); - if Euclidean::distance(&line, &acc_point) - > Euclidean::distance(&line, &candidate_point) + if Euclidean.distance(&line, &acc_point) + > Euclidean.distance(&line, &candidate_point) { candidate_point } else { @@ -154,8 +154,8 @@ where let closest_edge_option = match peeked_edge { None => None, Some(&edge) => Some(edges_nearby_point.fold(*edge, |acc, candidate| { - if Euclidean::distance(&closest_point, &acc) - > Euclidean::distance(&closest_point, candidate) + if Euclidean.distance(&closest_point, &acc) + > Euclidean.distance(&closest_point, candidate) { *candidate } else { @@ -164,8 +164,8 @@ where })), }; let decision_distance = partial_min( - Euclidean::distance(&closest_point, &line.start_point()), - Euclidean::distance(&closest_point, &line.end_point()), + Euclidean.distance(&closest_point, &line.start_point()), + Euclidean.distance(&closest_point, &line.end_point()), ); if let Some(closest_edge) = closest_edge_option { let far_enough = edge_length / decision_distance > concavity; @@ -217,7 +217,7 @@ where line_tree.insert(line); } while let Some(line) = line_queue.pop_front() { - let edge_length = line.length::(); + let edge_length = line.length(&Euclidean); let dist = edge_length / concavity; let possible_closest_point = find_point_closest_to_line( &interior_points_tree, diff --git a/geo/src/algorithm/cross_track_distance.rs b/geo/src/algorithm/cross_track_distance.rs index 7088d4b6b..0aac1f899 100644 --- a/geo/src/algorithm/cross_track_distance.rs +++ b/geo/src/algorithm/cross_track_distance.rs @@ -43,9 +43,9 @@ where { fn cross_track_distance(&self, line_point_a: &Point, line_point_b: &Point) -> T { let mean_earth_radius = T::from(MEAN_EARTH_RADIUS).unwrap(); - let l_delta_13: T = Haversine::distance(*line_point_a, *self) / mean_earth_radius; - let theta_13: T = Haversine::bearing(*line_point_a, *self).to_radians(); - let theta_12: T = Haversine::bearing(*line_point_a, *line_point_b).to_radians(); + let l_delta_13: T = Haversine.distance(*line_point_a, *self) / mean_earth_radius; + let theta_13: T = Haversine.bearing(*line_point_a, *self).to_radians(); + let theta_12: T = Haversine.bearing(*line_point_a, *line_point_b).to_radians(); let l_delta_xt: T = (l_delta_13.sin() * (theta_12 - theta_13).sin()).asin(); mean_earth_radius * l_delta_xt.abs() } @@ -90,13 +90,13 @@ mod test { assert_relative_eq!( p.cross_track_distance(&line_point_a, &line_point_b), - Haversine::distance(p, Point::new(1., 0.)), + Haversine.distance(p, Point::new(1., 0.)), epsilon = 1.0e-6 ); assert_relative_eq!( p.cross_track_distance(&line_point_b, &line_point_a), - Haversine::distance(p, Point::new(1., 0.)), + Haversine.distance(p, Point::new(1., 0.)), epsilon = 1.0e-6 ); } diff --git a/geo/src/algorithm/densify_haversine.rs b/geo/src/algorithm/densify_haversine.rs index e9ef38039..71b232672 100644 --- a/geo/src/algorithm/densify_haversine.rs +++ b/geo/src/algorithm/densify_haversine.rs @@ -10,7 +10,7 @@ use crate::{ #[deprecated( since = "0.29.0", - note = "Please use the `line.densify::()` via the `Densify` trait instead." + note = "Please use the `line.densify(&Haversine)` via the `Densify` trait instead." )] /// Returns a new spherical geometry containing both existing and new interpolated coordinates with /// a maximum distance of `max_distance` between them. @@ -50,7 +50,7 @@ where type Output = MultiPolygon; fn densify_haversine(&self, max_distance: T) -> Self::Output { - self.densify::(max_distance) + self.densify(&Haversine, max_distance) } } @@ -64,7 +64,7 @@ where type Output = Polygon; fn densify_haversine(&self, max_distance: T) -> Self::Output { - self.densify::(max_distance) + self.densify(&Haversine, max_distance) } } @@ -78,7 +78,7 @@ where type Output = MultiLineString; fn densify_haversine(&self, max_distance: T) -> Self::Output { - self.densify::(max_distance) + self.densify(&Haversine, max_distance) } } @@ -92,7 +92,7 @@ where type Output = LineString; fn densify_haversine(&self, max_distance: T) -> Self::Output { - self.densify::(max_distance) + self.densify(&Haversine, max_distance) } } @@ -106,7 +106,7 @@ where type Output = LineString; fn densify_haversine(&self, max_distance: T) -> Self::Output { - self.densify::(max_distance) + self.densify(&Haversine, max_distance) } } @@ -120,7 +120,7 @@ where type Output = Polygon; fn densify_haversine(&self, max_distance: T) -> Self::Output { - self.densify::(max_distance) + self.densify(&Haversine, max_distance) } } @@ -134,7 +134,7 @@ where type Output = Polygon; fn densify_haversine(&self, max_distance: T) -> Self::Output { - self.densify::(max_distance) + self.densify(&Haversine, max_distance) } } diff --git a/geo/src/algorithm/euclidean_distance.rs b/geo/src/algorithm/euclidean_distance.rs index d1b6dd1b1..d2b5af782 100644 --- a/geo/src/algorithm/euclidean_distance.rs +++ b/geo/src/algorithm/euclidean_distance.rs @@ -11,7 +11,7 @@ use rstar::RTreeNum; #[deprecated( since = "0.29.0", - note = "Please use the `Euclidean::distance` method from the `Distance` trait instead" + note = "Please use the `Euclidean.distance` method from the `Distance` trait instead" )] /// Returns the distance between two geometries. pub trait EuclideanDistance { @@ -105,7 +105,7 @@ where { /// Minimum distance between two `Coord`s fn euclidean_distance(&self, c: &Coord) -> T { - Euclidean::distance(Point(*self), Point(*c)) + Euclidean.distance(Point(*self), Point(*c)) } } @@ -116,7 +116,7 @@ where { /// Minimum distance from a `Coord` to a `Line` fn euclidean_distance(&self, line: &Line) -> T { - Euclidean::distance(&Point(*self), line) + Euclidean.distance(&Point(*self), line) } } @@ -131,7 +131,7 @@ where { /// Minimum distance between two Points fn euclidean_distance(&self, p: &Point) -> T { - Euclidean::distance(*self, *p) + Euclidean.distance(*self, *p) } } @@ -142,7 +142,7 @@ where { /// Minimum distance from a Line to a Point fn euclidean_distance(&self, line: &Line) -> T { - Euclidean::distance(self, line) + Euclidean.distance(self, line) } } @@ -153,7 +153,7 @@ where { /// Minimum distance from a Point to a LineString fn euclidean_distance(&self, line_string: &LineString) -> T { - Euclidean::distance(self, line_string) + Euclidean.distance(self, line_string) } } @@ -164,7 +164,7 @@ where { /// Minimum distance from a Point to a Polygon fn euclidean_distance(&self, polygon: &Polygon) -> T { - Euclidean::distance(self, polygon) + Euclidean.distance(self, polygon) } } @@ -179,7 +179,7 @@ where { /// Minimum distance from a `Line` to a `Coord` fn euclidean_distance(&self, coord: &Coord) -> T { - Euclidean::distance(self, *coord) + Euclidean.distance(self, *coord) } } @@ -190,7 +190,7 @@ where { /// Minimum distance from a Line to a Point fn euclidean_distance(&self, point: &Point) -> T { - Euclidean::distance(self, point) + Euclidean.distance(self, point) } } @@ -201,7 +201,7 @@ where T: GeoFloat + FloatConst + Signed + RTreeNum, { fn euclidean_distance(&self, other: &Line) -> T { - Euclidean::distance(self, other) + Euclidean.distance(self, other) } } @@ -212,7 +212,7 @@ where T: GeoFloat + FloatConst + Signed + RTreeNum, { fn euclidean_distance(&self, other: &LineString) -> T { - Euclidean::distance(self, other) + Euclidean.distance(self, other) } } @@ -223,7 +223,7 @@ where T: GeoFloat + Signed + RTreeNum + FloatConst, { fn euclidean_distance(&self, other: &Polygon) -> T { - Euclidean::distance(self, other) + Euclidean.distance(self, other) } } @@ -238,7 +238,7 @@ where { /// Minimum distance from a LineString to a Point fn euclidean_distance(&self, point: &Point) -> T { - Euclidean::distance(self, point) + Euclidean.distance(self, point) } } @@ -249,7 +249,7 @@ where T: GeoFloat + FloatConst + Signed + RTreeNum, { fn euclidean_distance(&self, other: &Line) -> T { - Euclidean::distance(self, other) + Euclidean.distance(self, other) } } @@ -260,7 +260,7 @@ where T: GeoFloat + Signed + RTreeNum, { fn euclidean_distance(&self, other: &LineString) -> T { - Euclidean::distance(self, other) + Euclidean.distance(self, other) } } @@ -271,7 +271,7 @@ where T: GeoFloat + FloatConst + Signed + RTreeNum, { fn euclidean_distance(&self, other: &Polygon) -> T { - Euclidean::distance(self, other) + Euclidean.distance(self, other) } } @@ -286,7 +286,7 @@ where { /// Minimum distance from a Polygon to a Point fn euclidean_distance(&self, point: &Point) -> T { - Euclidean::distance(self, point) + Euclidean.distance(self, point) } } @@ -297,7 +297,7 @@ where T: GeoFloat + FloatConst + Signed + RTreeNum, { fn euclidean_distance(&self, other: &Line) -> T { - Euclidean::distance(self, other) + Euclidean.distance(self, other) } } @@ -308,7 +308,7 @@ where T: GeoFloat + FloatConst + Signed + RTreeNum, { fn euclidean_distance(&self, other: &LineString) -> T { - Euclidean::distance(self, other) + Euclidean.distance(self, other) } } @@ -319,7 +319,7 @@ where T: GeoFloat + FloatConst + RTreeNum, { fn euclidean_distance(&self, poly2: &Polygon) -> T { - Euclidean::distance(self, poly2) + Euclidean.distance(self, poly2) } } @@ -337,7 +337,7 @@ macro_rules! impl_euclidean_distance_for_polygonlike_geometry { T: GeoFloat + Signed + RTreeNum + FloatConst, { fn euclidean_distance(&self, other: &$target) -> T { - Euclidean::distance(self, other) + Euclidean.distance(self, other) } } )* @@ -357,7 +357,7 @@ macro_rules! impl_euclidean_distance_to_polygonlike_geometry { T: GeoFloat + Signed + RTreeNum + FloatConst, { fn euclidean_distance(&self, other: &$target) -> T { - Euclidean::distance(self, other) + Euclidean.distance(self, other) } } )* @@ -387,7 +387,7 @@ macro_rules! impl_euclidean_distance_for_iter_geometry { T: GeoFloat + FloatConst + RTreeNum, { fn euclidean_distance(&self, target: &$target) -> T { - Euclidean::distance(self, target) + Euclidean.distance(self, target) } } )* @@ -410,7 +410,7 @@ macro_rules! impl_euclidean_distance_from_iter_geometry { T: GeoFloat + FloatConst + RTreeNum { fn euclidean_distance(&self, target: &$target) -> T { - Euclidean::distance(self, target) + Euclidean.distance(self, target) } } )* @@ -438,7 +438,7 @@ macro_rules! impl_euclidean_distance_to_geometry_for_specific { T: GeoFloat + FloatConst + RTreeNum, { fn euclidean_distance(&self, geom: &Geometry) -> T { - Euclidean::distance(self, geom) + Euclidean.distance(self, geom) } } )* @@ -486,7 +486,7 @@ where #[deprecated( since = "0.29.0", - note = "Please use the `Euclidean::distance` method from the `Distance` trait instead" + note = "Please use the `Euclidean.distance` method from the `Distance` trait instead" )] /// Uses an R* tree and nearest-neighbour lookups to calculate minimum distances // This is somewhat slow and memory-inefficient, but certainly better than quadratic time diff --git a/geo/src/algorithm/euclidean_length.rs b/geo/src/algorithm/euclidean_length.rs index e79f7f674..aa21a847b 100644 --- a/geo/src/algorithm/euclidean_length.rs +++ b/geo/src/algorithm/euclidean_length.rs @@ -5,7 +5,7 @@ use crate::{CoordFloat, Euclidean, Length, Line, LineString, MultiLineString}; /// Calculation of the length #[deprecated( since = "0.29.0", - note = "Please use the `line.length::()` via the `Length` trait instead." + note = "Please use the `line.length(&Euclidean)` via the `Length` trait instead." )] pub trait EuclideanLength { /// Calculation of the length of a Line @@ -35,7 +35,7 @@ where T: CoordFloat, { fn euclidean_length(&self) -> T { - self.length::() + self.length(&Euclidean) } } @@ -45,7 +45,7 @@ where T: CoordFloat + Sum, { fn euclidean_length(&self) -> T { - self.length::() + self.length(&Euclidean) } } @@ -55,7 +55,7 @@ where T: CoordFloat + Sum, { fn euclidean_length(&self) -> T { - self.length::() + self.length(&Euclidean) } } diff --git a/geo/src/algorithm/frechet_distance.rs b/geo/src/algorithm/frechet_distance.rs index 4b1e89d9f..d6be00777 100644 --- a/geo/src/algorithm/frechet_distance.rs +++ b/geo/src/algorithm/frechet_distance.rs @@ -78,7 +78,7 @@ where for (i, &a) in self.ls_a.coords().enumerate() { for (j, &b) in self.ls_b.coords().enumerate() { - let dist = Euclidean::distance(a, b); + let dist = Euclidean.distance(a, b); self.cache[i * columns_count + j] = match (i, j) { (0, 0) => dist, @@ -105,7 +105,7 @@ mod test { let ls_a = LineString::from(vec![(1., 1.)]); let ls_b = LineString::from(vec![(0., 2.)]); assert_relative_eq!( - Euclidean::distance(ls_a.0[0], ls_b.0[0]), + Euclidean.distance(ls_a.0[0], ls_b.0[0]), ls_a.frechet_distance(&ls_b) ); } diff --git a/geo/src/algorithm/geodesic_area.rs b/geo/src/algorithm/geodesic_area.rs index 20419e762..ed85f4d28 100644 --- a/geo/src/algorithm/geodesic_area.rs +++ b/geo/src/algorithm/geodesic_area.rs @@ -380,7 +380,7 @@ mod test { // Confirm that the exterior ring geodesic_length is the same as the perimeter assert_relative_eq!( - polygon.exterior().length::(), + polygon.exterior().length(&Geodesic), polygon.geodesic_perimeter() ); } @@ -410,7 +410,7 @@ mod test { // Confirm that the exterior ring geodesic_length is the same as the perimeter assert_relative_eq!( - polygon.exterior().length::(), + polygon.exterior().length(&Geodesic), polygon.geodesic_perimeter() ); } @@ -440,7 +440,7 @@ mod test { // Confirm that the exterior ring geodesic_length is the same as the perimeter assert_relative_eq!( - polygon.exterior().length::(), + polygon.exterior().length(&Geodesic), polygon.geodesic_perimeter() ); } diff --git a/geo/src/algorithm/geodesic_bearing.rs b/geo/src/algorithm/geodesic_bearing.rs index a1666506b..ec72d1a72 100644 --- a/geo/src/algorithm/geodesic_bearing.rs +++ b/geo/src/algorithm/geodesic_bearing.rs @@ -10,7 +10,7 @@ use geographiclib_rs::{Geodesic, InverseGeodesic}; pub trait GeodesicBearing { #[deprecated( since = "0.29.0", - note = "Please use the `Geodesic::bearing` method from the `Bearing` trait instead" + note = "Please use the `Geodesic.bearing` method from the `Bearing` trait instead" )] /// Returns the bearing to another Point in degrees, where North is 0° and East is 90°. /// diff --git a/geo/src/algorithm/geodesic_destination.rs b/geo/src/algorithm/geodesic_destination.rs index 25261a9bc..b6cc913a6 100644 --- a/geo/src/algorithm/geodesic_destination.rs +++ b/geo/src/algorithm/geodesic_destination.rs @@ -4,7 +4,7 @@ use geo_types::CoordNum; #[deprecated( since = "0.29.0", - note = "Please use the `Geodesic::destination` method from the `Destination` trait instead" + note = "Please use the `Geodesic.destination` method from the `Destination` trait instead" )] /// Returns a new Point using the distance to the existing Point and a bearing for the direction on a geodesic. /// @@ -43,7 +43,7 @@ pub trait GeodesicDestination { #[allow(deprecated)] impl GeodesicDestination for Point { fn geodesic_destination(&self, bearing: f64, distance: f64) -> Point { - Geodesic::destination(*self, bearing, distance) + Geodesic.destination(*self, bearing, distance) } } diff --git a/geo/src/algorithm/geodesic_distance.rs b/geo/src/algorithm/geodesic_distance.rs index 87d1755cc..1b6aa06d6 100644 --- a/geo/src/algorithm/geodesic_distance.rs +++ b/geo/src/algorithm/geodesic_distance.rs @@ -2,7 +2,7 @@ use crate::{Distance, Geodesic, Point}; #[deprecated( since = "0.29.0", - note = "Please use the `Geodesic::distance` method from the `Distance` trait instead" + note = "Please use the `Geodesic.distance` method from the `Distance` trait instead" )] /// Determine the distance between two geometries on an ellipsoidal model of the earth. /// @@ -46,6 +46,6 @@ pub trait GeodesicDistance { #[allow(deprecated)] impl GeodesicDistance for Point { fn geodesic_distance(&self, rhs: &Point) -> f64 { - Geodesic::distance(*self, *rhs) + Geodesic.distance(*self, *rhs) } } diff --git a/geo/src/algorithm/geodesic_intermediate.rs b/geo/src/algorithm/geodesic_intermediate.rs index 109cdb9e2..475d4063b 100644 --- a/geo/src/algorithm/geodesic_intermediate.rs +++ b/geo/src/algorithm/geodesic_intermediate.rs @@ -8,7 +8,7 @@ use crate::{CoordFloat, Geodesic, InterpolatePoint, Point}; pub trait GeodesicIntermediate { #[deprecated( since = "0.29.0", - note = "Please use `Geodesic::point_at_ratio_between` from the `InterpolatePoint` trait instead" + note = "Please use `Geodesic.point_at_ratio_between` from the `InterpolatePoint` trait instead" )] /// Returns a new Point along a route between two existing points on an ellipsoidal model of the earth /// @@ -39,7 +39,7 @@ pub trait GeodesicIntermediate { #[deprecated( since = "0.29.0", - note = "Please use `Geodesic::points_along_line` from the `InterpolatePoint` trait instead" + note = "Please use `Geodesic.points_along_line` from the `InterpolatePoint` trait instead" )] fn geodesic_intermediate_fill( &self, @@ -52,7 +52,7 @@ pub trait GeodesicIntermediate { #[allow(deprecated)] impl GeodesicIntermediate for Point { fn geodesic_intermediate(&self, other: &Point, f: f64) -> Point { - Geodesic::point_at_ratio_between(*self, *other, f) + Geodesic.point_at_ratio_between(*self, *other, f) } fn geodesic_intermediate_fill( @@ -61,7 +61,9 @@ impl GeodesicIntermediate for Point { max_dist: f64, include_ends: bool, ) -> Vec { - Geodesic::points_along_line(*self, *other, max_dist, include_ends).collect() + Geodesic + .points_along_line(*self, *other, max_dist, include_ends) + .collect() } } diff --git a/geo/src/algorithm/geodesic_length.rs b/geo/src/algorithm/geodesic_length.rs index d0efc1760..3d54fd0f8 100644 --- a/geo/src/algorithm/geodesic_length.rs +++ b/geo/src/algorithm/geodesic_length.rs @@ -2,7 +2,7 @@ use crate::{Geodesic, Length, Line, LineString, MultiLineString}; #[deprecated( since = "0.29.0", - note = "Please use the `line.length::()` via the `Length` trait instead." + note = "Please use the `line.length(&Geodesic)` via the `Length` trait instead." )] /// Determine the length of a geometry on an ellipsoidal model of the earth. /// @@ -52,20 +52,20 @@ pub trait GeodesicLength { impl GeodesicLength for Line { /// The units of the returned value is meters. fn geodesic_length(&self) -> f64 { - self.length::() + self.length(&Geodesic) } } #[allow(deprecated)] impl GeodesicLength for LineString { fn geodesic_length(&self) -> f64 { - self.length::() + self.length(&Geodesic) } } #[allow(deprecated)] impl GeodesicLength for MultiLineString { fn geodesic_length(&self) -> f64 { - self.length::() + self.length(&Geodesic) } } diff --git a/geo/src/algorithm/hausdorff_distance.rs b/geo/src/algorithm/hausdorff_distance.rs index ecf51f3b6..d3476cab0 100644 --- a/geo/src/algorithm/hausdorff_distance.rs +++ b/geo/src/algorithm/hausdorff_distance.rs @@ -34,7 +34,7 @@ where .coords_iter() .map(|c| { rhs.coords_iter() - .map(|c2| Euclidean::distance(c, c2)) + .map(|c2| Euclidean.distance(c, c2)) .fold(::max_value(), |accum, val| accum.min(val)) }) .fold(::min_value(), |accum, val| accum.max(val)); @@ -44,7 +44,7 @@ where .coords_iter() .map(|c| { self.coords_iter() - .map(|c2| Euclidean::distance(c, c2)) + .map(|c2| Euclidean.distance(c, c2)) .fold(::max_value(), |accum, val| accum.min(val)) }) .fold(::min_value(), |accum, val| accum.max(val)); diff --git a/geo/src/algorithm/haversine_bearing.rs b/geo/src/algorithm/haversine_bearing.rs index 031665270..3549553a3 100644 --- a/geo/src/algorithm/haversine_bearing.rs +++ b/geo/src/algorithm/haversine_bearing.rs @@ -2,7 +2,7 @@ use crate::{CoordFloat, Point}; #[deprecated( since = "0.29.0", - note = "Please use the `Haversine::bearing` method from the `Bearing` trait instead" + note = "Please use the `Haversine.bearing` method from the `Bearing` trait instead" )] /// Returns the bearing to another Point in degrees. /// diff --git a/geo/src/algorithm/haversine_closest_point.rs b/geo/src/algorithm/haversine_closest_point.rs index 53a582438..60b7fa31d 100644 --- a/geo/src/algorithm/haversine_closest_point.rs +++ b/geo/src/algorithm/haversine_closest_point.rs @@ -92,7 +92,7 @@ where } // This can probably be done cheaper - let d3 = Haversine::distance(p2, p1); + let d3 = Haversine.distance(p2, p1); if d3 <= T::epsilon() { // I think here it should be return Closest::SinglePoint(p1) // If the line segment is degenerated to a point, that point is still the closest @@ -102,18 +102,18 @@ where } let pi = T::from(std::f64::consts::PI).unwrap(); - let crs_ad = Haversine::bearing(p1, *from).to_radians(); - let crs_ab = Haversine::bearing(p1, p2).to_radians(); + let crs_ad = Haversine.bearing(p1, *from).to_radians(); + let crs_ab = Haversine.bearing(p1, p2).to_radians(); let crs_ba = if crs_ab > T::zero() { crs_ab - pi } else { crs_ab + pi }; - let crs_bd = Haversine::bearing(p2, *from).to_radians(); + let crs_bd = Haversine.bearing(p2, *from).to_radians(); let d_crs1 = crs_ad - crs_ab; let d_crs2 = crs_bd - crs_ba; - let d1 = Haversine::distance(p1, *from); + let d1 = Haversine.distance(p1, *from); // d1, d2, d3 are in principle not needed, only the sign matters let projection1 = d_crs1.cos(); @@ -127,13 +127,13 @@ where if xtd < T::epsilon() { return Closest::Intersection(*from); } else { - return Closest::SinglePoint(Haversine::destination(p1, crs_ab.to_degrees(), atd)); + return Closest::SinglePoint(Haversine.destination(p1, crs_ab.to_degrees(), atd)); } } // Projected falls outside the GC Arc // Return shortest distance pt, project either on point sp1 or sp2 - let d2 = Haversine::distance(p2, *from); + let d2 = Haversine.distance(p2, *from); if d1 < d2 { return Closest::SinglePoint(p1); } @@ -166,7 +166,7 @@ where return intersect; } Closest::SinglePoint(pt) => { - let dist = Haversine::distance(pt, *from); + let dist = Haversine.distance(pt, *from); if dist < min_distance { min_distance = dist; rv = Closest::SinglePoint(pt); @@ -198,7 +198,7 @@ where return (intersect, T::zero()); } Closest::SinglePoint(pt) => { - let dist = Haversine::distance(pt, *from); + let dist = Haversine.distance(pt, *from); if dist < min_distance { min_distance = dist; rv = Closest::SinglePoint(pt); @@ -301,7 +301,7 @@ where // This mean on top of the line. Closest::Intersection(pt) => return Closest::Intersection(pt), Closest::SinglePoint(pt) => { - let dist = Haversine::distance(pt, *from); + let dist = Haversine.distance(pt, *from); if dist < min_distance { min_distance = dist; rv = Closest::SinglePoint(pt); diff --git a/geo/src/algorithm/haversine_destination.rs b/geo/src/algorithm/haversine_destination.rs index b3f9a088c..f915569d0 100644 --- a/geo/src/algorithm/haversine_destination.rs +++ b/geo/src/algorithm/haversine_destination.rs @@ -3,7 +3,7 @@ use num_traits::FromPrimitive; #[deprecated( since = "0.29.0", - note = "Please use the `Haversine::destination` method from the `Destination` trait instead" + note = "Please use the `Haversine.destination` method from the `Destination` trait instead" )] /// Returns a new Point using the distance to the existing Point and a bearing for the direction /// @@ -39,7 +39,7 @@ where T: CoordFloat + FromPrimitive, { fn haversine_destination(&self, bearing: T, distance: T) -> Point { - Haversine::destination(*self, bearing, distance) + Haversine.destination(*self, bearing, distance) } } diff --git a/geo/src/algorithm/haversine_distance.rs b/geo/src/algorithm/haversine_distance.rs index e276351a9..df1cf7a40 100644 --- a/geo/src/algorithm/haversine_distance.rs +++ b/geo/src/algorithm/haversine_distance.rs @@ -3,7 +3,7 @@ use num_traits::FromPrimitive; #[deprecated( since = "0.29.0", - note = "Please use the `Haversine::distance` method from the `Distance` trait instead" + note = "Please use the `Haversine.distance` method from the `Distance` trait instead" )] /// Determine the distance between two geometries using the [haversine formula]. /// @@ -55,7 +55,7 @@ where T: CoordFloat + FromPrimitive, { fn haversine_distance(&self, rhs: &Point) -> T { - Haversine::distance(*self, *rhs) + Haversine.distance(*self, *rhs) } } diff --git a/geo/src/algorithm/haversine_intermediate.rs b/geo/src/algorithm/haversine_intermediate.rs index 55cf861a7..fce21e8c2 100644 --- a/geo/src/algorithm/haversine_intermediate.rs +++ b/geo/src/algorithm/haversine_intermediate.rs @@ -9,7 +9,7 @@ use num_traits::FromPrimitive; pub trait HaversineIntermediate { #[deprecated( since = "0.29.0", - note = "Please use `Haversine::point_at_ratio_between` from the `InterpolatePoint` trait instead" + note = "Please use `Haversine.point_at_ratio_between` from the `InterpolatePoint` trait instead" )] /// Returns a new `Point` along a great circle route between `self` and `other`. /// @@ -40,7 +40,7 @@ pub trait HaversineIntermediate { #[deprecated( since = "0.29.0", - note = "Please use `Haversine::points_along_line` from the `InterpolatePoint` trait instead" + note = "Please use `Haversine.points_along_line` from the `InterpolatePoint` trait instead" )] /// Interpolates `Point`s along a great circle route between self and `other`. /// @@ -62,7 +62,7 @@ where T: CoordFloat + FromPrimitive, { fn haversine_intermediate(&self, other: &Point, ratio: T) -> Point { - Haversine::point_at_ratio_between(*self, *other, ratio) + Haversine.point_at_ratio_between(*self, *other, ratio) } fn haversine_intermediate_fill( @@ -71,7 +71,9 @@ where max_dist: T, include_ends: bool, ) -> Vec> { - Haversine::points_along_line(*self, *other, max_dist, include_ends).collect() + Haversine + .points_along_line(*self, *other, max_dist, include_ends) + .collect() } } diff --git a/geo/src/algorithm/haversine_length.rs b/geo/src/algorithm/haversine_length.rs index a2730733b..1455bfe2a 100644 --- a/geo/src/algorithm/haversine_length.rs +++ b/geo/src/algorithm/haversine_length.rs @@ -5,7 +5,7 @@ use crate::{Haversine, Length}; #[deprecated( since = "0.29.0", - note = "Please use the `line.length::()` via the `Length` trait instead." + note = "Please use the `line.length(&Haversine)` via the `Length` trait instead." )] /// Determine the length of a geometry using the [haversine formula]. /// @@ -51,7 +51,7 @@ where T: CoordFloat + FromPrimitive, { fn haversine_length(&self) -> T { - self.length::() + self.length(&Haversine) } } @@ -61,7 +61,7 @@ where T: CoordFloat + FromPrimitive, { fn haversine_length(&self) -> T { - self.length::() + self.length(&Haversine) } } @@ -71,6 +71,6 @@ where T: CoordFloat + FromPrimitive, { fn haversine_length(&self) -> T { - self.length::() + self.length(&Haversine) } } diff --git a/geo/src/algorithm/interior_point.rs b/geo/src/algorithm/interior_point.rs index 3cfec74fe..5ed67d841 100644 --- a/geo/src/algorithm/interior_point.rs +++ b/geo/src/algorithm/interior_point.rs @@ -112,7 +112,7 @@ where .iter() .map(|coord| { let pt = Point::from(*coord); - (pt, Euclidean::distance(pt, centroid)) + (pt, Euclidean.distance(pt, centroid)) }) .min_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(Ordering::Less)) .map(|(pt, _distance)| pt) @@ -135,7 +135,7 @@ where .filter_map(|linestring| { linestring .interior_point() - .map(|pt| (pt, Euclidean::distance(pt, centroid))) + .map(|pt| (pt, Euclidean.distance(pt, centroid))) }) .min_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(Ordering::Less)) .map(|(pt, _distance)| pt) @@ -313,7 +313,7 @@ where fn interior_point(&self) -> Self::Output { if let Some(centroid) = self.centroid() { self.iter() - .map(|pt| (pt, Euclidean::distance(pt, ¢roid))) + .map(|pt| (pt, Euclidean.distance(pt, ¢roid))) .min_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(Ordering::Less)) .map(|(pt, _distance)| *pt) } else { @@ -347,10 +347,7 @@ where ( pt, // maximize dimensions, minimize distance - ( - Reverse(geom.dimensions()), - Euclidean::distance(pt, centroid), - ), + (Reverse(geom.dimensions()), Euclidean.distance(pt, centroid)), ) }) }) diff --git a/geo/src/algorithm/line_measures/bearing.rs b/geo/src/algorithm/line_measures/bearing.rs index 67a1694e7..5e1d35416 100644 --- a/geo/src/algorithm/line_measures/bearing.rs +++ b/geo/src/algorithm/line_measures/bearing.rs @@ -9,5 +9,5 @@ pub trait Bearing { /// # Units /// - `origin`, `destination`: Point where the units of x/y depend on the [trait implementation](#implementors). /// - returns: degrees, where: North: 0°, East: 90°, South: 180°, West: 270° - fn bearing(origin: Point, destination: Point) -> F; + fn bearing(&self, origin: Point, destination: Point) -> F; } diff --git a/geo/src/algorithm/line_measures/densify.rs b/geo/src/algorithm/line_measures/densify.rs index b0a3eb08b..3e63cab1e 100644 --- a/geo/src/algorithm/line_measures/densify.rs +++ b/geo/src/algorithm/line_measures/densify.rs @@ -22,7 +22,7 @@ use num_traits::FromPrimitive; /// // For Euclidean calculations, the unit of distance is the same as the units /// // of your coordinates. /// let max_dist = 2.0; -/// let densified = line_string.densify::(max_dist); +/// let densified = line_string.densify(&Euclidean, max_dist); /// let expected_output = wkt!(LINESTRING( /// 0.0 0.0, /// 0.0 2.0, @@ -43,7 +43,7 @@ use num_traits::FromPrimitive; /// /// // For Haversine, the unit of distance is in meters /// let max_dist = 200_000.0; -/// let densified = line_string.densify::(max_dist); +/// let densified = line_string.densify(&Haversine, max_dist); /// // Haversine interprets coordinate points as lng/lat /// let expected_output = wkt!(LINESTRING( /// 0.0 0.0, @@ -56,14 +56,21 @@ use num_traits::FromPrimitive; /// assert_relative_eq!(densified, expected_output, epsilon = 1e-14); /// ``` /// [metric space]: crate::line_measures::metric_spaces +// TODO: Change this to be a method implemented on the trait? +// And accept a Densifiable (new trait) as argument? pub trait Densify { type Output; - fn densify(&self, max_segment_length: F) -> Self::Output + fn densify( + &self, + metric_space: &MetricSpace, + max_segment_length: F, + ) -> Self::Output where MetricSpace: Distance, Point> + InterpolatePoint; } pub(crate) fn densify_between( + metric_space: &MetricSpace, line_start: Point, line_end: Point, container: &mut Vec>, @@ -73,7 +80,7 @@ pub(crate) fn densify_between( MetricSpace: Distance, Point> + InterpolatePoint, { assert!(max_segment_length > F::zero()); - let num_segments = (MetricSpace::distance(line_start, line_end) / max_segment_length) + let num_segments = (metric_space.distance(line_start, line_end) / max_segment_length) .ceil() .to_u64() .expect("unreasonable number of segments"); @@ -88,7 +95,7 @@ pub(crate) fn densify_between( // If we impl point_at_distance_between, we could compute it once and use it here. // At that point, I think this function could be a good candidate to be *the single* basis // for a unified generic of points_along_line for all metric spaces. - let interpolated_point = MetricSpace::point_at_ratio_between(line_start, line_end, ratio); + let interpolated_point = metric_space.point_at_ratio_between(line_start, line_end, ratio); container.push(interpolated_point); } } @@ -96,12 +103,17 @@ pub(crate) fn densify_between( impl Densify for Line { type Output = LineString; - fn densify(&self, max_segment_length: F) -> Self::Output + fn densify( + &self, + metric_space: &MetricSpace, + max_segment_length: F, + ) -> Self::Output where MetricSpace: Distance, Point> + InterpolatePoint, { let mut points = vec![self.start_point()]; - densify_between::( + densify_between( + metric_space, self.start_point(), self.end_point(), &mut points, @@ -115,7 +127,11 @@ impl Densify for Line { impl Densify for LineString { type Output = Self; - fn densify(&self, max_segment_length: F) -> LineString + fn densify( + &self, + metric_space: &MetricSpace, + max_segment_length: F, + ) -> LineString where MetricSpace: Distance, Point> + InterpolatePoint, { @@ -126,7 +142,8 @@ impl Densify for LineString { let mut points = vec![]; self.lines().for_each(|line| { points.push(line.start_point()); - densify_between::( + densify_between( + metric_space, line.start_point(), line.end_point(), &mut points, @@ -148,13 +165,17 @@ impl Densify for LineString { impl Densify for MultiLineString { type Output = Self; - fn densify(&self, max_segment_length: F) -> Self::Output + fn densify( + &self, + metric_space: &MetricSpace, + max_segment_length: F, + ) -> Self::Output where MetricSpace: Distance, Point> + InterpolatePoint, { MultiLineString::new( self.iter() - .map(|line_string| line_string.densify::(max_segment_length)) + .map(|line_string| line_string.densify(metric_space, max_segment_length)) .collect(), ) } @@ -163,15 +184,19 @@ impl Densify for MultiLineString { impl Densify for Polygon { type Output = Self; - fn densify(&self, max_segment_length: F) -> Self::Output + fn densify( + &self, + metric_space: &MetricSpace, + max_segment_length: F, + ) -> Self::Output where MetricSpace: Distance, Point> + InterpolatePoint, { Polygon::new( - self.exterior().densify::(max_segment_length), + self.exterior().densify(metric_space, max_segment_length), self.interiors() .iter() - .map(|interior| interior.densify::(max_segment_length)) + .map(|interior| interior.densify(metric_space, max_segment_length)) .collect(), ) } @@ -180,13 +205,17 @@ impl Densify for Polygon { impl Densify for MultiPolygon { type Output = Self; - fn densify(&self, max_segment_length: F) -> Self::Output + fn densify( + &self, + metric_space: &MetricSpace, + max_segment_length: F, + ) -> Self::Output where MetricSpace: Distance, Point> + InterpolatePoint, { MultiPolygon::new( self.iter() - .map(|polygon| polygon.densify::(max_segment_length)) + .map(|polygon| polygon.densify(metric_space, max_segment_length)) .collect(), ) } @@ -195,22 +224,30 @@ impl Densify for MultiPolygon { impl Densify for Rect { type Output = Polygon; - fn densify(&self, max_segment_length: F) -> Self::Output + fn densify( + &self, + metric_space: &MetricSpace, + max_segment_length: F, + ) -> Self::Output where MetricSpace: Distance, Point> + InterpolatePoint, { - self.to_polygon().densify::(max_segment_length) + self.to_polygon().densify(metric_space, max_segment_length) } } impl Densify for Triangle { type Output = Polygon; - fn densify(&self, max_segment_length: F) -> Self::Output + fn densify( + &self, + metric_space: &MetricSpace, + max_segment_length: F, + ) -> Self::Output where MetricSpace: Distance, Point> + InterpolatePoint, { - self.to_polygon().densify::(max_segment_length) + self.to_polygon().densify(metric_space, max_segment_length) } } @@ -227,13 +264,13 @@ mod tests { coord!(x: 2.3522, y: 48.8566), ); - let densified_line = line.densify::(100_000.0); // max segment length 100km + let densified_line = line.densify(&Geodesic, 100_000.0); // max segment length 100km assert!(densified_line.coords_count() > 2); - let densified_rhumb = line.densify::(100_000.0); + let densified_rhumb = line.densify(&Rhumb, 100_000.0); assert!(densified_rhumb.coords_count() > 2); - let densified_haversine = line.densify::(100_000.0); + let densified_haversine = line.densify(&Haversine, 100_000.0); assert!(densified_haversine.coords_count() > 2); } @@ -245,13 +282,13 @@ mod tests { coord!(x: -47.9292, y: -15.7801), // Brasília, Brazil ]); - let densified_ls = line_string.densify::(500_000.0); // 500 km max segment length + let densified_ls = line_string.densify(&Geodesic, 500_000.0); // 500 km max segment length assert!(densified_ls.coords_count() > line_string.coords_count()); - let densified_rhumb_ls = line_string.densify::(500_000.0); + let densified_rhumb_ls = line_string.densify(&Rhumb, 500_000.0); assert!(densified_rhumb_ls.coords_count() > line_string.coords_count()); - let densified_haversine_ls = line_string.densify::(500_000.0); + let densified_haversine_ls = line_string.densify(&Haversine, 500_000.0); assert!(densified_haversine_ls.coords_count() > line_string.coords_count()); } @@ -263,7 +300,7 @@ mod tests { (x: -47.9292, y: -15.7801), // Brasília ]; - let densified_polygon = polygon.densify::(500_000.0); // 500 km max segment length + let densified_polygon = polygon.densify(&Geodesic, 500_000.0); // 500 km max segment length assert!(densified_polygon.exterior().coords_count() > polygon.exterior().coords_count()); } @@ -284,7 +321,7 @@ mod tests { )); let max_dist = 2.0; - let densified = polygon.densify::(max_dist); + let densified = polygon.densify(&Euclidean, max_dist); assert_eq!(densified, expected); } @@ -292,7 +329,7 @@ mod tests { fn test_empty_linestring_densify() { let linestring = LineString::::new(vec![]); let max_dist = 2.0; - let densified = linestring.densify::(max_dist); + let densified = linestring.densify(&Euclidean, max_dist); assert!(densified.0.is_empty()); } @@ -314,7 +351,7 @@ mod tests { 1.0 8.0 )); let max_dist = 2.0; - let densified = linestring.densify::(max_dist); + let densified = linestring.densify(&Euclidean, max_dist); assert_eq!(densified, expected); } @@ -323,7 +360,7 @@ mod tests { let line: Line = Line::new(coord! {x: 0.0, y: 6.0}, coord! {x: 1.0, y: 8.0}); let correct: LineString = vec![[0.0, 6.0], [0.5, 7.0], [1.0, 8.0]].into(); let max_dist = 2.0; - let densified = line.densify::(max_dist); + let densified = line.densify(&Euclidean, max_dist); assert_eq!(densified, correct); } } @@ -357,7 +394,7 @@ mod tests { 4.925 45.804 ))); - let actual_haversine = polygon.densify::(50000.0); + let actual_haversine = polygon.densify(&Haversine, 50000.0); assert_relative_eq!(actual_haversine, exepcted_haversine); let expected_geodesic = wkt!(POLYGON(( @@ -372,7 +409,7 @@ mod tests { 5.355 45.883, 4.925 45.804 ))); - let actual_geodesic = polygon.densify::(50000.0); + let actual_geodesic = polygon.densify(&Geodesic, 50000.0); assert_relative_eq!(actual_geodesic, expected_geodesic); } @@ -402,7 +439,7 @@ mod tests { -3.1944 55.949 )); - let dense = linestring.densify::(110.0); + let dense = linestring.densify(&Haversine, 110.0); assert_relative_eq!(dense, expected); } @@ -410,7 +447,7 @@ mod tests { fn test_line_densify() { let output = wkt!(LINESTRING(0.0 0.0, 0.0 0.5, 0.0 1.0)); let line = Line::new(coord! {x: 0.0, y: 0.0}, coord! { x: 0.0, y: 1.0 }); - let dense = line.densify::(100000.0); + let dense = line.densify(&Haversine, 100000.0); assert_relative_eq!(dense, output); } } @@ -421,7 +458,7 @@ mod tests { #[test] fn test_empty_linestring() { let input = wkt!(LINESTRING EMPTY); - let dense = input.densify::(1.0); + let dense = input.densify(&Euclidean, 1.0); assert_eq!(0, dense.coords_count()); assert_eq!(input, dense); } @@ -429,7 +466,7 @@ mod tests { #[test] fn test_one_point_linestring() { let input = wkt!(LINESTRING(1.0 1.0)); - let dense = input.densify::(1.0); + let dense = input.densify(&Euclidean, 1.0); assert_eq!(1, dense.coords_count()); assert_eq!(input, dense); } diff --git a/geo/src/algorithm/line_measures/destination.rs b/geo/src/algorithm/line_measures/destination.rs index 513524937..cb15cb9ef 100644 --- a/geo/src/algorithm/line_measures/destination.rs +++ b/geo/src/algorithm/line_measures/destination.rs @@ -15,5 +15,5 @@ pub trait Destination { /// - returns: Point where the units of x/y depend on the [trait implementation](#implementors). /// /// [`metric_spaces`]: super::metric_spaces - fn destination(origin: Point, bearing: F, distance: F) -> Point; + fn destination(&self, origin: Point, bearing: F, distance: F) -> Point; } diff --git a/geo/src/algorithm/line_measures/distance.rs b/geo/src/algorithm/line_measures/distance.rs index 4ec9d994b..0d28f46df 100644 --- a/geo/src/algorithm/line_measures/distance.rs +++ b/geo/src/algorithm/line_measures/distance.rs @@ -8,5 +8,5 @@ pub trait Distance { /// /// - `origin`, `destination`: geometry where the units of x/y depend on the trait implementation. /// - returns: depends on the trait implementation. - fn distance(origin: Origin, destination: Destination) -> F; + fn distance(&self, origin: Origin, destination: Destination) -> F; } diff --git a/geo/src/algorithm/line_measures/interpolate_point.rs b/geo/src/algorithm/line_measures/interpolate_point.rs index 9f653b966..c60b8c6ba 100644 --- a/geo/src/algorithm/line_measures/interpolate_point.rs +++ b/geo/src/algorithm/line_measures/interpolate_point.rs @@ -6,6 +6,7 @@ pub trait InterpolatePoint { /// /// See [specific implementations](#implementors) for details. fn point_at_distance_between( + &self, start: Point, end: Point, distance_from_start: F, @@ -14,7 +15,12 @@ pub trait InterpolatePoint { /// Returns a new Point along a line between two existing points. /// /// See [specific implementations](#implementors) for details. - fn point_at_ratio_between(start: Point, end: Point, ratio_from_start: F) -> Point; + fn point_at_ratio_between( + &self, + start: Point, + end: Point, + ratio_from_start: F, + ) -> Point; /// Interpolates `Point`s along a line between `start` and `end`. /// @@ -26,6 +32,7 @@ pub trait InterpolatePoint { /// /// `include_ends`: Should the start and end points be included in the output? fn points_along_line( + &self, start: Point, end: Point, max_distance: F, @@ -43,16 +50,16 @@ mod tests { let end = Point::new(1.0, 1.0); let ratio = 0.0; - assert_eq!(Haversine::point_at_ratio_between(start, end, ratio), start); - assert_eq!(Euclidean::point_at_ratio_between(start, end, ratio), start); - assert_eq!(Geodesic::point_at_ratio_between(start, end, ratio), start); - assert_eq!(Rhumb::point_at_ratio_between(start, end, ratio), start); + assert_eq!(Haversine.point_at_ratio_between(start, end, ratio), start); + assert_eq!(Euclidean.point_at_ratio_between(start, end, ratio), start); + assert_eq!(Geodesic.point_at_ratio_between(start, end, ratio), start); + assert_eq!(Rhumb.point_at_ratio_between(start, end, ratio), start); let ratio = 1.0; - assert_eq!(Haversine::point_at_ratio_between(start, end, ratio), end); - assert_eq!(Euclidean::point_at_ratio_between(start, end, ratio), end); - assert_eq!(Geodesic::point_at_ratio_between(start, end, ratio), end); - assert_eq!(Rhumb::point_at_ratio_between(start, end, ratio), end); + assert_eq!(Haversine.point_at_ratio_between(start, end, ratio), end); + assert_eq!(Euclidean.point_at_ratio_between(start, end, ratio), end); + assert_eq!(Geodesic.point_at_ratio_between(start, end, ratio), end); + assert_eq!(Rhumb.point_at_ratio_between(start, end, ratio), end); } mod degenerate { @@ -63,40 +70,22 @@ mod tests { let start = Point::new(1.0, 1.0); let ratio = 0.0; - assert_eq!( - Haversine::point_at_ratio_between(start, start, ratio), - start - ); - assert_eq!( - Euclidean::point_at_ratio_between(start, start, ratio), - start - ); - assert_eq!(Geodesic::point_at_ratio_between(start, start, ratio), start); - assert_eq!(Rhumb::point_at_ratio_between(start, start, ratio), start); + assert_eq!(Haversine.point_at_ratio_between(start, start, ratio), start); + assert_eq!(Euclidean.point_at_ratio_between(start, start, ratio), start); + assert_eq!(Geodesic.point_at_ratio_between(start, start, ratio), start); + assert_eq!(Rhumb.point_at_ratio_between(start, start, ratio), start); let ratio = 0.5; - assert_eq!( - Haversine::point_at_ratio_between(start, start, ratio), - start - ); - assert_eq!( - Euclidean::point_at_ratio_between(start, start, ratio), - start - ); - assert_eq!(Geodesic::point_at_ratio_between(start, start, ratio), start); - assert_eq!(Rhumb::point_at_ratio_between(start, start, ratio), start); + assert_eq!(Haversine.point_at_ratio_between(start, start, ratio), start); + assert_eq!(Euclidean.point_at_ratio_between(start, start, ratio), start); + assert_eq!(Geodesic.point_at_ratio_between(start, start, ratio), start); + assert_eq!(Rhumb.point_at_ratio_between(start, start, ratio), start); let ratio = 1.0; - assert_eq!( - Haversine::point_at_ratio_between(start, start, ratio), - start - ); - assert_eq!( - Euclidean::point_at_ratio_between(start, start, ratio), - start - ); - assert_eq!(Geodesic::point_at_ratio_between(start, start, ratio), start); - assert_eq!(Rhumb::point_at_ratio_between(start, start, ratio), start); + assert_eq!(Haversine.point_at_ratio_between(start, start, ratio), start); + assert_eq!(Euclidean.point_at_ratio_between(start, start, ratio), start); + assert_eq!(Geodesic.point_at_ratio_between(start, start, ratio), start); + assert_eq!(Rhumb.point_at_ratio_between(start, start, ratio), start); } #[test] @@ -107,19 +96,19 @@ mod tests { let distance = 0.0; assert_eq!( - Haversine::point_at_distance_between(start, start, distance), + Haversine.point_at_distance_between(start, start, distance), start ); - let euclidean_result = Euclidean::point_at_distance_between(start, start, distance); + let euclidean_result = Euclidean.point_at_distance_between(start, start, distance); assert!(euclidean_result.x().is_nan()); assert!(euclidean_result.y().is_nan()); assert_eq!( - Geodesic::point_at_distance_between(start, start, distance), + Geodesic.point_at_distance_between(start, start, distance), start ); assert_eq!( - Rhumb::point_at_distance_between(start, start, distance), + Rhumb.point_at_distance_between(start, start, distance), start ); @@ -127,20 +116,20 @@ mod tests { let due_north = Point::new(1.0, 1.9); let due_south = Point::new(1.0, 0.1); assert_relative_eq!( - Haversine::point_at_distance_between(start, start, distance), + Haversine.point_at_distance_between(start, start, distance), due_north, epsilon = 1.0e-1 ); - let euclidean_result = Euclidean::point_at_distance_between(start, start, distance); + let euclidean_result = Euclidean.point_at_distance_between(start, start, distance); assert!(euclidean_result.x().is_nan()); assert!(euclidean_result.y().is_nan()); assert_relative_eq!( - Geodesic::point_at_distance_between(start, start, distance), + Geodesic.point_at_distance_between(start, start, distance), due_south, epsilon = 1.0e-1 ); assert_relative_eq!( - Rhumb::point_at_distance_between(start, start, distance), + Rhumb.point_at_distance_between(start, start, distance), due_north, epsilon = 1.0e-1 ); @@ -153,37 +142,45 @@ mod tests { let max_distance = 1.0; let include_ends = true; - let points: Vec<_> = - Haversine::points_along_line(start, start, max_distance, include_ends).collect(); + let points: Vec<_> = Haversine + .points_along_line(start, start, max_distance, include_ends) + .collect(); assert_eq!(points, vec![start, start]); - let points: Vec<_> = - Euclidean::points_along_line(start, start, max_distance, include_ends).collect(); + let points: Vec<_> = Euclidean + .points_along_line(start, start, max_distance, include_ends) + .collect(); assert_eq!(points, vec![start, start]); - let points: Vec<_> = - Geodesic::points_along_line(start, start, max_distance, include_ends).collect(); + let points: Vec<_> = Geodesic + .points_along_line(start, start, max_distance, include_ends) + .collect(); assert_eq!(points, vec![start, start]); - let points: Vec<_> = - Rhumb::points_along_line(start, start, max_distance, include_ends).collect(); + let points: Vec<_> = Rhumb + .points_along_line(start, start, max_distance, include_ends) + .collect(); assert_eq!(points, vec![start, start]); let include_ends = false; - let points: Vec<_> = - Haversine::points_along_line(start, start, max_distance, include_ends).collect(); + let points: Vec<_> = Haversine + .points_along_line(start, start, max_distance, include_ends) + .collect(); assert_eq!(points, vec![]); - let points: Vec<_> = - Euclidean::points_along_line(start, start, max_distance, include_ends).collect(); + let points: Vec<_> = Euclidean + .points_along_line(start, start, max_distance, include_ends) + .collect(); assert_eq!(points, vec![]); - let points: Vec<_> = - Geodesic::points_along_line(start, start, max_distance, include_ends).collect(); + let points: Vec<_> = Geodesic + .points_along_line(start, start, max_distance, include_ends) + .collect(); assert_eq!(points, vec![]); - let points: Vec<_> = - Rhumb::points_along_line(start, start, max_distance, include_ends).collect(); + let points: Vec<_> = Rhumb + .points_along_line(start, start, max_distance, include_ends) + .collect(); assert_eq!(points, vec![]); } } diff --git a/geo/src/algorithm/line_measures/length.rs b/geo/src/algorithm/line_measures/length.rs index 9eaf8884a..b240f7859 100644 --- a/geo/src/algorithm/line_measures/length.rs +++ b/geo/src/algorithm/line_measures/length.rs @@ -12,40 +12,40 @@ use crate::{CoordFloat, Line, LineString, MultiLineString, Point}; /// 3.0 4.0, /// 3.0 5.0 /// )); -/// assert_eq!(line_string.length::(), 6.); +/// assert_eq!(line_string.length(&Euclidean), 6.); /// /// let line_string_lon_lat = geo::wkt!(LINESTRING ( /// -47.9292 -15.7801f64, /// -58.4173 -34.6118, /// -70.6483 -33.4489 /// )); -/// assert_eq!(line_string_lon_lat.length::().round(), 3_474_956.0); +/// assert_eq!(line_string_lon_lat.length(&Haversine).round(), 3_474_956.0); /// ``` pub trait Length { - fn length, Point>>(&self) -> F; + fn length(&self, metric_space: &impl Distance, Point>) -> F; } impl Length for Line { - fn length, Point>>(&self) -> F { - MetricSpace::distance(self.start_point(), self.end_point()) + fn length(&self, metric_space: &impl Distance, Point>) -> F { + metric_space.distance(self.start_point(), self.end_point()) } } impl Length for LineString { - fn length, Point>>(&self) -> F { + fn length(&self, metric_space: &impl Distance, Point>) -> F { let mut length = F::zero(); for line in self.lines() { - length = length + line.length::(); + length = length + line.length(metric_space); } length } } impl Length for MultiLineString { - fn length, Point>>(&self) -> F { + fn length(&self, metric_space: &impl Distance, Point>) -> F { let mut length = F::zero(); for line in self { - length = length + line.length::(); + length = length + line.length(metric_space); } length } @@ -66,28 +66,28 @@ mod tests { assert_eq!( 343_923., // meters - line.length::().round() + line.length(&Geodesic).round() ); assert_eq!( 341_088., // meters - line.length::().round() + line.length(&Rhumb).round() ); assert_eq!( 343_557., // meters - line.length::().round() + line.length(&Haversine).round() ); // computing Euclidean length of an unprojected (lng/lat) line gives a nonsense answer assert_eq!( 4., // nonsense! - line.length::().round() + line.length(&Euclidean).round() ); // london to paris in EPSG:3035 let projected_line = Line::new( coord!(x: 3620451.74f64, y: 3203901.44), coord!(x: 3760771.86, y: 2889484.80), ); - assert_eq!(344_307., projected_line.length::().round()); + assert_eq!(344_307., projected_line.length(&Euclidean).round()); } #[test] @@ -100,21 +100,21 @@ mod tests { assert_eq!( 6_302_220., // meters - line_string.length::().round() + line_string.length(&Geodesic).round() ); assert_eq!( 6_332_790., // meters - line_string.length::().round() + line_string.length(&Rhumb).round() ); assert_eq!( 6_304_387., // meters - line_string.length::().round() + line_string.length(&Haversine).round() ); // computing Euclidean length of an unprojected (lng/lat) gives a nonsense answer assert_eq!( 59., // nonsense! - line_string.length::().round() + line_string.length(&Euclidean).round() ); // EPSG:102033 let projected_line_string = LineString::from(vec![ @@ -122,9 +122,6 @@ mod tests { coord!(x: -1797084.08, y: 583528.84), // Lima, Peru coord!(x: 1240052.27, y: 207169.12), // Brasília, Brazil ]); - assert_eq!( - 6_237_538., - projected_line_string.length::().round() - ); + assert_eq!(6_237_538., projected_line_string.length(&Euclidean).round()); } } diff --git a/geo/src/algorithm/line_measures/metric_spaces/euclidean/distance.rs b/geo/src/algorithm/line_measures/metric_spaces/euclidean/distance.rs index db5b7b8cb..868fff667 100644 --- a/geo/src/algorithm/line_measures/metric_spaces/euclidean/distance.rs +++ b/geo/src/algorithm/line_measures/metric_spaces/euclidean/distance.rs @@ -14,8 +14,8 @@ macro_rules! symmetric_distance_impl { where F: $t, { - fn distance(a: $a, b: $b) -> F { - Self::distance(b, a) + fn distance(&self, a: $a, b: $b) -> F { + self.distance(b, a) } } }; @@ -26,13 +26,13 @@ macro_rules! symmetric_distance_impl { // └───────────────────────────┘ impl Distance, Coord> for Euclidean { - fn distance(origin: Coord, destination: Coord) -> F { + fn distance(&self, origin: Coord, destination: Coord) -> F { let delta = origin - destination; delta.x.hypot(delta.y) } } impl Distance, &Line> for Euclidean { - fn distance(coord: Coord, line: &Line) -> F { + fn distance(&self, coord: Coord, line: &Line) -> F { ::geo_types::private_utils::point_line_euclidean_distance(Point(coord), *line) } } @@ -59,7 +59,7 @@ impl Distance, Point> for Euclidean { /// let new_york_city = Point::new(-8238310.24, 4942194.78); /// // web mercator /// let london = Point::new(-14226.63, 6678077.70); - /// let distance: f64 = Euclidean::distance(new_york_city, london); + /// let distance: f64 = Euclidean.distance(new_york_city, london); /// /// assert_eq!( /// 8_405_286., // meters in web mercator @@ -70,31 +70,31 @@ impl Distance, Point> for Euclidean { /// [`Haversine`]: crate::line_measures::metric_spaces::Haversine /// [`Geodesic`]: crate::line_measures::metric_spaces::Geodesic /// [metric spaces]: crate::line_measures::metric_spaces - fn distance(origin: Point, destination: Point) -> F { - Self::distance(origin.0, destination.0) + fn distance(&self, origin: Point, destination: Point) -> F { + self.distance(origin.0, destination.0) } } impl Distance, &Point> for Euclidean { - fn distance(origin: &Point, destination: &Point) -> F { - Self::distance(*origin, *destination) + fn distance(&self, origin: &Point, destination: &Point) -> F { + self.distance(*origin, *destination) } } impl Distance, &Line> for Euclidean { - fn distance(origin: &Point, destination: &Line) -> F { + fn distance(&self, origin: &Point, destination: &Line) -> F { geo_types::private_utils::point_line_euclidean_distance(*origin, *destination) } } impl Distance, &LineString> for Euclidean { - fn distance(origin: &Point, destination: &LineString) -> F { + fn distance(&self, origin: &Point, destination: &LineString) -> F { geo_types::private_utils::point_line_string_euclidean_distance(*origin, destination) } } impl Distance, &Polygon> for Euclidean { - fn distance(point: &Point, polygon: &Polygon) -> F { + fn distance(&self, point: &Point, polygon: &Polygon) -> F { // No need to continue if the polygon intersects the point, or is zero-length if polygon.exterior().0.is_empty() || polygon.intersects(point) { return F::zero(); @@ -104,7 +104,7 @@ impl Distance, &Polygon> for Euclidean { polygon .interiors() .iter() - .map(|ring| Self::distance(point, ring)) + .map(|ring| self.distance(point, ring)) .fold(Bounded::max_value(), |accum: F, val| accum.min(val)) .min( polygon @@ -128,30 +128,30 @@ symmetric_distance_impl!(CoordFloat, &Line, Coord); symmetric_distance_impl!(CoordFloat, &Line, &Point); impl Distance, &Line> for Euclidean { - fn distance(line_a: &Line, line_b: &Line) -> F { + fn distance(&self, line_a: &Line, line_b: &Line) -> F { if line_a.intersects(line_b) { return F::zero(); } // minimum of all Point-Line distances - Self::distance(&line_a.start_point(), line_b) - .min(Self::distance(&line_a.end_point(), line_b)) - .min(Self::distance(&line_b.start_point(), line_a)) - .min(Self::distance(&line_b.end_point(), line_a)) + self.distance(&line_a.start_point(), line_b) + .min(self.distance(&line_a.end_point(), line_b)) + .min(self.distance(&line_b.start_point(), line_a)) + .min(self.distance(&line_b.end_point(), line_a)) } } impl Distance, &LineString> for Euclidean { - fn distance(line: &Line, line_string: &LineString) -> F { + fn distance(&self, line: &Line, line_string: &LineString) -> F { line_string .lines() .fold(Bounded::max_value(), |acc, segment| { - acc.min(Self::distance(line, &segment)) + acc.min(self.distance(line, &segment)) }) } } impl Distance, &Polygon> for Euclidean { - fn distance(line: &Line, polygon: &Polygon) -> F { + fn distance(&self, line: &Line, polygon: &Polygon) -> F { if line.intersects(polygon) { return F::zero(); } @@ -160,7 +160,7 @@ impl Distance, &Polygon> for Euclidean { std::iter::once(polygon.exterior()) .chain(polygon.interiors().iter()) .fold(Bounded::max_value(), |acc, line_string| { - acc.min(Self::distance(line, line_string)) + acc.min(self.distance(line, line_string)) }) } } @@ -173,7 +173,7 @@ symmetric_distance_impl!(CoordFloat, &LineString, &Point); symmetric_distance_impl!(GeoFloat, &LineString, &Line); impl Distance, &LineString> for Euclidean { - fn distance(line_string_a: &LineString, line_string_b: &LineString) -> F { + fn distance(&self, line_string_a: &LineString, line_string_b: &LineString) -> F { if line_string_a.intersects(line_string_b) { F::zero() } else { @@ -183,7 +183,7 @@ impl Distance, &LineString> for Euclidean { } impl Distance, &Polygon> for Euclidean { - fn distance(line_string: &LineString, polygon: &Polygon) -> F { + fn distance(&self, line_string: &LineString, polygon: &Polygon) -> F { if line_string.intersects(polygon) { F::zero() } else if !polygon.interiors().is_empty() @@ -210,7 +210,7 @@ symmetric_distance_impl!(GeoFloat, &Polygon, &Point); symmetric_distance_impl!(GeoFloat, &Polygon, &Line); symmetric_distance_impl!(GeoFloat, &Polygon, &LineString); impl Distance, &Polygon> for Euclidean { - fn distance(polygon_a: &Polygon, polygon_b: &Polygon) -> F { + fn distance(&self, polygon_a: &Polygon, polygon_b: &Polygon) -> F { if polygon_a.intersects(polygon_b) { return F::zero(); } @@ -248,15 +248,15 @@ macro_rules! impl_euclidean_distance_for_polygonlike_geometry { ($polygonlike:ty, [$($geometry_b:ty),*]) => { impl Distance for Euclidean { - fn distance(origin: $polygonlike, destination: $polygonlike) -> F { - Self::distance(&origin.to_polygon(), destination) + fn distance(&self, origin: $polygonlike, destination: $polygonlike) -> F { + self.distance(&origin.to_polygon(), destination) } } $( impl Distance for Euclidean { - fn distance(polygonlike: $polygonlike, geometry_b: $geometry_b) -> F { - Self::distance(&polygonlike.to_polygon(), geometry_b) + fn distance(&self, polygonlike: $polygonlike, geometry_b: $geometry_b) -> F { + self.distance(&polygonlike.to_polygon(), geometry_b) } } symmetric_distance_impl!(GeoFloat, $geometry_b, $polygonlike); @@ -275,21 +275,21 @@ impl_euclidean_distance_for_polygonlike_geometry!(&Rect, [&Point, &Li macro_rules! impl_euclidean_distance_for_iter_geometry { ($iter_geometry:ty, [$($to_geometry:ty),*]) => { impl Distance for Euclidean { - fn distance(origin: $iter_geometry, destination: $iter_geometry) -> F { + fn distance(&self, origin: $iter_geometry, destination: $iter_geometry) -> F { origin .iter() .fold(Bounded::max_value(), |accum: F, member| { - accum.min(Self::distance(member, destination)) + accum.min(self.distance(member, destination)) }) } } $( impl Distance for Euclidean { - fn distance(iter_geometry: $iter_geometry, to_geometry: $to_geometry) -> F { + fn distance(&self, iter_geometry: $iter_geometry, to_geometry: $to_geometry) -> F { iter_geometry .iter() .fold(Bounded::max_value(), |accum: F, member| { - accum.min(Self::distance(member, to_geometry)) + accum.min(self.distance(member, to_geometry)) }) } } @@ -312,18 +312,18 @@ macro_rules! impl_euclidean_distance_for_geometry_and_variant { ([$($target:ty),*]) => { $( impl Distance> for Euclidean { - fn distance(origin: $target, destination: &Geometry) -> F { + fn distance(&self, origin: $target, destination: &Geometry) -> F { match destination { - Geometry::Point(point) => Self::distance(origin, point), - Geometry::Line(line) => Self::distance(origin, line), - Geometry::LineString(line_string) => Self::distance(origin, line_string), - Geometry::Polygon(polygon) => Self::distance(origin, polygon), - Geometry::MultiPoint(multi_point) => Self::distance(origin, multi_point), - Geometry::MultiLineString(multi_line_string) => Self::distance(origin, multi_line_string), - Geometry::MultiPolygon(multi_polygon) => Self::distance(origin, multi_polygon), - Geometry::GeometryCollection(geometry_collection) => Self::distance(origin, geometry_collection), - Geometry::Rect(rect) => Self::distance(origin, rect), - Geometry::Triangle(triangle) => Self::distance(origin, triangle), + Geometry::Point(point) => self.distance(origin, point), + Geometry::Line(line) => self.distance(origin, line), + Geometry::LineString(line_string) => self.distance(origin, line_string), + Geometry::Polygon(polygon) => self.distance(origin, polygon), + Geometry::MultiPoint(multi_point) => self.distance(origin, multi_point), + Geometry::MultiLineString(multi_line_string) => self.distance(origin, multi_line_string), + Geometry::MultiPolygon(multi_polygon) => self.distance(origin, multi_polygon), + Geometry::GeometryCollection(geometry_collection) => self.distance(origin, geometry_collection), + Geometry::Rect(rect) => self.distance(origin, rect), + Geometry::Triangle(triangle) => self.distance(origin, triangle), } } } @@ -335,22 +335,22 @@ macro_rules! impl_euclidean_distance_for_geometry_and_variant { impl_euclidean_distance_for_geometry_and_variant!([&Point, &MultiPoint, &Line, &LineString, &MultiLineString, &Polygon, &MultiPolygon, &Triangle, &Rect, &GeometryCollection]); impl Distance, &Geometry> for Euclidean { - fn distance(origin: &Geometry, destination: &Geometry) -> F { + fn distance(&self, origin: &Geometry, destination: &Geometry) -> F { match origin { - Geometry::Point(point) => Self::distance(point, destination), - Geometry::Line(line) => Self::distance(line, destination), - Geometry::LineString(line_string) => Self::distance(line_string, destination), - Geometry::Polygon(polygon) => Self::distance(polygon, destination), - Geometry::MultiPoint(multi_point) => Self::distance(multi_point, destination), + Geometry::Point(point) => self.distance(point, destination), + Geometry::Line(line) => self.distance(line, destination), + Geometry::LineString(line_string) => self.distance(line_string, destination), + Geometry::Polygon(polygon) => self.distance(polygon, destination), + Geometry::MultiPoint(multi_point) => self.distance(multi_point, destination), Geometry::MultiLineString(multi_line_string) => { - Self::distance(multi_line_string, destination) + self.distance(multi_line_string, destination) } - Geometry::MultiPolygon(multi_polygon) => Self::distance(multi_polygon, destination), + Geometry::MultiPolygon(multi_polygon) => self.distance(multi_polygon, destination), Geometry::GeometryCollection(geometry_collection) => { - Self::distance(geometry_collection, destination) + self.distance(geometry_collection, destination) } - Geometry::Rect(rect) => Self::distance(rect, destination), - Geometry::Triangle(triangle) => Self::distance(triangle, destination), + Geometry::Rect(rect) => self.distance(rect, destination), + Geometry::Triangle(triangle) => self.distance(triangle, destination), } } } @@ -369,11 +369,11 @@ fn nearest_neighbour_distance(geom1: &LineString, geom2: &LineSt .points() .fold(Bounded::max_value(), |acc: F, point| { let nearest = tree_a.nearest_neighbor(&point).unwrap(); - acc.min(Euclidean::distance(nearest as &Line, &point)) + acc.min(Euclidean.distance(nearest as &Line, &point)) }) .min(geom1.points().fold(Bounded::max_value(), |acc, point| { let nearest = tree_b.nearest_neighbor(&point).unwrap(); - acc.min(Euclidean::distance(nearest as &Line, &point)) + acc.min(Euclidean.distance(nearest as &Line, &point)) })) } @@ -433,7 +433,7 @@ mod test { let poly = Polygon::new(ls, vec![]); // A Random point outside the octagon let p = Point::new(2.5, 0.5); - let dist = Euclidean::distance(&p, &poly); + let dist = Euclidean.distance(&p, &poly); assert_relative_eq!(dist, 2.1213203435596424); } #[test] @@ -455,7 +455,7 @@ mod test { let poly = Polygon::new(ls, vec![]); // A Random point inside the octagon let p = Point::new(5.5, 2.1); - let dist = Euclidean::distance(&p, &poly); + let dist = Euclidean.distance(&p, &poly); assert_relative_eq!(dist, 0.0); } #[test] @@ -477,7 +477,7 @@ mod test { let poly = Polygon::new(ls, vec![]); // A point on the octagon let p = Point::new(5.0, 1.0); - let dist = Euclidean::distance(&p, &poly); + let dist = Euclidean.distance(&p, &poly); assert_relative_eq!(dist, 0.0); } #[test] @@ -493,7 +493,7 @@ mod test { let poly = Polygon::new(exterior, vec![]); let bugged_point = Point::new(0.0001, 0.); - assert_relative_eq!(Euclidean::distance(&poly, &bugged_point), 0.); + assert_relative_eq!(Euclidean.distance(&poly, &bugged_point), 0.); } #[test] // Point to Polygon, empty Polygon @@ -504,7 +504,7 @@ mod test { let poly = Polygon::new(ls, vec![]); // A point on the octagon let p = Point::new(2.5, 0.5); - let dist = Euclidean::distance(&p, &poly); + let dist = Euclidean.distance(&p, &poly); assert_relative_eq!(dist, 0.0); } #[test] @@ -529,7 +529,7 @@ mod test { let poly = Polygon::new(ls_ext, vec![ls_int]); // A point inside the cutout triangle let p = Point::new(3.5, 2.5); - let dist = Euclidean::distance(&p, &poly); + let dist = Euclidean.distance(&p, &poly); // 0.41036467732879783 <-- Shapely assert_relative_eq!(dist, 0.41036467732879767); @@ -569,8 +569,8 @@ mod test { let pnt1 = Point::new(0.0, 15.0); let pnt2 = Point::new(10.0, 20.0); let ln = Line::new(pnt1.0, pnt2.0); - let dist_mp_ln = Euclidean::distance(&ln, &mp); - let dist_pol1_ln = Euclidean::distance(&ln, &pol1); + let dist_mp_ln = Euclidean.distance(&ln, &mp); + let dist_pol1_ln = Euclidean.distance(&ln, &pol1); assert_relative_eq!(dist_mp_ln, dist_pol1_ln); } @@ -582,7 +582,7 @@ mod test { let p2 = Polygon::new(ls2, vec![]); let mp = MultiPolygon::new(vec![p1, p2]); let p = Point::new(50.0, 50.0); - assert_relative_eq!(Euclidean::distance(&p, &mp), 60.959002616512684); + assert_relative_eq!(Euclidean.distance(&p, &mp), 60.959002616512684); } #[test] // Point to LineString @@ -601,7 +601,7 @@ mod test { let ls = LineString::from(points); // A Random point "inside" the LineString let p = Point::new(5.5, 2.1); - let dist = Euclidean::distance(&p, &ls); + let dist = Euclidean.distance(&p, &ls); assert_relative_eq!(dist, 1.1313708498984762); } #[test] @@ -621,7 +621,7 @@ mod test { let ls = LineString::from(points); // A point which lies on the LineString let p = Point::new(5.0, 4.0); - let dist = Euclidean::distance(&p, &ls); + let dist = Euclidean.distance(&p, &ls); assert_relative_eq!(dist, 0.0); } #[test] @@ -630,7 +630,7 @@ mod test { let points = vec![(3.5, 3.5), (4.4, 2.0), (2.6, 2.0), (3.5, 3.5)]; let ls = LineString::from(points); let p = Point::new(3.5, 2.5); - let dist = Euclidean::distance(&p, &ls); + let dist = Euclidean.distance(&p, &ls); assert_relative_eq!(dist, 0.5); } #[test] @@ -639,7 +639,7 @@ mod test { let points = vec![]; let ls = LineString::new(points); let p = Point::new(5.0, 4.0); - let dist = Euclidean::distance(&p, &ls); + let dist = Euclidean.distance(&p, &ls); assert_relative_eq!(dist, 0.0); } #[test] @@ -648,19 +648,18 @@ mod test { let v2 = LineString::from(vec![(1.0, 10.0), (2.0, 0.0), (3.0, 1.0)]); let mls = MultiLineString::new(vec![v1, v2]); let p = Point::new(50.0, 50.0); - assert_relative_eq!(Euclidean::distance(&p, &mls), 63.25345840347388); + assert_relative_eq!(Euclidean.distance(&p, &mls), 63.25345840347388); } #[test] fn distance1_test() { assert_relative_eq!( - Euclidean::distance(&Point::new(0., 0.), &Point::new(1., 0.)), + Euclidean.distance(&Point::new(0., 0.), &Point::new(1., 0.)), 1. ); } #[test] fn distance2_test() { - let dist = - Euclidean::distance(&Point::new(-72.1235, 42.3521), &Point::new(72.1260, 70.612)); + let dist = Euclidean.distance(&Point::new(-72.1235, 42.3521), &Point::new(72.1260, 70.612)); assert_relative_eq!(dist, 146.99163308930207); } #[test] @@ -678,7 +677,7 @@ mod test { ]; let mp = MultiPoint::new(v); let p = Point::new(50.0, 50.0); - assert_relative_eq!(Euclidean::distance(&p, &mp), 64.03124237432849) + assert_relative_eq!(Euclidean.distance(&p, &mp), 64.03124237432849) } #[test] fn distance_line_test() { @@ -686,21 +685,21 @@ mod test { let p0 = Point::new(2., 3.); let p1 = Point::new(3., 0.); let p2 = Point::new(6., 0.); - assert_relative_eq!(Euclidean::distance(&line0, &p0), 3.); - assert_relative_eq!(Euclidean::distance(&p0, &line0), 3.); + assert_relative_eq!(Euclidean.distance(&line0, &p0), 3.); + assert_relative_eq!(Euclidean.distance(&p0, &line0), 3.); - assert_relative_eq!(Euclidean::distance(&line0, &p1), 0.); - assert_relative_eq!(Euclidean::distance(&p1, &line0), 0.); + assert_relative_eq!(Euclidean.distance(&line0, &p1), 0.); + assert_relative_eq!(Euclidean.distance(&p1, &line0), 0.); - assert_relative_eq!(Euclidean::distance(&line0, &p2), 1.); - assert_relative_eq!(Euclidean::distance(&p2, &line0), 1.); + assert_relative_eq!(Euclidean.distance(&line0, &p2), 1.); + assert_relative_eq!(Euclidean.distance(&p2, &line0), 1.); } #[test] fn distance_line_line_test() { let line0 = Line::from([(0., 0.), (5., 0.)]); let line1 = Line::from([(2., 1.), (7., 2.)]); - assert_relative_eq!(Euclidean::distance(&line0, &line1), 1.); - assert_relative_eq!(Euclidean::distance(&line1, &line0), 1.); + assert_relative_eq!(Euclidean.distance(&line0, &line1), 1.); + assert_relative_eq!(Euclidean.distance(&line1, &line0), 1.); } #[test] // See https://github.com/georust/geo/issues/476 @@ -733,7 +732,7 @@ mod test { y: -0.15433610862574643, }, ]; - assert_eq!(Euclidean::distance(&line, &poly), 0.18752558079168907); + assert_eq!(Euclidean.distance(&line, &poly), 0.18752558079168907); } #[test] // test edge-vertex minimum distance @@ -844,7 +843,7 @@ mod test { (4.921875, 66.33750501996518), ]; let poly2 = Polygon::new(vec2.into(), vec![]); - let distance = Euclidean::distance(&poly1, &poly2); + let distance = Euclidean.distance(&poly1, &poly2); // GEOS says 2.2864896295566055 assert_relative_eq!(distance, 2.2864896295566055); } @@ -858,14 +857,14 @@ mod test { // inside is "inside" outside's ring, but they are disjoint let outside = Polygon::new(shell, vec![ring]); let inside = Polygon::new(poly_in_ring, vec![]); - assert_relative_eq!(Euclidean::distance(&outside, &inside), 5.992772737231033); + assert_relative_eq!(Euclidean.distance(&outside, &inside), 5.992772737231033); } #[test] // two ring LineStrings; one encloses the other but they neither touch nor intersect fn test_linestring_distance() { let ring = geo_test_fixtures::ring::(); let poly_in_ring = geo_test_fixtures::poly_in_ring::(); - assert_relative_eq!(Euclidean::distance(&ring, &poly_in_ring), 5.992772737231033); + assert_relative_eq!(Euclidean.distance(&ring, &poly_in_ring), 5.992772737231033); } #[test] // Line-Polygon test: closest point on Polygon is NOT nearest to a Line end-point @@ -873,7 +872,7 @@ mod test { let line = Line::from([(0.0, 0.0), (0.0, 3.0)]); let v = vec![(5.0, 1.0), (5.0, 2.0), (0.25, 1.5), (5.0, 1.0)]; let poly = Polygon::new(v.into(), vec![]); - assert_relative_eq!(Euclidean::distance(&line, &poly), 0.25); + assert_relative_eq!(Euclidean.distance(&line, &poly), 0.25); } #[test] // Line-Polygon test: Line intersects Polygon @@ -881,7 +880,7 @@ mod test { let line = Line::from([(0.5, 0.0), (0.0, 3.0)]); let v = vec![(5.0, 1.0), (5.0, 2.0), (0.25, 1.5), (5.0, 1.0)]; let poly = Polygon::new(v.into(), vec![]); - assert_relative_eq!(Euclidean::distance(&line, &poly), 0.0); + assert_relative_eq!(Euclidean.distance(&line, &poly), 0.0); } #[test] // Line-Polygon test: Line contained by interior ring @@ -890,14 +889,14 @@ mod test { let v = vec![(5.0, 1.0), (5.0, 2.0), (0.25, 1.0), (5.0, 1.0)]; let v2 = vec![(4.5, 1.2), (4.5, 1.8), (3.5, 1.2), (4.5, 1.2)]; let poly = Polygon::new(v.into(), vec![v2.into()]); - assert_relative_eq!(Euclidean::distance(&line, &poly), 0.04999999999999982); + assert_relative_eq!(Euclidean.distance(&line, &poly), 0.04999999999999982); } #[test] // LineString-Line test fn test_linestring_line_distance() { let line = Line::from([(0.0, 0.0), (0.0, 2.0)]); let ls: LineString<_> = vec![(3.0, 0.0), (1.0, 1.0), (3.0, 2.0)].into(); - assert_relative_eq!(Euclidean::distance(&ls, &line), 1.0); + assert_relative_eq!(Euclidean.distance(&ls, &line), 1.0); } #[test] @@ -905,7 +904,7 @@ mod test { fn test_triangle_point_on_vertex_distance() { let triangle = Triangle::from([(0.0, 0.0), (2.0, 0.0), (2.0, 2.0)]); let point = Point::new(0.0, 0.0); - assert_relative_eq!(Euclidean::distance(&triangle, &point), 0.0); + assert_relative_eq!(Euclidean.distance(&triangle, &point), 0.0); } #[test] @@ -913,7 +912,7 @@ mod test { fn test_triangle_point_on_edge_distance() { let triangle = Triangle::from([(0.0, 0.0), (2.0, 0.0), (2.0, 2.0)]); let point = Point::new(1.5, 0.0); - assert_relative_eq!(Euclidean::distance(&triangle, &point), 0.0); + assert_relative_eq!(Euclidean.distance(&triangle, &point), 0.0); } #[test] @@ -921,7 +920,7 @@ mod test { fn test_triangle_point_distance() { let triangle = Triangle::from([(0.0, 0.0), (2.0, 0.0), (2.0, 2.0)]); let point = Point::new(2.0, 3.0); - assert_relative_eq!(Euclidean::distance(&triangle, &point), 1.0); + assert_relative_eq!(Euclidean.distance(&triangle, &point), 1.0); } #[test] @@ -929,7 +928,7 @@ mod test { fn test_triangle_point_inside_distance() { let triangle = Triangle::from([(0.0, 0.0), (2.0, 0.0), (2.0, 2.0)]); let point = Point::new(1.0, 0.5); - assert_relative_eq!(Euclidean::distance(&triangle, &point), 0.0); + assert_relative_eq!(Euclidean.distance(&triangle, &point), 0.0); } #[test] @@ -954,7 +953,7 @@ mod test { let second_polygon = Polygon::new(ls2, vec![]); assert_relative_eq!( - Euclidean::distance(&first_polygon, &second_polygon), + Euclidean.distance(&first_polygon, &second_polygon), 224.35357967013238 ); } @@ -987,10 +986,10 @@ mod test { (x: 50_f64, y: 200_f64), ) .orient(Direction::Reversed); - assert_eq!(Euclidean::distance(&p1, &p2), 50.0f64); - assert_eq!(Euclidean::distance(&p3, &p4), 50.0f64); - assert_eq!(Euclidean::distance(&p1, &p4), 50.0f64); - assert_eq!(Euclidean::distance(&p2, &p3), 50.0f64); + assert_eq!(Euclidean.distance(&p1, &p2), 50.0f64); + assert_eq!(Euclidean.distance(&p3, &p4), 50.0f64); + assert_eq!(Euclidean.distance(&p1, &p4), 50.0f64); + assert_eq!(Euclidean.distance(&p2, &p3), 50.0f64); } #[test] fn all_types_geometry_collection_test() { @@ -1040,22 +1039,22 @@ mod test { ]); let test_p = Point::new(50., 50.); - assert_relative_eq!(Euclidean::distance(&test_p, &gc), 60.959002616512684); + assert_relative_eq!(Euclidean.distance(&test_p, &gc), 60.959002616512684); let test_multipoint = MultiPoint::new(vec![test_p]); assert_relative_eq!( - Euclidean::distance(&test_multipoint, &gc), + Euclidean.distance(&test_multipoint, &gc), 60.959002616512684 ); let test_line = Line::from([(50., 50.), (60., 60.)]); - assert_relative_eq!(Euclidean::distance(&test_line, &gc), 60.959002616512684); + assert_relative_eq!(Euclidean.distance(&test_line, &gc), 60.959002616512684); let test_ls = LineString::from(vec![(50., 50.), (60., 60.), (70., 70.)]); - assert_relative_eq!(Euclidean::distance(&test_ls, &gc), 60.959002616512684); + assert_relative_eq!(Euclidean.distance(&test_ls, &gc), 60.959002616512684); let test_mls = MultiLineString::new(vec![test_ls]); - assert_relative_eq!(Euclidean::distance(&test_mls, &gc), 60.959002616512684); + assert_relative_eq!(Euclidean.distance(&test_mls, &gc), 60.959002616512684); let test_poly = Polygon::new( LineString::from(vec![ @@ -1067,21 +1066,18 @@ mod test { ]), vec![], ); - assert_relative_eq!(Euclidean::distance(&test_poly, &gc), 60.959002616512684); + assert_relative_eq!(Euclidean.distance(&test_poly, &gc), 60.959002616512684); let test_multipoly = MultiPolygon::new(vec![test_poly]); - assert_relative_eq!( - Euclidean::distance(&test_multipoly, &gc), - 60.959002616512684 - ); + assert_relative_eq!(Euclidean.distance(&test_multipoly, &gc), 60.959002616512684); let test_tri = Triangle::from([(50., 50.), (60., 50.), (55., 55.)]); - assert_relative_eq!(Euclidean::distance(&test_tri, &gc), 60.959002616512684); + assert_relative_eq!(Euclidean.distance(&test_tri, &gc), 60.959002616512684); let test_rect = Rect::new(coord! { x: 50., y: 50. }, coord! { x: 60., y: 60. }); - assert_relative_eq!(Euclidean::distance(&test_rect, &gc), 60.959002616512684); + assert_relative_eq!(Euclidean.distance(&test_rect, &gc), 60.959002616512684); let test_gc = GeometryCollection(vec![Geometry::Rect(test_rect)]); - assert_relative_eq!(Euclidean::distance(&test_gc, &gc), 60.959002616512684); + assert_relative_eq!(Euclidean.distance(&test_gc, &gc), 60.959002616512684); } } diff --git a/geo/src/algorithm/line_measures/metric_spaces/euclidean/mod.rs b/geo/src/algorithm/line_measures/metric_spaces/euclidean/mod.rs index c30fdc933..4312a308b 100644 --- a/geo/src/algorithm/line_measures/metric_spaces/euclidean/mod.rs +++ b/geo/src/algorithm/line_measures/metric_spaces/euclidean/mod.rs @@ -39,6 +39,7 @@ impl InterpolatePoint for Euclidean { /// [`Geodesic`]: crate::line_measures::Geodesic /// [metric spaces]: crate::line_measures::metric_spaces fn point_at_distance_between( + &self, start: Point, end: Point, distance_from_start: F, @@ -61,7 +62,12 @@ impl InterpolatePoint for Euclidean { /// [`Haversine`]: crate::line_measures::Haversine /// [`Geodesic`]: crate::line_measures::Geodesic /// [metric spaces]: crate::line_measures::metric_spaces - fn point_at_ratio_between(start: Point, end: Point, ratio_from_start: F) -> Point { + fn point_at_ratio_between( + &self, + start: Point, + end: Point, + ratio_from_start: F, + ) -> Point { let diff = end - start; start + diff * ratio_from_start } @@ -85,6 +91,7 @@ impl InterpolatePoint for Euclidean { /// [`Geodesic`]: crate::line_measures::Geodesic /// [metric spaces]: crate::line_measures::metric_spaces fn points_along_line( + &self, start: Point, end: Point, max_distance: F, @@ -94,7 +101,7 @@ impl InterpolatePoint for Euclidean { if include_ends { container.push(start); } - densify_between::(start, end, &mut container, max_distance); + densify_between(self, start, end, &mut container, max_distance); if include_ends { container.push(end); } @@ -106,8 +113,6 @@ impl InterpolatePoint for Euclidean { mod tests { use super::*; - type MetricSpace = Euclidean; - mod distance { use super::*; @@ -117,7 +122,7 @@ mod tests { let new_york_city = Point::new(-8238310.24, 4942194.78); // web mercator let london = Point::new(-14226.63, 6678077.70); - let distance: f64 = MetricSpace::distance(new_york_city, london); + let distance: f64 = Euclidean.distance(new_york_city, london); assert_relative_eq!( 8_405_286., // meters in web mercator @@ -130,14 +135,14 @@ mod tests { let new_york_city = Point::new(-8_238_310.24, 4_942_194.78); // web mercator let london = Point::new(-14_226.63, 6_678_077.70); - let start = MetricSpace::point_at_distance_between(new_york_city, london, 0.0); + let start = Euclidean.point_at_distance_between(new_york_city, london, 0.0); assert_relative_eq!(new_york_city, start); let midway = - MetricSpace::point_at_distance_between(new_york_city, london, 8_405_286.0 / 2.0); + Euclidean.point_at_distance_between(new_york_city, london, 8_405_286.0 / 2.0); assert_relative_eq!(Point::new(-4_126_268., 5_810_136.), midway, epsilon = 1.0); - let end = MetricSpace::point_at_distance_between(new_york_city, london, 8_405_286.0); + let end = Euclidean.point_at_distance_between(new_york_city, london, 8_405_286.0); assert_relative_eq!(london, end, epsilon = 1.0); } } diff --git a/geo/src/algorithm/line_measures/metric_spaces/geodesic.rs b/geo/src/algorithm/line_measures/metric_spaces/geodesic.rs index 89335ca87..2103561eb 100644 --- a/geo/src/algorithm/line_measures/metric_spaces/geodesic.rs +++ b/geo/src/algorithm/line_measures/metric_spaces/geodesic.rs @@ -25,7 +25,7 @@ impl Bearing for Geodesic { /// /// let origin = Point::new(9.0, 10.0); /// let destination = Point::new(9.5, 10.1); - /// let bearing = Geodesic::bearing(origin, destination); + /// let bearing = Geodesic.bearing(origin, destination); /// // A little north of east /// assert_relative_eq!(bearing, 78.54, epsilon = 1.0e-2); /// ``` @@ -36,7 +36,7 @@ impl Bearing for Geodesic { /// /// [geodesic line]: https://en.wikipedia.org/wiki/Geodesics_on_an_ellipsoid /// [Karney (2013)]: https://arxiv.org/pdf/1109.4448.pdf - fn bearing(origin: Point, destination: Point) -> f64 { + fn bearing(&self, origin: Point, destination: Point) -> f64 { let (azi1, _, _) = geographiclib_rs::Geodesic::wgs84().inverse( origin.y(), origin.x(), @@ -71,7 +71,7 @@ impl Destination for Geodesic { /// let northeast_bearing = 45.0; /// let distance = 100_000.0; /// - /// let northeast_of_jfk = Geodesic::destination(jfk, northeast_bearing, distance); + /// let northeast_of_jfk = Geodesic.destination(jfk, northeast_bearing, distance); /// assert_relative_eq!(Point::new(-72.94, 41.27), northeast_of_jfk, epsilon = 1.0e-2); /// ``` /// @@ -81,7 +81,7 @@ impl Destination for Geodesic { /// /// [geodesic line]: https://en.wikipedia.org/wiki/Geodesics_on_an_ellipsoid /// [Karney (2013)]: https://arxiv.org/pdf/1109.4448.pdf - fn destination(origin: Point, bearing: f64, distance: f64) -> Point { + fn destination(&self, origin: Point, bearing: f64, distance: f64) -> Point { let (lat, lon) = geographiclib_rs::Geodesic::wgs84().direct(origin.y(), origin.x(), bearing, distance); Point::new(lon, lat) @@ -106,7 +106,7 @@ impl Distance, Point> for Geodesic { /// // London /// let london = Point::new(-0.1278, 51.5074); /// - /// let distance = Geodesic::distance(new_york_city, london); + /// let distance = Geodesic.distance(new_york_city, london); /// /// assert_eq!( /// 5_585_234., // meters @@ -120,7 +120,7 @@ impl Distance, Point> for Geodesic { /// /// [geodesic line]: https://en.wikipedia.org/wiki/Geodesics_on_an_ellipsoid /// [Karney (2013)]: https://arxiv.org/pdf/1109.4448.pdf - fn distance(origin: Point, destination: Point) -> f64 { + fn distance(&self, origin: Point, destination: Point) -> f64 { geographiclib_rs::Geodesic::wgs84().inverse( origin.y(), origin.x(), @@ -150,10 +150,10 @@ impl InterpolatePoint for Geodesic { /// let p1 = Point::new(10.0, 20.0); /// let p2 = Point::new(125.0, 25.0); /// - /// let closer_to_p1 = Geodesic::point_at_distance_between(p1, p2, 100_000.0); + /// let closer_to_p1 = Geodesic.point_at_distance_between(p1, p2, 100_000.0); /// assert_relative_eq!(closer_to_p1, Point::new(10.81, 20.49), epsilon = 1.0e-2); /// - /// let closer_to_p2 = Geodesic::point_at_distance_between(p1, p2, 10_000_000.0); + /// let closer_to_p2 = Geodesic.point_at_distance_between(p1, p2, 10_000_000.0); /// assert_relative_eq!(closer_to_p2, Point::new(112.20, 30.67), epsilon = 1.0e-2); /// ``` /// @@ -164,6 +164,7 @@ impl InterpolatePoint for Geodesic { /// [geodesic line]: https://en.wikipedia.org/wiki/Geodesics_on_an_ellipsoid /// [Karney (2013)]: https://arxiv.org/pdf/1109.4448.pdf fn point_at_distance_between( + &self, start: Point, end: Point, meters_from_start: f64, @@ -171,8 +172,8 @@ impl InterpolatePoint for Geodesic { if meters_from_start == 0.0 { return start; } - let bearing = Self::bearing(start, end); - Self::destination(start, bearing, meters_from_start) + let bearing = Self.bearing(start, end); + Self.destination(start, bearing, meters_from_start) } /// Returns a new Point along a [geodesic line] between two existing points on an ellipsoidal model of the earth. @@ -187,13 +188,13 @@ impl InterpolatePoint for Geodesic { /// let p1 = Point::new(10.0, 20.0); /// let p2 = Point::new(125.0, 25.0); /// - /// let closer_to_p1 = Geodesic::point_at_ratio_between(p1, p2, 0.1); + /// let closer_to_p1 = Geodesic.point_at_ratio_between(p1, p2, 0.1); /// assert_relative_eq!(closer_to_p1, Point::new(19.52, 25.31), epsilon = 1.0e-2); /// - /// let closer_to_p2 = Geodesic::point_at_ratio_between(p1, p2, 0.9); + /// let closer_to_p2 = Geodesic.point_at_ratio_between(p1, p2, 0.9); /// assert_relative_eq!(closer_to_p2, Point::new(114.73, 29.69), epsilon = 1.0e-2); /// - /// let midpoint = Geodesic::point_at_ratio_between(p1, p2, 0.5); + /// let midpoint = Geodesic.point_at_ratio_between(p1, p2, 0.5); /// assert_relative_eq!(midpoint, Point::new(65.88, 37.72), epsilon = 1.0e-2); /// ``` /// @@ -204,6 +205,7 @@ impl InterpolatePoint for Geodesic { /// [geodesic line]: https://en.wikipedia.org/wiki/Geodesics_on_an_ellipsoid /// [Karney (2013)]: https://arxiv.org/pdf/1109.4448.pdf fn point_at_ratio_between( + &self, start: Point, end: Point, ratio_from_start: f64, @@ -218,7 +220,7 @@ impl InterpolatePoint for Geodesic { let g = geographiclib_rs::Geodesic::wgs84(); let (total_distance, azi1, _azi2, _a12) = g.inverse(start.y(), start.x(), end.y(), end.x()); let distance = total_distance * ratio_from_start; - Self::destination(start, azi1, distance) + Self.destination(start, azi1, distance) } /// Interpolates `Point`s along a [geodesic line] between `start` and `end`. @@ -236,6 +238,7 @@ impl InterpolatePoint for Geodesic { /// [geodesic line]: https://en.wikipedia.org/wiki/Geodesics_on_an_ellipsoid /// [Karney (2013)]: https://arxiv.org/pdf/1109.4448.pdf fn points_along_line( + &self, start: Point, end: Point, max_distance: f64, @@ -277,8 +280,6 @@ impl InterpolatePoint for Geodesic { mod tests { use super::*; - type MetricSpace = Geodesic; - mod bearing { use super::*; @@ -286,28 +287,28 @@ mod tests { fn north() { let origin = Point::new(0.0, 0.0); let destination = Point::new(0.0, 1.0); - assert_relative_eq!(0.0, MetricSpace::bearing(origin, destination)); + assert_relative_eq!(0.0, Geodesic.bearing(origin, destination)); } #[test] fn east() { let origin = Point::new(0.0, 0.0); let destination = Point::new(1.0, 0.0); - assert_relative_eq!(90.0, MetricSpace::bearing(origin, destination)); + assert_relative_eq!(90.0, Geodesic.bearing(origin, destination)); } #[test] fn south() { let origin = Point::new(0.0, 0.0); let destination = Point::new(0.0, -1.0); - assert_relative_eq!(180.0, MetricSpace::bearing(origin, destination)); + assert_relative_eq!(180.0, Geodesic.bearing(origin, destination)); } #[test] fn west() { let origin = Point::new(0.0, 0.0); let destination = Point::new(-1.0, 0.0); - assert_relative_eq!(270.0, MetricSpace::bearing(origin, destination)); + assert_relative_eq!(270.0, Geodesic.bearing(origin, destination)); } } @@ -320,7 +321,7 @@ mod tests { let bearing = 0.0; assert_relative_eq!( Point::new(0.0, 0.9043687229127633), - MetricSpace::destination(origin, bearing, 100_000.0) + Geodesic.destination(origin, bearing, 100_000.0) ); } @@ -330,7 +331,7 @@ mod tests { let bearing = 90.0; assert_relative_eq!( Point::new(0.8983152841195217, 0.0), - MetricSpace::destination(origin, bearing, 100_000.0) + Geodesic.destination(origin, bearing, 100_000.0) ); } @@ -340,7 +341,7 @@ mod tests { let bearing = 180.0; assert_relative_eq!( Point::new(0.0, -0.9043687229127633), - MetricSpace::destination(origin, bearing, 100_000.0) + Geodesic.destination(origin, bearing, 100_000.0) ); } @@ -350,7 +351,7 @@ mod tests { let bearing = 270.0; assert_relative_eq!( Point::new(-0.8983152841195217, 0.0), - MetricSpace::destination(origin, bearing, 100_000.0) + Geodesic.destination(origin, bearing, 100_000.0) ); } @@ -362,7 +363,7 @@ mod tests { let new_york_city = Point::new(-74.006f64, 40.7128f64); let london = Point::new(-0.1278f64, 51.5074f64); - let distance = MetricSpace::distance(new_york_city, london); + let distance = Geodesic.distance(new_york_city, london); assert_relative_eq!( 5_585_234.0, // meters @@ -378,7 +379,7 @@ mod tests { fn point_at_ratio_between_midpoint() { let start = Point::new(10.0, 20.0); let end = Point::new(125.0, 25.0); - let midpoint = MetricSpace::point_at_ratio_between(start, end, 0.5); + let midpoint = Geodesic.point_at_ratio_between(start, end, 0.5); assert_relative_eq!(midpoint, Point::new(65.87936072133309, 37.72225378005785)); } @@ -387,8 +388,9 @@ mod tests { let start = Point::new(10.0, 20.0); let end = Point::new(125.0, 25.0); let max_dist = 1000000.0; // meters - let route = - MetricSpace::points_along_line(start, end, max_dist, true).collect::>(); + let route = Geodesic + .points_along_line(start, end, max_dist, true) + .collect::>(); assert_eq!(route.len(), 13); assert_eq!(route[0], start); assert_eq!(route.last().unwrap(), &end); @@ -400,8 +402,9 @@ mod tests { let start = Point::new(10.0, 20.0); let end = Point::new(125.0, 25.0); let max_dist = 1000000.0; // meters - let route = - MetricSpace::points_along_line(start, end, max_dist, false).collect::>(); + let route = Geodesic + .points_along_line(start, end, max_dist, false) + .collect::>(); assert_eq!(route.len(), 11); assert_relative_eq!(route[0], Point::new(17.878754355562464, 24.466667836189565)); } diff --git a/geo/src/algorithm/line_measures/metric_spaces/haversine.rs b/geo/src/algorithm/line_measures/metric_spaces/haversine.rs index d110d5179..64f61868c 100644 --- a/geo/src/algorithm/line_measures/metric_spaces/haversine.rs +++ b/geo/src/algorithm/line_measures/metric_spaces/haversine.rs @@ -39,7 +39,7 @@ impl Bearing for Haversine { /// /// let origin = Point::new(9.0, 10.0); /// let destination = Point::new(9.5, 10.1); - /// let bearing = Haversine::bearing(origin, destination); + /// let bearing = Haversine.bearing(origin, destination); /// // A little north of east /// assert_relative_eq!(bearing, 78.47, epsilon = 1.0e-2); /// ``` @@ -50,7 +50,7 @@ impl Bearing for Haversine { /// () /// /// [great circle]: https://en.wikipedia.org/wiki/Great_circle - fn bearing(origin: Point, destination: Point) -> F { + fn bearing(&self, origin: Point, destination: Point) -> F { let three_sixty = F::from(360.0).expect("Numeric type to be constructable from primitive 360"); let (lng_a, lat_a) = (origin.x().to_radians(), origin.y().to_radians()); @@ -83,7 +83,7 @@ impl Destination for Haversine { /// use geo::Point; /// /// let origin = Point::new(9.177789688110352, 48.776781529534965); - /// let destination = Haversine::destination(origin, 45., 10000.); + /// let destination = Haversine.destination(origin, 45., 10000.); /// assert_relative_eq!(Point::new(9.274409949623532, 48.84033274015048), destination); /// ``` /// @@ -93,7 +93,7 @@ impl Destination for Haversine { /// the IUGG](ftp://athena.fsv.cvut.cz/ZFG/grs80-Moritz.pdf) /// /// [great circle]: https://en.wikipedia.org/wiki/Great_circle - fn destination(origin: Point, bearing: F, meters: F) -> Point { + fn destination(&self, origin: Point, bearing: F, meters: F) -> Point { let center_lng = origin.x().to_radians(); let center_lat = origin.y().to_radians(); let bearing_rad = bearing.to_radians(); @@ -129,7 +129,7 @@ impl Distance, Point> for Haversin /// let new_york_city = Point::new(-74.006f64, 40.7128f64); /// let london = Point::new(-0.1278f64, 51.5074f64); /// - /// let distance = Haversine::distance(new_york_city, london); + /// let distance = Haversine.distance(new_york_city, london); /// /// assert_relative_eq!( /// 5_570_230., // meters @@ -143,7 +143,7 @@ impl Distance, Point> for Haversin /// the IUGG](ftp://athena.fsv.cvut.cz/ZFG/grs80-Moritz.pdf) /// /// [haversine formula]: https://en.wikipedia.org/wiki/Haversine_formula - fn distance(origin: Point, destination: Point) -> F { + fn distance(&self, origin: Point, destination: Point) -> F { let two = F::one() + F::one(); let theta1 = origin.y().to_radians(); let theta2 = destination.y().to_radians(); @@ -172,17 +172,22 @@ impl InterpolatePoint for Haversine { /// let p1 = Point::new(10.0, 20.0); /// let p2 = Point::new(125.0, 25.0); /// - /// let closer_to_p1 = Haversine::point_at_distance_between(p1, p2, 100_000.0); + /// let closer_to_p1 = Haversine.point_at_distance_between(p1, p2, 100_000.0); /// assert_relative_eq!(closer_to_p1, Point::new(10.81, 20.49), epsilon = 1.0e-2); /// - /// let closer_to_p2 = Haversine::point_at_distance_between(p1, p2, 10_000_000.0); + /// let closer_to_p2 = Haversine.point_at_distance_between(p1, p2, 10_000_000.0); /// assert_relative_eq!(closer_to_p2, Point::new(112.33, 30.57), epsilon = 1.0e-2); /// ``` /// /// [great circle]: https://en.wikipedia.org/wiki/Great_circle - fn point_at_distance_between(start: Point, end: Point, meters_from_start: F) -> Point { - let bearing = Self::bearing(start, end); - Self::destination(start, bearing, meters_from_start) + fn point_at_distance_between( + &self, + start: Point, + end: Point, + meters_from_start: F, + ) -> Point { + let bearing = Self.bearing(start, end); + Self.destination(start, bearing, meters_from_start) } /// Returns a new Point along a [great circle] between two existing points. @@ -197,18 +202,23 @@ impl InterpolatePoint for Haversine { /// let p1 = Point::new(10.0, 20.0); /// let p2 = Point::new(125.0, 25.0); /// - /// let closer_to_p1 = Haversine::point_at_ratio_between(p1, p2, 0.1); + /// let closer_to_p1 = Haversine.point_at_ratio_between(p1, p2, 0.1); /// assert_relative_eq!(closer_to_p1, Point::new(19.52, 25.27), epsilon = 1.0e-2); /// - /// let closer_to_p2 = Haversine::point_at_ratio_between(p1, p2, 0.9); + /// let closer_to_p2 = Haversine.point_at_ratio_between(p1, p2, 0.9); /// assert_relative_eq!(closer_to_p2, Point::new(114.72, 29.65), epsilon = 1.0e-2); /// - /// let midpoint = Haversine::point_at_ratio_between(p1, p2, 0.5); + /// let midpoint = Haversine.point_at_ratio_between(p1, p2, 0.5); /// assert_relative_eq!(midpoint, Point::new(65.87, 37.62), epsilon = 1.0e-2); /// ``` /// /// [great circle]: https://en.wikipedia.org/wiki/Great_circle - fn point_at_ratio_between(start: Point, end: Point, ratio_from_start: F) -> Point { + fn point_at_ratio_between( + &self, + start: Point, + end: Point, + ratio_from_start: F, + ) -> Point { if start == end || ratio_from_start == F::zero() { return start; } @@ -230,6 +240,7 @@ impl InterpolatePoint for Haversine { /// [great circle]: https://en.wikipedia.org/wiki/Great_circle /// [haversine formula]: https://en.wikipedia.org/wiki/Haversine_formula fn points_along_line( + &self, start: Point, end: Point, max_distance: F, @@ -350,8 +361,6 @@ impl HaversineIntermediateFillCalculation { mod tests { use super::*; - type MetricSpace = Haversine; - mod bearing { use super::*; @@ -359,28 +368,28 @@ mod tests { fn north() { let origin = Point::new(0.0, 0.0); let destination = Point::new(0.0, 1.0); - assert_relative_eq!(0.0, MetricSpace::bearing(origin, destination)); + assert_relative_eq!(0.0, Haversine.bearing(origin, destination)); } #[test] fn east() { let origin = Point::new(0.0, 0.0); let destination = Point::new(1.0, 0.0); - assert_relative_eq!(90.0, MetricSpace::bearing(origin, destination)); + assert_relative_eq!(90.0, Haversine.bearing(origin, destination)); } #[test] fn south() { let origin = Point::new(0.0, 0.0); let destination = Point::new(0.0, -1.0); - assert_relative_eq!(180.0, MetricSpace::bearing(origin, destination)); + assert_relative_eq!(180.0, Haversine.bearing(origin, destination)); } #[test] fn west() { let origin = Point::new(0.0, 0.0); let destination = Point::new(-1.0, 0.0); - assert_relative_eq!(270.0, MetricSpace::bearing(origin, destination)); + assert_relative_eq!(270.0, Haversine.bearing(origin, destination)); } } @@ -393,7 +402,7 @@ mod tests { let bearing = 0.0; assert_relative_eq!( Point::new(0.0, 0.899320363724538), - MetricSpace::destination(origin, bearing, 100_000.0) + Haversine.destination(origin, bearing, 100_000.0) ); } @@ -403,7 +412,7 @@ mod tests { let bearing = 90.0; assert_relative_eq!( Point::new(0.8993203637245415, 5.506522912913066e-17), - MetricSpace::destination(origin, bearing, 100_000.0) + Haversine.destination(origin, bearing, 100_000.0) ); } @@ -413,7 +422,7 @@ mod tests { let bearing = 180.0; assert_relative_eq!( Point::new(0.0, -0.899320363724538), - MetricSpace::destination(origin, bearing, 100_000.0) + Haversine.destination(origin, bearing, 100_000.0) ); } @@ -423,7 +432,7 @@ mod tests { let bearing = 270.0; assert_relative_eq!( Point::new(-0.8993203637245415, -1.6519568738739197e-16), - MetricSpace::destination(origin, bearing, 100_000.0) + Haversine.destination(origin, bearing, 100_000.0) ); } } @@ -436,7 +445,7 @@ mod tests { let new_york_city = Point::new(-74.006f64, 40.7128f64); let london = Point::new(-0.1278f64, 51.5074f64); - let distance = MetricSpace::distance(new_york_city, london); + let distance = Haversine.distance(new_york_city, london); assert_relative_eq!( 5_570_230., // meters @@ -451,7 +460,7 @@ mod tests { fn point_at_ratio_between_midpoint() { let start = Point::new(10.0, 20.0); let end = Point::new(125.0, 25.0); - let midpoint = MetricSpace::point_at_ratio_between(start, end, 0.5); + let midpoint = Haversine.point_at_ratio_between(start, end, 0.5); assert_relative_eq!(midpoint, Point::new(65.87394172511485, 37.61809316888599)); } #[test] @@ -459,8 +468,9 @@ mod tests { let start = Point::new(10.0, 20.0); let end = Point::new(125.0, 25.0); let max_dist = 1000000.0; // meters - let route = - MetricSpace::points_along_line(start, end, max_dist, true).collect::>(); + let route = Haversine + .points_along_line(start, end, max_dist, true) + .collect::>(); assert_eq!(route.len(), 13); assert_eq!(route[0], start); assert_eq!(route.last().unwrap(), &end); @@ -471,8 +481,9 @@ mod tests { let start = Point::new(10.0, 20.0); let end = Point::new(125.0, 25.0); let max_dist = 1000000.0; // meters - let route = - MetricSpace::points_along_line(start, end, max_dist, false).collect::>(); + let route = Haversine + .points_along_line(start, end, max_dist, false) + .collect::>(); assert_eq!(route.len(), 11); assert_relative_eq!(route[0], Point::new(17.882467331860965, 24.435542998803793)); } diff --git a/geo/src/algorithm/line_measures/metric_spaces/rhumb.rs b/geo/src/algorithm/line_measures/metric_spaces/rhumb.rs index b13cd1b2d..571dc74aa 100644 --- a/geo/src/algorithm/line_measures/metric_spaces/rhumb.rs +++ b/geo/src/algorithm/line_measures/metric_spaces/rhumb.rs @@ -39,7 +39,7 @@ impl Bearing for Rhumb { /// /// let origin = Point::new(9.177789688110352, 48.776781529534965); /// let destination = Point::new(9.274348757829898, 48.84037308229984); - /// let bearing = Rhumb::bearing(origin, destination); + /// let bearing = Rhumb.bearing(origin, destination); /// assert_relative_eq!(bearing, 45., epsilon = 1.0e-6); /// ``` /// @@ -49,7 +49,7 @@ impl Bearing for Rhumb { /// /// Bullock, R.: Great Circle Distances and Bearings Between Two Locations, 2007. /// () - fn bearing(origin: Point, destination: Point) -> F { + fn bearing(&self, origin: Point, destination: Point) -> F { let three_sixty = F::from(360.0f64).unwrap(); let calculations = RhumbCalculations::new(&origin, &destination); @@ -76,12 +76,12 @@ impl Destination for Rhumb { /// use geo::Point; /// /// let p_1 = Point::new(9.177789688110352, 48.776781529534965); - /// let p_2 = Rhumb::destination(p_1, 45., 10000.); + /// let p_2 = Rhumb.destination(p_1, 45., 10000.); /// assert_relative_eq!(p_2, Point::new(9.274348757829898, 48.84037308229984)) /// ``` /// /// [rhumb line]: https://en.wikipedia.org/wiki/Rhumb_line - fn destination(origin: Point, bearing: F, distance: F) -> Point { + fn destination(&self, origin: Point, bearing: F, distance: F) -> Point { let delta = distance / F::from(MEAN_EARTH_RADIUS).unwrap(); // angular distance in radians let lambda1 = origin.x().to_radians(); let phi1 = origin.y().to_radians(); @@ -111,7 +111,7 @@ impl Distance, Point> for Rhumb { /// // London /// let p2 = point!(x: -0.1278, y: 51.5074); /// - /// let distance = Rhumb::distance(p1, p2); + /// let distance = Rhumb.distance(p1, p2); /// /// assert_eq!( /// 5_794_129., // meters @@ -120,7 +120,7 @@ impl Distance, Point> for Rhumb { /// ``` /// /// [rhumb line]: https://en.wikipedia.org/wiki/Rhumb_line - fn distance(origin: Point, destination: Point) -> F { + fn distance(&self, origin: Point, destination: Point) -> F { let calculations = RhumbCalculations::new(&origin, &destination); calculations.delta() * F::from(MEAN_EARTH_RADIUS).unwrap() } @@ -142,17 +142,22 @@ impl InterpolatePoint for Rhumb { /// let p1 = Point::new(10.0, 20.0); /// let p2 = Point::new(125.0, 25.0); /// - /// let closer_to_p1 = Rhumb::point_at_distance_between(p1, p2, 100_000.0); + /// let closer_to_p1 = Rhumb.point_at_distance_between(p1, p2, 100_000.0); /// assert_relative_eq!(closer_to_p1, Point::new(10.96, 20.04), epsilon = 1.0e-2); /// - /// let closer_to_p2 = Rhumb::point_at_distance_between(p1, p2, 10_000_000.0); + /// let closer_to_p2 = Rhumb.point_at_distance_between(p1, p2, 10_000_000.0); /// assert_relative_eq!(closer_to_p2, Point::new(107.00, 24.23), epsilon = 1.0e-2); /// ``` /// /// [rhumb line]: https://en.wikipedia.org/wiki/Rhumb_line - fn point_at_distance_between(start: Point, end: Point, meters_from_start: F) -> Point { - let bearing = Self::bearing(start, end); - Self::destination(start, bearing, meters_from_start) + fn point_at_distance_between( + &self, + start: Point, + end: Point, + meters_from_start: F, + ) -> Point { + let bearing = Self.bearing(start, end); + Self.destination(start, bearing, meters_from_start) } /// Returns a new Point along a [rhumb line] between two existing points. @@ -167,18 +172,23 @@ impl InterpolatePoint for Rhumb { /// let p1 = Point::new(10.0, 20.0); /// let p2 = Point::new(125.0, 25.0); /// - /// let closer_to_p1 = Rhumb::point_at_ratio_between(p1, p2, 0.1); + /// let closer_to_p1 = Rhumb.point_at_ratio_between(p1, p2, 0.1); /// assert_relative_eq!(closer_to_p1, Point::new(21.32, 20.50), epsilon = 1.0e-2); /// - /// let closer_to_p2 = Rhumb::point_at_ratio_between(p1, p2, 0.9); + /// let closer_to_p2 = Rhumb.point_at_ratio_between(p1, p2, 0.9); /// assert_relative_eq!(closer_to_p2, Point::new(113.31, 24.50), epsilon = 1.0e-2); /// - /// let midpoint = Rhumb::point_at_ratio_between(p1, p2, 0.5); + /// let midpoint = Rhumb.point_at_ratio_between(p1, p2, 0.5); /// assert_relative_eq!(midpoint, Point::new(66.98, 22.50), epsilon = 1.0e-2); /// ``` /// /// [rhumb line]: https://en.wikipedia.org/wiki/Rhumb_line - fn point_at_ratio_between(start: Point, end: Point, ratio_from_start: F) -> Point { + fn point_at_ratio_between( + &self, + start: Point, + end: Point, + ratio_from_start: F, + ) -> Point { let calculations = RhumbCalculations::new(&start, &end); calculations.intermediate(ratio_from_start) } @@ -193,6 +203,7 @@ impl InterpolatePoint for Rhumb { /// /// [rhumb line]: https://en.wikipedia.org/wiki/Rhumb_line fn points_along_line( + &self, start: Point, end: Point, max_distance: F, @@ -210,8 +221,6 @@ impl InterpolatePoint for Rhumb { mod tests { use super::*; - type MetricSpace = Rhumb; - mod bearing { use super::*; @@ -219,28 +228,28 @@ mod tests { fn north() { let origin = Point::new(0.0, 0.0); let destination = Point::new(0.0, 1.0); - assert_relative_eq!(0.0, MetricSpace::bearing(origin, destination)); + assert_relative_eq!(0.0, Rhumb.bearing(origin, destination)); } #[test] fn east() { let origin = Point::new(0.0, 0.0); let destination = Point::new(1.0, 0.0); - assert_relative_eq!(90.0, MetricSpace::bearing(origin, destination)); + assert_relative_eq!(90.0, Rhumb.bearing(origin, destination)); } #[test] fn south() { let origin = Point::new(0.0, 0.0); let destination = Point::new(0.0, -1.0); - assert_relative_eq!(180.0, MetricSpace::bearing(origin, destination)); + assert_relative_eq!(180.0, Rhumb.bearing(origin, destination)); } #[test] fn west() { let origin = Point::new(0.0, 0.0); let destination = Point::new(-1.0, 0.0); - assert_relative_eq!(270.0, MetricSpace::bearing(origin, destination)); + assert_relative_eq!(270.0, Rhumb.bearing(origin, destination)); } } @@ -253,7 +262,7 @@ mod tests { let bearing = 0.0; assert_relative_eq!( Point::new(0.0, 0.899320363724538), - MetricSpace::destination(origin, bearing, 100_000.0) + Rhumb.destination(origin, bearing, 100_000.0) ); } @@ -263,7 +272,7 @@ mod tests { let bearing = 90.0; assert_relative_eq!( Point::new(0.8993203637245415, 5.506522912913066e-17), - MetricSpace::destination(origin, bearing, 100_000.0) + Rhumb.destination(origin, bearing, 100_000.0) ); } @@ -273,7 +282,7 @@ mod tests { let bearing = 180.0; assert_relative_eq!( Point::new(0.0, -0.899320363724538), - MetricSpace::destination(origin, bearing, 100_000.0) + Rhumb.destination(origin, bearing, 100_000.0) ); } @@ -283,7 +292,7 @@ mod tests { let bearing = 270.0; assert_relative_eq!( Point::new(-0.8993203637245415, -1.6520247072649334e-16), - MetricSpace::destination(origin, bearing, 100_000.0) + Rhumb.destination(origin, bearing, 100_000.0) ); } } @@ -296,7 +305,7 @@ mod tests { let new_york_city = Point::new(-74.006, 40.7128); let london = Point::new(-0.1278, 51.5074); - let distance: f64 = MetricSpace::distance(new_york_city, london); + let distance: f64 = Rhumb.distance(new_york_city, london); assert_relative_eq!( 5_794_129., // meters @@ -312,7 +321,7 @@ mod tests { fn point_at_ratio_between_midpoint() { let start = Point::new(10.0, 20.0); let end = Point::new(125.0, 25.0); - let midpoint = MetricSpace::point_at_ratio_between(start, end, 0.5); + let midpoint = Rhumb.point_at_ratio_between(start, end, 0.5); assert_relative_eq!( midpoint, Point::new(66.98011173721943, 22.500000000000007), @@ -324,8 +333,9 @@ mod tests { let start = Point::new(10.0, 20.0); let end = Point::new(125.0, 25.0); let max_dist = 1000000.0; // meters - let route = - MetricSpace::points_along_line(start, end, max_dist, true).collect::>(); + let route = Rhumb + .points_along_line(start, end, max_dist, true) + .collect::>(); assert_eq!(route.len(), 13); assert_eq!(route[0], start); assert_eq!(route.last().unwrap(), &end); @@ -340,8 +350,9 @@ mod tests { let start = Point::new(10.0, 20.0); let end = Point::new(125.0, 25.0); let max_dist = 1000000.0; // meters - let route = - MetricSpace::points_along_line(start, end, max_dist, false).collect::>(); + let route = Rhumb + .points_along_line(start, end, max_dist, false) + .collect::>(); assert_eq!(route.len(), 11); assert_relative_eq!( route[0], diff --git a/geo/src/algorithm/linestring_segment.rs b/geo/src/algorithm/linestring_segment.rs index 7ee5cd3e3..ba60ba71b 100644 --- a/geo/src/algorithm/linestring_segment.rs +++ b/geo/src/algorithm/linestring_segment.rs @@ -45,7 +45,7 @@ pub trait LineStringSegmentizeHaversine { } macro_rules! implement_segmentize { - ($trait_name:ident, $method_name:ident, $metric_space:ty) => { + ($trait_name:ident, $method_name:ident, $metric_space:expr) => { impl $trait_name for LineString { fn $method_name(&self, n: usize) -> Option { if (n == usize::MIN) || (n == usize::MAX) { @@ -56,11 +56,11 @@ macro_rules! implement_segmentize { } let mut res_coords: Vec> = Vec::with_capacity(n); - let total_length = self.length::<$metric_space>(); + let total_length = self.length(&$metric_space); let mut cum_length = 0_f64; let segment_prop = (1_f64) / (n as f64); let segment_length = total_length * segment_prop; - let densified = self.densify::<$metric_space>(segment_length - f64::EPSILON); + let densified = self.densify(&$metric_space, segment_length - f64::EPSILON); if densified.lines().count() == n { let linestrings = densified @@ -79,7 +79,7 @@ macro_rules! implement_segmentize { ln_vec.push(segment.start) } - let length = segment.length::<$metric_space>(); + let length = segment.length(&$metric_space); cum_length += length; if (cum_length >= segment_length) && (i != (n_lines - 1)) { @@ -148,10 +148,7 @@ mod test { let segments = linestring.line_segmentize(4).unwrap(); assert_eq!(segments.0.len(), 4); - assert_eq!( - segments.length::(), - linestring.length::() - ); + assert_eq!(segments.length(&Euclidean), linestring.length(&Euclidean)); } #[test] @@ -170,8 +167,8 @@ mod test { let segments = linestring.line_segmentize(5).unwrap(); assert_eq!(segments.0.len(), 5); assert_relative_eq!( - linestring.length::(), - segments.length::(), + linestring.length(&Euclidean), + segments.length(&Euclidean), epsilon = f64::EPSILON ); } @@ -183,8 +180,8 @@ mod test { let segments = linestring.line_segmentize(5).unwrap(); assert_eq!(segments.0.len(), 5); assert_relative_eq!( - linestring.length::(), - segments.length::(), + linestring.length(&Euclidean), + segments.length(&Euclidean), epsilon = f64::EPSILON ); } @@ -221,8 +218,8 @@ mod test { assert_eq!(segments.0.len(), 5); assert_relative_eq!( - linestring.length::(), - segments.length::(), + linestring.length(&Euclidean), + segments.length(&Euclidean), epsilon = f64::EPSILON ); } @@ -253,7 +250,7 @@ mod test { // assert that the lines are equal length let lens = segments .into_iter() - .map(|x| x.length::()) + .map(|x| x.length(&Euclidean)) .collect::>(); let first = lens[0]; @@ -270,8 +267,8 @@ mod test { let segments = linestring.line_segmentize(2).unwrap(); assert_relative_eq!( - linestring.length::(), - segments.length::(), + linestring.length(&Euclidean), + segments.length(&Euclidean), epsilon = f64::EPSILON ) } @@ -329,7 +326,7 @@ mod test { let lens = segments .0 .iter() - .map(|li| li.length::()) + .map(|li| li.length(&Haversine)) .collect::>(); let epsilon = 1e-6; // 6th decimal place which is micrometers @@ -345,7 +342,7 @@ mod test { ] .into(); - assert_relative_eq!(linestring.length::(), 83.3523000093029); + assert_relative_eq!(linestring.length(&Haversine), 83.3523000093029); let n = 8; @@ -353,8 +350,8 @@ mod test { // different at 12th decimal which is a picometer assert_relative_eq!( - linestring.length::(), - segments.length::(), + linestring.length(&Haversine), + segments.length(&Haversine), epsilon = 1e-11 ); } diff --git a/geo/src/algorithm/rhumb/bearing.rs b/geo/src/algorithm/rhumb/bearing.rs index c67c5421d..e4df36b7f 100644 --- a/geo/src/algorithm/rhumb/bearing.rs +++ b/geo/src/algorithm/rhumb/bearing.rs @@ -4,7 +4,7 @@ use crate::{Bearing, CoordFloat, Point, Rhumb}; #[deprecated( since = "0.29.0", - note = "Please use the `Rhumb::bearing` method from the `Bearing` trait instead" + note = "Please use the `Rhumb.bearing` method from the `Bearing` trait instead" )] /// Returns the bearing to another Point in degrees. /// @@ -37,7 +37,7 @@ where T: CoordFloat + FromPrimitive, { fn rhumb_bearing(&self, point: Point) -> T { - Rhumb::bearing(*self, point) + Rhumb.bearing(*self, point) } } diff --git a/geo/src/algorithm/rhumb/destination.rs b/geo/src/algorithm/rhumb/destination.rs index a4e1bdbc1..82b590b52 100644 --- a/geo/src/algorithm/rhumb/destination.rs +++ b/geo/src/algorithm/rhumb/destination.rs @@ -3,7 +3,7 @@ use num_traits::FromPrimitive; #[deprecated( since = "0.29.0", - note = "Please use the `Rhumb::destination` method from the `Destination` trait instead" + note = "Please use the `Rhumb.destination` method from the `Destination` trait instead" )] /// Returns the destination Point having travelled the given distance along a [rhumb line] /// from the origin geometry with the given bearing @@ -41,7 +41,7 @@ where T: CoordFloat + FromPrimitive, { fn rhumb_destination(&self, bearing: T, distance: T) -> Point { - Rhumb::destination(*self, bearing, distance) + Rhumb.destination(*self, bearing, distance) } } diff --git a/geo/src/algorithm/rhumb/distance.rs b/geo/src/algorithm/rhumb/distance.rs index 0032b288a..aece4c148 100644 --- a/geo/src/algorithm/rhumb/distance.rs +++ b/geo/src/algorithm/rhumb/distance.rs @@ -3,7 +3,7 @@ use num_traits::FromPrimitive; #[deprecated( since = "0.29.0", - note = "Please use the `Rhumb::distance` method from the `Distance` trait instead" + note = "Please use the `Rhumb.distance` method from the `Distance` trait instead" )] /// Determine the distance between two geometries along a [rhumb line]. /// @@ -49,7 +49,7 @@ where T: CoordFloat + FromPrimitive, { fn rhumb_distance(&self, rhs: &Point) -> T { - Rhumb::distance(*self, *rhs) + Rhumb.distance(*self, *rhs) } } diff --git a/geo/src/algorithm/rhumb/intermediate.rs b/geo/src/algorithm/rhumb/intermediate.rs index 198be20ab..1924e8019 100644 --- a/geo/src/algorithm/rhumb/intermediate.rs +++ b/geo/src/algorithm/rhumb/intermediate.rs @@ -9,7 +9,7 @@ use num_traits::FromPrimitive; pub trait RhumbIntermediate { #[deprecated( since = "0.29.0", - note = "Please use `Rhumb::point_at_ratio_between` from the `InterpolatePoint` trait instead" + note = "Please use `Rhumb.point_at_ratio_between` from the `InterpolatePoint` trait instead" )] /// Returns a new Point along a [rhumb line] between two existing points. /// @@ -44,7 +44,7 @@ pub trait RhumbIntermediate { #[deprecated( since = "0.29.0", - note = "Please use `Rhumb::points_along_line` from the `InterpolatePoint` trait instead" + note = "Please use `Rhumb.points_along_line` from the `InterpolatePoint` trait instead" )] fn rhumb_intermediate_fill( &self, @@ -60,7 +60,7 @@ where T: CoordFloat + FromPrimitive, { fn rhumb_intermediate(&self, other: &Point, f: T) -> Point { - Rhumb::point_at_ratio_between(*self, *other, f) + Rhumb.point_at_ratio_between(*self, *other, f) } fn rhumb_intermediate_fill( @@ -69,7 +69,9 @@ where max_dist: T, include_ends: bool, ) -> Vec> { - Rhumb::points_along_line(*self, *other, max_dist, include_ends).collect() + Rhumb + .points_along_line(*self, *other, max_dist, include_ends) + .collect() } } diff --git a/geo/src/algorithm/rhumb/length.rs b/geo/src/algorithm/rhumb/length.rs index 2e97021d9..e33d0a954 100644 --- a/geo/src/algorithm/rhumb/length.rs +++ b/geo/src/algorithm/rhumb/length.rs @@ -50,7 +50,7 @@ where T: CoordFloat + FromPrimitive, { fn rhumb_length(&self) -> T { - self.length::() + self.length(&Rhumb) } } @@ -60,7 +60,7 @@ where T: CoordFloat + FromPrimitive, { fn rhumb_length(&self) -> T { - self.length::() + self.length(&Rhumb) } } @@ -70,6 +70,6 @@ where T: CoordFloat + FromPrimitive, { fn rhumb_length(&self) -> T { - self.length::() + self.length(&Rhumb) } } diff --git a/geo/src/algorithm/simplify.rs b/geo/src/algorithm/simplify.rs index 83e9ec41e..e7589043b 100644 --- a/geo/src/algorithm/simplify.rs +++ b/geo/src/algorithm/simplify.rs @@ -97,12 +97,7 @@ where .enumerate() .take(rdp_indices.len() - 1) // Don't include the last index .skip(1) // Don't include the first index - .map(|(index, rdp_index)| { - ( - index, - Euclidean::distance(rdp_index.coord, &first_last_line), - ) - }) + .map(|(index, rdp_index)| (index, Euclidean.distance(rdp_index.coord, &first_last_line))) .fold( (0usize, T::zero()), |(farthest_index, farthest_distance), (index, distance)| { diff --git a/geo/src/algorithm/triangulate_spade.rs b/geo/src/algorithm/triangulate_spade.rs index 87b5c10d1..1aba175db 100644 --- a/geo/src/algorithm/triangulate_spade.rs +++ b/geo/src/algorithm/triangulate_spade.rs @@ -530,12 +530,13 @@ fn snap_or_register_point( .iter() // find closest .min_by(|a, b| { - Euclidean::distance(**a, point) - .partial_cmp(&Euclidean::distance(**b, point)) + Euclidean + .distance(**a, point) + .partial_cmp(&Euclidean.distance(**b, point)) .expect("Couldn't compare coordinate distances") }) // only snap if closest is within epsilon range - .filter(|nearest_point| Euclidean::distance(**nearest_point, point) < snap_radius) + .filter(|nearest_point| Euclidean.distance(**nearest_point, point) < snap_radius) .cloned() // otherwise register and use input point .unwrap_or_else(|| { diff --git a/geo/src/types.rs b/geo/src/types.rs index 33ab22f98..affda72ac 100644 --- a/geo/src/types.rs +++ b/geo/src/types.rs @@ -29,7 +29,7 @@ impl Closest { Closest::SinglePoint(r) => r, }; - if Euclidean::distance(left, p) <= Euclidean::distance(right, p) { + if Euclidean.distance(left, p) <= Euclidean.distance(right, p) { *self } else { *other From 3b03d1a63ffa112de37897b3336743812a2981df Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Fri, 10 Jan 2025 10:52:22 -0800 Subject: [PATCH 2/9] Define Length and Densify on metric space "long ago" `Densify` was defined on the individual geometries: LineString, Line, etc. Recently, these methods were changed to be generic so they could work with Euclidean vs. Haversine, etc. However, to support configurable metric spaces (e.g. Haversine with custom planet radius) a "type" generic is not enough - we need to pass a (potentially configured) metric space as an argument. However, this made the arguments a little awkward: line.densify(&Haversine, 100) Passing `&Haversine` is a little awkward, so this commits switch to instead be: Haversine.densify(&line, 100) Which allows us to seemlessly add support for something like: let mars_radius = 3_389_500 let mars = CustomHaversine::with_radius(mars_radius); mars.densify(&line, 100) --- geo/src/algorithm/centroid.rs | 2 +- geo/src/algorithm/closest_point.rs | 2 +- geo/src/algorithm/concave_hull.rs | 4 +- geo/src/algorithm/densify_haversine.rs | 16 +-- geo/src/algorithm/euclidean_length.rs | 8 +- geo/src/algorithm/geodesic_area.rs | 6 +- geo/src/algorithm/geodesic_length.rs | 8 +- geo/src/algorithm/haversine_length.rs | 8 +- geo/src/algorithm/line_measures/densify.rs | 123 ++++++++++++++++----- geo/src/algorithm/line_measures/length.rs | 63 ++++++++--- geo/src/algorithm/line_measures/mod.rs | 4 +- geo/src/algorithm/linestring_segment.rs | 34 +++--- geo/src/algorithm/rhumb/length.rs | 8 +- 13 files changed, 192 insertions(+), 94 deletions(-) diff --git a/geo/src/algorithm/centroid.rs b/geo/src/algorithm/centroid.rs index ec507d90b..3c50ae342 100644 --- a/geo/src/algorithm/centroid.rs +++ b/geo/src/algorithm/centroid.rs @@ -466,7 +466,7 @@ impl CentroidOperation { match line.dimensions() { ZeroDimensional => self.add_coord(line.start), OneDimensional => { - self.add_centroid(OneDimensional, line.centroid().0, line.length(&Euclidean)) + self.add_centroid(OneDimensional, line.centroid().0, Euclidean.length(line)) } _ => unreachable!("Line must be zero or one dimensional"), } diff --git a/geo/src/algorithm/closest_point.rs b/geo/src/algorithm/closest_point.rs index ffee30ea7..2850805ad 100644 --- a/geo/src/algorithm/closest_point.rs +++ b/geo/src/algorithm/closest_point.rs @@ -52,7 +52,7 @@ impl ClosestPoint for Point { #[allow(clippy::many_single_char_names)] impl ClosestPoint for Line { fn closest_point(&self, p: &Point) -> Closest { - let line_length = self.length(&Euclidean); + let line_length = Euclidean.length(self); if line_length == F::zero() { // if we've got a zero length line, technically the entire line // is the closest point... diff --git a/geo/src/algorithm/concave_hull.rs b/geo/src/algorithm/concave_hull.rs index 4e5c0c868..4418cd36a 100644 --- a/geo/src/algorithm/concave_hull.rs +++ b/geo/src/algorithm/concave_hull.rs @@ -116,7 +116,7 @@ where T: GeoFloat + RTreeNum, { let h = max_dist + max_dist; - let w = line.length(&Euclidean) + h; + let w = Euclidean.length(&line) + h; let two = T::add(T::one(), T::one()); let search_dist = T::div(T::sqrt(T::powi(w, 2) + T::powi(h, 2)), two); let centroid = line.centroid(); @@ -217,7 +217,7 @@ where line_tree.insert(line); } while let Some(line) = line_queue.pop_front() { - let edge_length = line.length(&Euclidean); + let edge_length = Euclidean.length(&line); let dist = edge_length / concavity; let possible_closest_point = find_point_closest_to_line( &interior_points_tree, diff --git a/geo/src/algorithm/densify_haversine.rs b/geo/src/algorithm/densify_haversine.rs index 71b232672..03ad9ff7e 100644 --- a/geo/src/algorithm/densify_haversine.rs +++ b/geo/src/algorithm/densify_haversine.rs @@ -10,7 +10,7 @@ use crate::{ #[deprecated( since = "0.29.0", - note = "Please use the `line.densify(&Haversine)` via the `Densify` trait instead." + note = "Please use the `Haversine.densify(&line)` via the `Densify` trait instead." )] /// Returns a new spherical geometry containing both existing and new interpolated coordinates with /// a maximum distance of `max_distance` between them. @@ -50,7 +50,7 @@ where type Output = MultiPolygon; fn densify_haversine(&self, max_distance: T) -> Self::Output { - self.densify(&Haversine, max_distance) + Haversine.densify(self, max_distance) } } @@ -64,7 +64,7 @@ where type Output = Polygon; fn densify_haversine(&self, max_distance: T) -> Self::Output { - self.densify(&Haversine, max_distance) + Haversine.densify(self, max_distance) } } @@ -78,7 +78,7 @@ where type Output = MultiLineString; fn densify_haversine(&self, max_distance: T) -> Self::Output { - self.densify(&Haversine, max_distance) + Haversine.densify(self, max_distance) } } @@ -92,7 +92,7 @@ where type Output = LineString; fn densify_haversine(&self, max_distance: T) -> Self::Output { - self.densify(&Haversine, max_distance) + Haversine.densify(self, max_distance) } } @@ -106,7 +106,7 @@ where type Output = LineString; fn densify_haversine(&self, max_distance: T) -> Self::Output { - self.densify(&Haversine, max_distance) + Haversine.densify(self, max_distance) } } @@ -120,7 +120,7 @@ where type Output = Polygon; fn densify_haversine(&self, max_distance: T) -> Self::Output { - self.densify(&Haversine, max_distance) + Haversine.densify(self, max_distance) } } @@ -134,7 +134,7 @@ where type Output = Polygon; fn densify_haversine(&self, max_distance: T) -> Self::Output { - self.densify(&Haversine, max_distance) + Haversine.densify(self, max_distance) } } diff --git a/geo/src/algorithm/euclidean_length.rs b/geo/src/algorithm/euclidean_length.rs index aa21a847b..0bbc347cb 100644 --- a/geo/src/algorithm/euclidean_length.rs +++ b/geo/src/algorithm/euclidean_length.rs @@ -5,7 +5,7 @@ use crate::{CoordFloat, Euclidean, Length, Line, LineString, MultiLineString}; /// Calculation of the length #[deprecated( since = "0.29.0", - note = "Please use the `line.length(&Euclidean)` via the `Length` trait instead." + note = "Please use the `Euclidean.length(&line)` via the `Length` trait instead." )] pub trait EuclideanLength { /// Calculation of the length of a Line @@ -35,7 +35,7 @@ where T: CoordFloat, { fn euclidean_length(&self) -> T { - self.length(&Euclidean) + Euclidean.length(self) } } @@ -45,7 +45,7 @@ where T: CoordFloat + Sum, { fn euclidean_length(&self) -> T { - self.length(&Euclidean) + Euclidean.length(self) } } @@ -55,7 +55,7 @@ where T: CoordFloat + Sum, { fn euclidean_length(&self) -> T { - self.length(&Euclidean) + Euclidean.length(self) } } diff --git a/geo/src/algorithm/geodesic_area.rs b/geo/src/algorithm/geodesic_area.rs index ed85f4d28..662a45a20 100644 --- a/geo/src/algorithm/geodesic_area.rs +++ b/geo/src/algorithm/geodesic_area.rs @@ -380,7 +380,7 @@ mod test { // Confirm that the exterior ring geodesic_length is the same as the perimeter assert_relative_eq!( - polygon.exterior().length(&Geodesic), + Geodesic.length(polygon.exterior()), polygon.geodesic_perimeter() ); } @@ -410,7 +410,7 @@ mod test { // Confirm that the exterior ring geodesic_length is the same as the perimeter assert_relative_eq!( - polygon.exterior().length(&Geodesic), + Geodesic.length(polygon.exterior()), polygon.geodesic_perimeter() ); } @@ -440,7 +440,7 @@ mod test { // Confirm that the exterior ring geodesic_length is the same as the perimeter assert_relative_eq!( - polygon.exterior().length(&Geodesic), + Geodesic.length(polygon.exterior()), polygon.geodesic_perimeter() ); } diff --git a/geo/src/algorithm/geodesic_length.rs b/geo/src/algorithm/geodesic_length.rs index 3d54fd0f8..f36576a91 100644 --- a/geo/src/algorithm/geodesic_length.rs +++ b/geo/src/algorithm/geodesic_length.rs @@ -2,7 +2,7 @@ use crate::{Geodesic, Length, Line, LineString, MultiLineString}; #[deprecated( since = "0.29.0", - note = "Please use the `line.length(&Geodesic)` via the `Length` trait instead." + note = "Please use the `Geodesic.length(&line)` via the `Length` trait instead." )] /// Determine the length of a geometry on an ellipsoidal model of the earth. /// @@ -52,20 +52,20 @@ pub trait GeodesicLength { impl GeodesicLength for Line { /// The units of the returned value is meters. fn geodesic_length(&self) -> f64 { - self.length(&Geodesic) + Geodesic.length(self) } } #[allow(deprecated)] impl GeodesicLength for LineString { fn geodesic_length(&self) -> f64 { - self.length(&Geodesic) + Geodesic.length(self) } } #[allow(deprecated)] impl GeodesicLength for MultiLineString { fn geodesic_length(&self) -> f64 { - self.length(&Geodesic) + Geodesic.length(self) } } diff --git a/geo/src/algorithm/haversine_length.rs b/geo/src/algorithm/haversine_length.rs index 1455bfe2a..bac48e593 100644 --- a/geo/src/algorithm/haversine_length.rs +++ b/geo/src/algorithm/haversine_length.rs @@ -5,7 +5,7 @@ use crate::{Haversine, Length}; #[deprecated( since = "0.29.0", - note = "Please use the `line.length(&Haversine)` via the `Length` trait instead." + note = "Please use the `Haversine.length(&line)` via the `Length` trait instead." )] /// Determine the length of a geometry using the [haversine formula]. /// @@ -51,7 +51,7 @@ where T: CoordFloat + FromPrimitive, { fn haversine_length(&self) -> T { - self.length(&Haversine) + Haversine.length(self) } } @@ -61,7 +61,7 @@ where T: CoordFloat + FromPrimitive, { fn haversine_length(&self) -> T { - self.length(&Haversine) + Haversine.length(self) } } @@ -71,6 +71,6 @@ where T: CoordFloat + FromPrimitive, { fn haversine_length(&self) -> T { - self.length(&Haversine) + Haversine.length(self) } } diff --git a/geo/src/algorithm/line_measures/densify.rs b/geo/src/algorithm/line_measures/densify.rs index 3e63cab1e..ca57a65cd 100644 --- a/geo/src/algorithm/line_measures/densify.rs +++ b/geo/src/algorithm/line_measures/densify.rs @@ -22,7 +22,7 @@ use num_traits::FromPrimitive; /// // For Euclidean calculations, the unit of distance is the same as the units /// // of your coordinates. /// let max_dist = 2.0; -/// let densified = line_string.densify(&Euclidean, max_dist); +/// let densified = Euclidean.densify(&line_string, max_dist); /// let expected_output = wkt!(LINESTRING( /// 0.0 0.0, /// 0.0 2.0, @@ -35,7 +35,7 @@ use num_traits::FromPrimitive; /// /// For lng/lat geometries, consider using a different [metric space] like [`Haversine`](crate::Haversine) or [`Geodesic`](crate::Geodesic). /// -/// ``` +///``` /// # use approx::assert_relative_eq; /// use geo::{wkt, Densify}; /// use geo::line_measures::Haversine; @@ -43,7 +43,7 @@ use num_traits::FromPrimitive; /// /// // For Haversine, the unit of distance is in meters /// let max_dist = 200_000.0; -/// let densified = line_string.densify(&Haversine, max_dist); +/// let densified = Haversine.densify(&line_string, max_dist); /// // Haversine interprets coordinate points as lng/lat /// let expected_output = wkt!(LINESTRING( /// 0.0 0.0, @@ -56,9 +56,74 @@ use num_traits::FromPrimitive; /// assert_relative_eq!(densified, expected_output, epsilon = 1e-14); /// ``` /// [metric space]: crate::line_measures::metric_spaces -// TODO: Change this to be a method implemented on the trait? -// And accept a Densifiable (new trait) as argument? pub trait Densify { + fn densify>(&self, geometry: &D, max_segment_length: F) -> D::Output; +} + +impl Densify for MetricSpace +where + F: CoordFloat, + MetricSpace: Distance, Point> + InterpolatePoint, +{ + fn densify>(&self, geometry: &D, max_segment_length: F) -> D::Output { + geometry.densify(self, max_segment_length) + } +} + +/// Creates a copy of the geometry with additional points inserted as necessary to ensure there +/// is never more than `max_segment_length` between points. +/// +/// It's typically more convenient to use the [`Densify`] trait instead of this trait directly. +/// +/// ## Units +/// - `max_segment_length` units depend on the implementing [metric space]. It must be greater than 0. +/// +/// # Examples +/// ``` +/// # use approx::assert_relative_eq; +/// use geo::wkt; +/// use geo::line_measures::{Euclidean, Densifiable}; +/// +/// let line_string = wkt!(LINESTRING(0.0 0.0,0.0 6.0,1.0 7.0)); +/// +/// // For Euclidean calculations, the unit of distance is the same as the units +/// // of your coordinates. +/// let max_dist = 2.0; +/// let densified = line_string.densify(&Euclidean, max_dist); +/// let expected_output = wkt!(LINESTRING( +/// 0.0 0.0, +/// 0.0 2.0, +/// 0.0 4.0, +/// 0.0 6.0, +/// 1.0 7.0 +/// )); +/// assert_relative_eq!(densified, expected_output); +///``` +/// +/// For lng/lat geometries, consider using a different [metric space] like [`Haversine`](crate::Haversine) or [`Geodesic`](crate::Geodesic). +/// +///``` +/// # use approx::assert_relative_eq; +/// use geo::wkt; +/// use geo::line_measures::{Haversine, Densifiable}; +/// let line_string = wkt!(LINESTRING(0.0 0.0,0.0 6.0,1.0 7.0)); +/// +/// // For Haversine, the unit of distance is in meters +/// let max_dist = 200_000.0; +/// let densified = line_string.densify(&Haversine, max_dist); +/// // Haversine interprets coordinate points as lng/lat +/// let expected_output = wkt!(LINESTRING( +/// 0.0 0.0, +/// 0.0 1.5, +/// 0.0 3.0, +/// 0.0 4.5, +/// 0.0 6.0, +/// 1.0 7.0 +/// )); +/// assert_relative_eq!(densified, expected_output, epsilon = 1e-14); +/// ``` +/// [metric space]: crate::line_measures::metric_spaces +pub trait Densifiable { type Output; fn densify( &self, @@ -100,7 +165,7 @@ pub(crate) fn densify_between( } } -impl Densify for Line { +impl Densifiable for Line { type Output = LineString; fn densify( @@ -124,7 +189,7 @@ impl Densify for Line { } } -impl Densify for LineString { +impl Densifiable for LineString { type Output = Self; fn densify( @@ -162,7 +227,7 @@ impl Densify for LineString { } } -impl Densify for MultiLineString { +impl Densifiable for MultiLineString { type Output = Self; fn densify( @@ -181,7 +246,7 @@ impl Densify for MultiLineString { } } -impl Densify for Polygon { +impl Densifiable for Polygon { type Output = Self; fn densify( @@ -202,7 +267,7 @@ impl Densify for Polygon { } } -impl Densify for MultiPolygon { +impl Densifiable for MultiPolygon { type Output = Self; fn densify( @@ -221,7 +286,7 @@ impl Densify for MultiPolygon { } } -impl Densify for Rect { +impl Densifiable for Rect { type Output = Polygon; fn densify( @@ -236,7 +301,7 @@ impl Densify for Rect { } } -impl Densify for Triangle { +impl Densifiable for Triangle { type Output = Polygon; fn densify( @@ -264,13 +329,13 @@ mod tests { coord!(x: 2.3522, y: 48.8566), ); - let densified_line = line.densify(&Geodesic, 100_000.0); // max segment length 100km + let densified_line = Geodesic.densify(&line, 100_000.0); // max segment length 100km assert!(densified_line.coords_count() > 2); - let densified_rhumb = line.densify(&Rhumb, 100_000.0); + let densified_rhumb = Rhumb.densify(&line, 100_000.0); assert!(densified_rhumb.coords_count() > 2); - let densified_haversine = line.densify(&Haversine, 100_000.0); + let densified_haversine = Haversine.densify(&line, 100_000.0); assert!(densified_haversine.coords_count() > 2); } @@ -282,13 +347,13 @@ mod tests { coord!(x: -47.9292, y: -15.7801), // Brasília, Brazil ]); - let densified_ls = line_string.densify(&Geodesic, 500_000.0); // 500 km max segment length + let densified_ls = Geodesic.densify(&line_string, 500_000.0); // 500 km max segment length assert!(densified_ls.coords_count() > line_string.coords_count()); - let densified_rhumb_ls = line_string.densify(&Rhumb, 500_000.0); + let densified_rhumb_ls = Rhumb.densify(&line_string, 500_000.0); assert!(densified_rhumb_ls.coords_count() > line_string.coords_count()); - let densified_haversine_ls = line_string.densify(&Haversine, 500_000.0); + let densified_haversine_ls = Haversine.densify(&line_string, 500_000.0); assert!(densified_haversine_ls.coords_count() > line_string.coords_count()); } @@ -300,7 +365,7 @@ mod tests { (x: -47.9292, y: -15.7801), // Brasília ]; - let densified_polygon = polygon.densify(&Geodesic, 500_000.0); // 500 km max segment length + let densified_polygon = Geodesic.densify(&polygon, 500_000.0); // 500 km max segment length assert!(densified_polygon.exterior().coords_count() > polygon.exterior().coords_count()); } @@ -321,7 +386,7 @@ mod tests { )); let max_dist = 2.0; - let densified = polygon.densify(&Euclidean, max_dist); + let densified = Euclidean.densify(&polygon, max_dist); assert_eq!(densified, expected); } @@ -329,7 +394,7 @@ mod tests { fn test_empty_linestring_densify() { let linestring = LineString::::new(vec![]); let max_dist = 2.0; - let densified = linestring.densify(&Euclidean, max_dist); + let densified = Euclidean.densify(&linestring, max_dist); assert!(densified.0.is_empty()); } @@ -351,7 +416,7 @@ mod tests { 1.0 8.0 )); let max_dist = 2.0; - let densified = linestring.densify(&Euclidean, max_dist); + let densified = Euclidean.densify(&linestring, max_dist); assert_eq!(densified, expected); } @@ -360,7 +425,7 @@ mod tests { let line: Line = Line::new(coord! {x: 0.0, y: 6.0}, coord! {x: 1.0, y: 8.0}); let correct: LineString = vec![[0.0, 6.0], [0.5, 7.0], [1.0, 8.0]].into(); let max_dist = 2.0; - let densified = line.densify(&Euclidean, max_dist); + let densified = Euclidean.densify(&line, max_dist); assert_eq!(densified, correct); } } @@ -394,7 +459,7 @@ mod tests { 4.925 45.804 ))); - let actual_haversine = polygon.densify(&Haversine, 50000.0); + let actual_haversine = Haversine.densify(&polygon, 50000.0); assert_relative_eq!(actual_haversine, exepcted_haversine); let expected_geodesic = wkt!(POLYGON(( @@ -409,7 +474,7 @@ mod tests { 5.355 45.883, 4.925 45.804 ))); - let actual_geodesic = polygon.densify(&Geodesic, 50000.0); + let actual_geodesic = Geodesic.densify(&polygon, 50000.0); assert_relative_eq!(actual_geodesic, expected_geodesic); } @@ -439,7 +504,7 @@ mod tests { -3.1944 55.949 )); - let dense = linestring.densify(&Haversine, 110.0); + let dense = Haversine.densify(&linestring, 110.0); assert_relative_eq!(dense, expected); } @@ -447,7 +512,7 @@ mod tests { fn test_line_densify() { let output = wkt!(LINESTRING(0.0 0.0, 0.0 0.5, 0.0 1.0)); let line = Line::new(coord! {x: 0.0, y: 0.0}, coord! { x: 0.0, y: 1.0 }); - let dense = line.densify(&Haversine, 100000.0); + let dense = Haversine.densify(&line, 100000.0); assert_relative_eq!(dense, output); } } @@ -458,7 +523,7 @@ mod tests { #[test] fn test_empty_linestring() { let input = wkt!(LINESTRING EMPTY); - let dense = input.densify(&Euclidean, 1.0); + let dense = Euclidean.densify(&input, 1.0); assert_eq!(0, dense.coords_count()); assert_eq!(input, dense); } @@ -466,7 +531,7 @@ mod tests { #[test] fn test_one_point_linestring() { let input = wkt!(LINESTRING(1.0 1.0)); - let dense = input.densify(&Euclidean, 1.0); + let dense = Euclidean.densify(&input, 1.0); assert_eq!(1, dense.coords_count()); assert_eq!(input, dense); } diff --git a/geo/src/algorithm/line_measures/length.rs b/geo/src/algorithm/line_measures/length.rs index b240f7859..5657485de 100644 --- a/geo/src/algorithm/line_measures/length.rs +++ b/geo/src/algorithm/line_measures/length.rs @@ -1,7 +1,7 @@ use super::Distance; use crate::{CoordFloat, Line, LineString, MultiLineString, Point}; -/// Calculate the length of a `Line`, `LineString`, or `MultiLineString` in a given [metric space](crate::algorithm::line_measures::metric_spaces). +/// Calculate the length of a `Line`, `LineString`, or `MultiLineString` using a given [metric space](crate::algorithm::line_measures::metric_spaces). /// /// # Examples /// ``` @@ -12,6 +12,33 @@ use crate::{CoordFloat, Line, LineString, MultiLineString, Point}; /// 3.0 4.0, /// 3.0 5.0 /// )); +/// assert_eq!(Euclidean.length(&line_string), 6.); +/// +/// let line_string_lon_lat = geo::wkt!(LINESTRING ( +/// -47.9292 -15.7801f64, +/// -58.4173 -34.6118, +/// -70.6483 -33.4489 +/// )); +/// assert_eq!(Haversine.length(&line_string_lon_lat).round(), 3_474_956.0); +/// ``` +pub trait Length { + fn length(&self, geometry: &impl LengthMeasurable) -> F; +} + +/// Something which can be measured by a [metric space](crate::algorithm::line_measures::metric_spaces), +/// such as a `Line`, `LineString`, or `MultiLineString`. +/// +/// It's typically more convenient to use the [`Length`] trait instead of this trait directly. +/// +/// # Examples +/// ``` +/// use geo::algorithm::line_measures::{LengthMeasurable, Euclidean, Haversine}; +/// +/// let line_string = geo::wkt!(LINESTRING( +/// 0.0 0.0, +/// 3.0 4.0, +/// 3.0 5.0 +/// )); /// assert_eq!(line_string.length(&Euclidean), 6.); /// /// let line_string_lon_lat = geo::wkt!(LINESTRING ( @@ -21,17 +48,23 @@ use crate::{CoordFloat, Line, LineString, MultiLineString, Point}; /// )); /// assert_eq!(line_string_lon_lat.length(&Haversine).round(), 3_474_956.0); /// ``` -pub trait Length { +pub trait LengthMeasurable { fn length(&self, metric_space: &impl Distance, Point>) -> F; } -impl Length for Line { +impl, Point>> Length for PointDistance { + fn length(&self, geometry: &impl LengthMeasurable) -> F { + geometry.length(self) + } +} + +impl LengthMeasurable for Line { fn length(&self, metric_space: &impl Distance, Point>) -> F { metric_space.distance(self.start_point(), self.end_point()) } } -impl Length for LineString { +impl LengthMeasurable for LineString { fn length(&self, metric_space: &impl Distance, Point>) -> F { let mut length = F::zero(); for line in self.lines() { @@ -41,7 +74,7 @@ impl Length for LineString { } } -impl Length for MultiLineString { +impl LengthMeasurable for MultiLineString { fn length(&self, metric_space: &impl Distance, Point>) -> F { let mut length = F::zero(); for line in self { @@ -66,28 +99,28 @@ mod tests { assert_eq!( 343_923., // meters - line.length(&Geodesic).round() + Geodesic.length(&line).round() ); assert_eq!( 341_088., // meters - line.length(&Rhumb).round() + Rhumb.length(&line).round() ); assert_eq!( 343_557., // meters - line.length(&Haversine).round() + Haversine.length(&line).round() ); // computing Euclidean length of an unprojected (lng/lat) line gives a nonsense answer assert_eq!( 4., // nonsense! - line.length(&Euclidean).round() + Euclidean.length(&line).round() ); // london to paris in EPSG:3035 let projected_line = Line::new( coord!(x: 3620451.74f64, y: 3203901.44), coord!(x: 3760771.86, y: 2889484.80), ); - assert_eq!(344_307., projected_line.length(&Euclidean).round()); + assert_eq!(344_307., Euclidean.length(&projected_line).round()); } #[test] @@ -100,21 +133,21 @@ mod tests { assert_eq!( 6_302_220., // meters - line_string.length(&Geodesic).round() + Geodesic.length(&line_string).round() ); assert_eq!( 6_332_790., // meters - line_string.length(&Rhumb).round() + Rhumb.length(&line_string).round() ); assert_eq!( 6_304_387., // meters - line_string.length(&Haversine).round() + Haversine.length(&line_string).round() ); // computing Euclidean length of an unprojected (lng/lat) gives a nonsense answer assert_eq!( 59., // nonsense! - line_string.length(&Euclidean).round() + Euclidean.length(&line_string).round() ); // EPSG:102033 let projected_line_string = LineString::from(vec![ @@ -122,6 +155,6 @@ mod tests { coord!(x: -1797084.08, y: 583528.84), // Lima, Peru coord!(x: 1240052.27, y: 207169.12), // Brasília, Brazil ]); - assert_eq!(6_237_538., projected_line_string.length(&Euclidean).round()); + assert_eq!(6_237_538., Euclidean.length(&projected_line_string).round()); } } diff --git a/geo/src/algorithm/line_measures/mod.rs b/geo/src/algorithm/line_measures/mod.rs index cb9208489..2cee63c47 100644 --- a/geo/src/algorithm/line_measures/mod.rs +++ b/geo/src/algorithm/line_measures/mod.rs @@ -13,10 +13,10 @@ mod interpolate_point; pub use interpolate_point::InterpolatePoint; mod length; -pub use length::Length; +pub use length::{Length, LengthMeasurable}; mod densify; -pub use densify::Densify; +pub use densify::{Densifiable, Densify}; pub mod metric_spaces; pub use metric_spaces::{Euclidean, Geodesic, Haversine, Rhumb}; diff --git a/geo/src/algorithm/linestring_segment.rs b/geo/src/algorithm/linestring_segment.rs index ba60ba71b..4ab85bb38 100644 --- a/geo/src/algorithm/linestring_segment.rs +++ b/geo/src/algorithm/linestring_segment.rs @@ -56,11 +56,11 @@ macro_rules! implement_segmentize { } let mut res_coords: Vec> = Vec::with_capacity(n); - let total_length = self.length(&$metric_space); + let total_length = $metric_space.length(self); let mut cum_length = 0_f64; let segment_prop = (1_f64) / (n as f64); let segment_length = total_length * segment_prop; - let densified = self.densify(&$metric_space, segment_length - f64::EPSILON); + let densified = $metric_space.densify(self, segment_length - f64::EPSILON); if densified.lines().count() == n { let linestrings = densified @@ -79,7 +79,7 @@ macro_rules! implement_segmentize { ln_vec.push(segment.start) } - let length = segment.length(&$metric_space); + let length = $metric_space.length(&segment); cum_length += length; if (cum_length >= segment_length) && (i != (n_lines - 1)) { @@ -148,7 +148,7 @@ mod test { let segments = linestring.line_segmentize(4).unwrap(); assert_eq!(segments.0.len(), 4); - assert_eq!(segments.length(&Euclidean), linestring.length(&Euclidean)); + assert_eq!(Euclidean.length(&segments), Euclidean.length(&linestring)); } #[test] @@ -167,8 +167,8 @@ mod test { let segments = linestring.line_segmentize(5).unwrap(); assert_eq!(segments.0.len(), 5); assert_relative_eq!( - linestring.length(&Euclidean), - segments.length(&Euclidean), + Euclidean.length(&linestring), + Euclidean.length(&segments), epsilon = f64::EPSILON ); } @@ -180,8 +180,8 @@ mod test { let segments = linestring.line_segmentize(5).unwrap(); assert_eq!(segments.0.len(), 5); assert_relative_eq!( - linestring.length(&Euclidean), - segments.length(&Euclidean), + Euclidean.length(&linestring), + Euclidean.length(&segments), epsilon = f64::EPSILON ); } @@ -218,8 +218,8 @@ mod test { assert_eq!(segments.0.len(), 5); assert_relative_eq!( - linestring.length(&Euclidean), - segments.length(&Euclidean), + Euclidean.length(&linestring), + Euclidean.length(&segments), epsilon = f64::EPSILON ); } @@ -250,7 +250,7 @@ mod test { // assert that the lines are equal length let lens = segments .into_iter() - .map(|x| x.length(&Euclidean)) + .map(|x| Euclidean.length(&x)) .collect::>(); let first = lens[0]; @@ -267,8 +267,8 @@ mod test { let segments = linestring.line_segmentize(2).unwrap(); assert_relative_eq!( - linestring.length(&Euclidean), - segments.length(&Euclidean), + Euclidean.length(&linestring), + Euclidean.length(&segments), epsilon = f64::EPSILON ) } @@ -326,7 +326,7 @@ mod test { let lens = segments .0 .iter() - .map(|li| li.length(&Haversine)) + .map(|li| Haversine.length(li)) .collect::>(); let epsilon = 1e-6; // 6th decimal place which is micrometers @@ -342,7 +342,7 @@ mod test { ] .into(); - assert_relative_eq!(linestring.length(&Haversine), 83.3523000093029); + assert_relative_eq!(Haversine.length(&linestring), 83.3523000093029); let n = 8; @@ -350,8 +350,8 @@ mod test { // different at 12th decimal which is a picometer assert_relative_eq!( - linestring.length(&Haversine), - segments.length(&Haversine), + Haversine.length(&linestring), + Haversine.length(&segments), epsilon = 1e-11 ); } diff --git a/geo/src/algorithm/rhumb/length.rs b/geo/src/algorithm/rhumb/length.rs index e33d0a954..73d6b6944 100644 --- a/geo/src/algorithm/rhumb/length.rs +++ b/geo/src/algorithm/rhumb/length.rs @@ -4,7 +4,7 @@ use crate::{CoordFloat, Length, Line, LineString, MultiLineString, Rhumb}; #[deprecated( since = "0.29.0", - note = "Please use the `line.length::()` via the `Length` trait instead." + note = "Please use the `Rhumb.length(&line)` via the `Length` trait instead." )] /// Determine the length of a geometry assuming each segment is a [rhumb line]. /// @@ -50,7 +50,7 @@ where T: CoordFloat + FromPrimitive, { fn rhumb_length(&self) -> T { - self.length(&Rhumb) + Rhumb.length(self) } } @@ -60,7 +60,7 @@ where T: CoordFloat + FromPrimitive, { fn rhumb_length(&self) -> T { - self.length(&Rhumb) + Rhumb.length(self) } } @@ -70,6 +70,6 @@ where T: CoordFloat + FromPrimitive, { fn rhumb_length(&self) -> T { - self.length(&Rhumb) + Rhumb.length(self) } } From 6c2eb7b07a5bc68583af49d8ad18bce7162aa4b9 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Fri, 10 Jan 2025 14:44:27 -0800 Subject: [PATCH 3/9] Customizable radius for Haversine calculations. --- .../line_measures/metric_spaces/haversine.rs | 193 ++++++++++++++++-- .../line_measures/metric_spaces/mod.rs | 2 +- geo/src/algorithm/mod.rs | 2 +- 3 files changed, 182 insertions(+), 15 deletions(-) diff --git a/geo/src/algorithm/line_measures/metric_spaces/haversine.rs b/geo/src/algorithm/line_measures/metric_spaces/haversine.rs index 64f61868c..b48244922 100644 --- a/geo/src/algorithm/line_measures/metric_spaces/haversine.rs +++ b/geo/src/algorithm/line_measures/metric_spaces/haversine.rs @@ -22,6 +22,116 @@ use crate::{CoordFloat, Point, MEAN_EARTH_RADIUS}; /// [great circle]: https://en.wikipedia.org/wiki/Great_circle pub struct Haversine; +/// A spherical model using the [haversine formula]. +/// +/// For earth measurements you probably want the `Haversine` struct. +/// +/// If you are using a non-typical earth radius, or measuring other spheres, you can use this struct. +pub struct CustomHaversine { + radius: f64, +} + +/// A spherical model of the earth using the [haversine formula]. +/// +/// Distances are considered [great circle] lengths and are measured in meters. +/// +/// ⚠️: For normal spherical measurements of the Earth, you probably want [`Haversine`] instead. +/// Use [`CustomHaversine`] only if you need to measure some non-Earth sphere, or want to use a +/// different value for Earth's radius. +/// +/// [haversine formula]: https://en.wikipedia.org/wiki/Haversine_formula// +/// [great circle]: https://en.wikipedia.org/wiki/Great_circle +/// +/// ``` +/// # use approx::assert_relative_eq; +/// use geo::{CustomHaversine, Haversine, Distance}; +/// +/// let start = geo::wkt!(POINT(23.319941 42.698334)); // Sofia: Longitude, Latitude +/// let finish = geo::wkt!(POINT(24.742168 42.136097)); // Plovdiv: Longitude, Latitude +/// +/// // Typically, you can just use `Haversine` for measuring on the Earth's surface +/// assert_relative_eq!( +/// 132433.09929460194, +/// Haversine.distance(start, finish) +/// ); +/// +/// // Beneath the hood, `Haversine` uses the mean radius of the GRS80 ellipsoid. +/// assert_relative_eq!( +/// Haversine.distance(start, finish), +/// CustomHaversine::GRS80_MEAN_RADIUS.distance(start, finish) +/// ); +/// +/// // You may choose to use one of the other well known estimations of the Earth's radius, +/// // which may result in *slightly* different results. +/// assert_relative_eq!( +/// 132433.06564071847, +/// CustomHaversine::GRS80_EQUAL_AREA.distance(start, finish) +/// ); +/// +/// // Or you can specify whatever radius you want to get some "out of this world" results. +/// let mars_sphere = CustomHaversine::new(3_389_500.0); // 👽 Mars radius in meters +/// assert_relative_eq!( +/// 70456.97222377927, +/// mars_sphere.distance(start, finish) +/// ); +/// ``` +impl CustomHaversine { + /// ## Parameters + /// - radius: The radius of the sphere, typically in meters. + pub const fn new(radius: f64) -> Self { + Self { radius } + } + + pub const fn radius(&self) -> f64 { + self.radius + } + + /// A sphere with radius equal to the mean radius of the GRS80 ellipsoid — `R₁`, + /// copied from [Moritz (2000)]. + /// + /// Moritz, H. (2000). Geodetic Reference System 1980. Journal of Geodesy, 74(1), 128–133. doi:10.1007/s001900050278 + /// "Derived Geometric Constants: **R₁: mean radius**" (p131) + /// - + /// - + /// + /// [Moritz (2000)]: https://sci-hub.se/https://doi.org/10.1007/s001900050278 + pub const GRS80_MEAN_RADIUS: Self = Self { + radius: MEAN_EARTH_RADIUS, + }; + + /// A sphere with the same surface area as the GRS80 ellipsoid, having radius `R₂`, + /// copied from [Moritz (2000)]. + /// + /// Moritz, H. (2000). Geodetic Reference System 1980. Journal of Geodesy, 74(1), 128–133. doi:10.1007/s001900050278 + /// "Derived Geometric Constants: **R₂: radius of sphere of same surface**" (p131) + /// - + /// - + /// + /// [Moritz (2000)]: https://sci-hub.se/https://doi.org/10.1007/s001900050278 + pub const GRS80_EQUAL_AREA: Self = Self { + radius: 6_371_007.181_0, + }; + + /// A sphere with the same volume as the GRS80 ellipsoid, having radius `R₃`, + /// copied from [Moritz (2000)]. + /// + /// Moritz, H. (2000). Geodetic Reference System 1980. Journal of Geodesy, 74(1), 128–133. doi:10.1007/s001900050278 + /// "Derived Geometric Constants: **R₃: radius of sphere of same volume**" (p131) + /// - + /// - + /// + /// [Moritz (2000)]: https://sci-hub.se/https://doi.org/10.1007/s001900050278 + pub const GRS80_EQUAL_VOLUME: Self = Self { + radius: 6_371_000.790_0, + }; +} + +impl Default for CustomHaversine { + fn default() -> Self { + Self::GRS80_MEAN_RADIUS + } +} + impl Bearing for Haversine { /// Returns the bearing from `origin` to `destination` in degrees along a [great circle]. /// @@ -50,6 +160,13 @@ impl Bearing for Haversine { /// () /// /// [great circle]: https://en.wikipedia.org/wiki/Great_circle + fn bearing(&self, origin: Point, destination: Point) -> F { + CustomHaversine::default().bearing(origin, destination) + } +} + +impl Bearing for CustomHaversine { + /// Same as [`Haversine.bearing`], but on a sphere with a custom radius. fn bearing(&self, origin: Point, destination: Point) -> F { let three_sixty = F::from(360.0).expect("Numeric type to be constructable from primitive 360"); @@ -93,12 +210,19 @@ impl Destination for Haversine { /// the IUGG](ftp://athena.fsv.cvut.cz/ZFG/grs80-Moritz.pdf) /// /// [great circle]: https://en.wikipedia.org/wiki/Great_circle + fn destination(&self, origin: Point, bearing: F, meters: F) -> Point { + CustomHaversine::default().destination(origin, bearing, meters) + } +} + +impl Destination for CustomHaversine { + /// Same as [`Haversine.destination`], but on a sphere with a custom radius. fn destination(&self, origin: Point, bearing: F, meters: F) -> Point { let center_lng = origin.x().to_radians(); let center_lat = origin.y().to_radians(); let bearing_rad = bearing.to_radians(); - let rad = meters / F::from(MEAN_EARTH_RADIUS).unwrap(); + let rad = meters / F::from(self.radius).unwrap(); let lat = { center_lat.sin() * rad.cos() + center_lat.cos() * rad.sin() * bearing_rad.cos() } @@ -143,6 +267,13 @@ impl Distance, Point> for Haversin /// the IUGG](ftp://athena.fsv.cvut.cz/ZFG/grs80-Moritz.pdf) /// /// [haversine formula]: https://en.wikipedia.org/wiki/Haversine_formula + fn distance(&self, origin: Point, destination: Point) -> F { + CustomHaversine::default().distance(origin, destination) + } +} + +impl Distance, Point> for CustomHaversine { + /// Same as [`Haversine.distance`], but on a sphere with a custom radius. fn distance(&self, origin: Point, destination: Point) -> F { let two = F::one() + F::one(); let theta1 = origin.y().to_radians(); @@ -152,7 +283,7 @@ impl Distance, Point> for Haversin let a = (delta_theta / two).sin().powi(2) + theta1.cos() * theta2.cos() * (delta_lambda / two).sin().powi(2); let c = two * a.sqrt().asin(); - F::from(MEAN_EARTH_RADIUS).unwrap() * c + F::from(self.radius).unwrap() * c } } @@ -186,8 +317,7 @@ impl InterpolatePoint for Haversine { end: Point, meters_from_start: F, ) -> Point { - let bearing = Self.bearing(start, end); - Self.destination(start, bearing, meters_from_start) + CustomHaversine::default().point_at_distance_between(start, end, meters_from_start) } /// Returns a new Point along a [great circle] between two existing points. @@ -219,14 +349,7 @@ impl InterpolatePoint for Haversine { end: Point, ratio_from_start: F, ) -> Point { - if start == end || ratio_from_start == F::zero() { - return start; - } - if ratio_from_start == F::one() { - return end; - } - let calculation = HaversineIntermediateFillCalculation::new(start, end); - calculation.point_at_ratio(ratio_from_start) + CustomHaversine::default().point_at_ratio_between(start, end, ratio_from_start) } /// Interpolates `Point`s along a [great circle] between `start` and `end`. @@ -239,6 +362,50 @@ impl InterpolatePoint for Haversine { /// /// [great circle]: https://en.wikipedia.org/wiki/Great_circle /// [haversine formula]: https://en.wikipedia.org/wiki/Haversine_formula + fn points_along_line( + &self, + start: Point, + end: Point, + max_distance: F, + include_ends: bool, + ) -> impl Iterator> { + CustomHaversine::GRS80_MEAN_RADIUS.points_along_line(start, end, max_distance, include_ends) + } +} + +/// Interpolate Point(s) along a [great circle]. +/// +/// [great circle]: https://en.wikipedia.org/wiki/Great_circle +impl InterpolatePoint for CustomHaversine { + /// Same as [`Haversine.point_at_distance_between`], but on a sphere with a custom radius. + fn point_at_distance_between( + &self, + start: Point, + end: Point, + meters_from_start: F, + ) -> Point { + let bearing = self.bearing(start, end); + self.destination(start, bearing, meters_from_start) + } + + /// Same as [`Haversine.point_at_ratio_between`], but on a sphere with a custom radius. + fn point_at_ratio_between( + &self, + start: Point, + end: Point, + ratio_from_start: F, + ) -> Point { + if start == end || ratio_from_start == F::zero() { + return start; + } + if ratio_from_start == F::one() { + return end; + } + let calculation = HaversineIntermediateFillCalculation::new(start, end); + calculation.point_at_ratio(ratio_from_start) + } + + /// Same as [`Haversine.points_along_line`], but on a sphere with a custom radius. fn points_along_line( &self, start: Point, @@ -249,7 +416,7 @@ impl InterpolatePoint for Haversine { let calculation = HaversineIntermediateFillCalculation::new(start, end); let HaversineIntermediateFillCalculation { d, .. } = calculation; - let total_distance = d * F::from(MEAN_EARTH_RADIUS).unwrap(); + let total_distance = d * F::from(self.radius).unwrap(); if total_distance <= max_distance { return if include_ends { diff --git a/geo/src/algorithm/line_measures/metric_spaces/mod.rs b/geo/src/algorithm/line_measures/metric_spaces/mod.rs index 7a1a153fe..d9c582a33 100644 --- a/geo/src/algorithm/line_measures/metric_spaces/mod.rs +++ b/geo/src/algorithm/line_measures/metric_spaces/mod.rs @@ -5,7 +5,7 @@ mod geodesic; pub use geodesic::Geodesic; mod haversine; -pub use haversine::Haversine; +pub use haversine::{CustomHaversine, Haversine}; mod rhumb; pub use rhumb::Rhumb; diff --git a/geo/src/algorithm/mod.rs b/geo/src/algorithm/mod.rs index beac410b1..05da29174 100644 --- a/geo/src/algorithm/mod.rs +++ b/geo/src/algorithm/mod.rs @@ -187,7 +187,7 @@ pub mod lines_iter; pub use lines_iter::LinesIter; pub mod line_measures; -pub use line_measures::metric_spaces::{Euclidean, Geodesic, Haversine, Rhumb}; +pub use line_measures::metric_spaces::{CustomHaversine, Euclidean, Geodesic, Haversine, Rhumb}; pub use line_measures::{Bearing, Densify, Destination, Distance, InterpolatePoint, Length}; /// Split a LineString into n segments From bb72403e59470b10b908bd353c89fcb7f41908ac Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Fri, 10 Jan 2025 16:47:43 -0800 Subject: [PATCH 4/9] update CHANGES.md --- geo-types/CHANGES.md | 1 - geo/CHANGES.md | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/geo-types/CHANGES.md b/geo-types/CHANGES.md index e0d06f76c..e751f3a0a 100644 --- a/geo-types/CHANGES.md +++ b/geo-types/CHANGES.md @@ -2,7 +2,6 @@ ## Unreleased -- Fix page location of citation for mean earth radius used in Haversine calculations - Implement `RTreeObject` for `Triangle`. - Implement `AsRef` for `Point` and `Coord`. diff --git a/geo/CHANGES.md b/geo/CHANGES.md index 0e06c85f4..be504e05e 100644 --- a/geo/CHANGES.md +++ b/geo/CHANGES.md @@ -2,6 +2,20 @@ ## Unreleased +- BREAKING: `Densify` and `Length` are now defined on the metric space, rather than a generic method on the geometry. + ``` + // before + line_string.length::() + line_string.densify::() + + // after + Euclidean.length(&line_string) + Euclidean.densify(&line_string) + ``` +- Add `CustomHaversine` for doing calculations on spheres with a custom radius. + - +- Fix page location of citation for mean earth radius used in Haversine calculations + - - Add top-level doc link for `InteriorPoint` - Add Unary Union algorithm for fast union ops on adjacent / overlapping geometries - From 5e9c72e866d7523eb87bf286e114a0cbb87a42cc Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Fri, 10 Jan 2025 17:00:09 -0800 Subject: [PATCH 5/9] fixup! Customizable radius for Haversine calculations. I messed up some docs. --- .../line_measures/metric_spaces/haversine.rs | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/geo/src/algorithm/line_measures/metric_spaces/haversine.rs b/geo/src/algorithm/line_measures/metric_spaces/haversine.rs index b48244922..bbf8c3bc7 100644 --- a/geo/src/algorithm/line_measures/metric_spaces/haversine.rs +++ b/geo/src/algorithm/line_measures/metric_spaces/haversine.rs @@ -18,36 +18,26 @@ use crate::{CoordFloat, Point, MEAN_EARTH_RADIUS}; /// - /// - /// +/// If you'd like to use a different radius, see [`CustomHaversine`]. +/// /// [haversine formula]: https://en.wikipedia.org/wiki/Haversine_formula// /// [great circle]: https://en.wikipedia.org/wiki/Great_circle pub struct Haversine; -/// A spherical model using the [haversine formula]. -/// -/// For earth measurements you probably want the `Haversine` struct. -/// -/// If you are using a non-typical earth radius, or measuring other spheres, you can use this struct. -pub struct CustomHaversine { - radius: f64, -} - -/// A spherical model of the earth using the [haversine formula]. +/// A spherical model using the [haversine formula] and a custom radius. /// /// Distances are considered [great circle] lengths and are measured in meters. /// -/// ⚠️: For normal spherical measurements of the Earth, you probably want [`Haversine`] instead. -/// Use [`CustomHaversine`] only if you need to measure some non-Earth sphere, or want to use a -/// different value for Earth's radius. -/// -/// [haversine formula]: https://en.wikipedia.org/wiki/Haversine_formula// -/// [great circle]: https://en.wikipedia.org/wiki/Great_circle +/// ⚠️ For normal spherical measurements of the Earth, you probably want to use [`Haversine`] instead. +/// Use [`CustomHaversine`] only if you need to measure some non-Earth sphere, or if you want to +/// use a different value for Earth's radius. /// /// ``` /// # use approx::assert_relative_eq; -/// use geo::{CustomHaversine, Haversine, Distance}; +/// use geo::{wkt, CustomHaversine, Haversine, Distance}; /// -/// let start = geo::wkt!(POINT(23.319941 42.698334)); // Sofia: Longitude, Latitude -/// let finish = geo::wkt!(POINT(24.742168 42.136097)); // Plovdiv: Longitude, Latitude +/// let start = wkt!(POINT(23.319941 42.698334)); // Sofia: Longitude, Latitude +/// let finish = wkt!(POINT(24.742168 42.136097)); // Plovdiv: Longitude, Latitude /// /// // Typically, you can just use `Haversine` for measuring on the Earth's surface /// assert_relative_eq!( @@ -75,6 +65,15 @@ pub struct CustomHaversine { /// mars_sphere.distance(start, finish) /// ); /// ``` +/// +/// # References +/// +/// [haversine formula]: https://en.wikipedia.org/wiki/Haversine_formula// +/// [great circle]: https://en.wikipedia.org/wiki/Great_circle +pub struct CustomHaversine { + radius: f64, +} + impl CustomHaversine { /// ## Parameters /// - radius: The radius of the sphere, typically in meters. From f544f524ffbd04e5d63ef8163b750193e9086649 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Tue, 14 Jan 2025 11:04:48 -0800 Subject: [PATCH 6/9] Make all Haversine customizable, removing CustomHaversine --- geo/CHANGES.md | 12 +- geo/src/algorithm/cross_track_distance.rs | 14 +- geo/src/algorithm/densify_haversine.rs | 18 +- geo/src/algorithm/haversine_bearing.rs | 2 +- geo/src/algorithm/haversine_closest_point.rs | 22 +- geo/src/algorithm/haversine_destination.rs | 6 +- geo/src/algorithm/haversine_distance.rs | 6 +- geo/src/algorithm/haversine_intermediate.rs | 10 +- geo/src/algorithm/haversine_length.rs | 10 +- geo/src/algorithm/line_measures/densify.rs | 20 +- .../line_measures/interpolate_point.rs | 20 +- geo/src/algorithm/line_measures/length.rs | 14 +- .../line_measures/metric_spaces/haversine.rs | 189 ++++++------------ .../line_measures/metric_spaces/mod.rs | 2 +- geo/src/algorithm/line_measures/mod.rs | 2 +- geo/src/algorithm/linestring_segment.rs | 13 +- geo/src/algorithm/mod.rs | 2 +- 17 files changed, 155 insertions(+), 207 deletions(-) diff --git a/geo/CHANGES.md b/geo/CHANGES.md index 951dfd3cb..bbc25701b 100644 --- a/geo/CHANGES.md +++ b/geo/CHANGES.md @@ -3,7 +3,7 @@ ## Unreleased - BREAKING: `Densify` and `Length` are now defined on the metric space, rather than a generic method on the geometry. - ``` + ```rust // before line_string.length::() line_string.densify::() @@ -12,8 +12,16 @@ Euclidean.length(&line_string) Euclidean.densify(&line_string) ``` -- Add `CustomHaversine` for doing calculations on spheres with a custom radius. +- `Haversine` can be configured with a custom radius for doing calculations on custom sphere. Use `HAVERSINE` for the default earth radius. - + ```rust + // before + Haversine::distance(point1, point2) + + // after + Haversine::new(3_389_500.0).distance(point1, point2) + HAVERSINE.distance(point_1, point_2) + ``` - Docs: Fix page location of citation for mean earth radius used in Haversine calculations - - Docs: Add top-level doc link for `InteriorPoint` diff --git a/geo/src/algorithm/cross_track_distance.rs b/geo/src/algorithm/cross_track_distance.rs index 0aac1f899..37d9da75c 100644 --- a/geo/src/algorithm/cross_track_distance.rs +++ b/geo/src/algorithm/cross_track_distance.rs @@ -1,4 +1,4 @@ -use crate::{Bearing, Distance, Haversine, MEAN_EARTH_RADIUS}; +use crate::{Bearing, Distance, HAVERSINE, MEAN_EARTH_RADIUS}; use geo_types::{CoordFloat, Point}; use num_traits::FromPrimitive; @@ -43,9 +43,9 @@ where { fn cross_track_distance(&self, line_point_a: &Point, line_point_b: &Point) -> T { let mean_earth_radius = T::from(MEAN_EARTH_RADIUS).unwrap(); - let l_delta_13: T = Haversine.distance(*line_point_a, *self) / mean_earth_radius; - let theta_13: T = Haversine.bearing(*line_point_a, *self).to_radians(); - let theta_12: T = Haversine.bearing(*line_point_a, *line_point_b).to_radians(); + let l_delta_13: T = HAVERSINE.distance(*line_point_a, *self) / mean_earth_radius; + let theta_13: T = HAVERSINE.bearing(*line_point_a, *self).to_radians(); + let theta_12: T = HAVERSINE.bearing(*line_point_a, *line_point_b).to_radians(); let l_delta_xt: T = (l_delta_13.sin() * (theta_12 - theta_13).sin()).asin(); mean_earth_radius * l_delta_xt.abs() } @@ -55,7 +55,7 @@ where mod test { use crate::CrossTrackDistance; use crate::Point; - use crate::{Distance, Haversine}; + use crate::{Distance, HAVERSINE}; #[test] fn distance1_test() { @@ -90,13 +90,13 @@ mod test { assert_relative_eq!( p.cross_track_distance(&line_point_a, &line_point_b), - Haversine.distance(p, Point::new(1., 0.)), + HAVERSINE.distance(p, Point::new(1., 0.)), epsilon = 1.0e-6 ); assert_relative_eq!( p.cross_track_distance(&line_point_b, &line_point_a), - Haversine.distance(p, Point::new(1., 0.)), + HAVERSINE.distance(p, Point::new(1., 0.)), epsilon = 1.0e-6 ); } diff --git a/geo/src/algorithm/densify_haversine.rs b/geo/src/algorithm/densify_haversine.rs index 03ad9ff7e..f2689d59d 100644 --- a/geo/src/algorithm/densify_haversine.rs +++ b/geo/src/algorithm/densify_haversine.rs @@ -1,6 +1,6 @@ use num_traits::FromPrimitive; -use crate::line_measures::Haversine; +use crate::line_measures::HAVERSINE; // Densify will soon be deprecated too, so let's just allow deprecated for now #[allow(deprecated)] use crate::HaversineLength; @@ -10,7 +10,7 @@ use crate::{ #[deprecated( since = "0.29.0", - note = "Please use the `Haversine.densify(&line)` via the `Densify` trait instead." + note = "Please use the `HAVERSINE.densify(&line)` via the `Densify` trait instead." )] /// Returns a new spherical geometry containing both existing and new interpolated coordinates with /// a maximum distance of `max_distance` between them. @@ -50,7 +50,7 @@ where type Output = MultiPolygon; fn densify_haversine(&self, max_distance: T) -> Self::Output { - Haversine.densify(self, max_distance) + HAVERSINE.densify(self, max_distance) } } @@ -64,7 +64,7 @@ where type Output = Polygon; fn densify_haversine(&self, max_distance: T) -> Self::Output { - Haversine.densify(self, max_distance) + HAVERSINE.densify(self, max_distance) } } @@ -78,7 +78,7 @@ where type Output = MultiLineString; fn densify_haversine(&self, max_distance: T) -> Self::Output { - Haversine.densify(self, max_distance) + HAVERSINE.densify(self, max_distance) } } @@ -92,7 +92,7 @@ where type Output = LineString; fn densify_haversine(&self, max_distance: T) -> Self::Output { - Haversine.densify(self, max_distance) + HAVERSINE.densify(self, max_distance) } } @@ -106,7 +106,7 @@ where type Output = LineString; fn densify_haversine(&self, max_distance: T) -> Self::Output { - Haversine.densify(self, max_distance) + HAVERSINE.densify(self, max_distance) } } @@ -120,7 +120,7 @@ where type Output = Polygon; fn densify_haversine(&self, max_distance: T) -> Self::Output { - Haversine.densify(self, max_distance) + HAVERSINE.densify(self, max_distance) } } @@ -134,7 +134,7 @@ where type Output = Polygon; fn densify_haversine(&self, max_distance: T) -> Self::Output { - Haversine.densify(self, max_distance) + HAVERSINE.densify(self, max_distance) } } diff --git a/geo/src/algorithm/haversine_bearing.rs b/geo/src/algorithm/haversine_bearing.rs index 3549553a3..23db4056c 100644 --- a/geo/src/algorithm/haversine_bearing.rs +++ b/geo/src/algorithm/haversine_bearing.rs @@ -2,7 +2,7 @@ use crate::{CoordFloat, Point}; #[deprecated( since = "0.29.0", - note = "Please use the `Haversine.bearing` method from the `Bearing` trait instead" + note = "Please use the `HAVERSINE.bearing` method from the `Bearing` trait instead" )] /// Returns the bearing to another Point in degrees. /// diff --git a/geo/src/algorithm/haversine_closest_point.rs b/geo/src/algorithm/haversine_closest_point.rs index 60b7fa31d..e1afb145f 100644 --- a/geo/src/algorithm/haversine_closest_point.rs +++ b/geo/src/algorithm/haversine_closest_point.rs @@ -1,4 +1,4 @@ -use crate::line_measures::{Bearing, Destination, Distance, Haversine}; +use crate::line_measures::{Bearing, Destination, Distance, HAVERSINE}; use crate::{Closest, Contains}; use crate::{CoordsIter, GeoFloat, Point, MEAN_EARTH_RADIUS}; use geo_types::{ @@ -92,7 +92,7 @@ where } // This can probably be done cheaper - let d3 = Haversine.distance(p2, p1); + let d3 = HAVERSINE.distance(p2, p1); if d3 <= T::epsilon() { // I think here it should be return Closest::SinglePoint(p1) // If the line segment is degenerated to a point, that point is still the closest @@ -102,18 +102,18 @@ where } let pi = T::from(std::f64::consts::PI).unwrap(); - let crs_ad = Haversine.bearing(p1, *from).to_radians(); - let crs_ab = Haversine.bearing(p1, p2).to_radians(); + let crs_ad = HAVERSINE.bearing(p1, *from).to_radians(); + let crs_ab = HAVERSINE.bearing(p1, p2).to_radians(); let crs_ba = if crs_ab > T::zero() { crs_ab - pi } else { crs_ab + pi }; - let crs_bd = Haversine.bearing(p2, *from).to_radians(); + let crs_bd = HAVERSINE.bearing(p2, *from).to_radians(); let d_crs1 = crs_ad - crs_ab; let d_crs2 = crs_bd - crs_ba; - let d1 = Haversine.distance(p1, *from); + let d1 = HAVERSINE.distance(p1, *from); // d1, d2, d3 are in principle not needed, only the sign matters let projection1 = d_crs1.cos(); @@ -127,13 +127,13 @@ where if xtd < T::epsilon() { return Closest::Intersection(*from); } else { - return Closest::SinglePoint(Haversine.destination(p1, crs_ab.to_degrees(), atd)); + return Closest::SinglePoint(HAVERSINE.destination(p1, crs_ab.to_degrees(), atd)); } } // Projected falls outside the GC Arc // Return shortest distance pt, project either on point sp1 or sp2 - let d2 = Haversine.distance(p2, *from); + let d2 = HAVERSINE.distance(p2, *from); if d1 < d2 { return Closest::SinglePoint(p1); } @@ -166,7 +166,7 @@ where return intersect; } Closest::SinglePoint(pt) => { - let dist = Haversine.distance(pt, *from); + let dist = HAVERSINE.distance(pt, *from); if dist < min_distance { min_distance = dist; rv = Closest::SinglePoint(pt); @@ -198,7 +198,7 @@ where return (intersect, T::zero()); } Closest::SinglePoint(pt) => { - let dist = Haversine.distance(pt, *from); + let dist = HAVERSINE.distance(pt, *from); if dist < min_distance { min_distance = dist; rv = Closest::SinglePoint(pt); @@ -301,7 +301,7 @@ where // This mean on top of the line. Closest::Intersection(pt) => return Closest::Intersection(pt), Closest::SinglePoint(pt) => { - let dist = Haversine.distance(pt, *from); + let dist = HAVERSINE.distance(pt, *from); if dist < min_distance { min_distance = dist; rv = Closest::SinglePoint(pt); diff --git a/geo/src/algorithm/haversine_destination.rs b/geo/src/algorithm/haversine_destination.rs index f915569d0..fd48eeee2 100644 --- a/geo/src/algorithm/haversine_destination.rs +++ b/geo/src/algorithm/haversine_destination.rs @@ -1,9 +1,9 @@ -use crate::{CoordFloat, Destination, Haversine, Point}; +use crate::{CoordFloat, Destination, Point, HAVERSINE}; use num_traits::FromPrimitive; #[deprecated( since = "0.29.0", - note = "Please use the `Haversine.destination` method from the `Destination` trait instead" + note = "Please use the `HAVERSINE.destination` method from the `Destination` trait instead" )] /// Returns a new Point using the distance to the existing Point and a bearing for the direction /// @@ -39,7 +39,7 @@ where T: CoordFloat + FromPrimitive, { fn haversine_destination(&self, bearing: T, distance: T) -> Point { - Haversine.destination(*self, bearing, distance) + HAVERSINE.destination(*self, bearing, distance) } } diff --git a/geo/src/algorithm/haversine_distance.rs b/geo/src/algorithm/haversine_distance.rs index df1cf7a40..9ccc3f890 100644 --- a/geo/src/algorithm/haversine_distance.rs +++ b/geo/src/algorithm/haversine_distance.rs @@ -1,9 +1,9 @@ -use crate::{CoordFloat, Distance, Haversine, Point}; +use crate::{CoordFloat, Distance, Point, HAVERSINE}; use num_traits::FromPrimitive; #[deprecated( since = "0.29.0", - note = "Please use the `Haversine.distance` method from the `Distance` trait instead" + note = "Please use the `HAVERSINE.distance` method from the `Distance` trait instead" )] /// Determine the distance between two geometries using the [haversine formula]. /// @@ -55,7 +55,7 @@ where T: CoordFloat + FromPrimitive, { fn haversine_distance(&self, rhs: &Point) -> T { - Haversine.distance(*self, *rhs) + HAVERSINE.distance(*self, *rhs) } } diff --git a/geo/src/algorithm/haversine_intermediate.rs b/geo/src/algorithm/haversine_intermediate.rs index fce21e8c2..f2410752b 100644 --- a/geo/src/algorithm/haversine_intermediate.rs +++ b/geo/src/algorithm/haversine_intermediate.rs @@ -1,4 +1,4 @@ -use crate::{CoordFloat, Haversine, InterpolatePoint, Point}; +use crate::{CoordFloat, InterpolatePoint, Point, HAVERSINE}; use num_traits::FromPrimitive; #[deprecated( @@ -9,7 +9,7 @@ use num_traits::FromPrimitive; pub trait HaversineIntermediate { #[deprecated( since = "0.29.0", - note = "Please use `Haversine.point_at_ratio_between` from the `InterpolatePoint` trait instead" + note = "Please use `HAVERSINE.point_at_ratio_between` from the `InterpolatePoint` trait instead" )] /// Returns a new `Point` along a great circle route between `self` and `other`. /// @@ -40,7 +40,7 @@ pub trait HaversineIntermediate { #[deprecated( since = "0.29.0", - note = "Please use `Haversine.points_along_line` from the `InterpolatePoint` trait instead" + note = "Please use `HAVERSINE.points_along_line` from the `InterpolatePoint` trait instead" )] /// Interpolates `Point`s along a great circle route between self and `other`. /// @@ -62,7 +62,7 @@ where T: CoordFloat + FromPrimitive, { fn haversine_intermediate(&self, other: &Point, ratio: T) -> Point { - Haversine.point_at_ratio_between(*self, *other, ratio) + HAVERSINE.point_at_ratio_between(*self, *other, ratio) } fn haversine_intermediate_fill( @@ -71,7 +71,7 @@ where max_dist: T, include_ends: bool, ) -> Vec> { - Haversine + HAVERSINE .points_along_line(*self, *other, max_dist, include_ends) .collect() } diff --git a/geo/src/algorithm/haversine_length.rs b/geo/src/algorithm/haversine_length.rs index bac48e593..e999a25c5 100644 --- a/geo/src/algorithm/haversine_length.rs +++ b/geo/src/algorithm/haversine_length.rs @@ -1,11 +1,11 @@ use num_traits::FromPrimitive; use crate::{CoordFloat, Line, LineString, MultiLineString}; -use crate::{Haversine, Length}; +use crate::{Length, HAVERSINE}; #[deprecated( since = "0.29.0", - note = "Please use the `Haversine.length(&line)` via the `Length` trait instead." + note = "Please use the `HAVERSINE.length(&line)` via the `Length` trait instead." )] /// Determine the length of a geometry using the [haversine formula]. /// @@ -51,7 +51,7 @@ where T: CoordFloat + FromPrimitive, { fn haversine_length(&self) -> T { - Haversine.length(self) + HAVERSINE.length(self) } } @@ -61,7 +61,7 @@ where T: CoordFloat + FromPrimitive, { fn haversine_length(&self) -> T { - Haversine.length(self) + HAVERSINE.length(self) } } @@ -71,6 +71,6 @@ where T: CoordFloat + FromPrimitive, { fn haversine_length(&self) -> T { - Haversine.length(self) + HAVERSINE.length(self) } } diff --git a/geo/src/algorithm/line_measures/densify.rs b/geo/src/algorithm/line_measures/densify.rs index ca57a65cd..cc10f5ddd 100644 --- a/geo/src/algorithm/line_measures/densify.rs +++ b/geo/src/algorithm/line_measures/densify.rs @@ -38,12 +38,12 @@ use num_traits::FromPrimitive; ///``` /// # use approx::assert_relative_eq; /// use geo::{wkt, Densify}; -/// use geo::line_measures::Haversine; +/// use geo::line_measures::HAVERSINE; /// let line_string = wkt!(LINESTRING(0.0 0.0,0.0 6.0,1.0 7.0)); /// /// // For Haversine, the unit of distance is in meters /// let max_dist = 200_000.0; -/// let densified = Haversine.densify(&line_string, max_dist); +/// let densified = HAVERSINE.densify(&line_string, max_dist); /// // Haversine interprets coordinate points as lng/lat /// let expected_output = wkt!(LINESTRING( /// 0.0 0.0, @@ -105,12 +105,12 @@ where ///``` /// # use approx::assert_relative_eq; /// use geo::wkt; -/// use geo::line_measures::{Haversine, Densifiable}; +/// use geo::line_measures::{HAVERSINE, Densifiable}; /// let line_string = wkt!(LINESTRING(0.0 0.0,0.0 6.0,1.0 7.0)); /// /// // For Haversine, the unit of distance is in meters /// let max_dist = 200_000.0; -/// let densified = line_string.densify(&Haversine, max_dist); +/// let densified = line_string.densify(&HAVERSINE, max_dist); /// // Haversine interprets coordinate points as lng/lat /// let expected_output = wkt!(LINESTRING( /// 0.0 0.0, @@ -319,7 +319,7 @@ impl Densifiable for Triangle { #[cfg(test)] mod tests { use super::*; - use crate::{coord, polygon, wkt, Euclidean, Geodesic, Haversine, Rhumb}; + use crate::{coord, polygon, wkt, Euclidean, Geodesic, Rhumb, HAVERSINE}; #[test] fn densify_line() { @@ -335,7 +335,7 @@ mod tests { let densified_rhumb = Rhumb.densify(&line, 100_000.0); assert!(densified_rhumb.coords_count() > 2); - let densified_haversine = Haversine.densify(&line, 100_000.0); + let densified_haversine = HAVERSINE.densify(&line, 100_000.0); assert!(densified_haversine.coords_count() > 2); } @@ -353,7 +353,7 @@ mod tests { let densified_rhumb_ls = Rhumb.densify(&line_string, 500_000.0); assert!(densified_rhumb_ls.coords_count() > line_string.coords_count()); - let densified_haversine_ls = Haversine.densify(&line_string, 500_000.0); + let densified_haversine_ls = HAVERSINE.densify(&line_string, 500_000.0); assert!(densified_haversine_ls.coords_count() > line_string.coords_count()); } @@ -459,7 +459,7 @@ mod tests { 4.925 45.804 ))); - let actual_haversine = Haversine.densify(&polygon, 50000.0); + let actual_haversine = HAVERSINE.densify(&polygon, 50000.0); assert_relative_eq!(actual_haversine, exepcted_haversine); let expected_geodesic = wkt!(POLYGON(( @@ -504,7 +504,7 @@ mod tests { -3.1944 55.949 )); - let dense = Haversine.densify(&linestring, 110.0); + let dense = HAVERSINE.densify(&linestring, 110.0); assert_relative_eq!(dense, expected); } @@ -512,7 +512,7 @@ mod tests { fn test_line_densify() { let output = wkt!(LINESTRING(0.0 0.0, 0.0 0.5, 0.0 1.0)); let line = Line::new(coord! {x: 0.0, y: 0.0}, coord! { x: 0.0, y: 1.0 }); - let dense = Haversine.densify(&line, 100000.0); + let dense = HAVERSINE.densify(&line, 100000.0); assert_relative_eq!(dense, output); } } diff --git a/geo/src/algorithm/line_measures/interpolate_point.rs b/geo/src/algorithm/line_measures/interpolate_point.rs index c60b8c6ba..6e0d1d5f4 100644 --- a/geo/src/algorithm/line_measures/interpolate_point.rs +++ b/geo/src/algorithm/line_measures/interpolate_point.rs @@ -42,7 +42,7 @@ pub trait InterpolatePoint { #[cfg(test)] mod tests { - use crate::{Euclidean, Geodesic, Haversine, InterpolatePoint, Point, Rhumb}; + use crate::{Euclidean, Geodesic, InterpolatePoint, Point, Rhumb, HAVERSINE}; #[test] fn point_at_ratio_between_line_ends() { @@ -50,13 +50,13 @@ mod tests { let end = Point::new(1.0, 1.0); let ratio = 0.0; - assert_eq!(Haversine.point_at_ratio_between(start, end, ratio), start); + assert_eq!(HAVERSINE.point_at_ratio_between(start, end, ratio), start); assert_eq!(Euclidean.point_at_ratio_between(start, end, ratio), start); assert_eq!(Geodesic.point_at_ratio_between(start, end, ratio), start); assert_eq!(Rhumb.point_at_ratio_between(start, end, ratio), start); let ratio = 1.0; - assert_eq!(Haversine.point_at_ratio_between(start, end, ratio), end); + assert_eq!(HAVERSINE.point_at_ratio_between(start, end, ratio), end); assert_eq!(Euclidean.point_at_ratio_between(start, end, ratio), end); assert_eq!(Geodesic.point_at_ratio_between(start, end, ratio), end); assert_eq!(Rhumb.point_at_ratio_between(start, end, ratio), end); @@ -70,19 +70,19 @@ mod tests { let start = Point::new(1.0, 1.0); let ratio = 0.0; - assert_eq!(Haversine.point_at_ratio_between(start, start, ratio), start); + assert_eq!(HAVERSINE.point_at_ratio_between(start, start, ratio), start); assert_eq!(Euclidean.point_at_ratio_between(start, start, ratio), start); assert_eq!(Geodesic.point_at_ratio_between(start, start, ratio), start); assert_eq!(Rhumb.point_at_ratio_between(start, start, ratio), start); let ratio = 0.5; - assert_eq!(Haversine.point_at_ratio_between(start, start, ratio), start); + assert_eq!(HAVERSINE.point_at_ratio_between(start, start, ratio), start); assert_eq!(Euclidean.point_at_ratio_between(start, start, ratio), start); assert_eq!(Geodesic.point_at_ratio_between(start, start, ratio), start); assert_eq!(Rhumb.point_at_ratio_between(start, start, ratio), start); let ratio = 1.0; - assert_eq!(Haversine.point_at_ratio_between(start, start, ratio), start); + assert_eq!(HAVERSINE.point_at_ratio_between(start, start, ratio), start); assert_eq!(Euclidean.point_at_ratio_between(start, start, ratio), start); assert_eq!(Geodesic.point_at_ratio_between(start, start, ratio), start); assert_eq!(Rhumb.point_at_ratio_between(start, start, ratio), start); @@ -96,7 +96,7 @@ mod tests { let distance = 0.0; assert_eq!( - Haversine.point_at_distance_between(start, start, distance), + HAVERSINE.point_at_distance_between(start, start, distance), start ); @@ -116,7 +116,7 @@ mod tests { let due_north = Point::new(1.0, 1.9); let due_south = Point::new(1.0, 0.1); assert_relative_eq!( - Haversine.point_at_distance_between(start, start, distance), + HAVERSINE.point_at_distance_between(start, start, distance), due_north, epsilon = 1.0e-1 ); @@ -142,7 +142,7 @@ mod tests { let max_distance = 1.0; let include_ends = true; - let points: Vec<_> = Haversine + let points: Vec<_> = HAVERSINE .points_along_line(start, start, max_distance, include_ends) .collect(); assert_eq!(points, vec![start, start]); @@ -163,7 +163,7 @@ mod tests { assert_eq!(points, vec![start, start]); let include_ends = false; - let points: Vec<_> = Haversine + let points: Vec<_> = HAVERSINE .points_along_line(start, start, max_distance, include_ends) .collect(); assert_eq!(points, vec![]); diff --git a/geo/src/algorithm/line_measures/length.rs b/geo/src/algorithm/line_measures/length.rs index 5657485de..f64c67b31 100644 --- a/geo/src/algorithm/line_measures/length.rs +++ b/geo/src/algorithm/line_measures/length.rs @@ -5,7 +5,7 @@ use crate::{CoordFloat, Line, LineString, MultiLineString, Point}; /// /// # Examples /// ``` -/// use geo::algorithm::line_measures::{Length, Euclidean, Haversine}; +/// use geo::algorithm::line_measures::{Length, Euclidean, HAVERSINE}; /// /// let line_string = geo::wkt!(LINESTRING( /// 0.0 0.0, @@ -19,7 +19,7 @@ use crate::{CoordFloat, Line, LineString, MultiLineString, Point}; /// -58.4173 -34.6118, /// -70.6483 -33.4489 /// )); -/// assert_eq!(Haversine.length(&line_string_lon_lat).round(), 3_474_956.0); +/// assert_eq!(HAVERSINE.length(&line_string_lon_lat).round(), 3_474_956.0); /// ``` pub trait Length { fn length(&self, geometry: &impl LengthMeasurable) -> F; @@ -32,7 +32,7 @@ pub trait Length { /// /// # Examples /// ``` -/// use geo::algorithm::line_measures::{LengthMeasurable, Euclidean, Haversine}; +/// use geo::algorithm::line_measures::{LengthMeasurable, Euclidean, HAVERSINE}; /// /// let line_string = geo::wkt!(LINESTRING( /// 0.0 0.0, @@ -46,7 +46,7 @@ pub trait Length { /// -58.4173 -34.6118, /// -70.6483 -33.4489 /// )); -/// assert_eq!(line_string_lon_lat.length(&Haversine).round(), 3_474_956.0); +/// assert_eq!(line_string_lon_lat.length(&HAVERSINE).round(), 3_474_956.0); /// ``` pub trait LengthMeasurable { fn length(&self, metric_space: &impl Distance, Point>) -> F; @@ -87,7 +87,7 @@ impl LengthMeasurable for MultiLineString { #[cfg(test)] mod tests { use super::*; - use crate::{coord, Euclidean, Geodesic, Haversine, Rhumb}; + use crate::{coord, Euclidean, Geodesic, Rhumb, HAVERSINE}; #[test] fn lines() { @@ -107,7 +107,7 @@ mod tests { ); assert_eq!( 343_557., // meters - Haversine.length(&line).round() + HAVERSINE.length(&line).round() ); // computing Euclidean length of an unprojected (lng/lat) line gives a nonsense answer @@ -141,7 +141,7 @@ mod tests { ); assert_eq!( 6_304_387., // meters - Haversine.length(&line_string).round() + HAVERSINE.length(&line_string).round() ); // computing Euclidean length of an unprojected (lng/lat) gives a nonsense answer diff --git a/geo/src/algorithm/line_measures/metric_spaces/haversine.rs b/geo/src/algorithm/line_measures/metric_spaces/haversine.rs index bbf8c3bc7..97331f906 100644 --- a/geo/src/algorithm/line_measures/metric_spaces/haversine.rs +++ b/geo/src/algorithm/line_measures/metric_spaces/haversine.rs @@ -8,58 +8,40 @@ use crate::{CoordFloat, Point, MEAN_EARTH_RADIUS}; /// /// Distances are considered [great circle] lengths and are measured in meters. /// -/// # References -/// -/// *Note*: this implementation uses a mean earth radius of 6371.0088 km (6_371_008.7714 m), based on the recommendation of -/// the IUGG: -/// -/// Moritz, H. (2000). Geodetic Reference System 1980. Journal of Geodesy, 74(1), 128–133. doi:10.1007/s001900050278 -/// "Derived Geometric Constants: **R1: mean radius**" (p131) -/// - -/// - -/// -/// If you'd like to use a different radius, see [`CustomHaversine`]. -/// -/// [haversine formula]: https://en.wikipedia.org/wiki/Haversine_formula// -/// [great circle]: https://en.wikipedia.org/wiki/Great_circle -pub struct Haversine; - -/// A spherical model using the [haversine formula] and a custom radius. -/// -/// Distances are considered [great circle] lengths and are measured in meters. +/// You may specify a custom radius for the Earth (or other sphere), but for normal spherical +/// measurements of the Earth, you probably want to just use [`HAVERSINE`] which uses the +/// earth radius of 6371.0088 km (6_371_008.7714 m), based on the recommendation of the IUGG. /// -/// ⚠️ For normal spherical measurements of the Earth, you probably want to use [`Haversine`] instead. -/// Use [`CustomHaversine`] only if you need to measure some non-Earth sphere, or if you want to -/// use a different value for Earth's radius. +/// # Examples /// /// ``` /// # use approx::assert_relative_eq; -/// use geo::{wkt, CustomHaversine, Haversine, Distance}; +/// use geo::{wkt, Haversine, HAVERSINE, Distance}; /// /// let start = wkt!(POINT(23.319941 42.698334)); // Sofia: Longitude, Latitude /// let finish = wkt!(POINT(24.742168 42.136097)); // Plovdiv: Longitude, Latitude /// -/// // Typically, you can just use `Haversine` for measuring on the Earth's surface +/// // Typically, you can just use `HAVERSINE` for measuring on the Earth's surface /// assert_relative_eq!( /// 132433.09929460194, -/// Haversine.distance(start, finish) +/// HAVERSINE.distance(start, finish) /// ); /// -/// // Beneath the hood, `Haversine` uses the mean radius of the GRS80 ellipsoid. +/// // `HAVERSINE` has a radius equal to the mean radius of the GRS80 ellipsoid. /// assert_relative_eq!( -/// Haversine.distance(start, finish), -/// CustomHaversine::GRS80_MEAN_RADIUS.distance(start, finish) +/// HAVERSINE.distance(start, finish), +/// Haversine::GRS80_MEAN_RADIUS.distance(start, finish) /// ); /// /// // You may choose to use one of the other well known estimations of the Earth's radius, /// // which may result in *slightly* different results. /// assert_relative_eq!( /// 132433.06564071847, -/// CustomHaversine::GRS80_EQUAL_AREA.distance(start, finish) +/// Haversine::GRS80_EQUAL_AREA.distance(start, finish) /// ); /// /// // Or you can specify whatever radius you want to get some "out of this world" results. -/// let mars_sphere = CustomHaversine::new(3_389_500.0); // 👽 Mars radius in meters +/// let mars_sphere = Haversine::new(3_389_500.0); // 👽 Mars radius in meters /// assert_relative_eq!( /// 70456.97222377927, /// mars_sphere.distance(start, finish) @@ -68,13 +50,27 @@ pub struct Haversine; /// /// # References /// +/// Moritz, H. (2000). Geodetic Reference System 1980. Journal of Geodesy, 74(1), 128–133. doi:10.1007/s001900050278 +/// "Derived Geometric Constants: **R1: mean radius**" (p131) +/// - +/// - +/// /// [haversine formula]: https://en.wikipedia.org/wiki/Haversine_formula// /// [great circle]: https://en.wikipedia.org/wiki/Great_circle -pub struct CustomHaversine { +pub struct Haversine { radius: f64, } -impl CustomHaversine { +impl Default for Haversine { + fn default() -> Self { + Haversine::GRS80_MEAN_RADIUS + } +} + +/// A spherical model of the earth using the [haversine formula], and the GRS80 Mean Earth Radius of 6371008.8 meters. +pub const HAVERSINE: Haversine = Haversine::GRS80_MEAN_RADIUS; + +impl Haversine { /// ## Parameters /// - radius: The radius of the sphere, typically in meters. pub const fn new(radius: f64) -> Self { @@ -125,7 +121,7 @@ impl CustomHaversine { }; } -impl Default for CustomHaversine { +impl Default for Haversine { fn default() -> Self { Self::GRS80_MEAN_RADIUS } @@ -143,12 +139,12 @@ impl Bearing for Haversine { /// /// ``` /// # use approx::assert_relative_eq; - /// use geo::{Haversine, Bearing}; + /// use geo::{HAVERSINE, Bearing}; /// use geo::Point; /// /// let origin = Point::new(9.0, 10.0); /// let destination = Point::new(9.5, 10.1); - /// let bearing = Haversine.bearing(origin, destination); + /// let bearing = HAVERSINE.bearing(origin, destination); /// // A little north of east /// assert_relative_eq!(bearing, 78.47, epsilon = 1.0e-2); /// ``` @@ -159,13 +155,6 @@ impl Bearing for Haversine { /// () /// /// [great circle]: https://en.wikipedia.org/wiki/Great_circle - fn bearing(&self, origin: Point, destination: Point) -> F { - CustomHaversine::default().bearing(origin, destination) - } -} - -impl Bearing for CustomHaversine { - /// Same as [`Haversine.bearing`], but on a sphere with a custom radius. fn bearing(&self, origin: Point, destination: Point) -> F { let three_sixty = F::from(360.0).expect("Numeric type to be constructable from primitive 360"); @@ -195,11 +184,11 @@ impl Destination for Haversine { /// /// ``` /// # use approx::assert_relative_eq; - /// use geo::{Haversine, Destination}; + /// use geo::{HAVERSINE, Destination}; /// use geo::Point; /// /// let origin = Point::new(9.177789688110352, 48.776781529534965); - /// let destination = Haversine.destination(origin, 45., 10000.); + /// let destination = HAVERSINE.destination(origin, 45., 10000.); /// assert_relative_eq!(Point::new(9.274409949623532, 48.84033274015048), destination); /// ``` /// @@ -209,13 +198,6 @@ impl Destination for Haversine { /// the IUGG](ftp://athena.fsv.cvut.cz/ZFG/grs80-Moritz.pdf) /// /// [great circle]: https://en.wikipedia.org/wiki/Great_circle - fn destination(&self, origin: Point, bearing: F, meters: F) -> Point { - CustomHaversine::default().destination(origin, bearing, meters) - } -} - -impl Destination for CustomHaversine { - /// Same as [`Haversine.destination`], but on a sphere with a custom radius. fn destination(&self, origin: Point, bearing: F, meters: F) -> Point { let center_lng = origin.x().to_radians(); let center_lat = origin.y().to_radians(); @@ -246,13 +228,13 @@ impl Distance, Point> for Haversin /// /// ``` /// # use approx::assert_relative_eq; - /// use geo::{Haversine, Distance}; + /// use geo::{HAVERSINE, Distance}; /// use geo::Point; /// /// let new_york_city = Point::new(-74.006f64, 40.7128f64); /// let london = Point::new(-0.1278f64, 51.5074f64); /// - /// let distance = Haversine.distance(new_york_city, london); + /// let distance = HAVERSINE.distance(new_york_city, london); /// /// assert_relative_eq!( /// 5_570_230., // meters @@ -266,13 +248,6 @@ impl Distance, Point> for Haversin /// the IUGG](ftp://athena.fsv.cvut.cz/ZFG/grs80-Moritz.pdf) /// /// [haversine formula]: https://en.wikipedia.org/wiki/Haversine_formula - fn distance(&self, origin: Point, destination: Point) -> F { - CustomHaversine::default().distance(origin, destination) - } -} - -impl Distance, Point> for CustomHaversine { - /// Same as [`Haversine.distance`], but on a sphere with a custom radius. fn distance(&self, origin: Point, destination: Point) -> F { let two = F::one() + F::one(); let theta1 = origin.y().to_radians(); @@ -296,16 +271,16 @@ impl InterpolatePoint for Haversine { /// /// ``` /// # use approx::assert_relative_eq; - /// use geo::{Haversine, InterpolatePoint}; + /// use geo::{HAVERSINE, InterpolatePoint}; /// use geo::Point; /// /// let p1 = Point::new(10.0, 20.0); /// let p2 = Point::new(125.0, 25.0); /// - /// let closer_to_p1 = Haversine.point_at_distance_between(p1, p2, 100_000.0); + /// let closer_to_p1 = HAVERSINE.point_at_distance_between(p1, p2, 100_000.0); /// assert_relative_eq!(closer_to_p1, Point::new(10.81, 20.49), epsilon = 1.0e-2); /// - /// let closer_to_p2 = Haversine.point_at_distance_between(p1, p2, 10_000_000.0); + /// let closer_to_p2 = HAVERSINE.point_at_distance_between(p1, p2, 10_000_000.0); /// assert_relative_eq!(closer_to_p2, Point::new(112.33, 30.57), epsilon = 1.0e-2); /// ``` /// @@ -316,7 +291,8 @@ impl InterpolatePoint for Haversine { end: Point, meters_from_start: F, ) -> Point { - CustomHaversine::default().point_at_distance_between(start, end, meters_from_start) + let bearing = self.bearing(start, end); + self.destination(start, bearing, meters_from_start) } /// Returns a new Point along a [great circle] between two existing points. @@ -325,19 +301,19 @@ impl InterpolatePoint for Haversine { /// /// ``` /// # use approx::assert_relative_eq; - /// use geo::{Haversine, InterpolatePoint}; + /// use geo::{HAVERSINE, InterpolatePoint}; /// use geo::Point; /// /// let p1 = Point::new(10.0, 20.0); /// let p2 = Point::new(125.0, 25.0); /// - /// let closer_to_p1 = Haversine.point_at_ratio_between(p1, p2, 0.1); + /// let closer_to_p1 = HAVERSINE.point_at_ratio_between(p1, p2, 0.1); /// assert_relative_eq!(closer_to_p1, Point::new(19.52, 25.27), epsilon = 1.0e-2); /// - /// let closer_to_p2 = Haversine.point_at_ratio_between(p1, p2, 0.9); + /// let closer_to_p2 = HAVERSINE.point_at_ratio_between(p1, p2, 0.9); /// assert_relative_eq!(closer_to_p2, Point::new(114.72, 29.65), epsilon = 1.0e-2); /// - /// let midpoint = Haversine.point_at_ratio_between(p1, p2, 0.5); + /// let midpoint = HAVERSINE.point_at_ratio_between(p1, p2, 0.5); /// assert_relative_eq!(midpoint, Point::new(65.87, 37.62), epsilon = 1.0e-2); /// ``` /// @@ -348,7 +324,14 @@ impl InterpolatePoint for Haversine { end: Point, ratio_from_start: F, ) -> Point { - CustomHaversine::default().point_at_ratio_between(start, end, ratio_from_start) + if start == end || ratio_from_start == F::zero() { + return start; + } + if ratio_from_start == F::one() { + return end; + } + let calculation = HaversineIntermediateFillCalculation::new(start, end); + calculation.point_at_ratio(ratio_from_start) } /// Interpolates `Point`s along a [great circle] between `start` and `end`. @@ -361,50 +344,6 @@ impl InterpolatePoint for Haversine { /// /// [great circle]: https://en.wikipedia.org/wiki/Great_circle /// [haversine formula]: https://en.wikipedia.org/wiki/Haversine_formula - fn points_along_line( - &self, - start: Point, - end: Point, - max_distance: F, - include_ends: bool, - ) -> impl Iterator> { - CustomHaversine::GRS80_MEAN_RADIUS.points_along_line(start, end, max_distance, include_ends) - } -} - -/// Interpolate Point(s) along a [great circle]. -/// -/// [great circle]: https://en.wikipedia.org/wiki/Great_circle -impl InterpolatePoint for CustomHaversine { - /// Same as [`Haversine.point_at_distance_between`], but on a sphere with a custom radius. - fn point_at_distance_between( - &self, - start: Point, - end: Point, - meters_from_start: F, - ) -> Point { - let bearing = self.bearing(start, end); - self.destination(start, bearing, meters_from_start) - } - - /// Same as [`Haversine.point_at_ratio_between`], but on a sphere with a custom radius. - fn point_at_ratio_between( - &self, - start: Point, - end: Point, - ratio_from_start: F, - ) -> Point { - if start == end || ratio_from_start == F::zero() { - return start; - } - if ratio_from_start == F::one() { - return end; - } - let calculation = HaversineIntermediateFillCalculation::new(start, end); - calculation.point_at_ratio(ratio_from_start) - } - - /// Same as [`Haversine.points_along_line`], but on a sphere with a custom radius. fn points_along_line( &self, start: Point, @@ -534,28 +473,28 @@ mod tests { fn north() { let origin = Point::new(0.0, 0.0); let destination = Point::new(0.0, 1.0); - assert_relative_eq!(0.0, Haversine.bearing(origin, destination)); + assert_relative_eq!(0.0, HAVERSINE.bearing(origin, destination)); } #[test] fn east() { let origin = Point::new(0.0, 0.0); let destination = Point::new(1.0, 0.0); - assert_relative_eq!(90.0, Haversine.bearing(origin, destination)); + assert_relative_eq!(90.0, HAVERSINE.bearing(origin, destination)); } #[test] fn south() { let origin = Point::new(0.0, 0.0); let destination = Point::new(0.0, -1.0); - assert_relative_eq!(180.0, Haversine.bearing(origin, destination)); + assert_relative_eq!(180.0, HAVERSINE.bearing(origin, destination)); } #[test] fn west() { let origin = Point::new(0.0, 0.0); let destination = Point::new(-1.0, 0.0); - assert_relative_eq!(270.0, Haversine.bearing(origin, destination)); + assert_relative_eq!(270.0, HAVERSINE.bearing(origin, destination)); } } @@ -568,7 +507,7 @@ mod tests { let bearing = 0.0; assert_relative_eq!( Point::new(0.0, 0.899320363724538), - Haversine.destination(origin, bearing, 100_000.0) + HAVERSINE.destination(origin, bearing, 100_000.0) ); } @@ -578,7 +517,7 @@ mod tests { let bearing = 90.0; assert_relative_eq!( Point::new(0.8993203637245415, 5.506522912913066e-17), - Haversine.destination(origin, bearing, 100_000.0) + HAVERSINE.destination(origin, bearing, 100_000.0) ); } @@ -588,7 +527,7 @@ mod tests { let bearing = 180.0; assert_relative_eq!( Point::new(0.0, -0.899320363724538), - Haversine.destination(origin, bearing, 100_000.0) + HAVERSINE.destination(origin, bearing, 100_000.0) ); } @@ -598,7 +537,7 @@ mod tests { let bearing = 270.0; assert_relative_eq!( Point::new(-0.8993203637245415, -1.6519568738739197e-16), - Haversine.destination(origin, bearing, 100_000.0) + HAVERSINE.destination(origin, bearing, 100_000.0) ); } } @@ -611,7 +550,7 @@ mod tests { let new_york_city = Point::new(-74.006f64, 40.7128f64); let london = Point::new(-0.1278f64, 51.5074f64); - let distance = Haversine.distance(new_york_city, london); + let distance = HAVERSINE.distance(new_york_city, london); assert_relative_eq!( 5_570_230., // meters @@ -626,7 +565,7 @@ mod tests { fn point_at_ratio_between_midpoint() { let start = Point::new(10.0, 20.0); let end = Point::new(125.0, 25.0); - let midpoint = Haversine.point_at_ratio_between(start, end, 0.5); + let midpoint = HAVERSINE.point_at_ratio_between(start, end, 0.5); assert_relative_eq!(midpoint, Point::new(65.87394172511485, 37.61809316888599)); } #[test] @@ -634,7 +573,7 @@ mod tests { let start = Point::new(10.0, 20.0); let end = Point::new(125.0, 25.0); let max_dist = 1000000.0; // meters - let route = Haversine + let route = HAVERSINE .points_along_line(start, end, max_dist, true) .collect::>(); assert_eq!(route.len(), 13); @@ -647,7 +586,7 @@ mod tests { let start = Point::new(10.0, 20.0); let end = Point::new(125.0, 25.0); let max_dist = 1000000.0; // meters - let route = Haversine + let route = HAVERSINE .points_along_line(start, end, max_dist, false) .collect::>(); assert_eq!(route.len(), 11); diff --git a/geo/src/algorithm/line_measures/metric_spaces/mod.rs b/geo/src/algorithm/line_measures/metric_spaces/mod.rs index d9c582a33..c90a39375 100644 --- a/geo/src/algorithm/line_measures/metric_spaces/mod.rs +++ b/geo/src/algorithm/line_measures/metric_spaces/mod.rs @@ -5,7 +5,7 @@ mod geodesic; pub use geodesic::Geodesic; mod haversine; -pub use haversine::{CustomHaversine, Haversine}; +pub use haversine::{Haversine, HAVERSINE}; mod rhumb; pub use rhumb::Rhumb; diff --git a/geo/src/algorithm/line_measures/mod.rs b/geo/src/algorithm/line_measures/mod.rs index 2cee63c47..e3bdb66b1 100644 --- a/geo/src/algorithm/line_measures/mod.rs +++ b/geo/src/algorithm/line_measures/mod.rs @@ -19,4 +19,4 @@ mod densify; pub use densify::{Densifiable, Densify}; pub mod metric_spaces; -pub use metric_spaces::{Euclidean, Geodesic, Haversine, Rhumb}; +pub use metric_spaces::{Euclidean, Geodesic, Haversine, Rhumb, HAVERSINE}; diff --git a/geo/src/algorithm/linestring_segment.rs b/geo/src/algorithm/linestring_segment.rs index 4ab85bb38..11d33bfb4 100644 --- a/geo/src/algorithm/linestring_segment.rs +++ b/geo/src/algorithm/linestring_segment.rs @@ -1,6 +1,7 @@ use crate::algorithm::{Densify, Length, LineInterpolatePoint, LinesIter}; use crate::geometry::{Coord, LineString, MultiLineString}; -use crate::line_measures::{Euclidean, Haversine}; +use crate::line_measures::Euclidean; +use crate::HAVERSINE; /// Segments a LineString into `segment_count` equal length LineStrings as a MultiLineString /// using Euclidean distance calculations. See `LineStringSegmentizeHaversine` @@ -114,7 +115,7 @@ implement_segmentize!(LineStringSegmentize, line_segmentize, Euclidean); implement_segmentize!( LineStringSegmentizeHaversine, line_segmentize_haversine, - Haversine + HAVERSINE ); #[cfg(test)] @@ -326,7 +327,7 @@ mod test { let lens = segments .0 .iter() - .map(|li| Haversine.length(li)) + .map(|li| HAVERSINE.length(li)) .collect::>(); let epsilon = 1e-6; // 6th decimal place which is micrometers @@ -342,7 +343,7 @@ mod test { ] .into(); - assert_relative_eq!(Haversine.length(&linestring), 83.3523000093029); + assert_relative_eq!(HAVERSINE.length(&linestring), 83.3523000093029); let n = 8; @@ -350,8 +351,8 @@ mod test { // different at 12th decimal which is a picometer assert_relative_eq!( - Haversine.length(&linestring), - Haversine.length(&segments), + HAVERSINE.length(&linestring), + HAVERSINE.length(&segments), epsilon = 1e-11 ); } diff --git a/geo/src/algorithm/mod.rs b/geo/src/algorithm/mod.rs index 05da29174..57f977420 100644 --- a/geo/src/algorithm/mod.rs +++ b/geo/src/algorithm/mod.rs @@ -187,7 +187,7 @@ pub mod lines_iter; pub use lines_iter::LinesIter; pub mod line_measures; -pub use line_measures::metric_spaces::{CustomHaversine, Euclidean, Geodesic, Haversine, Rhumb}; +pub use line_measures::metric_spaces::{Euclidean, Geodesic, Haversine, Rhumb, HAVERSINE}; pub use line_measures::{Bearing, Densify, Destination, Distance, InterpolatePoint, Length}; /// Split a LineString into n segments From 0f26d76bc48b558b60a88d6ae247f7c050b52e1b Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Thu, 30 Jan 2025 16:20:43 -0800 Subject: [PATCH 7/9] Haversine is a constant of the customizable HaversineMeasure Incorporating some more feedback... Removes the unpopular SHOUT_CASE: HAVERSINE -> Haversine, while still retaining the single implementation vs having redundant Haversine vs CustomHaversine implementations. --- geo/CHANGES.md | 11 +- geo/src/algorithm/cross_track_distance.rs | 14 +- geo/src/algorithm/densify_haversine.rs | 18 +-- geo/src/algorithm/haversine_bearing.rs | 2 +- geo/src/algorithm/haversine_closest_point.rs | 22 +-- geo/src/algorithm/haversine_destination.rs | 6 +- geo/src/algorithm/haversine_distance.rs | 6 +- geo/src/algorithm/haversine_intermediate.rs | 10 +- geo/src/algorithm/haversine_length.rs | 10 +- geo/src/algorithm/line_measures/densify.rs | 24 +-- .../line_measures/interpolate_point.rs | 20 +-- geo/src/algorithm/line_measures/length.rs | 14 +- .../metric_spaces/euclidean/distance.rs | 2 +- .../metric_spaces/euclidean/mod.rs | 8 +- .../line_measures/metric_spaces/haversine.rs | 145 ++++++++++-------- .../line_measures/metric_spaces/mod.rs | 2 +- geo/src/algorithm/line_measures/mod.rs | 4 +- geo/src/algorithm/linestring_segment.rs | 12 +- geo/src/algorithm/mod.rs | 2 +- geo/src/lib.rs | 2 +- 20 files changed, 177 insertions(+), 157 deletions(-) diff --git a/geo/CHANGES.md b/geo/CHANGES.md index bbc25701b..2c836c1cf 100644 --- a/geo/CHANGES.md +++ b/geo/CHANGES.md @@ -3,24 +3,29 @@ ## Unreleased - BREAKING: `Densify` and `Length` are now defined on the metric space, rather than a generic method on the geometry. + - ```rust // before line_string.length::() line_string.densify::() + line_string.densify::() // after Euclidean.length(&line_string) Euclidean.densify(&line_string) + Haversine.densify(&line_string) ``` -- `Haversine` can be configured with a custom radius for doing calculations on custom sphere. Use `HAVERSINE` for the default earth radius. +- Add configurable `HaversineMeasure` for doing calculations on a custom sphere. Use `Haversine` for the default earth radius. - ```rust // before Haversine::distance(point1, point2) // after - Haversine::new(3_389_500.0).distance(point1, point2) - HAVERSINE.distance(point_1, point_2) + Haversine.distance(point1, point2) + + // For custom earth (or non-earth!) radius + HaversineMeasure::new(3_389_500.0).distance(point1, point2) ``` - Docs: Fix page location of citation for mean earth radius used in Haversine calculations - diff --git a/geo/src/algorithm/cross_track_distance.rs b/geo/src/algorithm/cross_track_distance.rs index 37d9da75c..0aac1f899 100644 --- a/geo/src/algorithm/cross_track_distance.rs +++ b/geo/src/algorithm/cross_track_distance.rs @@ -1,4 +1,4 @@ -use crate::{Bearing, Distance, HAVERSINE, MEAN_EARTH_RADIUS}; +use crate::{Bearing, Distance, Haversine, MEAN_EARTH_RADIUS}; use geo_types::{CoordFloat, Point}; use num_traits::FromPrimitive; @@ -43,9 +43,9 @@ where { fn cross_track_distance(&self, line_point_a: &Point, line_point_b: &Point) -> T { let mean_earth_radius = T::from(MEAN_EARTH_RADIUS).unwrap(); - let l_delta_13: T = HAVERSINE.distance(*line_point_a, *self) / mean_earth_radius; - let theta_13: T = HAVERSINE.bearing(*line_point_a, *self).to_radians(); - let theta_12: T = HAVERSINE.bearing(*line_point_a, *line_point_b).to_radians(); + let l_delta_13: T = Haversine.distance(*line_point_a, *self) / mean_earth_radius; + let theta_13: T = Haversine.bearing(*line_point_a, *self).to_radians(); + let theta_12: T = Haversine.bearing(*line_point_a, *line_point_b).to_radians(); let l_delta_xt: T = (l_delta_13.sin() * (theta_12 - theta_13).sin()).asin(); mean_earth_radius * l_delta_xt.abs() } @@ -55,7 +55,7 @@ where mod test { use crate::CrossTrackDistance; use crate::Point; - use crate::{Distance, HAVERSINE}; + use crate::{Distance, Haversine}; #[test] fn distance1_test() { @@ -90,13 +90,13 @@ mod test { assert_relative_eq!( p.cross_track_distance(&line_point_a, &line_point_b), - HAVERSINE.distance(p, Point::new(1., 0.)), + Haversine.distance(p, Point::new(1., 0.)), epsilon = 1.0e-6 ); assert_relative_eq!( p.cross_track_distance(&line_point_b, &line_point_a), - HAVERSINE.distance(p, Point::new(1., 0.)), + Haversine.distance(p, Point::new(1., 0.)), epsilon = 1.0e-6 ); } diff --git a/geo/src/algorithm/densify_haversine.rs b/geo/src/algorithm/densify_haversine.rs index f2689d59d..03ad9ff7e 100644 --- a/geo/src/algorithm/densify_haversine.rs +++ b/geo/src/algorithm/densify_haversine.rs @@ -1,6 +1,6 @@ use num_traits::FromPrimitive; -use crate::line_measures::HAVERSINE; +use crate::line_measures::Haversine; // Densify will soon be deprecated too, so let's just allow deprecated for now #[allow(deprecated)] use crate::HaversineLength; @@ -10,7 +10,7 @@ use crate::{ #[deprecated( since = "0.29.0", - note = "Please use the `HAVERSINE.densify(&line)` via the `Densify` trait instead." + note = "Please use the `Haversine.densify(&line)` via the `Densify` trait instead." )] /// Returns a new spherical geometry containing both existing and new interpolated coordinates with /// a maximum distance of `max_distance` between them. @@ -50,7 +50,7 @@ where type Output = MultiPolygon; fn densify_haversine(&self, max_distance: T) -> Self::Output { - HAVERSINE.densify(self, max_distance) + Haversine.densify(self, max_distance) } } @@ -64,7 +64,7 @@ where type Output = Polygon; fn densify_haversine(&self, max_distance: T) -> Self::Output { - HAVERSINE.densify(self, max_distance) + Haversine.densify(self, max_distance) } } @@ -78,7 +78,7 @@ where type Output = MultiLineString; fn densify_haversine(&self, max_distance: T) -> Self::Output { - HAVERSINE.densify(self, max_distance) + Haversine.densify(self, max_distance) } } @@ -92,7 +92,7 @@ where type Output = LineString; fn densify_haversine(&self, max_distance: T) -> Self::Output { - HAVERSINE.densify(self, max_distance) + Haversine.densify(self, max_distance) } } @@ -106,7 +106,7 @@ where type Output = LineString; fn densify_haversine(&self, max_distance: T) -> Self::Output { - HAVERSINE.densify(self, max_distance) + Haversine.densify(self, max_distance) } } @@ -120,7 +120,7 @@ where type Output = Polygon; fn densify_haversine(&self, max_distance: T) -> Self::Output { - HAVERSINE.densify(self, max_distance) + Haversine.densify(self, max_distance) } } @@ -134,7 +134,7 @@ where type Output = Polygon; fn densify_haversine(&self, max_distance: T) -> Self::Output { - HAVERSINE.densify(self, max_distance) + Haversine.densify(self, max_distance) } } diff --git a/geo/src/algorithm/haversine_bearing.rs b/geo/src/algorithm/haversine_bearing.rs index 23db4056c..3549553a3 100644 --- a/geo/src/algorithm/haversine_bearing.rs +++ b/geo/src/algorithm/haversine_bearing.rs @@ -2,7 +2,7 @@ use crate::{CoordFloat, Point}; #[deprecated( since = "0.29.0", - note = "Please use the `HAVERSINE.bearing` method from the `Bearing` trait instead" + note = "Please use the `Haversine.bearing` method from the `Bearing` trait instead" )] /// Returns the bearing to another Point in degrees. /// diff --git a/geo/src/algorithm/haversine_closest_point.rs b/geo/src/algorithm/haversine_closest_point.rs index e1afb145f..60b7fa31d 100644 --- a/geo/src/algorithm/haversine_closest_point.rs +++ b/geo/src/algorithm/haversine_closest_point.rs @@ -1,4 +1,4 @@ -use crate::line_measures::{Bearing, Destination, Distance, HAVERSINE}; +use crate::line_measures::{Bearing, Destination, Distance, Haversine}; use crate::{Closest, Contains}; use crate::{CoordsIter, GeoFloat, Point, MEAN_EARTH_RADIUS}; use geo_types::{ @@ -92,7 +92,7 @@ where } // This can probably be done cheaper - let d3 = HAVERSINE.distance(p2, p1); + let d3 = Haversine.distance(p2, p1); if d3 <= T::epsilon() { // I think here it should be return Closest::SinglePoint(p1) // If the line segment is degenerated to a point, that point is still the closest @@ -102,18 +102,18 @@ where } let pi = T::from(std::f64::consts::PI).unwrap(); - let crs_ad = HAVERSINE.bearing(p1, *from).to_radians(); - let crs_ab = HAVERSINE.bearing(p1, p2).to_radians(); + let crs_ad = Haversine.bearing(p1, *from).to_radians(); + let crs_ab = Haversine.bearing(p1, p2).to_radians(); let crs_ba = if crs_ab > T::zero() { crs_ab - pi } else { crs_ab + pi }; - let crs_bd = HAVERSINE.bearing(p2, *from).to_radians(); + let crs_bd = Haversine.bearing(p2, *from).to_radians(); let d_crs1 = crs_ad - crs_ab; let d_crs2 = crs_bd - crs_ba; - let d1 = HAVERSINE.distance(p1, *from); + let d1 = Haversine.distance(p1, *from); // d1, d2, d3 are in principle not needed, only the sign matters let projection1 = d_crs1.cos(); @@ -127,13 +127,13 @@ where if xtd < T::epsilon() { return Closest::Intersection(*from); } else { - return Closest::SinglePoint(HAVERSINE.destination(p1, crs_ab.to_degrees(), atd)); + return Closest::SinglePoint(Haversine.destination(p1, crs_ab.to_degrees(), atd)); } } // Projected falls outside the GC Arc // Return shortest distance pt, project either on point sp1 or sp2 - let d2 = HAVERSINE.distance(p2, *from); + let d2 = Haversine.distance(p2, *from); if d1 < d2 { return Closest::SinglePoint(p1); } @@ -166,7 +166,7 @@ where return intersect; } Closest::SinglePoint(pt) => { - let dist = HAVERSINE.distance(pt, *from); + let dist = Haversine.distance(pt, *from); if dist < min_distance { min_distance = dist; rv = Closest::SinglePoint(pt); @@ -198,7 +198,7 @@ where return (intersect, T::zero()); } Closest::SinglePoint(pt) => { - let dist = HAVERSINE.distance(pt, *from); + let dist = Haversine.distance(pt, *from); if dist < min_distance { min_distance = dist; rv = Closest::SinglePoint(pt); @@ -301,7 +301,7 @@ where // This mean on top of the line. Closest::Intersection(pt) => return Closest::Intersection(pt), Closest::SinglePoint(pt) => { - let dist = HAVERSINE.distance(pt, *from); + let dist = Haversine.distance(pt, *from); if dist < min_distance { min_distance = dist; rv = Closest::SinglePoint(pt); diff --git a/geo/src/algorithm/haversine_destination.rs b/geo/src/algorithm/haversine_destination.rs index fd48eeee2..f915569d0 100644 --- a/geo/src/algorithm/haversine_destination.rs +++ b/geo/src/algorithm/haversine_destination.rs @@ -1,9 +1,9 @@ -use crate::{CoordFloat, Destination, Point, HAVERSINE}; +use crate::{CoordFloat, Destination, Haversine, Point}; use num_traits::FromPrimitive; #[deprecated( since = "0.29.0", - note = "Please use the `HAVERSINE.destination` method from the `Destination` trait instead" + note = "Please use the `Haversine.destination` method from the `Destination` trait instead" )] /// Returns a new Point using the distance to the existing Point and a bearing for the direction /// @@ -39,7 +39,7 @@ where T: CoordFloat + FromPrimitive, { fn haversine_destination(&self, bearing: T, distance: T) -> Point { - HAVERSINE.destination(*self, bearing, distance) + Haversine.destination(*self, bearing, distance) } } diff --git a/geo/src/algorithm/haversine_distance.rs b/geo/src/algorithm/haversine_distance.rs index 9ccc3f890..df1cf7a40 100644 --- a/geo/src/algorithm/haversine_distance.rs +++ b/geo/src/algorithm/haversine_distance.rs @@ -1,9 +1,9 @@ -use crate::{CoordFloat, Distance, Point, HAVERSINE}; +use crate::{CoordFloat, Distance, Haversine, Point}; use num_traits::FromPrimitive; #[deprecated( since = "0.29.0", - note = "Please use the `HAVERSINE.distance` method from the `Distance` trait instead" + note = "Please use the `Haversine.distance` method from the `Distance` trait instead" )] /// Determine the distance between two geometries using the [haversine formula]. /// @@ -55,7 +55,7 @@ where T: CoordFloat + FromPrimitive, { fn haversine_distance(&self, rhs: &Point) -> T { - HAVERSINE.distance(*self, *rhs) + Haversine.distance(*self, *rhs) } } diff --git a/geo/src/algorithm/haversine_intermediate.rs b/geo/src/algorithm/haversine_intermediate.rs index f2410752b..fce21e8c2 100644 --- a/geo/src/algorithm/haversine_intermediate.rs +++ b/geo/src/algorithm/haversine_intermediate.rs @@ -1,4 +1,4 @@ -use crate::{CoordFloat, InterpolatePoint, Point, HAVERSINE}; +use crate::{CoordFloat, Haversine, InterpolatePoint, Point}; use num_traits::FromPrimitive; #[deprecated( @@ -9,7 +9,7 @@ use num_traits::FromPrimitive; pub trait HaversineIntermediate { #[deprecated( since = "0.29.0", - note = "Please use `HAVERSINE.point_at_ratio_between` from the `InterpolatePoint` trait instead" + note = "Please use `Haversine.point_at_ratio_between` from the `InterpolatePoint` trait instead" )] /// Returns a new `Point` along a great circle route between `self` and `other`. /// @@ -40,7 +40,7 @@ pub trait HaversineIntermediate { #[deprecated( since = "0.29.0", - note = "Please use `HAVERSINE.points_along_line` from the `InterpolatePoint` trait instead" + note = "Please use `Haversine.points_along_line` from the `InterpolatePoint` trait instead" )] /// Interpolates `Point`s along a great circle route between self and `other`. /// @@ -62,7 +62,7 @@ where T: CoordFloat + FromPrimitive, { fn haversine_intermediate(&self, other: &Point, ratio: T) -> Point { - HAVERSINE.point_at_ratio_between(*self, *other, ratio) + Haversine.point_at_ratio_between(*self, *other, ratio) } fn haversine_intermediate_fill( @@ -71,7 +71,7 @@ where max_dist: T, include_ends: bool, ) -> Vec> { - HAVERSINE + Haversine .points_along_line(*self, *other, max_dist, include_ends) .collect() } diff --git a/geo/src/algorithm/haversine_length.rs b/geo/src/algorithm/haversine_length.rs index e999a25c5..bac48e593 100644 --- a/geo/src/algorithm/haversine_length.rs +++ b/geo/src/algorithm/haversine_length.rs @@ -1,11 +1,11 @@ use num_traits::FromPrimitive; use crate::{CoordFloat, Line, LineString, MultiLineString}; -use crate::{Length, HAVERSINE}; +use crate::{Haversine, Length}; #[deprecated( since = "0.29.0", - note = "Please use the `HAVERSINE.length(&line)` via the `Length` trait instead." + note = "Please use the `Haversine.length(&line)` via the `Length` trait instead." )] /// Determine the length of a geometry using the [haversine formula]. /// @@ -51,7 +51,7 @@ where T: CoordFloat + FromPrimitive, { fn haversine_length(&self) -> T { - HAVERSINE.length(self) + Haversine.length(self) } } @@ -61,7 +61,7 @@ where T: CoordFloat + FromPrimitive, { fn haversine_length(&self) -> T { - HAVERSINE.length(self) + Haversine.length(self) } } @@ -71,6 +71,6 @@ where T: CoordFloat + FromPrimitive, { fn haversine_length(&self) -> T { - HAVERSINE.length(self) + Haversine.length(self) } } diff --git a/geo/src/algorithm/line_measures/densify.rs b/geo/src/algorithm/line_measures/densify.rs index cc10f5ddd..8bcd2a87f 100644 --- a/geo/src/algorithm/line_measures/densify.rs +++ b/geo/src/algorithm/line_measures/densify.rs @@ -33,17 +33,17 @@ use num_traits::FromPrimitive; /// assert_relative_eq!(densified, expected_output); ///``` /// -/// For lng/lat geometries, consider using a different [metric space] like [`Haversine`](crate::Haversine) or [`Geodesic`](crate::Geodesic). +/// For lng/lat geometries, consider using a different [metric space] like [`Haversine`](crate::HaversineMeasure) or [`Geodesic`](crate::Geodesic). /// ///``` /// # use approx::assert_relative_eq; /// use geo::{wkt, Densify}; -/// use geo::line_measures::HAVERSINE; +/// use geo::line_measures::Haversine; /// let line_string = wkt!(LINESTRING(0.0 0.0,0.0 6.0,1.0 7.0)); /// /// // For Haversine, the unit of distance is in meters /// let max_dist = 200_000.0; -/// let densified = HAVERSINE.densify(&line_string, max_dist); +/// let densified = Haversine.densify(&line_string, max_dist); /// // Haversine interprets coordinate points as lng/lat /// let expected_output = wkt!(LINESTRING( /// 0.0 0.0, @@ -100,17 +100,17 @@ where /// assert_relative_eq!(densified, expected_output); ///``` /// -/// For lng/lat geometries, consider using a different [metric space] like [`Haversine`](crate::Haversine) or [`Geodesic`](crate::Geodesic). +/// For lng/lat geometries, consider using a different [metric space] like [`Haversine`](crate::HaversineMeasure) or [`Geodesic`](crate::Geodesic). /// ///``` /// # use approx::assert_relative_eq; /// use geo::wkt; -/// use geo::line_measures::{HAVERSINE, Densifiable}; +/// use geo::line_measures::{Haversine, Densifiable}; /// let line_string = wkt!(LINESTRING(0.0 0.0,0.0 6.0,1.0 7.0)); /// /// // For Haversine, the unit of distance is in meters /// let max_dist = 200_000.0; -/// let densified = line_string.densify(&HAVERSINE, max_dist); +/// let densified = line_string.densify(&Haversine, max_dist); /// // Haversine interprets coordinate points as lng/lat /// let expected_output = wkt!(LINESTRING( /// 0.0 0.0, @@ -319,7 +319,7 @@ impl Densifiable for Triangle { #[cfg(test)] mod tests { use super::*; - use crate::{coord, polygon, wkt, Euclidean, Geodesic, Rhumb, HAVERSINE}; + use crate::{coord, polygon, wkt, Euclidean, Geodesic, Haversine, Rhumb}; #[test] fn densify_line() { @@ -335,7 +335,7 @@ mod tests { let densified_rhumb = Rhumb.densify(&line, 100_000.0); assert!(densified_rhumb.coords_count() > 2); - let densified_haversine = HAVERSINE.densify(&line, 100_000.0); + let densified_haversine = Haversine.densify(&line, 100_000.0); assert!(densified_haversine.coords_count() > 2); } @@ -353,7 +353,7 @@ mod tests { let densified_rhumb_ls = Rhumb.densify(&line_string, 500_000.0); assert!(densified_rhumb_ls.coords_count() > line_string.coords_count()); - let densified_haversine_ls = HAVERSINE.densify(&line_string, 500_000.0); + let densified_haversine_ls = Haversine.densify(&line_string, 500_000.0); assert!(densified_haversine_ls.coords_count() > line_string.coords_count()); } @@ -459,7 +459,7 @@ mod tests { 4.925 45.804 ))); - let actual_haversine = HAVERSINE.densify(&polygon, 50000.0); + let actual_haversine = Haversine.densify(&polygon, 50000.0); assert_relative_eq!(actual_haversine, exepcted_haversine); let expected_geodesic = wkt!(POLYGON(( @@ -504,7 +504,7 @@ mod tests { -3.1944 55.949 )); - let dense = HAVERSINE.densify(&linestring, 110.0); + let dense = Haversine.densify(&linestring, 110.0); assert_relative_eq!(dense, expected); } @@ -512,7 +512,7 @@ mod tests { fn test_line_densify() { let output = wkt!(LINESTRING(0.0 0.0, 0.0 0.5, 0.0 1.0)); let line = Line::new(coord! {x: 0.0, y: 0.0}, coord! { x: 0.0, y: 1.0 }); - let dense = HAVERSINE.densify(&line, 100000.0); + let dense = Haversine.densify(&line, 100000.0); assert_relative_eq!(dense, output); } } diff --git a/geo/src/algorithm/line_measures/interpolate_point.rs b/geo/src/algorithm/line_measures/interpolate_point.rs index 6e0d1d5f4..c60b8c6ba 100644 --- a/geo/src/algorithm/line_measures/interpolate_point.rs +++ b/geo/src/algorithm/line_measures/interpolate_point.rs @@ -42,7 +42,7 @@ pub trait InterpolatePoint { #[cfg(test)] mod tests { - use crate::{Euclidean, Geodesic, InterpolatePoint, Point, Rhumb, HAVERSINE}; + use crate::{Euclidean, Geodesic, Haversine, InterpolatePoint, Point, Rhumb}; #[test] fn point_at_ratio_between_line_ends() { @@ -50,13 +50,13 @@ mod tests { let end = Point::new(1.0, 1.0); let ratio = 0.0; - assert_eq!(HAVERSINE.point_at_ratio_between(start, end, ratio), start); + assert_eq!(Haversine.point_at_ratio_between(start, end, ratio), start); assert_eq!(Euclidean.point_at_ratio_between(start, end, ratio), start); assert_eq!(Geodesic.point_at_ratio_between(start, end, ratio), start); assert_eq!(Rhumb.point_at_ratio_between(start, end, ratio), start); let ratio = 1.0; - assert_eq!(HAVERSINE.point_at_ratio_between(start, end, ratio), end); + assert_eq!(Haversine.point_at_ratio_between(start, end, ratio), end); assert_eq!(Euclidean.point_at_ratio_between(start, end, ratio), end); assert_eq!(Geodesic.point_at_ratio_between(start, end, ratio), end); assert_eq!(Rhumb.point_at_ratio_between(start, end, ratio), end); @@ -70,19 +70,19 @@ mod tests { let start = Point::new(1.0, 1.0); let ratio = 0.0; - assert_eq!(HAVERSINE.point_at_ratio_between(start, start, ratio), start); + assert_eq!(Haversine.point_at_ratio_between(start, start, ratio), start); assert_eq!(Euclidean.point_at_ratio_between(start, start, ratio), start); assert_eq!(Geodesic.point_at_ratio_between(start, start, ratio), start); assert_eq!(Rhumb.point_at_ratio_between(start, start, ratio), start); let ratio = 0.5; - assert_eq!(HAVERSINE.point_at_ratio_between(start, start, ratio), start); + assert_eq!(Haversine.point_at_ratio_between(start, start, ratio), start); assert_eq!(Euclidean.point_at_ratio_between(start, start, ratio), start); assert_eq!(Geodesic.point_at_ratio_between(start, start, ratio), start); assert_eq!(Rhumb.point_at_ratio_between(start, start, ratio), start); let ratio = 1.0; - assert_eq!(HAVERSINE.point_at_ratio_between(start, start, ratio), start); + assert_eq!(Haversine.point_at_ratio_between(start, start, ratio), start); assert_eq!(Euclidean.point_at_ratio_between(start, start, ratio), start); assert_eq!(Geodesic.point_at_ratio_between(start, start, ratio), start); assert_eq!(Rhumb.point_at_ratio_between(start, start, ratio), start); @@ -96,7 +96,7 @@ mod tests { let distance = 0.0; assert_eq!( - HAVERSINE.point_at_distance_between(start, start, distance), + Haversine.point_at_distance_between(start, start, distance), start ); @@ -116,7 +116,7 @@ mod tests { let due_north = Point::new(1.0, 1.9); let due_south = Point::new(1.0, 0.1); assert_relative_eq!( - HAVERSINE.point_at_distance_between(start, start, distance), + Haversine.point_at_distance_between(start, start, distance), due_north, epsilon = 1.0e-1 ); @@ -142,7 +142,7 @@ mod tests { let max_distance = 1.0; let include_ends = true; - let points: Vec<_> = HAVERSINE + let points: Vec<_> = Haversine .points_along_line(start, start, max_distance, include_ends) .collect(); assert_eq!(points, vec![start, start]); @@ -163,7 +163,7 @@ mod tests { assert_eq!(points, vec![start, start]); let include_ends = false; - let points: Vec<_> = HAVERSINE + let points: Vec<_> = Haversine .points_along_line(start, start, max_distance, include_ends) .collect(); assert_eq!(points, vec![]); diff --git a/geo/src/algorithm/line_measures/length.rs b/geo/src/algorithm/line_measures/length.rs index f64c67b31..5657485de 100644 --- a/geo/src/algorithm/line_measures/length.rs +++ b/geo/src/algorithm/line_measures/length.rs @@ -5,7 +5,7 @@ use crate::{CoordFloat, Line, LineString, MultiLineString, Point}; /// /// # Examples /// ``` -/// use geo::algorithm::line_measures::{Length, Euclidean, HAVERSINE}; +/// use geo::algorithm::line_measures::{Length, Euclidean, Haversine}; /// /// let line_string = geo::wkt!(LINESTRING( /// 0.0 0.0, @@ -19,7 +19,7 @@ use crate::{CoordFloat, Line, LineString, MultiLineString, Point}; /// -58.4173 -34.6118, /// -70.6483 -33.4489 /// )); -/// assert_eq!(HAVERSINE.length(&line_string_lon_lat).round(), 3_474_956.0); +/// assert_eq!(Haversine.length(&line_string_lon_lat).round(), 3_474_956.0); /// ``` pub trait Length { fn length(&self, geometry: &impl LengthMeasurable) -> F; @@ -32,7 +32,7 @@ pub trait Length { /// /// # Examples /// ``` -/// use geo::algorithm::line_measures::{LengthMeasurable, Euclidean, HAVERSINE}; +/// use geo::algorithm::line_measures::{LengthMeasurable, Euclidean, Haversine}; /// /// let line_string = geo::wkt!(LINESTRING( /// 0.0 0.0, @@ -46,7 +46,7 @@ pub trait Length { /// -58.4173 -34.6118, /// -70.6483 -33.4489 /// )); -/// assert_eq!(line_string_lon_lat.length(&HAVERSINE).round(), 3_474_956.0); +/// assert_eq!(line_string_lon_lat.length(&Haversine).round(), 3_474_956.0); /// ``` pub trait LengthMeasurable { fn length(&self, metric_space: &impl Distance, Point>) -> F; @@ -87,7 +87,7 @@ impl LengthMeasurable for MultiLineString { #[cfg(test)] mod tests { use super::*; - use crate::{coord, Euclidean, Geodesic, Rhumb, HAVERSINE}; + use crate::{coord, Euclidean, Geodesic, Haversine, Rhumb}; #[test] fn lines() { @@ -107,7 +107,7 @@ mod tests { ); assert_eq!( 343_557., // meters - HAVERSINE.length(&line).round() + Haversine.length(&line).round() ); // computing Euclidean length of an unprojected (lng/lat) line gives a nonsense answer @@ -141,7 +141,7 @@ mod tests { ); assert_eq!( 6_304_387., // meters - HAVERSINE.length(&line_string).round() + Haversine.length(&line_string).round() ); // computing Euclidean length of an unprojected (lng/lat) gives a nonsense answer diff --git a/geo/src/algorithm/line_measures/metric_spaces/euclidean/distance.rs b/geo/src/algorithm/line_measures/metric_spaces/euclidean/distance.rs index 868fff667..fbfac7227 100644 --- a/geo/src/algorithm/line_measures/metric_spaces/euclidean/distance.rs +++ b/geo/src/algorithm/line_measures/metric_spaces/euclidean/distance.rs @@ -67,7 +67,7 @@ impl Distance, Point> for Euclidean { /// ); /// ``` /// - /// [`Haversine`]: crate::line_measures::metric_spaces::Haversine + /// [`Haversine`]: crate::line_measures::metric_spaces::HaversineMeasure /// [`Geodesic`]: crate::line_measures::metric_spaces::Geodesic /// [metric spaces]: crate::line_measures::metric_spaces fn distance(&self, origin: Point, destination: Point) -> F { diff --git a/geo/src/algorithm/line_measures/metric_spaces/euclidean/mod.rs b/geo/src/algorithm/line_measures/metric_spaces/euclidean/mod.rs index 4312a308b..9b3fb424f 100644 --- a/geo/src/algorithm/line_measures/metric_spaces/euclidean/mod.rs +++ b/geo/src/algorithm/line_measures/metric_spaces/euclidean/mod.rs @@ -17,7 +17,7 @@ use num_traits::FromPrimitive; /// /// [Euclidean plane]: https://en.wikipedia.org/wiki/Euclidean_plane /// [`Transform`]: crate::Transform -/// [`Haversine`]: super::Haversine +/// [`Haversine`]: super::HaversineMeasure /// [`Geodesic`]: super::Geodesic /// [metric spaces]: super pub struct Euclidean; @@ -35,7 +35,7 @@ impl InterpolatePoint for Euclidean { /// units, like meters or miles, **not** lon/lat. /// For lon/lat points, use the [`Haversine`] or [`Geodesic`] [metric spaces]. /// - /// [`Haversine`]: crate::line_measures::Haversine + /// [`Haversine`]: crate::line_measures::HaversineMeasure /// [`Geodesic`]: crate::line_measures::Geodesic /// [metric spaces]: crate::line_measures::metric_spaces fn point_at_distance_between( @@ -59,7 +59,7 @@ impl InterpolatePoint for Euclidean { /// units, like meters or miles, **not** lon/lat. /// For lon/lat points, use the [`Haversine`] or [`Geodesic`] [metric spaces]. /// - /// [`Haversine`]: crate::line_measures::Haversine + /// [`Haversine`]: crate::line_measures::HaversineMeasure /// [`Geodesic`]: crate::line_measures::Geodesic /// [metric spaces]: crate::line_measures::metric_spaces fn point_at_ratio_between( @@ -87,7 +87,7 @@ impl InterpolatePoint for Euclidean { /// units, like meters or miles, **not** lon/lat. /// For lon/lat points, use the [`Haversine`] or [`Geodesic`] [metric spaces]. /// - /// [`Haversine`]: crate::line_measures::Haversine + /// [`Haversine`]: crate::line_measures::HaversineMeasure /// [`Geodesic`]: crate::line_measures::Geodesic /// [metric spaces]: crate::line_measures::metric_spaces fn points_along_line( diff --git a/geo/src/algorithm/line_measures/metric_spaces/haversine.rs b/geo/src/algorithm/line_measures/metric_spaces/haversine.rs index 97331f906..4eef331b4 100644 --- a/geo/src/algorithm/line_measures/metric_spaces/haversine.rs +++ b/geo/src/algorithm/line_measures/metric_spaces/haversine.rs @@ -4,44 +4,47 @@ use super::super::{Bearing, Destination, Distance, InterpolatePoint}; use crate::utils::normalize_longitude; use crate::{CoordFloat, Point, MEAN_EARTH_RADIUS}; -/// A spherical model of the earth using the [haversine formula]. +/// Use the [`Haversine`] constant (an instance of `HaversineMeasure`) rather than building your own +/// customized [`HaversineMeasure`] for standard spherical Earth measurements. /// -/// Distances are considered [great circle] lengths and are measured in meters. +/// `HaversineMeasure` measures distance on a sphere using the [haversine formula]. +/// Distances are considered [great circle] lengths and given in units that match those of +/// the `radius` passed to [`HaversineMeasure::new`] (typically meters). /// /// You may specify a custom radius for the Earth (or other sphere), but for normal spherical -/// measurements of the Earth, you probably want to just use [`HAVERSINE`] which uses the +/// measurements of the Earth, you should just use [`Haversine`] which uses the standard /// earth radius of 6371.0088 km (6_371_008.7714 m), based on the recommendation of the IUGG. /// /// # Examples /// /// ``` /// # use approx::assert_relative_eq; -/// use geo::{wkt, Haversine, HAVERSINE, Distance}; +/// use geo::{wkt, HaversineMeasure, Haversine, Distance}; /// /// let start = wkt!(POINT(23.319941 42.698334)); // Sofia: Longitude, Latitude /// let finish = wkt!(POINT(24.742168 42.136097)); // Plovdiv: Longitude, Latitude /// -/// // Typically, you can just use `HAVERSINE` for measuring on the Earth's surface +/// // Typically, you can just use `Haversine` for measuring on the Earth's surface. /// assert_relative_eq!( /// 132433.09929460194, -/// HAVERSINE.distance(start, finish) +/// Haversine.distance(start, finish) /// ); /// -/// // `HAVERSINE` has a radius equal to the mean radius of the GRS80 ellipsoid. +/// // The default `Haversine` const has a radius equal to the mean radius of the GRS80 ellipsoid (6371.0088 km). /// assert_relative_eq!( -/// HAVERSINE.distance(start, finish), -/// Haversine::GRS80_MEAN_RADIUS.distance(start, finish) +/// Haversine.radius(), +/// HaversineMeasure::GRS80_MEAN_RADIUS.radius() /// ); /// /// // You may choose to use one of the other well known estimations of the Earth's radius, /// // which may result in *slightly* different results. /// assert_relative_eq!( /// 132433.06564071847, -/// Haversine::GRS80_EQUAL_AREA.distance(start, finish) +/// HaversineMeasure::GRS80_EQUAL_AREA.distance(start, finish) /// ); /// /// // Or you can specify whatever radius you want to get some "out of this world" results. -/// let mars_sphere = Haversine::new(3_389_500.0); // 👽 Mars radius in meters +/// let mars_sphere = HaversineMeasure::new(3_389_500.0); // 👽 Mars radius in meters /// assert_relative_eq!( /// 70456.97222377927, /// mars_sphere.distance(start, finish) @@ -55,22 +58,52 @@ use crate::{CoordFloat, Point, MEAN_EARTH_RADIUS}; /// - /// - /// -/// [haversine formula]: https://en.wikipedia.org/wiki/Haversine_formula// +/// [haversine formula]: https://en.wikipedia.org/wiki/Haversine_formula /// [great circle]: https://en.wikipedia.org/wiki/Great_circle -pub struct Haversine { +pub struct HaversineMeasure { radius: f64, } -impl Default for Haversine { +impl Default for HaversineMeasure { fn default() -> Self { - Haversine::GRS80_MEAN_RADIUS + HaversineMeasure::GRS80_MEAN_RADIUS } } -/// A spherical model of the earth using the [haversine formula], and the GRS80 Mean Earth Radius of 6371008.8 meters. -pub const HAVERSINE: Haversine = Haversine::GRS80_MEAN_RADIUS; +/// `Haversine` measures distance on a sphere using the [haversine formula]. Distances are +/// considered [great circle] lengths and given in meters. +/// +/// See [`HaversineMeasure`](HaversineMeasure#trait-implementations) for all the trait methods available to [`Haversine`]. +/// +// / # Examples +/// +/// ``` +/// # use approx::assert_relative_eq; +/// use geo::{wkt, Haversine, Distance}; +/// +/// let start = wkt!(POINT(23.319941 42.698334)); // Sofia: Longitude, Latitude +/// let finish = wkt!(POINT(24.742168 42.136097)); // Plovdiv: Longitude, Latitude +/// +/// // Typically, you can just use `Haversine` for measuring on the Earth's surface. +/// assert_relative_eq!( +/// 132433.09929460194, +/// Haversine.distance(start, finish) +/// ); +/// ``` +/// +/// # References +/// +/// Moritz, H. (2000). Geodetic Reference System 1980. Journal of Geodesy, 74(1), 128–133. doi:10.1007/s001900050278 +/// "Derived Geometric Constants: **R1: mean radius**" (p131) +/// - +/// - +/// +/// [haversine formula]: https://en.wikipedia.org/wiki/Haversine_formula +/// [great circle]: https://en.wikipedia.org/wiki/Great_circle +#[allow(non_upper_case_globals)] +pub const Haversine: HaversineMeasure = HaversineMeasure::GRS80_MEAN_RADIUS; -impl Haversine { +impl HaversineMeasure { /// ## Parameters /// - radius: The radius of the sphere, typically in meters. pub const fn new(radius: f64) -> Self { @@ -121,13 +154,7 @@ impl Haversine { }; } -impl Default for Haversine { - fn default() -> Self { - Self::GRS80_MEAN_RADIUS - } -} - -impl Bearing for Haversine { +impl Bearing for HaversineMeasure { /// Returns the bearing from `origin` to `destination` in degrees along a [great circle]. /// /// # Units @@ -139,12 +166,12 @@ impl Bearing for Haversine { /// /// ``` /// # use approx::assert_relative_eq; - /// use geo::{HAVERSINE, Bearing}; + /// use geo::{Haversine, Bearing}; /// use geo::Point; /// /// let origin = Point::new(9.0, 10.0); /// let destination = Point::new(9.5, 10.1); - /// let bearing = HAVERSINE.bearing(origin, destination); + /// let bearing = Haversine.bearing(origin, destination); /// // A little north of east /// assert_relative_eq!(bearing, 78.47, epsilon = 1.0e-2); /// ``` @@ -169,7 +196,7 @@ impl Bearing for Haversine { } } -impl Destination for Haversine { +impl Destination for HaversineMeasure { /// Returns a new point having travelled the `distance` along a [great circle] /// from the `origin` point with the given `bearing`. /// @@ -184,20 +211,13 @@ impl Destination for Haversine { /// /// ``` /// # use approx::assert_relative_eq; - /// use geo::{HAVERSINE, Destination}; + /// use geo::{Haversine, Destination}; /// use geo::Point; /// /// let origin = Point::new(9.177789688110352, 48.776781529534965); - /// let destination = HAVERSINE.destination(origin, 45., 10000.); + /// let destination = Haversine.destination(origin, 45., 10000.); /// assert_relative_eq!(Point::new(9.274409949623532, 48.84033274015048), destination); /// ``` - /// - /// # References - /// - /// *Note*: this implementation uses a mean earth radius of 6371.088 km, based on the [recommendation of - /// the IUGG](ftp://athena.fsv.cvut.cz/ZFG/grs80-Moritz.pdf) - /// - /// [great circle]: https://en.wikipedia.org/wiki/Great_circle fn destination(&self, origin: Point, bearing: F, meters: F) -> Point { let center_lng = origin.x().to_radians(); let center_lat = origin.y().to_radians(); @@ -216,7 +236,7 @@ impl Destination for Haversine { } } -impl Distance, Point> for Haversine { +impl Distance, Point> for HaversineMeasure { /// Determine the distance between two points using the [haversine formula]. /// /// # Units @@ -228,13 +248,13 @@ impl Distance, Point> for Haversin /// /// ``` /// # use approx::assert_relative_eq; - /// use geo::{HAVERSINE, Distance}; + /// use geo::{Haversine, Distance}; /// use geo::Point; /// /// let new_york_city = Point::new(-74.006f64, 40.7128f64); /// let london = Point::new(-0.1278f64, 51.5074f64); /// - /// let distance = HAVERSINE.distance(new_york_city, london); + /// let distance = Haversine.distance(new_york_city, london); /// /// assert_relative_eq!( /// 5_570_230., // meters @@ -242,11 +262,6 @@ impl Distance, Point> for Haversin /// ); /// ``` /// - /// # References - /// - /// *Note*: this implementation uses a mean earth radius of 6371.088 km, based on the [recommendation of - /// the IUGG](ftp://athena.fsv.cvut.cz/ZFG/grs80-Moritz.pdf) - /// /// [haversine formula]: https://en.wikipedia.org/wiki/Haversine_formula fn distance(&self, origin: Point, destination: Point) -> F { let two = F::one() + F::one(); @@ -264,23 +279,23 @@ impl Distance, Point> for Haversin /// Interpolate Point(s) along a [great circle]. /// /// [great circle]: https://en.wikipedia.org/wiki/Great_circle -impl InterpolatePoint for Haversine { +impl InterpolatePoint for HaversineMeasure { /// Returns a new Point along a [great circle] between two existing points. /// /// # Examples /// /// ``` /// # use approx::assert_relative_eq; - /// use geo::{HAVERSINE, InterpolatePoint}; + /// use geo::{Haversine, InterpolatePoint}; /// use geo::Point; /// /// let p1 = Point::new(10.0, 20.0); /// let p2 = Point::new(125.0, 25.0); /// - /// let closer_to_p1 = HAVERSINE.point_at_distance_between(p1, p2, 100_000.0); + /// let closer_to_p1 = Haversine.point_at_distance_between(p1, p2, 100_000.0); /// assert_relative_eq!(closer_to_p1, Point::new(10.81, 20.49), epsilon = 1.0e-2); /// - /// let closer_to_p2 = HAVERSINE.point_at_distance_between(p1, p2, 10_000_000.0); + /// let closer_to_p2 = Haversine.point_at_distance_between(p1, p2, 10_000_000.0); /// assert_relative_eq!(closer_to_p2, Point::new(112.33, 30.57), epsilon = 1.0e-2); /// ``` /// @@ -301,19 +316,19 @@ impl InterpolatePoint for Haversine { /// /// ``` /// # use approx::assert_relative_eq; - /// use geo::{HAVERSINE, InterpolatePoint}; + /// use geo::{Haversine, InterpolatePoint}; /// use geo::Point; /// /// let p1 = Point::new(10.0, 20.0); /// let p2 = Point::new(125.0, 25.0); /// - /// let closer_to_p1 = HAVERSINE.point_at_ratio_between(p1, p2, 0.1); + /// let closer_to_p1 = Haversine.point_at_ratio_between(p1, p2, 0.1); /// assert_relative_eq!(closer_to_p1, Point::new(19.52, 25.27), epsilon = 1.0e-2); /// - /// let closer_to_p2 = HAVERSINE.point_at_ratio_between(p1, p2, 0.9); + /// let closer_to_p2 = Haversine.point_at_ratio_between(p1, p2, 0.9); /// assert_relative_eq!(closer_to_p2, Point::new(114.72, 29.65), epsilon = 1.0e-2); /// - /// let midpoint = HAVERSINE.point_at_ratio_between(p1, p2, 0.5); + /// let midpoint = Haversine.point_at_ratio_between(p1, p2, 0.5); /// assert_relative_eq!(midpoint, Point::new(65.87, 37.62), epsilon = 1.0e-2); /// ``` /// @@ -473,28 +488,28 @@ mod tests { fn north() { let origin = Point::new(0.0, 0.0); let destination = Point::new(0.0, 1.0); - assert_relative_eq!(0.0, HAVERSINE.bearing(origin, destination)); + assert_relative_eq!(0.0, Haversine.bearing(origin, destination)); } #[test] fn east() { let origin = Point::new(0.0, 0.0); let destination = Point::new(1.0, 0.0); - assert_relative_eq!(90.0, HAVERSINE.bearing(origin, destination)); + assert_relative_eq!(90.0, Haversine.bearing(origin, destination)); } #[test] fn south() { let origin = Point::new(0.0, 0.0); let destination = Point::new(0.0, -1.0); - assert_relative_eq!(180.0, HAVERSINE.bearing(origin, destination)); + assert_relative_eq!(180.0, Haversine.bearing(origin, destination)); } #[test] fn west() { let origin = Point::new(0.0, 0.0); let destination = Point::new(-1.0, 0.0); - assert_relative_eq!(270.0, HAVERSINE.bearing(origin, destination)); + assert_relative_eq!(270.0, Haversine.bearing(origin, destination)); } } @@ -507,7 +522,7 @@ mod tests { let bearing = 0.0; assert_relative_eq!( Point::new(0.0, 0.899320363724538), - HAVERSINE.destination(origin, bearing, 100_000.0) + Haversine.destination(origin, bearing, 100_000.0) ); } @@ -517,7 +532,7 @@ mod tests { let bearing = 90.0; assert_relative_eq!( Point::new(0.8993203637245415, 5.506522912913066e-17), - HAVERSINE.destination(origin, bearing, 100_000.0) + Haversine.destination(origin, bearing, 100_000.0) ); } @@ -527,7 +542,7 @@ mod tests { let bearing = 180.0; assert_relative_eq!( Point::new(0.0, -0.899320363724538), - HAVERSINE.destination(origin, bearing, 100_000.0) + Haversine.destination(origin, bearing, 100_000.0) ); } @@ -537,7 +552,7 @@ mod tests { let bearing = 270.0; assert_relative_eq!( Point::new(-0.8993203637245415, -1.6519568738739197e-16), - HAVERSINE.destination(origin, bearing, 100_000.0) + Haversine.destination(origin, bearing, 100_000.0) ); } } @@ -550,7 +565,7 @@ mod tests { let new_york_city = Point::new(-74.006f64, 40.7128f64); let london = Point::new(-0.1278f64, 51.5074f64); - let distance = HAVERSINE.distance(new_york_city, london); + let distance = Haversine.distance(new_york_city, london); assert_relative_eq!( 5_570_230., // meters @@ -565,7 +580,7 @@ mod tests { fn point_at_ratio_between_midpoint() { let start = Point::new(10.0, 20.0); let end = Point::new(125.0, 25.0); - let midpoint = HAVERSINE.point_at_ratio_between(start, end, 0.5); + let midpoint = Haversine.point_at_ratio_between(start, end, 0.5); assert_relative_eq!(midpoint, Point::new(65.87394172511485, 37.61809316888599)); } #[test] @@ -573,7 +588,7 @@ mod tests { let start = Point::new(10.0, 20.0); let end = Point::new(125.0, 25.0); let max_dist = 1000000.0; // meters - let route = HAVERSINE + let route = Haversine .points_along_line(start, end, max_dist, true) .collect::>(); assert_eq!(route.len(), 13); @@ -586,7 +601,7 @@ mod tests { let start = Point::new(10.0, 20.0); let end = Point::new(125.0, 25.0); let max_dist = 1000000.0; // meters - let route = HAVERSINE + let route = Haversine .points_along_line(start, end, max_dist, false) .collect::>(); assert_eq!(route.len(), 11); diff --git a/geo/src/algorithm/line_measures/metric_spaces/mod.rs b/geo/src/algorithm/line_measures/metric_spaces/mod.rs index c90a39375..3a61c992b 100644 --- a/geo/src/algorithm/line_measures/metric_spaces/mod.rs +++ b/geo/src/algorithm/line_measures/metric_spaces/mod.rs @@ -5,7 +5,7 @@ mod geodesic; pub use geodesic::Geodesic; mod haversine; -pub use haversine::{Haversine, HAVERSINE}; +pub use haversine::{Haversine, HaversineMeasure}; mod rhumb; pub use rhumb::Rhumb; diff --git a/geo/src/algorithm/line_measures/mod.rs b/geo/src/algorithm/line_measures/mod.rs index e3bdb66b1..fed1ba2ad 100644 --- a/geo/src/algorithm/line_measures/mod.rs +++ b/geo/src/algorithm/line_measures/mod.rs @@ -1,4 +1,4 @@ -//! Line measurements like [`Bearing`] and [`Distance`] for various metric spaces like [`Euclidean`], [`Haversine`], [`Geodesic`], and [`Rhumb`]. +//! Line measurements like [`Bearing`] and [`Distance`] for various metric spaces like [`Euclidean`], [`HaversineMeasure`], [`Geodesic`], and [`Rhumb`]. mod bearing; pub use bearing::Bearing; @@ -19,4 +19,4 @@ mod densify; pub use densify::{Densifiable, Densify}; pub mod metric_spaces; -pub use metric_spaces::{Euclidean, Geodesic, Haversine, Rhumb, HAVERSINE}; +pub use metric_spaces::{Euclidean, Geodesic, Haversine, HaversineMeasure, Rhumb}; diff --git a/geo/src/algorithm/linestring_segment.rs b/geo/src/algorithm/linestring_segment.rs index 11d33bfb4..9d2691ab7 100644 --- a/geo/src/algorithm/linestring_segment.rs +++ b/geo/src/algorithm/linestring_segment.rs @@ -1,7 +1,7 @@ use crate::algorithm::{Densify, Length, LineInterpolatePoint, LinesIter}; use crate::geometry::{Coord, LineString, MultiLineString}; use crate::line_measures::Euclidean; -use crate::HAVERSINE; +use crate::Haversine; /// Segments a LineString into `segment_count` equal length LineStrings as a MultiLineString /// using Euclidean distance calculations. See `LineStringSegmentizeHaversine` @@ -115,7 +115,7 @@ implement_segmentize!(LineStringSegmentize, line_segmentize, Euclidean); implement_segmentize!( LineStringSegmentizeHaversine, line_segmentize_haversine, - HAVERSINE + Haversine ); #[cfg(test)] @@ -327,7 +327,7 @@ mod test { let lens = segments .0 .iter() - .map(|li| HAVERSINE.length(li)) + .map(|li| Haversine.length(li)) .collect::>(); let epsilon = 1e-6; // 6th decimal place which is micrometers @@ -343,7 +343,7 @@ mod test { ] .into(); - assert_relative_eq!(HAVERSINE.length(&linestring), 83.3523000093029); + assert_relative_eq!(Haversine.length(&linestring), 83.3523000093029); let n = 8; @@ -351,8 +351,8 @@ mod test { // different at 12th decimal which is a picometer assert_relative_eq!( - HAVERSINE.length(&linestring), - HAVERSINE.length(&segments), + Haversine.length(&linestring), + Haversine.length(&segments), epsilon = 1e-11 ); } diff --git a/geo/src/algorithm/mod.rs b/geo/src/algorithm/mod.rs index 57f977420..c4e5ca138 100644 --- a/geo/src/algorithm/mod.rs +++ b/geo/src/algorithm/mod.rs @@ -187,7 +187,7 @@ pub mod lines_iter; pub use lines_iter::LinesIter; pub mod line_measures; -pub use line_measures::metric_spaces::{Euclidean, Geodesic, Haversine, Rhumb, HAVERSINE}; +pub use line_measures::metric_spaces::{Euclidean, Geodesic, Haversine, HaversineMeasure, Rhumb}; pub use line_measures::{Bearing, Densify, Destination, Distance, InterpolatePoint, Length}; /// Split a LineString into n segments diff --git a/geo/src/lib.rs b/geo/src/lib.rs index bcfc2feef..36cbdf93c 100644 --- a/geo/src/lib.rs +++ b/geo/src/lib.rs @@ -213,7 +213,7 @@ //! //! [Euclidean plane]: https://en.wikipedia.org/wiki/Euclidean_plane //! [`geo-types`]: https://crates.io/crates/geo-types -//! [haversine formula]: https://en.wikipedia.org/wiki/Haversine_formula// +//! [haversine formula]: https://en.wikipedia.org/wiki/Haversine_formula //! [`proj` crate]: https://github.com/georust/proj //! [geojson crate]: https://crates.io/crates/geojson //! [Karney (2013)]: https://arxiv.org/pdf/1109.4448.pdf From 7a6860461cf617dd250225c8f294915ab167ef37 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Fri, 31 Jan 2025 12:16:12 -0800 Subject: [PATCH 8/9] NBD: put related numerical constants together --- geo/src/algorithm/line_measures/metric_spaces/haversine.rs | 4 ++-- geo/src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/geo/src/algorithm/line_measures/metric_spaces/haversine.rs b/geo/src/algorithm/line_measures/metric_spaces/haversine.rs index 4eef331b4..68c49e626 100644 --- a/geo/src/algorithm/line_measures/metric_spaces/haversine.rs +++ b/geo/src/algorithm/line_measures/metric_spaces/haversine.rs @@ -2,7 +2,7 @@ use num_traits::FromPrimitive; use super::super::{Bearing, Destination, Distance, InterpolatePoint}; use crate::utils::normalize_longitude; -use crate::{CoordFloat, Point, MEAN_EARTH_RADIUS}; +use crate::{CoordFloat, Point}; /// Use the [`Haversine`] constant (an instance of `HaversineMeasure`) rather than building your own /// customized [`HaversineMeasure`] for standard spherical Earth measurements. @@ -124,7 +124,7 @@ impl HaversineMeasure { /// /// [Moritz (2000)]: https://sci-hub.se/https://doi.org/10.1007/s001900050278 pub const GRS80_MEAN_RADIUS: Self = Self { - radius: MEAN_EARTH_RADIUS, + radius: 6_371_008.8, }; /// A sphere with the same surface area as the GRS80 ellipsoid, having radius `R₂`, diff --git a/geo/src/lib.rs b/geo/src/lib.rs index 36cbdf93c..2b9073dad 100644 --- a/geo/src/lib.rs +++ b/geo/src/lib.rs @@ -268,7 +268,7 @@ extern crate log; /// - /// - /// - -const MEAN_EARTH_RADIUS: f64 = 6371008.8; +const MEAN_EARTH_RADIUS: f64 = HaversineMeasure::GRS80_MEAN_RADIUS.radius(); // Radius of Earth at the equator in meters (derived from the WGS-84 ellipsoid) const EQUATORIAL_EARTH_RADIUS: f64 = 6_378_137.0; From ce3f8d9c06310953121806933a022a4c8605b3a3 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Fri, 31 Jan 2025 13:10:50 -0800 Subject: [PATCH 9/9] fixup botched merge --- geo/src/algorithm/line_measures/destination.rs | 6 +++--- geo/src/algorithm/line_measures/distance.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/geo/src/algorithm/line_measures/destination.rs b/geo/src/algorithm/line_measures/destination.rs index eb618cfea..3c078fa71 100644 --- a/geo/src/algorithm/line_measures/destination.rs +++ b/geo/src/algorithm/line_measures/destination.rs @@ -22,9 +22,9 @@ pub trait Destination { /// /// let point = Point::new(0.0, 0.0); /// - /// assert_relative_eq!(Haversine::destination(point, 45.0, 111_111.0), Point::new(0.706607921147679, 0.7065541919063233)); - /// assert_relative_eq!(Geodesic::destination(point, 45.0, 111_111.0), Point::new(0.7058183774535367, 0.7105205988658333)); - /// assert_relative_eq!(Rhumb::destination(point, 45.0, 111_111.0), Point::new(0.706590011673029, 0.7065721019258285)); + /// assert_relative_eq!(Haversine.destination(point, 45.0, 111_111.0), Point::new(0.706607921147679, 0.7065541919063233)); + /// assert_relative_eq!(Geodesic.destination(point, 45.0, 111_111.0), Point::new(0.7058183774535367, 0.7105205988658333)); + /// assert_relative_eq!(Rhumb.destination(point, 45.0, 111_111.0), Point::new(0.706590011673029, 0.7065721019258285)); /// ``` /// /// [`metric_spaces`]: super::metric_spaces diff --git a/geo/src/algorithm/line_measures/distance.rs b/geo/src/algorithm/line_measures/distance.rs index ad7f62f9e..e0ac4a44d 100644 --- a/geo/src/algorithm/line_measures/distance.rs +++ b/geo/src/algorithm/line_measures/distance.rs @@ -16,12 +16,12 @@ pub trait Distance { /// let p1: Point = Point::new(0.0, 0.0); /// let p2: Point = Point::new(0.0, 2.0); /// - /// assert_eq!(Euclidean::distance(p1, p2), 2.0); + /// assert_eq!(Euclidean.distance(p1, p2), 2.0); /// /// // The units of the output depend on the metric space. /// // In the case of [`Haversine`], it's meters. /// // See the documentation for each metric space for details. - /// assert_eq!(Haversine::distance(p1, p2).round(), 222_390.0); + /// assert_eq!(Haversine.distance(p1, p2).round(), 222_390.0); /// ``` fn distance(&self, origin: Origin, destination: Destination) -> F; }