use std::cell::RefCell;
use fast_paths::{deserialize_32, serialize_32, FastGraph, InputGraph, PathCalculator};
use serde::{Deserialize, Serialize};
use thread_local::ThreadLocal;
use abstutil::MultiMap;
use crate::pathfind::node_map::{deserialize_nodemap, NodeMap};
use crate::pathfind::uber_turns::{IntersectionCluster, UberTurn};
use crate::{
Lane, LaneID, Map, Path, PathConstraints, PathRequest, PathStep, RoutingParams, Turn, TurnID,
};
#[derive(Serialize, Deserialize)]
pub struct VehiclePathfinder {
#[serde(serialize_with = "serialize_32", deserialize_with = "deserialize_32")]
graph: FastGraph,
#[serde(deserialize_with = "deserialize_nodemap")]
nodes: NodeMap<Node>,
uber_turns: Vec<UberTurn>,
constraints: PathConstraints,
#[serde(skip_serializing, skip_deserializing)]
path_calc: ThreadLocal<RefCell<PathCalculator>>,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize)]
enum Node {
Lane(LaneID),
UberTurn(usize),
}
impl VehiclePathfinder {
pub fn new(
map: &Map,
constraints: PathConstraints,
seed: Option<&VehiclePathfinder>,
) -> VehiclePathfinder {
let mut nodes = NodeMap::new();
for l in map.all_lanes() {
nodes.get_or_insert(Node::Lane(l.id));
}
let mut uber_turns = Vec::new();
for ic in IntersectionCluster::find_all(map) {
for ut in ic.uber_turns {
nodes.get_or_insert(Node::UberTurn(uber_turns.len()));
uber_turns.push(ut);
}
}
let input_graph = make_input_graph(map, &nodes, &uber_turns, constraints);
let graph = if let Some(seed) = seed {
let node_ordering = seed.graph.get_node_ordering();
fast_paths::prepare_with_order(&input_graph, &node_ordering).unwrap()
} else {
fast_paths::prepare(&input_graph)
};
VehiclePathfinder {
graph,
nodes,
uber_turns,
constraints,
path_calc: ThreadLocal::new(),
}
}
pub fn pathfind(&self, req: &PathRequest, map: &Map) -> Option<(Path, usize)> {
assert!(!map.get_l(req.start.lane()).is_walkable());
let mut calc = self
.path_calc
.get_or(|| RefCell::new(fast_paths::create_calculator(&self.graph)))
.borrow_mut();
let raw_path = calc.calc_path(
&self.graph,
self.nodes.get(Node::Lane(req.start.lane())),
self.nodes.get(Node::Lane(req.end.lane())),
)?;
let mut steps = Vec::new();
let mut uber_turns = Vec::new();
for pair in self.nodes.translate(&raw_path).windows(2) {
match (pair[0], pair[1]) {
(Node::Lane(l1), Node::Lane(l2)) => {
steps.push(PathStep::Lane(l1));
steps.push(PathStep::Turn(TurnID {
parent: map.get_l(l1).dst_i,
src: l1,
dst: l2,
}));
}
(Node::Lane(l), Node::UberTurn(ut)) => {
steps.push(PathStep::Lane(l));
let ut = self.uber_turns[ut].clone();
for t in &ut.path {
steps.push(PathStep::Turn(*t));
steps.push(PathStep::Lane(t.dst));
}
steps.pop();
uber_turns.push(ut);
}
(Node::UberTurn(_), Node::Lane(_)) => {
}
(Node::UberTurn(_), Node::UberTurn(_)) => unreachable!(),
}
}
steps.push(PathStep::Lane(req.end.lane()));
Some((
Path::new(map, steps, req.clone(), uber_turns),
raw_path.get_weight(),
))
}
pub fn apply_edits(&mut self, map: &Map) {
let input_graph = make_input_graph(map, &self.nodes, &self.uber_turns, self.constraints);
let node_ordering = self.graph.get_node_ordering();
self.graph = fast_paths::prepare_with_order(&input_graph, &node_ordering).unwrap();
}
}
fn make_input_graph(
map: &Map,
nodes: &NodeMap<Node>,
uber_turns: &Vec<UberTurn>,
constraints: PathConstraints,
) -> InputGraph {
let mut input_graph = InputGraph::new();
let mut uber_turn_entrances: MultiMap<LaneID, usize> = MultiMap::new();
for (idx, ut) in uber_turns.iter().enumerate() {
nodes.get(Node::UberTurn(idx));
if ut
.path
.iter()
.all(|t| constraints.can_use(map.get_l(t.dst), map))
{
uber_turn_entrances.insert(ut.entry(), idx);
}
}
let num_lanes = map.all_lanes().len();
let mut used_last_uber_turn = false;
for l in map.all_lanes() {
let from = nodes.get(Node::Lane(l.id));
let mut any = false;
if constraints.can_use(l, map)
&& map
.get_r(l.parent)
.access_restrictions
.allow_through_traffic
.contains(constraints)
{
let indices = uber_turn_entrances.get(l.id);
if indices.is_empty() {
for turn in map.get_turns_for(l.id, constraints) {
any = true;
input_graph.add_edge(
from,
nodes.get(Node::Lane(turn.id.dst)),
round(driving_cost(
l,
turn,
constraints,
map.routing_params(),
map,
)),
);
}
} else {
for idx in indices {
any = true;
let ut = &uber_turns[*idx];
let mut sum_cost = 0.0;
for t in &ut.path {
sum_cost += driving_cost(
map.get_l(t.src),
map.get_t(*t),
constraints,
map.routing_params(),
map,
);
}
input_graph.add_edge(from, nodes.get(Node::UberTurn(*idx)), round(sum_cost));
input_graph.add_edge(
nodes.get(Node::UberTurn(*idx)),
nodes.get(Node::Lane(ut.exit())),
1,
);
if *idx == uber_turns.len() - 1 {
used_last_uber_turn = true;
}
}
}
}
if !any && l.id.0 == num_lanes - 1 {
input_graph.add_edge(from, nodes.get(Node::Lane(LaneID(0))), 1);
}
}
if !used_last_uber_turn && !uber_turns.is_empty() {
input_graph.add_edge(
nodes.get(Node::UberTurn(uber_turns.len() - 1)),
nodes.get(Node::UberTurn(0)),
1,
);
}
input_graph.freeze();
input_graph
}
pub fn driving_cost(
lane: &Lane,
turn: &Turn,
constraints: PathConstraints,
params: &RoutingParams,
map: &Map,
) -> f64 {
let base = match constraints {
PathConstraints::Car | PathConstraints::Train => {
let t1 = lane.length() / map.get_r(lane.parent).speed_limit;
let t2 = turn.geom.length() / map.get_parent(turn.id.dst).speed_limit;
(t1 + t2).inner_seconds()
}
PathConstraints::Bike => {
let dist = lane.length() + turn.geom.length();
let lt_penalty = if lane.is_biking() {
params.bike_lane_penalty
} else if lane.is_bus() {
params.bus_lane_penalty
} else {
assert!(lane.is_driving());
params.driving_lane_penalty
};
(lt_penalty * dist).inner_meters()
}
PathConstraints::Bus => {
let t1 = lane.length() / map.get_r(lane.parent).speed_limit;
let t2 = turn.geom.length() / map.get_parent(turn.id.dst).speed_limit;
let lt_penalty = if lane.is_bus() {
1.0
} else {
assert!(lane.is_driving());
1.1
};
(lt_penalty * (t1 + t2)).inner_seconds()
}
PathConstraints::Pedestrian => unreachable!(),
};
let (lt, lc, slow_lane) = turn.penalty(map);
let mut extra_penalty = lt + lc;
if constraints == PathConstraints::Bike {
extra_penalty = slow_lane;
}
base + (extra_penalty as f64)
}
fn round(cost: f64) -> usize {
(cost.round() as usize).max(1)
}