Skip to content

Commit

Permalink
Populating the map_model transit representation from the raw GTFS data.
Browse files Browse the repository at this point in the history
#372

(WIP)
  • Loading branch information
dabreegster committed Dec 3, 2021
1 parent 98557bd commit 881da63
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 0 deletions.
3 changes: 3 additions & 0 deletions map_model/src/make/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
}
}
Expand Down
137 changes: 137 additions & 0 deletions map_model/src/make/transit.rs
Original file line number Diff line number Diff line change
@@ -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<HashablePt2D> = 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<String, TransitStopID> = 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, &gtfs_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<LaneID>,
bus_outgoing_borders: FindClosest<LaneID>,
train_incoming_borders: FindClosest<LaneID>,
train_outgoing_borders: FindClosest<LaneID>,
}

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<String, TransitStopID>, 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");
}
1 change: 1 addition & 0 deletions map_model/src/objects/transit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 881da63

Please sign in to comment.