mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-11 08:15:34 +03:00
place parked cars close to buildings, in a stable way
This commit is contained in:
parent
98914ce208
commit
24edfcd784
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
102
sim/src/spawn.rs
102
sim/src/spawn.rs
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user