mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-26 16:02:23 +03:00
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:
parent
ec52adce77
commit
d22aa87139
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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")
|
||||
|
@ -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),
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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)),
|
||||
},
|
||||
|
@ -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,
|
||||
|
@ -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>> {
|
||||
|
@ -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| {
|
||||
|
@ -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
|
||||
|
@ -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::{
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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>,
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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),
|
||||
|
@ -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,
|
||||
|
@ -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!(),
|
||||
};
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)]
|
||||
|
@ -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(
|
||||
|
Loading…
Reference in New Issue
Block a user