From 881da6354d4da9c401e3446eb3bc95da4e8e5730 Mon Sep 17 00:00:00 2001 From: Dustin Carlino Date: Fri, 3 Dec 2021 14:17:31 +0000 Subject: [PATCH] Populating the map_model transit representation from the raw GTFS data. #372 (WIP) --- map_model/src/make/mod.rs | 3 + map_model/src/make/transit.rs | 137 +++++++++++++++++++++++++++++++ map_model/src/objects/transit.rs | 1 + 3 files changed, 141 insertions(+) create mode 100644 map_model/src/make/transit.rs diff --git a/map_model/src/make/mod.rs b/map_model/src/make/mod.rs index f46540a8bf..88c5fddf69 100644 --- a/map_model/src/make/mod.rs +++ b/map_model/src/make/mod.rs @@ -26,6 +26,7 @@ mod parking_lots; pub mod remove_disconnected; pub mod snappy; pub mod traffic_signals; +pub mod transit; pub mod turns; mod walking_turns; @@ -293,6 +294,8 @@ impl Map { map.pathfinder = Pathfinder::new(&map, map.routing_params().clone(), engine, timer); timer.stop("setup pathfinding"); + transit::finalize_transit(&mut map, &raw, timer); + map } } diff --git a/map_model/src/make/transit.rs b/map_model/src/make/transit.rs new file mode 100644 index 0000000000..3d21b2421f --- /dev/null +++ b/map_model/src/make/transit.rs @@ -0,0 +1,137 @@ +use std::collections::{HashSet, HashMap}; + +use anyhow::Result; + +use geom::{Distance, FindClosest, HashablePt2D}; +use abstutil::Timer; + +use crate::{TransitStopID, LaneID, TransitRouteID, TransitStop, TransitRoute, PathConstraints, Map}; +use crate::raw::{RawTransitRoute, RawMap}; +use crate::make::match_points_to_lanes; + +pub fn finalize_transit(map: &mut Map, raw: &RawMap, timer: &mut Timer) { + // Snap stops to sidewalks and driving lanes, similar to buildings + let mut query: HashSet = HashSet::new(); + for stop in raw.transit_stops.values() { + query.insert(stop.position.to_hashable()); + } + let sidewalk_buffer = Distance::meters(7.5); + let sidewalk_pts = match_points_to_lanes( + map, + query, + |l| l.is_walkable(), + // Stops can be very close to intersections + Distance::ZERO, + // Stops shouldn't be far from sidewalks + Distance::meters(3.0), + timer, + ); + + // Create all stops + let mut gtfs_to_stop_id: HashMap = HashMap::new(); + for stop in raw.transit_stops.values() { + // TODO How do we know this here? + let vehicle = PathConstraints::Bus; + if let Some(sidewalk_pos) = sidewalk_pts.get(&stop.position.to_hashable()) { + let sidewalk_lane = sidewalk_pos.lane(); + if let Some(driving_pos) = map + .get_parent(sidewalk_lane) + .find_closest_lane(sidewalk_lane, |l| vehicle.can_use(l, map)) + .map(|l| sidewalk_pos.equiv_pos(l, map)) + { + let id = TransitStopID { + sidewalk: sidewalk_lane, + idx: map.get_l(sidewalk_lane).transit_stops.len(), + }; + map.mut_lane(sidewalk_lane).transit_stops.insert(id); + map.transit_stops.insert(id, TransitStop { + id, + name: stop.name.clone(), + gtfs_id: stop.gtfs_id.clone(), + driving_pos, + sidewalk_pos: *sidewalk_pos, + is_train_stop: vehicle == PathConstraints::Train, + }); + gtfs_to_stop_id.insert(stop.gtfs_id.clone(), id); + } + } + } + + let snapper = BorderSnapper::new(map); + for route in &raw.transit_routes { + if let Err(err) = setup_route(route, map, >fs_to_stop_id, &snapper) { + warn!("Couldn't snap route {}: {}", route.gtfs_id, err); + } + } + + // TODO Clean up unused stops; maybe one of the routes didn't work. Re-map IDs... +} + +struct BorderSnapper { + bus_incoming_borders: FindClosest, + bus_outgoing_borders: FindClosest, + train_incoming_borders: FindClosest, + train_outgoing_borders: FindClosest, +} + +impl BorderSnapper { + fn new(map: &Map) -> BorderSnapper { + let mut snapper = BorderSnapper { + bus_incoming_borders: FindClosest::new(map.get_bounds()), + bus_outgoing_borders: FindClosest::new(map.get_bounds()), + train_incoming_borders: FindClosest::new(map.get_bounds()), + train_outgoing_borders: FindClosest::new(map.get_bounds()), + }; + for i in map.all_incoming_borders() { + for l in i.get_outgoing_lanes(map, PathConstraints::Bus) { + snapper.bus_incoming_borders.add(l, &vec![map.get_l(l).first_pt()]); + } + for l in i.get_outgoing_lanes(map, PathConstraints::Train) { + snapper.train_incoming_borders.add(l, &vec![map.get_l(l).first_pt()]); + } + } + for i in map.all_outgoing_borders() { + for l in i.get_incoming_lanes(map, PathConstraints::Bus) { + snapper.bus_outgoing_borders.add(l, &vec![map.get_l(l).last_pt()]); + } + for l in i.get_incoming_lanes(map, PathConstraints::Train) { + snapper.train_outgoing_borders.add(l, &vec![map.get_l(l).last_pt()]); + } + } + snapper + } +} + +fn setup_route(route: &RawTransitRoute, map: &mut Map, gtfs_to_stop_id: &HashMap, snapper: &BorderSnapper) -> Result<()> { + let start = if map.boundary_polygon.contains_pt(route.shape.first_pt()) { + let stop_id = gtfs_to_stop_id.get(&route.stops[0])?; + map.get_bs(stop_id).driving_pos.lane() + } else { + // Find the first time the route shape hits the map boundary + let entry_pt = route.shape.first_intersection(&map.boundary_polygon)?; + // Snap that to a border + let borders = if route.route_type == PathConstraints::Bus { + &snapper.bus_incoming_borders + } else { + &snapper.train_incoming_borders + }; + borders.closest_pt(entry_pt, Distance::meters(10.0))?.0 + }; + + let end_border = if map.boundary_polygon.contains_pt(route.shape.last_pt()) { + None + } else { + // Find the last time the route shape hits the map boundary + let entry_pt = route.shape.reversed().first_intersection(&map.boundary_polygon)?; + // Snap that to a border + let borders = if route.route_type == PathConstraints::Bus { + &snapper.bus_outgoing_borders_ + } else { + &snapper.train_outgoing_borders_ + }; + Some(borders.closest_pt(entry_pt, Distance::meters(10.0))?.0) + }; + + + bail!("nah"); +} diff --git a/map_model/src/objects/transit.rs b/map_model/src/objects/transit.rs index 974d2da8ea..54a050b301 100644 --- a/map_model/src/objects/transit.rs +++ b/map_model/src/objects/transit.rs @@ -42,6 +42,7 @@ impl fmt::Display for TransitRouteID { pub struct TransitStop { pub id: TransitStopID, pub name: String, + pub gtfs_id: String, /// These may be on different roads entirely, like for light rail platforms. pub driving_pos: Position, pub sidewalk_pos: Position,