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;
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<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 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<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) {
use popdat::psrc::Mode;
use sim::{DrivingGoal, Scenario, SidewalkSpot, TripSpec};

View File

@ -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(),
);
}

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::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<Pt2D>) {
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<LonLat>, gps_bounds: &GPSBounds) {

View File

@ -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());
}
}

View File

@ -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"

View File

@ -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();

View File

@ -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<String, LonLat>,
parcels_path: &str,
trips_path: &str,
timer: &mut Timer,
) -> Result<Vec<Trip>, 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::<usize>()?);
@ -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<HashMap<String, LonLat>, failure::Error> {
fn import_parcels(path: &str, timer: &mut Timer) -> Result<HashMap<String, i64>, 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<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 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)