geometry_algorithm/
simplify.rs1use geometry_cs::{CartesianFamily, CoordinateSystem};
14use geometry_strategy::{DouglasPeucker, PointToSegment, Pythagoras, SimplifyStrategy};
15use geometry_tag::SameAs;
16use geometry_trait::{Linestring, Point, PointMut};
17
18#[must_use]
27pub fn simplify<G, P>(g: &G, max_distance: f64) -> geometry_model::Linestring<P>
28where
29 G: Linestring<Point = P>,
30 P: Point<Scalar = f64> + PointMut + Default + Copy,
31 <P::Cs as CoordinateSystem>::Family: SameAs<CartesianFamily>,
32{
33 let strategy = DouglasPeucker::<PointToSegment<Pythagoras>>::default();
34 strategy.simplify(g, max_distance)
35}
36
37#[inline]
44#[must_use]
45#[allow(
46 clippy::needless_pass_by_value,
47 reason = "simplify strategies are zero-sized or small Copy values, matching the crate's other _with entries"
48)]
49pub fn simplify_with<G, S>(g: &G, max_distance: f64, strategy: S) -> S::Output
50where
51 S: SimplifyStrategy<G>,
52{
53 strategy.simplify(g, max_distance)
54}
55
56#[cfg(test)]
57#[allow(
58 clippy::float_cmp,
59 reason = "Simplified coordinates are exact literals."
60)]
61mod tests {
62 use super::simplify;
68 use geometry_cs::Cartesian;
69 use geometry_model::{Linestring, Point2D, linestring};
70 use geometry_trait::Point as _;
71
72 type Pt = Point2D<f64, Cartesian>;
73
74 fn coords(ls: &Linestring<Pt>) -> alloc::vec::Vec<(f64, f64)> {
75 ls.0.iter().map(|p| (p.get::<0>(), p.get::<1>())).collect()
76 }
77
78 #[test]
79 fn straight_three_point_line_collapses() {
80 let ls: Linestring<Pt> = linestring![(0., 0.), (1., 0.), (2., 0.)];
81 let s = simplify(&ls, 0.5);
82 assert_eq!(coords(&s), alloc::vec![(0., 0.), (2., 0.)]);
83 }
84
85 #[test]
86 fn zigzag_with_tiny_deviation_collapses() {
87 let ls: Linestring<Pt> =
90 linestring![(0., 0.), (1., 0.05), (2., -0.05), (3., 0.05), (4., 0.)];
91 let s = simplify(&ls, 0.5);
92 assert_eq!(coords(&s), alloc::vec![(0., 0.), (4., 0.)]);
93 }
94
95 #[test]
96 fn spike_above_tolerance_survives() {
97 let ls: Linestring<Pt> = linestring![(0., 0.), (1., 0.01), (2., 5.), (3., 0.01), (4., 0.)];
98 let s = simplify(&ls, 0.5);
99 let c = coords(&s);
100 assert_eq!(c.first().unwrap(), &(0., 0.));
101 assert_eq!(c.last().unwrap(), &(4., 0.));
102 assert!(c.contains(&(2., 5.)));
103 }
104}