using blockface kml in convert_osm to prescribe parking lanes

This commit is contained in:
Dustin Carlino 2018-11-21 17:18:31 -08:00
parent 9e91332b58
commit 5bc9992fc0
8 changed files with 115 additions and 23 deletions

View File

@ -25,7 +25,10 @@ mod split_ways;
mod srtm;
mod traffic_signals;
use map_model::raw_data;
use dimensioned::si;
use geom::{GPSBounds, PolyLine, Pt2D};
use kml::ExtraShapes;
use map_model::{raw_data, FindClosest, LANE_THICKNESS};
use ordered_float::NotNaN;
use srtm::Elevation;
use std::path::Path;
@ -51,6 +54,10 @@ pub struct Flags {
#[structopt(long = "parcels")]
pub parcels: String,
/// ExtraShapes file with blockface, produced using the kml crate
#[structopt(long = "parking_shapes")]
pub parking_shapes: String,
/// GTFS directory
#[structopt(long = "gtfs")]
pub gtfs: String,
@ -71,8 +78,13 @@ pub fn convert(flags: &Flags, timer: &mut abstutil::Timer) -> raw_data::Map {
remove_disconnected::remove_disconnected_roads(&mut map, timer);
let gps_bounds = map.get_gps_bounds();
println!("Loading blockface shapes from {}", flags.parking_shapes);
let parking_shapes: ExtraShapes =
abstutil::read_binary(&flags.parking_shapes, timer).expect("loading blockface failed");
use_parking_hints(&mut map, parking_shapes, &gps_bounds);
println!("Loading parcels from {}", flags.parcels);
let parcels: kml::ExtraShapes =
let parcels: ExtraShapes =
abstutil::read_binary(&flags.parcels, timer).expect("loading parcels failed");
println!(
"Finding matching parcels from {} candidates",
@ -91,7 +103,7 @@ pub fn convert(flags: &Flags, timer: &mut abstutil::Timer) -> raw_data::Map {
});
}
}
group_parcels::group_parcels(gps_bounds, &mut map.parcels);
group_parcels::group_parcels(&gps_bounds, &mut map.parcels);
for pt in traffic_signals::extract(&flags.traffic_signals)
.expect("loading traffic signals failed")
@ -125,7 +137,52 @@ pub fn convert(flags: &Flags, timer: &mut abstutil::Timer) -> raw_data::Map {
.to_os_string()
.into_string()
.unwrap();
neighborhoods::convert(&flags.neighborhoods, map_name, gps_bounds);
neighborhoods::convert(&flags.neighborhoods, map_name, &gps_bounds);
map
}
fn use_parking_hints(map: &mut raw_data::Map, shapes: ExtraShapes, gps_bounds: &GPSBounds) {
// Match shapes with the nearest road + direction (true for forwards)
let mut closest: FindClosest<(usize, bool)> = FindClosest::new(&gps_bounds.to_bounds());
for (idx, r) in map.roads.iter().enumerate() {
let pts = PolyLine::new(
r.points
.iter()
.map(|pt| Pt2D::from_gps(*pt, gps_bounds).unwrap())
.collect(),
);
closest.add((idx, true), &pts.shift_blindly(LANE_THICKNESS));
closest.add((idx, false), &pts.reversed().shift_blindly(LANE_THICKNESS));
}
'SHAPE: for s in shapes.shapes.into_iter() {
let mut pts: Vec<Pt2D> = Vec::new();
for pt in s.points.into_iter() {
if let Some(pt) = Pt2D::from_gps(pt, gps_bounds) {
pts.push(pt);
} else {
continue 'SHAPE;
}
}
if pts.len() > 1 {
// The blockface line endpoints will be close to other roads, so match based on the
// middle of the blockface.
// TODO Long blockfaces sometimes cover two roads. Should maybe find ALL matches within
// the threshold distance?
let middle = PolyLine::new(pts).middle();
if let Some(((r, fwds), _)) = closest.closest_pt(middle, 5.0 * LANE_THICKNESS * si::M) {
let category = s.attributes.get("PARKING_CATEGORY");
let has_parking = category != Some(&"None".to_string())
&& category != Some(&"No Parking Allowed".to_string());
// Blindly override prior values.
if fwds {
map.roads[r].parking_lane_fwd = has_parking;
} else {
map.roads[r].parking_lane_back = has_parking;
}
}
}
}
}

View File

