Skip to content

Commit d6a376b

Browse files
authored
Merge pull request #1433 from cookiedan42/covers-base
Covers base
2 parents 5efc762 + 760d908 commit d6a376b

File tree

18 files changed

+1080
-2
lines changed

18 files changed

+1080
-2
lines changed

geo-benches/Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ name = "contains_properly"
5555
path = "src/contains_properly.rs"
5656
harness = false
5757

58+
[[bench]]
59+
name = "covers"
60+
path = "src/covers.rs"
61+
harness = false
62+
5863
[[bench]]
5964
name = "convex_hull"
6065
path = "src/convex_hull.rs"

geo-benches/src/covers.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
use criterion::{BenchmarkId, Criterion, Throughput, 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+
// bench a method derived from intersects
43+
fn rect_linestring_scaling(c: &mut Criterion) {
44+
fn make_outer_rect(n: i32) -> Rect<f64> {
45+
Rect::new(coord! {x:0,y:0}, coord! {x:n,y:3}).convert()
46+
}
47+
fn make_inner_ls(n: i32) -> LineString<f64> {
48+
LineString::new((1..n).map(|i: i32| coord! {x:i,y:(1+i%2)}).collect()).convert()
49+
}
50+
51+
{
52+
// create two polygons, both of of n+2 sides and no holes
53+
let mut group = c.benchmark_group("covers rect linestring scaling");
54+
55+
// trait is faster for small polygons, but relate overtakes from around 700*700 boundary segment checks
56+
for i in [10, 1_000, 100_000] {
57+
group.throughput(Throughput::Elements(i as u64));
58+
59+
let inner_poly = make_inner_ls(i);
60+
let outer_poly = make_outer_rect(i);
61+
62+
group.bench_with_input(
63+
BenchmarkId::new("covers trait", i),
64+
&(&outer_poly, &inner_poly),
65+
|bencher, &(a, b)| {
66+
bencher.iter(|| assert!(a.covers(b)));
67+
},
68+
);
69+
70+
// contains delegates this to relate
71+
group.bench_with_input(
72+
BenchmarkId::new("contains trait", i),
73+
&(&outer_poly, &inner_poly),
74+
|bencher, &(a, b)| {
75+
bencher.iter(|| assert!(a.contains(b)));
76+
},
77+
);
78+
}
79+
group.finish();
80+
}
81+
}
82+
83+
criterion_group!(
84+
benches,
85+
rect_covers,
86+
linestring_covers_point,
87+
rect_linestring_scaling
88+
);
89+
criterion_main!(benches);

geo/CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
- BREAKING: The `concave_hull` method now has no `concavity` parameter.
2020
- Add `concave_hull_with_options` method which requires `ConcaveHullOptions` as a parameter with `concavity` and `length_threshold` options.
2121
- <https://github.com/georust/geo/pull/1442>
22+
- Add `Covers` trait to relate and as a standalone operation
23+
- custom implementations for checking Geometries covered by `Rect`, `Triangle`, `Line`, `Point`, `Coord`
24+
- custom implementations for checking Geometries covering `Point` and `MultiPoint`
2225

2326
## 0.31.0 - 2025-09-01
2427

geo/src/algorithm/covers/coord.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
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+
// valid because self is convex geometry
17+
// all exterior pts of RHS intersecting self means self covers RHS
18+
impl_covers_from_intersects!(Coord<T>, [
19+
Point<T>, MultiPoint<T>,
20+
Line<T>,
21+
LineString<T>, MultiLineString<T>,
22+
Rect<T>, Triangle<T>,
23+
Polygon<T>, MultiPolygon<T>,
24+
Geometry<T>, GeometryCollection<T>
25+
]);
26+
27+
#[cfg(test)]
28+
mod tests {
29+
use super::*;
30+
31+
#[test]
32+
fn test_rhs_empty() {
33+
let s: Coord<f64> = Coord::zero();
34+
assert!(!s.covers(&LineString::empty()));
35+
assert!(!s.covers(&Polygon::empty()));
36+
assert!(!s.covers(&MultiPoint::empty()));
37+
assert!(!s.covers(&MultiLineString::empty()));
38+
assert!(!s.covers(&MultiPolygon::empty()));
39+
assert!(!s.covers(&GeometryCollection::empty()));
40+
}
41+
}
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
use super::Covers;
2+
use crate::GeoFloat;
3+
use crate::geometry::*;
4+
use crate::geometry_delegate_impl;
5+
6+
impl<T> Covers<Point<T>> for Geometry<T>
7+
where
8+
T: GeoFloat,
9+
{
10+
geometry_delegate_impl! {
11+
fn covers(&self, point: &Point<T>) -> bool;
12+
}
13+
}
14+
15+
impl<T> Covers<Line<T>> for Geometry<T>
16+
where
17+
T: GeoFloat,
18+
{
19+
geometry_delegate_impl! {
20+
fn covers(&self, line: &Line<T>) -> bool;
21+
}
22+
}
23+
24+
impl<T> Covers<LineString<T>> for Geometry<T>
25+
where
26+
T: GeoFloat,
27+
{
28+
geometry_delegate_impl! {
29+
fn covers(&self, line_string: &LineString<T>) -> bool;
30+
}
31+
}
32+
33+
impl<T> Covers<Polygon<T>> for Geometry<T>
34+
where
35+
T: GeoFloat,
36+
{
37+
geometry_delegate_impl! {
38+
fn covers(&self, polygon: &Polygon<T>) -> bool;
39+
}
40+
}
41+
42+
impl<T> Covers<MultiPoint<T>> for Geometry<T>
43+
where
44+
T: GeoFloat,
45+
{
46+
geometry_delegate_impl! {
47+
fn covers(&self, multi_point: &MultiPoint<T>) -> bool;
48+
}
49+
}
50+
51+
impl<T> Covers<MultiLineString<T>> for Geometry<T>
52+
where
53+
T: GeoFloat,
54+
{
55+
geometry_delegate_impl! {
56+
fn covers(&self, multi_line_string: &MultiLineString<T>) -> bool;
57+
}
58+
}
59+
60+
impl<T> Covers<MultiPolygon<T>> for Geometry<T>
61+
where
62+
T: GeoFloat,
63+
{
64+
geometry_delegate_impl! {
65+
fn covers(&self, multi_line_string: &MultiPolygon<T>) -> bool;
66+
}
67+
}
68+
69+
impl<T> Covers<GeometryCollection<T>> for Geometry<T>
70+
where
71+
T: GeoFloat,
72+
{
73+
geometry_delegate_impl! {
74+
fn covers(&self, geometry_collection: &GeometryCollection<T>) -> bool;
75+
}
76+
}
77+
78+
impl<T> Covers<Rect<T>> for Geometry<T>
79+
where
80+
T: GeoFloat,
81+
{
82+
geometry_delegate_impl! {
83+
fn covers(&self, rect: &Rect<T>) -> bool;
84+
}
85+
}
86+
87+
impl<T> Covers<Triangle<T>> for Geometry<T>
88+
where
89+
T: GeoFloat,
90+
{
91+
geometry_delegate_impl! {
92+
fn covers(&self, triangle: &Triangle<T>) -> bool;
93+
}
94+
}
95+
96+
impl<T> Covers<Geometry<T>> for Geometry<T>
97+
where
98+
T: GeoFloat,
99+
{
100+
geometry_delegate_impl! {
101+
fn covers(&self, other: &Geometry<T>) -> bool;
102+
}
103+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
use super::{Covers, impl_covers_from_relate, impl_covers_geometry_for};
2+
use crate::covers::impl_covers_from_intersects;
3+
use crate::geometry::*;
4+
use crate::{GeoFloat, GeoNum};
5+
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+
]);
14+
impl_covers_geometry_for!(GeometryCollection<T>);
15+
16+
#[cfg(test)]
17+
mod tests {
18+
use super::*;
19+
use crate::algorithm::convert::Convert;
20+
use crate::wkt;
21+
22+
#[test]
23+
fn test_rhs_empty() {
24+
let s: GeometryCollection<f64> = wkt!(GEOMETRYCOLLECTION(POINT(0 0))).convert();
25+
assert!(!s.covers(&LineString::empty()));
26+
assert!(!s.covers(&Polygon::empty()));
27+
assert!(!s.covers(&MultiPoint::empty()));
28+
assert!(!s.covers(&MultiLineString::empty()));
29+
assert!(!s.covers(&MultiPolygon::empty()));
30+
assert!(!s.covers(&GeometryCollection::empty()));
31+
}
32+
33+
#[test]
34+
fn test_lhs_empty() {
35+
let s: GeometryCollection<f64> = GeometryCollection::empty();
36+
37+
assert!(!s.covers(&wkt!(POINT(0 0)).convert()));
38+
assert!(!s.covers(&wkt!(MULTIPOINT(0 0,1 1)).convert()));
39+
40+
assert!(!s.covers(&wkt!(LINE(0 0,1 1)).convert()));
41+
assert!(!s.covers(&wkt!(LINESTRING(0 0,1 1)).convert()));
42+
assert!(!s.covers(&wkt!(MULTILINESTRING((0 0,1 1),(2 2,3 3))).convert()));
43+
44+
assert!(!s.covers(&wkt!(POLYGON((0 0,1 1,1 0,0 0))).convert()));
45+
assert!(!s.covers(&wkt!(MULTIPOLYGON(((0 0,1 0, 1 1,0 1, 0 0)))).convert()));
46+
assert!(!s.covers(&wkt!(RECT(0 0, 1 1)).convert()));
47+
assert!(!s.covers(&wkt!(TRIANGLE(0 0, 0 1, 1 1)).convert()));
48+
49+
assert!(!s.covers(&Into::<Geometry>::into(wkt!(POINT(0 0)).convert())));
50+
assert!(!s.covers(&wkt!(GEOMETRYCOLLECTION(POINT(0 0))).convert()));
51+
}
52+
}

