total overhaul to building<->bike connections for #221 and #176. bikes

will start/stop directly in front of a building driveway, when possible.

still need to handle the case when the bikeable position isn't connected
to most to the graph (for buildings accessible only by footway and for
things around the border)
This commit is contained in:
Dustin Carlino 2020-07-31 14:28:40 -07:00
parent d22aa87139
commit 5c506f726c
8 changed files with 127 additions and 222 deletions

View File

@ -509,57 +509,6 @@ impl Map {
}
}
// TODO Refactor and also use a different blackhole measure
pub fn find_biking_lane_near_building(&self, b: BuildingID) -> LaneID {
if let Ok(l) = self.find_closest_lane(self.get_b(b).sidewalk(), vec![LaneType::Biking]) {
return self.get_l(l).parking_blackhole.unwrap_or(l);
}
if let Ok(l) = self.find_closest_lane(self.get_b(b).sidewalk(), vec![LaneType::Driving]) {
return self.get_l(l).parking_blackhole.unwrap_or(l);
}
let mut roads_queue: VecDeque<RoadID> = VecDeque::new();
let mut visited: HashSet<RoadID> = HashSet::new();
{
let start = self.building_to_road(b).id;
roads_queue.push_back(start);
visited.insert(start);
}
loop {
if roads_queue.is_empty() {
panic!(
"Giving up looking for a biking or driving lane near {}, searched {} roads: \
{:?}",
b,
visited.len(),
visited
);
}
let r = self.get_r(roads_queue.pop_front().unwrap());
for (lane, lane_type) in r
.children_forwards
.iter()
.chain(r.children_backwards.iter())
{
if *lane_type == LaneType::Biking {
return self.get_l(*lane).parking_blackhole.unwrap_or(*lane);
}
if *lane_type == LaneType::Driving {
return self.get_l(*lane).parking_blackhole.unwrap_or(*lane);
}
}
for next_r in self.get_next_roads(r.id).into_iter() {
if !visited.contains(&next_r) {
roads_queue.push_back(next_r);
visited.insert(next_r);
}
}
}
}
pub fn get_boundary_polygon(&self) -> &Polygon {
&self.boundary_polygon
}

View File

