From 48d5783bfd2a18a75728475f0b4d7008ac4fef99 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Thu, 5 May 2022 11:45:30 -0700 Subject: [PATCH 1/4] relate benches --- .../fixtures/east_baton_rouge.wkt | 1 + geo-test-fixtures/src/lib.rs | 83 ++++++++++++------- geo/benches/contains.rs | 63 +++++++++++--- geo/benches/relate.rs | 62 +++++++++++++- .../relate/geomgraph/intersection_matrix.rs | 2 +- jts-test-runner/src/input.rs | 2 +- jts-test-runner/src/runner.rs | 19 ++++- 7 files changed, 184 insertions(+), 48 deletions(-) create mode 100644 geo-test-fixtures/fixtures/east_baton_rouge.wkt diff --git a/geo-test-fixtures/fixtures/east_baton_rouge.wkt b/geo-test-fixtures/fixtures/east_baton_rouge.wkt new file mode 100644 index 000000000..1c384745a --- /dev/null +++ b/geo-test-fixtures/fixtures/east_baton_rouge.wkt @@ -0,0 +1 @@ +POLYGON((-91.316488 30.590003,-91.315282 30.594288,-91.309914 30.601297,-91.301863 30.609497,-91.29465 30.625207,-91.293072 30.629882,-91.292821 30.631539,-91.293596 30.63919,-91.294211 30.641591,-91.296807 30.648037,-91.297658 30.649548,-91.293604 30.655036,-91.292644 30.658244,-91.292608 30.662034,-91.289693 30.667624,-91.288731 30.677904,-91.27046 30.680864,-91.267699 30.686118,-91.268221 30.689126,-91.267518 30.690108,-91.267133 30.693745,-91.267882 30.69442,-91.265734 30.696264,-91.264865 30.698514,-91.260461 30.70129,-91.257352 30.702703,-91.254419 30.705294,-91.250223 30.705131,-91.216658 30.706386,-91.21374 30.706187,-91.193932 30.706939,-91.17396 30.707768,-91.169445 30.707978,-91.145132 30.708785,-91.145008 30.708785,-91.10764 30.710296,-91.101421 30.710579,-91.097704 30.710583,-91.078958 30.711282,-91.053393 30.712184,-91.023218 30.713182,-90.97269 30.715084,-90.968038 30.71528,-90.849041 30.719311,-90.849327 30.717661,-90.8475 30.71629,-90.847859 30.713883,-90.847421 30.712922,-90.845068 30.71246,-90.845888 30.70969,-90.844181 30.708047,-90.848658 30.70447,-90.849922 30.705858,-90.851101 30.70557,-90.851857 30.702998,-90.8537 30.702048,-90.854054 30.700822,-90.852545 30.700603,-90.85147 30.70141,-90.850499 30.700267,-90.8527 30.699303,-90.852985 30.695681,-90.854366 30.69515,-90.857988 30.695477,-90.859058 30.696311,-90.860664 30.6955,-90.859909 30.692764,-90.862696 30.691928,-90.865774 30.693349,-90.869114 30.692353,-90.869982 30.693779,-90.870916 30.693329,-90.870826 30.690951,-90.869282 30.689224,-90.872294 30.68704,-90.874186 30.686451,-90.876457 30.686588,-90.877024 30.684365,-90.879099 30.682584,-90.877092 30.680332,-90.87818 30.679072,-90.879783 30.678886,-90.881 30.677585,-90.8856 30.676884,-90.885735 30.67494,-90.8884 30.673584,-90.890389 30.673779,-90.890602 30.672414,-90.888798 30.671685,-90.888798 30.670584,-90.891899 30.671085,-90.893517 30.670603,-90.896569 30.668153,-90.893901 30.666485,-90.894875 30.66411,-90.8971 30.663685,-90.900459 30.665352,-90.90142 30.664068,-90.899698 30.662373,-90.89979 30.661149,-90.903225 30.659716,-90.902999 30.658187,-90.900677 30.658835,-90.899867 30.658084,-90.901455 30.655313,-90.902346 30.654722,-90.904169 30.655655,-90.904459 30.654692,-90.907358 30.655592,-90.907901 30.657185,-90.908792 30.65532,-90.911962 30.652148,-90.91224 30.65087,-90.910701 30.649385,-90.9096 30.647385,-90.911304 30.645989,-90.913582 30.646772,-90.9154 30.646685,-90.917001 30.643885,-90.916941 30.639759,-90.918315 30.638055,-90.920903 30.636684,-90.922799 30.637221,-90.924601 30.638786,-90.927337 30.637186,-90.9279 30.635085,-90.931854 30.631002,-90.935003 30.628286,-90.935672 30.626387,-90.937738 30.624671,-90.937629 30.622967,-90.939825 30.621947,-90.940636 30.620456,-90.938518 30.618039,-90.934782 30.619782,-90.935031 30.621483,-90.933528 30.620642,-90.932214 30.618409,-90.933206 30.616932,-90.936027 30.616147,-90.936051 30.615005,-90.934182 30.613829,-90.935098 30.612851,-90.936166 30.613716,-90.938669 30.612396,-90.939837 30.608952,-90.941707 30.608455,-90.944376 30.608685,-90.945809 30.609605,-90.947686 30.609826,-90.948301 30.606986,-90.947672 30.605735,-90.949101 30.604085,-90.952921 30.605533,-90.954707 30.604613,-90.956629 30.604644,-90.958064 30.605756,-90.960201 30.605203,-90.960117 30.603245,-90.958339 30.601296,-90.956783 30.600911,-90.956859 30.599497,-90.958215 30.598641,-90.959084 30.598267,-90.961037 30.59902,-90.960534 30.600678,-90.961634 30.600981,-90.962748 30.600001,-90.962898 30.596731,-90.963803 30.593925,-90.965602 30.593685,-90.96622 30.59447,-90.965551 30.597404,-90.971102 30.597383,-90.972247 30.596887,-90.972208 30.59385,-90.97352 30.592471,-90.975823 30.593886,-90.977401 30.593485,-90.977979 30.592319,-90.977564 30.59078,-90.979533 30.590148,-90.980695 30.590779,-90.980497 30.589081,-90.979403 30.587586,-90.980489 30.584757,-90.982203 30.584408,-90.985103 30.585601,-90.985601 30.584686,-90.985515 30.581627,-90.983835 30.579256,-90.986101 30.575986,-90.986141 30.574711,-90.987906 30.572643,-90.986843 30.571579,-90.98211 30.571012,-90.980464 30.568821,-90.976555 30.568453,-90.975718 30.567385,-90.975505 30.565471,-90.97204 30.564175,-90.971431 30.562004,-90.973216 30.56012,-90.972407 30.557335,-90.973276 30.554538,-90.972628 30.552923,-90.972012 30.550828,-90.972958 30.549596,-90.97566 30.547689,-90.975924 30.545495,-90.972802 30.543188,-90.972952 30.540928,-90.974033 30.539505,-90.977519 30.538068,-90.980003 30.537738,-90.981512 30.536387,-90.980931 30.535555,-90.980266 30.534383,-90.981441 30.532891,-90.983745 30.531724,-90.984385 30.530637,-90.982326 30.529691,-90.980723 30.530385,-90.979274 30.529695,-90.97816 30.527677,-90.977473 30.525067,-90.979487 30.523423,-90.981486 30.523259,-90.982197 30.522114,-90.978328 30.521299,-90.978327 30.519752,-90.980463 30.51837,-90.977727 30.515117,-90.975258 30.513965,-90.974228 30.512707,-90.971469 30.511803,-90.971333 30.509198,-90.971001 30.507389,-90.968744 30.506094,-90.96789 30.504381,-90.970957 30.503431,-90.974452 30.501177,-90.975929 30.498349,-90.97374 30.497059,-90.972221 30.497844,-90.970319 30.497527,-90.970035 30.495571,-90.970097 30.492371,-90.971382 30.491452,-90.973126 30.491844,-90.971892 30.489895,-90.972898 30.487617,-90.971933 30.485544,-90.975451 30.483899,-90.974807 30.481763,-90.974857 30.481508,-90.976008 30.480015,-90.978266 30.479956,-90.978662 30.479098,-90.976877 30.476923,-90.975065 30.476003,-90.975122 30.473147,-90.974268 30.471019,-90.976516 30.467267,-90.979607 30.467421,-90.981062 30.466533,-90.984251 30.468332,-90.985709 30.467645,-90.986354 30.466202,-90.988174 30.466515,-90.98923 30.465831,-90.989892 30.46488,-90.98995 30.464738,-90.991475 30.461954,-90.991053 30.460404,-90.989008 30.45866,-90.989195 30.456173,-90.988558 30.455145,-90.98505 30.45327,-90.981157 30.452765,-90.9815 30.451013,-90.983841 30.450237,-90.986847 30.450277,-90.986932 30.448361,-90.985706 30.44761,-90.984245 30.445308,-90.982808 30.444307,-90.981307 30.441883,-90.979362 30.440882,-90.977349 30.43879,-90.976358 30.438531,-90.974558 30.440124,-90.972402 30.440302,-90.973444 30.437701,-90.978066 30.434069,-90.980239 30.433383,-90.981034 30.432144,-90.981048 30.43035,-90.978957 30.42855,-90.982536 30.425479,-90.982351 30.423465,-90.981158 30.422263,-90.977503 30.420899,-90.970717 30.420846,-90.969301 30.420472,-90.967425 30.418801,-90.966114 30.418385,-90.964151 30.418868,-90.963312 30.417981,-90.966053 30.415194,-90.965794 30.414713,-90.963243 30.413719,-90.961949 30.411219,-90.958539 30.407409,-90.957922 30.406076,-90.958435 30.405233,-90.96144 30.403074,-90.963283 30.402806,-90.969401 30.405138,-90.972067 30.402318,-90.972346 30.397519,-90.968888 30.393287,-90.967888 30.39134,-90.968264 30.389151,-90.967357 30.388702,-90.96421 30.390357,-90.96224 30.388391,-90.961391 30.388144,-90.957574 30.388612,-90.954095 30.389642,-90.951012 30.392011,-90.9501 30.394092,-90.949197 30.394445,-90.946648 30.393529,-90.943917 30.391733,-90.942849 30.389448,-90.94468 30.386335,-90.947213 30.384939,-90.9446 30.383792,-90.944817 30.381551,-90.9459 30.379892,-90.9436 30.377992,-90.9437 30.376492,-90.940273 30.374184,-90.937695 30.373883,-90.932906 30.372749,-90.931533 30.371282,-90.9325 30.369697,-90.935477 30.367401,-90.93723 30.366565,-90.937527 30.364746,-90.936957 30.363913,-90.935037 30.36384,-90.934133 30.365846,-90.933056 30.365912,-90.931229 30.364373,-90.930764 30.361974,-90.929417 30.361873,-90.927622 30.364148,-90.926345 30.363723,-90.923101 30.364613,-90.920296 30.365947,-90.91807 30.364954,-90.917549 30.362627,-90.915434 30.361214,-90.915586 30.359497,-90.913523 30.358673,-90.910967 30.359404,-90.909935 30.360531,-90.907063 30.361196,-90.905379 30.36105,-90.903385 30.359774,-90.903678 30.35775,-90.905148 30.357207,-90.907226 30.35772,-90.90805 30.356018,-90.90647 30.355179,-90.903354 30.35564,-90.902341 30.355314,-90.900434 30.352134,-90.900119 30.349623,-90.900596 30.347745,-90.899754 30.347442,-90.897515 30.347981,-90.895577 30.347851,-90.891728 30.345244,-90.894305 30.345859,-90.900194 30.34251,-90.902606 30.342794,-90.904555 30.340624,-90.907057 30.341907,-90.908935 30.340863,-90.910074 30.339713,-90.912256 30.340706,-90.917343 30.340885,-90.917349 30.343786,-90.918851 30.345509,-90.920685 30.345714,-90.923852 30.34413,-90.927625 30.34334,-90.928926 30.341574,-90.930109 30.341389,-90.930743 30.342591,-90.93187 30.342683,-90.933619 30.341476,-90.935656 30.341839,-90.939418 30.340313,-90.941341 30.341117,-90.943858 30.341101,-90.945635 30.343197,-90.948525 30.342826,-90.948581 30.344706,-90.949289 30.345356,-90.951115 30.344634,-90.95311 30.345887,-90.954946 30.345216,-90.956912 30.345644,-90.958892 30.345139,-90.962884 30.345529,-90.963742 30.346704,-90.965546 30.346769,-90.968094 30.345804,-90.969738 30.345891,-90.974123 30.346933,-90.975656 30.346437,-90.976249 30.344656,-90.977364 30.343951,-90.981803 30.344331,-90.983178 30.343893,-90.98463 30.344003,-90.986968 30.345729,-90.988917 30.343089,-90.992883 30.342749,-90.994999 30.341604,-90.997494 30.341511,-91.000818 30.336556,-91.001367 30.33634,-91.006339 30.337795,-91.00778 30.337867,-91.009281 30.336015,-91.012205 30.335654,-91.014157 30.334779,-91.013825 30.330184,-91.014065 30.330029,-91.015148 30.329257,-91.016298 30.328273,-91.017168 30.326419,-91.017986 30.324094,-91.021014 30.321489,-91.025665 30.318262,-91.028271 30.31597,-91.032435 30.314822,-91.036783 30.314746,-91.040452 30.315318,-91.04499 30.31533,-91.046917 30.316134,-91.050206 30.31576,-91.052127 30.316288,-91.053483 30.31532,-91.053546 30.317465,-91.056481 30.317531,-91.058636 30.317081,-91.062717 30.318884,-91.064422 30.318148,-91.067805 30.319381,-91.071102 30.31966,-91.07951 30.319537,-91.082603 30.319693,-91.08633 30.31925,-91.09065 30.319365,-91.093004 30.320592,-91.095639 30.319262,-91.098798 30.319537,-91.10011 30.318793,-91.105204 30.317493,-91.107441 30.316062,-91.108504 30.315393,-91.113004 30.314893,-91.115459 30.312693,-91.118429 30.312469,-91.12013 30.313219,-91.125283 30.313232,-91.128323 30.314226,-91.132672 30.31341,-91.136277 30.315112,-91.137906 30.317194,-91.141478 30.3192,-91.142305 30.319893,-91.142042 30.322718,-91.142105 30.323293,-91.142439 30.324835,-91.145005 30.331293,-91.150801 30.337424,-91.151521 30.337809,-91.154105 30.339193,-91.161971 30.341528,-91.170333 30.343558,-91.174419 30.344187,-91.180251 30.345084,-91.187286 30.34651,-91.194507 30.346993,-91.204422 30.345198,-91.214093 30.342324,-91.222607 30.340593,-91.230307 30.341093,-91.235808 30.344493,-91.239534 30.351584,-91.241508 30.357592,-91.240617 30.362756,-91.233908 30.375292,-91.219429 30.389616,-91.215256 30.39401,-91.212807 30.397091,-91.208107 30.403691,-91.205707 30.407991,-91.199807 30.419091,-91.199056 30.422279,-91.198247 30.425935,-91.196307 30.434691,-91.195906 30.43939,-91.195907 30.43959,-91.196007 30.44689,-91.196114 30.452699,-91.196207 30.45779,-91.196507 30.46389,-91.196407 30.46639,-91.19653 30.469365,-91.196749 30.478845,-91.197205 30.506864,-91.198508 30.513089,-91.200808 30.517689,-91.203708 30.519789,-91.208004 30.522244,-91.209308 30.522989,-91.236101 30.513973,-91.253767 30.508208,-91.257014 30.506857,-91.262734 30.505238,-91.268258 30.504726,-91.274406 30.504613,-91.276486 30.504688,-91.281478 30.506115,-91.28276 30.507317,-91.284161 30.509635,-91.284735 30.511598,-91.284359 30.515339,-91.282767 30.51719,-91.280878 30.518573,-91.275351 30.521081,-91.265989 30.52658,-91.249475 30.533764,-91.246058 30.535851,-91.243333 30.540609,-91.242457 30.544218,-91.242312 30.547994,-91.24309 30.55314,-91.244409 30.555888,-91.247909 30.560688,-91.250109 30.562988,-91.254439 30.566543,-91.261522 30.571071,-91.264954 30.572176,-91.267121 30.572522,-91.280928 30.57295,-91.291498 30.572546,-91.295639 30.572228,-91.302914 30.573119,-91.305854 30.573027,-91.308012 30.573599,-91.310477 30.574679,-91.311825 30.575809,-91.314406 30.578817,-91.314839 30.579791,-91.316547 30.585244,-91.316488 30.590003)) diff --git a/geo-test-fixtures/src/lib.rs b/geo-test-fixtures/src/lib.rs index e36a3c23f..e627dfc23 100644 --- a/geo-test-fixtures/src/lib.rs +++ b/geo-test-fixtures/src/lib.rs @@ -1,6 +1,6 @@ use std::{fs, iter::FromIterator, path::PathBuf, str::FromStr}; -use geo_types::{LineString, MultiPolygon, Polygon}; +use geo_types::{LineString, MultiPolygon, Point, Polygon}; use wkt::{Geometry, Wkt, WktFloat}; pub fn louisiana() -> LineString @@ -10,6 +10,22 @@ where line_string("louisiana.wkt") } +pub fn baton_rouge() -> Point +where + T: WktFloat + Default + FromStr, +{ + let x = T::from(-91.147385).unwrap(); + let y = T::from(30.471165).unwrap(); + Point::new(x, y) +} + +pub fn east_baton_rouge() -> Polygon +where + T: WktFloat + Default + FromStr, +{ + polygon("east_baton_rouge.wkt") +} + pub fn norway_main() -> LineString where T: WktFloat + Default + FromStr, @@ -123,9 +139,18 @@ where { let wkt = Wkt::from_str(&file(name)).unwrap(); match wkt.item { - Geometry::LineString(line_string) => { - LineString::from_iter(line_string.0.into_iter().map(|coord| (coord.x, coord.y))) - } + Geometry::LineString(line_string) => wkt_line_string_to_geo(&line_string), + _ => unreachable!(), + } +} + +fn polygon(name: &str) -> Polygon +where + T: WktFloat + Default + FromStr, +{ + let wkt = Wkt::from_str(&file(name)).unwrap(); + match wkt.item { + Geometry::Polygon(wkt_polygon) => wkt_polygon_to_geo(&wkt_polygon), _ => unreachable!(), } } @@ -136,34 +161,36 @@ where { let wkt = Wkt::from_str(&file(name)).unwrap(); match wkt.item { - Geometry::MultiPolygon(multi_polygon) => { - let polygons: Vec> = multi_polygon - .0 - .into_iter() - .map(|wkt_polygon| { - let exterior: LineString = LineString::from_iter( - wkt_polygon.0[0].0.iter().map(|coord| (coord.x, coord.y)), - ); - - let interiors: Vec> = wkt_polygon.0[1..] - .iter() - .map(|wkt_line_string| { - LineString::from_iter( - wkt_line_string.0.iter().map(|coord| (coord.x, coord.y)), - ) - }) - .collect(); - - Polygon::new(exterior, interiors) - }) - .collect(); - - MultiPolygon(polygons) - } + Geometry::MultiPolygon(multi_polygon) => wkt_multi_polygon_to_geo(&multi_polygon), _ => unreachable!(), } } +fn wkt_line_string_to_geo(line_string: &wkt::types::LineString) -> LineString +where + T: WktFloat + Default + FromStr, +{ + LineString::from_iter(line_string.0.iter().map(|coord| (coord.x, coord.y))) +} + +fn wkt_polygon_to_geo(polygon: &wkt::types::Polygon) -> Polygon +where + T: WktFloat + Default + FromStr, +{ + let exterior: LineString = wkt_line_string_to_geo(&polygon.0[0]); + let interiors: Vec> = polygon.0[1..].iter().map(wkt_line_string_to_geo).collect(); + + Polygon::new(exterior, interiors) +} + +fn wkt_multi_polygon_to_geo(multi_polygon: &wkt::types::MultiPolygon) -> MultiPolygon +where + T: WktFloat + Default + FromStr, +{ + let polygons: Vec> = multi_polygon.0.iter().map(wkt_polygon_to_geo).collect(); + MultiPolygon(polygons) +} + fn file(name: &str) -> String { let mut res = PathBuf::from(env!("CARGO_MANIFEST_DIR")); res.push("fixtures"); diff --git a/geo/benches/contains.rs b/geo/benches/contains.rs index 9322f1b89..327615848 100644 --- a/geo/benches/contains.rs +++ b/geo/benches/contains.rs @@ -4,36 +4,73 @@ extern crate criterion; extern crate geo; use geo::contains::Contains; +use geo::{polygon, Line, Point, Polygon}; use criterion::Criterion; fn criterion_benchmark(c: &mut Criterion) { - c.bench_function("point in polygon", |bencher| { - let polygon = geo::polygon![ - (x: 0.0, y: 0.0), + c.bench_function("point in simple polygon", |bencher| { + let polygon = polygon![ + (x: 0.0f64, y: 0.0), (x: 1.0, y: 0.0), (x: 1.0, y: 1.0), (x: 0.0, y: 0.0), ]; - let in_candidate = geo::Point::new(0.5, 0.1); + let point = Point::new(0.5, 0.1); bencher.iter(|| { - criterion::black_box( - criterion::black_box(&polygon).contains(criterion::black_box(&in_candidate)), - ); + assert!(criterion::black_box(&polygon).contains(criterion::black_box(&point))); }); }); - c.bench_function("point outside polygon", |bencher| { - let polygon = geo::polygon![ - (x: 0.0, y: 0.0), + c.bench_function("point outside simple polygon", |bencher| { + let polygon = polygon![ + (x: 0.0f64, y: 0.0), (x: 1.0, y: 0.0), (x: 1.0, y: 1.0), (x: 0.0, y: 0.0), ]; - let out_candidate = geo::Point::new(2.0, 2.0); + let point = Point::new(2.0, 2.0); + bencher.iter(|| { + assert!(!criterion::black_box(&polygon).contains(criterion::black_box(&point))); + }); + }); + + c.bench_function("point inside complex polygon", |bencher| { + let polygon = Polygon::::new(geo_test_fixtures::louisiana(), vec![]); + let point = geo_test_fixtures::baton_rouge(); + bencher.iter(|| { + assert!(criterion::black_box(&polygon).contains(criterion::black_box(&point))); + }); + }); + + c.bench_function("point outside complex polygon", |bencher| { + let polygon = Polygon::::new(geo_test_fixtures::louisiana(), vec![]); + // lake borgne - near and mostly surrounded by, but not inside of, Louisiana + let point = point!(x: -89.641854, y: 30.026283); + bencher.iter(|| { + assert!(!criterion::black_box(&polygon).contains(criterion::black_box(&point))); + }); + }); + + c.bench_function("line across complex polygon", |bencher| { + let polygon = Polygon::::new(geo_test_fixtures::louisiana(), vec![]); + // crossing part of, but not contained by Louisiana + let line = Line::new( + geo_test_fixtures::baton_rouge(), + point!(x: -89.641854, y: 30.026283), + ); + bencher.iter(|| { + assert!(!criterion::black_box(&polygon).contains(criterion::black_box(&line))); + }); + }); + + c.bench_function("complex polygon contains polygon", |bencher| { + let polygon = Polygon::::new(geo_test_fixtures::louisiana(), vec![]); + let contained_polygon = geo_test_fixtures::east_baton_rouge(); + bencher.iter(|| { - criterion::black_box( - criterion::black_box(&polygon).contains(criterion::black_box(&out_candidate)), + assert!( + criterion::black_box(&polygon).contains(criterion::black_box(&contained_polygon)) ); }); }); diff --git a/geo/benches/relate.rs b/geo/benches/relate.rs index 13d10e44b..b6c50276a 100644 --- a/geo/benches/relate.rs +++ b/geo/benches/relate.rs @@ -3,7 +3,9 @@ extern crate criterion; extern crate geo; use crate::geo::relate::Relate; -use criterion::Criterion; +use crate::geo::rotate::Rotate; +use crate::geo::translate::Translate; +use criterion::{BatchSize, Criterion}; use geo::{LineString, Polygon}; fn criterion_benchmark(c: &mut Criterion) { @@ -31,6 +33,64 @@ fn criterion_benchmark(c: &mut Criterion) { ); }); }); + + c.bench_function("entire jts test suite", |bencher| { + let mut relate_tests = jts_test_runner::TestRunner::new(); + relate_tests.prepare_cases().unwrap(); + + bencher.iter_batched( + || relate_tests.clone(), + |mut test_runner| { + test_runner.run().unwrap(); + assert!(test_runner.failures().is_empty()); + assert!(!test_runner.successes().is_empty()); + }, + BatchSize::SmallInput, + ); + }); + + c.bench_function("jts test suite matching *Relate*", |bencher| { + let mut relate_tests = + jts_test_runner::TestRunner::new().matching_filename_glob("*Relate*"); + relate_tests.prepare_cases().unwrap(); + + bencher.iter_batched( + || relate_tests.clone(), + |mut test_runner| { + test_runner.run().unwrap(); + assert!(test_runner.failures().is_empty()); + assert!(!test_runner.successes().is_empty()); + }, + BatchSize::SmallInput, + ); + }); + + c.bench_function("disjoint polygons", |bencher| { + let norway = Polygon::new(geo_test_fixtures::norway_main::(), vec![]); + let louisiana = Polygon::new(geo_test_fixtures::louisiana::(), vec![]); + + bencher.iter(|| { + criterion::black_box(norway.relate(&louisiana)); + }); + }); + + c.bench_function("large rotated polygons", |bencher| { + let norway = Polygon::new(geo_test_fixtures::norway_main::(), vec![]); + let rotated_norway = norway.rotate_around_center(20.0); + + bencher.iter(|| { + criterion::black_box(norway.relate(&rotated_norway)); + }); + }); + + c.bench_function("offset polygons", |bencher| { + let norway = Polygon::new(geo_test_fixtures::norway_main::(), vec![]); + let translated_norway = norway.translate(10.0, 10.0); + + bencher.iter(|| { + criterion::black_box(norway.relate(&translated_norway)); + }); + }); } criterion_group!(benches, criterion_benchmark); diff --git a/geo/src/algorithm/relate/geomgraph/intersection_matrix.rs b/geo/src/algorithm/relate/geomgraph/intersection_matrix.rs index fb9f356b3..5cb89670e 100644 --- a/geo/src/algorithm/relate/geomgraph/intersection_matrix.rs +++ b/geo/src/algorithm/relate/geomgraph/intersection_matrix.rs @@ -20,7 +20,7 @@ use crate::algorithm::{coordinate_position::CoordPos, dimensions::Dimensions}; /// - Wikipedia article on [DE-9IM](https://en.wikipedia.org/wiki/DE-9IM) /// /// This implementation is heavily based on that from the [JTS project](https://github.com/locationtech/jts/blob/master/modules/core/src/main/java/org/locationtech/jts/geom/IntersectionMatrix.java). -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, Clone)] pub struct IntersectionMatrix(LocationArray>); /// Helper struct so we can index IntersectionMatrix by CoordPos diff --git a/jts-test-runner/src/input.rs b/jts-test-runner/src/input.rs index 80d5c72b5..58c64218d 100644 --- a/jts-test-runner/src/input.rs +++ b/jts-test-runner/src/input.rs @@ -119,7 +119,7 @@ pub(crate) enum OperationInput { Unsupported, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) enum Operation { Centroid { subject: Geometry, diff --git a/jts-test-runner/src/runner.rs b/jts-test-runner/src/runner.rs index e3b1d2cd9..c0d1246fb 100644 --- a/jts-test-runner/src/runner.rs +++ b/jts-test-runner/src/runner.rs @@ -9,23 +9,24 @@ use geo::{intersects::Intersects, prelude::Contains, Coordinate, Geometry, LineS const GENERAL_TEST_XML: Dir = include_dir!("resources/testxml/general"); -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct TestRunner { filename_filter: Option, desc_filter: Option, + cases: Option>, failures: Vec, unsupported: Vec, successes: Vec, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct TestCase { test_file_name: String, description: String, operation: Operation, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct TestFailure { error_description: String, test_case: TestCase, @@ -65,8 +66,18 @@ impl TestRunner { self } + pub fn prepare_cases(&mut self) -> Result<()> { + self.cases = Some(self.parse_cases()?); + Ok(()) + } + pub fn run(&mut self) -> Result<()> { - let cases = self.parse_cases()?; + let cases = if let Some(cases) = self.cases.take() { + cases + } else { + self.parse_cases()? + }; + debug!("cases.len(): {}", cases.len()); for test_case in cases { From 823cbde6e5e991655fa5d9e961b994393c730aed Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Thu, 5 May 2022 10:55:43 -0700 Subject: [PATCH 2/4] scaffold rstar edge set intersector Copied the "SimpleEdgeSetIntersector" into a new struct. I'll add the actual RTree implementation in a follow up, and hopefully this way it'll be easier to compare the two implementations. --- .../relate/geomgraph/geometry_graph.rs | 11 +++- .../algorithm/relate/geomgraph/index/mod.rs | 2 + .../index/rstar_edge_set_intersector.rs | 59 +++++++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 geo/src/algorithm/relate/geomgraph/index/rstar_edge_set_intersector.rs diff --git a/geo/src/algorithm/relate/geomgraph/geometry_graph.rs b/geo/src/algorithm/relate/geomgraph/geometry_graph.rs index a699c06ea..77efbc1da 100644 --- a/geo/src/algorithm/relate/geomgraph/geometry_graph.rs +++ b/geo/src/algorithm/relate/geomgraph/geometry_graph.rs @@ -1,5 +1,7 @@ use super::{ - index::{EdgeSetIntersector, SegmentIntersector, SimpleEdgeSetIntersector}, + index::{ + EdgeSetIntersector, RstarEdgeSetIntersector, SegmentIntersector, SimpleEdgeSetIntersector, + }, CoordNode, CoordPos, Direction, Edge, Label, LineIntersector, PlanarGraph, TopologyPosition, }; @@ -97,7 +99,12 @@ where fn create_edge_set_intersector() -> Box> { // PERF: faster algorithms exist. This one was chosen for simplicity of implementation and // debugging - Box::new(SimpleEdgeSetIntersector::new()) + // Slow, but simple and good for debugging + // Box::new(SimpleEdgeSetIntersector::new()) + + // Should be much faster for sparse intersections, while not much slower than + // SimpleEdgeSetIntersector in the dense case + Box::new(RstarEdgeSetIntersector::new()) } fn boundary_nodes(&self) -> impl Iterator> { diff --git a/geo/src/algorithm/relate/geomgraph/index/mod.rs b/geo/src/algorithm/relate/geomgraph/index/mod.rs index 06c4b2c21..ae48b3db9 100644 --- a/geo/src/algorithm/relate/geomgraph/index/mod.rs +++ b/geo/src/algorithm/relate/geomgraph/index/mod.rs @@ -1,7 +1,9 @@ mod edge_set_intersector; +mod rstar_edge_set_intersector; mod segment_intersector; mod simple_edge_set_intersector; pub(crate) use edge_set_intersector::EdgeSetIntersector; +pub(crate) use rstar_edge_set_intersector::RstarEdgeSetIntersector; pub(crate) use segment_intersector::SegmentIntersector; pub(crate) use simple_edge_set_intersector::SimpleEdgeSetIntersector; diff --git a/geo/src/algorithm/relate/geomgraph/index/rstar_edge_set_intersector.rs b/geo/src/algorithm/relate/geomgraph/index/rstar_edge_set_intersector.rs new file mode 100644 index 000000000..91619f919 --- /dev/null +++ b/geo/src/algorithm/relate/geomgraph/index/rstar_edge_set_intersector.rs @@ -0,0 +1,59 @@ +use super::super::Edge; +use super::{EdgeSetIntersector, SegmentIntersector}; +use crate::GeoFloat; + +use std::cell::RefCell; +use std::rc::Rc; + +pub(crate) struct RstarEdgeSetIntersector; + +impl RstarEdgeSetIntersector { + pub fn new() -> Self { + RstarEdgeSetIntersector + } + + fn compute_intersects( + &mut self, + edge0: &Rc>>, + edge1: &Rc>>, + segment_intersector: &mut SegmentIntersector, + ) { + let edge0_coords_len = edge0.borrow().coords().len() - 1; + let edge1_coords_len = edge1.borrow().coords().len() - 1; + for i0 in 0..edge0_coords_len { + for i1 in 0..edge1_coords_len { + segment_intersector.add_intersections(edge0, i0, edge1, i1); + } + } + } +} + +impl EdgeSetIntersector for RstarEdgeSetIntersector { + fn compute_intersections_within_set( + &mut self, + edges: &[Rc>>], + check_for_self_intersecting_edges: bool, + segment_intersector: &mut SegmentIntersector, + ) { + for edge0 in edges.iter() { + for edge1 in edges.iter() { + if check_for_self_intersecting_edges || edge0.as_ptr() != edge1.as_ptr() { + self.compute_intersects(edge0, edge1, segment_intersector); + } + } + } + } + + fn compute_intersections_between_sets( + &mut self, + edges0: &[Rc>>], + edges1: &[Rc>>], + segment_intersector: &mut SegmentIntersector, + ) { + for edge0 in edges0 { + for edge1 in edges1 { + self.compute_intersects(edge0, edge1, segment_intersector); + } + } + } +} From 2c2e7d038e33151e9df8b126d5761964ce3b6196 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Thu, 5 May 2022 11:11:38 -0700 Subject: [PATCH 3/4] CONTROVERSIAL: make GeoFloat compat with RTreeNum If we want to rely on using an RTree for some of our operations (like the Relate trait) we have to ensure our numeric types are RTree compatible. == Alternative considered Since RTreeNum isn't necessarily a float, we could instead add these new bounds to GeoNum instead of GeoFloat. However, doing so would mean dropping support for unsigned ints from GeoNum. Note that using unsigned ints now, while supported, can easily lead to underflow if you're using one of the many operations that involve subtraction. It would also put one more barrier between ever getting BigDecimal support in geo - which is not Bounded. Also, apparently Float isn't necessarily Signed, but having never personally encountered unsigned floating point in the wild, I don't have strong feelings about retaining support for it. And since Relate doesn't current support non-floats, this would be a cost with no benefit. If that changes, we could reconsider this decision, or perhaps add the required behavior to some derivative type, like one of the HasKernel implementations. --- geo/src/algorithm/euclidean_distance.rs | 12 ++++++------ geo/src/lib.rs | 5 +++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/geo/src/algorithm/euclidean_distance.rs b/geo/src/algorithm/euclidean_distance.rs index cbe44f2be..e3c7ed022 100644 --- a/geo/src/algorithm/euclidean_distance.rs +++ b/geo/src/algorithm/euclidean_distance.rs @@ -142,7 +142,7 @@ where .0 .iter() .map(|p| self.euclidean_distance(p)) - .fold(T::max_value(), |accum, val| accum.min(val)) + .fold(::max_value(), |accum, val| accum.min(val)) } } @@ -175,7 +175,7 @@ where mls.0 .iter() .map(|ls| self.euclidean_distance(ls)) - .fold(T::max_value(), |accum, val| accum.min(val)) + .fold(::max_value(), |accum, val| accum.min(val)) } } @@ -195,7 +195,7 @@ where .interiors() .iter() .map(|ring| self.euclidean_distance(ring)) - .fold(T::max_value(), |accum, val| accum.min(val)) + .fold(::max_value(), |accum, val| accum.min(val)) .min( polygon .exterior() @@ -205,7 +205,7 @@ where self.0, line.start, line.end, ) }) - .fold(T::max_value(), |accum, val| accum.min(val)), + .fold(::max_value(), |accum, val| accum.min(val)), ) } } @@ -220,7 +220,7 @@ where .0 .iter() .map(|p| self.euclidean_distance(p)) - .fold(T::max_value(), |accum, val| accum.min(val)) + .fold(::max_value(), |accum, val| accum.min(val)) } } @@ -530,7 +530,7 @@ where [(self.0, self.1), (self.1, self.2), (self.2, self.0)] .iter() .map(|edge| ::geo_types::private_utils::line_segment_distance(point.0, edge.0, edge.1)) - .fold(T::max_value(), |accum, val| accum.min(val)) + .fold(::max_value(), |accum, val| accum.min(val)) } } diff --git a/geo/src/lib.rs b/geo/src/lib.rs index 4dde1731b..ab4ee44ae 100644 --- a/geo/src/lib.rs +++ b/geo/src/lib.rs @@ -293,8 +293,9 @@ pub mod prelude { /// }) /// } /// ``` -pub trait GeoFloat: num_traits::Float + GeoNum {} -impl GeoFloat for T where T: num_traits::Float + GeoNum {} +pub trait GeoFloat: num_traits::Float + num_traits::Signed + num_traits::Bounded + GeoNum {} +impl GeoFloat for T where T: num_traits::Float + num_traits::Signed + num_traits::Bounded + GeoNum +{} pub trait GeoNum: CoordNum + algorithm::kernels::HasKernel {} impl GeoNum for T where T: CoordNum + algorithm::kernels::HasKernel {} From 68a4c408ba23103568805fabb0318e87734ff6c4 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Thu, 5 May 2022 11:22:05 -0700 Subject: [PATCH 4/4] working rtree intersector impl --- geo/CHANGES.md | 4 + .../index/rstar_edge_set_intersector.rs | 90 ++++++++++++++----- 2 files changed, 72 insertions(+), 22 deletions(-) diff --git a/geo/CHANGES.md b/geo/CHANGES.md index 86e467359..741c4d227 100644 --- a/geo/CHANGES.md +++ b/geo/CHANGES.md @@ -3,6 +3,10 @@ ## Unreleased +* POSSIBLY BREAKING: `GeoFloat` types must now implement `num_traits::Signed` and `num_traits::Bounded`. This shouldn't + affect you if you are using a standard `Geometry` or `Geometry` or `geo::GeoFloat` generically. +* Speed up `Relate` and `Contains` traits for large LineStrings and Polygons by using an RTree to more efficiently + inspect edges in our topology graph. * Speed up intersection checks by using a preliminary bbox check * Remove unneeded reference for `*MapCoords*` closure parameter. * diff --git a/geo/src/algorithm/relate/geomgraph/index/rstar_edge_set_intersector.rs b/geo/src/algorithm/relate/geomgraph/index/rstar_edge_set_intersector.rs index 91619f919..6bb180714 100644 --- a/geo/src/algorithm/relate/geomgraph/index/rstar_edge_set_intersector.rs +++ b/geo/src/algorithm/relate/geomgraph/index/rstar_edge_set_intersector.rs @@ -5,41 +5,71 @@ use crate::GeoFloat; use std::cell::RefCell; use std::rc::Rc; +use rstar::RTree; + pub(crate) struct RstarEdgeSetIntersector; impl RstarEdgeSetIntersector { pub fn new() -> Self { RstarEdgeSetIntersector } +} - fn compute_intersects( - &mut self, - edge0: &Rc>>, - edge1: &Rc>>, - segment_intersector: &mut SegmentIntersector, - ) { - let edge0_coords_len = edge0.borrow().coords().len() - 1; - let edge1_coords_len = edge1.borrow().coords().len() - 1; - for i0 in 0..edge0_coords_len { - for i1 in 0..edge1_coords_len { - segment_intersector.add_intersections(edge0, i0, edge1, i1); - } +struct Segment<'a, F: GeoFloat + rstar::RTreeNum> { + i: usize, + edge: &'a RefCell>, + envelope: rstar::AABB>, +} + +impl<'a, F> Segment<'a, F> +where + F: GeoFloat + rstar::RTreeNum, +{ + fn new(i: usize, edge: &'a RefCell>) -> Self { + use crate::rstar::RTreeObject; + let p1 = edge.borrow().coords()[i]; + let p2 = edge.borrow().coords()[i + 1]; + Self { + i, + edge, + envelope: rstar::AABB::from_corners(p1, p2), } } } -impl EdgeSetIntersector for RstarEdgeSetIntersector { +impl<'a, F> rstar::RTreeObject for Segment<'a, F> +where + F: GeoFloat + rstar::RTreeNum, +{ + type Envelope = rstar::AABB>; + + fn envelope(&self) -> Self::Envelope { + self.envelope + } +} + +impl EdgeSetIntersector for RstarEdgeSetIntersector +where + F: GeoFloat + rstar::RTreeNum, +{ fn compute_intersections_within_set( &mut self, edges: &[Rc>>], check_for_self_intersecting_edges: bool, segment_intersector: &mut SegmentIntersector, ) { - for edge0 in edges.iter() { - for edge1 in edges.iter() { - if check_for_self_intersecting_edges || edge0.as_ptr() != edge1.as_ptr() { - self.compute_intersects(edge0, edge1, segment_intersector); - } + let segments: Vec> = edges + .iter() + .flat_map(|edge| { + let start_of_final_segment: usize = RefCell::borrow(edge).coords().len() - 1; + (0..start_of_final_segment).map(|segment_i| Segment::new(segment_i, edge)) + }) + .collect(); + let tree = RTree::bulk_load(segments); + + for (edge0, edge1) in tree.intersection_candidates_with_other_tree(&tree) { + if check_for_self_intersecting_edges || edge0.edge.as_ptr() != edge1.edge.as_ptr() { + segment_intersector.add_intersections(edge0.edge, edge0.i, edge1.edge, edge1.i); } } } @@ -50,10 +80,26 @@ impl EdgeSetIntersector for RstarEdgeSetIntersector { edges1: &[Rc>>], segment_intersector: &mut SegmentIntersector, ) { - for edge0 in edges0 { - for edge1 in edges1 { - self.compute_intersects(edge0, edge1, segment_intersector); - } + let segments0: Vec> = edges0 + .iter() + .flat_map(|edge| { + let start_of_final_segment: usize = RefCell::borrow(edge).coords().len() - 1; + (0..start_of_final_segment).map(|segment_i| Segment::new(segment_i, edge)) + }) + .collect(); + let tree_0 = RTree::bulk_load(segments0); + + let segments1: Vec> = edges1 + .iter() + .flat_map(|edge| { + let start_of_final_segment: usize = RefCell::borrow(edge).coords().len() - 1; + (0..start_of_final_segment).map(|segment_i| Segment::new(segment_i, edge)) + }) + .collect(); + let tree_1 = RTree::bulk_load(segments1); + + for (edge0, edge1) in tree_0.intersection_candidates_with_other_tree(&tree_1) { + segment_intersector.add_intersections(edge0.edge, edge0.i, edge1.edge, edge1.i); } } }