diff --git a/geo/src/algorithm/linestring_segment.rs b/geo/src/algorithm/linestring_segment.rs index 426732c16..48e4f3f79 100644 --- a/geo/src/algorithm/linestring_segment.rs +++ b/geo/src/algorithm/linestring_segment.rs @@ -8,28 +8,14 @@ use crate::{Coord, Densify, EuclideanLength, LineString, LinesIter, MultiLineStr /// /// # Examples /// ``` -/// use geo::{LineString, MultiLineString, LineStringSegmentize, Coord}; +/// use geo::{LineString, MultiLineString, LineStringSegmentize}; /// // Create a simple line string /// let lns: LineString = vec![[0.0, 0.0], [1.0, 2.0], [3.0, 6.0]].into(); -/// // Segment it into 6 LineStrings inside of a MultiLineString -/// let segmentized = lns.line_segmentize(6).unwrap(); -/// -/// // Recreate the MultiLineString from scratch -/// // this is the inner vector used to create the MultiLineString -/// let all_lines = vec![ -/// LineString::new(vec![Coord { x: 0.0, y: 0.0 }, Coord { x: 0.5, y: 1.0 }]), -/// LineString::new(vec![Coord { x: 0.5, y: 1.0 }, Coord { x: 1.0, y: 2.0 }]), -/// LineString::new(vec![Coord { x: 1.0, y: 2.0 }, Coord { x: 1.5, y: 3.0 }]), -/// LineString::new(vec![Coord { x: 1.5, y: 3.0 }, Coord { x: 2.0, y: 4.0 }]), -/// LineString::new(vec![Coord { x: 2.0, y: 4.0 }, Coord { x: 2.5, y: 5.0 }]), -/// LineString::new(vec![Coord { x: 2.5, y: 5.0 }, Coord { x: 3.0, y: 6.0 }]) -/// ]; -/// -/// // Create the MultiLineString -/// let mlns = MultiLineString::new(all_lines); -/// -/// // Compare the two -/// assert_eq!(mlns, segmentized); +/// // Segment it into n LineStrings inside of a MultiLineString +/// let n = 6; +/// let segmentized = lns.line_segmentize(n).unwrap(); +/// // Compare the number of elements +/// assert_eq!(n, segmentized.0.len()); ///``` pub trait LineStringSegmentize { fn line_segmentize(&self, n: usize) -> Option; @@ -63,7 +49,9 @@ impl LineStringSegmentize for LineString { // densify the LineString so that each `Line` segment is not longer // than the segment length ensuring that we will never partition one // Line more than once. - let densified = self.densify(segment_length); + // in the case of super small distances floating point errors can arise + // the solution is to subtract by f64::EPSILON for these edge cases. + let densified = self.densify(segment_length - f64::EPSILON); // if the densified line is exactly equal to the number of requested // segments, return early. This will happen when a LineString has @@ -305,4 +293,20 @@ mod test { let segments = linestring.line_segmentize(2).unwrap(); assert_eq!(segments.0.len(), 2) } + + #[test] + fn tiny_distances() { + // this test is to ensure that at super small distances + // the number of units is still the specified one. + let linestring: LineString = vec![ + [-3.19416, 55.95524], + [-3.19352, 55.95535], + [-3.19288, 55.95546], + ] + .into(); + + let n = 8; + let segments = linestring.line_segmentize(n).unwrap(); + assert_eq!(segments.0.len(), n) + } }