forked from georust/geo
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into mkirk/im-matches
- Loading branch information
Showing
19 changed files
with
1,794 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
#[macro_use] | ||
extern crate criterion; | ||
|
||
extern crate geo; | ||
|
||
use std::fmt::Display; | ||
use std::panic::catch_unwind; | ||
|
||
use criterion::measurement::Measurement; | ||
use geo::coordinate_position::CoordPos; | ||
use geo::monotone::monotone_subdivision; | ||
use geo::{CoordinatePosition, MapCoords, Polygon}; | ||
|
||
use criterion::{BatchSize, BenchmarkGroup, BenchmarkId, Criterion}; | ||
use geo_types::{Coord, Rect}; | ||
use wkt::ToWkt; | ||
|
||
#[path = "utils/random.rs"] | ||
mod random; | ||
use rand::thread_rng; | ||
use random::*; | ||
|
||
fn criterion_benchmark_pt_in_poly(c: &mut Criterion) { | ||
let pt_samples = Samples::from_fn(512, || { | ||
uniform_point(&mut thread_rng(), Rect::new((-1., -1.), (1., 1.))) | ||
}); | ||
|
||
for size in [16, 64, 512, 1024, 2048] { | ||
let mut grp = c.benchmark_group("rand pt-in-poly steppy-polygon (worst case)".to_string()); | ||
let poly = steppy_polygon(&mut thread_rng(), size); | ||
bench_pt_in_poly(&mut grp, poly, size, &pt_samples) | ||
} | ||
for size in [16, 64, 512, 1024, 2048] { | ||
let mut grp = c.benchmark_group("rand pt-in-poly steppy-polygon (best case)".to_string()); | ||
let poly = steppy_polygon(&mut thread_rng(), size).map_coords(|c| (c.y, c.x).into()); | ||
bench_pt_in_poly(&mut grp, poly, size, &pt_samples) | ||
} | ||
for size in [16, 64, 512, 1024, 2048] { | ||
let mut grp = c.benchmark_group("rand pt-in-poly circular-polygon".to_string()); | ||
let poly = circular_polygon(&mut thread_rng(), size); | ||
bench_pt_in_poly(&mut grp, poly, size, &pt_samples) | ||
} | ||
} | ||
|
||
fn bench_pt_in_poly<T, I>( | ||
g: &mut BenchmarkGroup<T>, | ||
polygon: Polygon<f64>, | ||
param: I, | ||
samples: &Samples<Coord<f64>>, | ||
) where | ||
T: Measurement, | ||
I: Display + Copy, | ||
{ | ||
let mon = match catch_unwind(|| monotone_subdivision([polygon.clone()])) { | ||
Ok(m) => m, | ||
Err(_) => { | ||
panic!( | ||
"Monotone subdivision failed for polygon: {}", | ||
polygon.to_wkt() | ||
); | ||
} | ||
}; | ||
|
||
g.bench_with_input( | ||
BenchmarkId::new("Simple point-in-poly", param), | ||
&(), | ||
|b, _| { | ||
b.iter_batched( | ||
samples.sampler(), | ||
|pt| { | ||
polygon.coordinate_position(pt); | ||
}, | ||
BatchSize::SmallInput, | ||
); | ||
}, | ||
); | ||
g.bench_with_input( | ||
BenchmarkId::new("Pre-processed point-in-poly", param), | ||
&(), | ||
|b, _| { | ||
b.iter_batched( | ||
samples.sampler(), | ||
|pt| { | ||
mon.iter() | ||
.filter(|mp| mp.coordinate_position(pt) == CoordPos::Inside) | ||
.count(); | ||
}, | ||
BatchSize::SmallInput, | ||
); | ||
}, | ||
); | ||
} | ||
|
||
fn criterion_benchmark_monotone_subdiv(c: &mut Criterion) { | ||
for size in [16, 64, 2048, 32000] { | ||
let mut grp = c.benchmark_group("monotone_subdiv steppy-polygon (worst case)".to_string()); | ||
let poly_fn = |size| steppy_polygon(&mut thread_rng(), size); | ||
bench_monotone_subdiv(&mut grp, poly_fn, size) | ||
} | ||
for size in [16, 64, 2048, 32000] { | ||
let mut grp = c.benchmark_group("monotone_subdiv steppy-polygon (best case)".to_string()); | ||
let poly_fn = | ||
|size| steppy_polygon(&mut thread_rng(), size).map_coords(|c| (c.y, c.x).into()); | ||
bench_monotone_subdiv(&mut grp, poly_fn, size) | ||
} | ||
for size in [16, 64, 2048, 32000] { | ||
let mut grp = c.benchmark_group("monotone_subdiv circular-polygon".to_string()); | ||
let poly_fn = |size| circular_polygon(&mut thread_rng(), size); | ||
bench_monotone_subdiv(&mut grp, poly_fn, size) | ||
} | ||
} | ||
|
||
fn bench_monotone_subdiv<T, F>(g: &mut BenchmarkGroup<T>, mut f: F, param: usize) | ||
where | ||
T: Measurement, | ||
F: FnMut(usize) -> Polygon<f64>, | ||
{ | ||
let samples = Samples::from_fn(16, || f(param)); | ||
g.bench_with_input( | ||
BenchmarkId::new("Montone subdivision", param), | ||
&(), | ||
|b, _| { | ||
b.iter_batched( | ||
samples.sampler(), | ||
|pt| { | ||
let mon = monotone_subdivision([pt.clone()]); | ||
mon.len(); | ||
}, | ||
BatchSize::SmallInput, | ||
); | ||
}, | ||
); | ||
} | ||
|
||
criterion_group!( | ||
benches, | ||
criterion_benchmark_pt_in_poly, | ||
criterion_benchmark_monotone_subdiv | ||
); | ||
criterion_main!(benches); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
use crate::algorithm::EuclideanDistance; | ||
use crate::CoordsIter; | ||
use crate::GeoFloat; | ||
use geo_types::{Coord, Point}; | ||
use num_traits::Bounded; | ||
|
||
/// Determine the distance between two geometries using the [Hausdorff distance formula]. | ||
/// | ||
/// Hausdorff distance is used to compare two point sets. It measures the maximum euclidean | ||
/// distance of a point in one set to the nearest point in another set. Hausdorff distance | ||
/// is often used to measure the amount of mismatch between two sets. | ||
/// | ||
/// [Hausdorff distance formula]: https://en.wikipedia.org/wiki/Hausdorff_distance | ||
pub trait HausdorffDistance<T> | ||
where | ||
T: GeoFloat, | ||
{ | ||
fn hausdorff_distance<Rhs>(&self, rhs: &Rhs) -> T | ||
where | ||
Rhs: for<'a> CoordsIter<'a, Scalar = T>; | ||
} | ||
|
||
impl<T, G> HausdorffDistance<T> for G | ||
where | ||
T: GeoFloat, | ||
G: for<'a> CoordsIter<'a, Scalar = T>, | ||
{ | ||
fn hausdorff_distance<Rhs>(&self, rhs: &Rhs) -> T | ||
where | ||
Rhs: for<'a> CoordsIter<'a, Scalar = T>, | ||
{ | ||
// calculate from A -> B | ||
let hd1 = self | ||
.coords_iter() | ||
.map(|c| { | ||
rhs.coords_iter() | ||
.map(|c2| c.euclidean_distance(&c2)) | ||
.fold(<T as Bounded>::max_value(), |accum, val| accum.min(val)) | ||
}) | ||
.fold(<T as Bounded>::min_value(), |accum, val| accum.max(val)); | ||
|
||
// Calculate from B -> A | ||
let hd2 = rhs | ||
.coords_iter() | ||
.map(|c| { | ||
self.coords_iter() | ||
.map(|c2| c.euclidean_distance(&c2)) | ||
.fold(<T as Bounded>::max_value(), |accum, val| accum.min(val)) | ||
}) | ||
.fold(<T as Bounded>::min_value(), |accum, val| accum.max(val)); | ||
|
||
// The max of the two | ||
hd1.max(hd2) | ||
} | ||
} | ||
|
||
// ┌───────────────────────────┐ | ||
// │ Implementations for Coord │ | ||
// └───────────────────────────┘ | ||
|
||
impl<T> HausdorffDistance<T> for Coord<T> | ||
where | ||
T: GeoFloat, | ||
{ | ||
fn hausdorff_distance<Rhs>(&self, rhs: &Rhs) -> T | ||
where | ||
Rhs: for<'a> CoordsIter<'a, Scalar = T>, | ||
{ | ||
Point::from(*self).hausdorff_distance(rhs) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use crate::HausdorffDistance; | ||
use crate::{line_string, polygon, MultiPoint, MultiPolygon}; | ||
|
||
#[test] | ||
fn hd_mpnt_mpnt() { | ||
let p1: MultiPoint<_> = vec![(0., 0.), (1., 2.)].into(); | ||
let p2: MultiPoint<_> = vec![(2., 3.), (1., 2.)].into(); | ||
assert_relative_eq!(p1.hausdorff_distance(&p2), 2.236068, epsilon = 1.0e-6); | ||
} | ||
|
||
#[test] | ||
fn hd_mpnt_poly() { | ||
let p1: MultiPoint<_> = vec![(0., 0.), (1., 2.)].into(); | ||
let poly = polygon![ | ||
(x: 1., y: -3.1), (x: 3.7, y: 2.7), | ||
(x: 0.9, y: 7.6), (x: -4.8, y: 6.7), | ||
(x: -7.5, y: 0.9), (x: -4.7, y: -4.), | ||
(x: 1., y: -3.1) | ||
]; | ||
|
||
assert_relative_eq!(p1.hausdorff_distance(&poly), 7.553807, epsilon = 1.0e-6) | ||
} | ||
|
||
#[test] | ||
fn hd_mpnt_lns() { | ||
let p1: MultiPoint<_> = vec![(0., 0.), (1., 2.)].into(); | ||
let lns = line_string![ | ||
(x: 1., y: -3.1), (x: 3.7, y: 2.7), | ||
(x: 0.9, y: 7.6), (x: -4.8, y: 6.7), | ||
(x: -7.5, y: 0.9), (x: -4.7, y: -4.), | ||
(x: 1., y: -3.1) | ||
]; | ||
|
||
assert_relative_eq!(p1.hausdorff_distance(&lns), 7.553807, epsilon = 1.0e-6) | ||
} | ||
|
||
#[test] | ||
fn hd_mpnt_mply() { | ||
let p1: MultiPoint<_> = vec![(0., 0.), (1., 2.)].into(); | ||
let multi_polygon = MultiPolygon::new(vec![ | ||
polygon![ | ||
(x: 0.0f32, y: 0.0), | ||
(x: 2.0, y: 0.0), | ||
(x: 2.0, y: 1.0), | ||
(x: 0.0, y: 1.0), | ||
], | ||
polygon![ | ||
(x: 1.0, y: 1.0), | ||
(x: -2.0, y: 1.0), | ||
(x: -2.0, y: -1.0), | ||
(x: 1.0, y: -1.0), | ||
], | ||
]); | ||
|
||
assert_relative_eq!( | ||
p1.hausdorff_distance(&multi_polygon), | ||
2.236068, | ||
epsilon = 1.0e-6 | ||
) | ||
} | ||
} |
Oops, something went wrong.