gistools/geometry/tools/lines/
clean.rs1use crate::geometry::ClampWGS84Point;
2use alloc::{vec, vec::Vec};
3use libm::fabs;
4use s2json::{GetXY, SetXY};
5
6pub fn clean_linestrings<P: GetXY + SetXY + Clone + PartialEq>(
16 lines: &[Vec<P>],
17 is_poly: bool,
18 eps: Option<f64>,
19 clean_wgs84: bool,
20) -> Option<Vec<Vec<P>>> {
21 let res: Vec<Vec<P>> =
22 lines.iter().filter_map(|p| clean_linestring(p, is_poly, eps, clean_wgs84)).collect();
23 if res.is_empty() { None } else { Some(res) }
24}
25
26pub fn clean_linestring<P: GetXY + SetXY + Clone + PartialEq>(
36 line: &[P],
37 is_poly: bool,
38 eps: Option<f64>,
39 clean_wgs84: bool,
40) -> Option<Vec<P>> {
41 if if is_poly { line.len() < 4 } else { line.len() < 2 } {
42 return None;
43 }
44 let eps = eps.unwrap_or(1e-12);
45 let mut no_dups: Vec<&P> = vec![&line[0]];
47 for line in line.iter().skip(1) {
48 if line != no_dups[no_dups.len() - 1] {
49 no_dups.push(line);
50 }
51 }
52 let mut cleaned: Vec<P> = vec![no_dups[0].clone()];
54 for i in 1..no_dups.len() - 1 {
55 let prev = &no_dups[i - 1];
56 let curr = &no_dups[i];
57 let next = &no_dups[i + 1];
58 let area = (curr.y() - prev.y()) * (next.x() - curr.x())
59 - (curr.x() - prev.x()) * (next.y() - curr.y());
60 if fabs(area) > eps {
61 cleaned.push((*curr).clone());
62 }
63 }
64 cleaned.push(no_dups[no_dups.len() - 1].clone());
65 if if is_poly { cleaned.len() < 4 } else { cleaned.len() < 2 } {
67 return None;
68 }
69 if clean_wgs84 {
71 cleaned.iter_mut().for_each(|p| p.clamp_wgs84());
72 }
73
74 Some(cleaned)
75}