use anyhow::Result;
use serde::{Deserialize, Serialize};
use geom::Duration;
use crate::pathfind::uber_turns::UberTurnV2;
use crate::{
DirectedRoadID, IntersectionID, LaneID, Map, MovementID, Path, PathConstraints, PathRequest,
PathStep, TurnID, UberTurn,
};
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum PathStepV2 {
Along(DirectedRoadID),
Contraflow(DirectedRoadID),
Movement(MovementID),
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct PathV2 {
steps: Vec<PathStepV2>,
req: PathRequest,
cost: Duration,
uber_turns: Vec<UberTurnV2>,
}
impl PathV2 {
pub(crate) fn new(
steps: Vec<PathStepV2>,
req: PathRequest,
cost: Duration,
uber_turns: Vec<UberTurnV2>,
) -> PathV2 {
PathV2 {
steps,
req,
cost,
uber_turns,
}
}
pub(crate) fn from_roads(
mut roads: Vec<DirectedRoadID>,
req: PathRequest,
cost: Duration,
uber_turns: Vec<UberTurnV2>,
map: &Map,
) -> PathV2 {
let mut steps = Vec::new();
for pair in roads.windows(2) {
steps.push(PathStepV2::Along(pair[0]));
steps.push(PathStepV2::Movement(MovementID {
from: pair[0],
to: pair[1],
parent: pair[0].dst_i(map),
crosswalk: false,
}));
}
steps.push(PathStepV2::Along(roads.pop().unwrap()));
PathV2::new(steps, req, cost, uber_turns)
}
pub fn get_req(&self) -> &PathRequest {
&self.req
}
pub fn get_steps(&self) -> &Vec<PathStepV2> {
&self.steps
}
pub fn get_cost(&self) -> Duration {
self.cost
}
pub fn into_v1(mut self, map: &Map) -> Result<Path> {
if self.req.constraints == PathConstraints::Pedestrian {
return self.into_v1_walking(map);
}
let mut graph = petgraph::graphmap::DiGraphMap::new();
for step in &self.steps {
if let PathStepV2::Movement(mvmnt) = step {
for src in mvmnt.from.lanes(self.req.constraints, map) {
for dst in mvmnt.to.lanes(self.req.constraints, map) {
let turn = TurnID {
parent: map.get_l(src).dst_i,
src,
dst,
};
if map.maybe_get_t(turn).is_some() {
graph.add_edge(src, dst, turn);
}
}
}
}
}
let virtual_start_node = LaneID(map.lane_id_counter + 1);
let start_lane = self.req.start.lane();
let start_road = map.get_parent(start_lane);
let start_lane_idx = start_road.offset(start_lane) as isize;
for l in map
.get_l(start_lane)
.get_directed_parent()
.lanes(self.req.constraints, map)
{
let idx_dist = (start_lane_idx - (start_road.offset(l) as isize)).abs();
let cost = 100 * idx_dist as usize;
let fake_turn = TurnID {
parent: IntersectionID(cost),
src: virtual_start_node,
dst: virtual_start_node,
};
graph.add_edge(virtual_start_node, l, fake_turn);
}
match petgraph::algo::astar(
&graph,
virtual_start_node,
|l| l == self.req.end.lane(),
|(_, _, t)| {
if t.src == virtual_start_node {
return t.parent.0;
}
let (lt, lc, slow_lane) = map.get_t(*t).penalty(self.req.constraints, map);
let mut extra_penalty = lt + lc;
if self.req.constraints == PathConstraints::Bike {
extra_penalty += slow_lane;
}
let base = 1;
base + extra_penalty
},
|_| 0,
) {
Some((_, path)) => {
let mut steps = Vec::new();
assert_eq!(path[0], virtual_start_node);
for pair in path.windows(2) {
if pair[0] == virtual_start_node {
continue;
}
steps.push(PathStep::Lane(pair[0]));
steps.push(PathStep::Turn(TurnID {
parent: map.get_l(pair[0]).dst_i,
src: pair[0],
dst: pair[1],
}));
}
steps.push(PathStep::Lane(self.req.end.lane()));
let mut blocked_starts = Vec::new();
if steps[0] != PathStep::Lane(self.req.start.lane()) {
let actual_start = match steps[0] {
PathStep::Lane(l) => l,
_ => unreachable!(),
};
blocked_starts.push(self.req.start);
for l in start_road.get_lanes_between(start_lane, actual_start) {
blocked_starts.push(self.req.start.equiv_pos(l, map));
}
self.req.start = self.req.start.equiv_pos(actual_start, map);
}
let uber_turns = find_uber_turns(&steps, map, self.uber_turns);
Ok(Path::new(map, steps, self.req, uber_turns, blocked_starts))
}
None => bail!(
"Can't transform a road-based path to a lane-based path for {}",
self.req
),
}
}
fn into_v1_walking(self, map: &Map) -> Result<Path> {
let mut steps = Vec::new();
for step in self.steps {
steps.push(match step {
PathStepV2::Along(r) => PathStep::Lane(r.must_get_sidewalk(map)),
PathStepV2::Contraflow(r) => PathStep::ContraflowLane(r.must_get_sidewalk(map)),
PathStepV2::Movement(mvmnt) => PathStep::Turn(TurnID {
src: mvmnt.from.must_get_sidewalk(map),
dst: mvmnt.to.must_get_sidewalk(map),
parent: mvmnt.parent,
}),
});
}
Ok(Path::new(map, steps, self.req, Vec::new(), Vec::new()))
}
}
fn find_uber_turns(
steps: &[PathStep],
map: &Map,
mut uber_turns_v2: Vec<UberTurnV2>,
) -> Vec<UberTurn> {
let num_uts = uber_turns_v2.len();
let mut result = Vec::new();
let mut current_ut = Vec::new();
for step in steps {
if uber_turns_v2.is_empty() {
break;
}
if let PathStep::Turn(t) = step {
if current_ut.is_empty()
&& uber_turns_v2[0].path[0].from == map.get_l(t.src).get_directed_parent()
{
current_ut.push(*t);
}
if !current_ut.is_empty() {
if current_ut.last() != Some(t) {
current_ut.push(*t);
}
if uber_turns_v2[0].path[0].to == map.get_l(t.dst).get_directed_parent() {
result.push(UberTurn {
path: current_ut.drain(..).collect(),
});
uber_turns_v2.remove(0);
}
}
}
}
assert!(current_ut.is_empty());
assert_eq!(num_uts, result.len());
result
}