1use geometry_model::{
10 Box, Linestring, MultiLinestring, MultiPoint, MultiPolygon, Point, Polygon, Ring, Segment,
11};
12use geometry_trait::Polygon as _;
13
14#[inline]
23#[must_use]
24pub fn num_interior_rings<G: NumInteriorRings>(g: &G) -> usize {
25 g.num_interior_rings()
26}
27
28#[doc(hidden)]
33pub trait NumInteriorRings {
34 fn num_interior_rings(&self) -> usize;
36}
37
38impl<T, const D: usize, Cs> NumInteriorRings for Point<T, D, Cs>
40where
41 T: geometry_coords::CoordinateScalar,
42 Cs: geometry_cs::CoordinateSystem,
43{
44 fn num_interior_rings(&self) -> usize {
45 0
46 }
47}
48
49impl<P: geometry_trait::Point> NumInteriorRings for Linestring<P> {
50 fn num_interior_rings(&self) -> usize {
51 0
52 }
53}
54
55impl<P: geometry_trait::Point, const CW: bool, const CL: bool> NumInteriorRings
56 for Ring<P, CW, CL>
57{
58 fn num_interior_rings(&self) -> usize {
59 0
60 }
61}
62
63impl<P: geometry_trait::Point> NumInteriorRings for Segment<P> {
64 fn num_interior_rings(&self) -> usize {
65 0
66 }
67}
68
69impl<P: geometry_trait::Point> NumInteriorRings for Box<P> {
70 fn num_interior_rings(&self) -> usize {
71 0
72 }
73}
74
75impl<P: geometry_trait::Point> NumInteriorRings for MultiPoint<P> {
76 fn num_interior_rings(&self) -> usize {
77 0
78 }
79}
80
81impl<L: geometry_trait::Linestring> NumInteriorRings for MultiLinestring<L> {
82 fn num_interior_rings(&self) -> usize {
83 0
84 }
85}
86
87impl<P: geometry_trait::Point, const CW: bool, const CL: bool> NumInteriorRings
89 for Polygon<P, CW, CL>
90{
91 fn num_interior_rings(&self) -> usize {
92 self.interiors().count()
93 }
94}
95
96impl<Pg: geometry_trait::Polygon + NumInteriorRings> NumInteriorRings for MultiPolygon<Pg> {
97 fn num_interior_rings(&self) -> usize {
98 self.0
99 .iter()
100 .map(NumInteriorRings::num_interior_rings)
101 .sum()
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::num_interior_rings;
111 use geometry_cs::Cartesian;
112 use geometry_model::{Linestring, MultiPolygon, Point2D, Polygon, linestring, polygon};
113
114 type Pt = Point2D<f64, Cartesian>;
115 type Ls = Linestring<Pt>;
116 type Poly = Polygon<Pt>;
117
118 #[test]
120 fn point_has_zero() {
121 assert_eq!(num_interior_rings(&Pt::new(0.0, 0.0)), 0);
122 }
123
124 #[test]
126 fn linestring_has_zero() {
127 let ls: Ls = linestring![(0.0, 0.0), (1.0, 1.0)];
128 assert_eq!(num_interior_rings(&ls), 0);
129 }
130
131 #[test]
133 fn polygon_outer_only_has_zero() {
134 let pg: Poly = polygon![[(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 0.0)]];
135 assert_eq!(num_interior_rings(&pg), 0);
136 }
137
138 #[test]
140 fn polygon_with_two_holes() {
141 let pg: Poly = polygon![
142 [
143 (0.0, 0.0),
144 (10.0, 0.0),
145 (10.0, 10.0),
146 (0.0, 10.0),
147 (0.0, 0.0)
148 ],
149 [(1.0, 1.0), (2.0, 1.0), (2.0, 2.0), (1.0, 1.0)],
150 [(5.0, 5.0), (6.0, 5.0), (6.0, 6.0), (5.0, 5.0)],
151 ];
152 assert_eq!(num_interior_rings(&pg), 2);
153 }
154
155 #[test]
157 fn non_polygon_kinds_have_zero() {
158 use geometry_model::{Box, MultiLinestring, MultiPoint, Ring, Segment};
159 let ring: Ring<Pt> = Ring::from_vec(vec![
160 Pt::new(0.0, 0.0),
161 Pt::new(1.0, 0.0),
162 Pt::new(0.0, 0.0),
163 ]);
164 assert_eq!(num_interior_rings(&ring), 0);
165 assert_eq!(
166 num_interior_rings(&Segment::new(Pt::new(0.0, 0.0), Pt::new(1.0, 1.0))),
167 0
168 );
169 assert_eq!(
170 num_interior_rings(&Box::from_corners(Pt::new(0.0, 0.0), Pt::new(1.0, 1.0))),
171 0
172 );
173 assert_eq!(num_interior_rings(&MultiPoint(vec![Pt::new(0.0, 0.0)])), 0);
174 let mls: MultiLinestring<Ls> = MultiLinestring(vec![linestring![(0.0, 0.0), (1.0, 1.0)]]);
175 assert_eq!(num_interior_rings(&mls), 0);
176 }
177
178 #[test]
181 fn multi_polygon_sums_holes() {
182 let mpg: MultiPolygon<Poly> = MultiPolygon(vec![
183 polygon![
184 [
185 (0.0, 0.0),
186 (10.0, 0.0),
187 (10.0, 10.0),
188 (0.0, 10.0),
189 (0.0, 0.0)
190 ],
191 [(1.0, 1.0), (2.0, 1.0), (2.0, 2.0), (1.0, 1.0)],
192 ],
193 polygon![[(20.0, 0.0), (30.0, 0.0), (30.0, 10.0), (20.0, 0.0)]],
194 ]);
195 assert_eq!(num_interior_rings(&mpg), 1);
196 }
197}