Skip to content

Commit

Permalink
Add more implementations for Contains algorithm.
Browse files Browse the repository at this point in the history
  • Loading branch information
frewsxcv committed May 8, 2020
1 parent 9125aed commit e0a23ac
Show file tree
Hide file tree
Showing 3 changed files with 278 additions and 73 deletions.
274 changes: 205 additions & 69 deletions geo/src/algorithm/contains.rs
@@ -1,8 +1,10 @@
use num_traits::Float;

use crate::algorithm::intersects::Intersects;
use crate::utils;
use crate::{
Coordinate, CoordinateType, Line, LineString, MultiPolygon, Point, Polygon, Rect, Triangle,
Coordinate, CoordinateType, Geometry, GeometryCollection, Line, LineString, MultiLineString,
MultiPoint, MultiPolygon, Point, Polygon, Rect, Triangle,
};

/// Checks if the geometry A is completely inside the B geometry
Expand Down Expand Up @@ -30,6 +32,19 @@ pub trait Contains<Rhs = Self> {
fn contains(&self, rhs: &Rhs) -> bool;
}

// ┌───────────────────────────┐
// │ Implementations for Point │
// └───────────────────────────┘

impl<T> Contains<Coordinate<T>> for Point<T>
where
T: Float,
{
fn contains(&self, coord: &Coordinate<T>) -> bool {
self.contains(&Point(*coord))
}
}

impl<T> Contains<Point<T>> for Point<T>
where
T: Float,
Expand All @@ -39,12 +54,38 @@ where
}
}

impl<T> Contains<Point<T>> for LineString<T>
// ┌────────────────────────────────┐
// │ Implementations for MultiPoint │
// └────────────────────────────────┘

impl<T> Contains<Coordinate<T>> for MultiPoint<T>
where
T: Float,
{
fn contains(&self, p: &Point<T>) -> bool {
::geo_types::private_utils::line_string_contains_point(self, *p)
fn contains(&self, coord: &Coordinate<T>) -> bool {
self.0.iter().any(|point| point.contains(coord))
}
}

impl<T> Contains<Point<T>> for MultiPoint<T>
where
T: Float,
{
fn contains(&self, point: &Point<T>) -> bool {
self.contains(&point.0)
}
}

// ┌──────────────────────────┐
// │ Implementations for Line │
// └──────────────────────────┘

impl<T> Contains<Coordinate<T>> for Line<T>
where
T: Float,
{
fn contains(&self, coord: &Coordinate<T>) -> bool {
self.contains(&Point(*coord))
}
}

Expand Down Expand Up @@ -75,6 +116,28 @@ where
}
}

// ┌────────────────────────────────┐
// │ Implementations for LineString │
// └────────────────────────────────┘

impl<T> Contains<Coordinate<T>> for LineString<T>
where
T: Float,
{
fn contains(&self, coord: &Coordinate<T>) -> bool {
self.contains(&Point(*coord))
}
}

impl<T> Contains<Point<T>> for LineString<T>
where
T: Float,
{
fn contains(&self, p: &Point<T>) -> bool {
::geo_types::private_utils::line_string_contains_point(self, *p)
}
}

impl<T> Contains<Line<T>> for LineString<T>
where
T: Float,
Expand Down Expand Up @@ -109,77 +172,52 @@ where
}
}

/// The position of a `Point` with respect to a `LineString`
#[derive(PartialEq, Clone, Debug)]
pub(crate) enum PositionPoint {
OnBoundary,
Inside,
Outside,
}
// ┌─────────────────────────────────────┐
// │ Implementations for MultiLineString │
// └─────────────────────────────────────┘

