geo/algorithm/
remove_repeated_points.rs

1use crate::{
2    CoordNum, Geometry, Line, LineString, MultiLineString, MultiPoint, MultiPolygon, Point,
3    Polygon, Rect, Triangle,
4};
5use geo_types::GeometryCollection;
6use num_traits::FromPrimitive;
7
8/// Remove repeated points from a `MultiPoint` and repeated consecutive coordinates
9/// from `LineString`, `Polygon`, `MultiLineString` and `MultiPolygon`.
10///
11/// For `GeometryCollection` it individually removes the repeated points
12/// of each geometry in the collection.
13///
14/// For `Point`, `Line`, `Rect` and `Triangle` the geometry remains the same.
15pub trait RemoveRepeatedPoints<T>
16where
17    T: CoordNum + FromPrimitive,
18{
19    /// Create a new geometry with (consecutive) repeated points removed.
20    fn remove_repeated_points(&self) -> Self;
21    /// Remove (consecutive) repeated points inplace.
22    fn remove_repeated_points_mut(&mut self);
23}
24
25impl<T> RemoveRepeatedPoints<T> for MultiPoint<T>
26where
27    T: CoordNum + FromPrimitive,
28{
29    /// Create a MultiPoint with repeated points removed.
30    fn remove_repeated_points(&self) -> Self {
31        let mut points = vec![];
32        for p in self.0.iter() {
33            if !points.contains(p) {
34                points.push(*p);
35            }
36        }
37        MultiPoint(points)
38    }
39
40    /// Remove repeated points from a MultiPoint inplace.
41    fn remove_repeated_points_mut(&mut self) {
42        let mut points = vec![];
43        for p in self.0.iter() {
44            if !points.contains(p) {
45                points.push(*p);
46            }
47        }
48        self.0 = points;
49    }
50}
51
52impl<T> RemoveRepeatedPoints<T> for LineString<T>
53where
54    T: CoordNum + FromPrimitive,
55{
56    /// Create a LineString with consecutive repeated points removed.
57    fn remove_repeated_points(&self) -> Self {
58        let mut coords = self.0.clone();
59        coords.dedup();
60        LineString(coords)
61    }
62
63    /// Remove consecutive repeated points from a LineString inplace.
64    fn remove_repeated_points_mut(&mut self) {
65        self.0.dedup();
66    }
67}
68
69impl<T> RemoveRepeatedPoints<T> for Polygon<T>
70where
71    T: CoordNum + FromPrimitive,
72{
73    /// Create a Polygon with consecutive repeated points removed.
74    fn remove_repeated_points(&self) -> Self {
75        Polygon::new(
76            self.exterior().remove_repeated_points(),
77            self.interiors()
78                .iter()
79                .map(|ls| ls.remove_repeated_points())
80                .collect(),
81        )
82    }
83
84    /// Remove consecutive repeated points from a Polygon inplace.
85    fn remove_repeated_points_mut(&mut self) {
86        self.exterior_mut(|exterior| exterior.remove_repeated_points_mut());
87        self.interiors_mut(|interiors| {
88            for interior in interiors {
89                interior.remove_repeated_points_mut();
90            }
91        });
92    }
93}
94
95impl<T> RemoveRepeatedPoints<T> for MultiLineString<T>
96where
97    T: CoordNum + FromPrimitive,
98{
99    /// Create a MultiLineString with consecutive repeated points removed.
100    fn remove_repeated_points(&self) -> Self {
101        MultiLineString::new(
102            self.0
103                .iter()
104                .map(|ls| ls.remove_repeated_points())
105                .collect(),
106        )
107    }
108
109    /// Remove consecutive repeated points from a MultiLineString inplace.
110    fn remove_repeated_points_mut(&mut self) {
111        for ls in self.0.iter_mut() {
112            ls.remove_repeated_points_mut();
113        }
114    }
115}
116
117impl<T> RemoveRepeatedPoints<T> for MultiPolygon<T>
118where
119    T: CoordNum + FromPrimitive,
120{
121    /// Create a MultiPolygon with consecutive repeated points removed.
122    fn remove_repeated_points(&self) -> Self {
123        MultiPolygon::new(self.0.iter().map(|p| p.remove_repeated_points()).collect())
124    }
125
126    /// Remove consecutive repeated points from a MultiPolygon inplace.
127    fn remove_repeated_points_mut(&mut self) {
128        for p in self.0.iter_mut() {
129            p.remove_repeated_points_mut();
130        }
131    }
132}
133
134// Implementation for types that are not candidate for coordinates removal
135// (Point / Line / Triangle / Rect), where `remove_repeated_points` returns a clone of the geometry
136// and `remove_repeated_points_mut` is a no-op.
137macro_rules! impl_for_not_candidate_types {
138    ($type:ident) => {
139        impl<T> RemoveRepeatedPoints<T> for $type<T>
140        where
141            T: CoordNum + FromPrimitive,
142        {
143            fn remove_repeated_points(&self) -> Self {
144                self.clone()
145            }
146
147            fn remove_repeated_points_mut(&mut self) {
148                // no-op
149            }
150        }
151    };
152}
153
154impl_for_not_candidate_types!(Point);
155impl_for_not_candidate_types!(Rect);
156impl_for_not_candidate_types!(Triangle);
157impl_for_not_candidate_types!(Line);
158
159impl<T> RemoveRepeatedPoints<T> for GeometryCollection<T>
160where
161    T: CoordNum + FromPrimitive,
162{
163    /// Create a GeometryCollection with (consecutive) repeated points
164    /// of its geometries removed.
165    fn remove_repeated_points(&self) -> Self {
166        GeometryCollection::new_from(self.0.iter().map(|g| g.remove_repeated_points()).collect())
167    }
168
169    /// Remove (consecutive) repeated points of its geometries from a GeometryCollection inplace.
170    fn remove_repeated_points_mut(&mut self) {
171        for g in self.0.iter_mut() {
172            g.remove_repeated_points_mut();
173        }
174    }
175}
176
177impl<T> RemoveRepeatedPoints<T> for Geometry<T>
178where
179    T: CoordNum + FromPrimitive,
180{
181    // The following couldn't be used for implementing `remove_repeated_points` until
182    // "impl<T: CoordNum> From<GeometryCollection<T>> for Geometry<T>" is implemented
183    // (see geo-types/src/geometry/mod.rs, lines 101-106) so we implement it manually for now
184    //
185    //   crate::geometry_delegate_impl! {
186    //       fn remove_repeated_points(&self) -> Geometry<T>;
187    //   }
188
189    /// Create a Geometry with consecutive repeated points removed.
190    fn remove_repeated_points(&self) -> Self {
191        match self {
192            Geometry::Point(p) => Geometry::Point(p.remove_repeated_points()),
193            Geometry::Line(l) => Geometry::Line(l.remove_repeated_points()),
194            Geometry::LineString(ls) => Geometry::LineString(ls.remove_repeated_points()),
195            Geometry::Polygon(p) => Geometry::Polygon(p.remove_repeated_points()),
196            Geometry::MultiPoint(mp) => Geometry::MultiPoint(mp.remove_repeated_points()),
197            Geometry::MultiLineString(mls) => {
198                Geometry::MultiLineString(mls.remove_repeated_points())
199            }
200            Geometry::MultiPolygon(mp) => Geometry::MultiPolygon(mp.remove_repeated_points()),
201            Geometry::Rect(r) => Geometry::Rect(r.remove_repeated_points()),
202            Geometry::Triangle(t) => Geometry::Triangle(t.remove_repeated_points()),
203            Geometry::GeometryCollection(gc) => {
204                Geometry::GeometryCollection(gc.remove_repeated_points())
205            }
206        }
207    }
208
209    /// Remove consecutive repeated points from a Geometry inplace.
210    fn remove_repeated_points_mut(&mut self) {
211        match self {
212            Geometry::Point(p) => p.remove_repeated_points_mut(),
213            Geometry::Line(l) => l.remove_repeated_points_mut(),
214            Geometry::LineString(ls) => ls.remove_repeated_points_mut(),
215            Geometry::Polygon(p) => p.remove_repeated_points_mut(),
216            Geometry::MultiPoint(mp) => mp.remove_repeated_points_mut(),
217            Geometry::MultiLineString(mls) => mls.remove_repeated_points_mut(),
218            Geometry::MultiPolygon(mp) => mp.remove_repeated_points_mut(),
219            Geometry::Rect(r) => r.remove_repeated_points_mut(),
220            Geometry::Triangle(t) => t.remove_repeated_points_mut(),
221            Geometry::GeometryCollection(gc) => gc.remove_repeated_points_mut(),
222        }
223    }
224}
225
226#[cfg(test)]
227mod test {
228    use crate::RemoveRepeatedPoints;
229    use crate::{
230        Coord, GeometryCollection, LineString, MultiLineString, MultiPoint, MultiPolygon, Point,
231        Polygon,
232    };
233
234    fn make_test_mp_integer() -> MultiPoint<i32> {
235        MultiPoint(vec![
236            Point::new(0, 0),
237            Point::new(1, 1),
238            Point::new(1, 1),
239            Point::new(1, 1),
240            Point::new(2, 2),
241            Point::new(0, 0),
242        ])
243    }
244
245    fn make_result_mp_integer() -> MultiPoint<i32> {
246        MultiPoint(vec![Point::new(0, 0), Point::new(1, 1), Point::new(2, 2)])
247    }
248
249    fn make_test_mp1() -> MultiPoint {
250        MultiPoint(vec![
251            Point::new(0., 0.),
252            Point::new(1., 1.),
253            Point::new(1., 1.),
254            Point::new(1., 1.),
255            Point::new(2., 2.),
256            Point::new(0., 0.),
257        ])
258    }
259
260    fn make_result_mp1() -> MultiPoint {
261        MultiPoint(vec![
262            Point::new(0., 0.),
263            Point::new(1., 1.),
264            Point::new(2., 2.),
265        ])
266    }
267
268    fn make_test_line1() -> LineString {
269        LineString(vec![
270            Coord { x: 0., y: 0. },
271            Coord { x: 1., y: 1. },
272            Coord { x: 1., y: 1. },
273            Coord { x: 1., y: 1. },
274            Coord { x: 2., y: 2. },
275            Coord { x: 2., y: 2. },
276            Coord { x: 0., y: 0. },
277        ])
278    }
279
280    fn make_result_line1() -> LineString {
281        LineString(vec![
282            Coord { x: 0., y: 0. },
283            Coord { x: 1., y: 1. },
284            Coord { x: 2., y: 2. },
285            Coord { x: 0., y: 0. },
286        ])
287    }
288
289    fn make_test_line2() -> LineString {
290        LineString(vec![
291            Coord { x: 10., y: 10. },
292            Coord { x: 11., y: 11. },
293            Coord { x: 11., y: 11. },
294            Coord { x: 11., y: 11. },
295            Coord { x: 12., y: 12. },
296            Coord { x: 12., y: 12. },
297            Coord { x: 10., y: 10. },
298        ])
299    }
300
301    fn make_result_line2() -> LineString {
302        LineString(vec![
303            Coord { x: 10., y: 10. },
304            Coord { x: 11., y: 11. },
305            Coord { x: 12., y: 12. },
306            Coord { x: 10., y: 10. },
307        ])
308    }
309
310    fn make_test_poly1() -> Polygon {
311        Polygon::new(
312            LineString(vec![
313                Coord { x: 0., y: 0. },
314                Coord { x: 1., y: 1. },
315                Coord { x: 1., y: 1. },
316                Coord { x: 1., y: 1. },
317                Coord { x: 0., y: 2. },
318                Coord { x: 0., y: 2. },
319                Coord { x: 0., y: 0. },
320            ]),
321            vec![],
322        )
323    }
324
325    fn make_result_poly1() -> Polygon {
326        Polygon::new(
327            LineString(vec![
328                Coord { x: 0., y: 0. },
329                Coord { x: 1., y: 1. },
330                Coord { x: 0., y: 2. },
331                Coord { x: 0., y: 0. },
332            ]),
333            vec![],
334        )
335    }
336
337    fn make_test_poly2() -> Polygon {
338        Polygon::new(
339            LineString(vec![
340                Coord { x: 10., y: 10. },
341                Coord { x: 11., y: 11. },
342                Coord { x: 11., y: 11. },
343                Coord { x: 11., y: 11. },
344                Coord { x: 10., y: 12. },
345                Coord { x: 10., y: 12. },
346                Coord { x: 10., y: 10. },
347            ]),
348            vec![],
349        )
350    }
351
352    fn make_result_poly2() -> Polygon {
353        Polygon::new(
354            LineString(vec![
355                Coord { x: 10., y: 10. },
356                Coord { x: 11., y: 11. },
357                Coord { x: 10., y: 12. },
358                Coord { x: 10., y: 10. },
359            ]),
360            vec![],
361        )
362    }
363
364    #[test]
365    fn test_remove_repeated_points_multipoint_integer() {
366        let mp = make_test_mp_integer();
367        let expected = make_result_mp_integer();
368
369        assert_eq!(mp.remove_repeated_points(), expected);
370    }
371
372    #[test]
373    fn test_remove_repeated_points_multipoint() {
374        let mp = make_test_mp1();
375        let expected = make_result_mp1();
376
377        assert_eq!(mp.remove_repeated_points(), expected);
378    }
379
380    #[test]
381    fn test_remove_repeated_points_linestring() {
382        let ls = make_test_line1();
383        let expected = make_result_line1();
384
385        assert_eq!(ls.remove_repeated_points(), expected);
386    }
387
388    #[test]
389    fn test_remove_repeated_points_polygon() {
390        let poly = make_test_poly1();
391        let expected = make_result_poly1();
392
393        assert_eq!(poly.remove_repeated_points(), expected);
394    }
395
396    #[test]
397    fn test_remove_repeated_points_multilinestring() {
398        let mls = MultiLineString(vec![make_test_line1(), make_test_line2()]);
399
400        let expected = MultiLineString(vec![make_result_line1(), make_result_line2()]);
401
402        assert_eq!(mls.remove_repeated_points(), expected);
403    }
404
405    #[test]
406    fn test_remove_repeated_points_multipolygon() {
407        let mpoly = MultiPolygon(vec![make_test_poly1(), make_test_poly2()]);
408
409        let expected = MultiPolygon(vec![make_result_poly1(), make_result_poly2()]);
410
411        assert_eq!(mpoly.remove_repeated_points(), expected);
412    }
413
414    #[test]
415    fn test_remove_repeated_points_geometrycollection() {
416        let gc = GeometryCollection::new_from(vec![
417            make_test_mp1().into(),
418            make_test_line1().into(),
419            make_test_poly1().into(),
420        ]);
421
422        let expected = GeometryCollection::new_from(vec![
423            make_result_mp1().into(),
424            make_result_line1().into(),
425            make_result_poly1().into(),
426        ]);
427
428        assert_eq!(gc.remove_repeated_points(), expected);
429    }
430
431    #[test]
432    fn test_remove_repeated_points_mut_multipoint_integer() {
433        let mut mp = make_test_mp_integer();
434        mp.remove_repeated_points_mut();
435        let expected = make_result_mp_integer();
436
437        assert_eq!(mp, expected);
438    }
439
440    #[test]
441    fn test_remove_repeated_points_mut_multipoint() {
442        let mut mp = make_test_mp1();
443        mp.remove_repeated_points_mut();
444        let expected = make_result_mp1();
445
446        assert_eq!(mp, expected);
447    }
448
449    #[test]
450    fn test_remove_repeated_points_mut_linestring() {
451        let mut ls = make_test_line1();
452        ls.remove_repeated_points_mut();
453        let expected = make_result_line1();
454
455        assert_eq!(ls, expected);
456    }
457
458    #[test]
459    fn test_remove_repeated_points_mut_polygon() {
460        let mut poly = make_test_poly1();
461        poly.remove_repeated_points_mut();
462        let expected = make_result_poly1();
463
464        assert_eq!(poly, expected);
465    }
466
467    #[test]
468    fn test_remove_repeated_points_mut_multilinestring() {
469        let mut mls = MultiLineString(vec![make_test_line1(), make_test_line2()]);
470        mls.remove_repeated_points_mut();
471        let expected = MultiLineString(vec![make_result_line1(), make_result_line2()]);
472
473        assert_eq!(mls, expected);
474    }
475
476    #[test]
477    fn test_remove_repeated_points_mut_multipolygon() {
478        let mut mpoly = MultiPolygon(vec![make_test_poly1(), make_test_poly2()]);
479        mpoly.remove_repeated_points_mut();
480        let expected = MultiPolygon(vec![make_result_poly1(), make_result_poly2()]);
481
482        assert_eq!(mpoly, expected);
483    }
484
485    #[test]
486    fn test_remove_repeated_points_mut_geometrycollection() {
487        let mut gc = GeometryCollection::new_from(vec![
488            make_test_mp1().into(),
489            make_test_line1().into(),
490            make_test_poly1().into(),
491        ]);
492        gc.remove_repeated_points_mut();
493
494        let expected = GeometryCollection::new_from(vec![
495            make_result_mp1().into(),
496            make_result_line1().into(),
497            make_result_poly1().into(),
498        ]);
499
500        assert_eq!(gc, expected);
501    }
502}