diff --git a/geo/CHANGES.md b/geo/CHANGES.md index bbc25701b..27aabb62b 100644 --- a/geo/CHANGES.md +++ b/geo/CHANGES.md @@ -7,20 +7,24 @@ // 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