Skip to main content

oxigdal_algorithms/vector/
contains.rs

1//! Spatial predicates for geometric relationships
2//!
3//! This module provides binary spatial predicates that test topological
4//! relationships between geometries following the DE-9IM model.
5//!
6//! # Predicates
7//!
8//! - **Contains**: Tests if geometry A completely contains geometry B
9//! - **Within**: Tests if geometry A is completely within geometry B
10//! - **Intersects**: Tests if geometries share any points
11//! - **Touches**: Tests if geometries share boundary points but not interior points
12//! - **Disjoint**: Tests if geometries share no points
13//! - **Overlaps**: Tests if geometries share some but not all points
14//! - **Covers**: Tests if every point of B is a point of A
15//! - **CoveredBy**: Tests if every point of A is a point of B
16//!
17//! # Examples
18//!
19//! ```
20//! use oxigdal_algorithms::vector::{Polygon, LineString, Coordinate, point_in_polygon_or_boundary};
21//!
22//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
23//! let coords = vec![
24//!     Coordinate::new_2d(0.0, 0.0),
25//!     Coordinate::new_2d(4.0, 0.0),
26//!     Coordinate::new_2d(4.0, 4.0),
27//!     Coordinate::new_2d(0.0, 4.0),
28//!     Coordinate::new_2d(0.0, 0.0),
29//! ];
30//! let exterior = LineString::new(coords)?;
31//! let polygon = Polygon::new(exterior, vec![])?;
32//! let point = Coordinate::new_2d(2.0, 2.0);
33//! let result = point_in_polygon_or_boundary(&point, &polygon);
34//! // result should be true
35//! # Ok(())
36//! # }
37//! ```
38
39use crate::error::Result;
40use oxigdal_core::vector::{Coordinate, Point, Polygon};
41
42/// Tests if geometry A contains geometry B
43///
44/// Geometry A contains B if:
45/// - No points of B lie in the exterior of A
46/// - At least one point of the interior of B lies in the interior of A
47///
48/// # Arguments
49///
50/// * `a` - Container geometry
51/// * `b` - Contained geometry
52///
53/// # Returns
54///
55/// True if A contains B
56///
57/// # Errors
58///
59/// Returns error if geometries are invalid
60pub fn contains<T: ContainsPredicate>(a: &T, b: &T) -> Result<bool> {
61    a.contains(b)
62}
63
64/// Tests if geometry A is within geometry B (inverse of contains)
65///
66/// # Arguments
67///
68/// * `a` - Inner geometry
69/// * `b` - Outer geometry
70///
71/// # Returns
72///
73/// True if A is within B
74///
75/// # Errors
76///
77/// Returns error if geometries are invalid
78pub fn within<T: ContainsPredicate>(a: &T, b: &T) -> Result<bool> {
79    b.contains(a)
80}
81
82/// Tests if geometries intersect (share any points)
83///
84/// # Arguments
85///
86/// * `a` - First geometry
87/// * `b` - Second geometry
88///
89/// # Returns
90///
91/// True if geometries intersect
92///
93/// # Errors
94///
95/// Returns error if geometries are invalid
96pub fn intersects<T: IntersectsPredicate>(a: &T, b: &T) -> Result<bool> {
97    a.intersects(b)
98}
99
100/// Tests if geometries are disjoint (share no points)
101///
102/// # Arguments
103///
104/// * `a` - First geometry
105/// * `b` - Second geometry
106///
107/// # Returns
108///
109/// True if geometries are disjoint
110///
111/// # Errors
112///
113/// Returns error if geometries are invalid
114pub fn disjoint<T: IntersectsPredicate>(a: &T, b: &T) -> Result<bool> {
115    Ok(!a.intersects(b)?)
116}
117
118/// Tests if geometries touch (share boundary but not interior)
119///
120/// # Arguments
121///
122/// * `a` - First geometry
123/// * `b` - Second geometry
124///
125/// # Returns
126///
127/// True if geometries touch
128///
129/// # Errors
130///
131/// Returns error if geometries are invalid
132pub fn touches<T: TouchesPredicate>(a: &T, b: &T) -> Result<bool> {
133    a.touches(b)
134}
135
136/// Tests if geometries overlap (share some but not all points)
137///
138/// Two geometries overlap if:
139/// - They have the same dimension
140/// - Their interiors intersect
141/// - Neither geometry completely contains the other
142///
143/// # Arguments
144///
145/// * `a` - First geometry
146/// * `b` - Second geometry
147///
148/// # Returns
149///
150/// True if geometries overlap
151///
152/// # Errors
153///
154/// Returns error if geometries are invalid
155pub fn overlaps<T: OverlapsPredicate>(a: &T, b: &T) -> Result<bool> {
156    a.overlaps(b)
157}
158
159/// Tests if one geometry crosses another
160///
161/// Two geometries cross if:
162/// - They have some but not all interior points in common
163/// - The dimension of the intersection is less than the maximum dimension of the two geometries
164///
165/// # Arguments
166///
167/// * `a` - First geometry
168/// * `b` - Second geometry
169///
170/// # Returns
171///
172/// True if geometries cross
173///
174/// # Errors
175///
176/// Returns error if geometries are invalid
177pub fn crosses<T: CrossesPredicate>(a: &T, b: &T) -> Result<bool> {
178    a.crosses(b)
179}
180
181/// Trait for geometries that support contains predicate
182pub trait ContainsPredicate {
183    /// Tests if this geometry contains another
184    fn contains(&self, other: &Self) -> Result<bool>;
185}
186
187/// Trait for geometries that support intersects predicate
188pub trait IntersectsPredicate {
189    /// Tests if this geometry intersects another
190    fn intersects(&self, other: &Self) -> Result<bool>;
191}
192
193/// Trait for geometries that support touches predicate
194pub trait TouchesPredicate {
195    /// Tests if this geometry touches another
196    fn touches(&self, other: &Self) -> Result<bool>;
197}
198
199/// Trait for geometries that support overlaps predicate
200pub trait OverlapsPredicate {
201    /// Tests if this geometry overlaps another
202    fn overlaps(&self, other: &Self) -> Result<bool>;
203}
204
205/// Trait for geometries that support crosses predicate
206pub trait CrossesPredicate {
207    /// Tests if this geometry crosses another
208    fn crosses(&self, other: &Self) -> Result<bool>;
209}
210
211// Implement ContainsPredicate for Point
212impl ContainsPredicate for Point {
213    fn contains(&self, other: &Self) -> Result<bool> {
214        // A point contains another point only if they're the same
215        Ok((self.coord.x - other.coord.x).abs() < f64::EPSILON
216            && (self.coord.y - other.coord.y).abs() < f64::EPSILON)
217    }
218}
219
220// Implement ContainsPredicate for Polygon
221impl ContainsPredicate for Polygon {
222    fn contains(&self, other: &Self) -> Result<bool> {
223        // Check if all vertices of other are inside or on boundary of self
224        for coord in &other.exterior.coords {
225            if !point_in_polygon_or_boundary(coord, self) {
226                return Ok(false);
227            }
228        }
229
230        // Check if any vertex is strictly inside (not just on boundary)
231        let mut has_interior_point = false;
232        for coord in &other.exterior.coords {
233            if point_strictly_inside_polygon(coord, self) {
234                has_interior_point = true;
235                break;
236            }
237        }
238
239        Ok(has_interior_point)
240    }
241}
242
243// Implement IntersectsPredicate for Point
244impl IntersectsPredicate for Point {
245    fn intersects(&self, other: &Self) -> Result<bool> {
246        self.contains(other)
247    }
248}
249
250// Implement IntersectsPredicate for Polygon
251impl IntersectsPredicate for Polygon {
252    fn intersects(&self, other: &Self) -> Result<bool> {
253        // Check if any vertices are inside
254        for coord in &other.exterior.coords {
255            if point_in_polygon_or_boundary(coord, self) {
256                return Ok(true);
257            }
258        }
259
260        for coord in &self.exterior.coords {
261            if point_in_polygon_or_boundary(coord, other) {
262                return Ok(true);
263            }
264        }
265
266        // Check if any edges intersect
267        Ok(rings_intersect(
268            &self.exterior.coords,
269            &other.exterior.coords,
270        ))
271    }
272}
273
274// Implement TouchesPredicate for Polygon
275impl TouchesPredicate for Polygon {
276    fn touches(&self, other: &Self) -> Result<bool> {
277        let mut has_boundary_contact = false;
278        let mut has_interior_contact = false;
279
280        // Check vertices of other against self
281        for coord in &other.exterior.coords {
282            if point_on_polygon_boundary(coord, self) {
283                has_boundary_contact = true;
284            } else if point_strictly_inside_polygon(coord, self) {
285                has_interior_contact = true;
286            }
287        }
288
289        // Check vertices of self against other
290        for coord in &self.exterior.coords {
291            if point_strictly_inside_polygon(coord, other) {
292                has_interior_contact = true;
293            }
294        }
295
296        Ok(has_boundary_contact && !has_interior_contact)
297    }
298}
299
300// Implement OverlapsPredicate for Point
301impl OverlapsPredicate for Point {
302    fn overlaps(&self, _other: &Self) -> Result<bool> {
303        // Points cannot overlap - they are either the same (equal) or disjoint
304        Ok(false)
305    }
306}
307
308// Implement OverlapsPredicate for Polygon
309impl OverlapsPredicate for Polygon {
310    fn overlaps(&self, other: &Self) -> Result<bool> {
311        // Two polygons overlap if:
312        // 1. They intersect
313        // 2. Neither completely contains the other
314        // 3. They have interior points in common
315
316        // First check if they intersect at all
317        if !self.intersects(other)? {
318            return Ok(false);
319        }
320
321        // Check if either polygon completely contains the other
322        if self.contains(other)? || other.contains(self)? {
323            return Ok(false);
324        }
325
326        // Check if they have interior points in common
327        // If they intersect and neither contains the other, they must overlap
328        Ok(true)
329    }
330}
331
332// Implement CrossesPredicate for Point
333impl CrossesPredicate for Point {
334    fn crosses(&self, _other: &Self) -> Result<bool> {
335        // Points cannot cross each other
336        Ok(false)
337    }
338}
339
340// Implement CrossesPredicate for Polygon
341impl CrossesPredicate for Polygon {
342    fn crosses(&self, other: &Self) -> Result<bool> {
343        // For polygons to cross, they must have interior points in common
344        // and exterior points in common. This is similar to overlaps but
345        // typically applies more to lower dimensional geometries crossing
346        // higher dimensional ones (e.g., line crossing polygon).
347        // For polygon-polygon, we define crossing as:
348        // - They intersect
349        // - Neither completely contains the other
350        // - They share boundary segments (edges cross)
351
352        if !self.intersects(other)? {
353            return Ok(false);
354        }
355
356        // If one completely contains the other, they don't cross
357        if self.contains(other)? || other.contains(self)? {
358            return Ok(false);
359        }
360
361        // Check if some but not all vertices of other are inside self
362        let mut other_some_inside = false;
363        let mut other_some_outside = false;
364
365        for coord in &other.exterior.coords {
366            if point_in_polygon_or_boundary(coord, self) {
367                other_some_inside = true;
368            } else {
369                other_some_outside = true;
370            }
371        }
372
373        // Also check if some but not all vertices of self are inside other
374        let mut self_some_inside = false;
375        let mut self_some_outside = false;
376
377        for coord in &self.exterior.coords {
378            if point_in_polygon_or_boundary(coord, other) {
379                self_some_inside = true;
380            } else {
381                self_some_outside = true;
382            }
383        }
384
385        // Crosses if:
386        // 1. Either direction shows partial containment, OR
387        // 2. They intersect but no vertices are contained (edge-only intersection)
388        if (other_some_inside && other_some_outside) || (self_some_inside && self_some_outside) {
389            Ok(true)
390        } else if !other_some_inside && !self_some_inside {
391            // No vertices inside either polygon, but they intersect
392            // This means edges must be crossing
393            Ok(true)
394        } else {
395            Ok(false)
396        }
397    }
398}
399
400/// Tests if a point is inside or on the boundary of a polygon
401pub fn point_in_polygon_or_boundary(point: &Coordinate, polygon: &Polygon) -> bool {
402    point_in_polygon_boundary(point, polygon) || point_on_polygon_boundary(point, polygon)
403}
404
405/// Tests if a point is strictly inside a polygon (not on boundary)
406pub fn point_strictly_inside_polygon(point: &Coordinate, polygon: &Polygon) -> bool {
407    point_in_polygon_boundary(point, polygon) && !point_on_polygon_boundary(point, polygon)
408}
409
410/// Tests if a point is on the boundary of a polygon
411pub fn point_on_polygon_boundary(point: &Coordinate, polygon: &Polygon) -> bool {
412    point_on_ring(&polygon.exterior.coords, point)
413        || polygon
414            .interiors
415            .iter()
416            .any(|hole| point_on_ring(&hole.coords, point))
417}
418
419/// Tests if a point is on a ring (linestring)
420fn point_on_ring(ring: &[Coordinate], point: &Coordinate) -> bool {
421    for i in 0..ring.len().saturating_sub(1) {
422        if point_on_segment(point, &ring[i], &ring[i + 1]) {
423            return true;
424        }
425    }
426    false
427}
428
429/// Tests if a point lies on a line segment
430fn point_on_segment(point: &Coordinate, seg_start: &Coordinate, seg_end: &Coordinate) -> bool {
431    // Check if point is collinear with segment
432    let cross = (seg_end.y - seg_start.y) * (point.x - seg_start.x)
433        - (seg_end.x - seg_start.x) * (point.y - seg_start.y);
434
435    if cross.abs() > f64::EPSILON {
436        return false;
437    }
438
439    // Check if point is within segment bounds
440    let dot = (point.x - seg_start.x) * (seg_end.x - seg_start.x)
441        + (point.y - seg_start.y) * (seg_end.y - seg_start.y);
442
443    let len_sq = (seg_end.x - seg_start.x).powi(2) + (seg_end.y - seg_start.y).powi(2);
444
445    if dot < -f64::EPSILON || dot > len_sq + f64::EPSILON {
446        return false;
447    }
448
449    true
450}
451
452/// Ray casting algorithm for point-in-polygon test
453fn point_in_polygon_boundary(point: &Coordinate, polygon: &Polygon) -> bool {
454    let mut inside = ray_casting_test(point, &polygon.exterior.coords);
455
456    // Subtract holes using XOR
457    for hole in &polygon.interiors {
458        if ray_casting_test(point, &hole.coords) {
459            inside = !inside;
460        }
461    }
462
463    inside
464}
465
466/// Ray casting algorithm implementation
467fn ray_casting_test(point: &Coordinate, ring: &[Coordinate]) -> bool {
468    let mut inside = false;
469    let n = ring.len();
470
471    let mut j = n - 1;
472    for i in 0..n {
473        let xi = ring[i].x;
474        let yi = ring[i].y;
475        let xj = ring[j].x;
476        let yj = ring[j].y;
477
478        let intersect = ((yi > point.y) != (yj > point.y))
479            && (point.x < (xj - xi) * (point.y - yi) / (yj - yi) + xi);
480
481        if intersect {
482            inside = !inside;
483        }
484
485        j = i;
486    }
487
488    inside
489}
490
491/// Winding number algorithm for point-in-polygon test (alternative to ray casting)
492///
493/// More robust than ray casting for edge cases.
494#[allow(dead_code)]
495fn winding_number_test(point: &Coordinate, ring: &[Coordinate]) -> bool {
496    let mut winding_number = 0;
497    let n = ring.len();
498
499    for i in 0..n - 1 {
500        let p1 = &ring[i];
501        let p2 = &ring[i + 1];
502
503        if p1.y <= point.y {
504            if p2.y > point.y {
505                // Upward crossing
506                if is_left(p1, p2, point) > 0.0 {
507                    winding_number += 1;
508                }
509            }
510        } else if p2.y <= point.y {
511            // Downward crossing
512            if is_left(p1, p2, point) < 0.0 {
513                winding_number -= 1;
514            }
515        }
516    }
517
518    winding_number != 0
519}
520
521/// Tests if a point is to the left of a line
522fn is_left(p1: &Coordinate, p2: &Coordinate, point: &Coordinate) -> f64 {
523    (p2.x - p1.x) * (point.y - p1.y) - (point.x - p1.x) * (p2.y - p1.y)
524}
525
526/// Tests if two rings intersect
527fn rings_intersect(ring1: &[Coordinate], ring2: &[Coordinate]) -> bool {
528    for i in 0..ring1.len().saturating_sub(1) {
529        for j in 0..ring2.len().saturating_sub(1) {
530            if segments_intersect(&ring1[i], &ring1[i + 1], &ring2[j], &ring2[j + 1]) {
531                return true;
532            }
533        }
534    }
535    false
536}
537
538/// Tests if two line segments intersect
539fn segments_intersect(p1: &Coordinate, p2: &Coordinate, p3: &Coordinate, p4: &Coordinate) -> bool {
540    let d1 = direction(p3, p4, p1);
541    let d2 = direction(p3, p4, p2);
542    let d3 = direction(p1, p2, p3);
543    let d4 = direction(p1, p2, p4);
544
545    if ((d1 > 0.0 && d2 < 0.0) || (d1 < 0.0 && d2 > 0.0))
546        && ((d3 > 0.0 && d4 < 0.0) || (d3 < 0.0 && d4 > 0.0))
547    {
548        return true;
549    }
550
551    // Check collinear cases
552    if d1.abs() < f64::EPSILON && on_segment(p3, p1, p4) {
553        return true;
554    }
555    if d2.abs() < f64::EPSILON && on_segment(p3, p2, p4) {
556        return true;
557    }
558    if d3.abs() < f64::EPSILON && on_segment(p1, p3, p2) {
559        return true;
560    }
561    if d4.abs() < f64::EPSILON && on_segment(p1, p4, p2) {
562        return true;
563    }
564
565    false
566}
567
568/// Computes the direction/orientation
569fn direction(a: &Coordinate, b: &Coordinate, p: &Coordinate) -> f64 {
570    (b.x - a.x) * (p.y - a.y) - (p.x - a.x) * (b.y - a.y)
571}
572
573/// Tests if point q lies on segment pr
574fn on_segment(p: &Coordinate, q: &Coordinate, r: &Coordinate) -> bool {
575    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)
576}
577
578#[cfg(test)]
579mod tests {
580    use super::*;
581    use crate::error::AlgorithmError;
582    use oxigdal_core::vector::LineString;
583
584    fn create_square() -> Result<Polygon> {
585        let coords = vec![
586            Coordinate::new_2d(0.0, 0.0),
587            Coordinate::new_2d(4.0, 0.0),
588            Coordinate::new_2d(4.0, 4.0),
589            Coordinate::new_2d(0.0, 4.0),
590            Coordinate::new_2d(0.0, 0.0),
591        ];
592        let exterior = LineString::new(coords).map_err(|e| AlgorithmError::Core(e))?;
593        Polygon::new(exterior, vec![]).map_err(|e| AlgorithmError::Core(e))
594    }
595
596    #[test]
597    fn test_point_contains_point() {
598        let p1 = Point::new(1.0, 2.0);
599        let p2 = Point::new(1.0, 2.0);
600        let p3 = Point::new(3.0, 4.0);
601
602        let result1 = p1.contains(&p2);
603        assert!(result1.is_ok());
604        if let Ok(contains) = result1 {
605            assert!(contains);
606        }
607
608        let result2 = p1.contains(&p3);
609        assert!(result2.is_ok());
610        if let Ok(contains) = result2 {
611            assert!(!contains);
612        }
613    }
614
615    #[test]
616    fn test_polygon_contains_point() {
617        let poly = create_square();
618        assert!(poly.is_ok());
619
620        if let Ok(p) = poly {
621            // Point inside
622            let inside = Coordinate::new_2d(2.0, 2.0);
623            assert!(point_strictly_inside_polygon(&inside, &p));
624
625            // Point outside
626            let outside = Coordinate::new_2d(5.0, 5.0);
627            assert!(!point_in_polygon_or_boundary(&outside, &p));
628
629            // Point on boundary
630            let boundary = Coordinate::new_2d(0.0, 2.0);
631            assert!(point_on_polygon_boundary(&boundary, &p));
632        }
633    }
634
635    #[test]
636    fn test_point_in_polygon_boundary() {
637        let poly = create_square();
638        assert!(poly.is_ok());
639
640        if let Ok(p) = poly {
641            let inside = Coordinate::new_2d(2.0, 2.0);
642            assert!(point_in_polygon_boundary(&inside, &p));
643
644            let outside = Coordinate::new_2d(5.0, 5.0);
645            assert!(!point_in_polygon_boundary(&outside, &p));
646        }
647    }
648
649    #[test]
650    fn test_point_on_segment() {
651        let seg_start = Coordinate::new_2d(0.0, 0.0);
652        let seg_end = Coordinate::new_2d(4.0, 0.0);
653
654        // Point on segment
655        let on = Coordinate::new_2d(2.0, 0.0);
656        assert!(point_on_segment(&on, &seg_start, &seg_end));
657
658        // Point off segment
659        let off = Coordinate::new_2d(2.0, 1.0);
660        assert!(!point_on_segment(&off, &seg_start, &seg_end));
661    }
662
663    #[test]
664    fn test_polygon_intersects_polygon() {
665        let poly1 = create_square();
666        assert!(poly1.is_ok());
667
668        // Overlapping polygon
669        let coords2 = vec![
670            Coordinate::new_2d(2.0, 2.0),
671            Coordinate::new_2d(6.0, 2.0),
672            Coordinate::new_2d(6.0, 6.0),
673            Coordinate::new_2d(2.0, 6.0),
674            Coordinate::new_2d(2.0, 2.0),
675        ];
676        let exterior2 = LineString::new(coords2);
677        assert!(exterior2.is_ok());
678
679        if let (Ok(p1), Ok(ext2)) = (poly1, exterior2) {
680            let poly2 = Polygon::new(ext2, vec![]);
681            assert!(poly2.is_ok());
682
683            if let Ok(p2) = poly2 {
684                let result: crate::error::Result<bool> = intersects(&p1, &p2);
685                assert!(result.is_ok());
686
687                if let Ok(do_intersect) = result {
688                    assert!(do_intersect);
689                }
690            }
691        }
692    }
693
694    #[test]
695    fn test_disjoint_polygons() {
696        let poly1 = create_square();
697
698        // Disjoint polygon
699        let coords2 = vec![
700            Coordinate::new_2d(10.0, 10.0),
701            Coordinate::new_2d(14.0, 10.0),
702            Coordinate::new_2d(14.0, 14.0),
703            Coordinate::new_2d(10.0, 14.0),
704            Coordinate::new_2d(10.0, 10.0),
705        ];
706        let exterior2 = LineString::new(coords2);
707
708        assert!(poly1.is_ok() && exterior2.is_ok());
709
710        if let (Ok(p1), Ok(ext2)) = (poly1, exterior2) {
711            let poly2 = Polygon::new(ext2, vec![]);
712            assert!(poly2.is_ok());
713
714            if let Ok(p2) = poly2 {
715                let result: crate::error::Result<bool> = intersects(&p1, &p2);
716                assert!(result.is_ok());
717
718                if let Ok(do_intersect) = result {
719                    assert!(!do_intersect);
720                }
721            }
722        }
723    }
724
725    #[test]
726    fn test_segments_intersect() {
727        // Crossing segments
728        let p1 = Coordinate::new_2d(0.0, 0.0);
729        let p2 = Coordinate::new_2d(2.0, 2.0);
730        let p3 = Coordinate::new_2d(0.0, 2.0);
731        let p4 = Coordinate::new_2d(2.0, 0.0);
732
733        assert!(segments_intersect(&p1, &p2, &p3, &p4));
734    }
735
736    #[test]
737    fn test_segments_no_intersect() {
738        // Parallel segments
739        let p1 = Coordinate::new_2d(0.0, 0.0);
740        let p2 = Coordinate::new_2d(2.0, 0.0);
741        let p3 = Coordinate::new_2d(0.0, 1.0);
742        let p4 = Coordinate::new_2d(2.0, 1.0);
743
744        assert!(!segments_intersect(&p1, &p2, &p3, &p4));
745    }
746
747    #[test]
748    fn test_ray_casting() {
749        let ring = vec![
750            Coordinate::new_2d(0.0, 0.0),
751            Coordinate::new_2d(4.0, 0.0),
752            Coordinate::new_2d(4.0, 4.0),
753            Coordinate::new_2d(0.0, 4.0),
754            Coordinate::new_2d(0.0, 0.0),
755        ];
756
757        let inside = Coordinate::new_2d(2.0, 2.0);
758        assert!(ray_casting_test(&inside, &ring));
759
760        let outside = Coordinate::new_2d(5.0, 5.0);
761        assert!(!ray_casting_test(&outside, &ring));
762    }
763
764    #[test]
765    fn test_winding_number() {
766        let ring = vec![
767            Coordinate::new_2d(0.0, 0.0),
768            Coordinate::new_2d(4.0, 0.0),
769            Coordinate::new_2d(4.0, 4.0),
770            Coordinate::new_2d(0.0, 4.0),
771            Coordinate::new_2d(0.0, 0.0),
772        ];
773
774        let inside = Coordinate::new_2d(2.0, 2.0);
775        assert!(winding_number_test(&inside, &ring));
776
777        let outside = Coordinate::new_2d(5.0, 5.0);
778        assert!(!winding_number_test(&outside, &ring));
779    }
780
781    #[test]
782    fn test_overlaps_polygons_partial() {
783        // Two polygons that partially overlap
784        let poly1 = create_square();
785        assert!(poly1.is_ok());
786
787        let coords2 = vec![
788            Coordinate::new_2d(2.0, 2.0),
789            Coordinate::new_2d(6.0, 2.0),
790            Coordinate::new_2d(6.0, 6.0),
791            Coordinate::new_2d(2.0, 6.0),
792            Coordinate::new_2d(2.0, 2.0),
793        ];
794        let exterior2 = LineString::new(coords2);
795        assert!(exterior2.is_ok());
796
797        if let (Ok(p1), Ok(ext2)) = (poly1, exterior2) {
798            let poly2 = Polygon::new(ext2, vec![]);
799            assert!(poly2.is_ok());
800
801            if let Ok(p2) = poly2 {
802                let result = overlaps(&p1, &p2);
803                assert!(result.is_ok());
804                if let Ok(do_overlap) = result {
805                    assert!(do_overlap, "Partially overlapping polygons should overlap");
806                }
807            }
808        }
809    }
810
811    #[test]
812    fn test_overlaps_polygons_disjoint() {
813        // Two polygons that don't overlap (disjoint)
814        let poly1 = create_square();
815        assert!(poly1.is_ok());
816
817        let coords2 = vec![
818            Coordinate::new_2d(10.0, 10.0),
819            Coordinate::new_2d(14.0, 10.0),
820            Coordinate::new_2d(14.0, 14.0),
821            Coordinate::new_2d(10.0, 14.0),
822            Coordinate::new_2d(10.0, 10.0),
823        ];
824        let exterior2 = LineString::new(coords2);
825        assert!(exterior2.is_ok());
826
827        if let (Ok(p1), Ok(ext2)) = (poly1, exterior2) {
828            let poly2 = Polygon::new(ext2, vec![]);
829            assert!(poly2.is_ok());
830
831            if let Ok(p2) = poly2 {
832                let result = overlaps(&p1, &p2);
833                assert!(result.is_ok());
834                if let Ok(do_overlap) = result {
835                    assert!(!do_overlap, "Disjoint polygons should not overlap");
836                }
837            }
838        }
839    }
840
841    #[test]
842    fn test_overlaps_polygons_contained() {
843        // One polygon completely contains another
844        let poly1 = create_square();
845        assert!(poly1.is_ok());
846
847        let coords2 = vec![
848            Coordinate::new_2d(1.0, 1.0),
849            Coordinate::new_2d(3.0, 1.0),
850            Coordinate::new_2d(3.0, 3.0),
851            Coordinate::new_2d(1.0, 3.0),
852            Coordinate::new_2d(1.0, 1.0),
853        ];
854        let exterior2 = LineString::new(coords2);
855        assert!(exterior2.is_ok());
856
857        if let (Ok(p1), Ok(ext2)) = (poly1, exterior2) {
858            let poly2 = Polygon::new(ext2, vec![]);
859            assert!(poly2.is_ok());
860
861            if let Ok(p2) = poly2 {
862                let result = overlaps(&p1, &p2);
863                assert!(result.is_ok());
864                if let Ok(do_overlap) = result {
865                    assert!(!do_overlap, "Contained polygons should not overlap");
866                }
867            }
868        }
869    }
870
871    #[test]
872    fn test_overlaps_points() {
873        let p1 = Point::new(1.0, 2.0);
874        let p2 = Point::new(1.0, 2.0);
875
876        let result = overlaps(&p1, &p2);
877        assert!(result.is_ok());
878        if let Ok(do_overlap) = result {
879            assert!(!do_overlap, "Points should not overlap");
880        }
881    }
882
883    #[test]
884    fn test_crosses_polygons() {
885        // Two polygons where one crosses the other
886        let poly1 = create_square();
887        assert!(poly1.is_ok());
888
889        let coords2 = vec![
890            Coordinate::new_2d(-1.0, 2.0),
891            Coordinate::new_2d(5.0, 2.0),
892            Coordinate::new_2d(5.0, 3.0),
893            Coordinate::new_2d(-1.0, 3.0),
894            Coordinate::new_2d(-1.0, 2.0),
895        ];
896        let exterior2 = LineString::new(coords2);
897        assert!(exterior2.is_ok());
898
899        if let (Ok(p1), Ok(ext2)) = (poly1, exterior2) {
900            let poly2 = Polygon::new(ext2, vec![]);
901            assert!(poly2.is_ok());
902
903            if let Ok(p2) = poly2 {
904                let result = crosses(&p1, &p2);
905                assert!(result.is_ok());
906                if let Ok(do_cross) = result {
907                    assert!(do_cross, "Polygon crossing through another should cross");
908                }
909            }
910        }
911    }
912
913    #[test]
914    fn test_crosses_polygons_disjoint() {
915        // Two polygons that don't cross (disjoint)
916        let poly1 = create_square();
917        assert!(poly1.is_ok());
918
919        let coords2 = vec![
920            Coordinate::new_2d(10.0, 10.0),
921            Coordinate::new_2d(14.0, 10.0),
922            Coordinate::new_2d(14.0, 14.0),
923            Coordinate::new_2d(10.0, 14.0),
924            Coordinate::new_2d(10.0, 10.0),
925        ];
926        let exterior2 = LineString::new(coords2);
927        assert!(exterior2.is_ok());
928
929        if let (Ok(p1), Ok(ext2)) = (poly1, exterior2) {
930            let poly2 = Polygon::new(ext2, vec![]);
931            assert!(poly2.is_ok());
932
933            if let Ok(p2) = poly2 {
934                let result = crosses(&p1, &p2);
935                assert!(result.is_ok());
936                if let Ok(do_cross) = result {
937                    assert!(!do_cross, "Disjoint polygons should not cross");
938                }
939            }
940        }
941    }
942
943    #[test]
944    fn test_crosses_points() {
945        let p1 = Point::new(1.0, 2.0);
946        let p2 = Point::new(3.0, 4.0);
947
948        let result = crosses(&p1, &p2);
949        assert!(result.is_ok());
950        if let Ok(do_cross) = result {
951            assert!(!do_cross, "Points should not cross");
952        }
953    }
954
955    #[test]
956    fn test_touches_adjacent_polygons() {
957        // Two polygons that share a boundary
958        let poly1 = create_square();
959        assert!(poly1.is_ok());
960
961        let coords2 = vec![
962            Coordinate::new_2d(4.0, 0.0),
963            Coordinate::new_2d(8.0, 0.0),
964            Coordinate::new_2d(8.0, 4.0),
965            Coordinate::new_2d(4.0, 4.0),
966            Coordinate::new_2d(4.0, 0.0),
967        ];
968        let exterior2 = LineString::new(coords2);
969        assert!(exterior2.is_ok());
970
971        if let (Ok(p1), Ok(ext2)) = (poly1, exterior2) {
972            let poly2 = Polygon::new(ext2, vec![]);
973            assert!(poly2.is_ok());
974
975            if let Ok(p2) = poly2 {
976                let result = touches(&p1, &p2);
977                assert!(result.is_ok());
978                if let Ok(do_touch) = result {
979                    assert!(do_touch, "Adjacent polygons should touch");
980                }
981            }
982        }
983    }
984
985    #[test]
986    fn test_within_polygon() {
987        // Small polygon within larger polygon
988        let poly1 = create_square();
989        assert!(poly1.is_ok());
990
991        let coords2 = vec![
992            Coordinate::new_2d(1.0, 1.0),
993            Coordinate::new_2d(3.0, 1.0),
994            Coordinate::new_2d(3.0, 3.0),
995            Coordinate::new_2d(1.0, 3.0),
996            Coordinate::new_2d(1.0, 1.0),
997        ];
998        let exterior2 = LineString::new(coords2);
999        assert!(exterior2.is_ok());
1000
1001        if let (Ok(p1), Ok(ext2)) = (poly1, exterior2) {
1002            let poly2 = Polygon::new(ext2, vec![]);
1003            assert!(poly2.is_ok());
1004
1005            if let Ok(p2) = poly2 {
1006                let result = within(&p2, &p1);
1007                assert!(result.is_ok());
1008                if let Ok(is_within) = result {
1009                    assert!(is_within, "Small polygon should be within larger polygon");
1010                }
1011            }
1012        }
1013    }
1014
1015    #[test]
1016    fn test_contains_polygon() {
1017        // Large polygon contains smaller polygon
1018        let poly1 = create_square();
1019        assert!(poly1.is_ok());
1020
1021        let coords2 = vec![
1022            Coordinate::new_2d(1.0, 1.0),
1023            Coordinate::new_2d(3.0, 1.0),
1024            Coordinate::new_2d(3.0, 3.0),
1025            Coordinate::new_2d(1.0, 3.0),
1026            Coordinate::new_2d(1.0, 1.0),
1027        ];
1028        let exterior2 = LineString::new(coords2);
1029        assert!(exterior2.is_ok());
1030
1031        if let (Ok(p1), Ok(ext2)) = (poly1, exterior2) {
1032            let poly2 = Polygon::new(ext2, vec![]);
1033            assert!(poly2.is_ok());
1034
1035            if let Ok(p2) = poly2 {
1036                let result = contains(&p1, &p2);
1037                assert!(result.is_ok());
1038                if let Ok(does_contain) = result {
1039                    assert!(does_contain, "Large polygon should contain smaller polygon");
1040                }
1041            }
1042        }
1043    }
1044
1045    #[test]
1046    fn test_disjoint_polygons_separated() {
1047        let poly1 = create_square();
1048        assert!(poly1.is_ok());
1049
1050        let coords2 = vec![
1051            Coordinate::new_2d(10.0, 10.0),
1052            Coordinate::new_2d(14.0, 10.0),
1053            Coordinate::new_2d(14.0, 14.0),
1054            Coordinate::new_2d(10.0, 14.0),
1055            Coordinate::new_2d(10.0, 10.0),
1056        ];
1057        let exterior2 = LineString::new(coords2);
1058        assert!(exterior2.is_ok());
1059
1060        if let (Ok(p1), Ok(ext2)) = (poly1, exterior2) {
1061            let poly2 = Polygon::new(ext2, vec![]);
1062            assert!(poly2.is_ok());
1063
1064            if let Ok(p2) = poly2 {
1065                let result = disjoint(&p1, &p2);
1066                assert!(result.is_ok());
1067                if let Ok(are_disjoint) = result {
1068                    assert!(are_disjoint, "Separated polygons should be disjoint");
1069                }
1070            }
1071        }
1072    }
1073}