debug association between soundcast parcels and buildings

This commit is contained in:
Dustin Carlino 2020-05-16 12:36:10 -07:00
parent 2d69392e9c
commit 20f8548634
3 changed files with 131 additions and 39 deletions

View File

@ -178,7 +178,7 @@ data/input/seattle/osm/mt_baker.osm,2cd7ba808d94318975bd73e9ced01c51,https://www
data/input/seattle/osm/udistrict.osm,e5b0d978d1c74ae5c555a4ebd3ad9d70,https://www.dropbox.com/s/h66cidmzcq15pbo/udistrict.osm.zip?dl=0
data/input/seattle/osm/washington-latest.osm.pbf,363e061c2bf9aa49b48fa12ec56d0ed1,https://www.dropbox.com/s/xha9cx696czbzlf/washington-latest.osm.pbf.zip?dl=0
data/input/seattle/osm/west_seattle.osm,8f893f5a3302b3cf5a614b1d52e49b5f,https://www.dropbox.com/s/8m0vmvwg5zz3uod/west_seattle.osm.zip?dl=0
data/input/seattle/parcels.bin,dc3eee0a4ba74a4a6ee626f2c55d5e51,https://www.dropbox.com/s/39bymqayph3easc/parcels.bin.zip?dl=0
data/input/seattle/parcels.bin,494390afe74712bc296b29e02ef2711c,https://www.dropbox.com/s/39bymqayph3easc/parcels.bin.zip?dl=0
data/input/seattle/parcels_urbansim.txt,db63d7d606e8702d12f9399e87e6a00f,https://www.dropbox.com/s/6g8rbsf200dssj3/parcels_urbansim.txt.zip?dl=0
data/input/seattle/popdat.bin,d1fe506c5f4a65447646f0100e3e3a5a,https://www.dropbox.com/s/wrpji7e14rdwszs/popdat.bin.zip?dl=0
data/input/seattle/sidewalks.bin,034dd47ab77902dbc81c0107f13d8965,https://www.dropbox.com/s/ma9bmisijc7v7xa/sidewalks.bin.zip?dl=0

View File