/// Calculate the position of `Point` p relative to a linestring
pub(crate) fn get_position<T>(p: Point<T>, linestring: &LineString<T>) -> PositionPoint
impl<T> Contains<Coordinate<T>> for MultiLineString<T>
where
T: Float,
{
// See: http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
// http://geospatialpython.com/search
// ?updated-min=2011-01-01T00:00:00-06:00&updated-max=2012-01-01T00:00:00-06:00&max-results=19

// LineString without points
if linestring.0.is_empty() {
return PositionPoint::Outside;
}
// Point is on linestring
if linestring.contains(&p) {
return PositionPoint::OnBoundary;
}

let mut xints = T::zero();
let mut crossings = 0;
for line in linestring.lines() {
if p.y() > line.start.y.min(line.end.y)
&& p.y() <= line.start.y.max(line.end.y)
&& p.x() <= line.start.x.max(line.end.x)
{
if line.start.y != line.end.y {
xints = (p.y() - line.start.y) * (line.end.x - line.start.x)
/ (line.end.y - line.start.y)
+ line.start.x;
}
if (line.start.x == line.end.x) || (p.x() <= xints) {
crossings += 1;
}
}
fn contains(&self, coord: &Coordinate<T>) -> bool {
self.0.iter().any(|line_string| line_string.contains(coord))
}
if crossings % 2 == 1 {
PositionPoint::Inside
} else {
PositionPoint::Outside
}

impl<T> Contains<Point<T>> for MultiLineString<T>
where
T: Float,
{
fn contains(&self, point: &Point<T>) -> bool {
self.contains(&point.0)
}
}

impl<T> Contains<Point<T>> for Polygon<T>
// ┌─────────────────────────────┐
// │ Implementations for Polygon │
// └─────────────────────────────┘

impl<T> Contains<Coordinate<T>> for Polygon<T>
where
T: Float,
{
fn contains(&self, p: &Point<T>) -> bool {
match get_position(*p, &self.exterior()) {
PositionPoint::OnBoundary | PositionPoint::Outside => false,
_ => self
.interiors()
.iter()
.all(|ls| get_position(*p, ls) == PositionPoint::Outside),
fn contains(&self, coord: &Coordinate<T>) -> bool {
match utils::coord_pos_relative_to_line_string(*coord, &self.exterior()) {
utils::CoordPos::OnBoundary | utils::CoordPos::Outside => false,
_ => self.interiors().iter().all(|ls| {
utils::coord_pos_relative_to_line_string(*coord, ls) == utils::CoordPos::Outside
}),
}
}
}

impl<T> Contains<Point<T>> for MultiPolygon<T>
impl<T> Contains<Point<T>> for Polygon<T>
where
T: Float,
{
fn contains(&self, p: &Point<T>) -> bool {
self.0.iter().any(|poly| poly.contains(p))
self.contains(&p.0)
}
}

Expand Down Expand Up @@ -226,15 +264,50 @@ where
}
}

// ┌──────────────────────────────────┐
// │ Implementations for MultiPolygon │
// └──────────────────────────────────┘

impl<T> Contains<Coordinate<T>> for MultiPolygon<T>
where
T: Float,
{
fn contains(&self, coord: &Coordinate<T>) -> bool {
self.0.iter().any(|poly| poly.contains(coord))
}
}

impl<T> Contains<Point<T>> for MultiPolygon<T>
where
T: Float,
{
fn contains(&self, p: &Point<T>) -> bool {
self.contains(&p.0)
}
}

// ┌──────────────────────────┐
// │ Implementations for Rect │
// └──────────────────────────┘

impl<T> Contains<Coordinate<T>> for Rect<T>
where
T: CoordinateType,
{
fn contains(&self, coord: &Coordinate<T>) -> bool {
coord.x >= self.min().x
&& coord.x <= self.max().x
&& coord.y >= self.min().y
&& coord.y <= self.max().y
}
}

impl<T> Contains<Point<T>> for Rect<T>
where
T: CoordinateType,
{
fn contains(&self, p: &Point<T>) -> bool {
p.x() >= self.min().x
&& p.x() <= self.max().x
&& p.y() >= self.min().y
&& p.y() <= self.max().y
self.contains(&p.0)
}
}

Expand All @@ -251,28 +324,91 @@ where
}
}

// ┌──────────────────────────────┐
// │ Implementations for Triangle │
// └──────────────────────────────┘

