mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-24 23:15:24 +03:00
reverse lane direction as a new edit. might be a few determinism issues,
but seems to mostly work.
This commit is contained in:
parent
98d7f26f2d
commit
258f3d4528
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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),
|
||||
|
@ -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(¢er, Distance::meters(2.0), Distance::meters(1.0)),
|
||||
);
|
||||
|
||||
let mut label = Text::new();
|
||||
|
@ -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(),
|
||||
}
|
||||
|
@ -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,13 +897,27 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user