From ea49fe63bac6c852f13aba55e6539086ef62866f Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Tue, 28 May 2019 12:37:31 -0700 Subject: [PATCH] match PSRC trips to OSM building IDs up-front, trimming down filesize and speeding up clipping later --- editor/src/mission/mod.rs | 65 +++++++++------------------ editor/src/render/map.rs | 4 +- geom/src/find_closest.rs | 9 ++-- map_model/src/make/sidewalk_finder.rs | 2 +- popdat/Cargo.toml | 1 + popdat/src/main.rs | 7 +-- popdat/src/psrc.rs | 46 ++++++++++++++----- 7 files changed, 63 insertions(+), 71 deletions(-) diff --git a/editor/src/mission/mod.rs b/editor/src/mission/mod.rs index c136894f93..86bc44b660 100644 --- a/editor/src/mission/mod.rs +++ b/editor/src/mission/mod.rs @@ -5,15 +5,15 @@ mod neighborhood; mod scenario; use crate::game::{GameState, Mode}; -use crate::helpers::ID; use crate::render::DrawOptions; use crate::sandbox::SandboxMode; use crate::ui::ShowEverything; use crate::ui::UI; -use abstutil::Timer; +use abstutil::{skip_fail, Timer}; 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 std::collections::HashMap; pub struct MissionEditMode { state: State, @@ -202,60 +202,35 @@ pub fn clip_trips( max_results: usize, timer: &mut Timer, ) -> Vec { + 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 bounds = ui.primary.map.get_gps_bounds(); timer.start_iter("clip trips", popdat.trips.len()); for trip in &popdat.trips { timer.next(); if results.len() == max_results { 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; - } - results.push(Trip { - from, - to, - depart_at: trip.depart_at, - purpose: trip.purpose, - mode: trip.mode, - trip_time: trip.trip_time, - trip_dist: trip.trip_dist, - route: None, - }); - } + 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 { + from, + to, + depart_at: trip.depart_at, + purpose: trip.purpose, + mode: trip.mode, + trip_time: trip.trip_time, + trip_dist: trip.trip_dist, + route: None, + }); } results } -fn find_building_containing(pt: Pt2D, ui: &UI) -> Option { - 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) { use popdat::psrc::Mode; use sim::{DrivingGoal, Scenario, SidewalkSpot, TripSpec}; diff --git a/editor/src/render/map.rs b/editor/src/render/map.rs index aa26edbaa2..100f01d80a 100644 --- a/editor/src/render/map.rs +++ b/editor/src/render/map.rs @@ -143,11 +143,11 @@ impl DrawMap { for r in map.all_roads().iter() { closest.add( r.id.forwards(), - &r.center_pts.shift_right(LANE_THICKNESS).get(timer), + r.center_pts.shift_right(LANE_THICKNESS).get(timer).points(), ); closest.add( r.id.backwards(), - &r.center_pts.shift_left(LANE_THICKNESS).get(timer), + r.center_pts.shift_left(LANE_THICKNESS).get(timer).points(), ); } diff --git a/geom/src/find_closest.rs b/geom/src/find_closest.rs index c4db3682a7..1235110ac1 100644 --- a/geom/src/find_closest.rs +++ b/geom/src/find_closest.rs @@ -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::QuadTree; use geo; @@ -22,11 +22,10 @@ where } } - pub fn add(&mut self, key: K, pts: &PolyLine) { - self.geometries - .insert(key.clone(), pts_to_line_string(&pts.points())); + pub fn add(&mut self, key: K, pts: &Vec) { + self.geometries.insert(key.clone(), pts_to_line_string(pts)); 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, gps_bounds: &GPSBounds) { diff --git a/map_model/src/make/sidewalk_finder.rs b/map_model/src/make/sidewalk_finder.rs index 03aa9d37f9..bb5933c23d 100644 --- a/map_model/src/make/sidewalk_finder.rs +++ b/map_model/src/make/sidewalk_finder.rs @@ -21,7 +21,7 @@ pub fn find_sidewalk_points( for l in lanes { timer.next(); if l.is_sidewalk() { - closest.add(l.id, &l.lane_center_pts); + closest.add(l.id, l.lane_center_pts.points()); } } diff --git a/popdat/Cargo.toml b/popdat/Cargo.toml index 199cfec3b1..1022c615be 100644 --- a/popdat/Cargo.toml +++ b/popdat/Cargo.toml @@ -10,5 +10,6 @@ csv = "1.0.1" failure = "0.1.2" geom = { path = "../geom" } kml = { path = "../kml" } +map_model = { path = "../map_model" } serde = "1.0.89" serde_derive = "1.0.89" diff --git a/popdat/src/main.rs b/popdat/src/main.rs index 710e7a81ef..8032fe9150 100644 --- a/popdat/src/main.rs +++ b/popdat/src/main.rs @@ -5,14 +5,9 @@ fn main() { // TODO Productionize this. // https://file.ac/cLdO7Hp_OB0/ has trips_2014.csv. https://file.ac/Xdjmi8lb2dA/ has the 2014 // 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( + "/home/dabreegster/Downloads/psrc/2014/landuse/parcels_urbansim.txt", "/home/dabreegster/Downloads/psrc/trips_2014.csv", - parcels, &mut timer, ) .unwrap(); diff --git a/popdat/src/psrc.rs b/popdat/src/psrc.rs index 9c148e7023..27019ff4f6 100644 --- a/popdat/src/psrc.rs +++ b/popdat/src/psrc.rs @@ -1,5 +1,6 @@ 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 std::collections::HashMap; use std::fs::File; @@ -7,8 +8,9 @@ use std::io::{BufRead, BufReader, BufWriter, Write}; #[derive(Serialize, Deserialize)] pub struct Trip { - pub from: LonLat, - pub to: LonLat, + // OSM building IDs + pub from: i64, + pub to: i64, // Relative to midnight pub depart_at: Duration, pub mode: Mode, @@ -41,12 +43,14 @@ pub enum Purpose { } pub fn import_trips( - path: &str, - parcels: HashMap, + parcels_path: &str, + trips_path: &str, timer: &mut Timer, ) -> Result, failure::Error> { + let parcels = import_parcels(parcels_path, timer)?; + 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() { let rec = rec?; @@ -55,6 +59,14 @@ pub fn import_trips( // dpcl 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 let depart_at = Duration::minutes(rec[4].trim_end_matches(".0").parse::()?); @@ -84,10 +96,16 @@ pub fn import_trips( } // TODO Do we also need the zone ID, or is parcel ID globally unique? -pub fn import_parcels( - path: &str, - timer: &mut Timer, -) -> Result, failure::Error> { +fn import_parcels(path: &str, timer: &mut Timer) -> Result, failure::Error> { + let map: Map = abstutil::read_binary("../data/maps/huge_seattle.abst", timer)?; + + // 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 = 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 parcel_ids = Vec::new(); @@ -128,7 +146,7 @@ pub fn import_parcels( 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 mut result = HashMap::new(); timer.start_iter("read cs2cs output", parcel_ids.len()); @@ -140,7 +158,11 @@ pub fn import_parcels( let lat: f64 = pieces[1].parse()?; let pt = LonLat::new(lon, lat); 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)