Fix a bug in the LTN tool where route costs involving private roads balloon to many hours

Internally we penalize private roads, but that shouldn't be reported in
a user-facing app
This commit is contained in:
Dustin Carlino 2022-12-12 10:38:20 +00:00
parent f8cac65d83
commit 1f24af176c
4 changed files with 67 additions and 8 deletions

View File

@ -179,7 +179,7 @@ impl RoutePlanner {
.pathfind_with_params(map, req, params.clone())
})
{
total_time += path.get_cost();
total_time += path.estimate_duration(map, None, Some(params.main_road_penalty));
paths.push((path, *colors::PLAN_ROUTE_BEFORE));
}
}
@ -202,7 +202,7 @@ impl RoutePlanner {
.pathfind_with_params(map, req, params.clone())
})
{
total_time += path.get_cost();
total_time += path.estimate_duration(map, None, Some(params.main_road_penalty));
paths_after.push((path, *colors::PLAN_ROUTE_AFTER));
}
}
@ -229,7 +229,8 @@ impl RoutePlanner {
)
})
{
total_time += path.get_cost();
total_time +=
path.estimate_duration(map, Some(map_model::MAX_BIKE_SPEED), None);
paths.push((path, *colors::PLAN_ROUTE_BIKE));
}
}
@ -251,7 +252,8 @@ impl RoutePlanner {
)
})
{
total_time += path.get_cost();
total_time +=
path.estimate_duration(map, Some(map_model::MAX_WALKING_SPEED), None);
paths.push((path, *colors::PLAN_ROUTE_WALK));
}
}

View File

@ -360,6 +360,11 @@ impl Map {
&self.traffic_signals[&id]
}
/// This will return None for SharedSidewalkCorners
pub fn get_movement(&self, id: MovementID) -> Option<&Movement> {
self.get_i(id.parent).movements.get(&id)
}
// All these helpers should take IDs and return objects.
/// The turns may belong to two different intersections!

View File

@ -12,6 +12,8 @@ use crate::{osm, DirectedRoadID, Direction, IntersectionID, Map, OriginalRoad, T
/// road to another.
/// One road usually has 4 crosswalks, each a singleton Movement. We need all of the information
/// here to keep each crosswalk separate.
///
/// We don't create movements for SharedSidewalkCorners.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct MovementID {
pub from: DirectedRoadID,

View File

@ -5,11 +5,11 @@
use anyhow::Result;
use serde::{Deserialize, Serialize};
use geom::{Duration, Polygon, Ring};
use geom::{Duration, Polygon, Ring, Speed};
use crate::pathfind::uber_turns::UberTurnV2;
use crate::{
DirectedRoadID, Direction, IntersectionID, LaneID, Map, MovementID, Path, PathConstraints,
osm, DirectedRoadID, Direction, IntersectionID, LaneID, Map, MovementID, Path, PathConstraints,
PathRequest, PathStep, RoadID, TurnID, UberTurn,
};
@ -87,12 +87,62 @@ impl PathV2 {
}
/// The time needed to perform this path. This time is not a lower bound; physically following
/// the path might be faster. This time incorporates costs like using sub-optimal lanes or
/// taking difficult turns.
/// the path might be faster. This time incorporates costs like using sub-optimal lanes, taking
/// difficult turns, and crossing private roads (which are modelled with a large penalty!)
pub fn get_cost(&self) -> Duration {
self.cost
}
/// Estimate how long following the path will take in the best case, assuming no traffic, delay
/// at intersections, elevation, or penalties for crossing private roads. To determine the
/// speed along each step, the agent's optional max_speed must be known.
///
/// TODO Hack. The one use of this actually needs to apply main_road_penalty. We want to omit
/// some penalties, but use others. Come up with a better way of expressing this.
pub fn estimate_duration(
&self,
map: &Map,
max_speed: Option<Speed>,
main_road_penalty: Option<f64>,
) -> Duration {
let mut total = Duration::ZERO;
for step in &self.steps {
let (dist, mut speed);
let mut multiplier = 1.0;
match step {
PathStepV2::Along(dr) | PathStepV2::Contraflow(dr) => {
let road = map.get_r(dr.road);
dist = road.length();
speed = road.speed_limit;
if let Some(penalty) = main_road_penalty {
if road.get_rank() != osm::RoadRank::Local {
multiplier = penalty;
}
}
}
PathStepV2::Movement(m) | PathStepV2::ContraflowMovement(m) => {
if let Some(movement) = map.get_movement(*m) {
dist = movement.geom.length();
speed = map
.get_r(m.from.road)
.speed_limit
.min(map.get_r(m.to.road).speed_limit);
} else {
// Assume it's a SharedSidewalkCorner and just skip
continue;
}
}
}
if let Some(max) = max_speed {
speed = speed.min(max);
}
total += multiplier * (dist / speed);
}
total
}
/// Transform a sequence of roads representing a path into the current lane-based path, by
/// picking particular lanes and turns to use.
pub fn into_v1(mut self, map: &Map) -> Result<Path> {