match offstreet parking KML to buildings

This commit is contained in:
Dustin Carlino 2019-08-25 13:19:35 -07:00
parent 8aee410046
commit 1936f5f75e
11 changed files with 75 additions and 7 deletions

View File

@ -5,9 +5,9 @@ mod remove_disconnected;
mod split_ways;
use abstutil::Timer;
use geom::{FindClosest, GPSBounds, LonLat, PolyLine, Pt2D};
use geom::{Distance, FindClosest, GPSBounds, LonLat, PolyLine, Polygon, Pt2D};
use kml::ExtraShapes;
use map_model::{raw_data, LANE_THICKNESS};
use map_model::{raw_data, OffstreetParking, LANE_THICKNESS};
use std::fs::File;
use std::io::{BufRead, BufReader};
use structopt::StructOpt;
@ -23,6 +23,10 @@ pub struct Flags {
#[structopt(long = "parking_shapes", default_value = "")]
pub parking_shapes: String,
/// KML file with offstreet parking info. Optional.
#[structopt(long = "offstreet_parking", default_value = "")]
pub offstreet_parking: String,
/// GTFS directory. Optional.
#[structopt(long = "gtfs", default_value = "")]
pub gtfs: String,
@ -67,6 +71,9 @@ pub fn convert(flags: &Flags, timer: &mut abstutil::Timer) -> raw_data::Map {
if !flags.parking_shapes.is_empty() {
use_parking_hints(&mut map, &flags.parking_shapes, timer);
}
if !flags.offstreet_parking.is_empty() {
use_offstreet_parking(&mut map, &flags.offstreet_parking, timer);
}
if !flags.gtfs.is_empty() {
timer.start("load GTFS");
map.bus_routes = gtfs::load(&flags.gtfs).unwrap();
@ -155,3 +162,40 @@ fn read_osmosis_polygon(path: &str) -> Vec<LonLat> {
}
pts
}
fn use_offstreet_parking(map: &mut raw_data::Map, path: &str, timer: &mut Timer) {
timer.start("match offstreet parking points");
let shapes = kml::load(path, &map.gps_bounds, timer).expect("loading offstreet_parking failed");
// Building indices
let mut closest: FindClosest<usize> = FindClosest::new(&map.gps_bounds.to_bounds());
for (idx, b) in map.buildings.iter().enumerate() {
let mut pts = map.gps_bounds.must_convert(&b.points);
// Close off the polygon
pts.push(pts[0]);
closest.add(idx, &pts);
}
// TODO Another function just to use ?. Try blocks would rock.
let mut handle_shape: Box<dyn FnMut(kml::ExtraShape) -> Option<()>> = Box::new(|s| {
assert_eq!(s.points.len(), 1);
let pt = Pt2D::from_gps(s.points[0], &map.gps_bounds)?;
let (idx, _) = closest.closest_pt(pt, Distance::meters(50.0))?;
// TODO If we ditched LonLat up-front, things like this would be much easier.
let poly = Polygon::new(&map.gps_bounds.must_convert(&map.buildings[idx].points));
// TODO Handle parking lots.
if !poly.contains_pt(pt) {
return None;
}
let name = s.attributes.get("DEA_FACILITY_NAME")?.to_string();
let num_stalls = s.attributes.get("DEA_STALLS")?.parse::<usize>().ok()?;
assert_eq!(map.buildings[idx].parking, None);
map.buildings[idx].parking = Some(OffstreetParking { name, num_stalls });
None
});
for s in shapes.shapes.into_iter() {
handle_shape(s);
}
timer.stop("match offstreet parking points");
}

View File

@ -79,6 +79,7 @@ pub fn osm_to_raw_roads(
osm_way_id: way.id,
points: pts,
osm_tags: tags,
parking: None,
});
} else if let Some(at) = get_area_type(&tags) {
areas.push(raw_data::Area {

View File

@ -47,7 +47,8 @@ for some portion of Seattle. Each map has these objects:
distinguish crosswalks at each end of a sidewalk.)
- **Buildings**: A building has a position, OSM metadata, and a **front path**
connecting the edge of the building to the nearest sidewalk. Most trips in A/B
Street begin and end at buildings.
Street begin and end at buildings. Some buildings also contain a number of
off-street parking spots.
- **Area**: An area has geometry and OSM metadata and represents a body of
water, forest, park, etc. They're just used for drawing.
- **Bus stop**: A bus stop is placed some distance along a sidewalk, with a
@ -130,6 +131,9 @@ it only takes a few seconds to load a serialized map.
- `lib.rs`: Apply parking hints from a King County GIS blockface dataset
- Match each blockface to the nearest edge of a road
- Interpret the metadata to assign on-street parking there or not
- `lib.rs`: Apply offstreet parking hints from a King County GIS dataset
- Match each point to the building containing it, plumbing through the number
of spots
- `lib.rs` using the `gtfs` crate: Load bus route info from GTFS
- `neighborhoods.rs`: Load neighborhood polygons from an extra geojson file
- If the polygon isn't completely in-bounds, just remove it

View File

@ -112,9 +112,16 @@ impl CommonState {
osd.append(map.get_parent(*l).get_name(), Some(name_color));
}
Some(ID::Building(b)) => {
let bldg = map.get_b(*b);
osd.append(format!("{}", b), Some(id_color));
osd.append(" is ".to_string(), None);
osd.append(map.get_b(*b).get_name(), Some(name_color));
osd.append(bldg.get_name(), Some(name_color));
if let Some(ref p) = bldg.parking {
osd.append(
format!(" ({} parking spots via {})", p.num_stalls, p.name),
None,
);
}
}
Some(ID::Turn(t)) => {
osd.append(

View File

@ -95,10 +95,11 @@ for poly in `ls ../data/polygons/`; do
RUST_BACKTRACE=1 cargo run --release -- \
--osm=../data/input/$name.osm \
--parking_shapes=../data/shapes/blockface.bin \
--offstreet_parking=../data/input/offstreet_parking.kml \
--gtfs=../data/input/google_transit_2018_18_08 \
--neighborhoods=../data/input/neighborhoods.geojson \
--clip=../data/polygons/$name.poly \
--output=../data/raw_maps/$name.bin
done
# To run manually: cargo run -- --osm=../data/input/montlake.osm --parking_shapes=../data/shapes/blockface.bin --gtfs=../data/input/google_transit_2018_18_08 --neighborhoods=../data/input/neighborhoods.geojson --clip=../data/polygons/montlake.poly --output=../data/raw_maps/montlake.bin --fast_dev
# To run manually: cargo run -- --osm=../data/input/montlake.osm --parking_shapes=../data/shapes/blockface.bin --offstreet_parking=../data/input/offstreet_parking.kml --gtfs=../data/input/google_transit_2018_18_08 --neighborhoods=../data/input/neighborhoods.geojson --clip=../data/polygons/montlake.poly --output=../data/raw_maps/montlake.bin --fast_dev

View File

@ -23,6 +23,12 @@ pub struct FrontPath {
pub line: Line,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
pub struct OffstreetParking {
pub name: String,
pub num_stalls: usize,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Building {
pub id: BuildingID,
@ -34,6 +40,7 @@ pub struct Building {
pub label_center: Pt2D,
pub front_path: FrontPath,
pub parking: Option<OffstreetParking>,
}
impl Building {

View File

@ -16,7 +16,7 @@ mod traversable;
mod turn;
pub use crate::area::{Area, AreaID, AreaType};
pub use crate::building::{Building, BuildingID, FrontPath};
pub use crate::building::{Building, BuildingID, FrontPath, OffstreetParking};
pub use crate::bus_stop::{BusRoute, BusRouteID, BusStop, BusStopID};
pub use crate::edits::MapEdits;
pub use crate::intersection::{Intersection, IntersectionID, IntersectionType};

View File

@ -54,6 +54,7 @@ pub fn make_all_buildings(
sidewalk: *sidewalk_pos,
line,
},
parking: input[idx].parking.clone(),
label_center: Polygon::polylabel(&points),
});
}

View File

@ -1,6 +1,6 @@
use crate::make::get_lane_types;
pub use crate::make::{Hint, Hints, InitialMap};
use crate::{AreaType, IntersectionType, RoadSpec};
use crate::{AreaType, IntersectionType, OffstreetParking, RoadSpec};
use geom::{GPSBounds, LonLat};
use gtfs::Route;
use serde_derive::{Deserialize, Serialize};
@ -164,6 +164,7 @@ pub struct Building {
pub points: Vec<LonLat>,
pub osm_tags: BTreeMap<String, String>,
pub osm_way_id: i64,
pub parking: Option<OffstreetParking>,
}
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]

View File

@ -363,6 +363,7 @@ impl Model {
points: b.polygon().points().iter().map(|p| pt(*p)).collect(),
osm_tags,
osm_way_id: idx as i64,
parking: None,
});
}

View File

@ -8,6 +8,7 @@ pub fn run(t: &mut TestRunner) {
let flags = convert_osm::Flags {
osm: "../data/input/montlake.osm".to_string(),
parking_shapes: "../data/shapes/blockface.bin".to_string(),
offstreet_parking: "../data/input/offstreet_parking.kml".to_string(),
gtfs: "../data/input/google_transit_2018_18_08".to_string(),
neighborhoods: "../data/input/neighborhoods.geojson".to_string(),
clip: abstutil::path_polygon("montlake"),