rearrange Building internals for #176. edits can affect building

connections to driving/biking. just store the immutable stuff -- whether
there's parking in the building, the connection to the sidewalk, and the
physical driveway line. compute all the rest dynamically, so it responds
to edits without effort.

shouldn't be major behavior changes yet (besides maybe fixing some bugs
involving edits)
This commit is contained in:
Dustin Carlino 2020-07-31 12:49:33 -07:00
parent ec52adce77
commit d22aa87139
28 changed files with 252 additions and 283 deletions

View File

@ -16,9 +16,9 @@ data/input/raw_maps/south_seattle.bin,39f2484a46af741b8cc6d7f6e4965c3b,https://w
data/input/raw_maps/udistrict.bin,01a9b18687030019c7d922752b3f1819,https://www.dropbox.com/s/omhrxg9y02oejx2/udistrict.bin.zip?dl=0
data/input/raw_maps/west_seattle.bin,39116eb1a0a07356f736ea5bee552cc9,https://www.dropbox.com/s/k0or6wmygcj5h1l/west_seattle.bin.zip?dl=0
data/input/screenshots/downtown.zip,3e34c19d20f8c6c7def49374bc32dcf1,https://www.dropbox.com/s/qawd35wz62m2acl/downtown.zip.zip?dl=0
data/input/screenshots/huge_krakow.zip,c634e6ac360da746e1a9f4808ce83e8c,https://www.dropbox.com/s/dbzon7k5ukndtza/huge_krakow.zip.zip?dl=0
data/input/screenshots/huge_krakow.zip,a0ba93beabbc214257d3b1de3bc5a57f,https://www.dropbox.com/s/dbzon7k5ukndtza/huge_krakow.zip.zip?dl=0
data/input/screenshots/lakeslice.zip,003047ad84f970daa936f2e572c2459b,https://www.dropbox.com/s/z0z96lsn7bunqfy/lakeslice.zip.zip?dl=0
data/input/screenshots/montlake.zip,ef453f3a7d087ce0dc750103f8d55217,https://www.dropbox.com/s/ku5x06hdt0omway/montlake.zip.zip?dl=0
data/input/screenshots/montlake.zip,e4432e5afecd317f0e5a6b17ff83c07d,https://www.dropbox.com/s/ku5x06hdt0omway/montlake.zip.zip?dl=0
data/input/screenshots/udistrict.zip,dabdbe5bded617635d94072e2963725f,https://www.dropbox.com/s/ecnt1tyza48y9o2/udistrict.zip.zip?dl=0
data/input/seattle/N47W122.hgt,0db4e23e51f7680538b0bbbc72208e07,https://www.dropbox.com/s/mmb4mgutwotijdw/N47W122.hgt.zip?dl=0
data/input/seattle/blockface.bin,add872bab9040ae911366328a230f8b5,https://www.dropbox.com/s/rxd2care60tbe75/blockface.bin.zip?dl=0
@ -34,21 +34,21 @@ data/input/seattle/osm/south_seattle.osm,1107f8b545b09731c4619fde63bdbaed,https:
data/input/seattle/osm/udistrict.osm,ed26f2405c07c63161e902bcb5049fab,https://www.dropbox.com/s/ozqk7lb5nn0wace/udistrict.osm.zip?dl=0
data/input/seattle/osm/washington-latest.osm.pbf,332c0bbd80621a52e3765381718f3454,https://www.dropbox.com/s/iitat4cnhj5dop8/washington-latest.osm.pbf.zip?dl=0
data/input/seattle/osm/west_seattle.osm,f829d6a1c87e782fafbc1552acc5a523,https://www.dropbox.com/s/3vlyve59lfo9s99/west_seattle.osm.zip?dl=0
data/input/seattle/parcels.bin,8b5ce9b62edcc4ef15e97976416f4661,https://www.dropbox.com/s/in8a9hbhzvxyyy2/parcels.bin.zip?dl=0
data/input/seattle/parcels.bin,88ca5d9c59bb7bd97db3de2bd709215c,https://www.dropbox.com/s/nk59387ysb84dcf/parcels.bin.zip?dl=0
data/input/seattle/parcels_urbansim.txt,db63d7d606e8702d12f9399e87e6a00f,https://www.dropbox.com/s/6g8rbsf200dssj3/parcels_urbansim.txt.zip?dl=0
data/input/seattle/popdat.bin,0fd10698d2c6bf41da3d57804c617d15,https://www.dropbox.com/s/iboctakleznvslq/popdat.bin.zip?dl=0
data/input/seattle/trips_2014.csv,d4a8e733045b28c0385fb81359d6df03,https://www.dropbox.com/s/5ppravwmk6bf20d/trips_2014.csv.zip?dl=0
data/system/cities/seattle.bin,eb9e9621e989247f8cbd7260594cb4b9,https://www.dropbox.com/s/l2srf7cb1d3bx20/seattle.bin.zip?dl=0
data/system/maps/ballard.bin,85d4cff27f9cb3988e671e32d46c2790,https://www.dropbox.com/s/3ij8i0tczjrz57i/ballard.bin.zip?dl=0
data/system/maps/berlin_center.bin,f6ce50c4fd6605d799a1749947384c47,https://www.dropbox.com/s/yissbkoxox4vpuw/berlin_center.bin.zip?dl=0
data/system/maps/downtown.bin,149647d5a2a830c63713f209185f15fb,https://www.dropbox.com/s/bleeq53zz0lndyb/downtown.bin.zip?dl=0
data/system/maps/huge_krakow.bin,a08fac162b5816e3fb49429bdef9fa0a,https://www.dropbox.com/s/thimax62jtydhvp/huge_krakow.bin.zip?dl=0
data/system/maps/huge_seattle.bin,bd6d96c9ddd0d97b50cfb4d139395a42,https://www.dropbox.com/s/5kcynthbwju23aj/huge_seattle.bin.zip?dl=0
data/system/maps/lakeslice.bin,71c042e4d50af4365230f390aed402ee,https://www.dropbox.com/s/2qnmdta35fmtt33/lakeslice.bin.zip?dl=0
data/system/maps/montlake.bin,fa707f16525ec66141549859b193fb8d,https://www.dropbox.com/s/kj5phknx5ghi5fd/montlake.bin.zip?dl=0
data/system/maps/south_seattle.bin,9b5761e98c66d36efc1c8f913259ea2c,https://www.dropbox.com/s/53lcpce4420fv5q/south_seattle.bin.zip?dl=0
data/system/maps/udistrict.bin,64c575b6d693a4a54366b305ffd36552,https://www.dropbox.com/s/oa137355upa6ehs/udistrict.bin.zip?dl=0
data/system/maps/west_seattle.bin,d919037c8eac40329e70ddf567db8415,https://www.dropbox.com/s/1ed36uaaomdhy02/west_seattle.bin.zip?dl=0
data/system/maps/ballard.bin,00aed65d8920f8ecf3d0418f8d2ba022,https://www.dropbox.com/s/b7yv4haguj71fm0/ballard.bin.zip?dl=0
data/system/maps/berlin_center.bin,20bdf638ae21fde2b59499c70d777b3c,https://www.dropbox.com/s/971gf8uzdgv8e2o/berlin_center.bin.zip?dl=0
data/system/maps/downtown.bin,b9a7779770e65762e514d70f62fb6576,https://www.dropbox.com/s/o46idd1vtb63cby/downtown.bin.zip?dl=0
data/system/maps/huge_krakow.bin,9927b32e2a895e696a8caa2783c1d3ab,https://www.dropbox.com/s/tn0w8e681cnvl1w/huge_krakow.bin.zip?dl=0
data/system/maps/huge_seattle.bin,c5201cdfdfe7bb2f7131d8e9f19053a6,https://www.dropbox.com/s/qq1e56rwp4qh6v9/huge_seattle.bin.zip?dl=0
data/system/maps/lakeslice.bin,d91d6420dec5e09bb98a8a406e57f5c0,https://www.dropbox.com/s/ku76oob8omnopih/lakeslice.bin.zip?dl=0
data/system/maps/montlake.bin,032f4abaeab311a1f1bbf992a59ee882,https://www.dropbox.com/s/co7hpuc820b8t2y/montlake.bin.zip?dl=0
data/system/maps/south_seattle.bin,c2ea73914031391444662a6ec2b2b325,https://www.dropbox.com/s/97l2cx62dwo7tzl/south_seattle.bin.zip?dl=0
data/system/maps/udistrict.bin,65bd3b3890358bc6408578eb57d9e8ab,https://www.dropbox.com/s/0wvnn56fu7zzjm0/udistrict.bin.zip?dl=0
data/system/maps/west_seattle.bin,0988d2fdee81c3b0907e6ec1231a3656,https://www.dropbox.com/s/chi7v6451yune0h/west_seattle.bin.zip?dl=0
data/system/prebaked_results/lakeslice/weekday.bin,1a5a02661bb67f69dafd78c0a2d4565d,https://www.dropbox.com/s/3wt7lxncc9mba9q/weekday.bin.zip?dl=0
data/system/prebaked_results/montlake/car vs bike contention.bin,2dbd9fb3ee0b4fc12c4e7651dbcb0e0b,https://www.dropbox.com/s/jefg0ikjy9dsrdd/car%20vs%20bike%20contention.bin.zip?dl=0
data/system/prebaked_results/montlake/weekday.bin,fc0baa2160dcddb151d4ebc315db6bf3,https://www.dropbox.com/s/zp8e5munh3t38h6/weekday.bin.zip?dl=0

