geo_types/geometry/
mod.rs

1pub(crate) mod coord;
2pub(crate) mod geometry_collection;
3pub(crate) mod line;
4pub(crate) mod line_string;
5pub(crate) mod multi_line_string;
6pub(crate) mod multi_point;
7pub(crate) mod multi_polygon;
8pub(crate) mod point;
9pub(crate) mod polygon;
10pub(crate) mod rect;
11pub(crate) mod triangle;
12
13// re-export all the geometry variants:
14#[allow(deprecated)]
15pub use coord::{Coord, Coordinate};
16pub use geometry_collection::GeometryCollection;
17pub use line::Line;
18pub use line_string::LineString;
19pub use multi_line_string::MultiLineString;
20pub use multi_point::MultiPoint;
21pub use multi_polygon::MultiPolygon;
22pub use point::Point;
23pub use polygon::Polygon;
24pub use rect::Rect;
25pub use triangle::Triangle;
26
27use crate::{CoordNum, Error};
28
29use core::any::type_name;
30use core::convert::TryFrom;
31
32/// An enum representing any possible geometry type.
33///
34/// All geometry variants ([`Point`], [`LineString`], etc.) can be converted to a `Geometry` using
35/// [`Into::into`]. Conversely, [`TryFrom::try_from`] can be used to convert a [`Geometry`]
36/// _back_ to one of it's specific enum members.
37///
38/// # Example
39///
40/// ```
41/// use std::convert::TryFrom;
42/// use geo_types::{Point, point, Geometry, GeometryCollection};
43/// let p = point!(x: 1.0, y: 1.0);
44/// let pe: Geometry = p.into();
45/// let pn = Point::try_from(pe).unwrap();
46/// ```
47///
48#[derive(Eq, PartialEq, Clone, Hash)]
49#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
50pub enum Geometry<T: CoordNum = f64> {
51    Point(Point<T>),
52    Line(Line<T>),
53    LineString(LineString<T>),
54    Polygon(Polygon<T>),
55    MultiPoint(MultiPoint<T>),
56    MultiLineString(MultiLineString<T>),
57    MultiPolygon(MultiPolygon<T>),
58    GeometryCollection(GeometryCollection<T>),
59    Rect(Rect<T>),
60    Triangle(Triangle<T>),
61}
62
63impl<T: CoordNum> From<Point<T>> for Geometry<T> {
64    fn from(x: Point<T>) -> Self {
65        Self::Point(x)
66    }
67}
68impl<T: CoordNum> From<Line<T>> for Geometry<T> {
69    fn from(x: Line<T>) -> Self {
70        Self::Line(x)
71    }
72}
73impl<T: CoordNum> From<LineString<T>> for Geometry<T> {
74    fn from(x: LineString<T>) -> Self {
75        Self::LineString(x)
76    }
77}
78impl<T: CoordNum> From<Polygon<T>> for Geometry<T> {
79    fn from(x: Polygon<T>) -> Self {
80        Self::Polygon(x)
81    }
82}
83impl<T: CoordNum> From<MultiPoint<T>> for Geometry<T> {
84    fn from(x: MultiPoint<T>) -> Self {
85        Self::MultiPoint(x)
86    }
87}
88impl<T: CoordNum> From<MultiLineString<T>> for Geometry<T> {
89    fn from(x: MultiLineString<T>) -> Self {
90        Self::MultiLineString(x)
91    }
92}
93impl<T: CoordNum> From<MultiPolygon<T>> for Geometry<T> {
94    fn from(x: MultiPolygon<T>) -> Self {
95        Self::MultiPolygon(x)
96    }
97}
98
99// Disabled until we remove the deprecated GeometryCollection::from(single_geom) impl.
100// impl<T: CoordNum> From<GeometryCollection<T>> for Geometry<T> {
101//     fn from(x: GeometryCollection<T>) -> Self {
102//         Self::GeometryCollection(x)
103//     }
104// }
105
106impl<T: CoordNum> From<Rect<T>> for Geometry<T> {
107    fn from(x: Rect<T>) -> Self {
108        Self::Rect(x)
109    }
110}
111
112impl<T: CoordNum> From<Triangle<T>> for Geometry<T> {
113    fn from(x: Triangle<T>) -> Self {
114        Self::Triangle(x)
115    }
116}
117
118impl<T: CoordNum> Geometry<T> {
119    /// If this Geometry is a Point, then return that, else None.
120    ///
121    /// # Examples
122    ///
123    /// ```
124    /// use geo_types::*;
125    /// use std::convert::TryInto;
126    ///
127    /// let g = Geometry::Point(Point::new(0., 0.));
128    /// let p2: Point<f32> = g.try_into().unwrap();
129    /// assert_eq!(p2, Point::new(0., 0.,));
130    /// ```
131    #[deprecated(
132        note = "Will be removed in an upcoming version. Switch to std::convert::TryInto<Point>"
133    )]
134    pub fn into_point(self) -> Option<Point<T>> {
135        if let Geometry::Point(x) = self {
136            Some(x)
137        } else {
138            None
139        }
140    }
141
142    /// If this Geometry is a LineString, then return that LineString, else None.
143    #[deprecated(
144        note = "Will be removed in an upcoming version. Switch to std::convert::TryInto<LineString>"
145    )]
146    pub fn into_line_string(self) -> Option<LineString<T>> {
147        if let Geometry::LineString(x) = self {
148            Some(x)
149        } else {
150            None
151        }
152    }
153
154    /// If this Geometry is a Line, then return that Line, else None.
155    #[deprecated(
156        note = "Will be removed in an upcoming version. Switch to std::convert::TryInto<Line>"
157    )]
158    pub fn into_line(self) -> Option<Line<T>> {
159        if let Geometry::Line(x) = self {
160            Some(x)
161        } else {
162            None
163        }
164    }
165
166    /// If this Geometry is a Polygon, then return that, else None.
167    #[deprecated(
168        note = "Will be removed in an upcoming version. Switch to std::convert::TryInto<Polygon>"
169    )]
170    pub fn into_polygon(self) -> Option<Polygon<T>> {
171        if let Geometry::Polygon(x) = self {
172            Some(x)
173        } else {
174            None
175        }
176    }
177
178    /// If this Geometry is a MultiPoint, then return that, else None.
179    #[deprecated(
180        note = "Will be removed in an upcoming version. Switch to std::convert::TryInto<MultiPoint>"
181    )]
182    pub fn into_multi_point(self) -> Option<MultiPoint<T>> {
183        if let Geometry::MultiPoint(x) = self {
184            Some(x)
185        } else {
186            None
187        }
188    }
189
190    /// If this Geometry is a MultiLineString, then return that, else None.
191    #[deprecated(
192        note = "Will be removed in an upcoming version. Switch to std::convert::TryInto<MultiLineString>"
193    )]
194    pub fn into_multi_line_string(self) -> Option<MultiLineString<T>> {
195        if let Geometry::MultiLineString(x) = self {
196            Some(x)
197        } else {
198            None
199        }
200    }
201
202    /// If this Geometry is a MultiPolygon, then return that, else None.
203    #[deprecated(
204        note = "Will be removed in an upcoming version. Switch to std::convert::TryInto<MultiPolygon>"
205    )]
206    pub fn into_multi_polygon(self) -> Option<MultiPolygon<T>> {
207        if let Geometry::MultiPolygon(x) = self {
208            Some(x)
209        } else {
210            None
211        }
212    }
213}
214
215macro_rules! try_from_geometry_impl {
216    ($($type: ident),+) => {
217        $(
218        /// Convert a Geometry enum into its inner type.
219        ///
220        /// Fails if the enum case does not match the type you are trying to convert it to.
221        impl <T: CoordNum> TryFrom<Geometry<T>> for $type<T> {
222            type Error = Error;
223
224            fn try_from(geom: Geometry<T>) -> Result<Self, Self::Error> {
225                match geom {
226                    Geometry::$type(g) => Ok(g),
227                    other => Err(Error::MismatchedGeometry {
228                        expected: type_name::<$type<T>>(),
229                        found: inner_type_name(other)
230                    })
231                }
232            }
233        }
234        )+
235    }
236}
237
238try_from_geometry_impl!(
239    Point,
240    Line,
241    LineString,
242    Polygon,
243    MultiPoint,
244    MultiLineString,
245    MultiPolygon,
246    // Disabled until we remove the deprecated GeometryCollection::from(single_geom) impl.
247    // GeometryCollection,
248    Rect,
249    Triangle
250);
251
252fn inner_type_name<T>(geometry: Geometry<T>) -> &'static str
253where
254    T: CoordNum,
255{
256    match geometry {
257        Geometry::Point(_) => type_name::<Point<T>>(),
258        Geometry::Line(_) => type_name::<Line<T>>(),
259        Geometry::LineString(_) => type_name::<LineString<T>>(),
260        Geometry::Polygon(_) => type_name::<Polygon<T>>(),
261        Geometry::MultiPoint(_) => type_name::<MultiPoint<T>>(),
262        Geometry::MultiLineString(_) => type_name::<MultiLineString<T>>(),
263        Geometry::MultiPolygon(_) => type_name::<MultiPolygon<T>>(),
264        Geometry::GeometryCollection(_) => type_name::<GeometryCollection<T>>(),
265        Geometry::Rect(_) => type_name::<Rect<T>>(),
266        Geometry::Triangle(_) => type_name::<Triangle<T>>(),
267    }
268}
269
270#[cfg(any(feature = "approx", test))]
271mod approx_integration {
272    use super::*;
273    use approx::{AbsDiffEq, RelativeEq, UlpsEq};
274
275    impl<T> RelativeEq for Geometry<T>
276    where
277        T: CoordNum + RelativeEq<Epsilon = T>,
278    {
279        #[inline]
280        fn default_max_relative() -> Self::Epsilon {
281            T::default_max_relative()
282        }
283
284        /// Equality assertion within a relative limit.
285        ///
286        /// # Examples
287        ///
288        /// ```
289        /// use geo_types::{Geometry, polygon};
290        ///
291        /// let a: Geometry<f32> = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7., y: 9.), (x: 0., y: 0.)].into();
292        /// let b: Geometry<f32> = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7.01, y: 9.), (x: 0., y: 0.)].into();
293        ///
294        /// approx::assert_relative_eq!(a, b, max_relative=0.1);
295        /// approx::assert_relative_ne!(a, b, max_relative=0.001);
296        /// ```
297        ///
298        fn relative_eq(
299            &self,
300            other: &Self,
301            epsilon: Self::Epsilon,
302            max_relative: Self::Epsilon,
303        ) -> bool {
304            match (self, other) {
305                (Geometry::Point(g1), Geometry::Point(g2)) => {
306                    g1.relative_eq(g2, epsilon, max_relative)
307                }
308                (Geometry::Line(g1), Geometry::Line(g2)) => {
309                    g1.relative_eq(g2, epsilon, max_relative)
310                }
311                (Geometry::LineString(g1), Geometry::LineString(g2)) => {
312                    g1.relative_eq(g2, epsilon, max_relative)
313                }
314                (Geometry::Polygon(g1), Geometry::Polygon(g2)) => {
315                    g1.relative_eq(g2, epsilon, max_relative)
316                }
317                (Geometry::MultiPoint(g1), Geometry::MultiPoint(g2)) => {
318                    g1.relative_eq(g2, epsilon, max_relative)
319                }
320                (Geometry::MultiLineString(g1), Geometry::MultiLineString(g2)) => {
321                    g1.relative_eq(g2, epsilon, max_relative)
322                }
323                (Geometry::MultiPolygon(g1), Geometry::MultiPolygon(g2)) => {
324                    g1.relative_eq(g2, epsilon, max_relative)
325                }
326                (Geometry::GeometryCollection(g1), Geometry::GeometryCollection(g2)) => {
327                    g1.relative_eq(g2, epsilon, max_relative)
328                }
329                (Geometry::Rect(g1), Geometry::Rect(g2)) => {
330                    g1.relative_eq(g2, epsilon, max_relative)
331                }
332                (Geometry::Triangle(g1), Geometry::Triangle(g2)) => {
333                    g1.relative_eq(g2, epsilon, max_relative)
334                }
335                (_, _) => false,
336            }
337        }
338    }
339
340    impl<T> AbsDiffEq for Geometry<T>
341    where
342        T: CoordNum + AbsDiffEq<Epsilon = T>,
343    {
344        type Epsilon = T;
345
346        #[inline]
347        fn default_epsilon() -> Self::Epsilon {
348            T::default_epsilon()
349        }
350
351        /// Equality assertion with an absolute limit.
352        ///
353        /// # Examples
354        ///
355        /// ```
356        /// use geo_types::{Geometry, polygon};
357        ///
358        /// let a: Geometry<f32> = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7., y: 9.), (x: 0., y: 0.)].into();
359        /// let b: Geometry<f32> = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7.01, y: 9.), (x: 0., y: 0.)].into();
360        ///
361        /// approx::assert_abs_diff_eq!(a, b, epsilon=0.1);
362        /// approx::assert_abs_diff_ne!(a, b, epsilon=0.001);
363        /// ```
364        fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
365            match (self, other) {
366                (Geometry::Point(g1), Geometry::Point(g2)) => g1.abs_diff_eq(g2, epsilon),
367                (Geometry::Line(g1), Geometry::Line(g2)) => g1.abs_diff_eq(g2, epsilon),
368                (Geometry::LineString(g1), Geometry::LineString(g2)) => g1.abs_diff_eq(g2, epsilon),
369                (Geometry::Polygon(g1), Geometry::Polygon(g2)) => g1.abs_diff_eq(g2, epsilon),
370                (Geometry::MultiPoint(g1), Geometry::MultiPoint(g2)) => g1.abs_diff_eq(g2, epsilon),
371                (Geometry::MultiLineString(g1), Geometry::MultiLineString(g2)) => {
372                    g1.abs_diff_eq(g2, epsilon)
373                }
374                (Geometry::MultiPolygon(g1), Geometry::MultiPolygon(g2)) => {
375                    g1.abs_diff_eq(g2, epsilon)
376                }
377                (Geometry::GeometryCollection(g1), Geometry::GeometryCollection(g2)) => {
378                    g1.abs_diff_eq(g2, epsilon)
379                }
380                (Geometry::Rect(g1), Geometry::Rect(g2)) => g1.abs_diff_eq(g2, epsilon),
381                (Geometry::Triangle(g1), Geometry::Triangle(g2)) => g1.abs_diff_eq(g2, epsilon),
382                (_, _) => false,
383            }
384        }
385    }
386
387    impl<T> UlpsEq for Geometry<T>
388    where
389        T: CoordNum + UlpsEq<Epsilon = T>,
390    {
391        fn default_max_ulps() -> u32 {
392            T::default_max_ulps()
393        }
394
395        /// Approximate equality assertion for floating point geometries based on the number of
396        /// representable floats that fit between the two numbers being compared.
397        ///
398        /// "relative_eq" might be more intuitive, but it does floating point math in its error
399        /// calculation, introducing its **own** error into the error calculation.
400        ///
401        /// Working with `ulps` avoids this problem. `max_ulps` means "how many floating points
402        /// are representable that fit between these two numbers", which lets us tune how "sloppy"
403        /// we're willing to be while avoiding any danger of floating point rounding in the
404        /// comparison itself.
405        ///
406        /// # Examples
407        ///
408        /// ```
409        /// use geo_types::{Geometry, Point};
410        ///
411        /// let a: Geometry = Point::new(1.0, 1.0).into();
412        /// let b: Geometry = Point::new(1.0 + 4.0 * f64::EPSILON, 1.0 + 4.0 * f64::EPSILON).into();
413        ///
414        /// approx::assert_ulps_eq!(a, b);
415        /// approx::assert_ulps_ne!(a, b, max_ulps=3);
416        /// approx::assert_ulps_eq!(a, b, max_ulps=5);
417        /// ```
418        ///
419        /// # References
420        ///
421        /// <https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/>
422        fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
423            match (self, other) {
424                (Geometry::Point(g1), Geometry::Point(g2)) => g1.ulps_eq(g2, epsilon, max_ulps),
425                (Geometry::Line(g1), Geometry::Line(g2)) => g1.ulps_eq(g2, epsilon, max_ulps),
426                (Geometry::LineString(g1), Geometry::LineString(g2)) => {
427                    g1.ulps_eq(g2, epsilon, max_ulps)
428                }
429                (Geometry::Polygon(g1), Geometry::Polygon(g2)) => g1.ulps_eq(g2, epsilon, max_ulps),
430                (Geometry::MultiPoint(g1), Geometry::MultiPoint(g2)) => {
431                    g1.ulps_eq(g2, epsilon, max_ulps)
432                }
433                (Geometry::MultiLineString(g1), Geometry::MultiLineString(g2)) => {
434                    g1.ulps_eq(g2, epsilon, max_ulps)
435                }
436                (Geometry::MultiPolygon(g1), Geometry::MultiPolygon(g2)) => {
437                    g1.ulps_eq(g2, epsilon, max_ulps)
438                }
439                (Geometry::GeometryCollection(g1), Geometry::GeometryCollection(g2)) => {
440                    g1.ulps_eq(g2, epsilon, max_ulps)
441                }
442                (Geometry::Rect(g1), Geometry::Rect(g2)) => g1.ulps_eq(g2, epsilon, max_ulps),
443                (Geometry::Triangle(g1), Geometry::Triangle(g2)) => {
444                    g1.ulps_eq(g2, epsilon, max_ulps)
445                }
446                // mismatched geometry types
447                _ => false,
448            }
449        }
450    }
451}
452
453#[cfg(test)]
454mod tests {
455    mod approx_integration {
456        use crate::{Geometry, Point};
457
458        #[test]
459        fn test_abs_diff() {
460            let g = Geometry::from(Point::new(1.0, 1.0));
461            let abs_diff_eq_point =
462                Geometry::from(Point::new(1.0 + f64::EPSILON, 1.0 + f64::EPSILON));
463            assert_ne!(g, abs_diff_eq_point);
464            assert_abs_diff_eq!(g, abs_diff_eq_point);
465
466            let a_little_farther = Geometry::from(Point::new(1.001, 1.001));
467            assert_ne!(g, a_little_farther);
468            assert_abs_diff_ne!(g, a_little_farther);
469            assert_abs_diff_eq!(g, a_little_farther, epsilon = 1e-3);
470            assert_abs_diff_ne!(g, a_little_farther, epsilon = 5e-4);
471        }
472
473        #[test]
474        fn test_relative() {
475            let g = Geometry::from(Point::new(2.0, 2.0));
476
477            let relative_eq_point = Geometry::from(Point::new(
478                2.0 + 2.0 * f64::EPSILON,
479                2.0 + 2.0 * f64::EPSILON,
480            ));
481            assert_ne!(g, relative_eq_point);
482            assert_relative_eq!(g, relative_eq_point);
483
484            let a_little_farther = Geometry::from(Point::new(2.001, 2.001));
485            assert_ne!(g, a_little_farther);
486            assert_relative_ne!(g, a_little_farther);
487            assert_relative_eq!(g, a_little_farther, epsilon = 1e-3);
488            assert_relative_ne!(g, a_little_farther, epsilon = 5e-4);
489            assert_relative_eq!(g, a_little_farther, max_relative = 5e-4);
490
491            // point * 2
492            let far = Geometry::from(Point::new(4.0, 4.0));
493            assert_relative_eq!(g, far, max_relative = 1.0 / 2.0);
494            assert_relative_ne!(g, far, max_relative = 0.49);
495        }
496
497        #[test]
498        fn test_ulps() {
499            let g = Geometry::from(Point::new(1.0, 1.0));
500
501            let ulps_eq_point = Geometry::from(Point::new(1.0 + f64::EPSILON, 1.0 + f64::EPSILON));
502            assert_ne!(g, ulps_eq_point);
503            assert_ulps_eq!(g, ulps_eq_point);
504        }
505
506        #[test]
507        fn test_ulps_vs_relative() {
508            // "relative_eq" measures the difference between two floating point outputs, but to do
509            // so involves doing its own floating point math, which introduces some of its own
510            // error in the error calculation.
511            //
512            // Working with `ulps` avoids this problem. `max_ulps` means "how many floating points
513            // are representable that fit between these two numbers", which lets us tune how "sloppy"
514            // we're willing to be while avoiding any danger of floating point rounding in the
515            // comparison itself.
516            let a = 1000.000000000001;
517            let b = 1000.0000000000008;
518
519            let p1 = Point::new(a, a);
520            let p2 = Point::new(b, b);
521
522            assert_ne!(p1, p2);
523            assert_relative_ne!(p1, p2);
524            assert_ulps_eq!(p1, p2);
525        }
526    }
527}