├── 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::