1use std::collections::HashMap;
4
5use fast_paths::InputGraph;
6use serde::{Deserialize, Serialize};
7
8use geom::{Distance, Duration};
9
10use crate::pathfind::engine::{CreateEngine, PathfindEngine};
11use crate::pathfind::node_map::{deserialize_nodemap, NodeMap};
12use crate::pathfind::vehicles::VehiclePathfinder;
13use crate::pathfind::zone_cost;
14use crate::pathfind::{round, unround};
15use crate::{
16 DirectedRoadID, IntersectionID, Map, PathConstraints, PathRequest, PathStep, PathStepV2,
17 PathV2, Position, TransitRoute, TransitRouteID, TransitStopID, TurnType,
18};
19
20#[derive(Clone, Serialize, Deserialize)]
21pub struct SidewalkPathfinder {
22 #[serde(deserialize_with = "deserialize_nodemap")]
23 nodes: NodeMap<WalkingNode>,
24 use_transit: bool,
25 engine: PathfindEngine,
26}
27
28#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Serialize, Deserialize)]
29pub enum WalkingNode {
30 SidewalkEndpoint(DirectedRoadID, bool),
32 RideTransit(TransitStopID),
35 LeaveMap(IntersectionID),
36}
37
38impl WalkingNode {
39 pub fn closest(pos: Position, map: &Map) -> WalkingNode {
40 let lane = map.get_l(pos.lane());
41 let dst_i = lane.length() - pos.dist_along() <= pos.dist_along();
42 WalkingNode::SidewalkEndpoint(lane.get_directed_parent(), dst_i)
43 }
44
45 fn end_transit(pos: Position, map: &Map) -> WalkingNode {
46 let l = map.get_l(pos.lane());
47 if map.get_i(l.src_i).is_outgoing_border() && pos.dist_along() == Distance::ZERO {
48 return WalkingNode::LeaveMap(l.src_i);
49 }
50 if map.get_i(l.dst_i).is_outgoing_border() && pos.dist_along() == l.length() {
51 return WalkingNode::LeaveMap(l.dst_i);
52 }
53 WalkingNode::closest(pos, map)
54 }
55}
56
57impl SidewalkPathfinder {
58 pub fn empty() -> SidewalkPathfinder {
59 SidewalkPathfinder {
60 nodes: NodeMap::new(),
61 use_transit: false,
62 engine: PathfindEngine::Empty,
63 }
64 }
65
66 pub fn new(
67 map: &Map,
68 use_transit: Option<(&VehiclePathfinder, &VehiclePathfinder)>,
69 engine: &CreateEngine,
70 ) -> SidewalkPathfinder {
71 let mut nodes = NodeMap::new();
72 for r in map.all_roads() {
73 for dr in r.id.both_directions() {
76 for endpt in [true, false] {
77 nodes.get_or_insert(WalkingNode::SidewalkEndpoint(dr, endpt));
78 }
79 }
80 }
81 if use_transit.is_some() {
82 for ts in map.all_transit_stops().keys() {
84 nodes.get_or_insert(WalkingNode::RideTransit(*ts));
85 }
86 for i in map.all_outgoing_borders() {
87 nodes.get_or_insert(WalkingNode::LeaveMap(i.id));
89 }
90 }
91
92 let input_graph = make_input_graph(&nodes, use_transit, map);
93 let engine = engine.create(input_graph);
94
95 SidewalkPathfinder {
96 nodes,
97 use_transit: use_transit.is_some(),
98 engine,
99 }
100 }
101
102 pub fn apply_edits(
103 &mut self,
104 map: &Map,
105 use_transit: Option<(&VehiclePathfinder, &VehiclePathfinder)>,
106 ) {
107 if matches!(self.engine, PathfindEngine::Empty) {
108 return;
109 }
110
111 let input_graph = make_input_graph(&self.nodes, use_transit, map);
112 let engine = self.engine.reuse_ordering().create(input_graph);
113 self.engine = engine;
114 }
115
116 pub fn pathfind(&self, req: PathRequest, map: &Map) -> Option<PathV2> {
117 if matches!(self.engine, PathfindEngine::Empty) {
118 return None;
119 }
120
121 if req.start.lane() == req.end.lane() {
122 return Some(one_step_walking_path(req, map));
123 }
124 let (raw_weight, raw_nodes) = self.engine.calculate_path(
125 self.nodes.get(WalkingNode::closest(req.start, map)),
126 self.nodes.get(WalkingNode::closest(req.end, map)),
127 )?;
128 let nodes: Vec<WalkingNode> = raw_nodes
129 .into_iter()
130 .map(|id| self.nodes.translate_id(id))
131 .collect();
132 let steps = walking_path_to_steps(nodes, map);
133 let cost = unround(raw_weight);
134 Some(PathV2::new(map, steps, req, cost, Vec::new()))
135 }
136
137 pub fn should_use_transit(
140 &self,
141 map: &Map,
142 start: Position,
143 end: Position,
144 ) -> Option<(TransitStopID, Option<TransitStopID>, TransitRouteID)> {
145 if matches!(self.engine, PathfindEngine::Empty) {
146 return None;
147 }
148
149 assert!(self.use_transit);
150
151 let (_, raw_nodes) = self.engine.calculate_path(
152 self.nodes.get(WalkingNode::closest(start, map)),
153 self.nodes.get(WalkingNode::end_transit(end, map)),
154 )?;
155 let nodes: Vec<WalkingNode> = raw_nodes
156 .into_iter()
157 .map(|id| self.nodes.translate_id(id))
158 .collect();
159
160 if false {
161 println!("should_use_transit from {} to {}?", start, end);
162 for n in &nodes {
163 println!("- {:?}", n);
164 }
165 }
166
167 let mut first_stop = None;
168 let mut last_stop = None;
169 let mut possible_routes: Vec<&TransitRoute> = Vec::new();
170 for n in &nodes {
171 match n {
172 WalkingNode::RideTransit(stop2) => {
173 if let Some(stop1) = first_stop {
174 let mut filtered = possible_routes.clone();
181 filtered.retain(|r| {
182 let idx1 = r.stops.iter().position(|s| *s == stop1).unwrap();
183 let idx2 = r.stops.iter().position(|s| s == stop2);
184 idx2.map(|idx2| idx1 < idx2).unwrap_or(false)
185 });
186 if filtered.is_empty() {
187 return Some((
189 first_stop.unwrap(),
190 Some(last_stop?),
193 possible_routes[0].id,
194 ));
195 }
196 last_stop = Some(*stop2);
197 possible_routes = filtered;
198 } else {
199 first_stop = Some(*stop2);
200 possible_routes = map.get_routes_serving_stop(*stop2);
201 assert!(!possible_routes.is_empty());
202 }
203 }
204 WalkingNode::LeaveMap(i) => {
205 if let Some(r) = possible_routes.iter().find(|r| {
207 r.end_border
208 .map(|l| map.get_l(l).dst_i == *i)
209 .unwrap_or(false)
210 }) {
211 return Some((first_stop.unwrap(), None, r.id));
212 }
213 return Some((
215 first_stop.unwrap(),
216 Some(last_stop.expect("impossible transit transfer")),
217 possible_routes[0].id,
218 ));
219 }
220 WalkingNode::SidewalkEndpoint(_, _) => {
221 if let Some(stop1) = first_stop {
222 return Some((
223 stop1,
224 Some(last_stop.expect("impossible transit transfer")),
225 possible_routes[0].id,
226 ));
227 }
228 }
229 }
230 }
231 None
232 }
233
234 pub fn all_costs_from(&self, start: Position, map: &Map) -> HashMap<DirectedRoadID, Duration> {
235 if matches!(self.engine, PathfindEngine::Empty) {
236 return HashMap::new();
237 }
238
239 let start = self.nodes.get(WalkingNode::closest(start, map));
240 let raw_costs = if self.engine.is_dijkstra() {
241 self.engine.all_costs_from(start)
242 } else {
243 let input_graph = make_input_graph(&self.nodes, None, map);
245 CreateEngine::Dijkstra
246 .create(input_graph)
247 .all_costs_from(start)
248 };
249 raw_costs
250 .into_iter()
251 .filter_map(|(k, v)| {
252 if let WalkingNode::SidewalkEndpoint(dr, _) = self.nodes.translate_id(k) {
255 Some((dr, unround(v)))
256 } else {
257 None
258 }
259 })
260 .collect()
261 }
262}
263
264fn make_input_graph(
265 nodes: &NodeMap<WalkingNode>,
266 use_transit: Option<(&VehiclePathfinder, &VehiclePathfinder)>,
267 map: &Map,
268) -> InputGraph {
269 let max_speed = Some(crate::MAX_WALKING_SPEED);
270 let mut input_graph = InputGraph::new();
271
272 for l in map.all_lanes() {
273 if l.is_walkable() {
274 let n1 = nodes.get(WalkingNode::SidewalkEndpoint(
277 l.get_directed_parent(),
278 false,
279 ));
280 let n2 = nodes.get(WalkingNode::SidewalkEndpoint(l.get_directed_parent(), true));
281
282 for (step, pair) in [
283 (PathStep::Lane(l.id), (n1, n2)),
284 (PathStep::ContraflowLane(l.id), (n2, n1)),
285 ] {
286 let mut cost =
287 l.length() / step.max_speed_along(max_speed, PathConstraints::Pedestrian, map);
288 if l.is_shoulder() {
290 cost = 2.0 * cost;
291 }
292 input_graph.add_edge(pair.0, pair.1, round(cost));
293 }
294 }
295 }
296
297 for t in map.all_turns() {
298 if t.between_sidewalks() {
299 let src = map.get_l(t.id.src);
300 let dst = map.get_l(t.id.dst);
301 let from = nodes.get(WalkingNode::SidewalkEndpoint(
302 src.get_directed_parent(),
303 src.dst_i == t.id.parent,
304 ));
305 let to = nodes.get(WalkingNode::SidewalkEndpoint(
306 dst.get_directed_parent(),
307 dst.dst_i == t.id.parent,
308 ));
309
310 let mut cost = t.geom.length()
311 / PathStep::Turn(t.id).max_speed_along(max_speed, PathConstraints::Pedestrian, map)
312 + zone_cost(t.id.to_movement(map), PathConstraints::Pedestrian, map);
313
314 if t.turn_type == TurnType::UnmarkedCrossing {
315 cost = 3.0 * cost;
317 }
318
319 input_graph.add_edge(from, to, round(cost));
320 input_graph.add_edge(to, from, round(cost));
321 }
322 }
323
324 if let Some(graphs) = use_transit {
325 transit_input_graph(&mut input_graph, nodes, map, graphs.0, graphs.1);
326 }
327
328 nodes.guarantee_node_ordering(&mut input_graph);
329 input_graph.freeze();
330 input_graph
331}
332
333fn transit_input_graph(
334 input_graph: &mut InputGraph,
335 nodes: &NodeMap<WalkingNode>,
336 map: &Map,
337 bus_graph: &VehiclePathfinder,
338 train_graph: &VehiclePathfinder,
339) {
340 let max_speed = Some(crate::MAX_WALKING_SPEED);
341 for stop in map.all_transit_stops().values() {
343 let ride_transit = nodes.get(WalkingNode::RideTransit(stop.id));
344 let lane = map.get_l(stop.sidewalk_pos.lane());
345 for (endpt, step) in [
346 (false, PathStep::Lane(lane.id)),
347 (true, PathStep::ContraflowLane(lane.id)),
348 ] {
349 let dist = if endpt {
350 lane.length() - stop.sidewalk_pos.dist_along()
351 } else {
352 stop.sidewalk_pos.dist_along()
353 };
354 let cost = dist / step.max_speed_along(max_speed, PathConstraints::Pedestrian, map);
355 let penalty = Duration::seconds(10.0);
358 let sidewalk = nodes.get(WalkingNode::SidewalkEndpoint(
359 lane.get_directed_parent(),
360 endpt,
361 ));
362 input_graph.add_edge(sidewalk, ride_transit, round(cost + penalty));
363 input_graph.add_edge(ride_transit, sidewalk, round(cost + penalty));
364 }
365 }
366
367 for route in map.all_transit_routes() {
370 for pair in route.stops.windows(2) {
372 let (stop1, stop2) = (map.get_ts(pair[0]), map.get_ts(pair[1]));
373 let req = PathRequest::vehicle(stop1.driving_pos, stop2.driving_pos, route.route_type);
374 let maybe_driving_cost = match route.route_type {
375 PathConstraints::Bus => bus_graph.pathfind(req, map).map(|p| p.get_cost()),
376 PathConstraints::Train => train_graph.pathfind(req, map).map(|p| p.get_cost()),
377 _ => unreachable!(),
378 };
379 if let Some(driving_cost) = maybe_driving_cost {
380 input_graph.add_edge(
381 nodes.get(WalkingNode::RideTransit(stop1.id)),
382 nodes.get(WalkingNode::RideTransit(stop2.id)),
383 round(driving_cost),
384 );
385 } else {
386 panic!(
387 "No transit route from {} to {} now for {}! Prevent this edit",
388 stop1.driving_pos, stop2.driving_pos, route.long_name,
389 );
390 }
391 }
392
393 if let Some(l) = route.end_border {
394 let stop1 = map.get_ts(*route.stops.last().unwrap());
395 let req =
396 PathRequest::vehicle(stop1.driving_pos, Position::end(l, map), route.route_type);
397 let maybe_driving_cost = match route.route_type {
398 PathConstraints::Bus => bus_graph.pathfind(req, map).map(|p| p.get_cost()),
399 PathConstraints::Train => train_graph.pathfind(req, map).map(|p| p.get_cost()),
400 _ => unreachable!(),
401 };
402 if let Some(driving_cost) = maybe_driving_cost {
403 let border = map.get_i(map.get_l(l).dst_i);
404 input_graph.add_edge(
405 nodes.get(WalkingNode::RideTransit(stop1.id)),
406 nodes.get(WalkingNode::LeaveMap(border.id)),
407 round(driving_cost),
408 );
409 } else {
410 panic!(
411 "No transit route from {} to end of {} now for {}! Prevent this edit",
412 stop1.driving_pos, l, route.long_name,
413 );
414 }
415 }
416 }
417}
418
419fn walking_path_to_steps(path: Vec<WalkingNode>, map: &Map) -> Vec<PathStepV2> {
421 let mut steps = Vec::new();
422
423 for pair in path.windows(2) {
424 let (r1, r1_endpt) = match pair[0] {
425 WalkingNode::SidewalkEndpoint(r, endpt) => (r, endpt),
426 WalkingNode::RideTransit(_) => unreachable!(),
427 WalkingNode::LeaveMap(_) => unreachable!(),
428 };
429 let r2 = match pair[1] {
430 WalkingNode::SidewalkEndpoint(r, _) => r,
431 WalkingNode::RideTransit(_) => unreachable!(),
432 WalkingNode::LeaveMap(_) => unreachable!(),
433 };
434
435 if r1 == r2 {
436 if r1_endpt {
437 steps.push(PathStepV2::Contraflow(r1));
438 } else {
439 steps.push(PathStepV2::Along(r1));
440 }
441 } else {
442 let i = if r1_endpt {
443 r1.dst_i(map)
444 } else {
445 r1.src_i(map)
446 };
447 if let Some(t) =
449 map.get_turn_between(r1.must_get_sidewalk(map), r2.must_get_sidewalk(map), i)
450 {
451 steps.push(PathStepV2::Movement(t.id.to_movement(map)));
452 } else if let Some(t) =
453 map.get_turn_between(r2.must_get_sidewalk(map), r1.must_get_sidewalk(map), i)
454 {
455 steps.push(PathStepV2::ContraflowMovement(t.id.to_movement(map)));
456 } else {
457 println!("walking_path_to_steps has a weird path:");
458 for s in &path {
459 println!("- {:?}", s);
460 }
461 panic!(
462 "No turn from {} ({}) to {} ({}) at {}",
463 r1,
464 r1.must_get_sidewalk(map),
465 r2,
466 r2.must_get_sidewalk(map),
467 i
468 );
469 }
470 }
471 }
472
473 if let PathStepV2::Movement(mvmnt) | PathStepV2::ContraflowMovement(mvmnt) = steps[0] {
475 let lane = match steps[0] {
476 PathStepV2::Movement(m) => m.from,
477 PathStepV2::ContraflowMovement(m) => m.to,
478 _ => unreachable!(),
479 };
480 if lane.src_i(map) == mvmnt.parent {
481 steps.insert(0, PathStepV2::Contraflow(lane));
482 } else {
483 steps.insert(0, PathStepV2::Along(lane));
484 }
485 }
486 if let PathStepV2::Movement(mvmnt) | PathStepV2::ContraflowMovement(mvmnt) =
487 steps.last().cloned().unwrap()
488 {
489 let lane = match steps.last().unwrap() {
490 PathStepV2::Movement(m) => m.to,
491 PathStepV2::ContraflowMovement(m) => m.from,
492 _ => unreachable!(),
493 };
494 if lane.src_i(map) == mvmnt.parent {
495 steps.push(PathStepV2::Along(lane));
496 } else {
497 steps.push(PathStepV2::Contraflow(lane));
498 }
499 }
500
501 steps
502}
503
504fn one_step_walking_path(req: PathRequest, map: &Map) -> PathV2 {
506 let l = req.start.lane();
507 let (step_v2, step_v1) = if req.start.dist_along() <= req.end.dist_along() {
510 (
511 PathStepV2::Along(map.get_l(l).get_directed_parent()),
512 PathStep::Lane(l),
513 )
514 } else {
515 (
516 PathStepV2::Contraflow(map.get_l(l).get_directed_parent()),
517 PathStep::ContraflowLane(l),
518 )
519 };
520 let mut cost = (req.start.dist_along() - req.end.dist_along()).abs()
521 / step_v1.max_speed_along(
522 Some(crate::MAX_WALKING_SPEED),
523 PathConstraints::Pedestrian,
524 map,
525 );
526 if map.get_l(l).is_shoulder() {
527 cost = 2.0 * cost;
528 }
529 PathV2::new(map, vec![step_v2], req, cost, Vec::new())
530}