From 19160be3c29fb023480c68671fb70baf416a1553 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Tue, 4 Mar 2025 15:47:33 -0800 Subject: [PATCH 1/2] update geo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit == Perf Some big perf wins for the stats/autogen work! build stats: Hilltown in LAD_Dundee City time: [890.95 µs 894.88 µs 899.45 µs] change: [-34.332% -34.080% -33.830%] (p = 0.00 < 0.05) Performance has improved. generate auto boundaries (and stats) for all of LAD_Dundee City time: [192.06 ms 192.32 ms 192.67 ms] change: [-61.269% -61.036% -60.823%] (p = 0.00 < 0.05) Performance has improved. And some modest wins elsewhere... build map_model: bristol time: [83.446 ms 83.560 ms 83.688 ms] change: [-3.4679% -2.8247% -2.2423%] (p = 0.00 < 0.05) Performance has improved. --- Cargo.lock | 4 ++-- Cargo.toml | 6 +++--- backend/src/boundary_stats.rs | 2 +- backend/src/neighbourhood.rs | 4 ++-- data_prep/scotland/src/bin/generate_prioritization.rs | 3 ++- data_prep/scotland/src/lib.rs | 3 ++- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d1228a7f..d016ed50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -421,7 +421,7 @@ checksum = "8bf7cc16383c4b8d58b9905a8509f02926ce3058053c056376248d958c9df1e8" [[package]] name = "geo" version = "0.29.3" -source = "git+https://github.com/georust/geo?branch=mkirk%2Fgeo-buffer#cf28bf54d6d855defaffa810753056242acb8b87" +source = "git+https://github.com/georust/geo?branch=mkirk%2Fgeo-ltn#9286aa364bcaafb5c0732a300329a5fdccacc849" dependencies = [ "earcutr", "float_next_after", @@ -438,7 +438,7 @@ dependencies = [ [[package]] name = "geo-types" version = "0.7.15" -source = "git+https://github.com/georust/geo#c1f8131c4636c581c255a7762aa317354789decd" +source = "git+https://github.com/georust/geo?branch=mkirk%2Fgeo-ltn#9286aa364bcaafb5c0732a300329a5fdccacc849" dependencies = [ "approx", "num-traits", diff --git a/Cargo.toml b/Cargo.toml index 9e79b933..71da55ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ edition = "2021" [workspace.dependencies] anyhow = "1.0.82" bincode = "1.3.3" -geo = { git = "https://github.com/georust/geo", branch="mkirk/geo-buffer" } +geo = { git = "https://github.com/georust/geo", branch="mkirk/geo-ltn" } geojson = { git = "https://github.com/georust/geojson", features = ["geo-types"] } serde = "1.0.188" utils = { git = "https://github.com/a-b-street/utils", features = ["serde"] } @@ -23,6 +23,6 @@ utils = { git = "https://github.com/a-b-street/utils", features = ["serde"] } opt-level = 3 [patch.crates-io] -geo = { git = "https://github.com/georust/geo", branch="mkirk/geo-buffer" } +geo = { git = "https://github.com/georust/geo", branch="mkirk/geo-ltn" } #geo = { path = "../../georust/geo/geo" } -geo-types = { git = "https://github.com/georust/geo" } +geo-types = { git = "https://github.com/georust/geo", branch="mkirk/geo-ltn" } diff --git a/backend/src/boundary_stats.rs b/backend/src/boundary_stats.rs index 9db1a048..6eec481e 100644 --- a/backend/src/boundary_stats.rs +++ b/backend/src/boundary_stats.rs @@ -114,5 +114,5 @@ pub struct PopulationZone { pub struct PreparedPopulationZone { pub population_zone: PopulationZone, - pub prepared_geometry: PreparedGeometry<'static>, + pub prepared_geometry: PreparedGeometry<'static, MultiPolygon>, } diff --git a/backend/src/neighbourhood.rs b/backend/src/neighbourhood.rs index a9488d50..2c7d36d1 100644 --- a/backend/src/neighbourhood.rs +++ b/backend/src/neighbourhood.rs @@ -482,10 +482,10 @@ enum LineInPolygon { } // NOTE: polygon must be `valid` (no spikes!) to get reasonable results -fn line_in_polygon( +fn line_in_polygon<'a>( linestring: &LineString, polygon: &Polygon, - prepared_polygon: &PreparedGeometry, + prepared_polygon: &PreparedGeometry<'a, &'a Polygon>, ) -> LineInPolygon { let perimeter_likelihood = perimeter_likelihood(&linestring, polygon); // This is an arbitrary threshold - we could tune it if we're getting false positives/negatives diff --git a/data_prep/scotland/src/bin/generate_prioritization.rs b/data_prep/scotland/src/bin/generate_prioritization.rs index 557f2080..819d7171 100644 --- a/data_prep/scotland/src/bin/generate_prioritization.rs +++ b/data_prep/scotland/src/bin/generate_prioritization.rs @@ -91,7 +91,8 @@ impl PopulationZoneInput { Ok(population_zones) } - fn read_all_prepared_from_file() -> Result, Self)>> { + fn read_all_prepared_from_file() -> Result, Self)>> + { let iter = Self::read_all_from_file()? .into_iter() .map(|population_zone| { diff --git a/data_prep/scotland/src/lib.rs b/data_prep/scotland/src/lib.rs index b97b322d..f2b456e4 100644 --- a/data_prep/scotland/src/lib.rs +++ b/data_prep/scotland/src/lib.rs @@ -18,7 +18,8 @@ impl StudyArea { println!("Read {} study area boundaries", study_areas.len()); Ok(study_areas) } - pub fn read_all_prepared_from_file() -> Result, Self)>> { + pub fn read_all_prepared_from_file( + ) -> Result, Self)>> { let iter = Self::read_all_from_file()?.into_iter().map(|study_area| { ( PreparedGeometry::from(study_area.geometry.clone()), From 1b58b243385c05dc8b973173c36f8b2bef711fa9 Mon Sep 17 00:00:00 2001 From: Michael Kirk Date: Tue, 4 Mar 2025 16:14:06 -0800 Subject: [PATCH 2/2] adapt to new line_interpolate_point api --- backend/src/create.rs | 5 +++-- backend/src/geo_helpers/mod.rs | 6 +++--- backend/src/map_model.rs | 12 ++++++++---- backend/src/movements.rs | 24 ++++++++++-------------- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/backend/src/create.rs b/backend/src/create.rs index 1ff58a39..1194e828 100644 --- a/backend/src/create.rs +++ b/backend/src/create.rs @@ -1,7 +1,8 @@ use std::collections::{BTreeMap, BTreeSet, HashMap}; use anyhow::Result; -use geo::{Coord, LineInterpolatePoint, LineString, MultiPolygon, PreparedGeometry}; +use geo::line_measures::InterpolatableLine; +use geo::{Coord, Euclidean, LineString, MultiPolygon, PreparedGeometry}; use osm_reader::{NodeID, OsmID, RelationID, WayID}; use petgraph::graphmap::UnGraphMap; use rstar::{primitives::GeomWithData, RTree}; @@ -360,7 +361,7 @@ fn apply_existing_filters( let pt = map .get_r(r) .linestring - .line_interpolate_point(percent) + .point_at_ratio_from_start(&Euclidean, percent) .unwrap(); map.add_modal_filter(pt.into(), Some(vec![r]), filter); } diff --git a/backend/src/geo_helpers/mod.rs b/backend/src/geo_helpers/mod.rs index 1fe076e8..41ed1740 100644 --- a/backend/src/geo_helpers/mod.rs +++ b/backend/src/geo_helpers/mod.rs @@ -1,10 +1,10 @@ mod slice_nearest_boundary; pub use slice_nearest_boundary::SliceNearestFrechetBoundary; +use geo::line_measures::InterpolatableLine; use geo::{ BooleanOps, BoundingRect, Contains, Coord, Distance, Euclidean, Intersects, Length, Line, - LineInterpolatePoint, LineIntersection, LineLocatePoint, LineString, MultiPolygon, Point, - Polygon, Rect, Validation, + LineIntersection, LineLocatePoint, LineString, MultiPolygon, Point, Polygon, Rect, Validation, }; use rstar::AABB; use utils::LineSplit; @@ -71,7 +71,7 @@ pub fn clip_linestring_to_polygon(linestring: &LineString, polygon: &Polygon) -> continue; }; // Is this piece inside the polygon or not? Check the midpoint - if let Some(midpt) = split.line_interpolate_point(0.5) { + if let Some(midpt) = split.point_at_ratio_from_start(&Euclidean, 0.5) { if polygon.contains(&midpt) { results.push(split); } diff --git a/backend/src/map_model.rs b/backend/src/map_model.rs index d3354bf7..fc0613a0 100644 --- a/backend/src/map_model.rs +++ b/backend/src/map_model.rs @@ -9,7 +9,7 @@ use crate::route::RouterInput; use crate::{od::DemandModel, Router}; use anyhow::Result; use geo::{ - Closest, ClosestPoint, Coord, Distance, Euclidean, Length, LineInterpolatePoint, + line_measures::InterpolatableLine, Closest, ClosestPoint, Coord, Distance, Euclidean, Length, LineLocatePoint, LineString, MultiPolygon, Point, Polygon, }; use geojson::{Feature, FeatureCollection, GeoJson, Geometry, JsonValue}; @@ -511,7 +511,7 @@ impl MapModel { let road = self.get_r(*r); let pt = road .linestring - .line_interpolate_point(filter.percent_along) + .point_at_ratio_from_start(&Euclidean, filter.percent_along) .unwrap(); let angle = limit_angle(angle_of_pt_on_line(&road.linestring, pt.into()) + 90.0); let mut f = self.mercator.to_wgs84_gj(&pt); @@ -558,7 +558,7 @@ impl MapModel { let pt = self .get_r(*r) .linestring - .line_interpolate_point(filter.percent_along) + .point_at_ratio_from_start(&Euclidean, filter.percent_along) .unwrap(); let mut f = self.mercator.to_wgs84_gj(&pt); f.set_property("kind", "deleted_existing_modal_filter"); @@ -891,7 +891,11 @@ impl MapModel { let mut highest_time_ratio: f64 = 1.0; for r in from { let road = self.get_r(r); - let pt1 = road.linestring.line_interpolate_point(0.5).unwrap().into(); + let pt1 = road + .linestring + .point_at_ratio_from_start(&Euclidean, 0.5) + .unwrap() + .into(); // TODO How to handle missing one or both routes missing? if let (Some(before), Some(after)) = ( diff --git a/backend/src/movements.rs b/backend/src/movements.rs index 4e1f1467..a27ba2d3 100644 --- a/backend/src/movements.rs +++ b/backend/src/movements.rs @@ -1,5 +1,6 @@ use anyhow::Result; -use geo::{Euclidean, Length, Line, LineInterpolatePoint, Point, Polygon}; +use geo::line_measures::InterpolatableLine; +use geo::{Euclidean, Line, Point, Polygon}; use geojson::GeoJson; use itertools::Itertools; @@ -211,20 +212,15 @@ impl Road { /// Returns a point on the road this far away from the intersection. If the road is too short, /// clamps at the other end of the road. fn pt_from_intersection(&self, meters_away: f64, i: IntersectionID) -> Point { - let len = Euclidean.length(&self.linestring); - - if len > meters_away { - let pct = if self.src_i == i { - meters_away / len - } else { - 1.0 - (meters_away / len) - }; - return self.linestring.line_interpolate_point(pct).unwrap(); + if self.src_i == i { + self.linestring + .point_at_distance_from_start(&Euclidean, meters_away) + .unwrap() + } else { + self.linestring + .point_at_distance_from_end(&Euclidean, meters_away) + .unwrap() } - - // If not, just take the other endpoint - let pct = if self.src_i == i { 1.0 } else { 0.0 }; - self.linestring.line_interpolate_point(pct).unwrap() } }