diff --git a/data/MANIFEST.json b/data/MANIFEST.json index 8b08f304d5..c5ee1f786b 100644 --- a/data/MANIFEST.json +++ b/data/MANIFEST.json @@ -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", diff --git a/importer/src/soundcast/trips.rs b/importer/src/soundcast/trips.rs index 1521bd23ac..44ae5b8c02 100644 --- a/importer/src/soundcast/trips.rs +++ b/importer/src/soundcast/trips.rs @@ -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> = 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, diff --git a/map_editor/src/model.rs b/map_editor/src/model.rs index 60af30d772..76a4872e24 100644 --- a/map_editor/src/model.rs +++ b/map_editor/src/model.rs @@ -306,7 +306,9 @@ impl Model { } fn road_object(&self, id: OriginalRoad) -> Object { - 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), diff --git a/sim/src/lib.rs b/sim/src/lib.rs index 878139db2e..8865c8246e 100644 --- a/sim/src/lib.rs +++ b/sim/src/lib.rs @@ -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}; diff --git a/sim/src/make/external.rs b/sim/src/make/external.rs index 4d5e377cf4..d4911f37fa 100644 --- a/sim/src/make/external.rs +++ b/sim/src/make/external.rs @@ -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), + } + } +} diff --git a/sim/src/make/mod.rs b/sim/src/make/mod.rs index e1a9ec96f6..5c390e0b18 100644 --- a/sim/src/make/mod.rs +++ b/sim/src/make/mod.rs @@ -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;