@ -61,6 +61,9 @@ pub fn osm_to_raw_roads(osm_path: &str, timer: &mut Timer) -> raw_data::Map {
osm_way_id: way.id,
points: pts,
osm_tags: tags,
// We'll fill this out later
parking_lane_fwd: false,
parking_lane_back: false,
});
} else if is_bldg(&tags) {
map.buildings.push(raw_data::Building {

View File

@ -1,7 +1,7 @@
use aabb_quadtree::geom::{Point, Rect};
use std::f64;
use std::fmt;
use {HashablePt2D, Pt2D};
use {Bounds, HashablePt2D, Pt2D};
// longitude is x, latitude is y
#[derive(Copy, Clone, PartialEq, Debug, Serialize, Deserialize)]
@ -104,4 +104,11 @@ impl GPSBounds {
.gps_dist_meters(LonLat::new(self.min_lon, self.max_lat));
Pt2D::new(width, height)
}
pub fn to_bounds(&self) -> Bounds {
let mut b = Bounds::new();
b.update(Pt2D::new(0.0, 0.0));
b.update(self.get_max_world_pt());
b
}
}

View File

@ -74,6 +74,17 @@ if [ ! -f data/shapes/parcels ]; then
cd ..
fi
if [ ! -f data/shapes/blockface ]; then
# From http://data-seattlecitygis.opendata.arcgis.com/datasets/blockface
get_if_needed https://opendata.arcgis.com/datasets/a1458ad1abca41869b81f7c0db0cd777_0.kml data/input/blockface.kml;
cd kml
time cargo run --release -- \
--input=../data/input/blockface.kml \
--output=../data/shapes/blockface
cd ..
fi
cd convert_osm
for poly in `ls ../data/polygons/`; do
name=`basename -s .poly $poly`;
@ -82,6 +93,7 @@ for poly in `ls ../data/polygons/`; do
--elevation=../data/input/N47W122.hgt \
--traffic_signals=../data/input/TrafficSignals.shp \
--parcels=../data/shapes/parcels \
--parking_shapes=../data/shapes/blockface \
--gtfs=../data/input/google_transit_2018_18_08 \
--neighborhoods=../data/input/neighborhoods.geojson \
--output=../data/raw_maps/$name.abst

View File

@ -56,33 +56,48 @@ fn get_lanes(r: &raw_data::Road) -> (Vec<LaneType>, Vec<LaneType>) {
&& r.osm_tags.get("highway") != Some(&"motorway_link".to_string());
// TODO Bus/bike and parking lanes can coexist, but then we have to make sure cars are fine
// with merging in/out of the bus/bike lane to park. ><
let has_parking = has_sidewalk && !has_bus_lane && !has_bike_lane;
//let has_parking = has_sidewalk && !has_bus_lane && !has_bike_lane;
let mut full_side = driving_lanes_per_side;
let mut fwd_side = driving_lanes_per_side.clone();
if has_bus_lane {
full_side.push(LaneType::Bus);
fwd_side.push(LaneType::Bus);
}
if has_bike_lane {
full_side.push(LaneType::Biking);
fwd_side.push(LaneType::Biking);
}
if has_parking {
full_side.push(LaneType::Parking);
if r.parking_lane_fwd {
fwd_side.push(LaneType::Parking);
}
if has_sidewalk {
full_side.push(LaneType::Sidewalk);
fwd_side.push(LaneType::Sidewalk);
}
if oneway {
// Only residential highways have a sidewalk on the other side of a highway.
let other_side =
// Only residential streets have a sidewalk on the other side of a one-way.
// Ignore off-side parking, since cars don't know how to park on lanes without a driving
// lane in that direction too.
let back_side =
if has_sidewalk && r.osm_tags.get("highway") == Some(&"residential".to_string()) {
vec![LaneType::Sidewalk]
} else {
Vec::new()
};
(full_side, other_side)
(fwd_side, back_side)
} else {
(full_side.clone(), full_side)
let mut back_side = driving_lanes_per_side;
if has_bus_lane {
back_side.push(LaneType::Bus);
}
if has_bike_lane {
back_side.push(LaneType::Biking);
}
if r.parking_lane_back {
back_side.push(LaneType::Parking);
}
if has_sidewalk {
back_side.push(LaneType::Sidewalk);
}
(fwd_side, back_side)
}
}

View File

@ -58,13 +58,7 @@ impl Map {
) -> Map {
timer.start("raw_map to Map");
let gps_bounds = data.get_gps_bounds();
let bounds = {
let mut b = Bounds::new();
b.update(Pt2D::new(0.0, 0.0));
b.update(gps_bounds.get_max_world_pt());
b
};
let bounds = gps_bounds.to_bounds();
let mut m = Map {
name,

View File

@ -67,6 +67,8 @@ pub struct Road {
pub points: Vec<LonLat>,
pub osm_tags: BTreeMap<String, String>,
pub osm_way_id: i64,
pub parking_lane_fwd: bool,
pub parking_lane_back: bool,
}
impl Road {

View File

@ -176,6 +176,8 @@ impl Model {
],
osm_tags,
osm_way_id: idx as i64,
parking_lane_fwd: r.lanes.fwd.contains(&LaneType::Parking),
parking_lane_back: r.lanes.back.contains(&LaneType::Parking),
});
}