From fc4dd785c7c102e52fb5ac8616c0b3da0f7d1c9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Mon, 27 Dec 2021 17:47:21 +0000 Subject: [PATCH 01/12] Add more Coordinate iterators for LineString Hitherto, we only supported looping as opposed to direct iteration (though since .0 is public, we could access the underlying vector anyway) This allows explicit (mutable) iteration via a CoordinatesIter struct --- geo-types/src/line_string.rs | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/geo-types/src/line_string.rs b/geo-types/src/line_string.rs index d3f6d0429..85747cd1b 100644 --- a/geo-types/src/line_string.rs +++ b/geo-types/src/line_string.rs @@ -78,7 +78,7 @@ use std::ops::{Index, IndexMut}; /// let line_string: LineString = coords_iter.collect(); /// ``` /// -/// You can iterate over the coordinates in the `LineString`: +/// You can iterate and loop over the coordinates in the `LineString`, yielding [Coordinate]s: /// /// ``` /// use geo_types::{Coordinate, LineString}; @@ -88,6 +88,8 @@ use std::ops::{Index, IndexMut}; /// Coordinate { x: 10., y: 0. }, /// ]); /// +/// line_string.iter().for_each(|coord| println!("Coordinate x = {}, y = {}", coord.x, coord.y)); +/// /// for coord in line_string { /// println!("Coordinate x = {}, y = {}", coord.x, coord.y); /// } @@ -132,17 +134,49 @@ impl<'a, T: CoordNum> DoubleEndedIterator for PointsIter<'a, T> { } } +/// A [Coordinate] iterator returned by the `iter` method over a [LineString] +#[derive(Debug)] +pub struct CoordinatesIter<'a, T: CoordNum + 'a>(::std::slice::Iter<'a, Coordinate>); + +impl<'a, T: CoordNum> Iterator for CoordinatesIter<'a, T> { + type Item = &'a Coordinate; + + fn next(&mut self) -> Option { + self.0.next() + } +} + +impl<'a, T: CoordNum> DoubleEndedIterator for CoordinatesIter<'a, T> { + fn next_back(&mut self) -> Option { + self.0.next_back() + } +} + impl LineString { /// Return an iterator yielding the coordinates of a `LineString` as `Point`s pub fn points_iter(&self) -> PointsIter { PointsIter(self.0.iter()) } + /// Return an iterator yielding the members of a [LineString] as [Coordinate]s + pub fn iter(&self) -> CoordinatesIter { + CoordinatesIter(self.0.iter()) + } + /// Return an iterator yielding the coordinates of a [LineString] as mutable [Coordinate]s + pub fn iter_mut(&mut self) -> CoordinatesIter { + CoordinatesIter(self.0.iter()) + } + /// Return the coordinates of a `LineString` as a `Vec` of `Point`s pub fn into_points(self) -> Vec> { self.0.into_iter().map(Point).collect() } + /// Return the coordinates of a [LineString] as a `Vec` of [Coordinate]s + pub fn into_coordinates(self) -> Vec> { + self.0.into_iter().collect() + } + /// Return an iterator yielding one `Line` for each line segment /// in the `LineString`. /// From 696253d14bd393b7ab14ef07d37e20642608ea0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Tue, 28 Dec 2021 11:58:40 +0000 Subject: [PATCH 02/12] Implement IntoIter for a borrowed LineString --- geo-types/src/line_string.rs | 11 ++++++++++- geo-types/src/polygon.rs | 4 ++-- geo-types/src/private_utils.rs | 4 ++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/geo-types/src/line_string.rs b/geo-types/src/line_string.rs index 85747cd1b..3599412cc 100644 --- a/geo-types/src/line_string.rs +++ b/geo-types/src/line_string.rs @@ -311,7 +311,16 @@ impl IntoIterator for LineString { } } -/// Mutably iterate over all the [Coordinate](struct.Coordinates.html)s in this `LineString`. +impl<'a, T: CoordNum> IntoIterator for &'a LineString { + type Item = &'a Coordinate; + type IntoIter = CoordinatesIter<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + CoordinatesIter(self.0.iter()) + } +} + +/// Mutably iterate over all the [Coordinate](struct.Coordinates.html)s in this [LineString]. impl<'a, T: CoordNum> IntoIterator for &'a mut LineString { type Item = &'a mut Coordinate; type IntoIter = ::std::slice::IterMut<'a, Coordinate>; diff --git a/geo-types/src/polygon.rs b/geo-types/src/polygon.rs index 60c7a21bf..c1bed2bd9 100644 --- a/geo-types/src/polygon.rs +++ b/geo-types/src/polygon.rs @@ -433,8 +433,8 @@ where .map(|(idx, _)| { let prev_1 = self.previous_vertex(idx); let prev_2 = self.previous_vertex(prev_1); - Point(self.exterior.0[prev_2]) - .cross_prod(Point(self.exterior.0[prev_1]), Point(self.exterior.0[idx])) + Point(self.exterior[prev_2]) + .cross_prod(Point(self.exterior[prev_1]), Point(self.exterior[idx])) }) // accumulate and check cross-product result signs in a single pass // positive implies ccw convexity, negative implies cw convexity diff --git a/geo-types/src/private_utils.rs b/geo-types/src/private_utils.rs index cff4ae392..852301a3a 100644 --- a/geo-types/src/private_utils.rs +++ b/geo-types/src/private_utils.rs @@ -9,7 +9,7 @@ pub fn line_string_bounding_rect(line_string: &LineString) -> Option(line: Line) -> Rect @@ -132,7 +132,7 @@ where } // LineString with one point equal p if line_string.0.len() == 1 { - return point_contains_point(Point(line_string.0[0]), point); + return point_contains_point(Point(line_string[0]), point); } // check if point is a vertex if line_string.0.contains(&point.0) { From 6f4a8525f8346cb836d18c1b2ae93ed2e815b1a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Tue, 28 Dec 2021 11:59:19 +0000 Subject: [PATCH 03/12] Replace doc links --- geo-types/src/line_string.rs | 44 ++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/geo-types/src/line_string.rs b/geo-types/src/line_string.rs index 3599412cc..3e2f42795 100644 --- a/geo-types/src/line_string.rs +++ b/geo-types/src/line_string.rs @@ -5,7 +5,7 @@ use crate::{CoordNum, Coordinate, Line, Point, Triangle}; use std::iter::FromIterator; use std::ops::{Index, IndexMut}; -/// An ordered collection of two or more [`Coordinate`]s, representing a +/// An ordered collection of two or more [Coordinate]s, representing a /// path between locations. /// /// # Semantics @@ -25,9 +25,9 @@ use std::ops::{Index, IndexMut}; /// /// A `LineString` is valid if it is either empty or /// contains 2 or more coordinates. Further, a closed -/// `LineString` must not self intersect. Note that the -/// validity is not enforced, and the operations and -/// predicates are undefined on invalid linestrings. +/// `LineString` must not self-intersect. Note that its +/// validity is **not** enforced, and operations and +/// predicates are **undefined** on invalid `LineString`s. /// /// # Examples /// @@ -53,7 +53,7 @@ use std::ops::{Index, IndexMut}; /// ]; /// ``` /// -/// Converting a `Vec` of `Coordinate`-like things: +/// Converting from a `Vec` of [Coordinate]-like things: /// /// ``` /// use geo_types::LineString; @@ -67,7 +67,7 @@ use std::ops::{Index, IndexMut}; /// let line_string: LineString = vec![[0., 0.], [10., 0.]].into(); /// ``` // -/// Or `collect`ing from a `Coordinate` iterator +/// Or `collect`ing from a [Coordinate] iterator /// /// ``` /// use geo_types::{Coordinate, LineString}; @@ -78,7 +78,7 @@ use std::ops::{Index, IndexMut}; /// let line_string: LineString = coords_iter.collect(); /// ``` /// -/// You can iterate and loop over the coordinates in the `LineString`, yielding [Coordinate]s: +/// You can iterate and loop over the coordinates in the [LineString], yielding [Coordinate]s: /// /// ``` /// use geo_types::{Coordinate, LineString}; @@ -90,12 +90,16 @@ use std::ops::{Index, IndexMut}; /// /// line_string.iter().for_each(|coord| println!("Coordinate x = {}, y = {}", coord.x, coord.y)); /// +/// for coord in &line_string { +/// println!("Coordinate x = {}, y = {}", coord.x, coord.y); +/// } +/// /// for coord in line_string { /// println!("Coordinate x = {}, y = {}", coord.x, coord.y); /// } /// ``` /// -/// You can also iterate over the coordinates in the `LineString` as `Point`s: +/// You can also iterate over the coordinates in the [LineString] as [Point]s: /// /// ``` /// use geo_types::{Coordinate, LineString}; @@ -116,7 +120,7 @@ pub struct LineString(pub Vec>) where T: CoordNum; -/// A `Point` iterator returned by the `points_iter` method +/// A [Point] iterator returned by the `points_iter` method #[derive(Debug)] pub struct PointsIter<'a, T: CoordNum + 'a>(::std::slice::Iter<'a, Coordinate>); @@ -134,7 +138,7 @@ impl<'a, T: CoordNum> DoubleEndedIterator for PointsIter<'a, T> { } } -/// A [Coordinate] iterator returned by the `iter` method over a [LineString] +/// A [Coordinate] iterator returned by the `iter` method on a [LineString] #[derive(Debug)] pub struct CoordinatesIter<'a, T: CoordNum + 'a>(::std::slice::Iter<'a, Coordinate>); @@ -153,7 +157,7 @@ impl<'a, T: CoordNum> DoubleEndedIterator for CoordinatesIter<'a, T> { } impl LineString { - /// Return an iterator yielding the coordinates of a `LineString` as `Point`s + /// Return an iterator yielding the coordinates of a [LineString] as [Point]s pub fn points_iter(&self) -> PointsIter { PointsIter(self.0.iter()) } @@ -167,7 +171,7 @@ impl LineString { CoordinatesIter(self.0.iter()) } - /// Return the coordinates of a `LineString` as a `Vec` of `Point`s + /// Return the coordinates of a [LineString] as a `Vec` of [Point]s pub fn into_points(self) -> Vec> { self.0.into_iter().map(Point).collect() } @@ -177,8 +181,8 @@ impl LineString { self.0.into_iter().collect() } - /// Return an iterator yielding one `Line` for each line segment - /// in the `LineString`. + /// Return an iterator yielding one [Line] for each line segment + /// in the [LineString]. /// /// # Examples /// @@ -212,7 +216,7 @@ impl LineString { }) } - /// An iterator which yields the coordinates of a `LineString` as `Triangle`s + /// An iterator which yields the coordinates of a [LineString] as [Triangle]s pub fn triangles(&'_ self) -> impl ExactSizeIterator + Iterator> + '_ { self.0.windows(3).map(|w| { // slice::windows(N) is guaranteed to yield a slice with exactly N elements @@ -226,7 +230,7 @@ impl LineString { }) } - /// Close the `LineString`. Specifically, if the `LineString` has at least one coordinate, and + /// Close the [LineString]. Specifically, if the [LineString] has at least one coordinate, and /// the value of the first coordinate does not equal the value of the last coordinate, then a /// new coordinate is added to the end with the value of the first coordinate. pub fn close(&mut self) { @@ -237,7 +241,7 @@ impl LineString { } } - /// Return the number of coordinates in the `LineString`. + /// Return the number of coordinates in the [LineString]. /// /// # Examples /// @@ -281,7 +285,7 @@ impl LineString { } } -/// Turn a `Vec` of `Point`-like objects into a `LineString`. +/// Turn a `Vec` of [Point]-like objects into a [LineString]. impl>> From> for LineString { fn from(v: Vec) -> Self { LineString(v.into_iter().map(|c| c.into()).collect()) @@ -294,14 +298,14 @@ impl From> for LineString { } } -/// Turn an iterator of `Point`-like objects into a `LineString`. +/// Turn an iterator of [Point]-like objects into a [LineString]. impl>> FromIterator for LineString { fn from_iter>(iter: I) -> Self { LineString(iter.into_iter().map(|c| c.into()).collect()) } } -/// Iterate over all the [Coordinate](struct.Coordinates.html)s in this `LineString`. +/// Iterate over all the [Coordinate](struct.Coordinates.html)s in this [LineString]. impl IntoIterator for LineString { type Item = Coordinate; type IntoIter = ::std::vec::IntoIter>; From 476a6cfb2fca05a7dd86556a2ad99de12c4711cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Tue, 28 Dec 2021 15:32:35 +0000 Subject: [PATCH 04/12] Fix iter_mut impl --- geo-types/src/line_string.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/geo-types/src/line_string.rs b/geo-types/src/line_string.rs index 3e2f42795..ff7e7a249 100644 --- a/geo-types/src/line_string.rs +++ b/geo-types/src/line_string.rs @@ -166,9 +166,10 @@ impl LineString { pub fn iter(&self) -> CoordinatesIter { CoordinatesIter(self.0.iter()) } + /// Return an iterator yielding the coordinates of a [LineString] as mutable [Coordinate]s - pub fn iter_mut(&mut self) -> CoordinatesIter { - CoordinatesIter(self.0.iter()) + pub fn iter_mut(&mut self) -> impl Iterator> { + self.0.iter_mut() } /// Return the coordinates of a [LineString] as a `Vec` of [Point]s From 24321880708d6081f37531f65ca7d56935d27c5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Tue, 28 Dec 2021 19:20:19 +0000 Subject: [PATCH 05/12] Defer iter to underlying vec impl --- geo-types/src/line_string.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/geo-types/src/line_string.rs b/geo-types/src/line_string.rs index ff7e7a249..0b7ac4682 100644 --- a/geo-types/src/line_string.rs +++ b/geo-types/src/line_string.rs @@ -163,8 +163,8 @@ impl LineString { } /// Return an iterator yielding the members of a [LineString] as [Coordinate]s - pub fn iter(&self) -> CoordinatesIter { - CoordinatesIter(self.0.iter()) + pub fn iter(&self) -> impl Iterator> { + self.0.iter() } /// Return an iterator yielding the coordinates of a [LineString] as mutable [Coordinate]s From fbcbf474bf56055119ccb8255bf4a6bb909ecc9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Tue, 28 Dec 2021 19:44:35 +0000 Subject: [PATCH 06/12] Fix docstring --- geo-types/src/line_string.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geo-types/src/line_string.rs b/geo-types/src/line_string.rs index 0b7ac4682..595bf567f 100644 --- a/geo-types/src/line_string.rs +++ b/geo-types/src/line_string.rs @@ -138,7 +138,7 @@ impl<'a, T: CoordNum> DoubleEndedIterator for PointsIter<'a, T> { } } -/// A [Coordinate] iterator returned by the `iter` method on a [LineString] +/// A [Coordinate] iterator used by the `into_iter` method on a [LineString] #[derive(Debug)] pub struct CoordinatesIter<'a, T: CoordNum + 'a>(::std::slice::Iter<'a, Coordinate>); From 6405bf6cc25d038641bd7641fa96ce533fdfbf79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Fri, 31 Dec 2021 12:33:21 +0000 Subject: [PATCH 07/12] Update geo-types/src/line_string.rs Co-authored-by: Corey Farwell --- geo-types/src/line_string.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geo-types/src/line_string.rs b/geo-types/src/line_string.rs index 595bf567f..726ef8b55 100644 --- a/geo-types/src/line_string.rs +++ b/geo-types/src/line_string.rs @@ -5,7 +5,7 @@ use crate::{CoordNum, Coordinate, Line, Point, Triangle}; use std::iter::FromIterator; use std::ops::{Index, IndexMut}; -/// An ordered collection of two or more [Coordinate]s, representing a +/// An ordered collection of two or more [`Coordinate`]s, representing a /// path between locations. /// /// # Semantics From 9b6074ccac71849532d3fddc54870e8a58b359fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Fri, 31 Dec 2021 12:49:18 +0000 Subject: [PATCH 08/12] Implement suggestions --- geo-types/src/line_string.rs | 64 ++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/geo-types/src/line_string.rs b/geo-types/src/line_string.rs index 726ef8b55..52ea25ea0 100644 --- a/geo-types/src/line_string.rs +++ b/geo-types/src/line_string.rs @@ -10,28 +10,28 @@ use std::ops::{Index, IndexMut}; /// /// # Semantics /// -/// A `LineString` is _closed_ if it is empty, or if the +/// A [`LineString`] is _closed_ if it is empty, or if the /// first and last coordinates are the same. The _boundary_ -/// of a `LineString` is empty if closed, and otherwise the +/// of a [`LineString`] is empty if closed, and otherwise the /// end points. The _interior_ is the (infinite) set of all /// points along the linestring _not including_ the -/// boundary. A `LineString` is _simple_ if it does not +/// boundary. A [`LineString`] is _simple_ if it does not /// intersect except possibly at the first and last -/// coordinates. A simple and closed `LineString` is a +/// coordinates. A simple and closed [`LineString`] is a /// `LinearRing` as defined in the OGC-SFA (but is not a /// separate type here). /// /// # Validity /// -/// A `LineString` is valid if it is either empty or +/// A [`LineString`] is valid if it is either empty or /// contains 2 or more coordinates. Further, a closed -/// `LineString` must not self-intersect. Note that its +/// [`LineString`] must not self-intersect. Note that its /// validity is **not** enforced, and operations and /// predicates are **undefined** on invalid `LineString`s. /// /// # Examples /// -/// Create a `LineString` by calling it directly: +/// Create a [`LineString`] by calling it directly: /// /// ``` /// use geo_types::{Coordinate, LineString}; @@ -42,7 +42,7 @@ use std::ops::{Index, IndexMut}; /// ]); /// ``` /// -/// Create a `LineString` with the [`line_string!`] macro: +/// Create a [`LineString`] with the [`line_string!`] macro: /// /// ``` /// use geo_types::line_string; @@ -53,7 +53,7 @@ use std::ops::{Index, IndexMut}; /// ]; /// ``` /// -/// Converting from a `Vec` of [Coordinate]-like things: +/// Converting from a [`Vec`] of [`Coordinate`]-like things: /// /// ``` /// use geo_types::LineString; @@ -67,7 +67,7 @@ use std::ops::{Index, IndexMut}; /// let line_string: LineString = vec![[0., 0.], [10., 0.]].into(); /// ``` // -/// Or `collect`ing from a [Coordinate] iterator +/// Or `collect`ing from a [`Coordinate`] iterator /// /// ``` /// use geo_types::{Coordinate, LineString}; @@ -78,7 +78,7 @@ use std::ops::{Index, IndexMut}; /// let line_string: LineString = coords_iter.collect(); /// ``` /// -/// You can iterate and loop over the coordinates in the [LineString], yielding [Coordinate]s: +/// You can iterate and loop over the coordinates in the [`LineString`], yielding [`Coordinate`]s by default: /// /// ``` /// use geo_types::{Coordinate, LineString}; @@ -99,7 +99,7 @@ use std::ops::{Index, IndexMut}; /// } /// ``` /// -/// You can also iterate over the coordinates in the [LineString] as [Point]s: +/// …or yielding [`Point`]s: /// /// ``` /// use geo_types::{Coordinate, LineString}; @@ -120,7 +120,7 @@ pub struct LineString(pub Vec>) where T: CoordNum; -/// A [Point] iterator returned by the `points_iter` method +/// A [`Point`] iterator returned by the `points_iter` method #[derive(Debug)] pub struct PointsIter<'a, T: CoordNum + 'a>(::std::slice::Iter<'a, Coordinate>); @@ -138,7 +138,7 @@ impl<'a, T: CoordNum> DoubleEndedIterator for PointsIter<'a, T> { } } -/// A [Coordinate] iterator used by the `into_iter` method on a [LineString] +/// A [`Coordinate`] iterator used by the `into_iter` method on a [`LineString`] #[derive(Debug)] pub struct CoordinatesIter<'a, T: CoordNum + 'a>(::std::slice::Iter<'a, Coordinate>); @@ -150,6 +150,12 @@ impl<'a, T: CoordNum> Iterator for CoordinatesIter<'a, T> { } } +impl<'a, T: CoordNum> ExactSizeIterator for CoordinatesIter<'a, T> { + fn len(&self) -> usize { + self.0.len() + } +} + impl<'a, T: CoordNum> DoubleEndedIterator for CoordinatesIter<'a, T> { fn next_back(&mut self) -> Option { self.0.next_back() @@ -157,33 +163,33 @@ impl<'a, T: CoordNum> DoubleEndedIterator for CoordinatesIter<'a, T> { } impl LineString { - /// Return an iterator yielding the coordinates of a [LineString] as [Point]s + /// Return an iterator yielding the coordinates of a [`LineString`] as [`Point`]s pub fn points_iter(&self) -> PointsIter { PointsIter(self.0.iter()) } - /// Return an iterator yielding the members of a [LineString] as [Coordinate]s + /// Return an iterator yielding the members of a [`LineString`] as [`Coordinate`]s pub fn iter(&self) -> impl Iterator> { self.0.iter() } - /// Return an iterator yielding the coordinates of a [LineString] as mutable [Coordinate]s + /// Return an iterator yielding the coordinates of a [`LineString`] as mutable [`Coordinate`]s pub fn iter_mut(&mut self) -> impl Iterator> { self.0.iter_mut() } - /// Return the coordinates of a [LineString] as a `Vec` of [Point]s + /// Return the coordinates of a [`LineString`] as a [`Vec`] of [`Point`]s pub fn into_points(self) -> Vec> { self.0.into_iter().map(Point).collect() } - /// Return the coordinates of a [LineString] as a `Vec` of [Coordinate]s - pub fn into_coordinates(self) -> Vec> { - self.0.into_iter().collect() + /// Return the coordinates of a [`LineString`] as a [`Vec`] of [`Coordinate`]s + pub fn into_inner(self) -> Vec> { + self.0 } /// Return an iterator yielding one [Line] for each line segment - /// in the [LineString]. + /// in the [`LineString`]. /// /// # Examples /// @@ -217,7 +223,7 @@ impl LineString { }) } - /// An iterator which yields the coordinates of a [LineString] as [Triangle]s + /// An iterator which yields the coordinates of a [`LineString`] as [Triangle]s pub fn triangles(&'_ self) -> impl ExactSizeIterator + Iterator> + '_ { self.0.windows(3).map(|w| { // slice::windows(N) is guaranteed to yield a slice with exactly N elements @@ -231,7 +237,7 @@ impl LineString { }) } - /// Close the [LineString]. Specifically, if the [LineString] has at least one coordinate, and + /// Close the [`LineString`]. Specifically, if the [`LineString`] has at least one coordinate, and /// the value of the first coordinate does not equal the value of the last coordinate, then a /// new coordinate is added to the end with the value of the first coordinate. pub fn close(&mut self) { @@ -242,7 +248,7 @@ impl LineString { } } - /// Return the number of coordinates in the [LineString]. + /// Return the number of coordinates in the [`LineString`]. /// /// # Examples /// @@ -286,7 +292,7 @@ impl LineString { } } -/// Turn a `Vec` of [Point]-like objects into a [LineString]. +/// Turn a [`Vec`] of [`Point`]-like objects into a [`LineString`]. impl>> From> for LineString { fn from(v: Vec) -> Self { LineString(v.into_iter().map(|c| c.into()).collect()) @@ -299,14 +305,14 @@ impl From> for LineString { } } -/// Turn an iterator of [Point]-like objects into a [LineString]. +/// Turn an iterator of [`Point`]-like objects into a [`LineString`]. impl>> FromIterator for LineString { fn from_iter>(iter: I) -> Self { LineString(iter.into_iter().map(|c| c.into()).collect()) } } -/// Iterate over all the [Coordinate](struct.Coordinates.html)s in this [LineString]. +/// Iterate over all the [`Coordinate`]s in this [`LineString`]. impl IntoIterator for LineString { type Item = Coordinate; type IntoIter = ::std::vec::IntoIter>; @@ -325,7 +331,7 @@ impl<'a, T: CoordNum> IntoIterator for &'a LineString { } } -/// Mutably iterate over all the [Coordinate](struct.Coordinates.html)s in this [LineString]. +/// Mutably iterate over all the [`Coordinate`]s in this [`LineString`] impl<'a, T: CoordNum> IntoIterator for &'a mut LineString { type Item = &'a mut Coordinate; type IntoIter = ::std::slice::IterMut<'a, Coordinate>; From 2ffa8e8dfbd09f7bb8c507d110d76aa4e3ac6738 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Tue, 4 Jan 2022 11:31:12 +0000 Subject: [PATCH 09/12] Regularise iterators and revise docs --- geo-types/src/line_string.rs | 56 +++++++++++++++------ geo-types/src/private_utils.rs | 2 +- geo/src/algorithm/euclidean_distance.rs | 14 ++---- geo/src/algorithm/line_interpolate_point.rs | 4 +- geo/src/algorithm/map_coords.rs | 4 +- geo/src/algorithm/rotate.rs | 4 +- geo/src/algorithm/winding_order.rs | 8 +-- 7 files changed, 56 insertions(+), 36 deletions(-) diff --git a/geo-types/src/line_string.rs b/geo-types/src/line_string.rs index 52ea25ea0..e433d2426 100644 --- a/geo-types/src/line_string.rs +++ b/geo-types/src/line_string.rs @@ -30,6 +30,7 @@ use std::ops::{Index, IndexMut}; /// predicates are **undefined** on invalid `LineString`s. /// /// # Examples +/// ## Creation /// /// Create a [`LineString`] by calling it directly: /// @@ -53,7 +54,7 @@ use std::ops::{Index, IndexMut}; /// ]; /// ``` /// -/// Converting from a [`Vec`] of [`Coordinate`]-like things: +/// By converting from a [`Vec`] of coordinate-like things: /// /// ``` /// use geo_types::LineString; @@ -67,7 +68,7 @@ use std::ops::{Index, IndexMut}; /// let line_string: LineString = vec![[0., 0.], [10., 0.]].into(); /// ``` // -/// Or `collect`ing from a [`Coordinate`] iterator +/// Or by `collect`ing from a [`Coordinate`] iterator /// /// ``` /// use geo_types::{Coordinate, LineString}; @@ -78,7 +79,8 @@ use std::ops::{Index, IndexMut}; /// let line_string: LineString = coords_iter.collect(); /// ``` /// -/// You can iterate and loop over the coordinates in the [`LineString`], yielding [`Coordinate`]s by default: +/// ## Iteration +/// [`LineString`] provides five iterators: [`coords`](LineString::coords), [`coords_mut`](LineString::coords_mut), [`points`](LineString::points), [`lines`](LineString::lines), and [`triangles`](LineString::triangles): /// /// ``` /// use geo_types::{Coordinate, LineString}; @@ -88,7 +90,22 @@ use std::ops::{Index, IndexMut}; /// Coordinate { x: 10., y: 0. }, /// ]); /// -/// line_string.iter().for_each(|coord| println!("Coordinate x = {}, y = {}", coord.x, coord.y)); +/// line_string.coords().for_each(|coord| println!("{:?}", coord)); +/// +/// for point in line_string.points() { +/// println!("Point x = {}, y = {}", point.x(), point.y()); +/// } +/// ``` +/// +/// Note that its [`IntoIterator`] impl yields [`Coordinate`]s when looping: +/// +/// ``` +/// use geo_types::{Coordinate, LineString}; +/// +/// let line_string = LineString(vec![ +/// Coordinate { x: 0., y: 0. }, +/// Coordinate { x: 10., y: 0. }, +/// ]); /// /// for coord in &line_string { /// println!("Coordinate x = {}, y = {}", coord.x, coord.y); @@ -97,21 +114,22 @@ use std::ops::{Index, IndexMut}; /// for coord in line_string { /// println!("Coordinate x = {}, y = {}", coord.x, coord.y); /// } -/// ``` /// -/// …or yielding [`Point`]s: +/// ``` +/// ## Decomposition /// +/// You can decompose a [`LineString`] into a [`Vec`] of [`Coordinate`]s or [`Point`]s: /// ``` -/// use geo_types::{Coordinate, LineString}; +/// use geo_types::{Coordinate, LineString, Point}; /// /// let line_string = LineString(vec![ /// Coordinate { x: 0., y: 0. }, /// Coordinate { x: 10., y: 0. }, /// ]); /// -/// for point in line_string.points_iter() { -/// println!("Point x = {}, y = {}", point.x(), point.y()); -/// } +/// let coordinate_vec = line_string.clone().into_inner(); +/// let point_vec = line_string.clone().into_points(); +/// /// ``` #[derive(Eq, PartialEq, Clone, Debug, Hash)] @@ -120,7 +138,7 @@ pub struct LineString(pub Vec>) where T: CoordNum; -/// A [`Point`] iterator returned by the `points_iter` method +/// A [`Point`] iterator returned by the `points` method #[derive(Debug)] pub struct PointsIter<'a, T: CoordNum + 'a>(::std::slice::Iter<'a, Coordinate>); @@ -164,17 +182,23 @@ impl<'a, T: CoordNum> DoubleEndedIterator for CoordinatesIter<'a, T> { impl LineString { /// Return an iterator yielding the coordinates of a [`LineString`] as [`Point`]s + #[deprecated(note = "Use points() instead")] pub fn points_iter(&self) -> PointsIter { PointsIter(self.0.iter()) } + /// Return an iterator yielding the coordinates of a [`LineString`] as [`Point`]s + pub fn points(&self) -> PointsIter { + PointsIter(self.0.iter()) + } + /// Return an iterator yielding the members of a [`LineString`] as [`Coordinate`]s - pub fn iter(&self) -> impl Iterator> { + pub fn coords(&self) -> impl Iterator> { self.0.iter() } /// Return an iterator yielding the coordinates of a [`LineString`] as mutable [`Coordinate`]s - pub fn iter_mut(&mut self) -> impl Iterator> { + pub fn coords_mut(&mut self) -> impl Iterator> { self.0.iter_mut() } @@ -284,7 +308,7 @@ impl LineString { /// LinearRing type, and use a LineString in its place, we adopt the JTS LinearRing `is_closed` /// behavior in all places, that is, we consider an empty LineString as closed. /// - /// This is expected when used in the context of a Polygon.exterior and elswhere; And there + /// This is expected when used in the context of a [`Polygon.exterior`](crate::Polygon::exterior) and elsewhere; And there /// seems to be no reason to maintain the separate behavior for LineStrings used in /// non-LinearRing contexts. pub fn is_closed(&self) -> bool { @@ -391,7 +415,7 @@ where return false; } - let points_zipper = self.points_iter().zip(other.points_iter()); + let points_zipper = self.points().zip(other.points()); for (lhs, rhs) in points_zipper { if lhs.relative_ne(&rhs, epsilon, max_relative) { return false; @@ -430,7 +454,7 @@ impl + CoordNum> AbsDiffEq for LineString { if self.0.len() != other.0.len() { return false; } - let mut points_zipper = self.points_iter().zip(other.points_iter()); + let mut points_zipper = self.points().zip(other.points()); points_zipper.all(|(lhs, rhs)| lhs.abs_diff_eq(&rhs, epsilon)) } } diff --git a/geo-types/src/private_utils.rs b/geo-types/src/private_utils.rs index 852301a3a..a20ec101d 100644 --- a/geo-types/src/private_utils.rs +++ b/geo-types/src/private_utils.rs @@ -9,7 +9,7 @@ pub fn line_string_bounding_rect(line_string: &LineString) -> Option(line: Line) -> Rect diff --git a/geo/src/algorithm/euclidean_distance.rs b/geo/src/algorithm/euclidean_distance.rs index 5123fb044..e11572646 100644 --- a/geo/src/algorithm/euclidean_distance.rs +++ b/geo/src/algorithm/euclidean_distance.rs @@ -559,19 +559,15 @@ where let tree_b: RTree> = RTree::bulk_load(geom2.lines().collect::>()); // Return minimum distance between all geom a points and all geom b points geom2 - .points_iter() + .points() .fold(::max_value(), |acc, point| { let nearest = tree_a.nearest_neighbor(&point).unwrap(); acc.min(nearest.euclidean_distance(&point)) }) - .min( - geom1 - .points_iter() - .fold(Bounded::max_value(), |acc, point| { - let nearest = tree_b.nearest_neighbor(&point).unwrap(); - acc.min(nearest.euclidean_distance(&point)) - }), - ) + .min(geom1.points().fold(Bounded::max_value(), |acc, point| { + let nearest = tree_b.nearest_neighbor(&point).unwrap(); + acc.min(nearest.euclidean_distance(&point)) + })) } #[cfg(test)] diff --git a/geo/src/algorithm/line_interpolate_point.rs b/geo/src/algorithm/line_interpolate_point.rs index 9d8108ee0..65536fc37 100644 --- a/geo/src/algorithm/line_interpolate_point.rs +++ b/geo/src/algorithm/line_interpolate_point.rs @@ -240,11 +240,11 @@ mod test { // fraction is nan or inf assert_eq!( linestring.line_interpolate_point(Float::infinity()), - linestring.points_iter().last() + linestring.points().last() ); assert_eq!( linestring.line_interpolate_point(Float::neg_infinity()), - linestring.points_iter().next() + linestring.points().next() ); assert_eq!(linestring.line_interpolate_point(Float::nan()), None); diff --git a/geo/src/algorithm/map_coords.rs b/geo/src/algorithm/map_coords.rs index 5cbbfca9e..2cfb686ce 100644 --- a/geo/src/algorithm/map_coords.rs +++ b/geo/src/algorithm/map_coords.rs @@ -236,7 +236,7 @@ impl MapCoords for LineString { fn map_coords(&self, func: impl Fn(&(T, T)) -> (NT, NT) + Copy) -> Self::Output { LineString::from( - self.points_iter() + self.points() .map(|p| p.map_coords(func)) .collect::>(), ) @@ -251,7 +251,7 @@ impl TryMapCoords for LineString { func: impl Fn(&(T, T)) -> Result<(NT, NT), Box> + Copy, ) -> Result> { Ok(LineString::from( - self.points_iter() + self.points() .map(|p| p.try_map_coords(func)) .collect::, Box>>()?, )) diff --git a/geo/src/algorithm/rotate.rs b/geo/src/algorithm/rotate.rs index 6df531888..9c0b92288 100644 --- a/geo/src/algorithm/rotate.rs +++ b/geo/src/algorithm/rotate.rs @@ -222,10 +222,10 @@ where // return a rotated polygon, or a clone if no centroid is computable if let Some(centroid) = centroid { Polygon::new( - rotate_many(angle, centroid, self.exterior().points_iter()).collect(), + rotate_many(angle, centroid, self.exterior().points()).collect(), self.interiors() .iter() - .map(|ring| rotate_many(angle, centroid, ring.points_iter()).collect()) + .map(|ring| rotate_many(angle, centroid, ring.points()).collect()) .collect(), ) } else { diff --git a/geo/src/algorithm/winding_order.rs b/geo/src/algorithm/winding_order.rs index 8c1f0e137..662803419 100644 --- a/geo/src/algorithm/winding_order.rs +++ b/geo/src/algorithm/winding_order.rs @@ -157,8 +157,8 @@ where /// order, so that the resultant order makes it appear clockwise fn points_cw(&self) -> Points { match self.winding_order() { - Some(WindingOrder::CounterClockwise) => Points(EitherIter::B(self.points_iter().rev())), - _ => Points(EitherIter::A(self.points_iter())), + Some(WindingOrder::CounterClockwise) => Points(EitherIter::B(self.points().rev())), + _ => Points(EitherIter::A(self.points())), } } @@ -168,8 +168,8 @@ where /// order, so that the resultant order makes it appear counter-clockwise fn points_ccw(&self) -> Points { match self.winding_order() { - Some(WindingOrder::Clockwise) => Points(EitherIter::B(self.points_iter().rev())), - _ => Points(EitherIter::A(self.points_iter())), + Some(WindingOrder::Clockwise) => Points(EitherIter::B(self.points().rev())), + _ => Points(EitherIter::A(self.points())), } } From 053eb4d2af12557ad24af6cbfb7ae9767d3f4f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Tue, 4 Jan 2022 15:39:49 +0000 Subject: [PATCH 10/12] Re-format LineString semantics --- geo-types/src/line_string.rs | 42 +++++++++++++++++------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/geo-types/src/line_string.rs b/geo-types/src/line_string.rs index e433d2426..d9553b8b7 100644 --- a/geo-types/src/line_string.rs +++ b/geo-types/src/line_string.rs @@ -10,22 +10,20 @@ use std::ops::{Index, IndexMut}; /// /// # Semantics /// -/// A [`LineString`] is _closed_ if it is empty, or if the -/// first and last coordinates are the same. The _boundary_ -/// of a [`LineString`] is empty if closed, and otherwise the -/// end points. The _interior_ is the (infinite) set of all -/// points along the linestring _not including_ the -/// boundary. A [`LineString`] is _simple_ if it does not -/// intersect except possibly at the first and last -/// coordinates. A simple and closed [`LineString`] is a -/// `LinearRing` as defined in the OGC-SFA (but is not a -/// separate type here). +/// 1. A [`LineString`] is _closed_ if it is empty, **or** if the first and last coordinates are the same. +/// 2. The _boundary_ of a [`LineString`] is either: +/// - **empty** if it is closed **or** +/// - contains the **start** and **end** coordinates. +/// 3. The _interior_ is the (infinite) set of all coordinates along the [`LineString`], _not including_ the boundary. +/// 4. A [`LineString`] is _simple_ if it does not intersect except **optionally** at the first and last coordinates (in which case it is also _closed_, see **1**). +/// 5. A _simple_ **and** _closed_ [`LineString`] is a `LinearRing` as defined in the OGC-SFA (but is not defined as a separate type in this crate). /// /// # Validity /// /// A [`LineString`] is valid if it is either empty or -/// contains 2 or more coordinates. Further, a closed -/// [`LineString`] must not self-intersect. Note that its +/// contains 2 or more coordinates. +/// +/// Further, a closed [`LineString`] **must not** self-intersect. Note that its /// validity is **not** enforced, and operations and /// predicates are **undefined** on invalid `LineString`s. /// @@ -261,9 +259,9 @@ impl LineString { }) } - /// Close the [`LineString`]. Specifically, if the [`LineString`] has at least one coordinate, and - /// the value of the first coordinate does not equal the value of the last coordinate, then a - /// new coordinate is added to the end with the value of the first coordinate. + /// Close the [`LineString`]. Specifically, if the [`LineString`] has at least one [`Coordinate`], and + /// the value of the first [`Coordinate`] **does not** equal the value of the last [`Coordinate`], then a + /// new [`Coordinate`] is added to the end with the value of the first [`Coordinate`]. pub fn close(&mut self) { if !self.is_closed() { // by definition, we treat empty LineString's as closed. @@ -302,15 +300,15 @@ impl LineString { /// assert!(line_string.is_closed()); /// ``` /// - /// Note that we diverge from some libraries (JTS et al), which have a LinearRing type, - /// separate from LineString. Those libraries treat an empty LinearRing as closed, by - /// definition, while treating an empty LineString as open. Since we don't have a separate - /// LinearRing type, and use a LineString in its place, we adopt the JTS LinearRing `is_closed` - /// behavior in all places, that is, we consider an empty LineString as closed. + /// Note that we diverge from some libraries ([JTS](https://locationtech.github.io/jts/javadoc/org/locationtech/jts/geom/LinearRing.html) et al), which have a `LinearRing` type, + /// separate from [`LineString`]. Those libraries treat an empty `LinearRing` as **closed** by + /// definition, while treating an empty `LineString` as **open**. Since we don't have a separate + /// `LinearRing` type, and use a [`LineString`] in its place, we adopt the JTS `LinearRing` `is_closed` + /// behavior in all places: that is, **we consider an empty [`LineString`] as closed**. /// /// This is expected when used in the context of a [`Polygon.exterior`](crate::Polygon::exterior) and elsewhere; And there - /// seems to be no reason to maintain the separate behavior for LineStrings used in - /// non-LinearRing contexts. + /// seems to be no reason to maintain the separate behavior for [`LineString`]s used in + /// non-`LinearRing` contexts. pub fn is_closed(&self) -> bool { self.0.first() == self.0.last() } From 007647931f16224432c42334bda55845241737c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Tue, 4 Jan 2022 15:43:58 +0000 Subject: [PATCH 11/12] More re-formatting --- geo-types/src/line_string.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/geo-types/src/line_string.rs b/geo-types/src/line_string.rs index d9553b8b7..46f385010 100644 --- a/geo-types/src/line_string.rs +++ b/geo-types/src/line_string.rs @@ -12,7 +12,7 @@ use std::ops::{Index, IndexMut}; /// /// 1. A [`LineString`] is _closed_ if it is empty, **or** if the first and last coordinates are the same. /// 2. The _boundary_ of a [`LineString`] is either: -/// - **empty** if it is closed **or** +/// - **empty** if it is _closed_ (see **1**) **or** /// - contains the **start** and **end** coordinates. /// 3. The _interior_ is the (infinite) set of all coordinates along the [`LineString`], _not including_ the boundary. /// 4. A [`LineString`] is _simple_ if it does not intersect except **optionally** at the first and last coordinates (in which case it is also _closed_, see **1**). From 214af78a4775ea4442635ccc0612526f19841e4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20H=C3=BCgel?= Date: Tue, 4 Jan 2022 17:11:02 +0000 Subject: [PATCH 12/12] Update CHANGELOG --- geo-types/CHANGES.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/geo-types/CHANGES.md b/geo-types/CHANGES.md index 87ff7f00e..4f762ccc9 100644 --- a/geo-types/CHANGES.md +++ b/geo-types/CHANGES.md @@ -7,6 +7,9 @@ * `Geometry` and `GeometryCollection` now support serde. * +* add `Coordinate` iterators to LineString, regularise its iterator methods, and refactor its docs + * https://github.com/georust/geo/pull/705 + ## 0.7.2 * Implement `RelativeEq` and `AbsDiffEq` for fuzzy comparison of remaining Geometry Types