diff --git a/geo-types/CHANGES.md b/geo-types/CHANGES.md index 9e423f780..64c4909f2 100644 --- a/geo-types/CHANGES.md +++ b/geo-types/CHANGES.md @@ -1,8 +1,11 @@ # Changes -## 0.6.2 +## Unreleased * `Rect::new` automatically determines min/max points + * +* Add `MultiLineString::is_closed` method + * ## 0.6.1 diff --git a/geo-types/src/line_string.rs b/geo-types/src/line_string.rs index 746eb86de..c0a389427 100644 --- a/geo-types/src/line_string.rs +++ b/geo-types/src/line_string.rs @@ -178,11 +178,13 @@ impl LineString { }) } - /// Close the `LineString`. Specifically, if the `LineString` has is 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. + debug_assert!(self.0.len() > 0); self.0.push(self.0[0]); } } @@ -215,6 +217,16 @@ impl LineString { /// let line_string: LineString = coords.into_iter().collect(); /// 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. + /// + /// This is expected when used in the context of a Polygon.exterior and elswhere; 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 { self.0.first() == self.0.last() } diff --git a/geo-types/src/multi_line_string.rs b/geo-types/src/multi_line_string.rs index 0243ef777..bb0a3a870 100644 --- a/geo-types/src/multi_line_string.rs +++ b/geo-types/src/multi_line_string.rs @@ -3,7 +3,7 @@ use std::iter::FromIterator; /// A collection of /// [`LineString`s](line_string/struct.LineString.html). Can -/// be created from a `Vec` of `LineString`s, or from an +/// be created from a `Vec` of `LineString`s or from an /// Iterator which yields `LineString`s. Iterating over this /// object yields the component `LineString`s. /// @@ -34,6 +34,33 @@ pub struct MultiLineString(pub Vec>) where T: CoordinateType; +impl MultiLineString { + /// True if the MultiLineString is empty or if all of its LineStrings are closed - see + /// [`LineString::is_closed`]. + /// + /// # Examples + /// + /// ``` + /// use geo_types::{MultiLineString, LineString, line_string}; + /// + /// let open_line_string: LineString = line_string![(x: 0., y: 0.), (x: 5., y: 0.)]; + /// assert!(!MultiLineString(vec![open_line_string.clone()]).is_closed()); + /// + /// let closed_line_string: LineString = line_string![(x: 0., y: 0.), (x: 5., y: 0.), (x: 0., y: 0.)]; + /// assert!(MultiLineString(vec![closed_line_string.clone()]).is_closed()); + /// + /// // MultiLineString is not closed if *any* of it's LineStrings are not closed + /// assert!(!MultiLineString(vec![open_line_string, closed_line_string]).is_closed()); + /// + /// // An empty MultiLineString is closed + /// assert!(MultiLineString::(vec![]).is_closed()); + /// ``` + pub fn is_closed(&self) -> bool { + // Note: Unlike JTS et al, we consider an empty MultiLineString as closed. + self.0.iter().all(LineString::is_closed) + } +} + impl>> From for MultiLineString { fn from(ls: ILS) -> Self { MultiLineString(vec![ls.into()]) diff --git a/geo/src/algorithm/is_convex.rs b/geo/src/algorithm/is_convex.rs index 5159e16fa..bdc19557f 100644 --- a/geo/src/algorithm/is_convex.rs +++ b/geo/src/algorithm/is_convex.rs @@ -15,7 +15,7 @@ use crate::{Coordinate, LineString}; /// /// - This definition is closely related to the notion /// of [convexity of polygons][convex set]. In particular, a -/// [`Polygon`] is convex, if and only if its `exterior` is +/// [`Polygon`](crate::Polygon) is convex, if and only if its `exterior` is /// convex, and `interiors` is empty. /// /// - The [`ConvexHull`] algorithm always returns a strictly diff --git a/geo/src/utils.rs b/geo/src/utils.rs index 62bca87e4..0b78245ca 100644 --- a/geo/src/utils.rs +++ b/geo/src/utils.rs @@ -98,7 +98,7 @@ where // // See: https://en.wikipedia.org/wiki/Point_in_polygon - assert!(linestring.is_closed()); + debug_assert!(linestring.is_closed()); // LineString without points if linestring.0.is_empty() {