impl<T> Contains<Coordinate<T>> for Triangle<T>
where
T: CoordinateType,
{
fn contains(&self, coord: &Coordinate<T>) -> bool {
let sign_1 = utils::sign(coord, &self.0, &self.1);
let sign_2 = utils::sign(coord, &self.1, &self.2);
let sign_3 = utils::sign(coord, &self.2, &self.0);

(sign_1 == sign_2) && (sign_2 == sign_3)
}
}

impl<T> Contains<Point<T>> for Triangle<T>
where
T: CoordinateType,
{
fn contains(&self, point: &Point<T>) -> bool {
let sign_1 = sign(&point.0, &self.0, &self.1);
let sign_2 = sign(&point.0, &self.1, &self.2);
let sign_3 = sign(&point.0, &self.2, &self.0);
self.contains(&point.0)
}
}

((sign_1 == sign_2) && (sign_2 == sign_3))
// ┌──────────────────────────────┐
// │ Implementations for Geometry │
// └──────────────────────────────┘

impl<T> Contains<Coordinate<T>> for Geometry<T>
where
T: Float,
{
fn contains(&self, coord: &Coordinate<T>) -> bool {
match self {
Geometry::Point(g) => g.contains(coord),
Geometry::Line(g) => g.contains(coord),
Geometry::LineString(g) => g.contains(coord),
Geometry::Polygon(g) => g.contains(coord),
Geometry::MultiPoint(g) => g.contains(coord),
Geometry::MultiLineString(g) => g.contains(coord),
Geometry::MultiPolygon(g) => g.contains(coord),
Geometry::GeometryCollection(g) => g.contains(coord),
Geometry::Rect(g) => g.contains(coord),
Geometry::Triangle(g) => g.contains(coord),
}
}
}

fn sign<T>(point_1: &Coordinate<T>, point_2: &Coordinate<T>, point_3: &Coordinate<T>) -> bool
impl<T> Contains<Point<T>> for Geometry<T>
where
T: CoordinateType,
T: Float,
{
fn contains(&self, point: &Point<T>) -> bool {
self.contains(&point.0)
}
}

// ┌────────────────────────────────────────┐
// │ Implementations for GeometryCollection │
// └────────────────────────────────────────┘

impl<T> Contains<Coordinate<T>> for GeometryCollection<T>
where
T: Float,
{
fn contains(&self, coord: &Coordinate<T>) -> bool {
self.0.iter().any(|geometry| geometry.contains(coord))
}
}

impl<T> Contains<Point<T>> for GeometryCollection<T>
where
T: Float,
{
(point_1.x - point_3.x) * (point_2.y - point_3.y)
- (point_2.x - point_3.x) * (point_1.y - point_3.y)
< T::zero()
fn contains(&self, point: &Point<T>) -> bool {
self.contains(&point.0)
}
}

// ┌───────┐
// │ Tests │
// └───────┘

#[cfg(test)]
mod test {
use crate::algorithm::contains::Contains;
Expand Down
9 changes: 5 additions & 4 deletions geo/src/algorithm/euclidean_distance.rs
@@ -1,7 +1,8 @@
use crate::algorithm::contains::{get_position, Contains, PositionPoint};
use crate::algorithm::contains::Contains;
use crate::algorithm::euclidean_length::EuclideanLength;
use crate::algorithm::intersects::Intersects;
use crate::algorithm::polygon_distance_fast_path::*;
use crate::utils::{coord_pos_relative_to_line_string, CoordPos};
use crate::{
Line, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon, Triangle,
};
Expand Down Expand Up @@ -260,9 +261,9 @@ fn ring_contains_point<T>(poly: &Polygon<T>, p: Point<T>) -> bool
where
T: Float,
{
match get_position(p, &poly.exterior()) {
PositionPoint::Inside => true,
PositionPoint::OnBoundary | PositionPoint::Outside => false,
match coord_pos_relative_to_line_string(p.0, &poly.exterior()) {
CoordPos::Inside => true,
CoordPos::OnBoundary | CoordPos::Outside => false,
}
}

Expand Down

0 comments on commit e0a23ac

Please sign in to comment.