Skip to content

Commit

Permalink
Repair parking lot connections after road widening. #597
Browse files Browse the repository at this point in the history
  • Loading branch information
dabreegster committed Apr 12, 2021
1 parent 6087204 commit 7fa75b5
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 48 deletions.
3 changes: 3 additions & 0 deletions game/src/edit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -709,6 +709,9 @@ pub fn apply_map_edits(ctx: &mut EventCtx, app: &mut App, edits: MapEdits) {
.draw_map
.recreate_building_paths(ctx, &app.primary.map, &app.cs, &app.opts);
}
for pl in effects.changed_parking_lots {
app.primary.draw_map.get_pl(pl).clear_rendering();
}

if app.primary.layer.as_ref().and_then(|l| l.name()) == Some("map edits") {
app.primary.layer = Some(Box::new(crate::layer::map::Static::edits(ctx, app)));
Expand Down
4 changes: 4 additions & 0 deletions map_gui/src/render/parking_lot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ impl DrawParkingLot {

batch
}

pub fn clear_rendering(&self) {
*self.draw.borrow_mut() = None;
}
}

impl Renderable for DrawParkingLot {
Expand Down
63 changes: 58 additions & 5 deletions map_model/src/edits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ use geom::{Distance, HashablePt2D, Line, Speed, Time};

pub use self::perma::PermanentMapEdits;
use crate::make::initial::lane_specs::get_lane_specs_ltr;
use crate::make::{match_points_to_lanes, trim_path};
use crate::make::{match_points_to_lanes, snap_driveway, trim_path};
use crate::{
connectivity, AccessRestrictions, BuildingID, BusRouteID, ControlStopSign,
ControlTrafficSignal, IntersectionID, IntersectionType, LaneID, LaneSpec, Map, MapConfig,
PathConstraints, Pathfinder, Road, RoadID, TurnID, Zone,
ParkingLotID, PathConstraints, Pathfinder, Road, RoadID, TurnID, Zone,
};

mod compat;
Expand Down Expand Up @@ -139,6 +139,7 @@ pub struct EditEffects {
pub added_turns: BTreeSet<TurnID>,
pub deleted_turns: BTreeSet<TurnID>,
pub resnapped_buildings: bool,
pub changed_parking_lots: BTreeSet<ParkingLotID>,
}

impl MapEdits {
Expand Down Expand Up @@ -537,18 +538,31 @@ fn modify_lanes(map: &mut Map, r: RoadID, lanes_ltr: Vec<LaneSpec>, effects: &mu
modified_lanes.insert(id);
}
}
modified_lanes.extend(effects.deleted_lanes.clone());

// Find all buildings connected to modified/deleted sidewalks
let mut recalc_buildings = Vec::new();
for b in map.all_buildings() {
if effects.deleted_lanes.contains(&b.sidewalk()) || modified_lanes.contains(&b.sidewalk()) {
if modified_lanes.contains(&b.sidewalk()) {
recalc_buildings.push(b.id);
effects.resnapped_buildings = true;
}
effects.resnapped_buildings = true;
}
fix_buildings(map, recalc_buildings);

// TODO We need to update bus stops and parking lots -- they may refer to an old ID.
// Same for parking lots
let mut recalc_parking_lots = Vec::new();
for pl in map.all_parking_lots() {
if modified_lanes.contains(&pl.driving_pos.lane())
|| modified_lanes.contains(&pl.sidewalk_pos.lane())
{
recalc_parking_lots.push(pl.id);
effects.changed_parking_lots.insert(pl.id);
}
}
fix_parking_lots(map, recalc_parking_lots);

// TODO We need to update bus stops -- they may refer to an old ID.
}

// Returns the other roads affected by this change, not counting changed_road.
Expand Down Expand Up @@ -651,6 +665,44 @@ fn fix_buildings(map: &mut Map, input: Vec<BuildingID>) {
}
}

fn fix_parking_lots(map: &mut Map, input: Vec<ParkingLotID>) {
// TODO Partly copying from make/parking_lots.rs
let mut center_per_lot: Vec<(ParkingLotID, HashablePt2D)> = Vec::new();
let mut query: HashSet<HashablePt2D> = HashSet::new();
for id in input {
let center = map.get_pl(id).polygon.center().to_hashable();
center_per_lot.push((id, center));
query.insert(center);
}

let sidewalk_buffer = Distance::meters(7.5);
let sidewalk_pts = match_points_to_lanes(
map.get_bounds(),
query,
map.all_lanes(),
|l| l.is_walkable(),
sidewalk_buffer,
Distance::meters(1000.0),
&mut Timer::throwaway(),
);

for (id, center) in center_per_lot {
match snap_driveway(center, &map.get_pl(id).polygon, &sidewalk_pts, map) {
Ok((driveway_line, driving_pos, sidewalk_line, sidewalk_pos)) => {
let pl = &mut map.parking_lots[id.0];
pl.driveway_line = driveway_line;
pl.driving_pos = driving_pos;
pl.sidewalk_line = sidewalk_line;
pl.sidewalk_pos = sidewalk_pos;
}
Err(err) => {
// TODO Not sure what to do here yet.
error!("{} isn't snapped to a sidewalk now: {}", id, err);
}
}
}
}

