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 crate::vector::clipping::clip_polygons(
356 poly1,
357 poly2,
358 crate::vector::clipping::ClipOperation::Intersection,
359 )
360}
361
362pub fn point_in_polygon(point: &Coordinate, polygon: &Polygon) -> Result<bool> {
377 if polygon.exterior.coords.len() < 4 {
378 return Err(AlgorithmError::InsufficientData {
379 operation: "point_in_polygon",
380 message: "polygon exterior must have at least 4 coordinates".to_string(),
381 });
382 }
383
384 let inside = point_in_ring(point, &polygon.exterior.coords);
385
386 if !inside {
387 return Ok(false);
388 }
389
390 for hole in &polygon.interiors {
392 if point_in_ring(point, &hole.coords) {
393 return Ok(false);
394 }
395 }
396
397 Ok(true)
398}
399
400fn point_in_ring(point: &Coordinate, ring: &[Coordinate]) -> bool {
402 let mut inside = false;
403 let n = ring.len();
404
405 let mut j = n - 1;
406 for i in 0..n {
407 let xi = ring[i].x;
408 let yi = ring[i].y;
409 let xj = ring[j].x;
410 let yj = ring[j].y;
411
412 let intersect = ((yi > point.y) != (yj > point.y))
413 && (point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi);
414
415 if intersect {
416 inside = !inside;
417 }
418
419 j = i;
420 }
421
422 inside
423}
424
425#[allow(dead_code)]
433fn orientation(p1: &Coordinate, p2: &Coordinate, p3: &Coordinate) -> f64 {
434 (p2.y - p1.y) * (p3.x - p2.x) - (p2.x - p1.x) * (p3.y - p2.y)
435}
436
437#[allow(dead_code)]
439fn on_segment(p: &Coordinate, q: &Coordinate, r: &Coordinate) -> bool {
440 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)
441}
442
443pub fn intersect_polygons_pooled(
497 poly1: &Polygon,
498 poly2: &Polygon,
499) -> Result<PoolGuard<'static, Polygon>> {
500 let results = intersect_polygons(poly1, poly2)?;
501
502 if let Some(result) = results.first() {
504 let mut poly = get_pooled_polygon();
505 poly.exterior = result.exterior.clone();
506 poly.interiors = result.interiors.clone();
507 Ok(poly)
508 } else {
509 Err(AlgorithmError::InsufficientData {
510 operation: "intersect_polygons_pooled",
511 message: "intersection resulted in no polygons".to_string(),
512 })
513 }
514}
515
516#[cfg(test)]
517#[allow(clippy::panic)]
518mod tests {
519 use super::*;
520 use approx::assert_relative_eq;
521
522 #[test]
523 fn test_segment_intersection_point() {
524 let p1 = Coordinate::new_2d(0.0, 0.0);
525 let p2 = Coordinate::new_2d(10.0, 10.0);
526 let p3 = Coordinate::new_2d(0.0, 10.0);
527 let p4 = Coordinate::new_2d(10.0, 0.0);
528
529 let result = intersect_segment_segment(&p1, &p2, &p3, &p4);
530
531 match result {
532 SegmentIntersection::Point(pt) => {
533 assert_relative_eq!(pt.x, 5.0, epsilon = 1e-10);
534 assert_relative_eq!(pt.y, 5.0, epsilon = 1e-10);
535 }
536 _ => panic!("Expected point intersection"),
537 }
538 }
539
540 #[test]
541 fn test_segment_intersection_none() {
542 let p1 = Coordinate::new_2d(0.0, 0.0);
543 let p2 = Coordinate::new_2d(10.0, 0.0);
544 let p3 = Coordinate::new_2d(0.0, 5.0);
545 let p4 = Coordinate::new_2d(10.0, 5.0);
546
547 let result = intersect_segment_segment(&p1, &p2, &p3, &p4);
548
549 assert_eq!(result, SegmentIntersection::None);
550 }
551
552 #[test]
553 fn test_segment_intersection_collinear_overlap() {
554 let p1 = Coordinate::new_2d(0.0, 0.0);
555 let p2 = Coordinate::new_2d(10.0, 0.0);
556 let p3 = Coordinate::new_2d(5.0, 0.0);
557 let p4 = Coordinate::new_2d(15.0, 0.0);
558
559 let result = intersect_segment_segment(&p1, &p2, &p3, &p4);
560
561 match result {
562 SegmentIntersection::Overlap(c1, c2) => {
563 assert_relative_eq!(c1.x, 5.0, epsilon = 1e-10);
564 assert_relative_eq!(c1.y, 0.0, epsilon = 1e-10);
565 assert_relative_eq!(c2.x, 10.0, epsilon = 1e-10);
566 assert_relative_eq!(c2.y, 0.0, epsilon = 1e-10);
567 }
568 _ => panic!("Expected overlap intersection"),
569 }
570 }
571
572 #[test]
573 fn test_segment_intersection_endpoint() {
574 let p1 = Coordinate::new_2d(0.0, 0.0);
575 let p2 = Coordinate::new_2d(10.0, 0.0);
576 let p3 = Coordinate::new_2d(10.0, 0.0);
577 let p4 = Coordinate::new_2d(10.0, 10.0);
578
579 let result = intersect_segment_segment(&p1, &p2, &p3, &p4);
580
581 match result {
582 SegmentIntersection::Point(pt) => {
583 assert_relative_eq!(pt.x, 10.0, epsilon = 1e-10);
584 assert_relative_eq!(pt.y, 0.0, epsilon = 1e-10);
585 }
586 _ => panic!("Expected point intersection at endpoint"),
587 }
588 }
589
590 #[test]
591 fn test_intersect_linestrings_basic() {
592 let coords1 = vec![Coordinate::new_2d(0.0, 0.0), Coordinate::new_2d(10.0, 10.0)];
593 let line1 = LineString::new(coords1);
594 assert!(line1.is_ok());
595
596 let coords2 = vec![Coordinate::new_2d(0.0, 10.0), Coordinate::new_2d(10.0, 0.0)];
597 let line2 = LineString::new(coords2);
598 assert!(line2.is_ok());
599
600 if let (Ok(ls1), Ok(ls2)) = (line1, line2) {
601 let result = intersect_linestrings(&ls1, &ls2);
602 assert!(result.is_ok());
603
604 if let Ok(points) = result {
605 assert_eq!(points.len(), 1);
606 assert_relative_eq!(points[0].x, 5.0, epsilon = 1e-10);
607 assert_relative_eq!(points[0].y, 5.0, epsilon = 1e-10);
608 }
609 }
610 }
611
612 #[test]
613 fn test_intersect_linestrings_multiple() {
614 let coords1 = vec![Coordinate::new_2d(0.0, 5.0), Coordinate::new_2d(10.0, 5.0)];
615 let line1 = LineString::new(coords1);
616 assert!(line1.is_ok());
617
618 let coords2 = vec![
619 Coordinate::new_2d(2.0, 0.0),
620 Coordinate::new_2d(2.0, 10.0),
621 Coordinate::new_2d(8.0, 10.0),
622 Coordinate::new_2d(8.0, 0.0),
623 ];
624 let line2 = LineString::new(coords2);
625 assert!(line2.is_ok());
626
627 if let (Ok(ls1), Ok(ls2)) = (line1, line2) {
628 let result = intersect_linestrings(&ls1, &ls2);
629 assert!(result.is_ok());
630
631 if let Ok(points) = result {
632 assert_eq!(points.len(), 2);
634 }
635 }
636 }
637
638 #[test]
639 fn test_point_in_polygon_inside() {
640 let exterior_coords = vec![
641 Coordinate::new_2d(0.0, 0.0),
642 Coordinate::new_2d(10.0, 0.0),
643 Coordinate::new_2d(10.0, 10.0),
644 Coordinate::new_2d(0.0, 10.0),
645 Coordinate::new_2d(0.0, 0.0),
646 ];
647 let exterior = LineString::new(exterior_coords);
648 assert!(exterior.is_ok());
649
650 if let Ok(ext) = exterior {
651 let polygon = Polygon::new(ext, vec![]);
652 assert!(polygon.is_ok());
653
654 if let Ok(poly) = polygon {
655 let point = Coordinate::new_2d(5.0, 5.0);
656 let result = point_in_polygon(&point, &poly);
657 assert!(result.is_ok());
658 if let Ok(inside) = result {
659 assert!(inside);
660 }
661 }
662 }
663 }
664
665 #[test]
666 fn test_point_in_polygon_outside() {
667 let exterior_coords = vec![
668 Coordinate::new_2d(0.0, 0.0),
669 Coordinate::new_2d(10.0, 0.0),
670 Coordinate::new_2d(10.0, 10.0),
671 Coordinate::new_2d(0.0, 10.0),
672 Coordinate::new_2d(0.0, 0.0),
673 ];
674 let exterior = LineString::new(exterior_coords);
675 assert!(exterior.is_ok());
676
677 if let Ok(ext) = exterior {
678 let polygon = Polygon::new(ext, vec![]);
679 assert!(polygon.is_ok());
680
681 if let Ok(poly) = polygon {
682 let point = Coordinate::new_2d(15.0, 15.0);
683 let result = point_in_polygon(&point, &poly);
684 assert!(result.is_ok());
685 if let Ok(inside) = result {
686 assert!(!inside);
687 }
688 }
689 }
690 }
691
692 #[test]
693 fn test_intersect_polygons_disjoint() {
694 let coords1 = vec![
695 Coordinate::new_2d(0.0, 0.0),
696 Coordinate::new_2d(5.0, 0.0),
697 Coordinate::new_2d(5.0, 5.0),
698 Coordinate::new_2d(0.0, 5.0),
699 Coordinate::new_2d(0.0, 0.0),
700 ];
701 let ext1 = LineString::new(coords1);
702 assert!(ext1.is_ok());
703
704 let coords2 = vec![
705 Coordinate::new_2d(10.0, 10.0),
706 Coordinate::new_2d(15.0, 10.0),
707 Coordinate::new_2d(15.0, 15.0),
708 Coordinate::new_2d(10.0, 15.0),
709 Coordinate::new_2d(10.0, 10.0),
710 ];
711 let ext2 = LineString::new(coords2);
712 assert!(ext2.is_ok());
713
714 if let (Ok(e1), Ok(e2)) = (ext1, ext2) {
715 let poly1 = Polygon::new(e1, vec![]);
716 let poly2 = Polygon::new(e2, vec![]);
717 assert!(poly1.is_ok() && poly2.is_ok());
718
719 if let (Ok(p1), Ok(p2)) = (poly1, poly2) {
720 let result = intersect_polygons(&p1, &p2);
721 assert!(result.is_ok());
722 if let Ok(polys) = result {
723 assert_eq!(polys.len(), 0); }
725 }
726 }
727 }
728}