Skip to content

Commit

Permalink
Replace closest_intersection with closest_road
Browse files Browse the repository at this point in the history
  • Loading branch information
dabreegster committed Jul 7, 2024
1 parent 8dd0a04 commit 25b6434
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 75 deletions.
26 changes: 23 additions & 3 deletions backend/src/graph.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use anyhow::Result;
use enum_map::{Enum, EnumMap};
use geo::{LineString, Point, Polygon};
use geo::{Coord, LineLocatePoint, LineString, Point, Polygon};
use geojson::{Feature, GeoJson, Geometry};
use rstar::{primitives::GeomWithData, RTree};
use serde::{Deserialize, Serialize};
Expand All @@ -16,7 +16,7 @@ pub struct Graph {
pub intersections: Vec<Intersection>,
// All geometry stored in worldspace, including rtrees
pub mercator: Mercator,
pub closest_intersection: EnumMap<Mode, RTree<IntersectionLocation>>,
pub closest_road: EnumMap<Mode, RTree<EdgeLocation>>,
pub router: EnumMap<Mode, Router>,
pub boundary_polygon: Polygon,

Expand All @@ -26,7 +26,7 @@ pub struct Graph {
pub gtfs: GtfsModel,
}

pub type IntersectionLocation = GeomWithData<[f64; 2], IntersectionID>;
pub type EdgeLocation = GeomWithData<LineString, RoadID>;

#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct RoadID(pub usize);
Expand Down Expand Up @@ -143,6 +143,26 @@ impl Graph {
}
panic!("no road from {i1:?} to {i2:?} or vice versa");
}

// Returns the closest road and the fraction along it
pub fn snap_to_road(&self, pt: Coord, mode: Mode) -> (&Road, f64) {
let r = self.closest_road[mode]
.nearest_neighbor(&pt.into())
.unwrap()
.data;
let road = &self.roads[r.0];
let fraction = road.linestring.line_locate_point(&pt.into()).unwrap();
(road, fraction)
}

pub fn closest_intersection(&self, pt: Coord, mode: Mode) -> IntersectionID {
let (road, fraction) = self.snap_to_road(pt, mode);
if fraction <= 0.5 {
road.src_i
} else {
road.dst_i
}
}
}

