use std::collections::{BinaryHeap, HashMap, HashSet};
use abstutil::{MultiMap, PriorityQueueItem};
use geom::{Duration, Speed};
use crate::connectivity::Spot;
use crate::pathfind::{zone_cost, WalkingNode};
use crate::{BuildingID, Lane, LaneType, Map, PathConstraints, PathStep};
#[derive(Clone)]
pub struct WalkingOptions {
pub allow_shoulders: bool,
pub walking_speed: Speed,
}
impl WalkingOptions {
pub fn default() -> WalkingOptions {
WalkingOptions {
allow_shoulders: true,
walking_speed: WalkingOptions::default_speed(),
}
}
pub fn common_speeds() -> Vec<(&'static str, Speed)> {
vec![
("3 mph (average for an adult)", Speed::miles_per_hour(3.0)),
("1 mph (manual wheelchair)", Speed::miles_per_hour(1.0)),
("5 mph (moderate jog)", Speed::miles_per_hour(5.0)),
]
}
pub fn default_speed() -> Speed {
WalkingOptions::common_speeds()[0].1
}
}
pub fn all_walking_costs_from(
map: &Map,
starts: Vec<Spot>,
time_limit: Duration,
opts: WalkingOptions,
) -> HashMap<BuildingID, Duration> {
let mut queue: BinaryHeap<PriorityQueueItem<Duration, WalkingNode>> = BinaryHeap::new();
for spot in starts {
match spot {
Spot::Building(b_id) => {
queue.push(PriorityQueueItem {
cost: Duration::ZERO,
value: WalkingNode::closest(map.get_b(b_id).sidewalk_pos, map),
});
}
Spot::Border(i_id) => {
let intersection = map.get_i(i_id);
let incoming_lanes = intersection.incoming_lanes.clone();
let mut outgoing_lanes = intersection.outgoing_lanes.clone();
let mut all_lanes = incoming_lanes;
all_lanes.append(&mut outgoing_lanes);
let walkable_lanes: Vec<&Lane> = all_lanes
.into_iter()
.map(|l_id| map.get_l(l_id))
.filter(|l| l.is_walkable())
.collect();
for lane in walkable_lanes {
queue.push(PriorityQueueItem {
cost: Duration::ZERO,
value: WalkingNode::SidewalkEndpoint(
lane.get_directed_parent(),
lane.src_i == i_id,
),
});
}
}
Spot::DirectedRoad(dr) => {
queue.push(PriorityQueueItem {
cost: Duration::ZERO,
value: WalkingNode::SidewalkEndpoint(dr, false),
});
queue.push(PriorityQueueItem {
cost: Duration::ZERO,
value: WalkingNode::SidewalkEndpoint(dr, true),
});
}
}
}
if !opts.allow_shoulders {
let mut shoulder_endpoint = Vec::new();
for q in &queue {
if let WalkingNode::SidewalkEndpoint(dir_r, _) = q.value {
for lane in &map.get_r(dir_r.road).lanes {
shoulder_endpoint.push(lane.lane_type == LaneType::Shoulder);
}
}
}
if shoulder_endpoint.into_iter().all(|x| x) {
return HashMap::new();
}
}
let mut sidewalk_to_bldgs = MultiMap::new();
for b in map.all_buildings() {
sidewalk_to_bldgs.insert(b.sidewalk(), b.id);
}
let mut results = HashMap::new();
let mut visited_nodes = HashSet::new();
while let Some(current) = queue.pop() {
if visited_nodes.contains(¤t.value) {
continue;
}
if current.cost > time_limit {
continue;
}
visited_nodes.insert(current.value);
let (r, is_dst_i) = match current.value {
WalkingNode::SidewalkEndpoint(r, is_dst_i) => (r, is_dst_i),
_ => unreachable!(),
};
let lane = map.get_l(r.must_get_sidewalk(map));
if opts.allow_shoulders || lane.lane_type != LaneType::Shoulder {
let sidewalk_len = lane.length();
let step = if is_dst_i {
PathStep::ContraflowLane(lane.id)
} else {
PathStep::Lane(lane.id)
};
let speed =
step.max_speed_along(Some(opts.walking_speed), PathConstraints::Pedestrian, map);
let cross_to_node = WalkingNode::SidewalkEndpoint(r, !is_dst_i);
if !visited_nodes.contains(&cross_to_node) {
for b in sidewalk_to_bldgs.get(lane.id) {
let bldg_dist_along = map.get_b(*b).sidewalk_pos.dist_along();
let dist_to_bldg = if is_dst_i {
sidewalk_len - bldg_dist_along
} else {
bldg_dist_along
};
let bldg_cost = current.cost + dist_to_bldg / speed;
if bldg_cost <= time_limit {
results.insert(*b, bldg_cost);
}
}
queue.push(PriorityQueueItem {
cost: current.cost + sidewalk_len / speed,
value: cross_to_node,
});
}
}
for turn in map.get_turns_for(lane.id, PathConstraints::Pedestrian) {
if (turn.id.parent == lane.dst_i) != is_dst_i {
continue;
}
queue.push(PriorityQueueItem {
cost: current.cost
+ turn.geom.length()
/ PathStep::Turn(turn.id).max_speed_along(
Some(opts.walking_speed),
PathConstraints::Pedestrian,
map,
)
+ zone_cost(turn.id.to_movement(map), PathConstraints::Pedestrian, map),
value: WalkingNode::SidewalkEndpoint(
map.get_l(turn.id.dst).get_directed_parent(),
map.get_l(turn.id.dst).dst_i == turn.id.parent,
),
});
}
}
results
}