place parked cars close to buildings, in a stable way

This commit is contained in:
Dustin Carlino 2018-10-17 11:01:32 -07:00
parent 98914ce208
commit 24edfcd784
5 changed files with 115 additions and 51 deletions

View File

@ -370,6 +370,24 @@ impl Map {
.collect() .collect()
} }
// These come back sorted
pub fn get_next_roads(&self, from: RoadID) -> Vec<RoadID> {
let mut roads: BTreeSet<RoadID> = BTreeSet::new();
let (i1, i2) = self.get_r(from).get_endpoints(self);
for id in vec![i1, i2].into_iter() {
let i = self.get_i(id);
for l in i.incoming_lanes.iter().chain(i.outgoing_lanes.iter()) {
let r = self.get_l(*l).parent;
if r != from {
roads.insert(r);
}
}
}
roads.into_iter().collect()
}
pub fn get_parent(&self, id: LaneID) -> &Road { pub fn get_parent(&self, id: LaneID) -> &Road {
let l = self.get_l(id); let l = self.get_l(id);
self.get_r(l.parent) self.get_r(l.parent)
@ -436,4 +454,8 @@ impl Map {
stops.remove(&start); stops.remove(&start);
stops stops
} }
pub fn building_to_road(&self, id: BuildingID) -> &Road {
self.get_parent(self.get_b(id).front_path.sidewalk)
}
} }

View File

