diff --git a/geo-types/CHANGES.md b/geo-types/CHANGES.md index 2d8150d370..fe2e185db8 100644 --- a/geo-types/CHANGES.md +++ b/geo-types/CHANGES.md @@ -4,6 +4,29 @@ * Fixed: using empty `polygon![]` macro no longer requires including `line_string!` macro +### Breaking changes +* All geo types now support optional 3D (z coordinate) and M (measure) values. For example, `LineM` contains `x`, `y`, and `m` values, whereas `Line3D` has `x,y,z`. `Line3DM` has both `z` and `m`. When not used, `z` and `m` values are represented by the `NoValue` empty struct. `NoValue` behaves like a number. +* Remove deprecated functions on `Geometry`: + * `into_point` - Switch to `std::convert::TryInto` + * `into_line_string` - Switch to `std::convert::TryInto` + * `into_line` - Switch to `std::convert::TryInto` + * `into_polygon` - Switch to `std::convert::TryInto` + * `into_multi_point` - Switch to `std::convert::TryInto` + * `into_multi_line_string` - Switch to `std::convert::TryInto` + * `into_multi_polygon` - Switch to `std::convert::TryInto` +* Remove deprecated `CoordinateType` trait. Use `CoordFloat` or `CoordNum` instead. +* Remove deprecated functions from `LineString` + * Remove `points_iter()` -- use `points()` instead. + * Remove `num_coords()` -- use `geo::algorithm::coords_iter::CoordsIter::coords_count` instead. +* Remove deprecated functions from `Point` + * Remove `lng()` -- use `x()` instead. + * Remove `set_lng()` -- use `set_x()` instead. + * Remove `lat()` -- use `y()` instead. + * Remove `set_lat()` -- use `set_y()` instead. +* Remove deprecated `Polygon::is_convex()` -- use `geo::is_convex` on `poly.exterior()` instead. +* Remove deprecated `Rect::try_new()` -- use `Rect::new` instead, since `Rect::try_new` will never Error. Also removes corresponding `InvalidRectCoordinatesError`. +* Replace deprecated `GeometryCollection::new()` with `GeometryCollection::new(value)`, and remove deprecated `GeometryCollection::new_from(value)`. + ## 0.7.6 * You may now specify `Geometry` rather than `Geometry` since we've added @@ -25,6 +48,7 @@ * Deprecate `GeometryCollection::from(single_geom)` in favor of `GeometryCollection::from(vec![single_geom])` * + ## 0.7.4 * BREAKING: Make `Rect::to_lines` return lines in winding order for `Rect::to_polygon`. diff --git a/geo-types/src/arbitrary.rs b/geo-types/src/arbitrary.rs index bc0f3426a9..c823844b6f 100644 --- a/geo-types/src/arbitrary.rs +++ b/geo-types/src/arbitrary.rs @@ -1,36 +1,44 @@ use crate::{ - CoordFloat, Coordinate, Geometry, GeometryCollection, LineString, MultiLineString, MultiPoint, - MultiPolygon, Point, Polygon, Rect, Triangle, + CoordFloat, Coordinate, Geometry, GeometryCollection, LineString, Measure, MultiLineString, + MultiPoint, MultiPolygon, Point, Polygon, Rect, Triangle, ZCoord, }; use std::mem; -impl<'a, T> arbitrary::Arbitrary<'a> for Coordinate +impl<'a, T, Z, M> arbitrary::Arbitrary<'a> for Coordinate where T: arbitrary::Arbitrary<'a> + CoordFloat, + Z: arbitrary::Arbitrary<'a> + ZCoord, + M: arbitrary::Arbitrary<'a> + Measure, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { Ok(coord! { x: u.arbitrary::()?, y: u.arbitrary::()?, + z: u.arbitrary::()?, + m: u.arbitrary::()?, }) } } -impl<'a, T> arbitrary::Arbitrary<'a> for Point +impl<'a, T, Z, M> arbitrary::Arbitrary<'a> for Point where T: arbitrary::Arbitrary<'a> + CoordFloat, + Z: arbitrary::Arbitrary<'a> + ZCoord, + M: arbitrary::Arbitrary<'a> + Measure, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - u.arbitrary::>().map(Self) + u.arbitrary::>().map(Self) } } -impl<'a, T> arbitrary::Arbitrary<'a> for LineString +impl<'a, T, Z, M> arbitrary::Arbitrary<'a> for LineString where T: arbitrary::Arbitrary<'a> + CoordFloat, + Z: arbitrary::Arbitrary<'a> + ZCoord, + M: arbitrary::Arbitrary<'a> + Measure, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - let coords = u.arbitrary::>>()?; + let coords = u.arbitrary::>>()?; if coords.len() < 2 { Err(arbitrary::Error::IncorrectFormat) } else { @@ -39,86 +47,105 @@ where } fn size_hint(_depth: usize) -> (usize, Option) { - (mem::size_of::() * 2, None) + ( + mem::size_of::() * 2 + mem::size_of::() + mem::size_of::(), + None, + ) } } -impl<'a, T> arbitrary::Arbitrary<'a> for Polygon +impl<'a, T, Z, M> arbitrary::Arbitrary<'a> for Polygon where T: arbitrary::Arbitrary<'a> + CoordFloat, + Z: arbitrary::Arbitrary<'a> + ZCoord, + M: arbitrary::Arbitrary<'a> + Measure, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { Ok(Self::new( - u.arbitrary::>()?, - u.arbitrary::>>()?, + u.arbitrary::>()?, + u.arbitrary::>>()?, )) } } -impl<'a, T> arbitrary::Arbitrary<'a> for MultiPoint +impl<'a, T, Z, M> arbitrary::Arbitrary<'a> for MultiPoint where T: arbitrary::Arbitrary<'a> + CoordFloat, + Z: arbitrary::Arbitrary<'a> + ZCoord, + M: arbitrary::Arbitrary<'a> + Measure, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - u.arbitrary::>>().map(Self) + u.arbitrary::>>().map(Self) } } -impl<'a, T> arbitrary::Arbitrary<'a> for MultiLineString +impl<'a, T, Z, M> arbitrary::Arbitrary<'a> for MultiLineString where T: arbitrary::Arbitrary<'a> + CoordFloat, + Z: arbitrary::Arbitrary<'a> + ZCoord, + M: arbitrary::Arbitrary<'a> + Measure, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - u.arbitrary::>>().map(Self) + u.arbitrary::>>().map(Self) } } -impl<'a, T> arbitrary::Arbitrary<'a> for MultiPolygon +impl<'a, T, Z, M> arbitrary::Arbitrary<'a> for MultiPolygon where T: arbitrary::Arbitrary<'a> + CoordFloat, + Z: arbitrary::Arbitrary<'a> + ZCoord, + M: arbitrary::Arbitrary<'a> + Measure, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - u.arbitrary::>>().map(Self) + u.arbitrary::>>().map(Self) } } -impl<'a, T> arbitrary::Arbitrary<'a> for GeometryCollection +impl<'a, T, Z, M> arbitrary::Arbitrary<'a> for GeometryCollection where T: arbitrary::Arbitrary<'a> + CoordFloat, + Z: arbitrary::Arbitrary<'a> + ZCoord, + M: arbitrary::Arbitrary<'a> + Measure, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { u.arbitrary() } } -impl<'a, T> arbitrary::Arbitrary<'a> for Rect +impl<'a, T, Z, M> arbitrary::Arbitrary<'a> for Rect where T: arbitrary::Arbitrary<'a> + CoordFloat, + Z: arbitrary::Arbitrary<'a> + ZCoord, + M: arbitrary::Arbitrary<'a> + Measure, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { Ok(Self::new( - u.arbitrary::>()?, - u.arbitrary::>()?, + u.arbitrary::>()?, + u.arbitrary::>()?, )) } } -impl<'a, T> arbitrary::Arbitrary<'a> for Triangle +impl<'a, T, Z, M> arbitrary::Arbitrary<'a> for Triangle where T: arbitrary::Arbitrary<'a> + CoordFloat, + Z: arbitrary::Arbitrary<'a> + ZCoord, + M: arbitrary::Arbitrary<'a> + Measure, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { Ok(Self( - u.arbitrary::>()?, - u.arbitrary::>()?, - u.arbitrary::>()?, + u.arbitrary::>()?, + u.arbitrary::>()?, + u.arbitrary::>()?, )) } } -impl<'a, T> arbitrary::Arbitrary<'a> for Geometry +impl<'a, T, Z, M> arbitrary::Arbitrary<'a> for Geometry where T: arbitrary::Arbitrary<'a> + CoordFloat, + Z: arbitrary::Arbitrary<'a> + ZCoord, + M: arbitrary::Arbitrary<'a> + Measure, { fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { let n = u.int_in_range(0..=8)?; diff --git a/geo-types/src/geometry/coordinate.rs b/geo-types/src/geometry/coordinate.rs index 3acd24b353..cc5c5a9bc8 100644 --- a/geo-types/src/geometry/coordinate.rs +++ b/geo-types/src/geometry/coordinate.rs @@ -1,12 +1,14 @@ -use crate::{coord, CoordNum, Point}; - +use crate::{coord, CoordNum, Measure, NoValue, Point, ZCoord}; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq, UlpsEq}; +use num_traits::Zero; +use std::fmt::Debug; +use std::ops::{Add, Div, Mul, Neg, Sub}; -/// A lightweight struct used to store coordinates on the 2-dimensional -/// Cartesian plane. +/// A generic struct used to store a single coordinate with optional +/// 3D (Z) and measurement support. /// -/// Unlike `Point` (which in the future may contain additional information such +/// Unlike [`Point`] (which in the future may contain additional information such /// as an envelope, a precision model, and spatial reference system /// information), a `Coordinate` only contains ordinate values and accessor /// methods. @@ -25,11 +27,41 @@ use approx::{AbsDiffEq, RelativeEq, UlpsEq}; /// [vector space]: //en.wikipedia.org/wiki/Vector_space #[derive(Eq, PartialEq, Clone, Copy, Debug, Hash, Default)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct Coordinate { +pub struct Coordinate { pub x: T, pub y: T, + pub z: Z, + pub m: M, +} + +impl Coordinate { + /// Create a new instance of a coordinate. + /// **ATTENTION** Use [`coord!`] macro or one of the [`from`] methods instead. + #[inline] + #[doc(hidden)] + pub fn new__(x: T, y: T, z: Z, m: M) -> Self { + Self { x, y, z, m } + } } +/// A lightweight struct used to store coordinates on the 2-dimensional +/// Cartesian plane together with a Measure value of the same type. +/// +/// See also [Coordinate] +pub type CoordinateM = Coordinate; + +/// A lightweight struct used to store coordinates on the 3-dimensional +/// Cartesian plane. +/// +/// See also [Coordinate] +pub type Coordinate3D = Coordinate; + +/// A lightweight struct used to store coordinates on the 3-dimensional +/// Cartesian plane together with a Measure value of the same type. +/// +/// See also [Coordinate] +pub type Coordinate3DM = Coordinate; + impl From<(T, T)> for Coordinate { #[inline] fn from(coords: (T, T)) -> Self { @@ -50,31 +82,38 @@ impl From<[T; 2]> for Coordinate { } } -impl From> for Coordinate { +impl From> for Coordinate { #[inline] - fn from(point: Point) -> Self { - coord! { - x: point.x(), - y: point.y(), - } + fn from(point: Point) -> Self { + point.0 } } impl From> for (T, T) { #[inline] fn from(coord: Coordinate) -> Self { +impl From> for Coordinate { + #[inline] + fn from(point: Point) -> Self { + point.0 + } +} + +impl From> for (T, T) { + #[inline] + fn from(coord: Coordinate) -> Self { (coord.x, coord.y) } } -impl From> for [T; 2] { +impl From> for [T; 2] { #[inline] - fn from(coord: Coordinate) -> Self { + fn from(coord: Coordinate) -> Self { [coord.x, coord.y] } } -impl Coordinate { +impl Coordinate { /// Returns a tuple that contains the x/horizontal & y/vertical component of the coordinate. /// /// # Examples @@ -97,8 +136,6 @@ impl Coordinate { } } -use std::ops::{Add, Div, Mul, Neg, Sub}; - /// Negate a coordinate. /// /// # Examples @@ -112,9 +149,11 @@ use std::ops::{Add, Div, Mul, Neg, Sub}; /// assert_eq!(q.x, -p.x); /// assert_eq!(q.y, -p.y); /// ``` -impl Neg for Coordinate +impl Neg for Coordinate where T: CoordNum + Neg, + Z: ZCoord + Neg, + M: Measure + Neg, { type Output = Self; @@ -123,6 +162,8 @@ where coord! { x: -self.x, y: -self.y, + z: -self.z, + m: -self.m, } } } @@ -141,7 +182,7 @@ where /// assert_eq!(sum.x, 2.75); /// assert_eq!(sum.y, 5.0); /// ``` -impl Add for Coordinate { +impl Add for Coordinate { type Output = Self; #[inline] @@ -149,6 +190,8 @@ impl Add for Coordinate { coord! { x: self.x + rhs.x, y: self.y + rhs.y, + z: self.z + rhs.z, + m: self.m + rhs.m, } } } @@ -167,7 +210,7 @@ impl Add for Coordinate { /// assert_eq!(diff.x, 0.25); /// assert_eq!(diff.y, 0.); /// ``` -impl Sub for Coordinate { +impl Sub for Coordinate { type Output = Self; #[inline] @@ -175,6 +218,8 @@ impl Sub for Coordinate { coord! { x: self.x - rhs.x, y: self.y - rhs.y, + z: self.z - rhs.z, + m: self.m - rhs.m, } } } @@ -192,7 +237,12 @@ impl Sub for Coordinate { /// assert_eq!(q.x, 5.0); /// assert_eq!(q.y, 10.0); /// ``` -impl Mul for Coordinate { +impl Mul for Coordinate +where + T: CoordNum, + Z: ZCoord + Mul, + M: Measure + Mul, +{ type Output = Self; #[inline] @@ -200,6 +250,8 @@ impl Mul for Coordinate { coord! { x: self.x * rhs, y: self.y * rhs, + z: self.z * rhs, + m: self.m * rhs, } } } @@ -217,7 +269,12 @@ impl Mul for Coordinate { /// assert_eq!(q.x, 1.25); /// assert_eq!(q.y, 2.5); /// ``` -impl Div for Coordinate { +impl Div for Coordinate +where + T: CoordNum, + Z: ZCoord + Div, + M: Measure + Div, +{ type Output = Self; #[inline] @@ -225,11 +282,12 @@ impl Div for Coordinate { coord! { x: self.x / rhs, y: self.y / rhs, + z: self.z / rhs, + m: self.m / rhs, } } } -use num_traits::Zero; /// Create a coordinate at the origin. /// /// # Examples @@ -243,24 +301,26 @@ use num_traits::Zero; /// assert_eq!(p.x, 0.); /// assert_eq!(p.y, 0.); /// ``` -impl Coordinate { +impl Coordinate { #[inline] pub fn zero() -> Self { coord! { x: T::zero(), y: T::zero(), + z: Z::zero(), + m: M::zero(), } } } -impl Zero for Coordinate { +impl Zero for Coordinate { #[inline] fn zero() -> Self { Self::zero() } #[inline] fn is_zero(&self) -> bool { - self.x.is_zero() && self.y.is_zero() + self.x.is_zero() && self.y.is_zero() && self.z.is_zero() && self.m.is_zero() } } @@ -387,3 +447,35 @@ where } } } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_coordinates() { + let p = coord! { x: 1.0, y: 2.0 }; + assert_relative_eq!(p.x, 1.0); + assert_relative_eq!(p.y, 2.0); + assert_eq!(p.z, NoValue); + assert_eq!(p.m, NoValue); + + let p = coord! { x: 1.0, y: 2.0, z: 3.0 }; + assert_relative_eq!(p.x, 1.0); + assert_relative_eq!(p.y, 2.0); + assert_relative_eq!(p.z, 3.0); + assert_eq!(p.m, NoValue); + + let p = coord! { x: 1.0, y: 2.0, m: 4_u8 }; + assert_relative_eq!(p.x, 1.0); + assert_relative_eq!(p.y, 2.0); + assert_eq!(p.z, NoValue); + assert_eq!(p.m, 4_u8); + + let p = coord! { x: 1_i32, y: 2_i32, z: 3_i32, m: 4.0_f64 }; + assert_eq!(p.x, 1); + assert_eq!(p.y, 2); + assert_eq!(p.z, 3); + assert_relative_eq!(p.m, 4.0); + } +} diff --git a/geo-types/src/geometry/geometry_collection.rs b/geo-types/src/geometry/geometry_collection.rs index ebf931497d..bc4fb482c4 100644 --- a/geo-types/src/geometry/geometry_collection.rs +++ b/geo-types/src/geometry/geometry_collection.rs @@ -1,11 +1,11 @@ -use crate::{CoordNum, Geometry}; +use crate::{CoordNum, Geometry, Measure, NoValue, ZCoord}; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; use std::iter::FromIterator; use std::ops::{Index, IndexMut}; -/// A collection of [`Geometry`](enum.Geometry.html) types. +/// A generic collection of [Geometry] types with 3D space and measurement value support. /// /// It can be created from a `Vec` of Geometries, or from an Iterator which yields Geometries. /// @@ -23,9 +23,9 @@ use std::ops::{Index, IndexMut}; /// ``` /// use std::convert::TryFrom; /// use geo_types::{Point, point, Geometry, GeometryCollection}; -/// let p = point!(x: 1.0, y: 1.0); +/// let p = point! { x: 1.0, y: 1.0 }; /// let pe = Geometry::Point(p); -/// let gc = GeometryCollection::new_from(vec![pe]); +/// let gc = GeometryCollection::new(vec![pe]); /// for geom in gc { /// println!("{:?}", Point::try_from(geom).unwrap().x()); /// } @@ -35,9 +35,9 @@ use std::ops::{Index, IndexMut}; /// ``` /// use std::convert::TryFrom; /// use geo_types::{Point, point, Geometry, GeometryCollection}; -/// let p = point!(x: 1.0, y: 1.0); +/// let p = point! { x: 1.0, y: 1.0 }; /// let pe = Geometry::Point(p); -/// let gc = GeometryCollection::new_from(vec![pe]); +/// let gc = GeometryCollection::new(vec![pe]); /// gc.iter().for_each(|geom| println!("{:?}", geom)); /// ``` /// @@ -46,9 +46,9 @@ use std::ops::{Index, IndexMut}; /// ``` /// use std::convert::TryFrom; /// use geo_types::{Point, point, Geometry, GeometryCollection}; -/// let p = point!(x: 1.0, y: 1.0); +/// let p = point! { x: 1.0, y: 1.0 }; /// let pe = Geometry::Point(p); -/// let mut gc = GeometryCollection::new_from(vec![pe]); +/// let mut gc = GeometryCollection::new(vec![pe]); /// gc.iter_mut().for_each(|geom| { /// if let Geometry::Point(p) = geom { /// p.set_x(0.2); @@ -63,38 +63,52 @@ use std::ops::{Index, IndexMut}; /// ``` /// use std::convert::TryFrom; /// use geo_types::{Point, point, Geometry, GeometryCollection}; -/// let p = point!(x: 1.0, y: 1.0); +/// let p = point! { x: 1.0, y: 1.0 }; /// let pe = Geometry::Point(p); -/// let gc = GeometryCollection::new_from(vec![pe]); +/// let gc = GeometryCollection::new(vec![pe]); /// println!("{:?}", gc[0]); /// ``` -/// #[derive(Eq, PartialEq, Clone, Debug, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct GeometryCollection(pub Vec>); +pub struct GeometryCollection( + pub Vec>, +); + +/// A geometry collection in 2D space + Measure value. +/// +/// See [GeometryCollection] +pub type GeometryCollectionM = GeometryCollection; + +/// A geometry collection in 3D space. +/// +/// See [GeometryCollection] +pub type GeometryCollection3D = GeometryCollection; + +/// A geometry collection in 3D space + Measure value. +/// +/// See [GeometryCollection] +pub type GeometryCollection3DM = GeometryCollection; // Implementing Default by hand because T does not have Default restriction // todo: consider adding Default as a CoordNum requirement -impl Default for GeometryCollection { +impl Default for GeometryCollection { fn default() -> Self { Self(Vec::new()) } } -impl GeometryCollection { - /// Return an empty GeometryCollection - #[deprecated( - note = "Will be replaced with a parametrized version in upcoming version. Use GeometryCollection::default() instead" - )] - pub fn new() -> Self { - GeometryCollection::default() +impl GeometryCollection { + /// Instantiate Self from the raw content value + pub fn new(value: Vec>) -> Self { + Self(value) } /// DO NOT USE! /// This fn will be renamed to `new` in the upcoming version. /// This fn is not marked as deprecated because it would require extensive refactoring of the geo code. - pub fn new_from(value: Vec>) -> Self { - Self(value) + #[inline] + pub fn new_from(value: Vec>) -> Self { + Self::new(value) } /// Number of geometries in this GeometryCollection @@ -108,8 +122,12 @@ impl GeometryCollection { } } -#[deprecated(since = 0.7.5, note = "Use `GeometryCollection::from(vec![geom])` instead.")] -impl>> From for GeometryCollection { +/// Convert any Geometry (or anything that can be converted to a Geometry) into a +/// GeometryCollection +[deprecated(since = 0.7.5, note = "Use `GeometryCollection::from(vec![geom])` instead.")] +impl>> From + for GeometryCollection +{ fn from(x: IG) -> Self { Self(vec![x.into()]) } @@ -123,37 +141,39 @@ impl>> From> for GeometryCollection } /// Collect Geometries (or what can be converted to a Geometry) into a GeometryCollection -impl>> FromIterator for GeometryCollection { +impl>> FromIterator + for GeometryCollection +{ fn from_iter>(iter: I) -> Self { Self(iter.into_iter().map(|g| g.into()).collect()) } } -impl Index for GeometryCollection { - type Output = Geometry; +impl Index for GeometryCollection { + type Output = Geometry; - fn index(&self, index: usize) -> &Geometry { + fn index(&self, index: usize) -> &Geometry { self.0.index(index) } } -impl IndexMut for GeometryCollection { - fn index_mut(&mut self, index: usize) -> &mut Geometry { +impl IndexMut for GeometryCollection { + fn index_mut(&mut self, index: usize) -> &mut Geometry { self.0.index_mut(index) } } // structure helper for consuming iterator #[derive(Debug)] -pub struct IntoIteratorHelper { - iter: ::std::vec::IntoIter>, +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 = IntoIteratorHelper; +impl IntoIterator for GeometryCollection { + type Item = Geometry; + type IntoIter = IntoIteratorHelper; // note that into_iter() is consuming self fn into_iter(self) -> Self::IntoIter { @@ -164,8 +184,8 @@ impl IntoIterator for GeometryCollection { } // implement Iterator trait for the helper struct, to be used by adapters -impl Iterator for IntoIteratorHelper { - type Item = Geometry; +impl Iterator for IntoIteratorHelper { + type Item = Geometry; // just return the reference fn next(&mut self) -> Option { @@ -175,15 +195,15 @@ impl Iterator for IntoIteratorHelper { // structure helper for non-consuming iterator #[derive(Debug)] -pub struct IterHelper<'a, T: CoordNum> { - iter: ::std::slice::Iter<'a, Geometry>, +pub struct IterHelper<'a, T: CoordNum, Z: ZCoord, M: Measure> { + iter: ::std::slice::Iter<'a, Geometry>, } // implement the IntoIterator trait for a non-consuming iterator. Iteration will // borrow the GeometryCollection -impl<'a, T: CoordNum> IntoIterator for &'a GeometryCollection { - type Item = &'a Geometry; - type IntoIter = IterHelper<'a, T>; +impl<'a, T: CoordNum, Z: ZCoord, M: Measure> IntoIterator for &'a GeometryCollection { + type Item = &'a Geometry; + type IntoIter = IterHelper<'a, T, Z, M>; // note that into_iter() is consuming self fn into_iter(self) -> Self::IntoIter { @@ -194,8 +214,8 @@ impl<'a, T: CoordNum> IntoIterator for &'a GeometryCollection { } // implement the Iterator trait for the helper struct, to be used by adapters -impl<'a, T: CoordNum> Iterator for IterHelper<'a, T> { - type Item = &'a Geometry; +impl<'a, T: CoordNum, Z: 'a + ZCoord, M: 'a + Measure> Iterator for IterHelper<'a, T, Z, M> { + type Item = &'a Geometry; // just return the str reference fn next(&mut self) -> Option { @@ -205,15 +225,15 @@ impl<'a, T: CoordNum> Iterator for IterHelper<'a, T> { // structure helper for mutable non-consuming iterator #[derive(Debug)] -pub struct IterMutHelper<'a, T: CoordNum> { - iter: ::std::slice::IterMut<'a, Geometry>, +pub struct IterMutHelper<'a, T: CoordNum, Z: ZCoord, M: Measure> { + 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: CoordNum> IntoIterator for &'a mut GeometryCollection { - type Item = &'a mut Geometry; - type IntoIter = IterMutHelper<'a, T>; +impl<'a, T: CoordNum, Z: ZCoord, M: Measure> IntoIterator for &'a mut GeometryCollection { + type Item = &'a mut Geometry; + type IntoIter = IterMutHelper<'a, T, Z, M>; // note that into_iter() is consuming self fn into_iter(self) -> Self::IntoIter { @@ -224,8 +244,8 @@ impl<'a, T: CoordNum> IntoIterator for &'a mut GeometryCollection { } // implement the Iterator trait for the helper struct, to be used by adapters -impl<'a, T: CoordNum> Iterator for IterMutHelper<'a, T> { - type Item = &'a mut Geometry; +impl<'a, T: CoordNum, Z: ZCoord, M: Measure> Iterator for IterMutHelper<'a, T, Z, M> { + type Item = &'a mut Geometry; // just return the str reference fn next(&mut self) -> Option { @@ -233,12 +253,12 @@ impl<'a, T: CoordNum> Iterator for IterMutHelper<'a, T> { } } -impl<'a, T: CoordNum> GeometryCollection { - pub fn iter(&'a self) -> IterHelper<'a, T> { +impl<'a, T: CoordNum, Z: ZCoord, M: Measure> GeometryCollection { + pub fn iter(&'a self) -> IterHelper<'a, T, Z, M> { self.into_iter() } - pub fn iter_mut(&'a mut self) -> IterMutHelper<'a, T> { + pub fn iter_mut(&'a mut self) -> IterMutHelper<'a, T, Z, M> { self.into_iter() } } @@ -260,8 +280,8 @@ where /// ``` /// use geo_types::{GeometryCollection, point}; /// - /// let a = GeometryCollection::new_from(vec![point![x: 1.0, y: 2.0].into()]); - /// let b = GeometryCollection::new_from(vec![point![x: 1.0, y: 2.01].into()]); + /// let a = GeometryCollection::new(vec![point![x: 1.0, y: 2.0].into()]); + /// let b = GeometryCollection::new(vec![point![x: 1.0, y: 2.01].into()]); /// /// approx::assert_relative_eq!(a, b, max_relative=0.1); /// approx::assert_relative_ne!(a, b, max_relative=0.0001); @@ -302,8 +322,8 @@ where /// ``` /// use geo_types::{GeometryCollection, point}; /// - /// let a = GeometryCollection::new_from(vec![point![x: 0.0, y: 0.0].into()]); - /// let b = GeometryCollection::new_from(vec![point![x: 0.0, y: 0.1].into()]); + /// let a = GeometryCollection::new(vec![point![x: 0.0, y: 0.0].into()]); + /// let b = GeometryCollection::new(vec![point![x: 0.0, y: 0.1].into()]); /// /// approx::abs_diff_eq!(a, b, epsilon=0.1); /// approx::abs_diff_ne!(a, b, epsilon=0.001); diff --git a/geo-types/src/geometry/line.rs b/geo-types/src/geometry/line.rs index d1d00c8ccd..7a649fef49 100644 --- a/geo-types/src/geometry/line.rs +++ b/geo-types/src/geometry/line.rs @@ -1,9 +1,10 @@ -use crate::{CoordNum, Coordinate, Point}; +use crate::{CoordNum, Coordinate, Measure, NoValue, Point, ZCoord}; +#[cfg(doc)] +use crate::{Coordinate3D, Coordinate3DM, CoordinateM}; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; -/// A line segment made up of exactly two -/// [`Coordinate`s](struct.Coordinate.html). +/// A generic line segment made up of exactly two [Coordinate] values. /// /// # Semantics /// @@ -11,36 +12,65 @@ use approx::{AbsDiffEq, RelativeEq}; /// `LineString` with the two end points. #[derive(Eq, PartialEq, Clone, Copy, Debug, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct Line { - pub start: Coordinate, - pub end: Coordinate, +pub struct Line { + pub start: Coordinate, + pub end: Coordinate, } -impl Line { +/// A line segment made up of exactly two [CoordinateM] values. +/// +/// # Semantics +/// +/// The _interior_ and _boundary_ are defined as with a +/// `LineString` with the two end points. +pub type LineM = Line; + +/// A line segment in 3D made up of exactly two [Coordinate3D] values. +/// +/// # Semantics +/// +/// The _interior_ and _boundary_ are defined as with a +/// `LineString` with the two end points. +pub type Line3D = Line; + +/// A line segment in 3D made up of exactly two [Coordinate3DM] values. +/// +/// # Semantics +/// +/// The _interior_ and _boundary_ are defined as with a +/// `LineString` with the two end points. +pub type Line3DM = Line; + +impl Line { /// Creates a new line segment. /// /// # Examples /// /// ``` - /// use geo_types::{coord, Line}; + /// use geo_types::{coord, Line, Line3DM}; /// /// let line = Line::new(coord! { x: 0., y: 0. }, coord! { x: 1., y: 2. }); - /// /// assert_eq!(line.start, coord! { x: 0., y: 0. }); /// assert_eq!(line.end, coord! { x: 1., y: 2. }); + /// + /// let line = Line3DM::new(coord! { x: 0., y: 0., z: 0., m: 1. }, coord! { x: 1., y: 2., z: 3., m: 4. }); + /// assert_eq!(line.start, coord! { x: 0., y: 0., z: 0., m: 1. }); + /// assert_eq!(line.end, coord! { x: 1., y: 2., z: 3., m: 4. }); /// ``` pub fn new(start: C, end: C) -> Self where - C: Into>, + C: Into>, { Self { start: start.into(), end: end.into(), } } +} +impl Line { /// Calculate the difference in coordinates (Δx, Δy). - pub fn delta(&self) -> Coordinate { + pub fn delta(&self) -> Coordinate { self.end - self.start } @@ -82,6 +112,28 @@ impl Line { self.delta().y } + /// Calculate the difference in ‘z’ components (Δz). + /// + /// Equivalent to: + /// + /// ```rust + /// # use geo_types::{Line3D, point}; + /// # let line = Line3D::new( + /// # point! { x: 1., y: 3., z: 5. }, + /// # point! { x: 0., y: 9., z: 4. }, + /// # ); + /// # assert_eq!( + /// # line.dz(), + /// line.end.z - line.start.z + /// # ); + /// ``` + pub fn dz(&self) -> Z { + self.delta().z + } +} + +/// Implementations for 2D lines with optional Measure +impl Line { /// Calculate the slope (Δy/Δx). /// /// Equivalent to: @@ -141,16 +193,18 @@ impl Line { pub fn determinant(&self) -> T { self.start.x * self.end.y - self.start.y * self.end.x } +} - pub fn start_point(&self) -> Point { +impl Line { + pub fn start_point(&self) -> Point { Point::from(self.start) } - pub fn end_point(&self) -> Point { + pub fn end_point(&self) -> Point { Point::from(self.end) } - pub fn points(&self) -> (Point, Point) { + pub fn points(&self) -> (Point, Point) { (self.start_point(), self.end_point()) } } @@ -224,11 +278,11 @@ impl + CoordNum> AbsDiffEq for Line { #[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] macro_rules! impl_rstar_line { ($rstar:ident) => { - impl ::$rstar::RTreeObject for Line + impl ::$rstar::RTreeObject for crate::Line where T: ::num_traits::Float + ::$rstar::RTreeNum, { - type Envelope = ::$rstar::AABB>; + type Envelope = ::$rstar::AABB>; fn envelope(&self) -> Self::Envelope { let bounding_rect = crate::private_utils::line_bounding_rect(*self); @@ -236,11 +290,11 @@ macro_rules! impl_rstar_line { } } - impl ::$rstar::PointDistance for Line + impl ::$rstar::PointDistance for crate::Line where T: ::num_traits::Float + ::$rstar::RTreeNum, { - fn distance_2(&self, point: &Point) -> T { + fn distance_2(&self, point: &crate::Point) -> T { let d = crate::private_utils::point_line_euclidean_distance(*point, *self); d.powi(2) } diff --git a/geo-types/src/geometry/line_string.rs b/geo-types/src/geometry/line_string.rs index ac435b2d6d..fed71d7891 100644 --- a/geo-types/src/geometry/line_string.rs +++ b/geo-types/src/geometry/line_string.rs @@ -1,10 +1,9 @@ -#[cfg(any(feature = "approx", test))] -use approx::{AbsDiffEq, RelativeEq}; - -use crate::{CoordNum, Coordinate, Line, Point, Triangle}; +use crate::*; use std::iter::FromIterator; use std::ops::{Index, IndexMut}; +/// A generic line string with 3D space + Measure value support. +/// /// An ordered collection of two or more [`Coordinate`]s, representing a /// path between locations. /// @@ -129,17 +128,46 @@ use std::ops::{Index, IndexMut}; /// let point_vec = line_string.clone().into_points(); /// /// ``` - #[derive(Eq, PartialEq, Clone, Debug, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct LineString(pub Vec>); +pub struct LineString( + pub Vec>, +); + +/// A line string with a measurement value in 2D space. +/// +/// See [LineString] +pub type LineStringM = LineString; + +/// A line string in 3D space. +/// +/// See [LineString] +pub type LineString3D = LineString; + +/// A line string with a measurement value in 3D space. +/// +/// See [LineString] +pub type LineString3DM = LineString; /// A [`Point`] iterator returned by the `points` method #[derive(Debug)] -pub struct PointsIter<'a, T: CoordNum + 'a>(::std::slice::Iter<'a, Coordinate>); +pub struct PointsIter<'a, T, Z = NoValue, M = NoValue>(::std::slice::Iter<'a, Coordinate>) +where + T: CoordNum + 'a, + Z: ZCoord + 'a, + M: Measure + 'a; -impl<'a, T: CoordNum> Iterator for PointsIter<'a, T> { - type Item = Point; +pub type PointsIterM<'a, T> = PointsIter<'a, T, NoValue, T>; +pub type PointsIter3D<'a, T> = PointsIter<'a, T, T, NoValue>; +pub type PointsIter3DM<'a, T> = PointsIter<'a, T, T, T>; + +impl<'a, T, Z, M> Iterator for PointsIter<'a, T, Z, M> +where + T: CoordNum + 'a, + Z: ZCoord + 'a, + M: Measure + 'a, +{ + type Item = Point; fn next(&mut self) -> Option { self.0.next().map(|c| Point::from(*c)) @@ -150,13 +178,13 @@ impl<'a, T: CoordNum> Iterator for PointsIter<'a, T> { } } -impl<'a, T: CoordNum> ExactSizeIterator for PointsIter<'a, T> { +impl<'a, T: CoordNum, Z: ZCoord, M: Measure> ExactSizeIterator for PointsIter<'a, T, Z, M> { fn len(&self) -> usize { self.0.len() } } -impl<'a, T: CoordNum> DoubleEndedIterator for PointsIter<'a, T> { +impl<'a, T: CoordNum, Z: ZCoord, M: Measure> DoubleEndedIterator for PointsIter<'a, T, Z, M> { fn next_back(&mut self) -> Option { self.0.next_back().map(|c| Point::from(*c)) } @@ -164,10 +192,12 @@ impl<'a, T: CoordNum> DoubleEndedIterator for PointsIter<'a, T> { /// 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>); +pub struct CoordinatesIter<'a, T: CoordNum + 'a, Z: ZCoord + 'a, M: Measure + 'a>( + ::std::slice::Iter<'a, Coordinate>, +); -impl<'a, T: CoordNum> Iterator for CoordinatesIter<'a, T> { - type Item = &'a Coordinate; +impl<'a, T: CoordNum, Z: ZCoord, M: Measure> Iterator for CoordinatesIter<'a, T, Z, M> { + type Item = &'a Coordinate; fn next(&mut self) -> Option { self.0.next() @@ -178,52 +208,46 @@ impl<'a, T: CoordNum> Iterator for CoordinatesIter<'a, T> { } } -impl<'a, T: CoordNum> ExactSizeIterator for CoordinatesIter<'a, T> { +impl<'a, T: CoordNum, Z: ZCoord, M: Measure> ExactSizeIterator for CoordinatesIter<'a, T, Z, M> { fn len(&self) -> usize { self.0.len() } } -impl<'a, T: CoordNum> DoubleEndedIterator for CoordinatesIter<'a, T> { +impl<'a, T: CoordNum, Z: ZCoord, M: Measure> DoubleEndedIterator for CoordinatesIter<'a, T, Z, M> { fn next_back(&mut self) -> Option { self.0.next_back() } } -impl LineString { +impl LineString { /// Instantiate Self from the raw content value - pub fn new(value: Vec>) -> Self { + pub fn new(value: Vec>) -> Self { Self(value) } /// 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 { + pub fn points(&self) -> PointsIter { PointsIter(self.0.iter()) } /// Return an iterator yielding the members of a [`LineString`] as [`Coordinate`]s - pub fn coords(&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 coords_mut(&mut self) -> impl Iterator> { + pub fn coords_mut(&mut self) -> impl Iterator> { self.0.iter_mut() } /// Return the coordinates of a [`LineString`] as a [`Vec`] of [`Point`]s - pub fn into_points(self) -> Vec> { + pub fn into_points(self) -> Vec> { self.0.into_iter().map(Point::from).collect() } /// Return the coordinates of a [`LineString`] as a [`Vec`] of [`Coordinate`]s - pub fn into_inner(self) -> Vec> { + pub fn into_inner(self) -> Vec> { self.0 } @@ -255,7 +279,7 @@ impl LineString { /// ); /// assert!(lines.next().is_none()); /// ``` - pub fn lines(&'_ self) -> impl ExactSizeIterator + Iterator> + '_ { + pub fn lines(&'_ self) -> impl ExactSizeIterator + Iterator> + '_ { self.0.windows(2).map(|w| { // slice::windows(N) is guaranteed to yield a slice with exactly N elements unsafe { Line::new(*w.get_unchecked(0), *w.get_unchecked(1)) } @@ -263,7 +287,7 @@ impl LineString { } /// An iterator which yields the coordinates of a [`LineString`] as [Triangle]s - pub fn triangles(&'_ self) -> impl ExactSizeIterator + Iterator> + '_ { + 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 unsafe { @@ -336,38 +360,42 @@ impl LineString { } /// Turn a [`Vec`] of [`Point`]-like objects into a [`LineString`]. -impl>> From> for LineString { +impl>> From> + for LineString +{ fn from(v: Vec) -> Self { Self(v.into_iter().map(|c| c.into()).collect()) } } -impl From> for LineString { - fn from(line: Line) -> Self { +impl From> for LineString { + fn from(line: Line) -> Self { Self(vec![line.start, line.end]) } } /// Turn an iterator of [`Point`]-like objects into a [`LineString`]. -impl>> FromIterator for LineString { +impl>> FromIterator + for LineString +{ fn from_iter>(iter: I) -> Self { Self(iter.into_iter().map(|c| c.into()).collect()) } } /// Iterate over all the [`Coordinate`]s in this [`LineString`]. -impl IntoIterator for LineString { - type Item = Coordinate; - type IntoIter = ::std::vec::IntoIter>; +impl IntoIterator for LineString { + type Item = Coordinate; + type IntoIter = ::std::vec::IntoIter>; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } -impl<'a, T: CoordNum> IntoIterator for &'a LineString { - type Item = &'a Coordinate; - type IntoIter = CoordinatesIter<'a, T>; +impl<'a, T: CoordNum, Z: ZCoord, M: Measure> IntoIterator for &'a LineString { + type Item = &'a Coordinate; + type IntoIter = CoordinatesIter<'a, T, Z, M>; fn into_iter(self) -> Self::IntoIter { CoordinatesIter(self.0.iter()) @@ -375,33 +403,33 @@ impl<'a, T: CoordNum> IntoIterator for &'a 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>; +impl<'a, T: CoordNum, Z: ZCoord, M: Measure> IntoIterator for &'a mut LineString { + type Item = &'a mut Coordinate; + type IntoIter = ::std::slice::IterMut<'a, Coordinate>; - fn into_iter(self) -> ::std::slice::IterMut<'a, Coordinate> { + fn into_iter(self) -> ::std::slice::IterMut<'a, Coordinate> { self.0.iter_mut() } } -impl Index for LineString { - type Output = Coordinate; +impl Index for LineString { + type Output = Coordinate; - fn index(&self, index: usize) -> &Coordinate { + fn index(&self, index: usize) -> &Coordinate { self.0.index(index) } } -impl IndexMut for LineString { - fn index_mut(&mut self, index: usize) -> &mut Coordinate { +impl IndexMut for LineString { + fn index_mut(&mut self, index: usize) -> &mut Coordinate { self.0.index_mut(index) } } #[cfg(any(feature = "approx", test))] -impl RelativeEq for LineString +impl approx::RelativeEq for LineString where - T: AbsDiffEq + CoordNum + RelativeEq, + T: approx::AbsDiffEq + CoordNum + approx::RelativeEq, { #[inline] fn default_max_relative() -> Self::Epsilon { @@ -446,7 +474,7 @@ where } #[cfg(any(feature = "approx", test))] -impl + CoordNum> AbsDiffEq for LineString { +impl + CoordNum> approx::AbsDiffEq for LineString { type Epsilon = T; #[inline] @@ -481,33 +509,33 @@ impl + CoordNum> AbsDiffEq for LineString { #[cfg(any(feature = "rstar_0_8", feature = "rstar_0_9"))] macro_rules! impl_rstar_line_string { ($rstar:ident) => { - impl ::$rstar::RTreeObject for LineString + impl ::$rstar::RTreeObject for crate::LineString where T: ::num_traits::Float + ::$rstar::RTreeNum, { - type Envelope = ::$rstar::AABB>; + type Envelope = ::$rstar::AABB>; fn envelope(&self) -> Self::Envelope { use num_traits::Bounded; let bounding_rect = crate::private_utils::line_string_bounding_rect(self); match bounding_rect { None => ::$rstar::AABB::from_corners( - Point::new(Bounded::min_value(), Bounded::min_value()), - Point::new(Bounded::max_value(), Bounded::max_value()), + crate::Point::new(Bounded::min_value(), Bounded::min_value()), + crate::Point::new(Bounded::max_value(), Bounded::max_value()), ), Some(b) => ::$rstar::AABB::from_corners( - Point::new(b.min().x, b.min().y), - Point::new(b.max().x, b.max().y), + crate::Point::new(b.min().x, b.min().y), + crate::Point::new(b.max().x, b.max().y), ), } } } - impl ::$rstar::PointDistance for LineString + impl ::$rstar::PointDistance for crate::LineString where T: ::num_traits::Float + ::$rstar::RTreeNum, { - fn distance_2(&self, point: &Point) -> T { + fn distance_2(&self, point: &crate::Point) -> T { let d = crate::private_utils::point_line_string_euclidean_distance(*point, self); if d == T::zero() { d @@ -528,8 +556,8 @@ impl_rstar_line_string!(rstar_0_9); #[cfg(test)] mod test { use super::*; - use crate::coord; - use approx::AbsDiffEq; + use crate::{coord, Line}; + use approx::{AbsDiffEq, RelativeEq}; #[test] fn test_exact_size() { diff --git a/geo-types/src/geometry/mod.rs b/geo-types/src/geometry/mod.rs index 5a950fb076..83570c2299 100644 --- a/geo-types/src/geometry/mod.rs +++ b/geo-types/src/geometry/mod.rs @@ -41,192 +41,99 @@ use std::convert::TryFrom; /// ``` /// use std::convert::TryFrom; /// use geo_types::{Point, point, Geometry, GeometryCollection}; -/// let p = point!(x: 1.0, y: 1.0); +/// +/// let p = point!{ x: 1.0, y: 1.0 }; /// let pe: Geometry = p.into(); /// let pn = Point::try_from(pe).unwrap(); /// ``` -/// #[derive(Eq, PartialEq, Clone, Debug, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub enum Geometry { - Point(Point), - Line(Line), - LineString(LineString), - Polygon(Polygon), - MultiPoint(MultiPoint), - MultiLineString(MultiLineString), - MultiPolygon(MultiPolygon), - GeometryCollection(GeometryCollection), - Rect(Rect), - Triangle(Triangle), +pub enum Geometry { + Point(Point), + Line(Line), + LineString(LineString), + Polygon(Polygon), + MultiPoint(MultiPoint), + MultiLineString(MultiLineString), + MultiPolygon(MultiPolygon), + GeometryCollection(GeometryCollection), + Rect(Rect), + Triangle(Triangle), } -impl From> for Geometry { - fn from(x: Point) -> Self { +pub type GeometryM = Geometry; +pub type Geometry3D = Geometry; +pub type Geometry3DM = Geometry; + +impl From> for Geometry { + fn from(x: Point) -> Self { Self::Point(x) } } -impl From> for Geometry { - fn from(x: Line) -> Self { +impl From> for Geometry { + fn from(x: Line) -> Self { Self::Line(x) } } -impl From> for Geometry { - fn from(x: LineString) -> Self { +impl From> for Geometry { + fn from(x: LineString) -> Self { Self::LineString(x) } } -impl From> for Geometry { - fn from(x: Polygon) -> Self { +impl From> for Geometry { + fn from(x: Polygon) -> Self { Self::Polygon(x) } } -impl From> for Geometry { - fn from(x: MultiPoint) -> Self { +impl From> for Geometry { + fn from(x: MultiPoint) -> Self { Self::MultiPoint(x) } } -impl From> for Geometry { - fn from(x: MultiLineString) -> Self { +impl From> for Geometry { + fn from(x: MultiLineString) -> Self { Self::MultiLineString(x) } } -impl From> for Geometry { - fn from(x: MultiPolygon) -> Self { +impl From> for Geometry { + fn from(x: MultiPolygon) -> Self { Self::MultiPolygon(x) } } // Disabled until we remove the deprecated GeometryCollection::from(single_geom) impl. -// impl From> for Geometry { -// fn from(x: GeometryCollection) -> Self { +// impl From> for Geometry { +// fn from(x: GeometryCollection) -> Self { // Self::GeometryCollection(x) // } // } -impl From> for Geometry { - fn from(x: Rect) -> Self { +impl From> for Geometry { + fn from(x: Rect) -> Self { Self::Rect(x) } } -impl From> for Geometry { - fn from(x: Triangle) -> Self { +impl From> for Geometry { + fn from(x: Triangle) -> Self { Self::Triangle(x) } } -impl Geometry { - /// If this Geometry is a Point, then return that, else None. - /// - /// # Examples - /// - /// ``` - /// use geo_types::*; - /// use std::convert::TryInto; - /// - /// let g = Geometry::Point(Point::new(0., 0.)); - /// let p2: Point = g.try_into().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) - } else { - None - } - } - - /// 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) - } else { - None - } - } - - /// 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) - } else { - None - } - } - - /// 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) - } else { - None - } - } - - /// 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) - } else { - None - } - } - - /// 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) - } else { - None - } - } - - /// 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) - } else { - None - } - } -} - macro_rules! try_from_geometry_impl { - ($($type: ident),+) => { + ($($type: ident),+ $(,)? ) => { $( /// Convert a Geometry enum into its inner type. /// /// Fails if the enum case does not match the type you are trying to convert it to. - impl TryFrom> for $type { + impl TryFrom> for $type { type Error = Error; - fn try_from(geom: Geometry) -> Result { + fn try_from(geom: Geometry) -> Result { match geom { Geometry::$type(g) => Ok(g), other => Err(Error::MismatchedGeometry { - expected: type_name::<$type>(), + expected: type_name::<$type>(), found: inner_type_name(other) }) } @@ -236,6 +143,7 @@ macro_rules! try_from_geometry_impl { } } +// `concat_idents` is not available, so hacking around it try_from_geometry_impl!( Point, Line, @@ -247,24 +155,23 @@ try_from_geometry_impl!( // Disabled until we remove the deprecated GeometryCollection::from(single_geom) impl. // GeometryCollection, Rect, - Triangle + Triangle, ); -fn inner_type_name(geometry: Geometry) -> &'static str -where - T: CoordNum, -{ +fn inner_type_name( + geometry: Geometry, +) -> &'static str { match geometry { - Geometry::Point(_) => type_name::>(), - Geometry::Line(_) => type_name::>(), - Geometry::LineString(_) => type_name::>(), - Geometry::Polygon(_) => type_name::>(), - Geometry::MultiPoint(_) => type_name::>(), - Geometry::MultiLineString(_) => type_name::>(), - Geometry::MultiPolygon(_) => type_name::>(), - Geometry::GeometryCollection(_) => type_name::>(), - Geometry::Rect(_) => type_name::>(), - Geometry::Triangle(_) => type_name::>(), + Geometry::Point(_) => type_name::>(), + Geometry::Line(_) => type_name::>(), + Geometry::LineString(_) => type_name::>(), + Geometry::Polygon(_) => type_name::>(), + Geometry::MultiPoint(_) => type_name::>(), + Geometry::MultiLineString(_) => type_name::>(), + Geometry::MultiPolygon(_) => type_name::>(), + Geometry::GeometryCollection(_) => type_name::>(), + Geometry::Rect(_) => type_name::>(), + Geometry::Triangle(_) => type_name::>(), } } diff --git a/geo-types/src/geometry/multi_line_string.rs b/geo-types/src/geometry/multi_line_string.rs index d632f76873..f3a1c867aa 100644 --- a/geo-types/src/geometry/multi_line_string.rs +++ b/geo-types/src/geometry/multi_line_string.rs @@ -1,4 +1,4 @@ -use crate::{CoordNum, LineString}; +use crate::{CoordNum, LineString, Measure, NoValue, ZCoord}; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; @@ -33,11 +33,17 @@ use std::iter::FromIterator; /// of a closed `MultiLineString` is always empty. #[derive(Eq, PartialEq, Clone, Debug, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct MultiLineString(pub Vec>); +pub struct MultiLineString( + pub Vec>, +); -impl MultiLineString { +pub type MultiLineStringM = MultiLineString; +pub type MultiLineString3D = MultiLineString; +pub type MultiLineString3DM = MultiLineString; + +impl MultiLineString { /// Instantiate Self from the raw content value - pub fn new(value: Vec>) -> Self { + pub fn new(value: Vec>) -> Self { Self(value) } @@ -67,51 +73,55 @@ impl MultiLineString { } } -impl>> From for MultiLineString { +impl>> From + for MultiLineString +{ fn from(ls: ILS) -> Self { Self(vec![ls.into()]) } } -impl>> FromIterator for MultiLineString { +impl>> FromIterator + for MultiLineString +{ fn from_iter>(iter: I) -> Self { Self(iter.into_iter().map(|ls| ls.into()).collect()) } } -impl IntoIterator for MultiLineString { - type Item = LineString; - type IntoIter = ::std::vec::IntoIter>; +impl IntoIterator for MultiLineString { + type Item = LineString; + type IntoIter = ::std::vec::IntoIter>; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } -impl<'a, T: CoordNum> IntoIterator for &'a MultiLineString { - type Item = &'a LineString; - type IntoIter = ::std::slice::Iter<'a, LineString>; +impl<'a, T: CoordNum, Z: ZCoord, M: Measure> IntoIterator for &'a MultiLineString { + type Item = &'a LineString; + type IntoIter = ::std::slice::Iter<'a, LineString>; fn into_iter(self) -> Self::IntoIter { (&self.0).iter() } } -impl<'a, T: CoordNum> IntoIterator for &'a mut MultiLineString { - type Item = &'a mut LineString; - type IntoIter = ::std::slice::IterMut<'a, LineString>; +impl<'a, T: CoordNum, Z: ZCoord, M: Measure> IntoIterator for &'a mut MultiLineString { + type Item = &'a mut LineString; + type IntoIter = ::std::slice::IterMut<'a, LineString>; fn into_iter(self) -> Self::IntoIter { (&mut self.0).iter_mut() } } -impl MultiLineString { - pub fn iter(&self) -> impl Iterator> { +impl MultiLineString { + pub fn iter(&self) -> impl Iterator> { self.0.iter() } - pub fn iter_mut(&mut self) -> impl Iterator> { + pub fn iter_mut(&mut self) -> impl Iterator> { self.0.iter_mut() } } @@ -195,7 +205,7 @@ where #[cfg(test)] mod test { use super::*; - use crate::line_string; + use crate::{line_string, LineString}; #[test] fn test_iter() { diff --git a/geo-types/src/geometry/multi_point.rs b/geo-types/src/geometry/multi_point.rs index a39bb22526..d0bfdce0ec 100644 --- a/geo-types/src/geometry/multi_point.rs +++ b/geo-types/src/geometry/multi_point.rs @@ -1,11 +1,11 @@ -use crate::{CoordNum, Point}; - +use crate::{CoordNum, Measure, NoValue, Point, ZCoord}; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; - use std::iter::FromIterator; -/// A collection of [`Point`s](struct.Point.html). Can +/// A generic line string with 3D space + Measure value support. +/// +/// A collection of [Point]s. Can /// be created from a `Vec` of `Point`s, or from an /// Iterator which yields `Point`s. Iterating over this /// object yields the component `Point`s. @@ -30,9 +30,28 @@ use std::iter::FromIterator; /// ``` #[derive(Eq, PartialEq, Clone, Debug, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct MultiPoint(pub Vec>); +pub struct MultiPoint( + pub Vec>, +); + +/// A collection of points with a measurement value in 2D space. +/// +/// See [MultiPoint] +pub type MultiPointM = MultiPoint; + +/// A collection of points in 3D space. +/// +/// See [MultiPoint] +pub type MultiPoint3D = MultiPoint; + +/// A collection of points with a measurement value in 3D space. +/// +/// See [MultiPoint] +pub type MultiPoint3DM = MultiPoint; -impl>> From for MultiPoint { +impl>> From + for MultiPoint +{ /// Convert a single `Point` (or something which can be converted to a `Point`) into a /// one-member `MultiPoint` fn from(x: IP) -> Self { @@ -40,7 +59,9 @@ impl>> From for MultiPoint { } } -impl>> From> for MultiPoint { +impl>> From> + for MultiPoint +{ /// Convert a `Vec` of `Points` (or `Vec` of things which can be converted to a `Point`) into a /// `MultiPoint`. fn from(v: Vec) -> Self { @@ -48,7 +69,9 @@ impl>> From> for MultiPoint { } } -impl>> FromIterator for MultiPoint { +impl>> FromIterator + for MultiPoint +{ /// Collect the results of a `Point` iterator into a `MultiPoint` fn from_iter>(iter: I) -> Self { Self(iter.into_iter().map(|p| p.into()).collect()) @@ -56,43 +79,43 @@ impl>> FromIterator for MultiPoint { } /// Iterate over the `Point`s in this `MultiPoint`. -impl IntoIterator for MultiPoint { - type Item = Point; - type IntoIter = ::std::vec::IntoIter>; +impl IntoIterator for MultiPoint { + type Item = Point; + type IntoIter = ::std::vec::IntoIter>; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } -impl<'a, T: CoordNum> IntoIterator for &'a MultiPoint { - type Item = &'a Point; - type IntoIter = ::std::slice::Iter<'a, Point>; +impl<'a, T: CoordNum, Z: ZCoord, M: Measure> IntoIterator for &'a MultiPoint { + type Item = &'a Point; + type IntoIter = ::std::slice::Iter<'a, Point>; fn into_iter(self) -> Self::IntoIter { (&self.0).iter() } } -impl<'a, T: CoordNum> IntoIterator for &'a mut MultiPoint { - type Item = &'a mut Point; - type IntoIter = ::std::slice::IterMut<'a, Point>; +impl<'a, T: CoordNum, Z: ZCoord, M: Measure> IntoIterator for &'a mut MultiPoint { + type Item = &'a mut Point; + type IntoIter = ::std::slice::IterMut<'a, Point>; fn into_iter(self) -> Self::IntoIter { (&mut self.0).iter_mut() } } -impl MultiPoint { - pub fn new(value: Vec>) -> Self { +impl MultiPoint { + pub fn new(value: Vec>) -> Self { Self(value) } - pub fn iter(&self) -> impl Iterator> { + pub fn iter(&self) -> impl Iterator> { self.0.iter() } - pub fn iter_mut(&mut self) -> impl Iterator> { + pub fn iter_mut(&mut self) -> impl Iterator> { self.0.iter_mut() } } diff --git a/geo-types/src/geometry/multi_polygon.rs b/geo-types/src/geometry/multi_polygon.rs index 3e9bb73cd5..7b47b0fb54 100644 --- a/geo-types/src/geometry/multi_polygon.rs +++ b/geo-types/src/geometry/multi_polygon.rs @@ -1,10 +1,11 @@ -use crate::{CoordNum, Polygon}; - +use crate::{CoordNum, Measure, NoValue, Polygon, ZCoord}; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; use std::iter::FromIterator; -/// A collection of [`Polygon`s](struct.Polygon.html). Can +/// A generic collection of polygons with 3D space + Measure value support. +/// +/// A collection of [Polygon]s. Can /// be created from a `Vec` of `Polygon`s, or from an /// Iterator which yields `Polygon`s. Iterating over this /// object yields the component `Polygon`s. @@ -27,64 +28,87 @@ use std::iter::FromIterator; /// predicates that operate on it. #[derive(Eq, PartialEq, Clone, Debug, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct MultiPolygon(pub Vec>); +pub struct MultiPolygon( + pub Vec>, +); + +/// A collection of polygons with a measurement value in 2D space. +/// +/// See [MultiPolygon] +pub type MultiPolygonM = MultiPolygon; + +/// A collection of polygons in 3D space. +/// +/// See [MultiPolygon] +pub type MultiPolygon3D = MultiPolygon; -impl>> From for MultiPolygon { +/// A collection of polygons with a measurement value in 3D space. +/// +/// See [MultiPolygon] +pub type MultiPolygon3DM = MultiPolygon; + +impl>> From + for MultiPolygon +{ fn from(x: IP) -> Self { Self(vec![x.into()]) } } -impl>> From> for MultiPolygon { +impl>> From> + for MultiPolygon +{ fn from(x: Vec) -> Self { Self(x.into_iter().map(|p| p.into()).collect()) } } -impl>> FromIterator for MultiPolygon { +impl>> FromIterator + for MultiPolygon +{ fn from_iter>(iter: I) -> Self { Self(iter.into_iter().map(|p| p.into()).collect()) } } -impl IntoIterator for MultiPolygon { - type Item = Polygon; - type IntoIter = ::std::vec::IntoIter>; +impl IntoIterator for MultiPolygon { + type Item = Polygon; + type IntoIter = ::std::vec::IntoIter>; fn into_iter(self) -> Self::IntoIter { self.0.into_iter() } } -impl<'a, T: CoordNum> IntoIterator for &'a MultiPolygon { - type Item = &'a Polygon; - type IntoIter = ::std::slice::Iter<'a, Polygon>; +impl<'a, T: CoordNum, Z: ZCoord, M: Measure> IntoIterator for &'a MultiPolygon { + type Item = &'a Polygon; + type IntoIter = ::std::slice::Iter<'a, Polygon>; fn into_iter(self) -> Self::IntoIter { (&self.0).iter() } } -impl<'a, T: CoordNum> IntoIterator for &'a mut MultiPolygon { - type Item = &'a mut Polygon; - type IntoIter = ::std::slice::IterMut<'a, Polygon>; +impl<'a, T: CoordNum, Z: ZCoord, M: Measure> IntoIterator for &'a mut MultiPolygon { + type Item = &'a mut Polygon; + type IntoIter = ::std::slice::IterMut<'a, Polygon>; fn into_iter(self) -> Self::IntoIter { (&mut self.0).iter_mut() } } -impl MultiPolygon { +impl MultiPolygon { /// Instantiate Self from the raw content value - pub fn new(value: Vec>) -> Self { + pub fn new(value: Vec>) -> Self { Self(value) } - pub fn iter(&self) -> impl Iterator> { + pub fn iter(&self) -> impl Iterator> { self.0.iter() } - pub fn iter_mut(&mut self) -> impl Iterator> { + pub fn iter_mut(&mut self) -> impl Iterator> { self.0.iter_mut() } } diff --git a/geo-types/src/geometry/point.rs b/geo-types/src/geometry/point.rs index e0f0cf061f..384b54d6d9 100644 --- a/geo-types/src/geometry/point.rs +++ b/geo-types/src/geometry/point.rs @@ -1,16 +1,14 @@ -use crate::{point, CoordFloat, CoordNum, Coordinate}; +use crate::*; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -/// A single point in 2D space. +/// A generic single point in 3D space with a measurement value. /// -/// Points can be created using the [`Point::new`] constructor, -/// the [`point!`] macro, or from a `Coordinate`, two-element -/// tuples, or arrays – see the `From` impl section for a -/// complete list. +/// Points can be created using the the [point!] macro, +/// or from a [Coordinate] or other types – see the `From` implementations below. /// /// # Semantics /// @@ -21,18 +19,56 @@ use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssi /// # Examples /// /// ``` -/// use geo_types::{coord, Point}; -/// let p1: Point = (0., 1.).into(); -/// let c = coord! { x: 10., y: 20. }; -/// let p2: Point = c.into(); +/// use geo_types::{coord, Coordinate, Point}; +/// let c: Coordinate = coord! { x: 10., y: 20., z: 30., m: 40 }; +/// let p = Point::from(c); /// ``` #[derive(Eq, PartialEq, Clone, Copy, Debug, Hash, Default)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct Point(pub Coordinate); +pub struct Point(pub Coordinate); -impl From> for Point { - fn from(x: Coordinate) -> Self { - Point(x) +/// A single point in 2D space + Measure value. +/// +/// Points can be created using the the [point!] macro, +/// or from a [CoordinateM] or other types – see the `From` implementations below. +/// +/// # Examples +/// +/// ``` +/// use geo_types::{coord, PointM}; +/// let p = PointM::from(coord! { x: 10., y: 20., m: 40. }); +/// ``` +pub type PointM = Point; + +/// A single point in 3D space. +/// +/// Points can be created using the the [point!] macro, +/// or from a [Coordinate3D] or other types – see the `From` implementations below. +/// +/// # Examples +/// +/// ``` +/// use geo_types::{coord, Point3D}; +/// let p = Point3D::from(coord! { x: 10., y: 20., z: 30. }); +/// ``` +pub type Point3D = Point; + +/// A single point in 3D space with a measurement value. +/// +/// Points can be created using the the [point!] macro, +/// or from a [Coordinate3DM] or other types – see the `From` implementations below. +/// +/// # Examples +/// +/// ``` +/// use geo_types::{coord, Point3DM}; +/// let p = Point3DM::from(coord! { x: 10., y: 20., z: 30., m: 40. }); +/// ``` +pub type Point3DM = Point; + +impl From> for Point { + fn from(x: Coordinate) -> Self { + Self(x) } } @@ -76,7 +112,9 @@ impl Point { pub fn new(x: T, y: T) -> Self { point! { x: x, y: y } } +} +impl Point { /// Returns the x/horizontal component of the point. /// /// # Examples @@ -157,71 +195,69 @@ impl Point { pub fn x_y(self) -> (T, T) { (self.0.x, self.0.y) } - /// Returns the longitude/horizontal component of the point. + + /// Returns the z component of the point. /// /// # Examples /// /// ``` - /// use geo_types::Point; + /// use geo_types::point; /// - /// let p = Point::new(1.234, 2.345); + /// let p = point! { x: 1.234, y: 2.345, z: 3.456 }; /// - /// assert_eq!(p.x(), 1.234); + /// assert_eq!(p.z(), 3.456); /// ``` - #[deprecated = "use `Point::x` instead, it's less ambiguous"] - pub fn lng(self) -> T { - self.x() + pub fn z(self) -> Z { + self.0.z } - /// Sets the longitude/horizontal component of the point. + /// Sets the z component of the point. /// /// # Examples /// /// ``` - /// use geo_types::Point; + /// use geo_types::point; /// - /// let mut p = Point::new(1.234, 2.345); - /// #[allow(deprecated)] - /// p.set_lng(9.876); + /// let mut p = point! { x: 1.234, y: 2.345, z: 3.456 }; /// - /// assert_eq!(p.x(), 9.876); + /// p.set_z(9.876); + /// assert_eq!(p.z(), 9.876); /// ``` - #[deprecated = "use `Point::set_x` instead, it's less ambiguous"] - pub fn set_lng(&mut self, lng: T) -> &mut Self { - self.set_x(lng) + pub fn set_z(&mut self, z: Z) -> &mut Self { + self.0.z = z; + self } - /// Returns the latitude/vertical component of the point. + /// Returns the m/measure component of the point. /// /// # Examples /// /// ``` - /// use geo_types::Point; + /// use geo_types::point; /// - /// let p = Point::new(1.234, 2.345); + /// let p = point! { x: 1.234, y: 2.345, z: 3.456, m: 100 }; /// - /// assert_eq!(p.y(), 2.345); + /// assert_eq!(p.m(), 100); /// ``` - #[deprecated = "use `Point::y` instead, it's less ambiguous"] - pub fn lat(self) -> T { - self.y() + pub fn m(self) -> M { + self.0.m } - /// Sets the latitude/vertical component of the point. + + /// Sets the m/measure component of the point. /// /// # Examples /// /// ``` - /// use geo_types::Point; + /// use geo_types::point; /// - /// let mut p = Point::new(1.234, 2.345); - /// #[allow(deprecated)] - /// p.set_lat(9.876); + /// let mut p = point! { x: 1.234, y: 2.345, z: 3.456, m: 100 }; /// - /// assert_eq!(p.y(), 9.876); + /// p.set_m(100); + /// assert_eq!(p.m(), 100); /// ``` - #[deprecated = "use `Point::set_y` instead, it's less ambiguous"] - pub fn set_lat(&mut self, lat: T) -> &mut Self { - self.set_y(lat) + pub fn set_m(&mut self, m: M) -> &mut Self { + self.0.m = m; + self } } @@ -617,9 +653,36 @@ where #[cfg(test)] mod test { use super::*; - + #[cfg(any(feature = "approx", test))] use approx::AbsDiffEq; + #[test] + fn test_point() { + let p: Point<_> = point! { x: 1.0, y: 2.0 }; + assert_relative_eq!(p.x(), 1.0); + assert_relative_eq!(p.y(), 2.0); + assert_eq!(p.z(), NoValue); + assert_eq!(p.m(), NoValue); + + let p: Point3D<_> = point! { x: 1.0, y: 2.0, z: 3.0 }; + assert_relative_eq!(p.x(), 1.0); + assert_relative_eq!(p.y(), 2.0); + assert_relative_eq!(p.z(), 3.0); + assert_eq!(p.m(), NoValue); + + let p: PointM<_> = point! { x: 1.0, y: 2.0, m: 4.0 }; + assert_relative_eq!(p.x(), 1.0); + assert_relative_eq!(p.y(), 2.0); + assert_eq!(p.z(), NoValue); + assert_eq!(p.m(), 4.0); + + let p: Point3DM<_> = point! { x: 1_i32, y: 2_i32, z: 3_i32, m: 4_i32 }; + assert_eq!(p.x(), 1); + assert_eq!(p.y(), 2); + assert_eq!(p.z(), 3); + assert_eq!(p.m(), 4); + } + #[test] fn test_abs_diff_eq() { let delta = 1e-6; diff --git a/geo-types/src/geometry/polygon.rs b/geo-types/src/geometry/polygon.rs index 1312d712bf..0b94f052fa 100644 --- a/geo-types/src/geometry/polygon.rs +++ b/geo-types/src/geometry/polygon.rs @@ -1,10 +1,8 @@ -use crate::{CoordFloat, CoordNum, LineString, Point, Rect, Triangle}; -use num_traits::{Float, Signed}; - +use crate::{CoordNum, LineString, Measure, NoValue, Rect, Triangle, ZCoord}; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; -/// A bounded two-dimensional area. +/// A generic bounded area with 3D space + Measure value support. /// /// A `Polygon`’s outer boundary (_exterior ring_) is represented by a /// [`LineString`]. It may contain zero or more holes (_interior rings_), also @@ -62,16 +60,29 @@ use approx::{AbsDiffEq, RelativeEq}; /// If a `LineString`’s first and last `Coordinate` have different values, a /// new `Coordinate` will be appended to the `LineString` with a value equal to /// the first `Coordinate`. -/// -/// [`LineString`]: line_string/struct.LineString.html #[derive(Eq, PartialEq, Clone, Debug, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct Polygon { - exterior: LineString, - interiors: Vec>, +pub struct Polygon { + exterior: LineString, + interiors: Vec>, } -impl Polygon { +/// A bounded area with a measurement value in 2D space. +/// +/// See [Polygon] +pub type PolygonM = Polygon; + +/// A bounded area in 3D space. +/// +/// See [Polygon] +pub type Polygon3D = Polygon; + +/// A bounded area with a measurement value in 3D space. +/// +/// See [Polygon] +pub type Polygon3DM = Polygon; + +impl Polygon { /// Create a new `Polygon` with the provided exterior `LineString` ring and /// interior `LineString` rings. /// @@ -122,7 +133,7 @@ impl Polygon { /// &LineString::from(vec![(0., 0.), (1., 1.), (1., 0.), (0., 0.),]) /// ); /// ``` - pub fn new(mut exterior: LineString, mut interiors: Vec>) -> Self { + pub fn new(mut exterior: LineString, mut interiors: Vec>) -> Self { exterior.close(); for interior in &mut interiors { interior.close(); @@ -168,7 +179,8 @@ impl Polygon { /// ])] /// ); /// ``` - pub fn into_inner(self) -> (LineString, Vec>) { + #[allow(clippy::type_complexity)] + pub fn into_inner(self) -> (LineString, Vec>) { (self.exterior, self.interiors) } @@ -185,7 +197,7 @@ impl Polygon { /// /// assert_eq!(polygon.exterior(), &exterior); /// ``` - pub fn exterior(&self) -> &LineString { + pub fn exterior(&self) -> &LineString { &self.exterior } @@ -238,7 +250,7 @@ impl Polygon { /// [will be closed]: #linestring-closing-operation pub fn exterior_mut(&mut self, f: F) where - F: FnOnce(&mut LineString), + F: FnOnce(&mut LineString), { f(&mut self.exterior); self.exterior.close(); @@ -265,7 +277,7 @@ impl Polygon { /// /// assert_eq!(interiors, polygon.interiors()); /// ``` - pub fn interiors(&self) -> &[LineString] { + pub fn interiors(&self) -> &[LineString] { &self.interiors } @@ -340,7 +352,7 @@ impl Polygon { /// [will be closed]: #linestring-closing-operation pub fn interiors_mut(&mut self, f: F) where - F: FnOnce(&mut [LineString]), + F: FnOnce(&mut [LineString]), { f(&mut self.interiors); for interior in &mut self.interiors { @@ -378,67 +390,11 @@ impl Polygon { /// ``` /// /// [will be closed]: #linestring-closing-operation - pub fn interiors_push(&mut self, new_interior: impl Into>) { + pub fn interiors_push(&mut self, new_interior: impl Into>) { let mut new_interior = new_interior.into(); new_interior.close(); self.interiors.push(new_interior); } - - /// Wrap-around previous-vertex - fn previous_vertex(&self, current_vertex: usize) -> usize - where - T: Float, - { - (current_vertex + (self.exterior.0.len() - 1) - 1) % (self.exterior.0.len() - 1) - } -} - -// used to check the sign of a vec of floats -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -enum ListSign { - Empty, - Positive, - Negative, - Mixed, -} - -impl Polygon { - /// Determine whether a Polygon is convex - // For each consecutive pair of edges of the polygon (each triplet of points), - // compute the z-component of the cross product of the vectors defined by the - // edges pointing towards the points in increasing order. - // Take the cross product of these vectors - // The polygon is convex if the z-components of the cross products are either - // all positive or all negative. Otherwise, the polygon is non-convex. - // see: http://stackoverflow.com/a/1881201/416626 - #[deprecated( - since = "0.6.1", - note = "Please use `geo::is_convex` on `poly.exterior()` instead" - )] - pub fn is_convex(&self) -> bool { - let convex = self - .exterior - .0 - .iter() - .enumerate() - .map(|(idx, _)| { - let prev_1 = self.previous_vertex(idx); - let prev_2 = self.previous_vertex(prev_1); - Point::from(self.exterior[prev_2]).cross_prod( - Point::from(self.exterior[prev_1]), - Point::from(self.exterior[idx]), - ) - }) - // accumulate and check cross-product result signs in a single pass - // positive implies ccw convexity, negative implies cw convexity - // anything else implies non-convexity - .fold(ListSign::Empty, |acc, n| match (acc, n.is_positive()) { - (ListSign::Empty, true) | (ListSign::Positive, true) => ListSign::Positive, - (ListSign::Empty, false) | (ListSign::Negative, false) => ListSign::Negative, - _ => ListSign::Mixed, - }); - convex != ListSign::Mixed - } } impl From> for Polygon { @@ -457,9 +413,9 @@ impl From> for Polygon { } } -impl From> for Polygon { - fn from(t: Triangle) -> Self { - Polygon::new(vec![t.0, t.1, t.2, t.0].into(), Vec::new()) +impl From> for Polygon { + fn from(t: Triangle) -> Self { + Self::new(vec![t.0, t.1, t.2, t.0].into(), Vec::new()) } } diff --git a/geo-types/src/geometry/rect.rs b/geo-types/src/geometry/rect.rs index 75e4b5a6de..ead5cbaf30 100644 --- a/geo-types/src/geometry/rect.rs +++ b/geo-types/src/geometry/rect.rs @@ -1,8 +1,11 @@ -use crate::{coord, polygon, CoordFloat, CoordNum, Coordinate, Line, Polygon}; +use crate::{coord, polygon, CoordNum, Coordinate, Line, Measure, NoValue, Polygon, ZCoord}; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; +use num_traits::{NumOps, One}; +/// A generic bounded rectangle with 3D space + Measure value support. +/// /// An _axis-aligned_ bounded 2D rectangle whose area is /// defined by minimum and maximum `Coordinate`s. /// @@ -39,12 +42,27 @@ use approx::{AbsDiffEq, RelativeEq}; /// ``` #[derive(Eq, PartialEq, Clone, Copy, Debug, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct Rect { - min: Coordinate, - max: Coordinate, +pub struct Rect { + min: Coordinate, + max: Coordinate, } -impl Rect { +/// A bounded rectangle with a measurement value in 2D space. +/// +/// See [Rect] +pub type RectM = Rect; + +/// A bounded rectangle in 3D space. +/// +/// See [Rect] +pub type Rect3D = Rect; + +/// A bounded rectangle with a measurement value in 3D space. +/// +/// See [Rect] +pub type Rect3DM = Rect; + +impl Rect { /// Creates a new rectangle from two corner coordinates. /// /// # Examples @@ -61,7 +79,7 @@ impl Rect { /// ``` pub fn new(c1: C, c2: C) -> Self where - C: Into>, + C: Into>, { let c1 = c1.into(); let c2 = c2.into(); @@ -75,24 +93,22 @@ impl Rect { } else { (c2.y, c1.y) }; + let (min_z, max_z) = if c1.z < c2.z { + (c1.z, c2.z) + } else { + (c2.z, c1.z) + }; + let (min_m, max_m) = if c1.m < c2.m { + (c1.m, c2.m) + } else { + (c2.m, c1.m) + }; Self { - min: coord! { x: min_x, y: min_y }, - max: coord! { x: max_x, y: max_y }, + min: coord! { x: min_x, y: min_y, z: min_z, m: min_m }, + max: coord! { x: max_x, y: max_y, z: max_z, m: max_m }, } } - #[deprecated( - since = "0.6.2", - note = "Use `Rect::new` instead, since `Rect::try_new` will never Error" - )] - #[allow(deprecated)] - pub fn try_new(c1: C, c2: C) -> Result, InvalidRectCoordinatesError> - where - C: Into>, - { - Ok(Rect::new(c1, c2)) - } - /// Returns the minimum `Coordinate` of the `Rect`. /// /// # Examples @@ -107,7 +123,7 @@ impl Rect { /// /// assert_eq!(rect.min(), coord! { x: 5., y: 5. }); /// ``` - pub fn min(self) -> Coordinate { + pub fn min(self) -> Coordinate { self.min } @@ -116,10 +132,7 @@ impl Rect { /// # Panics /// /// Panics if `min`’s x/y is greater than the maximum coordinate’s x/y. - pub fn set_min(&mut self, min: C) - where - C: Into>, - { + pub fn set_min>>(&mut self, min: C) { self.min = min.into(); self.assert_valid_bounds(); } @@ -138,7 +151,7 @@ impl Rect { /// /// assert_eq!(rect.max(), coord! { x: 15., y: 15. }); /// ``` - pub fn max(self) -> Coordinate { + pub fn max(self) -> Coordinate { self.max } @@ -147,10 +160,7 @@ impl Rect { /// # Panics /// /// Panics if `max`’s x/y is less than the minimum coordinate’s x/y. - pub fn set_max(&mut self, max: C) - where - C: Into>, - { + pub fn set_max>>(&mut self, max: C) { self.max = max.into(); self.assert_valid_bounds(); } @@ -352,26 +362,50 @@ impl Rect { } } -impl Rect { +impl Rect +where + T: CoordNum, + Z: ZCoord + One + NumOps, + M: Measure + One + NumOps, +{ /// Returns the center `Coordinate` of the `Rect`. /// /// # Examples /// /// ```rust - /// use geo_types::{coord, Rect}; + /// use geo_types::{coord, Rect, Rect3D, RectM, Rect3DM}; /// /// let rect = Rect::new( /// coord! { x: 5., y: 5. }, /// coord! { x: 15., y: 15. }, /// ); - /// /// assert_eq!(rect.center(), coord! { x: 10., y: 10. }); + /// + /// let rect = Rect3D::new( + /// coord! { x: 1., y: 2., z: 3. }, + /// coord! { x: 3., y: 4., z: 5. }, + /// ); + /// assert_eq!(rect.center(), coord! { x: 2., y: 3., z: 4. }); + /// + /// let rect = RectM::new( + /// coord! { x: 1., y: 2., m: 4. }, + /// coord! { x: 3., y: 4., m: 6. }, + /// ); + /// assert_eq!(rect.center(), coord! { x: 2., y: 3., m: 5. }); + /// + /// let rect = Rect3DM::new( + /// coord! { x: 1., y: 2., z: 3., m: 4. }, + /// coord! { x: 3., y: 4., z: 5., m: 6. }, + /// ); + /// assert_eq!(rect.center(), coord! { x: 2., y: 3., z: 4., m: 5. }); /// ``` - pub fn center(self) -> Coordinate { + pub fn center(self) -> Coordinate { let two = T::one() + T::one(); coord! { x: (self.max.x + self.min.x) / two, y: (self.max.y + self.min.y) / two, + z: (self.max.z + self.min.z) / (Z::one() + Z::one()), + m: (self.max.m + self.min.m) / (M::one() + M::one()), } } } @@ -460,27 +494,9 @@ where } } -#[deprecated( - since = "0.6.2", - note = "Use `Rect::new` instead, since `Rect::try_new` will never Error" -)] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub struct InvalidRectCoordinatesError; - -#[allow(deprecated)] -impl std::error::Error for InvalidRectCoordinatesError {} - -#[allow(deprecated)] -impl std::fmt::Display for InvalidRectCoordinatesError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", RECT_INVALID_BOUNDS_ERROR) - } -} - #[cfg(test)] mod test { - use super::*; - use crate::coord; + use crate::{coord, Coordinate, Rect}; #[test] fn rect() { diff --git a/geo-types/src/geometry/triangle.rs b/geo-types/src/geometry/triangle.rs index b0ebbff365..3da5a9e7ef 100644 --- a/geo-types/src/geometry/triangle.rs +++ b/geo-types/src/geometry/triangle.rs @@ -1,27 +1,50 @@ -use crate::{polygon, CoordNum, Coordinate, Line, Polygon}; - +use crate::{polygon, CoordNum, Coordinate, Line, Measure, NoValue, Polygon, ZCoord}; +#[cfg(doc)] +use crate::{Polygon3D, Polygon3DM, PolygonM}; #[cfg(any(feature = "approx", test))] use approx::{AbsDiffEq, RelativeEq}; -/// A bounded 2D area whose three vertices are defined by +/// A generic area with 3D+M support whose three vertices are defined by /// `Coordinate`s. The semantics and validity are that of -/// the equivalent [`Polygon`]; in addition, the three +/// the equivalent [Polygon]; in addition, the three /// vertices must not be collinear and they must be distinct. #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct Triangle(pub Coordinate, pub Coordinate, pub Coordinate); +pub struct Triangle( + pub Coordinate, + pub Coordinate, + pub Coordinate, +); + +/// A bounded 2D area whose three vertices are defined by +/// `Coordinate`s. The semantics and validity are that of +/// the equivalent [PolygonM]; in addition, the three +/// vertices must not be collinear and they must be distinct. +pub type TriangleM = Triangle; -impl Triangle { +/// A bounded 2D area whose three vertices are defined by +/// `Coordinate`s. The semantics and validity are that of +/// the equivalent [Polygon3D]; in addition, the three +/// vertices must not be collinear and they must be distinct. +pub type Triangle3D = Triangle; + +/// A bounded 2D area whose three vertices are defined by +/// `Coordinate`s. The semantics and validity are that of +/// the equivalent [Polygon3DM]; in addition, the three +/// vertices must not be collinear and they must be distinct. +pub type Triangle3DM = Triangle; + +impl Triangle { /// Instantiate Self from the raw content value - pub fn new(v1: Coordinate, v2: Coordinate, v3: Coordinate) -> Self { + pub fn new(v1: Coordinate, v2: Coordinate, v3: Coordinate) -> Self { Self(v1, v2, v3) } - pub fn to_array(&self) -> [Coordinate; 3] { + pub fn to_array(&self) -> [Coordinate; 3] { [self.0, self.1, self.2] } - pub fn to_lines(&self) -> [Line; 3] { + pub fn to_lines(&self) -> [Line; 3] { [ Line::new(self.0, self.1), Line::new(self.1, self.2), @@ -52,12 +75,14 @@ impl Triangle { /// ], /// ); /// ``` - pub fn to_polygon(self) -> Polygon { + pub fn to_polygon(self) -> Polygon { polygon![self.0, self.1, self.2, self.0] } } -impl> + Copy, T: CoordNum> From<[IC; 3]> for Triangle { +impl> + Copy, T: CoordNum, Z: ZCoord, M: Measure> From<[IC; 3]> + for Triangle +{ fn from(array: [IC; 3]) -> Self { Self(array[0].into(), array[1].into(), array[2].into()) } diff --git a/geo-types/src/lib.rs b/geo-types/src/lib.rs index 642e6d482f..398b4f7cdf 100644 --- a/geo-types/src/lib.rs +++ b/geo-types/src/lib.rs @@ -76,7 +76,7 @@ //! [rstar]: https://github.com/Stoeoef/rstar //! [Serde]: https://serde.rs/ extern crate num_traits; -use num_traits::{Float, Num, NumCast}; +use num_traits::{Float, Num, NumCast, NumOps, One, Zero}; use std::fmt::Debug; #[cfg(feature = "serde")] @@ -110,6 +110,72 @@ impl CoordNum for T {} pub trait CoordFloat: CoordNum + Float {} impl CoordFloat for T {} +/// The type of the optional z value of a point/coordinate. +/// +/// Floats (`f32` and `f64`) and Integers (`u8`, `i32` etc.) implement this. +/// Also, an empty [`NoValue`] generic type can be used instead of the real value, +/// allowing geo-types to avoid having Z value if it is not needed. +/// +/// Unlike [CoordNum], this trait does not require [NumCast] and [Num] traits. +pub trait ZCoord: NumOps + One + Zero + Copy + PartialEq + PartialOrd + Debug {} +impl ZCoord for Z {} + +/// The type of the optional measurement (m) value of a point/coordinate. +/// +/// Floats (`f32` and `f64`) and Integers (`u8`, `i32` etc.) implement this. +/// Also, an empty [`NoValue`] generic type can be used instead of the real value, +/// allowing geo-types to avoid having M value if it is not needed. +/// +/// Unlike [CoordNum], this trait does not require [NumCast] and [Num] traits. +pub trait Measure: NumOps + One + Zero + Copy + PartialEq + PartialOrd + Debug {} +impl Measure for M {} + +mod novalue; +pub use crate::novalue::NoValue; + +mod coordinate; +pub use crate::coordinate::{Coordinate, Coordinate3D, Coordinate3DM, CoordinateM}; + +mod point; +pub use crate::point::{Point, Point3D, Point3DM, PointM}; + +mod multi_point; +pub use crate::multi_point::{MultiPoint, MultiPoint3D, MultiPoint3DM, MultiPointM}; + +mod line; +pub use crate::line::{Line, Line3D, Line3DM, LineM}; + +mod line_string; +pub use crate::line_string::{ + LineString, LineString3D, LineString3DM, LineStringM, PointsIter, PointsIter3D, PointsIter3DM, + PointsIterM, +}; + +mod multi_line_string; +pub use crate::multi_line_string::{ + MultiLineString, MultiLineString3D, MultiLineString3DM, MultiLineStringM, +}; + +mod polygon; +pub use crate::polygon::{Polygon, Polygon3D, Polygon3DM, PolygonM}; + +mod multi_polygon; +pub use crate::multi_polygon::{MultiPolygon, MultiPolygon3D, MultiPolygon3DM, MultiPolygonM}; + +mod geometry; +pub use crate::geometry::{Geometry, Geometry3D, Geometry3DM, GeometryM}; + +mod geometry_collection; +pub use crate::geometry_collection::{ + GeometryCollection, GeometryCollection3D, GeometryCollection3DM, GeometryCollectionM, +}; + +mod triangle; +pub use crate::triangle::{Triangle, Triangle3D, Triangle3DM, TriangleM}; + +mod rect; +pub use crate::rect::{Rect, Rect3D, Rect3DM, RectM}; + pub mod geometry; pub use geometry::*; diff --git a/geo-types/src/macros.rs b/geo-types/src/macros.rs index 7251cb3abb..7994471228 100644 --- a/geo-types/src/macros.rs +++ b/geo-types/src/macros.rs @@ -56,7 +56,21 @@ macro_rules! point { #[macro_export] macro_rules! coord { (x: $x:expr, y: $y:expr $(,)* ) => { - $crate::Coordinate { x: $x, y: $y } + $crate::Coordinate::new__( + $x, + $y, + $crate::NoValue::default(), + $crate::NoValue::default(), + ) + }; + (x: $x:expr, y: $y:expr, z: $z:expr $(,)* ) => { + $crate::Coordinate::new__($x, $y, $z, $crate::NoValue::default()) + }; + (x: $x:expr, y: $y:expr, m: $m:expr $(,)* ) => { + $crate::Coordinate::new__($x, $y, $crate::NoValue::default(), $m) + }; + (x: $x:expr, y: $y:expr, z: $z:expr, m: $m:expr $(,)* ) => { + $crate::Coordinate::new__($x, $y, $z, $m) }; } diff --git a/geo-types/src/novalue.rs b/geo-types/src/novalue.rs new file mode 100644 index 0000000000..c98ace2846 --- /dev/null +++ b/geo-types/src/novalue.rs @@ -0,0 +1,109 @@ +#[cfg(any(feature = "approx", test))] +use approx::AbsDiffEq; +use num_traits::{One, Zero}; +use std::fmt::Debug; +use std::ops::{Add, Div, Mul, Neg, Rem, Sub}; + +/// An empty placeholder type that can be used instead of the real +/// numerical value types for 3D (z) and measurement (m) values. +#[derive(Eq, PartialEq, PartialOrd, Clone, Copy, Debug, Hash, Default)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +pub struct NoValue; + +impl Add for NoValue { + type Output = Self; + + #[inline] + fn add(self, _: Self) -> Self::Output { + NoValue::default() + } +} + +impl Div for NoValue { + type Output = Self; + + #[inline] + fn div(self, _: T) -> Self::Output { + NoValue::default() + } +} + +impl Mul for NoValue { + type Output = Self; + + #[inline] + fn mul(self, _: T) -> Self::Output { + NoValue::default() + } +} + +impl Neg for NoValue { + type Output = Self; + + #[inline] + fn neg(self) -> Self::Output { + NoValue::default() + } +} + +impl Rem for NoValue { + type Output = Self; + + #[inline] + fn rem(self, _: T) -> Self::Output { + NoValue::default() + } +} + +impl Sub for NoValue { + type Output = Self; + + #[inline] + fn sub(self, _: Self) -> Self::Output { + NoValue::default() + } +} + +/// This hack allows mathematical operations that result in noop due to above ops +impl Zero for NoValue { + #[inline] + fn zero() -> Self { + NoValue::default() + } + + #[inline] + fn is_zero(&self) -> bool { + true + } +} + +/// This hack allows mathematical operations that result in noop due to above ops +impl One for NoValue { + #[inline] + fn one() -> Self { + NoValue::default() + } +} + +#[cfg(any(feature = "approx", test))] +impl AbsDiffEq for NoValue { + type Epsilon = Self; + + #[inline] + fn default_epsilon() -> Self::Epsilon { + NoValue::default() + } + + #[inline] + fn abs_diff_eq(&self, _: &Self, _: Self::Epsilon) -> bool { + true + } +} + +#[cfg(feature = "arbitrary")] +impl<'a> arbitrary::Arbitrary<'a> for NoValue { + #[inline] + fn arbitrary(_: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { + Ok(NoValue::default()) + } +} diff --git a/geo-types/src/private_utils.rs b/geo-types/src/private_utils.rs index a70e68e1c7..17b0ee57c8 100644 --- a/geo-types/src/private_utils.rs +++ b/geo-types/src/private_utils.rs @@ -3,45 +3,54 @@ // hidden module is public so the geo crate can reuse these algorithms to // prevent duplication. These functions are _not_ meant for public consumption. -use crate::{CoordFloat, CoordNum, Coordinate, Line, LineString, Point, Rect}; +use crate::{ + CoordFloat, CoordNum, Coordinate, Line, LineString, Measure, NoValue, Point, Rect, ZCoord, +}; -pub fn line_string_bounding_rect(line_string: &LineString) -> Option> -where - T: CoordNum, -{ +pub fn line_string_bounding_rect( + line_string: &LineString, +) -> Option> { get_bounding_rect(line_string.coords().cloned()) } -pub fn line_bounding_rect(line: Line) -> Rect -where - T: CoordNum, -{ +pub fn line_bounding_rect( + line: Line, +) -> Rect { Rect::new(line.start, line.end) } -pub fn get_bounding_rect(collection: I) -> Option> +pub fn get_bounding_rect(collection: I) -> Option> where T: CoordNum, - I: IntoIterator>, + Z: ZCoord, + M: Measure, + I: IntoIterator>, { let mut iter = collection.into_iter(); if let Some(pnt) = iter.next() { let mut xrange = (pnt.x, pnt.x); let mut yrange = (pnt.y, pnt.y); + let mut zrange = (pnt.z, pnt.z); + let mut mrange = (pnt.m, pnt.m); for pnt in iter { - let (px, py) = pnt.x_y(); - xrange = get_min_max(px, xrange.0, xrange.1); - yrange = get_min_max(py, yrange.0, yrange.1); + xrange = get_min_max(pnt.x, xrange.0, xrange.1); + yrange = get_min_max(pnt.y, yrange.0, yrange.1); + zrange = get_min_max(pnt.z, zrange.0, zrange.1); + mrange = get_min_max(pnt.m, mrange.0, mrange.1); } return Some(Rect::new( coord! { x: xrange.0, y: yrange.0, + z: zrange.0, + m: mrange.0, }, coord! { x: xrange.1, y: yrange.1, + z: zrange.1, + m: mrange.1, }, )); } @@ -83,10 +92,7 @@ where s.abs() * dx.hypot(dy) } -pub fn line_euclidean_length(line: Line) -> T -where - T: CoordFloat, -{ +pub fn line_euclidean_length(line: Line) -> T { line.dx().hypot(line.dy()) }