use super::{Contains, impl_contains_from_relate, impl_contains_geometry_for};
use crate::coordinate_position::CoordPos;
use crate::geometry::*;
use crate::indexed::IntervalTreeMultiPolygon;
use crate::{GeoFloat, GeoNum};
use crate::{HasDimensions, Relate};
use crate::{Coord, LineString, MultiPolygon, Polygon};
impl<T> Contains<Coord<T>> for Polygon<T>
where
T: GeoNum,
{
fn contains(&self, coord: &Coord<T>) -> bool {
use crate::coordinate_position::{CoordPos, CoordinatePosition};
self.coordinate_position(coord) == CoordPos::Inside
}
}
impl<T> Contains<Point<T>> for Polygon<T>
where
T: GeoNum,
{
fn contains(&self, p: &Point<T>) -> bool {
self.contains(&p.0)
}
}
impl<T> Contains<MultiPoint<T>> for Polygon<T>
where
T: GeoNum,
{
fn contains(&self, mp: &MultiPoint<T>) -> bool {
use crate::coordinate_position::{CoordPos, CoordinatePosition};
mp.iter().any(|p| self.contains(p))
&& mp
.iter()
.all(|p| self.coordinate_position(&p.0) != CoordPos::Outside)
}
}
impl_contains_from_relate!(Polygon<T>, [Line<T>, LineString<T>, Polygon<T>, MultiLineString<T>, MultiPolygon<T>, GeometryCollection<T>, Rect<T>, Triangle<T>]);
impl_contains_geometry_for!(Polygon<T>);
impl<T> Contains<Coord<T>> for MultiPolygon<T>
where
T: GeoNum,
{
fn contains(&self, coord: &Coord<T>) -> bool {
self.iter().any(|poly| poly.contains(coord))
}
}
impl<T> Contains<Point<T>> for MultiPolygon<T>
where
T: GeoNum,
{
fn contains(&self, p: &Point<T>) -> bool {
self.contains(&p.0)
}
}
impl<T: GeoNum> Contains<MultiPoint<T>> for MultiPolygon<T> {
fn contains(&self, rhs: &MultiPoint<T>) -> bool {
use crate::coordinate_position::CoordPos;
if self.is_empty() || rhs.is_empty() {
return false;
}
let indexed = IntervalTreeMultiPolygon::new(self);
let mut has_inside = false;
for point in rhs.iter() {
match indexed.containment(point.0) {
CoordPos::Outside => return false,
CoordPos::Inside => has_inside = true,
CoordPos::OnBoundary => {}
}
}
has_inside
}
}
impl<T: GeoNum> Contains<Coord<T>> for IntervalTreeMultiPolygon<T> {
fn contains(&self, rhs: &Coord<T>) -> bool {
self.containment(*rhs) == CoordPos::Inside
}
}
impl<T: GeoNum> Contains<Point<T>> for IntervalTreeMultiPolygon<T> {
fn contains(&self, rhs: &Point<T>) -> bool {
let c = Coord::from(*rhs);
self.containment(c) == CoordPos::Inside
}
}
impl<F> Contains<Line<F>> for MultiPolygon<F>
where
F: GeoFloat,
{
fn contains(&self, rhs: &Line<F>) -> bool {
rhs.relate(self).is_within()
}
}
impl<F> Contains<LineString<F>> for MultiPolygon<F>
where
F: GeoFloat,
{
fn contains(&self, rhs: &LineString<F>) -> bool {
rhs.relate(self).is_within()
}
}
impl<F> Contains<MultiLineString<F>> for MultiPolygon<F>
where
F: GeoFloat,
{
fn contains(&self, rhs: &MultiLineString<F>) -> bool {
rhs.relate(self).is_within()
}
}
impl<F> Contains<Polygon<F>> for MultiPolygon<F>
where
F: GeoFloat,
{
fn contains(&self, rhs: &Polygon<F>) -> bool {
rhs.relate(self).is_within()
}
}
impl<F> Contains<MultiPolygon<F>> for MultiPolygon<F>
where
F: GeoFloat,
{
fn contains(&self, rhs: &MultiPolygon<F>) -> bool {
rhs.relate(self).is_within()
}
}
impl<F> Contains<GeometryCollection<F>> for MultiPolygon<F>
where
F: GeoFloat,
{
fn contains(&self, rhs: &GeometryCollection<F>) -> bool {
rhs.relate(self).is_within()
}
}
impl<F> Contains<Rect<F>> for MultiPolygon<F>
where
F: GeoFloat,
{
fn contains(&self, rhs: &Rect<F>) -> bool {
rhs.relate(self).is_within()
}
}
impl<F> Contains<Triangle<F>> for MultiPolygon<F>
where
F: GeoFloat,
{
fn contains(&self, rhs: &Triangle<F>) -> bool {
rhs.relate(self).is_within()
}
}
impl_contains_geometry_for!(MultiPolygon<T>);
#[cfg(test)]
mod test {
use super::*;
use crate::{Convert, MultiPoint, Relate, coord, polygon, wkt};
fn make_test_pts() -> [Coord<f64>; 7] {
let pt_a = coord! {x: 0., y: 0.};
let pt_b = coord! {x: 10., y: 0.};
let pt_c = coord! {x: 10., y: 10.};
let pt_d = coord! {x: 0., y: 10.};
let pt_edge = coord! {x: 0., y: 5.};
let pt_mid = coord! {x: 5., y: 5.};
let pt_out = coord! {x: 11., y: 11.};
[pt_a, pt_b, pt_c, pt_d, pt_edge, pt_mid, pt_out]
}
#[test]
fn test_multipolygon_boundary() {
let p_1: Polygon<f64> = Polygon::new(geo_test_fixtures::poly1(), vec![]);
let p_2: Polygon<f64> = Polygon::new(geo_test_fixtures::poly2(), vec![]);
let multi_poly = MultiPolygon(vec![p_1, p_2]);
let multi_point: MultiPoint<f64> = wkt!(MULTIPOINT (-160 10,-60 -70,-120 -70,-120 10,-40 80,30 80,30 10,-40 10,100 210,100 120,30 120,30 210,-185 -135,-100 -135,-100 -230,-185 -230)).convert();
assert_eq!(
multi_poly.contains(&multi_point),
multi_poly.relate(&multi_point).is_contains()
);
}
#[test]
fn test_point_on_boundary_indexed_polygon() {
let square: MultiPolygon = wkt!(MULTIPOLYGON(((-1 1, 1 1, 1 -1, -1 -1, -1 1)))).convert();
let square_index = IntervalTreeMultiPolygon::new(&square);
assert!(!square_index.contains(&Coord { x: -1.0, y: 1.0 }));
assert!(!square_index.contains(&Coord { x: -1.0, y: 0.5 }));
assert!(!square_index.contains(&Coord { x: -1.0, y: 0.0 }));
assert!(!square_index.contains(&Coord { x: -2.0, y: 0.0 }));
}
#[test]
fn test_external_point_horizontal_from_tip() {
let triangle: MultiPolygon = wkt!(MULTIPOLYGON (((-1 0, 0 1, 1 0, -1 0)))).convert();
let triangle_index = IntervalTreeMultiPolygon::new(&triangle);
assert!(!triangle.contains(&Coord { x: -0.75, y: 1.0 }));
assert!(!triangle_index.contains(&Coord { x: -0.75, y: 1.0 }));
assert!(!triangle.contains(&Coord { x: -0.5, y: 0.5 }));
assert!(!triangle_index.contains(&Coord { x: -0.5, y: 0.5 }));
assert!(!triangle.contains(&Coord { x: 0.0, y: 1.0 }));
assert!(!triangle_index.contains(&Coord { x: 0.0, y: 1.0 }));
assert!(!triangle.contains(&Coord { x: 0.75, y: 1.0 }));
assert!(!triangle_index.contains(&Coord { x: 0.75, y: 1.0 }));
}
#[test]
fn test_point_inside_indexed_polygon() {
let square = MultiPolygon::new(vec![polygon!(
exterior: [
(x: -1., y: 1.),
(x: 1., y: 1.),
(x: 1., y: 0.),
(x: 1., y: -1.),
(x: -1., y: -1.),
],
interiors: [],
)]);
let square_index = IntervalTreeMultiPolygon::new(&square);
assert!(square_index.contains(&Coord { x: 0.0, y: 0.0 }));
}
#[test]
fn test_polygon_should_never_contain_empty_multipoint() {
let [pt_a, pt_b, pt_c, pt_d, _pt_edge, _pt_mid, _pt_out] = make_test_pts();
let poly = polygon![pt_a, pt_b, pt_c, pt_d, pt_a];
let empty: MultiPoint<f64> = MultiPoint::empty();
assert!(!poly.contains(&empty));
assert!(!poly.relate(&empty).is_contains());
}
#[test]
fn test_polygon_should_contains_multipoint() {
let [pt_a, pt_b, pt_c, pt_d, pt_edge, pt_mid, _pt_out] = make_test_pts();
let poly = polygon![pt_a, pt_b, pt_c, pt_d, pt_a];
let mp_a_mid = MultiPoint::from(vec![pt_a, pt_mid]);
let mp_bc_mid = MultiPoint::from(vec![pt_a, pt_b, pt_mid]);
let mp_bc_edge_mid = MultiPoint::from(vec![pt_a, pt_b, pt_edge, pt_mid]);
assert!(poly.contains(&mp_a_mid));
assert!(poly.contains(&mp_bc_mid));
assert!(poly.contains(&mp_bc_edge_mid));
}
#[test]
fn test_polygon_should_not_contains_multipoint_on_edge() {
let [pt_a, pt_b, pt_c, pt_d, pt_edge, _pt_mid, _pt_out] = make_test_pts();
let poly = polygon![pt_a, pt_b, pt_c, pt_d, pt_a];
let mp_a = MultiPoint::from(vec![pt_a]);
let mp_bc = MultiPoint::from(vec![pt_a, pt_b]);
let mp_bc_edge = MultiPoint::from(vec![pt_a, pt_b, pt_edge]);
assert!(!poly.contains(&mp_a));
assert!(!poly.contains(&mp_bc));
assert!(!poly.contains(&mp_bc_edge));
}
#[test]
fn test_polygon_should_not_contains_out_multipoint() {
let [pt_a, pt_b, pt_c, pt_d, pt_edge, _pt_mid, pt_out] = make_test_pts();
let poly = polygon![pt_a, pt_b, pt_c, pt_d, pt_a];
let mp_a_out = MultiPoint::from(vec![pt_a, pt_out]);
let mp_bc_out = MultiPoint::from(vec![pt_a, pt_b, pt_out]);
let mp_bc_edge_out = MultiPoint::from(vec![pt_a, pt_b, pt_edge, pt_out]);
assert!(!poly.contains(&mp_a_out));
assert!(!poly.contains(&mp_bc_out));
assert!(!poly.contains(&mp_bc_edge_out));
}
#[test]
fn test_hollow_square_ccw_exterior_cw_interior() {
let hollow_square = wkt!(MULTIPOLYGON(((-2.0 -2.0,2.0 -2.0,2.0 2.0,-2.0 2.0,-2.0 -2.0),(-1.0 -1.0,-1.0 1.0,1.0 1.0,1.0 -1.0,-1.0 -1.0))));
let hollow_square_index = IntervalTreeMultiPolygon::new(&hollow_square);
assert!(!hollow_square_index.contains(&Coord { x: 0.0, y: 0.0 }));
assert!(hollow_square_index.contains(&Coord { x: 1.5, y: 0.0 }));
}
#[test]
fn test_hollow_square_cw_exterior_ccw_interior() {
let hollow_square = wkt!(MULTIPOLYGON(((-2.0 -2.0,-2.0 2.0,2.0 2.0,2.0 -2.0,-2.0 -2.0),(-1.0 -1.0,1.0 -1.0,1.0 1.0,-1.0 1.0,-1.0 -1.0))));
let hollow_square_index = IntervalTreeMultiPolygon::new(&hollow_square);
assert!(!hollow_square_index.contains(&Coord { x: 0.0, y: 0.0 }));
assert!(hollow_square_index.contains(&Coord { x: 1.5, y: 0.0 }));
}
}