View File

@ -30,14 +30,14 @@ love with a Nintendo 64 game called Banjo Kazooie, which led me to the online
fan communities of the early 2000's. I wanted to create games too, so I started
learning programming via library books and lots of questions on IRC. Because I
never had any confidence in art, I wound up working on
[roguelikes](https://github.com/dabreegster/mnemonicrl/), which led to a
fervent interest in pathfinding algorithms and [collaborative
diffusion](http://www.cs.colorado.edu/~ralex/papers/PDF/OOPSLA06antiobjects.pdf).
When I started driving in high school, I quickly realized how bad people were
at it. I remember being stuck at the intersection of [Florida Blvd and
Cloud](https://www.openstreetmap.org/node/1279204989) and first wondering if
the pathfinding algorithms could help with traffic. Can you see where this is
going?
[roguelikes](https://github.com/dabreegster/mnemonicrl/), which led to a fervent
interest in pathfinding algorithms and
[collaborative diffusion](http://www.cs.colorado.edu/~ralex/papers/PDF/OOPSLA06antiobjects.pdf).
When I started driving in high school, I quickly realized how bad people were at
it. I remember being stuck at the intersection of
[Florida Blvd and Cloud](https://www.openstreetmap.org/node/1279204989) and
first wondering if the pathfinding algorithms could help with traffic. Can you
see where this is going?
![Impatience is a virtue](cloud_florida.jpg)
@ -174,8 +174,19 @@ calling out milestones. "UI churn" is pretty much constantly happening.
## Year 3 (June 2020-2021)
- June: parking lots, real minimap controls, road labels
- **June 22**: alpha launch! [r/Seattle](https://old.reddit.com/r/Seattle/comments/hdtucd/ab_street_think_you_can_fix_seattles_traffic/), [r/SeattleWA](https://old.reddit.com/r/SeattleWA/comments/hdttu8/ab_street_think_you_can_fix_seattles_traffic/), [r/UrbanPlanning](https://old.reddit.com/r/urbanplanning/comments/hdylmo/ab_street_a_traffic_simulation_game/), [HN](https://news.ycombinator.com/item?id=23605048#23608365), [GeekWire](https://www.geekwire.com/2020/want-fix-seattle-traffic-redditor-makes-game-allows-players-tweak-city-streets/), [The Stranger](https://www.thestranger.com/slog/2020/06/29/43999454/ab-streets-game-lets-you-create-the-seattle-street-grid-of-your-dreams)
- July: ...
- **June 22**: alpha launch!
[r/Seattle](https://old.reddit.com/r/Seattle/comments/hdtucd/ab_street_think_you_can_fix_seattles_traffic/),
[r/SeattleWA](https://old.reddit.com/r/SeattleWA/comments/hdttu8/ab_street_think_you_can_fix_seattles_traffic/),
[r/UrbanPlanning](https://old.reddit.com/r/urbanplanning/comments/hdylmo/ab_street_a_traffic_simulation_game/),
[HN](https://news.ycombinator.com/item?id=23605048#23608365),
[GeekWire](https://www.geekwire.com/2020/want-fix-seattle-traffic-redditor-makes-game-allows-players-tweak-city-streets/),
[The Stranger](https://www.thestranger.com/slog/2020/06/29/43999454/ab-streets-game-lets-you-create-the-seattle-street-grid-of-your-dreams)
- July: loads of bugfixes, map geometry improvements, UI cleanups,
access-restricted zones for private neighborhoods and no-through-traffic,
better traffic generation between home<->work for new maps, complete overhaul
to bus routes and introduction of light rail, commute pattern explorer,
importing Krakow and Berlin, smarter lane-changing, walkable shoulders for
roads without sidewalks
- [KING 5 Evening](https://www.youtube.com/watch?v=Pk8V-egsUxU) interview
## Retrospective

View File

@ -553,6 +553,8 @@ fn generate_osmc(
fn find_divided_highways(app: &App) -> HashSet<RoadID> {
let map = &app.primary.map;
let mut closest: FindClosest<RoadID> = FindClosest::new(map.get_bounds());
// TODO Consider not even filtering by oneway. I keep finding mistakes where people split a
// road, but didn't mark one side oneway!
let mut oneways = Vec::new();
for r in map.all_roads() {
if r.osm_tags.contains_key("oneway") {
@ -564,19 +566,25 @@ fn find_divided_highways(app: &App) -> HashSet<RoadID> {
let mut found = HashSet::new();
for r1 in oneways {
let r1 = map.get_r(r1);
let (middle, angle) = r1.center_pts.must_dist_along(r1.center_pts.length() / 2.0);
for (r2, _, _) in closest.all_close_pts(middle, Distance::meters(250.0)) {
if r1.id != r2
&& PolyLine::must_new(vec![
middle.project_away(Distance::meters(100.0), angle.rotate_degs(90.0)),
middle.project_away(Distance::meters(100.0), angle.rotate_degs(-90.0)),
])
.intersection(&map.get_r(r2).center_pts)
.is_some()
&& r1.get_name() == map.get_r(r2).get_name()
{
found.insert(r1.id);
found.insert(r2);
for dist in vec![
Distance::ZERO,
r1.center_pts.length() / 2.0,
r1.center_pts.length(),
] {
let (pt, angle) = r1.center_pts.must_dist_along(dist);
for (r2, _, _) in closest.all_close_pts(pt, Distance::meters(250.0)) {
if r1.id != r2
&& PolyLine::must_new(vec![
pt.project_away(Distance::meters(100.0), angle.rotate_degs(90.0)),
pt.project_away(Distance::meters(100.0), angle.rotate_degs(-90.0)),
])
.intersection(&map.get_r(r2).center_pts)
.is_some()
&& r1.get_name() == map.get_r(r2).get_name()
{
found.insert(r1.id);
found.insert(r2);
}
}
}
}

View File

@ -3,7 +3,7 @@ use crate::info::{header_btns, make_table, make_tabs, Details, Tab};
use crate::render::DrawPedestrian;
use ezgui::{Btn, Color, EventCtx, Line, Text, TextExt, Widget};
use geom::{Angle, Circle, Distance, Speed, Time};
use map_model::{BuildingID, LaneID, Traversable, SIDEWALK_THICKNESS};
use map_model::{BuildingID, LaneID, OffstreetParking, Traversable, SIDEWALK_THICKNESS};
use sim::{DrawPedestrianInput, PedestrianID, PersonID, TripMode, TripResult};
use std::collections::BTreeMap;
@ -21,20 +21,18 @@ pub fn info(ctx: &mut EventCtx, app: &App, details: &mut Details, id: BuildingID
kv.push(("OSM ID", format!("{}", b.osm_way_id)));
}
if let Some(ref p) = b.parking {
let num_spots = b.num_parking_spots();
if num_spots > 0 {
let free = app.primary.sim.get_free_offstreet_spots(b.id).len();
if let Some(ref n) = p.public_garage_name {
if let OffstreetParking::PublicGarage(ref n, _) = b.parking {
kv.push((
"Parking",
format!(
"{} / {} public spots available via {}",
free, p.num_spots, n
),
format!("{} / {} public spots available via {}", free, num_spots, n),
));
} else {
kv.push((
"Parking",
format!("{} / {} private spots available", free, p.num_spots),
format!("{} / {} private spots available", free, num_spots),
));
}
} else {

View File

@ -7,7 +7,7 @@ use ezgui::{
Outcome, Text, TextExt, VerticalAlignment, Widget,
};
use geom::Time;
use map_model::{BuildingID, Map, ParkingLotID, RoadID};
use map_model::{BuildingID, Map, OffstreetParking, ParkingLotID, RoadID};
use sim::{ParkingSpot, VehicleType};
use std::collections::BTreeSet;
@ -131,15 +131,8 @@ impl Occupancy {
*public_counter += 1;
}
ParkingSpot::Offstreet(b, _) => {
if app
.primary
.map
.get_b(b)
.parking
.as_ref()
.unwrap()
.public_garage_name
.is_some()
if let OffstreetParking::PublicGarage(_, _) =
app.primary.map.get_b(b).parking
{
if !garages {
continue;

View File

@ -4,7 +4,7 @@ use crate::helpers::ID;
use crate::render::{DrawOptions, Renderable, OUTLINE_THICKNESS};
use ezgui::{Color, Drawable, GeomBatch, GfxCtx, Line, Prerender, Text};
use geom::{Distance, Polygon, Pt2D};
use map_model::{Building, BuildingID, Map, NORMAL_LANE_THICKNESS};
use map_model::{Building, BuildingID, Map, OffstreetParking, NORMAL_LANE_THICKNESS};
use std::cell::RefCell;
pub struct DrawBuilding {
@ -22,35 +22,28 @@ impl DrawBuilding {
outlines_batch: &mut GeomBatch,
prerender: &Prerender,
) -> DrawBuilding {
// Trim the front path line away from the sidewalk's center line, so that it doesn't
// overlap. For now, this cleanup is visual; it doesn't belong in the map_model layer.
let orig_line = &bldg.front_path.line;
let front_path_line = orig_line
// Trim the driveway away from the sidewalk's center line, so that it doesn't overlap. For
// now, this cleanup is visual; it doesn't belong in the map_model layer.
let orig_pl = &bldg.driveway_geom;
let driveway = orig_pl
.slice(
Distance::ZERO,
orig_line.length() - map.get_l(bldg.sidewalk()).width / 2.0,
orig_pl.length() - map.get_l(bldg.sidewalk()).width / 2.0,
)
.unwrap_or_else(|| orig_line.clone());
.map(|(pl, _)| pl)
.unwrap_or_else(|_| orig_pl.clone());
if bldg.amenities.is_empty() {
bldg_batch.push(cs.residential_building, bldg.polygon.clone());
} else {
bldg_batch.push(cs.commerical_building, bldg.polygon.clone());
}
paths_batch.push(
cs.sidewalk,
front_path_line.make_polygons(NORMAL_LANE_THICKNESS),
);
paths_batch.push(cs.sidewalk, driveway.make_polygons(NORMAL_LANE_THICKNESS));
if let Ok(p) = bldg.polygon.to_outline(Distance::meters(0.1)) {
outlines_batch.push(cs.building_outline, p);
}
if bldg
.parking
.as_ref()
.map(|p| p.public_garage_name.is_some())
.unwrap_or(false)
{
if let OffstreetParking::PublicGarage(_, _) = bldg.parking {
// Might need to scale down more for some buildings, but so far, this works everywhere.
bldg_batch.append(
GeomBatch::mapspace_svg(prerender, "system/assets/map/parking.svg")

View File

@ -211,13 +211,11 @@ impl DrawPedCrowd {
map.right_shift(pl_slice, SIDEWALK_THICKNESS / 4.0)
}
}
PedCrowdLocation::BldgFrontPath(b) => map
PedCrowdLocation::BldgDriveway(b) => map
.get_b(b)
.front_path
.line
.to_polyline()
.driveway_geom
.exact_slice(input.low, input.high),
PedCrowdLocation::LotFrontPath(pl) => map
PedCrowdLocation::LotDriveway(pl) => map
.get_pl(pl)
.sidewalk_line
.to_polyline()
@ -239,8 +237,8 @@ impl DrawPedCrowd {
blob,
zorder: match input.location {
PedCrowdLocation::Sidewalk(on, _) => on.get_zorder(map),
PedCrowdLocation::BldgFrontPath(_) => 0,
PedCrowdLocation::LotFrontPath(_) => 0,
PedCrowdLocation::BldgDriveway(_) => 0,
PedCrowdLocation::LotDriveway(_) => 0,
},
draw_default: prerender.upload(batch),
}

View File

@ -578,6 +578,11 @@ fn partition_sidewalk_loops(app: &App) -> Vec<Loop> {
let mut todo_bldgs: BTreeSet<BuildingID> = map.all_buildings().iter().map(|b| b.id).collect();
let mut remainder = HashSet::new();
let mut sidewalk_to_bldgs = MultiMap::new();
for b in map.all_buildings() {
sidewalk_to_bldgs.insert(b.sidewalk(), b.id);
}
while !todo_bldgs.is_empty() {
let mut sidewalks = HashSet::new();
let mut bldgs = HashSet::new();
@ -586,7 +591,7 @@ fn partition_sidewalk_loops(app: &App) -> Vec<Loop> {
let ok = loop {
sidewalks.insert(current_l);
for b in &map.get_l(current_l).building_paths {
for b in sidewalk_to_bldgs.get(current_l) {
bldgs.insert(*b);
// TODO I wanted to assert that we haven't assigned this one yet, but...
todo_bldgs.remove(b);

View File

@ -447,7 +447,7 @@ fn path_request(
fn pos(endpt: TripEndpoint, mode: TripMode, from: bool, map: &Map) -> Option<Position> {
match endpt {
TripEndpoint::Bldg(b) => match mode {
TripMode::Walk | TripMode::Transit => Some(map.get_b(b).front_path.sidewalk),
TripMode::Walk | TripMode::Transit => Some(map.get_b(b).sidewalk_pos),
TripMode::Bike => Some(DrivingGoal::ParkNear(b).goal_pos(PathConstraints::Bike, map)),
TripMode::Drive => Some(DrivingGoal::ParkNear(b).goal_pos(PathConstraints::Car, map)),
},

View File

@ -1087,7 +1087,7 @@ impl TutorialState {
)],
});
// Will definitely get there first
for i in 0..map.get_b(goal_bldg).parking.as_ref().unwrap().num_spots {
for i in 0..map.get_b(goal_bldg).num_parking_spots() {
scenario.people.push(PersonSpec {
id: PersonID(i + 1),
orig_id: None,

View File

@ -150,6 +150,10 @@ impl PolyLine {
pub fn must_extend(self, other: PolyLine) -> PolyLine {
self.extend(other).unwrap()
}
pub fn must_push(self, pt: Pt2D) -> PolyLine {
let new = PolyLine::must_new(vec![self.last_pt(), pt]);
self.must_extend(new)
}
// One or both args might be empty.
pub fn append(first: Vec<Pt2D>, second: Vec<Pt2D>) -> Result<Vec<Pt2D>, Box<dyn Error>> {

View File

@ -71,14 +71,20 @@ fn endpoints(
if let (Some(b1), Some(b2)) = (maybe_b1, maybe_b2) {
// TODO Super rough...
let start = if constraints == PathConstraints::Pedestrian {
Some(huge_map.get_b(b1).front_path.sidewalk)
Some(huge_map.get_b(b1).sidewalk_pos)
} else {
huge_map.get_b(b1).parking.as_ref().map(|p| p.driving_pos)
huge_map
.get_b(b1)
.driving_connection(huge_map)
.map(|(pos, _)| pos)
};
let end = if constraints == PathConstraints::Pedestrian {
Some(huge_map.get_b(b2).front_path.sidewalk)
Some(huge_map.get_b(b2).sidewalk_pos)
} else {
huge_map.get_b(b2).parking.as_ref().map(|p| p.driving_pos)
huge_map
.get_b(b2)
.driving_connection(huge_map)
.map(|(pos, _)| pos)
};
if let Some(path) = start.and_then(|start| {
end.and_then(|end| {

View File

@ -98,13 +98,13 @@ fn bidi_flood(map: &Map, start: LaneID, largest_group: &HashSet<LaneID>) -> Opti
// TODO Different cost function
pub fn all_costs_from(map: &Map, start: BuildingID) -> HashMap<BuildingID, Distance> {
let mut results = HashMap::new();
let start = map.get_b(start).front_path.sidewalk;
let start = map.get_b(start).sidewalk_pos;
// TODO This is SO inefficient. Flood out and mark off buildings as we go. Granularity of lane
// makes more sense.
for b in map.all_buildings() {
if let Some(path) = map.pathfind(PathRequest {
start,
end: b.front_path.sidewalk,
end: b.sidewalk_pos,
constraints: PathConstraints::Pedestrian,
}) {
// TODO Distance isn't an interesting thing to show at all, we want the path cost

View File

@ -16,9 +16,7 @@ pub use crate::edits::{
pub use crate::make::initial::lane_specs::RoadSpec;
pub use crate::map::MapConfig;
pub use crate::objects::area::{Area, AreaID, AreaType};
pub use crate::objects::building::{
Building, BuildingID, BuildingType, FrontPath, OffstreetParking,
};
pub use crate::objects::building::{Building, BuildingID, BuildingType, OffstreetParking};
pub use crate::objects::bus_stop::{BusRoute, BusRouteID, BusStop, BusStopID};
pub use crate::objects::intersection::{Intersection, IntersectionID, IntersectionType};
pub use crate::objects::lane::{

View File

@ -1,10 +1,8 @@
use crate::make::match_points_to_lanes;
use crate::raw::{OriginalBuilding, RawBuilding};
use crate::{
osm, Building, BuildingID, BuildingType, FrontPath, LaneID, LaneType, Map, OffstreetParking,
};
use crate::{osm, Building, BuildingID, BuildingType, LaneID, Map, OffstreetParking};
use abstutil::{Tags, Timer};
use geom::{Distance, HashablePt2D, Line, PolyLine, Polygon};
use geom::{Distance, HashablePt2D, Line, Polygon};
use rand::{Rng, SeedableRng};
use rand_xorshift::XorShiftRng;
use std::collections::{BTreeMap, BTreeSet, HashSet};
@ -27,7 +25,6 @@ pub fn make_all_buildings(
// equiv_pos could be a little closer, so use two buffers
let sidewalk_buffer = Distance::meters(7.5);
let driveway_buffer = Distance::meters(7.0);
let sidewalk_pts = match_points_to_lanes(
map.get_bounds(),
query,
@ -41,7 +38,7 @@ pub fn make_all_buildings(
);
let mut results = Vec::new();
timer.start_iter("create building front paths", center_per_bldg.len());
timer.start_iter("match buildings to sidewalks", center_per_bldg.len());
for (orig_id, bldg_center) in center_per_bldg {
timer.next();
if let Some(sidewalk_pos) = sidewalk_pts.get(&bldg_center) {
@ -59,61 +56,24 @@ pub fn make_all_buildings(
let id = BuildingID(results.len());
let mut rng = XorShiftRng::seed_from_u64(orig_id.osm_way_id as u64);
let mut bldg = Building {
results.push(Building {
id,
polygon: b.polygon.clone(),
address: get_address(&b.osm_tags, sidewalk_pos.lane(), map),
name: b.osm_tags.get(osm::NAME).cloned(),
osm_way_id: orig_id.osm_way_id,
front_path: FrontPath {
sidewalk: *sidewalk_pos,
line: sidewalk_line.clone(),
},
amenities: b.amenities.clone(),
parking: None,
label_center: b.polygon.polylabel(),
bldg_type: classify_bldg(
b.osm_tags.clone(),
&b.amenities,
b.polygon.area(),
&mut rng,
),
};
amenities: b.amenities.clone(),
bldg_type: classify_bldg(&b.osm_tags, &b.amenities, b.polygon.area(), &mut rng),
parking: if let Some(n) = b.public_garage_name.clone() {
OffstreetParking::PublicGarage(n, b.num_parking_spots)
} else {
OffstreetParking::Private(b.num_parking_spots)
},
// Can this building have a driveway? If it's not next to a driving lane, then no.
let sidewalk_lane = sidewalk_pos.lane();
if let Ok(driving_lane) = map
.get_parent(sidewalk_lane)
.find_closest_lane(sidewalk_lane, vec![LaneType::Driving])
{
let driving_pos = sidewalk_pos.equiv_pos(driving_lane, Distance::ZERO, map);
// This shouldn't fail much anymore, unless equiv_pos winds up being pretty
// different
if driving_pos.dist_along() > driveway_buffer
&& map.get_l(driving_lane).length() - driving_pos.dist_along() > driveway_buffer
{
let driveway_line = PolyLine::must_new(vec![
sidewalk_line.pt1(),
sidewalk_line.pt2(),
driving_pos.pt(map),
]);
bldg.parking = Some(OffstreetParking {
public_garage_name: b.public_garage_name.clone(),
num_spots: b.num_parking_spots,
driveway_line,
driving_pos,
});
}
}
if bldg.parking.is_none() {
timer.warn(format!(
"{} can't have a driveway. Forfeiting {} parking spots",
bldg.id, b.num_parking_spots
));
}
results.push(bldg);
sidewalk_pos: *sidewalk_pos,
driveway_geom: sidewalk_line.to_polyline(),
});
}
}
@ -150,7 +110,7 @@ fn get_address(tags: &Tags, sidewalk: LaneID, map: &Map) -> String {
}
fn classify_bldg(
tags: Tags,
tags: &Tags,
amenities: &BTreeSet<(String, String)>,
area_sq_meters: f64,
rng: &mut XorShiftRng,

View File

@ -188,7 +188,6 @@ impl Map {
dst_i,
lane_type: lane.lane_type,
parent: road_id,
building_paths: Vec::new(),
bus_stops: BTreeSet::new(),
parking_blackhole: None,
});
@ -268,15 +267,6 @@ impl Map {
timer.stop("find parking blackholes");
map.buildings = buildings::make_all_buildings(&raw.buildings, &map, timer);
for b in &map.buildings {
let lane = b.sidewalk();
// TODO Could be more performant and cleanly written
let mut bldgs = map.lanes[lane.0].building_paths.clone();
bldgs.push(b.id);
bldgs.sort_by_key(|b| map.buildings[b.0].front_path.sidewalk.dist_along());
map.lanes[lane.0].building_paths = bldgs;
}
map.parking_lots = parking_lots::make_all_parking_lots(
&raw.parking_lots,

View File

@ -2,8 +2,8 @@ use crate::raw::{DrivingSide, RawMap};
use crate::{
Area, AreaID, Building, BuildingID, BuildingType, BusRoute, BusRouteID, BusStop, BusStopID,
ControlStopSign, ControlTrafficSignal, Intersection, IntersectionID, Lane, LaneID, LaneType,
Map, MapEdits, ParkingLot, ParkingLotID, Path, PathConstraints, PathRequest, Position, Road,
RoadID, Turn, TurnGroupID, TurnID, TurnType,
Map, MapEdits, OffstreetParking, ParkingLot, ParkingLotID, Path, PathConstraints, PathRequest,
Position, Road, RoadID, Turn, TurnGroupID, TurnID, TurnType,
};
use abstutil::Timer;
use geom::{Angle, Bounds, Distance, GPSBounds, Line, PolyLine, Polygon, Pt2D, Ring};
@ -657,19 +657,15 @@ impl Map {
// TODO Sort of a temporary hack
pub fn hack_override_offstreet_spots(&mut self, spots_per_bldg: usize) {
for b in &mut self.buildings {
if let Some(ref mut p) = b.parking {
if p.public_garage_name.is_none() {
p.num_spots = spots_per_bldg;
}
if let OffstreetParking::Private(ref mut num_spots) = b.parking {
*num_spots = spots_per_bldg;
}
}
}
pub fn hack_override_offstreet_spots_individ(&mut self, b: BuildingID, spots: usize) {
let b = &mut self.buildings[b.0];
if let Some(ref mut p) = b.parking {
if p.public_garage_name.is_none() {
p.num_spots = spots;
}
if let OffstreetParking::Private(ref mut num_spots) = b.parking {
*num_spots = spots;
}
}

View File

@ -1,6 +1,6 @@
use crate::{LaneID, Position};
use crate::{LaneID, LaneType, Map, Position};
use abstutil::{deserialize_usize, serialize_usize};
use geom::{Line, PolyLine, Polygon, Pt2D};
use geom::{Distance, PolyLine, Polygon, Pt2D};
use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;
use std::fmt;
@ -20,26 +20,6 @@ impl fmt::Display for BuildingID {
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct FrontPath {
pub sidewalk: Position,
// Goes from the building to the sidewalk
pub line: Line,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct OffstreetParking {
// If filled out, this is a public parking garage with a name. If not, it's private parking
// just for trips to/from this building.
pub public_garage_name: Option<String>,
pub num_spots: usize,
// Goes from the building to the driving lane
pub driveway_line: PolyLine,
// Guaranteed to be at least 7m (MAX_CAR_LENGTH + a little buffer) away from both ends of the
// lane, to prevent various headaches
pub driving_pos: Position,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Building {
pub id: BuildingID,
@ -53,12 +33,22 @@ pub struct Building {
// TODO Might fold these into BuildingType::Commercial
// (Name, amenity)
pub amenities: BTreeSet<(String, String)>,
pub front_path: FrontPath,
// Every building can't have OffstreetParking, because the nearest usable driving lane (not in
// a parking blackhole) might be far away
pub parking: Option<OffstreetParking>,
pub bldg_type: BuildingType,
pub parking: OffstreetParking,
// The building's connection for pedestrians is immutable. For cars and bikes, it can change
// based on map edits, so don't cache it.
pub sidewalk_pos: Position,
// Goes from building to sidewalk
pub driveway_geom: PolyLine,
}
// Represent None as Private(0).
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub enum OffstreetParking {
// (Name, spots)
PublicGarage(String, usize),
Private(usize),
}
#[derive(Serialize, Deserialize, Debug)]
@ -81,7 +71,7 @@ impl BuildingType {
impl Building {
pub fn sidewalk(&self) -> LaneID {
self.front_path.sidewalk.lane()
self.sidewalk_pos.lane()
}
pub fn house_number(&self) -> Option<String> {
@ -92,4 +82,29 @@ impl Building {
None
}
}
// The polyline goes from the building to the driving position
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
let lane = map
.get_parent(self.sidewalk())
.find_closest_lane(self.sidewalk(), vec![LaneType::Driving])
.ok()?;
let pos = self.sidewalk_pos.equiv_pos(lane, Distance::ZERO, map);
// TODO Do we need to insist on this buffer, now that we can make cars gradually appear?
let buffer = Distance::meters(7.0);
if pos.dist_along() <= buffer || map.get_l(lane).length() - pos.dist_along() <= buffer {
return None;
}
Some((pos, self.driveway_geom.clone().must_push(pos.pt(map))))
}
pub fn num_parking_spots(&self) -> usize {
match self.parking {
OffstreetParking::PublicGarage(_, n) => n,
OffstreetParking::Private(n) => n,
}
}
}

View File

@ -1,7 +1,6 @@
use crate::pathfind;
use crate::{
osm, BuildingID, BusStopID, DirectedRoadID, IntersectionID, Map, PathConstraints, Road, RoadID,
TurnType,
osm, BusStopID, DirectedRoadID, IntersectionID, Map, PathConstraints, Road, RoadID, TurnType,
};
use abstutil::{deserialize_usize, serialize_usize};
use geom::{Distance, Line, PolyLine, Pt2D};
@ -112,8 +111,6 @@ pub struct Lane {
pub src_i: IntersectionID,
pub dst_i: IntersectionID,
// Sorted by distance of the front path
pub building_paths: Vec<BuildingID>,
// Meaningless order
pub bus_stops: BTreeSet<BusStopID>,

View File

@ -415,11 +415,10 @@ impl SidewalkSpot {
}
}
pub fn building(bldg: BuildingID, map: &Map) -> SidewalkSpot {
let front_path = &map.get_b(bldg).front_path;
pub fn building(b: BuildingID, map: &Map) -> SidewalkSpot {
SidewalkSpot {
connection: SidewalkPOI::Building(bldg),
sidewalk_pos: front_path.sidewalk,
connection: SidewalkPOI::Building(b),
sidewalk_pos: map.get_b(b).sidewalk_pos,
}
}

View File

@ -47,8 +47,8 @@ impl ScenarioGenerator {
let work = *workplaces.choose(rng).unwrap();
// Decide mode based on walking distance.
let dist = if let Some(path) = map.pathfind(PathRequest {
start: map.get_b(home).front_path.sidewalk,
end: map.get_b(work).front_path.sidewalk,
start: map.get_b(home).sidewalk_pos,
end: map.get_b(work).sidewalk_pos,
constraints: PathConstraints::Pedestrian,
}) {
path.total_length()

View File

@ -6,7 +6,8 @@ use crate::{
use abstutil::{prettyprint_usize, Counter, Timer};
use geom::{Distance, Duration, LonLat, Speed, Time, EPSILON_DIST};
use map_model::{
BuildingID, BusRouteID, BusStopID, DirectedRoadID, Map, PathConstraints, Position, RoadID,
BuildingID, BusRouteID, BusStopID, DirectedRoadID, Map, OffstreetParking, PathConstraints,
Position, RoadID,
};
use rand::seq::SliceRandom;
use rand::{Rng, SeedableRng};
@ -269,17 +270,9 @@ fn seed_parked_cars(
ParkingSpot::Onstreet(l, _) => (map.get_l(l).parent, None),
ParkingSpot::Offstreet(b, _) => (
map.get_l(map.get_b(b).sidewalk()).parent,
if map
.get_b(b)
.parking
.as_ref()
.unwrap()
.public_garage_name
.is_some()
{
None
} else {
Some(b)
match map.get_b(b).parking {
OffstreetParking::PublicGarage(_, _) => None,
OffstreetParking::Private(_) => Some(b),
},
),
ParkingSpot::Lot(pl, _) => (map.get_l(map.get_pl(pl).driving_pos.lane()).parent, None),

View File

@ -399,7 +399,7 @@ impl TripSpec {
constraints: PathConstraints::Pedestrian,
}),
TripSpec::UsingBike { start, .. } => Some(PathRequest {
start: map.get_b(*start).front_path.sidewalk,
start: map.get_b(*start).sidewalk_pos,
end: SidewalkSpot::bike_from_bike_rack(map.get_b(*start).sidewalk(), map)
.unwrap()
.sidewalk_pos,

View File

@ -149,9 +149,9 @@ impl Car {
_ => {
let driveway = match spot {
ParkingSpot::Offstreet(b, _) => {
&map.get_b(*b).parking.as_ref().unwrap().driveway_line
map.get_b(*b).driving_connection(map).unwrap().1
}
ParkingSpot::Lot(pl, _) => &map.get_pl(*pl).driveway_line,
ParkingSpot::Lot(pl, _) => map.get_pl(*pl).driveway_line.clone(),
_ => unreachable!(),
};

View File

@ -5,8 +5,8 @@ use abstutil::{
};
use geom::{Distance, PolyLine, Pt2D};
use map_model::{
BuildingID, Lane, LaneID, LaneType, Map, ParkingLotID, PathConstraints, PathStep, Position,
Traversable, TurnID,
BuildingID, Lane, LaneID, LaneType, Map, OffstreetParking, ParkingLotID, PathConstraints,
PathStep, Position, Traversable, TurnID,
};
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap};
@ -36,11 +36,12 @@ pub struct ParkingSimState {
// Off-street
num_spots_per_offstreet: BTreeMap<BuildingID, usize>,
// Cache dist_along
#[serde(
serialize_with = "serialize_multimap",
deserialize_with = "deserialize_multimap"
)]
driving_to_offstreet: MultiMap<LaneID, BuildingID>,
driving_to_offstreet: MultiMap<LaneID, (BuildingID, Distance)>,
// Parking lots
num_spots_per_lot: BTreeMap<ParkingLotID, usize>,
@ -78,10 +79,14 @@ impl ParkingSimState {
}
}
for b in map.all_buildings() {
if let Some(ref p) = b.parking {
if map.get_l(p.driving_pos.lane()).parking_blackhole.is_none() {
sim.num_spots_per_offstreet.insert(b.id, p.num_spots);
sim.driving_to_offstreet.insert(p.driving_pos.lane(), b.id);
if let Some((pos, _)) = b.driving_connection(map) {
if map.get_l(pos.lane()).parking_blackhole.is_none() {
let num_spots = b.num_parking_spots();
if num_spots > 0 {
sim.num_spots_per_offstreet.insert(b.id, num_spots);
sim.driving_to_offstreet
.insert(pos.lane(), (b.id, pos.dist_along()));
}
}
}
}
@ -293,13 +298,13 @@ impl ParkingSimState {
}
}
for b in self.driving_to_offstreet.get(driving_pos.lane()) {
let parking = map.get_b(*b).parking.as_ref().unwrap();
if parking.public_garage_name.is_none() && target != *b {
continue;
for (b, bldg_dist) in self.driving_to_offstreet.get(driving_pos.lane()) {
if let OffstreetParking::Private(_) = map.get_b(*b).parking {
if target != *b {
continue;
}
}
let bldg_dist = parking.driving_pos.dist_along();
if driving_pos.dist_along() < bldg_dist {
if driving_pos.dist_along() < *bldg_dist {
for idx in 0..self.num_spots_per_offstreet[b] {
let spot = ParkingSpot::Offstreet(*b, idx);
if self.is_free(spot) {
@ -337,7 +342,7 @@ impl ParkingSimState {
map,
)
}
ParkingSpot::Offstreet(b, _) => map.get_b(b).parking.as_ref().unwrap().driving_pos,
ParkingSpot::Offstreet(b, _) => map.get_b(b).driving_connection(map).unwrap().0,
ParkingSpot::Lot(pl, _) => map.get_pl(pl).driving_pos,
}
}
@ -353,7 +358,7 @@ impl ParkingSimState {
)
.equiv_pos(lane.sidewalk, Distance::ZERO, map)
}
ParkingSpot::Offstreet(b, _) => map.get_b(b).front_path.sidewalk,
ParkingSpot::Offstreet(b, _) => map.get_b(b).sidewalk_pos,
ParkingSpot::Lot(pl, _) => map.get_pl(pl).sidewalk_pos,
}
}

View File

@ -72,7 +72,7 @@ impl WalkingSimState {
SidewalkPOI::Building(b) | SidewalkPOI::ParkingSpot(ParkingSpot::Offstreet(b, _)) => {
PedState::LeavingBuilding(
b,
TimeInterval::new(now, now + map.get_b(b).front_path.line.length() / ped.speed),
TimeInterval::new(now, now + map.get_b(b).driveway_geom.length() / ped.speed),
)
}
SidewalkPOI::ParkingSpot(ParkingSpot::Lot(pl, _)) => PedState::LeavingParkingLot(
@ -158,7 +158,7 @@ impl WalkingSimState {
b,
TimeInterval::new(
now,
now + map.get_b(b).front_path.line.length() / ped.speed,
now + map.get_b(b).driveway_geom.length() / ped.speed,
),
);
scheduler.push(ped.state.get_end_time(), Command::UpdatePed(ped.id));
@ -247,8 +247,7 @@ impl WalkingSimState {
}
}
PedState::LeavingBuilding(b, _) => {
ped.state =
ped.crossing_state(map.get_b(b).front_path.sidewalk.dist_along(), now, map);
ped.state = ped.crossing_state(map.get_b(b).sidewalk_pos.dist_along(), now, map);
scheduler.push(ped.state.get_end_time(), Command::UpdatePed(ped.id));
}
PedState::EnteringBuilding(bldg, _) => {
@ -396,11 +395,11 @@ impl WalkingSimState {
on: Traversable,
map: &Map,
) -> (Vec<DrawPedestrianInput>, Vec<DrawPedCrowdInput>) {
// Classify into direction-based groups or by building/parking lot front path.
// Classify into direction-based groups or by building/parking lot driveway.
let mut forwards: Vec<(PedestrianID, Distance)> = Vec::new();
let mut backwards: Vec<(PedestrianID, Distance)> = Vec::new();
let mut bldg_front_path: MultiMap<BuildingID, (PedestrianID, Distance)> = MultiMap::new();
let mut lot_front_path: MultiMap<ParkingLotID, (PedestrianID, Distance)> = MultiMap::new();
let mut bldg_driveway: MultiMap<BuildingID, (PedestrianID, Distance)> = MultiMap::new();
let mut lot_driveway: MultiMap<ParkingLotID, (PedestrianID, Distance)> = MultiMap::new();
for id in self.peds_per_traversable.get(on) {
let ped = &self.peds[id];
@ -422,20 +421,20 @@ impl WalkingSimState {
}
}
PedState::LeavingBuilding(b, ref int) => {
let len = map.get_b(b).front_path.line.length();
bldg_front_path.insert(b, (*id, int.percent(now) * len));
let len = map.get_b(b).driveway_geom.length();
bldg_driveway.insert(b, (*id, int.percent(now) * len));
}
PedState::EnteringBuilding(b, ref int) => {
let len = map.get_b(b).front_path.line.length();
bldg_front_path.insert(b, (*id, (1.0 - int.percent(now)) * len));
let len = map.get_b(b).driveway_geom.length();
bldg_driveway.insert(b, (*id, (1.0 - int.percent(now)) * len));
}
PedState::LeavingParkingLot(pl, ref int) => {
let len = map.get_pl(pl).sidewalk_line.length();
lot_front_path.insert(pl, (*id, int.percent(now) * len));
lot_driveway.insert(pl, (*id, int.percent(now) * len));
}
PedState::EnteringParkingLot(pl, ref int) => {
let len = map.get_pl(pl).sidewalk_line.length();
lot_front_path.insert(pl, (*id, (1.0 - int.percent(now)) * len));
lot_driveway.insert(pl, (*id, (1.0 - int.percent(now)) * len));
}
PedState::StartingToBike(_, _, _)
| PedState::FinishingBiking(_, _, _)
@ -463,17 +462,17 @@ impl WalkingSimState {
),
]
.into_iter()
.chain(bldg_front_path.consume().into_iter().map(|(b, set)| {
.chain(bldg_driveway.consume().into_iter().map(|(b, set)| {
(
set.into_iter().collect::<Vec<_>>(),
PedCrowdLocation::BldgFrontPath(b),
map.get_b(b).front_path.line.length(),
PedCrowdLocation::BldgDriveway(b),
map.get_b(b).driveway_geom.length(),
)
}))
.chain(lot_front_path.consume().into_iter().map(|(pl, set)| {
.chain(lot_driveway.consume().into_iter().map(|(pl, set)| {
(
set.into_iter().collect::<Vec<_>>(),
PedCrowdLocation::LotFrontPath(pl),
PedCrowdLocation::LotDriveway(pl),
map.get_pl(pl).sidewalk_line.length(),
)
})) {
@ -542,7 +541,7 @@ impl Pedestrian {
PedState::Crossing(ref dist_int, ref time_int) => dist_int.lerp(time_int.percent(now)),
PedState::WaitingToTurn(dist, _) => dist,
PedState::LeavingBuilding(b, _) | PedState::EnteringBuilding(b, _) => {
map.get_b(b).front_path.sidewalk.dist_along()
map.get_b(b).sidewalk_pos.dist_along()
}
PedState::LeavingParkingLot(pl, _) | PedState::EnteringParkingLot(pl, _) => {
map.get_pl(pl).sidewalk_pos.dist_along()
@ -592,24 +591,24 @@ impl Pedestrian {
facing,
)
}
// If we're on some tiny line and percent_along fails, just fall back to to some point
// on the line instead of crashing.
PedState::LeavingBuilding(b, ref time_int) => {
let line = &map.get_b(b).front_path.line;
(
line.percent_along(time_int.percent(now))
.unwrap_or(line.pt1()),
line.angle(),
)
let pl = &map.get_b(b).driveway_geom;
// If we're on some tiny line and percent_along fails, just fall back to to some
// point on the line instead of crashing.
if let Ok(pair) = pl.dist_along(time_int.percent(now) * pl.length()) {
pair
} else {
(pl.first_pt(), pl.first_line().angle())
}
}
PedState::EnteringBuilding(b, ref time_int) => {
let line = &map.get_b(b).front_path.line;
(
line.reverse()
.percent_along(time_int.percent(now))
.unwrap_or(line.pt1()),
line.angle().opposite(),
)
let pl = &map.get_b(b).driveway_geom;
if let Ok((pt, angle)) = pl.dist_along((1.0 - time_int.percent(now)) * pl.length())
{
(pt, angle.opposite())
} else {
(pl.first_pt(), pl.first_line().angle().opposite())
}
}
PedState::LeavingParkingLot(pl, ref time_int) => {
let line = &map.get_pl(pl).sidewalk_line;

View File

@ -25,8 +25,8 @@ pub struct DrawPedCrowdInput {
pub enum PedCrowdLocation {
// bool is contraflow
Sidewalk(Traversable, bool),
BldgFrontPath(BuildingID),
LotFrontPath(ParkingLotID),
BldgDriveway(BuildingID),
LotDriveway(ParkingLotID),
}
#[derive(Clone)]

View File

@ -243,25 +243,26 @@ impl Router {
target,
map,
);
let best = if let Some(ref p) = map.get_b(target).parking {
if p.driving_pos.lane() == current_lane {
let target_dist = p.driving_pos.dist_along();
// Closest to the building
candidates
.into_iter()
.min_by_key(|(_, pos)| (pos.dist_along() - target_dist).abs())
let best =
if let Some((driving_pos, _)) = map.get_b(target).driving_connection(map) {
if driving_pos.lane() == current_lane {
let target_dist = driving_pos.dist_along();
// Closest to the building
candidates
.into_iter()
.min_by_key(|(_, pos)| (pos.dist_along() - target_dist).abs())
} else {
// Closest to the road endpoint, I guess
candidates
.into_iter()
.min_by_key(|(_, pos)| pos.dist_along())
}
} else {
// Closest to the road endpoint, I guess
candidates
.into_iter()
.min_by_key(|(_, pos)| pos.dist_along())
}
} else {
// Closest to the road endpoint, I guess
candidates
.into_iter()
.min_by_key(|(_, pos)| pos.dist_along())
};
};
if let Some((new_spot, new_pos)) = best {
if let Some((t, p)) = trip_and_person {
events.push(Event::TripPhaseStarting(