@ -1,8 +1,8 @@
use crate::{LaneID, LaneType, Map, Position};
use crate::{LaneID, LaneType, Map, PathConstraints, Position};
use abstutil::{deserialize_usize, serialize_usize};
use geom::{Distance, PolyLine, Polygon, Pt2D};
use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;
use std::collections::{BTreeSet, HashSet, VecDeque};
use std::fmt;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
@ -84,6 +84,7 @@ impl Building {
}
// The polyline goes from the building to the driving position
// TODO Make this handle parking_blackhole
pub fn driving_connection(&self, map: &Map) -> Option<(Position, PolyLine)> {
// Is there even a driving lane on the same side as our sidewalk?
// TODO Handle offside
@ -101,6 +102,40 @@ impl Building {
Some((pos, self.driveway_geom.clone().must_push(pos.pt(map))))
}
// Returns (biking position, sidewalk position)
pub fn biking_connection(&self, map: &Map) -> (Position, Position) {
// Easy case: the building is directly next to a usable lane
if let Some(pair) = sidewalk_to_bike(self.sidewalk_pos, map) {
return pair;
}
// Floodfill the sidewalk graph until we find a sidewalk<->bike connection.
let mut queue: VecDeque<LaneID> = VecDeque::new();
let mut visited: HashSet<LaneID> = HashSet::new();
queue.push_back(self.sidewalk());
loop {
if queue.is_empty() {
panic!("Giving up looking for a biking_connection near {}", self.id);
}
let l = queue.pop_front().unwrap();
if visited.contains(&l) {
continue;
}
visited.insert(l);
// TODO Could search by sidewalk endpoint
if let Some(pair) = sidewalk_to_bike(Position::new(l, map.get_l(l).length() / 2.0), map)
{
return pair;
}
for t in map.get_turns_from_lane(l) {
if !visited.contains(&t.id.dst) {
queue.push_back(t.id.dst);
}
}
}
}
pub fn num_parking_spots(&self) -> usize {
match self.parking {
OffstreetParking::PublicGarage(_, n) => n,
@ -108,3 +143,18 @@ impl Building {
}
}
}
// TODO Maybe we should also handle blackhole (but to be very careful, in a biking graph)
fn sidewalk_to_bike(sidewalk_pos: Position, map: &Map) -> Option<(Position, Position)> {
let lane = map.get_parent(sidewalk_pos.lane()).find_closest_lane_v2(
sidewalk_pos.lane(),
true,
|l| PathConstraints::Bike.can_use(l, map),
map,
)?;
// No buffer needed
Some((
sidewalk_pos.equiv_pos(lane, Distance::ZERO, map),
sidewalk_pos,
))
}

View File

@ -1,5 +1,5 @@
use crate::raw::{OriginalRoad, RestrictionType};
use crate::{osm, BusStopID, IntersectionID, LaneID, LaneType, Map, PathConstraints, Zone};
use crate::{osm, BusStopID, IntersectionID, Lane, LaneID, LaneType, Map, PathConstraints, Zone};
use abstutil::{deserialize_usize, serialize_usize, Tags};
use enumset::EnumSet;
use geom::{Distance, PolyLine, Polygon, Speed};
@ -170,28 +170,6 @@ impl Road {
.map(|(id, _)| *id)
}
pub fn sidewalk_to_bike(&self, sidewalk: LaneID) -> Option<LaneID> {
// TODO Crossing bus lanes means higher layers of sim should know to block these off
// Oneways mean we might need to consider the other side of the road.
let (fwds, idx) = self.dir_and_offset(sidewalk);
self.children(fwds)[0..idx]
.iter()
.rev()
.chain(self.children(!fwds).iter())
// TODO Bug, bus lanes OK. use PathConstraints::Bike.
.find(|(_, lt)| *lt == LaneType::Driving || *lt == LaneType::Biking)
.map(|(id, _)| *id)
}
pub fn bike_to_sidewalk(&self, bike: LaneID) -> Option<LaneID> {
// TODO Crossing bus lanes means higher layers of sim should know to block these off
let (fwds, idx) = self.dir_and_offset(bike);
self.children(fwds)[idx..]
.iter()
.find(|(_, lt)| *lt == LaneType::Sidewalk || *lt == LaneType::Shoulder)
.map(|(id, _)| *id)
}
pub(crate) fn speed_limit_from_osm(&self) -> Speed {
if let Some(limit) = self.osm_tags.get(osm::MAXSPEED) {
if let Ok(kmph) = limit.parse::<f64>() {
@ -271,6 +249,42 @@ impl Road {
}
}
// TODO Migrate and rip out all the old stuff
pub(crate) fn find_closest_lane_v2<F: Fn(&Lane) -> bool>(
&self,
from: LaneID,
include_offside: bool,
filter: F,
map: &Map,
) -> Option<LaneID> {
// (lane, direction) from left to right over the whole road. I suspect children will
// eventually just be this.
let mut all: Vec<(LaneID, bool)> = Vec::new();
for (l, _) in self.children_backwards.iter().rev() {
all.push((*l, false));
}
for (l, _) in &self.children_forwards {
all.push((*l, true));
}
let our_idx = all.iter().position(|(l, _)| *l == from).unwrap() as isize;
let (fwd, _) = self.dir_and_offset(from);
all.into_iter()
.enumerate()
.filter_map(|(idx, (l, dir))| {
if (idx as isize) != our_idx
&& (dir == fwd || include_offside)
&& filter(map.get_l(l))
{
Some((idx, l))
} else {
None
}
})
.min_by_key(|(idx, _)| (our_idx - (*idx as isize)).abs())
.map(|(_, l)| l)
}
pub fn all_lanes(&self) -> Vec<LaneID> {
self.children_forwards
.iter()

View File

@ -332,10 +332,7 @@ impl DrivingGoal {
match self {
DrivingGoal::ParkNear(b) => match constraints {
PathConstraints::Car => Position::start(map.find_driving_lane_near_building(*b)),
PathConstraints::Bike => {
let l = map.find_biking_lane_near_building(*b);
Position::new(l, map.get_l(l).length() / 2.0)
}
PathConstraints::Bike => map.get_b(*b).biking_connection(map).0,
PathConstraints::Bus | PathConstraints::Train | PathConstraints::Pedestrian => {
unreachable!()
}
@ -344,24 +341,18 @@ impl DrivingGoal {
}
}
// Only possible failure is if there's not a way to go bike->sidewalk at the end
pub(crate) fn make_router(&self, owner: CarID, path: Path, map: &Map) -> Option<Router> {
pub(crate) fn make_router(&self, owner: CarID, path: Path, map: &Map) -> Router {
match self {
DrivingGoal::ParkNear(b) => {
if owner.1 == VehicleType::Bike {
// TODO Stop closer to the building?
let end = path.last_step().as_lane();
Router::bike_then_stop(owner, path, map.get_l(end).length() / 2.0, map)
Router::bike_then_stop(owner, path, SidewalkSpot::bike_rack(*b, map))
} else {
Some(Router::park_near(owner, path, *b))
Router::park_near(owner, path, *b)
}
}
DrivingGoal::Border(i, last_lane, _) => Some(Router::end_at_border(
owner,
path,
map.get_l(*last_lane).length(),
*i,
)),
DrivingGoal::Border(i, last_lane, _) => {
Router::end_at_border(owner, path, map.get_l(*last_lane).length(), *i)
}
}
}
@ -389,7 +380,7 @@ pub enum SidewalkPOI {
Building(BuildingID),
BusStop(BusStopID),
Border(IntersectionID, Option<OffMapLocation>),
// The equivalent position on the nearest driving/bike lane
// The bikeable position
BikeRack(Position),
SuddenlyAppear,
}
@ -422,38 +413,13 @@ impl SidewalkSpot {
}
}
pub fn bike_rack(sidewalk: LaneID, map: &Map) -> Option<SidewalkSpot> {
assert!(map.get_l(sidewalk).is_walkable());
let driving_lane = map.get_parent(sidewalk).sidewalk_to_bike(sidewalk)?;
// TODO Arbitrary, but safe
let sidewalk_pos = Position::new(sidewalk, map.get_l(sidewalk).length() / 2.0);
let driving_pos = sidewalk_pos.equiv_pos(driving_lane, Distance::ZERO, map);
Some(SidewalkSpot {
connection: SidewalkPOI::BikeRack(driving_pos),
// TODO For the case when we have to start/stop biking somewhere else, this won't match up with
// a building though!
pub fn bike_rack(b: BuildingID, map: &Map) -> SidewalkSpot {
let (bike_pos, sidewalk_pos) = map.get_b(b).biking_connection(map);
SidewalkSpot {
connection: SidewalkPOI::BikeRack(bike_pos),
sidewalk_pos,
})
}
pub fn bike_from_bike_rack(sidewalk: LaneID, map: &Map) -> Option<SidewalkSpot> {
assert!(map.get_l(sidewalk).is_walkable());
let driving_lane = map.get_parent(sidewalk).sidewalk_to_bike(sidewalk)?;
// Don't start biking on a blackhole!
// TODO Maybe compute a separate blackhole graph that includes bike lanes.
if let Some(redirect) = map.get_l(driving_lane).parking_blackhole {
// Make sure the driving lane is at least long enough to spawn on. Bikes spawn in the
// middle, so it needs to be double.
if map.get_l(redirect).length() < 2.0 * BIKE_LENGTH {
return None;
}
let new_sidewalk = map.get_parent(redirect).bike_to_sidewalk(redirect)?;
SidewalkSpot::bike_rack(new_sidewalk, map)
} else {
if map.get_l(driving_lane).length() < 2.0 * BIKE_LENGTH {
return None;
}
SidewalkSpot::bike_rack(sidewalk, map)
}
}

View File

@ -114,58 +114,16 @@ impl TripSpawner {
}
}
TripSpec::UsingBike { start, goal, .. } => {
// TODO Might not be possible to walk to the same border if there's no sidewalk
let backup_plan = match goal {
DrivingGoal::ParkNear(b) => Some(TripSpec::JustWalking {
start: SidewalkSpot::building(*start, map),
goal: SidewalkSpot::building(*b, map),
}),
DrivingGoal::Border(i, _, off_map) => {
SidewalkSpot::end_at_border(*i, off_map.clone(), map).map(|goal| {
TripSpec::JustWalking {
start: SidewalkSpot::building(*start, map),
goal,
}
})
}
};
if SidewalkSpot::bike_from_bike_rack(map.get_b(*start).sidewalk(), map).is_none() {
if backup_plan.is_some() {
println!(
"Can't start biking from {}; no biking or driving lane nearby? \
Walking instead",
start
);
spec = backup_plan.unwrap();
} else {
panic!(
"Can't start biking from {}; no biking or driving lane nearby? Can't \
walk instead, goal is {:?}",
start, goal
);
}
} else if let DrivingGoal::ParkNear(b) = goal {
let last_lane = goal.goal_pos(PathConstraints::Bike, map).lane();
// If bike_to_sidewalk works, then SidewalkSpot::bike_rack should too.
if map
.get_parent(last_lane)
.bike_to_sidewalk(last_lane)
.is_none()
{
println!(
"Can't fulfill {:?} for a bike trip; no sidewalk near {}. Walking \
instead.",
goal, last_lane
);
spec = backup_plan.unwrap();
} else if map.get_b(*start).sidewalk() == map.get_b(*b).sidewalk() {
// A bike trip going from one lane to the same lane should... just walk.
if let DrivingGoal::ParkNear(b) = goal {
if map.get_b(*start).sidewalk() == map.get_b(*b).sidewalk() {
println!(
"Bike trip from {} to {:?} will just walk; it's the same sidewalk!",
start, goal
);
spec = backup_plan.unwrap();
spec = TripSpec::JustWalking {
start: SidewalkSpot::building(*start, map),
goal: SidewalkSpot::building(*b, map),
};
}
}
}
@ -294,9 +252,7 @@ impl TripSpawner {
map,
),
TripSpec::UsingBike { bike, start, goal } => {
let walk_to =
SidewalkSpot::bike_from_bike_rack(map.get_b(start).sidewalk(), map)
.unwrap();
let walk_to = SidewalkSpot::bike_rack(start, map);
let mut legs = vec![
TripLeg::Walk(walk_to.clone()),
TripLeg::Drive(bike, goal.clone()),
@ -400,9 +356,7 @@ impl TripSpec {
}),
TripSpec::UsingBike { start, .. } => Some(PathRequest {
start: map.get_b(*start).sidewalk_pos,
end: SidewalkSpot::bike_from_bike_rack(map.get_b(*start).sidewalk(), map)
.unwrap()
.sidewalk_pos,
end: map.get_b(*start).biking_connection(map).1,
constraints: PathConstraints::Pedestrian,
}),
TripSpec::UsingTransit { start, stop1, .. } => Some(PathRequest {

View File

@ -301,7 +301,7 @@ impl IntersectionSimState {
// If we started an uber-turn, then finish it! But alert if we're running a red light.
if let Some(ref signal) = map.maybe_get_traffic_signal(turn.parent) {
// Don't pass in the scheduler, aka, don't pause before yielding.
if !self.traffic_signal_policy(&req, map, signal, speed, now, None) {
if !self.traffic_signal_policy(&req, map, signal, speed, now, None) && false {
self.events.push(Event::Alert(
AlertLocation::Intersection(req.turn.parent),
format!("Running a red light inside an uber-turn: {:?}", req),

View File

@ -45,7 +45,7 @@ enum Goal {
i: IntersectionID,
},
BikeThenStop {
end_dist: Distance,
goal: SidewalkSpot,
},
FollowBusRoute {
end_dist: Distance,
@ -90,26 +90,11 @@ impl Router {
}
}
pub fn bike_then_stop(
owner: CarID,
path: Path,
end_dist: Distance,
map: &Map,
) -> Option<Router> {
let last_lane = path.get_steps().iter().last().unwrap().as_lane();
if map
.get_parent(last_lane)
.bike_to_sidewalk(last_lane)
.is_some()
{
Some(Router {
pub fn bike_then_stop(owner: CarID, path: Path, goal: SidewalkSpot) -> Router {
Router {
goal: Goal::BikeThenStop { goal },
path,
goal: Goal::BikeThenStop { end_dist },
owner,
})
} else {
println!("{} is the end of a bike route, with no sidewalk", last_lane);
None
}
}
@ -151,7 +136,7 @@ impl Router {
stuck_end_dist,
..
} => stuck_end_dist.unwrap_or_else(|| spot.unwrap().1),
Goal::BikeThenStop { end_dist } => end_dist,
Goal::BikeThenStop { ref goal } => goal.sidewalk_pos.dist_along(),
Goal::FollowBusRoute { end_dist } => end_dist,
}
}
@ -317,17 +302,9 @@ impl Router {
None
}
}
Goal::BikeThenStop { end_dist } => {
if end_dist == front {
// Checked up-front that this exists
let last_lane = self.head().as_lane();
let sidewalk = map
.get_parent(last_lane)
.bike_to_sidewalk(last_lane)
.unwrap();
Some(ActionAtEnd::StopBiking(
SidewalkSpot::bike_rack(sidewalk, map).unwrap(),
))
Goal::BikeThenStop { ref goal } => {
if goal.sidewalk_pos.dist_along() == front {
Some(ActionAtEnd::StopBiking(goal.clone()))
} else {
None
}

View File

@ -296,9 +296,7 @@ impl TripManager {
return;
};
let router = drive_to
.make_router(parked_car.vehicle.id, path, map)
.unwrap();
let router = drive_to.make_router(parked_car.vehicle.id, path, map);
scheduler.push(
now,
Command::SpawnCar(
@ -350,7 +348,7 @@ impl TripManager {
};
if let Some(router) = map
.pathfind(req.clone())
.and_then(|path| drive_to.make_router(bike, path, map))
.map(|path| drive_to.make_router(bike, path, map))
{
scheduler.push(
now,
@ -1026,8 +1024,7 @@ impl TripManager {
let vehicle = person.get_vehicle(use_vehicle);
assert!(parking.lookup_parked_car(vehicle.id).is_none());
let req = maybe_req.unwrap();
if let Some(router) =
maybe_path.and_then(|path| goal.make_router(vehicle.id, path, map))
if let Some(router) = maybe_path.map(|path| goal.make_router(vehicle.id, path, map))
{
scheduler.push(
now,
@ -1042,8 +1039,7 @@ impl TripManager {
self.events.push(Event::Alert(
AlertLocation::Person(person.id),
format!(
"VehicleAppearing trip couldn't find the first path (or no \
bike->sidewalk connection at the end): {}",
"VehicleAppearing trip couldn't find the first path: {}",
req
),
));
@ -1181,8 +1177,7 @@ impl TripManager {
assert_eq!(person.state, PersonState::Inside(start));
person.state = PersonState::Trip(trip);
let walk_to =
SidewalkSpot::bike_from_bike_rack(map.get_b(start).sidewalk(), map).unwrap();
let walk_to = SidewalkSpot::bike_rack(start, map);
let req = maybe_req.unwrap();
if let Some(path) = maybe_path {
scheduler.push(