diff --git a/geo-types/src/geometry.rs b/geo-types/src/geometry.rs index b6d498576..85b28bea4 100644 --- a/geo-types/src/geometry.rs +++ b/geo-types/src/geometry.rs @@ -2,11 +2,27 @@ use crate::{ CoordinateType, GeometryCollection, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, }; +use num_traits::Float; +use std::convert::TryFrom; +use std::error::Error; +use std::fmt; /// An enum representing any possible geometry type. /// /// All `Geo` types can be converted to a `Geometry` member using `.into()` (as part of the -/// `std::convert::Into` pattern). +/// `std::convert::Into` pattern), and `Geo` types implement the `TryFrom` trait in order to +/// convert _back_ from enum members. +/// +/// # Example +/// +/// ``` +/// use std::convert::TryFrom; +/// use geo_types::{Point, point, Geometry, GeometryCollection}; +/// let p = point!(x: 1.0, y: 1.0); +/// let pe: Geometry = p.into(); +/// let pn = Point::try_from(pe).unwrap(); +/// ``` +/// #[derive(PartialEq, Clone, Debug, Hash)] pub enum Geometry where @@ -69,6 +85,9 @@ impl Geometry { /// let p2: Point = g.into_point().unwrap(); /// assert_eq!(p2, Point::new(0., 0.,)); /// ``` + #[deprecated( + note = "Will be removed in an upcoming version. Switch to std::convert::TryInto" + )] pub fn into_point(self) -> Option> { if let Geometry::Point(x) = self { Some(x) @@ -78,6 +97,9 @@ impl Geometry { } /// If this Geometry is a LineString, then return that LineString, else None. + #[deprecated( + note = "Will be removed in an upcoming version. Switch to std::convert::TryInto" + )] pub fn into_line_string(self) -> Option> { if let Geometry::LineString(x) = self { Some(x) @@ -87,6 +109,9 @@ impl Geometry { } /// If this Geometry is a Line, then return that Line, else None. + #[deprecated( + note = "Will be removed in an upcoming version. Switch to std::convert::TryInto" + )] pub fn into_line(self) -> Option> { if let Geometry::Line(x) = self { Some(x) @@ -96,6 +121,9 @@ impl Geometry { } /// If this Geometry is a Polygon, then return that, else None. + #[deprecated( + note = "Will be removed in an upcoming version. Switch to std::convert::TryInto" + )] pub fn into_polygon(self) -> Option> { if let Geometry::Polygon(x) = self { Some(x) @@ -105,6 +133,9 @@ impl Geometry { } /// If this Geometry is a MultiPoint, then return that, else None. + #[deprecated( + note = "Will be removed in an upcoming version. Switch to std::convert::TryInto" + )] pub fn into_multi_point(self) -> Option> { if let Geometry::MultiPoint(x) = self { Some(x) @@ -114,6 +145,9 @@ impl Geometry { } /// If this Geometry is a MultiLineString, then return that, else None. + #[deprecated( + note = "Will be removed in an upcoming version. Switch to std::convert::TryInto" + )] pub fn into_multi_line_string(self) -> Option> { if let Geometry::MultiLineString(x) = self { Some(x) @@ -123,6 +157,9 @@ impl Geometry { } /// If this Geometry is a MultiPolygon, then return that, else None. + #[deprecated( + note = "Will be removed in an upcoming version. Switch to std::convert::TryInto" + )] pub fn into_multi_polygon(self) -> Option> { if let Geometry::MultiPolygon(x) = self { Some(x) @@ -131,3 +168,116 @@ impl Geometry { } } } + +#[derive(Debug)] +pub struct FailedToConvertError; + +impl fmt::Display for FailedToConvertError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Could not convert from enum member to concrete type") + } +} + +impl Error for FailedToConvertError { + fn description(&self) -> &str { + "Could not convert from enum member to concrete type" + } +} + +impl TryFrom> for Point +where + T: Float, +{ + type Error = FailedToConvertError; + + fn try_from(geom: Geometry) -> Result, Self::Error> { + match geom { + Geometry::Point(p) => Ok(p), + _ => Err(FailedToConvertError), + } + } +} + +impl TryFrom> for Line +where + T: Float, +{ + type Error = FailedToConvertError; + + fn try_from(geom: Geometry) -> Result, Self::Error> { + match geom { + Geometry::Line(l) => Ok(l), + _ => Err(FailedToConvertError), + } + } +} + +impl TryFrom> for LineString +where + T: Float, +{ + type Error = FailedToConvertError; + + fn try_from(geom: Geometry) -> Result, Self::Error> { + match geom { + Geometry::LineString(ls) => Ok(ls), + _ => Err(FailedToConvertError), + } + } +} + +impl TryFrom> for Polygon +where + T: Float, +{ + type Error = FailedToConvertError; + + fn try_from(geom: Geometry) -> Result, Self::Error> { + match geom { + Geometry::Polygon(ls) => Ok(ls), + _ => Err(FailedToConvertError), + } + } +} + +impl TryFrom> for MultiPoint +where + T: Float, +{ + type Error = FailedToConvertError; + + fn try_from(geom: Geometry) -> Result, Self::Error> { + match geom { + Geometry::MultiPoint(mp) => Ok(mp), + _ => Err(FailedToConvertError), + } + } +} + +impl TryFrom> for MultiLineString +where + T: Float, +{ + type Error = FailedToConvertError; + + fn try_from(geom: Geometry) -> Result, Self::Error> { + match geom { + Geometry::MultiLineString(mls) => Ok(mls), + _ => Err(FailedToConvertError), + } + } +} + +impl TryFrom> for MultiPolygon +where + T: Float, +{ + type Error = FailedToConvertError; + + fn try_from(geom: Geometry) -> Result, Self::Error> { + match geom { + Geometry::MultiPolygon(mp) => Ok(mp), + _ => Err(FailedToConvertError), + } + } +} diff --git a/geo-types/src/geometry_collection.rs b/geo-types/src/geometry_collection.rs index 862d21718..679e7f1e2 100644 --- a/geo-types/src/geometry_collection.rs +++ b/geo-types/src/geometry_collection.rs @@ -1,11 +1,68 @@ use crate::{CoordinateType, Geometry}; use std::iter::FromIterator; +use std::ops::{Index, IndexMut}; /// A collection of [`Geometry`](enum.Geometry.html) types. /// -/// Can be created from a `Vec` of Geometries, or from an Iterator which yields Geometries. +/// It can be created from a `Vec` of Geometries, or from an Iterator which yields Geometries. +/// +/// Looping over this object yields its component **Geometry enum members** (_not_ the underlying geometry primitives), +/// and it supports iteration and indexing +/// as well as the various [`MapCoords`](algorithm/map_coords/index.html) functions, which _are_ directly +/// applied to the underlying geometry primitives. +/// +/// # Examples +/// ## Looping +/// +/// ``` +/// use std::convert::TryFrom; +/// use geo_types::{Point, point, Geometry, GeometryCollection}; +/// let p = point!(x: 1.0, y: 1.0); +/// let pe = Geometry::Point(p); +/// let gc = GeometryCollection(vec![pe]); +/// for geom in gc { +/// println!("{:?}", Point::try_from(geom).unwrap().x()); +/// } +/// ``` +/// ## Implements `iter()` +/// +/// ``` +/// use std::convert::TryFrom; +/// use geo_types::{Point, point, Geometry, GeometryCollection}; +/// let p = point!(x: 1.0, y: 1.0); +/// let pe = Geometry::Point(p); +/// let gc = GeometryCollection(vec![pe]); +/// gc.iter().for_each(|geom| println!("{:?}", geom)); +/// ``` +/// +/// ## Mutable Iteration +/// +/// ``` +/// use std::convert::TryFrom; +/// use geo_types::{Point, point, Geometry, GeometryCollection}; +/// let p = point!(x: 1.0, y: 1.0); +/// let pe = Geometry::Point(p); +/// let mut gc = GeometryCollection(vec![pe]); +/// gc.iter_mut().for_each(|geom| { +/// if let Geometry::Point(p) = geom { +/// p.set_x(0.2); +/// } +/// }); +/// let updated = gc[0].clone(); +/// assert_eq!(Point::try_from(updated).unwrap().x(), 0.2); +/// ``` +/// +/// ## Indexing +/// +/// ``` +/// use std::convert::TryFrom; +/// use geo_types::{Point, point, Geometry, GeometryCollection}; +/// let p = point!(x: 1.0, y: 1.0); +/// let pe = Geometry::Point(p); +/// let gc = GeometryCollection(vec![pe]); +/// println!("{:?}", gc[0]); +/// ``` /// -/// Iterating over this objects, yields the component Geometries. #[derive(PartialEq, Clone, Debug, Hash)] pub struct GeometryCollection(pub Vec>) where @@ -43,11 +100,113 @@ impl>> FromIterator for GeometryColl } } +impl Index for GeometryCollection { + type Output = Geometry; + + fn index(&self, index: usize) -> &Geometry { + self.0.index(index) + } +} + +impl IndexMut for GeometryCollection { + fn index_mut(&mut self, index: usize) -> &mut Geometry { + self.0.index_mut(index) + } +} + +// structure helper for consuming iterator +pub struct IntoIteratorHelper { + iter: ::std::vec::IntoIter>, +} + +// implement the IntoIterator trait for a consuming iterator. Iteration will +// consume the GeometryCollection impl IntoIterator for GeometryCollection { type Item = Geometry; - type IntoIter = ::std::vec::IntoIter>; + type IntoIter = IntoIteratorHelper; + // note that into_iter() is consuming self fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() + IntoIteratorHelper { + iter: self.0.into_iter(), + } + } +} + +// implement Iterator trait for the helper struct, to be used by adapters +impl Iterator for IntoIteratorHelper { + type Item = Geometry; + + // just return the reference + fn next(&mut self) -> Option { + self.iter.next() + } +} + +// structure helper for non-consuming iterator +pub struct IterHelper<'a, T: CoordinateType> { + iter: ::std::slice::Iter<'a, Geometry>, +} + +// implement the IntoIterator trait for a non-consuming iterator. Iteration will +// borrow the GeometryCollection +impl<'a, T: CoordinateType> IntoIterator for &'a GeometryCollection { + type Item = &'a Geometry; + type IntoIter = IterHelper<'a, T>; + + // note that into_iter() is consuming self + fn into_iter(self) -> Self::IntoIter { + IterHelper { + iter: self.0.iter(), + } + } +} + +// implement the Iterator trait for the helper struct, to be used by adapters +impl<'a, T: CoordinateType> Iterator for IterHelper<'a, T> { + type Item = &'a Geometry; + + // just return the str reference + fn next(&mut self) -> Option { + self.iter.next() + } +} + +// structure helper for mutable non-consuming iterator +pub struct IterMutHelper<'a, T: CoordinateType> { + iter: ::std::slice::IterMut<'a, Geometry>, +} + +// implement the IntoIterator trait for a mutable non-consuming iterator. Iteration will +// mutably borrow the GeometryCollection +impl<'a, T: CoordinateType> IntoIterator for &'a mut GeometryCollection { + type Item = &'a mut Geometry; + type IntoIter = IterMutHelper<'a, T>; + + // note that into_iter() is consuming self + fn into_iter(self) -> Self::IntoIter { + IterMutHelper { + iter: self.0.iter_mut(), + } + } +} + +// implement the Iterator trait for the helper struct, to be used by adapters +impl<'a, T: CoordinateType> Iterator for IterMutHelper<'a, T> { + type Item = &'a mut Geometry; + + // just return the str reference + fn next(&mut self) -> Option { + self.iter.next() + } +} + +impl<'a, T: CoordinateType> GeometryCollection { + pub fn iter(&'a self) -> IterHelper<'a, T> { + self.into_iter() + } + + pub fn iter_mut(&'a mut self) -> IterMutHelper<'a, T> { + self.into_iter() } } diff --git a/geo-types/src/lib.rs b/geo-types/src/lib.rs index c105b3ef1..0a054b6ed 100644 --- a/geo-types/src/lib.rs +++ b/geo-types/src/lib.rs @@ -74,6 +74,7 @@ extern crate approx; #[cfg(test)] mod tests { use super::*; + use std::convert::TryFrom; #[test] fn type_test() { @@ -99,7 +100,7 @@ mod tests { let p: Point = Point::new(0., 0.); let p1 = p.clone(); let g: Geometry = p.into(); - let p2 = g.into_point().unwrap(); + let p2 = Point::try_from(g).unwrap(); assert_eq!(p1, p2); }