match PSRC trips to OSM building IDs up-front, trimming down filesize and speeding up clipping later

This commit is contained in:
Dustin Carlino 2019-05-28 12:37:31 -07:00
parent 28b2734d59
commit ea49fe63ba
7 changed files with 63 additions and 71 deletions

View File

@ -5,15 +5,15 @@ mod neighborhood;
mod scenario; mod scenario;
use crate::game::{GameState, Mode}; use crate::game::{GameState, Mode};
use crate::helpers::ID;
use crate::render::DrawOptions; use crate::render::DrawOptions;
use crate::sandbox::SandboxMode; use crate::sandbox::SandboxMode;
use crate::ui::ShowEverything; use crate::ui::ShowEverything;
use crate::ui::UI; use crate::ui::UI;
use abstutil::Timer; use abstutil::{skip_fail, Timer};
use ezgui::{EventCtx, EventLoopMode, GfxCtx, Key, ModalMenu, Wizard}; use ezgui::{EventCtx, EventLoopMode, GfxCtx, Key, ModalMenu, Wizard};
use geom::{Circle, Distance, Duration, PolyLine, Pt2D}; use geom::{Distance, Duration, PolyLine};
use map_model::{BuildingID, Map, PathRequest, Position}; use map_model::{BuildingID, Map, PathRequest, Position};
use std::collections::HashMap;
pub struct MissionEditMode { pub struct MissionEditMode {
state: State, state: State,
@ -202,30 +202,21 @@ pub fn clip_trips(
max_results: usize, max_results: usize,
timer: &mut Timer, timer: &mut Timer,
) -> Vec<Trip> { ) -> Vec<Trip> {
let mut osm_id_to_bldg = HashMap::new();
for b in ui.primary.map.all_buildings() {
osm_id_to_bldg.insert(b.osm_way_id, b.id);
}
let mut results = Vec::new(); let mut results = Vec::new();
let bounds = ui.primary.map.get_gps_bounds();
timer.start_iter("clip trips", popdat.trips.len()); timer.start_iter("clip trips", popdat.trips.len());
for trip in &popdat.trips { for trip in &popdat.trips {
timer.next(); timer.next();
if results.len() == max_results { if results.len() == max_results {
continue; continue;
} }
if !bounds.contains(trip.from) || !bounds.contains(trip.to) {
continue;
}
let from = find_building_containing(Pt2D::from_gps(trip.from, bounds).unwrap(), ui);
let to = find_building_containing(Pt2D::from_gps(trip.to, bounds).unwrap(), ui);
if from.is_some() && to.is_some() {
let from = from.unwrap();
let to = to.unwrap();
if from == to {
timer.warn(format!(
"Trip leaving at {} goes from {} to {}, both matching {}",
trip.depart_at, trip.from, trip.to, from
));
continue;
}
let from = *skip_fail!(osm_id_to_bldg.get(&trip.from));
let to = *skip_fail!(osm_id_to_bldg.get(&trip.to));
results.push(Trip { results.push(Trip {
from, from,
to, to,
@ -237,25 +228,9 @@ pub fn clip_trips(
route: None, route: None,
}); });
} }
}
results results
} }
fn find_building_containing(pt: Pt2D, ui: &UI) -> Option<BuildingID> {
for obj in ui
.primary
.draw_map
.get_matching_objects(Circle::new(pt, Distance::meters(3.0)).get_bounds())
{
if let ID::Building(b) = obj {
if ui.primary.map.get_b(b).polygon.contains_pt(pt) {
return Some(b);
}
}
}
None
}
fn instantiate_trips(ctx: &mut EventCtx, ui: &mut UI) { fn instantiate_trips(ctx: &mut EventCtx, ui: &mut UI) {
use popdat::psrc::Mode; use popdat::psrc::Mode;
use sim::{DrivingGoal, Scenario, SidewalkSpot, TripSpec}; use sim::{DrivingGoal, Scenario, SidewalkSpot, TripSpec};

View File

@ -143,11 +143,11 @@ impl DrawMap {
for r in map.all_roads().iter() { for r in map.all_roads().iter() {
closest.add( closest.add(
r.id.forwards(), r.id.forwards(),
&r.center_pts.shift_right(LANE_THICKNESS).get(timer), r.center_pts.shift_right(LANE_THICKNESS).get(timer).points(),
); );
closest.add( closest.add(
r.id.backwards(), r.id.backwards(),
&r.center_pts.shift_left(LANE_THICKNESS).get(timer), r.center_pts.shift_left(LANE_THICKNESS).get(timer).points(),
); );
} }

View File

@ -1,4 +1,4 @@
use crate::{Bounds, Distance, GPSBounds, LonLat, PolyLine, Pt2D}; use crate::{Bounds, Distance, GPSBounds, LonLat, Pt2D};
use aabb_quadtree::geom::{Point, Rect}; use aabb_quadtree::geom::{Point, Rect};
use aabb_quadtree::QuadTree; use aabb_quadtree::QuadTree;
use geo; use geo;
@ -22,11 +22,10 @@ where
} }
} }
pub fn add(&mut self, key: K, pts: &PolyLine) { pub fn add(&mut self, key: K, pts: &Vec<Pt2D>) {
self.geometries self.geometries.insert(key.clone(), pts_to_line_string(pts));
.insert(key.clone(), pts_to_line_string(&pts.points()));
self.quadtree self.quadtree
.insert_with_box(key, pts.get_bounds().as_bbox()); .insert_with_box(key, Bounds::from(pts).as_bbox());
} }
pub fn add_gps(&mut self, key: K, raw_pts: &Vec<LonLat>, gps_bounds: &GPSBounds) { pub fn add_gps(&mut self, key: K, raw_pts: &Vec<LonLat>, gps_bounds: &GPSBounds) {

View File

@ -21,7 +21,7 @@ pub fn find_sidewalk_points(
for l in lanes { for l in lanes {
timer.next(); timer.next();
if l.is_sidewalk() { if l.is_sidewalk() {
closest.add(l.id, &l.lane_center_pts); closest.add(l.id, l.lane_center_pts.points());
} }
} }

View File

@ -10,5 +10,6 @@ csv = "1.0.1"
failure = "0.1.2" failure = "0.1.2"
geom = { path = "../geom" } geom = { path = "../geom" }
kml = { path = "../kml" } kml = { path = "../kml" }
map_model = { path = "../map_model" }
serde = "1.0.89" serde = "1.0.89"
serde_derive = "1.0.89" serde_derive = "1.0.89"

View File

@ -5,14 +5,9 @@ fn main() {
// TODO Productionize this. // TODO Productionize this.
// https://file.ac/cLdO7Hp_OB0/ has trips_2014.csv. https://file.ac/Xdjmi8lb2dA/ has the 2014 // https://file.ac/cLdO7Hp_OB0/ has trips_2014.csv. https://file.ac/Xdjmi8lb2dA/ has the 2014
// inputs. // inputs.
let parcels = popdat::psrc::import_parcels(
"/home/dabreegster/Downloads/psrc/2014/landuse/parcels_urbansim.txt",
&mut timer,
)
.unwrap();
popdat.trips = popdat::psrc::import_trips( popdat.trips = popdat::psrc::import_trips(
"/home/dabreegster/Downloads/psrc/2014/landuse/parcels_urbansim.txt",
"/home/dabreegster/Downloads/psrc/trips_2014.csv", "/home/dabreegster/Downloads/psrc/trips_2014.csv",
parcels,
&mut timer, &mut timer,
) )
.unwrap(); .unwrap();

View File

@ -1,5 +1,6 @@
use abstutil::{prettyprint_usize, skip_fail, FileWithProgress, Timer}; use abstutil::{prettyprint_usize, skip_fail, FileWithProgress, Timer};
use geom::{Distance, Duration, GPSBounds, LonLat}; use geom::{Distance, Duration, FindClosest, LonLat, Pt2D};
use map_model::Map;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::File; use std::fs::File;
@ -7,8 +8,9 @@ use std::io::{BufRead, BufReader, BufWriter, Write};
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Trip { pub struct Trip {
pub from: LonLat, // OSM building IDs
pub to: LonLat, pub from: i64,
pub to: i64,
// Relative to midnight // Relative to midnight
pub depart_at: Duration, pub depart_at: Duration,
pub mode: Mode, pub mode: Mode,
@ -41,12 +43,14 @@ pub enum Purpose {
} }
pub fn import_trips( pub fn import_trips(
path: &str, parcels_path: &str,
parcels: HashMap<String, LonLat>, trips_path: &str,
timer: &mut Timer, timer: &mut Timer,
) -> Result<Vec<Trip>, failure::Error> { ) -> Result<Vec<Trip>, failure::Error> {
let parcels = import_parcels(parcels_path, timer)?;
let mut trips = Vec::new(); let mut trips = Vec::new();
let (reader, done) = FileWithProgress::new(path)?; let (reader, done) = FileWithProgress::new(trips_path)?;
for rec in csv::Reader::from_reader(reader).records() { for rec in csv::Reader::from_reader(reader).records() {
let rec = rec?; let rec = rec?;
@ -55,6 +59,14 @@ pub fn import_trips(
// dpcl // dpcl
let to = *skip_fail!(parcels.get(rec[6].trim_end_matches(".0"))); let to = *skip_fail!(parcels.get(rec[6].trim_end_matches(".0")));
if from == to {
timer.warn(format!(
"Skipping trip from parcel {} to {}; both match OSM building {}",
&rec[15], &rec[6], from
));
continue;
}
// deptm // deptm
let depart_at = Duration::minutes(rec[4].trim_end_matches(".0").parse::<usize>()?); let depart_at = Duration::minutes(rec[4].trim_end_matches(".0").parse::<usize>()?);
@ -84,10 +96,16 @@ pub fn import_trips(
} }
// TODO Do we also need the zone ID, or is parcel ID globally unique? // TODO Do we also need the zone ID, or is parcel ID globally unique?
pub fn import_parcels( fn import_parcels(path: &str, timer: &mut Timer) -> Result<HashMap<String, i64>, failure::Error> {
path: &str, let map: Map = abstutil::read_binary("../data/maps/huge_seattle.abst", timer)?;
timer: &mut Timer,
) -> Result<HashMap<String, LonLat>, failure::Error> { // TODO I really just want to do polygon containment with a quadtree. FindClosest only does
// line-string stuff right now, which'll be weird for the last->first pt line and stuff.
let mut closest_bldg: FindClosest<i64> = FindClosest::new(map.get_bounds());
for b in map.all_buildings() {
closest_bldg.add(b.osm_way_id, b.polygon.points());
}
let mut coords = BufWriter::new(File::create("/tmp/parcels")?); let mut coords = BufWriter::new(File::create("/tmp/parcels")?);
let mut parcel_ids = Vec::new(); let mut parcel_ids = Vec::new();
@ -128,7 +146,7 @@ pub fn import_parcels(
prettyprint_usize(parcel_ids.len()) prettyprint_usize(parcel_ids.len())
)); ));
let bounds = GPSBounds::seattle_bounds(); let bounds = map.get_gps_bounds();
let reader = BufReader::new(output.stdout.as_slice()); let reader = BufReader::new(output.stdout.as_slice());
let mut result = HashMap::new(); let mut result = HashMap::new();
timer.start_iter("read cs2cs output", parcel_ids.len()); timer.start_iter("read cs2cs output", parcel_ids.len());
@ -140,7 +158,11 @@ pub fn import_parcels(
let lat: f64 = pieces[1].parse()?; let lat: f64 = pieces[1].parse()?;
let pt = LonLat::new(lon, lat); let pt = LonLat::new(lon, lat);
if bounds.contains(pt) { if bounds.contains(pt) {
result.insert(id, pt); if let Some((bldg, _)) =
closest_bldg.closest_pt(Pt2D::from_gps(pt, bounds).unwrap(), Distance::meters(30.0))
{
result.insert(id, bldg);
}
} }
} }
Ok(result) Ok(result)