diff --git a/editor/src/edit/mod.rs b/editor/src/edit/mod.rs index 6a0704e88d..0bc652134c 100644 --- a/editor/src/edit/mod.rs +++ b/editor/src/edit/mod.rs @@ -16,7 +16,9 @@ use ezgui::{ hotkey, lctrl, Color, EventCtx, EventLoopMode, GfxCtx, Key, ModalMenu, Text, Wizard, WrappedWizard, }; -use map_model::{IntersectionID, Lane, LaneID, LaneType, Map, MapEdits, Road, TurnID, TurnType}; +use map_model::{ + IntersectionID, Lane, LaneID, LaneType, Map, MapEdits, Road, RoadID, TurnID, TurnType, +}; use std::collections::{BTreeSet, HashMap}; pub enum EditMode { @@ -25,6 +27,7 @@ pub enum EditMode { Loading(Wizard), EditingStopSign(stop_signs::StopSignEditor), EditingTrafficSignal(traffic_signals::TrafficSignalEditor), + BulkEditLanes(RoadID, Wizard), } impl EditMode { @@ -154,6 +157,16 @@ impl EditMode { } } } + + if ctx + .input + .contextual_action(Key::U, "bulk edit lanes on this road") + { + state.mode = Mode::Edit(EditMode::BulkEditLanes( + state.ui.primary.map.get_l(id).parent, + Wizard::new(), + )); + } } if let Some(ID::Intersection(id)) = state.ui.primary.current_selection { if state.ui.primary.map.maybe_get_stop_sign(id).is_some() @@ -177,6 +190,7 @@ impl EditMode { } } Mode::Edit(EditMode::Saving(ref mut wizard)) => { + ctx.canvas.handle_event(ctx.input); if save_edits(wizard.wrap(ctx), &mut state.ui.primary.map).is_some() || wizard.aborted() { @@ -184,6 +198,7 @@ impl EditMode { } } Mode::Edit(EditMode::Loading(ref mut wizard)) => { + ctx.canvas.handle_event(ctx.input); if let Some(new_edits) = load_edits( &state.ui.primary.map, &mut wizard.wrap(ctx), @@ -205,6 +220,15 @@ impl EditMode { state.mode = Mode::Edit(EditMode::new(ctx, &mut state.ui)); } } + Mode::Edit(EditMode::BulkEditLanes(r, ref mut wizard)) => { + ctx.canvas.handle_event(ctx.input); + if let Some(edits) = bulk_edit(r, &mut wizard.wrap(ctx), &state.ui.primary.map) { + apply_map_edits(&mut state.ui, ctx, edits); + state.mode = Mode::Edit(EditMode::new(ctx, &mut state.ui)); + } else if wizard.aborted() { + state.mode = Mode::Edit(EditMode::new(ctx, &mut state.ui)); + } + } _ => unreachable!(), } @@ -288,7 +312,8 @@ impl EditMode { menu.draw(g); } Mode::Edit(EditMode::Saving(ref wizard)) - | Mode::Edit(EditMode::Loading(ref wizard)) => { + | Mode::Edit(EditMode::Loading(ref wizard)) + | Mode::Edit(EditMode::BulkEditLanes(_, ref wizard)) => { state.ui.draw( g, DrawOptions::new(), @@ -488,3 +513,60 @@ fn load_edits(map: &Map, wizard: &mut WrappedWizard, query: &str) -> Option Option { + let from = wizard + .choose_something( + "Change all lanes of type...", + Box::new(|| { + vec![ + (None, "driving".to_string(), LaneType::Driving), + (None, "parking".to_string(), LaneType::Parking), + (None, "biking".to_string(), LaneType::Biking), + (None, "bus".to_string(), LaneType::Bus), + ] + }), + )? + .1; + let to = wizard + .choose_something( + "Change to all lanes of type...", + Box::new(move || { + vec![ + (None, "driving".to_string(), LaneType::Driving), + (None, "parking".to_string(), LaneType::Parking), + (None, "biking".to_string(), LaneType::Biking), + (None, "bus".to_string(), LaneType::Bus), + ] + .into_iter() + .filter(|(_, _, lt)| *lt != from) + .collect() + }), + )? + .1; + + // Do the dirty deed. Match by road name; OSM way ID changes a fair bit. + let road_name = map.get_r(r).get_name(); + let mut edits = map.get_edits().clone(); + let mut cnt = 0; + for l in map.all_lanes() { + if l.lane_type != from { + continue; + } + let parent = map.get_parent(l.id); + if parent.get_name() != road_name { + continue; + } + // TODO This looks at the original state of the map, not with all the edits applied so far! + if can_change_lane_type(parent, l, to, map) { + edits.lane_overrides.insert(l.id, to); + cnt += 1; + } + } + // TODO pop this up. warn about road names changing and being weird. :) + println!( + "Changed {} {:?} lanes to {:?} lanes on {}", + cnt, from, to, road_name + ); + Some(edits) +} diff --git a/map_model/src/lib.rs b/map_model/src/lib.rs index 1d0b5d0574..b5d1118667 100644 --- a/map_model/src/lib.rs +++ b/map_model/src/lib.rs @@ -37,6 +37,7 @@ pub const LANE_THICKNESS: Distance = Distance::const_meters(2.5); impl Cloneable for ControlTrafficSignal {} impl Cloneable for IntersectionID {} +impl Cloneable for LaneType {} impl Cloneable for MapEdits {} impl Cloneable for Neighborhood {} impl Cloneable for NeighborhoodBuilder {}