mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-01 02:33:54 +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()
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
||||
|
102
sim/src/spawn.rs
102
sim/src/spawn.rs
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user