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