displaying extra arbitraryish kml for debugging

This commit is contained in:
Dustin Carlino 2018-08-10 13:28:34 -07:00
parent 432c9644fc
commit b55e0ae263
10 changed files with 312 additions and 10 deletions

View File

@ -18,6 +18,7 @@ piston2d-graphics = "*"
piston2d-opengl_graphics = "*"
pistoncore-glutin_window = "*"
pretty_assertions = "0.5.1"
quick-xml = "0.10.0"
rand = "0.5.1"
serde = "1.0"
serde_derive = "1.0"

View File

@ -174,6 +174,12 @@
0.7,
1.0
],
"ExtraShape": [
1.0,
0.0,
0.0,
1.0
],
"MatchClassification": [
0.0,
1.0,

View File

@ -41,6 +41,7 @@ pub enum Colors {
NextQueued,
TurnIconCircle,
TurnIconInactive,
ExtraShape,
MatchClassification,
DontMatchClassification,

128
editor/src/kml.rs Normal file
View File

@ -0,0 +1,128 @@
use geom::{Bounds, LonLat, PolyLine, Pt2D};
use quick_xml::events::Event;
use quick_xml::reader::Reader;
use std::collections::HashMap;
use std::fs::File;
use std::{f64, fmt, io};
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct ExtraShapeID(pub usize);
impl fmt::Display for ExtraShapeID {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "ExtraShapeID({0})", self.0)
}
}
#[derive(Debug)]
pub struct ExtraShape {
pub id: ExtraShapeID,
pub pts: PolyLine,
pub attributes: HashMap<String, String>,
}
pub fn load(path: &String, gps_bounds: &Bounds) -> Result<Vec<ExtraShape>, io::Error> {
println!("Opening {}", path);
let f = File::open(path).unwrap();
let mut reader = Reader::from_reader(io::BufReader::new(f));
reader.trim_text(true);
let mut buf = Vec::new();
let mut last_progress_byte = 0;
// TODO uncomfortably stateful
let mut shapes = Vec::new();
let mut scanned_schema = false;
let mut attributes: HashMap<String, String> = HashMap::new();
let mut attrib_key: Option<String> = None;
let mut skipped_count = 0;
loop {
if reader.buffer_position() - last_progress_byte >= 1024 * 1024 * 10 {
last_progress_byte = reader.buffer_position();
println!(
"Processed {} MB of {}",
last_progress_byte / (1024 * 1024),
path
);
}
match reader.read_event(&mut buf) {
Ok(Event::Start(e)) => {
let name = e.unescape_and_decode(&reader).unwrap();
if name == "Placemark" {
scanned_schema = true;
} else if name.starts_with("SimpleData name=\"") {
attrib_key = Some(name["SimpleData name=\"".len()..name.len() - 1].to_string());
} else if name == "coordinates" {
attrib_key = Some(name);
} else {
attrib_key = None;
}
}
Ok(Event::Text(e)) => {
if scanned_schema {
if let Some(ref key) = attrib_key {
let text = e.unescape_and_decode(&reader).unwrap();
if key == "coordinates" {
let mut ok = true;
let mut pts: Vec<Pt2D> = Vec::new();
for pair in text.split(" ") {
if let Some(pt) = parse_pt(pair, gps_bounds) {
pts.push(pt);
} else {
ok = false;
break;
}
}
if ok {
let id = ExtraShapeID(shapes.len());
shapes.push(ExtraShape {
id,
pts: PolyLine::new(pts),
attributes: attributes.clone(),
});
} else {
skipped_count += 1;
}
attributes.clear();
} else {
attributes.insert(key.to_string(), text);
}
}
}
}
Ok(Event::Eof) => break,
Err(e) => panic!(
"XML error at position {}: {:?}",
reader.buffer_position(),
e
),
_ => (),
}
buf.clear();
}
println!(
"Got {} shapes from {} and skipped {} shapes",
shapes.len(),
path,
skipped_count
);
return Ok(shapes);
}
fn parse_pt(input: &str, gps_bounds: &Bounds) -> Option<Pt2D> {
let coords: Vec<&str> = input.split(",").collect();
if coords.len() != 2 {
return None;
}
return match (coords[0].parse::<f64>(), coords[1].parse::<f64>()) {
(Ok(lon), Ok(lat)) => if gps_bounds.contains(lon, lat) {
Some(Pt2D::from_gps(&LonLat::new(lon, lat), gps_bounds))
} else {
None
},
_ => None,
};
}

