1use crate::error::{AlgorithmError, Result};
27use crate::vector::pool::{PoolGuard, get_pooled_polygon};
28use oxigdal_core::vector::{Coordinate, LineString, Polygon};
29
30#[cfg(feature = "std")]
31use std::vec::Vec;
32
33#[derive(Debug, Clone, PartialEq)]
35pub enum SegmentIntersection {
36 None,
38 Point(Coordinate),
40 Overlap(Coordinate, Coordinate),
42}
43
44pub fn intersect_segment_segment(
62 p1: &Coordinate,
63 p2: &Coordinate,
64 p3: &Coordinate,
65 p4: &Coordinate,
66) -> SegmentIntersection {
67 let d1x = p2.x - p1.x;
69 let d1y = p2.y - p1.y;
70
71 let d2x = p4.x - p3.x;
73 let d2y = p4.y - p3.y;
74
75 let cross = d1x * d2y - d1y * d2x;
77
78 let dx = p3.x - p1.x;
80 let dy = p3.y - p1.y;
81
82 if cross.abs() < f64::EPSILON {
83 let cross_start = dx * d1y - dy * d1x;
85 if cross_start.abs() < f64::EPSILON {
86 check_collinear_overlap(p1, p2, p3, p4)
88 } else {
89 SegmentIntersection::None
91 }
92 } else {
93 let t = (dx * d2y - dy * d2x) / cross;
95 let u = (dx * d1y - dy * d1x) / cross;
96
97 if (0.0..=1.0).contains(&t) && (0.0..=1.0).contains(&u) {
99 let x = p1.x + t * d1x;
100 let y = p1.y + t * d1y;
101 SegmentIntersection::Point(Coordinate::new_2d(x, y))
102 } else {
103 SegmentIntersection::None
104 }
105 }
106}
107
108fn check_collinear_overlap(
110 p1: &Coordinate,
111 p2: &Coordinate,
112 p3: &Coordinate,
113 p4: &Coordinate,
114) -> SegmentIntersection {
115 let dx = p2.x - p1.x;
117 let dy = p2.y - p1.y;
118 let len_sq = dx * dx + dy * dy;
119
120 if len_sq < f64::EPSILON {
121 return SegmentIntersection::None;
123 }
124
125 let t3 = ((p3.x - p1.x) * dx + (p3.y - p1.y) * dy) / len_sq;
127 let t4 = ((p4.x - p1.x) * dx + (p4.y - p1.y) * dy) / len_sq;
128
129 let (t_min, t_max) = if t3 <= t4 { (t3, t4) } else { (t4, t3) };
131
132 let overlap_min = t_min.max(0.0);
134 let overlap_max = t_max.min(1.0);
135
136 if overlap_min <= overlap_max {
137 let c1 = Coordinate::new_2d(p1.x + overlap_min * dx, p1.y + overlap_min * dy);
139 let c2 = Coordinate::new_2d(p1.x + overlap_max * dx, p1.y + overlap_max * dy);
140
141 if (overlap_max - overlap_min).abs() < f64::EPSILON {
142 SegmentIntersection::Point(c1)
144 } else {
145 SegmentIntersection::Overlap(c1, c2)
147 }
148 } else {
149 SegmentIntersection::None
150 }
151}
152
153pub fn intersect_linestrings(line1: &LineString, line2: &LineString) -> Result<Vec<Coordinate>> {
171 if line1.coords.len() < 2 {
172 return Err(AlgorithmError::InsufficientData {
173 operation: "intersect_linestrings",
174 message: "line1 must have at least 2 coordinates".to_string(),
175 });
176 }
177
178 if line2.coords.len() < 2 {
179 return Err(AlgorithmError::InsufficientData {
180 operation: "intersect_linestrings",
181 message: "line2 must have at least 2 coordinates".to_string(),
182 });
183 }
184
185 let mut intersections = Vec::new();
186
187 for i in 0..(line1.coords.len() - 1) {
189 for j in 0..(line2.coords.len() - 1) {
190 let p1 = &line1.coords[i];
191 let p2 = &line1.coords[i + 1];
192 let p3 = &line2.coords[j];
193 let p4 = &line2.coords[j + 1];
194
195 match intersect_segment_segment(p1, p2, p3, p4) {
196 SegmentIntersection::Point(pt) => {
197 if !intersections.iter().any(|c: &Coordinate| {
199 (c.x - pt.x).abs() < f64::EPSILON && (c.y - pt.y).abs() < f64::EPSILON
200 }) {
201 intersections.push(pt);
202 }
203 }
204 SegmentIntersection::Overlap(c1, c2) => {
205 if !intersections.iter().any(|c: &Coordinate| {
207 (c.x - c1.x).abs() < f64::EPSILON && (c.y - c1.y).abs() < f64::EPSILON
208 }) {
209 intersections.push(c1);
210 }
211 if !intersections.iter().any(|c: &Coordinate| {
212 (c.x - c2.x).abs() < f64::EPSILON && (c.y - c2.y).abs() < f64::EPSILON
213 }) {
214 intersections.push(c2);
215 }
216 }
217 SegmentIntersection::None => {}
218 }
219 }
220 }
221
222 Ok(intersections)
223}
224
225#[derive(Debug, Clone, Copy, PartialEq, Eq)]
227enum EventType {
228 Start,
230 End,
232 Intersection,
234}
235
236#[derive(Debug, Clone)]
238struct SweepEvent {
239 coord: Coordinate,
241 event_type: EventType,
243 segment_idx: Option<usize>,
245}
246
247pub fn intersect_linestrings_sweep(
265 line1: &LineString,
266 line2: &LineString,
267) -> Result<Vec<Coordinate>> {
268 if line1.coords.len() < 2 {
269 return Err(AlgorithmError::InsufficientData {
270 operation: "intersect_linestrings_sweep",
271 message: "line1 must have at least 2 coordinates".to_string(),
272 });
273 }
274
275 if line2.coords.len() < 2 {
276 return Err(AlgorithmError::InsufficientData {
277 operation: "intersect_linestrings_sweep",
278 message: "line2 must have at least 2 coordinates".to_string(),
279 });
280 }
281
282 intersect_linestrings(line1, line2)
285}
286
287pub fn intersect_polygons(poly1: &Polygon, poly2: &Polygon) -> Result<Vec<Polygon>> {
305 if poly1.exterior.coords.len() < 4 {
307 return Err(AlgorithmError::InsufficientData {
308 operation: "intersect_polygons",
309 message: "poly1 exterior must have at least 4 coordinates".to_string(),
310 });
311 }
312
313 if poly2.exterior.coords.len() < 4 {
314 return Err(AlgorithmError::InsufficientData {
315 operation: "intersect_polygons",
316 message: "poly2 exterior must have at least 4 coordinates".to_string(),
317 });
318 }
319
320 let bounds1 = poly1.bounds();
322 let bounds2 = poly2.bounds();
323
324 if let (Some((min_x1, min_y1, max_x1, max_y1)), Some((min_x2, min_y2, max_x2, max_y2))) =
325 (bounds1, bounds2)
326 {
327 if max_x1 < min_x2 || max_x2 < min_x1 || max_y1 < min_y2 || max_y2 < min_y1 {
329 return Ok(vec![]);
331 }
332 }
333
334 let intersection_points = intersect_linestrings(&poly1.exterior, &poly2.exterior)?;
336
337 if intersection_points.is_empty() {
338 if point_in_polygon(&poly1.exterior.coords[0], poly2)? {
341 return Ok(vec![poly1.clone()]);
343 }
344
345 if point_in_polygon(&poly2.exterior.coords[0], poly1)? {
346 return Ok(vec![poly2.clone()]);
348 }
349
350 return Ok(vec![]);
352 }
353
354 Ok(vec![])
358}
359
360pub fn point_in_polygon(point: &Coordinate, polygon: &Polygon) -> Result<bool> {
375 if polygon.exterior.coords.len() < 4 {
376 return Err(AlgorithmError::InsufficientData {
377 operation: "point_in_polygon",
378 message: "polygon exterior must have at least 4 coordinates".to_string(),
379 });
380 }
381
382 let inside = point_in_ring(point, &polygon.exterior.coords);
383
384 if !inside {
385 return Ok(false);
386 }
387
388 for hole in &polygon.interiors {
390 if point_in_ring(point, &hole.coords) {
391 return Ok(false);
392 }
393 }
394
395 Ok(true)
396}
397
398fn point_in_ring(point: &Coordinate, ring: &[Coordinate]) -> bool {
400 let mut inside = false;
401 let n = ring.len();
402
403 let mut j = n - 1;
404 for i in 0..n {
405 let xi = ring[i].x;
406 let yi = ring[i].y;
407 let xj = ring[j].x;
408 let yj = ring[j].y;
409
410 let intersect = ((yi > point.y) != (yj > point.y))
411 && (point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi);
412
413 if intersect {
414 inside = !inside;
415 }
416
417 j = i;
418 }
419
420 inside
421}
422
423#[allow(dead_code)]
431fn orientation(p1: &Coordinate, p2: &Coordinate, p3: &Coordinate) -> f64 {
432 (p2.y - p1.y) * (p3.x - p2.x) - (p2.x - p1.x) * (p3.y - p2.y)
433}
434
435#[allow(dead_code)]
437fn on_segment(p: &Coordinate, q: &Coordinate, r: &Coordinate) -> bool {
438 q.x <= p.x.max(r.x) && q.x >= p.x.min(r.x) && q.y <= p.y.max(r.y) && q.y >= p.y.min(r.y)
439}
440
441pub fn intersect_polygons_pooled(
495 poly1: &Polygon,
496 poly2: &Polygon,
497) -> Result<PoolGuard<'static, Polygon>> {
498 let results = intersect_polygons(poly1, poly2)?;
499
500 if let Some(result) = results.first() {
502 let mut poly = get_pooled_polygon();
503 poly.exterior = result.exterior.clone();
504 poly.interiors = result.interiors.clone();
505 Ok(poly)
506 } else {
507 Err(AlgorithmError::InsufficientData {
508 operation: "intersect_polygons_pooled",
509 message: "intersection resulted in no polygons".to_string(),
510 })
511 }
512}
513
514#[cfg(test)]
515#[allow(clippy::panic)]
516mod tests {
517 use super::*;
518 use approx::assert_relative_eq;
519
520 #[test]
521 fn test_segment_intersection_point() {
522 let p1 = Coordinate::new_2d(0.0, 0.0);
523 let p2 = Coordinate::new_2d(10.0, 10.0);
524 let p3 = Coordinate::new_2d(0.0, 10.0);
525 let p4 = Coordinate::new_2d(10.0, 0.0);
526
527 let result = intersect_segment_segment(&p1, &p2, &p3, &p4);
528
529 match result {
530 SegmentIntersection::Point(pt) => {
531 assert_relative_eq!(pt.x, 5.0, epsilon = 1e-10);
532 assert_relative_eq!(pt.y, 5.0, epsilon = 1e-10);
533 }
534 _ => panic!("Expected point intersection"),
535 }
536 }
537
538 #[test]
539 fn test_segment_intersection_none() {
540 let p1 = Coordinate::new_2d(0.0, 0.0);
541 let p2 = Coordinate::new_2d(10.0, 0.0);
542 let p3 = Coordinate::new_2d(0.0, 5.0);
543 let p4 = Coordinate::new_2d(10.0, 5.0);
544
545 let result = intersect_segment_segment(&p1, &p2, &p3, &p4);
546
547 assert_eq!(result, SegmentIntersection::None);
548 }
549
550 #[test]
551 fn test_segment_intersection_collinear_overlap() {
552 let p1 = Coordinate::new_2d(0.0, 0.0);
553 let p2 = Coordinate::new_2d(10.0, 0.0);
554 let p3 = Coordinate::new_2d(5.0, 0.0);
555 let p4 = Coordinate::new_2d(15.0, 0.0);
556
557 let result = intersect_segment_segment(&p1, &p2, &p3, &p4);
558
559 match result {
560 SegmentIntersection::Overlap(c1, c2) => {
561 assert_relative_eq!(c1.x, 5.0, epsilon = 1e-10);
562 assert_relative_eq!(c1.y, 0.0, epsilon = 1e-10);
563 assert_relative_eq!(c2.x, 10.0, epsilon = 1e-10);
564 assert_relative_eq!(c2.y, 0.0, epsilon = 1e-10);
565 }
566 _ => panic!("Expected overlap intersection"),
567 }
568 }
569
570 #[test]
571 fn test_segment_intersection_endpoint() {
572 let p1 = Coordinate::new_2d(0.0, 0.0);
573 let p2 = Coordinate::new_2d(10.0, 0.0);
574 let p3 = Coordinate::new_2d(10.0, 0.0);
575 let p4 = Coordinate::new_2d(10.0, 10.0);
576
577 let result = intersect_segment_segment(&p1, &p2, &p3, &p4);
578
579 match result {
580 SegmentIntersection::Point(pt) => {
581 assert_relative_eq!(pt.x, 10.0, epsilon = 1e-10);
582 assert_relative_eq!(pt.y, 0.0, epsilon = 1e-10);
583 }
584 _ => panic!("Expected point intersection at endpoint"),
585 }
586 }
587
588 #[test]
589 fn test_intersect_linestrings_basic() {
590 let coords1 = vec![Coordinate::new_2d(0.0, 0.0), Coordinate::new_2d(10.0, 10.0)];
591 let line1 = LineString::new(coords1);
592 assert!(line1.is_ok());
593
594 let coords2 = vec![Coordinate::new_2d(0.0, 10.0), Coordinate::new_2d(10.0, 0.0)];
595 let line2 = LineString::new(coords2);
596 assert!(line2.is_ok());
597
598 if let (Ok(ls1), Ok(ls2)) = (line1, line2) {
599 let result = intersect_linestrings(&ls1, &ls2);
600 assert!(result.is_ok());
601
602 if let Ok(points) = result {
603 assert_eq!(points.len(), 1);
604 assert_relative_eq!(points[0].x, 5.0, epsilon = 1e-10);
605 assert_relative_eq!(points[0].y, 5.0, epsilon = 1e-10);
606 }
607 }
608 }
609
610 #[test]
611 fn test_intersect_linestrings_multiple() {
612 let coords1 = vec![Coordinate::new_2d(0.0, 5.0), Coordinate::new_2d(10.0, 5.0)];
613 let line1 = LineString::new(coords1);
614 assert!(line1.is_ok());
615
616 let coords2 = vec![
617 Coordinate::new_2d(2.0, 0.0),
618 Coordinate::new_2d(2.0, 10.0),
619 Coordinate::new_2d(8.0, 10.0),
620 Coordinate::new_2d(8.0, 0.0),
621 ];
622 let line2 = LineString::new(coords2);
623 assert!(line2.is_ok());
624
625 if let (Ok(ls1), Ok(ls2)) = (line1, line2) {
626 let result = intersect_linestrings(&ls1, &ls2);
627 assert!(result.is_ok());
628
629 if let Ok(points) = result {
630 assert_eq!(points.len(), 2);
632 }
633 }
634 }
635
636 #[test]
637 fn test_point_in_polygon_inside() {
638 let exterior_coords = vec![
639 Coordinate::new_2d(0.0, 0.0),
640 Coordinate::new_2d(10.0, 0.0),
641 Coordinate::new_2d(10.0, 10.0),
642 Coordinate::new_2d(0.0, 10.0),
643 Coordinate::new_2d(0.0, 0.0),
644 ];
645 let exterior = LineString::new(exterior_coords);
646 assert!(exterior.is_ok());
647
648 if let Ok(ext) = exterior {
649 let polygon = Polygon::new(ext, vec![]);
650 assert!(polygon.is_ok());
651
652 if let Ok(poly) = polygon {
653 let point = Coordinate::new_2d(5.0, 5.0);
654 let result = point_in_polygon(&point, &poly);
655 assert!(result.is_ok());
656 if let Ok(inside) = result {
657 assert!(inside);
658 }
659 }
660 }
661 }
662
663 #[test]
664 fn test_point_in_polygon_outside() {
665 let exterior_coords = vec![
666 Coordinate::new_2d(0.0, 0.0),
667 Coordinate::new_2d(10.0, 0.0),
668 Coordinate::new_2d(10.0, 10.0),
669 Coordinate::new_2d(0.0, 10.0),
670 Coordinate::new_2d(0.0, 0.0),
671 ];
672 let exterior = LineString::new(exterior_coords);
673 assert!(exterior.is_ok());
674
675 if let Ok(ext) = exterior {
676 let polygon = Polygon::new(ext, vec![]);
677 assert!(polygon.is_ok());
678
679 if let Ok(poly) = polygon {
680 let point = Coordinate::new_2d(15.0, 15.0);
681 let result = point_in_polygon(&point, &poly);
682 assert!(result.is_ok());
683 if let Ok(inside) = result {
684 assert!(!inside);
685 }
686 }
687 }
688 }
689
690 #[test]
691 fn test_intersect_polygons_disjoint() {
692 let coords1 = vec![
693 Coordinate::new_2d(0.0, 0.0),
694 Coordinate::new_2d(5.0, 0.0),
695 Coordinate::new_2d(5.0, 5.0),
696 Coordinate::new_2d(0.0, 5.0),
697 Coordinate::new_2d(0.0, 0.0),
698 ];
699 let ext1 = LineString::new(coords1);
700 assert!(ext1.is_ok());
701
702 let coords2 = vec![
703 Coordinate::new_2d(10.0, 10.0),
704 Coordinate::new_2d(15.0, 10.0),
705 Coordinate::new_2d(15.0, 15.0),
706 Coordinate::new_2d(10.0, 15.0),
707 Coordinate::new_2d(10.0, 10.0),
708 ];
709 let ext2 = LineString::new(coords2);
710 assert!(ext2.is_ok());
711
712 if let (Ok(e1), Ok(e2)) = (ext1, ext2) {
713 let poly1 = Polygon::new(e1, vec![]);
714 let poly2 = Polygon::new(e2, vec![]);
715 assert!(poly1.is_ok() && poly2.is_ok());
716
717 if let (Ok(p1), Ok(p2)) = (poly1, poly2) {
718 let result = intersect_polygons(&p1, &p2);
719 assert!(result.is_ok());
720 if let Ok(polys) = result {
721 assert_eq!(polys.len(), 0); }
723 }
724 }
725 }
726}