Skip to main content

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    /// Get simple geometry type name as a string.
215    pub fn static_name(&self) -> &'static str {
216        match self {
217            Geometry::Point(_) => "Point",
218            Geometry::Line(_) => "Line",
219            Geometry::LineString(_) => "LineString",
220            Geometry::Polygon(_) => "Polygon",
221            Geometry::MultiPoint(_) => "MultiPoint",
222            Geometry::MultiLineString(_) => "MultiLineString",
223            Geometry::MultiPolygon(_) => "MultiPolygon",
224            Geometry::GeometryCollection(_) => "GeometryCollection",
225            Geometry::Rect(_) => "Rect",
226            Geometry::Triangle(_) => "Triangle",
227        }
228    }
229}
230
231macro_rules! try_from_geometry_impl {
232    ($($type: ident),+) => {
233        $(
234        /// Convert a Geometry enum into its inner type.
235        ///
236        /// Fails if the enum case does not match the type you are trying to convert it to.
237        impl <T: CoordNum> TryFrom<Geometry<T>> for $type<T> {
238            type Error = Error;
239
240            fn try_from(geom: Geometry<T>) -> Result<Self, Self::Error> {
241                match geom {
242                    Geometry::$type(g) => Ok(g),
243                    other => Err(Error::MismatchedGeometry {
244                        expected: type_name::<$type<T>>(),
245                        found: inner_type_name(other)
246                    })
247                }
248            }
249        }
250        )+
251    }
252}
253
254try_from_geometry_impl!(
255    Point,
256    Line,
257    LineString,
258    Polygon,
259    MultiPoint,
260    MultiLineString,
261    MultiPolygon,
262    // Disabled until we remove the deprecated GeometryCollection::from(single_geom) impl.
263    // GeometryCollection,
264    Rect,
265    Triangle
266);
267
268fn inner_type_name<T>(geometry: Geometry<T>) -> &'static str
269where
270    T: CoordNum,
271{
272    match geometry {
273        Geometry::Point(_) => type_name::<Point<T>>(),
274        Geometry::Line(_) => type_name::<Line<T>>(),
275        Geometry::LineString(_) => type_name::<LineString<T>>(),
276        Geometry::Polygon(_) => type_name::<Polygon<T>>(),
277        Geometry::MultiPoint(_) => type_name::<MultiPoint<T>>(),
278        Geometry::MultiLineString(_) => type_name::<MultiLineString<T>>(),
279        Geometry::MultiPolygon(_) => type_name::<MultiPolygon<T>>(),
280        Geometry::GeometryCollection(_) => type_name::<GeometryCollection<T>>(),
281        Geometry::Rect(_) => type_name::<Rect<T>>(),
282        Geometry::Triangle(_) => type_name::<Triangle<T>>(),
283    }
284}
285
286#[cfg(any(feature = "approx", test))]
287mod approx_integration {
288    use super::*;
289    use approx::{AbsDiffEq, RelativeEq, UlpsEq};
290
291    impl<T> RelativeEq for Geometry<T>
292    where
293        T: CoordNum + RelativeEq<Epsilon = T>,
294    {
295        #[inline]
296        fn default_max_relative() -> Self::Epsilon {
297            T::default_max_relative()
298        }
299
300        /// Equality assertion within a relative limit.
301        ///
302        /// # Examples
303        ///
304        /// ```
305        /// use geo_types::{Geometry, polygon};
306        ///
307        /// let a: Geometry<f32> = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7., y: 9.), (x: 0., y: 0.)].into();
308        /// let b: Geometry<f32> = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7.01, y: 9.), (x: 0., y: 0.)].into();
309        ///
310        /// approx::assert_relative_eq!(a, b, max_relative=0.1);
311        /// approx::assert_relative_ne!(a, b, max_relative=0.001);
312        /// ```
313        ///
314        fn relative_eq(
315            &self,
316            other: &Self,
317            epsilon: Self::Epsilon,
318            max_relative: Self::Epsilon,
319        ) -> bool {
320            match (self, other) {
321                (Geometry::Point(g1), Geometry::Point(g2)) => {
322                    g1.relative_eq(g2, epsilon, max_relative)
323                }
324                (Geometry::Line(g1), Geometry::Line(g2)) => {
325                    g1.relative_eq(g2, epsilon, max_relative)
326                }
327                (Geometry::LineString(g1), Geometry::LineString(g2)) => {
328                    g1.relative_eq(g2, epsilon, max_relative)
329                }
330                (Geometry::Polygon(g1), Geometry::Polygon(g2)) => {
331                    g1.relative_eq(g2, epsilon, max_relative)
332                }
333                (Geometry::MultiPoint(g1), Geometry::MultiPoint(g2)) => {
334                    g1.relative_eq(g2, epsilon, max_relative)
335                }
336                (Geometry::MultiLineString(g1), Geometry::MultiLineString(g2)) => {
337                    g1.relative_eq(g2, epsilon, max_relative)
338                }
339                (Geometry::MultiPolygon(g1), Geometry::MultiPolygon(g2)) => {
340                    g1.relative_eq(g2, epsilon, max_relative)
341                }
342                (Geometry::GeometryCollection(g1), Geometry::GeometryCollection(g2)) => {
343                    g1.relative_eq(g2, epsilon, max_relative)
344                }
345                (Geometry::Rect(g1), Geometry::Rect(g2)) => {
346                    g1.relative_eq(g2, epsilon, max_relative)
347                }
348                (Geometry::Triangle(g1), Geometry::Triangle(g2)) => {
349                    g1.relative_eq(g2, epsilon, max_relative)
350                }
351                (_, _) => false,
352            }
353        }
354    }
355
356    impl<T> AbsDiffEq for Geometry<T>
357    where
358        T: CoordNum + AbsDiffEq<Epsilon = T>,
359    {
360        type Epsilon = T;
361
362        #[inline]
363        fn default_epsilon() -> Self::Epsilon {
364            T::default_epsilon()
365        }
366
367        /// Equality assertion with an absolute limit.
368        ///
369        /// # Examples
370        ///
371        /// ```
372        /// use geo_types::{Geometry, polygon};
373        ///
374        /// let a: Geometry<f32> = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7., y: 9.), (x: 0., y: 0.)].into();
375        /// let b: Geometry<f32> = polygon![(x: 0., y: 0.), (x: 5., y: 0.), (x: 7.01, y: 9.), (x: 0., y: 0.)].into();
376        ///
377        /// approx::assert_abs_diff_eq!(a, b, epsilon=0.1);
378        /// approx::assert_abs_diff_ne!(a, b, epsilon=0.001);
379        /// ```
380        fn abs_diff_eq(&self, other: &Self, epsilon: Self::Epsilon) -> bool {
381            match (self, other) {
382                (Geometry::Point(g1), Geometry::Point(g2)) => g1.abs_diff_eq(g2, epsilon),
383                (Geometry::Line(g1), Geometry::Line(g2)) => g1.abs_diff_eq(g2, epsilon),
384                (Geometry::LineString(g1), Geometry::LineString(g2)) => g1.abs_diff_eq(g2, epsilon),
385                (Geometry::Polygon(g1), Geometry::Polygon(g2)) => g1.abs_diff_eq(g2, epsilon),
386                (Geometry::MultiPoint(g1), Geometry::MultiPoint(g2)) => g1.abs_diff_eq(g2, epsilon),
387                (Geometry::MultiLineString(g1), Geometry::MultiLineString(g2)) => {
388                    g1.abs_diff_eq(g2, epsilon)
389                }
390                (Geometry::MultiPolygon(g1), Geometry::MultiPolygon(g2)) => {
391                    g1.abs_diff_eq(g2, epsilon)
392                }
393                (Geometry::GeometryCollection(g1), Geometry::GeometryCollection(g2)) => {
394                    g1.abs_diff_eq(g2, epsilon)
395                }
396                (Geometry::Rect(g1), Geometry::Rect(g2)) => g1.abs_diff_eq(g2, epsilon),
397                (Geometry::Triangle(g1), Geometry::Triangle(g2)) => g1.abs_diff_eq(g2, epsilon),
398                (_, _) => false,
399            }
400        }
401    }
402
403    impl<T> UlpsEq for Geometry<T>
404    where
405        T: CoordNum + UlpsEq<Epsilon = T>,
406    {
407        fn default_max_ulps() -> u32 {
408            T::default_max_ulps()
409        }
410
411        /// Approximate equality assertion for floating point geometries based on the number of
412        /// representable floats that fit between the two numbers being compared.
413        ///
414        /// "relative_eq" might be more intuitive, but it does floating point math in its error
415        /// calculation, introducing its **own** error into the error calculation.
416        ///
417        /// Working with `ulps` avoids this problem. `max_ulps` means "how many floating points
418        /// are representable that fit between these two numbers", which lets us tune how "sloppy"
419        /// we're willing to be while avoiding any danger of floating point rounding in the
420        /// comparison itself.
421        ///
422        /// # Examples
423        ///
424        /// ```
425        /// use geo_types::{Geometry, Point};
426        ///
427        /// let a: Geometry = Point::new(1.0, 1.0).into();
428        /// let b: Geometry = Point::new(1.0 + 4.0 * f64::EPSILON, 1.0 + 4.0 * f64::EPSILON).into();
429        ///
430        /// approx::assert_ulps_eq!(a, b);
431        /// approx::assert_ulps_ne!(a, b, max_ulps=3);
432        /// approx::assert_ulps_eq!(a, b, max_ulps=5);
433        /// ```
434        ///
435        /// # References
436        ///
437        /// <https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/>
438        fn ulps_eq(&self, other: &Self, epsilon: Self::Epsilon, max_ulps: u32) -> bool {
439            match (self, other) {
440                (Geometry::Point(g1), Geometry::Point(g2)) => g1.ulps_eq(g2, epsilon, max_ulps),
441                (Geometry::Line(g1), Geometry::Line(g2)) => g1.ulps_eq(g2, epsilon, max_ulps),
442                (Geometry::LineString(g1), Geometry::LineString(g2)) => {
443                    g1.ulps_eq(g2, epsilon, max_ulps)
444                }
445                (Geometry::Polygon(g1), Geometry::Polygon(g2)) => g1.ulps_eq(g2, epsilon, max_ulps),
446                (Geometry::MultiPoint(g1), Geometry::MultiPoint(g2)) => {
447                    g1.ulps_eq(g2, epsilon, max_ulps)
448                }
449                (Geometry::MultiLineString(g1), Geometry::MultiLineString(g2)) => {
450                    g1.ulps_eq(g2, epsilon, max_ulps)
451                }
452                (Geometry::MultiPolygon(g1), Geometry::MultiPolygon(g2)) => {
453                    g1.ulps_eq(g2, epsilon, max_ulps)
454                }
455                (Geometry::GeometryCollection(g1), Geometry::GeometryCollection(g2)) => {
456                    g1.ulps_eq(g2, epsilon, max_ulps)
457                }
458                (Geometry::Rect(g1), Geometry::Rect(g2)) => g1.ulps_eq(g2, epsilon, max_ulps),
459                (Geometry::Triangle(g1), Geometry::Triangle(g2)) => {
460                    g1.ulps_eq(g2, epsilon, max_ulps)
461                }
462                // mismatched geometry types
463                _ => false,
464            }
465        }
466    }
467}
468
469#[cfg(test)]
470mod tests {
471    mod approx_integration {
472        use crate::{Geometry, Point};
473
474        #[test]
475        fn test_abs_diff() {
476            let g = Geometry::from(Point::new(1.0, 1.0));
477            let abs_diff_eq_point =
478                Geometry::from(Point::new(1.0 + f64::EPSILON, 1.0 + f64::EPSILON));
479            assert_ne!(g, abs_diff_eq_point);
480            assert_abs_diff_eq!(g, abs_diff_eq_point);
481
482            let a_little_farther = Geometry::from(Point::new(1.001, 1.001));
483            assert_ne!(g, a_little_farther);
484            assert_abs_diff_ne!(g, a_little_farther);
485            assert_abs_diff_eq!(g, a_little_farther, epsilon = 1e-3);
486            assert_abs_diff_ne!(g, a_little_farther, epsilon = 5e-4);
487        }
488
489        #[test]
490        fn test_relative() {
491            let g = Geometry::from(Point::new(2.0, 2.0));
492
493            let relative_eq_point = Geometry::from(Point::new(
494                2.0 + 2.0 * f64::EPSILON,
495                2.0 + 2.0 * f64::EPSILON,
496            ));
497            assert_ne!(g, relative_eq_point);
498            assert_relative_eq!(g, relative_eq_point);
499
500            let a_little_farther = Geometry::from(Point::new(2.001, 2.001));
501            assert_ne!(g, a_little_farther);
502            assert_relative_ne!(g, a_little_farther);
503            assert_relative_eq!(g, a_little_farther, epsilon = 1e-3);
504            assert_relative_ne!(g, a_little_farther, epsilon = 5e-4);
505            assert_relative_eq!(g, a_little_farther, max_relative = 5e-4);
506
507            // point * 2
508            let far = Geometry::from(Point::new(4.0, 4.0));
509            assert_relative_eq!(g, far, max_relative = 1.0 / 2.0);
510            assert_relative_ne!(g, far, max_relative = 0.49);
511        }
512
513        #[test]
514        fn test_ulps() {
515            let g = Geometry::from(Point::new(1.0, 1.0));
516
517            let ulps_eq_point = Geometry::from(Point::new(1.0 + f64::EPSILON, 1.0 + f64::EPSILON));
518            assert_ne!(g, ulps_eq_point);
519            assert_ulps_eq!(g, ulps_eq_point);
520        }
521
522        #[test]
523        fn test_ulps_vs_relative() {
524            // "relative_eq" measures the difference between two floating point outputs, but to do
525            // so involves doing its own floating point math, which introduces some of its own
526            // error in the error calculation.
527            //
528            // Working with `ulps` avoids this problem. `max_ulps` means "how many floating points
529            // are representable that fit between these two numbers", which lets us tune how "sloppy"
530            // we're willing to be while avoiding any danger of floating point rounding in the
531            // comparison itself.
532            let a = 1000.000000000001;
533            let b = 1000.0000000000008;
534
535            let p1 = Point::new(a, a);
536            let p2 = Point::new(b, b);
537
538            assert_ne!(p1, p2);
539            assert_relative_ne!(p1, p2);
540            assert_ulps_eq!(p1, p2);
541        }
542    }
543}