interpret some OSM turn restrictions as meaning no lane-changing on approach to turn lanes. deeply sanity-checked for monlake only, seems reasonable from spot checks elsewhere

closes #261 -- not sure it's worth actually handling changes:lanes yet,
but this fixes the original issue
This commit is contained in:
Dustin Carlino 2020-08-06 15:57:36 -07:00
parent d2d9b7b092
commit 5998afaea4
6 changed files with 149 additions and 96 deletions

View File

@ -19,11 +19,11 @@ data/input/raw_maps/montlake.bin,11e3f20b67fb76359dc69737a039e8d1,https://www.dr
data/input/raw_maps/south_seattle.bin,12b7dddeac86abcb738a13cda8252be5,https://www.dropbox.com/s/vciigy9gbcrclsq/south_seattle.bin.zip?dl=0
data/input/raw_maps/udistrict.bin,a84071c4832e64a0d756290175a19423,https://www.dropbox.com/s/qsh2ktay3sf4oi1/udistrict.bin.zip?dl=0
data/input/raw_maps/west_seattle.bin,f10e59d67ed701edbaa36cf0a55d93a7,https://www.dropbox.com/s/q042wvb54xzbtgl/west_seattle.bin.zip?dl=0
data/input/screenshots/downtown.zip,95690fe5a765270e2457809b1f8fbee1,https://www.dropbox.com/s/qawd35wz62m2acl/downtown.zip.zip?dl=0
data/input/screenshots/krakow_center.zip,f2306793db2c360d3731caaadd565ba3,https://www.dropbox.com/s/azea6v6mnxbe0vc/krakow_center.zip.zip?dl=0
data/input/screenshots/lakeslice.zip,23c45e633b0c278c205f26b7c61a4d76,https://www.dropbox.com/s/z0z96lsn7bunqfy/lakeslice.zip.zip?dl=0
data/input/screenshots/montlake.zip,0fb325e75b6f40f0b38a73ce1c309888,https://www.dropbox.com/s/eblgq5zj3gflhwx/montlake.zip.zip?dl=0
data/input/screenshots/udistrict.zip,a78bf24fa03721c349787e21218ab277,https://www.dropbox.com/s/ecnt1tyza48y9o2/udistrict.zip.zip?dl=0
data/input/screenshots/downtown.zip,9a44c845024179d6876217f0b0f43338,https://www.dropbox.com/s/qawd35wz62m2acl/downtown.zip.zip?dl=0
data/input/screenshots/krakow_center.zip,44b8e8301931e97fb87f928676723a6d,https://www.dropbox.com/s/azea6v6mnxbe0vc/krakow_center.zip.zip?dl=0
data/input/screenshots/lakeslice.zip,3a1e28ad0a6a7833ee76853f908075e9,https://www.dropbox.com/s/z0z96lsn7bunqfy/lakeslice.zip.zip?dl=0
data/input/screenshots/montlake.zip,2b7b40e003ed4bdfad43405ce6f28e7b,https://www.dropbox.com/s/eblgq5zj3gflhwx/montlake.zip.zip?dl=0
data/input/screenshots/udistrict.zip,5b4014eb2749eac4b9aecfb02fd0efcf,https://www.dropbox.com/s/ecnt1tyza48y9o2/udistrict.zip.zip?dl=0
data/input/seattle/N47W122.hgt,0db4e23e51f7680538b0bbbc72208e07,https://www.dropbox.com/s/mmb4mgutwotijdw/N47W122.hgt.zip?dl=0
data/input/seattle/blockface.bin,add872bab9040ae911366328a230f8b5,https://www.dropbox.com/s/rxd2care60tbe75/blockface.bin.zip?dl=0
data/input/seattle/blockface.kml,350bd9e59bf2af4e885a7c0741e6ee6b,https://www.dropbox.com/s/ukknmpjdvilncq9/blockface.kml.zip?dl=0
@ -57,24 +57,24 @@ data/input/seattle/popdat.bin,b7e57a345ca118f305fc5a942264e65e,https://www.dropb
data/input/seattle/service_roads.bin,0d82356f76e87c2ec31528ded8265d7c,https://www.dropbox.com/s/ghpf2ngndddn7c7/service_roads.bin.zip?dl=0
data/input/seattle/trips_2014.csv,d4a8e733045b28c0385fb81359d6df03,https://www.dropbox.com/s/5ppravwmk6bf20d/trips_2014.csv.zip?dl=0
data/system/cities/seattle.bin,ed96e1b845a551d2335dabece281bc90,https://www.dropbox.com/s/b9c5v7jcxe3pu5t/seattle.bin.zip?dl=0
data/system/maps/ballard.bin,e55a8f60430d8816287f35204f60e0ac,https://www.dropbox.com/s/4fec03huqlc1j9g/ballard.bin.zip?dl=0
data/system/maps/berlin_center.bin,7d976361beefca6a3146d468fe83526f,https://www.dropbox.com/s/99014il31nv8ucy/berlin_center.bin.zip?dl=0
data/system/maps/downtown.bin,6dfe315077ea53dd195696089f2abf09,https://www.dropbox.com/s/0yvz7r3pj2w9han/downtown.bin.zip?dl=0
data/system/maps/huge_seattle.bin,faea69695b10bfa1c418d3de10141bac,https://www.dropbox.com/s/knghqh3uaodvopk/huge_seattle.bin.zip?dl=0
data/system/maps/krakow_center.bin,a47cafba7b1dc6b074c44c17405168a1,https://www.dropbox.com/s/vvezi9j9whld9yn/krakow_center.bin.zip?dl=0
data/system/maps/lakeslice.bin,3aaaa56f68e2aa54e2d0585cccbac9b0,https://www.dropbox.com/s/b5ewbosjog6ev1j/lakeslice.bin.zip?dl=0
data/system/maps/montlake.bin,eff3b42a67181b39b069250286910876,https://www.dropbox.com/s/3mqy6hwrqdl0xdk/montlake.bin.zip?dl=0
data/system/maps/south_seattle.bin,f83152bc19f762ecf16ab28725c7d14a,https://www.dropbox.com/s/xtcjmxgjv2m366d/south_seattle.bin.zip?dl=0
data/system/maps/udistrict.bin,1d35a34f557f5d2d2d9e0076ede5bbc8,https://www.dropbox.com/s/n79slbxskwvagsn/udistrict.bin.zip?dl=0
data/system/maps/west_seattle.bin,cff22206593aa5c2b2816684a3b90554,https://www.dropbox.com/s/ltea5gr7ua6v4h3/west_seattle.bin.zip?dl=0
data/system/prebaked_results/lakeslice/weekday.bin,d1d2a766b1c8728ef57bbd67f9b4c9de,https://www.dropbox.com/s/6pfse99ox19k8dj/weekday.bin.zip?dl=0
data/system/maps/ballard.bin,9f7cc88172c3fbe1af5f23d764a3be4b,https://www.dropbox.com/s/9ria0hl1bdahiwp/ballard.bin.zip?dl=0
data/system/maps/berlin_center.bin,065b4979987b228e19fe63adb5ee3856,https://www.dropbox.com/s/kivu0qhpdl2dfo6/berlin_center.bin.zip?dl=0
data/system/maps/downtown.bin,10040ca42f209cd3cbfdc21e18fe5d85,https://www.dropbox.com/s/4fipln0wkb9rrke/downtown.bin.zip?dl=0
data/system/maps/huge_seattle.bin,1453a6a8303c941cfed4ac443dc88d2b,https://www.dropbox.com/s/rek64eokl98wi1e/huge_seattle.bin.zip?dl=0
data/system/maps/krakow_center.bin,ae135fdfc30743833c71d9c2b6ecf3e3,https://www.dropbox.com/s/5o2u3bt4d58qdi7/krakow_center.bin.zip?dl=0
data/system/maps/lakeslice.bin,43d8306e6334f206311d363fb0c9d3c9,https://www.dropbox.com/s/fpvbusqntoaz4qm/lakeslice.bin.zip?dl=0
data/system/maps/montlake.bin,75d2fa79912db2d287b7bb3e68904f1e,https://www.dropbox.com/s/sfrnr6gajffvaxa/montlake.bin.zip?dl=0
data/system/maps/south_seattle.bin,90305faa011ccb2210eb9127ea7b7546,https://www.dropbox.com/s/nn5b4vvm45bvih2/south_seattle.bin.zip?dl=0
data/system/maps/udistrict.bin,0e723897052015d5b96d0d198544062a,https://www.dropbox.com/s/yuikz2qaev72cwk/udistrict.bin.zip?dl=0
data/system/maps/west_seattle.bin,56bb5ab6154f9dff692c44e11f49cc7c,https://www.dropbox.com/s/4jorjserrwtj5w2/west_seattle.bin.zip?dl=0
data/system/prebaked_results/lakeslice/weekday.bin,9077472f746b87a51200490dde46bd74,https://www.dropbox.com/s/8jqn37tvo2rcute/weekday.bin.zip?dl=0
data/system/prebaked_results/montlake/car vs bike contention.bin,faa041af70a41bb8fcacd3dfd7a7d467,https://www.dropbox.com/s/jefg0ikjy9dsrdd/car%20vs%20bike%20contention.bin.zip?dl=0
data/system/prebaked_results/montlake/weekday.bin,b390e7edba392306fbbd0887899ecde4,https://www.dropbox.com/s/74irerxdoy25b3d/weekday.bin.zip?dl=0
data/system/scenarios/ballard/weekday.bin,1543d78d62c614057d553ce5442302de,https://www.dropbox.com/s/by0hoe0m1gx9gsv/weekday.bin.zip?dl=0
data/system/scenarios/downtown/weekday.bin,2d46781ba4a6109bd7a5e1919e8621fc,https://www.dropbox.com/s/fumnvbuznaowe3w/weekday.bin.zip?dl=0
data/system/scenarios/huge_seattle/weekday.bin,a0b16717e99eb95243029532ead4b028,https://www.dropbox.com/s/1mubef9nrihekm3/weekday.bin.zip?dl=0
data/system/scenarios/lakeslice/weekday.bin,c1235e261ccd97b63bd64711df59de98,https://www.dropbox.com/s/x2hqbb8dlv3gu0z/weekday.bin.zip?dl=0
data/system/scenarios/montlake/weekday.bin,d1d53aca909f71f421df649a75029720,https://www.dropbox.com/s/fn9o4bolxrhucvm/weekday.bin.zip?dl=0
data/system/scenarios/south_seattle/weekday.bin,3b0844b3b8749207d11306517a70ee18,https://www.dropbox.com/s/pdv5pmg15znsbvw/weekday.bin.zip?dl=0
data/system/scenarios/udistrict/weekday.bin,88122ff5562b6247ef6894f908e8e1b9,https://www.dropbox.com/s/s0nbs2ylbna5wjy/weekday.bin.zip?dl=0
data/system/scenarios/west_seattle/weekday.bin,bdaefd28b90c51e7732062850fd00ac7,https://www.dropbox.com/s/wt912vh38ths713/weekday.bin.zip?dl=0
data/system/prebaked_results/montlake/weekday.bin,3f177553c4e31d364f8b5713bff2c251,https://www.dropbox.com/s/j1zy9bdadzis62o/weekday.bin.zip?dl=0
data/system/scenarios/ballard/weekday.bin,3a244dd17be96ea961b75f0f97d7954d,https://www.dropbox.com/s/2nv0p6lqbqqfsuo/weekday.bin.zip?dl=0
data/system/scenarios/downtown/weekday.bin,97ee4cbe65a6334d582a7cc6efe19c8b,https://www.dropbox.com/s/t67pdiea3perzzb/weekday.bin.zip?dl=0
data/system/scenarios/huge_seattle/weekday.bin,116e1fb01835ec449a8226727dedde46,https://www.dropbox.com/s/1zsliubcgba433g/weekday.bin.zip?dl=0
data/system/scenarios/lakeslice/weekday.bin,d82f24639b8d85b94e8af85fe909cad5,https://www.dropbox.com/s/ql01vfg1i4uvesp/weekday.bin.zip?dl=0
data/system/scenarios/montlake/weekday.bin,834dee7fb416aa27ce57f4789af2ccb6,https://www.dropbox.com/s/qa67b1dq3sdlqa5/weekday.bin.zip?dl=0
data/system/scenarios/south_seattle/weekday.bin,515c708b3d42cee89cd70d5baf405808,https://www.dropbox.com/s/j9j2c7f6seyx9b9/weekday.bin.zip?dl=0
data/system/scenarios/udistrict/weekday.bin,166be61208e936e0b29f6286827fc80a,https://www.dropbox.com/s/f1ew96ay71tsb11/weekday.bin.zip?dl=0
data/system/scenarios/west_seattle/weekday.bin,2bebf5bec15d5618ba6660dcb82cf285,https://www.dropbox.com/s/01c40kube985uq0/weekday.bin.zip?dl=0

