Compare commits
22 commits
dijkstra_d
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 8052f4a4d7 | |||
| d4d621eea7 | |||
| da70169c17 | |||
| 5b93233915 | |||
| 275c2d933e | |||
| ea0e22506d | |||
| be8effeb18 | |||
| a9fd341b45 | |||
| cab977451c | |||
| 0b3d2db135 | |||
| a2cb9520cc | |||
| 1067c6a514 | |||
| 6f6df97af8 | |||
| 0821515ab9 | |||
| 367fc0ef3e | |||
| 32a81b3561 | |||
| f11dda3939 | |||
| 2aeb770b8f | |||
| 076b19215a | |||
| f6198434c7 | |||
| b11eef1cf7 | |||
| 7d657b7222 |
27 changed files with 1471 additions and 702 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1 +1,2 @@
|
|||
/target
|
||||
/landmarks
|
||||
|
|
|
|||
347
Cargo.lock
generated
347
Cargo.lock
generated
|
|
@ -146,24 +146,6 @@ 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"
|
||||
|
|
@ -176,12 +158,6 @@ 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"
|
||||
|
|
@ -209,17 +185,6 @@ 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"
|
||||
|
|
@ -234,7 +199,7 @@ dependencies = [
|
|||
"once_cell",
|
||||
"strsim",
|
||||
"termcolor",
|
||||
"textwrap 0.15.0",
|
||||
"textwrap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -295,87 +260,6 @@ 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"
|
||||
|
|
@ -386,28 +270,6 @@ 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"
|
||||
|
|
@ -481,8 +343,7 @@ name = "fapra_osm_2"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"clap 3.2.16",
|
||||
"criterion",
|
||||
"clap",
|
||||
"geojson",
|
||||
"handlebars",
|
||||
"osmpbfreader",
|
||||
|
|
@ -761,12 +622,6 @@ 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"
|
||||
|
|
@ -829,7 +684,7 @@ checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399"
|
|||
dependencies = [
|
||||
"bytes",
|
||||
"fnv",
|
||||
"itoa 1.0.3",
|
||||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -870,7 +725,7 @@ dependencies = [
|
|||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa 1.0.3",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
|
|
@ -934,36 +789,12 @@ 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"
|
||||
|
|
@ -1041,15 +872,6 @@ 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"
|
||||
|
|
@ -1212,12 +1034,6 @@ 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"
|
||||
|
|
@ -1368,34 +1184,6 @@ 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"
|
||||
|
|
@ -1530,30 +1318,6 @@ 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"
|
||||
|
|
@ -1759,16 +1523,6 @@ 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"
|
||||
|
|
@ -1786,7 +1540,7 @@ version = "1.0.83"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
|
||||
dependencies = [
|
||||
"itoa 1.0.3",
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
|
@ -1942,15 +1696,6 @@ 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"
|
||||
|
|
@ -1992,7 +1737,7 @@ version = "0.3.13"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db76ff9fa4b1458b3c7f077f3ff9887394058460d21e634355b273aaf11eea45"
|
||||
dependencies = [
|
||||
"itoa 1.0.3",
|
||||
"itoa",
|
||||
"libc",
|
||||
"num_threads",
|
||||
"time-macros",
|
||||
|
|
@ -2004,16 +1749,6 @@ 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"
|
||||
|
|
@ -2190,12 +1925,6 @@ 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"
|
||||
|
|
@ -2251,70 +1980,6 @@ 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"
|
||||
|
|
|
|||
|
|
@ -16,10 +16,3 @@ 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
|
||||
|
|
|
|||
171
README.md
Normal file
171
README.md
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
# 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 <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 <landmarkfile>` is given it runs ALT with that landmark file.
|
||||
By setting `--alt_best_size <number>` 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)
|
||||
25
grids/grid_3x3.fmi
Normal file
25
grids/grid_3x3.fmi
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
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
|
||||
489
landmarks/ocean_handpicked_44.json
Normal file
489
landmarks/ocean_handpicked_44.json
Normal file
|
|
@ -0,0 +1,489 @@
|
|||
{
|
||||
"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
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
1
routes/10x10_1.json
Normal file
1
routes/10x10_1.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
[{"source":51,"destination":11}]
|
||||
1
routes/3x3_1.json
Normal file
1
routes/3x3_1.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
[{"source":2,"destination":5}]
|
||||
111
src/alt.rs
111
src/alt.rs
|
|
@ -1,83 +1,64 @@
|
|||
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<EdgeCost>,
|
||||
}
|
||||
|
||||
|
||||
/// A set of Landmarks
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct LandmarkSet {
|
||||
pub landmarks: Vec<Landmark>,
|
||||
}
|
||||
|
||||
|
||||
/// 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<usize>,
|
||||
}
|
||||
|
||||
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<Ordering> {
|
||||
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 landmark.distances[index as usize] <= cost {
|
||||
if cost > landmark.distances[index as usize] {
|
||||
continue;
|
||||
};
|
||||
|
||||
counter += 1;
|
||||
|
||||
if counter % 1000 == 0 {
|
||||
println!("Finished {} nodes", counter);
|
||||
}
|
||||
|
||||
landmark.distances[index as usize] = cost;
|
||||
|
||||
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() {
|
||||
for edge in graph.get_edges(index as NodeId) {
|
||||
let new_cost = cost + edge.cost;
|
||||
|
||||
if new_cost < landmark.distances[edge.neighbor as usize] {
|
||||
|
|
@ -86,11 +67,11 @@ impl Landmark {
|
|||
index: edge.neighbor,
|
||||
cost: new_cost,
|
||||
});
|
||||
landmark.distances[edge.neighbor as usize] = new_cost;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now the shortest paths to all reachable nodes is calculated.
|
||||
// now the costs to all reachable nodes is calculated.
|
||||
|
||||
landmark
|
||||
}
|
||||
|
|
@ -98,23 +79,28 @@ 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 `0` is returned.
|
||||
/// an estimate of `EdgeCost::MAX` 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 {
|
||||
0
|
||||
if l_to == EdgeCost::MAX || l_from == EdgeCost::MAX {
|
||||
EdgeCost::MAX
|
||||
} else {
|
||||
l_to.saturating_sub(l_from)
|
||||
// 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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LandmarkSet {
|
||||
pub fn random_set(size: usize, best_size: usize, graph: &GridGraph) -> Self {
|
||||
pub fn random_set(size: usize, graph: &GridGraph) -> Self {
|
||||
let mut set = LandmarkSet::default();
|
||||
set.best_size = best_size;
|
||||
|
||||
let nodes = graph.get_random_nodes(size);
|
||||
|
||||
|
|
@ -125,11 +111,13 @@ impl LandmarkSet {
|
|||
|
||||
set
|
||||
}
|
||||
}
|
||||
|
||||
impl LandmarkBestSet<'_> {
|
||||
pub fn select_best(&mut self, from: NodeId, to: NodeId) {
|
||||
let mut results = vec![];
|
||||
|
||||
for (index, landmark) in self.landmarks.iter().enumerate() {
|
||||
for (index, landmark) in self.landmark_set.landmarks.iter().enumerate() {
|
||||
results.push((index, landmark.estimate(from, to)));
|
||||
}
|
||||
|
||||
|
|
@ -137,24 +125,35 @@ impl LandmarkSet {
|
|||
results.reverse();
|
||||
|
||||
self.best_landmarks.clear();
|
||||
for result in results[..self.best_size].iter() {
|
||||
|
||||
for result in results[..(self.best_size.min(results.len()))].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 {
|
||||
distance = distance.max(self.landmarks[*index].estimate(from, to));
|
||||
};
|
||||
let candidate = self.landmark_set.landmarks[*index].estimate(from, to);
|
||||
|
||||
if distance == 0 {
|
||||
distance = EdgeCost::MAX;
|
||||
if candidate == EdgeCost::MAX {
|
||||
continue;
|
||||
}
|
||||
|
||||
distance = distance.max(candidate)
|
||||
}
|
||||
|
||||
distance
|
||||
//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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
105
src/astar.rs
105
src/astar.rs
|
|
@ -4,22 +4,27 @@ use crate::utils::EARTH_RADIUS;
|
|||
use std::cmp::Ordering;
|
||||
use std::collections::BinaryHeap;
|
||||
|
||||
pub struct AStar {
|
||||
pub graph: GridGraph,
|
||||
|
||||
/// datastructure to hold data required by the A* algorithm
|
||||
pub struct AStar<'a> {
|
||||
pub graph: &'a GridGraph,
|
||||
}
|
||||
|
||||
#[derive(Eq)]
|
||||
/// 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)]
|
||||
struct HeapElement {
|
||||
index: u32,
|
||||
cost: EdgeCost, // the cost so far plus the estimated cost until we reach the
|
||||
// destination
|
||||
|
||||
// the cost so far plus the estimated cost until we reach the destination
|
||||
cost: EdgeCost,
|
||||
path_cost: EdgeCost, // the cost to reach this node from the start node
|
||||
ancestor: Option<u32>,
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
|
@ -31,80 +36,84 @@ impl PartialOrd for HeapElement {
|
|||
}
|
||||
}
|
||||
|
||||
impl PartialEq for HeapElement {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.cost == other.cost
|
||||
}
|
||||
}
|
||||
|
||||
/// A simple haversine distance heuristic.
|
||||
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_a = (node.position.lat - destination.position.lat).abs();
|
||||
let lat_dist_b = (destination.position.lat - node.position.lat).abs();
|
||||
let lat_dist = (node.position.lat - destination.position.lat).abs();
|
||||
|
||||
(lat_dist_a.min(lat_dist_b) * EARTH_RADIUS) as EdgeCost
|
||||
(lat_dist * 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<F>(&self, start: &GraphNode, end: &GraphNode, estimate: F) -> Option<Route>
|
||||
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<Option<u32>> = vec![None; self.graph.nodes.len()];
|
||||
|
||||
distance[start.index as usize] = 0;
|
||||
|
||||
let mut popcount = 0;
|
||||
|
||||
while let Some(HeapElement {
|
||||
index,
|
||||
cost, // the cost value, no longer needed, because it is only important for the
|
||||
// distance estimate
|
||||
path_cost,
|
||||
ancestor: prev,
|
||||
index, path_cost, ..
|
||||
}) = 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 distance[index as usize] <= path_cost {
|
||||
if path_cost > distance[index as usize] {
|
||||
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");
|
||||
heap.push(HeapElement{
|
||||
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 {
|
||||
index: edge.neighbor,
|
||||
cost: new_cost + estimate(&self.graph.nodes[edge.neighbor as usize], end),
|
||||
cost: new_cost + remaining_cost,
|
||||
path_cost: new_cost,
|
||||
ancestor: Some(index),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -112,26 +121,6 @@ impl AStar {
|
|||
|
||||
println!("popped {} elements from the heap", popcount);
|
||||
|
||||
// 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
|
||||
Route::construct(self.graph, &ancestor, &distance, start, end)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
94
src/bin/gen_landmarks_geojson.rs
Normal file
94
src/bin/gen_landmarks_geojson.rs
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
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::<GeoJson>().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<RadianCoordinate> = 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");
|
||||
}
|
||||
88
src/bin/gen_landmarks_greedy.rs
Normal file
88
src/bin/gen_landmarks_greedy.rs
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
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<EdgeCost>, 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<EdgeCost> = 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");
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
use bincode;
|
||||
use clap::Parser;
|
||||
use fapra_osm_2::alt::LandmarkSet;
|
||||
use fapra_osm_2::gridgraph::GridGraph;
|
||||
use fapra_osm_2::utils::load_graph;
|
||||
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)]
|
||||
input: String,
|
||||
graph: String,
|
||||
|
||||
/// the file to which to write the landmarks
|
||||
#[clap(short, long)]
|
||||
|
|
@ -25,16 +25,7 @@ struct Args {
|
|||
fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
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 graph = load_graph(&args.graph);
|
||||
|
||||
let mut output = match File::create(args.output.clone()) {
|
||||
Ok(f) => f,
|
||||
|
|
@ -44,7 +35,7 @@ fn main() {
|
|||
}
|
||||
};
|
||||
|
||||
let set = LandmarkSet::random_set(args.amount, 4, &grid);
|
||||
let set = LandmarkSet::random_set(args.amount, &graph);
|
||||
|
||||
let encoded = bincode::serialize(&set).unwrap();
|
||||
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
use fapra_osm_2::gridgraph::GridGraph;
|
||||
use std::fs::File;
|
||||
use fapra_osm_2::utils::load_graph;
|
||||
use clap::Parser;
|
||||
use std::process::exit;
|
||||
use rand::seq::SliceRandom;
|
||||
|
|
@ -21,21 +20,7 @@ struct Args {
|
|||
fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
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 graph = load_graph(&args.graph);
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,15 @@ 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() {
|
||||
|
|
@ -47,7 +56,7 @@ fn main() {
|
|||
|
||||
println!("{:?}", coasts.polygons[0].bbox);
|
||||
|
||||
let grid = GridGraph::generate_regular_grid(10, 10, Some(&coasts));
|
||||
let grid = GridGraph::generate_regular_grid(args.lat, args.lon, Some(&coasts));
|
||||
// let grid = GridGraph::generate_regular_grid(3, 4, None);
|
||||
|
||||
match grid.write_fmi_file(output) {
|
||||
|
|
|
|||
|
|
@ -1,28 +1,18 @@
|
|||
use fapra_osm_2::gridgraph::GridGraph;
|
||||
use fapra_osm_2::utils::load_graph;
|
||||
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)]
|
||||
input: String,
|
||||
graph: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
let file = match File::open(args.input.clone()) {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
println!("Error while opening file: {}", e);
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
let graph = load_graph(&args.graph);
|
||||
|
||||
let grid = GridGraph::from_fmi_file(file).unwrap();
|
||||
|
||||
println!("{}", grid.to_geojson().to_string());
|
||||
println!("{}", graph.to_geojson().to_string());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use clap::Parser;
|
||||
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 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 serde_json;
|
||||
use std::fs::File;
|
||||
use std::io::BufReader;
|
||||
|
|
@ -20,9 +20,9 @@ struct Args {
|
|||
#[clap(short, long)]
|
||||
targets: String,
|
||||
|
||||
/// landmark file
|
||||
/// landmark file. ALT Benchmarks are only run, if this is given.
|
||||
#[clap(short, long)]
|
||||
landmarks: String,
|
||||
landmarks: Option<String>,
|
||||
|
||||
/// run dijkstra
|
||||
#[clap(long, action)]
|
||||
|
|
@ -32,29 +32,17 @@ struct Args {
|
|||
#[clap(long, action)]
|
||||
astar: bool,
|
||||
|
||||
/// run ALT
|
||||
#[clap(long, action)]
|
||||
alt: bool,
|
||||
/// How many of the given landmarks to select
|
||||
#[clap(long, action, default_value_t = 4)]
|
||||
alt_best_size: usize,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
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 = load_graph(&args.graph);
|
||||
|
||||
let graph = match GridGraph::from_fmi_file(file) {
|
||||
Ok(g) => g,
|
||||
Err(e) => {
|
||||
println!("Error while reading the graph: {:?}", e);
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
let astar = AStar { graph: &(*graph) };
|
||||
|
||||
let targets = match File::open(args.targets.clone()) {
|
||||
Ok(f) => f,
|
||||
|
|
@ -66,23 +54,33 @@ fn main() {
|
|||
|
||||
let targets: Vec<RoutingQuery> = serde_json::from_reader(BufReader::new(targets)).unwrap();
|
||||
|
||||
let landmarks = match File::open(args.landmarks.clone()) {
|
||||
Ok(f) => f,
|
||||
Err(e) => {
|
||||
println!(
|
||||
"Error while opening landmark file {}: {:?}",
|
||||
args.landmarks, e
|
||||
);
|
||||
exit(1);
|
||||
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 mut landmarks: LandmarkSet = bincode::deserialize_from(BufReader::new(landmarks)).unwrap();
|
||||
landmarks.best_size = 4;
|
||||
let elapsed = start.elapsed();
|
||||
|
||||
let astar = AStar { graph: *graph };
|
||||
let time_per_route = elapsed.as_secs_f64() / (targets.len() as f64);
|
||||
|
||||
println!("{:?}", args);
|
||||
println!("It took {} seconds per route for ALT.", time_per_route);
|
||||
}
|
||||
|
||||
if args.astar {
|
||||
println!("running A*");
|
||||
|
|
@ -109,13 +107,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();
|
||||
|
|
@ -123,27 +121,4 @@ 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
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());
|
||||
}
|
||||
|
|
@ -1,26 +1,33 @@
|
|||
#[macro_use] extern crate rocket;
|
||||
#[macro_use]
|
||||
extern crate rocket;
|
||||
|
||||
use clap::Parser;
|
||||
use std::process::exit;
|
||||
use std::fs::File;
|
||||
use fapra_osm_2::gridgraph::{GridGraph, Route};
|
||||
use fapra_osm_2::coordinates::{RadianCoordinate, DegreeCoordinate};
|
||||
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 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_dyn_templates::{Template, context, Engines};
|
||||
use rocket::State;
|
||||
use rocket_dyn_templates::{context, Engines, Template};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[clap(author, version, about, long_about=None)]
|
||||
struct Args {
|
||||
/// name of the FMI file to read
|
||||
#[clap(short, long)]
|
||||
filename: String,
|
||||
graph: String,
|
||||
|
||||
/// the landmarks to load
|
||||
#[clap(short, long)]
|
||||
landmarks: String,
|
||||
}
|
||||
|
||||
#[get("/")]
|
||||
|
|
@ -47,73 +54,107 @@ fn random_route(graphwrapper: &State<GraphWrapper>) -> String {
|
|||
struct RouteQuery<'r> {
|
||||
r#from: &'r str,
|
||||
r#to: &'r str,
|
||||
r#algorithm: &'r str,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Serialize)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct RouteResponse {
|
||||
success: bool,
|
||||
route: Option<Route>,
|
||||
algorithm: String,
|
||||
}
|
||||
|
||||
#[post("/route", data = "<routequery>")]
|
||||
fn route(routequery: Form<RouteQuery<'_>>, graphwrapper: &State<GraphWrapper>) -> Result<Json<RouteResponse>, BadRequest<String>> {
|
||||
|
||||
fn route(
|
||||
routequery: Form<RouteQuery<'_>>,
|
||||
graphwrapper: &State<GraphWrapper>,
|
||||
) -> Result<Json<RouteResponse>, BadRequest<String>> {
|
||||
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();
|
||||
|
||||
let route = graphwrapper.graph.shortest_path(from, to);
|
||||
println!("Working on a route from {:?} to {:?}", from, to);
|
||||
|
||||
println!("from: {:?}, to: {:?}", from, to);
|
||||
let astar = AStar {
|
||||
graph: &graphwrapper.graph,
|
||||
};
|
||||
|
||||
let response = RouteResponse{success: route.is_some(), route};
|
||||
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(),
|
||||
};
|
||||
Ok(Json(response))
|
||||
}
|
||||
|
||||
struct GraphWrapper {
|
||||
graph: Box<GridGraph>
|
||||
graph: GridGraph,
|
||||
landmarks: LandmarkSet,
|
||||
}
|
||||
|
||||
#[launch]
|
||||
fn rocket() -> _ {
|
||||
let args = Args::parse();
|
||||
|
||||
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 graph = load_graph(&args.graph);
|
||||
|
||||
let graph = match GridGraph::from_fmi_file(file) {
|
||||
Ok(g) => g,
|
||||
Err(e) => {
|
||||
println!("Error while reading the graph: {:?}", e);
|
||||
exit(1);
|
||||
}
|
||||
};
|
||||
let landmarks = load_landmarks(&args.landmarks);
|
||||
|
||||
println!("Loaded graph file");
|
||||
|
||||
// let graph = GridGraph::generate_regular_grid(10,10);
|
||||
println!("Listening on http://localhost:8000");
|
||||
|
||||
rocket::build()
|
||||
.manage(GraphWrapper{graph: graph})
|
||||
.manage(GraphWrapper {
|
||||
graph: *graph,
|
||||
landmarks,
|
||||
})
|
||||
.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);
|
||||
}))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ impl From<RadianCoordinate> for DegreeCoordinate {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
impl RadianCoordinate {
|
||||
/// Builds a RadianCoordinate from latitude and longitude given in
|
||||
/// degrees.
|
||||
|
|
@ -154,6 +155,20 @@ impl DegreeCoordinate {
|
|||
|
||||
Ok(DegreeCoordinate{lat, lon})
|
||||
}
|
||||
|
||||
|
||||
/// tries to parse a DegreeCoordinate from a GeoJSON Position
|
||||
pub fn from_geojson_position(position: geojson::Position) -> Result<Self, String> {
|
||||
|
||||
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]
|
||||
|
|
|
|||
149
src/gridgraph.rs
149
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,6 +107,34 @@ 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<Option<u32>>, distance: &Vec<EdgeCost>, start: &GraphNode, end: &GraphNode) -> Option<Self>{
|
||||
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<FeatureCollection> {
|
||||
let mut features: Box<FeatureCollection> = Box::new(FeatureCollection {
|
||||
bbox: None,
|
||||
|
|
@ -134,8 +162,6 @@ impl Route {
|
|||
}
|
||||
|
||||
impl GridGraph {
|
||||
|
||||
|
||||
/// selects a single random graph node.
|
||||
pub fn get_random_node(&self) -> Option<&GraphNode> {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
|
@ -143,14 +169,11 @@ 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.
|
||||
|
|
@ -169,7 +192,10 @@ 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))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -190,11 +216,7 @@ 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 {
|
||||
|
|
@ -209,16 +231,13 @@ impl GridGraph {
|
|||
continue;
|
||||
};
|
||||
|
||||
// println!("working on node {} with cost {}", index, cost);
|
||||
//println!("working on node {} with cost {}", index, cost);
|
||||
|
||||
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() {
|
||||
for edge in self.get_edges(index as usize).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");
|
||||
|
|
@ -227,34 +246,14 @@ 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);
|
||||
|
||||
// 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
|
||||
Route::construct(self, &ancestor, &distance, start, end)
|
||||
}
|
||||
|
||||
/// returns the GraphNode nearest to that positon.
|
||||
|
|
@ -297,7 +296,6 @@ 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;
|
||||
|
|
@ -305,7 +303,7 @@ impl GridGraph {
|
|||
let mut node_count: u32 = 0;
|
||||
let mut edge_count: u32 = 0;
|
||||
|
||||
let mut nodes: HashMap<u32, GraphNode> = HashMap::new();
|
||||
let mut node_map: HashMap<u32, u32> = HashMap::new();
|
||||
let mut edges: HashMap<u32, Vec<TemporaryGraphEdge>> = HashMap::new();
|
||||
|
||||
enum ParserState {
|
||||
|
|
@ -316,6 +314,7 @@ impl GridGraph {
|
|||
Done,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TemporaryGraphEdge {
|
||||
src: u32,
|
||||
dst: u32,
|
||||
|
|
@ -388,25 +387,46 @@ 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 node = parse_node(line)?;
|
||||
nodes.entry(node.index).or_insert(node);
|
||||
let mut node = parse_node(line)?;
|
||||
// println!("parsed node: {:?}", 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 edge = parse_edge(line)?;
|
||||
edges.entry(edge.src).or_default().push(edge);
|
||||
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];
|
||||
|
||||
edges.entry(edge.src).or_default().push(edge);
|
||||
|
||||
if edge_count == total_edge_count {
|
||||
println!("done parsing edges: {} edges parsed", edge_count);
|
||||
state = ParserState::Done;
|
||||
}
|
||||
}
|
||||
|
|
@ -423,31 +443,22 @@ impl GridGraph {
|
|||
return Err(FmiParsingError::WrongEdgeAmount);
|
||||
}
|
||||
|
||||
// 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());
|
||||
}
|
||||
// println!("{:?}", gridgraph);
|
||||
|
||||
// add the edges
|
||||
gridgraph.edge_offsets.push(0);
|
||||
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;
|
||||
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);
|
||||
gridgraph.edges.push(GraphEdge {
|
||||
cost: edge.cost,
|
||||
neighbor: dst_index,
|
||||
neighbor: edge.dst,
|
||||
});
|
||||
}
|
||||
gridgraph.edge_offsets.push(gridgraph.edges.len());
|
||||
}
|
||||
// println!("{:?}", gridgraph);
|
||||
|
||||
gridgraph.lookup_grid = LookupGrid::new(100, 100, &gridgraph.nodes);
|
||||
|
||||
|
|
@ -485,7 +496,6 @@ impl GridGraph {
|
|||
lon_resolution: usize,
|
||||
polygons: Option<&PolygonSet>,
|
||||
) -> Box<GridGraph> {
|
||||
|
||||
#[derive(Debug)]
|
||||
struct TemporaryGraphNode {
|
||||
final_index: u32,
|
||||
|
|
@ -518,7 +528,11 @@ 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<usize> {
|
||||
pub fn top_neighbor_index(
|
||||
&self,
|
||||
lat_resolution: usize,
|
||||
lon_resolution: usize,
|
||||
) -> Option<usize> {
|
||||
if self.lat_index >= lat_resolution - 1 {
|
||||
return None;
|
||||
};
|
||||
|
|
@ -572,7 +586,6 @@ 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;
|
||||
|
|
|
|||
71
src/utils.rs
71
src/utils.rs
|
|
@ -1,9 +1,78 @@
|
|||
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<GridGraph> {
|
||||
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<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,6 +67,11 @@ 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);
|
||||
|
|
@ -74,6 +79,7 @@ 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);
|
||||
|
|
|
|||
|
|
@ -31,10 +31,20 @@
|
|||
<input type="text" class="form-control" name="to" id="to">
|
||||
</div>
|
||||
|
||||
<div class="form group">
|
||||
<label for="algorithm">Algorithm</label>
|
||||
<select name="algorithm" id="algorithm" class="form-control">
|
||||
<option value="dijkstra">Dijkstra</option>
|
||||
<option value="astar-haversine">A* with haversine</option>
|
||||
<option value="alt">ALT</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
<button class="btn btn-primary" onclick="get_route()">Submit</button>
|
||||
<button class="btn btn-primary" onclick="get_random_route()">Random Route</button>
|
||||
<p>Route Cost: <input class="form-control " type="text" id="routecost" readonly></p>
|
||||
<p>Algorithm: <input class="form-control " type="text" id="algorithm_used" readonly></p>
|
||||
</div>
|
||||
<div id="map" class="col-9"></div>
|
||||
<script type="text/javascript" src="/static/js/script.js"></script>
|
||||
|
|
|
|||
17
utils/generate_landmarks.py
Executable file
17
utils/generate_landmarks.py
Executable file
|
|
@ -0,0 +1,17 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from os import system
|
||||
from sys import argv, exit
|
||||
|
||||
if len(argv) != 3:
|
||||
print(f"usage: {argv[0]} <graph> <output_prefix>")
|
||||
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 }")
|
||||
128
utils/plot_results.py
Executable file
128
utils/plot_results.py
Executable file
|
|
@ -0,0 +1,128 @@
|
|||
#!/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] } <results_dir>")
|
||||
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<pops>\d+)\s.*", line)
|
||||
if m is not None:
|
||||
pops.append(int(m.groupdict()["pops"]))
|
||||
continue
|
||||
|
||||
m = re.match(r"It took\s(?P<time>\S+).*", line)
|
||||
if m is not None:
|
||||
time = float(m.groupdict()["time"])
|
||||
|
||||
if len(pops) == 0:
|
||||
raise Exception(f"Parsing { file } failed, no heap pops found!")
|
||||
|
||||
return time, pops
|
||||
|
||||
|
||||
frames = list()
|
||||
|
||||
for i, file in enumerate(files):
|
||||
time, pops = parse_file(f"{ path }/{ file }")
|
||||
|
||||
parts = file.split(".")[0].split("_")
|
||||
|
||||
data = {
|
||||
"name": parts[0],
|
||||
"time": time,
|
||||
"pops": int(sum(pops)),
|
||||
}
|
||||
|
||||
if len(parts) > 1:
|
||||
data["num_landmarks"] = int(parts[1])
|
||||
if len(parts) > 2:
|
||||
data["best_size"] = int(parts[2])
|
||||
|
||||
frames.append(pd.DataFrame(data, index=[i]))
|
||||
|
||||
results = pd.concat(frames)
|
||||
|
||||
|
||||
# dijkstra vs A* vs greedy and random ALT
|
||||
|
||||
#condition = ((results["name"] == "astar")
|
||||
# | (results["name"] == "dijkstra")
|
||||
# | ((results["num_landmarks"] == 32) & (results["best_size"] == 4)))
|
||||
#
|
||||
# basic = results[condition].sort_values(by=["time"], ascending=False)
|
||||
# names = basic["name"].to_list()
|
||||
# for i in range(len(names)):
|
||||
# if names[i] == "greedy":
|
||||
# names[i] = "ALT 32 greedy"
|
||||
# if names[i] == "random":
|
||||
# names[i] = "ALT 32 random"
|
||||
# times = basic["time"] * 1000
|
||||
#
|
||||
# fig, ax = plt.subplots()
|
||||
#
|
||||
# bar = ax.bar(names, times)
|
||||
# ax.set_title("Average query time by algorithm")
|
||||
# ax.set_ylabel("time in ms")
|
||||
# ax.bar_label(bar)
|
||||
# fig.savefig("basic.svg")
|
||||
# plt.show()
|
||||
|
||||
|
||||
# comparison greedy vs. random, best_size = 4
|
||||
|
||||
condition = (((results["name"] == "greedy") | (results["name"] == "random")) & (results["best_size"] == 4))
|
||||
|
||||
data = results[condition]
|
||||
greedy = data[data["name"] == "greedy"]
|
||||
random = data[data["name"] == "random"]
|
||||
|
||||
print(greedy)
|
||||
print(random)
|
||||
|
||||
labels = greedy["num_landmarks"].to_list()
|
||||
labels = [int(label) for label in labels]
|
||||
greedy_times = greedy["time"].to_list()
|
||||
random_times = random["time"].to_list()
|
||||
|
||||
width = 0.35
|
||||
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
x = np.arange(len(labels))
|
||||
|
||||
greedy_bars = ax.bar(x - width/2, greedy_times, width, label="Greedy")
|
||||
random_bars = ax.bar(x + width/2, random_times, width, label="Random")
|
||||
ax.set_title("Comparison of ALT with random and greedy landmark selection")
|
||||
ax.set_ylabel("time in ms")
|
||||
ax.set_xlabel("number of landmarks")
|
||||
ax.set_xticks(x, labels)
|
||||
ax.legend()
|
||||
# ax.bar_label(greedy_bars)
|
||||
# ax.bar_label(random_bars)
|
||||
|
||||
fig.save("random_greedy_comp.svg")
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
# comparison greedy best_size
|
||||
# comparison random best_size
|
||||
28
utils/run_benchmarks.py
Executable file
28
utils/run_benchmarks.py
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from os import system
|
||||
from sys import argv, exit
|
||||
|
||||
if len(argv) != 4:
|
||||
print(f"usage: {argv[0]} <graph> <landmark_prefix> <targets>")
|
||||
exit(1)
|
||||
|
||||
graph = argv[1]
|
||||
landmarks = argv[2]
|
||||
targets = argv[3]
|
||||
|
||||
print("running A*")
|
||||
system(f"cargo run --release --bin=performance -- --graph={graph} --astar --targets {targets} > results/astar.txt")
|
||||
print("running Dijkstra")
|
||||
system(f"cargo run --release --bin=performance -- --graph={graph} --dijkstra --targets {targets} > results/dijkstra.txt")
|
||||
|
||||
print("running ALT with 44 handpicked landmarks")
|
||||
system(f"cargo run --release --bin=performance -- --graph={graph} --landmarks={landmarks}_handpicked_44.bin --targets {targets} > results/handpicked_44.txt")
|
||||
for i in range(6, 7):
|
||||
num = 2**i
|
||||
|
||||
for best in [12, 24, ]:
|
||||
|
||||
for lm_type in ["greedy", "random"]:
|
||||
print(f"running ALT with {num} {lm_type} landmarks")
|
||||
system(f"cargo run --release --bin=performance -- --graph={graph} --landmarks={landmarks}_{lm_type}_{ num }.bin --targets {targets} --alt-best-size { best} > results/{lm_type}_{num}_{ best }.txt")
|
||||
Loading…
Add table
Add a link
Reference in a new issue