From b668088f180b414a81c1e7cc4be572a3d1741db6 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sat, 19 Aug 2023 17:19:43 -0400 Subject: [PATCH 01/12] add segmentize functionality --- geo/CHANGES.md | 1 + geo/src/algorithm/linestring_segment.rs | 189 ++++++++++++++++++++++++ geo/src/algorithm/mod.rs | 4 + geo/src/lib.rs | 5 +- 4 files changed, 197 insertions(+), 2 deletions(-) create mode 100644 geo/src/algorithm/linestring_segment.rs diff --git a/geo/CHANGES.md b/geo/CHANGES.md index dac3bb734..db2ec6885 100644 --- a/geo/CHANGES.md +++ b/geo/CHANGES.md @@ -2,6 +2,7 @@ ## Unreleased +* Add `LineStringSegmentize` trait to split a single `LineString` into `n` `LineStrings` as a `MultiLineString` * Add `EuclideanDistance` implementations for all remaining geometries. * * Add `HausdorffDistance` algorithm trait to calculate the Hausdorff distance between any two geometries. diff --git a/geo/src/algorithm/linestring_segment.rs b/geo/src/algorithm/linestring_segment.rs new file mode 100644 index 000000000..44fcef4a5 --- /dev/null +++ b/geo/src/algorithm/linestring_segment.rs @@ -0,0 +1,189 @@ +use crate::line_interpolate_point::LineInterpolatePoint; +use crate::{Coord, EuclideanLength, LineString, LinesIter, MultiLineString}; + +/// Segments a LineString into `n` LineStrings as a MultiLineString. +/// `None` will be returned when `n` is equal to 0, `n` is larger than the +/// number of `Line`s that make up the `LineString`, or when a point +/// cannot be interpolated on a `Line` segment. +pub trait LineStringSegmentize { + fn line_segmentize(&self, n: usize) -> Option; +} + +impl LineStringSegmentize for LineString { + fn line_segmentize(&self, n: usize) -> Option { + let n_lines = self.lines().count(); + + // Return None if n is 0 or the maximum usize + if n == usize::MIN || n == usize::MAX { + return None; + } else if n_lines < n { + // if n is greater than the number of Lines in the + // LineString return None + return None; + } else if n_lines == n { + // if the number of line segments equals n then return the + // lines as LineStrings + let lns = self + .lines_iter() + .map(|l| LineString::from(l)) + .collect::>(); + + return Some(MultiLineString::new(lns)); + } else if n == 1 { + let mlns = MultiLineString::from(self.clone()); + return Some(mlns); + } + + // Convert X into an iterator of `Lines` + let lns = self.lines_iter().peekable(); + + // Vec to allocate the new LineString segments Coord Vec + // will be iterated over at end to create new vecs + let mut res_coords: Vec> = Vec::with_capacity(n); + + // calculate total length to track cumulative against + let total_length = self.euclidean_length().abs(); + + // tracks total length + let mut cum_length = 0_f64; + + // calculate the target fraction for the first iteration + // fraction will change based on each iteration + // let mut fraction = (1_f64 / (n as f64)) * (idx as f64); + let segment_prop = (1_f64) / (n as f64); + let segment_length = total_length * segment_prop; + // let mut fraction = segment_prop; + + // // fractional length will change dependent upon which `n` we're on. + // let mut fractional_length = total_length * fraction; + + // instantiate the first Vec + let mut ln_vec: Vec = Vec::new(); + + // push the first coord in + // each subsequent coord will be the end point + // let c1 = lns.peek(); + // ln_vec.push(c1.unwrap().start); + //ln_vec.push(lns.nth(0).clone().unwrap().start); + + // iterate through each line segment in the LineString + for (i, segment) in lns.enumerate() { + // first line string push the first coord immediately + if i == 0 { + ln_vec.push(segment.start) + } + + let length = segment.euclidean_length().abs(); + + // update cumulative length + cum_length += length; + + if (cum_length >= segment_length) && (i != (n_lines - 1)) { + let remainder = cum_length - segment_length; + // if we get None, we exit the function and return None + let Some(endpoint) = segment.line_interpolate_point((length - remainder) / length) else { + return None + }; + + // add final coord to ln_vec + ln_vec.push(endpoint.into()); + + // now we drain all elements from the vector into an iterator + // this will be collected into a vector to be pushed into the + // results coord vec of vec + let to_push = ln_vec.drain(..); + + // now collect & push this vector into the results vector + res_coords.push(to_push.collect::>()); + + // now add the last endpoint as the first coord + // and the endpoint of the linesegment as well only + // if i != n_lines + if i != n_lines { + ln_vec.push(endpoint.into()); + } + + // // we need to adjust our fraction and fractional length + // fraction += segment_prop; + // fractional_length = total_length * fraction; + cum_length = remainder; + } + + // push the end coordinate into the Vec to continue + // building the linestring + ln_vec.push(segment.end); + } + + // push the last linestring vector which isn't done by the for loop + res_coords.push(ln_vec); + + // collect the coords into vectors of LineStrings so we can createa + // a multi linestring + let res_lines = res_coords + .into_iter() + .map(|xi| LineString::new(xi)) + .collect::>(); + + Some(MultiLineString::new(res_lines)) + } +} + +// rather than calculating the sum up until 1, i should calculate +// the amount per segment. Once we exceed that, we take the remainder +// of cum_length - segment prop + +#[cfg(test)] +mod test { + use super::*; + use crate::{EuclideanLength, LineString}; + + #[test] + // that 0 returns None and that usize::MAX returns None + fn n_is_zero() { + let linestring: LineString = vec![[-1.0, 0.0], [0.5, 1.0], [1.0, 2.0]].into(); + let segments = linestring.line_segmentize(0); + assert_eq!(segments.is_none(), true) + } + + #[test] + fn n_is_max() { + let linestring: LineString = vec![[-1.0, 0.0], [0.5, 1.0], [1.0, 2.0]].into(); + let segments = linestring.line_segmentize(usize::MAX); + assert_eq!(segments.is_none(), true) + } + + #[test] + // test that n > n_lines returns None + fn n_greater_than_lines() { + let linestring: LineString = vec![[-1.0, 0.0], [0.5, 1.0], [1.0, 2.0]].into(); + let segments = linestring.line_segmentize(3); + assert_eq!(segments.is_none(), true) + } + + #[test] + // identical line_iter to original + fn line_iter() { + let linestring: LineString = vec![[-1.0, 0.0], [0.5, 1.0], [1.0, 2.0], [1.0, 3.0]].into(); + + let segments = linestring.line_segmentize(3).unwrap(); + + assert_eq!(linestring.lines_iter().eq(segments.lines_iter()), true) + } + + #[test] + // test the cumulative length is the same + fn cumul_length() { + let linestring: LineString = vec![[0.0, 0.0], [1.0, 1.0], [1.0, 2.0], [3.0, 3.0]].into(); + let segments = linestring.line_segmentize(2).unwrap(); + + assert_eq!(linestring.euclidean_length(), segments.euclidean_length()) + } + + #[test] + fn n_elems() { + let linestring: LineString = vec![[0.0, 0.0], [1.0, 1.0], [1.0, 2.0], [3.0, 3.0]].into(); + let segments = linestring.line_segmentize(2).unwrap(); + println!("{:?}", segments.0); + assert_eq!(segments.0.len(), 2) + } +} diff --git a/geo/src/algorithm/mod.rs b/geo/src/algorithm/mod.rs index bc2555431..0d5f2b0bd 100644 --- a/geo/src/algorithm/mod.rs +++ b/geo/src/algorithm/mod.rs @@ -180,6 +180,10 @@ pub use line_locate_point::LineLocatePoint; pub mod lines_iter; pub use lines_iter::LinesIter; +/// Split a LineString into n segments +pub mod linestring_segment; +pub use linestring_segment::LineStringSegmentize; + /// Apply a function to all `Coord`s of a `Geometry`. pub mod map_coords; pub use map_coords::{MapCoords, MapCoordsInPlace}; diff --git a/geo/src/lib.rs b/geo/src/lib.rs index 4f61f9b9e..ba313ea96 100644 --- a/geo/src/lib.rs +++ b/geo/src/lib.rs @@ -150,13 +150,14 @@ //! ## Miscellaneous //! //! - **[`Centroid`](Centroid)**: Calculate the centroid of a geometry +//! - **[`ChaikinSmoothing`](ChaikinSmoothing)**: Smoothen `LineString`, `Polygon`, `MultiLineString` and `MultiPolygon` using Chaikins algorithm. +//! - **[`Densify`](Densify)**: Densify linear geometry components by interpolating points //! - **[`GeodesicDestination`](GeodesicDestination)**: Given a start point, bearing, and distance, calculate the destination point on a [geodesic](https://en.wikipedia.org/wiki/Geodesics_on_an_ellipsoid) //! - **[`GeodesicIntermediate`](GeodesicIntermediate)**: Calculate intermediate points on a [geodesic](https://en.wikipedia.org/wiki/Geodesics_on_an_ellipsoid) //! - **[`HaversineDestination`]**: Given a start point, bearing, and distance, calculate the destination point on a sphere //! - **[`HaversineIntermediate`](HaversineIntermediate)**: Calculate intermediate points on a sphere //! - **[`proj`](proj)**: Project geometries with the `proj` crate (requires the `use-proj` feature) -//! - **[`ChaikinSmoothing`](ChaikinSmoothing)**: Smoothen `LineString`, `Polygon`, `MultiLineString` and `MultiPolygon` using Chaikins algorithm. -//! - **[`Densify`](Densify)**: Densify linear geometry components by interpolating points +//! - **[`LineStringSegmentize`](LineStringSegmentize)**: Segment a LineString into `n` segments. //! - **[`Transform`](Transform)**: Transform a geometry using Proj. //! - **[`RemoveRepeatedPoints`](RemoveRepeatedPoints)**: Remove repeated points from a geometry. //! From dc7d8bcb76acf0b8b983988bc38f9b6d5e972bba Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sat, 19 Aug 2023 17:23:05 -0400 Subject: [PATCH 02/12] modify test to be relative_eq assertion --- geo/src/algorithm/linestring_segment.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/geo/src/algorithm/linestring_segment.rs b/geo/src/algorithm/linestring_segment.rs index 44fcef4a5..79caad1ce 100644 --- a/geo/src/algorithm/linestring_segment.rs +++ b/geo/src/algorithm/linestring_segment.rs @@ -176,14 +176,13 @@ mod test { let linestring: LineString = vec![[0.0, 0.0], [1.0, 1.0], [1.0, 2.0], [3.0, 3.0]].into(); let segments = linestring.line_segmentize(2).unwrap(); - assert_eq!(linestring.euclidean_length(), segments.euclidean_length()) + assert_relative_eq!(linestring.euclidean_length(), segments.euclidean_length(), epsilon = f64::EPSILON) } #[test] fn n_elems() { let linestring: LineString = vec![[0.0, 0.0], [1.0, 1.0], [1.0, 2.0], [3.0, 3.0]].into(); let segments = linestring.line_segmentize(2).unwrap(); - println!("{:?}", segments.0); assert_eq!(segments.0.len(), 2) } } From 5d29811c45bbc190b1fa1ac7b04cfdfe9a0f8d87 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sat, 19 Aug 2023 17:52:04 -0400 Subject: [PATCH 03/12] address cargo fmt CI error --- geo/src/algorithm/linestring_segment.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/geo/src/algorithm/linestring_segment.rs b/geo/src/algorithm/linestring_segment.rs index 79caad1ce..eaa147ca9 100644 --- a/geo/src/algorithm/linestring_segment.rs +++ b/geo/src/algorithm/linestring_segment.rs @@ -176,7 +176,11 @@ mod test { let linestring: LineString = vec![[0.0, 0.0], [1.0, 1.0], [1.0, 2.0], [3.0, 3.0]].into(); let segments = linestring.line_segmentize(2).unwrap(); - assert_relative_eq!(linestring.euclidean_length(), segments.euclidean_length(), epsilon = f64::EPSILON) + assert_relative_eq!( + linestring.euclidean_length(), + segments.euclidean_length(), + epsilon = f64::EPSILON + ) } #[test] From 3ea7b318fa9fb9d180a7e670e8a702b3bb9acc5a Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sat, 19 Aug 2023 22:38:14 -0400 Subject: [PATCH 04/12] remove let else to address MSRV of 1.63 and fix CI --- geo/src/algorithm/linestring_segment.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/geo/src/algorithm/linestring_segment.rs b/geo/src/algorithm/linestring_segment.rs index eaa147ca9..5fc01424d 100644 --- a/geo/src/algorithm/linestring_segment.rs +++ b/geo/src/algorithm/linestring_segment.rs @@ -81,9 +81,7 @@ impl LineStringSegmentize for LineString { if (cum_length >= segment_length) && (i != (n_lines - 1)) { let remainder = cum_length - segment_length; // if we get None, we exit the function and return None - let Some(endpoint) = segment.line_interpolate_point((length - remainder) / length) else { - return None - }; + let endpoint = segment.line_interpolate_point((length - remainder) / length)?; // add final coord to ln_vec ln_vec.push(endpoint.into()); From c02a915022c81e09d6fa971d03e86f8c861947ef Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sun, 20 Aug 2023 07:35:57 -0400 Subject: [PATCH 05/12] To please the clippy god, i bow to thee --- geo/src/algorithm/linestring_segment.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/geo/src/algorithm/linestring_segment.rs b/geo/src/algorithm/linestring_segment.rs index 5fc01424d..d4d3a9bad 100644 --- a/geo/src/algorithm/linestring_segment.rs +++ b/geo/src/algorithm/linestring_segment.rs @@ -14,18 +14,14 @@ impl LineStringSegmentize for LineString { let n_lines = self.lines().count(); // Return None if n is 0 or the maximum usize - if n == usize::MIN || n == usize::MAX { - return None; - } else if n_lines < n { - // if n is greater than the number of Lines in the - // LineString return None + if (n == usize::MIN) || (n == usize::MAX) || (n_lines < n) { return None; } else if n_lines == n { // if the number of line segments equals n then return the // lines as LineStrings let lns = self .lines_iter() - .map(|l| LineString::from(l)) + .map(LineString::from) .collect::>(); return Some(MultiLineString::new(lns)); @@ -119,7 +115,7 @@ impl LineStringSegmentize for LineString { // a multi linestring let res_lines = res_coords .into_iter() - .map(|xi| LineString::new(xi)) + .map(LineString::new) .collect::>(); Some(MultiLineString::new(res_lines)) From be7fc935d99d36df1fecd81992f8375b6ef80166 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Sun, 20 Aug 2023 08:42:00 -0400 Subject: [PATCH 06/12] use assert!() over asser_eq(expr, true) --- geo/src/algorithm/linestring_segment.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/geo/src/algorithm/linestring_segment.rs b/geo/src/algorithm/linestring_segment.rs index d4d3a9bad..b8ea17a06 100644 --- a/geo/src/algorithm/linestring_segment.rs +++ b/geo/src/algorithm/linestring_segment.rs @@ -136,14 +136,14 @@ mod test { fn n_is_zero() { let linestring: LineString = vec![[-1.0, 0.0], [0.5, 1.0], [1.0, 2.0]].into(); let segments = linestring.line_segmentize(0); - assert_eq!(segments.is_none(), true) + assert!(segments.is_none()) } #[test] fn n_is_max() { let linestring: LineString = vec![[-1.0, 0.0], [0.5, 1.0], [1.0, 2.0]].into(); let segments = linestring.line_segmentize(usize::MAX); - assert_eq!(segments.is_none(), true) + assert!(segments.is_none()) } #[test] @@ -151,7 +151,7 @@ mod test { fn n_greater_than_lines() { let linestring: LineString = vec![[-1.0, 0.0], [0.5, 1.0], [1.0, 2.0]].into(); let segments = linestring.line_segmentize(3); - assert_eq!(segments.is_none(), true) + assert!(segments.is_none()) } #[test] @@ -161,7 +161,7 @@ mod test { let segments = linestring.line_segmentize(3).unwrap(); - assert_eq!(linestring.lines_iter().eq(segments.lines_iter()), true) + assert!(linestring.lines_iter().eq(segments.lines_iter())) } #[test] From 8e22ffd1fa66effdadd4f4b457bccf2f06574d66 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Tue, 22 Aug 2023 15:22:11 -0400 Subject: [PATCH 07/12] allow n to be more than n lines and run cargo fmt --all --- geo/src/algorithm/linestring_segment.rs | 54 ++++++++++++------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/geo/src/algorithm/linestring_segment.rs b/geo/src/algorithm/linestring_segment.rs index b8ea17a06..0804c3ca8 100644 --- a/geo/src/algorithm/linestring_segment.rs +++ b/geo/src/algorithm/linestring_segment.rs @@ -1,9 +1,8 @@ use crate::line_interpolate_point::LineInterpolatePoint; -use crate::{Coord, EuclideanLength, LineString, LinesIter, MultiLineString}; +use crate::{Coord, Densify, EuclideanLength, LineString, LinesIter, MultiLineString}; -/// Segments a LineString into `n` LineStrings as a MultiLineString. -/// `None` will be returned when `n` is equal to 0, `n` is larger than the -/// number of `Line`s that make up the `LineString`, or when a point +/// Segments a LineString into `n` equal length LineStrings as a MultiLineString. +/// `None` will be returned when `n` is equal to 0 or when a point /// cannot be interpolated on a `Line` segment. pub trait LineStringSegmentize { fn line_segmentize(&self, n: usize) -> Option; @@ -14,8 +13,13 @@ impl LineStringSegmentize for LineString { let n_lines = self.lines().count(); // Return None if n is 0 or the maximum usize - if (n == usize::MIN) || (n == usize::MAX) || (n_lines < n) { + if (n == usize::MIN) || (n == usize::MAX) { return None; + } else if n > n_lines { + let total_len = self.euclidean_length(); + let densified = self.densify(total_len / (n as f64)); + return densified.line_segmentize(n); + // return Some(MultiLineString::new(vec![densified])) } else if n_lines == n { // if the number of line segments equals n then return the // lines as LineStrings @@ -45,23 +49,12 @@ impl LineStringSegmentize for LineString { // calculate the target fraction for the first iteration // fraction will change based on each iteration - // let mut fraction = (1_f64 / (n as f64)) * (idx as f64); let segment_prop = (1_f64) / (n as f64); let segment_length = total_length * segment_prop; - // let mut fraction = segment_prop; - - // // fractional length will change dependent upon which `n` we're on. - // let mut fractional_length = total_length * fraction; // instantiate the first Vec let mut ln_vec: Vec = Vec::new(); - // push the first coord in - // each subsequent coord will be the end point - // let c1 = lns.peek(); - // ln_vec.push(c1.unwrap().start); - //ln_vec.push(lns.nth(0).clone().unwrap().start); - // iterate through each line segment in the LineString for (i, segment) in lns.enumerate() { // first line string push the first coord immediately @@ -92,14 +85,10 @@ impl LineStringSegmentize for LineString { // now add the last endpoint as the first coord // and the endpoint of the linesegment as well only - // if i != n_lines if i != n_lines { ln_vec.push(endpoint.into()); } - // // we need to adjust our fraction and fractional length - // fraction += segment_prop; - // fractional_length = total_length * fraction; cum_length = remainder; } @@ -122,12 +111,10 @@ impl LineStringSegmentize for LineString { } } -// rather than calculating the sum up until 1, i should calculate -// the amount per segment. Once we exceed that, we take the remainder -// of cum_length - segment prop - #[cfg(test)] mod test { + use approx::RelativeEq; + use super::*; use crate::{EuclideanLength, LineString}; @@ -147,11 +134,24 @@ mod test { } #[test] - // test that n > n_lines returns None fn n_greater_than_lines() { let linestring: LineString = vec![[-1.0, 0.0], [0.5, 1.0], [1.0, 2.0]].into(); - let segments = linestring.line_segmentize(3); - assert!(segments.is_none()) + let segments = linestring.line_segmentize(5).unwrap(); + + // assert that there are n linestring segments + assert_eq!(segments.clone().0.len(), 5); + + // assert that the lines are equal length + let lens = segments + .into_iter() + .map(|x| x.euclidean_length()) + .collect::>(); + + let first = lens[0]; + + assert!(lens + .iter() + .all(|x| first.relative_eq(x, f64::EPSILON, 1e-10))) } #[test] From d05f1cd7a4ea69a5a6533456a56d6563c7ff4f7e Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Tue, 22 Aug 2023 15:29:24 -0400 Subject: [PATCH 08/12] address clippy in cloning --- geo/src/algorithm/linestring_segment.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geo/src/algorithm/linestring_segment.rs b/geo/src/algorithm/linestring_segment.rs index 0804c3ca8..1a7d94c04 100644 --- a/geo/src/algorithm/linestring_segment.rs +++ b/geo/src/algorithm/linestring_segment.rs @@ -139,7 +139,7 @@ mod test { let segments = linestring.line_segmentize(5).unwrap(); // assert that there are n linestring segments - assert_eq!(segments.clone().0.len(), 5); + assert_eq!(segments.0.len(), 5); // assert that the lines are equal length let lens = segments From 1898363708d5e8b98a54badff50a24e19d2d1111 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Fri, 22 Sep 2023 16:51:07 -0400 Subject: [PATCH 09/12] address typos / comment suggestions --- geo/src/algorithm/linestring_segment.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/geo/src/algorithm/linestring_segment.rs b/geo/src/algorithm/linestring_segment.rs index 1a7d94c04..caf5509df 100644 --- a/geo/src/algorithm/linestring_segment.rs +++ b/geo/src/algorithm/linestring_segment.rs @@ -19,7 +19,6 @@ impl LineStringSegmentize for LineString { let total_len = self.euclidean_length(); let densified = self.densify(total_len / (n as f64)); return densified.line_segmentize(n); - // return Some(MultiLineString::new(vec![densified])) } else if n_lines == n { // if the number of line segments equals n then return the // lines as LineStrings @@ -57,7 +56,10 @@ impl LineStringSegmentize for LineString { // iterate through each line segment in the LineString for (i, segment) in lns.enumerate() { - // first line string push the first coord immediately + // All iterations only keep track of the second coordinate + // in the Line. We need to push the first coordinate in the + // first line string to ensure the linestring starts at the + // correct place if i == 0 { ln_vec.push(segment.start) } From 8c9489f8f97c92ce16adfbc026e3e3aeb82f2c3f Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Fri, 22 Sep 2023 17:04:02 -0400 Subject: [PATCH 10/12] add example in docs --- geo/src/algorithm/linestring_segment.rs | 32 ++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/geo/src/algorithm/linestring_segment.rs b/geo/src/algorithm/linestring_segment.rs index caf5509df..0aba654b5 100644 --- a/geo/src/algorithm/linestring_segment.rs +++ b/geo/src/algorithm/linestring_segment.rs @@ -4,6 +4,32 @@ use crate::{Coord, Densify, EuclideanLength, LineString, LinesIter, MultiLineStr /// Segments a LineString into `n` equal length LineStrings as a MultiLineString. /// `None` will be returned when `n` is equal to 0 or when a point /// cannot be interpolated on a `Line` segment. +/// +/// +/// # Examples +/// ``` +/// // 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); +///``` pub trait LineStringSegmentize { fn line_segmentize(&self, n: usize) -> Option; } @@ -57,9 +83,9 @@ impl LineStringSegmentize for LineString { // iterate through each line segment in the LineString for (i, segment) in lns.enumerate() { // All iterations only keep track of the second coordinate - // in the Line. We need to push the first coordinate in the - // first line string to ensure the linestring starts at the - // correct place + // in the Line. We need to push the first coordinate in the + // first line string to ensure the linestring starts at the + // correct place` if i == 0 { ln_vec.push(segment.start) } From 76008beae7995efaaef6b2ce293fc3151beb6ef4 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Fri, 22 Sep 2023 17:04:46 -0400 Subject: [PATCH 11/12] fix Chaikin typo that was absolutely mine --- geo/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geo/src/lib.rs b/geo/src/lib.rs index 492fdbf6b..0131dc71e 100644 --- a/geo/src/lib.rs +++ b/geo/src/lib.rs @@ -150,7 +150,7 @@ //! ## Miscellaneous //! //! - **[`Centroid`](Centroid)**: Calculate the centroid of a geometry -//! - **[`ChaikinSmoothing`](ChaikinSmoothing)**: Smoothen `LineString`, `Polygon`, `MultiLineString` and `MultiPolygon` using Chaikins algorithm. +//! - **[`ChaikinSmoothing`](ChaikinSmoothing)**: Smoothen `LineString`, `Polygon`, `MultiLineString` and `MultiPolygon` using Chaikin's algorithm. //! - **[`Densify`](Densify)**: Densify linear geometry components by interpolating points //! - **[`GeodesicDestination`](GeodesicDestination)**: Given a start point, bearing, and distance, calculate the destination point on a [geodesic](https://en.wikipedia.org/wiki/Geodesics_on_an_ellipsoid) //! - **[`GeodesicIntermediate`](GeodesicIntermediate)**: Calculate intermediate points on a [geodesic](https://en.wikipedia.org/wiki/Geodesics_on_an_ellipsoid) From 8caa1c00d2c160ffc5b3d321a43fe9d1ed648f01 Mon Sep 17 00:00:00 2001 From: Josiah Parry Date: Fri, 22 Sep 2023 17:20:20 -0400 Subject: [PATCH 12/12] add use geo:: statement for example --- geo/src/algorithm/linestring_segment.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/geo/src/algorithm/linestring_segment.rs b/geo/src/algorithm/linestring_segment.rs index 0aba654b5..c1ad54973 100644 --- a/geo/src/algorithm/linestring_segment.rs +++ b/geo/src/algorithm/linestring_segment.rs @@ -8,6 +8,7 @@ use crate::{Coord, Densify, EuclideanLength, LineString, LinesIter, MultiLineStr /// /// # Examples /// ``` +/// use geo::{LineString, MultiLineString, LineStringSegmentize, Coord}; /// // 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