View File

@ -777,13 +777,9 @@ fn recalculate_turns(
return;
}
for t in crate::make::turns::make_all_turns(
map.config.driving_side,
i,
&map.roads,
&map.lanes,
timer,
) {
let turns = crate::make::turns::make_all_turns(map, map.get_i(id), timer);
let i = &mut map.intersections[id.0];
for t in turns {
effects.added_turns.insert(t.id);
i.turns.insert(t.id);
if let Some(_existing_t) = old_turns.iter().find(|turn| turn.id == t.id) {

View File

@ -217,10 +217,6 @@ impl Map {
i.id, i.orig_id, i.roads
);
}
continue;
}
if i.is_closed() {
continue;
}
if i.intersection_type == IntersectionType::TrafficSignal {
let mut ok = false;
@ -238,22 +234,27 @@ impl Map {
i.intersection_type = IntersectionType::StopSign;
}
}
}
let mut all_turns = Vec::new();
for i in &map.intersections {
if i.is_border() || i.is_closed() {
continue;
}
if i.incoming_lanes.is_empty() || i.outgoing_lanes.is_empty() {
timer.warn(format!("{} is orphaned!", i.orig_id));
continue;
}
for t in
turns::make_all_turns(map.config.driving_side, i, &map.roads, &map.lanes, timer)
{
assert!(!map.turns.contains_key(&t.id));
i.turns.insert(t.id);
if t.geom.length() < geom::EPSILON_DIST {
timer.warn(format!("{} is a very short turn", t.id));
}
map.turns.insert(t.id, t);
all_turns.extend(turns::make_all_turns(&map, i, timer));
}
for t in all_turns {
assert!(!map.turns.contains_key(&t.id));
map.intersections[t.id.parent.0].turns.insert(t.id);
if t.geom.length() < geom::EPSILON_DIST {
timer.warn(format!("{} is a very short turn", t.id));
}
map.turns.insert(t.id, t);
}
timer.start("find blackholes");

View File

@ -1,38 +1,28 @@
use crate::raw::{DrivingSide, RestrictionType};
use crate::{Intersection, Lane, LaneID, Road, RoadID, Turn, TurnID, TurnType};
use crate::raw::RestrictionType;
use crate::{Intersection, Lane, LaneID, Map, Turn, TurnID, TurnType};
use abstutil::Timer;
use geom::{Distance, PolyLine, Pt2D};
use nbez::{Bez3o, BezCurve, Point2d};
use std::collections::{BTreeSet, HashMap, HashSet};
pub fn make_all_turns(
driving_side: DrivingSide,
i: &Intersection,
roads: &Vec<Road>,
lanes: &Vec<Lane>,
timer: &mut Timer,
) -> Vec<Turn> {
pub fn make_all_turns(map: &Map, i: &Intersection, timer: &mut Timer) -> Vec<Turn> {
assert!(!i.is_border());
let mut raw_turns: Vec<Turn> = Vec::new();
raw_turns.extend(make_vehicle_turns(i, lanes, timer));
raw_turns.extend(make_vehicle_turns(i, map, timer));
raw_turns.extend(crate::make::walking_turns::make_walking_turns(
driving_side,
i,
roads,
lanes,
timer,
map, i, timer,
));
let unique_turns = ensure_unique(raw_turns);
let mut final_turns: Vec<Turn> = Vec::new();
let mut filtered_turns: HashMap<LaneID, Vec<Turn>> = HashMap::new();
for turn in unique_turns {
if !does_turn_pass_restrictions(&turn, &i.roads, roads, lanes) {
if !does_turn_pass_restrictions(&turn, i, map) {
continue;
}
if is_turn_allowed(&turn, roads, lanes) {
if is_turn_allowed(&turn, map) {
final_turns.push(turn);
} else {
filtered_turns
@ -46,28 +36,61 @@ pub fn make_all_turns(
// turn leading to it.
let mut incoming_missing: HashSet<LaneID> = HashSet::new();
for l in &i.incoming_lanes {
if lanes[l.0].lane_type.supports_any_movement() {
if map.get_l(*l).lane_type.supports_any_movement() {
incoming_missing.insert(*l);
}
}
for t in &final_turns {
incoming_missing.remove(&t.id.src);
}
// Turn restrictions are buggy. If they orphan a lane, restore the filtered turns.
for (l, turns) in filtered_turns {
// Do turn restrictions orphan a lane?
if incoming_missing.contains(&l) {
timer.warn(format!(
"Turn restrictions broke {} outbound, so restoring turns",
l
));
final_turns.extend(turns);
// Restrictions on turn lanes may sometimes actually be more like change:lanes
// (https://wiki.openstreetmap.org/wiki/Key:change). Try to interpret them that way
// here, choosing one turn from a bunch of options.
// If all the turns go to a single road, then ignore the turn type.
let dst_r = map.get_l(turns[0].id.dst).parent;
let single_group: Vec<Turn> =
if turns.iter().all(|t| map.get_l(t.id.dst).parent == dst_r) {
turns.clone()
} else {
// Fall back to preferring all the straight turns
turns
.iter()
.filter(|t| t.turn_type == TurnType::Straight)
.cloned()
.collect()
};
if !single_group.is_empty() {
// Just pick one, with the lowest lane-changing cost. Not using Turn's penalty()
// here, because
// 1) We haven't populated turns yet, so from_idx won't work
// 2) It counts from the right, but I think we actually want to count from the left
let best = single_group
.into_iter()
.min_by_key(|t| lc_penalty(t, map))
.unwrap();
final_turns.push(best);
timer.note(format!(
"Restricted lane-changing on approach to turn lanes at {}",
l
));
} else {
timer.warn(format!(
"Turn restrictions broke {} outbound, so restoring turns",
l
));
final_turns.extend(turns);
}
incoming_missing.remove(&l);
}
}
let mut outgoing_missing: HashSet<LaneID> = HashSet::new();
for l in &i.outgoing_lanes {
if lanes[l.0].lane_type.supports_any_movement() {
if map.get_l(*l).lane_type.supports_any_movement() {
outgoing_missing.insert(*l);
}
}
@ -102,32 +125,28 @@ fn ensure_unique(turns: Vec<Turn>) -> Vec<Turn> {
keep
}
fn is_turn_allowed(turn: &Turn, roads: &Vec<Road>, lanes: &Vec<Lane>) -> bool {
let l = &lanes[turn.id.src.0];
let r = &roads[l.parent.0];
if let Some(mut types) = l.get_turn_restrictions(r) {
fn is_turn_allowed(turn: &Turn, map: &Map) -> bool {
if let Some(mut types) = map
.get_l(turn.id.src)
.get_turn_restrictions(map.get_parent(turn.id.src))
{
types.any(|turn_type| turn_type == turn.turn_type)
} else {
true
}
}
fn does_turn_pass_restrictions(
turn: &Turn,
intersection_roads: &BTreeSet<RoadID>,
roads: &Vec<Road>,
lanes: &Vec<Lane>,
) -> bool {
fn does_turn_pass_restrictions(turn: &Turn, i: &Intersection, map: &Map) -> bool {
if turn.between_sidewalks() {
return true;
}
let src = lanes[turn.id.src.0].parent;
let dst = lanes[turn.id.dst.0].parent;
let src = map.get_parent(turn.id.src);
let dst = map.get_l(turn.id.dst).parent;
for (restriction, to) in &roads[src.0].turn_restrictions {
for (restriction, to) in &src.turn_restrictions {
// The restriction only applies to one direction of the road.
if !intersection_roads.contains(to) {
if !i.roads.contains(to) {
continue;
}
match restriction {
@ -147,18 +166,18 @@ fn does_turn_pass_restrictions(
true
}
fn make_vehicle_turns(i: &Intersection, lanes: &Vec<Lane>, timer: &mut Timer) -> Vec<Turn> {
fn make_vehicle_turns(i: &Intersection, map: &Map, timer: &mut Timer) -> Vec<Turn> {
let mut turns = Vec::new();
// Just generate every possible combination of turns between incoming and outgoing lanes.
let is_deadend = i.roads.len() == 1;
for src in &i.incoming_lanes {
let src = &lanes[src.0];
let src = map.get_l(*src);
if !src.lane_type.is_for_moving_vehicles() {
continue;
}
for dst in &i.outgoing_lanes {
let dst = &lanes[dst.0];
let dst = map.get_l(*dst);
if !dst.lane_type.is_for_moving_vehicles() {
continue;
}
@ -236,3 +255,40 @@ fn to_pt(pt: Pt2D) -> Point2d<f64> {
fn from_pt(pt: Point2d<f64>) -> Pt2D {
Pt2D::new(pt.x, pt.y)
}
fn lc_penalty(t: &Turn, map: &Map) -> isize {
let from = map.get_l(t.id.src);
let to = map.get_l(t.id.dst);
let from_idx = {
let mut cnt = 0;
let r = map.get_r(from.parent);
for (l, lt) in r.children(r.is_forwards(from.id)) {
if from.lane_type != *lt {
continue;
}
cnt += 1;
if from.id == *l {
break;
}
}
cnt
};
let to_idx = {
let mut cnt = 0;
let r = map.get_r(to.parent);
for (l, lt) in r.children(r.is_forwards(to.id)) {
if to.lane_type != *lt {
continue;
}
cnt += 1;
if to.id == *l {
break;
}
}
cnt
};
((from_idx as isize) - (to_idx as isize)).abs()
}

View File

@ -1,16 +1,16 @@
use crate::raw::DrivingSide;
use crate::{Intersection, IntersectionID, Lane, LaneID, LaneType, Road, Turn, TurnID, TurnType};
use crate::{
Intersection, IntersectionID, Lane, LaneID, LaneType, Map, Road, Turn, TurnID, TurnType,
};
use abstutil::{wraparound_get, Timer};
use geom::{Distance, Line, PolyLine, Pt2D, Ring};
use std::collections::BTreeSet;
pub fn make_walking_turns(
driving_side: DrivingSide,
i: &Intersection,
all_roads: &Vec<Road>,
lanes: &Vec<Lane>,
timer: &mut Timer,
) -> Vec<Turn> {
pub fn make_walking_turns(map: &Map, i: &Intersection, timer: &mut Timer) -> Vec<Turn> {
let driving_side = map.config.driving_side;
let all_roads = map.all_roads();
let lanes = map.all_lanes();
let roads: Vec<&Road> = i
.get_roads_sorted_by_incoming_angle(all_roads)
.into_iter()

View File

@ -157,7 +157,7 @@ impl Turn {
// TODO I thought about different cases where there are the same/more/less lanes going in
// and out, but then actually, I think the reasonable thing in all cases is just to do
// this. If that holds true, simplify travel_lane_offset; don't need the count.
// this.
let lc_cost = ((from_idx as isize) - (to_idx as isize)).abs() as usize;
// Always prefer a dedicated bike or bus lane. This takes care of entering one from a