diff --git a/game/src/edit/traffic_signals/offsets.rs b/game/src/edit/traffic_signals/offsets.rs index bfd13e7502..92775322b6 100644 --- a/game/src/edit/traffic_signals/offsets.rs +++ b/game/src/edit/traffic_signals/offsets.rs @@ -237,9 +237,10 @@ impl TuneRelative { " about {} for a car if there's no congestion", car_dt )), + // TODO We could calculate a full path and incorporate incline Line(format!( " about {} for a bike", - dist_btwn / Scenario::max_bike_speed() + dist_btwn / map_model::MAX_BIKE_SPEED )), Line(format!( " about {} for a pedestrian", diff --git a/map_model/src/lib.rs b/map_model/src/lib.rs index cadf4d363c..eb400c7ac3 100644 --- a/map_model/src/lib.rs +++ b/map_model/src/lib.rs @@ -57,7 +57,7 @@ pub use crate::objects::zone::{AccessRestrictions, Zone}; pub use crate::pathfind::uber_turns::{IntersectionCluster, UberTurn, UberTurnGroup}; use crate::pathfind::Pathfinder; pub use crate::pathfind::{Path, PathConstraints, PathRequest, PathStep, RoutingParams}; -pub use crate::traversable::{Position, Traversable}; +pub use crate::traversable::{Position, Traversable, MAX_BIKE_SPEED}; mod city; pub mod connectivity; diff --git a/map_model/src/pathfind/mod.rs b/map_model/src/pathfind/mod.rs index 9d1cd5428f..630bafb083 100644 --- a/map_model/src/pathfind/mod.rs +++ b/map_model/src/pathfind/mod.rs @@ -422,15 +422,9 @@ impl Path { let mut total = Duration::ZERO; for step in &self.steps { let dist = self.dist_crossed_from_step(map, step); - let speed_limit = step.as_traversable().speed_limit(map); - let speed = if constraints == PathConstraints::Pedestrian { - // Pedestrians don't care about the road's speed limit - max_speed.unwrap() - } else if let Some(max) = max_speed { - speed_limit.min(max) - } else { - speed_limit - }; + let speed = step + .as_traversable() + .max_speed_along(max_speed, constraints, map); total += dist / speed; } total diff --git a/map_model/src/pathfind/vehicles.rs b/map_model/src/pathfind/vehicles.rs index af36626fb1..e876824d52 100644 --- a/map_model/src/pathfind/vehicles.rs +++ b/map_model/src/pathfind/vehicles.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use thread_local::ThreadLocal; use abstutil::MultiMap; -use geom::{Duration, Speed}; +use geom::Duration; use crate::pathfind::ch::round; use crate::pathfind::node_map::{deserialize_nodemap, NodeMap}; @@ -15,7 +15,7 @@ use crate::pathfind::uber_turns::{IntersectionCluster, UberTurn}; use crate::pathfind::zone_cost; use crate::{ DrivingSide, Lane, LaneID, Map, Path, PathConstraints, PathRequest, PathStep, RoutingParams, - Turn, TurnID, TurnType, + Traversable, Turn, TurnID, TurnType, }; #[derive(Serialize, Deserialize)] @@ -253,14 +253,13 @@ pub fn vehicle_cost( t1 + t2 } PathConstraints::Bike => { - // TODO Copied from sim. Probably move to map_model. - let max_bike_speed = Speed::miles_per_hour(10.0); - // Usually the bike's speed limit matters, not the road's. - let t1 = lane.length() / map.get_r(lane.parent).speed_limit.min(max_bike_speed); - let t2 = - turn.geom.length() / map.get_parent(turn.id.dst).speed_limit.min(max_bike_speed); + // We assume MAX_BIKE_SPEED for pathfinding. + let max_speed = Some(crate::MAX_BIKE_SPEED); + let t1 = lane.length() + / Traversable::Lane(lane.id).max_speed_along(max_speed, constraints, map); + let t2 = turn.geom.length() + / Traversable::Turn(turn.id).max_speed_along(max_speed, constraints, map); - // TODO Elevation gain is bad, loss is good. // TODO If we're on a driving lane, higher speed limit is worse. // TODO Bike lanes next to parking is dangerous. diff --git a/map_model/src/traversable.rs b/map_model/src/traversable.rs index b67796b73b..d858e12387 100644 --- a/map_model/src/traversable.rs +++ b/map_model/src/traversable.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use geom::{Angle, Distance, PolyLine, Pt2D, Speed}; -use crate::{LaneID, Map, TurnID}; +use crate::{LaneID, Map, PathConstraints, TurnID}; /// Represents a specific point some distance along a lane. #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] @@ -197,17 +197,51 @@ impl Traversable { } } - pub fn speed_limit(&self, map: &Map) -> Speed { - match *self { - Traversable::Lane(id) => map.get_parent(id).speed_limit, - Traversable::Turn(id) => map.get_parent(id.dst).speed_limit, - } - } - pub fn get_zorder(&self, map: &Map) -> isize { match *self { Traversable::Lane(id) => map.get_parent(id).zorder, Traversable::Turn(id) => map.get_i(id.parent).get_zorder(map), } } + + /// The single definitive place to determine how fast somebody could go along a single road or + /// turn. This should be used for pathfinding and simulation. + pub fn max_speed_along( + &self, + max_speed_on_flat_ground: Option, + constraints: PathConstraints, + map: &Map, + ) -> Speed { + let base = match self { + Traversable::Lane(l) => { + if constraints == PathConstraints::Bike { + // We assume every bike has a max_speed defined. + bike_speed_on_incline( + max_speed_on_flat_ground.unwrap(), + map.get_parent(*l).percent_incline, + ) + } else { + map.get_parent(*l).speed_limit + } + } + // TODO Ignore elevation on turns? + Traversable::Turn(t) => map + .get_parent(t.src) + .speed_limit + .min(map.get_parent(t.dst).speed_limit), + }; + if let Some(s) = max_speed_on_flat_ground { + base.min(s) + } else { + base + } + } +} + +// 10mph +pub const MAX_BIKE_SPEED: Speed = Speed::const_meters_per_second(4.4704); + +fn bike_speed_on_incline(max_speed: Speed, _percent_incline: f64) -> Speed { + // TODO Incorporate percent_incline here + max_speed } diff --git a/sim/src/make/scenario.rs b/sim/src/make/scenario.rs index 30523ad078..11ff945905 100644 --- a/sim/src/make/scenario.rs +++ b/sim/src/make/scenario.rs @@ -225,7 +225,7 @@ impl Scenario { let max_speed = Some(Scenario::rand_speed( rng, Speed::miles_per_hour(8.0), - Scenario::max_bike_speed(), + map_model::MAX_BIKE_SPEED, )); VehicleSpec { vehicle_type: VehicleType::Bike, @@ -233,9 +233,6 @@ impl Scenario { max_speed, } } - pub fn max_bike_speed() -> Speed { - Speed::miles_per_hour(10.0) - } pub fn rand_dist(rng: &mut XorShiftRng, low: Distance, high: Distance) -> Distance { assert!(high > low); diff --git a/sim/src/mechanics/car.rs b/sim/src/mechanics/car.rs index b1395aca92..950508b511 100644 --- a/sim/src/mechanics/car.rs +++ b/sim/src/mechanics/car.rs @@ -47,11 +47,11 @@ impl Car { start_time: Time, map: &Map, ) -> CarState { - let on = self.router.head(); - let mut speed = on.speed_limit(map); - if let Some(s) = self.vehicle.max_speed { - speed = speed.min(s); - } + let speed = self.router.head().max_speed_along( + self.vehicle.max_speed, + self.vehicle.vehicle_type.to_constraints(), + map, + ); let dt = (dist_int.end - dist_int.start) / speed; CarState::Crossing(TimeInterval::new(start_time, start_time + dt), dist_int) } diff --git a/sim/src/mechanics/driving.rs b/sim/src/mechanics/driving.rs index 86e667ab58..e8a93df296 100644 --- a/sim/src/mechanics/driving.rs +++ b/sim/src/mechanics/driving.rs @@ -273,19 +273,16 @@ impl DrivingSimState { ) -> bool { match car.state { CarState::Crossing(time_int, dist_int) => { - let time_cross = now - time_int.start; - if time_cross > Duration::ZERO { - let avg_speed = Speed::from_dist_time(dist_int.length(), time_cross); - - let route = car.router.head(); - let max_speed = route.speed_limit(ctx.map).min( - car.vehicle - .max_speed - .unwrap_or(Speed::meters_per_second(100.0)), - ); - - if let Some((trip, _)) = car.trip_and_person { - if let Traversable::Lane(lane) = route { + if let Some((trip, _)) = car.trip_and_person { + if let Traversable::Lane(lane) = car.router.head() { + let time_to_cross = now - time_int.start; + if time_to_cross > Duration::ZERO { + let avg_speed = Speed::from_dist_time(dist_int.length(), time_to_cross); + let max_speed = car.router.head().max_speed_along( + car.vehicle.max_speed, + car.vehicle.vehicle_type.to_constraints(), + ctx.map, + ); self.events .push(Event::LaneSpeedPercentage(trip, lane, avg_speed, max_speed)); } @@ -397,14 +394,14 @@ impl DrivingSimState { assert!(from != goto); if let Traversable::Turn(t) = goto { - let mut speed = goto.speed_limit(ctx.map); - if let Some(s) = car.vehicle.max_speed { - speed = speed.min(s); - } if !ctx.intersections.maybe_start_turn( AgentID::Car(car.vehicle.id), t, - speed, + goto.max_speed_along( + car.vehicle.max_speed, + car.vehicle.vehicle_type.to_constraints(), + ctx.map, + ), now, ctx.map, ctx.scheduler,