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()
}
// 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 {
let l = self.get_l(id);
self.get_r(l.parent)
@ -436,4 +454,8 @@ impl Map {
stops.remove(&start);
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 std::collections::BTreeMap;
use std::fmt;
use {LaneID, LaneType};
use {IntersectionID, LaneID, LaneType, Map};
// TODO reconsider pub usize. maybe outside world shouldnt know.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
@ -83,6 +83,16 @@ impl Road {
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> {
self.get_siblings(parking_or_driving)
.iter()

View File

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

View File

@ -1,6 +1,6 @@
use abstutil;
use geom::{Polygon, Pt2D};
use map_model::{BuildingID, LaneID, Map};
use map_model::{BuildingID, Map};
use rand::Rng;
use std::collections::{HashMap, HashSet};
use {CarID, Sim, Tick};
@ -53,19 +53,6 @@ impl Neighborhood {
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) {
abstutil::save_object("neighborhoods", &self.map_name, &self.name, self);
}
@ -95,9 +82,9 @@ impl Scenario {
for s in &self.seed_parked_cars {
sim.seed_parked_cars(
neighborhoods[&s.neighborhood].find_matching_lanes(map),
&bldgs_per_neighborhood[&s.neighborhood],
s.percent_buildings_with_car,
map,
);
}

View File

@ -1,11 +1,11 @@
use abstutil::elapsed_seconds;
use driving::{CreateCar, DrivingSimState};
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 rand::{Rng, XorShiftRng};
use router::Router;
use std::collections::VecDeque;
use std::collections::{HashMap, HashSet, VecDeque};
use std::time::Instant;
use transit::TransitSimState;
use trips::{TripLeg, TripManager};
@ -210,41 +210,59 @@ impl Spawner {
pub fn seed_parked_cars(
&mut self,
percent_buildings_with_one_car: f64,
in_lanes: Vec<LaneID>,
owner_buildings: &Vec<BuildingID>,
parking_sim: &mut ParkingSimState,
base_rng: &mut XorShiftRng,
map: &Map,
) {
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
// shuffling dramatically.
let mut all_open_spots: Vec<ParkingSpot> = Vec::new();
for l in in_lanes.into_iter() {
all_open_spots.extend(parking_sim.get_free_spots(l));
// Track the available parking spots per road, only for the roads next to relevant
// buildings.
let mut total_spots = 0;
let mut open_spots_per_road: HashMap<RoadID, Vec<ParkingSpot>> = HashMap::new();
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;
for b in owner_buildings {
if base_rng.gen_bool(percent_buildings_with_one_car) {
// Pick a parking spot for this building.
// TODO Prefer spots closer to the building
let spot = all_open_spots
.pop()
.expect("No available parking spots left to seed");
new_cars += 1;
let car = CarID(self.car_id_counter);
// TODO since spawning applies during the next step, lots of stuff breaks without
// this :(
parking_sim.add_parked_car(ParkedCar::new(
car,
spot,
Vehicle::generate_typical_car(car, base_rng),
Some(*b),
));
self.car_id_counter += 1;
if let Some(spot) = find_spot_near_building(*b, &mut open_spots_per_road, map) {
new_cars += 1;
let car = CarID(self.car_id_counter);
// TODO since spawning applies during the next step, lots of stuff breaks without
// this :(
parking_sim.add_parked_car(ParkedCar::new(
car,
spot,
Vehicle::generate_typical_car(car, base_rng),
Some(*b),
));
self.car_id_counter += 1;
} else {
panic!(
"No room to seed parked cars. {} total spots, {} of {} buildings requested",
total_spots,
percent_buildings_with_one_car,
owner_buildings.len()
);
}
}
}
@ -473,3 +491,35 @@ fn calculate_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);
}
}
}
}