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]
158 fn multi_polygon_sums_holes() {
159 let mpg: MultiPolygon<Poly> = MultiPolygon(vec![
160 polygon![
161 [
162 (0.0, 0.0),
163 (10.0, 0.0),
164 (10.0, 10.0),
165 (0.0, 10.0),
166 (0.0, 0.0)
167 ],
168 [(1.0, 1.0), (2.0, 1.0), (2.0, 2.0), (1.0, 1.0)],
169 ],
170 polygon![[(20.0, 0.0), (30.0, 0.0), (30.0, 10.0), (20.0, 0.0)]],
171 ]);
172 assert_eq!(num_interior_rings(&mpg), 1);
173 }
174}