impl Map {
pub fn new_edits(&self) -> MapEdits {
let mut edits = MapEdits::new();
Expand Down Expand Up @@ -731,6 +783,7 @@ impl Map {
added_turns: BTreeSet::new(),
deleted_turns: BTreeSet::new(),
resnapped_buildings: false,
changed_parking_lots: BTreeSet::new(),
};

// Short-circuit to avoid marking pathfinder_dirty
Expand Down
1 change: 1 addition & 0 deletions map_model/src/make/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use geom::{
Bounds, Distance, FindClosest, GPSBounds, HashablePt2D, Line, Polygon, Speed, EPSILON_DIST,
};

pub use self::parking_lots::snap_driveway;
use crate::pathfind::Pathfinder;
use crate::raw::{OriginalRoad, RawMap};
use crate::{
Expand Down
98 changes: 55 additions & 43 deletions map_model/src/make/parking_lots.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};

use anyhow::Result;

use abstutil::{Parallelism, Timer};
use geom::{Angle, Distance, FindClosest, HashablePt2D, Line, PolyLine, Polygon, Pt2D, Ring};
Expand Down Expand Up @@ -29,7 +31,6 @@ pub fn make_all_parking_lots(
}

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,
Expand All @@ -44,40 +45,8 @@ pub fn make_all_parking_lots(
timer.start_iter("create parking lot driveways", center_per_lot.len());
for (lot_center, orig) in center_per_lot.into_iter().zip(input.iter()) {
timer.next();
// TODO Refactor this
if let Some(sidewalk_pos) = sidewalk_pts.get(&lot_center) {
let sidewalk_line = match Line::new(lot_center.to_pt2d(), sidewalk_pos.pt(map)) {
Some(l) => trim_path(&orig.polygon, l),
None => {
warn!(
"Skipping parking lot {} because front path has 0 length",
orig.osm_id
);
continue;
}
};

// Can this lot have a driveway? If it's not next to a driving lane, then no.
let mut driveway: Option<(PolyLine, Position)> = None;
let sidewalk_lane = sidewalk_pos.lane();
if let Some(driving_pos) = map
.get_parent(sidewalk_lane)
.find_closest_lane(sidewalk_lane, |l| PathConstraints::Car.can_use(l, map), map)
.and_then(|l| {
sidewalk_pos
.equiv_pos(l, map)
.buffer_dist(driveway_buffer, map)
})
{
if let Ok(pl) = PolyLine::new(vec![
sidewalk_line.pt1(),
sidewalk_line.pt2(),
driving_pos.pt(map),
]) {
driveway = Some((pl, driving_pos));
}
}
if let Some((driveway_line, driving_pos)) = driveway {
match snap_driveway(lot_center, &orig.polygon, &sidewalk_pts, map) {
Ok((driveway_line, driving_pos, sidewalk_line, sidewalk_pos)) => {
let id = ParkingLotID(results.len());
results.push(ParkingLot {
id,
Expand All @@ -90,14 +59,11 @@ pub fn make_all_parking_lots(
driveway_line,
driving_pos,
sidewalk_line,
sidewalk_pos: *sidewalk_pos,
sidewalk_pos,
});
} else {
warn!(
"Parking lot from {}, near sidewalk {}, can't have a driveway.",
orig.osm_id,
sidewalk_pos.lane()
);
}
Err(err) => {
warn!("Skipping parking lot {}: {}", orig.osm_id, err);
}
}
}
Expand Down Expand Up @@ -169,6 +135,52 @@ pub fn make_all_parking_lots(
results
}

/// Returns (driveway_line, driving_pos, sidewalk_line, sidewalk_pos)
pub fn snap_driveway(
center: HashablePt2D,
polygon: &Polygon,
sidewalk_pts: &HashMap<HashablePt2D, Position>,
map: &Map,
) -> Result<(PolyLine, Position, Line, Position)> {
let driveway_buffer = Distance::meters(7.0);

let sidewalk_pos = sidewalk_pts
.get(&center)
.ok_or(anyhow!("parking lot center didn't snap to a sidewalk"))?;
let sidewalk_line = match Line::new(center.to_pt2d(), sidewalk_pos.pt(map)) {
Some(l) => trim_path(polygon, l),
None => {
bail!("front path has 0 length");
}
};

// Can this lot have a driveway? If it's not next to a driving lane, then no.
let mut driveway: Option<(PolyLine, Position)> = None;
let sidewalk_lane = sidewalk_pos.lane();
if let Some(driving_pos) = map
.get_parent(sidewalk_lane)
.find_closest_lane(sidewalk_lane, |l| PathConstraints::Car.can_use(l, map), map)
.and_then(|l| {
sidewalk_pos
.equiv_pos(l, map)
.buffer_dist(driveway_buffer, map)
})
{
if let Ok(pl) = PolyLine::new(vec![
sidewalk_line.pt1(),
sidewalk_line.pt2(),
driving_pos.pt(map),
]) {
driveway = Some((pl, driving_pos));
}
}
let (driveway_line, driving_pos) = driveway.ok_or(anyhow!(
"snapped to sidewalk {}, but no driving connection",
sidewalk_pos.lane()
))?;
Ok((driveway_line, driving_pos, sidewalk_line, *sidewalk_pos))
}

fn infer_spots(lot_polygon: &Polygon, aisles: &Vec<Vec<Pt2D>>) -> Vec<(Pt2D, Angle)> {
let mut spots = Vec::new();
let mut finalized_lines = Vec::new();
Expand Down

0 comments on commit 7fa75b5

Please sign in to comment.