View File

@ -14,6 +14,7 @@ extern crate graphics;
extern crate map_model;
extern crate opengl_graphics;
extern crate piston;
extern crate quick_xml;
#[macro_use]
extern crate pretty_assertions;
extern crate rand;
@ -26,6 +27,14 @@ extern crate strum;
#[macro_use]
extern crate strum_macros;
mod colors;
mod experimental;
mod gui;
mod kml;
mod plugins;
mod render;
mod ui;
use ezgui::input::UserInput;
use glutin_window::GlutinWindow;
use opengl_graphics::{Filter, GlGraphics, GlyphCache, OpenGL, TextureSettings};
@ -34,13 +43,6 @@ use piston::input::RenderEvent;
use piston::window::{Window, WindowSettings};
use structopt::StructOpt;
mod colors;
mod experimental;
mod gui;
mod plugins;
mod render;
mod ui;
#[derive(StructOpt, Debug)]
#[structopt(name = "editor")]
struct Flags {
@ -59,6 +61,10 @@ struct Flags {
/// Use the old parametric sim
#[structopt(long = "parametric_sim")]
parametric_sim: bool,
/// Extra KML to display
#[structopt(long = "kml")]
kml: Option<String>,
}
fn main() {
@ -103,6 +109,7 @@ fn main() {
window_size,
flags.rng_seed,
flags.parametric_sim,
flags.kml,
),
);
}

View File

