Commit b02fc683 authored by Stephen D's avatar Stephen D
Browse files

Bug fix when finding closest node

parent 3826c6e3
......@@ -207,16 +207,13 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
[[package]]
name = "fast_paths"
version = "0.1.1"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4628fcf1bf810d021009b7e70fa2bbd5a0d517b9f7a0e47c2bb65b0aa318120d"
checksum = "4a8a62c6d5e007415b7ddc5092ae0c26ba0b8d365e58d175381e1362ca7198ec"
dependencies = [
"bincode",
"log 0.4.11",
"priority-queue",
"rand 0.6.5",
"serde",
"stopwatch",
]
[[package]]
......@@ -421,7 +418,7 @@ dependencies = [
"futures-sink",
"futures-util",
"http",
"indexmap 1.5.0",
"indexmap",
"slab",
"tokio",
"tokio-util",
......@@ -554,12 +551,6 @@ dependencies = [
"unicode-normalization",
]
[[package]]
name = "indexmap"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7164c96d6e18ccc3ce43f3dedac996c21a220670a106c275b96ad92110401362"
[[package]]
name = "indexmap"
version = "1.5.0"
......@@ -767,75 +758,6 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "num"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e"
dependencies = [
"num-bigint",
"num-complex",
"num-integer",
"num-iter",
"num-rational",
"num-traits",
]
[[package]]
name = "num-bigint"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e63899ad0da84ce718c14936262a41cee2c79c981fc0a0e7c7beb47d5a07e8c1"
dependencies = [
"num-integer",
"num-traits",
"rand 0.4.6",
"rustc-serialize",
]
[[package]]
name = "num-complex"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b288631d7878aaf59442cffd36910ea604ecd7745c36054328595114001c9656"
dependencies = [
"num-traits",
"rustc-serialize",
]
[[package]]
name = "num-integer"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b"
dependencies = [
"autocfg 1.0.0",
"num-traits",
]
[[package]]
name = "num-iter"
version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6e6b7c748f995c4c29c5f5ae0248536e04a5739927c74ec0fa564805094b9f"
dependencies = [
"autocfg 1.0.0",
"num-integer",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.1.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e"
dependencies = [
"num-bigint",
"num-integer",
"num-traits",
"rustc-serialize",
]
[[package]]
name = "num-traits"
version = "0.2.12"
......@@ -989,11 +911,12 @@ checksum = "237a5ed80e274dbc66f86bd59c1e25edc039660be53194b5fe0a482e0f2612ea"
[[package]]
name = "priority-queue"
version = "0.6.0"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "602c2e38842965277b124586dbc4691d83f37af5b4ecd7c9e46908e1bd7d5b35"
checksum = "8e1340009a04e81f656a4e45e295f0b1191c81de424bf940c865e33577a8e223"
dependencies = [
"indexmap 0.4.1",
"autocfg 1.0.0",
"indexmap",
]
[[package]]
......@@ -1063,19 +986,6 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
dependencies = [
"fuchsia-cprng",
"libc",
"rand_core 0.3.1",
"rdrand",
"winapi 0.3.9",
]
[[package]]
name = "rand"
version = "0.6.5"
......@@ -1306,12 +1216,6 @@ dependencies = [
"smallvec",
]
[[package]]
name = "rustc-serialize"
version = "0.3.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
[[package]]
name = "rustic_roads"
version = "0.1.0"
......@@ -1458,15 +1362,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "stopwatch"
version = "0.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d04b5ebc78da44d3a456319d8bc2783e7d8cc7ccbb5cb4dc3f54afbd93bf728"
dependencies = [
"num",
]
[[package]]
name = "syn"
version = "1.0.35"
......
......@@ -8,7 +8,7 @@ edition = "2018"
[dependencies]
osmpbfreader = "0.14"
fast_paths = "0.1"
fast_paths = "0.2.0"
rstar = { version = "0.8", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }
bincode = "1.3"
......
tab_spaces = 4
hard_tabs = true
edition = "2018"
const EARTH_RADIUS_KILOMETER: f64 = 6371.0;
pub fn haversine(a: (f64, f64), b: (f64, f64)) -> f64 {
let a_lat = a.0.to_radians();
let b_lat = b.0.to_radians();
let a_lat = a.0.to_radians();
let b_lat = b.0.to_radians();
let delta_latitude = (a.0 - b.0).to_radians();
let delta_longitude = (a.1 - b.1).to_radians();
let tmp1 = (delta_latitude / 2.0).sin();
let tmp2 = (delta_longitude / 2.0).sin();
let central_angle_inner = tmp1 * tmp1
+ a_lat.cos() * b_lat.cos() * tmp2 * tmp2;
let tmp1 = (delta_latitude / 2.0).sin();
let tmp2 = (delta_longitude / 2.0).sin();
let central_angle_inner = tmp1 * tmp1 + a_lat.cos() * b_lat.cos() * tmp2 * tmp2;
let central_angle = 2.0 * central_angle_inner.sqrt().asin();
EARTH_RADIUS_KILOMETER * central_angle
......@@ -19,50 +17,50 @@ pub fn haversine(a: (f64, f64), b: (f64, f64)) -> f64 {
#[cfg(test)]
pub mod haversine_tests {
use crate::haversine::haversine;
fn assert_pretty_close(a: f64, b: f64) {
let percent_err = 100.0 * (a - b).abs() / a;
assert!(percent_err < 0.5, format!("Expected {}, found {}", a, b));
}
use crate::haversine::haversine;
fn assert_pretty_close(a: f64, b: f64) {
let percent_err = 100.0 * (a - b).abs() / a;
assert!(percent_err < 0.5, "Expected {}, found {}", a, b);
}
#[test]
fn test_dist_long_range() {
//Distance between Halifax and Rabat
let halifax = (44.651070, -63.582687);
let rabat = (34.01325, -6.83255);
#[test]
fn test_dist_long_range() {
//Distance between Halifax and Rabat
let halifax = (44.651070, -63.582687);
let rabat = (34.01325, -6.83255);
assert_pretty_close(4916.0, haversine(halifax, rabat));
assert_pretty_close(4916.0, haversine(rabat, halifax));
}
assert_pretty_close(4916.0, haversine(halifax, rabat));
assert_pretty_close(4916.0, haversine(rabat, halifax));
}
#[test]
fn test_dist_med_range() {
//Distance between Halifax and Fredericton
let halifax = (44.651070, -63.582687);
let fredericton = (45.9636, -66.6431);
#[test]
fn test_dist_med_range() {
//Distance between Halifax and Fredericton
let halifax = (44.651070, -63.582687);
let fredericton = (45.9636, -66.6431);
assert_pretty_close(280.0, haversine(halifax, fredericton));
assert_pretty_close(280.0, haversine(fredericton, halifax));
}
assert_pretty_close(280.0, haversine(halifax, fredericton));
assert_pretty_close(280.0, haversine(fredericton, halifax));
}
#[test]
fn test_dist_large_diff_in_lat() {
//Distance between Halifax and Buenos Aires
let halifax = (44.651070, -63.582687);
let aires = (-34.603722, -58.381592);
#[test]
fn test_dist_large_diff_in_lat() {
//Distance between Halifax and Buenos Aires
let halifax = (44.651070, -63.582687);
let aires = (-34.603722, -58.381592);
assert_pretty_close(8822.0, haversine(halifax, aires));
assert_pretty_close(8822.0, haversine(aires, halifax));
}
assert_pretty_close(8822.0, haversine(halifax, aires));
assert_pretty_close(8822.0, haversine(aires, halifax));
}
#[test]
fn test_dist_short_range() {
//Random buildings in Halifax
let building1 = (44.657062, -63.595678);
let building2 = (44.656307, -63.595914);
#[test]
fn test_dist_short_range() {
//Random buildings in Halifax
let building1 = (44.657062, -63.595678);
let building2 = (44.656307, -63.595914);
assert_pretty_close(0.086, haversine(building1, building2));
assert_pretty_close(0.086, haversine(building2, building1));
}
assert_pretty_close(0.086, haversine(building1, building2));
assert_pretty_close(0.086, haversine(building2, building1));
}
}
use std::fs::File;
use std::collections::HashMap;
use std::io::BufWriter;
use std::fs::File;
use std::io::prelude::*;
use std::io::BufWriter;
use osmpbfreader::OsmObj::{Node, Way};
use osmpbfreader::objects::NodeId;
use osmpbfreader::OsmObj::{Node, Way};
use fast_paths::InputGraph;
use rstar::RTree;
use crate::structures::{ MyNode, MyEdge, MyRTreeNode };
use crate::haversine::haversine;
use crate::structures::{MyEdge, MyNode, MyRTreeNode};
macro_rules! map(
{ $($key:expr => $value:expr),+ } => {
......@@ -29,7 +29,7 @@ pub fn preprocess(filename: &str) {
// TODO these numbers are wrong(mph?)
// We're not using the numbers right now anyway
// Can't put this in a const due to the macro
let allowed_roads_car: HashMap<&str, u32> = map!{
let allowed_roads_car: HashMap<&str, u32> = map! {
"motorway" => 90,
"motorway_link" => 45,
"trunk" => 85,
......@@ -45,27 +45,28 @@ pub fn preprocess(filename: &str) {
"living_street" => 10,
"service" => 15
};
println!("Preprocessing {}...", filename);
let mut input_graph = InputGraph::new();
{
let mut nodes: HashMap<i64, MyNode> = HashMap::new();
let mut edges: Vec<MyEdge> = Vec::new();
let mut pbf = osmpbfreader::OsmPbfReader::new(File::open(filename).unwrap());
for obj in pbf.par_iter() {
let obj = obj.unwrap_or_else(|e| { println!("{:?}", e); panic!() });
let obj = obj.unwrap();
match obj {
Node(x) => {
let NodeId(id) = x.id;
nodes.insert(i64::from(id), MyNode::new(x, 0));
nodes.insert(id, MyNode::new(x, 0));
}
Way(x) => {
if x.tags.contains_key("highway") &&
allowed_roads_car.contains_key::<str>(x.tags["highway"].to_lowercase().as_ref())
if x.tags.contains_key("highway")
&& allowed_roads_car
.contains_key::<str>(x.tags["highway"].to_lowercase().as_ref())
{
let mut cur_node = x.nodes[0];
for node in x.nodes.iter().skip(1) {
......@@ -79,35 +80,48 @@ pub fn preprocess(filename: &str) {
//Calculate weight
//Weight is duration in ms
let distance = haversine(actual_node.get_coords(), actual_cur_node.get_coords()); // in km
let distance =
haversine(actual_node.get_coords(), actual_cur_node.get_coords()); // in km
if distance.is_nan() {
panic!("Distance is NaN!");
}
let speed = process_maxspeed_tag(&x); // in km/h
let weight = (distance / speed * 3_600_000.0) as usize;
let weight = (distance / speed * 3_600_000.0) as usize; // ms
let is_one_way = if x.tags.contains_key("oneway") {
let lwr_tag = x.tags["oneway"].to_lowercase();
lwr_tag == "yes" || lwr_tag == "true"
}
else { false };
} else {
false
};
// Create edge between cur_node and node
edges.push(MyEdge::new(cur_node_id as usize, *node_id as usize, weight, !is_one_way));
edges.push(MyEdge::new(
cur_node_id as usize,
*node_id as usize,
weight,
!is_one_way,
));
// Update cur_node
cur_node = node.clone();
cur_node = *node;
}
}
}
_ => ()
_ => (),
};
}
println!("{} edges.\r\nRemoving unused nodes...\t", edges.len());
let all_nodes_count = nodes.len();
nodes.retain(|_k, v| { v.is_used });
nodes.retain(|_k, v| v.is_used);
let remaining_nodes_count = nodes.len();
let removed_percent = 100.0 * (1.0 - (remaining_nodes_count as f64 / all_nodes_count as f64));
println!("Done.\r\n{}% of nodes removed. {} remaining.\r\nMoving nodes...", removed_percent, remaining_nodes_count);
let removed_percent =
100.0 * (1.0 - (remaining_nodes_count as f64 / all_nodes_count as f64));
println!(
"Done.\r\n{}% of nodes removed. {} remaining.\r\nMoving nodes...",
removed_percent, remaining_nodes_count
);
println!("{} edges.", edges.len());
nodes.iter_mut().enumerate().for_each(|(i, (_, v))| {
v.id = i;
});
......@@ -117,8 +131,8 @@ pub fn preprocess(filename: &str) {
e.end_node = nodes[&(e.end_node as i64)].id;
}
println!("Done.\r\nSaving nodes...");
process_node_tree(nodes.iter().map(|(_k, v)| { v.clone() }).collect(), filename);
process_node_tree(nodes.values(), filename);
println!("Done. Generating graph...");
for e in edges {
input_graph.add_edge(e.start_node, e.end_node, e.weight);
......@@ -132,20 +146,26 @@ pub fn preprocess(filename: &str) {
println!("Calculating optimized graph...");
let fast_graph = fast_paths::prepare(&input_graph);
println!("Done.\r\nSaving graph...\t");
fast_paths::save_to_disk(&fast_graph, format!("{}.ff", filename).as_ref()).unwrap();
let mut f = BufWriter::new(File::create(format!("{}.ff", filename)).unwrap());
bincode::serialize_into(&mut f, &fast_graph).unwrap();
println!("Done.");
}
/// Creates an R-tree from a vector of nodes and saves it.
fn process_node_tree(nodes: Vec<MyNode>, filename: &str) {
let mapped_nodes = nodes.iter().map(|x| {
MyRTreeNode::new(x.id, [x.decimicro_lat, x.decimicro_lon])
}).collect();
fn process_node_tree<'a, I>(nodes: I, filename: &str)
where
I: Iterator<Item = &'a MyNode>,
{
let mapped_nodes = nodes
.map(|x| MyRTreeNode::new(x.id, [x.lat, x.lon]))
.collect();
let tree: RTree<MyRTreeNode> = RTree::bulk_load(mapped_nodes);
let mut buffer = BufWriter::new(File::create(format!("{}.nodes", filename)).unwrap());
buffer.write_all(&bincode::serialize(&tree).unwrap()).unwrap();
buffer
.write_all(&bincode::serialize(&tree).unwrap())
.unwrap();
buffer.flush().unwrap();
}
......@@ -163,11 +183,10 @@ fn process_maxspeed_tag(way: &osmpbfreader::objects::Way) -> f64 {
50.0
}
}
}
else {
} else {
match max_speed.parse::<f64>() {
Ok(x) => x,
Err(_) =>{
Err(_) => {
println!("Could not parse {}, defaulting to 50", max_speed);
50.0
}
......
......@@ -4,19 +4,20 @@ use fast_paths::FastGraph;
use rstar::RTree;
use serde::Serialize;
use serde::Deserialize;
use serde::Serialize;
use std::fs::File;
use std::io::prelude::*;
use std::sync::RwLock;
use std::io::BufReader;
use std::sync::Arc;
use std::sync::RwLock;
use warp::{Filter};
use warp::Filter;
struct DataStore {
node_tree: RTree<MyRTreeNode>,
fast_graph: FastGraph
fast_graph: FastGraph,
}
pub async fn serve(filename: &str) {
......@@ -24,18 +25,22 @@ pub async fn serve(filename: &str) {
println!("Loading tree...");
let tree: RTree<MyRTreeNode> = {
let mut buffer = Vec::new();
File::open(format!("{}.nodes", filename)).unwrap().read_to_end(&mut buffer).unwrap();
bincode::deserialize(&mut buffer).unwrap()
File::open(format!("{}.nodes", filename))
.unwrap()
.read_to_end(&mut buffer)
.unwrap();
bincode::deserialize(&buffer).unwrap()
};
println!("Done.\r\nLoading graph...");
let graph = fast_paths::load_from_disk(format!("{}.ff", filename).as_ref()).unwrap();
let f = BufReader::new(File::open(format!("{}.ff", filename)).unwrap());
let graph = bincode::deserialize_from(f).unwrap();
println!("Done.");
let data_store_mutex = Arc::new(RwLock::new(DataStore {
node_tree: tree,
fast_graph: graph
fast_graph: graph,
}));
let post_table_endpoint = warp::post()
......@@ -54,43 +59,53 @@ pub async fn serve(filename: &str) {
#[derive(Serialize, Deserialize, Debug)]
struct NodeRequest {
latitude: f64,
longitude: f64
latitude: f32,
longitude: f32,
}
impl NodeRequest {
fn as_arr(&self) -> [i32; 2] {
[(self.latitude * 1E7) as i32, (self.longitude * 1E7) as i32]
fn as_arr(&self) -> [f32; 2] {
[self.latitude, self.longitude]
}
}
#[derive(Serialize, Deserialize)]
struct TableRequest {
source: NodeRequest,
dests: Vec<NodeRequest>
dests: Vec<NodeRequest>,
}
#[derive(Serialize, Deserialize)]
struct TableResponse {
times: Vec<Option<usize>>
times: Vec<Option<usize>>,
}
async fn post_table(req: TableRequest, store: Arc<RwLock<DataStore>>) -> Result<impl warp::Reply, warp::Rejection> {
async fn post_table(
req: TableRequest,
store: Arc<RwLock<DataStore>>,
) -> Result<impl warp::Reply, warp::Rejection> {
let store = &store.read().unwrap();
let tree = &store.node_tree;
let graph = &store.fast_graph;
// TODO only calculate this once
let mut path_calculator = fast_paths::create_calculator(&graph);
let source_node_id = tree.nearest_neighbor(&req.source.as_arr()).unwrap().data;
let dest_node_ids: Vec<usize> = req.dests.iter().map(|x| { tree.nearest_neighbor(&x.as_arr()).unwrap().data }).collect();
let weights: Vec<Option<usize>> = dest_node_ids.iter().map(|x| {
match path_calculator.calc_path(&graph, source_node_id, *x) {
Some(y) => Some(y.get_weight()),
None => None
}
}).collect();
Ok(warp::reply::json(&TableResponse{ times: weights }))
let dest_node_ids: Vec<usize> = req
.dests
.iter()
.map(|x| tree.nearest_neighbor_iter(&x.as_arr()).next().unwrap().data)
.collect();
let weights: Vec<Option<usize>> = dest_node_ids
.iter()
.map(|x| {
path_calculator
.calc_path(&graph, source_node_id, *x)
.map(|y| y.get_weight())
})
.collect();
Ok(warp::reply::json(&TableResponse { times: weights }))
}
/*
......
......@@ -4,19 +4,24 @@ pub struct MyEdge {
pub start_node: usize,
pub end_node: usize,
pub weight: usize,
pub two_way: bool
pub two_way: bool,
}
impl MyEdge {
pub fn new(start_node: usize, end_node: usize, weight: usize, two_way: bool) -> Self {
Self { start_node, end_node, weight, two_way }
Self {
start_node,
end_node,