mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 23:43:25 +03:00
match PSRC trips to OSM building IDs up-front, trimming down filesize and speeding up clipping later
This commit is contained in:
parent
28b2734d59
commit
ea49fe63ba
@ -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};
|
||||||
|
@ -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(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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"
|
||||||
|
@ -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();
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user