mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 09:24:26 +03:00
try to handle walking around private zones too. gluing the paths
together is buggy, giving up for now and leaving disabled
This commit is contained in:
parent
694b70c438
commit
2e1612c3db
@ -37,6 +37,7 @@ Measure the effects:
|
||||
- [Map model](docs/articles/map/article.md)
|
||||
- [Traffic simulation](docs/articles/trafficsim/article.md)
|
||||
- [Running A/B Street in a new city](docs/new_city.md)
|
||||
- [UX design](https://yuwen-li.com/work/abstreet)
|
||||
- Presentations
|
||||
- April 2020 Rust meetup:
|
||||
[recording](https://www.youtube.com/watch?v=chYd5I-5oyc),
|
||||
|
@ -273,4 +273,14 @@ impl Lane {
|
||||
0
|
||||
})
|
||||
}
|
||||
|
||||
pub fn common_endpt(&self, other: &Lane) -> IntersectionID {
|
||||
if self.src_i == other.src_i || self.src_i == other.dst_i {
|
||||
self.src_i
|
||||
} else if self.dst_i == other.src_i || self.dst_i == other.dst_i {
|
||||
self.dst_i
|
||||
} else {
|
||||
panic!("{} and {} don't share an endpoint", self.id, other.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ mod walking;
|
||||
pub use self::driving::cost;
|
||||
use self::driving::VehiclePathfinder;
|
||||
use self::walking::SidewalkPathfinder;
|
||||
pub use self::walking::{one_step_walking_path, walking_cost, walking_path_to_steps, WalkingNode};
|
||||
use crate::{
|
||||
osm, BusRouteID, BusStopID, Intersection, Lane, LaneID, LaneType, Map, Position, Traversable,
|
||||
TurnID, Zone,
|
||||
@ -320,18 +321,26 @@ impl Path {
|
||||
}
|
||||
|
||||
fn prepend(&mut self, other: Path, map: &Map) {
|
||||
match (*other.steps.back().unwrap(), self.steps[0]) {
|
||||
(PathStep::Lane(src), PathStep::Lane(dst)) => {
|
||||
let turn = TurnID {
|
||||
parent: map.get_l(src).dst_i,
|
||||
src,
|
||||
dst,
|
||||
};
|
||||
self.steps.push_front(PathStep::Turn(turn));
|
||||
self.total_length += map.get_t(turn).geom.length();
|
||||
let common_i = map
|
||||
.get_l(other.steps.back().unwrap().as_lane())
|
||||
.common_endpt(map.get_l(self.steps[0].as_lane()));
|
||||
match *other.steps.back().unwrap() {
|
||||
PathStep::Lane(l) => {
|
||||
if map.get_l(l).src_i == common_i {
|
||||
self.steps.push_front(PathStep::ContraflowLane(l));
|
||||
}
|
||||
}
|
||||
PathStep::ContraflowLane(l) => {
|
||||
if map.get_l(l).dst_i == common_i {
|
||||
self.steps.push_front(PathStep::Lane(l));
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
let turn = glue(*other.steps.back().unwrap(), self.steps[0], map);
|
||||
self.steps.push_front(PathStep::Turn(turn));
|
||||
self.total_length += map.get_t(turn).geom.length();
|
||||
for step in other.steps.into_iter().rev() {
|
||||
self.steps.push_front(step);
|
||||
}
|
||||
@ -340,24 +349,56 @@ impl Path {
|
||||
}
|
||||
|
||||
fn append(&mut self, other: Path, map: &Map) {
|
||||
match (*self.steps.back().unwrap(), other.steps[0]) {
|
||||
(PathStep::Lane(src), PathStep::Lane(dst)) => {
|
||||
let turn = TurnID {
|
||||
parent: map.get_l(src).dst_i,
|
||||
src,
|
||||
dst,
|
||||
};
|
||||
self.steps.push_back(PathStep::Turn(turn));
|
||||
self.total_length += map.get_t(turn).geom.length();
|
||||
// TODO There's a better way to do this. We might be able to remove a step instead of
|
||||
// doubling back sometimes.
|
||||
let common_i = map
|
||||
.get_l(self.steps.back().unwrap().as_lane())
|
||||
.common_endpt(map.get_l(other.steps[0].as_lane()));
|
||||
match *self.steps.back().unwrap() {
|
||||
PathStep::Lane(l) => {
|
||||
if map.get_l(l).src_i == common_i {
|
||||
self.steps.push_back(PathStep::ContraflowLane(l));
|
||||
}
|
||||
}
|
||||
PathStep::ContraflowLane(l) => {
|
||||
if map.get_l(l).dst_i == common_i {
|
||||
self.steps.push_back(PathStep::Lane(l));
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
let turn = glue(*self.steps.back().unwrap(), other.steps[0], map);
|
||||
self.steps.push_back(PathStep::Turn(turn));
|
||||
self.total_length += map.get_t(turn).geom.length();
|
||||
self.steps.extend(other.steps);
|
||||
self.total_length += other.total_length;
|
||||
self.total_lanes += other.total_lanes;
|
||||
}
|
||||
}
|
||||
|
||||
fn glue(step1: PathStep, step2: PathStep, map: &Map) -> TurnID {
|
||||
match step1 {
|
||||
PathStep::Lane(src) => match step2 {
|
||||
PathStep::Lane(dst) | PathStep::ContraflowLane(dst) => TurnID {
|
||||
parent: map.get_l(src).dst_i,
|
||||
src,
|
||||
dst,
|
||||
},
|
||||
_ => unreachable!(),
|
||||
},
|
||||
PathStep::ContraflowLane(src) => match step2 {
|
||||
PathStep::Lane(dst) | PathStep::ContraflowLane(dst) => TurnID {
|
||||
parent: map.get_l(src).src_i,
|
||||
src,
|
||||
dst,
|
||||
},
|
||||
_ => unreachable!(),
|
||||
},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
// Who's asking for a path?
|
||||
// TODO This is an awful name.
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
|
||||
@ -553,9 +594,6 @@ impl Pathfinder {
|
||||
let end_r = map.get_parent(req.end.lane());
|
||||
|
||||
if start_r.is_private() && end_r.is_private() {
|
||||
if req.constraints == PathConstraints::Pedestrian {
|
||||
return None;
|
||||
}
|
||||
let zone1 = map.road_to_zone(start_r.id);
|
||||
let zone2 = map.road_to_zone(end_r.id);
|
||||
if zone1.id == zone2.id {
|
||||
@ -634,7 +672,14 @@ impl Pathfinder {
|
||||
},
|
||||
map,
|
||||
)?;
|
||||
req.start = Position::new(dst, Distance::ZERO);
|
||||
req.start = Position::new(
|
||||
dst,
|
||||
match interior_path.steps.back().unwrap() {
|
||||
PathStep::Lane(_) => Distance::ZERO,
|
||||
PathStep::ContraflowLane(_) => map.get_l(dst).length(),
|
||||
_ => unreachable!(),
|
||||
},
|
||||
);
|
||||
let mut main_path = match req.constraints {
|
||||
PathConstraints::Pedestrian => self.walking_graph.pathfind(&req, map),
|
||||
PathConstraints::Car => self.car_graph.pathfind(&req, map).map(|(p, _)| p),
|
||||
@ -652,15 +697,38 @@ impl Pathfinder {
|
||||
zone: &Zone,
|
||||
map: &Map,
|
||||
) -> Option<Path> {
|
||||
// TODO Should probably try all combos of incoming/outgoing lanes per border, but I think
|
||||
// generally one should suffice?
|
||||
let src = i
|
||||
// Because sidewalks aren't all immediately linked, insist on a (src, dst) combo that
|
||||
// are actually connected by a turn.
|
||||
let src_choices = i
|
||||
.get_incoming_lanes(map, req.constraints)
|
||||
.find(|l| !zone.members.contains(&map.get_l(*l).parent))?;
|
||||
let dst = i
|
||||
.filter(|l| !zone.members.contains(&map.get_l(*l).parent))
|
||||
.collect::<Vec<_>>();
|
||||
let dst_choices = i
|
||||
.get_outgoing_lanes(map, req.constraints)
|
||||
.into_iter()
|
||||
.find(|l| zone.members.contains(&map.get_l(*l).parent))?;
|
||||
.filter(|l| zone.members.contains(&map.get_l(*l).parent))
|
||||
.collect::<Vec<_>>();
|
||||
let (src, dst) = {
|
||||
let mut result = None;
|
||||
'OUTER: for l1 in src_choices {
|
||||
for l2 in &dst_choices {
|
||||
if l1 != *l2
|
||||
&& map
|
||||
.maybe_get_t(TurnID {
|
||||
parent: i.id,
|
||||
src: l1,
|
||||
dst: *l2,
|
||||
})
|
||||
.is_some()
|
||||
{
|
||||
result = Some((l1, *l2));
|
||||
break 'OUTER;
|
||||
}
|
||||
}
|
||||
}
|
||||
result?
|
||||
};
|
||||
|
||||
let interior_path = zone.pathfind(
|
||||
PathRequest {
|
||||
start: Position::new(dst, Distance::ZERO),
|
||||
@ -670,7 +738,15 @@ impl Pathfinder {
|
||||
map,
|
||||
)?;
|
||||
let orig_end_dist = req.end.dist_along();
|
||||
req.end = Position::new(src, map.get_l(src).length());
|
||||
req.end = Position::new(
|
||||
src,
|
||||
match interior_path.steps[0] {
|
||||
PathStep::Lane(_) => map.get_l(src).length(),
|
||||
PathStep::ContraflowLane(_) => Distance::ZERO,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
);
|
||||
|
||||
let mut main_path = match req.constraints {
|
||||
PathConstraints::Pedestrian => self.walking_graph.pathfind(&req, map),
|
||||
PathConstraints::Car => self.car_graph.pathfind(&req, map).map(|(p, _)| p),
|
||||
|
@ -14,34 +14,41 @@ pub struct SidewalkPathfinder {
|
||||
#[serde(serialize_with = "serialize_32", deserialize_with = "deserialize_32")]
|
||||
graph: FastGraph,
|
||||
#[serde(deserialize_with = "deserialize_nodemap")]
|
||||
nodes: NodeMap<Node>,
|
||||
nodes: NodeMap<WalkingNode>,
|
||||
use_transit: bool,
|
||||
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
path_calc: ThreadLocal<RefCell<PathCalculator>>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Serialize, Deserialize)]
|
||||
enum Node {
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Serialize, Deserialize)]
|
||||
pub enum WalkingNode {
|
||||
// false is src_i, true is dst_i
|
||||
SidewalkEndpoint(LaneID, bool),
|
||||
RideBus(BusStopID),
|
||||
}
|
||||
|
||||
impl WalkingNode {
|
||||
pub fn closest(pos: Position, map: &Map) -> WalkingNode {
|
||||
let dst_i = map.get_l(pos.lane()).length() - pos.dist_along() <= pos.dist_along();
|
||||
WalkingNode::SidewalkEndpoint(pos.lane(), dst_i)
|
||||
}
|
||||
}
|
||||
|
||||
impl SidewalkPathfinder {
|
||||
pub fn new(map: &Map, use_transit: bool, bus_graph: &VehiclePathfinder) -> SidewalkPathfinder {
|
||||
let mut nodes = NodeMap::new();
|
||||
// We're assuming that to start with, no sidewalks are closed for construction!
|
||||
for l in map.all_lanes() {
|
||||
if l.is_sidewalk() {
|
||||
nodes.get_or_insert(Node::SidewalkEndpoint(l.id, true));
|
||||
nodes.get_or_insert(Node::SidewalkEndpoint(l.id, false));
|
||||
nodes.get_or_insert(WalkingNode::SidewalkEndpoint(l.id, true));
|
||||
nodes.get_or_insert(WalkingNode::SidewalkEndpoint(l.id, false));
|
||||
}
|
||||
}
|
||||
if use_transit {
|
||||
// Add a node for each bus stop.
|
||||
for stop in map.all_bus_stops().values() {
|
||||
nodes.get_or_insert(Node::RideBus(stop.id));
|
||||
nodes.get_or_insert(WalkingNode::RideBus(stop.id));
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,30 +70,8 @@ impl SidewalkPathfinder {
|
||||
}
|
||||
|
||||
pub fn pathfind(&self, req: &PathRequest, map: &Map) -> Option<Path> {
|
||||
// Special-case one-step paths.
|
||||
// TODO Maybe we don't need these special cases anymore.
|
||||
if req.start.lane() == req.end.lane() {
|
||||
// Weird case, but it can happen for walking from a building path to a bus stop that're
|
||||
// actually at the same spot.
|
||||
if req.start.dist_along() == req.end.dist_along() {
|
||||
return Some(Path::new(
|
||||
map,
|
||||
vec![PathStep::Lane(req.start.lane())],
|
||||
req.start.dist_along(),
|
||||
));
|
||||
} else if req.start.dist_along() < req.end.dist_along() {
|
||||
return Some(Path::new(
|
||||
map,
|
||||
vec![PathStep::Lane(req.start.lane())],
|
||||
req.end.dist_along(),
|
||||
));
|
||||
} else {
|
||||
return Some(Path::new(
|
||||
map,
|
||||
vec![PathStep::ContraflowLane(req.start.lane())],
|
||||
req.end.dist_along(),
|
||||
));
|
||||
}
|
||||
return Some(one_step_walking_path(req, map));
|
||||
}
|
||||
|
||||
let mut calc = self
|
||||
@ -95,62 +80,11 @@ impl SidewalkPathfinder {
|
||||
.borrow_mut();
|
||||
let raw_path = calc.calc_path(
|
||||
&self.graph,
|
||||
self.nodes.get(closest_node(req.start, map)),
|
||||
self.nodes.get(closest_node(req.end, map)),
|
||||
self.nodes.get(WalkingNode::closest(req.start, map)),
|
||||
self.nodes.get(WalkingNode::closest(req.end, map)),
|
||||
)?;
|
||||
let path = self.nodes.translate(&raw_path);
|
||||
|
||||
let mut steps: Vec<PathStep> = Vec::new();
|
||||
|
||||
for pair in path.windows(2) {
|
||||
let (l1, l1_endpt) = match pair[0] {
|
||||
Node::SidewalkEndpoint(l, endpt) => (l, endpt),
|
||||
Node::RideBus(_) => unreachable!(),
|
||||
};
|
||||
let l2 = match pair[1] {
|
||||
Node::SidewalkEndpoint(l, _) => l,
|
||||
Node::RideBus(_) => unreachable!(),
|
||||
};
|
||||
|
||||
if l1 == l2 {
|
||||
if l1_endpt {
|
||||
steps.push(PathStep::ContraflowLane(l1));
|
||||
} else {
|
||||
steps.push(PathStep::Lane(l1));
|
||||
}
|
||||
} else {
|
||||
let i = {
|
||||
let l = map.get_l(l1);
|
||||
if l1_endpt {
|
||||
l.dst_i
|
||||
} else {
|
||||
l.src_i
|
||||
}
|
||||
};
|
||||
// Could assert the intersection matches (l2, l2_endpt).
|
||||
let turn = map.get_turn_between(l1, l2, i).unwrap();
|
||||
steps.push(PathStep::Turn(turn));
|
||||
}
|
||||
}
|
||||
|
||||
// Don't start or end a path in a turn; sim layer breaks.
|
||||
if let PathStep::Turn(t) = steps[0] {
|
||||
let lane = map.get_l(t.src);
|
||||
if lane.src_i == t.parent {
|
||||
steps.insert(0, PathStep::ContraflowLane(lane.id));
|
||||
} else {
|
||||
steps.insert(0, PathStep::Lane(lane.id));
|
||||
}
|
||||
}
|
||||
if let PathStep::Turn(t) = steps.last().unwrap() {
|
||||
let lane = map.get_l(t.dst);
|
||||
if lane.src_i == t.parent {
|
||||
steps.push(PathStep::Lane(lane.id));
|
||||
} else {
|
||||
steps.push(PathStep::ContraflowLane(lane.id));
|
||||
}
|
||||
}
|
||||
|
||||
let steps = walking_path_to_steps(path, map);
|
||||
Some(Path::new(map, steps, req.end.dist_along()))
|
||||
}
|
||||
|
||||
@ -163,14 +97,14 @@ impl SidewalkPathfinder {
|
||||
) -> Option<(BusStopID, BusStopID, BusRouteID)> {
|
||||
let raw_path = fast_paths::calc_path(
|
||||
&self.graph,
|
||||
self.nodes.get(closest_node(start, map)),
|
||||
self.nodes.get(closest_node(end, map)),
|
||||
self.nodes.get(WalkingNode::closest(start, map)),
|
||||
self.nodes.get(WalkingNode::closest(end, map)),
|
||||
)?;
|
||||
|
||||
let mut nodes = self.nodes.translate(&raw_path);
|
||||
let mut first_stop = None;
|
||||
for n in &nodes {
|
||||
if let Node::RideBus(stop) = n {
|
||||
if let WalkingNode::RideBus(stop) = n {
|
||||
first_stop = Some(*stop);
|
||||
break;
|
||||
}
|
||||
@ -180,7 +114,7 @@ impl SidewalkPathfinder {
|
||||
|
||||
nodes.reverse();
|
||||
for n in nodes {
|
||||
if let Node::RideBus(stop2) = n {
|
||||
if let WalkingNode::RideBus(stop2) = n {
|
||||
if let Some(route) = possible_routes.iter().find(|r| r.stops.contains(&stop2)) {
|
||||
assert_ne!(first_stop, stop2);
|
||||
return Some((first_stop, stop2, route.id));
|
||||
@ -191,14 +125,9 @@ impl SidewalkPathfinder {
|
||||
}
|
||||
}
|
||||
|
||||
fn closest_node(pos: Position, map: &Map) -> Node {
|
||||
let dst_i = map.get_l(pos.lane()).length() - pos.dist_along() <= pos.dist_along();
|
||||
Node::SidewalkEndpoint(pos.lane(), dst_i)
|
||||
}
|
||||
|
||||
fn make_input_graph(
|
||||
map: &Map,
|
||||
nodes: &NodeMap<Node>,
|
||||
nodes: &NodeMap<WalkingNode>,
|
||||
use_transit: bool,
|
||||
bus_graph: &VehiclePathfinder,
|
||||
) -> InputGraph {
|
||||
@ -206,9 +135,9 @@ fn make_input_graph(
|
||||
|
||||
for l in map.all_lanes() {
|
||||
if l.is_sidewalk() && !map.get_r(l.parent).is_private() {
|
||||
let cost = to_s(l.length());
|
||||
let n1 = nodes.get(Node::SidewalkEndpoint(l.id, true));
|
||||
let n2 = nodes.get(Node::SidewalkEndpoint(l.id, false));
|
||||
let cost = walking_cost(l.length());
|
||||
let n1 = nodes.get(WalkingNode::SidewalkEndpoint(l.id, true));
|
||||
let n2 = nodes.get(WalkingNode::SidewalkEndpoint(l.id, false));
|
||||
input_graph.add_edge(n1, n2, cost);
|
||||
input_graph.add_edge(n2, n1, cost);
|
||||
}
|
||||
@ -216,27 +145,33 @@ fn make_input_graph(
|
||||
|
||||
for t in map.all_turns().values() {
|
||||
if t.between_sidewalks() {
|
||||
let from = Node::SidewalkEndpoint(t.id.src, map.get_l(t.id.src).dst_i == t.id.parent);
|
||||
let to = Node::SidewalkEndpoint(t.id.dst, map.get_l(t.id.dst).dst_i == t.id.parent);
|
||||
input_graph.add_edge(nodes.get(from), nodes.get(to), to_s(t.geom.length()));
|
||||
let from =
|
||||
WalkingNode::SidewalkEndpoint(t.id.src, map.get_l(t.id.src).dst_i == t.id.parent);
|
||||
let to =
|
||||
WalkingNode::SidewalkEndpoint(t.id.dst, map.get_l(t.id.dst).dst_i == t.id.parent);
|
||||
input_graph.add_edge(
|
||||
nodes.get(from),
|
||||
nodes.get(to),
|
||||
walking_cost(t.geom.length()),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if use_transit {
|
||||
// Connect bus stops with both sidewalk endpoints, using the appropriate distance.
|
||||
for stop in map.all_bus_stops().values() {
|
||||
let ride_bus = nodes.get(Node::RideBus(stop.id));
|
||||
let ride_bus = nodes.get(WalkingNode::RideBus(stop.id));
|
||||
let lane = map.get_l(stop.sidewalk_pos.lane());
|
||||
for endpt in &[true, false] {
|
||||
let cost = if *endpt {
|
||||
to_s(lane.length() - stop.sidewalk_pos.dist_along())
|
||||
walking_cost(lane.length() - stop.sidewalk_pos.dist_along())
|
||||
} else {
|
||||
to_s(stop.sidewalk_pos.dist_along())
|
||||
walking_cost(stop.sidewalk_pos.dist_along())
|
||||
};
|
||||
// Add some extra penalty (equivalent to 1m) to using a bus stop. Otherwise a path
|
||||
// might try to pass through it uselessly.
|
||||
let penalty = 100;
|
||||
let sidewalk = nodes.get(Node::SidewalkEndpoint(lane.id, *endpt));
|
||||
let sidewalk = nodes.get(WalkingNode::SidewalkEndpoint(lane.id, *endpt));
|
||||
input_graph.add_edge(sidewalk, ride_bus, cost + penalty);
|
||||
input_graph.add_edge(ride_bus, sidewalk, cost + penalty);
|
||||
}
|
||||
@ -264,8 +199,8 @@ fn make_input_graph(
|
||||
map,
|
||||
) {
|
||||
input_graph.add_edge(
|
||||
nodes.get(Node::RideBus(*stop1)),
|
||||
nodes.get(Node::RideBus(*stop2)),
|
||||
nodes.get(WalkingNode::RideBus(*stop1)),
|
||||
nodes.get(WalkingNode::RideBus(*stop2)),
|
||||
driving_cost,
|
||||
);
|
||||
} else {
|
||||
@ -282,8 +217,87 @@ fn make_input_graph(
|
||||
input_graph
|
||||
}
|
||||
|
||||
fn to_s(dist: Distance) -> usize {
|
||||
pub fn walking_cost(dist: Distance) -> usize {
|
||||
let walking_speed = Speed::meters_per_second(1.34);
|
||||
let time = dist / walking_speed;
|
||||
(time.inner_seconds().round() as usize).max(1)
|
||||
}
|
||||
|
||||
pub fn walking_path_to_steps(path: Vec<WalkingNode>, map: &Map) -> Vec<PathStep> {
|
||||
let mut steps: Vec<PathStep> = Vec::new();
|
||||
|
||||
for pair in path.windows(2) {
|
||||
let (l1, l1_endpt) = match pair[0] {
|
||||
WalkingNode::SidewalkEndpoint(l, endpt) => (l, endpt),
|
||||
WalkingNode::RideBus(_) => unreachable!(),
|
||||
};
|
||||
let l2 = match pair[1] {
|
||||
WalkingNode::SidewalkEndpoint(l, _) => l,
|
||||
WalkingNode::RideBus(_) => unreachable!(),
|
||||
};
|
||||
|
||||
if l1 == l2 {
|
||||
if l1_endpt {
|
||||
steps.push(PathStep::ContraflowLane(l1));
|
||||
} else {
|
||||
steps.push(PathStep::Lane(l1));
|
||||
}
|
||||
} else {
|
||||
let i = {
|
||||
let l = map.get_l(l1);
|
||||
if l1_endpt {
|
||||
l.dst_i
|
||||
} else {
|
||||
l.src_i
|
||||
}
|
||||
};
|
||||
// Could assert the intersection matches (l2, l2_endpt).
|
||||
let turn = map.get_turn_between(l1, l2, i).unwrap();
|
||||
steps.push(PathStep::Turn(turn));
|
||||
}
|
||||
}
|
||||
|
||||
// Don't start or end a path in a turn; sim layer breaks.
|
||||
if let PathStep::Turn(t) = steps[0] {
|
||||
let lane = map.get_l(t.src);
|
||||
if lane.src_i == t.parent {
|
||||
steps.insert(0, PathStep::ContraflowLane(lane.id));
|
||||
} else {
|
||||
steps.insert(0, PathStep::Lane(lane.id));
|
||||
}
|
||||
}
|
||||
if let PathStep::Turn(t) = steps.last().unwrap() {
|
||||
let lane = map.get_l(t.dst);
|
||||
if lane.src_i == t.parent {
|
||||
steps.push(PathStep::Lane(lane.id));
|
||||
} else {
|
||||
steps.push(PathStep::ContraflowLane(lane.id));
|
||||
}
|
||||
}
|
||||
|
||||
steps
|
||||
}
|
||||
|
||||
pub fn one_step_walking_path(req: &PathRequest, map: &Map) -> Path {
|
||||
// Weird case, but it can happen for walking from a building path to a bus stop that're
|
||||
// actually at the same spot.
|
||||
if req.start.dist_along() == req.end.dist_along() {
|
||||
Path::new(
|
||||
map,
|
||||
vec![PathStep::Lane(req.start.lane())],
|
||||
req.start.dist_along(),
|
||||
)
|
||||
} else if req.start.dist_along() < req.end.dist_along() {
|
||||
Path::new(
|
||||
map,
|
||||
vec![PathStep::Lane(req.start.lane())],
|
||||
req.end.dist_along(),
|
||||
)
|
||||
} else {
|
||||
Path::new(
|
||||
map,
|
||||
vec![PathStep::ContraflowLane(req.start.lane())],
|
||||
req.end.dist_along(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,9 @@
|
||||
use crate::pathfind::cost;
|
||||
use crate::{IntersectionID, LaneID, Map, Path, PathRequest, PathStep, RoadID, TurnID};
|
||||
use crate::pathfind::{
|
||||
cost, one_step_walking_path, walking_cost, walking_path_to_steps, WalkingNode,
|
||||
};
|
||||
use crate::{
|
||||
IntersectionID, LaneID, Map, Path, PathConstraints, PathRequest, PathStep, RoadID, TurnID,
|
||||
};
|
||||
use petgraph::graphmap::DiGraphMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::BTreeSet;
|
||||
@ -25,7 +29,11 @@ pub struct Zone {
|
||||
impl Zone {
|
||||
// Run slower Dijkstra's within the interior of a private zone. Don't go outside the borders.
|
||||
pub(crate) fn pathfind(&self, req: PathRequest, map: &Map) -> Option<Path> {
|
||||
// Edge type is the Turn, but we don't need it
|
||||
// TODO Not happy this works so differently
|
||||
if req.constraints == PathConstraints::Pedestrian {
|
||||
return self.pathfind_walking(req, map);
|
||||
}
|
||||
|
||||
let mut graph: DiGraphMap<LaneID, TurnID> = DiGraphMap::new();
|
||||
for r in &self.members {
|
||||
for l in map.get_r(*r).all_lanes() {
|
||||
@ -60,4 +68,51 @@ impl Zone {
|
||||
assert_eq!(steps[0], PathStep::Lane(req.start.lane()));
|
||||
Some(Path::new(map, steps, req.end.dist_along()))
|
||||
}
|
||||
|
||||
fn pathfind_walking(&self, req: PathRequest, map: &Map) -> Option<Path> {
|
||||
if req.start.lane() == req.end.lane() {
|
||||
return Some(one_step_walking_path(&req, map));
|
||||
}
|
||||
|
||||
let mut graph: DiGraphMap<WalkingNode, usize> = DiGraphMap::new();
|
||||
for r in &self.members {
|
||||
for l in map.get_r(*r).all_lanes() {
|
||||
let l = map.get_l(l);
|
||||
if l.is_sidewalk() {
|
||||
let cost = walking_cost(l.length());
|
||||
let n1 = WalkingNode::SidewalkEndpoint(l.id, true);
|
||||
let n2 = WalkingNode::SidewalkEndpoint(l.id, false);
|
||||
graph.add_edge(n1, n2, cost);
|
||||
graph.add_edge(n2, n1, cost);
|
||||
|
||||
for turn in map.get_turns_for(l.id, PathConstraints::Pedestrian) {
|
||||
if self.members.contains(&map.get_l(turn.id.dst).parent) {
|
||||
graph.add_edge(
|
||||
WalkingNode::SidewalkEndpoint(l.id, l.dst_i == turn.id.parent),
|
||||
WalkingNode::SidewalkEndpoint(
|
||||
turn.id.dst,
|
||||
map.get_l(turn.id.dst).dst_i == turn.id.parent,
|
||||
),
|
||||
walking_cost(turn.geom.length()),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let closest_start = WalkingNode::closest(req.start, map);
|
||||
let closest_end = WalkingNode::closest(req.end, map);
|
||||
let (_, path) = petgraph::algo::astar(
|
||||
&graph,
|
||||
closest_start,
|
||||
|end| end == closest_end,
|
||||
|(_, _, cost)| *cost,
|
||||
|_| 0,
|
||||
)?;
|
||||
let steps = walking_path_to_steps(path, map);
|
||||
assert_eq!(steps[0].as_lane(), req.start.lane());
|
||||
assert_eq!(steps.last().unwrap().as_lane(), req.end.lane());
|
||||
Some(Path::new(map, steps, req.end.dist_along()))
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user