mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-25 07:25:47 +03:00
encode parking spots cheaply, compute up-front
This commit is contained in:
parent
b69d155812
commit
ea6a5c0f9d
@ -364,16 +364,16 @@ data/input/seattle/sidewalks.bin,034dd47ab77902dbc81c0107f13d8965,https://www.dr
|
|||||||
data/input/seattle/sidewalks.kml,94d385ba03ef1b57a5ba10965913ec6c,https://www.dropbox.com/s/vn8amar9xi6vbvh/sidewalks.kml.zip?dl=0
|
data/input/seattle/sidewalks.kml,94d385ba03ef1b57a5ba10965913ec6c,https://www.dropbox.com/s/vn8amar9xi6vbvh/sidewalks.kml.zip?dl=0
|
||||||
data/input/seattle/trips_2014.csv,d4a8e733045b28c0385fb81359d6df03,https://www.dropbox.com/s/5ppravwmk6bf20d/trips_2014.csv.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,018968486daedf1b69a1b3be5a3749bf,https://www.dropbox.com/s/eupzog6iw7wtaas/seattle.bin.zip?dl=0
|
data/system/cities/seattle.bin,018968486daedf1b69a1b3be5a3749bf,https://www.dropbox.com/s/eupzog6iw7wtaas/seattle.bin.zip?dl=0
|
||||||
data/system/maps/ballard.bin,2abeb9837c0c2ecb89dfe7ec6c9223df,https://www.dropbox.com/s/u4rvz50she3yrk0/ballard.bin.zip?dl=0
|
data/system/maps/ballard.bin,78198be4e5a03a59c63cdd61846efc60,https://www.dropbox.com/s/u4rvz50she3yrk0/ballard.bin.zip?dl=0
|
||||||
data/system/maps/downtown.bin,f89e8ab3aefd66cc7d98adfd5071f6ce,https://www.dropbox.com/s/4do5cg4vc17lafo/downtown.bin.zip?dl=0
|
data/system/maps/downtown.bin,3555b5dea0a14c15e313026dc28f00ea,https://www.dropbox.com/s/4do5cg4vc17lafo/downtown.bin.zip?dl=0
|
||||||
data/system/maps/downtown_atx.bin,0902409dad2c3f933027ae3651936254,https://www.dropbox.com/s/5avnbkd4oxby2hs/downtown_atx.bin.zip?dl=0
|
data/system/maps/downtown_atx.bin,0902409dad2c3f933027ae3651936254,https://www.dropbox.com/s/5avnbkd4oxby2hs/downtown_atx.bin.zip?dl=0
|
||||||
data/system/maps/huge_austin.bin,6a891b6a7597fd7f44e0229dd002773a,https://www.dropbox.com/s/khy0m6v9yt0gjnt/huge_austin.bin.zip?dl=0
|
data/system/maps/huge_austin.bin,6a891b6a7597fd7f44e0229dd002773a,https://www.dropbox.com/s/khy0m6v9yt0gjnt/huge_austin.bin.zip?dl=0
|
||||||
data/system/maps/huge_seattle.bin,53b71c4d1b723f2a61d2bf29cc3ff0ab,https://www.dropbox.com/s/btvr3qajshnivhb/huge_seattle.bin.zip?dl=0
|
data/system/maps/huge_seattle.bin,80e2d8068ada8624b9d8129c8728e74a,https://www.dropbox.com/s/btvr3qajshnivhb/huge_seattle.bin.zip?dl=0
|
||||||
data/system/maps/lakeslice.bin,6e02eaf499068e1a135a88fa38f8ae54,https://www.dropbox.com/s/99zi0gcbyvqrkud/lakeslice.bin.zip?dl=0
|
data/system/maps/lakeslice.bin,061a9c3e377bac0ae5db460c67c3eea9,https://www.dropbox.com/s/99zi0gcbyvqrkud/lakeslice.bin.zip?dl=0
|
||||||
data/system/maps/montlake.bin,e1991e7ae12df5e24a9e01d0409b184d,https://www.dropbox.com/s/zvhm2j5lavixxcr/montlake.bin.zip?dl=0
|
data/system/maps/montlake.bin,d58d14b9fbae8cfdf6c547fe140ff3fe,https://www.dropbox.com/s/zvhm2j5lavixxcr/montlake.bin.zip?dl=0
|
||||||
data/system/maps/mt_baker.bin,57a2761b4d1dfe9ab23c5ab9ba7e70b8,https://www.dropbox.com/s/cetje663p04cbgp/mt_baker.bin.zip?dl=0
|
data/system/maps/mt_baker.bin,09dd0d4220e1ebbed4fc730ed2081933,https://www.dropbox.com/s/cetje663p04cbgp/mt_baker.bin.zip?dl=0
|
||||||
data/system/maps/udistrict.bin,0e848ef2bd824d9656097e3f4ac39b19,https://www.dropbox.com/s/zqt2je8fadssz5j/udistrict.bin.zip?dl=0
|
data/system/maps/udistrict.bin,4de690facca9297f148396f3f09c3a6a,https://www.dropbox.com/s/zqt2je8fadssz5j/udistrict.bin.zip?dl=0
|
||||||
data/system/maps/west_seattle.bin,c9c394a9fb831bcbaa915bce81ae937f,https://www.dropbox.com/s/5pp1ik9l40yj3wh/west_seattle.bin.zip?dl=0
|
data/system/maps/west_seattle.bin,867b16afdf12c484d680bfbc73cbd919,https://www.dropbox.com/s/5pp1ik9l40yj3wh/west_seattle.bin.zip?dl=0
|
||||||
data/system/prebaked_results/lakeslice/weekday.bin,cbd73a7a14a8406a99cafac4e2eedf5b,https://www.dropbox.com/s/1c1sohvy50263wg/weekday.bin.zip?dl=0
|
data/system/prebaked_results/lakeslice/weekday.bin,cbd73a7a14a8406a99cafac4e2eedf5b,https://www.dropbox.com/s/1c1sohvy50263wg/weekday.bin.zip?dl=0
|
||||||
data/system/prebaked_results/montlake/car vs bike contention.bin,ac46a6b81a65431209075a7de5ed9227,https://www.dropbox.com/s/jefg0ikjy9dsrdd/car%20vs%20bike%20contention.bin.zip?dl=0
|
data/system/prebaked_results/montlake/car vs bike contention.bin,ac46a6b81a65431209075a7de5ed9227,https://www.dropbox.com/s/jefg0ikjy9dsrdd/car%20vs%20bike%20contention.bin.zip?dl=0
|
||||||
data/system/prebaked_results/montlake/weekday.bin,cd953900d32389809fb2dda206789528,https://www.dropbox.com/s/1aq7n9ow8tfqb5d/weekday.bin.zip?dl=0
|
data/system/prebaked_results/montlake/weekday.bin,cd953900d32389809fb2dda206789528,https://www.dropbox.com/s/1aq7n9ow8tfqb5d/weekday.bin.zip?dl=0
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate::app::App;
|
use crate::app::App;
|
||||||
use crate::info::{header_btns, make_tabs, Details, Tab};
|
use crate::info::{header_btns, make_tabs, Details, Tab};
|
||||||
|
use abstutil::prettyprint_usize;
|
||||||
use ezgui::{EventCtx, Line, TextExt, Widget};
|
use ezgui::{EventCtx, Line, TextExt, Widget};
|
||||||
use map_model::ParkingLotID;
|
use map_model::ParkingLotID;
|
||||||
|
|
||||||
@ -8,16 +9,22 @@ pub fn info(ctx: &mut EventCtx, app: &App, details: &mut Details, id: ParkingLot
|
|||||||
let pl = app.primary.map.get_pl(id);
|
let pl = app.primary.map.get_pl(id);
|
||||||
|
|
||||||
if let Some(n) = pl.capacity {
|
if let Some(n) = pl.capacity {
|
||||||
rows.push(format!("{} spots (from OSM)", n).draw_text(ctx));
|
rows.push(format!("{} spots (from OSM)", prettyprint_usize(n)).draw_text(ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 250 square feet is around 23 square meters
|
// 250 square feet is around 23 square meters
|
||||||
rows.push(format!("{} spots (from area)", (pl.polygon.area() / 23.0) as usize).draw_text(ctx));
|
rows.push(
|
||||||
|
format!(
|
||||||
|
"{} spots (from area)",
|
||||||
|
prettyprint_usize((pl.polygon.area() / 23.0) as usize)
|
||||||
|
)
|
||||||
|
.draw_text(ctx),
|
||||||
|
);
|
||||||
|
|
||||||
rows.push(
|
rows.push(
|
||||||
format!(
|
format!(
|
||||||
"{} spots (from geometry)",
|
"{} spots (from geometry)",
|
||||||
app.primary.draw_map.get_pl(id).inferred_spots
|
prettyprint_usize(pl.spots.len())
|
||||||
)
|
)
|
||||||
.draw_text(ctx),
|
.draw_text(ctx),
|
||||||
);
|
);
|
||||||
|
@ -10,7 +10,6 @@ use map_model::{
|
|||||||
|
|
||||||
pub struct DrawParkingLot {
|
pub struct DrawParkingLot {
|
||||||
pub id: ParkingLotID,
|
pub id: ParkingLotID,
|
||||||
pub inferred_spots: usize,
|
|
||||||
draw: Drawable,
|
draw: Drawable,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,11 +58,33 @@ impl DrawParkingLot {
|
|||||||
front_path_line.make_polygons(NORMAL_LANE_THICKNESS),
|
front_path_line.make_polygons(NORMAL_LANE_THICKNESS),
|
||||||
);
|
);
|
||||||
batch.push(cs.parking_lot, lot.polygon.clone());
|
batch.push(cs.parking_lot, lot.polygon.clone());
|
||||||
let inferred_spots = infer_spots(cs, lot, &mut batch);
|
for aisle in &lot.aisles {
|
||||||
|
let aisle_thickness = NORMAL_LANE_THICKNESS / 2.0;
|
||||||
|
batch.push(
|
||||||
|
cs.driving_lane,
|
||||||
|
PolyLine::unchecked_new(aisle.clone()).make_polygons(aisle_thickness),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let width = NORMAL_LANE_THICKNESS;
|
||||||
|
let height = 0.8 * PARKING_SPOT_LENGTH;
|
||||||
|
for (pt, angle) in &lot.spots {
|
||||||
|
let left = pt.project_away(width / 2.0, angle.rotate_degs(90.0));
|
||||||
|
let right = pt.project_away(width / 2.0, angle.rotate_degs(-90.0));
|
||||||
|
|
||||||
|
batch.push(
|
||||||
|
cs.general_road_marking,
|
||||||
|
PolyLine::new(vec![
|
||||||
|
left.project_away(height, *angle),
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
right.project_away(height, *angle),
|
||||||
|
])
|
||||||
|
.make_polygons(Distance::meters(0.25)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
DrawParkingLot {
|
DrawParkingLot {
|
||||||
id: lot.id,
|
id: lot.id,
|
||||||
inferred_spots,
|
|
||||||
draw: prerender.upload(batch),
|
draw: prerender.upload(batch),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,89 +116,3 @@ impl Renderable for DrawParkingLot {
|
|||||||
map.get_pl(self.id).polygon.contains_pt(pt)
|
map.get_pl(self.id).polygon.contains_pt(pt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn line_valid(lot: &ParkingLot, line: &Line, finalized_lines: &Vec<Line>) -> bool {
|
|
||||||
// Don't leak out of the parking lot
|
|
||||||
// TODO Entire line
|
|
||||||
if !lot.polygon.contains_pt(line.pt1()) || !lot.polygon.contains_pt(line.pt2()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't let this line hit another line
|
|
||||||
if finalized_lines.iter().any(|other| line.crosses(other)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't hit an aisle
|
|
||||||
if lot.aisles.iter().any(|pts| {
|
|
||||||
PolyLine::unchecked_new(pts.clone())
|
|
||||||
.intersection(&line.to_polyline())
|
|
||||||
.is_some()
|
|
||||||
}) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the number of spots
|
|
||||||
fn infer_spots(cs: &ColorScheme, lot: &ParkingLot, batch: &mut GeomBatch) -> usize {
|
|
||||||
let mut total_spots = 0;
|
|
||||||
let mut finalized_lines = Vec::new();
|
|
||||||
|
|
||||||
for aisle in &lot.aisles {
|
|
||||||
let aisle_thickness = NORMAL_LANE_THICKNESS / 2.0;
|
|
||||||
let pl = PolyLine::unchecked_new(aisle.clone());
|
|
||||||
batch.push(cs.driving_lane, pl.make_polygons(aisle_thickness));
|
|
||||||
|
|
||||||
for rotate in vec![90.0, -90.0] {
|
|
||||||
// Blindly generate all of the lines
|
|
||||||
let lines = {
|
|
||||||
let mut lines = Vec::new();
|
|
||||||
let mut start = Distance::ZERO;
|
|
||||||
while start + NORMAL_LANE_THICKNESS < pl.length() {
|
|
||||||
let (pt, angle) = pl.dist_along(start);
|
|
||||||
start += NORMAL_LANE_THICKNESS;
|
|
||||||
let theta = angle.rotate_degs(rotate);
|
|
||||||
lines.push(Line::new(
|
|
||||||
pt.project_away(aisle_thickness / 2.0, theta),
|
|
||||||
// The full PARKING_SPOT_LENGTH used for on-street is looking too
|
|
||||||
// conservative for some manually audited cases in Seattle
|
|
||||||
pt.project_away(aisle_thickness / 2.0 + 0.8 * PARKING_SPOT_LENGTH, theta),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
lines
|
|
||||||
};
|
|
||||||
|
|
||||||
for pair in lines.windows(2) {
|
|
||||||
let l1 = &pair[0];
|
|
||||||
let l2 = &pair[1];
|
|
||||||
let back = Line::new(l1.pt2(), l2.pt2());
|
|
||||||
if l1.intersection(&l2).is_none()
|
|
||||||
&& l1.angle().approx_eq(l2.angle(), 5.0)
|
|
||||||
&& line_valid(lot, l1, &finalized_lines)
|
|
||||||
&& line_valid(lot, l2, &finalized_lines)
|
|
||||||
&& line_valid(lot, &back, &finalized_lines)
|
|
||||||
{
|
|
||||||
total_spots += 1;
|
|
||||||
batch.push(
|
|
||||||
cs.general_road_marking,
|
|
||||||
l1.make_polygons(Distance::meters(0.25)),
|
|
||||||
);
|
|
||||||
batch.push(
|
|
||||||
cs.general_road_marking,
|
|
||||||
l2.make_polygons(Distance::meters(0.25)),
|
|
||||||
);
|
|
||||||
batch.push(
|
|
||||||
cs.general_road_marking,
|
|
||||||
back.make_polygons(Distance::meters(0.25)),
|
|
||||||
);
|
|
||||||
finalized_lines.push(l1.clone());
|
|
||||||
finalized_lines.push(l2.clone());
|
|
||||||
finalized_lines.push(back);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
total_spots
|
|
||||||
}
|
|
||||||
|
@ -8,25 +8,25 @@ pub struct Angle(f64);
|
|||||||
impl Angle {
|
impl Angle {
|
||||||
pub const ZERO: Angle = Angle(0.0);
|
pub const ZERO: Angle = Angle(0.0);
|
||||||
|
|
||||||
pub(crate) fn new(rads: f64) -> Angle {
|
pub(crate) fn new_rads(rads: f64) -> Angle {
|
||||||
// Retain more precision for angles...
|
// Retain more precision for angles...
|
||||||
Angle((rads * 10_000_000.0).round() / 10_000_000.0)
|
Angle((rads * 10_000_000.0).round() / 10_000_000.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_degs(degs: f64) -> Angle {
|
pub fn new_degs(degs: f64) -> Angle {
|
||||||
Angle::new(degs.to_radians())
|
Angle::new_rads(degs.to_radians())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn opposite(self) -> Angle {
|
pub fn opposite(self) -> Angle {
|
||||||
Angle::new(self.0 + std::f64::consts::PI)
|
Angle::new_rads(self.0 + std::f64::consts::PI)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn invert_y(self) -> Angle {
|
pub fn invert_y(self) -> Angle {
|
||||||
Angle::new(2.0 * std::f64::consts::PI - self.0)
|
Angle::new_rads(2.0 * std::f64::consts::PI - self.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rotate_degs(self, degrees: f64) -> Angle {
|
pub fn rotate_degs(self, degrees: f64) -> Angle {
|
||||||
Angle::new(self.0 + degrees.to_radians())
|
Angle::new_rads(self.0 + degrees.to_radians())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn normalized_radians(self) -> f64 {
|
pub fn normalized_radians(self) -> f64 {
|
||||||
@ -65,10 +65,29 @@ impl fmt::Display for Angle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::ops::Add for Angle {
|
||||||
|
type Output = Angle;
|
||||||
|
|
||||||
|
fn add(self, other: Angle) -> Angle {
|
||||||
|
Angle::new_rads(self.0 + other.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl std::ops::Neg for Angle {
|
impl std::ops::Neg for Angle {
|
||||||
type Output = Angle;
|
type Output = Angle;
|
||||||
|
|
||||||
fn neg(self) -> Angle {
|
fn neg(self) -> Angle {
|
||||||
Angle::new(-self.0)
|
Angle::new_rads(-self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::ops::Div<f64> for Angle {
|
||||||
|
type Output = Angle;
|
||||||
|
|
||||||
|
fn div(self, scalar: f64) -> Angle {
|
||||||
|
if scalar == 0.0 {
|
||||||
|
panic!("Can't divide {} / {}", self, scalar);
|
||||||
|
}
|
||||||
|
Angle::new_rads(self.0 / scalar)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -144,6 +144,10 @@ impl Line {
|
|||||||
self.percent_along(dist / len)
|
self.percent_along(dist / len)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn middle(&self) -> Pt2D {
|
||||||
|
self.dist_along(self.length() / 2.0)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn unbounded_percent_along(&self, percent: f64) -> Pt2D {
|
pub fn unbounded_percent_along(&self, percent: f64) -> Pt2D {
|
||||||
Pt2D::new(
|
Pt2D::new(
|
||||||
self.pt1().x() + percent * (self.pt2().x() - self.pt1().x()),
|
self.pt1().x() + percent * (self.pt2().x() - self.pt1().x()),
|
||||||
|
@ -115,7 +115,7 @@ impl Pt2D {
|
|||||||
|
|
||||||
pub fn angle_to(self, to: Pt2D) -> Angle {
|
pub fn angle_to(self, to: Pt2D) -> Angle {
|
||||||
// DON'T invert y here
|
// DON'T invert y here
|
||||||
Angle::new((to.y() - self.y()).atan2(to.x() - self.x()))
|
Angle::new_rads((to.y() - self.y()).atan2(to.x() - self.x()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn offset(self, dx: f64, dy: f64) -> Pt2D {
|
pub fn offset(self, dx: f64, dy: f64) -> Pt2D {
|
||||||
|
@ -2,10 +2,10 @@ use crate::make::sidewalk_finder::find_sidewalk_points;
|
|||||||
use crate::raw::{OriginalBuilding, RawBuilding, RawParkingLot};
|
use crate::raw::{OriginalBuilding, RawBuilding, RawParkingLot};
|
||||||
use crate::{
|
use crate::{
|
||||||
osm, Building, BuildingID, FrontPath, LaneID, LaneType, Map, OffstreetParking, ParkingLot,
|
osm, Building, BuildingID, FrontPath, LaneID, LaneType, Map, OffstreetParking, ParkingLot,
|
||||||
ParkingLotID, Position,
|
ParkingLotID, Position, NORMAL_LANE_THICKNESS, PARKING_SPOT_LENGTH,
|
||||||
};
|
};
|
||||||
use abstutil::Timer;
|
use abstutil::Timer;
|
||||||
use geom::{Distance, HashablePt2D, Line, PolyLine, Polygon, Pt2D, Ring};
|
use geom::{Angle, Distance, HashablePt2D, Line, PolyLine, Polygon, Pt2D, Ring};
|
||||||
use std::collections::{BTreeMap, HashSet};
|
use std::collections::{BTreeMap, HashSet};
|
||||||
|
|
||||||
pub fn make_all_buildings(
|
pub fn make_all_buildings(
|
||||||
@ -183,6 +183,7 @@ pub fn make_all_parking_lots(
|
|||||||
// TODO Rethink this approach. 250 square feet is around 23 square meters
|
// TODO Rethink this approach. 250 square feet is around 23 square meters
|
||||||
capacity: orig.capacity,
|
capacity: orig.capacity,
|
||||||
osm_id: orig.osm_id,
|
osm_id: orig.osm_id,
|
||||||
|
spots: Vec::new(),
|
||||||
|
|
||||||
driveway_line,
|
driveway_line,
|
||||||
driving_pos,
|
driving_pos,
|
||||||
@ -226,6 +227,12 @@ pub fn make_all_parking_lots(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
timer.start_iter("generate parking lot spots", results.len());
|
||||||
|
for lot in results.iter_mut() {
|
||||||
|
timer.next();
|
||||||
|
lot.spots = infer_spots(&lot.polygon, &lot.aisles);
|
||||||
|
}
|
||||||
|
|
||||||
timer.stop("convert parking lots");
|
timer.stop("convert parking lots");
|
||||||
|
|
||||||
results
|
results
|
||||||
@ -252,3 +259,81 @@ fn get_address(tags: &BTreeMap<String, String>, sidewalk: LaneID, map: &Map) ->
|
|||||||
_ => format!("??? {}", map.get_parent(sidewalk).get_name()),
|
_ => format!("??? {}", map.get_parent(sidewalk).get_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn infer_spots(lot_polygon: &Polygon, aisles: &Vec<Vec<Pt2D>>) -> Vec<(Pt2D, Angle)> {
|
||||||
|
let mut spots = Vec::new();
|
||||||
|
let mut finalized_lines = Vec::new();
|
||||||
|
|
||||||
|
for aisle in aisles {
|
||||||
|
let aisle_thickness = NORMAL_LANE_THICKNESS / 2.0;
|
||||||
|
let pl = PolyLine::unchecked_new(aisle.clone());
|
||||||
|
|
||||||
|
for rotate in vec![90.0, -90.0] {
|
||||||
|
// Blindly generate all of the lines
|
||||||
|
let lines = {
|
||||||
|
let mut lines = Vec::new();
|
||||||
|
let mut start = Distance::ZERO;
|
||||||
|
while start + NORMAL_LANE_THICKNESS < pl.length() {
|
||||||
|
let (pt, angle) = pl.dist_along(start);
|
||||||
|
start += NORMAL_LANE_THICKNESS;
|
||||||
|
let theta = angle.rotate_degs(rotate);
|
||||||
|
lines.push(Line::new(
|
||||||
|
pt.project_away(aisle_thickness / 2.0, theta),
|
||||||
|
// The full PARKING_SPOT_LENGTH used for on-street is looking too
|
||||||
|
// conservative for some manually audited cases in Seattle
|
||||||
|
pt.project_away(aisle_thickness / 2.0 + 0.8 * PARKING_SPOT_LENGTH, theta),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
lines
|
||||||
|
};
|
||||||
|
|
||||||
|
for pair in lines.windows(2) {
|
||||||
|
let l1 = &pair[0];
|
||||||
|
let l2 = &pair[1];
|
||||||
|
let back = Line::new(l1.pt2(), l2.pt2());
|
||||||
|
if l1.intersection(&l2).is_none()
|
||||||
|
&& l1.angle().approx_eq(l2.angle(), 5.0)
|
||||||
|
&& line_valid(lot_polygon, aisles, l1, &finalized_lines)
|
||||||
|
&& line_valid(lot_polygon, aisles, l2, &finalized_lines)
|
||||||
|
&& line_valid(lot_polygon, aisles, &back, &finalized_lines)
|
||||||
|
{
|
||||||
|
let avg_angle = (l1.angle() + l2.angle()) / 2.0;
|
||||||
|
spots.push((back.middle(), avg_angle.opposite()));
|
||||||
|
finalized_lines.push(l1.clone());
|
||||||
|
finalized_lines.push(l2.clone());
|
||||||
|
finalized_lines.push(back);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
spots
|
||||||
|
}
|
||||||
|
|
||||||
|
fn line_valid(
|
||||||
|
lot_polygon: &Polygon,
|
||||||
|
aisles: &Vec<Vec<Pt2D>>,
|
||||||
|
line: &Line,
|
||||||
|
finalized_lines: &Vec<Line>,
|
||||||
|
) -> bool {
|
||||||
|
// Don't leak out of the parking lot
|
||||||
|
// TODO Entire line
|
||||||
|
if !lot_polygon.contains_pt(line.pt1()) || !lot_polygon.contains_pt(line.pt2()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't let this line hit another line
|
||||||
|
if finalized_lines.iter().any(|other| line.crosses(other)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't hit an aisle
|
||||||
|
if aisles.iter().any(|pts| {
|
||||||
|
PolyLine::unchecked_new(pts.clone())
|
||||||
|
.intersection(&line.to_polyline())
|
||||||
|
.is_some()
|
||||||
|
}) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::Position;
|
use crate::Position;
|
||||||
use geom::{Line, PolyLine, Polygon, Pt2D};
|
use geom::{Angle, Line, PolyLine, Polygon, Pt2D};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
@ -22,6 +22,8 @@ pub struct ParkingLot {
|
|||||||
pub aisles: Vec<Vec<Pt2D>>,
|
pub aisles: Vec<Vec<Pt2D>>,
|
||||||
pub capacity: Option<usize>,
|
pub capacity: Option<usize>,
|
||||||
pub osm_id: i64,
|
pub osm_id: i64,
|
||||||
|
// The middle of the "T", pointing towards the parking aisle
|
||||||
|
pub spots: Vec<(Pt2D, Angle)>,
|
||||||
|
|
||||||
// Goes from the lot to the driving lane
|
// Goes from the lot to the driving lane
|
||||||
pub driveway_line: PolyLine,
|
pub driveway_line: PolyLine,
|
||||||
|
Loading…
Reference in New Issue
Block a user