geo/src/algorithm/covers/line.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
use super::{Covers, impl_covers_from_intersects};
2+
use crate::GeoNum;
3+
use crate::geometry::*;
4+
5+
// valid because self is convex geometry
6+
// all exterior pts of RHS intersecting self means self covers RHS
7+
impl_covers_from_intersects!(Line<T>, [
8+
Point<T>, MultiPoint<T>,
9+
Line<T>,
10+
LineString<T>, MultiLineString<T>,
11+
Rect<T>, Triangle<T>,
12+
Polygon<T>, MultiPolygon<T> ,
13+
Geometry<T>, GeometryCollection<T>
14+
]);
15+
16+
#[cfg(test)]
17+
mod tests {
18+
use super::*;
19+
use crate::algorithm::convert::Convert;
20+
use crate::wkt;
21+
22+
#[test]
23+
fn test_rhs_empty() {
24+
let s: Line<f64> = wkt!(LINE(0 0,1 1)).convert();
25+
assert!(!s.covers(&LineString::empty()));
26+
assert!(!s.covers(&Polygon::empty()));
27+
assert!(!s.covers(&MultiPoint::empty()));
28+
assert!(!s.covers(&MultiLineString::empty()));
29+
assert!(!s.covers(&MultiPolygon::empty()));
30+
assert!(!s.covers(&GeometryCollection::empty()));
31+
}
32+
}

0 commit comments

Comments
 (0)