map_model/pathfind/
v2.rs

1//! Structures related to the new road-based pathfinding
2//! (https://github.com/a-b-street/abstreet/issues/555) live here. When the transition is done,
3//! things here will probably move into pathfind/mod.rs.
4
5use anyhow::Result;
6use serde::{Deserialize, Serialize};
7
8use geom::{Duration, Polygon, Ring, Speed};
9
10use crate::pathfind::uber_turns::UberTurnV2;
11use crate::{
12    osm, DirectedRoadID, Direction, IntersectionID, LaneID, Map, MovementID, Path, PathConstraints,
13    PathRequest, PathStep, RoadID, TurnID, UberTurn,
14};
15
16/// One step along a path.
17#[derive(Clone, Debug, Serialize, Deserialize)]
18pub enum PathStepV2 {
19    /// Original direction
20    Along(DirectedRoadID),
21    /// Opposite direction, sidewalks only
22    Contraflow(DirectedRoadID),
23    Movement(MovementID),
24    ContraflowMovement(MovementID),
25}
26
27/// A path between two endpoints for a particular mode. This representation is immutable and doesn't
28/// prescribe specific lanes and turns to follow.
29#[derive(Clone, Debug, Serialize, Deserialize)]
30pub struct PathV2 {
31    steps: Vec<PathStepV2>,
32    // TODO There will be a PathRequestV2, but I'm not sure how it'll change yet.
33    req: PathRequest,
34    cost: Duration,
35    // TODO Temporarily we'll keep plumbing these along for path_v2_to_v1 to work, but we'll
36    // probably just discover uber-turns lazily at the simulation layer instead.
37    uber_turns: Vec<UberTurnV2>,
38
39    // If alt_start was used, this may differ from req.start.lane()
40    orig_start_lane: LaneID,
41}
42
43impl PathV2 {
44    pub(crate) fn new(
45        map: &Map,
46        steps: Vec<PathStepV2>,
47        mut req: PathRequest,
48        cost: Duration,
49        uber_turns: Vec<UberTurnV2>,
50    ) -> PathV2 {
51        let orig_start_lane = req.start.lane();
52
53        // If we had two possible start positions, figure out which one we wound up using
54        // TODO Doesn't make sense for pedestrians yet
55        if req.constraints != PathConstraints::Pedestrian {
56            if let Some((pos, _)) = req.alt_start {
57                if let PathStepV2::Along(dr) = steps[0] {
58                    if map.get_l(req.start.lane()).get_directed_parent() == dr {
59                        // We used the original side, fine. No need to preserve this.
60                    } else {
61                        assert_eq!(map.get_l(pos.lane()).get_directed_parent(), dr);
62                        req.start = pos;
63                    }
64                    req.alt_start = None;
65                } else {
66                    unreachable!()
67                }
68            }
69        }
70
71        // TODO Port validate_continuity and validate_restrictions?
72        PathV2 {
73            steps,
74            req,
75            cost,
76            uber_turns,
77            orig_start_lane,
78        }
79    }
80
81    /// Vehicle implementations often just calculate the sequence of roads. Turn that into
82    /// PathStepV2 here.
83    pub fn from_roads(
84        mut roads: Vec<DirectedRoadID>,
85        req: PathRequest,
86        cost: Duration,
87        uber_turns: Vec<UberTurnV2>,
88        map: &Map,
89    ) -> PathV2 {
90        let mut steps = Vec::new();
91        for pair in roads.windows(2) {
92            steps.push(PathStepV2::Along(pair[0]));
93            steps.push(PathStepV2::Movement(MovementID {
94                from: pair[0],
95                to: pair[1],
96                parent: pair[0].dst_i(map),
97                crosswalk: false,
98            }));
99        }
100        steps.push(PathStepV2::Along(roads.pop().unwrap()));
101        PathV2::new(map, steps, req, cost, uber_turns)
102    }
103
104    /// The original PathRequest used to produce this path.
105    pub fn get_req(&self) -> &PathRequest {
106        &self.req
107    }
108
109    /// All steps in this path.
110    pub fn get_steps(&self) -> &Vec<PathStepV2> {
111        &self.steps
112    }
113
114    /// The time needed to perform this path. This time is not a lower bound; physically following
115    /// the path might be faster. This time incorporates costs like using sub-optimal lanes, taking
116    /// difficult turns, and crossing private roads (which are modelled with a large penalty!)
117    pub fn get_cost(&self) -> Duration {
118        self.cost
119    }
120
121    /// Estimate how long following the path will take in the best case, assuming no traffic, delay
122    /// at intersections, elevation, or penalties for crossing private roads. To determine the
123    /// speed along each step, the agent's optional max_speed must be known.
124    ///
125    /// TODO Hack. The one use of this actually needs to apply main_road_penalty. We want to omit
126    /// some penalties, but use others. Come up with a better way of expressing this.
127    pub fn estimate_duration(
128        &self,
129        map: &Map,
130        max_speed: Option<Speed>,
131        main_road_penalty: Option<f64>,
132    ) -> Duration {
133        let mut total = Duration::ZERO;
134        for step in &self.steps {
135            let (dist, mut speed);
136            let mut multiplier = 1.0;
137            match step {
138                PathStepV2::Along(dr) | PathStepV2::Contraflow(dr) => {
139                    let road = map.get_r(dr.road);
140                    dist = road.length();
141                    speed = road.speed_limit;
142
143                    if let Some(penalty) = main_road_penalty {
144                        if road.get_rank() != osm::RoadRank::Local {
145                            multiplier = penalty;
146                        }
147                    }
148                }
149                PathStepV2::Movement(m) | PathStepV2::ContraflowMovement(m) => {
150                    if let Some(movement) = map.get_movement(*m) {
151                        dist = movement.geom.length();
152                        speed = map
153                            .get_r(m.from.road)
154                            .speed_limit
155                            .min(map.get_r(m.to.road).speed_limit);
156                    } else {
157                        // Assume it's a SharedSidewalkCorner and just skip
158                        continue;
159                    }
160                }
161            }
162            if let Some(max) = max_speed {
163                speed = speed.min(max);
164            }
165
166            total += multiplier * (dist / speed);
167        }
168        total
169    }
170
171    /// Transform a sequence of roads representing a path into the current lane-based path, by
172    /// picking particular lanes and turns to use.
173    pub fn into_v1(mut self, map: &Map) -> Result<Path> {
174        if self.req.constraints == PathConstraints::Pedestrian {
175            return self.into_v1_walking(map);
176        }
177
178        // This is a somewhat brute-force method: run Dijkstra's algorithm on a graph of lanes and
179        // turns, but only build the graph along the path of roads we've already found. This handles
180        // arbitrary lookahead needed, and forces use of the original start/end lanes requested.
181        let mut graph = petgraph::graphmap::DiGraphMap::new();
182        for step in &self.steps {
183            if let PathStepV2::Movement(mvmnt) = step {
184                for src in mvmnt.from.lanes(self.req.constraints, map) {
185                    for dst in mvmnt.to.lanes(self.req.constraints, map) {
186                        let turn = TurnID {
187                            parent: map.get_l(src).dst_i,
188                            src,
189                            dst,
190                        };
191                        if map.maybe_get_t(turn).is_some() {
192                            graph.add_edge(src, dst, turn);
193                        }
194                    }
195                }
196            }
197        }
198
199        // The v2 path might immediately require a turn that's only available from some lanes. If
200        // the req.start lane can't make that turn, then producing the v1 path would fail. So let's
201        // allow starting from any lane on the same side of the road. Since petgraph can only start
202        // from a single node and since we want to prefer the originally requested lane anyway,
203        // create a virtual start node and connect it to all possible starting lanes.
204        let virtual_start_node = LaneID {
205            road: RoadID(map.all_roads().len()),
206            offset: 0,
207        };
208        let start_lane = self.req.start.lane();
209        let start_road = map.get_parent(start_lane);
210        let start_lane_idx = start_lane.offset as isize;
211        for l in map
212            .get_l(start_lane)
213            .get_directed_parent()
214            .lanes(self.req.constraints, map)
215        {
216            // Heavily penalize starting from something other than the originally requested lane.
217            // At the simulation layer, we may need to block intermediate lanes to exit a driveway,
218            // so reflect that cost here. The high cost should only be worth it when the v2 path
219            // requires that up-front turn from certain lanes.
220            //
221            // TODO This is only valid if we were leaving from a driveway! This is making some
222            // buses warp after making a stop.
223            let idx_dist = (start_lane_idx - (l.offset as isize)).abs();
224            let cost = 100 * idx_dist as usize;
225            let fake_turn = TurnID {
226                // Just encode the cost here for convenience
227                parent: IntersectionID(cost),
228                src: virtual_start_node,
229                dst: virtual_start_node,
230            };
231            graph.add_edge(virtual_start_node, l, fake_turn);
232        }
233
234        match petgraph::algo::astar(
235            &graph,
236            virtual_start_node,
237            |l| l == self.req.end.lane(),
238            |(_, _, t)| {
239                if t.src == virtual_start_node {
240                    return t.parent.0;
241                }
242
243                // Normally opportunistic lane-changing adjusts the path live, but that doesn't work
244                // near uber-turns. So still use some of the penalties here.
245                let (lt, lc, slow_lane) = map.get_t(*t).penalty(self.req.constraints, map);
246                let mut extra_penalty = lt + lc;
247                if self.req.constraints == PathConstraints::Bike {
248                    extra_penalty += slow_lane;
249                }
250                // Always treat every lane/turn as at least cost 1; otherwise A* can't understand
251                // that a final path with 10 steps costs more than one with 5. The
252                // road-based pathfinding has already chosen the overall route; when
253                // we're picking individual lanes, the length of each lane along one
254                // road is going to be about the same.
255                let base = 1;
256                base + extra_penalty
257            },
258            |_| 0,
259        ) {
260            Some((_, path)) => {
261                let mut steps = Vec::new();
262                // Skip the first node; it's always virtual_start_node
263                assert_eq!(path[0], virtual_start_node);
264                for pair in path.windows(2) {
265                    if pair[0] == virtual_start_node {
266                        continue;
267                    }
268
269                    steps.push(PathStep::Lane(pair[0]));
270                    // We don't need to look for this turn in the map; we know it exists.
271                    steps.push(PathStep::Turn(TurnID {
272                        parent: map.get_l(pair[0]).dst_i,
273                        src: pair[0],
274                        dst: pair[1],
275                    }));
276                }
277                steps.push(PathStep::Lane(self.req.end.lane()));
278                let mut blocked_starts = Vec::new();
279                if steps[0] != PathStep::Lane(self.orig_start_lane) {
280                    let actual_start = match steps[0] {
281                        PathStep::Lane(l) => l,
282                        _ => unreachable!(),
283                    };
284                    blocked_starts.push(self.orig_start_lane);
285                    blocked_starts
286                        .extend(start_road.get_lanes_between(self.orig_start_lane, actual_start));
287                    // Sometimes a no-op for exiting off-side
288                    self.req.start = self.req.start.equiv_pos(actual_start, map);
289                }
290                let uber_turns = find_uber_turns(&steps, map, self.uber_turns);
291                Ok(Path::new(map, steps, self.req, uber_turns, blocked_starts))
292            }
293            None => bail!(
294                "Can't transform a road-based path to a lane-based path for {}",
295                self.req
296            ),
297        }
298    }
299
300    fn into_v1_walking(self, map: &Map) -> Result<Path> {
301        let mut steps = Vec::new();
302        for step in self.steps {
303            steps.push(match step {
304                PathStepV2::Along(r) => PathStep::Lane(r.must_get_sidewalk(map)),
305                PathStepV2::Contraflow(r) => PathStep::ContraflowLane(r.must_get_sidewalk(map)),
306                PathStepV2::Movement(mvmnt) => PathStep::Turn(TurnID {
307                    src: mvmnt.from.must_get_sidewalk(map),
308                    dst: mvmnt.to.must_get_sidewalk(map),
309                    parent: mvmnt.parent,
310                }),
311                PathStepV2::ContraflowMovement(mvmnt) => PathStep::ContraflowTurn(TurnID {
312                    src: mvmnt.from.must_get_sidewalk(map),
313                    dst: mvmnt.to.must_get_sidewalk(map),
314                    parent: mvmnt.parent,
315                }),
316            });
317        }
318        Ok(Path::new(map, steps, self.req, Vec::new(), Vec::new()))
319    }
320
321    pub fn crosses_road(&self, r: RoadID) -> bool {
322        self.steps.iter().any(|step| match step {
323            PathStepV2::Along(dr) => dr.road == r,
324            _ => false,
325        })
326    }
327
328    /// Draws the thickened path, matching entire roads. Ignores the path's exact starting and
329    /// ending distance. Doesn't handle contraflow yet.
330    pub fn trace_v2(&self, map: &Map) -> Result<Polygon> {
331        let mut left_pts = Vec::new();
332        let mut right_pts = Vec::new();
333        for step in &self.steps {
334            match step {
335                PathStepV2::Along(dr) => {
336                    let road = map.get_r(dr.road);
337                    let width = road.get_half_width();
338                    if dr.dir == Direction::Fwd {
339                        left_pts.extend(road.center_pts.shift_left(width)?.into_points());
340                        right_pts.extend(road.center_pts.shift_right(width)?.into_points());
341                    } else {
342                        left_pts
343                            .extend(road.center_pts.shift_right(width)?.reversed().into_points());
344                        right_pts
345                            .extend(road.center_pts.shift_left(width)?.reversed().into_points());
346                    }
347                }
348                PathStepV2::Contraflow(_) => todo!(),
349                // Just make a straight line across the intersection. It'd be fancier to try and
350                // trace along.
351                PathStepV2::Movement(_) | PathStepV2::ContraflowMovement(_) => {}
352            }
353        }
354        right_pts.reverse();
355        left_pts.extend(right_pts);
356        left_pts.push(left_pts[0]);
357        Ok(Ring::deduping_new(left_pts)?.into_polygon())
358    }
359
360    /// Returns polygons covering the entire path. Ignores the path's exact starting and ending
361    /// distance.
362    pub fn trace_all_polygons(&self, map: &Map) -> Vec<Polygon> {
363        let mut polygons = Vec::new();
364        for step in &self.steps {
365            match step {
366                PathStepV2::Along(dr) | PathStepV2::Contraflow(dr) => {
367                    polygons.push(map.get_r(dr.road).get_thick_polygon());
368                }
369                PathStepV2::Movement(m) | PathStepV2::ContraflowMovement(m) => {
370                    polygons.push(map.get_i(m.parent).polygon.clone());
371                }
372            }
373        }
374        polygons
375    }
376}
377
378fn find_uber_turns(
379    steps: &[PathStep],
380    map: &Map,
381    mut uber_turns_v2: Vec<UberTurnV2>,
382) -> Vec<UberTurn> {
383    // Pathfinding v1 needs to know the uber turns that the path crosses, for the simulation layer.
384    // Since we now construct the path in two stages, it's easiest to just reconstruct the uber
385    // turns after building the lane-based path.
386
387    let num_uts = uber_turns_v2.len();
388    let mut result = Vec::new();
389    let mut current_ut = Vec::new();
390    for step in steps {
391        // Optimization
392        if uber_turns_v2.is_empty() {
393            break;
394        }
395
396        if let PathStep::Turn(t) = step {
397            if current_ut.is_empty()
398                && uber_turns_v2[0].path[0].from == map.get_l(t.src).get_directed_parent()
399            {
400                current_ut.push(*t);
401            }
402
403            if !current_ut.is_empty() {
404                if current_ut.last() != Some(t) {
405                    current_ut.push(*t);
406                }
407                if uber_turns_v2[0].path[0].to == map.get_l(t.dst).get_directed_parent() {
408                    result.push(UberTurn {
409                        path: current_ut.drain(..).collect(),
410                    });
411                    uber_turns_v2.remove(0);
412                }
413            }
414        }
415    }
416    assert!(current_ut.is_empty());
417    assert_eq!(num_uts, result.len());
418    result
419}