impl Road {
Expand Down
5 changes: 1 addition & 4 deletions backend/src/isochrone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,7 @@ pub fn get_costs(
start_time: NaiveTime,
end_time: NaiveTime,
) -> HashMap<RoadID, Duration> {
let start = graph.closest_intersection[mode]
.nearest_neighbor(&[req.x, req.y])
.unwrap()
.data;
let start = graph.closest_intersection(req, mode);

let mut visited: HashSet<IntersectionID> = HashSet::new();
let mut cost_per_road: HashMap<RoadID, Duration> = HashMap::new();
Expand Down
24 changes: 10 additions & 14 deletions backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,20 +132,20 @@ impl MapModel {
// TODO error plumbing
x => panic!("bad input {x}"),
};
let start = self.graph.closest_intersection[mode]
.nearest_neighbor(&x_y(self.graph.mercator.pt_to_mercator(Coord {
let start = self.graph.closest_intersection(
self.graph.mercator.pt_to_mercator(Coord {
x: req.x1,
y: req.y1,
})))
.unwrap()
.data;
let end = self.graph.closest_intersection[mode]
.nearest_neighbor(&x_y(self.graph.mercator.pt_to_mercator(Coord {
}),
mode,
);
let end = self.graph.closest_intersection(
self.graph.mercator.pt_to_mercator(Coord {
x: req.x2,
y: req.y2,
})))
.unwrap()
.data;
}),
mode,
);

if req.mode == "transit" {
transit_route::route(
Expand Down Expand Up @@ -216,10 +216,6 @@ fn err_to_js<E: std::fmt::Display>(err: E) -> JsValue {
JsValue::from_str(&err.to_string())
}

fn x_y(c: Coord) -> [f64; 2] {
[c.x, c.y]
}

pub enum GtfsSource {
Dir(String),
FGB(String),
Expand Down
86 changes: 32 additions & 54 deletions backend/src/scrape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@ use std::collections::HashMap;

use anyhow::Result;
use enum_map::EnumMap;
use geo::{Coord, EuclideanLength, LineString};
use geo::{Coord, EuclideanLength};
use muv_osm::{AccessLevel, TMode};
use osm_reader::OsmID;
use rstar::primitives::GeomWithData;
use rstar::RTree;
use utils::Tags;

use crate::amenity::Amenity;
use crate::graph::{
AmenityID, Direction, Graph, Intersection, IntersectionID, IntersectionLocation, Mode, Road,
RoadID,
AmenityID, Direction, EdgeLocation, Graph, Intersection, IntersectionID, Mode, Road, RoadID,
};
use crate::gtfs::{GtfsModel, StopID};
use crate::route::Router;
Expand Down Expand Up @@ -86,7 +84,7 @@ impl Graph {
.collect();

// Add in a bit
let mut roads = graph
let mut roads: Vec<Road> = graph
.edges
.into_iter()
.map(|e| {
Expand All @@ -109,28 +107,25 @@ impl Graph {
}
})
.collect();
for a in &mut amenities.amenities {
a.point = graph.mercator.pt_to_mercator(a.point.into()).into();
}

snap_amenities(&mut roads, &amenities.amenities, &mut timer);

timer.push("build closest_intersection");
let closest_intersection = EnumMap::from_fn(|mode| {
timer.push("build closest_road");
let closest_road = EnumMap::from_fn(|mode| {
timer.step(format!("for {mode:?}"));
let mut points = Vec::new();
for i in &intersections {
if i.roads
RTree::bulk_load(
roads
.iter()
.any(|r| roads[r.0].allows_forwards(mode) || roads[r.0].allows_backwards(mode))
{
points.push(IntersectionLocation::new(i.point.into(), i.id));
}
}
RTree::bulk_load(points)
.filter(|r| r.access[mode] != Direction::None)
.map(|r| EdgeLocation::new(r.linestring.clone(), r.id))
.collect(),
)
});
timer.pop();

for a in &mut amenities.amenities {
a.point = graph.mercator.pt_to_mercator(a.point.into()).into();
}
snap_amenities(&mut roads, &amenities.amenities, &closest_road, &mut timer);

timer.push("building router");
let router = EnumMap::from_fn(|mode| {
timer.step(format!("for {mode:?}"));
Expand All @@ -145,7 +140,7 @@ impl Graph {
GtfsSource::FGB(url) => GtfsModel::from_fgb(&url, &graph.mercator).await?,
GtfsSource::None => GtfsModel::empty(),
};
snap_stops(&mut roads, &mut gtfs, &mut timer);
snap_stops(&mut roads, &mut gtfs, &closest_road[Mode::Foot], &mut timer);
timer.pop();

timer.done();
Expand All @@ -154,7 +149,7 @@ impl Graph {
roads,
intersections,
mercator: graph.mercator,
closest_intersection,
closest_road,
router,
boundary_polygon: graph.boundary_polygon,

Expand Down Expand Up @@ -245,49 +240,32 @@ fn calculate_max_speed(tags: &Tags) -> f64 {
30.0 * 0.44704
}

type EdgeLocation = GeomWithData<LineString, RoadID>;

fn snap_amenities(roads: &mut Vec<Road>, amenities: &Vec<Amenity>, timer: &mut Timer) {
timer.push("snap amenities");
timer.push("build closest_per_mode");
let closest_per_mode = EnumMap::from_fn(|mode| {
timer.step(format!("for {mode:?}"));
RTree::bulk_load(
roads
.iter()
.filter(|r| r.access[mode] != Direction::None)
.map(|r| EdgeLocation::new(r.linestring.clone(), r.id))
.collect(),
)
});
timer.pop();
timer.step("find closest roads");
fn snap_amenities(
roads: &mut Vec<Road>,
amenities: &Vec<Amenity>,
closest_road: &EnumMap<Mode, RTree<EdgeLocation>>,
timer: &mut Timer,
) {
timer.step("snap amenities");
for amenity in amenities {
for (mode, closest) in &closest_per_mode {
for (mode, closest) in closest_road {
if let Some(r) = closest.nearest_neighbor(&amenity.point) {
roads[r.data.0].amenities[mode].push(amenity.id);
}
}
}
timer.pop();
}

fn snap_stops(roads: &mut Vec<Road>, gtfs: &mut GtfsModel, timer: &mut Timer) {
fn snap_stops(
roads: &mut Vec<Road>,
gtfs: &mut GtfsModel,
closest_road: &RTree<EdgeLocation>,
timer: &mut Timer,
) {
if gtfs.stops.is_empty() {
return;
}

// Only care about one mode
// TODO Could we reuse from snap_amenities for some perf?
timer.step("build closest_road");
let closest_road = RTree::bulk_load(
roads
.iter()
.filter(|r| r.access[Mode::Foot] != Direction::None)
.map(|r| EdgeLocation::new(r.linestring.clone(), r.id))
.collect(),
);

timer.step("find closest roads per stop");
// TODO Make an iterator method that returns the IDs too
for (idx, stop) in gtfs.stops.iter_mut().enumerate() {
Expand Down

0 comments on commit 25b6434

Please sign in to comment.