mirror of
https://github.com/a-b-street/abstreet.git
synced 2025-01-04 20:44:52 +03:00
making bike lane turn handling use the common driving turn code too, with preferred lanes
This commit is contained in:
parent
6e786d5630
commit
023bb5cf5d
@ -24,7 +24,7 @@ impl fmt::Display for LaneID {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
|
||||
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub enum LaneType {
|
||||
Driving,
|
||||
Parking,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use abstutil::{wraparound_get, MultiMap};
|
||||
use abstutil::wraparound_get;
|
||||
use geom::{Angle, Line};
|
||||
use std::collections::HashSet;
|
||||
use std::collections::{BTreeSet, HashSet};
|
||||
use std::iter;
|
||||
use {
|
||||
Intersection, IntersectionID, Lane, LaneID, LaneType, Map, Road, RoadID, Turn, TurnID, TurnType,
|
||||
@ -8,8 +8,7 @@ use {
|
||||
|
||||
pub fn make_all_turns(i: &Intersection, map: &Map) -> Vec<Turn> {
|
||||
let mut turns: Vec<Turn> = Vec::new();
|
||||
turns.extend(make_driving_turns(i, map));
|
||||
turns.extend(make_biking_turns(i, map));
|
||||
turns.extend(make_vehicle_turns(i, map));
|
||||
turns.extend(make_walking_turns(i, map));
|
||||
let turns = dedupe(turns);
|
||||
|
||||
@ -56,51 +55,73 @@ fn dedupe(turns: Vec<Turn>) -> Vec<Turn> {
|
||||
keep
|
||||
}
|
||||
|
||||
fn make_driving_turns(i: &Intersection, map: &Map) -> Vec<Turn> {
|
||||
if i.is_dead_end() {
|
||||
return make_driving_turns_for_dead_end(i, map);
|
||||
}
|
||||
|
||||
fn make_vehicle_turns(i: &Intersection, map: &Map) -> Vec<Turn> {
|
||||
let roads: Vec<&Road> = i.roads.iter().map(|r| map.get_r(*r)).collect();
|
||||
let mut lane_types: BTreeSet<LaneType> = BTreeSet::new();
|
||||
for r in &roads {
|
||||
let (t1, t2) = r.get_lane_types();
|
||||
for lt in t1.into_iter().chain(t2.into_iter()) {
|
||||
lane_types.insert(lt);
|
||||
}
|
||||
}
|
||||
lane_types.remove(&LaneType::Parking);
|
||||
lane_types.remove(&LaneType::Sidewalk);
|
||||
|
||||
let mut result = Vec::new();
|
||||
|
||||
for r1 in &roads {
|
||||
let incoming = filter_driving_lanes(r1.incoming_lanes(i.id));
|
||||
if incoming.is_empty() {
|
||||
for lane_type in lane_types.into_iter() {
|
||||
if i.is_dead_end() {
|
||||
result.extend(make_vehicle_turns_for_dead_end(i, map, lane_type));
|
||||
continue;
|
||||
}
|
||||
|
||||
for r2 in &roads {
|
||||
if r1.id == r2.id {
|
||||
continue;
|
||||
}
|
||||
let outgoing = filter_driving_lanes(r2.outgoing_lanes(i.id));
|
||||
if outgoing.is_empty() {
|
||||
for r1 in &roads {
|
||||
// We can't filter incoming just on the preferred type, because we might be forced to
|
||||
// make a turn from a driving lane to a bike/bus lane.
|
||||
let incoming = filter_vehicle_lanes(r1.incoming_lanes(i.id), lane_type);
|
||||
if incoming.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Use an arbitrary lane from each road to get the angle between r1 and r2.
|
||||
let angle1 = map.get_l(incoming[0]).last_line().angle();
|
||||
let angle2 = map.get_l(outgoing[0]).first_line().angle();
|
||||
let diff = angle1
|
||||
.shortest_rotation_towards(angle2)
|
||||
.normalized_degrees();
|
||||
for r2 in &roads {
|
||||
if r1.id == r2.id {
|
||||
continue;
|
||||
}
|
||||
let outgoing = filter_vehicle_lanes(r2.outgoing_lanes(i.id), lane_type);
|
||||
if outgoing.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if diff < 10.0 || diff > 350.0 {
|
||||
// Straight. Match up based on the relative number of lanes.
|
||||
result.extend(match_up_driving_lanes(map, i.id, &incoming, &outgoing));
|
||||
} else if diff > 180.0 {
|
||||
// Clockwise rotation means a right turn?
|
||||
result.push(make_driving_turn(
|
||||
map,
|
||||
i.id,
|
||||
*incoming.last().unwrap(),
|
||||
*outgoing.last().unwrap(),
|
||||
));
|
||||
} else {
|
||||
// Counter-clockwise rotation means a left turn
|
||||
result.push(make_driving_turn(map, i.id, incoming[0], outgoing[0]));
|
||||
// If we fell back to driving lanes for both incoming and outgoing and it's not
|
||||
// time, then skip. This should prevent duplicates.
|
||||
if map.get_l(incoming[0]).lane_type != lane_type
|
||||
&& map.get_l(outgoing[0]).lane_type != lane_type
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Use an arbitrary lane from each road to get the angle between r1 and r2.
|
||||
let angle1 = map.get_l(incoming[0]).last_line().angle();
|
||||
let angle2 = map.get_l(outgoing[0]).first_line().angle();
|
||||
let diff = angle1
|
||||
.shortest_rotation_towards(angle2)
|
||||
.normalized_degrees();
|
||||
|
||||
if diff < 10.0 || diff > 350.0 {
|
||||
// Straight. Match up based on the relative number of lanes.
|
||||
result.extend(match_up_lanes(map, i.id, &incoming, &outgoing));
|
||||
} else if diff > 180.0 {
|
||||
// Clockwise rotation means a right turn?
|
||||
result.push(make_vehicle_turn(
|
||||
map,
|
||||
i.id,
|
||||
*incoming.last().unwrap(),
|
||||
*outgoing.last().unwrap(),
|
||||
));
|
||||
} else {
|
||||
// Counter-clockwise rotation means a left turn
|
||||
result.push(make_vehicle_turn(map, i.id, incoming[0], outgoing[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -108,7 +129,7 @@ fn make_driving_turns(i: &Intersection, map: &Map) -> Vec<Turn> {
|
||||
result
|
||||
}
|
||||
|
||||
fn match_up_driving_lanes(
|
||||
fn match_up_lanes(
|
||||
map: &Map,
|
||||
i: IntersectionID,
|
||||
incoming: &Vec<LaneID>,
|
||||
@ -123,7 +144,7 @@ fn match_up_driving_lanes(
|
||||
.collect();
|
||||
assert_eq!(padded_incoming.len(), outgoing.len());
|
||||
for (l1, l2) in padded_incoming.iter().zip(outgoing.iter()) {
|
||||
result.push(make_driving_turn(map, i, **l1, *l2));
|
||||
result.push(make_vehicle_turn(map, i, **l1, *l2));
|
||||
}
|
||||
} else if incoming.len() > outgoing.len() {
|
||||
// TODO For non-dead-ends: Ideally if the left/rightmost lanes are for turning, use the
|
||||
@ -135,140 +156,27 @@ fn match_up_driving_lanes(
|
||||
.collect();
|
||||
assert_eq!(padded_outgoing.len(), incoming.len());
|
||||
for (l1, l2) in incoming.iter().zip(&padded_outgoing) {
|
||||
result.push(make_driving_turn(map, i, *l1, **l2));
|
||||
result.push(make_vehicle_turn(map, i, *l1, **l2));
|
||||
}
|
||||
} else {
|
||||
// The easy case!
|
||||
for (l1, l2) in incoming.iter().zip(outgoing.iter()) {
|
||||
result.push(make_driving_turn(map, i, *l1, *l2));
|
||||
result.push(make_vehicle_turn(map, i, *l1, *l2));
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn make_driving_turns_for_dead_end(i: &Intersection, map: &Map) -> Vec<Turn> {
|
||||
fn make_vehicle_turns_for_dead_end(i: &Intersection, map: &Map, lane_type: LaneType) -> Vec<Turn> {
|
||||
let road = map.get_r(*i.roads.iter().next().unwrap());
|
||||
let incoming = filter_driving_lanes(road.incoming_lanes(i.id));
|
||||
let outgoing = filter_driving_lanes(road.outgoing_lanes(i.id));
|
||||
let incoming = filter_vehicle_lanes(road.incoming_lanes(i.id), lane_type);
|
||||
let outgoing = filter_vehicle_lanes(road.outgoing_lanes(i.id), lane_type);
|
||||
if incoming.is_empty() || outgoing.is_empty() {
|
||||
error!("{} needs to be a border node!", i.id);
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
match_up_driving_lanes(map, i.id, &incoming, &outgoing)
|
||||
}
|
||||
|
||||
fn make_biking_turns(i: &Intersection, m: &Map) -> Vec<Turn> {
|
||||
// TODO Road should make this easier, but how?
|
||||
let mut incoming_roads: HashSet<RoadID> = HashSet::new();
|
||||
let mut incoming_bike_lanes_per_road: MultiMap<RoadID, LaneID> = MultiMap::new();
|
||||
let mut incoming_driving_lanes_per_road: MultiMap<RoadID, LaneID> = MultiMap::new();
|
||||
for id in &i.incoming_lanes {
|
||||
let l = m.get_l(*id);
|
||||
incoming_roads.insert(l.parent);
|
||||
match l.lane_type {
|
||||
LaneType::Biking => incoming_bike_lanes_per_road.insert(l.parent, *id),
|
||||
LaneType::Driving => incoming_driving_lanes_per_road.insert(l.parent, *id),
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
let mut outgoing_roads: HashSet<RoadID> = HashSet::new();
|
||||
let mut outgoing_bike_lanes_per_road: MultiMap<RoadID, LaneID> = MultiMap::new();
|
||||
let mut outgoing_driving_lanes_per_road: MultiMap<RoadID, LaneID> = MultiMap::new();
|
||||
for id in &i.outgoing_lanes {
|
||||
let l = m.get_l(*id);
|
||||
outgoing_roads.insert(l.parent);
|
||||
match l.lane_type {
|
||||
LaneType::Biking => outgoing_bike_lanes_per_road.insert(l.parent, *id),
|
||||
LaneType::Driving => outgoing_driving_lanes_per_road.insert(l.parent, *id),
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
|
||||
let mut incoming: Vec<LaneID> = Vec::new();
|
||||
for incoming_road in &incoming_roads {
|
||||
// Prefer a bike lane if it's there, otherwise use all driving lanes
|
||||
let lanes = incoming_bike_lanes_per_road.get(*incoming_road);
|
||||
if !lanes.is_empty() {
|
||||
incoming.extend(lanes);
|
||||
} else {
|
||||
incoming.extend(incoming_driving_lanes_per_road.get(*incoming_road));
|
||||
}
|
||||
}
|
||||
|
||||
let mut outgoing: Vec<LaneID> = Vec::new();
|
||||
for outgoing_road in &outgoing_roads {
|
||||
let lanes = outgoing_bike_lanes_per_road.get(*outgoing_road);
|
||||
if !lanes.is_empty() {
|
||||
outgoing.extend(lanes);
|
||||
} else {
|
||||
outgoing.extend(outgoing_driving_lanes_per_road.get(*outgoing_road));
|
||||
}
|
||||
}
|
||||
|
||||
// Stay deterministic! Iteration earlier used HashSets.
|
||||
incoming.sort();
|
||||
outgoing.sort();
|
||||
|
||||
// Kind of a hack. We wind up making some driving->driving turns here, but make_driving_turns
|
||||
// will create those, and duplicates are bad. Filter them out here.
|
||||
make_turns(m, i.id, &incoming, &outgoing)
|
||||
.into_iter()
|
||||
.filter(|t| m.get_l(t.id.src).is_biking() || m.get_l(t.id.dst).is_biking())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn make_turns(
|
||||
map: &Map,
|
||||
parent: IntersectionID,
|
||||
incoming: &Vec<LaneID>,
|
||||
outgoing: &Vec<LaneID>,
|
||||
) -> Vec<Turn> {
|
||||
// TODO: Figure out why this happens in the huge map
|
||||
if incoming.is_empty() {
|
||||
if false {
|
||||
warn!("{} has no incoming lanes of some type", parent);
|
||||
}
|
||||
return Vec::new();
|
||||
}
|
||||
if outgoing.is_empty() {
|
||||
if false {
|
||||
warn!("{} has no outgoing lanes of some type", parent);
|
||||
}
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
// Sanity check...
|
||||
for l in incoming {
|
||||
assert_eq!(map.get_l(*l).dst_i, parent);
|
||||
}
|
||||
for l in outgoing {
|
||||
assert_eq!(map.get_l(*l).src_i, parent);
|
||||
}
|
||||
|
||||
let dead_end = map.get_i(parent).is_dead_end();
|
||||
|
||||
let mut result = Vec::new();
|
||||
for src in incoming {
|
||||
let src_l = map.get_l(*src);
|
||||
|
||||
for dst in outgoing {
|
||||
let dst_l = map.get_l(*dst);
|
||||
// Don't create U-turns unless it's a dead-end
|
||||
if src_l.parent == dst_l.parent && !dead_end {
|
||||
continue;
|
||||
}
|
||||
// TODO if it's a multi-lane dead-end, ideally match up lanes or something
|
||||
|
||||
result.push(Turn {
|
||||
id: turn_id(parent, src_l.id, dst_l.id),
|
||||
turn_type: TurnType::Other,
|
||||
line: Line::new(src_l.last_pt(), dst_l.first_pt()),
|
||||
});
|
||||
}
|
||||
}
|
||||
result
|
||||
match_up_lanes(map, i.id, &incoming, &outgoing)
|
||||
}
|
||||
|
||||
fn make_walking_turns(i: &Intersection, map: &Map) -> Vec<Turn> {
|
||||
@ -356,19 +264,22 @@ fn get_sidewalk<'a>(map: &'a Map, children: &Vec<(LaneID, LaneType)>) -> Option<
|
||||
None
|
||||
}
|
||||
|
||||
fn filter_driving_lanes(lanes: &Vec<(LaneID, LaneType)>) -> Vec<LaneID> {
|
||||
lanes
|
||||
.iter()
|
||||
.filter_map(|(id, lt)| {
|
||||
if *lt == LaneType::Driving {
|
||||
Some(*id)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}).collect()
|
||||
fn filter_vehicle_lanes(lanes: &Vec<(LaneID, LaneType)>, preferred: LaneType) -> Vec<LaneID> {
|
||||
let preferred = filter_lanes(lanes, preferred);
|
||||
if !preferred.is_empty() {
|
||||
return preferred;
|
||||
}
|
||||
filter_lanes(lanes, LaneType::Driving)
|
||||
}
|
||||
|
||||
fn make_driving_turn(map: &Map, i: IntersectionID, l1: LaneID, l2: LaneID) -> Turn {
|
||||
fn filter_lanes(lanes: &Vec<(LaneID, LaneType)>, filter: LaneType) -> Vec<LaneID> {
|
||||
lanes
|
||||
.iter()
|
||||
.filter_map(|(id, lt)| if *lt == filter { Some(*id) } else { None })
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn make_vehicle_turn(map: &Map, i: IntersectionID, l1: LaneID, l2: LaneID) -> Turn {
|
||||
Turn {
|
||||
id: turn_id(i, l1, l2),
|
||||
turn_type: TurnType::Other,
|
||||
|
Loading…
Reference in New Issue
Block a user