Skip to content

Commit

Permalink
Undo hacky APIs for snapping just by distance-based costs, now that w…
Browse files Browse the repository at this point in the history
…e have proper profiles. #17
  • Loading branch information
dabreegster committed Oct 25, 2024
1 parent 17da1bb commit fa882b0
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 73 deletions.
4 changes: 4 additions & 0 deletions backend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,10 @@ impl MapModel {
pub fn graph(&self) -> &Graph {
&self.graph
}

pub fn graph_mut(&mut self) -> &mut Graph {
&mut self.graph
}
}

#[derive(Deserialize)]
Expand Down
49 changes: 17 additions & 32 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use chrono::NaiveTime;
use clap::{Parser, Subcommand};
use geo::{Contains, Coord, EuclideanLength, LineString, Point};
use geojson::{de::deserialize_geometry, Feature, GeoJson, Geometry};
use graph::{Graph, GtfsModel, ProfileID, Route, Router, Timer};
use graph::{Direction, Graph, GtfsModel, ProfileID, Route, Timer};
use serde::{Deserialize, Serialize};

#[derive(Parser)]
Expand Down Expand Up @@ -90,12 +90,20 @@ fn snap_test(model_path: String, routes_path: String, limit: Duration) -> Result
let mut timer = Timer::new("snap routes", None);

timer.step("load model");
let model: MapModel = bincode::deserialize(&fs_err::read(&model_path)?)?;
let graph = model.graph();
let profile = graph.profile_names["bicycle"];
let mut model: MapModel = bincode::deserialize(&fs_err::read(&model_path)?)?;

timer.step("prepare distance-based routing");
let router = Router::by_distance(&graph.roads);
let distance_profile = model.graph_mut().add_profile(
"distance".to_string(),
Box::new(|_, linestring| {
(
Direction::Both,
Duration::from_secs_f64(linestring.euclidean_length()),
)
}),
);

let graph = model.graph();

timer.step("snap routes");
let mut features = Vec::new();
Expand All @@ -122,7 +130,7 @@ fn snap_test(model_path: String, routes_path: String, limit: Duration) -> Result

graph.mercator.to_mercator_in_place(&mut input.geometry);

match snap(&input, graph, &router, profile) {
match snap(&input, graph, distance_profile) {
Ok(route) => {
let output = route.linestring(graph);
let mut f = Feature::from(Geometry::from(&graph.mercator.to_wgs84(&output)));
Expand Down Expand Up @@ -175,7 +183,7 @@ fn snap_test(model_path: String, routes_path: String, limit: Duration) -> Result
let start_time = NaiveTime::from_hms_opt(7, 0, 0).unwrap();
fs_err::write(
"buffered.geojson",
model.buffer_routes(routes, profile, start_time, limit)?,
model.buffer_routes(routes, graph.profile_names["bicycle"], start_time, limit)?,
)?;

timer.done();
Expand Down Expand Up @@ -209,12 +217,7 @@ struct Waypoint {
snapped: bool,
}

fn snap(
input: &GeoJsonLineString,
graph: &Graph,
router: &Router,
profile: ProfileID,
) -> Result<Route> {
fn snap(input: &GeoJsonLineString, graph: &Graph, profile: ProfileID) -> Result<Route> {
// Use waypoints if they're all snapped
if !input.waypoints.is_empty() && input.waypoints.iter().all(|waypt| waypt.snapped) {
let pts: Vec<Coord> = input
Expand All @@ -232,12 +235,7 @@ fn snap(
}
let mut routes = Vec::new();
for pair in pts.windows(2) {
routes.push(hack_snap_route(
&LineString::new(pair.to_vec()),
graph,
router,
profile,
)?);
routes.push(graph.snap_route(&LineString::new(pair.to_vec()), profile)?);
}

// TODO Naively concatenate
Expand All @@ -253,17 +251,4 @@ fn snap(
}

bail!("TODO, skip without all snapped waypoints");
//hack_snap_route(&input.geometry, graph, router)
}

// TODO API is getting so messy
fn hack_snap_route(
input: &LineString,
graph: &Graph,
router: &Router,
profile: ProfileID,
) -> Result<Route> {
let start = graph.snap_to_road(*input.coords().next().unwrap(), profile);
let end = graph.snap_to_road(*input.coords().last().unwrap(), profile);
router.route(graph, start, end)
}
17 changes: 17 additions & 0 deletions graph/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,23 @@ impl Graph {
intersection,
}
}

pub fn add_profile(
&mut self,
name: String,
profile: Box<dyn Fn(&Tags, &LineString) -> (Direction, Duration)>,
) -> ProfileID {
for road in &mut self.roads {
let (dir, c) = profile(&road.osm_tags, &road.linestring);
road.access.push(dir);
road.cost.push(c);
}

let id = ProfileID(self.profile_names.len());
self.routers.push(Router::new(&self.roads, id));
self.profile_names.insert(name, id);
id
}
}

impl Road {
Expand Down
41 changes: 0 additions & 41 deletions graph/src/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,47 +79,6 @@ impl Router {
}
}

/// Create a router allowing every road in both directions, and just costing by distance.
/// TODO Rethink the APIs here
pub fn by_distance(roads: &Vec<Road>) -> Self {
let mut input_graph = InputGraph::new();
let mut node_map = NodeMap::new();

for road in roads {
// cm
let cost = (road.length_meters * 100.0) as usize;
let node1 = node_map.get_or_insert(road.src_i);
let node2 = node_map.get_or_insert(road.dst_i);

// Loops aren't ever part of a shortest path, and fast_paths warns loudly, so just skip
if node1 == node2 {
continue;
}

input_graph.add_edge(node1, node2, cost);
input_graph.add_edge(node2, node1, cost);
}
input_graph.freeze();
let ch = fast_paths::prepare(&input_graph);

let path_calc = RefCell::new(Some(fast_paths::create_calculator(&ch)));

let closest_road = RTree::bulk_load(
roads
.iter()
.map(|r| EdgeLocation::new(r.linestring.clone(), r.id))
.collect(),
);

Self {
node_map,
ch,
path_calc,

closest_road,
}
}

/// Calculates a route between two positions.
pub fn route(&self, graph: &Graph, start: Position, end: Position) -> Result<Route> {
debug!("route from {start:?} to {end:?}");
Expand Down

0 comments on commit fa882b0

Please sign in to comment.