@ -6,6 +6,7 @@ use ezgui::canvas::Canvas;
use ezgui::input::UserInput;
use ezgui::GfxCtx;
use graphics::types::Color;
use kml::ExtraShapeID;
use map_model;
use map_model::{BuildingID, IntersectionID, LaneID, Map, TurnID};
use piston::input::{Button, Key, ReleaseEvent};
@ -21,6 +22,7 @@ pub enum ID {
Building(BuildingID),
Car(CarID),
Pedestrian(PedestrianID),
ExtraShape(ExtraShapeID),
//Parcel(ParcelID),
}
@ -36,6 +38,7 @@ pub enum SelectionState {
SelectedTurn(TurnID),
SelectedCar(CarID),
SelectedPedestrian(PedestrianID),
SelectedExtraShape(ExtraShapeID),
Tooltip(ID),
}
@ -135,6 +138,14 @@ impl SelectionState {
false
}
}
SelectionState::SelectedExtraShape(id) => {
if input.key_pressed(Key::LCtrl, &format!("Hold Ctrl to show {}'s tooltip", id)) {
new_state = Some(SelectionState::Tooltip(ID::ExtraShape(*id)));
true
} else {
false
}
}
SelectionState::Empty => false,
};
if let Some(s) = new_state {
@ -158,7 +169,8 @@ impl SelectionState {
| SelectionState::SelectedTurn(_)
| SelectionState::SelectedBuilding(_)
| SelectionState::SelectedCar(_)
| SelectionState::SelectedPedestrian(_) => {}
| SelectionState::SelectedPedestrian(_)
| SelectionState::SelectedExtraShape(_) => {}
SelectionState::SelectedIntersection(id) => {
if let Some(signal) = control_map.traffic_signals.get(&id) {
let (cycle, _) = signal.current_cycle_and_remaining_time(sim.time.as_time());
@ -199,6 +211,7 @@ impl SelectionState {
ID::Pedestrian(id) => sim.ped_tooltip(id),
ID::Intersection(id) => vec![format!("{}", id)],
ID::Turn(id) => vec![format!("{}", id)],
ID::ExtraShape(id) => draw_map.get_es(id).tooltip_lines(),
};
canvas.draw_mouse_tooltip(g, &lines);
}
@ -259,6 +272,16 @@ impl SelectionState {
_ => None,
}
}
pub fn color_es(&self, es: ExtraShapeID, cs: &ColorScheme) -> Option<Color> {
match *self {
SelectionState::SelectedExtraShape(id) if es == id => Some(cs.get(Colors::Selected)),
SelectionState::Tooltip(ID::ExtraShape(id)) if es == id => {
Some(cs.get(Colors::Selected))
}
_ => None,
}
}
}
fn selection_state_for(some_id: ID) -> SelectionState {
@ -269,6 +292,7 @@ fn selection_state_for(some_id: ID) -> SelectionState {
ID::Turn(id) => SelectionState::SelectedTurn(id),
ID::Car(id) => SelectionState::SelectedCar(id),
ID::Pedestrian(id) => SelectionState::SelectedPedestrian(id),
ID::ExtraShape(id) => SelectionState::SelectedExtraShape(id),
}
}
@ -294,6 +318,7 @@ impl Hider {
SelectionState::SelectedIntersection(id) => Some(ID::Intersection(*id)),
SelectionState::SelectedLane(id, _) => Some(ID::Lane(*id)),
SelectionState::SelectedBuilding(id) => Some(ID::Building(*id)),
SelectionState::SelectedExtraShape(id) => Some(ID::ExtraShape(*id)),
_ => None,
};
if let Some(id) = item {
@ -318,4 +343,8 @@ impl Hider {
pub fn show_i(&self, id: IntersectionID) -> bool {
!self.items.contains(&ID::Intersection(id))
}
pub fn show_es(&self, id: ExtraShapeID) -> bool {
!self.items.contains(&ID::ExtraShape(id))
}
}

View File

@ -0,0 +1,44 @@
use aabb_quadtree::geom::Rect;
use ezgui::GfxCtx;
use geom::{Polygon, Pt2D};
use graphics::types::Color;
use kml::{ExtraShape, ExtraShapeID};
use render::{get_bbox, EXTRA_SHAPE_THICKNESS};
use std::collections::HashMap;
#[derive(Debug)]
pub struct DrawExtraShape {
pub id: ExtraShapeID,
polygon: Polygon,
attributes: HashMap<String, String>,
}
impl DrawExtraShape {
pub fn new(s: ExtraShape) -> DrawExtraShape {
DrawExtraShape {
id: s.id,
polygon: s.pts.make_polygons(EXTRA_SHAPE_THICKNESS).unwrap(),
attributes: s.attributes,
}
}
pub fn draw(&self, g: &mut GfxCtx, color: Color) {
g.draw_polygon(color, &self.polygon);
}
pub fn contains_pt(&self, pt: Pt2D) -> bool {
self.polygon.contains_pt(pt)
}
pub fn get_bbox(&self) -> Rect {
get_bbox(&self.polygon.get_bounds())
}
pub fn tooltip_lines(&self) -> Vec<String> {
let mut lines = Vec::new();
for (k, v) in &self.attributes {
lines.push(format!("{} = {}", k, v));
}
lines
}
}

View File

@ -3,9 +3,11 @@
use aabb_quadtree::geom::{Point, Rect};
use aabb_quadtree::QuadTree;
use geom::{LonLat, Pt2D};
use kml::{ExtraShape, ExtraShapeID};
use map_model::{BuildingID, IntersectionID, Lane, LaneID, Map, ParcelID, Turn, TurnID};
use plugins::selection::Hider;
use render::building::DrawBuilding;
use render::extra_shape::DrawExtraShape;
use render::intersection::DrawIntersection;
use render::lane::DrawLane;
use render::parcel::DrawParcel;
@ -18,16 +20,18 @@ pub struct DrawMap {
pub turns: HashMap<TurnID, DrawTurn>,
pub buildings: Vec<DrawBuilding>,
pub parcels: Vec<DrawParcel>,
pub extra_shapes: Vec<DrawExtraShape>,
lanes_quadtree: QuadTree<LaneID>,
intersections_quadtree: QuadTree<IntersectionID>,
buildings_quadtree: QuadTree<BuildingID>,
parcels_quadtree: QuadTree<ParcelID>,
extra_shapes_quadtree: QuadTree<ExtraShapeID>,
}
impl DrawMap {
// Also returns the center of the map in map-space
pub fn new(map: &Map) -> (DrawMap, Pt2D) {
pub fn new(map: &Map, raw_extra_shapes: Vec<ExtraShape>) -> (DrawMap, Pt2D) {
let mut lanes: Vec<DrawLane> = Vec::new();
for l in map.all_lanes() {
lanes.push(DrawLane::new(l, map));
@ -56,6 +60,11 @@ impl DrawMap {
.map(|p| DrawParcel::new(p))
.collect();
let extra_shapes: Vec<DrawExtraShape> = raw_extra_shapes
.into_iter()
.map(|s| DrawExtraShape::new(s))
.collect();
// min_y here due to the wacky y inversion
let bounds = map.get_gps_bounds();
let max_screen_pt = Pt2D::from_gps(&LonLat::new(bounds.max_x, bounds.min_y), &bounds);
@ -84,6 +93,11 @@ impl DrawMap {
parcels_quadtree.insert_with_box(p.id, p.get_bbox());
}
let mut extra_shapes_quadtree = QuadTree::default(map_bbox);
for s in &extra_shapes {
extra_shapes_quadtree.insert_with_box(s.id, s.get_bbox());
}
(
DrawMap {
lanes,
@ -91,11 +105,13 @@ impl DrawMap {
turns,
buildings,
parcels,
extra_shapes,
lanes_quadtree,
intersections_quadtree,
buildings_quadtree,
parcels_quadtree,
extra_shapes_quadtree,
},
Pt2D::new(max_screen_pt.x() / 2.0, max_screen_pt.y() / 2.0),
)
@ -159,6 +175,10 @@ impl DrawMap {
&self.parcels[id.0]
}
pub fn get_es(&self, id: ExtraShapeID) -> &DrawExtraShape {
&self.extra_shapes[id.0]
}
pub fn get_loads_onscreen(&self, screen_bbox: Rect, hider: &Hider) -> Vec<&DrawLane> {
let mut v = Vec::new();
for &(id, _, _) in &self.lanes_quadtree.query(screen_bbox) {
@ -200,4 +220,18 @@ impl DrawMap {
}
v
}
pub fn get_extra_shapes_onscreen(
&self,
screen_bbox: Rect,
hider: &Hider,
) -> Vec<&DrawExtraShape> {
let mut v = Vec::new();
for &(id, _, _) in &self.extra_shapes_quadtree.query(screen_bbox) {
if hider.show_es(*id) {
v.push(self.get_es(*id));
}
}
v
}
}

View File

@ -1,6 +1,7 @@
// Copyright 2018 Google LLC, licensed under http://www.apache.org/licenses/LICENSE-2.0
mod building;
mod extra_shape;
mod intersection;
mod lane;
mod map;
@ -17,6 +18,7 @@ use std::f64;
// These are all in meters
const PARCEL_BOUNDARY_THICKNESS: f64 = 0.5;
const EXTRA_SHAPE_THICKNESS: f64 = 1.0;
const TURN_ICON_ARROW_THICKNESS: f64 = geometry::BIG_ARROW_THICKNESS / 3.0;
const BIG_ARROW_TIP_LENGTH: f64 = 1.0;

View File

@ -13,6 +13,7 @@ use ezgui::{GfxCtx, ToggleableLayer};
use geom::Pt2D;
use graphics::types::Color;
use gui;
use kml;
use map_model;
use map_model::{Edits, IntersectionID};
use piston::input::{Key, MouseCursorEvent};
@ -50,6 +51,7 @@ pub struct UI {
show_buildings: ToggleableLayer,
show_intersections: ToggleableLayer,
show_parcels: ToggleableLayer,
show_extra_shapes: ToggleableLayer,
debug_mode: ToggleableLayer,
// This is a particularly special plugin, since it's always kind of active and other things
@ -81,12 +83,20 @@ impl UI {
window_size: Size,
rng_seed: Option<u8>,
parametric_sim: bool,
kml: Option<String>,
) -> UI {
let edits: Edits = abstutil::read_json("road_edits.json").unwrap_or(Edits::new());
println!("Opening {}", abst_path);
let map = map_model::Map::new(abst_path, &edits).expect("Couldn't load map");
let (draw_map, center_pt) = render::DrawMap::new(&map);
let extra_shapes = if let Some(path) = kml {
kml::load(&path, &map.get_gps_bounds()).expect("Couldn't load extra KML shapes")
} else {
Vec::new()
};
let (draw_map, center_pt) = render::DrawMap::new(&map, extra_shapes);
let control_map = ControlMap::new(&map);
let steepness_viz = SteepnessVisualizer::new(&map);
@ -110,6 +120,12 @@ impl UI {
Some(MIN_ZOOM_FOR_LANES),
),
show_parcels: ToggleableLayer::new("parcels", Key::D4, "4", Some(MIN_ZOOM_FOR_PARCELS)),
show_extra_shapes: ToggleableLayer::new(
"extra KML shapes",
Key::D7,
"7",
Some(MIN_ZOOM_FOR_LANES),
),
debug_mode: ToggleableLayer::new("debug mode", Key::G, "G", None),
current_selection_state: SelectionState::Empty,
@ -155,6 +171,7 @@ impl UI {
self.show_buildings.handle_zoom(old_zoom, new_zoom);
self.show_intersections.handle_zoom(old_zoom, new_zoom);
self.show_parcels.handle_zoom(old_zoom, new_zoom);
self.show_extra_shapes.handle_zoom(old_zoom, new_zoom);
self.debug_mode.handle_zoom(old_zoom, new_zoom);
}
@ -211,6 +228,16 @@ impl UI {
}
}
if self.show_extra_shapes.is_enabled() {
for s in &self.draw_map
.get_extra_shapes_onscreen(screen_bbox, &self.hider)
{
if s.contains_pt(pt) {
return Some(ID::ExtraShape(s.id));
}
}
}
if self.show_lanes.is_enabled() {
for l in &lanes_onscreen {
if l.contains_pt(pt) {
@ -447,6 +474,15 @@ impl gui::GUI for UI {
}
return gui::EventLoopMode::InputOnly;
}
if self.show_extra_shapes.handle_event(input) {
if let SelectionState::SelectedExtraShape(_) = self.current_selection_state {
self.current_selection_state = SelectionState::Empty;
}
if let SelectionState::Tooltip(ID::ExtraShape(_)) = self.current_selection_state {
self.current_selection_state = SelectionState::Empty;
}
return gui::EventLoopMode::InputOnly;
}
stop_if_done!(self.show_parcels.handle_event(input));
stop_if_done!(self.debug_mode.handle_event(input));
@ -611,6 +647,20 @@ impl gui::GUI for UI {
}
}
if self.show_extra_shapes.is_enabled() {
for s in &self.draw_map
.get_extra_shapes_onscreen(screen_bbox, &self.hider)
{
// TODO no separate color method?
s.draw(
g,
self.current_selection_state
.color_es(s.id, &self.cs)
.unwrap_or(self.cs.get(Colors::ExtraShape)),
);
}
}
self.current_selection_state.draw(
&self.map,
&self.canvas,