Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LineStringSegmentize returning n - 1 linestrings at small distances #1106

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 25 additions & 21 deletions geo/src/algorithm/linestring_segment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<f64> = 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<MultiLineString>;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
}
}