count the buildings and parking spots in each tract

This commit is contained in:
Dustin Carlino 2019-05-20 09:53:37 -07:00
parent 083b96f0e7
commit 6f2b907927
2 changed files with 59 additions and 29 deletions

View File

@ -1,15 +1,16 @@
use crate::common::CommonState; use crate::common::CommonState;
use crate::helpers::rotating_color; use crate::helpers::{rotating_color, ID};
use crate::ui::UI; use crate::ui::UI;
use abstutil::Timer; use abstutil::{prettyprint_usize, Timer};
use ezgui::{Color, EventCtx, GfxCtx, Key, ModalMenu, Text}; use ezgui::{Color, EventCtx, GfxCtx, Key, ModalMenu, Text};
use geom::{GPSBounds, Polygon}; use geom::Polygon;
use popdat::PopDat; use popdat::PopDat;
use std::collections::BTreeMap;
pub struct DataVisualizer { pub struct DataVisualizer {
menu: ModalMenu, menu: ModalMenu,
popdat: PopDat, popdat: PopDat,
tracts: Vec<Tract>, tracts: BTreeMap<String, Tract>,
// TODO Urgh. 0, 1, or 2. // TODO Urgh. 0, 1, or 2.
current_dataset: usize, current_dataset: usize,
@ -17,9 +18,11 @@ pub struct DataVisualizer {
} }
struct Tract { struct Tract {
name: String,
polygon: Polygon, polygon: Polygon,
color: Color, color: Color,
num_bldgs: usize,
num_parking_spots: usize,
} }
impl DataVisualizer { impl DataVisualizer {
@ -39,7 +42,7 @@ impl DataVisualizer {
], ],
ctx, ctx,
), ),
tracts: clip(&popdat, &ui.primary.map.get_gps_bounds()), tracts: clip(&popdat, ui, &mut timer),
popdat, popdat,
current_dataset: 0, current_dataset: 0,
current_tract: None, current_tract: None,
@ -52,6 +55,12 @@ impl DataVisualizer {
if let Some(ref name) = self.current_tract { if let Some(ref name) = self.current_tract {
txt.add_line("Census ".to_string()); txt.add_line("Census ".to_string());
txt.append(name.clone(), Some(ui.cs.get("OSD name color"))); txt.append(name.clone(), Some(ui.cs.get("OSD name color")));
let tract = &self.tracts[name];
txt.add_line(format!("{} buildings", prettyprint_usize(tract.num_bldgs)));
txt.add_line(format!(
"{} parking spots ",
prettyprint_usize(tract.num_parking_spots)
));
} }
self.menu.handle_event(ctx, Some(txt)); self.menu.handle_event(ctx, Some(txt));
ctx.canvas.handle_event(ctx.input); ctx.canvas.handle_event(ctx.input);
@ -70,9 +79,9 @@ impl DataVisualizer {
if !ctx.canvas.is_dragging() && ctx.input.get_moved_mouse().is_some() { if !ctx.canvas.is_dragging() && ctx.input.get_moved_mouse().is_some() {
if let Some(pt) = ctx.canvas.get_cursor_in_map_space() { if let Some(pt) = ctx.canvas.get_cursor_in_map_space() {
self.current_tract = None; self.current_tract = None;
for tract in &self.tracts { for (name, tract) in &self.tracts {
if tract.polygon.contains_pt(pt) { if tract.polygon.contains_pt(pt) {
self.current_tract = Some(tract.name.clone()); self.current_tract = Some(name.clone());
break; break;
} }
} }
@ -83,8 +92,8 @@ impl DataVisualizer {
} }
pub fn draw(&self, g: &mut GfxCtx, ui: &UI) { pub fn draw(&self, g: &mut GfxCtx, ui: &UI) {
for tract in &self.tracts { for (name, tract) in &self.tracts {
let color = if Some(tract.name.clone()) == self.current_tract { let color = if Some(name.clone()) == self.current_tract {
ui.cs.get("selected") ui.cs.get("selected")
} else { } else {
tract.color tract.color
@ -104,18 +113,50 @@ impl DataVisualizer {
} }
} }
fn clip(popdat: &PopDat, bounds: &GPSBounds) -> Vec<Tract> { fn clip(popdat: &PopDat, ui: &UI, timer: &mut Timer) -> BTreeMap<String, Tract> {
// TODO Partial clipping could be neat, except it'd be confusing to interpret totals. // TODO Partial clipping could be neat, except it'd be confusing to interpret totals.
let mut results = Vec::new(); let mut results = BTreeMap::new();
timer.start_iter("clip tracts", popdat.tracts.len());
for (name, tract) in &popdat.tracts { for (name, tract) in &popdat.tracts {
if let Some(pts) = bounds.try_convert(&tract.pts) { timer.next();
if let Some(pts) = ui.primary.map.get_gps_bounds().try_convert(&tract.pts) {
// TODO We should actually make sure the polygon is completely contained within the // TODO We should actually make sure the polygon is completely contained within the
// map's boundary. // map's boundary.
results.push(Tract { let polygon = Polygon::new(&pts);
name: name.clone(),
polygon: Polygon::new(&pts), // TODO Don't just use the center...
color: rotating_color(results.len()), let mut num_bldgs = 0;
}); let mut num_parking_spots = 0;
for id in ui
.primary
.draw_map
.get_matching_objects(polygon.get_bounds())
{
match id {
ID::Building(b) => {
if polygon.contains_pt(ui.primary.map.get_b(b).polygon.center()) {
num_bldgs += 1;
}
}
ID::Lane(l) => {
let lane = ui.primary.map.get_l(l);
if lane.is_parking() && polygon.contains_pt(lane.lane_center_pts.middle()) {
num_parking_spots += lane.number_parking_spots();
}
}
_ => {}
}
}
results.insert(
name.clone(),
Tract {
polygon,
color: rotating_color(results.len()),
num_bldgs,
num_parking_spots,
},
);
} }
} }
println!( println!(

View File

@ -298,17 +298,6 @@ impl DrawMap {
&self.areas[id.0] &self.areas[id.0]
} }
#[allow(dead_code)]
pub fn get_matching_lanes(&self, bounds: Bounds) -> Vec<LaneID> {
let mut results: Vec<LaneID> = Vec::new();
for &(id, _, _) in &self.quadtree.query(bounds.as_bbox()) {
if let ID::Lane(id) = id {
results.push(*id);
}
}
results
}
// Unsorted, unexpanded, raw result. // Unsorted, unexpanded, raw result.
pub fn get_matching_objects(&self, bounds: Bounds) -> Vec<ID> { pub fn get_matching_objects(&self, bounds: Bounds) -> Vec<ID> {
let mut results: Vec<ID> = Vec::new(); let mut results: Vec<ID> = Vec::new();