Skip to content

Commit 676e638

Browse files
author
cookiedan42
committed
Add some basic custom impl from intersects
1 parent 061dfff commit 676e638

File tree

16 files changed

+311
-231
lines changed

16 files changed

+311
-231
lines changed

geo/CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
- LOF algorithm efficiency improvements due to caching kth distance
88
- Add DBSCAN clustering algorithm implementation
99
- Add `distance_within` method with default impl for any geometry that implements `Distance`, with similar semantics to the PostGIS [ST_DWithin](https://postgis.net/docs/ST_DWithin.html) function
10+
- Add `Covers` trait, a simplification of `Contains` in which geometries in the boundary are considered to be Covered
1011

1112
## 0.31.0 - 2025-09-01
1213

geo/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ harness = false
5656
name = "contains"
5757
harness = false
5858

59+
[[bench]]
60+
name = "covers"
61+
harness = false
62+
5963
[[bench]]
6064
name = "convex_hull"
6165
harness = false

geo/benches/covers.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use criterion::{Criterion, criterion_group, criterion_main};
2+
use geo::algorithm::{Contains, Covers};
3+
use geo::{Convert, wkt};
4+
use geo::{coord, geometry::*};
5+
6+
fn rect_covers(c: &mut Criterion) {
7+
c.bench_function("rect covers line", |bencher| {
8+
let rect = Rect::new(coord! { x: 0., y: 0. }, coord! { x: 10., y: 10. });
9+
let line = Line::new(coord! { x: 5., y: 5. }, coord! { x: 7., y: 7. });
10+
bencher.iter(|| {
11+
assert!(criterion::black_box(&rect).covers(criterion::black_box(&line)));
12+
});
13+
});
14+
15+
c.bench_function("rect contains line", |bencher| {
16+
let rect = Rect::new(coord! { x: 0., y: 0. }, coord! { x: 10., y: 10. });
17+
let line = Line::new(coord! { x: 5., y: 5. }, coord! { x: 7., y: 7. });
18+
bencher.iter(|| {
19+
assert!(criterion::black_box(&rect).contains(criterion::black_box(&line)));
20+
});
21+
});
22+
}
23+
24+
fn linestring_covers_point(c: &mut Criterion) {
25+
c.bench_function("linestring covers point", |bencher| {
26+
let ls: LineString<f64> = wkt! {LINESTRING(0 0, 10 0, 10 10, 0 10)}.convert();
27+
let pt: Point<f64> = wkt! {POINT(5 10)}.convert();
28+
bencher.iter(|| {
29+
assert!(criterion::black_box(&ls).covers(criterion::black_box(&pt)));
30+
});
31+
});
32+
33+
c.bench_function("linestring contains point", |bencher| {
34+
let ls: LineString<f64> = wkt! {LINESTRING(0 0, 10 0, 10 10, 0 10)}.convert();
35+
let pt: Point<f64> = wkt! {POINT(5 10)}.convert();
36+
bencher.iter(|| {
37+
assert!(criterion::black_box(&ls).contains(criterion::black_box(&pt)));
38+
});
39+
});
40+
}
41+
42+
criterion_group!(benches, rect_covers, linestring_covers_point,);
43+
criterion_main!(benches);

geo/src/algorithm/covers/coord.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
use super::{Covers, impl_covers_from_intersects};
2+
use crate::GeoNum;
3+
use crate::Intersects;
4+
use crate::geometry::*;
5+
6+
impl<T, G> Covers<Coord<T>> for G
7+
where
8+
T: GeoNum,
9+
G: Intersects<Coord<T>>,
10+
{
11+
fn covers(&self, rhs: &Coord<T>) -> bool {
12+
self.intersects(rhs)
13+
}
14+
}
15+
16+
impl_covers_from_intersects!(Coord<T>, [
17+
Point<T>, MultiPoint<T>,
18+
Line<T>,
19+
LineString<T>, MultiLineString<T>,
20+
Rect<T>, Triangle<T>,
21+
Polygon<T>, MultiPolygon<T>,
22+
Geometry<T>, GeometryCollection<T>
23+
]);

geo/src/algorithm/covers/geometry.rs

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,7 @@
11
use super::Covers;
2+
use crate::GeoFloat;
23
use crate::geometry::*;
34
use crate::geometry_delegate_impl;
4-
use crate::{GeoFloat, GeoNum};
5-
6-
impl<T> Covers<Coord<T>> for Geometry<T>
7-
where
8-
T: GeoFloat,
9-
{
10-
fn covers(&self, coord: &Coord<T>) -> bool {
11-
self.covers(&Point::from(*coord))
12-
}
13-
}
145

156
impl<T> Covers<Point<T>> for Geometry<T>
167
where
@@ -106,18 +97,7 @@ impl<T> Covers<Geometry<T>> for Geometry<T>
10697
where
10798
T: GeoFloat,
10899
{
109-
fn covers(&self, other: &Geometry<T>) -> bool {
110-
match other {
111-
Geometry::Point(geom) => self.covers(geom),
112-
Geometry::Line(geom) => self.covers(geom),
113-
Geometry::LineString(geom) => self.covers(geom),
114-
Geometry::Polygon(geom) => self.covers(geom),
115-
Geometry::MultiPoint(geom) => self.covers(geom),
116-
Geometry::MultiLineString(geom) => self.covers(geom),
117-
Geometry::MultiPolygon(geom) => self.covers(geom),
118-
Geometry::GeometryCollection(geom) => self.covers(geom),
119-
Geometry::Rect(geom) => self.covers(geom),
120-
Geometry::Triangle(geom) => self.covers(geom),
121-
}
100+
geometry_delegate_impl! {
101+
fn covers(&self, other: &Geometry<T>) -> bool;
122102
}
123103
}
Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,14 @@
1-
use super::{impl_covers_from_relate, impl_covers_geometry_for, Covers};
1+
use super::{Covers, impl_covers_from_relate, impl_covers_geometry_for};
2+
use crate::covers::impl_covers_from_intersects;
23
use crate::geometry::*;
34
use crate::{GeoFloat, GeoNum};
45

5-
impl<T> Covers<Coord<T>> for GeometryCollection<T>
6-
where
7-
T: GeoFloat,
8-
{
9-
fn covers(&self, rhs: &Coord<T>) -> bool {
10-
self.iter().any(|geometry| geometry.covers(rhs))
11-
}
12-
}
13-
14-
impl<T> Covers<Point<T>> for GeometryCollection<T>
15-
where
16-
T: GeoFloat,
17-
{
18-
fn covers(&self, point: &Point<T>) -> bool {
19-
self.covers(&point.0)
20-
}
21-
}
22-
23-
impl_covers_from_relate!(GeometryCollection<T>, [Line<T>, LineString<T>, Polygon<T>, MultiPoint<T>, MultiLineString<T>, MultiPolygon<T>, GeometryCollection<T>, Rect<T>, Triangle<T>]);
6+
impl_covers_from_intersects!(GeometryCollection<T>,[Point<T>,MultiPoint<T>]);
7+
impl_covers_from_relate!(GeometryCollection<T>, [
8+
Line<T>,
9+
LineString<T>, MultiLineString<T>,
10+
Rect<T>, Triangle<T>,
11+
Polygon<T>, MultiPolygon<T>,
12+
GeometryCollection<T>
13+
]);
2414
impl_covers_geometry_for!(GeometryCollection<T>);

geo/src/algorithm/covers/line.rs

Lines changed: 11 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,12 @@
1-
use super::{impl_covers_from_relate, Covers};
2-
use crate::{geometry::*, Intersects};
3-
use crate::{GeoFloat, GeoNum};
1+
use super::{Covers, impl_covers_from_intersects};
2+
use crate::GeoNum;
3+
use crate::geometry::*;
44

5-
impl<T> Covers<Coord<T>> for Line<T>
6-
where
7-
T: GeoFloat,
8-
Self: Covers<Point<T>>,
9-
{
10-
fn covers(&self, rhs: &Coord<T>) -> bool {
11-
self.covers(&Point::new(rhs.x, rhs.y))
12-
}
13-
}
14-
15-
impl<T> Covers<Point<T>> for Line<T>
16-
where
17-
T: GeoFloat,
18-
{
19-
fn covers(&self, rhs: &Point<T>) -> bool {
20-
self.intersects(rhs)
21-
}
22-
}
23-
impl_covers_from_relate!(Line<T>, [MultiPoint<T>]);
24-
impl_covers_from_relate!(Line<T>, [Line<T>]);
25-
impl_covers_from_relate!(Line<T>, [ LineString<T>, MultiLineString<T>]);
26-
impl_covers_from_relate!(Line<T>, [ Rect<T>, Triangle<T>]);
27-
impl_covers_from_relate!(Line<T>, [Polygon<T>, MultiPolygon<T>]);
28-
impl_covers_from_relate!(Line<T>, [Geometry<T>, GeometryCollection<T>]);
5+
impl_covers_from_intersects!(Line<T>, [
6+
Point<T>, MultiPoint<T>,
7+
Line<T>,
8+
LineString<T>, MultiLineString<T>,
9+
Rect<T>, Triangle<T>,
10+
Polygon<T>, MultiPolygon<T> ,
11+
Geometry<T>, GeometryCollection<T>
12+
]);
Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,46 @@
1-
use super::{impl_covers_from_relate, Covers};
2-
use crate::geometry::*;
1+
use super::{Covers, impl_covers_from_intersects, impl_covers_from_relate};
2+
use crate::{Contains, HasDimensions, geometry::*};
33
use crate::{GeoFloat, GeoNum};
44

5-
impl<T> Covers<Coord<T>> for LineString<T>
5+
impl_covers_from_intersects!(LineString<T>, [Point<T>, MultiPoint<T>]);
6+
7+
impl<T> Covers<Line<T>> for LineString<T>
68
where
7-
T: GeoFloat,
8-
Self: Covers<Point<T>>,
9+
T: GeoNum,
910
{
10-
fn covers(&self, rhs: &Coord<T>) -> bool {
11-
self.covers(&Point::new(rhs.x, rhs.y))
11+
fn covers(&self, rhs: &Line<T>) -> bool {
12+
if rhs.start == rhs.end {
13+
self.covers(&rhs.start)
14+
} else {
15+
self.contains(rhs)
16+
}
1217
}
1318
}
14-
impl_covers_from_relate!(LineString<T>, [Point<T>, MultiPoint<T>]);
15-
impl_covers_from_relate!(LineString<T>, [Line<T>]);
16-
impl_covers_from_relate!(LineString<T>, [ LineString<T>, MultiLineString<T>]);
17-
impl_covers_from_relate!(LineString<T>, [ Rect<T>, Triangle<T>]);
18-
impl_covers_from_relate!(LineString<T>, [Polygon<T>, MultiPolygon<T>]);
19-
impl_covers_from_relate!(LineString<T>, [Geometry<T>, GeometryCollection<T>]);
2019

21-
impl<T> Covers<Coord<T>> for MultiLineString<T>
20+
impl<T> Covers<LineString<T>> for LineString<T>
2221
where
23-
T: GeoFloat,
24-
Self: Covers<Point<T>>,
22+
T: GeoNum,
2523
{
26-
fn covers(&self, rhs: &Coord<T>) -> bool {
27-
self.covers(&Point::new(rhs.x, rhs.y))
24+
fn covers(&self, rhs: &LineString<T>) -> bool {
25+
if self.is_empty() || rhs.is_empty() {
26+
return false;
27+
}
28+
rhs.lines().all(|l| self.covers(&l))
2829
}
2930
}
30-
impl_covers_from_relate!(MultiLineString<T>, [Point<T>, MultiPoint<T>]);
31-
impl_covers_from_relate!(MultiLineString<T>, [Line<T>]);
32-
impl_covers_from_relate!(MultiLineString<T>, [ LineString<T>, MultiLineString<T>]);
33-
impl_covers_from_relate!(MultiLineString<T>, [ Rect<T>, Triangle<T>]);
34-
impl_covers_from_relate!(MultiLineString<T>, [Polygon<T>, MultiPolygon<T>]);
35-
impl_covers_from_relate!(MultiLineString<T>, [Geometry<T>, GeometryCollection<T>]);
31+
32+
impl_covers_from_relate!(LineString<T>, [
33+
MultiLineString<T>,
34+
Rect<T>, Triangle<T>,
35+
Polygon<T>, MultiPolygon<T>,
36+
Geometry<T>, GeometryCollection<T>
37+
]);
38+
39+
impl_covers_from_intersects!(MultiLineString<T>, [Point<T>, MultiPoint<T>]);
40+
impl_covers_from_relate!(MultiLineString<T>, [
41+
Line<T>,
42+
LineString<T>, MultiLineString<T>,
43+
Rect<T>, Triangle<T>,
44+
Polygon<T>, MultiPolygon<T>,
45+
Geometry<T>, GeometryCollection<T>
46+
]);

geo/src/algorithm/covers/mod.rs

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
/// Checks if `rhs` is completely contained within `self`.
2-
/// More formally, the interior of `rhs` has non-empty
3-
/// (set-theoretic) intersection but neither the interior,
4-
/// nor the boundary of `rhs` intersects the exterior of
5-
/// `self`. In other words, the [DE-9IM] intersection matrix
6-
/// of `(rhs, self)` is `T*F**F***`.
1+
/// Checks if every point in `rhs` lies inside `self`.
2+
/// (i.e. intersects the interior or boundary of) `self`.
3+
/// Equivalently, tests that no point of `other` lies outside (in the exterior of) `self`
4+
/// In other words, the [DE-9IM] intersection matrix
5+
/// of `(rhs, self)` matches one of the following patterns:
6+
/// `[T*****FF*], [*T****FF*], [***T**FF*], [****T*FF*]`
77
///
88
/// [DE-9IM]: https://en.wikipedia.org/wiki/DE-9IM
99
///
@@ -32,13 +32,19 @@
3232
/// // Point in Polygon
3333
/// assert!(polygon.covers(&point!(x: 1., y: 1.)));
3434
/// ```
35+
///
36+
/// # Performance Note
37+
/// Much of this trait is currently implemented by delegating to the Relate trait.
38+
/// Custom Contains implementations might be faster if you need speed
39+
///
3540
pub trait Covers<Rhs = Self> {
3641
fn covers(&self, rhs: &Rhs) -> bool;
3742
}
3843

44+
pub(crate) mod coord;
3945
pub(crate) mod line_string;
40-
pub(crate)mod point;
41-
pub(crate)mod polygon;
46+
pub(crate) mod point;
47+
pub(crate) mod polygon;
4248

4349
pub(crate) mod line;
4450
pub(crate) mod rect;
@@ -64,23 +70,54 @@ macro_rules! impl_covers_from_relate {
6470
}
6571
pub(crate) use impl_covers_from_relate;
6672

73+
// returns true if all coords of rhs intersects with self
74+
// always valid if self is Point, Line or Convex Polygon (Rect/Triangle)
75+
// always valid if other is Point or MultiPoint
76+
macro_rules! impl_covers_from_intersects {
77+
78+
(Coord<T>, [$($target:ty),*]) => {
79+
80+
$(
81+
impl<T> Covers<$target> for Coord<T>
82+
where
83+
T: GeoNum,
84+
Self: $crate::algorithm::Intersects<Coord<T>>,
85+
{
86+
fn covers(&self, target: &$target) -> bool {
87+
use $crate::CoordsIter;
88+
use $crate::algorithm::Intersects;
89+
use $crate::algorithm::HasDimensions;
90+
if HasDimensions::is_empty(target) {
91+
return false;
92+
}
93+
target.coords_iter().all(|pt| self.intersects(&pt))
94+
}
95+
}
96+
)*
97+
};
6798

68-
macro_rules! impl_covers_convex_poly {
6999
($for:ty, [$($target:ty),*]) => {
100+
70101
$(
71102
impl<T> Covers<$target> for $for
72103
where
73104
T: GeoNum,
74-
Self: Intersects<Coord<T>>,
105+
Self: $crate::algorithm::Intersects<Coord<T>>,
75106
{
76107
fn covers(&self, target: &$target) -> bool {
108+
use $crate::CoordsIter;
109+
use $crate::algorithm::Intersects;
110+
use $crate::algorithm::HasDimensions;
111+
if HasDimensions::is_empty(self) || HasDimensions::is_empty(target){
112+
return false;
113+
}
77114
target.coords_iter().all(|pt| self.intersects(&pt))
78115
}
79116
}
80117
)*
81118
};
82119
}
83-
pub(crate) use impl_covers_convex_poly;
120+
pub(crate) use impl_covers_from_intersects;
84121

85122
macro_rules! impl_covers_geometry_for {
86123
($geom_type: ty) => {
@@ -113,9 +150,8 @@ pub(crate) use impl_covers_geometry_for;
113150

114151
#[cfg(test)]
115152
mod test {
116-
use crate::line_string;
117153
use crate::Covers;
118-
use crate::Relate;
154+
use crate::line_string;
119155
use crate::*;
120156

121157
#[test]

0 commit comments

Comments
 (0)