When importing external scenario data for cyipt/actdev#32, snap border endpoints based on the allowed modes of each border. Without this, some driving trips snap to the cycleway next to a road.

212 cancelled trips (that immediately failed) down to 140.
This commit is contained in:
Dustin Carlino 2021-01-15 21:45:42 -08:00
parent 87f13a0d0c
commit 0a353f67f8
6 changed files with 108 additions and 74 deletions

View File

@ -661,14 +661,14 @@
"compressed_size_bytes": 9924393
},
"data/system/cambridge/scenarios/trumpington/baseline.bin": {
"checksum": "b26dd3395d63b23613be2f6e3f596e93",
"checksum": "5f560b95626e8635c00783d26d02be01",
"uncompressed_size_bytes": 71752,
"compressed_size_bytes": 18925
"compressed_size_bytes": 18975
},
"data/system/cambridge/scenarios/trumpington/go_dutch.bin": {
"checksum": "b9af9a890c908f641b5773c50c731cb6",
"checksum": "a943a280ba33228e5b88d41ca9e73939",
"uncompressed_size_bytes": 71752,
"compressed_size_bytes": 18772
"compressed_size_bytes": 18825
},
"data/system/detroit/maps/downtown.bin": {
"checksum": "a582e9865daf7055e676cda6dad1800f",

View File

@ -3,7 +3,7 @@ use std::collections::HashMap;
use abstutil::{prettyprint_usize, MultiMap, Parallelism, Timer};
use geom::LonLat;
use map_model::{osm, BuildingID, IntersectionID, Map, PathConstraints, PathRequest, PathStep};
use sim::{IndividTrip, OrigPersonID, PersonSpec, Scenario, TripEndpoint, TripMode};
use sim::{IndividTrip, MapBorders, OrigPersonID, PersonSpec, Scenario, TripEndpoint, TripMode};
use crate::soundcast::popdat::{Endpoint, OrigTrip, PopDat};
@ -134,49 +134,7 @@ fn clip_trips(map: &Map, popdat: &PopDat, huge_map: &Map, timer: &mut Timer) ->
for b in map.all_buildings() {
osm_id_to_bldg.insert(b.orig_id, b.id);
}
let bounds = map.get_gps_bounds();
let incoming_borders_walking: Vec<(IntersectionID, LonLat)> = map
.all_incoming_borders()
.into_iter()
.filter(|i| {
!i.get_outgoing_lanes(map, PathConstraints::Pedestrian)
.is_empty()
})
.map(|i| (i.id, i.polygon.center().to_gps(bounds)))
.collect();
let incoming_borders_driving: Vec<(IntersectionID, LonLat)> = map
.all_incoming_borders()
.into_iter()
.filter(|i| !i.get_outgoing_lanes(map, PathConstraints::Car).is_empty())
.map(|i| (i.id, i.polygon.center().to_gps(bounds)))
.collect();
let incoming_borders_biking: Vec<(IntersectionID, LonLat)> = map
.all_incoming_borders()
.into_iter()
.filter(|i| !i.get_outgoing_lanes(map, PathConstraints::Bike).is_empty())
.map(|i| (i.id, i.polygon.center().to_gps(bounds)))
.collect();
let outgoing_borders_walking: Vec<(IntersectionID, LonLat)> = map
.all_outgoing_borders()
.into_iter()
.filter(|i| {
!i.get_incoming_lanes(map, PathConstraints::Pedestrian)
.is_empty()
})
.map(|i| (i.id, i.polygon.center().to_gps(bounds)))
.collect();
let outgoing_borders_driving: Vec<(IntersectionID, LonLat)> = map
.all_outgoing_borders()
.into_iter()
.filter(|i| !i.get_incoming_lanes(map, PathConstraints::Car).is_empty())
.map(|i| (i.id, i.polygon.center().to_gps(bounds)))
.collect();
let outgoing_borders_biking: Vec<(IntersectionID, LonLat)> = map
.all_outgoing_borders()
.into_iter()
.filter(|i| !i.get_incoming_lanes(map, PathConstraints::Bike).is_empty())
.map(|i| (i.id, i.polygon.center().to_gps(bounds)))
.collect();
let borders = MapBorders::new(map);
let total_trips = popdat.trips.len();
let maybe_results: Vec<Option<Trip>> = timer.parallelize(
@ -189,13 +147,7 @@ fn clip_trips(map: &Map, popdat: &PopDat, huge_map: &Map, timer: &mut Timer) ->
&orig.to,
map,
&osm_id_to_bldg,
match orig.mode {
TripMode::Walk | TripMode::Transit => {
(&incoming_borders_walking, &outgoing_borders_walking)
}
TripMode::Drive => (&incoming_borders_driving, &outgoing_borders_driving),
TripMode::Bike => (&incoming_borders_biking, &outgoing_borders_biking),
},
borders.for_mode(orig.mode),
match orig.mode {
TripMode::Walk | TripMode::Transit => PathConstraints::Pedestrian,
TripMode::Drive => PathConstraints::Car,

View File

@ -306,7 +306,9 @@ impl Model {
}
fn road_object(&self, id: OriginalRoad) -> Object<ID> {
let (center, total_width) = self.map.roads[&id].get_geometry(id, &self.map.config);
let (center, total_width) = self.map.roads[&id]
.get_geometry(id, &self.map.config)
.unwrap();
Object::new(
ID::Road(id),
Color::grey(0.8),

View File

@ -36,7 +36,7 @@ pub(crate) use self::events::Event;
pub use self::events::{AlertLocation, TripPhaseType};
pub use self::make::{
fork_rng, BorderSpawnOverTime, ExternalPerson, ExternalTrip, ExternalTripEndpoint, IndividTrip,
PersonSpec, Scenario, ScenarioGenerator, ScenarioModifier, SimFlags, SpawnOverTime,
MapBorders, PersonSpec, Scenario, ScenarioGenerator, ScenarioModifier, SimFlags, SpawnOverTime,
TripEndpoint, TripPurpose,
};
pub(crate) use self::make::{StartTripArgs, TripSpec};

View File

@ -5,7 +5,7 @@ use anyhow::Result;
use serde::Deserialize;
use geom::{Distance, FindClosest, LonLat, Time};
use map_model::Map;
use map_model::{IntersectionID, Map, PathConstraints};
use crate::{IndividTrip, PersonSpec, TripEndpoint, TripMode, TripPurpose};
@ -39,14 +39,9 @@ impl ExternalPerson {
for b in map.all_buildings() {
closest.add(TripEndpoint::Bldg(b.id), b.polygon.points());
}
let mut borders = Vec::new();
for i in map.all_intersections() {
if i.is_border() {
borders.push((TripEndpoint::Border(i.id), i.polygon.center()));
}
}
let borders = MapBorders::new(map);
let lookup_pt = |endpt| match endpt {
let lookup_pt = |endpt, is_origin, mode| match endpt {
ExternalTripEndpoint::TripEndpoint(endpt) => Ok(endpt),
ExternalTripEndpoint::Position(gps) => {
let pt = gps.to_pt(map.get_gps_bounds());
@ -56,12 +51,15 @@ impl ExternalPerson {
None => Err(anyhow!("No building within 100m of {}", gps)),
}
} else {
Ok(borders
.iter()
.min_by_key(|(_, border)| border.fast_dist(pt))
.unwrap()
.0
.clone())
let (incoming, outgoing) = borders.for_mode(mode);
let candidates = if is_origin { incoming } else { outgoing };
Ok(TripEndpoint::Border(
candidates
.iter()
.min_by_key(|(_, border)| border.fast_dist(gps))
.ok_or_else(|| anyhow!("No border for {}", mode.ongoing_verb()))?
.0,
))
}
}
};
@ -70,7 +68,7 @@ impl ExternalPerson {
for person in input {
let mut spec = PersonSpec {
orig_id: None,
origin: lookup_pt(person.origin)?,
origin: lookup_pt(person.origin, true, person.trips[0].mode)?,
trips: Vec::new(),
};
for trip in person.trips {
@ -78,7 +76,9 @@ impl ExternalPerson {
spec.trips.push(IndividTrip::new(
trip.departure,
TripPurpose::Shopping,
lookup_pt(trip.destination)?,
// TODO Do we handle somebody going off-map via one one-way bridge, and
// re-entering using the other?
lookup_pt(trip.destination, false, trip.mode)?,
trip.mode,
));
}
@ -87,3 +87,83 @@ impl ExternalPerson {
Ok(results)
}
}
pub struct MapBorders {
pub incoming_walking: Vec<(IntersectionID, LonLat)>,
pub incoming_driving: Vec<(IntersectionID, LonLat)>,
pub incoming_biking: Vec<(IntersectionID, LonLat)>,
pub outgoing_walking: Vec<(IntersectionID, LonLat)>,
pub outgoing_driving: Vec<(IntersectionID, LonLat)>,
pub outgoing_biking: Vec<(IntersectionID, LonLat)>,
}
impl MapBorders {
pub fn new(map: &Map) -> MapBorders {
let bounds = map.get_gps_bounds();
let incoming_walking: Vec<(IntersectionID, LonLat)> = map
.all_incoming_borders()
.into_iter()
.filter(|i| {
!i.get_outgoing_lanes(map, PathConstraints::Pedestrian)
.is_empty()
})
.map(|i| (i.id, i.polygon.center().to_gps(bounds)))
.collect();
let incoming_driving: Vec<(IntersectionID, LonLat)> = map
.all_incoming_borders()
.into_iter()
.filter(|i| !i.get_outgoing_lanes(map, PathConstraints::Car).is_empty())
.map(|i| (i.id, i.polygon.center().to_gps(bounds)))
.collect();
let incoming_biking: Vec<(IntersectionID, LonLat)> = map
.all_incoming_borders()
.into_iter()
.filter(|i| !i.get_outgoing_lanes(map, PathConstraints::Bike).is_empty())
.map(|i| (i.id, i.polygon.center().to_gps(bounds)))
.collect();
let outgoing_walking: Vec<(IntersectionID, LonLat)> = map
.all_outgoing_borders()
.into_iter()
.filter(|i| {
!i.get_incoming_lanes(map, PathConstraints::Pedestrian)
.is_empty()
})
.map(|i| (i.id, i.polygon.center().to_gps(bounds)))
.collect();
let outgoing_driving: Vec<(IntersectionID, LonLat)> = map
.all_outgoing_borders()
.into_iter()
.filter(|i| !i.get_incoming_lanes(map, PathConstraints::Car).is_empty())
.map(|i| (i.id, i.polygon.center().to_gps(bounds)))
.collect();
let outgoing_biking: Vec<(IntersectionID, LonLat)> = map
.all_outgoing_borders()
.into_iter()
.filter(|i| !i.get_incoming_lanes(map, PathConstraints::Bike).is_empty())
.map(|i| (i.id, i.polygon.center().to_gps(bounds)))
.collect();
MapBorders {
incoming_walking,
incoming_driving,
incoming_biking,
outgoing_walking,
outgoing_driving,
outgoing_biking,
}
}
/// Returns the (incoming, outgoing) borders for the specififed mode.
pub fn for_mode(
&self,
mode: TripMode,
) -> (
&Vec<(IntersectionID, LonLat)>,
&Vec<(IntersectionID, LonLat)>,
) {
match mode {
TripMode::Walk | TripMode::Transit => (&self.incoming_walking, &self.outgoing_walking),
TripMode::Drive => (&self.incoming_driving, &self.outgoing_driving),
TripMode::Bike => (&self.incoming_biking, &self.outgoing_biking),
}
}
}

View File

@ -4,7 +4,7 @@
use rand::{RngCore, SeedableRng};
use rand_xorshift::XorShiftRng;
pub use self::external::{ExternalPerson, ExternalTrip, ExternalTripEndpoint};
pub use self::external::{ExternalPerson, ExternalTrip, ExternalTripEndpoint, MapBorders};
pub use self::generator::{BorderSpawnOverTime, ScenarioGenerator, SpawnOverTime};
pub use self::load::SimFlags;
pub use self::modifier::ScenarioModifier;