From d55ebb71ca28ecd9f36f40dd83072ddbab19d3b3 Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Mon, 11 May 2020 10:45:36 -0700 Subject: [PATCH] starting a dedicated OSM mapping mode for parking in the main game, not map_editor --- game/src/devtools/mapping.rs | 159 +++++++++++++++++++++++++++++++++++ game/src/devtools/mod.rs | 10 +++ map_editor/src/main.rs | 32 +------ map_editor/src/model.rs | 75 +---------------- 4 files changed, 172 insertions(+), 104 deletions(-) create mode 100644 game/src/devtools/mapping.rs diff --git a/game/src/devtools/mapping.rs b/game/src/devtools/mapping.rs new file mode 100644 index 0000000000..078fb1fcc2 --- /dev/null +++ b/game/src/devtools/mapping.rs @@ -0,0 +1,159 @@ +use crate::app::{App, ShowEverything}; +use crate::common::ColorLegend; +use crate::game::{State, Transition}; +use crate::helpers::ID; +use abstutil::prettyprint_usize; +use ezgui::{ + hotkey, Btn, Checkbox, Color, Composite, Drawable, EventCtx, GeomBatch, GfxCtx, + HorizontalAlignment, Key, Line, Outcome, TextExt, VerticalAlignment, Widget, +}; +use map_model::{osm, RoadID}; +use sim::DontDrawAgents; +use std::collections::HashSet; + +pub struct ParkingMapper { + composite: Composite, + draw_layer: Drawable, + show_todo: bool, + selected: Option<(HashSet, Drawable)>, +} + +impl ParkingMapper { + pub fn new(ctx: &mut EventCtx, app: &App, show_todo: bool) -> Box { + let map = &app.primary.map; + + let color = if show_todo { + Color::RED.alpha(0.5) + } else { + Color::BLUE.alpha(0.5) + }; + let mut batch = GeomBatch::new(); + let mut done = HashSet::new(); + let mut todo = HashSet::new(); + for r in map.all_roads() { + if r.osm_tags.contains_key(osm::INFERRED_PARKING) { + todo.insert(r.orig_id); + if show_todo { + batch.push(color, map.get_r(r.id).get_thick_polygon(map).unwrap()); + } + } else { + done.insert(r.orig_id); + if !show_todo { + batch.push(color, map.get_r(r.id).get_thick_polygon(map).unwrap()); + } + } + } + + // Nicer display + for i in map.all_intersections() { + let is_todo = i + .roads + .iter() + .any(|r| map.get_r(*r).osm_tags.contains_key(osm::INFERRED_PARKING)); + if show_todo == is_todo { + batch.push(color, i.polygon.clone()); + } + } + + Box::new(ParkingMapper { + draw_layer: ctx.upload(batch), + show_todo, + composite: Composite::new( + Widget::col(vec![ + Widget::row(vec![ + Line("Parking mapper") + .small_heading() + .draw(ctx) + .margin_right(10), + Btn::text_fg("X") + .build_def(ctx, hotkey(Key::Escape)) + .align_right(), + ]), + format!( + "{} / {} ways done", + prettyprint_usize(done.len()), + prettyprint_usize(done.len() + todo.len()) + ) + .draw_text(ctx), + Widget::row(vec![ + Checkbox::text(ctx, "show ways with missing tags", None, show_todo) + .margin_right(15), + ColorLegend::row(ctx, color, if show_todo { "TODO" } else { "done" }), + ]), + ]) + .padding(10) + .bg(app.cs.panel_bg), + ) + .aligned(HorizontalAlignment::Center, VerticalAlignment::Top) + .build(ctx), + selected: None, + }) + } +} + +impl State for ParkingMapper { + fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition { + let map = &app.primary.map; + + ctx.canvas_movement(); + if ctx.redo_mouseover() { + let maybe_r = match app.calculate_current_selection( + ctx, + &DontDrawAgents {}, + &ShowEverything::new(), + false, + true, + ) { + Some(ID::Road(r)) => Some(r), + Some(ID::Lane(l)) => Some(map.get_l(l).parent), + _ => None, + }; + if let Some(id) = maybe_r { + if self + .selected + .as_ref() + .map(|(ids, _)| !ids.contains(&id)) + .unwrap_or(true) + { + // Select all roads part of this way + let way = map.get_r(id).orig_id.osm_way_id; + let mut ids = HashSet::new(); + let mut batch = GeomBatch::new(); + for r in map.all_roads() { + if r.orig_id.osm_way_id == way { + ids.insert(r.id); + batch.push(Color::GREEN.alpha(0.5), r.get_thick_polygon(map).unwrap()); + } + } + + self.selected = Some((ids, ctx.upload(batch))); + } + } else { + self.selected = None; + } + } + + match self.composite.event(ctx) { + Some(Outcome::Clicked(x)) => match x.as_ref() { + "X" => { + return Transition::Pop; + } + _ => unreachable!(), + }, + None => {} + } + if self.composite.is_checked("show ways with missing tags") != self.show_todo { + return Transition::Replace(ParkingMapper::new(ctx, app, !self.show_todo)); + } + + Transition::Keep + } + + fn draw(&self, g: &mut GfxCtx, _: &App) { + g.redraw(&self.draw_layer); + if let Some((_, ref roads)) = self.selected { + g.redraw(roads); + } + self.composite.draw(g); + } +} diff --git a/game/src/devtools/mod.rs b/game/src/devtools/mod.rs index e40927f8ae..84ac55be24 100644 --- a/game/src/devtools/mod.rs +++ b/game/src/devtools/mod.rs @@ -1,4 +1,5 @@ mod blocks; +mod mapping; mod polygon; mod scenario; @@ -22,6 +23,7 @@ impl DevToolsMode { (hotkey(Key::E), "edit a polygon"), (hotkey(Key::P), "draw a polygon"), (hotkey(Key::W), "load scenario"), + (hotkey(Key::M), "OSM mapping"), ], )) .cb("X", Box::new(|_, _| Some(Transition::Pop))) @@ -42,6 +44,14 @@ impl DevToolsMode { .cb( "load scenario", Box::new(|_, _| Some(Transition::Push(WizardState::new(Box::new(load_scenario))))), + ) + .cb( + "OSM mapping", + Box::new(|ctx, app| { + Some(Transition::Push(mapping::ParkingMapper::new( + ctx, app, true, + ))) + }), ), ) } diff --git a/map_editor/src/main.rs b/map_editor/src/main.rs index ed1da4ffeb..a396b1f980 100644 --- a/map_editor/src/main.rs +++ b/map_editor/src/main.rs @@ -69,7 +69,7 @@ impl UI { } let bounds = model.map.gps_bounds.to_bounds(); ctx.canvas.map_dims = (bounds.width(), bounds.height()); - let mut ui = UI { + UI { model, state: State::viewing(), composite: Composite::new( @@ -100,33 +100,7 @@ impl UI { info_key_held: false, last_id: None, - }; - ui.recount_parking_tags(ctx); - ui - } - - fn recount_parking_tags(&mut self, ctx: &mut EventCtx) { - let mut ways_audited = HashSet::new(); - let mut ways_missing = HashSet::new(); - for r in self.model.map.roads.values() { - if r.synthetic() { - continue; - } - if r.osm_tags.contains_key(osm::INFERRED_PARKING) { - ways_missing.insert(r.osm_tags[osm::OSM_WAY_ID].clone()); - } else { - ways_audited.insert(r.osm_tags[osm::OSM_WAY_ID].clone()); - } } - - let mut txt = Text::from(Line(format!( - "Parking data audited: {} / {} ways", - abstutil::prettyprint_usize(ways_audited.len()), - abstutil::prettyprint_usize(ways_audited.len() + ways_missing.len()) - ))); - txt.add(Line("Hold right Control to show info about objects")); - self.composite - .replace(ctx, "current info", txt.draw(ctx).named("current info")); } } @@ -223,10 +197,6 @@ impl GUI for UI { } else if could_swap && ctx.input.key_pressed(Key::S, "swap lanes") { self.model.swap_lanes(r, ctx.prerender); self.model.world.handle_mouseover(ctx); - } else if ctx.input.key_pressed(Key::T, "toggle parking") { - self.model.toggle_r_parking(r, ctx.prerender); - self.model.world.handle_mouseover(ctx); - self.recount_parking_tags(ctx); } else if ctx.input.key_pressed(Key::F, "toggle sidewalks") { self.model.toggle_r_sidewalks(r, ctx.prerender); self.model.world.handle_mouseover(ctx); diff --git a/map_editor/src/model.rs b/map_editor/src/model.rs index a65754b3b3..c16420db34 100644 --- a/map_editor/src/model.rs +++ b/map_editor/src/model.rs @@ -432,76 +432,6 @@ impl Model { self.road_added(id, prerender); } - pub fn toggle_r_parking(&mut self, some_id: OriginalRoad, prerender: &Prerender) { - // Update every road belonging to the way. - let osm_id = self.map.roads[&some_id].osm_tags[osm::OSM_WAY_ID].clone(); - let matching_roads = self - .map - .roads - .iter() - .filter_map(|(k, v)| { - if v.osm_tags[osm::OSM_WAY_ID] == osm_id { - Some(*k) - } else { - None - } - }) - .collect::>(); - - // Verify every road has the same parking tags. Blockface hints might've applied to just - // some parts. If this is really true, then the way has to be split. Example is McGraw St. - let both = self.map.roads[&some_id] - .osm_tags - .get(osm::PARKING_BOTH) - .cloned(); - let left = self.map.roads[&some_id] - .osm_tags - .get(osm::PARKING_LEFT) - .cloned(); - let right = self.map.roads[&some_id] - .osm_tags - .get(osm::PARKING_RIGHT) - .cloned(); - for r in &matching_roads { - let tags = &self.map.roads[r].osm_tags; - if tags.get(osm::PARKING_BOTH) != both.as_ref() - || tags.get(osm::PARKING_LEFT) != left.as_ref() - || tags.get(osm::PARKING_RIGHT) != right.as_ref() - { - println!( - "WARNING: {} and {} belong to same way, but have different parking tags!", - some_id, r - ); - } - } - - for id in matching_roads { - self.road_deleted(id); - - let osm_tags = &mut self.map.roads.get_mut(&id).unwrap().osm_tags; - osm_tags.remove(osm::INFERRED_PARKING); - let yes = "parallel".to_string(); - let no = "no_parking".to_string(); - if both.as_ref() == Some(&yes) { - osm_tags.insert(osm::PARKING_BOTH.to_string(), no); - } else if both.as_ref() == Some(&no) { - osm_tags.remove(osm::PARKING_BOTH); - osm_tags.insert(osm::PARKING_LEFT.to_string(), yes); - osm_tags.insert(osm::PARKING_RIGHT.to_string(), no); - } else if left.as_ref() == Some(&yes) { - osm_tags.insert(osm::PARKING_LEFT.to_string(), no); - osm_tags.insert(osm::PARKING_RIGHT.to_string(), yes); - } else if right.as_ref() == Some(&yes) { - osm_tags.remove(osm::PARKING_LEFT); - osm_tags.remove(osm::PARKING_RIGHT); - osm_tags.insert(osm::PARKING_BOTH.to_string(), yes); - } - - self.road_added(id, prerender); - } - } - - // TODO Refactor with toggle_r_parking? pub fn toggle_r_sidewalks(&mut self, some_id: OriginalRoad, prerender: &Prerender) { // Update every road belonging to the way. let osm_id = self.map.roads[&some_id].osm_tags[osm::OSM_WAY_ID].clone(); @@ -527,7 +457,7 @@ impl Model { for r in &matching_roads { if self.map.roads[r].osm_tags.get(osm::SIDEWALK) != value.as_ref() { println!( - "WARNING: {} and {} belong to same way, but have different parking tags!", + "WARNING: {} and {} belong to same way, but have different sidewalk tags!", some_id, r ); } @@ -567,8 +497,7 @@ impl Model { let r = &self.map.roads[&id]; let unset = r.synthetic() && r.osm_tags.get(osm::NAME) == Some(&"Streety McStreetFace".to_string()); - let lanes_unknown = r.osm_tags.contains_key(osm::INFERRED_PARKING) - || r.osm_tags.contains_key(osm::INFERRED_SIDEWALKS); + let lanes_unknown = r.osm_tags.contains_key(osm::INFERRED_SIDEWALKS); let spec = r.get_spec(); let center_pts = PolyLine::new(r.center_points.clone());