1use crate::error::{AlgorithmError, Result};
35use crate::vector::intersection::{intersect_linestrings, point_in_polygon};
36use crate::vector::pool::{PoolGuard, get_pooled_polygon};
37use oxigdal_core::vector::{Coordinate, Polygon};
38
39#[cfg(feature = "std")]
40use std::vec::Vec;
41
42pub fn union_polygon(poly1: &Polygon, poly2: &Polygon) -> Result<Vec<Polygon>> {
62 if poly1.exterior.coords.len() < 4 {
64 return Err(AlgorithmError::InsufficientData {
65 operation: "union_polygon",
66 message: "poly1 exterior must have at least 4 coordinates".to_string(),
67 });
68 }
69
70 if poly2.exterior.coords.len() < 4 {
71 return Err(AlgorithmError::InsufficientData {
72 operation: "union_polygon",
73 message: "poly2 exterior must have at least 4 coordinates".to_string(),
74 });
75 }
76
77 let bounds1 = poly1.bounds();
79 let bounds2 = poly2.bounds();
80
81 if let (Some((min_x1, min_y1, max_x1, max_y1)), Some((min_x2, min_y2, max_x2, max_y2))) =
82 (bounds1, bounds2)
83 {
84 if max_x1 < min_x2 || max_x2 < min_x1 || max_y1 < min_y2 || max_y2 < min_y1 {
86 return Ok(vec![poly1.clone(), poly2.clone()]);
88 }
89 }
90
91 let intersection_points = intersect_linestrings(&poly1.exterior, &poly2.exterior)?;
93
94 if intersection_points.is_empty() {
95 if point_in_polygon(&poly1.exterior.coords[0], poly2)? {
98 return Ok(vec![poly2.clone()]);
100 }
101
102 if point_in_polygon(&poly2.exterior.coords[0], poly1)? {
103 return Ok(vec![poly1.clone()]);
105 }
106
107 return Ok(vec![poly1.clone(), poly2.clone()]);
109 }
110
111 let area1 = compute_polygon_area(poly1);
117 let area2 = compute_polygon_area(poly2);
118
119 if area1 >= area2 {
120 Ok(vec![poly1.clone()])
121 } else {
122 Ok(vec![poly2.clone()])
123 }
124}
125
126pub fn union_polygons(polygons: &[Polygon]) -> Result<Vec<Polygon>> {
140 cascaded_union(polygons)
141}
142
143pub fn cascaded_union(polygons: &[Polygon]) -> Result<Vec<Polygon>> {
161 if polygons.is_empty() {
162 return Ok(vec![]);
163 }
164
165 if polygons.len() == 1 {
166 return Ok(vec![polygons[0].clone()]);
167 }
168
169 for (i, poly) in polygons.iter().enumerate() {
171 if poly.exterior.coords.len() < 4 {
172 return Err(AlgorithmError::InsufficientData {
173 operation: "cascaded_union",
174 message: format!("polygon {} exterior must have at least 4 coordinates", i),
175 });
176 }
177 }
178
179 let mut current = polygons.to_vec();
183 let max_iterations = (polygons.len() as f64).log2().ceil() as usize + 1;
184 let mut iteration = 0;
185
186 while current.len() > 1 && iteration < max_iterations {
187 let mut next_level = Vec::new();
188 let initial_count = current.len();
189
190 let mut i = 0;
192 while i < current.len() {
193 if i + 1 < current.len() {
194 let union_result = union_polygon(¤t[i], ¤t[i + 1])?;
196 next_level.extend(union_result);
197 i += 2;
198 } else {
199 next_level.push(current[i].clone());
201 i += 1;
202 }
203 }
204
205 current = next_level;
206 iteration += 1;
207
208 if current.len() >= initial_count {
210 break;
211 }
212 }
213
214 Ok(current)
215}
216
217pub fn merge_polygons(polygons: &[Polygon], tolerance: f64) -> Result<Vec<Polygon>> {
235 if polygons.is_empty() {
236 return Ok(vec![]);
237 }
238
239 if tolerance < 0.0 {
240 return Err(AlgorithmError::InvalidParameter {
241 parameter: "tolerance",
242 message: "tolerance must be non-negative".to_string(),
243 });
244 }
245
246 let mut merged = Vec::new();
248 let mut used = vec![false; polygons.len()];
249
250 for i in 0..polygons.len() {
251 if used[i] {
252 continue;
253 }
254
255 let mut group = vec![polygons[i].clone()];
256 used[i] = true;
257
258 for j in (i + 1)..polygons.len() {
260 if used[j] {
261 continue;
262 }
263
264 let mut is_close = false;
266 for poly in &group {
267 if polygons_are_close(poly, &polygons[j], tolerance) {
268 is_close = true;
269 break;
270 }
271 }
272
273 if is_close {
274 group.push(polygons[j].clone());
275 used[j] = true;
276 }
277 }
278
279 let union_result = cascaded_union(&group)?;
281 merged.extend(union_result);
282 }
283
284 Ok(merged)
285}
286
287fn polygons_are_close(poly1: &Polygon, poly2: &Polygon, tolerance: f64) -> bool {
289 if let (Some(b1), Some(b2)) = (poly1.bounds(), poly2.bounds()) {
291 let (min_x1, min_y1, max_x1, max_y1) = b1;
292 let (min_x2, min_y2, max_x2, max_y2) = b2;
293
294 let x_gap = if max_x1 < min_x2 {
296 min_x2 - max_x1
297 } else if max_x2 < min_x1 {
298 min_x1 - max_x2
299 } else {
300 0.0
301 };
302
303 let y_gap = if max_y1 < min_y2 {
304 min_y2 - max_y1
305 } else if max_y2 < min_y1 {
306 min_y1 - max_y2
307 } else {
308 0.0
309 };
310
311 x_gap <= tolerance && y_gap <= tolerance
312 } else {
313 false
314 }
315}
316
317fn compute_polygon_area(polygon: &Polygon) -> f64 {
319 let mut area = ring_area(&polygon.exterior.coords);
320
321 for hole in &polygon.interiors {
323 area -= ring_area(&hole.coords);
324 }
325
326 area.abs()
327}
328
329fn ring_area(coords: &[Coordinate]) -> f64 {
331 if coords.len() < 3 {
332 return 0.0;
333 }
334
335 let mut area = 0.0;
336 let n = coords.len();
337
338 for i in 0..n {
339 let j = (i + 1) % n;
340 area += coords[i].x * coords[j].y;
341 area -= coords[j].x * coords[i].y;
342 }
343
344 area / 2.0
345}
346
347pub fn convex_hull(points: &[Coordinate]) -> Result<Vec<Coordinate>> {
363 if points.len() < 3 {
364 return Err(AlgorithmError::InsufficientData {
365 operation: "convex_hull",
366 message: "need at least 3 points for convex hull".to_string(),
367 });
368 }
369
370 let mut lowest_idx = 0;
372 for i in 1..points.len() {
373 if points[i].y < points[lowest_idx].y
374 || (points[i].y == points[lowest_idx].y && points[i].x < points[lowest_idx].x)
375 {
376 lowest_idx = i;
377 }
378 }
379
380 let pivot = points[lowest_idx];
381 let mut sorted_points: Vec<(usize, Coordinate, f64)> = points
382 .iter()
383 .enumerate()
384 .filter(|(i, _)| *i != lowest_idx)
385 .map(|(i, p)| {
386 let angle = (p.y - pivot.y).atan2(p.x - pivot.x);
387 (i, *p, angle)
388 })
389 .collect();
390
391 sorted_points.sort_by(|a, b| a.2.partial_cmp(&b.2).unwrap_or(std::cmp::Ordering::Equal));
393
394 let mut hull = vec![pivot];
395
396 for (_, point, _) in sorted_points {
397 while hull.len() >= 2 {
399 let n = hull.len();
400 let cross = cross_product(&hull[n - 2], &hull[n - 1], &point);
401 if cross <= 0.0 {
402 hull.pop();
403 } else {
404 break;
405 }
406 }
407 hull.push(point);
408 }
409
410 Ok(hull)
411}
412
413fn cross_product(o: &Coordinate, a: &Coordinate, b: &Coordinate) -> f64 {
415 (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x)
416}
417
418pub fn union_polygon_pooled(
468 poly1: &Polygon,
469 poly2: &Polygon,
470) -> Result<PoolGuard<'static, Polygon>> {
471 let results = union_polygon(poly1, poly2)?;
472
473 if let Some(result) = results.first() {
475 let mut poly = get_pooled_polygon();
476 poly.exterior = result.exterior.clone();
477 poly.interiors = result.interiors.clone();
478 Ok(poly)
479 } else {
480 Err(AlgorithmError::InsufficientData {
481 operation: "union_polygon_pooled",
482 message: "union resulted in no polygons".to_string(),
483 })
484 }
485}
486
487pub fn union_polygons_pooled(polygons: &[Polygon]) -> Result<PoolGuard<'static, Polygon>> {
523 let results = union_polygons(polygons)?;
524
525 if let Some(result) = results.first() {
527 let mut poly = get_pooled_polygon();
528 poly.exterior = result.exterior.clone();
529 poly.interiors = result.interiors.clone();
530 Ok(poly)
531 } else {
532 Err(AlgorithmError::InsufficientData {
533 operation: "union_polygons_pooled",
534 message: "union resulted in no polygons".to_string(),
535 })
536 }
537}
538
539pub fn cascaded_union_pooled(polygons: &[Polygon]) -> Result<PoolGuard<'static, Polygon>> {
544 union_polygons_pooled(polygons)
545}
546
547pub fn convex_hull_pooled(points: &[Coordinate]) -> Result<PoolGuard<'static, Polygon>> {
579 use oxigdal_core::vector::LineString;
580
581 let hull_coords = convex_hull(points)?;
582
583 let mut poly = get_pooled_polygon();
585 poly.exterior.coords.clear();
586 poly.exterior.coords.extend(hull_coords);
587
588 if let (Some(&first), Some(&last)) = (poly.exterior.coords.first(), poly.exterior.coords.last())
590 {
591 if first.x != last.x || first.y != last.y {
592 poly.exterior.coords.push(first);
593 }
594 }
595
596 while poly.exterior.len() < 4 {
598 if let Some(&first) = poly.exterior.coords.first() {
599 poly.exterior.coords.push(first);
600 }
601 }
602
603 Ok(poly)
604}
605
606#[cfg(test)]
607mod tests {
608 use super::*;
609 use oxigdal_core::vector::LineString;
610
611 fn create_square(x: f64, y: f64, size: f64) -> Result<Polygon> {
612 let coords = vec![
613 Coordinate::new_2d(x, y),
614 Coordinate::new_2d(x + size, y),
615 Coordinate::new_2d(x + size, y + size),
616 Coordinate::new_2d(x, y + size),
617 Coordinate::new_2d(x, y),
618 ];
619 let exterior = LineString::new(coords).map_err(|e| AlgorithmError::Core(e))?;
620 Polygon::new(exterior, vec![]).map_err(|e| AlgorithmError::Core(e))
621 }
622
623 #[test]
624 fn test_union_polygon_disjoint() {
625 let poly1 = create_square(0.0, 0.0, 5.0);
626 let poly2 = create_square(10.0, 10.0, 5.0);
627
628 assert!(poly1.is_ok() && poly2.is_ok());
629 if let (Ok(p1), Ok(p2)) = (poly1, poly2) {
630 let result = union_polygon(&p1, &p2);
631 assert!(result.is_ok());
632 if let Ok(polys) = result {
633 assert_eq!(polys.len(), 2); }
635 }
636 }
637
638 #[test]
639 fn test_union_polygon_contained() {
640 let poly1 = create_square(0.0, 0.0, 10.0);
641 let poly2 = create_square(2.0, 2.0, 3.0);
642
643 assert!(poly1.is_ok() && poly2.is_ok());
644 if let (Ok(p1), Ok(p2)) = (poly1, poly2) {
645 let result = union_polygon(&p1, &p2);
646 assert!(result.is_ok());
647 if let Ok(polys) = result {
648 assert_eq!(polys.len(), 1); }
650 }
651 }
652
653 #[test]
654 fn test_union_polygon_overlapping() {
655 let poly1 = create_square(0.0, 0.0, 5.0);
656 let poly2 = create_square(3.0, 0.0, 5.0);
657
658 assert!(poly1.is_ok() && poly2.is_ok());
659 if let (Ok(p1), Ok(p2)) = (poly1, poly2) {
660 let result = union_polygon(&p1, &p2);
661 assert!(result.is_ok());
662 if let Ok(polys) = result {
664 assert!(!polys.is_empty());
665 }
666 }
667 }
668
669 #[test]
670 fn test_cascaded_union_empty() {
671 let result = cascaded_union(&[]);
672 assert!(result.is_ok());
673 if let Ok(polys) = result {
674 assert!(polys.is_empty());
675 }
676 }
677
678 #[test]
679 fn test_cascaded_union_single() {
680 let poly = create_square(0.0, 0.0, 5.0);
681 assert!(poly.is_ok());
682
683 if let Ok(p) = poly {
684 let result = cascaded_union(std::slice::from_ref(&p));
685 assert!(result.is_ok());
686 if let Ok(polys) = result {
687 assert_eq!(polys.len(), 1);
688 }
689 }
690 }
691
692 #[test]
693 fn test_cascaded_union_multiple() {
694 let poly1 = create_square(0.0, 0.0, 5.0);
695 let poly2 = create_square(10.0, 0.0, 5.0);
696 let poly3 = create_square(20.0, 0.0, 5.0);
697
698 assert!(poly1.is_ok() && poly2.is_ok() && poly3.is_ok());
699 if let (Ok(p1), Ok(p2), Ok(p3)) = (poly1, poly2, poly3) {
700 let result = cascaded_union(&[p1, p2, p3]);
701 assert!(result.is_ok());
702 if let Ok(polys) = result {
704 assert!(!polys.is_empty());
705 }
706 }
707 }
708
709 #[test]
710 fn test_merge_polygons() {
711 let poly1 = create_square(0.0, 0.0, 5.0);
712 let poly2 = create_square(10.0, 0.0, 5.0);
713
714 assert!(poly1.is_ok() && poly2.is_ok());
715 if let (Ok(p1), Ok(p2)) = (poly1, poly2) {
716 let result = merge_polygons(&[p1, p2], 1.0);
717 assert!(result.is_ok());
718 }
719 }
720
721 #[test]
722 fn test_compute_polygon_area() {
723 let poly = create_square(0.0, 0.0, 10.0);
724 assert!(poly.is_ok());
725
726 if let Ok(p) = poly {
727 let area = compute_polygon_area(&p);
728 assert!((area - 100.0).abs() < 1e-10);
729 }
730 }
731
732 #[test]
733 fn test_ring_area() {
734 let coords = vec![
735 Coordinate::new_2d(0.0, 0.0),
736 Coordinate::new_2d(10.0, 0.0),
737 Coordinate::new_2d(10.0, 10.0),
738 Coordinate::new_2d(0.0, 10.0),
739 Coordinate::new_2d(0.0, 0.0),
740 ];
741 let area = ring_area(&coords);
742 assert!((area.abs() - 100.0).abs() < 1e-10);
743 }
744
745 #[test]
746 fn test_convex_hull_triangle() {
747 let points = vec![
748 Coordinate::new_2d(0.0, 0.0),
749 Coordinate::new_2d(4.0, 0.0),
750 Coordinate::new_2d(2.0, 3.0),
751 ];
752 let result = convex_hull(&points);
753 assert!(result.is_ok());
754 if let Ok(hull) = result {
755 assert_eq!(hull.len(), 3);
756 }
757 }
758
759 #[test]
760 fn test_convex_hull_square_with_interior() {
761 let points = vec![
762 Coordinate::new_2d(0.0, 0.0),
763 Coordinate::new_2d(4.0, 0.0),
764 Coordinate::new_2d(4.0, 4.0),
765 Coordinate::new_2d(0.0, 4.0),
766 Coordinate::new_2d(2.0, 2.0), ];
768 let result = convex_hull(&points);
769 assert!(result.is_ok());
770 if let Ok(hull) = result {
771 assert_eq!(hull.len(), 4); }
773 }
774
775 #[test]
776 fn test_convex_hull_insufficient_points() {
777 let points = vec![Coordinate::new_2d(0.0, 0.0), Coordinate::new_2d(1.0, 1.0)];
778 let result = convex_hull(&points);
779 assert!(result.is_err());
780 }
781
782 #[test]
783 fn test_polygons_are_close() {
784 let poly1 = create_square(0.0, 0.0, 5.0);
785 let poly2 = create_square(5.5, 0.0, 5.0);
786
787 assert!(poly1.is_ok() && poly2.is_ok());
788 if let (Ok(p1), Ok(p2)) = (poly1, poly2) {
789 assert!(polygons_are_close(&p1, &p2, 1.0)); assert!(!polygons_are_close(&p1, &p2, 0.1)); }
792 }
793
794 #[test]
795 fn test_cascaded_union_many_disjoint_no_infinite_loop() {
796 let mut polygons = Vec::new();
800
801 for i in 0..10 {
803 let x = (i as f64) * 20.0; if let Ok(poly) = create_square(x, 0.0, 5.0) {
805 polygons.push(poly);
806 }
807 }
808
809 let start = std::time::Instant::now();
810 let result = cascaded_union(&polygons);
811 let elapsed = start.elapsed();
812
813 assert!(
815 elapsed.as_secs() < 1,
816 "cascaded_union took too long: {elapsed:?}"
817 );
818 assert!(result.is_ok());
819
820 if let Ok(results) = result {
821 assert_eq!(results.len(), 10);
823 }
824 }
825}