geo_traits/
polygon.rs

1use std::marker::PhantomData;
2
3use crate::iterator::PolygonInteriorIterator;
4use crate::line_string::UnimplementedLineString;
5use crate::{GeometryTrait, LineStringTrait};
6#[cfg(feature = "geo-types")]
7use geo_types::{CoordNum, LineString, Polygon};
8
9/// A trait for accessing data from a generic Polygon.
10///
11/// A `Polygon`’s outer boundary (_exterior ring_) is represented by a
12/// [`LineString`][LineStringTrait]. It may contain zero or more holes (_interior rings_), also
13/// represented by `LineString`s.
14///
15/// Refer to [geo_types::Polygon] for information about semantics and validity.
16pub trait PolygonTrait: Sized + GeometryTrait {
17    /// The coordinate type of this geometry
18    /// The type of each underlying ring, which implements [LineStringTrait]
19    type RingType<'a>: 'a + LineStringTrait<T = <Self as GeometryTrait>::T>
20    where
21        Self: 'a;
22
23    /// The exterior ring of the polygon
24    fn exterior(&self) -> Option<Self::RingType<'_>>;
25
26    /// An iterator of the interior rings of this Polygon
27    fn interiors(&self) -> impl DoubleEndedIterator + ExactSizeIterator<Item = Self::RingType<'_>> {
28        PolygonInteriorIterator::new(self, 0, self.num_interiors())
29    }
30
31    /// The number of interior rings in this Polygon
32    fn num_interiors(&self) -> usize;
33
34    /// Access to a specified interior ring in this Polygon
35    /// Will return None if the provided index is out of bounds
36    fn interior(&self, i: usize) -> Option<Self::RingType<'_>> {
37        if i >= self.num_interiors() {
38            None
39        } else {
40            unsafe { Some(self.interior_unchecked(i)) }
41        }
42    }
43
44    /// Access to a specified interior ring in this Polygon
45    ///
46    /// # Safety
47    ///
48    /// Accessing an index out of bounds is UB.
49    unsafe fn interior_unchecked(&self, i: usize) -> Self::RingType<'_>;
50}
51
52#[cfg(feature = "geo-types")]
53impl<T: CoordNum> PolygonTrait for Polygon<T> {
54    type RingType<'a>
55        = &'a LineString<<Self as GeometryTrait>::T>
56    where
57        Self: 'a;
58
59    fn exterior(&self) -> Option<Self::RingType<'_>> {
60        let ext_ring = Polygon::exterior(self);
61        if LineStringTrait::num_coords(&ext_ring) == 0 {
62            None
63        } else {
64            Some(ext_ring)
65        }
66    }
67
68    fn num_interiors(&self) -> usize {
69        Polygon::interiors(self).len()
70    }
71
72    unsafe fn interior_unchecked(&self, i: usize) -> Self::RingType<'_> {
73        unsafe { Polygon::interiors(self).get_unchecked(i) }
74    }
75}
76
77#[cfg(feature = "geo-types")]
78impl<'a, T: CoordNum> PolygonTrait for &'a Polygon<T> {
79    type RingType<'b>
80        = &'a LineString<<Self as GeometryTrait>::T>
81    where
82        Self: 'b;
83
84    fn exterior(&self) -> Option<Self::RingType<'_>> {
85        let ext_ring = Polygon::exterior(self);
86        if LineStringTrait::num_coords(&ext_ring) == 0 {
87            None
88        } else {
89            Some(ext_ring)
90        }
91    }
92
93    fn num_interiors(&self) -> usize {
94        Polygon::interiors(self).len()
95    }
96
97    unsafe fn interior_unchecked(&self, i: usize) -> Self::RingType<'_> {
98        unsafe { Polygon::interiors(self).get_unchecked(i) }
99    }
100}
101
102/// An empty struct that implements [PolygonTrait].
103///
104/// This can be used as the `PolygonType` of the `GeometryTrait` by implementations that don't have a
105/// Polygon concept
106pub struct UnimplementedPolygon<T>(PhantomData<T>);
107
108impl<T> PolygonTrait for UnimplementedPolygon<T> {
109    type RingType<'a>
110        = UnimplementedLineString<<Self as GeometryTrait>::T>
111    where
112        Self: 'a;
113
114    fn exterior(&self) -> Option<Self::RingType<'_>> {
115        unimplemented!()
116    }
117
118    fn num_interiors(&self) -> usize {
119        unimplemented!()
120    }
121
122    unsafe fn interior_unchecked(&self, _i: usize) -> Self::RingType<'_> {
123        unimplemented!()
124    }
125}