removing gpconv

pull/2234/head
frederic wagner 2022-11-06 10:42:20 +01:00
parent c98f6a9c71
commit a73c38794a
8 changed files with 0 additions and 1605 deletions

View File

@ -1,732 +0,0 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom",
"once_cell",
"version_check",
]
[[package]]
name = "anyhow"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb07d2053ccdbe10e2af2995a2f116c1330396493dc1269f6a91d0ae82e19704"
[[package]]
name = "assert_approx_eq"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c07dab4369547dbe5114677b33fbbf724971019f3818172d59a97a61c774ffd"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
version = "0.3.65"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11a17d453482a265fd5f8479f2a3f405566e6ca627837aaddb85af8b1ab8ef61"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bumpalo"
version = "3.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
[[package]]
name = "byteorder"
version = "1.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bzip2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0"
dependencies = [
"bzip2-sys",
"libc",
]
[[package]]
name = "bzip2-sys"
version = "0.1.11+1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc"
dependencies = [
"cc",
"libc",
"pkg-config",
]
[[package]]
name = "cc"
version = "1.0.73"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73"
dependencies = [
"libc",
"num-integer",
"num-traits",
"time 0.1.44",
"winapi",
]
[[package]]
name = "crc32fast"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if",
]
[[package]]
name = "darling"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f2c43f534ea4b0b049015d00269734195e6d3f0f6635cb692251aca6f9f8b3c"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e91455b86830a1c21799d94524df0845183fa55bafd9aa137b01c7d1065fa36"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn",
]
[[package]]
name = "darling_macro"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a"
dependencies = [
"darling_core",
"quote",
"syn",
]
[[package]]
name = "derive_builder"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d13202debe11181040ae9063d739fa32cfcaaebe2275fe387703460ae2365b30"
dependencies = [
"derive_builder_macro",
]
[[package]]
name = "derive_builder_core"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66e616858f6187ed828df7c64a6d71720d83767a7f19740b2d1b6fe6327b36e5"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "derive_builder_macro"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58a94ace95092c5acb1e97a7e846b310cfbd499652f72297da7493f618a98d73"
dependencies = [
"derive_builder_core",
"syn",
]
[[package]]
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "error-chain"
version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d2f06b9cac1506ece98fe3231e3cc9c4410ec3d5b1f24ae1c8946f0742cdefc"
dependencies = [
"backtrace",
"version_check",
]
[[package]]
name = "fallible-iterator"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]]
name = "fallible-streaming-iterator"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "flate2"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]]
name = "fnv"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "geo-types"
version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9805fbfcea97de816e6408e938603241879cc41eea3fba3f84f122f4f6f9c54"
dependencies = [
"num-traits",
]
[[package]]
name = "getrandom"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
dependencies = [
"cfg-if",
"libc",
"wasi 0.11.0+wasi-snapshot-preview1",
]
[[package]]
name = "gimli"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4"
[[package]]
name = "gpconv"
version = "0.1.0"
dependencies = [
"gpx",
"itertools",
"lazy_static",
"osmio",
"wasm-bindgen",
]
[[package]]
name = "gpx"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b03599b85866c88fd0125db7ca7a683be1550724918682c736c7893a399dc5e"
dependencies = [
"assert_approx_eq",
"error-chain",
"geo-types",
"thiserror",
"time 0.3.11",
"xml-rs",
]
[[package]]
name = "hashbrown"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
dependencies = [
"ahash",
]
[[package]]
name = "hashlink"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7249a3129cbc1ffccd74857f81464a323a152173cdb134e0fd81bc803b29facf"
dependencies = [
"hashbrown",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]]
name = "iter-progress"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "97059d64dd4e3a8e16696f6c0be50c1d5da3a709983f39b73fd7f84f120c5cd4"
[[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 = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
[[package]]
name = "libsqlite3-sys"
version = "0.22.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290b64917f8b0cb885d9de0f9959fe1f775d7fa12f1da2db9001c1c8ab60f89d"
dependencies = [
"pkg-config",
"vcpkg",
]
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "miniz_oxide"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc"
dependencies = [
"adler",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "num_threads"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44"
dependencies = [
"libc",
]
[[package]]
name = "object"
version = "0.28.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e42c982f2d955fac81dd7e1d0e1426a7d702acd9c98d19ab01083a6a0328c424"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
[[package]]
name = "osmio"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0db40ae840afac7f6c710abf757bb76c6b95b4a34a20d55811ef70d30b3ea24f"
dependencies = [
"anyhow",
"byteorder",
"bzip2",
"chrono",
"derive_builder",
"flate2",
"iter-progress",
"protobuf",
"quick-xml",
"rusqlite",
"separator",
"serde",
"serde_json",
"xml-rs",
]
[[package]]
name = "pkg-config"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
[[package]]
name = "proc-macro2"
version = "1.0.40"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7"
dependencies = [
"unicode-ident",
]
[[package]]
name = "protobuf"
version = "2.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70731852eec72c56d11226c8a5f96ad5058a3dab73647ca5f7ee351e464f2571"
[[package]]
name = "quick-xml"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8533f14c8382aaad0d592c812ac3b826162128b65662331e1127b45c3d18536b"
dependencies = [
"memchr",
]
[[package]]
name = "quote"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rusqlite"
version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c4b1eaf239b47034fb450ee9cdedd7d0226571689d8823030c4b6c2cb407152"
dependencies = [
"bitflags",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
"libsqlite3-sys",
"memchr",
"smallvec",
]
[[package]]
name = "rustc-demangle"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
[[package]]
name = "ryu"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f3f6f92acf49d1b98f7a81226834412ada05458b7364277387724a237f062695"
[[package]]
name = "separator"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f97841a747eef040fcd2e7b3b9a220a7205926e60488e673d9e4926d27772ce5"
[[package]]
name = "serde"
version = "1.0.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.139"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc1d3230c1de7932af58ad8ffbe1d784bd55efd5a9d84ac24f69c72d83543dfb"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "smallvec"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "time"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
"winapi",
]
[[package]]
name = "time"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72c91f41dcb2f096c05f0873d667dceec1087ce5bcf984ec8ffb19acddbb3217"
dependencies = [
"itoa",
"libc",
"num_threads",
]
[[package]]
name = "unicode-ident"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "wasi"
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.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "xml-rs"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3"

View File

@ -1,18 +0,0 @@
[package]
name = "gpconv"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
osm = ["dep:osmio"]
# [lib]
# crate-type = ["cdylib"]
[dependencies]
gpx="*"
itertools="*"
lazy_static="*"
osmio={version="*", optional=true}
wasm-bindgen="*"

View File

@ -1,58 +0,0 @@
This file documents the .gpc file format.
current version is version 2.
every number is encoded in little endian order.
# header
We start by a header of 5 16bytes unsigned ints.
- the first int is a marker with value 47490
- second int is version of this file format
- third int is **NP** the number of points composing the path
- fourth int is **IP** the number of interest points bordering the path
- fifth int is **LP** the number of interest points as encountered when looping through the path (higher than previous int since some points can be met several times)
# points
We continue with an array of **2 NP** f64 containing
for each point its x and y coordinate.
# interest points
After that comes the storage for interest points.
- we start by an array of **2 IP** f64 containing
the x and y coordinates of each interest point
- we continue with an **IP** u8 array named **IPOINTS** containing the id of the point's type.
you can see the `Interest` enum in `src/osm.rs` to know what int is what.
for example 0 is a Bakery and 1 is a water point.
Now we need to store the relationship between segments and points.
The idea is that in a display phase we don't want to loop on all interest points
to figure out if they should appear on the map or not.
We'll use the fact that we now the segments we want to display and therefore we should only
need to display the points bordering these segments.
- we store an array **LOOP** of **LP** u16 indices of interest points in **IPOINTS**
while this is a contiguous array it contains points along the path grouped in buckets of 5 points.
to figure out on which segments they are :
- we store an array **STARTS** of **ceil(LP/5)** u16 indices of groups of segments.
Segments are grouped by 3.
This array tells us on which group of segment is the first point of any bucket.
## display algorithm
If we want to display the interest points for the segments between 10 and 16 for example we proceed
as follows:
* segments are grouped by 3 so instead of segment indices of 10..=16 we will look at group indices 10/3 ..= 16/3 so 3..=5
* we do a binary search of the highest number below 3 in the **STARTS** array. we call *s* the obtained index
* we do a binary search of the smallest number above 5 in the **STARTS** array. we call *e* the obtained index
* we now loop on all buckets between *s* and *e* that is : on all indices *i* in `LOOP[(s*5)..=(e*5)]`
* display `IPOINTS[i]`

View File

@ -1,78 +0,0 @@
use super::Point;
use lazy_static::lazy_static;
use std::collections::HashMap;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum Interest {
Bakery,
DrinkingWater,
Toilets,
// BikeShop,
// ChargingStation,
// Bank,
// Supermarket,
// Table,
// TourismOffice,
Artwork,
// Pharmacy,
}
impl Into<u8> for Interest {
fn into(self) -> u8 {
match self {
Interest::Bakery => 0,
Interest::DrinkingWater => 1,
Interest::Toilets => 2,
// Interest::BikeShop => 8,
// Interest::ChargingStation => 4,
// Interest::Bank => 5,
// Interest::Supermarket => 6,
// Interest::Table => 7,
Interest::Artwork => 3,
// Interest::Pharmacy => 9,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct InterestPoint {
pub point: Point,
pub interest: Interest,
}
lazy_static! {
static ref INTERESTS: HashMap<(&'static str, &'static str), Interest> = {
[
(("shop", "bakery"), Interest::Bakery),
(("amenity", "drinking_water"), Interest::DrinkingWater),
(("amenity", "toilets"), Interest::Toilets),
// (("shop", "bicycle"), Interest::BikeShop),
// (("amenity", "charging_station"), Interest::ChargingStation),
// (("amenity", "bank"), Interest::Bank),
// (("shop", "supermarket"), Interest::Supermarket),
// (("leisure", "picnic_table"), Interest::Table),
// (("tourism", "information"), Interest::TourismOffice),
(("tourism", "artwork"), Interest::Artwork),
// (("amenity", "pharmacy"), Interest::Pharmacy),
]
.into_iter()
.collect()
};
}
impl InterestPoint {
pub fn color(&self) -> &'static str {
match self.interest {
Interest::Bakery => "red",
Interest::DrinkingWater => "blue",
Interest::Toilets => "brown",
// Interest::BikeShop => "purple",
// Interest::ChargingStation => "green",
// Interest::Bank => "black",
// Interest::Supermarket => "red",
// Interest::Table => "pink",
Interest::Artwork => "orange",
// Interest::Pharmacy => "chartreuse",
}
}
}

View File

@ -1,575 +0,0 @@
use itertools::Itertools;
use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::io::{BufReader, BufWriter, Read, Write};
use std::path::Path;
use wasm_bindgen::prelude::*;
use gpx::read;
use gpx::Gpx;
mod interests;
use interests::InterestPoint;
mod svg;
#[cfg(feature = "osm")]
mod osm;
#[cfg(feature = "osm")]
use osm::{parse_osm_data, InterestPoint};
const LOWER_SHARP_TURN: f64 = 80.0 * std::f64::consts::PI / 180.0;
const UPPER_SHARP_TURN: f64 = std::f64::consts::PI * 2.0 - LOWER_SHARP_TURN;
const KEY: u16 = 47490;
const FILE_VERSION: u16 = 3;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Point {
x: f64,
y: f64,
}
impl Eq for Point {}
impl std::hash::Hash for Point {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
unsafe { std::mem::transmute::<f64, u64>(self.x) }.hash(state);
unsafe { std::mem::transmute::<f64, u64>(self.y) }.hash(state);
}
}
impl Point {
fn squared_distance_between(&self, other: &Point) -> f64 {
let dx = other.x - self.x;
let dy = other.y - self.y;
dx * dx + dy * dy
}
fn distance_to_segment(&self, v: &Point, w: &Point) -> f64 {
let l2 = v.squared_distance_between(w);
if l2 == 0.0 {
return self.squared_distance_between(v).sqrt();
}
// Consider the line extending the segment, parameterized as v + t (w - v).
// We find projection of point p onto the line.
// It falls where t = [(p-v) . (w-v)] / |w-v|^2
// We clamp t from [0,1] to handle points outside the segment vw.
let x0 = self.x - v.x;
let y0 = self.y - v.y;
let x1 = w.x - v.x;
let y1 = w.y - v.y;
let dot = x0 * x1 + y0 * y1;
let t = (dot / l2).min(1.0).max(0.0);
let proj = Point {
x: v.x + x1 * t,
y: v.y + y1 * t,
};
proj.squared_distance_between(self).sqrt()
}
}
fn points<R: Read>(reader: R) -> (HashSet<Point>, Vec<Point>) {
// read takes any io::Read and gives a Result<Gpx, Error>.
let mut gpx: Gpx = read(reader).unwrap();
eprintln!("we have {} tracks", gpx.tracks.len());
let mut waypoints = HashSet::new();
let points = gpx
.tracks
.pop()
.unwrap()
.segments
.into_iter()
.flat_map(|segment| segment.points.into_iter())
.map(|p| {
let is_commented = p.comment.is_some();
let (x, y) = p.point().x_y();
let p = Point { x, y };
if is_commented {
waypoints.insert(p);
}
p
})
.collect::<Vec<_>>();
(waypoints, points)
}
// // NOTE: this angles idea could maybe be use to get dp from n^3 to n^2
// fn acceptable_angles(p1: &(f64, f64), p2: &(f64, f64), epsilon: f64) -> (f64, f64) {
// // first, convert p2's coordinates for p1 as origin
// let (x1, y1) = *p1;
// let (x2, y2) = *p2;
// let (x, y) = (x2 - x1, y2 - y1);
// // rotate so that (p1, p2) ends on x axis
// let theta = y.atan2(x);
// let rx = x * theta.cos() - y * theta.sin();
// let ry = x * theta.sin() + y * theta.cos();
// assert!(ry.abs() <= std::f64::EPSILON);
//
// // now imagine a line at an angle alpha.
// // we want the distance d from (rx, 0) to our line
// // we have sin(alpha) = d / rx
// // limiting d to epsilon, we solve
// // sin(alpha) = e / rx
// // and get
// // alpha = arcsin(e/rx)
// let alpha = (epsilon / rx).asin();
//
// // now we just need to rotate back
// let a1 = theta + alpha.abs();
// let a2 = theta - alpha.abs();
// assert!(a1 >= a2);
// (a1, a2)
// }
//
// // this is like ramer douglas peucker algorithm
// // except that we advance from the start without knowing the end.
// // each point we meet constrains the chosen segment's angle
// // a bit more.
// //
// fn simplify(mut points: &[(f64, f64)]) -> Vec<(f64, f64)> {
// let mut remaining_points = Vec::new();
// while !points.is_empty() {
// let (sx, sy) = points.first().unwrap();
// let i = match points
// .iter()
// .enumerate()
// .map(|(i, (x, y))| todo!("compute angles"))
// .try_fold(
// (0.0f64, std::f64::consts::FRAC_2_PI),
// |(amin, amax), (i, (amin2, amax2))| -> Result<(f64, f64), usize> {
// let new_amax = amax.min(amax2);
// let new_amin = amin.max(amin2);
// if new_amin >= new_amax {
// Err(i)
// } else {
// Ok((new_amin, new_amax))
// }
// },
// ) {
// Err(i) => i,
// Ok(_) => points.len(),
// };
// remaining_points.push(points.first().cloned().unwrap());
// points = &points[i..];
// }
// remaining_points
// }
fn extract_prog_dyn_solution(
points: &[Point],
start: usize,
end: usize,
cache: &HashMap<(usize, usize), (Option<usize>, usize)>,
) -> Vec<Point> {
if let Some(choice) = cache.get(&(start, end)).unwrap().0 {
let mut v1 = extract_prog_dyn_solution(points, start, choice + 1, cache);
let mut v2 = extract_prog_dyn_solution(points, choice, end, cache);
v1.pop();
v1.append(&mut v2);
v1
} else {
vec![points[start], points[end - 1]]
}
}
fn simplify_prog_dyn(
points: &[Point],
start: usize,
end: usize,
epsilon: f64,
cache: &mut HashMap<(usize, usize), (Option<usize>, usize)>,
) -> usize {
if let Some(val) = cache.get(&(start, end)) {
val.1
} else {
let res = if end - start <= 2 {
assert_eq!(end - start, 2);
(None, end - start)
} else {
let first_point = &points[start];
let last_point = &points[end - 1];
if points[(start + 1)..end]
.iter()
.map(|p| p.distance_to_segment(first_point, last_point))
.all(|d| d <= epsilon)
{
(None, 2)
} else {
// now we test all possible cutting points
((start + 1)..(end - 1)) //TODO: take middle min
.map(|i| {
let v1 = simplify_prog_dyn(points, start, i + 1, epsilon, cache);
let v2 = simplify_prog_dyn(points, i, end, epsilon, cache);
(Some(i), v1 + v2 - 1)
})
.min_by_key(|(_, v)| *v)
.unwrap()
}
};
cache.insert((start, end), res);
res.1
}
}
fn rdp(points: &[Point], epsilon: f64) -> Vec<Point> {
if points.len() <= 2 {
points.iter().copied().collect()
} else {
if points.first().unwrap() == points.last().unwrap() {
let first = points.first().unwrap();
let index_farthest = points
.iter()
.enumerate()
.skip(1)
.max_by(|(_, p1), (_, p2)| {
first
.squared_distance_between(p1)
.partial_cmp(&first.squared_distance_between(p2))
.unwrap()
})
.map(|(i, _)| i)
.unwrap();
let start = &points[..(index_farthest + 1)];
let end = &points[index_farthest..];
let mut res = rdp(start, epsilon);
res.pop();
res.append(&mut rdp(end, epsilon));
res
} else {
let (index_farthest, farthest_distance) = points
.iter()
.map(|p| p.distance_to_segment(points.first().unwrap(), points.last().unwrap()))
.enumerate()
.max_by(|(_, d1), (_, d2)| {
if d1.is_nan() {
std::cmp::Ordering::Greater
} else {
if d2.is_nan() {
std::cmp::Ordering::Less
} else {
d1.partial_cmp(d2).unwrap()
}
}
})
.unwrap();
if farthest_distance <= epsilon {
vec![
points.first().copied().unwrap(),
points.last().copied().unwrap(),
]
} else {
let start = &points[..(index_farthest + 1)];
let end = &points[index_farthest..];
let mut res = rdp(start, epsilon);
res.pop();
res.append(&mut rdp(end, epsilon));
res
}
}
}
}
fn simplify_path(points: &[Point], epsilon: f64) -> Vec<Point> {
if points.len() <= 600 {
optimal_simplification(points, epsilon)
} else {
hybrid_simplification(points, epsilon)
}
}
fn save_gpc<W: Write>(
mut writer: W,
points: &[Point],
waypoints: &HashSet<Point>,
buckets: &[Bucket],
) -> std::io::Result<()> {
eprintln!("saving {} points", points.len());
let mut unique_interest_points = Vec::new();
let mut correspondance = HashMap::new();
let interests_on_path = buckets
.iter()
.flat_map(|b| &b.points)
.map(|p| match correspondance.entry(*p) {
std::collections::hash_map::Entry::Occupied(o) => *o.get(),
std::collections::hash_map::Entry::Vacant(v) => {
let index = unique_interest_points.len();
unique_interest_points.push(*p);
v.insert(index);
index
}
})
.collect::<Vec<_>>();
writer.write_all(&KEY.to_le_bytes())?;
writer.write_all(&FILE_VERSION.to_le_bytes())?;
writer.write_all(&(points.len() as u16).to_le_bytes())?;
writer.write_all(&(unique_interest_points.len() as u16).to_le_bytes())?;
writer.write_all(&(interests_on_path.len() as u16).to_le_bytes())?;
points
.iter()
.flat_map(|p| [p.x, p.y])
.try_for_each(|c| writer.write_all(&c.to_le_bytes()))?;
let mut waypoints_bits = std::iter::repeat(0u8)
.take(points.len() / 8 + if points.len() % 8 != 0 { 1 } else { 0 })
.collect::<Vec<u8>>();
points.iter().enumerate().for_each(|(i, p)| {
if waypoints.contains(p) {
waypoints_bits[i / 8] |= 1 << (i % 8)
}
});
waypoints_bits
.iter()
.try_for_each(|byte| writer.write_all(&byte.to_le_bytes()))?;
unique_interest_points
.iter()
.flat_map(|p| [p.point.x, p.point.y])
.try_for_each(|c| writer.write_all(&c.to_le_bytes()))?;
let counts: HashMap<_, usize> =
unique_interest_points
.iter()
.fold(HashMap::new(), |mut h, p| {
*h.entry(p.interest).or_default() += 1;
h
});
counts.into_iter().for_each(|(interest, count)| {
eprintln!("{:?} appears {} times", interest, count);
});
unique_interest_points
.iter()
.map(|p| p.interest.into())
.try_for_each(|i: u8| writer.write_all(&i.to_le_bytes()))?;
interests_on_path
.iter()
.map(|i| *i as u16)
.try_for_each(|i| writer.write_all(&i.to_le_bytes()))?;
buckets
.iter()
.map(|b| b.start as u16)
.try_for_each(|i| writer.write_all(&i.to_le_bytes()))?;
Ok(())
}
fn optimal_simplification(points: &[Point], epsilon: f64) -> Vec<Point> {
let mut cache = HashMap::new();
simplify_prog_dyn(&points, 0, points.len(), epsilon, &mut cache);
extract_prog_dyn_solution(&points, 0, points.len(), &cache)
}
fn hybrid_simplification(points: &[Point], epsilon: f64) -> Vec<Point> {
if points.len() <= 300 {
optimal_simplification(points, epsilon)
} else {
if points.first().unwrap() == points.last().unwrap() {
let first = points.first().unwrap();
let index_farthest = points
.iter()
.enumerate()
.skip(1)
.max_by(|(_, p1), (_, p2)| {
first
.squared_distance_between(p1)
.partial_cmp(&first.squared_distance_between(p2))
.unwrap()
})
.map(|(i, _)| i)
.unwrap();
let start = &points[..(index_farthest + 1)];
let end = &points[index_farthest..];
let mut res = hybrid_simplification(start, epsilon);
res.pop();
res.append(&mut hybrid_simplification(end, epsilon));
res
} else {
let (index_farthest, farthest_distance) = points
.iter()
.map(|p| p.distance_to_segment(points.first().unwrap(), points.last().unwrap()))
.enumerate()
.max_by(|(_, d1), (_, d2)| {
if d1.is_nan() {
std::cmp::Ordering::Greater
} else {
if d2.is_nan() {
std::cmp::Ordering::Less
} else {
d1.partial_cmp(d2).unwrap()
}
}
})
.unwrap();
if farthest_distance <= epsilon {
vec![
points.first().copied().unwrap(),
points.last().copied().unwrap(),
]
} else {
let start = &points[..(index_farthest + 1)];
let end = &points[index_farthest..];
let mut res = hybrid_simplification(start, epsilon);
res.pop();
res.append(&mut hybrid_simplification(end, epsilon));
res
}
}
}
}
pub struct Bucket {
points: Vec<InterestPoint>,
start: usize,
}
fn position_interests_along_path(
interests: &mut [InterestPoint],
path: &[Point],
d: f64,
buckets_size: usize, // final points are indexed in buckets
groups_size: usize, // how many segments are compacted together
) -> Vec<Bucket> {
interests.sort_unstable_by(|p1, p2| p1.point.x.partial_cmp(&p2.point.x).unwrap());
// first compute for each segment a vec containing its nearby points
let mut positions = Vec::new();
for segment in path.windows(2) {
let mut local_interests = Vec::new();
let x0 = segment[0].x;
let x1 = segment[1].x;
let (xmin, xmax) = if x0 <= x1 { (x0, x1) } else { (x1, x0) };
let i = interests.partition_point(|p| p.point.x < xmin - d);
let interests = &interests[i..];
let i = interests.partition_point(|p| p.point.x <= xmax + d);
let interests = &interests[..i];
for interest in interests {
if interest.point.distance_to_segment(&segment[0], &segment[1]) <= d {
local_interests.push(*interest);
}
}
positions.push(local_interests);
}
// fuse points on chunks of consecutive segments together
let grouped_positions = positions
.chunks(groups_size)
.map(|c| c.iter().flatten().unique().copied().collect::<Vec<_>>())
.collect::<Vec<_>>();
// now, group the points in buckets
let chunks = grouped_positions
.iter()
.enumerate()
.flat_map(|(i, points)| points.iter().map(move |p| (i, p)))
.chunks(buckets_size);
let mut buckets = Vec::new();
for bucket_points in &chunks {
let mut bucket_points = bucket_points.peekable();
let start = bucket_points.peek().unwrap().0;
let points = bucket_points.map(|(_, p)| *p).collect();
buckets.push(Bucket { points, start });
}
buckets
}
fn detect_sharp_turns(path: &[Point], waypoints: &mut HashSet<Point>) {
path.iter()
.tuple_windows()
.map(|(a, b, c)| {
let xd1 = b.x - a.x;
let yd1 = b.y - a.y;
let angle1 = yd1.atan2(xd1);
let xd2 = c.x - b.x;
let yd2 = c.y - b.y;
let angle2 = yd2.atan2(xd2);
let adiff = angle2 - angle1;
let adiff = if adiff < 0.0 {
adiff + std::f64::consts::PI * 2.0
} else {
adiff
};
(adiff, b)
})
.filter_map(|(adiff, b)| {
if adiff > LOWER_SHARP_TURN && adiff < UPPER_SHARP_TURN {
Some(b)
} else {
None
}
})
.for_each(|b| {
waypoints.insert(*b);
});
}
#[wasm_bindgen]
pub fn convert_gpx_strings(input_str: &str) -> Vec<u8> {
let mut interests = Vec::new();
let mut output: Vec<u8> = Vec::new();
convert_gpx(input_str.as_bytes(), &mut output, &mut interests);
output
}
pub fn convert_gpx_files(input_file: &str, interests: &mut [InterestPoint]) {
let file = File::open(input_file).unwrap();
let reader = BufReader::new(file);
let output_path = Path::new(&input_file).with_extension("gpc");
let writer = BufWriter::new(File::create(output_path).unwrap());
convert_gpx(reader, writer, interests);
}
fn convert_gpx<R: Read, W: Write>(
input_reader: R,
output_writer: W,
interests: &mut [InterestPoint],
) {
// load all points composing the trace and mark commented points
// as special waypoints.
let (mut waypoints, p) = points(input_reader);
// detect sharp turns before path simplification to keep them
detect_sharp_turns(&p, &mut waypoints);
waypoints.insert(p.first().copied().unwrap());
waypoints.insert(p.last().copied().unwrap());
println!("we have {} waypoints", waypoints.len());
println!("initially we had {} points", p.len());
// simplify path
let mut rp = Vec::new();
let mut segment = Vec::new();
for point in &p {
segment.push(*point);
if waypoints.contains(point) {
if segment.len() >= 2 {
let mut s = simplify_path(&segment, 0.00015);
rp.append(&mut s);
segment = rp.pop().into_iter().collect();
}
}
}
rp.append(&mut segment);
println!("we now have {} points", rp.len());
// add interest points from open street map if we have any
let buckets = position_interests_along_path(interests, &rp, 0.001, 5, 3);
// save_svg(
// "test.svg",
// &p,
// &rp,
// buckets.iter().flat_map(|b| &b.points),
// &waypoints,
// )
// .unwrap();
save_gpc(output_writer, &rp, &waypoints, &buckets).unwrap();
}

View File

@ -1,22 +0,0 @@
use gpconv::convert_gpx_files;
fn main() {
let input_file = std::env::args().nth(1).unwrap_or("m.gpx".to_string());
let mut interests;
#[cfg(feature = "osm")]
{
let osm_file = std::env::args().nth(2);
let mut interests = if let Some(osm) = osm_file {
interests = parse_osm_data(osm);
} else {
Vec::new()
};
}
#[cfg(not(feature = "osm"))]
{
interests = Vec::new()
}
convert_gpx_files(&input_file, &mut interests);
}

View File

@ -1,36 +0,0 @@
use super::Interest;
use super::Point;
use itertools::Itertools;
use lazy_static::lazy_static;
use osmio::OSMObjBase;
use osmio::{prelude::*, ObjId};
use std::collections::{HashMap, HashSet};
use std::path::Path;
pub fn parse_osm_data<P: AsRef<Path>>(path: P) -> Vec<InterestPoint> {
let reader = osmio::read_pbf(path).ok();
reader
.map(|mut reader| {
let mut interests = Vec::new();
for obj in reader.objects() {
match obj {
osmio::obj_types::ArcOSMObj::Node(n) => {
n.lat_lon_f64().map(|(lat, lon)| {
for p in n.tags().filter_map(move |(k, v)| {
Interest::new(k, v).map(|i| InterestPoint {
point: Point { x: lon, y: lat },
interest: i,
})
}) {
interests.push(p);
}
});
}
osmio::obj_types::ArcOSMObj::Way(w) => {}
osmio::obj_types::ArcOSMObj::Relation(_) => {}
}
}
interests
})
.unwrap_or_default()
}

View File

@ -1,86 +0,0 @@
use itertools::Itertools;
use std::{
collections::HashSet,
io::{BufWriter, Write},
path::Path,
};
use crate::{interests::InterestPoint, Point};
fn save_path<W: Write>(writer: &mut W, p: &[Point], stroke: &str) -> std::io::Result<()> {
write!(
writer,
"<polyline fill='none' stroke='{}' stroke-width='0.2%' points='",
stroke
)?;
p.iter()
.try_for_each(|p| write!(writer, "{},{} ", p.x, p.y))?;
writeln!(writer, "'/>")?;
Ok(())
}
// save svg file from given path and interest points.
// useful for debugging path simplification and previewing traces.
pub fn save_svg<'a, P: AsRef<Path>, I: IntoIterator<Item = &'a InterestPoint>>(
filename: P,
p: &[Point],
rp: &[Point],
interest_points: I,
waypoints: &HashSet<Point>,
) -> std::io::Result<()> {
let mut writer = BufWriter::new(std::fs::File::create(filename)?);
let (xmin, xmax) = p
.iter()
.map(|p| p.x)
.minmax_by(|a, b| a.partial_cmp(b).unwrap())
.into_option()
.unwrap();
let (ymin, ymax) = p
.iter()
.map(|p| p.y)
.minmax_by(|a, b| a.partial_cmp(b).unwrap())
.into_option()
.unwrap();
writeln!(
&mut writer,
"<svg width='800' height='600' viewBox='{} {} {} {}'>",
xmin,
ymin,
xmax - xmin,
ymax - ymin
)?;
write!(
&mut writer,
"<rect fill='white' x='{}' y='{}' width='{}' height='{}'/>",
xmin,
ymin,
xmax - xmin,
ymax - ymin
)?;
save_path(&mut writer, &p, "red")?;
save_path(&mut writer, &rp, "black")?;
for point in interest_points {
writeln!(
&mut writer,
"<circle cx='{}' cy='{}' fill='{}' r='0.8%'/>",
point.point.x,
point.point.y,
point.color(),
)?;
}
waypoints.iter().try_for_each(|p| {
writeln!(
&mut writer,
"<circle cx='{}' cy='{}' fill='black' r='0.3%'/>",
p.x, p.y,
)
})?;
writeln!(&mut writer, "</svg>")?;
Ok(())
}