map_model/connectivity/
walking.rs
1use std::collections::{BinaryHeap, HashMap, HashSet};
2
3use abstutil::{MultiMap, PriorityQueueItem};
4use geom::{Duration, Speed};
5
6use crate::connectivity::Spot;
7use crate::pathfind::{zone_cost, WalkingNode};
8use crate::{BuildingID, Lane, LaneType, Map, PathConstraints, PathStep};
9
10#[derive(Clone)]
11pub struct WalkingOptions {
12 pub allow_shoulders: bool,
14 pub walking_speed: Speed,
15}
16
17impl WalkingOptions {
18 pub fn default() -> WalkingOptions {
19 WalkingOptions {
20 allow_shoulders: true,
21 walking_speed: WalkingOptions::default_speed(),
22 }
23 }
24
25 pub fn common_speeds() -> Vec<(&'static str, Speed)> {
26 vec![
27 ("3 mph (average for an adult)", Speed::miles_per_hour(3.0)),
28 ("1 mph (manual wheelchair)", Speed::miles_per_hour(1.0)),
29 ("5 mph (moderate jog)", Speed::miles_per_hour(5.0)),
30 ]
31 }
32
33 pub fn default_speed() -> Speed {
34 WalkingOptions::common_speeds()[0].1
35 }
36}
37
38pub fn all_walking_costs_from(
45 map: &Map,
46 starts: Vec<Spot>,
47 time_limit: Duration,
48 opts: WalkingOptions,
49) -> HashMap<BuildingID, Duration> {
50 let mut queue: BinaryHeap<PriorityQueueItem<Duration, WalkingNode>> = BinaryHeap::new();
51
52 for spot in starts {
53 match spot {
54 Spot::Building(b_id) => {
55 queue.push(PriorityQueueItem {
56 cost: Duration::ZERO,
57 value: WalkingNode::closest(map.get_b(b_id).sidewalk_pos, map),
58 });
59 }
60 Spot::Border(i_id) => {
61 let intersection = map.get_i(i_id);
62 let incoming_lanes = intersection.incoming_lanes.clone();
63 let mut outgoing_lanes = intersection.outgoing_lanes.clone();
64 let mut all_lanes = incoming_lanes;
65 all_lanes.append(&mut outgoing_lanes);
66 let walkable_lanes: Vec<&Lane> = all_lanes
67 .into_iter()
68 .map(|l_id| map.get_l(l_id))
69 .filter(|l| l.is_walkable())
70 .collect();
71 for lane in walkable_lanes {
72 queue.push(PriorityQueueItem {
73 cost: Duration::ZERO,
74 value: WalkingNode::SidewalkEndpoint(
75 lane.get_directed_parent(),
76 lane.src_i == i_id,
77 ),
78 });
79 }
80 }
81 Spot::DirectedRoad(dr) => {
82 queue.push(PriorityQueueItem {
84 cost: Duration::ZERO,
85 value: WalkingNode::SidewalkEndpoint(dr, false),
86 });
87 queue.push(PriorityQueueItem {
88 cost: Duration::ZERO,
89 value: WalkingNode::SidewalkEndpoint(dr, true),
90 });
91 }
92 }
93 }
94
95 if !opts.allow_shoulders {
96 let mut shoulder_endpoint = Vec::new();
97 for q in &queue {
98 if let WalkingNode::SidewalkEndpoint(dir_r, _) = q.value {
99 for lane in &map.get_r(dir_r.road).lanes {
100 shoulder_endpoint.push(lane.lane_type == LaneType::Shoulder);
101 }
102 }
103 }
104 if shoulder_endpoint.into_iter().all(|x| x) {
105 return HashMap::new();
106 }
107 }
108
109 let mut sidewalk_to_bldgs = MultiMap::new();
110 for b in map.all_buildings() {
111 sidewalk_to_bldgs.insert(b.sidewalk(), b.id);
112 }
113
114 let mut results = HashMap::new();
115
116 let mut visited_nodes = HashSet::new();
117 while let Some(current) = queue.pop() {
118 if visited_nodes.contains(¤t.value) {
119 continue;
120 }
121 if current.cost > time_limit {
122 continue;
123 }
124 visited_nodes.insert(current.value);
125
126 let (r, is_dst_i) = match current.value {
127 WalkingNode::SidewalkEndpoint(r, is_dst_i) => (r, is_dst_i),
128 _ => unreachable!(),
129 };
130 let lane = map.get_l(r.must_get_sidewalk(map));
131 if opts.allow_shoulders || lane.lane_type != LaneType::Shoulder {
133 let sidewalk_len = lane.length();
134 let step = if is_dst_i {
135 PathStep::ContraflowLane(lane.id)
136 } else {
137 PathStep::Lane(lane.id)
138 };
139 let speed =
140 step.max_speed_along(Some(opts.walking_speed), PathConstraints::Pedestrian, map);
141 let cross_to_node = WalkingNode::SidewalkEndpoint(r, !is_dst_i);
142
143 if !visited_nodes.contains(&cross_to_node) {
149 for b in sidewalk_to_bldgs.get(lane.id) {
150 let bldg_dist_along = map.get_b(*b).sidewalk_pos.dist_along();
151 let dist_to_bldg = if is_dst_i {
152 sidewalk_len - bldg_dist_along
154 } else {
155 bldg_dist_along
156 };
157 let bldg_cost = current.cost + dist_to_bldg / speed;
158 if bldg_cost <= time_limit {
159 results.insert(*b, bldg_cost);
160 }
161 }
162
163 queue.push(PriorityQueueItem {
164 cost: current.cost + sidewalk_len / speed,
165 value: cross_to_node,
166 });
167 }
168 }
169 for turn in map.get_turns_for(lane.id, PathConstraints::Pedestrian) {
171 if (turn.id.parent == lane.dst_i) != is_dst_i {
172 continue;
173 }
174 queue.push(PriorityQueueItem {
175 cost: current.cost
176 + turn.geom.length()
177 / PathStep::Turn(turn.id).max_speed_along(
178 Some(opts.walking_speed),
179 PathConstraints::Pedestrian,
180 map,
181 )
182 + zone_cost(turn.id.to_movement(map), PathConstraints::Pedestrian, map),
183 value: WalkingNode::SidewalkEndpoint(
184 map.get_l(turn.id.dst).get_directed_parent(),
185 map.get_l(turn.id.dst).dst_i == turn.id.parent,
186 ),
187 });
188 }
189 }
190
191 results
192}