some investigation into psrc tour segments again

This commit is contained in:
Dustin Carlino 2020-03-02 15:50:32 -08:00
parent 03d4b4d717
commit 7407e5778f
4 changed files with 70 additions and 31 deletions

View File

@ -30,19 +30,6 @@ use std::fmt::Write;
const PROGRESS_FREQUENCY_SECONDS: f64 = 0.2; const PROGRESS_FREQUENCY_SECONDS: f64 = 0.2;
// Thanks https://stackoverflow.com/a/49806368
#[macro_export]
macro_rules! skip_fail {
($res:expr) => {
match $res {
Some(val) => val,
None => {
continue;
}
}
};
}
pub fn clamp(x: f64, min: f64, max: f64) -> f64 { pub fn clamp(x: f64, min: f64, max: f64) -> f64 {
if x < min { if x < min {
min min

View File

@ -165,6 +165,8 @@ d15fe87c4174463e84b9a440162129ed data/system/assets/tools/undo.svg
512477d8556de48901712165e905782f data/system/assets/tools/locate.svg 512477d8556de48901712165e905782f data/system/assets/tools/locate.svg
5f3912d972b47419295ded39c52e647d data/system/assets/tools/next.svg 5f3912d972b47419295ded39c52e647d data/system/assets/tools/next.svg
e469148085a2046afd447d31a54d15ab data/system/assets/tools/search.svg e469148085a2046afd447d31a54d15ab data/system/assets/tools/search.svg
b9e963ca693bb9329b065454047742e7 data/system/assets/tools/start_pos.svg
c8433d44c82e2471ee777c90d4bcd449 data/system/assets/tools/goal_pos.svg
4a5d6456d7c3d9ad94fc1a9e456d6f06 data/system/assets/tools/prev.svg 4a5d6456d7c3d9ad94fc1a9e456d6f06 data/system/assets/tools/prev.svg
fe358c0fdf48b65117f7c4970fa35d91 data/system/assets/tools/settings.svg fe358c0fdf48b65117f7c4970fa35d91 data/system/assets/tools/settings.svg
1e0135f13d0aea11650460d6a61b5463 data/system/assets/tools/edit.svg 1e0135f13d0aea11650460d6a61b5463 data/system/assets/tools/edit.svg

View File

@ -1,4 +1,4 @@
use abstutil::{prettyprint_usize, skip_fail, FileWithProgress, Timer}; use abstutil::{prettyprint_usize, FileWithProgress, Timer};
use geom::{Distance, Duration, FindClosest, LonLat, Pt2D, Time}; use geom::{Distance, Duration, FindClosest, LonLat, Pt2D, Time};
use map_model::Map; use map_model::Map;
use serde_derive::{Deserialize, Serialize}; use serde_derive::{Deserialize, Serialize};
@ -63,30 +63,51 @@ pub fn import_trips(
trips_path: &str, trips_path: &str,
timer: &mut Timer, timer: &mut Timer,
) -> Result<(Vec<Trip>, BTreeMap<i64, Parcel>), failure::Error> { ) -> Result<(Vec<Trip>, BTreeMap<i64, Parcel>), failure::Error> {
let (parcels, metadata) = import_parcels(parcels_path, timer)?; let (parcels, metadata, oob_parcels) = import_parcels(parcels_path, timer)?;
let mut trips = Vec::new(); let mut trips = Vec::new();
let (reader, done) = FileWithProgress::new(trips_path)?; let (reader, done) = FileWithProgress::new(trips_path)?;
let mut total_records = 0;
for rec in csv::Reader::from_reader(reader).deserialize() { for rec in csv::Reader::from_reader(reader).deserialize() {
total_records += 1;
let rec: RawTrip = rec?; let rec: RawTrip = rec?;
let from = skip_fail!(parcels.get(&(rec.opcl as usize))).clone(); let from = if let Some(f) = parcels.get(&(rec.opcl as usize)) {
let to = skip_fail!(parcels.get(&(rec.dpcl as usize))).clone(); f.clone()
} else {
if false {
println!(
"skipping missing from {}",
oob_parcels[&(rec.opcl as usize)]
);
}
continue;
};
let to = if let Some(t) = parcels.get(&(rec.dpcl as usize)) {
t.clone()
} else {
continue;
};
if from.osm_building == to.osm_building { if from.osm_building == to.osm_building {
// TODO Plumb along pass-through trips later // TODO Plumb along pass-through trips later
if from.osm_building.is_some() { if from.osm_building.is_some() {
timer.warn(format!( /*timer.warn(format!(
"Skipping trip from parcel {} to {}; both match OSM building {:?}", "Skipping trip from parcel {} to {}; both match OSM building {:?}",
rec.opcl, rec.dpcl, from.osm_building rec.opcl, rec.dpcl, from.osm_building
)); ));*/
} }
continue; continue;
} }
let depart_at = Time::START_OF_DAY + Duration::minutes(rec.deptm as usize); let depart_at = Time::START_OF_DAY + Duration::minutes(rec.deptm as usize);
let mode = skip_fail!(get_mode(&rec.mode)); let mode = if let Some(m) = get_mode(&rec.mode) {
m
} else {
continue;
};
let purpose = (get_purpose(&rec.opurp), get_purpose(&rec.dpurp)); let purpose = (get_purpose(&rec.opurp), get_purpose(&rec.dpurp));
@ -110,7 +131,11 @@ pub fn import_trips(
} }
done(timer); done(timer);
timer.note(format!("{} trips total", prettyprint_usize(trips.len()))); timer.note(format!(
"{} trips total. {} records filtered out",
prettyprint_usize(trips.len()),
prettyprint_usize(total_records - trips.len())
));
trips.sort_by_key(|t| t.depart_at); trips.sort_by_key(|t| t.depart_at);
@ -118,11 +143,19 @@ 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?
// Returns (parcel ID -> Endpoint) and (OSM building ID -> metadata) // Returns (parcel ID -> Endpoint), (OSM building ID -> metadata), and (parcel ID -> LonLat) of
// filtered parcels
fn import_parcels( fn import_parcels(
path: &str, path: &str,
timer: &mut Timer, timer: &mut Timer,
) -> Result<(HashMap<usize, Endpoint>, BTreeMap<i64, Parcel>), failure::Error> { ) -> Result<
(
HashMap<usize, Endpoint>,
BTreeMap<i64, Parcel>,
HashMap<usize, LonLat>,
),
failure::Error,
> {
let map = Map::new(abstutil::path_map("huge_seattle"), false, timer); let map = Map::new(abstutil::path_map("huge_seattle"), false, timer);
// TODO I really just want to do polygon containment with a quadtree. FindClosest only does // TODO I really just want to do polygon containment with a quadtree. FindClosest only does
@ -167,9 +200,9 @@ fn import_parcels(
// cs2cs +init=esri:102748 +to +init=epsg:4326 -f '%.5f' foo // cs2cs +init=esri:102748 +to +init=epsg:4326 -f '%.5f' foo
let output = std::process::Command::new("cs2cs") let output = std::process::Command::new("cs2cs")
.args(vec![ .args(vec![
"esri:102748", "+init=esri:102748",
"+to", "+to",
"epsg:4326", "+init=epsg:4326",
"-f", "-f",
"%.5f", "%.5f",
"/tmp/parcels", "/tmp/parcels",
@ -185,6 +218,8 @@ fn import_parcels(
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();
let mut metadata = BTreeMap::new(); let mut metadata = BTreeMap::new();
let mut oob = HashMap::new();
let orig_parcels = parcel_metadata.len();
timer.start_iter("read cs2cs output", parcel_metadata.len()); timer.start_iter("read cs2cs output", parcel_metadata.len());
for (line, (id, num_households, num_employees, offstreet_parking_spaces)) in for (line, (id, num_households, num_employees, offstreet_parking_spaces)) in
reader.lines().zip(parcel_metadata.into_iter()) reader.lines().zip(parcel_metadata.into_iter())
@ -197,7 +232,7 @@ fn import_parcels(
let pt = LonLat::new(lon, lat); let pt = LonLat::new(lon, lat);
if bounds.contains(pt) { if bounds.contains(pt) {
let osm_building = closest_bldg let osm_building = closest_bldg
.closest_pt(Pt2D::from_gps(pt, bounds).unwrap(), Distance::meters(30.0)) .closest_pt(Pt2D::forcibly_from_gps(pt, bounds), Distance::meters(30.0))
.map(|(b, _)| b); .map(|(b, _)| b);
if let Some(b) = osm_building { if let Some(b) = osm_building {
metadata.insert( metadata.insert(
@ -216,9 +251,16 @@ fn import_parcels(
osm_building, osm_building,
}, },
); );
} else {
oob.insert(id, pt);
} }
} }
Ok((result, metadata)) timer.note(format!(
"{} parcels. {} filtered out",
prettyprint_usize(result.len()),
prettyprint_usize(orig_parcels - result.len())
));
Ok((result, metadata, oob))
} }
// From https://github.com/psrc/soundcast/wiki/Outputs#trip-file-_triptsv, opurp and dpurp // From https://github.com/psrc/soundcast/wiki/Outputs#trip-file-_triptsv, opurp and dpurp
@ -244,9 +286,15 @@ fn get_mode(code: &str) -> Option<Mode> {
match code { match code {
"1.0" => Some(Mode::Walk), "1.0" => Some(Mode::Walk),
"2.0" => Some(Mode::Bike), "2.0" => Some(Mode::Bike),
"6.0" => Some(Mode::Transit),
"3.0" | "4.0" | "5.0" => Some(Mode::Drive), "3.0" | "4.0" | "5.0" => Some(Mode::Drive),
_ => None, "6.0" => Some(Mode::Transit),
// TODO Park-and-ride!
"7.0" => None,
// TODO School bus!
"8.0" => None,
// TODO Invalid code, what's this one mean?
"0.0" => None,
_ => panic!("Unknown mode {}", code),
} }
} }

View File

@ -291,6 +291,7 @@ pub fn trips_to_scenario(map: &Map, timer: &mut Timer) -> Scenario {
let mut individ_trips: Vec<SpawnTrip> = Vec::new(); let mut individ_trips: Vec<SpawnTrip> = Vec::new();
// TODO Don't clone trips for parallelize // TODO Don't clone trips for parallelize
let mut num_ppl = 0; let mut num_ppl = 0;
let orig_trips = trips.len();
for (person, list) in timer for (person, list) in timer
.parallelize("turn PSRC trips into SpawnTrips", trips.clone(), |trip| { .parallelize("turn PSRC trips into SpawnTrips", trips.clone(), |trip| {
trip.to_spawn_trip(map) trip.to_spawn_trip(map)
@ -314,9 +315,10 @@ pub fn trips_to_scenario(map: &Map, timer: &mut Timer) -> Scenario {
} }
} }
timer.note(format!( timer.note(format!(
"{} trips over {} people", "{} trips over {} people. {} trips filtered out",
prettyprint_usize(individ_trips.len()), prettyprint_usize(individ_trips.len()),
prettyprint_usize(num_ppl) prettyprint_usize(num_ppl),
prettyprint_usize(orig_trips - individ_trips.len())
)); ));
// How many parked cars do we need to spawn near each building? // How many parked cars do we need to spawn near each building?