diff --git a/.gitignore b/.gitignore index 5eb854d..ea8c4bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ /target -/landmarks diff --git a/Cargo.lock b/Cargo.lock index a425b74..18d4047 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -146,6 +146,24 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" + [[package]] name = "byteorder" version = "1.4.3" @@ -158,6 +176,12 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.0.73" @@ -185,6 +209,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "bitflags", + "textwrap 0.11.0", + "unicode-width", +] + [[package]] name = "clap" version = "3.2.16" @@ -199,7 +234,7 @@ dependencies = [ "once_cell", "strsim", "termcolor", - "textwrap", + "textwrap 0.15.0", ] [[package]] @@ -260,6 +295,87 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "criterion" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f" +dependencies = [ + "atty", + "cast", + "clap 2.34.0", + "criterion-plot", + "csv", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2dd04ddaf88237dc3b8d8f9a3c1004b506b54b3313403944054d23c0870c521" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "715e8152b692bba2d374b53d4875445368fdf21a94751410af607a5ac677d1fc" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "crossbeam-utils", + "memoffset", + "once_cell", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -270,6 +386,28 @@ dependencies = [ "typenum", ] +[[package]] +name = "csv" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +dependencies = [ + "bstr", + "csv-core", + "itoa 0.4.8", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + [[package]] name = "ctr" version = "0.8.0" @@ -343,7 +481,8 @@ name = "fapra_osm_2" version = "0.1.0" dependencies = [ "bincode", - "clap", + "clap 3.2.16", + "criterion", "geojson", "handlebars", "osmpbfreader", @@ -622,6 +761,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + [[package]] name = "handlebars" version = "4.3.3" @@ -684,7 +829,7 @@ checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" dependencies = [ "bytes", "fnv", - "itoa", + "itoa 1.0.3", ] [[package]] @@ -725,7 +870,7 @@ dependencies = [ "http-body", "httparse", "httpdate", - "itoa", + "itoa 1.0.3", "pin-project-lite", "socket2", "tokio", @@ -789,12 +934,36 @@ dependencies = [ "libc", ] +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + [[package]] name = "itoa" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +[[package]] +name = "js-sys" +version = "0.3.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "kernel32-sys" version = "0.2.2" @@ -872,6 +1041,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "mime" version = "0.3.16" @@ -1034,6 +1212,12 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + [[package]] name = "opaque-debug" version = "0.3.0" @@ -1184,6 +1368,34 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "plotters" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9428003b84df1496fb9d6eeee9c5f8145cb41ca375eb0dad204328888832811f" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" + +[[package]] +name = "plotters-svg" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0918736323d1baff32ee0eade54984f6f201ad7e97d5cfb5d6ab4a358529615" +dependencies = [ + "plotters-backend", +] + [[package]] name = "polyval" version = "0.5.3" @@ -1318,6 +1530,30 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rayon" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -1523,6 +1759,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + [[package]] name = "serde_derive" version = "1.0.143" @@ -1540,7 +1786,7 @@ version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" dependencies = [ - "itoa", + "itoa 1.0.3", "ryu", "serde", ] @@ -1696,6 +1942,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + [[package]] name = "textwrap" version = "0.15.0" @@ -1737,7 +1992,7 @@ version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db76ff9fa4b1458b3c7f077f3ff9887394058460d21e634355b273aaf11eea45" dependencies = [ - "itoa", + "itoa 1.0.3", "libc", "num_threads", "time-macros", @@ -1749,6 +2004,16 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tokio" version = "1.20.1" @@ -1925,6 +2190,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + [[package]] name = "unicode-xid" version = "0.2.3" @@ -1980,6 +2251,70 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" + +[[package]] +name = "web-sys" +version = "0.3.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed055ab27f941423197eb86b2035720b1a3ce40504df082cac2ecc6ed73335a1" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.2.8" diff --git a/Cargo.toml b/Cargo.toml index 72036b5..966732a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,10 @@ serde = {version="1.0", features=["derive"]} serde_json = "1.0" bincode = "1.3.3" osmpbfreader = "0.15.2" + +[dev-dependencies] +criterion = "0.3.6" + +[[bench]] +name = "task5" +harness = false diff --git a/README.md b/README.md deleted file mode 100644 index 3405b2b..0000000 --- a/README.md +++ /dev/null @@ -1,171 +0,0 @@ -# FaPra Algorithms on OSM Data - -This repository contains implementations for the Fachpraktikum Algorithms on -OSM Data. The code is written in Rust, a stable rust compiler and `cargo`, the -rust build tool/package manager is required. - -# Building - -simply run `cargo build --release` to build release versions of all binaries. - -# Tasks - -## Task 1 - -There is no real implementation work for task 1, next! - -## Task 2 + Task 3 + Task 4 - -Reading the data from an OSM PDF file and converting it to a graph is done in -`src/bin/generate_grid.rs`. - -The implementation of the spherical point in polygon test is done in `src/polygon.rs` -with the function `Polygon::contains`. - -There is one polygon in the graph, for which no valid outside polygon can be found. -I did not have the time to investigate this further. - -### Extracting Coastlines from the PBF file - -The code uses the osmpbfreader crate. -Sadly this module uses ~10GB of memory to extract the data from the PBF file -with all the coastlines. - -### Point in Polygon - -The test by Bevis and Chatelain is implemented. -Instead of using a point that is inside the polygon a point outside of the -polygon is used, because here we can simply specify several "well-known" outside -points that are somewhere in the ocean and therefore are outside of every polygon. - -### Grid Graph - -The Grid Graph is implemented in `gridgraph.rs`. - -A regular grid graph can be generated with the `generate_regular_grid` function. -Import and Export from/to a file can be done with the `from_fmi_file` and `write_fmi_file` functions. - -## Task 5 - -### Dijkstra Benchmarks - -Dijkstras algorithm is implenented in `gridgraph.rs` with `GridGraph::shortest_path`. -It uses a Heap to store the nodes. -For details on how to run benchmarks see the benchmarks section at the end. - -## Task 6 - -The UI is a Web-UI based on leaflet. -To start it, run `task6` with a .fmi file as a graph layout and a set of landmarks -(see Task 7 for details). - -The start and end nodes can be placed on arbitrary positions and an algorithm -searches for the closes grid node and then runs the Routing. - -The webserver listens on http://localhost:8000 - -Currently there is a display bug when routes wrap around the globe at 180 degrees -longitude. Instead of continuing the line as expected the line is drawn around -the globe in the "wrong" direction. -This is however just a display bug, the route itself is correct. - -## Task 7 - -I implemented ALT, as described in [1]. -Additionally A\* is available with a simple, unoptimized haversine distance -as the heuristic. - -A\* is implemented in `src/astar.rs` and the heuristics for ALT are implemented -in `src/alt.rs`. - -### Landmarks for ALT - -currently 3 different landmark generation methods are available - -- random selection -- greedy, distance-maximizing selection -- manual selection (from a GeoJSON file) - -These can be generated with the `gen_landmarks_random`, `gen_landmarks_greedy` -and `gen_landmarks_geojson` binaries. The random and greedy methods take -the number of landmarks to select from a parameter. - -A handy wrapper for that can be found as a Python script in `utils/generate_landmarks.py` that -generates landmarks for 4, 8, 16, 32 and 64 landmarks, both greedy and random. - -# Running the benchmarks - -First a set of queries is needed. -These can be generated with `generate_benchmark_targets --graph > targets.json`. -This generates 1000 random, distinct source and destination pairs. -The `--amount` parameter allows to adjust the number of pairs generated. - -The actual benchmark is located in `benchmark`. -It needs a set of queries and a graph. - -If the `--dijkstra` option is given it runs Dijkstras algorithm. -If the `--astar` option is given it runs A\* with the haversine distance -If `--landmarks ` is given it runs ALT with that landmark file. -By setting `--alt_best_size ` one can select how many of the landmarks -are used to answer the query. - -The benchmark prints out how many nodes were popped from the heap for -each run and the average time per route. - -`utils/run_benchmarks.py` is a wrapper script that runs the benchmarks for a -big set of parameters. - -`utils/plot_results.py` generates several plots of the results. - -# Results - -These are some quick tests, further results will be presented later. -Everything was run on a Thinkpad X260 laptop with an Intel i7-6600U CPU @ 2.60GHz -processor. -Each test used the same 1000 queries. -Rust v1.57.0 was used for all tests. - -The ALT variants were used with the 4 best landmarks. -Further tests on the performance of more landmarks will be presented laster. -The set of 44 handpicked landmarks were spread around the extremeties of the -continents and into "dead ends" like the Mediteranean and the Gulf of Mexico -with the goal to provide landmarks that are "behind" the source or target -node. - -All benchmarks were run on the provided benchmark graph. - -## raw data: -``` -# name, (avg. heap pops per query, avg. time) -{'astar': (155019.451, 0.044386497025), - 'dijkstra': (423046.796, 0.058129875474999995), - 'greedy_32': (42514.751, 0.013299024275000002), - 'greedy_64': (35820.461, 0.011887869759), - 'handpicked_44': (70868.721, 0.01821366828), - 'random_32': (58830.082, 0.016845884717), - 'random_64': (51952.261, 0.015234422699)} -``` - -## Interpretation - -Dijkstra needs ~58ms per route, while the best version is greedy\_64 (that is -with 64 landmarks) needs only 12 seconds, which is ~5 times faster. -We also see, that the greedy versions perform slightly better than their -random counterparts with the same amount of nodes. -While the 44 handpicked landmarks outperformed A\* and Dijkstra, they are beaten -by both the random and greedy landmark selections which had fewer nodes. - -## Memory Consumption - -The landmarks are basically arrays of the cost to each node. -Since the distances are currently calculates with 64 bit integers -each landmark needs 8 byte per node in the graph. -With a graph that has about 700k nodes this leads to ~5.5MB of memory per -landmark. -So 64 landmarks need ~350MB of memory. - -One could also use 32 bit integers which would half the memory requirements. - -# References - -[1](Computing the Shortest Path: A\* meets Graph Theory, A. Goldberg and C. Harrelson, Microsoft Research, Technical Report MSR-TR-2004-24, 2004) diff --git a/grids/grid_3x3.fmi b/grids/grid_3x3.fmi deleted file mode 100644 index ac0a31f..0000000 --- a/grids/grid_3x3.fmi +++ /dev/null @@ -1,25 +0,0 @@ -7 -16 -0 -90 180 -1 -90 -60.00000000000001 -2 -90 59.999999999999986 -3 -30.000000000000004 180 -4 -30.000000000000004 59.999999999999986 -5 29.999999999999993 180 -6 29.999999999999993 -60.00000000000001 -0 2 0 -0 1 0 -0 3 6671695 -1 0 0 -1 2 0 -2 1 0 -2 0 0 -2 4 6671695 -3 4 10806007 -3 5 6671695 -3 0 6671695 -4 3 10806007 -4 2 6671695 -5 6 10806007 -5 3 6671695 -6 5 10806007 diff --git a/landmarks/ocean_handpicked_44.json b/landmarks/ocean_handpicked_44.json deleted file mode 100644 index 90daa82..0000000 --- a/landmarks/ocean_handpicked_44.json +++ /dev/null @@ -1,489 +0,0 @@ -{ - "type": "FeatureCollection", - "features": [ - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - -68.5546875, - -56.237244700410315 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 19.84130859375, - -35.281500657891186 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 146.5576171875, - -44.18220395771564 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 168.4423828125, - -47.75409797968002 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - -18.6328125, - 17.97873309555617 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - -33.046875, - -6.315298538330033 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - -83.84765625, - -3.513421045640032 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - -33.57421875, - 83.81102365639774 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 21.62109375, - 80.64703474739618 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 103.974609375, - 77.89725517594933 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 191.42578125, - 65.94647177615738 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 26.894531249999996, - 71.46912418989677 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 68.115234375, - 77.07878389624943 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - -43.681640625, - 59.265880628258095 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - -90.439453125, - 25.085598897064752 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - -126.38671874999999, - 40.17887331434696 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - -155.7421875, - 71.74643171904148 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - -160.02685546875, - 54.380557368630654 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - -93.42773437499999, - 74.3785127963314 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 157.236328125, - 50.84757295365389 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 134.033203125, - 39.30029918615029 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 111.884765625, - 8.146242825034385 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 129.638671875, - -5.878332109674314 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 58.798828125, - 18.145851771694467 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 31.289062500000004, - 33.32134852669881 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 34.453125, - 43.16512263158296 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - -9.9755859375, - 38.90813299596705 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 1.5380859375, - 51.01375465718821 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 13.2275390625, - 54.901882187385006 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 80.7275390625, - 5.528510525692801 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 119.99267578124999, - 24.647017162630366 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 154.51171875, - -28.613459424004414 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 111.4453125, - -26.03704188651583 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 142.55859375, - 74.44935750063425 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - -42.1875, - 29.53522956294847 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 150.46875, - 20.96143961409684 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - -18.984375, - -44.840290651397986 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 76.9921875, - -21.616579336740593 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - -111.09374999999999, - -32.54681317351514 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - -20.0390625, - 60.23981116999893 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - -55.37109374999999, - -62.226996036319726 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 25.6640625, - -69.41124235697255 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - 122.6953125, - -65.21989393613208 - ] - } - }, - { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - -122.6953125, - -72.81607371878991 - ] - } - } - ] -} diff --git a/routes/10x10_1.json b/routes/10x10_1.json deleted file mode 100644 index 7d08745..0000000 --- a/routes/10x10_1.json +++ /dev/null @@ -1 +0,0 @@ -[{"source":51,"destination":11}] diff --git a/routes/3x3_1.json b/routes/3x3_1.json deleted file mode 100644 index 7f74b99..0000000 --- a/routes/3x3_1.json +++ /dev/null @@ -1 +0,0 @@ -[{"source":2,"destination":5}] diff --git a/src/alt.rs b/src/alt.rs index 30aac24..1050da4 100644 --- a/src/alt.rs +++ b/src/alt.rs @@ -1,64 +1,83 @@ use crate::gridgraph::{EdgeCost, GraphNode, GridGraph, NodeId}; use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; use std::collections::BinaryHeap; -use crate::utils::DijkstraElement; -/// a single Landmark #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Landmark { pub node: GraphNode, pub distances: Vec, } - -/// A set of Landmarks #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct LandmarkSet { pub landmarks: Vec, -} - - -/// The LandmarkBestSet is the datastructure in which the indices of the -/// best landmarks for a certain query are stored. -#[derive(Debug, Clone)] -pub struct LandmarkBestSet<'a> { - pub landmark_set: &'a LandmarkSet, pub best_size: usize, best_landmarks: Vec, } impl Landmark { - - /// generates a landmark (calculates all distances) for a given node. pub fn generate(node: GraphNode, graph: &GridGraph) -> Landmark { - - // This is running a simplified version of dijkstra. - // It also does not track the ancestors of a node, because it is - // not needed for generating hte landmarks. - let mut landmark = Landmark { node, distances: vec![EdgeCost::MAX; graph.nodes.len()], }; landmark.node = node; + #[derive(Eq)] + struct DijkstraElement { + index: u32, + cost: EdgeCost, + } + + impl Ord for DijkstraElement { + // inverted cmp function, such that the Max-Heap provided by Rust + // can be used as a Min-Heap + fn cmp(&self, other: &Self) -> Ordering { + other.cost.cmp(&self.cost) + } + } + + impl PartialOrd for DijkstraElement { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } + } + + impl PartialEq for DijkstraElement { + fn eq(&self, other: &Self) -> bool { + self.cost == other.cost + } + } + let mut heap = BinaryHeap::new(); heap.push(DijkstraElement { cost: 0, index: landmark.node.index, }); + let mut counter = 0; + while let Some(DijkstraElement { index, cost }) = heap.pop() { // the heap does not support "update" operations, so we // insert elements again and if they come out of the heap but have // been processed earlier we simply skip them. - if cost > landmark.distances[index as usize] { + if landmark.distances[index as usize] <= cost { continue; }; + counter += 1; + + if counter % 1000 == 0 { + println!("Finished {} nodes", counter); + } + landmark.distances[index as usize] = cost; - for edge in graph.get_edges(index as NodeId) { + let edge_start = graph.edge_offsets[index as usize] as usize; + let edge_end = graph.edge_offsets[(index + 1) as usize] as usize; + + for edge in graph.edges[edge_start..edge_end].iter() { let new_cost = cost + edge.cost; if new_cost < landmark.distances[edge.neighbor as usize] { @@ -67,11 +86,11 @@ impl Landmark { index: edge.neighbor, cost: new_cost, }); - landmark.distances[edge.neighbor as usize] = new_cost; } } } - // now the costs to all reachable nodes is calculated. + + // now the shortest paths to all reachable nodes is calculated. landmark } @@ -79,28 +98,23 @@ impl Landmark { /// calculates the lower-bounding distance estimate between the 2 nodes /// via the landmark. /// If one or more of the nodes are not reachable from the landmark - /// an estimate of `EdgeCost::MAX` is returned. + /// an estimate of `0` is returned. pub fn estimate(&self, from: NodeId, to: NodeId) -> EdgeCost { let l_to = self.distances[to]; let l_from = self.distances[from]; - if l_to == EdgeCost::MAX || l_from == EdgeCost::MAX { - EdgeCost::MAX + if l_to == EdgeCost::MAX { + 0 } else { - // since we are working on an undirected graph we can - // use the distances once from and once to the landmark. - // This leads to l_to - l_from and l_from - l_to (as signed subtractions) - // which except for the sign are the same value. - // We can simply take the bigger one, which is handled - // nicely the abs() function - (l_to as i64 - l_from as i64).abs() as EdgeCost + l_to.saturating_sub(l_from) } } } impl LandmarkSet { - pub fn random_set(size: usize, graph: &GridGraph) -> Self { + pub fn random_set(size: usize, best_size: usize, graph: &GridGraph) -> Self { let mut set = LandmarkSet::default(); + set.best_size = best_size; let nodes = graph.get_random_nodes(size); @@ -111,13 +125,11 @@ impl LandmarkSet { set } -} -impl LandmarkBestSet<'_> { pub fn select_best(&mut self, from: NodeId, to: NodeId) { let mut results = vec![]; - for (index, landmark) in self.landmark_set.landmarks.iter().enumerate() { + for (index, landmark) in self.landmarks.iter().enumerate() { results.push((index, landmark.estimate(from, to))); } @@ -125,35 +137,24 @@ impl LandmarkBestSet<'_> { results.reverse(); self.best_landmarks.clear(); - - for result in results[..(self.best_size.min(results.len()))].iter() { + for result in results[..self.best_size].iter() { self.best_landmarks.push(result.0); } } pub fn estimate(&self, from: NodeId, to: NodeId) -> EdgeCost { + let mut distance = 0; for index in &self.best_landmarks { - let candidate = self.landmark_set.landmarks[*index].estimate(from, to); + distance = distance.max(self.landmarks[*index].estimate(from, to)); + }; - if candidate == EdgeCost::MAX { - continue; - } - - distance = distance.max(candidate) + if distance == 0 { + distance = EdgeCost::MAX; } - //println!("calculated estimate {:?} for {} to {}", distance, from, to); - distance - } - pub fn new<'a>(best_size: usize, landmark_set: &'a LandmarkSet) -> LandmarkBestSet<'a> { - LandmarkBestSet { - best_size, - landmark_set, - best_landmarks: Vec::new(), - } } } diff --git a/src/astar.rs b/src/astar.rs index f919459..d5e78a1 100644 --- a/src/astar.rs +++ b/src/astar.rs @@ -4,27 +4,22 @@ use crate::utils::EARTH_RADIUS; use std::cmp::Ordering; use std::collections::BinaryHeap; - -/// datastructure to hold data required by the A* algorithm -pub struct AStar<'a> { - pub graph: &'a GridGraph, +pub struct AStar { + pub graph: GridGraph, } -/// An element on the Heap for A*. -/// The Ord and PartialOrd Traits are inverted so that the MaxHeap works as a -/// MinHeap. -/// index is the index of the Node, cost is the cost which the heap sorts after. -/// path_cost is the actual cost of the path to that node. -#[derive(Eq, PartialEq)] +#[derive(Eq)] struct HeapElement { index: u32, - - // the cost so far plus the estimated cost until we reach the destination - cost: EdgeCost, + cost: EdgeCost, // the cost so far plus the estimated cost until we reach the + // destination path_cost: EdgeCost, // the cost to reach this node from the start node + ancestor: Option, } impl Ord for HeapElement { + // inverted cmp function, such that the Max-Heap provided by Rust + // can be used as a Min-Heap fn cmp(&self, other: &Self) -> Ordering { other.cost.cmp(&self.cost) } @@ -36,84 +31,80 @@ impl PartialOrd for HeapElement { } } -/// A simple haversine distance heuristic. +impl PartialEq for HeapElement { + fn eq(&self, other: &Self) -> bool { + self.cost == other.cost + } +} + pub fn estimate_haversine(node: &GraphNode, destination: &GraphNode) -> EdgeCost { // simple haversine distance (node.position.distance_to(&destination.position) * EARTH_RADIUS) as EdgeCost + // let lat_dist_a = (node.position.lat - destination.position.lat).abs(); + // let lat_dist_b = (destination.position.lat - node.position.lat).abs(); + + // (lat_dist_a.min(lat_dist_b) * EARTH_RADIUS) as EdgeCost } -/// a simple heuristic based on the difference in lattitude between two points -/// The idea is that it is cheaper to calculate than the haversine distance. pub fn estimate_latitude(node: &GraphNode, destination: &GraphNode) -> EdgeCost { - let lat_dist = (node.position.lat - destination.position.lat).abs(); + let lat_dist_a = (node.position.lat - destination.position.lat).abs(); + let lat_dist_b = (destination.position.lat - node.position.lat).abs(); - (lat_dist * EARTH_RADIUS) as EdgeCost + (lat_dist_a.min(lat_dist_b) * EARTH_RADIUS) as EdgeCost } +impl AStar { -impl AStar<'_> { - - /// calculates the shortest path from start to end given the `estimate` - /// heuristic function. - /// - /// Returns `None` if no path exists. pub fn shortest_path(&self, start: &GraphNode, end: &GraphNode, estimate: F) -> Option - where - F: Fn(&GraphNode, &GraphNode) -> EdgeCost, - { + where F: Fn(&GraphNode, &GraphNode) -> EdgeCost { let mut heap = BinaryHeap::new(); heap.push(HeapElement { cost: estimate(start, end), path_cost: 0, index: start.index, + ancestor: None, }); let mut distance = vec![EdgeCost::MAX; self.graph.nodes.len()]; let mut ancestor: Vec> = vec![None; self.graph.nodes.len()]; - distance[start.index as usize] = 0; - let mut popcount = 0; while let Some(HeapElement { - index, path_cost, .. + index, + cost, // the cost value, no longer needed, because it is only important for the + // distance estimate + path_cost, + ancestor: prev, }) = heap.pop() { - //println!("working on node {} with cost {} and path cost {}", index, cost, path_cost); - popcount += 1; // the heap does not support "update" operations, so we // insert elements again and if they come out of the heap but have // been processed earlier (which implies a shorter distance) // we simply skip them. - if path_cost > distance[index as usize] { + if distance[index as usize] <= path_cost { continue; }; + popcount += 1; + + distance[index as usize] = path_cost; + ancestor[index as usize] = prev; if index == end.index { break; } for edge in self.graph.get_edges(index as usize).iter() { - //println!("working on edge {:?}", edge); let new_cost = path_cost + edge.cost; if new_cost < distance[edge.neighbor as usize] { //println!("adding new element to heap"); - let remaining_cost = estimate(&self.graph.nodes[edge.neighbor as usize], end); - - // check for unreachable nodes - if remaining_cost == EdgeCost::MAX { - continue; - } - - distance[edge.neighbor as usize] = new_cost; - ancestor[edge.neighbor as usize] = Some(index); - - heap.push(HeapElement { + heap.push(HeapElement{ index: edge.neighbor, - cost: new_cost + remaining_cost, + cost: new_cost + estimate(&self.graph.nodes[edge.neighbor as usize], end), path_cost: new_cost, + ancestor: Some(index), }); } } @@ -121,6 +112,26 @@ impl AStar<'_> { println!("popped {} elements from the heap", popcount); - Route::construct(self.graph, &ancestor, &distance, start, end) + // now the route calculation is done. If a route exist we can construct + // it from the ancestors. + if ancestor[end.index as usize].is_some() { + let mut route = Route { + cost: distance[end.index as usize], + nodes: Vec::new(), + }; + + let mut current = end.index; + while current != start.index { + route.nodes.push(self.graph.nodes[current as usize].position); + current = ancestor[current as usize].unwrap(); + } + route.nodes.push(self.graph.nodes[current as usize].position); + + route.nodes.reverse(); + + return Some(route); + } + + None } } diff --git a/src/bin/gen_landmarks_geojson.rs b/src/bin/gen_landmarks_geojson.rs deleted file mode 100644 index 32c8fe0..0000000 --- a/src/bin/gen_landmarks_geojson.rs +++ /dev/null @@ -1,94 +0,0 @@ -use bincode; -use clap::Parser; -use fapra_osm_2::alt::{Landmark, LandmarkSet}; -use fapra_osm_2::utils::load_graph; -use fapra_osm_2::coordinates::{RadianCoordinate, DegreeCoordinate}; -use std::fs::{read_to_string, File}; -use std::io::prelude::*; -use std::process::exit; -use geojson::{GeoJson, Value}; - -#[derive(Parser, Debug)] -#[clap(author, version, about, long_about=None)] -struct Args { - /// the FMI file to load - #[clap(short, long)] - graph: String, - - /// the file to which to write the landmarks - #[clap(short, long)] - output: String, - - /// the geojson file from which to load the landmarks - #[clap(short, long)] - geojson: String, -} - -fn main() { - let args = Args::parse(); - - let graph = load_graph(&args.graph); - - let mut output = match File::create(args.output.clone()) { - Ok(f) => f, - Err(e) => { - println!("Error while creating the file {}: {}", args.output, e); - exit(2) - } - }; - - let geojson_str = match read_to_string(args.geojson.clone()) { - Ok(f) => f, - Err(e) => { - println!("Error while opening the file {}: {}", args.geojson, e); - exit(2) - } - }; - - let geojson: GeoJson = geojson_str.parse::().unwrap(); - - let features = match geojson { - GeoJson::FeatureCollection(features) => features.features, - _ => { - println!("Expected a FeatureCollection, didn't get one!"); - exit(3); - } - }; - - - // parse the GeoJSON - let mut points: Vec = Vec::new(); - - for feature in features.iter() { - if let Some(geometry) = &feature.geometry { - if let Value::Point(point) = &geometry.value { - if let Ok(coordinate) = DegreeCoordinate::from_geojson_position(point.clone()) { - points.push(RadianCoordinate::from(coordinate)); - continue; - } - } - } - println!("failed to parse {:?}", feature); - exit(4); - } - - let mut set = LandmarkSet::default(); - - // generate the landmarks - for position in points.iter() { - if let Some(node) = graph.get_nearest_node(*position) { - let landmark = Landmark::generate(*node, &graph); - set.landmarks.push(landmark); - } else { - println!("Could not find a nearest node to {:?}", position); - exit(5); - } - } - - - let encoded = bincode::serialize(&set).unwrap(); - - output - .write_all(&encoded) - .expect("Error while writing LandmarkSet data"); -} diff --git a/src/bin/gen_landmarks_greedy.rs b/src/bin/gen_landmarks_greedy.rs deleted file mode 100644 index 9352115..0000000 --- a/src/bin/gen_landmarks_greedy.rs +++ /dev/null @@ -1,88 +0,0 @@ -use bincode; -use clap::Parser; -use fapra_osm_2::alt::{Landmark, LandmarkSet}; -use fapra_osm_2::gridgraph::EdgeCost; -use fapra_osm_2::utils::load_graph; -use std::fs::File; -use std::io::prelude::*; -use std::process::exit; - -#[derive(Parser, Debug)] -#[clap(author, version, about, long_about=None)] -struct Args { - /// the FMI file to load - #[clap(short, long)] - graph: String, - - /// the file to which to write the landmarks - #[clap(short, long)] - output: String, - - /// the amount of landmarks to generate - #[clap(short, long)] - amount: usize, -} - -fn main() { - let args = Args::parse(); - - let graph = load_graph(&args.graph); - - let mut output = match File::create(args.output.clone()) { - Ok(f) => f, - Err(e) => { - println!("Error while creating the file {}: {}", args.output, e); - exit(2) - } - }; - - fn compute_next_index(costs: &mut Vec, landmark: &Landmark) -> usize { - let mut max_index = 0; - let mut max_cost = 0; - - for (i, cost) in landmark.distances.iter().enumerate() { - if *cost < costs[i] { - costs[i] = *cost; - } - - if costs[i] > max_cost && costs[i] != EdgeCost::MAX { - max_cost = costs[i]; - max_index = i; - } - } - - max_index - } - - let initial_node = graph.get_random_node().unwrap(); - - println!("selected initial node: {:?}", initial_node); - - let mut landmark = Landmark::generate(*initial_node, &graph); - - let mut costs: Vec = landmark.distances.clone(); - - let mut set = LandmarkSet::default(); - - let mut next = compute_next_index(&mut costs, &landmark); - set.landmarks.push(landmark); - - while set.landmarks.len() < args.amount { - let node = graph.nodes[next]; - - println!( - "the next node will be {:?} with a distance of {}", - node, costs[node.index as usize] - ); - - landmark = Landmark::generate(node, &graph); - next = compute_next_index(&mut costs, &landmark); - set.landmarks.push(landmark); - } - - let encoded = bincode::serialize(&set).unwrap(); - - output - .write_all(&encoded) - .expect("Error while writing LandmarkSet data"); -} diff --git a/src/bin/generate_benchmark_targets.rs b/src/bin/generate_benchmark_targets.rs index 92e566f..d2e58b6 100644 --- a/src/bin/generate_benchmark_targets.rs +++ b/src/bin/generate_benchmark_targets.rs @@ -1,4 +1,5 @@ -use fapra_osm_2::utils::load_graph; +use fapra_osm_2::gridgraph::GridGraph; +use std::fs::File; use clap::Parser; use std::process::exit; use rand::seq::SliceRandom; @@ -20,7 +21,21 @@ struct Args { fn main() { let args = Args::parse(); - let graph = load_graph(&args.graph); + let file = match File::open(args.graph.clone()) { + Ok(f) => f, + Err(e) => { + println!("Error while opening the file {}: {}", args.graph, e); + exit(1) + } + }; + + let graph = match GridGraph::from_fmi_file(file) { + Ok(g) => g, + Err(e) => { + println!("Error while reading the graph: {:?}", e); + exit(1); + } + }; let mut rng = rand::thread_rng(); diff --git a/src/bin/generate_grid.rs b/src/bin/generate_grid.rs index a8dd190..3478bf0 100644 --- a/src/bin/generate_grid.rs +++ b/src/bin/generate_grid.rs @@ -15,15 +15,6 @@ struct Args { /// the output file to write the grid to #[clap(short, long)] output: String, - - - /// the latitude resolution - #[clap(short, long)] - lat: usize, - - /// the longitude resolution - #[clap(short, long)] - lon: usize, } fn main() { @@ -56,7 +47,7 @@ fn main() { println!("{:?}", coasts.polygons[0].bbox); - let grid = GridGraph::generate_regular_grid(args.lat, args.lon, Some(&coasts)); + let grid = GridGraph::generate_regular_grid(10, 10, Some(&coasts)); // let grid = GridGraph::generate_regular_grid(3, 4, None); match grid.write_fmi_file(output) { diff --git a/src/bin/grid_to_geojson.rs b/src/bin/grid_to_geojson.rs index ea78b91..e6fc2f6 100644 --- a/src/bin/grid_to_geojson.rs +++ b/src/bin/grid_to_geojson.rs @@ -1,18 +1,28 @@ -use fapra_osm_2::utils::load_graph; +use fapra_osm_2::gridgraph::GridGraph; use clap::Parser; +use std::fs::File; +use std::process::exit; #[derive(Parser, Debug)] #[clap(author, version, about, long_about=None)] struct Args { /// the FMI file to load #[clap(short, long)] - graph: String, + input: String, } fn main() { let args = Args::parse(); - let graph = load_graph(&args.graph); + let file = match File::open(args.input.clone()) { + Ok(f) => f, + Err(e) => { + println!("Error while opening file: {}", e); + exit(1); + } + }; - println!("{}", graph.to_geojson().to_string()); + let grid = GridGraph::from_fmi_file(file).unwrap(); + + println!("{}", grid.to_geojson().to_string()); } diff --git a/src/bin/performance.rs b/src/bin/performance.rs index a284fa7..e84ba38 100644 --- a/src/bin/performance.rs +++ b/src/bin/performance.rs @@ -1,8 +1,8 @@ use clap::Parser; -use fapra_osm_2::alt::LandmarkBestSet; -use fapra_osm_2::astar::{estimate_haversine, AStar}; -use fapra_osm_2::gridgraph::NodeId; -use fapra_osm_2::utils::{load_graph, load_landmarks, RoutingQuery}; +use fapra_osm_2::alt::LandmarkSet; +use fapra_osm_2::astar::{estimate_haversine, estimate_latitude, AStar}; +use fapra_osm_2::gridgraph::{GridGraph, NodeId}; +use fapra_osm_2::utils::RoutingQuery; use serde_json; use std::fs::File; use std::io::BufReader; @@ -20,9 +20,9 @@ struct Args { #[clap(short, long)] targets: String, - /// landmark file. ALT Benchmarks are only run, if this is given. + /// landmark file #[clap(short, long)] - landmarks: Option, + landmarks: String, /// run dijkstra #[clap(long, action)] @@ -32,17 +32,29 @@ struct Args { #[clap(long, action)] astar: bool, - /// How many of the given landmarks to select - #[clap(long, action, default_value_t = 4)] - alt_best_size: usize, + /// run ALT + #[clap(long, action)] + alt: bool, } fn main() { let args = Args::parse(); - let graph = load_graph(&args.graph); + let file = match File::open(args.graph.clone()) { + Ok(f) => f, + Err(e) => { + println!("Error while opening the file {}: {}", args.graph, e); + exit(1) + } + }; - let astar = AStar { graph: &(*graph) }; + let graph = match GridGraph::from_fmi_file(file) { + Ok(g) => g, + Err(e) => { + println!("Error while reading the graph: {:?}", e); + exit(1); + } + }; let targets = match File::open(args.targets.clone()) { Ok(f) => f, @@ -54,33 +66,23 @@ fn main() { let targets: Vec = serde_json::from_reader(BufReader::new(targets)).unwrap(); - if let Some(landmark_path) = args.landmarks { - let landmarks = load_landmarks(&landmark_path); - - println!("running ALT"); - - let mut best_landmarks = LandmarkBestSet::new(args.alt_best_size, &landmarks); - - let start = Instant::now(); - - for query in targets.iter() { - // println!("working on {:?}", query); - let source = astar.graph.nodes[query.source]; - let destination = astar.graph.nodes[query.destination]; - - best_landmarks.select_best(source.index as NodeId, destination.index as NodeId); - - let _result = astar.shortest_path(&source, &destination, |src, dest| { - best_landmarks.estimate(src.index as NodeId, dest.index as NodeId) - }); + let landmarks = match File::open(args.landmarks.clone()) { + Ok(f) => f, + Err(e) => { + println!( + "Error while opening landmark file {}: {:?}", + args.landmarks, e + ); + exit(1); } + }; - let elapsed = start.elapsed(); + let mut landmarks: LandmarkSet = bincode::deserialize_from(BufReader::new(landmarks)).unwrap(); + landmarks.best_size = 4; - let time_per_route = elapsed.as_secs_f64() / (targets.len() as f64); + let astar = AStar { graph: *graph }; - println!("It took {} seconds per route for ALT.", time_per_route); - } + println!("{:?}", args); if args.astar { println!("running A*"); @@ -107,13 +109,13 @@ fn main() { let start = Instant::now(); for query in targets.iter() { - // println!("working on {:?}", query); + println!("working on {:?}", query); let source = astar.graph.nodes[query.source]; let destination = astar.graph.nodes[query.destination]; - let _result = astar.graph.shortest_path(&source, &destination); + let result = astar.graph.shortest_path(&source, &destination); - //println!("{}", result.unwrap().to_geojson()); + println!("{}", result.unwrap().to_geojson()); } let elapsed = start.elapsed(); @@ -121,4 +123,27 @@ fn main() { let time_per_route = elapsed.as_secs_f64() / (targets.len() as f64); println!("It took {} seconds per round for Dijkstra.", time_per_route); } + + if args.alt { + println!("running ALT"); + // Landmarks + let start = Instant::now(); + + for query in targets.iter() { + let source = astar.graph.nodes[query.source]; + let destination = astar.graph.nodes[query.destination]; + + landmarks.select_best(source.index as NodeId, destination.index as NodeId); + + let _result = astar.shortest_path(&source, &destination, |src, dest| { + landmarks.estimate(src.index as NodeId, dest.index as NodeId) + }); + } + + let elapsed = start.elapsed(); + + let time_per_route = elapsed.as_secs_f64() / (targets.len() as f64); + + println!("It took {} seconds per route for ALT.", time_per_route); + } } diff --git a/src/bin/polygon_print.rs b/src/bin/polygon_print.rs new file mode 100644 index 0000000..98447d4 --- /dev/null +++ b/src/bin/polygon_print.rs @@ -0,0 +1,24 @@ +use fapra_osm_2::coordinates::RadianCoordinate; +use fapra_osm_2::polygon::Polygon; +use geojson::FeatureCollection; + + +/// a simple program to show the geojson export of polygons +fn main() { + let nodes = vec![ + RadianCoordinate::from_degrees(10.0, 15.0), + RadianCoordinate::from_degrees(20.0, 10.0), + RadianCoordinate::from_degrees(20.0, 25.0), + RadianCoordinate::from_degrees(10.0, 15.0), + ]; + + let outside1 = RadianCoordinate::from_degrees(30.0, 15.0); + let outside2 = RadianCoordinate::from_degrees(-10.0, 15.0); + + let polygon = + Polygon::build_polygon(nodes, outside1, outside2).expect("error while building polygon"); + + let fc: FeatureCollection = polygon.into(); + + println!("{}", fc.to_string()); +} diff --git a/src/bin/task6-rocket.rs b/src/bin/task6-rocket.rs index 11dbae3..b2aa5b4 100644 --- a/src/bin/task6-rocket.rs +++ b/src/bin/task6-rocket.rs @@ -1,33 +1,26 @@ -#[macro_use] -extern crate rocket; +#[macro_use] extern crate rocket; use clap::Parser; -use fapra_osm_2::alt::{LandmarkBestSet, LandmarkSet}; -use fapra_osm_2::astar::{estimate_haversine, AStar}; -use fapra_osm_2::coordinates::{DegreeCoordinate, RadianCoordinate}; -use fapra_osm_2::gridgraph::{GridGraph, NodeId, Route}; -use fapra_osm_2::utils::{load_graph, load_landmarks}; +use std::process::exit; +use std::fs::File; +use fapra_osm_2::gridgraph::{GridGraph, Route}; +use fapra_osm_2::coordinates::{RadianCoordinate, DegreeCoordinate}; use rand::seq::SliceRandom; use serde::Serialize; -use std::time::Instant; +use rocket::State; +use rocket::response::status::BadRequest; use rocket::form::Form; use rocket::fs::FileServer; -use rocket::response::status::BadRequest; use rocket::serde::json::Json; -use rocket::State; -use rocket_dyn_templates::{context, Engines, Template}; +use rocket_dyn_templates::{Template, context, Engines}; #[derive(Parser, Debug)] #[clap(author, version, about, long_about=None)] struct Args { /// name of the FMI file to read #[clap(short, long)] - graph: String, - - /// the landmarks to load - #[clap(short, long)] - landmarks: String, + filename: String, } #[get("/")] @@ -54,107 +47,73 @@ fn random_route(graphwrapper: &State) -> String { struct RouteQuery<'r> { r#from: &'r str, r#to: &'r str, - r#algorithm: &'r str, } -#[derive(Debug, Serialize)] +#[derive(Debug)] +#[derive(Serialize)] pub struct RouteResponse { success: bool, route: Option, - algorithm: String, } #[post("/route", data = "")] -fn route( - routequery: Form>, - graphwrapper: &State, -) -> Result, BadRequest> { +fn route(routequery: Form>, graphwrapper: &State) -> Result, BadRequest> { + let from = match DegreeCoordinate::from_string_tuple(routequery.from) { Ok(from) => RadianCoordinate::from(from), - Err(e) => { - return Result::Err(BadRequest(Some(format!( - "Error while parsing from field: {:?}", - e - )))); - } + Err(e) => {return Result::Err(BadRequest(Some(format!("Error while parsing from field: {:?}", e))));} }; let to = match DegreeCoordinate::from_string_tuple(routequery.to) { Ok(to) => RadianCoordinate::from(to), - Err(e) => { - return Result::Err(BadRequest(Some(format!( - "Error while parsing to field: {:?}", - e - )))); - } + Err(e) => {return Result::Err(BadRequest(Some(format!("Error while parsing to field: {:?}", e))));} }; let from = graphwrapper.graph.get_nearest_node(from).unwrap(); let to = graphwrapper.graph.get_nearest_node(to).unwrap(); - println!("Working on a route from {:?} to {:?}", from, to); + let route = graphwrapper.graph.shortest_path(from, to); - let astar = AStar { - graph: &graphwrapper.graph, - }; + println!("from: {:?}, to: {:?}", from, to); - let mut algorithm = routequery.algorithm; - - let start = Instant::now(); - - let route = if algorithm == "astar-haversine" { - println!("running A* with haversine distance"); - astar.shortest_path(from, to, estimate_haversine) - } else if algorithm == "alt" { - println!("running ALT"); - let mut best_landmarks = LandmarkBestSet::new(4, &graphwrapper.landmarks); - - best_landmarks.select_best(from.index as usize, to.index as usize); - astar.shortest_path(from, to, |src, dest| { - best_landmarks.estimate(src.index as NodeId, dest.index as NodeId) - }) - } else { - println!("running dijkstra"); - algorithm = "dijkstra"; - graphwrapper.graph.shortest_path(from, to) - }; - - let time = start.elapsed(); - - println!("The query took {} ms", time.as_millis()); - - let response = RouteResponse { - success: route.is_some(), - route, - algorithm: algorithm.to_string(), - }; + let response = RouteResponse{success: route.is_some(), route}; Ok(Json(response)) } struct GraphWrapper { - graph: GridGraph, - landmarks: LandmarkSet, + graph: Box } #[launch] fn rocket() -> _ { let args = Args::parse(); - let graph = load_graph(&args.graph); + println!("Loading file from {}", args.filename); + let file = match File::open(args.filename.clone()) { + Ok(f) => f, + Err(e) => { + println!("Error while opening the file {}: {}", args.filename, e); + exit(1) + } + }; - let landmarks = load_landmarks(&args.landmarks); + let graph = match GridGraph::from_fmi_file(file) { + Ok(g) => g, + Err(e) => { + println!("Error while reading the graph: {:?}", e); + exit(1); + } + }; - println!("Listening on http://localhost:8000"); + println!("Loaded graph file"); + + // let graph = GridGraph::generate_regular_grid(10,10); rocket::build() - .manage(GraphWrapper { - graph: *graph, - landmarks, - }) + .manage(GraphWrapper{graph: graph}) .mount("/", routes![index]) .mount("/", routes![random_route]) .mount("/", routes![route]) .mount("/static", FileServer::from("static")) - .attach(Template::custom(|engines: &mut Engines| { - engines.handlebars.set_dev_mode(true); - })) + .attach(Template::custom(|engines: &mut Engines| {engines.handlebars.set_dev_mode(true);})) + } diff --git a/src/bin/gen_landmarks_random.rs b/src/bin/test_alt_gen.rs similarity index 67% rename from src/bin/gen_landmarks_random.rs rename to src/bin/test_alt_gen.rs index 438fec7..fb199ec 100644 --- a/src/bin/gen_landmarks_random.rs +++ b/src/bin/test_alt_gen.rs @@ -1,7 +1,7 @@ use bincode; use clap::Parser; use fapra_osm_2::alt::LandmarkSet; -use fapra_osm_2::utils::load_graph; +use fapra_osm_2::gridgraph::GridGraph; use std::fs::File; use std::io::prelude::*; use std::process::exit; @@ -11,7 +11,7 @@ use std::process::exit; struct Args { /// the FMI file to load #[clap(short, long)] - graph: String, + input: String, /// the file to which to write the landmarks #[clap(short, long)] @@ -25,7 +25,16 @@ struct Args { fn main() { let args = Args::parse(); - let graph = load_graph(&args.graph); + let file = match File::open(args.input.clone()) { + Ok(f) => f, + Err(e) => { + println!("Error while opening file: {}", e); + exit(1); + } + }; + + let grid = GridGraph::from_fmi_file(file).unwrap(); + println!("finished loading grid from file"); let mut output = match File::create(args.output.clone()) { Ok(f) => f, @@ -35,7 +44,7 @@ fn main() { } }; - let set = LandmarkSet::random_set(args.amount, &graph); + let set = LandmarkSet::random_set(args.amount, 4, &grid); let encoded = bincode::serialize(&set).unwrap(); diff --git a/src/coordinates.rs b/src/coordinates.rs index 0c7fbd5..560ef87 100644 --- a/src/coordinates.rs +++ b/src/coordinates.rs @@ -51,7 +51,6 @@ impl From for DegreeCoordinate { } } - impl RadianCoordinate { /// Builds a RadianCoordinate from latitude and longitude given in /// degrees. @@ -155,20 +154,6 @@ impl DegreeCoordinate { Ok(DegreeCoordinate{lat, lon}) } - - - /// tries to parse a DegreeCoordinate from a GeoJSON Position - pub fn from_geojson_position(position: geojson::Position) -> Result { - - if position.len() != 2 { - return Err("String has more than 2 values".to_string()); - } - - let lat = position[1]; - let lon = position[0]; - - Ok(DegreeCoordinate { lat, lon }) - } } /// normalizes longitude values given in radians to the range (-PI, PI] diff --git a/src/gridgraph.rs b/src/gridgraph.rs index 2fc00fb..1291462 100644 --- a/src/gridgraph.rs +++ b/src/gridgraph.rs @@ -1,16 +1,16 @@ use crate::coordinates::{DegreeCoordinate, RadianCoordinate}; use crate::polygonset::PolygonSet; -use crate::utils::EARTH_RADIUS; use geojson::{Feature, FeatureCollection, Geometry, Position, Value}; -use rand::seq::SliceRandom; use serde::ser::{SerializeStruct, Serializer}; -use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::collections::{BinaryHeap, HashMap}; use std::f64::consts::{FRAC_PI_2, PI, TAU}; use std::fs::File; use std::io::{BufRead, BufReader, Write}; use std::vec::Vec; +use crate::utils::EARTH_RADIUS; +use rand::seq::SliceRandom; +use serde::{Serialize, Deserialize}; /// Type for all edge costs /// Allows for easy adjustments for bigger/smaller number types if that is @@ -107,34 +107,6 @@ impl LookupGrid { } impl Route { - - /// Constructs a route from the start to the end node on the graph - /// based on the ancestor and distance lists of a routing algorithm. - pub fn construct(graph: &GridGraph, ancestors: &Vec>, distance: &Vec, start: &GraphNode, end: &GraphNode) -> Option{ - if ancestors[end.index as usize].is_some() { - let mut route = Route { - cost: distance[end.index as usize], - nodes: Vec::new(), - }; - - let mut current = end.index; - while current != start.index { - route - .nodes - .push(graph.nodes[current as usize].position); - current = ancestors[current as usize].unwrap(); - } - route - .nodes - .push(graph.nodes[current as usize].position); - - route.nodes.reverse(); - - return Some(route); - } - None - } - pub fn to_geojson(&self) -> Box { let mut features: Box = Box::new(FeatureCollection { bbox: None, @@ -162,6 +134,8 @@ impl Route { } impl GridGraph { + + /// selects a single random graph node. pub fn get_random_node(&self) -> Option<&GraphNode> { let mut rng = rand::thread_rng(); @@ -169,11 +143,14 @@ impl GridGraph { self.nodes.choose(&mut rng) } + /// selects up to `amount` random but distinct nodes from the graph. - pub fn get_random_nodes(&self, amount: usize) -> Vec<&GraphNode> { + pub fn get_random_nodes(&self, amount: usize) -> Vec<&GraphNode>{ + let mut rng = rand::thread_rng(); self.nodes.choose_multiple(&mut rng, amount).collect() + } /// calculates the shortest path from the start node to the end node. @@ -192,10 +169,7 @@ impl GridGraph { // inverted cmp function, such that the Max-Heap provided by Rust // can be used as a Min-Heap fn cmp(&self, other: &Self) -> Ordering { - other - .cost - .cmp(&self.cost) - .then_with(|| self.index.cmp(&other.index)) + other.cost.cmp(&self.cost).then_with(|| self.index.cmp(&other.index)) } } @@ -216,7 +190,11 @@ impl GridGraph { let mut popcount = 0; - while let Some(DijkstraElement { index, cost }) = heap.pop() { + while let Some(DijkstraElement { + index, + cost, + }) = heap.pop() + { popcount += 1; if index == end.index { @@ -231,13 +209,16 @@ impl GridGraph { continue; }; - //println!("working on node {} with cost {}", index, cost); + // println!("working on node {} with cost {}", index, cost); - for edge in self.get_edges(index as usize).iter() { + let edge_start = self.edge_offsets[index as usize] as usize; + let edge_end = self.edge_offsets[(index + 1) as usize] as usize; + + for edge in self.edges[edge_start..edge_end].iter() { let new_cost = cost + edge.cost; if new_cost < distance[edge.neighbor as usize] { - //println!("found cheaper edge {:?} with cost {} than previous best {}", edge, new_cost, distance[edge.neighbor as usize]); + // println!("found cheaper edge {:?} with cost {} than previous best {}", edge, new_cost, distance[edge.neighbor as usize]); distance[edge.neighbor as usize] = new_cost; ancestor[edge.neighbor as usize] = Some(index); //println!("adding new element to heap"); @@ -246,14 +227,34 @@ impl GridGraph { cost: new_cost, }); } else { - //println!("edge {:?} is more expensive ({}) than the previous best {}", edge, new_cost, distance[edge.neighbor as usize]); + // println!("edge {:?} is more expensive ({}) than the previous best {}", edge, new_cost, distance[edge.neighbor as usize]); } } } println!("popped {} elements from the heap", popcount); - Route::construct(self, &ancestor, &distance, start, end) + // now the route calculation is done. If a route exist we can construct + // it from the ancestors. + if ancestor[end.index as usize].is_some() { + let mut route = Route { + cost: distance[end.index as usize], + nodes: Vec::new(), + }; + + let mut current = end.index; + while current != start.index { + route.nodes.push(self.nodes[current as usize].position); + current = ancestor[current as usize].unwrap(); + } + route.nodes.push(self.nodes[current as usize].position); + + route.nodes.reverse(); + + return Some(route); + } + + None } /// returns the GraphNode nearest to that positon. @@ -296,6 +297,7 @@ impl GridGraph { let mut gridgraph = Box::new(GridGraph::default()); let reader = BufReader::new(file); + let lines = reader.lines(); let mut total_node_count: u32 = 0; @@ -303,7 +305,7 @@ impl GridGraph { let mut node_count: u32 = 0; let mut edge_count: u32 = 0; - let mut node_map: HashMap = HashMap::new(); + let mut nodes: HashMap = HashMap::new(); let mut edges: HashMap> = HashMap::new(); enum ParserState { @@ -314,7 +316,6 @@ impl GridGraph { Done, } - #[derive(Debug)] struct TemporaryGraphEdge { src: u32, dst: u32, @@ -387,46 +388,25 @@ impl GridGraph { match state { ParserState::NodeCount => { total_node_count = parse_int(line)?; - println!("found node count: {}", total_node_count); state = ParserState::EdgeCount {}; } ParserState::EdgeCount => { total_edge_count = parse_int(line)?; - println!("found edge count: {}", total_edge_count); state = ParserState::Nodes {}; } ParserState::Nodes => { - let mut node = parse_node(line)?; - // println!("parsed node: {:?}", node); + let node = parse_node(line)?; + nodes.entry(node.index).or_insert(node); node_count += 1; - - let new_index = gridgraph.nodes.len() as u32; - - node_map.insert(node.index, new_index); - node.index = new_index; - - gridgraph.nodes.push(node); - if node_count == total_node_count { - println!("done parsing nodes: {} nodes parsed", node_count); state = ParserState::Edges {}; } } ParserState::Edges => { - let mut edge = parse_edge(line)?; - // println!("parsed edge: {:?}", edge); - edge_count += 1; - - let src = edge.src; - let dst = edge.dst; - - edge.src = node_map[&src]; - edge.dst = node_map[&dst]; - + let edge = parse_edge(line)?; edges.entry(edge.src).or_default().push(edge); - + edge_count += 1; if edge_count == total_edge_count { - println!("done parsing edges: {} edges parsed", edge_count); state = ParserState::Done; } } @@ -443,22 +423,31 @@ impl GridGraph { return Err(FmiParsingError::WrongEdgeAmount); } - // println!("{:?}", gridgraph); + // add the nodes to the gridgraph + for (_, mut item) in nodes.iter_mut() { + item.index = gridgraph.nodes.len() as u32; + gridgraph.nodes.push(item.clone()); + } // add the edges gridgraph.edge_offsets.push(0); - for index in 0..gridgraph.nodes.len() { - //println!("working on the edges for node {}", index); - for edge in edges.entry(index as u32).or_default() { - //println!("working on edge {:?}", edge); + for index in nodes.keys() { + for edge in edges.entry(*index).or_default() { + if !nodes.contains_key(&edge.dst) { + println!( + "An edge refers to the non-existing destination node {} skipping.", + edge.dst + ); + continue; + } + let dst_index = nodes[&edge.dst].index; gridgraph.edges.push(GraphEdge { cost: edge.cost, - neighbor: edge.dst, + neighbor: dst_index, }); } gridgraph.edge_offsets.push(gridgraph.edges.len()); } - // println!("{:?}", gridgraph); gridgraph.lookup_grid = LookupGrid::new(100, 100, &gridgraph.nodes); @@ -496,6 +485,7 @@ impl GridGraph { lon_resolution: usize, polygons: Option<&PolygonSet>, ) -> Box { + #[derive(Debug)] struct TemporaryGraphNode { final_index: u32, @@ -528,11 +518,7 @@ impl GridGraph { Some(self.lon_index + (self.lat_index - 1) * lon_resolution) } - pub fn top_neighbor_index( - &self, - lat_resolution: usize, - lon_resolution: usize, - ) -> Option { + pub fn top_neighbor_index(&self, lat_resolution: usize, lon_resolution: usize) -> Option { if self.lat_index >= lat_resolution - 1 { return None; }; @@ -586,6 +572,7 @@ impl GridGraph { // add all edges to the final graph for node in temp_nodes.iter() { + println!("working on edges for node {:?}", node); if !node.used { continue; diff --git a/src/utils.rs b/src/utils.rs index fd63548..7cc84e4 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,78 +1,9 @@ -use crate::alt::LandmarkSet; -use crate::gridgraph::{EdgeCost, GridGraph}; use serde::{Deserialize, Serialize}; -use std::cmp::Ordering; -use std::fs::File; -use std::io::BufReader; -use std::process::exit; -/// an approximation of the earths radius. pub const EARTH_RADIUS: f64 = 6_371_000.0; // meters -/// serialization format for routing queries. #[derive(Serialize, Deserialize, Debug, Copy, Clone)] -pub struct RoutingQuery { +pub struct RoutingQuery{ pub source: usize, pub destination: usize, } - -/// loads the graph from the given path. -/// exits if an error occurs during loading. -pub fn load_graph(path: &str) -> Box { - println!("Loading file from {}", path); - let file = match File::open(path) { - Ok(f) => f, - Err(e) => { - println!("Error while opening the file {}: {}", path, e); - exit(1) - } - }; - - let graph = match GridGraph::from_fmi_file(file) { - Ok(g) => g, - Err(e) => { - println!("Error while reading the graph: {:?}", e); - exit(1); - } - }; - - println!("Loaded graph file"); - - graph -} - -/// loads a set of landmarks from the given path. -/// exits if an error occurs during loading. -pub fn load_landmarks(path: &str) -> LandmarkSet { - let landmarks = match File::open(path) { - Ok(f) => f, - Err(e) => { - println!("Error while opening landmark file {}: {:?}", path, e); - exit(1); - } - }; - - bincode::deserialize_from(BufReader::new(landmarks)).unwrap() -} - -/// A heap element for Dijkstra's algorithm. -/// -/// The comparison functions are inverted, so that Rusts MaxHeap works as a -/// MinHeap. -#[derive(Eq, PartialEq)] -pub struct DijkstraElement { - pub index: u32, - pub cost: EdgeCost, -} - -impl Ord for DijkstraElement { - fn cmp(&self, other: &Self) -> Ordering { - other.cost.cmp(&self.cost) - } -} - -impl PartialOrd for DijkstraElement { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} diff --git a/static/js/script.js b/static/js/script.js index 47c6992..627db96 100644 --- a/static/js/script.js +++ b/static/js/script.js @@ -67,11 +67,6 @@ function set_route_cost(cost) { field.value = cost; } -function set_algorithm_type(alg) { - let field = document.getElementById("algorithm_used"); - field.value = alg; -} - async function get_route() { let form = document.getElementById("queryform"); data = new FormData(form); @@ -79,7 +74,6 @@ async function get_route() { let response = await(fetch("/route", { method: "POST", body: data })); let result = await response.json(); - set_algorithm_type(result.algorithm); if (result.success === true) { set_route_cost(result.route.cost); diff --git a/templates/index.html.hbs b/templates/index.html.hbs index d62085a..d84b886 100644 --- a/templates/index.html.hbs +++ b/templates/index.html.hbs @@ -31,20 +31,10 @@ -
- - -
-

Route Cost:

-

Algorithm:

diff --git a/utils/generate_landmarks.py b/utils/generate_landmarks.py deleted file mode 100755 index 88356b7..0000000 --- a/utils/generate_landmarks.py +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env python3 - -from os import system -from sys import argv, exit - -if len(argv) != 3: - print(f"usage: {argv[0]} ") - exit(1) - -graph = argv[1] -output = argv[2] - -for i in range(2, 7): - num = 2**i - - system(f"cargo run --release --bin=gen_landmarks_greedy -- --graph={graph} --output={output}_greedy_{ num }.bin --amount { num }") - system(f"cargo run --release --bin=gen_landmarks_random -- --graph={graph} --output={output}_random_{ num }.bin --amount { num }") diff --git a/utils/plot_results.py b/utils/plot_results.py deleted file mode 100755 index 9e99208..0000000 --- a/utils/plot_results.py +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/env python3 - -from sys import argv, exit -import os -from typing import Tuple, List -import re - -import numpy as np -import pandas as pd -import matplotlib.pyplot as plt - -if len(argv) != 2: - print(f"Usage: { argv[0] } ") - exit(1) - -path = argv[1] - -files = [f for f in os.listdir(path) if os.path.isfile(f"{ path }/{f}")] - - -def parse_file(file: str) -> Tuple[float, List[int]]: - - pops = list() - time = None - with open(file) as f: - for line in f.readlines(): - m = re.match(r"popped\s(?P\d+)\s.*", line) - if m is not None: - pops.append(int(m.groupdict()["pops"])) - continue - - m = re.match(r"It took\s(?P