geo/algorithm/contains/
polygon.rs

1use super::{Contains, impl_contains_from_relate, impl_contains_geometry_for};
2use crate::geometry::*;
3use crate::{GeoFloat, GeoNum};
4use crate::{HasDimensions, Relate};
5
6// ┌─────────────────────────────┐
7// │ Implementations for Polygon │
8// └─────────────────────────────┘
9impl<T> Contains<Coord<T>> for Polygon<T>
10where
11    T: GeoNum,
12{
13    fn contains(&self, coord: &Coord<T>) -> bool {
14        use crate::coordinate_position::{CoordPos, CoordinatePosition};
15
16        self.coordinate_position(coord) == CoordPos::Inside
17    }
18}
19
20impl<T> Contains<Point<T>> for Polygon<T>
21where
22    T: GeoNum,
23{
24    fn contains(&self, p: &Point<T>) -> bool {
25        self.contains(&p.0)
26    }
27}
28
29impl<T> Contains<MultiPoint<T>> for Polygon<T>
30where
31    T: GeoNum,
32{
33    fn contains(&self, mp: &MultiPoint<T>) -> bool {
34        use crate::coordinate_position::{CoordPos, CoordinatePosition};
35        // at least one point must be fully within
36        // others can be on the boundary
37        mp.iter().any(|p| self.contains(p))
38            && mp
39                .iter()
40                .all(|p| self.coordinate_position(&p.0) != CoordPos::Outside)
41    }
42}
43
44impl_contains_from_relate!(Polygon<T>, [Line<T>, LineString<T>, Polygon<T>, MultiLineString<T>, MultiPolygon<T>, GeometryCollection<T>, Rect<T>, Triangle<T>]);
45impl_contains_geometry_for!(Polygon<T>);
46
47// ┌──────────────────────────────────┐
48// │ Implementations for MultiPolygon │
49// └──────────────────────────────────┘
50
51impl<T> Contains<Coord<T>> for MultiPolygon<T>
52where
53    T: GeoNum,
54{
55    fn contains(&self, coord: &Coord<T>) -> bool {
56        self.iter().any(|poly| poly.contains(coord))
57    }
58}
59
60impl<T> Contains<Point<T>> for MultiPolygon<T>
61where
62    T: GeoNum,
63{
64    fn contains(&self, p: &Point<T>) -> bool {
65        self.contains(&p.0)
66    }
67}
68
69impl<T: GeoNum> Contains<MultiPoint<T>> for MultiPolygon<T> {
70    fn contains(&self, rhs: &MultiPoint<T>) -> bool {
71        if self.is_empty() || rhs.is_empty() {
72            return false;
73        }
74        rhs.iter().all(|point| self.contains(point))
75    }
76}
77
78impl<F> Contains<Line<F>> for MultiPolygon<F>
79where
80    F: GeoFloat,
81{
82    fn contains(&self, rhs: &Line<F>) -> bool {
83        rhs.relate(self).is_within()
84    }
85}
86
87impl<F> Contains<LineString<F>> for MultiPolygon<F>
88where
89    F: GeoFloat,
90{
91    fn contains(&self, rhs: &LineString<F>) -> bool {
92        rhs.relate(self).is_within()
93    }
94}
95
96impl<F> Contains<MultiLineString<F>> for MultiPolygon<F>
97where
98    F: GeoFloat,
99{
100    fn contains(&self, rhs: &MultiLineString<F>) -> bool {
101        rhs.relate(self).is_within()
102    }
103}
104
105impl<F> Contains<Polygon<F>> for MultiPolygon<F>
106where
107    F: GeoFloat,
108{
109    fn contains(&self, rhs: &Polygon<F>) -> bool {
110        rhs.relate(self).is_within()
111    }
112}
113
114impl<F> Contains<MultiPolygon<F>> for MultiPolygon<F>
115where
116    F: GeoFloat,
117{
118    fn contains(&self, rhs: &MultiPolygon<F>) -> bool {
119        rhs.relate(self).is_within()
120    }
121}
122
123impl<F> Contains<GeometryCollection<F>> for MultiPolygon<F>
124where
125    F: GeoFloat,
126{
127    fn contains(&self, rhs: &GeometryCollection<F>) -> bool {
128        rhs.relate(self).is_within()
129    }
130}
131
132impl<F> Contains<Rect<F>> for MultiPolygon<F>
133where
134    F: GeoFloat,
135{
136    fn contains(&self, rhs: &Rect<F>) -> bool {
137        rhs.relate(self).is_within()
138    }
139}
140
141impl<F> Contains<Triangle<F>> for MultiPolygon<F>
142where
143    F: GeoFloat,
144{
145    fn contains(&self, rhs: &Triangle<F>) -> bool {
146        rhs.relate(self).is_within()
147    }
148}
149
150#[cfg(test)]
151mod test {
152    use super::*;
153    use crate::{MultiPoint, Relate, coord, polygon};
154
155    fn make_test_pts() -> [Coord<f64>; 7] {
156        let pt_a = coord! {x: 0., y: 0.};
157        let pt_b = coord! {x: 10., y: 0.};
158        let pt_c = coord! {x: 10., y: 10.};
159        let pt_d = coord! {x: 0., y: 10.};
160
161        let pt_edge = coord! {x: 0., y: 5.};
162        let pt_mid = coord! {x: 5., y: 5.};
163        let pt_out = coord! {x: 11., y: 11.};
164        [pt_a, pt_b, pt_c, pt_d, pt_edge, pt_mid, pt_out]
165    }
166
167    #[test]
168    fn test_polygon_should_never_contain_empty_multipoint() {
169        let [pt_a, pt_b, pt_c, pt_d, _pt_edge, _pt_mid, _pt_out] = make_test_pts();
170
171        let poly = polygon![pt_a, pt_b, pt_c, pt_d, pt_a];
172        let empty: MultiPoint<f64> = MultiPoint::new(Vec::new());
173
174        // contains implementation follows `Relate`` trait
175        assert!(!poly.contains(&empty));
176        assert!(!poly.relate(&empty).is_contains());
177    }
178
179    #[test]
180    fn test_polygon_should_contains_multipoint() {
181        let [pt_a, pt_b, pt_c, pt_d, pt_edge, pt_mid, _pt_out] = make_test_pts();
182
183        let poly = polygon![pt_a, pt_b, pt_c, pt_d, pt_a];
184
185        // contains requires at least one point fully within the polygon
186        let mp_a_mid = MultiPoint::from(vec![pt_a, pt_mid]);
187        let mp_bc_mid = MultiPoint::from(vec![pt_a, pt_b, pt_mid]);
188        let mp_bc_edge_mid = MultiPoint::from(vec![pt_a, pt_b, pt_edge, pt_mid]);
189
190        assert!(poly.contains(&mp_a_mid));
191        assert!(poly.contains(&mp_bc_mid));
192        assert!(poly.contains(&mp_bc_edge_mid));
193    }
194
195    #[test]
196    fn test_polygon_should_not_contains_multipoint_on_edge() {
197        let [pt_a, pt_b, pt_c, pt_d, pt_edge, _pt_mid, _pt_out] = make_test_pts();
198
199        let poly = polygon![pt_a, pt_b, pt_c, pt_d, pt_a];
200
201        // contains should return false if all points lie on the boundary of the polygon
202        let mp_a = MultiPoint::from(vec![pt_a]);
203        let mp_bc = MultiPoint::from(vec![pt_a, pt_b]);
204        let mp_bc_edge = MultiPoint::from(vec![pt_a, pt_b, pt_edge]);
205
206        assert!(!poly.contains(&mp_a));
207        assert!(!poly.contains(&mp_bc));
208        assert!(!poly.contains(&mp_bc_edge));
209    }
210
211    #[test]
212    fn test_polygon_should_not_contains_out_multipoint() {
213        let [pt_a, pt_b, pt_c, pt_d, pt_edge, _pt_mid, pt_out] = make_test_pts();
214
215        let poly = polygon![pt_a, pt_b, pt_c, pt_d, pt_a];
216
217        // contains should return false if any point lies outside the polygon
218        let mp_a_out = MultiPoint::from(vec![pt_a, pt_out]);
219        let mp_bc_out = MultiPoint::from(vec![pt_a, pt_b, pt_out]);
220        let mp_bc_edge_out = MultiPoint::from(vec![pt_a, pt_b, pt_edge, pt_out]);
221
222        assert!(!poly.contains(&mp_a_out));
223        assert!(!poly.contains(&mp_bc_out));
224        assert!(!poly.contains(&mp_bc_edge_out));
225    }
226}