1use geometry_tag::{
12 BoxTag, LinestringTag, MultiLinestringTag, MultiPointTag, MultiPolygonTag, PointTag,
13 PolygonTag, RingTag, SegmentTag,
14};
15use geometry_trait::{
16 Box as BoxTrait, Geometry, Linestring as LinestringTrait,
17 MultiLinestring as MultiLinestringTrait, MultiPoint as MultiPointTrait,
18 MultiPolygon as MultiPolygonTrait, Point as PointTrait, Polygon as PolygonTrait,
19 Ring as RingTrait, Segment as SegmentTrait,
20};
21
22#[inline]
37#[must_use]
38pub fn num_points<G>(g: &G) -> usize
39where
40 G: Geometry,
41 G::Kind: NumPointsStrategyForKind,
42 <G::Kind as NumPointsStrategyForKind>::S: NumPointsStrategy<G>,
43{
44 <<G::Kind as NumPointsStrategyForKind>::S as Default>::default().num_points(g)
45}
46
47#[doc(hidden)]
52pub trait NumPointsStrategy<G> {
53 fn num_points(&self, g: &G) -> usize;
55}
56
57#[derive(Default)]
58#[doc(hidden)]
59pub struct PointNumPoints;
60#[derive(Default)]
61#[doc(hidden)]
62pub struct SegmentNumPoints;
63#[derive(Default)]
64#[doc(hidden)]
65pub struct BoxNumPoints;
66#[derive(Default)]
67#[doc(hidden)]
68pub struct LinestringNumPoints;
69#[derive(Default)]
70#[doc(hidden)]
71pub struct RingNumPoints;
72#[derive(Default)]
73#[doc(hidden)]
74pub struct PolygonNumPoints;
75#[derive(Default)]
76#[doc(hidden)]
77pub struct MultiPointNumPoints;
78#[derive(Default)]
79#[doc(hidden)]
80pub struct MultiLinestringNumPoints;
81#[derive(Default)]
82#[doc(hidden)]
83pub struct MultiPolygonNumPoints;
84
85impl<G: PointTrait> NumPointsStrategy<G> for PointNumPoints {
86 fn num_points(&self, _g: &G) -> usize {
87 1
88 }
89}
90
91impl<G: SegmentTrait> NumPointsStrategy<G> for SegmentNumPoints {
92 fn num_points(&self, _g: &G) -> usize {
93 2
94 }
95}
96
97impl<G: BoxTrait> NumPointsStrategy<G> for BoxNumPoints {
98 fn num_points(&self, _g: &G) -> usize {
99 1usize << <G::Point as PointTrait>::DIM
102 }
103}
104
105impl<G: LinestringTrait> NumPointsStrategy<G> for LinestringNumPoints {
106 fn num_points(&self, g: &G) -> usize {
107 g.points().count()
108 }
109}
110
111impl<G: RingTrait> NumPointsStrategy<G> for RingNumPoints {
112 fn num_points(&self, g: &G) -> usize {
113 g.points().count()
114 }
115}
116
117impl<G: PolygonTrait> NumPointsStrategy<G> for PolygonNumPoints {
118 fn num_points(&self, g: &G) -> usize {
119 let mut n = g.exterior().points().count();
120 for inner in g.interiors() {
121 n += inner.points().count();
122 }
123 n
124 }
125}
126
127impl<G: MultiPointTrait> NumPointsStrategy<G> for MultiPointNumPoints {
128 fn num_points(&self, g: &G) -> usize {
129 g.points().count()
130 }
131}
132
133impl<G: MultiLinestringTrait> NumPointsStrategy<G> for MultiLinestringNumPoints {
134 fn num_points(&self, g: &G) -> usize {
135 g.linestrings().map(num_points).sum()
139 }
140}
141
142impl<G: MultiPolygonTrait> NumPointsStrategy<G> for MultiPolygonNumPoints {
143 fn num_points(&self, g: &G) -> usize {
144 g.polygons().map(num_points).sum()
145 }
146}
147
148#[doc(hidden)]
154pub trait NumPointsStrategyForKind {
155 type S: Default;
157}
158
159impl NumPointsStrategyForKind for PointTag {
160 type S = PointNumPoints;
161}
162impl NumPointsStrategyForKind for SegmentTag {
163 type S = SegmentNumPoints;
164}
165impl NumPointsStrategyForKind for BoxTag {
166 type S = BoxNumPoints;
167}
168impl NumPointsStrategyForKind for LinestringTag {
169 type S = LinestringNumPoints;
170}
171impl NumPointsStrategyForKind for RingTag {
172 type S = RingNumPoints;
173}
174impl NumPointsStrategyForKind for PolygonTag {
175 type S = PolygonNumPoints;
176}
177impl NumPointsStrategyForKind for MultiPointTag {
178 type S = MultiPointNumPoints;
179}
180impl NumPointsStrategyForKind for MultiLinestringTag {
181 type S = MultiLinestringNumPoints;
182}
183impl NumPointsStrategyForKind for MultiPolygonTag {
184 type S = MultiPolygonNumPoints;
185}
186
187#[cfg(test)]
188mod tests {
189 use super::num_points;
194 use geometry_cs::Cartesian;
195 use geometry_model::{
196 Box, Linestring, MultiLinestring, MultiPoint, MultiPolygon, Point2D, Polygon, Segment,
197 linestring, polygon,
198 };
199
200 type Pt = Point2D<f64, Cartesian>;
201 type Ls = Linestring<Pt>;
202 type Poly = Polygon<Pt>;
203
204 #[test]
206 fn point_is_one() {
207 assert_eq!(num_points(&Pt::new(0.0, 0.0)), 1);
208 }
209
210 #[test]
212 fn segment_is_two() {
213 let s = Segment::new(Pt::new(0.0, 0.0), Pt::new(1.0, 1.0));
214 assert_eq!(num_points(&s), 2);
215 }
216
217 #[test]
219 fn box_2d_is_four() {
220 let b = Box::from_corners(Pt::new(0.0, 0.0), Pt::new(1.0, 1.0));
221 assert_eq!(num_points(&b), 4);
222 }
223
224 #[test]
227 fn linestring_three_points() {
228 let ls: Ls = linestring![(0.0, 0.0), (1.0, 1.0), (2.0, 2.0)];
229 assert_eq!(num_points(&ls), 3);
230 }
231
232 #[test]
235 fn polygon_outer_only() {
236 let pg: Poly = polygon![[(0.0, 0.0), (4.0, 0.0), (4.0, 3.0), (0.0, 3.0), (0.0, 0.0)]];
237 assert_eq!(num_points(&pg), 5);
238 }
239
240 #[test]
244 fn polygon_with_hole_adds_inner_count() {
245 let pg: Poly = polygon![
246 [
247 (0.0, 0.0),
248 (10.0, 0.0),
249 (10.0, 10.0),
250 (0.0, 10.0),
251 (0.0, 0.0)
252 ],
253 [(1.0, 1.0), (2.0, 1.0), (2.0, 2.0), (1.0, 2.0), (1.0, 1.0)],
254 ];
255 assert_eq!(num_points(&pg), 10);
256 }
257
258 #[test]
261 fn multi_linestring_sums_members() {
262 let mls: MultiLinestring<Ls> = MultiLinestring(vec![
263 linestring![(0.0, 0.0), (1.0, 1.0)],
264 linestring![(2.0, 2.0), (3.0, 3.0), (4.0, 4.0)],
265 ]);
266 assert_eq!(num_points(&mls), 5);
267 }
268
269 #[test]
271 fn multi_point_counts_members() {
272 let mp = MultiPoint(vec![Pt::new(0.0, 0.0), Pt::new(1.0, 1.0)]);
273 assert_eq!(num_points(&mp), 2);
274 }
275
276 #[test]
280 fn multi_polygon_sums_members() {
281 let mpg: MultiPolygon<Poly> = MultiPolygon(vec![
282 polygon![[
283 (0.0, 0.0),
284 (0.0, 10.0),
285 (10.0, 10.0),
286 (10.0, 0.0),
287 (0.0, 0.0)
288 ]],
289 polygon![[(0.0, 10.0), (1.0, 10.0), (1.0, 9.0), (0.0, 10.0)]],
290 ]);
291 assert_eq!(num_points(&mpg), 9);
292 }
293}