@ -3,12 +3,13 @@ use crate::game::{State, Transition};
use aabb_quadtree::QuadTree;
use abstutil::prettyprint_usize;
use ezgui::{
hotkey, Btn, Color, Composite, Drawable, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key,
Line, Outcome, Text, TextExt, VerticalAlignment, Widget,
hotkey, Btn, Choice, Color, Composite, Drawable, EventCtx, GeomBatch, GfxCtx,
HorizontalAlignment, Key, Line, Outcome, Text, TextExt, VerticalAlignment, Widget,
};
use geom::{Circle, Distance, PolyLine, Polygon, Pt2D, Ring};
use kml::ExtraShapes;
use std::collections::BTreeMap;
use map_model::BuildingID;
use std::collections::{BTreeMap, HashSet};
pub struct ViewKML {
composite: Composite,
@ -17,11 +18,15 @@ pub struct ViewKML {
selected: Option<usize>,
quadtree: QuadTree<usize>,
analysis: String,
draw_analysis: Drawable,
}
struct Object {
polygon: Polygon,
attribs: BTreeMap<String, String>,
osm_bldg: Option<BuildingID>,
}
const RADIUS: Distance = Distance::const_meters(5.0);
@ -37,10 +42,14 @@ impl ViewKML {
};
let bounds = app.primary.map.get_gps_bounds();
let dataset_name = abstutil::basename(path);
let mut batch = GeomBatch::new();
let mut objects = Vec::new();
let mut quadtree = QuadTree::default(app.primary.map.get_bounds().as_bbox());
timer.start_iter("convert shapes", raw_shapes.shapes.len());
for shape in raw_shapes.shapes {
timer.next();
if !bounds.contains(shape.points[0]) {
continue;
}
@ -49,23 +58,17 @@ impl ViewKML {
.into_iter()
.map(|gps| Pt2D::forcibly_from_gps(gps, bounds))
.collect();
let obj = make_object(app, shape.attributes, pts, &dataset_name);
let polygon = if pts.len() == 1 {
Circle::new(pts[0], RADIUS).to_polygon()
} else if pts[0] == *pts.last().unwrap() {
// TODO Toggle between these better
//Polygon::new(&pts)
Ring::new(pts).make_polygons(THICKNESS)
} else {
PolyLine::new(pts).make_polygons(THICKNESS)
};
quadtree.insert_with_box(objects.len(), obj.polygon.get_bounds().as_bbox());
batch.push(Color::RED.alpha(0.8), obj.polygon.clone());
objects.push(obj);
}
quadtree.insert_with_box(objects.len(), polygon.get_bounds().as_bbox());
batch.push(Color::RED.alpha(0.8), polygon.clone());
objects.push(Object {
polygon,
attribs: shape.attributes,
});
let mut choices = vec![Choice::string("None")];
if dataset_name == "parcels" {
choices.push(Choice::string("parcels without buildings"));
choices.push(Choice::string("parcels with multiple buildings"));
}
Box::new(ViewKML {
@ -81,8 +84,16 @@ impl ViewKML {
.build_def(ctx, hotkey(Key::Escape))
.align_right(),
]),
format!("{}: {} objects", path, prettyprint_usize(objects.len()))
.draw_text(ctx),
format!(
"{}: {} objects",
dataset_name,
prettyprint_usize(objects.len())
)
.draw_text(ctx),
Widget::row(vec![
"Analysis:".draw_text(ctx).margin_right(10),
Widget::dropdown(ctx, "analysis", "None".to_string(), choices),
]),
])
.padding(10)
.bg(app.cs.panel_bg),
@ -92,13 +103,15 @@ impl ViewKML {
objects,
quadtree,
selected: None,
analysis: "None".to_string(),
draw_analysis: ctx.upload(GeomBatch::new()),
})
})
}
}
impl State for ViewKML {
fn event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
fn event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
ctx.canvas_movement();
if ctx.redo_mouseover() {
self.selected = None;
@ -126,20 +139,96 @@ impl State for ViewKML {
None => {}
}
let analysis: String = self.composite.dropdown_value("analysis");
if analysis != self.analysis {
self.draw_analysis = ctx.upload(make_analysis(app, &self.objects, &analysis));
self.analysis = analysis;
}
Transition::Keep
}
fn draw(&self, g: &mut GfxCtx, _: &App) {
fn draw(&self, g: &mut GfxCtx, app: &App) {
g.redraw(&self.draw);
g.redraw(&self.draw_analysis);
self.composite.draw(g);
if let Some(idx) = self.selected {
g.draw_polygon(Color::BLUE, &self.objects[idx].polygon);
let obj = &self.objects[idx];
g.draw_polygon(Color::BLUE, &obj.polygon);
let mut txt = Text::new();
for (k, v) in &self.objects[idx].attribs {
for (k, v) in &obj.attribs {
txt.add(Line(format!("{} = {}", k, v)));
}
g.draw_mouse_tooltip(txt);
if let Some(b) = obj.osm_bldg {
g.draw_polygon(Color::GREEN, &app.primary.map.get_b(b).polygon);
}
}
}
}
fn make_object(
app: &App,
attribs: BTreeMap<String, String>,
pts: Vec<Pt2D>,
dataset_name: &str,
) -> Object {
let polygon = if pts.len() == 1 {
Circle::new(pts[0], RADIUS).to_polygon()
} else if pts[0] == *pts.last().unwrap() {
// TODO Toggle between these better
//Polygon::new(&pts)
Ring::new(pts).make_polygons(THICKNESS)
} else {
PolyLine::new(pts).make_polygons(THICKNESS)
};
let mut osm_bldg = None;
if dataset_name == "parcels" {
if let Some(bldg) = attribs.get("osm_bldg") {
for b in app.primary.map.all_buildings() {
if b.osm_way_id.to_string() == bldg.to_string() {
osm_bldg = Some(b.id);
break;
}
}
}
}
Object {
polygon,
attribs,
osm_bldg,
}
}
fn make_analysis(app: &App, objects: &Vec<Object>, analysis: &str) -> GeomBatch {
let mut batch = GeomBatch::new();
match analysis {
"None" => {}
"parcels without buildings" => {
for obj in objects {
if obj.osm_bldg.is_none() {
batch.push(Color::BLUE, obj.polygon.clone());
}
}
}
"parcels with multiple buildings" => {
let mut seen = HashSet::new();
for obj in objects {
if let Some(b) = obj.osm_bldg {
if seen.contains(&b) {
batch.push(Color::BLUE, app.primary.map.get_b(b).polygon.clone());
} else {
seen.insert(b);
}
}
}
}
_ => unreachable!(),
}
batch
}

View File

@ -21,7 +21,7 @@ pub fn import_data() {
}
fn import_trips(
parcels_lookup: HashMap<usize, Endpoint>,
parcels: HashMap<usize, Endpoint>,
trips_path: &str,
timer: &mut Timer,
) -> Vec<OrigTrip> {
@ -34,8 +34,8 @@ fn import_trips(
total_records += 1;
let rec: RawTrip = rec.unwrap();
let from = parcels_lookup[&(rec.opcl as usize)].clone();
let to = parcels_lookup[&(rec.dpcl as usize)].clone();
let from = parcels[&(rec.opcl as usize)].clone();
let to = parcels[&(rec.dpcl as usize)].clone();
// If both are None, then skip -- the trip doesn't start or end within huge_seattle.
// If both are the same building, also skip -- that's a redundant trip.
@ -153,17 +153,6 @@ fn import_parcels(path: &str, timer: &mut Timer) -> HashMap<usize, Endpoint> {
let gps = LonLat::new(x, y);
let pt = Pt2D::forcibly_from_gps(gps, bounds);
let osm_building = if bounds.contains(gps) {
if boundary.contains_pt(pt) {
let mut attributes = BTreeMap::new();
attributes.insert("id".to_string(), id.to_string());
attributes.insert("households".to_string(), num_households.to_string());
attributes.insert("parking".to_string(), offstreet_parking_spaces.to_string());
shapes.push(ExtraShape {
points: vec![gps],
attributes,
});
}
closest_bldg
.closest_pt(pt, Distance::meters(30.0))
.map(|(b, _)| b)
@ -178,6 +167,20 @@ fn import_parcels(path: &str, timer: &mut Timer) -> HashMap<usize, Endpoint> {
parcel_id: id,
},
);
if boundary.contains_pt(pt) {
let mut attributes = BTreeMap::new();
attributes.insert("id".to_string(), id.to_string());
attributes.insert("households".to_string(), num_households.to_string());
attributes.insert("parking".to_string(), offstreet_parking_spaces.to_string());
if let Some(b) = osm_building {
attributes.insert("osm_bldg".to_string(), b.to_string());
}
shapes.push(ExtraShape {
points: vec![gps],
attributes,
});
}
}
timer.note(format!("{} parcels", prettyprint_usize(result.len())));