Skip to content

Commit

Permalink
Unified Length trait, generic over MetricSpace
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelkirk committed Oct 15, 2024
1 parent 1cafb00 commit 3cb3f0e
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 0 deletions.
4 changes: 4 additions & 0 deletions geo/src/algorithm/euclidean_length.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ use crate::{CoordFloat, Line, LineString, MultiLineString};

/// Calculation of the length

#[deprecated(
since = "0.29.0",
note = "Please use the `line.length::<Euclidean>()` via the `Length` trait instead."
)]
pub trait EuclideanLength<T, RHS = Self> {
/// Calculation of the length of a Line
///
Expand Down
4 changes: 4 additions & 0 deletions geo/src/algorithm/geodesic_length.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use crate::{Distance, Geodesic, Line, LineString, MultiLineString};

#[deprecated(
since = "0.29.0",
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.
///
/// This uses the geodesic measurement methods given by [Karney (2013)]. As opposed to older methods
Expand Down
4 changes: 4 additions & 0 deletions geo/src/algorithm/haversine_length.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ use num_traits::FromPrimitive;
use crate::{CoordFloat, Line, LineString, MultiLineString};
use crate::{Distance, Haversine};

#[deprecated(
since = "0.29.0",
note = "Please use the `line.length::<Haversine>()` via the `Length` trait instead."
)]
/// Determine the length of a geometry using the [haversine formula].
///
/// [haversine formula]: https://en.wikipedia.org/wiki/Haversine_formula
Expand Down
130 changes: 130 additions & 0 deletions geo/src/algorithm/line_measures/length.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
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).
///
/// # Examples
/// ```
/// use geo::algorithm::line_measures::{Length, 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 (
/// -47.9292 -15.7801f64,
/// -58.4173 -34.6118,
/// -70.6483 -33.4489
/// ));
/// assert_eq!(line_string_lon_lat.length::<Haversine>().round(), 3_474_956.0);
/// ```
pub trait Length<F: CoordFloat> {
fn length<MetricSpace: Distance<F, Point<F>, Point<F>>>(&self) -> F;
}

impl<F: CoordFloat> Length<F> for Line<F> {
fn length<MetricSpace: Distance<F, Point<F>, Point<F>>>(&self) -> F {
MetricSpace::distance(self.start_point(), self.end_point())
}
}

impl<F: CoordFloat> Length<F> for LineString<F> {
fn length<MetricSpace: Distance<F, Point<F>, Point<F>>>(&self) -> F {
let mut length = F::zero();
for line in self.lines() {
length = length + line.length::<MetricSpace>();
}
length
}
}

impl<F: CoordFloat> Length<F> for MultiLineString<F> {
fn length<MetricSpace: Distance<F, Point<F>, Point<F>>>(&self) -> F {
let mut length = F::zero();
for line in self {
length = length + line.length::<MetricSpace>();
}
length
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::{coord, Euclidean, Geodesic, Haversine, Rhumb};

#[test]
fn lines() {
// london to paris
let line = Line::new(
coord!(x: -0.1278f64, y: 51.5074),
coord!(x: 2.3522, y: 48.8566),
);

assert_eq!(
343_923., // meters
line.length::<Geodesic>().round()
);
assert_eq!(
341_088., // meters
line.length::<Rhumb>().round()
);
assert_eq!(
343_557., // meters
line.length::<Haversine>().round()
);

// computing Euclidean length of an unprojected (lng/lat) line gives a nonsense answer
assert_eq!(
4., // nonsense!
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::<Euclidean>().round());
}

#[test]
fn line_strings() {
let line_string = LineString::new(vec![
coord!(x: -58.3816f64, y: -34.6037), // Buenos Aires, Argentina
coord!(x: -77.0428, y: -12.0464), // Lima, Peru
coord!(x: -47.9292, y: -15.7801), // Brasília, Brazil
]);

assert_eq!(
6_302_220., // meters
line_string.length::<Geodesic>().round()
);
assert_eq!(
6_332_790., // meters
line_string.length::<Rhumb>().round()
);
assert_eq!(
6_304_387., // meters
line_string.length::<Haversine>().round()
);

// computing Euclidean length of an unprojected (lng/lat) gives a nonsense answer
assert_eq!(
59., // nonsense!
line_string.length::<Euclidean>().round()
);
// EPSG:102033
let projected_line_string = LineString::from(vec![
coord!(x: 143042.46f64, y: -1932485.45), // Buenos Aires, Argentina
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()
);
}
}
3 changes: 3 additions & 0 deletions geo/src/algorithm/line_measures/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,8 @@ pub use distance::Distance;
mod interpolate_point;
pub use interpolate_point::InterpolatePoint;

mod length;
pub use length::Length;

pub mod metric_spaces;
pub use metric_spaces::{Euclidean, Geodesic, Haversine, Rhumb};
4 changes: 4 additions & 0 deletions geo/src/algorithm/rhumb/length.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ use num_traits::FromPrimitive;

use crate::{CoordFloat, Distance, Line, LineString, MultiLineString, Rhumb};

#[deprecated(
since = "0.29.0",
note = "Please use the `line.length::<Rhumb>()` via the `Length` trait instead."
)]
/// Determine the length of a geometry assuming each segment is a [rhumb line].
///
/// [rhumb line]: https://en.wikipedia.org/wiki/Rhumb_line
Expand Down

0 comments on commit 3cb3f0e

Please sign in to comment.