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 ring_counts_stored_points() {
236 let r: geometry_model::Ring<Pt> = geometry_model::Ring::from_vec(vec![
237 Pt::new(0.0, 0.0),
238 Pt::new(4.0, 0.0),
239 Pt::new(4.0, 3.0),
240 Pt::new(0.0, 3.0),
241 Pt::new(0.0, 0.0),
242 ]);
243 assert_eq!(num_points(&r), 5);
244 }
245
246 #[test]
249 fn polygon_outer_only() {
250 let pg: Poly = polygon![[(0.0, 0.0), (4.0, 0.0), (4.0, 3.0), (0.0, 3.0), (0.0, 0.0)]];
251 assert_eq!(num_points(&pg), 5);
252 }
253
254 #[test]
258 fn polygon_with_hole_adds_inner_count() {
259 let pg: Poly = polygon![
260 [
261 (0.0, 0.0),
262 (10.0, 0.0),
263 (10.0, 10.0),
264 (0.0, 10.0),
265 (0.0, 0.0)
266 ],
267 [(1.0, 1.0), (2.0, 1.0), (2.0, 2.0), (1.0, 2.0), (1.0, 1.0)],
268 ];
269 assert_eq!(num_points(&pg), 10);
270 }
271
272 #[test]
275 fn multi_linestring_sums_members() {
276 let mls: MultiLinestring<Ls> = MultiLinestring(vec![
277 linestring![(0.0, 0.0), (1.0, 1.0)],
278 linestring![(2.0, 2.0), (3.0, 3.0), (4.0, 4.0)],
279 ]);
280 assert_eq!(num_points(&mls), 5);
281 }
282
283 #[test]
285 fn multi_point_counts_members() {
286 let mp = MultiPoint(vec![Pt::new(0.0, 0.0), Pt::new(1.0, 1.0)]);
287 assert_eq!(num_points(&mp), 2);
288 }
289
290 #[test]
294 fn multi_polygon_sums_members() {
295 let mpg: MultiPolygon<Poly> = MultiPolygon(vec![
296 polygon![[
297 (0.0, 0.0),
298 (0.0, 10.0),
299 (10.0, 10.0),
300 (10.0, 0.0),
301 (0.0, 0.0)
302 ]],
303 polygon![[(0.0, 10.0), (1.0, 10.0), (1.0, 9.0), (0.0, 10.0)]],
304 ]);
305 assert_eq!(num_points(&mpg), 9);
306 }
307}