starting a dedicated OSM mapping mode for parking in the main game, not map_editor

This commit is contained in:
Dustin Carlino 2020-05-11 10:45:36 -07:00
parent c6f34de3d7
commit d55ebb71ca
4 changed files with 172 additions and 104 deletions

View File

@ -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<RoadID>, Drawable)>,
}
impl ParkingMapper {
pub fn new(ctx: &mut EventCtx, app: &App, show_todo: bool) -> Box<dyn State> {
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);
}
}

View File

@ -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,
)))
}),
),
)
}

View File

@ -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);

View File

@ -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::<Vec<_>>();
// 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());