reverse lane direction as a new edit. might be a few determinism issues,

but seems to mostly work.
This commit is contained in:
Dustin Carlino 2019-10-07 18:04:31 -07:00
parent 98d7f26f2d
commit 258f3d4528
8 changed files with 156 additions and 31 deletions

View File

@ -36,7 +36,8 @@ Watching overall traffic patterns and zooming into a few slow areas:
- Intersections governed by stop signs and traffic signals, with default
signal timings heuristically inferred. Hand-tuned geometry to reasonably
model Seattle's strangest intersections.
- You can adjust lane types, stop signs, and traffic signals.
- You can adjust lane types, stop signs, and traffic signals, and reverse
lanes.
- The traffic
- Individual cars, buses, bikes, and pedestrians move through the map.
- Most trips are multi-modal -- for example, a pedestrian exits a building,

View File

@ -302,10 +302,10 @@ A key feature of A/B Street is the player editing the map and seeing how traffic
responds. The possible edits include:
- Change lane types (driving, bus, bike, parking -- sidewalks are fixed)
- Reverse a lane
- Change a stop sign policy (which roads have a stop sign and which have
priority)
- Change a traffic signal policy
- Planned eventually: Convert between two- and one-way roads
The map conversion process outlined above takes a few minutes, so reusing this
process directly to compute a map with edits wouldn't work at all for real

View File

