1use crate::GeoFloat;
2use crate::algorithm::{CoordsIter, Distance, Euclidean};
3use crate::geometry::{Coord, Line, LineString, MultiLineString, MultiPolygon, Polygon};
4
5const LINE_STRING_INITIAL_MIN: usize = 2;
6const POLYGON_INITIAL_MIN: usize = 4;
7
8#[derive(Copy, Clone)]
12struct RdpIndex<T>
13where
14 T: GeoFloat,
15{
16 index: usize,
17 coord: Coord<T>,
18}
19
20fn rdp<T, I: Iterator<Item = Coord<T>>, const INITIAL_MIN: usize>(
22 coords: I,
23 epsilon: T,
24) -> Vec<Coord<T>>
25where
26 T: GeoFloat,
27{
28 if epsilon <= T::zero() {
30 return coords.collect::<Vec<Coord<T>>>();
31 }
32 let rdp_indices = &coords
33 .enumerate()
34 .map(|(idx, coord)| RdpIndex { index: idx, coord })
35 .collect::<Vec<RdpIndex<T>>>();
36 let mut simplified_len = rdp_indices.len();
37 let simplified_coords: Vec<_> =
38 compute_rdp::<T, INITIAL_MIN>(rdp_indices, &mut simplified_len, epsilon)
39 .into_iter()
40 .map(|rdpindex| rdpindex.coord)
41 .collect();
42 debug_assert_eq!(simplified_coords.len(), simplified_len);
43 simplified_coords
44}
45
46fn calculate_rdp_indices<T, const INITIAL_MIN: usize>(
48 rdp_indices: &[RdpIndex<T>],
49 epsilon: T,
50) -> Vec<usize>
51where
52 T: GeoFloat,
53{
54 if epsilon <= T::zero() {
55 return rdp_indices
56 .iter()
57 .map(|rdp_index| rdp_index.index)
58 .collect();
59 }
60
61 let mut simplified_len = rdp_indices.len();
62 let simplified_coords =
63 compute_rdp::<T, INITIAL_MIN>(rdp_indices, &mut simplified_len, epsilon)
64 .into_iter()
65 .map(|rdpindex| rdpindex.index)
66 .collect::<Vec<usize>>();
67 debug_assert_eq!(simplified_len, simplified_coords.len());
68 simplified_coords
69}
70
71fn compute_rdp<T, const INITIAL_MIN: usize>(
75 rdp_indices: &[RdpIndex<T>],
76 simplified_len: &mut usize,
77 epsilon: T,
78) -> Vec<RdpIndex<T>>
79where
80 T: GeoFloat,
81{
82 let (first, last) = match rdp_indices {
83 [] => return vec![],
84 &[only] => return vec![only],
85 &[first, last] => return vec![first, last],
86 &[first, .., last] => (first, last),
87 };
88
89 let first_last_line = Line::new(first.coord, last.coord);
90
91 let (farthest_index, farthest_distance) = rdp_indices
93 .iter()
94 .enumerate()
95 .take(rdp_indices.len() - 1) .skip(1) .map(|(index, rdp_index)| (index, Euclidean.distance(rdp_index.coord, &first_last_line)))
98 .fold(
99 (0usize, T::zero()),
100 |(farthest_index, farthest_distance), (index, distance)| {
101 if distance >= farthest_distance {
102 (index, distance)
103 } else {
104 (farthest_index, farthest_distance)
105 }
106 },
107 );
108 debug_assert_ne!(farthest_index, 0);
109
110 if farthest_distance > epsilon {
111 let mut intermediate =
114 compute_rdp::<T, INITIAL_MIN>(&rdp_indices[..=farthest_index], simplified_len, epsilon);
115
116 intermediate.pop(); intermediate.extend_from_slice(&compute_rdp::<T, INITIAL_MIN>(
119 &rdp_indices[farthest_index..],
120 simplified_len,
121 epsilon,
122 ));
123 return intermediate;
124 }
125
126 let number_culled = rdp_indices.len() - 2;
132 let new_length = *simplified_len - number_culled;
133
134 if new_length < INITIAL_MIN {
137 return rdp_indices.to_owned();
138 }
139 *simplified_len = new_length;
140
141 vec![first, last]
143}
144
145pub trait Simplify<T, Epsilon = T> {
162 fn simplify(&self, epsilon: T) -> Self
190 where
191 T: GeoFloat;
192}
193
194pub trait SimplifyIdx<T, Epsilon = T> {
207 fn simplify_idx(&self, epsilon: T) -> Vec<usize>
235 where
236 T: GeoFloat;
237}
238
239impl<T> Simplify<T> for LineString<T>
240where
241 T: GeoFloat,
242{
243 fn simplify(&self, epsilon: T) -> Self {
244 LineString::from(rdp::<_, _, LINE_STRING_INITIAL_MIN>(
245 self.coords_iter(),
246 epsilon,
247 ))
248 }
249}
250
251impl<T> SimplifyIdx<T> for LineString<T>
252where
253 T: GeoFloat,
254{
255 fn simplify_idx(&self, epsilon: T) -> Vec<usize> {
256 calculate_rdp_indices::<_, LINE_STRING_INITIAL_MIN>(
257 &self
258 .0
259 .iter()
260 .enumerate()
261 .map(|(idx, coord)| RdpIndex {
262 index: idx,
263 coord: *coord,
264 })
265 .collect::<Vec<RdpIndex<T>>>(),
266 epsilon,
267 )
268 }
269}
270
271impl<T> Simplify<T> for MultiLineString<T>
272where
273 T: GeoFloat,
274{
275 fn simplify(&self, epsilon: T) -> Self {
276 MultiLineString::new(self.iter().map(|l| l.simplify(epsilon)).collect())
277 }
278}
279
280impl<T> Simplify<T> for Polygon<T>
281where
282 T: GeoFloat,
283{
284 fn simplify(&self, epsilon: T) -> Self {
285 Polygon::new(
286 LineString::from(rdp::<_, _, POLYGON_INITIAL_MIN>(
287 self.exterior().coords_iter(),
288 epsilon,
289 )),
290 self.interiors()
291 .iter()
292 .map(|l| {
293 LineString::from(rdp::<_, _, POLYGON_INITIAL_MIN>(l.coords_iter(), epsilon))
294 })
295 .collect(),
296 )
297 }
298}
299
300impl<T> Simplify<T> for MultiPolygon<T>
301where
302 T: GeoFloat,
303{
304 fn simplify(&self, epsilon: T) -> Self {
305 MultiPolygon::new(self.iter().map(|p| p.simplify(epsilon)).collect())
306 }
307}
308
309#[cfg(test)]
310mod test {
311 use super::*;
312 use crate::{coord, line_string, polygon};
313
314 #[test]
315 fn recursion_test() {
316 let input = [
317 coord! { x: 8.0, y: 100.0 },
318 coord! { x: 9.0, y: 100.0 },
319 coord! { x: 12.0, y: 100.0 },
320 ];
321 let actual = rdp::<_, _, 2>(input.into_iter(), 1.0);
322 let expected = [coord! { x: 8.0, y: 100.0 }, coord! { x: 12.0, y: 100.0 }];
323 assert_eq!(actual, expected);
324 }
325
326 #[test]
327 fn rdp_test() {
328 let vec = vec![
329 coord! { x: 0.0, y: 0.0 },
330 coord! { x: 5.0, y: 4.0 },
331 coord! { x: 11.0, y: 5.5 },
332 coord! { x: 17.3, y: 3.2 },
333 coord! { x: 27.8, y: 0.1 },
334 ];
335 let compare = vec![
336 coord! { x: 0.0, y: 0.0 },
337 coord! { x: 5.0, y: 4.0 },
338 coord! { x: 11.0, y: 5.5 },
339 coord! { x: 27.8, y: 0.1 },
340 ];
341 let simplified = rdp::<_, _, 2>(vec.into_iter(), 1.0);
342 assert_eq!(simplified, compare);
343 }
344 #[test]
345 fn rdp_test_empty_linestring() {
346 let vec = Vec::new();
347 let compare = Vec::new();
348 let simplified = rdp::<_, _, 2>(vec.into_iter(), 1.0);
349 assert_eq!(simplified, compare);
350 }
351
352 #[test]
353 fn rdp_test_one_point_linestring() {
354 let vec = vec![coord! { x: 27.8, y: 0.1 }];
355 let compare = vec![coord! { x: 27.8, y: 0.1 }];
356 let simplified = rdp::<_, _, 2>(vec.into_iter(), 1.0);
357 assert_eq!(simplified, compare);
358 }
359
360 #[test]
361 fn rdp_test_two_point_linestring() {
362 let vec = vec![coord! { x: 0.0, y: 0.0 }, coord! { x: 27.8, y: 0.1 }];
363 let compare = vec![coord! { x: 0.0, y: 0.0 }, coord! { x: 27.8, y: 0.1 }];
364 let simplified = rdp::<_, _, 2>(vec.into_iter(), 1.0);
365 assert_eq!(simplified, compare);
366 }
367
368 #[test]
369 fn multilinestring() {
370 let mline = MultiLineString::new(vec![LineString::from(vec![
371 (0.0, 0.0),
372 (5.0, 4.0),
373 (11.0, 5.5),
374 (17.3, 3.2),
375 (27.8, 0.1),
376 ])]);
377
378 let mline2 = mline.simplify(1.0);
379
380 assert_eq!(
381 mline2,
382 MultiLineString::new(vec![LineString::from(vec![
383 (0.0, 0.0),
384 (5.0, 4.0),
385 (11.0, 5.5),
386 (27.8, 0.1),
387 ])])
388 );
389 }
390
391 #[test]
392 fn polygon() {
393 let poly = polygon![
394 (x: 0., y: 0.),
395 (x: 0., y: 10.),
396 (x: 5., y: 11.),
397 (x: 10., y: 10.),
398 (x: 10., y: 0.),
399 (x: 0., y: 0.),
400 ];
401
402 let poly2 = poly.simplify(2.);
403
404 assert_eq!(
405 poly2,
406 polygon![
407 (x: 0., y: 0.),
408 (x: 0., y: 10.),
409 (x: 10., y: 10.),
410 (x: 10., y: 0.),
411 (x: 0., y: 0.),
412 ],
413 );
414 }
415
416 #[test]
417 fn multipolygon() {
418 let mpoly = MultiPolygon::new(vec![polygon![
419 (x: 0., y: 0.),
420 (x: 0., y: 10.),
421 (x: 5., y: 11.),
422 (x: 10., y: 10.),
423 (x: 10., y: 0.),
424 (x: 0., y: 0.),
425 ]]);
426
427 let mpoly2 = mpoly.simplify(2.);
428
429 assert_eq!(
430 mpoly2,
431 MultiPolygon::new(vec![polygon![
432 (x: 0., y: 0.),
433 (x: 0., y: 10.),
434 (x: 10., y: 10.),
435 (x: 10., y: 0.),
436 (x: 0., y: 0.)
437 ]]),
438 );
439 }
440
441 #[test]
442 fn simplify_negative_epsilon() {
443 let ls = line_string![
444 (x: 0., y: 0.),
445 (x: 0., y: 10.),
446 (x: 5., y: 11.),
447 (x: 10., y: 10.),
448 (x: 10., y: 0.),
449 ];
450 let simplified = ls.simplify(-1.0);
451 assert_eq!(ls, simplified);
452 }
453
454 #[test]
455 fn simplify_idx_negative_epsilon() {
456 let ls = line_string![
457 (x: 0., y: 0.),
458 (x: 0., y: 10.),
459 (x: 5., y: 11.),
460 (x: 10., y: 10.),
461 (x: 10., y: 0.),
462 ];
463 let indices = ls.simplify_idx(-1.0);
464 assert_eq!(vec![0usize, 1, 2, 3, 4], indices);
465 }
466
467 #[test]
469 fn simplify_line_string_polygon_initial_min() {
470 let ls = line_string![
471 ( x: 1.4324054e-16, y: 1.4324054e-16 ),
472 ( x: 1.4324054e-16, y: 1.4324054e-16 ),
473 ( x: -5.9730447e26, y: 1.5590374e-27 ),
474 ( x: 1.4324054e-16, y: 1.4324054e-16 ),
475 ];
476 let epsilon: f64 = 3.46e-43;
477
478 let result = ls.simplify(epsilon);
480 assert_eq!(
481 line_string![
482 ( x: 1.4324054e-16, y: 1.4324054e-16 ),
483 ( x: -5.9730447e26, y: 1.5590374e-27 ),
484 ( x: 1.4324054e-16, y: 1.4324054e-16 ),
485 ],
486 result
487 );
488
489 let result = Polygon::new(ls, vec![]).simplify(epsilon);
491 assert_eq!(
492 polygon![
493 ( x: 1.4324054e-16, y: 1.4324054e-16 ),
494 ( x: 1.4324054e-16, y: 1.4324054e-16 ),
495 ( x: -5.9730447e26, y: 1.5590374e-27 ),
496 ( x: 1.4324054e-16, y: 1.4324054e-16 ),
497 ],
498 result,
499 );
500 }
501
502 #[test]
504 fn dont_oversimplify() {
505 let unsimplified = line_string![
506 (x: 0.0, y: 0.0),
507 (x: 5.0, y: 4.0),
508 (x: 11.0, y: 5.5),
509 (x: 17.3, y: 3.2),
510 (x: 27.8, y: 0.1)
511 ];
512 let actual = unsimplified.simplify(30.0);
513 let expected = line_string![
514 (x: 0.0, y: 0.0),
515 (x: 27.8, y: 0.1)
516 ];
517 assert_eq!(actual, expected);
518 }
519}