├── src ├── prep │ ├── mod.rs │ ├── graph_from_xml.rs │ ├── weighted_graph.rs │ └── undirected_graph.rs ├── lib.rs ├── test_helpers.rs ├── a_star.rs ├── road_weights.rs ├── dijkstra.rs ├── set_dijkstra.rs ├── gtfs_dijkstra.rs ├── pareto_sets.rs ├── a_star_heuristics.rs ├── graph_from_xml.rs ├── arc_flags.rs ├── connected_component.rs ├── weighted_graph.rs ├── pathfinder.rs ├── transit_nodes.rs ├── transfer_patterns.rs ├── contraction.rs └── graph_from_gtfs.rs ├── .gitignore ├── data ├── gtfs_example │ ├── calendar.txt │ ├── stops.txt │ ├── trips.txt │ └── stop_times.txt ├── basic_graph.xml └── example.osm ├── Cargo.toml ├── README.md └── examples └── build_graph.rs /src/prep/mod.rs: -------------------------------------------------------------------------------- 1 | mod undirected_graph; 2 | mod graph_from_xml; 3 | mod weighted_graph; 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | data/baden-wuerttemberg.osm 4 | data/saarland.osm 5 | data/manhattan.gtfs 6 | -------------------------------------------------------------------------------- /data/gtfs_example/calendar.txt: -------------------------------------------------------------------------------- 1 | service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date 2 | weekday,1,1,1,1,1,0,0,20160101,20161231 3 | weekend,0,0,0,0,0,1,1,20160101,20161231 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "efficient_route_planning" 3 | version = "0.1.0" 4 | authors = ["stuart"] 5 | 6 | [dependencies] 7 | xml-rs = "0.3" 8 | lazy_static = "0.1.*" 9 | time = "0.1" 10 | rand = "0.3" 11 | csv = "0.14" 12 | -------------------------------------------------------------------------------- /data/gtfs_example/stops.txt: -------------------------------------------------------------------------------- 1 | stop_id,stop_code,stop_name,stop_desc,stop_lat,stop_lon,zone_id,stop_url,location_type,parent_station 2 | A,,Stop-A,,1,0,,,, 3 | B,,Stop-B,,3,1,,,, 4 | C,,Stop-C,,0,1,,,, 5 | D,,Stop-D,,1,2,,,, 6 | E,,Stop-E,,2,3,,,, 7 | F,,Stop-F,,1,4,,,, 8 | -------------------------------------------------------------------------------- /data/gtfs_example/trips.txt: -------------------------------------------------------------------------------- 1 | route_id,service_id,trip_id,trip_headsign,direction_id,block_id,shape_id 2 | green,weekday,g1,east,,, 3 | green,weekday,g2,east,,, 4 | green,weekday,g3,east,,, 5 | green,weekday,g4,east,,, 6 | green,weekday,g5,east,,, 7 | red,weekday,r1,east,,, 8 | red,weekday,r2,east,,, 9 | red,weekday,r3,east,,, 10 | green,weekend,g1w,east,,, 11 | red,weekend,r1w,east,,, 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Efficient Route Planning 2 | 3 | This repository is for work related to the [Efficient Route Planning 4 | course](http://ad-wiki.informatik.uni-freiburg.de/teaching/EfficientRoutePlanningSS2012) 5 | at the University of Freiburg. It is also the beginnings of learning 6 | [Rust](https://rust-lang.org) 7 | 8 | The `prep/` folder consists of self-imposed exercises completed before the start 9 | of the course to help learn rust in a situation similar to what might be 10 | experienced in the class. 11 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // pub mod prep; 2 | #[macro_use] 3 | extern crate lazy_static; 4 | 5 | extern crate rand; 6 | extern crate time; 7 | 8 | pub mod pathfinder; 9 | pub mod road_weights; 10 | pub mod graph_from_xml; 11 | pub mod weighted_graph; 12 | pub mod test_helpers; 13 | pub mod a_star; 14 | pub mod a_star_heuristics; 15 | pub mod dijkstra; 16 | pub mod connected_component; 17 | pub mod arc_flags; 18 | pub mod contraction; 19 | pub mod transit_nodes; 20 | pub mod graph_from_gtfs; 21 | pub mod gtfs_dijkstra; 22 | pub mod pareto_sets; 23 | pub mod set_dijkstra; 24 | pub mod transfer_patterns; 25 | -------------------------------------------------------------------------------- /examples/build_graph.rs: -------------------------------------------------------------------------------- 1 | extern crate efficient_route_planning; 2 | extern crate time; 3 | 4 | use efficient_route_planning::graph_from_xml::build_graph_from_xml; 5 | 6 | fn main() { 7 | { 8 | println!("Starting saarland.osm"); 9 | let t1 = time::now(); 10 | build_graph_from_xml("data/saarland.osm"); 11 | println!("Done! in {}", time::now() - t1); 12 | } 13 | { 14 | println!("Starting baden-wuerttemberg.osm"); 15 | let t1 = time::now(); 16 | build_graph_from_xml("data/baden-wuerttemberg.osm"); 17 | println!("Done! in {}", time::now() - t1); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/test_helpers.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | use graph_from_gtfs::{ NodeType, GtfsId, time_to_seconds_after_midnight }; 3 | 4 | #[cfg(test)] 5 | pub fn floats_nearly_eq(float_1: f64, float_2: f64) -> bool { 6 | (float_1 - float_2).abs() < 0.0001 7 | } 8 | 9 | #[cfg(test)] 10 | pub fn to_node_id(data: (&'static str, &'static str, NodeType, Option<&str>)) -> GtfsId { 11 | let (id, t, stop_type, trip) = data; 12 | 13 | GtfsId { stop_id: id.to_string(), 14 | time: time_to_seconds_after_midnight(&t.to_string()).unwrap(), 15 | node_type: stop_type, 16 | trip_id: trip.map(|n| n.to_string()) 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /data/basic_graph.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |

13 |

14 |

15 | 16 | 17 |

18 |

19 | 20 | 21 |

22 |

23 | 24 | 25 |

26 |

27 | 28 | 29 |

30 |

31 | 32 | 33 |

34 |

35 |

36 | 37 | 38 |

39 |

40 | 41 | 42 |

43 |

44 | 45 | 46 | -------------------------------------------------------------------------------- /data/gtfs_example/stop_times.txt: -------------------------------------------------------------------------------- 1 | trip_id,arrival_time,departure_time,stop_id,stop_sequence,stop_headsign,pickup_type,drop_off_type,shape_dist_traveled 2 | g1,06:15:00,06:15:00,A,,,,, 3 | g1,06:45:00,06:45:00,C,,,,, 4 | g1,07:00:00,07:00:00,D,,,,, 5 | g1,07:30:00,07:30:00,E,,,,, 6 | g1,07:40:00,07:40:00,F,,,,, 7 | g2,06:45:00,06:45:00,A,,,,, 8 | g2,07:15:00,07:15:00,C,,,,, 9 | g2,07:30:00,07:30:00,D,,,,, 10 | g2,08:00:00,08:00:00,E,,,,, 11 | g2,08:10:00,08:10:00,F,,,,, 12 | g3,07:15:00,07:15:00,A,,,,, 13 | g3,07:45:00,07:45:00,C,,,,, 14 | g3,08:00:00,08:00:00,D,,,,, 15 | g3,08:30:00,08:30:00,E,,,,, 16 | g3,08:40:00,08:40:00,F,,,,, 17 | g4,07:45:00,07:45:00,A,,,,, 18 | g4,08:15:00,08:15:00,C,,,,, 19 | g4,08:30:00,08:30:00,D,,,,, 20 | g4,09:00:00,09:00:00,E,,,,, 21 | g4,09:10:00,09:10:00,F,,,,, 22 | g5,08:15:00,08:15:00,A,,,,, 23 | g5,08:45:00,08:45:00,C,,,,, 24 | g5,09:00:00,09:00:00,D,,,,, 25 | g5,09:30:00,09:30:00,E,,,,, 26 | g5,09:40:00,09:40:00,F,,,,, 27 | r1,06:00:00,06:00:00,A,,,,, 28 | r1,06:25:00,06:25:00,B,,,,, 29 | r1,06:50:00,06:50:00,E,,,,, 30 | r2,07:00:00,07:00:00,A,,,,, 31 | r2,07:25:00,07:25:00,B,,,,, 32 | r2,07:50:00,07:50:00,E,,,,, 33 | r3,08:00:00,08:00:00,A,,,,, 34 | r3,08:25:00,08:25:00,B,,,,, 35 | r3,08:50:00,08:50:00,E,,,,, 36 | -------------------------------------------------------------------------------- /data/example.osm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 10 | 13 | 14 | 15 | 16 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 30 | 31 | 32 | 33 | 34 | 36 | 37 | 38 | 39 | 40 | 41 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/a_star.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use weighted_graph::{ GraphKey, Graph }; 4 | use pathfinder::{ Pathfinder, CurrentBest, HeuristicFn, EdgeIterator }; 5 | 6 | pub fn shortest_path<'a, T>(graph: &'a Graph, 7 | source: &T, 8 | destination: Option<&T>, 9 | heuristic: HeuristicFn<'a, T> 10 | ) -> (i64, HashMap>) 11 | where T: GraphKey { 12 | let edge_iterator = |g: &'a Graph, node_id: &T| -> 13 | EdgeIterator<'a, T> { 14 | Box::new(g.get_edges(node_id).iter().filter(|_| true)) 15 | }; 16 | let terminator = |_: &CurrentBest, _: &HashMap>| false; 17 | let pathfinder = Pathfinder::new(heuristic, 18 | Box::new(edge_iterator), 19 | Box::new(terminator) 20 | ); 21 | pathfinder.shortest_path(graph, source, destination) 22 | } 23 | 24 | #[cfg(test)] 25 | mod test { 26 | use std::collections::HashMap; 27 | use weighted_graph::{ Graph, Node }; 28 | use super::shortest_path; 29 | 30 | fn build_graph() -> Graph<&'static str> { 31 | let mut graph = Graph::new(); 32 | graph.add_node("1", 1.0, 1.0); 33 | graph.add_node("2", 2.0, 4.0); 34 | graph.add_node("3", 3.0, 2.0); 35 | graph.add_node("4", 4.0, 1.0); 36 | graph.add_node("5", 5.0, 3.0); 37 | graph.add_node("6", 5.0, 5.0); 38 | 39 | let edges = vec![("a", "1", "2", 5), 40 | ("b", "2", "6", 2), 41 | ("c", "1", "3", 3), 42 | ("d", "3", "5", 3), 43 | ("e", "3", "4", 2), 44 | ("f", "4", "5", 3), 45 | ("g", "5", "6", 4)]; 46 | 47 | let mut iter = edges.into_iter(); 48 | 49 | while let Some((edge_id, node_id_1, node_id_2, cost)) = iter.next() { 50 | graph.add_edge(edge_id.clone(), node_id_1.clone(), node_id_2.clone(), cost); 51 | graph.add_edge(edge_id.clone(), node_id_2.clone(), node_id_1.clone(), cost); 52 | } 53 | 54 | graph 55 | } 56 | 57 | #[test] 58 | fn uses_heuristic_short_circuit() { 59 | let graph = build_graph(); 60 | let identity = |_: Option<&Node<&'static str>>, _: Option<&Node<&'static str>>| 0; 61 | let mut h = HashMap::new(); 62 | h.insert("1", 6); 63 | h.insert("2", 1); 64 | h.insert("3", 6); 65 | h.insert("4", 7); 66 | h.insert("5", 3); 67 | h.insert("6", 0); 68 | 69 | let heuristic = move |current: Option<&Node<&'static str>>, _: Option<&Node<&'static str>>| { 70 | *current.and_then(|node| h.get(&node.id)).unwrap() 71 | }; 72 | 73 | let (_, naive) = shortest_path(&graph, &"1", Some(&"6"), Box::new(identity)); 74 | let(_, heuristified) = shortest_path(&graph, &"1", Some(&"6"), Box::new(heuristic)); 75 | 76 | assert_eq!(naive.get(&"4").map(|b| b.cost), Some(5)); 77 | assert_eq!(heuristified.get(&"4"), None); 78 | assert_eq!(naive.get(&"5").map(|b| b.cost), Some(6)); 79 | assert_eq!(heuristified.get(&"5"), None); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/road_weights.rs: -------------------------------------------------------------------------------- 1 | use std::f64; 2 | use std::collections::HashMap; 3 | use weighted_graph::{ GraphKey, Node }; 4 | 5 | lazy_static! { 6 | pub static ref ROAD_TYPE_SPEED: HashMap<&'static str, i32> = { 7 | let mut m = HashMap::new(); 8 | m.insert("motorway", 110); 9 | m.insert("trunk", 110); 10 | m.insert("primary", 70); 11 | m.insert("secondary", 60); 12 | m.insert("tertiary", 50); 13 | m.insert("motorway_link", 50); 14 | m.insert("trunk_link", 50); 15 | m.insert("primary_link", 50); 16 | m.insert("secondary_link", 50); 17 | m.insert("road", 40); 18 | m.insert("unclassified", 40); 19 | m.insert("residential", 30); 20 | m.insert("unsurfaced", 30); 21 | m.insert("living_street", 10); 22 | m.insert("service", 5); 23 | m 24 | }; 25 | static ref RADIUS_EARTH_METERS: f64 = 6371000.0; 26 | } 27 | 28 | pub fn road_weight(from: &Node, to: &Node, road_type: &str) -> Option 29 | where T: GraphKey { 30 | ROAD_TYPE_SPEED.get(road_type).map(|speed| 31 | ((haversine(from.x, from.y, to.x, to.y) / *speed as f64) * 3600.0) as i64 32 | ) 33 | } 34 | 35 | fn degrees_to_radians(degrees: f64) -> f64 { 36 | (degrees / 180.0) * f64::consts::PI 37 | } 38 | 39 | fn haversine(from_lng: f64, from_lat: f64, to_lng: f64, to_lat: f64) -> f64 { 40 | let lat1 = degrees_to_radians(from_lat); 41 | let lat2 = degrees_to_radians(to_lat); 42 | let dlat = degrees_to_radians(to_lat - from_lat); 43 | let dlng = degrees_to_radians(to_lng - from_lng); 44 | 45 | let a = (dlat / 2.0).sin().powi(2) + 46 | lat1.cos() * lat2.cos() * (dlng / 2.0).sin().powi(2); 47 | let c = 2.0 * (a.sqrt().atan2((1.0 - a).sqrt())); 48 | 49 | *RADIUS_EARTH_METERS * c / 1000.0 // to km 50 | } 51 | 52 | #[cfg(test)] 53 | mod test { 54 | use super::{ ROAD_TYPE_SPEED, road_weight, haversine }; 55 | use weighted_graph::Node; 56 | use test_helpers::floats_nearly_eq; 57 | 58 | #[test] 59 | fn test_road_type_speed_construction() { 60 | assert_eq!(ROAD_TYPE_SPEED.get("motorway"), Some(&110)); 61 | } 62 | 63 | #[test] 64 | fn test_haversine() { 65 | let distance_1 = haversine(-71.085743, 42.343212, -71.087792, 42.347249); 66 | assert!(floats_nearly_eq(distance_1, 0.4794)); 67 | 68 | let distance_2 = haversine(-71.085743, 42.343212, -73.982969, 40.773046); 69 | assert!(floats_nearly_eq(distance_2, 297.6200)); 70 | } 71 | 72 | #[test] 73 | fn test_road_weight() { 74 | let node_1 = Node { id: "node-1".to_string(), 75 | x: -71.085743, 76 | y: 42.343212, 77 | contraction_order: None 78 | }; 79 | let node_2 = Node { id: "node-2".to_string(), 80 | x: -71.087792, 81 | y: 42.347249, 82 | contraction_order: None 83 | }; 84 | 85 | let motorway_weight = road_weight(&node_1, &node_2, "motorway"); 86 | let road_type_weight = road_weight(&node_1, &node_2, "road"); 87 | let service_weight = road_weight(&node_1, &node_2, "service"); 88 | let not_a_road_weight = road_weight(&node_1, &node_2, "notaroad"); 89 | 90 | assert_eq!(motorway_weight.unwrap(), 15); 91 | assert_eq!(road_type_weight.unwrap(), 43); 92 | assert_eq!(service_weight.unwrap(), 345); 93 | assert_eq!(not_a_road_weight, None); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/dijkstra.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use pathfinder::{ Pathfinder, CurrentBest, EdgeIterator }; 4 | use weighted_graph::{ GraphKey, Graph, Node }; 5 | 6 | pub fn shortest_path<'a, T>(graph: &'a Graph, 7 | source: &T, 8 | destination: Option<&T> 9 | ) -> (i64, HashMap>) 10 | where T: GraphKey { 11 | let identity = |_: Option<&Node>, _ :Option<&Node>| 0; 12 | let edge_iterator = |g: &'a Graph, node_id: &T| -> 13 | EdgeIterator<'a, T> { 14 | Box::new(g.get_edges(node_id).iter().filter(|_| true)) 15 | }; 16 | let terminator = |_: &CurrentBest, _: &HashMap>| false; 17 | let pathfinder = Pathfinder::new(Box::new(identity), 18 | Box::new(edge_iterator), 19 | Box::new(terminator) 20 | ); 21 | pathfinder.shortest_path(graph, source, destination) 22 | } 23 | 24 | #[cfg(test)] 25 | mod test { 26 | use super::shortest_path; 27 | use pathfinder::CurrentBest; 28 | use weighted_graph::Graph; 29 | use std::collections::HashMap; 30 | 31 | fn build_graph() -> Graph<&'static str> { 32 | let mut graph = Graph::new(); 33 | graph.add_node("1", 1.0, 1.0); 34 | graph.add_node("2", 1.0, 2.0); 35 | graph.add_node("3", 2.0, 1.0); 36 | graph.add_node("4", 2.0, 2.0); 37 | graph.add_node("5", 2.0, 3.0); 38 | graph.add_node("6", 3.0, 1.0); 39 | 40 | let edges = vec![("a", "1", "4", 1), 41 | ("b", "4", "2", 4), 42 | ("c", "2", "5", 3), 43 | ("d", "5", "6", 3), 44 | ("e", "6", "3", 1), 45 | ("f", "6", "4", 2)]; 46 | 47 | let mut iter = edges.into_iter(); 48 | 49 | while let Some((edge_id, node_id_1, node_id_2, cost)) = iter.next() { 50 | graph.add_edge(edge_id.clone(), node_id_1.clone(), node_id_2.clone(), cost); 51 | graph.add_edge(edge_id.clone(), node_id_2.clone(), node_id_1.clone(), cost); 52 | } 53 | 54 | graph 55 | } 56 | 57 | #[test] 58 | fn find_shortest_path() { 59 | let graph = build_graph(); 60 | 61 | let (cost, _) = shortest_path(&graph, &"1", Some(&"5")); 62 | 63 | assert_eq!(cost, 6); 64 | } 65 | 66 | #[test] 67 | fn find_all_shortest_paths() { 68 | let graph = build_graph(); 69 | let mut expected = HashMap::new(); 70 | expected.insert("1", CurrentBest { id: "1", 71 | cost: 0, 72 | predecessor: None 73 | }); 74 | expected.insert("2", CurrentBest { id: "2", 75 | cost: 5, 76 | predecessor: Some("4") 77 | }); 78 | expected.insert("3", CurrentBest { id: "3", 79 | cost: 4, 80 | predecessor: Some("6") 81 | }); 82 | expected.insert("4", CurrentBest { id: "4", 83 | cost: 1, 84 | predecessor: Some("1") 85 | }); 86 | expected.insert("5", CurrentBest { id: "5", 87 | cost: 6, 88 | predecessor: Some("6") 89 | }); 90 | expected.insert("6", CurrentBest { id: "6", 91 | cost: 3, 92 | predecessor: Some("4") 93 | }); 94 | 95 | let (_, results) = shortest_path(&graph, &"1", None); 96 | 97 | assert_eq!(results, expected); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/set_dijkstra.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use pathfinder::{ Pathfinder, CurrentBest, EdgeIterator }; 4 | use weighted_graph::{ GraphKey, Graph, Node }; 5 | 6 | pub fn shortest_path<'a, T>(graph: &'a Graph, 7 | sources: &Vec<&T>, 8 | destination: Option<&T> 9 | ) -> (i64, HashMap>) 10 | where T: GraphKey { 11 | let identity = |_: Option<&Node>, _ :Option<&Node>| 0; 12 | let edge_iterator = |g: &'a Graph, node_id: &T| -> 13 | EdgeIterator<'a, T> { 14 | Box::new(g.get_edges(node_id).iter().filter(|_| true)) 15 | }; 16 | let terminator = |_: &CurrentBest, _: &HashMap>| false; 17 | let pathfinder = Pathfinder::new(Box::new(identity), 18 | Box::new(edge_iterator), 19 | Box::new(terminator) 20 | ); 21 | pathfinder.set_shortest_path(graph, sources, destination) 22 | } 23 | 24 | #[cfg(test)] 25 | mod test { 26 | use super::shortest_path; 27 | use pathfinder::CurrentBest; 28 | use weighted_graph::Graph; 29 | use std::collections::HashMap; 30 | 31 | fn build_graph() -> Graph<&'static str> { 32 | let mut graph = Graph::new(); 33 | graph.add_node("1", 1.0, 1.0); 34 | graph.add_node("2", 1.0, 2.0); 35 | graph.add_node("3", 2.0, 1.0); 36 | graph.add_node("4", 2.0, 2.0); 37 | graph.add_node("5", 2.0, 3.0); 38 | graph.add_node("6", 3.0, 1.0); 39 | 40 | let edges = vec![("a", "1", "4", 1), 41 | ("b", "4", "2", 4), 42 | ("c", "2", "5", 3), 43 | ("d", "5", "6", 3), 44 | ("e", "6", "3", 1), 45 | ("f", "6", "4", 2)]; 46 | 47 | let mut iter = edges.into_iter(); 48 | 49 | while let Some((edge_id, node_id_1, node_id_2, cost)) = iter.next() { 50 | graph.add_edge(edge_id.clone(), node_id_1.clone(), node_id_2.clone(), cost); 51 | graph.add_edge(edge_id.clone(), node_id_2.clone(), node_id_1.clone(), cost); 52 | } 53 | 54 | graph 55 | } 56 | 57 | #[test] 58 | fn find_shortest_path() { 59 | let graph = build_graph(); 60 | let one = "1"; 61 | let three = "3"; 62 | let sources = vec![&one, &three]; 63 | let (cost, _) = shortest_path(&graph, &sources, Some(&"5")); 64 | 65 | assert_eq!(cost, 4); 66 | } 67 | 68 | #[test] 69 | fn find_all_shortest_paths() { 70 | let graph = build_graph(); 71 | let one = "1"; 72 | let three = "3"; 73 | let sources = vec![&one, &three]; 74 | 75 | let mut expected = HashMap::new(); 76 | expected.insert("1", CurrentBest { id: "1", 77 | cost: 0, 78 | predecessor: None 79 | }); 80 | expected.insert("2", CurrentBest { id: "2", 81 | cost: 5, 82 | predecessor: Some("4") 83 | }); 84 | expected.insert("3", CurrentBest { id: "3", 85 | cost: 0, 86 | predecessor: None 87 | }); 88 | expected.insert("4", CurrentBest { id: "4", 89 | cost: 1, 90 | predecessor: Some("1") 91 | }); 92 | expected.insert("5", CurrentBest { id: "5", 93 | cost: 4, 94 | predecessor: Some("6") 95 | }); 96 | expected.insert("6", CurrentBest { id: "6", 97 | cost: 1, 98 | predecessor: Some("3") 99 | }); 100 | 101 | let (_, results) = shortest_path(&graph, &sources, None); 102 | 103 | assert_eq!(results, expected); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/gtfs_dijkstra.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use pathfinder::{ Pathfinder, CurrentBest, EdgeIterator }; 4 | use weighted_graph::{ Graph, Node }; 5 | use graph_from_gtfs::{ GtfsId, 6 | StopId, 7 | }; 8 | 9 | pub fn shortest_path<'a>(graph: &'a Graph, 10 | source: &GtfsId, 11 | destination: StopId 12 | ) -> (i64, HashMap>) { 13 | let identity = |_: Option<&Node>, _ :Option<&Node>| 0; 14 | let edge_iterator = |g: &'a Graph, node_id: &GtfsId| -> 15 | EdgeIterator<'a, GtfsId> { 16 | Box::new(g.get_edges(node_id).iter().filter(|_| true)) 17 | }; 18 | let terminator = move |current: &CurrentBest, _: &HashMap>| { 19 | destination == current.id.stop_id 20 | }; 21 | let pathfinder = Pathfinder::new(Box::new(identity), 22 | Box::new(edge_iterator), 23 | Box::new(terminator) 24 | ); 25 | pathfinder.shortest_path(graph, source, None) 26 | } 27 | 28 | #[cfg(test)] 29 | mod test { 30 | use weighted_graph::Graph; 31 | use graph_from_gtfs::{ build_graph_from_gtfs, 32 | time_to_seconds_after_midnight, 33 | GtfsId, 34 | NodeType 35 | }; 36 | use super::shortest_path; 37 | 38 | fn build_graph() -> Graph { 39 | build_graph_from_gtfs("data/gtfs_example/", "wednesday") 40 | } 41 | 42 | #[test] 43 | fn direct_shortest_path() { 44 | let graph = build_graph(); 45 | let start_time = time_to_seconds_after_midnight(&"06:15:00".to_string()).unwrap(); 46 | let (cost, _) = shortest_path(&graph, 47 | &GtfsId { stop_id: "A".to_string(), 48 | time: start_time, 49 | node_type: NodeType::Arrival, 50 | trip_id: Some("g1".to_string()) 51 | }, 52 | "F".to_string()); 53 | 54 | assert_eq!(cost, 85 * 60); 55 | } 56 | 57 | #[test] 58 | fn shortest_path_with_transfer() { 59 | let graph = build_graph(); 60 | let start_time = time_to_seconds_after_midnight(&"07:00:00".to_string()).unwrap(); 61 | let (cost, _) = shortest_path(&graph, 62 | &GtfsId { stop_id: "A".to_string(), 63 | time: start_time, 64 | node_type: NodeType::Arrival, 65 | trip_id: Some("r2".to_string()) 66 | }, 67 | "F".to_string()); 68 | 69 | assert_eq!(cost, 70 * 60); 70 | } 71 | 72 | #[test] 73 | fn start_time_dependent_shortest_path() { 74 | let graph = build_graph(); 75 | let made_red_line = time_to_seconds_after_midnight(&"07:00:00".to_string()).unwrap(); 76 | let missed_red_line = time_to_seconds_after_midnight(&"07:15:00".to_string()).unwrap(); 77 | 78 | let (cost_red, _) = shortest_path(&graph, 79 | &GtfsId { stop_id: "A".to_string(), 80 | time: made_red_line, 81 | node_type: NodeType::Arrival, 82 | trip_id: Some("r2".to_string()) 83 | }, 84 | "E".to_string()); 85 | let (cost_green, _) = shortest_path(&graph, 86 | &GtfsId { stop_id: "A".to_string(), 87 | time: missed_red_line, 88 | node_type: NodeType::Arrival, 89 | trip_id: Some("g3".to_string()) 90 | }, 91 | "E".to_string()); 92 | 93 | assert_eq!(cost_red, 50 * 60); 94 | assert_eq!(cost_green, 75 * 60); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/prep/graph_from_xml.rs: -------------------------------------------------------------------------------- 1 | extern crate xml; 2 | 3 | use std::fs::File; 4 | use std::io::BufReader; 5 | use std::collections::HashMap; 6 | use self::xml::attribute::OwnedAttribute; 7 | use self::xml::reader::{ EventReader, XmlEvent }; 8 | 9 | use prep::weighted_graph::{ Graph, weight }; 10 | 11 | pub fn build_graph_from_xml(path: &str) -> Graph { 12 | let file = File::open(path).unwrap(); 13 | let reader = BufReader::new(file); 14 | 15 | let mut parser = EventReader::new(reader); 16 | let mut graph = Graph::new(); 17 | let mut current_edge_id = "".to_string(); 18 | let mut edge_nodes = vec![]; 19 | let mut eof = false; 20 | 21 | while !eof { 22 | match parser.next() { 23 | Ok(e) => match e { 24 | XmlEvent::StartElement { ref name, ref attributes, .. } => { 25 | match name.local_name.as_str() { 26 | "point" => { 27 | add_node(&mut graph, &attributes); 28 | } 29 | "edge" => { 30 | current_edge_id = get_attribute(&attributes, "id").unwrap_or("".to_string()); 31 | } 32 | "p" => { 33 | edge_nodes.push(get_attribute(&attributes, "ref").unwrap_or("".to_string())); 34 | } 35 | _ => {} 36 | } 37 | } 38 | XmlEvent::EndElement { ref name, .. } => { 39 | match name.local_name.as_str() { 40 | "edge" => { 41 | add_edge(&mut graph, ¤t_edge_id, &edge_nodes); 42 | current_edge_id = "".to_string(); 43 | edge_nodes.clear(); 44 | } 45 | _ => {} 46 | } 47 | } 48 | XmlEvent::EndDocument => { 49 | eof = true; 50 | } 51 | _ => {} 52 | }, 53 | Err(e) => println!("Error parsing XML document: {}", e) 54 | } 55 | } 56 | 57 | graph 58 | } 59 | 60 | fn add_node(graph: &mut Graph, attributes: &Vec) { 61 | let mut map = HashMap::new(); 62 | let mut atrb = attributes.iter().fold(&mut map, |m, attribute| { 63 | m.insert(attribute.name.local_name.clone(), 64 | attribute.value.clone()); 65 | m 66 | } 67 | ); 68 | graph.add_node(atrb.remove("id").unwrap(), 69 | atrb.remove("x").unwrap().parse::().unwrap(), 70 | atrb.remove("y").unwrap().parse::().unwrap() 71 | ) 72 | } 73 | 74 | fn add_edge(graph: &mut Graph, edge_id: &String, nodes: &Vec) { 75 | let mut pairs = nodes.windows(2); 76 | while let Some(pair) = pairs.next() { 77 | graph.add_edge(edge_id.clone(), 78 | pair[0].clone(), 79 | pair[1].clone()); 80 | graph.add_edge(edge_id.clone(), 81 | pair[1].clone(), 82 | pair[0].clone()); 83 | } 84 | } 85 | 86 | fn get_attribute(attributes: &Vec, attribute_name: &str) -> Option { 87 | let mut matches = attributes.iter().filter_map(|attribute| 88 | if attribute.name.local_name == attribute_name { 89 | Some(attribute.value.clone()) 90 | } else { 91 | None 92 | } 93 | ); 94 | matches.next() 95 | } 96 | 97 | #[cfg(test)] 98 | mod test { 99 | use super::{ build_graph_from_xml }; 100 | use prep::weighted_graph:: { Graph, Node }; 101 | 102 | fn has_node_ids(graph: &Graph) -> bool { 103 | vec!["0", "1", "2", "3", "4", "5", "6"].iter().all(|id| 104 | graph.get_node(id).is_some() 105 | ) 106 | } 107 | 108 | fn node_spot_check(graph: &Graph) -> bool { 109 | match graph.get_node("2") { 110 | Some(node) => { 111 | node == &Node { id: "2".to_string(), x: 3.0, y: -1.0 } 112 | } 113 | None => false 114 | } 115 | } 116 | 117 | fn has_edge_ids(graph: &Graph) -> bool { 118 | vec![("0", 3), ("1", 5), ("2", 3), ("3", 1), ("4", 3), ("5", 2), ("6", 3)] 119 | .iter().all(|t| 120 | graph.get_edges(t.0).is_some() && 121 | graph.get_edges(t.0).unwrap().len() == t.1 122 | ) 123 | } 124 | 125 | #[test] 126 | fn populate_graph() { 127 | let graph = build_graph_from_xml("data/basic_graph.xml"); 128 | 129 | assert!(has_node_ids(&graph)); 130 | assert!(node_spot_check(&graph)); 131 | assert!(has_edge_ids(&graph)); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/prep/weighted_graph.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | pub struct Graph { 4 | nodes: HashMap, 5 | edges: HashMap> 6 | } 7 | 8 | pub type NodeId = String; 9 | 10 | #[derive(PartialEq, Debug)] 11 | pub struct Node { 12 | pub id: NodeId, 13 | pub x: f64, 14 | pub y: f64 15 | } 16 | 17 | #[derive(PartialEq, Debug)] 18 | pub struct Edge { 19 | pub id: String, 20 | pub from_id: NodeId, 21 | pub to_id: NodeId, 22 | pub weight: f64 23 | } 24 | 25 | impl Graph { 26 | pub fn new() -> Self { 27 | Graph { 28 | edges: HashMap::new(), 29 | nodes: HashMap::new() 30 | } 31 | } 32 | 33 | pub fn add_node(&mut self, id: String, x: f64, y: f64) { 34 | let node = Node { id: id.clone(), x: x, y: y }; 35 | self.nodes.insert(id, node); 36 | } 37 | 38 | pub fn get_node(&self, id: &str) -> Option<&Node> { 39 | self.nodes.get(id) 40 | } 41 | 42 | pub fn add_edge(&mut self, id: String, from_id: NodeId, to_id: NodeId) { 43 | let edge = self.build_edge(&id, &from_id, &to_id); 44 | match edge { 45 | Some(e) => { 46 | let mut edges = self.edges.entry(from_id).or_insert(Vec::new()); 47 | edges.push(e); 48 | } 49 | None => {} 50 | } 51 | } 52 | 53 | fn build_edge(&self, id: &str, from_id: &NodeId, to_id: &NodeId) -> Option { 54 | let from = self.get_node(&from_id); 55 | let to = self.get_node(&to_id); 56 | if from.is_some() && to.is_some() { 57 | Some(Edge { id: id.to_string(), 58 | from_id: from_id.to_string(), 59 | to_id: to_id.to_string(), 60 | weight: weight(from.unwrap(), to.unwrap()) 61 | }) 62 | } else { 63 | None 64 | } 65 | } 66 | 67 | pub fn get_edges(&self, node_id: &str) -> Option<&Vec> { 68 | self.edges.get(node_id) 69 | } 70 | } 71 | 72 | pub fn weight(from: &Node, to: &Node) -> f64 { 73 | ((to.x - from.x).powi(2) + (to.y - from.y).powi(2)).sqrt() 74 | } 75 | 76 | #[cfg(test)] 77 | mod test { 78 | use super::{ Graph, Node, Edge, weight }; 79 | 80 | fn floats_nearly_eq(float_1: f64, float_2: f64) -> bool { 81 | (float_1 - float_2).abs() < 0.0001 82 | } 83 | 84 | #[test] 85 | fn test_weight() { 86 | let node_1 = Node { id: "1".to_string(), x: 3.0, y: 0.0 }; 87 | let node_2 = Node { id: "2".to_string(), x: 0.0, y: 4.0 }; 88 | 89 | assert!(floats_nearly_eq(weight(&node_1, &node_2), 5.0)); 90 | 91 | let node_3 = Node { id: "1".to_string(), x: 3.0, y: 0.0 }; 92 | let node_4 = Node { id: "2".to_string(), x: 0.0, y: -4.0 }; 93 | 94 | assert!(floats_nearly_eq(weight(&node_3, &node_4), 5.0)); 95 | } 96 | 97 | #[test] 98 | fn build_graph() { 99 | let mut graph = Graph::new(); 100 | 101 | graph.add_node("1".to_string(), 1.0, 1.0); 102 | graph.add_node("2".to_string(), 3.0, 5.0); 103 | 104 | let node_1 = graph.get_node("1"); 105 | assert!(node_1.is_some()); 106 | match node_1 { 107 | Some(node) => { 108 | assert_eq!(node.id, "1".to_string()); 109 | assert!(floats_nearly_eq(node.x, 1.0)); 110 | assert!(floats_nearly_eq(node.y, 1.0)); 111 | } 112 | None => {} 113 | } 114 | 115 | let node_2 = graph.get_node("2"); 116 | assert!(node_2.is_some()); 117 | match node_2 { 118 | Some(node) => { 119 | assert_eq!(node.id, "2".to_string()); 120 | assert!(floats_nearly_eq(node.x, 3.0)); 121 | assert!(floats_nearly_eq(node.y, 5.0)); 122 | } 123 | None => {} 124 | } 125 | 126 | let still_present = graph.get_node("1"); 127 | assert!(still_present.is_some()); 128 | } 129 | 130 | #[test] 131 | fn adding_edges() { 132 | let mut graph = Graph::new(); 133 | 134 | graph.add_node("n1".to_string(), 0.0, 12.0); 135 | graph.add_node("n2".to_string(), 5.0, 0.0); 136 | graph.add_node("n3".to_string(), 2.0, 4.0); 137 | 138 | graph.add_edge("e1".to_string(), "n2".to_string(), "n1".to_string()); 139 | graph.add_edge("e2".to_string(), "n3".to_string(), "n2".to_string()); 140 | graph.add_edge("e3".to_string(), "n2".to_string(), "n3".to_string()); 141 | 142 | let edges_n1 = graph.get_edges("n1"); 143 | let edges_n2 = graph.get_edges("n2"); 144 | let edges_n3 = graph.get_edges("n3"); 145 | 146 | assert_eq!(edges_n1, None); 147 | assert_eq!(edges_n2, Some(&vec![Edge { id: "e1".to_string(), 148 | from_id: "n2".to_string(), 149 | to_id: "n1".to_string(), 150 | weight: 13.0 151 | }, 152 | Edge { id: "e3".to_string(), 153 | from_id: "n2".to_string(), 154 | to_id: "n3".to_string(), 155 | weight: 5.0 156 | }])); 157 | assert_eq!(edges_n3, Some(&vec![Edge { id: "e2".to_string(), 158 | from_id: "n3".to_string(), 159 | to_id: "n2".to_string(), 160 | weight: 5.0 161 | }])); 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/pareto_sets.rs: -------------------------------------------------------------------------------- 1 | /* 2 | * Prove that if a pareto set of two-element vectors is ordered by increasing x 3 | * then every y in the ordering will be strictly decreasing. 4 | * 5 | * Proof by induction. 6 | * Base case, element 0 and element 1: 7 | * Because the vectors must be incomparable, x0 < x1 => y0 > y1. Otherwise, 8 | * e0 <= e1 9 | * Induction: 10 | * Given e(i), e(i+1) such that x(i) < x(i+1) and y(i) > y(i+1) 11 | * e(i+2) must not be comparable to e(i+1). Therefore x(i+1) < x(i+2) by hypothesis 12 | * and y(i+2) < y(i+1) by incomparability requirement. 13 | * Therefore, for all elements, x is strictly increasing, and y is strictly decreasing 14 | * 15 | * Also: I hate writing code to "optimize the number of comparisons". This could 16 | * be much cleaner if that was not a requirement. In order to return a new (ie copied, 17 | * not mutated) vector, it is required to iterate through the entire vector anyway. 18 | * The actual efficency savings is very small. The math bit is interesting though. 19 | */ 20 | use std::cmp::Ordering; 21 | 22 | #[derive(Debug, Eq, PartialEq)] 23 | pub enum PartialOrdering { 24 | Less, 25 | Equal, 26 | Greater, 27 | Incomparable 28 | } 29 | 30 | pub type Cost = (i64, i64); 31 | 32 | pub fn insert_element(ordered_pareto: &Vec, new_element: &Cost) -> Vec { 33 | let length = ordered_pareto.len(); 34 | let largest_x = ordered_pareto[length - 1].0; 35 | let largest_y = ordered_pareto[0].1; 36 | if new_element.0 >= largest_x && new_element.1 >= largest_y { 37 | return ordered_pareto.clone() 38 | } 39 | 40 | let mut new_pareto = vec![]; 41 | let mut compare = true; 42 | for elem in ordered_pareto { 43 | match compare { 44 | true => { 45 | if new_element.0 <= elem.0 && new_element.1 >= elem.1 { 46 | new_pareto.push(new_element.clone()); 47 | new_pareto.push(elem.clone()); 48 | compare = false; 49 | } else if partial_cmp(new_element, &elem) == PartialOrdering::Less { 50 | new_pareto.push(new_element.clone()); 51 | compare = false; 52 | } else if partial_cmp(new_element, &elem) == PartialOrdering::Greater { 53 | new_pareto.push(elem.clone()); 54 | compare = false; 55 | } else { 56 | new_pareto.push(elem.clone()); 57 | } 58 | } 59 | false => { 60 | new_pareto.push(elem.clone()); 61 | } 62 | } 63 | } 64 | 65 | if compare == true { 66 | new_pareto.push(new_element.clone()); 67 | } 68 | 69 | return new_pareto 70 | } 71 | 72 | pub fn partial_cmp(&(x1, y1): &Cost, &(x2, y2): &Cost) -> PartialOrdering { 73 | match (x1.cmp(&x2), y1.cmp(&y2)) { 74 | (Ordering::Equal, Ordering::Equal) => PartialOrdering::Equal, 75 | (Ordering::Less, Ordering::Equal) => PartialOrdering::Less, 76 | (Ordering::Equal, Ordering::Less) => PartialOrdering::Less, 77 | (Ordering::Less, Ordering::Less) => PartialOrdering::Less, 78 | (Ordering::Greater, Ordering::Equal) => PartialOrdering::Greater, 79 | (Ordering::Equal, Ordering::Greater) => PartialOrdering::Greater, 80 | (Ordering::Greater, Ordering::Greater) => PartialOrdering::Greater, 81 | (Ordering::Less, Ordering::Greater) => PartialOrdering::Incomparable, 82 | (Ordering::Greater, Ordering::Less) => PartialOrdering::Incomparable 83 | } 84 | } 85 | 86 | #[cfg(test)] 87 | mod test { 88 | use super::{ PartialOrdering, 89 | partial_cmp, 90 | insert_element 91 | }; 92 | 93 | #[test] 94 | fn comparing_costs() { 95 | let cost = (4, 7); 96 | let less = (3, 5); 97 | let greater = (5, 9); 98 | let equal = (4, 7); 99 | let one_elem_less = (4, 6); 100 | let one_elem_greater = (5, 7); 101 | let incomparable = (3, 8); 102 | let other_incomparable = (5, 6); 103 | 104 | assert_eq!(partial_cmp(&less, &cost), PartialOrdering::Less); 105 | assert_eq!(partial_cmp(&greater, &cost), PartialOrdering::Greater); 106 | assert_eq!(partial_cmp(&equal, &cost), PartialOrdering::Equal); 107 | assert_eq!(partial_cmp(&one_elem_less, &cost), PartialOrdering::Less); 108 | assert_eq!(partial_cmp(&one_elem_greater, &cost), PartialOrdering::Greater); 109 | assert_eq!(partial_cmp(&incomparable, &cost), PartialOrdering::Incomparable); 110 | assert_eq!(partial_cmp(&other_incomparable, &cost), PartialOrdering::Incomparable); 111 | } 112 | 113 | #[test] 114 | fn new_element_greater_than_all_existing() { 115 | let ordered_pareto = vec![(1, 5), (2, 4), (4, 3), (7, 1)]; 116 | let new_element = (8, 6); 117 | 118 | let new_pareto = insert_element(&ordered_pareto, &new_element); 119 | 120 | assert_eq!(new_pareto, ordered_pareto); 121 | } 122 | 123 | #[test] 124 | fn new_element_greater_than_one_but_not_all() { 125 | let ordered_pareto = vec![(1, 5), (2, 4), (4, 3), (7, 1)]; 126 | let new_element = (4, 4); 127 | 128 | let new_pareto = insert_element(&ordered_pareto, &new_element); 129 | 130 | assert_eq!(new_pareto, ordered_pareto); 131 | } 132 | 133 | #[test] 134 | fn new_element_incomparable_to_all_existing() { 135 | let ordered_pareto = vec![(1, 5), (2, 4), (4, 3), (7, 1)]; 136 | let new_element = (5, 2); 137 | 138 | let new_pareto = insert_element(&ordered_pareto, &new_element); 139 | 140 | assert_eq!(new_pareto, vec![(1, 5), (2, 4), (4, 3), (5, 2), (7, 1)]); 141 | } 142 | 143 | #[test] 144 | fn new_element_incomparable_to_all_existing_at_end() { 145 | let ordered_pareto = vec![(1, 5), (2, 4), (4, 3), (7, 1)]; 146 | let new_element = (8, 0); 147 | 148 | let new_pareto = insert_element(&ordered_pareto, &new_element); 149 | 150 | assert_eq!(new_pareto, vec![(1, 5), (2, 4), (4, 3), (7, 1), (8, 0)]); 151 | } 152 | 153 | #[test] 154 | fn new_element_incomparable_to_all_existing_at_beginning() { 155 | let ordered_pareto = vec![(1, 5), (2, 4), (4, 3), (7, 1)]; 156 | let new_element = (0, 6); 157 | 158 | let new_pareto = insert_element(&ordered_pareto, &new_element); 159 | 160 | assert_eq!(new_pareto, vec![(0, 6), (1, 5), (2, 4), (4, 3), (7, 1)]); 161 | } 162 | 163 | #[test] 164 | fn new_element_less_than_an_existing_element() { 165 | let ordered_pareto = vec![(1, 5), (2, 4), (4, 3), (7, 1)]; 166 | let new_element = (3, 2); 167 | 168 | let new_pareto = insert_element(&ordered_pareto, &new_element); 169 | 170 | assert_eq!(new_pareto, vec![(1, 5), (2, 4), (3, 2), (7, 1)]); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/a_star_heuristics.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use rand::{thread_rng, Rng}; 3 | 4 | use weighted_graph::{ GraphKey, Graph, Node }; 5 | use road_weights::road_weight; 6 | use dijkstra::shortest_path; 7 | use pathfinder::HeuristicFn; 8 | 9 | pub fn crow_files<'a, T>() -> HeuristicFn<'a, T> 10 | where T: 'a + GraphKey { 11 | Box::new(|current: Option<&Node>, target: Option<&Node>| { 12 | match (current, target) { 13 | (Some(cnode), Some(tnode)) => { 14 | road_weight(cnode, tnode, "motorway").unwrap_or(0) 15 | } 16 | _ => 0 17 | } 18 | }) 19 | } 20 | 21 | pub fn build_landmark_heuristic<'a, T>(graph: &Graph, num_landmarks: usize) -> HeuristicFn<'a, T> 22 | where T: 'a + GraphKey { 23 | landmarks( 24 | build_landmark_distances( 25 | graph, 26 | select_landmarks(graph, num_landmarks))) 27 | } 28 | 29 | fn landmarks<'a, T>(landmark_distances: Vec>) -> HeuristicFn<'a, T> 30 | where T: 'a + GraphKey { 31 | Box::new(move |current: Option<&Node>, target: Option<&Node>| { 32 | match (current, target) { 33 | (Some(c_node), Some(t_node)) => { 34 | landmark_distances.iter().filter_map(|distances| 35 | distances.get(&c_node.id) 36 | .and_then(|dist| 37 | distances.get(&t_node.id) 38 | .map(|t_dist| 39 | (dist - t_dist).abs())) 40 | ).max().unwrap_or(0) 41 | } 42 | _ => 0 43 | } 44 | }) 45 | } 46 | 47 | fn build_landmark_distances(graph: &Graph, landmarks: Vec) 48 | -> Vec> 49 | where T: GraphKey { 50 | landmarks.iter().map(|landmark_id| 51 | dijkstra_distances(graph, landmark_id) 52 | ).collect() 53 | } 54 | 55 | fn dijkstra_distances(graph: &Graph, source: &T) -> HashMap 56 | where T: GraphKey { 57 | let (_, results) = shortest_path(graph, source, None); 58 | results.iter().map(|(node_id, results)| 59 | (node_id.clone(), results.cost) 60 | ).collect() 61 | } 62 | 63 | fn select_landmarks(graph: &Graph, num_landmarks: usize) -> Vec 64 | where T: GraphKey { 65 | let mut nodes = graph.all_nodes(); 66 | let slice = nodes.as_mut_slice(); 67 | 68 | thread_rng().shuffle(slice); 69 | slice.iter().take(num_landmarks).map(|node| node.id.clone()).collect() 70 | } 71 | 72 | #[cfg(test)] 73 | mod test { 74 | use std::collections::HashMap; 75 | use weighted_graph::{ Graph, Node }; 76 | use road_weights::road_weight; 77 | use super::{ crow_files, 78 | select_landmarks, 79 | build_landmark_distances, 80 | landmarks 81 | }; 82 | 83 | fn build_graph() -> Graph<&'static str> { 84 | let mut graph = Graph::new(); 85 | graph.add_node("1", 1.0, 1.0); 86 | graph.add_node("2", 2.0, 4.0); 87 | graph.add_node("3", 3.0, 2.0); 88 | graph.add_node("4", 4.0, 1.0); 89 | graph.add_node("5", 5.0, 3.0); 90 | graph.add_node("6", 5.0, 5.0); 91 | 92 | let edges = vec![("a", "1", "2", 5), 93 | ("-a", "2", "1", 5), 94 | ("b", "2", "6", 2), 95 | ("-b", "6", "2", 2), 96 | ("c", "1", "3", 3), 97 | ("-c", "3", "1", 3), 98 | ("d", "3", "5", 3), 99 | ("-d", "5", "3", 3), 100 | ("e", "3", "4", 2), 101 | ("-e", "4", "3", 2), 102 | ("f", "4", "5", 3), 103 | ("-f", "5", "4", 3), 104 | ("g", "5", "6", 4), 105 | ("-g", "6", "5", 4)]; 106 | 107 | let mut iter = edges.into_iter(); 108 | 109 | while let Some((edge_id, node_id_1, node_id_2, cost)) = iter.next() { 110 | graph.add_edge(edge_id.clone(), node_id_1.clone(), node_id_2.clone(), cost); 111 | } 112 | 113 | graph 114 | } 115 | 116 | #[test] 117 | fn calculate_crow_flying_distance() { 118 | let node_1 = Node { id: "1", x: 0.0, y: 0.0, contraction_order: None }; 119 | let node_2 = Node { id: "2", x: 1.0, y: 1.0, contraction_order: None }; 120 | 121 | let heuristic = crow_files(); 122 | 123 | let expected = road_weight(&node_1, &node_2, "motorway").unwrap(); 124 | let actual = heuristic(Some(&node_1), Some(&node_2)); 125 | 126 | assert_eq!(actual, expected); 127 | } 128 | 129 | #[test] 130 | fn crow_flies_0_if_none() { 131 | let node_1 = Node { id: "1", x: 0.0, y: 0.0, contraction_order: None }; 132 | 133 | let heuristic = crow_files(); 134 | 135 | let actual = heuristic(Some(&node_1), None); 136 | 137 | assert_eq!(actual, 0); 138 | } 139 | 140 | #[test] 141 | fn pick_landmarks_from_graph() { 142 | let graph = build_graph(); 143 | 144 | for n in 1..6 { 145 | let landmarks = select_landmarks(&graph, n); 146 | assert_eq!(landmarks.len(), n); 147 | for landmark in landmarks { 148 | assert!(graph.get_node(&landmark).is_some()); 149 | } 150 | } 151 | 152 | } 153 | 154 | #[test] 155 | fn build_distances_to_landmarks() { 156 | let graph = build_graph(); 157 | 158 | let landmark_nodes = vec!["2", "3"]; 159 | 160 | let distances = build_landmark_distances(&graph, landmark_nodes); 161 | 162 | let mut results_2 = HashMap::new(); 163 | let mut results_3 = HashMap::new(); 164 | 165 | results_2.insert("1", 5); 166 | results_2.insert("2", 0); 167 | results_2.insert("3", 8); 168 | results_2.insert("4", 9); 169 | results_2.insert("5", 6); 170 | results_2.insert("6", 2); 171 | results_3.insert("1", 3); 172 | results_3.insert("2", 8); 173 | results_3.insert("3", 0); 174 | results_3.insert("4", 2); 175 | results_3.insert("5", 3); 176 | results_3.insert("6", 7); 177 | 178 | let expected = vec![results_2, results_3]; 179 | 180 | assert_eq!(distances, expected); 181 | } 182 | 183 | #[test] 184 | fn landmark_heuristic_returns_max_difference_landmark_distance() { 185 | let graph = build_graph(); 186 | let node_1 = graph.get_node(&"1"); 187 | let node_6 = graph.get_node(&"6"); 188 | let landmark_nodes = vec!["2", "3"]; 189 | 190 | let heuristic = landmarks( 191 | build_landmark_distances(&graph, landmark_nodes) 192 | ); 193 | 194 | 195 | assert_eq!(heuristic(node_1, node_6), 4); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/graph_from_xml.rs: -------------------------------------------------------------------------------- 1 | extern crate xml; 2 | 3 | use std::fs::File; 4 | use std::io::BufReader; 5 | use std::collections::HashMap; 6 | use self::xml::attribute::OwnedAttribute; 7 | use self::xml::reader::{ EventReader, XmlEvent }; 8 | 9 | use weighted_graph::Graph; 10 | use road_weights::road_weight; 11 | 12 | pub fn build_graph_from_xml(path: &str) -> Graph { 13 | let file = File::open(path).unwrap(); 14 | let reader = BufReader::new(file); 15 | 16 | let mut parser = EventReader::new(reader); 17 | let mut graph = Graph::new(); 18 | let mut current_edge_id = "".to_string(); 19 | let mut current_edge_type = "".to_string(); 20 | let mut edge_nodes = vec![]; 21 | let mut eof = false; 22 | 23 | while !eof { 24 | match parser.next() { 25 | Ok(e) => match e { 26 | XmlEvent::StartElement { ref name, ref attributes, .. } => { 27 | match name.local_name.as_str() { 28 | "node" => { 29 | add_node(&mut graph, &attributes); 30 | } 31 | "way" => { 32 | current_edge_id = get_attribute(&attributes, "id").unwrap_or("".to_string()); 33 | } 34 | "nd" => { 35 | edge_nodes.push(get_attribute(&attributes, "ref").unwrap_or("".to_string())); 36 | } 37 | "tag" => { 38 | get_attribute(&attributes, "k").map(|key| 39 | if key == "highway" { 40 | current_edge_type = get_attribute(&attributes, "v").unwrap(); 41 | } 42 | ); 43 | } 44 | _ => {} 45 | } 46 | } 47 | XmlEvent::EndElement { ref name, .. } => { 48 | match name.local_name.as_str() { 49 | "way" => { 50 | add_edge(&mut graph, ¤t_edge_id, ¤t_edge_type, &edge_nodes); 51 | current_edge_id = "".to_string(); 52 | current_edge_type = "".to_string(); 53 | edge_nodes.clear(); 54 | } 55 | _ => {} 56 | } 57 | } 58 | XmlEvent::EndDocument => { 59 | eof = true; 60 | } 61 | _ => {} 62 | }, 63 | Err(e) => println!("Error parsing XML document: {}", e) 64 | } 65 | } 66 | 67 | graph 68 | } 69 | 70 | fn add_node(graph: &mut Graph, attributes: &Vec) { 71 | let mut map = HashMap::new(); 72 | let mut atrb = attributes.iter().fold(&mut map, |m, attribute| { 73 | m.insert(attribute.name.local_name.clone(), 74 | attribute.value.clone()); 75 | m 76 | } 77 | ); 78 | graph.add_node(atrb.remove("id").unwrap(), 79 | atrb.remove("lon").unwrap().parse::().unwrap(), 80 | atrb.remove("lat").unwrap().parse::().unwrap() 81 | ) 82 | } 83 | 84 | fn add_edge(graph: &mut Graph, edge_id: &String, edge_type: &str, nodes: &Vec) { 85 | let mut pairs = nodes.windows(2); 86 | while let Some(pair) = pairs.next() { 87 | match road_weight(graph.get_node(&pair[0]).unwrap(), 88 | graph.get_node(&pair[1]).unwrap(), 89 | edge_type) { 90 | Some(weight) => { 91 | graph.add_edge(edge_id.clone(), 92 | pair[0].clone(), 93 | pair[1].clone(), 94 | weight); 95 | graph.add_edge(edge_id.clone(), 96 | pair[1].clone(), 97 | pair[0].clone(), 98 | weight); 99 | } 100 | None => {} 101 | }; 102 | } 103 | } 104 | 105 | fn get_attribute(attributes: &Vec, attribute_name: &str) -> Option { 106 | let mut matches = attributes.iter().filter_map(|attribute| 107 | if attribute.name.local_name == attribute_name { 108 | Some(attribute.value.clone()) 109 | } else { 110 | None 111 | } 112 | ); 113 | matches.next() 114 | } 115 | 116 | #[cfg(test)] 117 | mod test { 118 | use super::{ build_graph_from_xml }; 119 | use weighted_graph:: { Graph, Node }; 120 | use road_weights::road_weight; 121 | 122 | fn has_node_ids(graph: &Graph) -> bool { 123 | vec!["292403538", "298884289", "261728686", "298884272"].iter().all(|id| 124 | graph.get_node(&id.to_string()).is_some() 125 | ) 126 | } 127 | 128 | fn node_spot_check(graph: &Graph) -> bool { 129 | match graph.get_node(&"292403538".to_string()) { 130 | Some(node) => { 131 | node == &Node { id: "292403538".to_string(), 132 | x: 12.2482632, 133 | y: 54.0901746, 134 | contraction_order: None 135 | } 136 | } 137 | None => false 138 | } 139 | } 140 | 141 | fn has_edges_for_nodes(graph: &Graph) -> bool { 142 | vec![("292403538".to_string(), 2), 143 | ("298884289".to_string(), 2), 144 | ("261728686".to_string(), 2), 145 | ("298884272".to_string(), 2)] 146 | .iter().all(|t| 147 | graph.get_edges(&t.0).len() == t.1 148 | ) 149 | } 150 | 151 | fn edge_spot_check(graph: &Graph) -> bool { 152 | let edges = graph.get_edges(&"298884289".to_string()); 153 | edges.len() == 2 && 154 | edges.iter().any(|edge| 155 | edge.from_id == "298884289" && 156 | edge.to_id == "292403538" && 157 | (edge.weight == 158 | road_weight(graph.get_node(&"298884289".to_string()).unwrap(), 159 | graph.get_node(&"292403538".to_string()).unwrap(), 160 | "unclassified").unwrap()) 161 | ) && 162 | edges.iter().any(|edge| 163 | edge.from_id == "298884289" && 164 | edge.to_id == "261728686" && 165 | (edge.weight == 166 | road_weight(graph.get_node(&"298884289".to_string()).unwrap(), 167 | graph.get_node(&"261728686".to_string()).unwrap(), 168 | "unclassified").unwrap()) 169 | ) 170 | } 171 | 172 | #[test] 173 | fn populate_graph() { 174 | let graph = build_graph_from_xml("data/example.osm"); 175 | 176 | assert!(has_node_ids(&graph)); 177 | assert!(node_spot_check(&graph)); 178 | assert!(has_edges_for_nodes(&graph)); 179 | assert!(edge_spot_check(&graph)); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/arc_flags.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use weighted_graph::{ GraphKey, Graph, Node }; 3 | use dijkstra::shortest_path as dijkstra; 4 | use pathfinder::{ CurrentBest, Pathfinder, EdgeIterator }; 5 | 6 | pub fn shortest_path<'a, T>(graph: &'a Graph, 7 | source: &T, 8 | destination: Option<&T> 9 | ) -> (i64, HashMap>) 10 | where T: GraphKey { 11 | let identity = |_: Option<&Node>, _ :Option<&Node>| 0; 12 | let edge_iterator = |g: &'a Graph, node_id: &T| -> 13 | EdgeIterator<'a, T> { 14 | Box::new(g.get_edges(node_id).iter().filter(|edge| edge.arc_flag)) 15 | }; 16 | let terminator = |_: &CurrentBest, _: &HashMap>| false; 17 | let pathfinder = Pathfinder::new(Box::new(identity), 18 | Box::new(edge_iterator), 19 | Box::new(terminator) 20 | ); 21 | pathfinder.shortest_path(graph, source, destination) 22 | } 23 | 24 | pub struct Rect { 25 | x_max: f64, 26 | x_min: f64, 27 | y_max: f64, 28 | y_min: f64 29 | } 30 | 31 | impl Rect { 32 | pub fn contains(&self, node: &Node) -> bool 33 | where T: GraphKey { 34 | node.x <= self.x_max && 35 | node.x >= self.x_min && 36 | node.y <= self.y_max && 37 | node.y >= self.y_min 38 | } 39 | } 40 | 41 | pub fn assign_arc_flags(graph: &mut Graph, region: Rect) 42 | where T: GraphKey { 43 | let internal = &internal_nodes(graph, ®ion)[..]; 44 | let results = inbound_paths(graph, internal, ®ion); 45 | for result in results { 46 | if let Some(predecessor) = result.predecessor { 47 | graph.get_mut_edge(&result.id, &predecessor) 48 | .map(|edge| edge.arc_flag = true); 49 | } 50 | } 51 | 52 | for from_id in internal { 53 | for to_id in internal { 54 | graph.get_mut_edge(&from_id, &to_id).map(|edge| edge.arc_flag = true); 55 | } 56 | } 57 | } 58 | 59 | fn inbound_paths(graph: &Graph, node_ids: &[T], region: &Rect) -> Vec> 60 | where T: GraphKey { 61 | node_ids.iter() 62 | .filter(|node_id| boundary_node(graph, region, *node_id)) 63 | .flat_map(|node_id| 64 | dijkstra(graph, &node_id, None).1.into_iter() 65 | .map(|(_, v)| v) 66 | ).collect() 67 | } 68 | 69 | fn internal_nodes(graph: &Graph, rect: &Rect) -> Vec 70 | where T: GraphKey { 71 | graph.all_nodes() 72 | .into_iter() 73 | .filter(|node_ref| rect.contains(node_ref)) 74 | .map(|node_ref| node_ref.id.clone()) 75 | .collect::>() 76 | } 77 | 78 | fn boundary_node(graph: &Graph, rect: &Rect, node_id: &T) -> bool 79 | where T: GraphKey { 80 | match graph.get_node(node_id) { 81 | Some(node) => { 82 | rect.contains(node) && 83 | graph.get_edges(&node.id).iter().any(|edge| 84 | graph.get_node(&edge.to_id).map(|node| 85 | !rect.contains(node) 86 | ).unwrap_or(false) 87 | ) 88 | } 89 | None => false 90 | } 91 | } 92 | 93 | #[cfg(test)] 94 | mod test { 95 | use std::collections::HashSet; 96 | use weighted_graph::{ Graph, Node }; 97 | use super::{ Rect, 98 | boundary_node, 99 | assign_arc_flags, 100 | shortest_path 101 | }; 102 | 103 | fn build_graph() -> Graph<&'static str> { 104 | let mut graph = Graph::new(); 105 | graph.add_node("1", 1.0, 2.0); 106 | graph.add_node("2", 2.0, 1.0); 107 | graph.add_node("3", 2.0, 2.0); 108 | graph.add_node("4", 3.0, 3.0); 109 | graph.add_node("5", 3.0, 4.0); 110 | graph.add_node("6", 4.0, 3.0); 111 | 112 | let edges = vec![("af", "1", "5", 1), 113 | ("ar", "5", "1", 1), 114 | ("bf", "5", "6", 1), 115 | ("br", "6", "5", 1), 116 | ("cf", "2", "6", 4), 117 | ("cr", "6", "2", 4), 118 | ("df", "2", "4", 1), 119 | ("dr", "4", "2", 1), 120 | ("ef", "3", "4", 1), 121 | ("er", "4", "3", 1)]; 122 | 123 | for (edge_id, node_id_1, node_id_2, cost) in edges { 124 | graph.add_edge(edge_id.clone(), node_id_1.clone(), node_id_2.clone(), cost); 125 | } 126 | 127 | graph 128 | } 129 | 130 | #[test] 131 | fn node_contains_rectangle() { 132 | let rect = Rect { x_min: 0.0, x_max: 5.0, y_min: 0.0, y_max: 5.0 }; 133 | let contains = Node { id: "contains", x: 1.0, y: 1.0, contraction_order: None }; 134 | let outside = Node { id: "outside", x: 10.0, y: 10.0, contraction_order: None }; 135 | let border = Node { id: "border", x: 0.0, y: 3.0, contraction_order: None }; 136 | 137 | assert!(rect.contains(&contains)); 138 | assert!(!rect.contains(&outside)); 139 | assert!(rect.contains(&border)); 140 | } 141 | 142 | #[test] 143 | fn identify_boundary_node() { 144 | let graph = build_graph(); 145 | let rect = Rect { x_min: 1.5, 146 | x_max: 3.5, 147 | y_min: 1.5, 148 | y_max: 3.5 149 | }; 150 | 151 | assert!(boundary_node(&graph, &rect, &"4")); 152 | assert!(!boundary_node(&graph, &rect, &"3")); 153 | assert!(!boundary_node(&graph, &rect, &"1")); 154 | } 155 | 156 | #[test] 157 | fn arc_flag_assignments() { 158 | let mut graph = build_graph(); 159 | let region = Rect { x_min: 1.5, 160 | x_max: 3.5, 161 | y_min: 1.5, 162 | y_max: 3.5 163 | }; 164 | 165 | assign_arc_flags(&mut graph, region); 166 | 167 | let flagged_arcs: HashSet<&str> = vec!["af", 168 | "bf", 169 | "cr", 170 | "df", 171 | "ef", 172 | "er"].into_iter().collect(); 173 | 174 | for node in graph.all_nodes() { 175 | for edge in graph.get_edges(&node.id) { 176 | if flagged_arcs.contains(&edge.id) { 177 | assert!(edge.arc_flag); 178 | } else { 179 | assert!(!edge.arc_flag); 180 | } 181 | } 182 | } 183 | } 184 | 185 | #[test] 186 | fn shortest_path_uses_arc_flags() { 187 | let mut graph = build_graph(); 188 | 189 | let region = Rect { x_min: 1.5, 190 | x_max: 3.5, 191 | y_min: 1.5, 192 | y_max: 3.5 193 | }; 194 | 195 | assign_arc_flags(&mut graph, region); 196 | 197 | let (cost, results) = shortest_path(&graph, &"6", Some(&"4")); 198 | 199 | assert!(!results.values().any(|r| r.id == "5")); 200 | assert_eq!(cost, 5) 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/connected_component.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{ HashSet }; 2 | use weighted_graph::{ GraphKey, Graph }; 3 | use dijkstra::shortest_path; 4 | 5 | pub fn reduce_to_largest_connected_component(graph: Graph) -> Graph 6 | where T: GraphKey { 7 | let untested_nodes = node_ids(&graph); 8 | reducer(graph, untested_nodes, vec![]) 9 | } 10 | 11 | fn reducer(graph: Graph, untested_nodes: HashSet, mut results: Vec>) -> Graph 12 | where T: GraphKey { 13 | match untested_nodes.iter().next() { 14 | None => { 15 | collapsed_graph(&graph, &results) 16 | } 17 | Some(root) => { 18 | let connected_nodes = explore_from(root, &graph); 19 | let difference = untested_nodes.difference(&connected_nodes) 20 | .cloned() 21 | .collect(); 22 | results.push(connected_nodes); 23 | reducer(graph, 24 | difference, 25 | results 26 | ) 27 | } 28 | } 29 | } 30 | 31 | fn explore_from(root: &T, graph: &Graph) -> HashSet { 32 | let (_, results) = shortest_path(graph, root, None); 33 | results.values() 34 | .map(|result| result.id.clone()) 35 | .collect() 36 | } 37 | 38 | fn collapsed_graph(graph: &Graph, results: &Vec>) -> Graph 39 | where T: GraphKey { 40 | let mut new_graph = Graph::new(); 41 | if let Some(nodes) = results.iter().max_by_key(|results| results.len()) { 42 | for node_id in nodes { 43 | add_node(graph, &mut new_graph, &node_id); 44 | } 45 | for node_id in nodes { 46 | add_edges(graph, &mut new_graph, &node_id); 47 | } 48 | } 49 | new_graph 50 | } 51 | 52 | fn add_node(old_graph: &Graph, mut new_graph: &mut Graph, id: &T) 53 | where T: GraphKey { 54 | if let Some(node) = old_graph.get_node(id) { 55 | new_graph.add_node(id.clone(), 56 | node.x, 57 | node.y); 58 | } 59 | } 60 | 61 | fn add_edges(old_graph: &Graph, mut new_graph: &mut Graph, id: &T) 62 | where T: GraphKey { 63 | for edge in old_graph.get_edges(id) { 64 | new_graph.add_edge(edge.id.clone(), 65 | id.clone(), 66 | edge.to_id.clone(), 67 | edge.weight); 68 | } 69 | } 70 | 71 | fn node_ids(graph: &Graph) -> HashSet 72 | where T: GraphKey { 73 | graph.all_nodes() 74 | .iter() 75 | .map(|node| node.id.clone()) 76 | .collect::>() 77 | } 78 | 79 | #[cfg(test)] 80 | mod test { 81 | use std::collections::HashSet; 82 | use weighted_graph::Graph; 83 | use super::{ reduce_to_largest_connected_component, 84 | node_ids, 85 | explore_from, 86 | add_node, 87 | add_edges 88 | }; 89 | 90 | fn build_graph() -> Graph<&'static str> { 91 | let mut graph = Graph::new(); 92 | graph.add_node("1", 1.0, 1.0); 93 | graph.add_node("2", 1.0, 2.0); 94 | graph.add_node("3", 2.0, 1.0); 95 | graph.add_node("4", 2.0, 2.0); 96 | graph.add_node("5", 2.0, 3.0); 97 | graph.add_node("6", 3.0, 1.0); 98 | graph.add_node("7", 4.0, 2.0); 99 | graph.add_node("8", 5.0, 3.0); 100 | graph.add_node("9", 5.0, 2.0); 101 | 102 | let edges = vec![("a", "1", "4", 1), 103 | ("b", "4", "2", 4), 104 | ("c", "2", "5", 3), 105 | ("d", "5", "6", 3), 106 | ("e", "6", "3", 1), 107 | ("f", "6", "4", 2), 108 | ("g", "7", "8", 1), 109 | ("h", "8", "9", 3), 110 | ("i", "9", "7", 2)]; 111 | 112 | let mut iter = edges.into_iter(); 113 | 114 | while let Some((edge_id, node_id_1, node_id_2, cost)) = iter.next() { 115 | graph.add_edge(edge_id.clone(), node_id_1.clone(), node_id_2.clone(), cost); 116 | graph.add_edge(edge_id.clone(), node_id_2.clone(), node_id_1.clone(), cost); 117 | } 118 | 119 | graph 120 | } 121 | 122 | #[test] 123 | fn initial_node_ids() { 124 | let graph = build_graph(); 125 | 126 | let expected: HashSet<&str> = vec!["1", 127 | "2", 128 | "3", 129 | "4", 130 | "5", 131 | "6", 132 | "7", 133 | "8", 134 | "9"].into_iter().collect(); 135 | 136 | let nodes = node_ids(&graph); 137 | 138 | assert_eq!(nodes, expected); 139 | } 140 | 141 | #[test] 142 | fn return_connected_nodes() { 143 | let graph = build_graph(); 144 | 145 | let root = "9"; 146 | 147 | let connected_nodes = explore_from(&root, &graph); 148 | let small_connection: HashSet<&str> = vec!["7", 149 | "8", 150 | root].into_iter() 151 | .collect(); 152 | 153 | assert_eq!(connected_nodes, small_connection); 154 | } 155 | 156 | #[test] 157 | fn test_add_node() { 158 | let graph = build_graph(); 159 | let node_id = "1"; 160 | let mut new_graph = Graph::new(); 161 | 162 | add_node(&graph, &mut new_graph, &node_id); 163 | 164 | assert_eq!(new_graph.get_node(&node_id), graph.get_node(&node_id)); 165 | } 166 | 167 | #[test] 168 | fn test_add_edges() { 169 | let graph = build_graph(); 170 | let node_id = "7"; 171 | let mut new_graph = Graph::new(); 172 | 173 | add_node(&graph, &mut new_graph, &node_id); 174 | add_node(&graph, &mut new_graph, &"9"); 175 | add_edges(&graph, &mut new_graph, &node_id); 176 | add_edges(&graph, &mut new_graph, &"9"); 177 | 178 | let nodes_from_seven: Vec<&str> = new_graph.get_edges(&node_id) 179 | .iter() 180 | .map(|edge| edge.to_id) 181 | .collect(); 182 | let nodes_from_nine: Vec<&str> = new_graph.get_edges(&"9") 183 | .iter() 184 | .map(|edge| edge.to_id) 185 | .collect(); 186 | 187 | assert_eq!(nodes_from_seven, vec!["9"]); 188 | assert_eq!(nodes_from_nine, vec!["7"]); 189 | } 190 | 191 | #[test] 192 | fn find_connected_component() { 193 | let graph = build_graph(); 194 | 195 | assert!(graph.get_node(&"7").is_some()); 196 | assert!(graph.get_node(&"8").is_some()); 197 | assert!(graph.get_node(&"9").is_some()); 198 | 199 | let connected_graph = reduce_to_largest_connected_component(graph); 200 | 201 | assert!(connected_graph.get_node(&"7").is_none()); 202 | assert!(connected_graph.get_node(&"8").is_none()); 203 | assert!(connected_graph.get_node(&"9").is_none()); 204 | assert!(connected_graph.get_node(&"1").is_some()); 205 | assert!(connected_graph.get_node(&"2").is_some()); 206 | assert!(connected_graph.get_node(&"3").is_some()); 207 | assert!(connected_graph.get_node(&"4").is_some()); 208 | assert!(connected_graph.get_node(&"5").is_some()); 209 | assert!(connected_graph.get_node(&"6").is_some()); 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/prep/undirected_graph.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{ HashMap, HashSet, VecDeque }; 2 | 3 | pub struct Graph(HashMap>); 4 | 5 | impl<'a> Graph { 6 | pub fn new() -> Self { 7 | Graph(HashMap::new()) 8 | } 9 | 10 | pub fn add_edge(&mut self, a: String, b: String) { 11 | { 12 | let a_adjacent = self.0.entry(a.clone()).or_insert(HashSet::new()); 13 | a_adjacent.insert(b.clone()); 14 | } 15 | { 16 | let b_adjacent = self.0.entry(b).or_insert(HashSet::new()); 17 | b_adjacent.insert(a); 18 | } 19 | } 20 | 21 | pub fn adjacent_nodes(&'a self, node: &String) -> Option<&'a HashSet> { 22 | self.0.get(node) 23 | } 24 | } 25 | 26 | struct Queue(VecDeque); 27 | struct Stack(VecDeque); 28 | 29 | pub trait Seq { 30 | fn push(&mut self, val: T); 31 | fn pop(&mut self) -> Option; 32 | fn new() -> Self; 33 | } 34 | 35 | impl Seq for Stack { 36 | fn new() -> Self { 37 | Stack(VecDeque::new()) 38 | } 39 | 40 | fn push(&mut self, val: T) { 41 | self.0.push_back(val) 42 | } 43 | 44 | fn pop(&mut self) -> Option { 45 | self.0.pop_back() 46 | } 47 | } 48 | 49 | impl Seq for Queue { 50 | fn new() -> Self { 51 | Queue(VecDeque::new()) 52 | } 53 | 54 | fn push(&mut self, val: T) { 55 | self.0.push_back(val) 56 | } 57 | 58 | fn pop(&mut self) -> Option { 59 | self.0.pop_front() 60 | } 61 | } 62 | 63 | fn search<'a, F, S: Seq<&'a str>>(graph: &'a Graph, root: &'a str, f: &mut F, seq: S) 64 | where F: FnMut(&str) { 65 | 66 | let root_ref = &root.to_string(); 67 | let mut seen = seq; 68 | let mut marked = HashSet::new(); 69 | seen.push(root); 70 | marked.insert(root_ref); 71 | while let Some(current_node) = seen.pop() { 72 | f(current_node); 73 | 74 | match graph.adjacent_nodes(¤t_node.to_string()) { 75 | Some(adjacent_nodes) => { 76 | let mut iter = adjacent_nodes.iter(); 77 | while let Some(node) = iter.next() { 78 | if !marked.contains(&node) { 79 | marked.insert(node); 80 | seen.push(node); 81 | } 82 | } 83 | } 84 | None => {} 85 | } 86 | } 87 | } 88 | 89 | pub fn breadth_first_search(graph: &Graph, root: &str, f: &mut F) 90 | where F: FnMut(&str) { 91 | search(graph, root, f, as Seq<&str>>::new()); 92 | } 93 | 94 | pub fn depth_first_search(graph: &Graph, root: &str, f: &mut F) 95 | where F: FnMut(&str) { 96 | search(graph, root, f, as Seq<&str>>::new()); 97 | } 98 | 99 | #[cfg(test)] 100 | mod test { 101 | use std::collections::{ HashSet, VecDeque }; 102 | use super::{ Graph, 103 | breadth_first_search, 104 | depth_first_search, 105 | Stack, 106 | Queue, 107 | Seq 108 | }; 109 | 110 | #[test] 111 | fn stack() { 112 | let a = "A".to_string(); 113 | let b = "B".to_string(); 114 | let c = "C".to_string(); 115 | 116 | let mut stack = Stack::new(); 117 | stack.push(&a); 118 | stack.push(&b); 119 | stack.push(&c); 120 | 121 | assert_eq!(stack.pop(), Some(&c)); 122 | assert_eq!(stack.pop(), Some(&b)); 123 | assert_eq!(stack.pop(), Some(&a)); 124 | assert_eq!(stack.pop(), None); 125 | } 126 | 127 | #[test] 128 | fn queue() { 129 | let a = "A".to_string(); 130 | let b = "B".to_string(); 131 | let c = "C".to_string(); 132 | 133 | let mut queue = Queue::new(); 134 | queue.push(&a); 135 | queue.push(&b); 136 | queue.push(&c); 137 | 138 | assert_eq!(queue.pop(), Some(&a)); 139 | assert_eq!(queue.pop(), Some(&b)); 140 | assert_eq!(queue.pop(), Some(&c)); 141 | assert_eq!(queue.pop(), None); 142 | } 143 | 144 | #[test] 145 | fn build_graph() { 146 | let mut graph = Graph::new(); 147 | 148 | // A -- B 149 | // | | 150 | // C -- D 151 | 152 | graph.add_edge("A".to_string(), "B".to_string()); 153 | graph.add_edge("A".to_string(), "C".to_string()); 154 | graph.add_edge("B".to_string(), "D".to_string()); 155 | graph.add_edge("C".to_string(), "D".to_string()); 156 | 157 | let mut a_adj = HashSet::new(); 158 | a_adj.insert("B".to_string()); 159 | a_adj.insert("C".to_string()); 160 | assert_eq!(graph.adjacent_nodes(&"A".to_string()), Some(&a_adj)); 161 | 162 | let mut b_adj = HashSet::new(); 163 | b_adj.insert("A".to_string()); 164 | b_adj.insert("D".to_string()); 165 | assert_eq!(graph.adjacent_nodes(&"B".to_string()), Some(&b_adj)); 166 | 167 | let mut c_adj = HashSet::new(); 168 | c_adj.insert("A".to_string()); 169 | c_adj.insert("D".to_string()); 170 | assert_eq!(graph.adjacent_nodes(&"C".to_string()), Some(&c_adj)); 171 | 172 | let mut d_adj = HashSet::new(); 173 | d_adj.insert("B".to_string()); 174 | d_adj.insert("C".to_string()); 175 | assert_eq!(graph.adjacent_nodes(&"D".to_string()), Some(&d_adj)); 176 | } 177 | 178 | #[test] 179 | fn bfs() { 180 | let mut graph = Graph::new(); 181 | 182 | // A -- B 183 | // | | 184 | // C -- D 185 | 186 | graph.add_edge("A".to_string(), "B".to_string()); 187 | graph.add_edge("A".to_string(), "C".to_string()); 188 | graph.add_edge("B".to_string(), "D".to_string()); 189 | graph.add_edge("C".to_string(), "D".to_string()); 190 | let root = "A"; 191 | 192 | let mut visited = VecDeque::new(); 193 | 194 | { 195 | breadth_first_search( 196 | &graph, 197 | &root, 198 | &mut (|node: &str| visited.push_back(node.to_string())) 199 | ); 200 | } 201 | 202 | let inner_node = |node| 203 | node == Some("B".to_string()) || node == Some("C".to_string()); 204 | 205 | assert_eq!(visited.pop_front(), Some("A".to_string())); 206 | assert_eq!(visited.pop_back(), Some("D".to_string())); 207 | assert_eq!(visited.len(), 2); 208 | assert!(inner_node(visited.pop_front())); 209 | assert!(inner_node(visited.pop_front())); 210 | 211 | let mut a_adj = HashSet::new(); 212 | a_adj.insert("B".to_string()); 213 | a_adj.insert("C".to_string()); 214 | assert_eq!(graph.adjacent_nodes(&"A".to_string()), Some(&a_adj)); 215 | } 216 | 217 | #[test] 218 | fn dfs() { 219 | let mut graph = Graph::new(); 220 | 221 | // A -- B 222 | // | | 223 | // C -- D 224 | 225 | graph.add_edge("A".to_string(), "B".to_string()); 226 | graph.add_edge("A".to_string(), "C".to_string()); 227 | graph.add_edge("B".to_string(), "D".to_string()); 228 | graph.add_edge("C".to_string(), "D".to_string()); 229 | 230 | let mut visited = VecDeque::new(); 231 | 232 | { 233 | depth_first_search( 234 | &graph, 235 | &"A".to_string(), 236 | &mut (|node: &str| visited.push_back(node.to_string())) 237 | ); 238 | } 239 | 240 | let inner_node = |node| 241 | node == Some("B".to_string()) || node == Some("C".to_string()); 242 | 243 | assert_eq!(visited.pop_front(), Some("A".to_string())); 244 | assert!(inner_node(visited.pop_front())); 245 | assert_eq!(visited.pop_front(), Some("D".to_string())); 246 | assert!(inner_node(visited.pop_front())); 247 | 248 | let mut a_adj = HashSet::new(); 249 | a_adj.insert("B".to_string()); 250 | a_adj.insert("C".to_string()); 251 | assert_eq!(graph.adjacent_nodes(&"A".to_string()), Some(&a_adj)); 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /src/weighted_graph.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | use std::collections::HashMap; 3 | use std::hash::Hash; 4 | use std::borrow::Borrow; 5 | 6 | pub trait GraphKey : Clone + Hash + Eq + Debug {} 7 | impl GraphKey for String {} 8 | impl GraphKey for &'static str {} 9 | 10 | #[derive(Debug)] 11 | pub struct Graph { 12 | nodes: HashMap>, 13 | edges: HashMap>> 14 | } 15 | 16 | #[derive(PartialEq, Debug)] 17 | pub struct Node { 18 | pub id: T, 19 | pub x: f64, 20 | pub y: f64, 21 | pub contraction_order: Option 22 | } 23 | 24 | #[derive(PartialEq, Debug)] 25 | pub struct Edge { 26 | pub id: T, 27 | pub from_id: T, 28 | pub to_id: T, 29 | pub weight: i64, 30 | pub arc_flag: bool, 31 | pub shortcut: Option 32 | } 33 | 34 | impl Graph { 35 | pub fn new() -> Self { 36 | Graph { 37 | edges: HashMap::new(), 38 | nodes: HashMap::new() 39 | } 40 | } 41 | 42 | pub fn add_node(&mut self, id: T, x: f64, y: f64) { 43 | let node = Node { id: id.clone(), 44 | x: x, 45 | y: y, 46 | contraction_order: None 47 | }; 48 | self.nodes.insert(id, node); 49 | } 50 | 51 | pub fn get_node(&self, id: &S) -> Option<&Node> 52 | where T: Borrow, 53 | S: Hash + Eq { 54 | self.nodes.get(id) 55 | } 56 | 57 | pub fn get_mut_node(&mut self, id: &S) -> Option<&mut Node> 58 | where T: Borrow, 59 | S: Hash + Eq { 60 | self.nodes.get_mut(id) 61 | } 62 | 63 | pub fn all_nodes(&self) -> Vec<&Node> { 64 | self.nodes.values().collect() 65 | } 66 | 67 | pub fn add_edge(&mut self, id: T, from_id: T, to_id: T, weight: i64) 68 | where T: GraphKey { 69 | let edge = self.build_edge(&id, &from_id, &to_id, weight); 70 | match edge { 71 | Some(e) => { 72 | let mut edges = self.edges.entry(from_id).or_insert(Vec::new()); 73 | edges.push(e); 74 | } 75 | None => {} 76 | } 77 | } 78 | 79 | fn build_edge(&self, id: &T, from_id: &T, to_id: &T, weight: i64) -> Option> 80 | where T: GraphKey { 81 | let from = self.get_node(&from_id); 82 | let to = self.get_node(&to_id); 83 | if from.is_some() && to.is_some() { 84 | Some(Edge { id: id.clone(), 85 | from_id: from_id.clone(), 86 | to_id: to_id.clone(), 87 | weight: weight, 88 | arc_flag: false, 89 | shortcut: None 90 | }) 91 | } else { 92 | None 93 | } 94 | } 95 | 96 | pub fn get_edges<'a, S>(&'a self, node_id: &S) -> &[Edge] 97 | where T: Borrow, 98 | S: Hash + Eq { 99 | self.edges.get(node_id).map(Vec::borrow).unwrap_or(&[]) 100 | } 101 | 102 | pub fn get_mut_edge(&mut self, from_node_id: &T, to_node_id: &T) -> Option<&mut Edge> 103 | where T: GraphKey { 104 | self.edges.get_mut(from_node_id).and_then(|edges| 105 | edges.iter_mut().find(|edge| edge.to_id == *to_node_id) 106 | ) 107 | } 108 | } 109 | 110 | #[cfg(test)] 111 | mod test { 112 | use std::collections::HashSet; 113 | use super::{ Graph, Edge }; 114 | use test_helpers::floats_nearly_eq; 115 | 116 | #[test] 117 | fn build_graph() { 118 | let mut graph = Graph::new(); 119 | 120 | graph.add_node("1", 1.0, 1.0); 121 | graph.add_node("2", 3.0, 5.0); 122 | 123 | let node_1 = graph.get_node(&"1"); 124 | assert!(node_1.is_some()); 125 | match node_1 { 126 | Some(node) => { 127 | assert_eq!(node.id, "1"); 128 | assert!(floats_nearly_eq(node.x, 1.0)); 129 | assert!(floats_nearly_eq(node.y, 1.0)); 130 | } 131 | None => {} 132 | } 133 | 134 | let node_2 = graph.get_node(&"2"); 135 | assert!(node_2.is_some()); 136 | match node_2 { 137 | Some(node) => { 138 | assert_eq!(node.id, "2"); 139 | assert!(floats_nearly_eq(node.x, 3.0)); 140 | assert!(floats_nearly_eq(node.y, 5.0)); 141 | } 142 | None => {} 143 | } 144 | 145 | let still_present = graph.get_node(&"1"); 146 | assert!(still_present.is_some()); 147 | } 148 | 149 | #[test] 150 | fn adding_edges() { 151 | let mut graph = Graph::new(); 152 | 153 | graph.add_node("n1", 0.0, 12.0); 154 | graph.add_node("n2", 5.0, 0.0); 155 | graph.add_node("n3", 2.0, 4.0); 156 | 157 | graph.add_edge("e1", "n2", "n1", 13); 158 | graph.add_edge("e2", "n3", "n2", 5); 159 | graph.add_edge("e3", "n2", "n3", 5); 160 | 161 | let edges_n1 = graph.get_edges(&"n1"); 162 | let edges_n2 = graph.get_edges(&"n2"); 163 | let edges_n3 = graph.get_edges(&"n3"); 164 | 165 | assert_eq!(edges_n1, &[]); 166 | assert_eq!(edges_n2, &[Edge { id: "e1", 167 | from_id: "n2", 168 | to_id: "n1", 169 | weight: 13, 170 | arc_flag: false, 171 | shortcut: None 172 | }, 173 | Edge { id: "e3", 174 | from_id: "n2", 175 | to_id: "n3", 176 | weight: 5, 177 | arc_flag: false, 178 | shortcut: None 179 | }]); 180 | assert_eq!(edges_n3, &[Edge { id: "e2", 181 | from_id: "n3", 182 | to_id: "n2", 183 | weight: 5, 184 | arc_flag: false, 185 | shortcut: None 186 | }]); 187 | } 188 | 189 | #[test] 190 | fn returns_all_nodes() { 191 | let mut graph = Graph::new(); 192 | 193 | graph.add_node("n1", 0.0, 12.0); 194 | graph.add_node("n2", 5.0, 0.0); 195 | graph.add_node("n3", 2.0, 4.0); 196 | 197 | let mut expected_node_ids = HashSet::new(); 198 | expected_node_ids.insert("n1"); 199 | expected_node_ids.insert("n2"); 200 | expected_node_ids.insert("n3"); 201 | 202 | let nodes = graph.all_nodes() 203 | .iter() 204 | .map(|n| n.id) 205 | .collect::>(); 206 | 207 | assert_eq!(nodes, expected_node_ids); 208 | } 209 | 210 | #[test] 211 | fn edit_edge() { 212 | let mut graph = Graph::new(); 213 | 214 | graph.add_node("n1", 0.0, 12.0); 215 | graph.add_node("n2", 5.0, 0.0); 216 | graph.add_node("n3", 2.0, 4.0); 217 | 218 | graph.add_edge("e1", "n2", "n1", 13); 219 | graph.add_edge("e2", "n3", "n2", 5); 220 | graph.add_edge("e3", "n2", "n3", 5); 221 | 222 | if let Some(mut edge) = graph.get_mut_edge(&"n2", &"n3") { 223 | edge.arc_flag = true; 224 | } 225 | 226 | for edge in graph.get_edges(&"n2") { 227 | if edge.to_id == "n3" { 228 | assert!(edge.arc_flag); 229 | } else { 230 | assert!(!edge.arc_flag); 231 | } 232 | } 233 | } 234 | 235 | #[test] 236 | fn edit_node() { 237 | let mut graph = Graph::new(); 238 | graph.add_node("n", 0.0, 0.0); 239 | 240 | assert_eq!(graph.get_node(&"n").and_then(|n| n.contraction_order), None); 241 | 242 | graph.get_mut_node(&"n").map(|n| n.contraction_order = Some(1)); 243 | 244 | assert_eq!(graph.get_node(&"n").and_then(|n| n.contraction_order), Some(1)); 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /src/pathfinder.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{ BinaryHeap, HashMap }; 2 | use std::iter::Iterator; 3 | use std::cmp::Ordering; 4 | 5 | use weighted_graph::{ GraphKey, Graph, Node, Edge }; 6 | 7 | pub type HeuristicFn<'a, T> = Box>, Option<&Node>) -> i64 + 'a>; 8 | pub type EdgeIterator<'a, T> = Box> + 'a>; 9 | pub type EdgeIteratorFn<'a, T> = Box, &T) -> 10 | EdgeIterator<'a, T>>; 11 | pub type TerminatorFn<'a, T> = Box, &HashMap>) -> bool>; 12 | 13 | pub struct Pathfinder<'a, T: GraphKey + 'a> { 14 | h: HeuristicFn<'a, T>, 15 | eit: EdgeIteratorFn<'a, T>, 16 | t: TerminatorFn<'a, T> 17 | } 18 | 19 | impl<'a, T: GraphKey> Pathfinder<'a, T> { 20 | pub fn new(heuristic: HeuristicFn<'a, T>, 21 | edge_iterator: EdgeIteratorFn<'a, T>, 22 | terminator: TerminatorFn<'a, T>) -> Self { 23 | Pathfinder { h: heuristic, 24 | eit: edge_iterator, 25 | t: terminator 26 | } 27 | } 28 | 29 | fn heuristic(&self, from: Option<&Node>, to: Option<&Node>) -> i64 { 30 | (self.h)(from, to) 31 | } 32 | 33 | fn edges(&self, graph: &'a Graph, node_id: &T) -> EdgeIterator<'a, T> { 34 | (self.eit)(graph, node_id) 35 | } 36 | 37 | fn early_termination(&self, current: &CurrentBest, results: &HashMap>) -> bool { 38 | (self.t)(current, results) 39 | } 40 | 41 | pub fn shortest_path(&self, 42 | graph: &'a Graph, 43 | source: &T, 44 | destination: Option<&T> 45 | ) -> (i64, HashMap>) { 46 | 47 | let mut min_heap = BinaryHeap::new(); 48 | let mut results = HashMap::new(); 49 | 50 | let initial = CurrentBest { id: source.clone(), 51 | cost: self.heuristic(graph.get_node(source), 52 | destination.and_then(|id| 53 | graph.get_node(id) 54 | ) 55 | ), 56 | predecessor: None 57 | }; 58 | results.insert(source.clone(), initial.clone()); 59 | min_heap.push(initial.clone()); 60 | self.compute_shortest_path(graph, results, min_heap, destination) 61 | } 62 | 63 | pub fn set_shortest_path(&self, 64 | graph: &'a Graph, 65 | sources: &Vec<&T>, 66 | destination: Option<&T>, 67 | ) -> (i64, HashMap>) { 68 | let mut min_heap = BinaryHeap::new(); 69 | let mut results = HashMap::new(); 70 | 71 | for &source in sources { 72 | let initial = CurrentBest { id: source.clone(), 73 | cost: self.heuristic(graph.get_node(source), 74 | destination.and_then(|id| 75 | graph.get_node(id) 76 | ) 77 | ), 78 | predecessor: None 79 | }; 80 | results.insert(source.clone(), initial.clone()); 81 | min_heap.push(initial.clone()); 82 | } 83 | 84 | self.compute_shortest_path(graph, results, min_heap, destination) 85 | } 86 | 87 | pub fn compute_shortest_path(&self, 88 | graph: &'a Graph, 89 | mut results: HashMap>, 90 | mut min_heap: BinaryHeap>, 91 | destination: Option<&T> 92 | ) -> (i64, HashMap>) { 93 | 94 | while let Some(current) = min_heap.pop() { 95 | if let Some(target) = destination { 96 | if current.id == *target { 97 | return (current.cost, results) 98 | } 99 | } 100 | if self.early_termination(¤t, &results) { 101 | return (current.cost, results) 102 | } 103 | 104 | for edge in self.edges(graph, ¤t.id) { 105 | if let Some(node) = graph.get_node(&edge.to_id) { 106 | let node_cost = results.get(&node.id) 107 | .map_or(i64::max_value(), |node| node.cost); 108 | if current.cost + edge.weight < node_cost { 109 | let cost = current.cost + 110 | edge.weight + 111 | self.heuristic(Some(&node), 112 | destination.and_then(|id| graph.get_node(id)) 113 | ); 114 | let hnode = CurrentBest { id: node.id.clone(), 115 | cost: cost, 116 | predecessor: Some(current.id.clone()) 117 | }; 118 | min_heap.push(hnode.clone()); 119 | results.insert(node.id.clone(), hnode.clone()); 120 | } 121 | } 122 | } 123 | } 124 | (0, results) 125 | } 126 | } 127 | 128 | #[derive(Clone, Eq, PartialEq, Debug)] 129 | pub struct CurrentBest { 130 | pub cost: i64, 131 | pub id: T, 132 | pub predecessor: Option 133 | } 134 | 135 | impl Ord for CurrentBest 136 | where T: GraphKey { 137 | // flip order so min-heap instead of max-heap 138 | fn cmp(&self, other: &CurrentBest) -> Ordering { 139 | other.cost.cmp(&self.cost) 140 | } 141 | } 142 | 143 | impl PartialOrd for CurrentBest 144 | where T: GraphKey { 145 | fn partial_cmp(&self, other: &CurrentBest) -> Option { 146 | Some(self.cmp(other)) 147 | } 148 | } 149 | 150 | #[cfg(test)] 151 | mod test { 152 | use std::collections::HashMap; 153 | use std::iter::Iterator; 154 | use weighted_graph::{ GraphKey, Graph, Node }; 155 | use super::{ Pathfinder, CurrentBest, EdgeIterator }; 156 | 157 | fn build_graph() -> Graph<&'static str> { 158 | let mut graph = Graph::new(); 159 | graph.add_node("1", 1.0, 1.0); 160 | graph.add_node("2", 2.0, 4.0); 161 | graph.add_node("3", 3.0, 2.0); 162 | graph.add_node("4", 4.0, 1.0); 163 | graph.add_node("5", 5.0, 3.0); 164 | graph.add_node("6", 5.0, 5.0); 165 | 166 | let edges = vec![("a", "1", "2", 5), 167 | ("b", "2", "6", 2), 168 | ("c", "1", "3", 3), 169 | ("d", "3", "5", 3), 170 | ("e", "3", "4", 2), 171 | ("f", "4", "5", 3), 172 | ("g", "5", "6", 4)]; 173 | 174 | let mut iter = edges.into_iter(); 175 | 176 | while let Some((edge_id, node_id_1, node_id_2, cost)) = iter.next() { 177 | graph.add_edge(edge_id.clone(), node_id_1.clone(), node_id_2.clone(), cost); 178 | graph.add_edge(edge_id.clone(), node_id_2.clone(), node_id_1.clone(), cost); 179 | } 180 | 181 | graph 182 | } 183 | 184 | #[test] 185 | fn orderable_node_ref() { 186 | let less = CurrentBest { id: "less", cost: 1, predecessor: None }; 187 | let more = CurrentBest { id: "more", cost: 5, predecessor: None }; 188 | 189 | assert!(less > more); 190 | assert!(more < less); 191 | } 192 | 193 | fn find_shortest_path<'a, T>(graph: &'a Graph, 194 | source: &T, 195 | destination: Option<&T> 196 | ) -> (i64, HashMap>) 197 | where T: GraphKey { 198 | let identity = |_: Option<&Node>, _ :Option<&Node>| 0; 199 | let edge_iterator = |g: &'a Graph, node_id: &T| -> EdgeIterator<'a, T> { 200 | Box::new(g.get_edges(node_id).iter().filter(|_| true)) 201 | }; 202 | let terminator = |_: &CurrentBest, _: &HashMap>| false; 203 | let pathfinder = Pathfinder::new(Box::new(identity), 204 | Box::new(edge_iterator), 205 | Box::new(terminator) 206 | ); 207 | pathfinder.shortest_path(graph, source, destination) 208 | } 209 | 210 | #[test] 211 | fn reduction_to_dijkstra() { 212 | let graph: Graph<&str> = build_graph(); 213 | 214 | let (cost, _): (i64, HashMap<&str, CurrentBest<&str>>) = find_shortest_path(&graph, &"1", Some(&"6")); 215 | assert_eq!(cost, 7); 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /src/transit_nodes.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{ HashSet, HashMap }; 2 | 3 | use weighted_graph::{ Graph, GraphKey }; 4 | use pathfinder::CurrentBest; 5 | use contraction::preprocess_contraction; 6 | use arc_flags::shortest_path as arc_flags_shortest_path; 7 | use dijkstra::shortest_path as dijkstra_shortest_path; 8 | use contraction::shortest_path as contraction_shortest_path; 9 | 10 | pub fn shortest_path(source_distances: &HashMap, 11 | destination_distances: &HashMap, 12 | inter_transit_node_distances: &HashMap<(T, T), i64> 13 | ) -> Option<(i64, (T, T))> 14 | where T: GraphKey { 15 | inter_transit_node_distances.iter() 16 | .filter_map(|(&(ref fm, ref to), &inter_cost)| 17 | path_cost_through_transits(fm, 18 | to, 19 | inter_cost, 20 | source_distances, 21 | destination_distances).map(|cost| 22 | (cost, (fm, to))) 23 | ) 24 | .min_by_key(|&(cost, _)| cost) 25 | .map(|(cost, (source_transit, dest_transit))| 26 | (cost, (source_transit.clone(), dest_transit.clone())) 27 | ) 28 | } 29 | 30 | fn path_cost_through_transits(from: &T, 31 | to: &T, 32 | inter_cost: i64, 33 | source_distances: &HashMap, 34 | destination_distances: &HashMap) -> Option 35 | where T: GraphKey { 36 | source_distances.get(from) 37 | .and_then(|sd| destination_distances.get(to).map(|dd| dd + sd)) 38 | .map(|dist| dist + inter_cost) 39 | } 40 | 41 | pub fn transit_nodes_contraction(graph: &mut Graph) -> HashSet 42 | where T: GraphKey { 43 | let number_transit_nodes = (graph.all_nodes().len() as f64).sqrt().floor() as usize; 44 | preprocess_contraction(graph); 45 | 46 | let mut nodes = graph.all_nodes() 47 | .iter() 48 | .map(|node| (node.contraction_order.unwrap().clone(), node.id.clone())) 49 | .collect::>(); 50 | 51 | nodes.as_mut_slice() 52 | .sort_by(|a, b| b.0.cmp(&a.0)); 53 | 54 | nodes.iter() 55 | .map(|node| node.1.clone()) 56 | .take(number_transit_nodes) 57 | .collect() 58 | } 59 | 60 | pub fn neighboring_transit_nodes(graph: &Graph, 61 | transit_nodes: &HashSet, 62 | origin: &T) 63 | -> HashMap 64 | where T: GraphKey { 65 | let (_, results) = arc_flags_shortest_path(graph, origin, None); 66 | 67 | results.iter() 68 | .filter_map(|(node_id, _)| 69 | first_transit_node(node_id, &results, transit_nodes)) 70 | .filter_map(|transit_node| 71 | contraction_shortest_path(graph, 72 | origin, 73 | &transit_node 74 | ).map(|(cost, _)| (transit_node, cost))) 75 | .collect() 76 | } 77 | 78 | fn first_transit_node(node_id: &T, 79 | results: &HashMap>, 80 | transit_nodes: &HashSet) 81 | -> Option 82 | where T: GraphKey { 83 | if transit_nodes.contains(node_id) { 84 | first_transit_node_helper(node_id.clone(), 85 | Some(node_id.clone()), 86 | results, 87 | transit_nodes).map(|id| id.clone()) 88 | } else { 89 | first_transit_node_helper(node_id.clone(), 90 | None, 91 | results, 92 | transit_nodes).map(|id| id.clone()) 93 | } 94 | } 95 | 96 | fn first_transit_node_helper(node_id: T, 97 | current_transit_node: Option, 98 | results: &HashMap>, 99 | transit_nodes: &HashSet) 100 | -> Option 101 | where T: GraphKey { 102 | match results.get(&node_id).and_then(|result| result.predecessor.clone()) { 103 | Some(predecessor_id) => { 104 | if transit_nodes.contains(&predecessor_id) { 105 | first_transit_node_helper(predecessor_id.clone(), 106 | Some(predecessor_id), 107 | results, 108 | transit_nodes) 109 | } else { 110 | first_transit_node_helper(predecessor_id, 111 | current_transit_node, 112 | results, 113 | transit_nodes) 114 | } 115 | } 116 | None => current_transit_node 117 | } 118 | } 119 | 120 | pub fn pairwise_transit_node_distances(graph: &Graph, 121 | transit_nodes: &HashSet 122 | ) -> HashMap<(T, T), i64> 123 | where T: GraphKey { 124 | let mut pairs = vec![]; 125 | for from in transit_nodes { 126 | for to in transit_nodes { 127 | pairs.push((from.clone(), to.clone())); 128 | } 129 | } 130 | 131 | pairs.iter().map(|&(ref from, ref to)| 132 | match dijkstra_shortest_path(graph, from, Some(to)) { 133 | (cost, _) => ((from.clone(), to.clone()), cost) 134 | } 135 | ).collect() 136 | } 137 | 138 | #[cfg(test)] 139 | mod test { 140 | use std::collections::HashSet; 141 | 142 | use weighted_graph::{ Graph }; 143 | use dijkstra::shortest_path as dijkstra; 144 | use super::{ transit_nodes_contraction, 145 | neighboring_transit_nodes, 146 | pairwise_transit_node_distances, 147 | shortest_path 148 | }; 149 | 150 | fn build_full_graph() -> (Vec<(&'static str, f64, f64)>, // nodes 151 | Vec<(&'static str, &'static str, i64)>, // edges 152 | Graph<&'static str>) { 153 | let mut graph = Graph::new(); 154 | let nodes = vec![("a", 0.0, 3.0), 155 | ("b", 0.0, 1.0), 156 | ("c", 0.0, 0.0), 157 | ("d", 1.0, 3.0), 158 | ("e", 1.0, 2.0), 159 | ("f", 1.0, 0.0), 160 | ("g", 2.0, 3.0), 161 | ("h", 2.0, 1.0), 162 | ("i", 2.0, 0.0)]; 163 | for &(id, x, y) in &nodes { 164 | graph.add_node(id, x, y); 165 | } 166 | 167 | let edges = vec![("a", "b", 3), 168 | ("a", "d", 2), 169 | ("b", "c", 1), 170 | ("b", "e", 1), 171 | ("c", "f", 2), 172 | ("d", "e", 1), 173 | ("d", "g", 2), 174 | ("e", "f", 3), 175 | ("e", "h", 1), 176 | ("f", "i", 2), 177 | ("g", "h", 4), 178 | ("h", "i", 2), 179 | ]; 180 | for &(n1, n2, w) in &edges { 181 | graph.add_edge(n1, n1, n2, w); 182 | graph.add_edge(n2, n2, n1, w); 183 | graph.get_mut_edge(&n1, &n2).map(|edge| edge.arc_flag = true); 184 | graph.get_mut_edge(&n2, &n1).map(|edge| edge.arc_flag = true); 185 | } 186 | 187 | (nodes, edges, graph) 188 | } 189 | 190 | #[test] 191 | fn compute_set_of_transit_nodes() { 192 | let (_, _, mut graph) = build_full_graph(); 193 | 194 | let transit_nodes = transit_nodes_contraction(&mut graph); 195 | 196 | let mut expected = HashSet::new(); 197 | expected.insert(7); 198 | expected.insert(8); 199 | expected.insert(9); 200 | 201 | assert_eq!(transit_nodes.len(), 3); 202 | assert_eq!(transit_nodes.iter() 203 | .filter_map(|tn| 204 | graph.get_node(tn) 205 | .map(|node| node.contraction_order.unwrap())) 206 | .collect::>(), 207 | expected); 208 | } 209 | 210 | #[test] 211 | fn transit_node_distances_from_node() { 212 | let (_, _, mut graph) = build_full_graph(); 213 | let transit_nodes = transit_nodes_contraction(&mut graph); 214 | 215 | let first_contracted = graph.all_nodes() 216 | .iter() 217 | .min_by_key(|node| node.contraction_order) 218 | .map(|node| node.id) 219 | .unwrap(); 220 | 221 | let transit_node_distances = neighboring_transit_nodes(&graph, 222 | &transit_nodes, 223 | &first_contracted); 224 | 225 | assert!(transit_node_distances.len() > 0); 226 | assert!(transit_node_distances.keys().all(|node_id| transit_nodes.contains(node_id))); 227 | println!("GRAPH {:?}", graph); 228 | for (node_id, _) in &transit_node_distances { 229 | let (cost, _) = dijkstra(&graph, &first_contracted, Some(&node_id)); 230 | println!("FROM {:?} TO {:?}", first_contracted, node_id); 231 | assert_eq!(*transit_node_distances.get(node_id).unwrap(), cost); 232 | } 233 | } 234 | 235 | #[test] 236 | fn inter_transit_node_distances() { 237 | let (_, _, mut graph) = build_full_graph(); 238 | let transit_nodes = transit_nodes_contraction(&mut graph); 239 | 240 | let transit_node_distances = pairwise_transit_node_distances(&graph, 241 | &transit_nodes); 242 | 243 | for &from in &transit_nodes { 244 | for &to in &transit_nodes { 245 | let (cost, _) = dijkstra(&graph, &from, Some(&to)); 246 | assert_eq!(cost, *transit_node_distances.get(&(from, to)).unwrap()); 247 | } 248 | } 249 | } 250 | 251 | #[test] 252 | fn find_shortest_path() { 253 | let (_, _, mut graph) = build_full_graph(); 254 | let source = "c"; 255 | let destination = "g"; 256 | 257 | let transit_nodes = transit_nodes_contraction(&mut graph); 258 | let source_distances = neighboring_transit_nodes(&graph, 259 | &transit_nodes, 260 | &source); 261 | let destination_distances = neighboring_transit_nodes(&graph, 262 | &transit_nodes, 263 | &destination); 264 | let inter_transit_node_distances = pairwise_transit_node_distances(&graph, 265 | &transit_nodes); 266 | match shortest_path(&source_distances, 267 | &destination_distances, 268 | &inter_transit_node_distances) { 269 | Some((cost, (fm_transit, to_transit))) => { 270 | assert_eq!(cost, 5); 271 | assert!(transit_nodes.contains(&fm_transit)); 272 | assert!(transit_nodes.contains(&to_transit)); 273 | } 274 | None => assert!(false) 275 | } 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /src/transfer_patterns.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{ HashMap, HashSet }; 2 | 3 | use pathfinder::CurrentBest; 4 | use weighted_graph::{ Graph }; 5 | use graph_from_gtfs::{ GtfsId, 6 | StopId, 7 | NodeType 8 | }; 9 | use set_dijkstra::shortest_path as set_dijkstra; 10 | 11 | pub fn transfer_patterns_for_all_stations(graph: &Graph 12 | ) -> HashMap<(StopId, StopId), HashSet>> { 13 | let partition = partition_station_nodes(&graph); 14 | 15 | let pairs = station_pairs(partition.keys().collect::>()); 16 | pairs.iter().fold(HashMap::new(), |mut transfers, station_pair| { 17 | let dijkstra_results = full_dijkstra_from_station(&graph, 18 | &partition, 19 | &station_pair.0); 20 | let partitioned_dijkstra = partition_dijkstra_results(&dijkstra_results); 21 | if let Some(destination_node) = partitioned_dijkstra.get(&station_pair.1) { 22 | let smoothed = smooth_results(destination_node); 23 | transfers.insert((station_pair.0.clone(), station_pair.1.clone()), 24 | transfer_patterns_for_station_pair(&dijkstra_results, &smoothed)); 25 | } 26 | transfers 27 | }) 28 | } 29 | 30 | fn station_pairs<'a>(stations: Vec<&&'a StopId>) -> Vec<(&'a StopId, &'a StopId)> { 31 | let mut pairs = vec![]; 32 | for &s1 in &stations { 33 | for &s2 in &stations { 34 | pairs.push((*s1, *s2)); 35 | } 36 | } 37 | pairs 38 | } 39 | 40 | fn partition_station_nodes<'a>(graph: &'a Graph) -> HashMap<&'a StopId, HashSet<&'a GtfsId>> { 41 | graph.all_nodes().iter().fold(HashMap::new(), |mut partition, node| { 42 | partition.entry(&node.id.stop_id).or_insert(HashSet::new()).insert(&node.id); 43 | partition 44 | }) 45 | } 46 | 47 | fn full_dijkstra_from_station<'a>(graph: &'a Graph, 48 | partition: &'a HashMap<&'a StopId, HashSet<&'a GtfsId>>, 49 | station: &StopId 50 | ) -> HashMap> { 51 | let sources = partition.get(station).unwrap().into_iter().map(|&e| e).collect::>(); 52 | set_dijkstra(graph, &sources, None).1 53 | } 54 | 55 | fn partition_dijkstra_results<'a>(results: &'a HashMap>) 56 | -> HashMap<&'a StopId, Vec<&'a CurrentBest>> { 57 | let mut partition = results.iter() 58 | .filter(|&(node_id, _)| node_id.node_type == NodeType::Arrival) 59 | .fold(HashMap::new(), |mut p, (node_id, node_result)| { 60 | p.entry(&node_id.stop_id).or_insert(vec![]).push(node_result); 61 | p 62 | }); 63 | for mut nodes in partition.values_mut() { 64 | nodes.sort_by(|&a, &b| a.id.time.cmp(&b.id.time)); 65 | } 66 | partition 67 | } 68 | 69 | fn smooth_results(results: &Vec<&CurrentBest>) -> Vec> { 70 | results.windows(2).fold(vec![results[0].clone()], |mut smoothed, nodes| { 71 | let prev = nodes[0]; 72 | let curr = nodes[1]; 73 | let wait_cost = prev.cost + (curr.id.time - prev.id.time); 74 | if curr.cost > wait_cost { 75 | smoothed.push(CurrentBest { id: curr.id.clone(), 76 | cost: wait_cost, 77 | predecessor: Some(prev.id.clone()) 78 | }); 79 | } else { 80 | smoothed.push(curr.clone()); 81 | } 82 | smoothed 83 | }) 84 | } 85 | 86 | fn transfer_patterns_for_station_pair(dijkstra_results: &HashMap>, 87 | smoothed: &Vec> 88 | ) 89 | -> HashSet> { 90 | smoothed.iter().fold(HashSet::new(), |mut patterns, node| { 91 | patterns.insert(collect_transfer_points(dijkstra_results, node)); 92 | patterns 93 | }) 94 | } 95 | 96 | fn collect_transfer_points(dijkstra_results: &HashMap>, 97 | final_node: &CurrentBest, 98 | ) 99 | -> Vec { 100 | let path = backtrack(dijkstra_results, final_node); 101 | let mut transfers = path.iter().fold(vec![], |mut points, next_node| { 102 | if points.last().is_none() || next_node.node_type.is_transfer() { 103 | points.push(next_node); 104 | } 105 | points 106 | }); 107 | if transfers.last().map(|node| node.stop_id != final_node.id.stop_id).unwrap_or(true) { 108 | transfers.push(&final_node.id); 109 | } 110 | 111 | transfers.iter().map(|node| node.stop_id.clone()).collect() 112 | 113 | } 114 | 115 | fn backtrack(dijkstra_results: &HashMap>, 116 | current: &CurrentBest 117 | ) -> Vec { 118 | match current.predecessor { 119 | Some(ref predecessor) => { 120 | let mut path = dijkstra_results.get(&predecessor) 121 | .map(|cb| backtrack(dijkstra_results, cb)) 122 | .unwrap_or(vec![]); 123 | path.push(current.id.clone()); 124 | path 125 | } 126 | None => { 127 | vec![current.id.clone()] 128 | } 129 | } 130 | } 131 | 132 | #[cfg(test)] 133 | mod test { 134 | use std::collections::{ HashSet, HashMap }; 135 | use test_helpers::to_node_id; 136 | use weighted_graph::Graph; 137 | use pathfinder::CurrentBest; 138 | use graph_from_gtfs::{ 139 | GtfsId, 140 | build_graph_from_gtfs, 141 | NodeType 142 | }; 143 | use super::{ 144 | partition_station_nodes, 145 | full_dijkstra_from_station, 146 | partition_dijkstra_results, 147 | smooth_results, 148 | transfer_patterns_for_station_pair, 149 | transfer_patterns_for_all_stations 150 | }; 151 | 152 | fn graph() -> Graph { 153 | build_graph_from_gtfs("data/gtfs_example/", "wednesday") 154 | } 155 | 156 | #[test] 157 | fn assoc_nodes_with_stations() { 158 | let graph = graph(); 159 | 160 | let partition = partition_station_nodes(&graph); 161 | 162 | let station_a = vec![("A", "06:00:00", NodeType::Arrival, Some("r1")), 163 | ("A", "06:00:00", NodeType::Departure, Some("r1")), 164 | ("A", "06:05:00", NodeType::Transfer, None), 165 | ("A", "06:15:00", NodeType::Arrival, Some("g1")), 166 | ("A", "06:15:00", NodeType::Departure, Some("g1")), 167 | ("A", "06:20:00", NodeType::Transfer, None), 168 | ("A", "06:45:00", NodeType::Arrival, Some("g2")), 169 | ("A", "06:45:00", NodeType::Departure, Some("g2")), 170 | ("A", "06:50:00", NodeType::Transfer, None), 171 | ("A", "07:00:00", NodeType::Arrival, Some("r2")), 172 | ("A", "07:00:00", NodeType::Departure, Some("r2")), 173 | ("A", "07:05:00", NodeType::Transfer, None), 174 | ("A", "07:15:00", NodeType::Arrival, Some("g3")), 175 | ("A", "07:15:00", NodeType::Departure, Some("g3")), 176 | ("A", "07:20:00", NodeType::Transfer, None), 177 | ("A", "07:45:00", NodeType::Arrival, Some("g4")), 178 | ("A", "07:45:00", NodeType::Departure, Some("g4")), 179 | ("A", "07:50:00", NodeType::Transfer, None), 180 | ("A", "08:00:00", NodeType::Arrival, Some("r3")), 181 | ("A", "08:00:00", NodeType::Departure, Some("r3")), 182 | ("A", "08:05:00", NodeType::Transfer, None), 183 | ("A", "08:15:00", NodeType::Arrival, Some("g5")), 184 | ("A", "08:15:00", NodeType::Departure, Some("g5")), 185 | ("A", "08:20:00", NodeType::Transfer, None)]; 186 | let station_b = vec![("B", "06:25:00", NodeType::Arrival, Some("r1")), 187 | ("B", "06:25:00", NodeType::Departure, Some("r1")), 188 | ("B", "06:30:00", NodeType::Transfer, None), 189 | ("B", "07:25:00", NodeType::Arrival, Some("r2")), 190 | ("B", "07:25:00", NodeType::Departure, Some("r2")), 191 | ("B", "07:30:00", NodeType::Transfer, None), 192 | ("B", "08:25:00", NodeType::Arrival, Some("r3")), 193 | ("B", "08:25:00", NodeType::Departure, Some("r3")), 194 | ("B", "08:30:00", NodeType::Transfer, None)]; 195 | let station_c = vec![("C", "06:45:00", NodeType::Arrival, Some("g1")), 196 | ("C", "06:45:00", NodeType::Departure, Some("g1")), 197 | ("C", "06:50:00", NodeType::Transfer, None), 198 | ("C", "07:15:00", NodeType::Arrival, Some("g2")), 199 | ("C", "07:15:00", NodeType::Departure, Some("g2")), 200 | ("C", "07:20:00", NodeType::Transfer, None), 201 | ("C", "07:45:00", NodeType::Arrival, Some("g3")), 202 | ("C", "07:45:00", NodeType::Departure, Some("g3")), 203 | ("C", "07:50:00", NodeType::Transfer, None), 204 | ("C", "08:15:00", NodeType::Arrival, Some("g4")), 205 | ("C", "08:15:00", NodeType::Departure, Some("g4")), 206 | ("C", "08:20:00", NodeType::Transfer, None), 207 | ("C", "08:45:00", NodeType::Arrival, Some("g5")), 208 | ("C", "08:45:00", NodeType::Departure, Some("g5")), 209 | ("C", "08:50:00", NodeType::Transfer, None)]; 210 | let station_d = vec![("D", "07:00:00", NodeType::Arrival, Some("g1")), 211 | ("D", "07:00:00", NodeType::Departure, Some("g1")), 212 | ("D", "07:05:00", NodeType::Transfer, None), 213 | ("D", "07:30:00", NodeType::Arrival, Some("g2")), 214 | ("D", "07:30:00", NodeType::Departure, Some("g2")), 215 | ("D", "07:35:00", NodeType::Transfer, None), 216 | ("D", "08:00:00", NodeType::Arrival, Some("g3")), 217 | ("D", "08:00:00", NodeType::Departure, Some("g3")), 218 | ("D", "08:05:00", NodeType::Transfer, None), 219 | ("D", "08:30:00", NodeType::Arrival, Some("g4")), 220 | ("D", "08:30:00", NodeType::Departure, Some("g4")), 221 | ("D", "08:35:00", NodeType::Transfer, None), 222 | ("D", "09:00:00", NodeType::Arrival, Some("g5")), 223 | ("D", "09:00:00", NodeType::Departure, Some("g5")), 224 | ("D", "09:05:00", NodeType::Transfer, None)]; 225 | let station_e = vec![("E", "06:50:00", NodeType::Arrival, Some("r1")), 226 | ("E", "06:50:00", NodeType::Departure, Some("r1")), 227 | ("E", "06:55:00", NodeType::Transfer, None), 228 | ("E", "07:30:00", NodeType::Arrival, Some("g1")), 229 | ("E", "07:30:00", NodeType::Departure, Some("g1")), 230 | ("E", "07:35:00", NodeType::Transfer, None), 231 | ("E", "07:50:00", NodeType::Arrival, Some("r2")), 232 | ("E", "07:50:00", NodeType::Departure, Some("r2")), 233 | ("E", "07:55:00", NodeType::Transfer, None), 234 | ("E", "08:00:00", NodeType::Arrival, Some("g2")), 235 | ("E", "08:00:00", NodeType::Departure, Some("g2")), 236 | ("E", "08:05:00", NodeType::Transfer, None), 237 | ("E", "08:30:00", NodeType::Arrival, Some("g3")), 238 | ("E", "08:30:00", NodeType::Departure, Some("g3")), 239 | ("E", "08:35:00", NodeType::Transfer, None), 240 | ("E", "08:50:00", NodeType::Arrival, Some("r3")), 241 | ("E", "08:50:00", NodeType::Departure, Some("r3")), 242 | ("E", "08:55:00", NodeType::Transfer, None), 243 | ("E", "09:00:00", NodeType::Arrival, Some("g4")), 244 | ("E", "09:00:00", NodeType::Departure, Some("g4")), 245 | ("E", "09:05:00", NodeType::Transfer, None), 246 | ("E", "09:30:00", NodeType::Arrival, Some("g5")), 247 | ("E", "09:30:00", NodeType::Departure, Some("g5")), 248 | ("E", "09:35:00", NodeType::Transfer, None)]; 249 | let station_f = vec![("F", "07:40:00", NodeType::Arrival, Some("g1")), 250 | ("F", "07:40:00", NodeType::Departure, Some("g1")), 251 | ("F", "07:45:00", NodeType::Transfer, None), 252 | ("F", "08:10:00", NodeType::Arrival, Some("g2")), 253 | ("F", "08:10:00", NodeType::Departure, Some("g2")), 254 | ("F", "08:15:00", NodeType::Transfer, None), 255 | ("F", "08:40:00", NodeType::Arrival, Some("g3")), 256 | ("F", "08:40:00", NodeType::Departure, Some("g3")), 257 | ("F", "08:45:00", NodeType::Transfer, None), 258 | ("F", "09:10:00", NodeType::Arrival, Some("g4")), 259 | ("F", "09:10:00", NodeType::Departure, Some("g4")), 260 | ("F", "09:15:00", NodeType::Transfer, None), 261 | ("F", "09:40:00", NodeType::Arrival, Some("g5")), 262 | ("F", "09:40:00", NodeType::Departure, Some("g5")), 263 | ("F", "09:45:00", NodeType::Transfer, None)]; 264 | 265 | let station_a_nodes = station_a.into_iter() 266 | .map(|data| to_node_id(data)) 267 | .collect::>(); 268 | assert_eq!(*partition.get(&"A".to_string()).unwrap(), 269 | station_a_nodes.iter().map(|n| n).collect::>()); 270 | 271 | let station_b_nodes = station_b.into_iter() 272 | .map(|data| to_node_id(data)) 273 | .collect::>(); 274 | assert_eq!(*partition.get(&"B".to_string()).unwrap(), 275 | station_b_nodes.iter().map(|n| n).collect::>()); 276 | 277 | let station_c_nodes = station_c.into_iter() 278 | .map(|data| to_node_id(data)) 279 | .collect::>(); 280 | assert_eq!(*partition.get(&"C".to_string()).unwrap(), 281 | station_c_nodes.iter().map(|n| n).collect::>()); 282 | 283 | let station_d_nodes = station_d.into_iter() 284 | .map(|data| to_node_id(data)) 285 | .collect::>(); 286 | assert_eq!(*partition.get(&"D".to_string()).unwrap(), 287 | station_d_nodes.iter().map(|n| n).collect::>()); 288 | 289 | let station_e_nodes = station_e.into_iter() 290 | .map(|data| to_node_id(data)) 291 | .collect::>(); 292 | assert_eq!(*partition.get(&"E".to_string()).unwrap(), 293 | station_e_nodes.iter().map(|n| n).collect::>()); 294 | 295 | let station_f_nodes = station_f.into_iter() 296 | .map(|data| to_node_id(data)) 297 | .collect::>(); 298 | assert_eq!(*partition.get(&"F".to_string()).unwrap(), 299 | station_f_nodes.iter().map(|n| n).collect::>()); 300 | 301 | } 302 | 303 | #[test] 304 | fn find_all_shortest_paths_from_station() { 305 | let graph = graph(); 306 | let partition = partition_station_nodes(&graph); 307 | 308 | let shortest_paths = full_dijkstra_from_station(&graph, &partition, &"A".to_string()); 309 | 310 | // no transfers 311 | let spot_check_1 = to_node_id(("F", "09:40:00", NodeType::Arrival, Some("g5"))); 312 | assert_eq!(shortest_paths.get(&spot_check_1).unwrap().cost, 85 * 60); 313 | 314 | // requires a transfer 315 | let spot_check_2 = to_node_id(("F", "09:10:00", NodeType::Arrival, Some("g4"))); 316 | assert_eq!(shortest_paths.get(&spot_check_2).unwrap().cost, 70 * 60); 317 | } 318 | 319 | #[test] 320 | fn results_partition_by_station_and_filtered_to_arrivals() { 321 | let first_a_arrival = CurrentBest { 322 | id: to_node_id(("A", "09:40:00", NodeType::Arrival, Some("g5"))), 323 | cost: 5, 324 | predecessor: None 325 | }; 326 | let second_a_arrival = CurrentBest { 327 | id: to_node_id(("A", "10:40:00", NodeType::Arrival, Some("g5"))), 328 | cost: 5, 329 | predecessor: None 330 | }; 331 | let first_b_arrival = CurrentBest { 332 | id: to_node_id(("B", "09:40:00", NodeType::Arrival, Some("g5"))), 333 | cost: 5, 334 | predecessor: None 335 | }; 336 | 337 | let result_data = vec![CurrentBest { 338 | id: to_node_id(("A", "10:40:00", NodeType::Arrival, Some("g5"))), 339 | cost: 5, 340 | predecessor: None 341 | }, 342 | CurrentBest { 343 | id: to_node_id(("A", "09:40:00", NodeType::Departure, Some("g5"))), 344 | cost: 5, 345 | predecessor: None 346 | }, 347 | CurrentBest { 348 | id: to_node_id(("A", "09:40:00", NodeType::Transfer, Some("g5"))), 349 | cost: 5, 350 | predecessor: None 351 | }, 352 | CurrentBest { 353 | id: to_node_id(("A", "09:40:00", NodeType::Arrival, Some("g5"))), 354 | cost: 5, 355 | predecessor: None 356 | }, 357 | CurrentBest { 358 | id: to_node_id(("B", "09:40:00", NodeType::Arrival, Some("g5"))), 359 | cost: 5, 360 | predecessor: None 361 | }]; 362 | 363 | let results = &result_data.iter() 364 | .map(|result| (result.id.clone(), result.clone())) 365 | .collect::>>(); 366 | 367 | let partition = partition_dijkstra_results(&results); 368 | 369 | let stop_a = &"A".to_string(); 370 | let stop_b = &"B".to_string(); 371 | let mut expected_partition = HashMap::new(); 372 | expected_partition.insert(stop_a, vec![]); 373 | expected_partition.insert(stop_b, vec![]); 374 | expected_partition.get_mut(&stop_a).map(|mut rs| rs.push(&first_a_arrival)); 375 | expected_partition.get_mut(&stop_a).map(|mut rs| rs.push(&second_a_arrival)); 376 | expected_partition.get_mut(&stop_b).map(|mut rs| rs.push(&first_b_arrival)); 377 | 378 | assert_eq!(partition, expected_partition); 379 | } 380 | 381 | #[test] 382 | fn modify_arrival_times_and_paths() { 383 | let result_1 = CurrentBest { id: to_node_id(("E", "09:40:00", NodeType::Arrival, Some("g5"))), 384 | cost: 3000, 385 | predecessor: Some(to_node_id(("D", "09:30:00", NodeType::Departure, Some("g5")))) 386 | }; 387 | let result_2 = CurrentBest { id: to_node_id(("E", "10:00:00", NodeType::Arrival, Some("g6"))), 388 | cost: 3000, 389 | predecessor: Some(to_node_id(("D", "09:50:00", NodeType::Departure, Some("g6")))) 390 | }; 391 | let result_3 = CurrentBest { id: to_node_id(("E", "10:20:00", NodeType::Arrival, Some("r3"))), 392 | cost: 7000, 393 | predecessor: Some(to_node_id(("C", "10:00:00", NodeType::Departure, Some("r3")))) 394 | }; 395 | 396 | let results = vec![&result_1, &result_2, &result_3]; 397 | 398 | let cleaned = smooth_results(&results); 399 | 400 | let smooth_results = cleaned.iter() 401 | .map(|cb| (cb.cost, cb.predecessor.clone().unwrap())) 402 | .collect::>(); 403 | let expected = vec![(3000, result_1.clone().predecessor.unwrap()), 404 | (3000, result_2.clone().predecessor.unwrap()), 405 | (3000 + 20 * 60, result_2.clone().id)]; 406 | 407 | assert_eq!(smooth_results, expected); 408 | } 409 | 410 | #[test] 411 | fn find_transfer_patterns_for_single_station_pair() { 412 | let origin_station = "A".to_string(); 413 | let destination_station = "F".to_string(); 414 | let graph = graph(); 415 | 416 | let partition = partition_station_nodes(&graph); 417 | let dijkstra_results = full_dijkstra_from_station(&graph, 418 | &partition, 419 | &origin_station); 420 | let partitioned_dijkstra = partition_dijkstra_results(&dijkstra_results); 421 | let smoothed = smooth_results(partitioned_dijkstra.get(&destination_station).unwrap()); 422 | 423 | let transfer_patterns = transfer_patterns_for_station_pair(&dijkstra_results, 424 | &smoothed); 425 | 426 | let mut expected = HashSet::new(); 427 | expected.insert(vec!["A".to_string(), "E".to_string(), "F".to_string()]); 428 | expected.insert(vec!["A".to_string(), "F".to_string()]); 429 | 430 | assert_eq!(transfer_patterns, expected); 431 | } 432 | 433 | #[test] 434 | fn find_all_transfer_patterns() { 435 | let graph = graph(); 436 | let stations = vec!["A", "B", "C", "D", "E", "F"]; 437 | let mut station_pairs = HashSet::new(); 438 | for i in &stations { 439 | for j in &stations { 440 | station_pairs.insert((i.to_string(), j.to_string())); 441 | } 442 | } 443 | 444 | let all_transfer_patterns = transfer_patterns_for_all_stations(&graph); 445 | 446 | for key in &station_pairs { 447 | let partition = partition_station_nodes(&graph); 448 | let dijkstra_results = full_dijkstra_from_station(&graph, 449 | &partition, 450 | &key.0); 451 | let partitioned_dijkstra = partition_dijkstra_results(&dijkstra_results); 452 | if let Some(destination_node) = partitioned_dijkstra.get(&key.1) { 453 | let smoothed = smooth_results(destination_node); 454 | assert_eq!(all_transfer_patterns.get(&key).unwrap(), 455 | &transfer_patterns_for_station_pair(&dijkstra_results, &smoothed)); 456 | } 457 | } 458 | } 459 | } 460 | -------------------------------------------------------------------------------- /src/contraction.rs: -------------------------------------------------------------------------------- 1 | use std::collections::{ BinaryHeap, HashMap }; 2 | use std::cmp::Ordering; 3 | 4 | use weighted_graph::{ GraphKey, Graph, Node, Edge }; 5 | use arc_flags::shortest_path as arc_flags_shortest_path; 6 | use pathfinder::{ CurrentBest, Pathfinder, EdgeIterator }; 7 | 8 | pub fn shortest_path(graph: &Graph, 9 | source: &T, 10 | destination: &T 11 | ) -> Option<(i64, Vec)> 12 | where T: GraphKey { 13 | let (_, from_source) = arc_flags_shortest_path(graph, source, None); 14 | let (_, from_dest) = arc_flags_shortest_path(graph, destination, None); 15 | 16 | match from_source.iter() 17 | .filter_map(|(node_id, source_result)| 18 | from_dest.get(node_id) 19 | .map(|dest_result| 20 | (dest_result.cost + source_result.cost, node_id.clone()) 21 | ) 22 | ).min_by_key(|&(cost, _)| cost) { 23 | Some((cost, joint)) => { 24 | let mut back_path = backtrack_path(graph, &joint, &from_source); 25 | let mut fore_path = backtrack_path(graph, &joint, &from_dest); 26 | back_path.reverse(); 27 | back_path.pop(); 28 | back_path.append(&mut fore_path); 29 | Some((cost, back_path)) 30 | }, 31 | None => None 32 | } 33 | } 34 | 35 | pub fn preprocess_contraction(graph: &mut Graph) 36 | where T: GraphKey { 37 | let node_order = preorder_nodes(graph); 38 | contract_graph(graph, node_order); 39 | set_increasing_arc_flags(graph); 40 | } 41 | 42 | fn contract_graph(graph: &mut Graph, 43 | mut order: BinaryHeap>) 44 | where T: GraphKey { 45 | let mut contraction_order = 0; 46 | 47 | while let Some(next_node) = order.pop() { 48 | let contracted = graph.get_node(&next_node.node_id) 49 | .and_then(|n| n.contraction_order) 50 | .is_some(); 51 | if !contracted { 52 | let edge_difference = contract_node(graph, &next_node.node_id, true); 53 | 54 | if edge_difference <= next_node.edge_difference { 55 | contraction_order += 1; 56 | graph.get_mut_node(&next_node.node_id).map(|n| n.contraction_order = Some(contraction_order)); 57 | contract_node(graph, &next_node.node_id, false); 58 | } else { 59 | order.push(EdgeDifference { node_id: next_node.node_id, 60 | edge_difference: edge_difference 61 | }); 62 | } 63 | } 64 | } 65 | } 66 | 67 | fn set_increasing_arc_flags(graph: &mut Graph) 68 | where T: GraphKey { 69 | let node_ids: Vec = graph.all_nodes() 70 | .iter() 71 | .map(|node| node.id.clone()) 72 | .collect(); 73 | for id in node_ids { 74 | let current_order = graph.get_node(&id).and_then(|n| n.contraction_order).unwrap(); 75 | let connected_node_ids: Vec = graph.get_edges(&id) 76 | .iter() 77 | .map(|e| e.to_id.clone()) 78 | .collect(); 79 | for cid in connected_node_ids { 80 | if graph.get_node(&cid).and_then(|n| n.contraction_order).unwrap() > current_order { 81 | graph.get_mut_edge(&id, &cid).map(|e| e.arc_flag = true); 82 | } 83 | } 84 | } 85 | } 86 | 87 | fn local_shortest_path<'a, T>(graph: &'a Graph, 88 | source: &T, 89 | destination: &T, 90 | max_nodes: usize, 91 | max_cost: i64 92 | ) -> (i64, HashMap>) 93 | where T: GraphKey { 94 | let identity = |_: Option<&Node>, _ :Option<&Node>| 0; 95 | let edge_iterator = |g: &'a Graph, node_id: &T| -> 96 | EdgeIterator<'a, T> { 97 | Box::new(g.get_edges(node_id).iter().filter(|edge| edge.arc_flag)) 98 | }; 99 | let terminator = move |r: &CurrentBest, rs: &HashMap>| { 100 | rs.len() >= max_nodes || r.cost > max_cost 101 | }; 102 | let pathfinder = Pathfinder::new(Box::new(identity), 103 | Box::new(edge_iterator), 104 | Box::new(terminator) 105 | ); 106 | pathfinder.shortest_path(graph, source, Some(destination)) 107 | } 108 | 109 | fn contract_node(graph: &mut Graph, node_id: &T, count_only: bool) -> i64 110 | where T: GraphKey { 111 | let adjacent_nodes = find_adjacent_nodes(graph, node_id); 112 | // assuming the graph is symmetric and directed 113 | // edges = 2 * adjacent nodes 114 | let mut ed: i64 = adjacent_nodes.len() as i64 * 2 * -1; 115 | 116 | for adjacent in &adjacent_nodes { 117 | remove_from_graph(graph, adjacent, node_id); 118 | } 119 | 120 | for from_node in &adjacent_nodes { 121 | for to_node in &adjacent_nodes { 122 | let weight_across = weight_across_node(graph, 123 | from_node, 124 | to_node, 125 | node_id 126 | ); 127 | let (min_weight, _) = local_shortest_path(graph, 128 | from_node, 129 | to_node, 130 | 20, 131 | weight_across); 132 | 133 | if min_weight > weight_across { 134 | ed += 1; 135 | if !count_only { 136 | add_shortcut(graph, from_node, to_node, node_id, weight_across); 137 | } 138 | } 139 | } 140 | } 141 | if count_only { 142 | for adjacent in &adjacent_nodes { 143 | unremove_from_graph(graph, adjacent, node_id); 144 | } 145 | } 146 | ed 147 | } 148 | 149 | fn find_adjacent_nodes(graph: &Graph, node_id: &T) -> Vec 150 | // assuming the graph is symmetric and directed 151 | // adjacent nodes <=> nodes on outgoing edges 152 | where T: GraphKey { 153 | graph.get_edges(node_id) 154 | .iter() 155 | .filter(|edge| edge.arc_flag) 156 | .map(|edge| edge.to_id.clone()) 157 | .collect() 158 | } 159 | 160 | fn remove_from_graph(graph: &mut Graph, adjacent_id: &T, node_id: &T) 161 | where T: GraphKey { 162 | change_arc_flag(graph, adjacent_id, node_id, false); 163 | } 164 | 165 | fn unremove_from_graph(graph: &mut Graph, adjacent_id: &T, node_id: &T) 166 | where T: GraphKey { 167 | change_arc_flag(graph, adjacent_id, node_id, true); 168 | } 169 | 170 | fn change_arc_flag(graph: &mut Graph, adjacent_id: &T, node_id: &T, flag: bool) 171 | where T: GraphKey { 172 | graph.get_mut_edge(node_id, adjacent_id) 173 | .map(|edge| edge.arc_flag = flag); 174 | graph.get_mut_edge(adjacent_id, node_id) 175 | .map(|edge| edge.arc_flag = flag); 176 | } 177 | 178 | fn weight_across_node(graph: &Graph, 179 | from_node: &T, 180 | to_node: &T, 181 | cur_node: &T) -> i64 182 | where T: GraphKey { 183 | edge_weight(graph, from_node, cur_node) + edge_weight(graph, cur_node, to_node) 184 | } 185 | 186 | fn edge_weight(graph: &Graph, from_node: &T, to_node: &T) -> i64 187 | where T: GraphKey { 188 | graph.get_edges(from_node) 189 | .iter() 190 | .find(|edge| edge.to_id == *to_node) 191 | .map(|edge| edge.weight) 192 | .unwrap_or(0) 193 | } 194 | 195 | fn add_shortcut(graph: &mut Graph, 196 | from_node: &T, 197 | to_node: &T, 198 | shortcut: &T, 199 | weight: i64) 200 | where T: GraphKey { 201 | graph.add_edge(from_node.clone(), 202 | from_node.clone(), 203 | to_node.clone(), 204 | weight); 205 | graph.get_mut_edge(from_node, to_node).map(|edge| edge.arc_flag = true); 206 | graph.get_mut_edge(from_node, to_node).map(|edge| edge.shortcut = Some(shortcut.clone())); 207 | } 208 | 209 | fn backtrack_path(graph: &Graph, path_start: &T, results: &HashMap>) -> Vec 210 | where T: GraphKey { 211 | let mut path = vec![]; 212 | path.push(path_start.clone()); 213 | 214 | if let Some(predecessor) = results.get(path_start) 215 | .and_then(|cb| cb.clone().predecessor) { 216 | expand_shortcut(graph, 217 | &predecessor, 218 | &path_start, 219 | &mut path); 220 | let mut shortcuts = backtrack_path(graph, &predecessor, results); 221 | path.append(&mut shortcuts); 222 | path 223 | } else { 224 | path 225 | } 226 | } 227 | 228 | fn expand_shortcut(graph: &Graph, 229 | predecessor: &T, 230 | current: &T, 231 | path: &mut Vec) 232 | where T: GraphKey { 233 | match graph.get_edges(predecessor).iter().find(|e| e.to_id == *current) { 234 | Some(&Edge { ref shortcut, .. }) => { 235 | match shortcut { 236 | &Some(ref shortcut_node) => { 237 | path.push(shortcut_node.clone()); 238 | expand_shortcut(graph, &predecessor, &shortcut_node, path); 239 | }, 240 | &None => {} 241 | } 242 | }, 243 | None => {} 244 | } 245 | } 246 | 247 | #[derive(Clone, Eq, PartialEq, Debug)] 248 | struct EdgeDifference { 249 | node_id: T, 250 | edge_difference: i64 251 | } 252 | 253 | impl Ord for EdgeDifference 254 | where T: GraphKey { 255 | // flip order so min-heap instead of max-heap 256 | fn cmp(&self, other: &EdgeDifference) -> Ordering { 257 | other.edge_difference.cmp(&self.edge_difference) 258 | } 259 | } 260 | 261 | impl PartialOrd for EdgeDifference 262 | where T: GraphKey { 263 | fn partial_cmp(&self, other: &EdgeDifference) -> Option { 264 | Some(self.cmp(other)) 265 | } 266 | } 267 | 268 | fn preorder_nodes(graph: &mut Graph) -> BinaryHeap> 269 | where T: GraphKey { 270 | let mut preorder = BinaryHeap::new(); 271 | let node_ids: Vec = graph.all_nodes() 272 | .iter() 273 | .map(|node| node.id.clone()) 274 | .collect(); 275 | for node_id in node_ids { 276 | let edge_difference = contract_node(graph, &node_id, true); 277 | preorder.push(EdgeDifference { node_id: node_id, 278 | edge_difference: edge_difference 279 | }); 280 | } 281 | 282 | preorder 283 | } 284 | 285 | #[cfg(test)] 286 | mod test { 287 | use weighted_graph::{ Graph }; 288 | use arc_flags::shortest_path as arc_flags_shortest_path; 289 | use super::{ local_shortest_path, 290 | contract_node, 291 | contract_graph, 292 | preorder_nodes, 293 | set_increasing_arc_flags, 294 | preprocess_contraction, 295 | shortest_path 296 | }; 297 | 298 | #[test] 299 | fn local_shortest_path_terminates_early_by_cost() { 300 | let mut graph = Graph::new(); 301 | graph.add_node("a", 0.0, 0.0); 302 | graph.add_node("b", 1.0, 1.0); 303 | graph.add_node("c", 2.0, 2.0); 304 | graph.add_node("d", 3.0, 3.0); 305 | graph.add_edge("ab", "a", "b", 2); 306 | graph.add_edge("bc", "b", "c", 3); 307 | graph.add_edge("cd", "c", "d", 4); 308 | 309 | for (from, to) in vec![("a", "b"), ("b", "c"), ("c", "d")] { 310 | graph.get_mut_edge(&from, &to).map(|edge| edge.arc_flag = true); 311 | } 312 | 313 | let (cost, _) = local_shortest_path(&graph, &"a", &"d", 10, 4); 314 | assert_eq!(cost, 5); 315 | } 316 | 317 | #[test] 318 | fn local_shortest_path_terminates_early_by_neighborhood() { 319 | let mut graph = Graph::new(); 320 | graph.add_node("a", 0.0, 0.0); 321 | graph.add_node("b", 1.0, 1.0); 322 | graph.add_node("c", 2.0, 2.0); 323 | graph.add_node("d", 3.0, 3.0); 324 | graph.add_edge("ab", "a", "b", 2); 325 | graph.add_edge("bc", "b", "c", 3); 326 | graph.add_edge("cd", "c", "d", 4); 327 | 328 | for (from, to) in vec![("a", "b"), ("b", "c"), ("c", "d")] { 329 | graph.get_mut_edge(&from, &to).map(|edge| edge.arc_flag = true); 330 | } 331 | 332 | let (_, results) = local_shortest_path(&graph, &"a", &"d", 2, 10); 333 | assert_eq!(results.len(), 2); 334 | } 335 | 336 | #[test] 337 | fn local_shortest_path_ignores_arc_flags_false() { 338 | let mut graph = Graph::new(); 339 | graph.add_node("a", 0.0, 0.0); 340 | graph.add_node("b", 1.0, 1.0); 341 | graph.add_node("c", 2.0, 2.0); 342 | graph.add_node("d", 3.0, 3.0); 343 | graph.add_edge("ab", "a", "b", 2); 344 | graph.add_edge("bc", "b", "c", 3); 345 | graph.add_edge("cd", "c", "d", 4); 346 | 347 | for (from, to) in vec![("a", "b"), ("b", "c"), ("c", "d")] { 348 | graph.get_mut_edge(&from, &to).map(|edge| edge.arc_flag = true); 349 | } 350 | 351 | graph.get_mut_edge(&"c", &"d").map(|edge| edge.arc_flag = false); 352 | 353 | let(_, results) = local_shortest_path(&graph, &"a", &"d", 10, 10); 354 | assert_eq!(results.len(), 3) 355 | } 356 | 357 | #[test] 358 | fn contract_node_in_shortest_path() { 359 | let mut graph = Graph::new(); 360 | graph.add_node("a", 0.0, 1.0); 361 | graph.add_node("b", 1.0, 0.0); 362 | graph.add_node("c", 2.0, 1.0); 363 | graph.add_node("d", 1.0, 1.0); 364 | let edges = vec![("a", "b", 1), 365 | ("b", "c", 1), 366 | ("c", "d", 3), 367 | ("d", "a", 3)]; 368 | for (n1, n2, w) in edges { 369 | graph.add_edge(n1, n1, n2, w); 370 | graph.add_edge(n2, n2, n1, w); 371 | graph.get_mut_edge(&n1, &n2).map(|edge| edge.arc_flag = true); 372 | graph.get_mut_edge(&n2, &n1).map(|edge| edge.arc_flag = true); 373 | } 374 | 375 | contract_node(&mut graph, &"b", false); 376 | 377 | let added_ac = graph.get_edges(&"a") 378 | .iter() 379 | .find(|edge| edge.to_id == "c") 380 | .unwrap(); 381 | let added_ca = graph.get_edges(&"c") 382 | .iter() 383 | .find(|edge| edge.to_id == "a") 384 | .unwrap(); 385 | assert!(added_ac.arc_flag); 386 | assert_eq!(added_ac.shortcut, Some("b")); 387 | assert_eq!(added_ac.weight, 2); 388 | assert!(added_ca.arc_flag); 389 | assert_eq!(added_ca.shortcut, Some("b")); 390 | assert_eq!(added_ca.weight, 2); 391 | 392 | for edge in graph.get_edges(&"b") { 393 | assert!(!edge.arc_flag); 394 | } 395 | for edge in graph.get_edges(&"a") 396 | .iter() 397 | .filter(|edge| edge.to_id == "b") { 398 | assert!(!edge.arc_flag); 399 | } 400 | for edge in graph.get_edges(&"c") 401 | .iter() 402 | .filter(|edge| edge.to_id == "b") { 403 | assert!(!edge.arc_flag); 404 | } 405 | } 406 | 407 | #[test] 408 | fn calculate_edge_difference_in_shortest_path() { 409 | let mut graph = Graph::new(); 410 | graph.add_node("a", 0.0, 1.0); 411 | graph.add_node("b", 1.0, 0.0); 412 | graph.add_node("c", 2.0, 1.0); 413 | graph.add_node("d", 1.0, 1.0); 414 | let edges = vec![("a", "b", 1), 415 | ("b", "c", 1), 416 | ("c", "d", 3), 417 | ("d", "a", 3)]; 418 | for (n1, n2, w) in edges { 419 | graph.add_edge(n1, n1, n2, w); 420 | graph.add_edge(n2, n2, n1, w); 421 | graph.get_mut_edge(&n1, &n2).map(|edge| edge.arc_flag = true); 422 | graph.get_mut_edge(&n2, &n1).map(|edge| edge.arc_flag = true); 423 | } 424 | 425 | let ed = contract_node(&mut graph, &"b", true); 426 | assert_eq!(ed, 2 - 4); 427 | 428 | for edge in graph.get_edges(&"b") { 429 | assert!(edge.arc_flag); 430 | } 431 | for edge in graph.get_edges(&"a") { 432 | assert!(edge.arc_flag); 433 | } 434 | for edge in graph.get_edges(&"c") { 435 | assert!(edge.arc_flag); 436 | } 437 | } 438 | 439 | #[test] 440 | fn contract_node_not_in_shortest_path() { 441 | let mut graph = Graph::new(); 442 | graph.add_node("a", 0.0, 1.0); 443 | graph.add_node("b", 1.0, 0.0); 444 | graph.add_node("c", 2.0, 1.0); 445 | graph.add_node("d", 1.0, 1.0); 446 | let edges = vec![("a", "b", 2), 447 | ("b", "c", 2), 448 | ("c", "d", 1), 449 | ("d", "a", 1)]; 450 | for (n1, n2, w) in edges { 451 | graph.add_edge(n1, n1, n2, w); 452 | graph.add_edge(n2, n2, n1, w); 453 | graph.get_mut_edge(&n1, &n2).map(|edge| edge.arc_flag = true); 454 | graph.get_mut_edge(&n2, &n1).map(|edge| edge.arc_flag = true); 455 | } 456 | 457 | contract_node(&mut graph, &"b", false); 458 | 459 | let added_ac = graph.get_edges(&"a") 460 | .iter() 461 | .find(|edge| edge.to_id == "c"); 462 | let added_ca = graph.get_edges(&"c") 463 | .iter() 464 | .find(|edge| edge.to_id == "a"); 465 | assert_eq!(added_ac, None); 466 | assert_eq!(added_ca, None); 467 | 468 | for edge in graph.get_edges(&"b") { 469 | assert!(!edge.arc_flag); 470 | } 471 | for edge in graph.get_edges(&"a") 472 | .iter() 473 | .filter(|edge| edge.to_id == "b") { 474 | assert!(!edge.arc_flag); 475 | } 476 | for edge in graph.get_edges(&"c") 477 | .iter() 478 | .filter(|edge| edge.to_id == "b") { 479 | assert!(!edge.arc_flag); 480 | } 481 | } 482 | 483 | #[test] 484 | fn calculate_edge_difference_not_in_shortest_path() { 485 | let mut graph = Graph::new(); 486 | graph.add_node("a", 0.0, 1.0); 487 | graph.add_node("b", 1.0, 0.0); 488 | graph.add_node("c", 2.0, 1.0); 489 | graph.add_node("d", 1.0, 1.0); 490 | let edges = vec![("a", "b", 2), 491 | ("b", "c", 2), 492 | ("c", "d", 1), 493 | ("d", "a", 1)]; 494 | for (n1, n2, w) in edges { 495 | graph.add_edge(n1, n1, n2, w); 496 | graph.add_edge(n2, n2, n1, w); 497 | graph.get_mut_edge(&n1, &n2).map(|edge| edge.arc_flag = true); 498 | graph.get_mut_edge(&n2, &n1).map(|edge| edge.arc_flag = true); 499 | } 500 | 501 | let ed = contract_node(&mut graph, &"b", true); 502 | assert_eq!(ed, 0 - 4); 503 | 504 | for edge in graph.get_edges(&"b") { 505 | assert!(edge.arc_flag); 506 | } 507 | for edge in graph.get_edges(&"a") { 508 | assert!(edge.arc_flag); 509 | } 510 | for edge in graph.get_edges(&"c") { 511 | assert!(edge.arc_flag); 512 | } 513 | } 514 | 515 | fn build_full_graph() -> (Vec<(&'static str, f64, f64)>, // nodes 516 | Vec<(&'static str, &'static str, i64)>, // edges 517 | Graph<&'static str>) { 518 | let mut graph = Graph::new(); 519 | let nodes = vec![("a", 0.0, 3.0), 520 | ("b", 0.0, 1.0), 521 | ("c", 0.0, 0.0), 522 | ("d", 1.0, 3.0), 523 | ("e", 1.0, 2.0), 524 | ("f", 1.0, 0.0), 525 | ("g", 2.0, 3.0), 526 | ("h", 2.0, 1.0), 527 | ("i", 2.0, 0.0)]; 528 | for &(id, x, y) in &nodes { 529 | graph.add_node(id, x, y); 530 | } 531 | 532 | let edges = vec![("a", "b", 3), 533 | ("a", "d", 2), 534 | ("b", "c", 1), 535 | ("b", "e", 1), 536 | ("c", "f", 2), 537 | ("d", "e", 1), 538 | ("d", "g", 2), 539 | ("e", "f", 3), 540 | ("e", "h", 1), 541 | ("f", "i", 2), 542 | ("g", "h", 4), 543 | ("h", "i", 2), 544 | ]; 545 | for &(n1, n2, w) in &edges { 546 | graph.add_edge(n1, n1, n2, w); 547 | graph.add_edge(n2, n2, n1, w); 548 | graph.get_mut_edge(&n1, &n2).map(|edge| edge.arc_flag = true); 549 | graph.get_mut_edge(&n2, &n1).map(|edge| edge.arc_flag = true); 550 | } 551 | 552 | (nodes, edges, graph) 553 | } 554 | 555 | #[test] 556 | fn order_nodes_by_edge_difference() { 557 | let (_, _, mut graph) = build_full_graph(); 558 | 559 | let mut node_order = preorder_nodes(&mut graph); 560 | let mut current_edge_difference = i64::min_value(); 561 | 562 | while let Some(next_node) = node_order.pop() { 563 | assert!(current_edge_difference <= next_node.edge_difference); 564 | current_edge_difference = next_node.edge_difference; 565 | } 566 | assert_eq!(current_edge_difference, 0); 567 | } 568 | 569 | #[test] 570 | fn contract_all_nodes() { 571 | let (nodes, edges, mut graph) = build_full_graph(); 572 | 573 | let node_order = preorder_nodes(&mut graph); 574 | contract_graph(&mut graph, node_order); 575 | 576 | for &(id, _, _) in &nodes { 577 | assert!(graph.get_edges(&id).iter().all(|edge| !edge.arc_flag)); 578 | assert!(graph.get_node(&id) 579 | .map(|node| 580 | node.contraction_order.is_some()) 581 | .unwrap_or(false)) 582 | } 583 | let edge_count = nodes.iter() 584 | .map(|&(id, _, _)| graph.get_edges(&id).len()) 585 | .fold(0, |sum, l| sum + l); 586 | assert!(edge_count >= edges.len() * 2); 587 | } 588 | 589 | #[test] 590 | fn mark_edges_where_contraction_order_increases() { 591 | let (_, _, mut graph) = build_full_graph(); 592 | 593 | let node_order = preorder_nodes(&mut graph); 594 | contract_graph(&mut graph, node_order); 595 | 596 | set_increasing_arc_flags(&mut graph); 597 | 598 | for node in graph.all_nodes() { 599 | for edge in graph.get_edges(&node.id) { 600 | if graph.get_node(&edge.to_id) 601 | .and_then(|n| n.contraction_order) 602 | .unwrap() > 603 | node.contraction_order.unwrap() { 604 | assert!(edge.arc_flag); 605 | } else { 606 | assert!(!edge.arc_flag); 607 | } 608 | } 609 | } 610 | } 611 | 612 | #[test] 613 | fn full_preprocessing_returns_walkable_graph() { 614 | let (nodes, _, mut graph) = build_full_graph(); 615 | 616 | preprocess_contraction(&mut graph); 617 | 618 | for (id, _, _) in nodes { 619 | let (_, results) = arc_flags_shortest_path(&graph, 620 | &id, 621 | None); 622 | let start_node_contraction = graph.get_node(&id) 623 | .unwrap() 624 | .contraction_order 625 | .unwrap(); 626 | let result_contractions: Vec = results.keys() 627 | .map(|id| graph.get_node(id) 628 | .unwrap() 629 | .contraction_order 630 | .unwrap()) 631 | .collect(); 632 | assert!(result_contractions.iter().all(|&co| co >= start_node_contraction)); 633 | } 634 | } 635 | 636 | #[test] 637 | fn find_shortest_path_cost() { 638 | let (_, _, mut graph) = build_full_graph(); 639 | 640 | preprocess_contraction(&mut graph); 641 | let result = shortest_path(&graph, &"a", &"i"); 642 | 643 | match result { 644 | Some((cost, _)) => assert_eq!(cost, 6), 645 | None => assert!(false) 646 | } 647 | } 648 | 649 | #[test] 650 | fn find_shortest_path() { 651 | let (_, _, mut graph) = build_full_graph(); 652 | 653 | preprocess_contraction(&mut graph); 654 | let result = shortest_path(&graph, &"a", &"i"); 655 | 656 | match result { 657 | Some((_, path)) => assert_eq!(path, vec!["a", "d", "e", "h", "i"]), 658 | None => assert!(false) 659 | } 660 | } 661 | } 662 | -------------------------------------------------------------------------------- /src/graph_from_gtfs.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | use std::collections::HashMap; 3 | use time::{ strptime }; 4 | 5 | use weighted_graph::{ GraphKey, Graph }; 6 | 7 | extern crate csv; 8 | 9 | type ServiceId = String; 10 | pub type TripId = String; 11 | pub type StopId = String; 12 | 13 | #[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Hash, Clone)] 14 | pub enum NodeType { 15 | Arrival, 16 | Departure, 17 | Transfer 18 | } 19 | 20 | impl NodeType { 21 | pub fn is_arrival(&self) -> bool { 22 | match self { 23 | &NodeType::Arrival => true, 24 | _ => false 25 | } 26 | } 27 | 28 | pub fn is_departure(&self) -> bool { 29 | match self { 30 | &NodeType::Departure => true, 31 | _ => false 32 | } 33 | } 34 | 35 | pub fn is_transfer(&self) -> bool { 36 | match self { 37 | &NodeType::Transfer => true, 38 | _ => false 39 | } 40 | } 41 | } 42 | 43 | #[derive(Eq, PartialEq, Hash, Clone, Debug)] 44 | pub struct GtfsId { 45 | pub stop_id: StopId, 46 | pub time: i64, 47 | pub node_type: NodeType, 48 | pub trip_id: Option 49 | } 50 | impl GraphKey for GtfsId {} 51 | 52 | const FIVE_MINUTES: i64 = 5 * 60; 53 | 54 | pub fn build_graph_from_gtfs(gtfs_dir: &str, day: &str) -> Graph { 55 | let schedule_path = gtfs_dir.to_string() + "calendar.txt"; 56 | let trip_path = gtfs_dir.to_string() + "trips.txt"; 57 | let stops_path = gtfs_dir.to_string() + "stops.txt"; 58 | 59 | let services = service_on_day(&schedule_path, &day); 60 | let trips = trips_for_services(&trip_path, 61 | &services); 62 | let stops = stops_data(&stops_path); 63 | 64 | assemble_graph(gtfs_dir, &trips, &stops) 65 | } 66 | 67 | type StopTimeRow = (String, 68 | String, 69 | String, 70 | String, 71 | Option, 72 | Option, 73 | Option, 74 | Option, 75 | Option); 76 | 77 | fn assemble_graph(gtfs_dir: &str, 78 | trips: &HashSet, 79 | stops: &HashMap) -> Graph { 80 | let mut reader = csv::Reader::from_file(gtfs_dir.to_string() + "stop_times.txt").unwrap(); 81 | let mut graph = Graph::new(); 82 | for row in reader.decode() { 83 | let data: StopTimeRow = row.unwrap(); 84 | if trips.contains(&data.0) { 85 | build_nodes(&data, stops, &mut graph); 86 | } 87 | } 88 | build_trip_edges(&mut graph); 89 | link_transfer_nodes(&mut graph); 90 | graph 91 | } 92 | 93 | fn build_nodes(data: &StopTimeRow, 94 | stops: &HashMap, 95 | graph: &mut Graph) { 96 | if let (Some(arrival_time), 97 | Some(departure_time)) = (time_to_seconds_after_midnight(&data.1), 98 | time_to_seconds_after_midnight(&data.2)) { 99 | 100 | let arr_node_id = GtfsId { stop_id: data.3.clone(), 101 | time: arrival_time, 102 | node_type: NodeType::Arrival, 103 | trip_id: Some(data.0.clone()) 104 | }; 105 | let dep_node_id = GtfsId { stop_id: data.3.clone(), 106 | time: departure_time, 107 | node_type: NodeType::Departure, 108 | trip_id: Some(data.0.clone()) 109 | }; 110 | let trf_node_id = GtfsId { stop_id: data.3.clone(), 111 | time: arrival_time + FIVE_MINUTES, 112 | node_type: NodeType::Transfer, 113 | trip_id: None 114 | }; 115 | 116 | if let Some(stop_data) = stops.get(&data.3) { 117 | for node_id in vec![&arr_node_id, &dep_node_id, &trf_node_id] { 118 | graph.add_node(node_id.clone(), stop_data.x, stop_data.y); 119 | } 120 | graph.add_edge(edge_id(&arr_node_id, &trf_node_id), 121 | arr_node_id, 122 | trf_node_id, 123 | FIVE_MINUTES); 124 | } 125 | } 126 | } 127 | 128 | fn edge_id(from: &GtfsId, to: &GtfsId) -> GtfsId { 129 | GtfsId { 130 | stop_id: from.stop_id.clone() + &to.stop_id.clone(), 131 | time: from.time.clone(), 132 | node_type: to.node_type.clone(), 133 | trip_id: None 134 | } 135 | } 136 | 137 | fn build_trip_edges(graph: &mut Graph) { 138 | let mut trip_nodes = HashMap::new(); 139 | for node in graph.all_nodes() { 140 | if let Some(ref trip_id) = node.id.trip_id { 141 | let mut nodes_for_trip = trip_nodes.entry(trip_id.clone()).or_insert(Vec::new()); 142 | nodes_for_trip.push(node.id.clone()); 143 | } 144 | } 145 | 146 | for (_, nodes) in trip_nodes.iter_mut() { 147 | let mut ns = nodes.iter().filter(|n| !n.node_type.is_transfer()).collect::>(); 148 | ns.sort_by(|a, b| 149 | if a.time == b.time { 150 | a.node_type.cmp(&b.node_type) 151 | } else { 152 | a.time.cmp(&b.time) 153 | }); 154 | 155 | for adj_nodes in ns.windows(2) { 156 | let from = adj_nodes[0].clone(); 157 | let to = adj_nodes[1].clone(); 158 | let edge_weight = to.time - from.time; 159 | graph.add_edge(edge_id(&from, &to), 160 | from, 161 | to, 162 | edge_weight); 163 | } 164 | } 165 | } 166 | 167 | fn link_transfer_nodes(graph: &mut Graph) { 168 | let mut stop_nodes = HashMap::new(); 169 | for node in graph.all_nodes().iter().filter(|n| !n.id.node_type.is_arrival()) { 170 | let mut nodes_for_stop = stop_nodes.entry(node.id.stop_id.clone()).or_insert(Vec::new()); 171 | nodes_for_stop.push(node.id.clone()); 172 | } 173 | 174 | for (_, nodes) in stop_nodes.into_iter() { 175 | let (mut transfers, 176 | mut departures): (Vec, 177 | Vec) = nodes.into_iter() 178 | .partition(|n| n.node_type.is_transfer()); 179 | 180 | transfers.sort_by(|a, b| a.time.cmp(&b.time)); 181 | departures.sort_by(|a, b| a.time.cmp(&b.time)); 182 | 183 | link_adjacent_transfers(graph, &transfers); 184 | link_transfers_to_departures(graph, &transfers, departures); 185 | } 186 | } 187 | 188 | fn link_adjacent_transfers(graph: &mut Graph, transfers: &Vec) { 189 | for adj_transfers in transfers.windows(2) { 190 | let from = adj_transfers[0].clone(); 191 | let to = adj_transfers[1].clone(); 192 | let edge_weight = to.time - from.time; 193 | graph.add_edge(edge_id(&from, &to), 194 | from, 195 | to, 196 | edge_weight); 197 | } 198 | 199 | } 200 | 201 | fn link_transfers_to_departures(graph: &mut Graph, 202 | transfers: &Vec, 203 | departures: Vec) { 204 | 205 | for departure in departures { 206 | if let Some(transfer) = transfers.iter() 207 | .filter(|t| t.time <= departure.time) 208 | .max_by_key(|t| t.time) { 209 | let edge_weight = departure.time - transfer.time; 210 | graph.add_edge(edge_id(&transfer, &departure), 211 | transfer.clone(), 212 | departure, 213 | edge_weight); 214 | } 215 | } 216 | } 217 | 218 | type ScheduleRow = (String, 219 | usize, 220 | usize, 221 | usize, 222 | usize, 223 | usize, 224 | usize, 225 | usize, 226 | String, 227 | String); 228 | 229 | fn service_on_day(path: &str, day: &str) -> HashSet { 230 | let mut reader = csv::Reader::from_file(path).unwrap(); 231 | reader.decode() 232 | .filter_map(|row| 233 | match row { 234 | Ok(data) => Some(data), 235 | Err(_) => None 236 | } 237 | ) 238 | .filter(|row: &ScheduleRow| runs_on_day(&day, row)) 239 | .map(|row: ScheduleRow| row.0) 240 | .collect::>() 241 | } 242 | 243 | fn runs_on_day(day: &str, row: &ScheduleRow) -> bool { 244 | let mut days = HashMap::new(); 245 | days.insert("monday", row.1); 246 | days.insert("tuesday", row.2); 247 | days.insert("wednesday", row.3); 248 | days.insert("thursday", row.4); 249 | days.insert("friday", row.5); 250 | days.insert("saturday", row.6); 251 | days.insert("sunday", row.7); 252 | 253 | days.get(day).map(|&val| val == 1).unwrap_or(false) 254 | } 255 | 256 | type TripRow = (String, 257 | String, 258 | String, 259 | String, 260 | String, 261 | String, 262 | String); 263 | 264 | fn trips_for_services(path: &str, services: &HashSet) -> HashSet { 265 | let mut reader = csv::Reader::from_file(path).unwrap(); 266 | reader.decode() 267 | .filter_map(|row| 268 | match row { 269 | Ok(data) => Some(data), 270 | Err(_) => None 271 | } 272 | ).filter_map(|row: TripRow| 273 | if services.contains(&row.1) { 274 | Some(row.2) 275 | } else { 276 | None 277 | } 278 | ).collect::>() 279 | } 280 | 281 | type StopRow = (String, 282 | Option, 283 | String, 284 | Option, 285 | f64, 286 | f64, 287 | Option, 288 | Option, 289 | Option, 290 | Option); 291 | 292 | #[derive(Clone, PartialEq, Debug)] 293 | struct Location { 294 | x: f64, 295 | y: f64 296 | } 297 | 298 | fn stops_data(path: &str) -> HashMap { 299 | let mut reader = csv::Reader::from_file(path).unwrap(); 300 | reader.decode() 301 | .filter_map(|row| 302 | match row { 303 | Ok(data) => Some(data), 304 | Err(_) => None 305 | } 306 | ) 307 | .map(|row: StopRow| 308 | (row.0, Location { x: row.5, y: row.4 }) 309 | ) 310 | .collect() 311 | } 312 | 313 | pub fn time_to_seconds_after_midnight(t_str: &String) -> Option { 314 | match strptime(t_str, "%T") { 315 | Ok(t) => { 316 | Some((t.tm_sec + 60 * t.tm_min + 60 * 60 * t.tm_hour) as i64) 317 | } 318 | Err(_) => None 319 | } 320 | } 321 | 322 | #[cfg(test)] 323 | mod test { 324 | use std::collections::HashMap; 325 | use std::collections::HashSet; 326 | use test_helpers::to_node_id; 327 | use super::{ GtfsId, 328 | TripId, 329 | Location, 330 | NodeType, 331 | service_on_day, 332 | trips_for_services, 333 | stops_data, 334 | time_to_seconds_after_midnight, 335 | build_graph_from_gtfs 336 | }; 337 | 338 | #[test] 339 | fn return_services_active_on_a_day() { 340 | let services = service_on_day("data/gtfs_example/calendar.txt", "wednesday"); 341 | 342 | let mut expected = HashSet::new(); 343 | expected.insert("weekday".to_string()); 344 | 345 | assert_eq!(services, expected); 346 | } 347 | 348 | #[test] 349 | fn return_trips_for_services() { 350 | let mut services = HashSet::new(); 351 | services.insert("weekday".to_string()); 352 | 353 | let trips = trips_for_services("data/gtfs_example/trips.txt", &services); 354 | 355 | let expected_trips = vec!["g1", 356 | "g2", 357 | "g3", 358 | "g4", 359 | "g5", 360 | "r1", 361 | "r2", 362 | "r3"]; 363 | let expected = expected_trips.iter() 364 | .map(|t| t.to_string()) 365 | .collect::>(); 366 | 367 | assert_eq!(trips, expected); 368 | 369 | } 370 | 371 | #[test] 372 | fn build_stop_data_map() { 373 | let stops = stops_data("data/gtfs_example/stops.txt"); 374 | 375 | let expected_stops = vec![("A".to_string(), Location { x: 0.0, y: 1.0 }), 376 | ("B".to_string(), Location { x: 1.0, y: 3.0 }), 377 | ("C".to_string(), Location { x: 1.0, y: 0.0 }), 378 | ("D".to_string(), Location { x: 2.0, y: 1.0 }), 379 | ("E".to_string(), Location { x: 3.0, y: 2.0 }), 380 | ("F".to_string(), Location { x: 4.0, y: 1.0 })]; 381 | let expected = expected_stops.into_iter() 382 | .collect::>(); 383 | assert_eq!(stops, expected); 384 | } 385 | 386 | #[test] 387 | fn parse_times_to_seconds() { 388 | let t = "08:00:00".to_string(); 389 | let invalid = "notatime".to_string(); 390 | 391 | assert_eq!(time_to_seconds_after_midnight(&t), Some(8 * 60 * 60)); 392 | assert_eq!(time_to_seconds_after_midnight(&invalid), None); 393 | } 394 | 395 | #[test] 396 | fn build_transit_graph_with_valid_nodes() { 397 | let nodes = vec![("A", "06:00:00", NodeType::Arrival, Some("r1")), 398 | ("A", "06:00:00", NodeType::Departure, Some("r1")), 399 | ("A", "06:05:00", NodeType::Transfer, None), 400 | ("A", "07:00:00", NodeType::Arrival, Some("r2")), 401 | ("A", "07:00:00", NodeType::Departure, Some("r2")), 402 | ("A", "07:05:00", NodeType::Transfer, None), 403 | ("A", "08:00:00", NodeType::Arrival, Some("r3")), 404 | ("A", "08:00:00", NodeType::Departure, Some("r3")), 405 | ("A", "08:05:00", NodeType::Transfer, None), 406 | ("B", "06:25:00", NodeType::Arrival, Some("r1")), 407 | ("B", "06:25:00", NodeType::Departure, Some("r1")), 408 | ("B", "06:30:00", NodeType::Transfer, None), 409 | ("B", "07:25:00", NodeType::Arrival, Some("r2")), 410 | ("B", "07:25:00", NodeType::Departure, Some("r2")), 411 | ("B", "07:30:00", NodeType::Transfer, None), 412 | ("B", "08:25:00", NodeType::Arrival, Some("r3")), 413 | ("B", "08:25:00", NodeType::Departure, Some("r3")), 414 | ("B", "08:30:00", NodeType::Transfer, None), 415 | ("E", "06:50:00", NodeType::Arrival, Some("r1")), 416 | ("E", "06:50:00", NodeType::Departure, Some("r1")), 417 | ("E", "06:55:00", NodeType::Transfer, None), 418 | ("E", "07:50:00", NodeType::Arrival, Some("r2")), 419 | ("E", "07:50:00", NodeType::Departure, Some("r2")), 420 | ("E", "07:55:00", NodeType::Transfer, None), 421 | ("E", "08:50:00", NodeType::Arrival, Some("r3")), 422 | ("E", "08:50:00", NodeType::Departure, Some("r3")), 423 | ("E", "08:55:00", NodeType::Transfer, None), 424 | ("A", "06:15:00", NodeType::Arrival, Some("g1")), 425 | ("A", "06:15:00", NodeType::Departure, Some("g1")), 426 | ("A", "06:20:00", NodeType::Transfer, None), 427 | ("A", "06:45:00", NodeType::Arrival, Some("g2")), 428 | ("A", "06:45:00", NodeType::Departure, Some("g2")), 429 | ("A", "06:50:00", NodeType::Transfer, None), 430 | ("A", "07:15:00", NodeType::Arrival, Some("g3")), 431 | ("A", "07:15:00", NodeType::Departure, Some("g3")), 432 | ("A", "07:20:00", NodeType::Transfer, None), 433 | ("A", "07:45:00", NodeType::Arrival, Some("g4")), 434 | ("A", "07:45:00", NodeType::Departure, Some("g4")), 435 | ("A", "07:50:00", NodeType::Transfer, None), 436 | ("A", "08:15:00", NodeType::Arrival, Some("g5")), 437 | ("A", "08:15:00", NodeType::Departure, Some("g5")), 438 | ("A", "08:20:00", NodeType::Transfer, None), 439 | ("C", "06:45:00", NodeType::Arrival, Some("g1")), 440 | ("C", "06:45:00", NodeType::Departure, Some("g1")), 441 | ("C", "06:50:00", NodeType::Transfer, None), 442 | ("C", "07:15:00", NodeType::Arrival, Some("g2")), 443 | ("C", "07:15:00", NodeType::Departure, Some("g2")), 444 | ("C", "07:20:00", NodeType::Transfer, None), 445 | ("C", "07:45:00", NodeType::Arrival, Some("g3")), 446 | ("C", "07:45:00", NodeType::Departure, Some("g3")), 447 | ("C", "07:50:00", NodeType::Transfer, None), 448 | ("C", "08:15:00", NodeType::Arrival, Some("g4")), 449 | ("C", "08:15:00", NodeType::Departure, Some("g4")), 450 | ("C", "08:20:00", NodeType::Transfer, None), 451 | ("C", "08:45:00", NodeType::Arrival, Some("g5")), 452 | ("C", "08:45:00", NodeType::Departure, Some("g5")), 453 | ("C", "08:50:00", NodeType::Transfer, None), 454 | ("D", "07:00:00", NodeType::Arrival, Some("g1")), 455 | ("D", "07:00:00", NodeType::Departure, Some("g1")), 456 | ("D", "07:05:00", NodeType::Transfer, None), 457 | ("D", "07:30:00", NodeType::Arrival, Some("g2")), 458 | ("D", "07:30:00", NodeType::Departure, Some("g2")), 459 | ("D", "07:35:00", NodeType::Transfer, None), 460 | ("D", "08:00:00", NodeType::Arrival, Some("g3")), 461 | ("D", "08:00:00", NodeType::Departure, Some("g3")), 462 | ("D", "08:05:00", NodeType::Transfer, None), 463 | ("D", "08:30:00", NodeType::Arrival, Some("g4")), 464 | ("D", "08:30:00", NodeType::Departure, Some("g4")), 465 | ("D", "08:35:00", NodeType::Transfer, None), 466 | ("D", "09:00:00", NodeType::Arrival, Some("g5")), 467 | ("D", "09:00:00", NodeType::Departure, Some("g5")), 468 | ("D", "09:05:00", NodeType::Transfer, None), 469 | ("E", "07:30:00", NodeType::Arrival, Some("g1")), 470 | ("E", "07:30:00", NodeType::Departure, Some("g1")), 471 | ("E", "07:35:00", NodeType::Transfer, None), 472 | ("E", "08:00:00", NodeType::Arrival, Some("g2")), 473 | ("E", "08:00:00", NodeType::Departure, Some("g2")), 474 | ("E", "08:05:00", NodeType::Transfer, None), 475 | ("E", "08:30:00", NodeType::Arrival, Some("g3")), 476 | ("E", "08:30:00", NodeType::Departure, Some("g3")), 477 | ("E", "08:35:00", NodeType::Transfer, None), 478 | ("E", "09:00:00", NodeType::Arrival, Some("g4")), 479 | ("E", "09:00:00", NodeType::Departure, Some("g4")), 480 | ("E", "09:05:00", NodeType::Transfer, None), 481 | ("E", "09:30:00", NodeType::Arrival, Some("g5")), 482 | ("E", "09:30:00", NodeType::Departure, Some("g5")), 483 | ("E", "09:35:00", NodeType::Transfer, None), 484 | ("F", "07:40:00", NodeType::Arrival, Some("g1")), 485 | ("F", "07:40:00", NodeType::Departure, Some("g1")), 486 | ("F", "07:45:00", NodeType::Transfer, None), 487 | ("F", "08:10:00", NodeType::Arrival, Some("g2")), 488 | ("F", "08:10:00", NodeType::Departure, Some("g2")), 489 | ("F", "08:15:00", NodeType::Transfer, None), 490 | ("F", "08:40:00", NodeType::Arrival, Some("g3")), 491 | ("F", "08:40:00", NodeType::Departure, Some("g3")), 492 | ("F", "08:45:00", NodeType::Transfer, None), 493 | ("F", "09:10:00", NodeType::Arrival, Some("g4")), 494 | ("F", "09:10:00", NodeType::Departure, Some("g4")), 495 | ("F", "09:15:00", NodeType::Transfer, None), 496 | ("F", "09:40:00", NodeType::Arrival, Some("g5")), 497 | ("F", "09:40:00", NodeType::Departure, Some("g5")), 498 | ("F", "09:45:00", NodeType::Transfer, None) 499 | ]; 500 | 501 | let expected_node_ids = nodes.into_iter() 502 | .map(|data| to_node_id(data)) 503 | .collect::>(); 504 | 505 | let graph = build_graph_from_gtfs("data/gtfs_example/", "wednesday"); 506 | 507 | let actual_nodes = graph.all_nodes() 508 | .iter() 509 | .map(|&node| node.id.clone()) 510 | .collect::>(); 511 | 512 | assert_eq!(actual_nodes, expected_node_ids); 513 | } 514 | 515 | #[test] 516 | fn build_transit_graph_with_edges_within_trip() { 517 | let edges = vec![ 518 | (("A", "06:15:00", NodeType::Departure, Some("g1")), 519 | ("C", "06:45:00", NodeType::Arrival, Some("g1")), 520 | 30), 521 | (("C", "06:45:00", NodeType::Arrival, Some("g1")), 522 | ("C", "06:45:00", NodeType::Departure, Some("g1")), 523 | 0), 524 | (("C", "06:45:00", NodeType::Arrival, Some("g1")), 525 | ("C", "06:50:00", NodeType::Transfer, None), 526 | 5), 527 | (("C", "06:45:00", NodeType::Departure, Some("g1")), 528 | ("D", "07:00:00", NodeType::Arrival, Some("g1")), 529 | 15), 530 | (("D", "07:00:00", NodeType::Arrival, Some("g1")), 531 | ("D", "07:00:00", NodeType::Departure, Some("g1")), 532 | 0), 533 | (("D", "07:00:00", NodeType::Arrival, Some("g1")), 534 | ("D", "07:05:00", NodeType::Transfer, None), 535 | 5), 536 | (("D", "07:00:00", NodeType::Departure, Some("g1")), 537 | ("E", "07:30:00", NodeType::Arrival, Some("g1")), 538 | 30), 539 | (("E", "07:30:00", NodeType::Arrival, Some("g1")), 540 | ("E", "07:30:00", NodeType::Departure, Some("g1")), 541 | 0), 542 | (("E", "07:30:00", NodeType::Arrival, Some("g1")), 543 | ("E", "07:35:00", NodeType::Transfer, None), 544 | 5), 545 | (("E", "07:30:00", NodeType::Departure, Some("g1")), 546 | ("F", "07:40:00", NodeType::Arrival, Some("g1")), 547 | 10), 548 | (("F", "07:40:00", NodeType::Arrival, Some("g1")), 549 | ("F", "07:40:00", NodeType::Departure, Some("g1")), 550 | 0), 551 | (("F", "07:40:00", NodeType::Arrival, Some("g1")), 552 | ("F", "07:45:00", NodeType::Transfer, None), 553 | 5)]; 554 | 555 | let mut graph = build_graph_from_gtfs("data/gtfs_example/", "wednesday"); 556 | 557 | for edge in edges { 558 | let from = to_node_id(edge.0); 559 | let to = to_node_id(edge.1); 560 | let cost = edge.2; 561 | 562 | let actual_edge = graph.get_mut_edge(&from, &to); 563 | assert!(actual_edge.is_some()); 564 | assert_eq!(actual_edge.map(|e| e.weight), Some(cost * 60)); 565 | } 566 | } 567 | 568 | #[test] 569 | fn attaches_transfer_nodes() { 570 | let transfer_edges = vec![ 571 | // arrival -> transfer 572 | (("E", "06:50:00", NodeType::Arrival, Some("r1")), 573 | ("E", "06:55:00", NodeType::Transfer, None), 574 | 5), 575 | (("E", "07:50:00", NodeType::Arrival, Some("r2")), 576 | ("E", "07:55:00", NodeType::Transfer, None), 577 | 5), 578 | (("E", "08:50:00", NodeType::Arrival, Some("r3")), 579 | ("E", "08:55:00", NodeType::Transfer, None), 580 | 5), 581 | (("E", "07:30:00", NodeType::Arrival, Some("g1")), 582 | ("E", "07:35:00", NodeType::Transfer, None), 583 | 5), 584 | (("E", "08:00:00", NodeType::Arrival, Some("g2")), 585 | ("E", "08:05:00", NodeType::Transfer, None), 586 | 5), 587 | (("E", "08:30:00", NodeType::Arrival, Some("g3")), 588 | ("E", "08:35:00", NodeType::Transfer, None), 589 | 5), 590 | (("E", "09:00:00", NodeType::Arrival, Some("g4")), 591 | ("E", "09:05:00", NodeType::Transfer, None), 592 | 5), 593 | (("E", "09:30:00", NodeType::Arrival, Some("g5")), 594 | ("E", "09:35:00", NodeType::Transfer, None), 595 | 5), 596 | // transfer -> transfer 597 | (("E", "06:55:00", NodeType::Transfer, None), 598 | ("E", "07:35:00", NodeType::Transfer, None), 599 | 40), 600 | (("E", "07:35:00", NodeType::Transfer, None), 601 | ("E", "07:55:00", NodeType::Transfer, None), 602 | 20), 603 | (("E", "07:55:00", NodeType::Transfer, None), 604 | ("E", "08:05:00", NodeType::Transfer, None), 605 | 10), 606 | (("E", "08:05:00", NodeType::Transfer, None), 607 | ("E", "08:35:00", NodeType::Transfer, None), 608 | 30), 609 | (("E", "08:35:00", NodeType::Transfer, None), 610 | ("E", "08:55:00", NodeType::Transfer, None), 611 | 20), 612 | (("E", "08:55:00", NodeType::Transfer, None), 613 | ("E", "09:05:00", NodeType::Transfer, None), 614 | 10), 615 | (("E", "09:05:00", NodeType::Transfer, None), 616 | ("E", "09:35:00", NodeType::Transfer, None), 617 | 30), 618 | // transfer -> departure 619 | (("E", "06:55:00", NodeType::Transfer, None), 620 | ("E", "07:30:00", NodeType::Departure, Some("g1")), 621 | 35), 622 | (("E", "07:35:00", NodeType::Transfer, None), 623 | ("E", "07:50:00", NodeType::Departure, Some("r2")), 624 | 15), 625 | (("E", "07:55:00", NodeType::Transfer, None), 626 | ("E", "08:00:00", NodeType::Departure, Some("g2")), 627 | 5), 628 | (("E", "08:05:00", NodeType::Transfer, None), 629 | ("E", "08:30:00", NodeType::Departure, Some("g3")), 630 | 25), 631 | (("E", "08:35:00", NodeType::Transfer, None), 632 | ("E", "08:50:00", NodeType::Departure, Some("r3")), 633 | 15), 634 | (("E", "08:55:00", NodeType::Transfer, None), 635 | ("E", "09:00:00", NodeType::Departure, Some("g4")), 636 | 5), 637 | ]; 638 | 639 | let mut graph = build_graph_from_gtfs("data/gtfs_example/", "wednesday"); 640 | 641 | for edge in transfer_edges { 642 | let from = to_node_id(edge.0); 643 | let to = to_node_id(edge.1); 644 | let cost = edge.2; 645 | 646 | let actual_edge = graph.get_mut_edge(&from, &to); 647 | assert!(actual_edge.is_some()); 648 | assert_eq!(actual_edge.map(|e| e.weight), Some(cost * 60)); 649 | } 650 | } 651 | } 652 | --------------------------------------------------------------------------------