@ -6,7 +6,7 @@ use crate::debug::DebugMode;
use crate::game::{State, Transition, WizardState};
use crate::helpers::{ColorScheme, ID};
use crate::render::{
DrawCtx, DrawIntersection, DrawLane, DrawMap, DrawOptions, DrawTurn, Renderable,
DrawCtx, DrawIntersection, DrawLane, DrawMap, DrawOptions, DrawRoad, DrawTurn, Renderable,
MIN_ZOOM_FOR_DETAIL,
};
use crate::sandbox::SandboxMode;
@ -145,6 +145,18 @@ impl State for EditMode {
}
}
}
{
let lane = ui.primary.map.get_l(id);
let road = ui.primary.map.get_r(lane.parent);
// TODO More validity checks
if lane.lane_type.is_for_moving_vehicles() && road.dir_and_offset(id).1 == 0 {
if ctx.input.contextual_action(Key::F, "swap lane direction") {
let mut new_edits = orig_edits.clone();
new_edits.contraflow_lanes.insert(lane.id, lane.src_i);
apply_map_edits(&mut ui.primary, &ui.cs, ctx, new_edits);
}
}
}
if ctx
.input
@ -156,6 +168,7 @@ impl State for EditMode {
{
let mut new_edits = orig_edits.clone();
new_edits.lane_overrides.remove(&id);
new_edits.contraflow_lanes.remove(&id);
apply_map_edits(&mut ui.primary, &ui.cs, ctx, new_edits);
}
}
@ -225,7 +238,11 @@ impl State for EditMode {
// supply a set of things to highlight and have something else take care of drawing
// with detail or not.
if g.canvas.cam_zoom >= MIN_ZOOM_FOR_DETAIL {
for l in edits.lane_overrides.keys() {
for l in edits
.lane_overrides
.keys()
.chain(edits.contraflow_lanes.keys())
{
opts.override_colors.insert(ID::Lane(*l), Color::Hatching);
ctx.draw_map.get_l(*l).draw(g, &opts, &ctx);
}
@ -420,7 +437,8 @@ pub fn apply_map_edits(
) {
let mut timer = Timer::new("apply map edits");
let (lanes_changed, turns_deleted, turns_added) = bundle.map.apply_edits(edits, &mut timer);
let (lanes_changed, roads_changed, turns_deleted, turns_added) =
bundle.map.apply_edits(edits, &mut timer);
for l in lanes_changed {
bundle.draw_map.lanes[l.0] = DrawLane::new(
@ -432,6 +450,11 @@ pub fn apply_map_edits(
)
.finish(ctx.prerender);
}
for r in roads_changed {
bundle.draw_map.roads[r.0] =
DrawRoad::new(bundle.map.get_r(r), &bundle.map, cs, ctx.prerender);
}
let mut modified_intersections: BTreeSet<IntersectionID> = BTreeSet::new();
let mut lanes_of_modified_turns: BTreeSet<LaneID> = BTreeSet::new();
for t in turns_deleted {

View File

@ -61,7 +61,7 @@ impl DrawMap {
timer.start_iter("make DrawRoads", map.all_roads().len());
for r in map.all_roads() {
timer.next();
let draw_r = DrawRoad::new(r, cs, ctx.prerender);
let draw_r = DrawRoad::new(r, map, cs, ctx.prerender);
all_roads.push(
osm_rank_to_color(cs, r.get_rank()),
r.get_thick_polygon().get(timer),

View File

@ -2,7 +2,7 @@ use crate::helpers::{ColorScheme, ID};
use crate::render::{dashed_lines, DrawCtx, DrawOptions, Renderable, OUTLINE_THICKNESS};
use ezgui::{Color, Drawable, GeomBatch, GfxCtx, Line, Prerender, Text};
use geom::{Distance, Polygon, Pt2D};
use map_model::{Map, Road, RoadID};
use map_model::{Map, Road, RoadID, LANE_THICKNESS};
pub struct DrawRoad {
pub id: RoadID,
@ -14,11 +14,21 @@ pub struct DrawRoad {
}
impl DrawRoad {
pub fn new(r: &Road, cs: &ColorScheme, prerender: &Prerender) -> DrawRoad {
pub fn new(r: &Road, map: &Map, cs: &ColorScheme, prerender: &Prerender) -> DrawRoad {
let mut draw = GeomBatch::new();
// The road's original center_pts don't account for contraflow lane edits.
let center = map
.get_l(if !r.children_forwards.is_empty() {
r.children_forwards[0].0
} else {
r.children_backwards[0].0
})
.lane_center_pts
.shift_left(LANE_THICKNESS / 2.0)
.unwrap();
draw.extend(
cs.get_def("road center line", Color::YELLOW),
dashed_lines(&r.center_pts, Distance::meters(2.0), Distance::meters(1.0)),
dashed_lines(&center, Distance::meters(2.0), Distance::meters(1.0)),
);
let mut label = Text::new();

View File

@ -8,6 +8,9 @@ pub struct MapEdits {
pub(crate) map_name: String,
pub edits_name: String,
pub lane_overrides: BTreeMap<LaneID, LaneType>,
// TODO Order matters if validity checks happen...
// The IntersectionID says where this lane now points. Used to detect reverts.
pub contraflow_lanes: BTreeMap<LaneID, IntersectionID>,
// TODO Storing the entire thing is maybe a bit dramatic, but works for now.
pub stop_sign_overrides: BTreeMap<IntersectionID, ControlStopSign>,
pub traffic_signal_overrides: BTreeMap<IntersectionID, ControlTrafficSignal>,
@ -20,6 +23,7 @@ impl MapEdits {
// Something has to fill this out later
edits_name: "no_edits".to_string(),
lane_overrides: BTreeMap::new(),
contraflow_lanes: BTreeMap::new(),
stop_sign_overrides: BTreeMap::new(),
traffic_signal_overrides: BTreeMap::new(),
}

View File

@ -1,4 +1,3 @@
use crate::make::get_lane_types;
use crate::pathfind::Pathfinder;
use crate::raw::{MapFixes, RawMap, StableIntersectionID, StableRoadID};
use crate::{
@ -610,14 +609,21 @@ impl Map {
&self.edits
}
// new_edits assumed to be valid. Returns actual lanes that changed, turns deleted, turns added. Doesn't update pathfinding yet.
// new_edits assumed to be valid. Returns actual lanes that changed, roads changed, turns
// deleted, turns added. Doesn't update pathfinding yet.
pub fn apply_edits(
&mut self,
new_edits: MapEdits,
timer: &mut Timer,
) -> (BTreeSet<LaneID>, BTreeSet<TurnID>, BTreeSet<TurnID>) {
) -> (
BTreeSet<LaneID>,
BTreeSet<RoadID>,
BTreeSet<TurnID>,
BTreeSet<TurnID>,
) {
// Ignore if there's no change from current
let mut all_lane_edits: BTreeMap<LaneID, LaneType> = BTreeMap::new();
let mut all_contraflow_lanes: BTreeMap<LaneID, IntersectionID> = BTreeMap::new();
let mut all_stop_sign_edits: BTreeMap<IntersectionID, ControlStopSign> = BTreeMap::new();
let mut all_traffic_signals: BTreeMap<IntersectionID, ControlTrafficSignal> =
BTreeMap::new();
@ -626,6 +632,11 @@ impl Map {
all_lane_edits.insert(*id, *lt);
}
}
for (id, i) in &new_edits.contraflow_lanes {
if self.edits.contraflow_lanes.get(id) != Some(i) {
all_contraflow_lanes.insert(*id, *i);
}
}
for (id, ss) in &new_edits.stop_sign_overrides {
if self.edits.stop_sign_overrides.get(id) != Some(ss) {
all_stop_sign_edits.insert(*id, ss.clone());
@ -643,6 +654,11 @@ impl Map {
all_lane_edits.insert(*id, self.get_original_lt(*id));
}
}
for id in self.edits.contraflow_lanes.keys() {
if !new_edits.contraflow_lanes.contains_key(id) {
all_contraflow_lanes.insert(*id, self.get_original_endpt(*id));
}
}
for id in self.edits.stop_sign_overrides.keys() {
if !new_edits.stop_sign_overrides.contains_key(id) {
all_stop_sign_edits.insert(*id, ControlStopSign::new(self, *id, timer));
@ -655,8 +671,9 @@ impl Map {
}
timer.note(format!(
"Total diff: {} lanes, {} stop signs, {} traffic signals",
"Total diff: {} lane types, {} contraflow lanes, {} stop signs, {} traffic signals",
all_lane_edits.len(),
all_contraflow_lanes.len(),
all_stop_sign_edits.len(),
all_traffic_signals.len()
));
@ -664,6 +681,8 @@ impl Map {
let mut changed_lanes = BTreeSet::new();
let mut changed_intersections = BTreeSet::new();
let mut changed_roads = BTreeSet::new();
let mut changed_contraflow_roads = BTreeSet::new();
for (id, lt) in all_lane_edits {
changed_lanes.insert(id);
@ -683,6 +702,47 @@ impl Map {
changed_roads.insert(l.parent);
}
for (id, dst_i) in all_contraflow_lanes {
changed_lanes.insert(id);
let l = &mut self.lanes[id.0];
self.intersections[l.src_i.0]
.outgoing_lanes
.retain(|x| *x != id);
self.intersections[l.dst_i.0]
.incoming_lanes
.retain(|x| *x != id);
std::mem::swap(&mut l.src_i, &mut l.dst_i);
assert_eq!(l.dst_i, dst_i);
l.lane_center_pts = l.lane_center_pts.reversed();
self.intersections[l.src_i.0].outgoing_lanes.push(id);
self.intersections[l.dst_i.0].incoming_lanes.push(id);
// We can only reverse the lane closest to the center.
let r = &mut self.roads[l.parent.0];
if dst_i == r.dst_i {
assert_eq!(r.children_backwards.remove(0).0, id);
r.children_forwards.insert(0, (id, l.lane_type));
for (l, _) in &r.children_forwards {
changed_lanes.insert(*l);
}
} else {
assert_eq!(r.children_forwards.remove(0).0, id);
r.children_backwards.insert(0, (id, l.lane_type));
for (l, _) in &r.children_backwards {
changed_lanes.insert(*l);
}
}
changed_contraflow_roads.insert(r.id);
changed_intersections.insert(l.src_i);
changed_intersections.insert(l.dst_i);
changed_roads.insert(l.parent);
}
for id in changed_roads {
let stops = self.get_r(id).all_bus_stops(self);
for s in stops {
@ -763,7 +823,12 @@ impl Map {
self.edits = new_edits;
self.pathfinder_dirty = true;
(changed_lanes, delete_turns, add_turns)
(
changed_lanes,
changed_contraflow_roads,
delete_turns,
add_turns,
)
}
pub fn recalculate_pathfinding_after_edits(&mut self, timer: &mut Timer) {
@ -799,6 +864,16 @@ impl Map {
self.edits.lane_overrides.remove(&id);
}
let mut delete_contraflow_lanes = Vec::new();
for (id, dst_i) in &self.edits.contraflow_lanes {
if *dst_i == self.get_original_endpt(*id) {
delete_contraflow_lanes.push(*id);
}
}
for id in delete_contraflow_lanes {
self.edits.contraflow_lanes.remove(&id);
}
let mut delete_stop_signs = Vec::new();
for (id, ss) in &self.edits.stop_sign_overrides {
if *ss == ControlStopSign::new(self, *id, timer) {
@ -822,14 +897,28 @@ impl Map {
fn get_original_lt(&self, id: LaneID) -> LaneType {
let parent = self.get_parent(id);
let (side1, side2) = get_lane_types(&parent.osm_tags);
let (fwds, idx) = parent.dir_and_offset(id);
if fwds {
side1[idx]
} else {
side2[idx]
for (l, lt) in parent
.orig_children_forwards
.iter()
.chain(parent.orig_children_backwards.iter())
{
if id == *l {
return *lt;
}
}
panic!("get_original_lt broke for {}", id);
}
fn get_original_endpt(&self, id: LaneID) -> IntersectionID {
let parent = self.get_parent(id);
if parent.orig_children_forwards.iter().any(|(l, _)| *l == id) {
return parent.dst_i;
}
if parent.orig_children_backwards.iter().any(|(l, _)| *l == id) {
return parent.src_i;
}
panic!("get_original_endpt broke for {}", id);
}
}
fn make_half_map(
@ -909,6 +998,8 @@ fn make_half_map(
stable_id: r.id,
children_forwards: Vec::new(),
children_backwards: Vec::new(),
orig_children_forwards: Vec::new(),
orig_children_backwards: Vec::new(),
center_pts: r.trimmed_center_pts.clone(),
src_i: i1,
dst_i: i2,
@ -923,12 +1014,14 @@ fn make_half_map(
let (unshifted_pts, offset) = if lane.reverse_pts {
road.children_backwards.push((id, lane.lane_type));
road.orig_children_backwards.push((id, lane.lane_type));
(
road.center_pts.reversed(),
road.children_backwards.len() - 1,
)
} else {
road.children_forwards.push((id, lane.lane_type));
road.orig_children_forwards.push((id, lane.lane_type));
(road.center_pts.clone(), road.children_forwards.len() - 1)
};
// TODO probably different behavior for oneways

View File

@ -70,9 +70,12 @@ pub struct Road {
// These are ordered from left-most lane (closest to center lane) to rightmost (sidewalk)
pub children_forwards: Vec<(LaneID, LaneType)>,
pub children_backwards: Vec<(LaneID, LaneType)>,
// TODO should consider having a redundant lookup from LaneID
// Same as above, but not changed as map edits happen.
pub orig_children_forwards: Vec<(LaneID, LaneType)>,
pub orig_children_backwards: Vec<(LaneID, LaneType)>,
// Unshifted center points. Order implies road orientation.
// Unshifted original center points. Order implies road orientation. Reversing lanes doesn't
// change this.
pub center_pts: PolyLine,
pub src_i: IntersectionID,
pub dst_i: IntersectionID,
@ -170,15 +173,6 @@ impl Road {
}
}
// Is this lane the arbitrary canonical lane of this road? Used for deciding who should draw
// yellow center lines.
pub fn is_canonical_lane(&self, lane: LaneID) -> bool {
if !self.children_forwards.is_empty() {
return lane == self.children_forwards[0].0;
}
lane == self.children_backwards[0].0
}
pub fn get_speed_limit(&self) -> Speed {
// TODO Should probably cache this
if let Some(limit) = self.osm_tags.get(osm::MAXSPEED) {