@ -3,7 +3,7 @@ use dimensioned::si;
use geom::PolyLine; use geom::PolyLine;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt; use std::fmt;
use {LaneID, LaneType}; use {IntersectionID, LaneID, LaneType, Map};
// TODO reconsider pub usize. maybe outside world shouldnt know. // TODO reconsider pub usize. maybe outside world shouldnt know.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
@ -83,6 +83,16 @@ impl Road {
lane == self.children_backwards[0].0 lane == self.children_backwards[0].0
} }
// In an arbitrary order
pub fn get_endpoints(&self, map: &Map) -> (IntersectionID, IntersectionID) {
let l = if !self.children_forwards.is_empty() {
map.get_l(self.children_forwards[0].0)
} else {
map.get_l(self.children_backwards[0].0)
};
(l.src_i, l.dst_i)
}
pub fn find_sidewalk(&self, parking_or_driving: LaneID) -> Result<LaneID, Error> { pub fn find_sidewalk(&self, parking_or_driving: LaneID) -> Result<LaneID, Error> {
self.get_siblings(parking_or_driving) self.get_siblings(parking_or_driving)
.iter() .iter()

View File

@ -144,9 +144,9 @@ impl Sim {
impl Sim { impl Sim {
pub fn small_spawn(&mut self, map: &Map) { pub fn small_spawn(&mut self, map: &Map) {
self.seed_parked_cars( self.seed_parked_cars(
map.all_lanes().iter().map(|l| l.id).collect(),
&map.all_buildings().iter().map(|b| b.id).collect(), &map.all_buildings().iter().map(|b| b.id).collect(),
0.5, 0.5,
map,
); );
self.seed_walking_trips(&map, 100); self.seed_walking_trips(&map, 100);
self.seed_driving_trips(&map, 100); self.seed_driving_trips(&map, 100);
@ -167,26 +167,21 @@ impl Sim {
pub fn big_spawn(&mut self, map: &Map) { pub fn big_spawn(&mut self, map: &Map) {
self.seed_parked_cars( self.seed_parked_cars(
map.all_lanes().iter().map(|l| l.id).collect(),
&map.all_buildings().iter().map(|b| b.id).collect(), &map.all_buildings().iter().map(|b| b.id).collect(),
0.95, 0.95,
map,
); );
self.seed_walking_trips(&map, 1000); self.seed_walking_trips(&map, 1000);
self.seed_driving_trips(&map, 1000); self.seed_driving_trips(&map, 1000);
} }
pub fn seed_parked_cars( pub fn seed_parked_cars(&mut self, owner_buildins: &Vec<BuildingID>, percent: f64, map: &Map) {
&mut self,
in_lanes: Vec<LaneID>,
owner_buildins: &Vec<BuildingID>,
percent: f64,
) {
self.spawner.seed_parked_cars( self.spawner.seed_parked_cars(
percent, percent,
in_lanes,
owner_buildins, owner_buildins,
&mut self.parking_state, &mut self.parking_state,
&mut self.rng, &mut self.rng,
map,
); );
} }

View File

@ -1,6 +1,6 @@
use abstutil; use abstutil;
use geom::{Polygon, Pt2D}; use geom::{Polygon, Pt2D};
use map_model::{BuildingID, LaneID, Map}; use map_model::{BuildingID, Map};
use rand::Rng; use rand::Rng;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use {CarID, Sim, Tick}; use {CarID, Sim, Tick};
@ -53,19 +53,6 @@ impl Neighborhood {
results results
} }
// TODO This should use quadtrees and/or not just match the first point of each lane.
fn find_matching_lanes(&self, map: &Map) -> Vec<LaneID> {
let poly = Polygon::new(&self.points);
let mut results: Vec<LaneID> = Vec::new();
for l in map.all_lanes() {
if poly.contains_pt(l.first_pt()) {
results.push(l.id);
}
}
results
}
pub fn save(&self) { pub fn save(&self) {
abstutil::save_object("neighborhoods", &self.map_name, &self.name, self); abstutil::save_object("neighborhoods", &self.map_name, &self.name, self);
} }
@ -95,9 +82,9 @@ impl Scenario {
for s in &self.seed_parked_cars { for s in &self.seed_parked_cars {
sim.seed_parked_cars( sim.seed_parked_cars(
neighborhoods[&s.neighborhood].find_matching_lanes(map),
&bldgs_per_neighborhood[&s.neighborhood], &bldgs_per_neighborhood[&s.neighborhood],
s.percent_buildings_with_car, s.percent_buildings_with_car,
map,
); );
} }

View File

@ -1,11 +1,11 @@
use abstutil::elapsed_seconds; use abstutil::elapsed_seconds;
use driving::{CreateCar, DrivingSimState}; use driving::{CreateCar, DrivingSimState};
use kinematics::Vehicle; use kinematics::Vehicle;
use map_model::{BuildingID, BusRoute, BusStopID, LaneID, Map, Pathfinder}; use map_model::{BuildingID, BusRoute, BusStopID, LaneID, LaneType, Map, Pathfinder, RoadID};
use parking::ParkingSimState; use parking::ParkingSimState;
use rand::{Rng, XorShiftRng}; use rand::{Rng, XorShiftRng};
use router::Router; use router::Router;
use std::collections::VecDeque; use std::collections::{HashMap, HashSet, VecDeque};
use std::time::Instant; use std::time::Instant;
use transit::TransitSimState; use transit::TransitSimState;
use trips::{TripLeg, TripManager}; use trips::{TripLeg, TripManager};
@ -210,41 +210,59 @@ impl Spawner {
pub fn seed_parked_cars( pub fn seed_parked_cars(
&mut self, &mut self,
percent_buildings_with_one_car: f64, percent_buildings_with_one_car: f64,
in_lanes: Vec<LaneID>,
owner_buildings: &Vec<BuildingID>, owner_buildings: &Vec<BuildingID>,
parking_sim: &mut ParkingSimState, parking_sim: &mut ParkingSimState,
base_rng: &mut XorShiftRng, base_rng: &mut XorShiftRng,
map: &Map,
) { ) {
assert!(percent_buildings_with_one_car >= 0.0 && percent_buildings_with_one_car <= 1.0); assert!(percent_buildings_with_one_car >= 0.0 && percent_buildings_with_one_car <= 1.0);
// TODO This is temporary. It's very unstable -- few extra or missing spots changes the // Track the available parking spots per road, only for the roads next to relevant
// shuffling dramatically. // buildings.
let mut all_open_spots: Vec<ParkingSpot> = Vec::new(); let mut total_spots = 0;
for l in in_lanes.into_iter() { let mut open_spots_per_road: HashMap<RoadID, Vec<ParkingSpot>> = HashMap::new();
all_open_spots.extend(parking_sim.get_free_spots(l)); for b in owner_buildings {
let r = map.building_to_road(*b);
if !open_spots_per_road.contains_key(&r.id) {
let mut spots: Vec<ParkingSpot> = Vec::new();
for (lane, lane_type) in r
.children_forwards
.iter()
.chain(r.children_backwards.iter())
{
if *lane_type == LaneType::Parking {
spots.extend(parking_sim.get_free_spots(*lane));
}
}
total_spots += spots.len();
fork_rng(base_rng).shuffle(&mut spots);
open_spots_per_road.insert(r.id, spots);
}
} }
fork_rng(base_rng).shuffle(&mut all_open_spots);
let total_spots = all_open_spots.len();
let mut new_cars = 0; let mut new_cars = 0;
for b in owner_buildings { for b in owner_buildings {
if base_rng.gen_bool(percent_buildings_with_one_car) { if base_rng.gen_bool(percent_buildings_with_one_car) {
// Pick a parking spot for this building. if let Some(spot) = find_spot_near_building(*b, &mut open_spots_per_road, map) {
// TODO Prefer spots closer to the building new_cars += 1;
let spot = all_open_spots let car = CarID(self.car_id_counter);
.pop() // TODO since spawning applies during the next step, lots of stuff breaks without
.expect("No available parking spots left to seed"); // this :(
new_cars += 1; parking_sim.add_parked_car(ParkedCar::new(
let car = CarID(self.car_id_counter); car,
// TODO since spawning applies during the next step, lots of stuff breaks without spot,
// this :( Vehicle::generate_typical_car(car, base_rng),
parking_sim.add_parked_car(ParkedCar::new( Some(*b),
car, ));
spot, self.car_id_counter += 1;
Vehicle::generate_typical_car(car, base_rng), } else {
Some(*b), panic!(
)); "No room to seed parked cars. {} total spots, {} of {} buildings requested",
self.car_id_counter += 1; total_spots,
percent_buildings_with_one_car,
owner_buildings.len()
);
}
} }
} }
@ -473,3 +491,35 @@ fn calculate_paths(
); );
paths paths
} }
// Pick a parking spot for this building. If the building's road has a free spot, use it. If not,
// start BFSing out from the road in a deterministic way until finding a nearby road with an open
// spot.
fn find_spot_near_building(
b: BuildingID,
open_spots_per_road: &mut HashMap<RoadID, Vec<ParkingSpot>>,
map: &Map,
) -> Option<ParkingSpot> {
let mut roads_queue: VecDeque<RoadID> = VecDeque::new();
let mut visited: HashSet<RoadID> = HashSet::new();
{
let start = map.building_to_road(b).id;
roads_queue.push_back(start);
visited.insert(start);
}
loop {
let r = roads_queue.pop_front()?;
let spots = open_spots_per_road.get_mut(&r).unwrap();
if !spots.is_empty() {
return spots.pop();
}
for next_r in map.get_next_roads(r).into_iter() {
if !visited.contains(&next_r) {
roads_queue.push_back(next_r);
visited.insert(next_r);
}
}
}
}