mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 09:24:26 +03:00
be able to start and end bike trips on bike lanes. use driving
blackholes for now.
This commit is contained in:
parent
ade23dac5f
commit
ba2206d02a
@ -33,9 +33,11 @@ will eventually shift mode or take different trips altogether. Not attempting
|
||||
any of that yet -- just using PSRC trips. I don't understand the demand modeling
|
||||
process well at all yet.
|
||||
|
||||
## Bikes using bus lanes
|
||||
## Bike/bus lane connectivity
|
||||
|
||||
This should be pretty easy to allow.
|
||||
Bikes and buses can make crazy left turns from the rightmost protected lane.
|
||||
Alternatively, stop generating those turns and start generating turns between
|
||||
protected and general lanes.
|
||||
|
||||
## Parking
|
||||
|
||||
|
@ -37,6 +37,7 @@
|
||||
- https://www.the74million.org/article/building-a-smarter-and-cheaper-school-bus-system-how-a-boston-mit-partnership-led-to-new-routes-that-are-20-more-efficient-use-400-fewer-buses-save-5-million/
|
||||
- https://www.citylab.com/perspective/2019/10/micromobility-urban-design-car-free-infrastruture-futurama/600163/
|
||||
- https://www.sanjorn.com/
|
||||
- https://ui.kpf.com/
|
||||
|
||||
## Similar projects
|
||||
|
||||
|
@ -96,7 +96,8 @@ impl AgentSpawner {
|
||||
maybe_goal: None,
|
||||
}));
|
||||
}
|
||||
// TODO First lane might be a bike lane! Need to pass PathConstraints.
|
||||
}
|
||||
if let Some(pos) = Position::bldg_via_biking(id, map) {
|
||||
if ctx
|
||||
.input
|
||||
.contextual_action(Key::F7, "spawn a bike starting here")
|
||||
@ -198,8 +199,7 @@ impl State for AgentSpawner {
|
||||
if constraints == PathConstraints::Pedestrian {
|
||||
Position::bldg_via_walking(to, map)
|
||||
} else {
|
||||
// TODO Specify biking maybe
|
||||
DrivingGoal::ParkNear(to).goal_pos(map)
|
||||
DrivingGoal::ParkNear(to).goal_pos(constraints, map)
|
||||
}
|
||||
}
|
||||
Goal::Border(to) => {
|
||||
@ -208,7 +208,7 @@ impl State for AgentSpawner {
|
||||
constraints,
|
||||
map,
|
||||
) {
|
||||
g.goal_pos(map)
|
||||
g.goal_pos(constraints, map)
|
||||
} else {
|
||||
self.maybe_goal = None;
|
||||
return Transition::Keep;
|
||||
|
@ -404,7 +404,7 @@ fn calculate_bike_network(ctx: &EventCtx, ui: &UI) -> RoadColorer {
|
||||
fn calculate_bus_network(ctx: &EventCtx, ui: &UI) -> RoadColorer {
|
||||
let mut colorer = RoadColorerBuilder::new(
|
||||
Text::prompt("bus networks"),
|
||||
vec![("bike lanes", Color::GREEN)],
|
||||
vec![("bus lanes", Color::GREEN)],
|
||||
);
|
||||
for l in ui.primary.map.all_lanes() {
|
||||
if l.is_bus() {
|
||||
|
@ -531,6 +531,56 @@ 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
|
||||
}
|
||||
|
@ -79,7 +79,8 @@ impl VehiclePathfinder {
|
||||
req.end.dist_along(),
|
||||
Distance::centimeters(raw_path.get_weight()),
|
||||
);
|
||||
if self.constraints == PathConstraints::Bike {
|
||||
// Disabled, because this looks stable now.
|
||||
if false && self.constraints == PathConstraints::Bike {
|
||||
check_bike_route(&path, map);
|
||||
}
|
||||
Some(path)
|
||||
@ -176,7 +177,6 @@ pub fn cost(lane: &Lane, turn: &Turn, constraints: PathConstraints, map: &Map) -
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Temporary, while I'm figuring out why bike lanes aren't always used.
|
||||
fn check_bike_route(path: &Path, map: &Map) {
|
||||
let steps: Vec<PathStep> = path.get_steps().iter().cloned().collect();
|
||||
for pair in steps.windows(2) {
|
||||
|
@ -72,6 +72,19 @@ impl Position {
|
||||
.equiv_pos(driving_lane, Distance::ZERO, map),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn bldg_via_biking(b: BuildingID, map: &Map) -> Option<Position> {
|
||||
let bldg = map.get_b(b);
|
||||
let driving_lane = map
|
||||
.find_closest_lane(bldg.sidewalk(), vec![LaneType::Biking])
|
||||
.or_else(|_| map.find_closest_lane(bldg.sidewalk(), vec![LaneType::Driving]))
|
||||
.ok()?;
|
||||
Some(
|
||||
bldg.front_path
|
||||
.sidewalk
|
||||
.equiv_pos(driving_lane, Distance::ZERO, map),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO also building paths?
|
||||
|
@ -183,6 +183,8 @@ pub struct ParkedCar {
|
||||
pub spot: ParkingSpot,
|
||||
}
|
||||
|
||||
// It'd be nice to inline the goal_pos like SidewalkSpot does, but DrivingGoal is persisted in
|
||||
// Scenarios, so this wouldn't survive map edits.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum DrivingGoal {
|
||||
ParkNear(BuildingID),
|
||||
@ -204,17 +206,19 @@ impl DrivingGoal {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO Stick this in the DrivingGoal directly, like SidewalkSpot. Find it upon construction.
|
||||
pub fn goal_pos(&self, map: &Map) -> Position {
|
||||
pub fn goal_pos(&self, constraints: PathConstraints, map: &Map) -> Position {
|
||||
let lane = match self {
|
||||
// TODO Biking option.
|
||||
DrivingGoal::ParkNear(b) => map.find_driving_lane_near_building(*b),
|
||||
DrivingGoal::ParkNear(b) => match constraints {
|
||||
PathConstraints::Car => map.find_driving_lane_near_building(*b),
|
||||
PathConstraints::Bike => map.find_biking_lane_near_building(*b),
|
||||
PathConstraints::Bus | PathConstraints::Pedestrian => unreachable!(),
|
||||
},
|
||||
DrivingGoal::Border(_, l) => *l,
|
||||
};
|
||||
Position::new(lane, map.get_l(lane).length())
|
||||
}
|
||||
|
||||
pub fn make_router(&self, path: Path, map: &Map, vt: VehicleType) -> Router {
|
||||
pub(crate) fn make_router(&self, path: Path, map: &Map, vt: VehicleType) -> Router {
|
||||
match self {
|
||||
DrivingGoal::ParkNear(b) => {
|
||||
if vt == VehicleType::Bike {
|
||||
|
@ -135,7 +135,7 @@ impl TripSpawner {
|
||||
return;
|
||||
}
|
||||
if let DrivingGoal::ParkNear(_) = goal {
|
||||
let last_lane = goal.goal_pos(map).lane();
|
||||
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)
|
||||
@ -475,11 +475,14 @@ impl TripSpec {
|
||||
vehicle_spec,
|
||||
goal,
|
||||
..
|
||||
} => PathRequest {
|
||||
start: *start_pos,
|
||||
end: goal.goal_pos(map),
|
||||
constraints: vehicle_spec.vehicle_type.to_constraints(),
|
||||
},
|
||||
} => {
|
||||
let constraints = vehicle_spec.vehicle_type.to_constraints();
|
||||
PathRequest {
|
||||
start: *start_pos,
|
||||
end: goal.goal_pos(constraints, map),
|
||||
constraints,
|
||||
}
|
||||
}
|
||||
TripSpec::UsingParkedCar { start, spot, .. } => PathRequest {
|
||||
start: start.sidewalk_pos,
|
||||
end: SidewalkSpot::parking_spot(*spot, map, parking).sidewalk_pos,
|
||||
|
@ -191,7 +191,7 @@ impl TripManager {
|
||||
// Actually, to unpark, the car's front should be where it'll wind up at the end.
|
||||
start = Position::new(start.lane(), start.dist_along() + parked_car.vehicle.length);
|
||||
}
|
||||
let end = drive_to.goal_pos(map);
|
||||
let end = drive_to.goal_pos(PathConstraints::Car, map);
|
||||
let path = if let Some(p) = map.pathfind(PathRequest {
|
||||
start,
|
||||
end,
|
||||
@ -243,7 +243,7 @@ impl TripManager {
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let end = drive_to.goal_pos(map);
|
||||
let end = drive_to.goal_pos(PathConstraints::Bike, map);
|
||||
let path = if let Some(p) = map.pathfind(PathRequest {
|
||||
start: driving_pos,
|
||||
end,
|
||||
|
Loading…
Reference in New Issue
Block a user