a rust tui to view amtrak train status
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

Initial commit: real-time Amtrak train tracker TUI

Terminal-based dashboard that decrypts and displays live Amtrak train
positions on a braille-rendered zoomable map with state boundaries and
route lines. Includes search/filter, jump-to-train, and per-station
delay status.

41 tests covering crypto, model parsing, viewport math, search, and
UI rendering via TestBackend.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Austin Parker edac3b79

+4546
+22
.gitignore
··· 1 + /target 2 + 3 + # Scratch files from reverse engineering 4 + *.dat 5 + *.html 6 + decrypt_trains.py 7 + PKs.js 8 + AS.js 9 + Helper.js 10 + config666.js 11 + mapApplication.js 12 + config_bundle.js 13 + page.html 14 + routeslist.json 15 + routeslist_v.json 16 + trains_raw.dat 17 + trains_decoded.dat 18 + trains_decrypted.json 19 + rttl_app.html 20 + decrypt.js 21 + utility.js 22 + xdr.js
+2466
Cargo.lock
··· 1 + # This file is automatically @generated by Cargo. 2 + # It is not intended for manual editing. 3 + version = 4 4 + 5 + [[package]] 6 + name = "aes" 7 + version = "0.8.4" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" 10 + dependencies = [ 11 + "cfg-if", 12 + "cipher", 13 + "cpufeatures", 14 + ] 15 + 16 + [[package]] 17 + name = "allocator-api2" 18 + version = "0.2.21" 19 + source = "registry+https://github.com/rust-lang/crates.io-index" 20 + checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" 21 + 22 + [[package]] 23 + name = "amtracker" 24 + version = "0.1.0" 25 + dependencies = [ 26 + "aes", 27 + "anyhow", 28 + "base64", 29 + "cbc", 30 + "crossterm", 31 + "geojson", 32 + "hex", 33 + "hmac", 34 + "pbkdf2", 35 + "ratatui", 36 + "reqwest", 37 + "serde", 38 + "serde_json", 39 + "sha1", 40 + "tokio", 41 + ] 42 + 43 + [[package]] 44 + name = "anyhow" 45 + version = "1.0.102" 46 + source = "registry+https://github.com/rust-lang/crates.io-index" 47 + checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" 48 + 49 + [[package]] 50 + name = "approx" 51 + version = "0.5.1" 52 + source = "registry+https://github.com/rust-lang/crates.io-index" 53 + checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" 54 + dependencies = [ 55 + "num-traits", 56 + ] 57 + 58 + [[package]] 59 + name = "as-slice" 60 + version = "0.1.5" 61 + source = "registry+https://github.com/rust-lang/crates.io-index" 62 + checksum = "45403b49e3954a4b8428a0ac21a4b7afadccf92bfd96273f1a58cd4812496ae0" 63 + dependencies = [ 64 + "generic-array 0.12.4", 65 + "generic-array 0.13.3", 66 + "generic-array 0.14.7", 67 + "stable_deref_trait", 68 + ] 69 + 70 + [[package]] 71 + name = "atomic-polyfill" 72 + version = "1.0.3" 73 + source = "registry+https://github.com/rust-lang/crates.io-index" 74 + checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4" 75 + dependencies = [ 76 + "critical-section", 77 + ] 78 + 79 + [[package]] 80 + name = "atomic-waker" 81 + version = "1.1.2" 82 + source = "registry+https://github.com/rust-lang/crates.io-index" 83 + checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" 84 + 85 + [[package]] 86 + name = "autocfg" 87 + version = "1.5.0" 88 + source = "registry+https://github.com/rust-lang/crates.io-index" 89 + checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" 90 + 91 + [[package]] 92 + name = "base64" 93 + version = "0.22.1" 94 + source = "registry+https://github.com/rust-lang/crates.io-index" 95 + checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 96 + 97 + [[package]] 98 + name = "bitflags" 99 + version = "2.11.0" 100 + source = "registry+https://github.com/rust-lang/crates.io-index" 101 + checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" 102 + 103 + [[package]] 104 + name = "block-buffer" 105 + version = "0.10.4" 106 + source = "registry+https://github.com/rust-lang/crates.io-index" 107 + checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 108 + dependencies = [ 109 + "generic-array 0.14.7", 110 + ] 111 + 112 + [[package]] 113 + name = "block-padding" 114 + version = "0.3.3" 115 + source = "registry+https://github.com/rust-lang/crates.io-index" 116 + checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" 117 + dependencies = [ 118 + "generic-array 0.14.7", 119 + ] 120 + 121 + [[package]] 122 + name = "bumpalo" 123 + version = "3.20.2" 124 + source = "registry+https://github.com/rust-lang/crates.io-index" 125 + checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" 126 + 127 + [[package]] 128 + name = "byteorder" 129 + version = "1.5.0" 130 + source = "registry+https://github.com/rust-lang/crates.io-index" 131 + checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 132 + 133 + [[package]] 134 + name = "bytes" 135 + version = "1.11.1" 136 + source = "registry+https://github.com/rust-lang/crates.io-index" 137 + checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" 138 + 139 + [[package]] 140 + name = "cassowary" 141 + version = "0.3.0" 142 + source = "registry+https://github.com/rust-lang/crates.io-index" 143 + checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" 144 + 145 + [[package]] 146 + name = "castaway" 147 + version = "0.2.4" 148 + source = "registry+https://github.com/rust-lang/crates.io-index" 149 + checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a" 150 + dependencies = [ 151 + "rustversion", 152 + ] 153 + 154 + [[package]] 155 + name = "cbc" 156 + version = "0.1.2" 157 + source = "registry+https://github.com/rust-lang/crates.io-index" 158 + checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" 159 + dependencies = [ 160 + "cipher", 161 + ] 162 + 163 + [[package]] 164 + name = "cc" 165 + version = "1.2.57" 166 + source = "registry+https://github.com/rust-lang/crates.io-index" 167 + checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" 168 + dependencies = [ 169 + "find-msvc-tools", 170 + "shlex", 171 + ] 172 + 173 + [[package]] 174 + name = "cfg-if" 175 + version = "1.0.4" 176 + source = "registry+https://github.com/rust-lang/crates.io-index" 177 + checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 178 + 179 + [[package]] 180 + name = "cipher" 181 + version = "0.4.4" 182 + source = "registry+https://github.com/rust-lang/crates.io-index" 183 + checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" 184 + dependencies = [ 185 + "crypto-common", 186 + "inout", 187 + ] 188 + 189 + [[package]] 190 + name = "compact_str" 191 + version = "0.8.1" 192 + source = "registry+https://github.com/rust-lang/crates.io-index" 193 + checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" 194 + dependencies = [ 195 + "castaway", 196 + "cfg-if", 197 + "itoa", 198 + "rustversion", 199 + "ryu", 200 + "static_assertions", 201 + ] 202 + 203 + [[package]] 204 + name = "core-foundation" 205 + version = "0.9.4" 206 + source = "registry+https://github.com/rust-lang/crates.io-index" 207 + checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" 208 + dependencies = [ 209 + "core-foundation-sys", 210 + "libc", 211 + ] 212 + 213 + [[package]] 214 + name = "core-foundation" 215 + version = "0.10.1" 216 + source = "registry+https://github.com/rust-lang/crates.io-index" 217 + checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" 218 + dependencies = [ 219 + "core-foundation-sys", 220 + "libc", 221 + ] 222 + 223 + [[package]] 224 + name = "core-foundation-sys" 225 + version = "0.8.7" 226 + source = "registry+https://github.com/rust-lang/crates.io-index" 227 + checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 228 + 229 + [[package]] 230 + name = "cpufeatures" 231 + version = "0.2.17" 232 + source = "registry+https://github.com/rust-lang/crates.io-index" 233 + checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" 234 + dependencies = [ 235 + "libc", 236 + ] 237 + 238 + [[package]] 239 + name = "critical-section" 240 + version = "1.2.0" 241 + source = "registry+https://github.com/rust-lang/crates.io-index" 242 + checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" 243 + 244 + [[package]] 245 + name = "crossterm" 246 + version = "0.28.1" 247 + source = "registry+https://github.com/rust-lang/crates.io-index" 248 + checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" 249 + dependencies = [ 250 + "bitflags", 251 + "crossterm_winapi", 252 + "mio", 253 + "parking_lot", 254 + "rustix 0.38.44", 255 + "signal-hook", 256 + "signal-hook-mio", 257 + "winapi", 258 + ] 259 + 260 + [[package]] 261 + name = "crossterm_winapi" 262 + version = "0.9.1" 263 + source = "registry+https://github.com/rust-lang/crates.io-index" 264 + checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" 265 + dependencies = [ 266 + "winapi", 267 + ] 268 + 269 + [[package]] 270 + name = "crypto-common" 271 + version = "0.1.7" 272 + source = "registry+https://github.com/rust-lang/crates.io-index" 273 + checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" 274 + dependencies = [ 275 + "generic-array 0.14.7", 276 + "typenum", 277 + ] 278 + 279 + [[package]] 280 + name = "darling" 281 + version = "0.23.0" 282 + source = "registry+https://github.com/rust-lang/crates.io-index" 283 + checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" 284 + dependencies = [ 285 + "darling_core", 286 + "darling_macro", 287 + ] 288 + 289 + [[package]] 290 + name = "darling_core" 291 + version = "0.23.0" 292 + source = "registry+https://github.com/rust-lang/crates.io-index" 293 + checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" 294 + dependencies = [ 295 + "ident_case", 296 + "proc-macro2", 297 + "quote", 298 + "strsim", 299 + "syn", 300 + ] 301 + 302 + [[package]] 303 + name = "darling_macro" 304 + version = "0.23.0" 305 + source = "registry+https://github.com/rust-lang/crates.io-index" 306 + checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" 307 + dependencies = [ 308 + "darling_core", 309 + "quote", 310 + "syn", 311 + ] 312 + 313 + [[package]] 314 + name = "digest" 315 + version = "0.10.7" 316 + source = "registry+https://github.com/rust-lang/crates.io-index" 317 + checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 318 + dependencies = [ 319 + "block-buffer", 320 + "crypto-common", 321 + "subtle", 322 + ] 323 + 324 + [[package]] 325 + name = "displaydoc" 326 + version = "0.2.5" 327 + source = "registry+https://github.com/rust-lang/crates.io-index" 328 + checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" 329 + dependencies = [ 330 + "proc-macro2", 331 + "quote", 332 + "syn", 333 + ] 334 + 335 + [[package]] 336 + name = "either" 337 + version = "1.15.0" 338 + source = "registry+https://github.com/rust-lang/crates.io-index" 339 + checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 340 + 341 + [[package]] 342 + name = "encoding_rs" 343 + version = "0.8.35" 344 + source = "registry+https://github.com/rust-lang/crates.io-index" 345 + checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" 346 + dependencies = [ 347 + "cfg-if", 348 + ] 349 + 350 + [[package]] 351 + name = "equivalent" 352 + version = "1.0.2" 353 + source = "registry+https://github.com/rust-lang/crates.io-index" 354 + checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" 355 + 356 + [[package]] 357 + name = "errno" 358 + version = "0.3.14" 359 + source = "registry+https://github.com/rust-lang/crates.io-index" 360 + checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" 361 + dependencies = [ 362 + "libc", 363 + "windows-sys 0.61.2", 364 + ] 365 + 366 + [[package]] 367 + name = "fastrand" 368 + version = "2.3.0" 369 + source = "registry+https://github.com/rust-lang/crates.io-index" 370 + checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 371 + 372 + [[package]] 373 + name = "find-msvc-tools" 374 + version = "0.1.9" 375 + source = "registry+https://github.com/rust-lang/crates.io-index" 376 + checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" 377 + 378 + [[package]] 379 + name = "fnv" 380 + version = "1.0.7" 381 + source = "registry+https://github.com/rust-lang/crates.io-index" 382 + checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 383 + 384 + [[package]] 385 + name = "foldhash" 386 + version = "0.1.5" 387 + source = "registry+https://github.com/rust-lang/crates.io-index" 388 + checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 389 + 390 + [[package]] 391 + name = "foreign-types" 392 + version = "0.3.2" 393 + source = "registry+https://github.com/rust-lang/crates.io-index" 394 + checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 395 + dependencies = [ 396 + "foreign-types-shared", 397 + ] 398 + 399 + [[package]] 400 + name = "foreign-types-shared" 401 + version = "0.1.1" 402 + source = "registry+https://github.com/rust-lang/crates.io-index" 403 + checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 404 + 405 + [[package]] 406 + name = "form_urlencoded" 407 + version = "1.2.2" 408 + source = "registry+https://github.com/rust-lang/crates.io-index" 409 + checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" 410 + dependencies = [ 411 + "percent-encoding", 412 + ] 413 + 414 + [[package]] 415 + name = "futures-channel" 416 + version = "0.3.32" 417 + source = "registry+https://github.com/rust-lang/crates.io-index" 418 + checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" 419 + dependencies = [ 420 + "futures-core", 421 + ] 422 + 423 + [[package]] 424 + name = "futures-core" 425 + version = "0.3.32" 426 + source = "registry+https://github.com/rust-lang/crates.io-index" 427 + checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" 428 + 429 + [[package]] 430 + name = "futures-sink" 431 + version = "0.3.32" 432 + source = "registry+https://github.com/rust-lang/crates.io-index" 433 + checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" 434 + 435 + [[package]] 436 + name = "futures-task" 437 + version = "0.3.32" 438 + source = "registry+https://github.com/rust-lang/crates.io-index" 439 + checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" 440 + 441 + [[package]] 442 + name = "futures-util" 443 + version = "0.3.32" 444 + source = "registry+https://github.com/rust-lang/crates.io-index" 445 + checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" 446 + dependencies = [ 447 + "futures-core", 448 + "futures-task", 449 + "pin-project-lite", 450 + "slab", 451 + ] 452 + 453 + [[package]] 454 + name = "generic-array" 455 + version = "0.12.4" 456 + source = "registry+https://github.com/rust-lang/crates.io-index" 457 + checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" 458 + dependencies = [ 459 + "typenum", 460 + ] 461 + 462 + [[package]] 463 + name = "generic-array" 464 + version = "0.13.3" 465 + source = "registry+https://github.com/rust-lang/crates.io-index" 466 + checksum = "f797e67af32588215eaaab8327027ee8e71b9dd0b2b26996aedf20c030fce309" 467 + dependencies = [ 468 + "typenum", 469 + ] 470 + 471 + [[package]] 472 + name = "generic-array" 473 + version = "0.14.7" 474 + source = "registry+https://github.com/rust-lang/crates.io-index" 475 + checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 476 + dependencies = [ 477 + "typenum", 478 + "version_check", 479 + ] 480 + 481 + [[package]] 482 + name = "geo-types" 483 + version = "0.7.18" 484 + source = "registry+https://github.com/rust-lang/crates.io-index" 485 + checksum = "24f8647af4005fa11da47cd56252c6ef030be8fa97bdbf355e7dfb6348f0a82c" 486 + dependencies = [ 487 + "approx", 488 + "num-traits", 489 + "rstar 0.10.0", 490 + "rstar 0.11.0", 491 + "rstar 0.12.2", 492 + "rstar 0.8.4", 493 + "rstar 0.9.3", 494 + "serde", 495 + ] 496 + 497 + [[package]] 498 + name = "geojson" 499 + version = "0.24.2" 500 + source = "registry+https://github.com/rust-lang/crates.io-index" 501 + checksum = "e26f3c45b36fccc9cf2805e61d4da6bc4bbd5a3a9589b01afa3a40eff703bd79" 502 + dependencies = [ 503 + "geo-types", 504 + "log", 505 + "serde", 506 + "serde_json", 507 + "thiserror", 508 + ] 509 + 510 + [[package]] 511 + name = "getrandom" 512 + version = "0.2.17" 513 + source = "registry+https://github.com/rust-lang/crates.io-index" 514 + checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" 515 + dependencies = [ 516 + "cfg-if", 517 + "libc", 518 + "wasi", 519 + ] 520 + 521 + [[package]] 522 + name = "getrandom" 523 + version = "0.4.2" 524 + source = "registry+https://github.com/rust-lang/crates.io-index" 525 + checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" 526 + dependencies = [ 527 + "cfg-if", 528 + "libc", 529 + "r-efi", 530 + "wasip2", 531 + "wasip3", 532 + ] 533 + 534 + [[package]] 535 + name = "h2" 536 + version = "0.4.13" 537 + source = "registry+https://github.com/rust-lang/crates.io-index" 538 + checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" 539 + dependencies = [ 540 + "atomic-waker", 541 + "bytes", 542 + "fnv", 543 + "futures-core", 544 + "futures-sink", 545 + "http", 546 + "indexmap", 547 + "slab", 548 + "tokio", 549 + "tokio-util", 550 + "tracing", 551 + ] 552 + 553 + [[package]] 554 + name = "hash32" 555 + version = "0.1.1" 556 + source = "registry+https://github.com/rust-lang/crates.io-index" 557 + checksum = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc" 558 + dependencies = [ 559 + "byteorder", 560 + ] 561 + 562 + [[package]] 563 + name = "hash32" 564 + version = "0.2.1" 565 + source = "registry+https://github.com/rust-lang/crates.io-index" 566 + checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" 567 + dependencies = [ 568 + "byteorder", 569 + ] 570 + 571 + [[package]] 572 + name = "hash32" 573 + version = "0.3.1" 574 + source = "registry+https://github.com/rust-lang/crates.io-index" 575 + checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" 576 + dependencies = [ 577 + "byteorder", 578 + ] 579 + 580 + [[package]] 581 + name = "hashbrown" 582 + version = "0.15.5" 583 + source = "registry+https://github.com/rust-lang/crates.io-index" 584 + checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" 585 + dependencies = [ 586 + "allocator-api2", 587 + "equivalent", 588 + "foldhash", 589 + ] 590 + 591 + [[package]] 592 + name = "hashbrown" 593 + version = "0.16.1" 594 + source = "registry+https://github.com/rust-lang/crates.io-index" 595 + checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" 596 + 597 + [[package]] 598 + name = "heapless" 599 + version = "0.6.1" 600 + source = "registry+https://github.com/rust-lang/crates.io-index" 601 + checksum = "634bd4d29cbf24424d0a4bfcbf80c6960129dc24424752a7d1d1390607023422" 602 + dependencies = [ 603 + "as-slice", 604 + "generic-array 0.14.7", 605 + "hash32 0.1.1", 606 + "stable_deref_trait", 607 + ] 608 + 609 + [[package]] 610 + name = "heapless" 611 + version = "0.7.17" 612 + source = "registry+https://github.com/rust-lang/crates.io-index" 613 + checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" 614 + dependencies = [ 615 + "atomic-polyfill", 616 + "hash32 0.2.1", 617 + "rustc_version", 618 + "spin", 619 + "stable_deref_trait", 620 + ] 621 + 622 + [[package]] 623 + name = "heapless" 624 + version = "0.8.0" 625 + source = "registry+https://github.com/rust-lang/crates.io-index" 626 + checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" 627 + dependencies = [ 628 + "hash32 0.3.1", 629 + "stable_deref_trait", 630 + ] 631 + 632 + [[package]] 633 + name = "heck" 634 + version = "0.5.0" 635 + source = "registry+https://github.com/rust-lang/crates.io-index" 636 + checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 637 + 638 + [[package]] 639 + name = "hex" 640 + version = "0.4.3" 641 + source = "registry+https://github.com/rust-lang/crates.io-index" 642 + checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 643 + 644 + [[package]] 645 + name = "hmac" 646 + version = "0.12.1" 647 + source = "registry+https://github.com/rust-lang/crates.io-index" 648 + checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" 649 + dependencies = [ 650 + "digest", 651 + ] 652 + 653 + [[package]] 654 + name = "http" 655 + version = "1.4.0" 656 + source = "registry+https://github.com/rust-lang/crates.io-index" 657 + checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" 658 + dependencies = [ 659 + "bytes", 660 + "itoa", 661 + ] 662 + 663 + [[package]] 664 + name = "http-body" 665 + version = "1.0.1" 666 + source = "registry+https://github.com/rust-lang/crates.io-index" 667 + checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 668 + dependencies = [ 669 + "bytes", 670 + "http", 671 + ] 672 + 673 + [[package]] 674 + name = "http-body-util" 675 + version = "0.1.3" 676 + source = "registry+https://github.com/rust-lang/crates.io-index" 677 + checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" 678 + dependencies = [ 679 + "bytes", 680 + "futures-core", 681 + "http", 682 + "http-body", 683 + "pin-project-lite", 684 + ] 685 + 686 + [[package]] 687 + name = "httparse" 688 + version = "1.10.1" 689 + source = "registry+https://github.com/rust-lang/crates.io-index" 690 + checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" 691 + 692 + [[package]] 693 + name = "hyper" 694 + version = "1.8.1" 695 + source = "registry+https://github.com/rust-lang/crates.io-index" 696 + checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" 697 + dependencies = [ 698 + "atomic-waker", 699 + "bytes", 700 + "futures-channel", 701 + "futures-core", 702 + "h2", 703 + "http", 704 + "http-body", 705 + "httparse", 706 + "itoa", 707 + "pin-project-lite", 708 + "pin-utils", 709 + "smallvec", 710 + "tokio", 711 + "want", 712 + ] 713 + 714 + [[package]] 715 + name = "hyper-rustls" 716 + version = "0.27.7" 717 + source = "registry+https://github.com/rust-lang/crates.io-index" 718 + checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" 719 + dependencies = [ 720 + "http", 721 + "hyper", 722 + "hyper-util", 723 + "rustls", 724 + "rustls-pki-types", 725 + "tokio", 726 + "tokio-rustls", 727 + "tower-service", 728 + ] 729 + 730 + [[package]] 731 + name = "hyper-tls" 732 + version = "0.6.0" 733 + source = "registry+https://github.com/rust-lang/crates.io-index" 734 + checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" 735 + dependencies = [ 736 + "bytes", 737 + "http-body-util", 738 + "hyper", 739 + "hyper-util", 740 + "native-tls", 741 + "tokio", 742 + "tokio-native-tls", 743 + "tower-service", 744 + ] 745 + 746 + [[package]] 747 + name = "hyper-util" 748 + version = "0.1.20" 749 + source = "registry+https://github.com/rust-lang/crates.io-index" 750 + checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" 751 + dependencies = [ 752 + "base64", 753 + "bytes", 754 + "futures-channel", 755 + "futures-util", 756 + "http", 757 + "http-body", 758 + "hyper", 759 + "ipnet", 760 + "libc", 761 + "percent-encoding", 762 + "pin-project-lite", 763 + "socket2", 764 + "system-configuration", 765 + "tokio", 766 + "tower-service", 767 + "tracing", 768 + "windows-registry", 769 + ] 770 + 771 + [[package]] 772 + name = "icu_collections" 773 + version = "2.1.1" 774 + source = "registry+https://github.com/rust-lang/crates.io-index" 775 + checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" 776 + dependencies = [ 777 + "displaydoc", 778 + "potential_utf", 779 + "yoke", 780 + "zerofrom", 781 + "zerovec", 782 + ] 783 + 784 + [[package]] 785 + name = "icu_locale_core" 786 + version = "2.1.1" 787 + source = "registry+https://github.com/rust-lang/crates.io-index" 788 + checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" 789 + dependencies = [ 790 + "displaydoc", 791 + "litemap", 792 + "tinystr", 793 + "writeable", 794 + "zerovec", 795 + ] 796 + 797 + [[package]] 798 + name = "icu_normalizer" 799 + version = "2.1.1" 800 + source = "registry+https://github.com/rust-lang/crates.io-index" 801 + checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" 802 + dependencies = [ 803 + "icu_collections", 804 + "icu_normalizer_data", 805 + "icu_properties", 806 + "icu_provider", 807 + "smallvec", 808 + "zerovec", 809 + ] 810 + 811 + [[package]] 812 + name = "icu_normalizer_data" 813 + version = "2.1.1" 814 + source = "registry+https://github.com/rust-lang/crates.io-index" 815 + checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" 816 + 817 + [[package]] 818 + name = "icu_properties" 819 + version = "2.1.2" 820 + source = "registry+https://github.com/rust-lang/crates.io-index" 821 + checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" 822 + dependencies = [ 823 + "icu_collections", 824 + "icu_locale_core", 825 + "icu_properties_data", 826 + "icu_provider", 827 + "zerotrie", 828 + "zerovec", 829 + ] 830 + 831 + [[package]] 832 + name = "icu_properties_data" 833 + version = "2.1.2" 834 + source = "registry+https://github.com/rust-lang/crates.io-index" 835 + checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" 836 + 837 + [[package]] 838 + name = "icu_provider" 839 + version = "2.1.1" 840 + source = "registry+https://github.com/rust-lang/crates.io-index" 841 + checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" 842 + dependencies = [ 843 + "displaydoc", 844 + "icu_locale_core", 845 + "writeable", 846 + "yoke", 847 + "zerofrom", 848 + "zerotrie", 849 + "zerovec", 850 + ] 851 + 852 + [[package]] 853 + name = "id-arena" 854 + version = "2.3.0" 855 + source = "registry+https://github.com/rust-lang/crates.io-index" 856 + checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" 857 + 858 + [[package]] 859 + name = "ident_case" 860 + version = "1.0.1" 861 + source = "registry+https://github.com/rust-lang/crates.io-index" 862 + checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 863 + 864 + [[package]] 865 + name = "idna" 866 + version = "1.1.0" 867 + source = "registry+https://github.com/rust-lang/crates.io-index" 868 + checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" 869 + dependencies = [ 870 + "idna_adapter", 871 + "smallvec", 872 + "utf8_iter", 873 + ] 874 + 875 + [[package]] 876 + name = "idna_adapter" 877 + version = "1.2.1" 878 + source = "registry+https://github.com/rust-lang/crates.io-index" 879 + checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" 880 + dependencies = [ 881 + "icu_normalizer", 882 + "icu_properties", 883 + ] 884 + 885 + [[package]] 886 + name = "indexmap" 887 + version = "2.13.0" 888 + source = "registry+https://github.com/rust-lang/crates.io-index" 889 + checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" 890 + dependencies = [ 891 + "equivalent", 892 + "hashbrown 0.16.1", 893 + "serde", 894 + "serde_core", 895 + ] 896 + 897 + [[package]] 898 + name = "indoc" 899 + version = "2.0.7" 900 + source = "registry+https://github.com/rust-lang/crates.io-index" 901 + checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" 902 + dependencies = [ 903 + "rustversion", 904 + ] 905 + 906 + [[package]] 907 + name = "inout" 908 + version = "0.1.4" 909 + source = "registry+https://github.com/rust-lang/crates.io-index" 910 + checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" 911 + dependencies = [ 912 + "block-padding", 913 + "generic-array 0.14.7", 914 + ] 915 + 916 + [[package]] 917 + name = "instability" 918 + version = "0.3.11" 919 + source = "registry+https://github.com/rust-lang/crates.io-index" 920 + checksum = "357b7205c6cd18dd2c86ed312d1e70add149aea98e7ef72b9fdf0270e555c11d" 921 + dependencies = [ 922 + "darling", 923 + "indoc", 924 + "proc-macro2", 925 + "quote", 926 + "syn", 927 + ] 928 + 929 + [[package]] 930 + name = "ipnet" 931 + version = "2.12.0" 932 + source = "registry+https://github.com/rust-lang/crates.io-index" 933 + checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" 934 + 935 + [[package]] 936 + name = "iri-string" 937 + version = "0.7.10" 938 + source = "registry+https://github.com/rust-lang/crates.io-index" 939 + checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" 940 + dependencies = [ 941 + "memchr", 942 + "serde", 943 + ] 944 + 945 + [[package]] 946 + name = "itertools" 947 + version = "0.13.0" 948 + source = "registry+https://github.com/rust-lang/crates.io-index" 949 + checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" 950 + dependencies = [ 951 + "either", 952 + ] 953 + 954 + [[package]] 955 + name = "itoa" 956 + version = "1.0.17" 957 + source = "registry+https://github.com/rust-lang/crates.io-index" 958 + checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" 959 + 960 + [[package]] 961 + name = "js-sys" 962 + version = "0.3.91" 963 + source = "registry+https://github.com/rust-lang/crates.io-index" 964 + checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" 965 + dependencies = [ 966 + "once_cell", 967 + "wasm-bindgen", 968 + ] 969 + 970 + [[package]] 971 + name = "leb128fmt" 972 + version = "0.1.0" 973 + source = "registry+https://github.com/rust-lang/crates.io-index" 974 + checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" 975 + 976 + [[package]] 977 + name = "libc" 978 + version = "0.2.183" 979 + source = "registry+https://github.com/rust-lang/crates.io-index" 980 + checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" 981 + 982 + [[package]] 983 + name = "libm" 984 + version = "0.2.16" 985 + source = "registry+https://github.com/rust-lang/crates.io-index" 986 + checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" 987 + 988 + [[package]] 989 + name = "linux-raw-sys" 990 + version = "0.4.15" 991 + source = "registry+https://github.com/rust-lang/crates.io-index" 992 + checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" 993 + 994 + [[package]] 995 + name = "linux-raw-sys" 996 + version = "0.12.1" 997 + source = "registry+https://github.com/rust-lang/crates.io-index" 998 + checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" 999 + 1000 + [[package]] 1001 + name = "litemap" 1002 + version = "0.8.1" 1003 + source = "registry+https://github.com/rust-lang/crates.io-index" 1004 + checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" 1005 + 1006 + [[package]] 1007 + name = "lock_api" 1008 + version = "0.4.14" 1009 + source = "registry+https://github.com/rust-lang/crates.io-index" 1010 + checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" 1011 + dependencies = [ 1012 + "scopeguard", 1013 + ] 1014 + 1015 + [[package]] 1016 + name = "log" 1017 + version = "0.4.29" 1018 + source = "registry+https://github.com/rust-lang/crates.io-index" 1019 + checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" 1020 + 1021 + [[package]] 1022 + name = "lru" 1023 + version = "0.12.5" 1024 + source = "registry+https://github.com/rust-lang/crates.io-index" 1025 + checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" 1026 + dependencies = [ 1027 + "hashbrown 0.15.5", 1028 + ] 1029 + 1030 + [[package]] 1031 + name = "memchr" 1032 + version = "2.8.0" 1033 + source = "registry+https://github.com/rust-lang/crates.io-index" 1034 + checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" 1035 + 1036 + [[package]] 1037 + name = "mime" 1038 + version = "0.3.17" 1039 + source = "registry+https://github.com/rust-lang/crates.io-index" 1040 + checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 1041 + 1042 + [[package]] 1043 + name = "mio" 1044 + version = "1.1.1" 1045 + source = "registry+https://github.com/rust-lang/crates.io-index" 1046 + checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" 1047 + dependencies = [ 1048 + "libc", 1049 + "log", 1050 + "wasi", 1051 + "windows-sys 0.61.2", 1052 + ] 1053 + 1054 + [[package]] 1055 + name = "native-tls" 1056 + version = "0.2.18" 1057 + source = "registry+https://github.com/rust-lang/crates.io-index" 1058 + checksum = "465500e14ea162429d264d44189adc38b199b62b1c21eea9f69e4b73cb03bbf2" 1059 + dependencies = [ 1060 + "libc", 1061 + "log", 1062 + "openssl", 1063 + "openssl-probe", 1064 + "openssl-sys", 1065 + "schannel", 1066 + "security-framework", 1067 + "security-framework-sys", 1068 + "tempfile", 1069 + ] 1070 + 1071 + [[package]] 1072 + name = "num-traits" 1073 + version = "0.2.19" 1074 + source = "registry+https://github.com/rust-lang/crates.io-index" 1075 + checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 1076 + dependencies = [ 1077 + "autocfg", 1078 + "libm", 1079 + ] 1080 + 1081 + [[package]] 1082 + name = "once_cell" 1083 + version = "1.21.4" 1084 + source = "registry+https://github.com/rust-lang/crates.io-index" 1085 + checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" 1086 + 1087 + [[package]] 1088 + name = "openssl" 1089 + version = "0.10.76" 1090 + source = "registry+https://github.com/rust-lang/crates.io-index" 1091 + checksum = "951c002c75e16ea2c65b8c7e4d3d51d5530d8dfa7d060b4776828c88cfb18ecf" 1092 + dependencies = [ 1093 + "bitflags", 1094 + "cfg-if", 1095 + "foreign-types", 1096 + "libc", 1097 + "once_cell", 1098 + "openssl-macros", 1099 + "openssl-sys", 1100 + ] 1101 + 1102 + [[package]] 1103 + name = "openssl-macros" 1104 + version = "0.1.1" 1105 + source = "registry+https://github.com/rust-lang/crates.io-index" 1106 + checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 1107 + dependencies = [ 1108 + "proc-macro2", 1109 + "quote", 1110 + "syn", 1111 + ] 1112 + 1113 + [[package]] 1114 + name = "openssl-probe" 1115 + version = "0.2.1" 1116 + source = "registry+https://github.com/rust-lang/crates.io-index" 1117 + checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" 1118 + 1119 + [[package]] 1120 + name = "openssl-sys" 1121 + version = "0.9.112" 1122 + source = "registry+https://github.com/rust-lang/crates.io-index" 1123 + checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" 1124 + dependencies = [ 1125 + "cc", 1126 + "libc", 1127 + "pkg-config", 1128 + "vcpkg", 1129 + ] 1130 + 1131 + [[package]] 1132 + name = "parking_lot" 1133 + version = "0.12.5" 1134 + source = "registry+https://github.com/rust-lang/crates.io-index" 1135 + checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" 1136 + dependencies = [ 1137 + "lock_api", 1138 + "parking_lot_core", 1139 + ] 1140 + 1141 + [[package]] 1142 + name = "parking_lot_core" 1143 + version = "0.9.12" 1144 + source = "registry+https://github.com/rust-lang/crates.io-index" 1145 + checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" 1146 + dependencies = [ 1147 + "cfg-if", 1148 + "libc", 1149 + "redox_syscall", 1150 + "smallvec", 1151 + "windows-link", 1152 + ] 1153 + 1154 + [[package]] 1155 + name = "paste" 1156 + version = "1.0.15" 1157 + source = "registry+https://github.com/rust-lang/crates.io-index" 1158 + checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 1159 + 1160 + [[package]] 1161 + name = "pbkdf2" 1162 + version = "0.12.2" 1163 + source = "registry+https://github.com/rust-lang/crates.io-index" 1164 + checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" 1165 + dependencies = [ 1166 + "digest", 1167 + "hmac", 1168 + ] 1169 + 1170 + [[package]] 1171 + name = "pdqselect" 1172 + version = "0.1.0" 1173 + source = "registry+https://github.com/rust-lang/crates.io-index" 1174 + checksum = "4ec91767ecc0a0bbe558ce8c9da33c068066c57ecc8bb8477ef8c1ad3ef77c27" 1175 + 1176 + [[package]] 1177 + name = "percent-encoding" 1178 + version = "2.3.2" 1179 + source = "registry+https://github.com/rust-lang/crates.io-index" 1180 + checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" 1181 + 1182 + [[package]] 1183 + name = "pin-project-lite" 1184 + version = "0.2.17" 1185 + source = "registry+https://github.com/rust-lang/crates.io-index" 1186 + checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" 1187 + 1188 + [[package]] 1189 + name = "pin-utils" 1190 + version = "0.1.0" 1191 + source = "registry+https://github.com/rust-lang/crates.io-index" 1192 + checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1193 + 1194 + [[package]] 1195 + name = "pkg-config" 1196 + version = "0.3.32" 1197 + source = "registry+https://github.com/rust-lang/crates.io-index" 1198 + checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" 1199 + 1200 + [[package]] 1201 + name = "potential_utf" 1202 + version = "0.1.4" 1203 + source = "registry+https://github.com/rust-lang/crates.io-index" 1204 + checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" 1205 + dependencies = [ 1206 + "zerovec", 1207 + ] 1208 + 1209 + [[package]] 1210 + name = "prettyplease" 1211 + version = "0.2.37" 1212 + source = "registry+https://github.com/rust-lang/crates.io-index" 1213 + checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" 1214 + dependencies = [ 1215 + "proc-macro2", 1216 + "syn", 1217 + ] 1218 + 1219 + [[package]] 1220 + name = "proc-macro2" 1221 + version = "1.0.106" 1222 + source = "registry+https://github.com/rust-lang/crates.io-index" 1223 + checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" 1224 + dependencies = [ 1225 + "unicode-ident", 1226 + ] 1227 + 1228 + [[package]] 1229 + name = "quote" 1230 + version = "1.0.45" 1231 + source = "registry+https://github.com/rust-lang/crates.io-index" 1232 + checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" 1233 + dependencies = [ 1234 + "proc-macro2", 1235 + ] 1236 + 1237 + [[package]] 1238 + name = "r-efi" 1239 + version = "6.0.0" 1240 + source = "registry+https://github.com/rust-lang/crates.io-index" 1241 + checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" 1242 + 1243 + [[package]] 1244 + name = "ratatui" 1245 + version = "0.29.0" 1246 + source = "registry+https://github.com/rust-lang/crates.io-index" 1247 + checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" 1248 + dependencies = [ 1249 + "bitflags", 1250 + "cassowary", 1251 + "compact_str", 1252 + "crossterm", 1253 + "indoc", 1254 + "instability", 1255 + "itertools", 1256 + "lru", 1257 + "paste", 1258 + "strum", 1259 + "unicode-segmentation", 1260 + "unicode-truncate", 1261 + "unicode-width 0.2.0", 1262 + ] 1263 + 1264 + [[package]] 1265 + name = "redox_syscall" 1266 + version = "0.5.18" 1267 + source = "registry+https://github.com/rust-lang/crates.io-index" 1268 + checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" 1269 + dependencies = [ 1270 + "bitflags", 1271 + ] 1272 + 1273 + [[package]] 1274 + name = "reqwest" 1275 + version = "0.12.28" 1276 + source = "registry+https://github.com/rust-lang/crates.io-index" 1277 + checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" 1278 + dependencies = [ 1279 + "base64", 1280 + "bytes", 1281 + "encoding_rs", 1282 + "futures-core", 1283 + "h2", 1284 + "http", 1285 + "http-body", 1286 + "http-body-util", 1287 + "hyper", 1288 + "hyper-rustls", 1289 + "hyper-tls", 1290 + "hyper-util", 1291 + "js-sys", 1292 + "log", 1293 + "mime", 1294 + "native-tls", 1295 + "percent-encoding", 1296 + "pin-project-lite", 1297 + "rustls-pki-types", 1298 + "serde", 1299 + "serde_json", 1300 + "serde_urlencoded", 1301 + "sync_wrapper", 1302 + "tokio", 1303 + "tokio-native-tls", 1304 + "tower", 1305 + "tower-http", 1306 + "tower-service", 1307 + "url", 1308 + "wasm-bindgen", 1309 + "wasm-bindgen-futures", 1310 + "web-sys", 1311 + ] 1312 + 1313 + [[package]] 1314 + name = "ring" 1315 + version = "0.17.14" 1316 + source = "registry+https://github.com/rust-lang/crates.io-index" 1317 + checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" 1318 + dependencies = [ 1319 + "cc", 1320 + "cfg-if", 1321 + "getrandom 0.2.17", 1322 + "libc", 1323 + "untrusted", 1324 + "windows-sys 0.52.0", 1325 + ] 1326 + 1327 + [[package]] 1328 + name = "rstar" 1329 + version = "0.8.4" 1330 + source = "registry+https://github.com/rust-lang/crates.io-index" 1331 + checksum = "3a45c0e8804d37e4d97e55c6f258bc9ad9c5ee7b07437009dd152d764949a27c" 1332 + dependencies = [ 1333 + "heapless 0.6.1", 1334 + "num-traits", 1335 + "pdqselect", 1336 + "serde", 1337 + "smallvec", 1338 + ] 1339 + 1340 + [[package]] 1341 + name = "rstar" 1342 + version = "0.9.3" 1343 + source = "registry+https://github.com/rust-lang/crates.io-index" 1344 + checksum = "b40f1bfe5acdab44bc63e6699c28b74f75ec43afb59f3eda01e145aff86a25fa" 1345 + dependencies = [ 1346 + "heapless 0.7.17", 1347 + "num-traits", 1348 + "serde", 1349 + "smallvec", 1350 + ] 1351 + 1352 + [[package]] 1353 + name = "rstar" 1354 + version = "0.10.0" 1355 + source = "registry+https://github.com/rust-lang/crates.io-index" 1356 + checksum = "1f39465655a1e3d8ae79c6d9e007f4953bfc5d55297602df9dc38f9ae9f1359a" 1357 + dependencies = [ 1358 + "heapless 0.7.17", 1359 + "num-traits", 1360 + "serde", 1361 + "smallvec", 1362 + ] 1363 + 1364 + [[package]] 1365 + name = "rstar" 1366 + version = "0.11.0" 1367 + source = "registry+https://github.com/rust-lang/crates.io-index" 1368 + checksum = "73111312eb7a2287d229f06c00ff35b51ddee180f017ab6dec1f69d62ac098d6" 1369 + dependencies = [ 1370 + "heapless 0.7.17", 1371 + "num-traits", 1372 + "serde", 1373 + "smallvec", 1374 + ] 1375 + 1376 + [[package]] 1377 + name = "rstar" 1378 + version = "0.12.2" 1379 + source = "registry+https://github.com/rust-lang/crates.io-index" 1380 + checksum = "421400d13ccfd26dfa5858199c30a5d76f9c54e0dba7575273025b43c5175dbb" 1381 + dependencies = [ 1382 + "heapless 0.8.0", 1383 + "num-traits", 1384 + "serde", 1385 + "smallvec", 1386 + ] 1387 + 1388 + [[package]] 1389 + name = "rustc_version" 1390 + version = "0.4.1" 1391 + source = "registry+https://github.com/rust-lang/crates.io-index" 1392 + checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 1393 + dependencies = [ 1394 + "semver", 1395 + ] 1396 + 1397 + [[package]] 1398 + name = "rustix" 1399 + version = "0.38.44" 1400 + source = "registry+https://github.com/rust-lang/crates.io-index" 1401 + checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" 1402 + dependencies = [ 1403 + "bitflags", 1404 + "errno", 1405 + "libc", 1406 + "linux-raw-sys 0.4.15", 1407 + "windows-sys 0.59.0", 1408 + ] 1409 + 1410 + [[package]] 1411 + name = "rustix" 1412 + version = "1.1.4" 1413 + source = "registry+https://github.com/rust-lang/crates.io-index" 1414 + checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" 1415 + dependencies = [ 1416 + "bitflags", 1417 + "errno", 1418 + "libc", 1419 + "linux-raw-sys 0.12.1", 1420 + "windows-sys 0.61.2", 1421 + ] 1422 + 1423 + [[package]] 1424 + name = "rustls" 1425 + version = "0.23.37" 1426 + source = "registry+https://github.com/rust-lang/crates.io-index" 1427 + checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" 1428 + dependencies = [ 1429 + "once_cell", 1430 + "rustls-pki-types", 1431 + "rustls-webpki", 1432 + "subtle", 1433 + "zeroize", 1434 + ] 1435 + 1436 + [[package]] 1437 + name = "rustls-pki-types" 1438 + version = "1.14.0" 1439 + source = "registry+https://github.com/rust-lang/crates.io-index" 1440 + checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" 1441 + dependencies = [ 1442 + "zeroize", 1443 + ] 1444 + 1445 + [[package]] 1446 + name = "rustls-webpki" 1447 + version = "0.103.9" 1448 + source = "registry+https://github.com/rust-lang/crates.io-index" 1449 + checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" 1450 + dependencies = [ 1451 + "ring", 1452 + "rustls-pki-types", 1453 + "untrusted", 1454 + ] 1455 + 1456 + [[package]] 1457 + name = "rustversion" 1458 + version = "1.0.22" 1459 + source = "registry+https://github.com/rust-lang/crates.io-index" 1460 + checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" 1461 + 1462 + [[package]] 1463 + name = "ryu" 1464 + version = "1.0.23" 1465 + source = "registry+https://github.com/rust-lang/crates.io-index" 1466 + checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" 1467 + 1468 + [[package]] 1469 + name = "schannel" 1470 + version = "0.1.29" 1471 + source = "registry+https://github.com/rust-lang/crates.io-index" 1472 + checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" 1473 + dependencies = [ 1474 + "windows-sys 0.61.2", 1475 + ] 1476 + 1477 + [[package]] 1478 + name = "scopeguard" 1479 + version = "1.2.0" 1480 + source = "registry+https://github.com/rust-lang/crates.io-index" 1481 + checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1482 + 1483 + [[package]] 1484 + name = "security-framework" 1485 + version = "3.7.0" 1486 + source = "registry+https://github.com/rust-lang/crates.io-index" 1487 + checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" 1488 + dependencies = [ 1489 + "bitflags", 1490 + "core-foundation 0.10.1", 1491 + "core-foundation-sys", 1492 + "libc", 1493 + "security-framework-sys", 1494 + ] 1495 + 1496 + [[package]] 1497 + name = "security-framework-sys" 1498 + version = "2.17.0" 1499 + source = "registry+https://github.com/rust-lang/crates.io-index" 1500 + checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" 1501 + dependencies = [ 1502 + "core-foundation-sys", 1503 + "libc", 1504 + ] 1505 + 1506 + [[package]] 1507 + name = "semver" 1508 + version = "1.0.27" 1509 + source = "registry+https://github.com/rust-lang/crates.io-index" 1510 + checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" 1511 + 1512 + [[package]] 1513 + name = "serde" 1514 + version = "1.0.228" 1515 + source = "registry+https://github.com/rust-lang/crates.io-index" 1516 + checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" 1517 + dependencies = [ 1518 + "serde_core", 1519 + "serde_derive", 1520 + ] 1521 + 1522 + [[package]] 1523 + name = "serde_core" 1524 + version = "1.0.228" 1525 + source = "registry+https://github.com/rust-lang/crates.io-index" 1526 + checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" 1527 + dependencies = [ 1528 + "serde_derive", 1529 + ] 1530 + 1531 + [[package]] 1532 + name = "serde_derive" 1533 + version = "1.0.228" 1534 + source = "registry+https://github.com/rust-lang/crates.io-index" 1535 + checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" 1536 + dependencies = [ 1537 + "proc-macro2", 1538 + "quote", 1539 + "syn", 1540 + ] 1541 + 1542 + [[package]] 1543 + name = "serde_json" 1544 + version = "1.0.149" 1545 + source = "registry+https://github.com/rust-lang/crates.io-index" 1546 + checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" 1547 + dependencies = [ 1548 + "itoa", 1549 + "memchr", 1550 + "serde", 1551 + "serde_core", 1552 + "zmij", 1553 + ] 1554 + 1555 + [[package]] 1556 + name = "serde_urlencoded" 1557 + version = "0.7.1" 1558 + source = "registry+https://github.com/rust-lang/crates.io-index" 1559 + checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1560 + dependencies = [ 1561 + "form_urlencoded", 1562 + "itoa", 1563 + "ryu", 1564 + "serde", 1565 + ] 1566 + 1567 + [[package]] 1568 + name = "sha1" 1569 + version = "0.10.6" 1570 + source = "registry+https://github.com/rust-lang/crates.io-index" 1571 + checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" 1572 + dependencies = [ 1573 + "cfg-if", 1574 + "cpufeatures", 1575 + "digest", 1576 + ] 1577 + 1578 + [[package]] 1579 + name = "shlex" 1580 + version = "1.3.0" 1581 + source = "registry+https://github.com/rust-lang/crates.io-index" 1582 + checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1583 + 1584 + [[package]] 1585 + name = "signal-hook" 1586 + version = "0.3.18" 1587 + source = "registry+https://github.com/rust-lang/crates.io-index" 1588 + checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" 1589 + dependencies = [ 1590 + "libc", 1591 + "signal-hook-registry", 1592 + ] 1593 + 1594 + [[package]] 1595 + name = "signal-hook-mio" 1596 + version = "0.2.5" 1597 + source = "registry+https://github.com/rust-lang/crates.io-index" 1598 + checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" 1599 + dependencies = [ 1600 + "libc", 1601 + "mio", 1602 + "signal-hook", 1603 + ] 1604 + 1605 + [[package]] 1606 + name = "signal-hook-registry" 1607 + version = "1.4.8" 1608 + source = "registry+https://github.com/rust-lang/crates.io-index" 1609 + checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" 1610 + dependencies = [ 1611 + "errno", 1612 + "libc", 1613 + ] 1614 + 1615 + [[package]] 1616 + name = "slab" 1617 + version = "0.4.12" 1618 + source = "registry+https://github.com/rust-lang/crates.io-index" 1619 + checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" 1620 + 1621 + [[package]] 1622 + name = "smallvec" 1623 + version = "1.15.1" 1624 + source = "registry+https://github.com/rust-lang/crates.io-index" 1625 + checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 1626 + 1627 + [[package]] 1628 + name = "socket2" 1629 + version = "0.6.3" 1630 + source = "registry+https://github.com/rust-lang/crates.io-index" 1631 + checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" 1632 + dependencies = [ 1633 + "libc", 1634 + "windows-sys 0.61.2", 1635 + ] 1636 + 1637 + [[package]] 1638 + name = "spin" 1639 + version = "0.9.8" 1640 + source = "registry+https://github.com/rust-lang/crates.io-index" 1641 + checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 1642 + dependencies = [ 1643 + "lock_api", 1644 + ] 1645 + 1646 + [[package]] 1647 + name = "stable_deref_trait" 1648 + version = "1.2.1" 1649 + source = "registry+https://github.com/rust-lang/crates.io-index" 1650 + checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" 1651 + 1652 + [[package]] 1653 + name = "static_assertions" 1654 + version = "1.1.0" 1655 + source = "registry+https://github.com/rust-lang/crates.io-index" 1656 + checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 1657 + 1658 + [[package]] 1659 + name = "strsim" 1660 + version = "0.11.1" 1661 + source = "registry+https://github.com/rust-lang/crates.io-index" 1662 + checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 1663 + 1664 + [[package]] 1665 + name = "strum" 1666 + version = "0.26.3" 1667 + source = "registry+https://github.com/rust-lang/crates.io-index" 1668 + checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" 1669 + dependencies = [ 1670 + "strum_macros", 1671 + ] 1672 + 1673 + [[package]] 1674 + name = "strum_macros" 1675 + version = "0.26.4" 1676 + source = "registry+https://github.com/rust-lang/crates.io-index" 1677 + checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" 1678 + dependencies = [ 1679 + "heck", 1680 + "proc-macro2", 1681 + "quote", 1682 + "rustversion", 1683 + "syn", 1684 + ] 1685 + 1686 + [[package]] 1687 + name = "subtle" 1688 + version = "2.6.1" 1689 + source = "registry+https://github.com/rust-lang/crates.io-index" 1690 + checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1691 + 1692 + [[package]] 1693 + name = "syn" 1694 + version = "2.0.117" 1695 + source = "registry+https://github.com/rust-lang/crates.io-index" 1696 + checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" 1697 + dependencies = [ 1698 + "proc-macro2", 1699 + "quote", 1700 + "unicode-ident", 1701 + ] 1702 + 1703 + [[package]] 1704 + name = "sync_wrapper" 1705 + version = "1.0.2" 1706 + source = "registry+https://github.com/rust-lang/crates.io-index" 1707 + checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 1708 + dependencies = [ 1709 + "futures-core", 1710 + ] 1711 + 1712 + [[package]] 1713 + name = "synstructure" 1714 + version = "0.13.2" 1715 + source = "registry+https://github.com/rust-lang/crates.io-index" 1716 + checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" 1717 + dependencies = [ 1718 + "proc-macro2", 1719 + "quote", 1720 + "syn", 1721 + ] 1722 + 1723 + [[package]] 1724 + name = "system-configuration" 1725 + version = "0.7.0" 1726 + source = "registry+https://github.com/rust-lang/crates.io-index" 1727 + checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" 1728 + dependencies = [ 1729 + "bitflags", 1730 + "core-foundation 0.9.4", 1731 + "system-configuration-sys", 1732 + ] 1733 + 1734 + [[package]] 1735 + name = "system-configuration-sys" 1736 + version = "0.6.0" 1737 + source = "registry+https://github.com/rust-lang/crates.io-index" 1738 + checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" 1739 + dependencies = [ 1740 + "core-foundation-sys", 1741 + "libc", 1742 + ] 1743 + 1744 + [[package]] 1745 + name = "tempfile" 1746 + version = "3.27.0" 1747 + source = "registry+https://github.com/rust-lang/crates.io-index" 1748 + checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" 1749 + dependencies = [ 1750 + "fastrand", 1751 + "getrandom 0.4.2", 1752 + "once_cell", 1753 + "rustix 1.1.4", 1754 + "windows-sys 0.61.2", 1755 + ] 1756 + 1757 + [[package]] 1758 + name = "thiserror" 1759 + version = "2.0.18" 1760 + source = "registry+https://github.com/rust-lang/crates.io-index" 1761 + checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" 1762 + dependencies = [ 1763 + "thiserror-impl", 1764 + ] 1765 + 1766 + [[package]] 1767 + name = "thiserror-impl" 1768 + version = "2.0.18" 1769 + source = "registry+https://github.com/rust-lang/crates.io-index" 1770 + checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" 1771 + dependencies = [ 1772 + "proc-macro2", 1773 + "quote", 1774 + "syn", 1775 + ] 1776 + 1777 + [[package]] 1778 + name = "tinystr" 1779 + version = "0.8.2" 1780 + source = "registry+https://github.com/rust-lang/crates.io-index" 1781 + checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" 1782 + dependencies = [ 1783 + "displaydoc", 1784 + "zerovec", 1785 + ] 1786 + 1787 + [[package]] 1788 + name = "tokio" 1789 + version = "1.50.0" 1790 + source = "registry+https://github.com/rust-lang/crates.io-index" 1791 + checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" 1792 + dependencies = [ 1793 + "bytes", 1794 + "libc", 1795 + "mio", 1796 + "parking_lot", 1797 + "pin-project-lite", 1798 + "signal-hook-registry", 1799 + "socket2", 1800 + "tokio-macros", 1801 + "windows-sys 0.61.2", 1802 + ] 1803 + 1804 + [[package]] 1805 + name = "tokio-macros" 1806 + version = "2.6.1" 1807 + source = "registry+https://github.com/rust-lang/crates.io-index" 1808 + checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" 1809 + dependencies = [ 1810 + "proc-macro2", 1811 + "quote", 1812 + "syn", 1813 + ] 1814 + 1815 + [[package]] 1816 + name = "tokio-native-tls" 1817 + version = "0.3.1" 1818 + source = "registry+https://github.com/rust-lang/crates.io-index" 1819 + checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" 1820 + dependencies = [ 1821 + "native-tls", 1822 + "tokio", 1823 + ] 1824 + 1825 + [[package]] 1826 + name = "tokio-rustls" 1827 + version = "0.26.4" 1828 + source = "registry+https://github.com/rust-lang/crates.io-index" 1829 + checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" 1830 + dependencies = [ 1831 + "rustls", 1832 + "tokio", 1833 + ] 1834 + 1835 + [[package]] 1836 + name = "tokio-util" 1837 + version = "0.7.18" 1838 + source = "registry+https://github.com/rust-lang/crates.io-index" 1839 + checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" 1840 + dependencies = [ 1841 + "bytes", 1842 + "futures-core", 1843 + "futures-sink", 1844 + "pin-project-lite", 1845 + "tokio", 1846 + ] 1847 + 1848 + [[package]] 1849 + name = "tower" 1850 + version = "0.5.3" 1851 + source = "registry+https://github.com/rust-lang/crates.io-index" 1852 + checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" 1853 + dependencies = [ 1854 + "futures-core", 1855 + "futures-util", 1856 + "pin-project-lite", 1857 + "sync_wrapper", 1858 + "tokio", 1859 + "tower-layer", 1860 + "tower-service", 1861 + ] 1862 + 1863 + [[package]] 1864 + name = "tower-http" 1865 + version = "0.6.8" 1866 + source = "registry+https://github.com/rust-lang/crates.io-index" 1867 + checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" 1868 + dependencies = [ 1869 + "bitflags", 1870 + "bytes", 1871 + "futures-util", 1872 + "http", 1873 + "http-body", 1874 + "iri-string", 1875 + "pin-project-lite", 1876 + "tower", 1877 + "tower-layer", 1878 + "tower-service", 1879 + ] 1880 + 1881 + [[package]] 1882 + name = "tower-layer" 1883 + version = "0.3.3" 1884 + source = "registry+https://github.com/rust-lang/crates.io-index" 1885 + checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 1886 + 1887 + [[package]] 1888 + name = "tower-service" 1889 + version = "0.3.3" 1890 + source = "registry+https://github.com/rust-lang/crates.io-index" 1891 + checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 1892 + 1893 + [[package]] 1894 + name = "tracing" 1895 + version = "0.1.44" 1896 + source = "registry+https://github.com/rust-lang/crates.io-index" 1897 + checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" 1898 + dependencies = [ 1899 + "pin-project-lite", 1900 + "tracing-core", 1901 + ] 1902 + 1903 + [[package]] 1904 + name = "tracing-core" 1905 + version = "0.1.36" 1906 + source = "registry+https://github.com/rust-lang/crates.io-index" 1907 + checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" 1908 + dependencies = [ 1909 + "once_cell", 1910 + ] 1911 + 1912 + [[package]] 1913 + name = "try-lock" 1914 + version = "0.2.5" 1915 + source = "registry+https://github.com/rust-lang/crates.io-index" 1916 + checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 1917 + 1918 + [[package]] 1919 + name = "typenum" 1920 + version = "1.19.0" 1921 + source = "registry+https://github.com/rust-lang/crates.io-index" 1922 + checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" 1923 + 1924 + [[package]] 1925 + name = "unicode-ident" 1926 + version = "1.0.24" 1927 + source = "registry+https://github.com/rust-lang/crates.io-index" 1928 + checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" 1929 + 1930 + [[package]] 1931 + name = "unicode-segmentation" 1932 + version = "1.12.0" 1933 + source = "registry+https://github.com/rust-lang/crates.io-index" 1934 + checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" 1935 + 1936 + [[package]] 1937 + name = "unicode-truncate" 1938 + version = "1.1.0" 1939 + source = "registry+https://github.com/rust-lang/crates.io-index" 1940 + checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" 1941 + dependencies = [ 1942 + "itertools", 1943 + "unicode-segmentation", 1944 + "unicode-width 0.1.14", 1945 + ] 1946 + 1947 + [[package]] 1948 + name = "unicode-width" 1949 + version = "0.1.14" 1950 + source = "registry+https://github.com/rust-lang/crates.io-index" 1951 + checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 1952 + 1953 + [[package]] 1954 + name = "unicode-width" 1955 + version = "0.2.0" 1956 + source = "registry+https://github.com/rust-lang/crates.io-index" 1957 + checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" 1958 + 1959 + [[package]] 1960 + name = "unicode-xid" 1961 + version = "0.2.6" 1962 + source = "registry+https://github.com/rust-lang/crates.io-index" 1963 + checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" 1964 + 1965 + [[package]] 1966 + name = "untrusted" 1967 + version = "0.9.0" 1968 + source = "registry+https://github.com/rust-lang/crates.io-index" 1969 + checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 1970 + 1971 + [[package]] 1972 + name = "url" 1973 + version = "2.5.8" 1974 + source = "registry+https://github.com/rust-lang/crates.io-index" 1975 + checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" 1976 + dependencies = [ 1977 + "form_urlencoded", 1978 + "idna", 1979 + "percent-encoding", 1980 + "serde", 1981 + ] 1982 + 1983 + [[package]] 1984 + name = "utf8_iter" 1985 + version = "1.0.4" 1986 + source = "registry+https://github.com/rust-lang/crates.io-index" 1987 + checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" 1988 + 1989 + [[package]] 1990 + name = "vcpkg" 1991 + version = "0.2.15" 1992 + source = "registry+https://github.com/rust-lang/crates.io-index" 1993 + checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 1994 + 1995 + [[package]] 1996 + name = "version_check" 1997 + version = "0.9.5" 1998 + source = "registry+https://github.com/rust-lang/crates.io-index" 1999 + checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 2000 + 2001 + [[package]] 2002 + name = "want" 2003 + version = "0.3.1" 2004 + source = "registry+https://github.com/rust-lang/crates.io-index" 2005 + checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 2006 + dependencies = [ 2007 + "try-lock", 2008 + ] 2009 + 2010 + [[package]] 2011 + name = "wasi" 2012 + version = "0.11.1+wasi-snapshot-preview1" 2013 + source = "registry+https://github.com/rust-lang/crates.io-index" 2014 + checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 2015 + 2016 + [[package]] 2017 + name = "wasip2" 2018 + version = "1.0.2+wasi-0.2.9" 2019 + source = "registry+https://github.com/rust-lang/crates.io-index" 2020 + checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" 2021 + dependencies = [ 2022 + "wit-bindgen", 2023 + ] 2024 + 2025 + [[package]] 2026 + name = "wasip3" 2027 + version = "0.4.0+wasi-0.3.0-rc-2026-01-06" 2028 + source = "registry+https://github.com/rust-lang/crates.io-index" 2029 + checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" 2030 + dependencies = [ 2031 + "wit-bindgen", 2032 + ] 2033 + 2034 + [[package]] 2035 + name = "wasm-bindgen" 2036 + version = "0.2.114" 2037 + source = "registry+https://github.com/rust-lang/crates.io-index" 2038 + checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" 2039 + dependencies = [ 2040 + "cfg-if", 2041 + "once_cell", 2042 + "rustversion", 2043 + "wasm-bindgen-macro", 2044 + "wasm-bindgen-shared", 2045 + ] 2046 + 2047 + [[package]] 2048 + name = "wasm-bindgen-futures" 2049 + version = "0.4.64" 2050 + source = "registry+https://github.com/rust-lang/crates.io-index" 2051 + checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" 2052 + dependencies = [ 2053 + "cfg-if", 2054 + "futures-util", 2055 + "js-sys", 2056 + "once_cell", 2057 + "wasm-bindgen", 2058 + "web-sys", 2059 + ] 2060 + 2061 + [[package]] 2062 + name = "wasm-bindgen-macro" 2063 + version = "0.2.114" 2064 + source = "registry+https://github.com/rust-lang/crates.io-index" 2065 + checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" 2066 + dependencies = [ 2067 + "quote", 2068 + "wasm-bindgen-macro-support", 2069 + ] 2070 + 2071 + [[package]] 2072 + name = "wasm-bindgen-macro-support" 2073 + version = "0.2.114" 2074 + source = "registry+https://github.com/rust-lang/crates.io-index" 2075 + checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" 2076 + dependencies = [ 2077 + "bumpalo", 2078 + "proc-macro2", 2079 + "quote", 2080 + "syn", 2081 + "wasm-bindgen-shared", 2082 + ] 2083 + 2084 + [[package]] 2085 + name = "wasm-bindgen-shared" 2086 + version = "0.2.114" 2087 + source = "registry+https://github.com/rust-lang/crates.io-index" 2088 + checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" 2089 + dependencies = [ 2090 + "unicode-ident", 2091 + ] 2092 + 2093 + [[package]] 2094 + name = "wasm-encoder" 2095 + version = "0.244.0" 2096 + source = "registry+https://github.com/rust-lang/crates.io-index" 2097 + checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" 2098 + dependencies = [ 2099 + "leb128fmt", 2100 + "wasmparser", 2101 + ] 2102 + 2103 + [[package]] 2104 + name = "wasm-metadata" 2105 + version = "0.244.0" 2106 + source = "registry+https://github.com/rust-lang/crates.io-index" 2107 + checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" 2108 + dependencies = [ 2109 + "anyhow", 2110 + "indexmap", 2111 + "wasm-encoder", 2112 + "wasmparser", 2113 + ] 2114 + 2115 + [[package]] 2116 + name = "wasmparser" 2117 + version = "0.244.0" 2118 + source = "registry+https://github.com/rust-lang/crates.io-index" 2119 + checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" 2120 + dependencies = [ 2121 + "bitflags", 2122 + "hashbrown 0.15.5", 2123 + "indexmap", 2124 + "semver", 2125 + ] 2126 + 2127 + [[package]] 2128 + name = "web-sys" 2129 + version = "0.3.91" 2130 + source = "registry+https://github.com/rust-lang/crates.io-index" 2131 + checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" 2132 + dependencies = [ 2133 + "js-sys", 2134 + "wasm-bindgen", 2135 + ] 2136 + 2137 + [[package]] 2138 + name = "winapi" 2139 + version = "0.3.9" 2140 + source = "registry+https://github.com/rust-lang/crates.io-index" 2141 + checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 2142 + dependencies = [ 2143 + "winapi-i686-pc-windows-gnu", 2144 + "winapi-x86_64-pc-windows-gnu", 2145 + ] 2146 + 2147 + [[package]] 2148 + name = "winapi-i686-pc-windows-gnu" 2149 + version = "0.4.0" 2150 + source = "registry+https://github.com/rust-lang/crates.io-index" 2151 + checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 2152 + 2153 + [[package]] 2154 + name = "winapi-x86_64-pc-windows-gnu" 2155 + version = "0.4.0" 2156 + source = "registry+https://github.com/rust-lang/crates.io-index" 2157 + checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 2158 + 2159 + [[package]] 2160 + name = "windows-link" 2161 + version = "0.2.1" 2162 + source = "registry+https://github.com/rust-lang/crates.io-index" 2163 + checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 2164 + 2165 + [[package]] 2166 + name = "windows-registry" 2167 + version = "0.6.1" 2168 + source = "registry+https://github.com/rust-lang/crates.io-index" 2169 + checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" 2170 + dependencies = [ 2171 + "windows-link", 2172 + "windows-result", 2173 + "windows-strings", 2174 + ] 2175 + 2176 + [[package]] 2177 + name = "windows-result" 2178 + version = "0.4.1" 2179 + source = "registry+https://github.com/rust-lang/crates.io-index" 2180 + checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" 2181 + dependencies = [ 2182 + "windows-link", 2183 + ] 2184 + 2185 + [[package]] 2186 + name = "windows-strings" 2187 + version = "0.5.1" 2188 + source = "registry+https://github.com/rust-lang/crates.io-index" 2189 + checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" 2190 + dependencies = [ 2191 + "windows-link", 2192 + ] 2193 + 2194 + [[package]] 2195 + name = "windows-sys" 2196 + version = "0.52.0" 2197 + source = "registry+https://github.com/rust-lang/crates.io-index" 2198 + checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2199 + dependencies = [ 2200 + "windows-targets", 2201 + ] 2202 + 2203 + [[package]] 2204 + name = "windows-sys" 2205 + version = "0.59.0" 2206 + source = "registry+https://github.com/rust-lang/crates.io-index" 2207 + checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 2208 + dependencies = [ 2209 + "windows-targets", 2210 + ] 2211 + 2212 + [[package]] 2213 + name = "windows-sys" 2214 + version = "0.61.2" 2215 + source = "registry+https://github.com/rust-lang/crates.io-index" 2216 + checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 2217 + dependencies = [ 2218 + "windows-link", 2219 + ] 2220 + 2221 + [[package]] 2222 + name = "windows-targets" 2223 + version = "0.52.6" 2224 + source = "registry+https://github.com/rust-lang/crates.io-index" 2225 + checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 2226 + dependencies = [ 2227 + "windows_aarch64_gnullvm", 2228 + "windows_aarch64_msvc", 2229 + "windows_i686_gnu", 2230 + "windows_i686_gnullvm", 2231 + "windows_i686_msvc", 2232 + "windows_x86_64_gnu", 2233 + "windows_x86_64_gnullvm", 2234 + "windows_x86_64_msvc", 2235 + ] 2236 + 2237 + [[package]] 2238 + name = "windows_aarch64_gnullvm" 2239 + version = "0.52.6" 2240 + source = "registry+https://github.com/rust-lang/crates.io-index" 2241 + checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2242 + 2243 + [[package]] 2244 + name = "windows_aarch64_msvc" 2245 + version = "0.52.6" 2246 + source = "registry+https://github.com/rust-lang/crates.io-index" 2247 + checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2248 + 2249 + [[package]] 2250 + name = "windows_i686_gnu" 2251 + version = "0.52.6" 2252 + source = "registry+https://github.com/rust-lang/crates.io-index" 2253 + checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2254 + 2255 + [[package]] 2256 + name = "windows_i686_gnullvm" 2257 + version = "0.52.6" 2258 + source = "registry+https://github.com/rust-lang/crates.io-index" 2259 + checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2260 + 2261 + [[package]] 2262 + name = "windows_i686_msvc" 2263 + version = "0.52.6" 2264 + source = "registry+https://github.com/rust-lang/crates.io-index" 2265 + checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2266 + 2267 + [[package]] 2268 + name = "windows_x86_64_gnu" 2269 + version = "0.52.6" 2270 + source = "registry+https://github.com/rust-lang/crates.io-index" 2271 + checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2272 + 2273 + [[package]] 2274 + name = "windows_x86_64_gnullvm" 2275 + version = "0.52.6" 2276 + source = "registry+https://github.com/rust-lang/crates.io-index" 2277 + checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2278 + 2279 + [[package]] 2280 + name = "windows_x86_64_msvc" 2281 + version = "0.52.6" 2282 + source = "registry+https://github.com/rust-lang/crates.io-index" 2283 + checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2284 + 2285 + [[package]] 2286 + name = "wit-bindgen" 2287 + version = "0.51.0" 2288 + source = "registry+https://github.com/rust-lang/crates.io-index" 2289 + checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" 2290 + dependencies = [ 2291 + "wit-bindgen-rust-macro", 2292 + ] 2293 + 2294 + [[package]] 2295 + name = "wit-bindgen-core" 2296 + version = "0.51.0" 2297 + source = "registry+https://github.com/rust-lang/crates.io-index" 2298 + checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" 2299 + dependencies = [ 2300 + "anyhow", 2301 + "heck", 2302 + "wit-parser", 2303 + ] 2304 + 2305 + [[package]] 2306 + name = "wit-bindgen-rust" 2307 + version = "0.51.0" 2308 + source = "registry+https://github.com/rust-lang/crates.io-index" 2309 + checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" 2310 + dependencies = [ 2311 + "anyhow", 2312 + "heck", 2313 + "indexmap", 2314 + "prettyplease", 2315 + "syn", 2316 + "wasm-metadata", 2317 + "wit-bindgen-core", 2318 + "wit-component", 2319 + ] 2320 + 2321 + [[package]] 2322 + name = "wit-bindgen-rust-macro" 2323 + version = "0.51.0" 2324 + source = "registry+https://github.com/rust-lang/crates.io-index" 2325 + checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" 2326 + dependencies = [ 2327 + "anyhow", 2328 + "prettyplease", 2329 + "proc-macro2", 2330 + "quote", 2331 + "syn", 2332 + "wit-bindgen-core", 2333 + "wit-bindgen-rust", 2334 + ] 2335 + 2336 + [[package]] 2337 + name = "wit-component" 2338 + version = "0.244.0" 2339 + source = "registry+https://github.com/rust-lang/crates.io-index" 2340 + checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" 2341 + dependencies = [ 2342 + "anyhow", 2343 + "bitflags", 2344 + "indexmap", 2345 + "log", 2346 + "serde", 2347 + "serde_derive", 2348 + "serde_json", 2349 + "wasm-encoder", 2350 + "wasm-metadata", 2351 + "wasmparser", 2352 + "wit-parser", 2353 + ] 2354 + 2355 + [[package]] 2356 + name = "wit-parser" 2357 + version = "0.244.0" 2358 + source = "registry+https://github.com/rust-lang/crates.io-index" 2359 + checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" 2360 + dependencies = [ 2361 + "anyhow", 2362 + "id-arena", 2363 + "indexmap", 2364 + "log", 2365 + "semver", 2366 + "serde", 2367 + "serde_derive", 2368 + "serde_json", 2369 + "unicode-xid", 2370 + "wasmparser", 2371 + ] 2372 + 2373 + [[package]] 2374 + name = "writeable" 2375 + version = "0.6.2" 2376 + source = "registry+https://github.com/rust-lang/crates.io-index" 2377 + checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" 2378 + 2379 + [[package]] 2380 + name = "yoke" 2381 + version = "0.8.1" 2382 + source = "registry+https://github.com/rust-lang/crates.io-index" 2383 + checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" 2384 + dependencies = [ 2385 + "stable_deref_trait", 2386 + "yoke-derive", 2387 + "zerofrom", 2388 + ] 2389 + 2390 + [[package]] 2391 + name = "yoke-derive" 2392 + version = "0.8.1" 2393 + source = "registry+https://github.com/rust-lang/crates.io-index" 2394 + checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" 2395 + dependencies = [ 2396 + "proc-macro2", 2397 + "quote", 2398 + "syn", 2399 + "synstructure", 2400 + ] 2401 + 2402 + [[package]] 2403 + name = "zerofrom" 2404 + version = "0.1.6" 2405 + source = "registry+https://github.com/rust-lang/crates.io-index" 2406 + checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" 2407 + dependencies = [ 2408 + "zerofrom-derive", 2409 + ] 2410 + 2411 + [[package]] 2412 + name = "zerofrom-derive" 2413 + version = "0.1.6" 2414 + source = "registry+https://github.com/rust-lang/crates.io-index" 2415 + checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" 2416 + dependencies = [ 2417 + "proc-macro2", 2418 + "quote", 2419 + "syn", 2420 + "synstructure", 2421 + ] 2422 + 2423 + [[package]] 2424 + name = "zeroize" 2425 + version = "1.8.2" 2426 + source = "registry+https://github.com/rust-lang/crates.io-index" 2427 + checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" 2428 + 2429 + [[package]] 2430 + name = "zerotrie" 2431 + version = "0.2.3" 2432 + source = "registry+https://github.com/rust-lang/crates.io-index" 2433 + checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" 2434 + dependencies = [ 2435 + "displaydoc", 2436 + "yoke", 2437 + "zerofrom", 2438 + ] 2439 + 2440 + [[package]] 2441 + name = "zerovec" 2442 + version = "0.11.5" 2443 + source = "registry+https://github.com/rust-lang/crates.io-index" 2444 + checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" 2445 + dependencies = [ 2446 + "yoke", 2447 + "zerofrom", 2448 + "zerovec-derive", 2449 + ] 2450 + 2451 + [[package]] 2452 + name = "zerovec-derive" 2453 + version = "0.11.2" 2454 + source = "registry+https://github.com/rust-lang/crates.io-index" 2455 + checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" 2456 + dependencies = [ 2457 + "proc-macro2", 2458 + "quote", 2459 + "syn", 2460 + ] 2461 + 2462 + [[package]] 2463 + name = "zmij" 2464 + version = "1.0.21" 2465 + source = "registry+https://github.com/rust-lang/crates.io-index" 2466 + checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
+34
Cargo.toml
··· 1 + [package] 2 + name = "amtracker" 3 + version = "0.1.0" 4 + edition = "2021" 5 + 6 + [dependencies] 7 + # TUI 8 + ratatui = "0.29" 9 + crossterm = "0.28" 10 + 11 + # Async runtime 12 + tokio = { version = "1", features = ["full"] } 13 + reqwest = { version = "0.12", features = ["json"] } 14 + 15 + # Crypto (for Amtrak data decryption) 16 + aes = "0.8" 17 + cbc = "0.1" 18 + pbkdf2 = "0.12" 19 + sha1 = "0.10" 20 + hmac = "0.12" 21 + base64 = "0.22" 22 + 23 + # Data 24 + serde = { version = "1", features = ["derive"] } 25 + serde_json = "1" 26 + geojson = "0.24" 27 + 28 + # Encoding 29 + hex = "0.4" 30 + 31 + # Error handling 32 + anyhow = "1" 33 + 34 + [dev-dependencies]
+21
LICENSE
··· 1 + MIT License 2 + 3 + Copyright (c) 2026 Austin Parker 4 + 5 + Permission is hereby granted, free of charge, to any person obtaining a copy 6 + of this software and associated documentation files (the "Software"), to deal 7 + in the Software without restriction, including without limitation the rights 8 + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 + copies of the Software, and to permit persons to whom the Software is 10 + furnished to do so, subject to the following conditions: 11 + 12 + The above copyright notice and this permission notice shall be included in all 13 + copies or substantial portions of the Software. 14 + 15 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 + SOFTWARE.
+83
README.md
··· 1 + # amtracker 2 + 3 + A terminal-based real-time Amtrak train tracker with a zoomable braille-rendered map. 4 + 5 + ![Rust](https://img.shields.io/badge/rust-stable-orange) 6 + ![License](https://img.shields.io/badge/license-MIT-blue) 7 + 8 + ## Features 9 + 10 + - Live map of all active Amtrak trains rendered with Unicode braille characters 11 + - US state boundaries and Amtrak route lines as base layers 12 + - Color-coded train markers: green (on time), yellow (unknown), red (delayed) 13 + - Zoom and pan with keyboard controls 14 + - Search/filter trains by number, route name, or station code 15 + - Jump-to-train: center the map on any selected train 16 + - Train detail view with per-station delay status 17 + - Auto-refresh every 60 seconds 18 + 19 + ## Install 20 + 21 + ``` 22 + cargo install --path . 23 + ``` 24 + 25 + Or run directly: 26 + 27 + ``` 28 + cargo run --release 29 + ``` 30 + 31 + ## Controls 32 + 33 + | Key | Action | 34 + |-----|--------| 35 + | `←↑↓→` | Pan the map | 36 + | `+` / `-` | Zoom in / out | 37 + | `Tab` / `Shift-Tab` | Cycle through trains | 38 + | `Enter` | Jump to selected train | 39 + | `/` | Open search (filter by train #, route, station) | 40 + | `Esc` | Clear selection / exit search | 41 + | `0` | Reset to full US view | 42 + | `r` | Manual refresh | 43 + | `q` | Quit | 44 + 45 + ## How it works 46 + 47 + Amtrak's Track Your Train page fetches real-time GPS data from an internal API 48 + (`maps.amtrak.com/services/MapDataService/trains/getTrainsData`). The response 49 + is AES-128-CBC encrypted with keys derived via PBKDF2-SHA1. amtracker 50 + reverse-engineered the decryption pipeline from Amtrak's client-side JavaScript 51 + and reimplemented it in Rust. 52 + 53 + The decrypted data is GeoJSON containing coordinates, speed, heading, route, 54 + and per-station schedule/delay information for every active train. 55 + 56 + ## Architecture 57 + 58 + ``` 59 + src/ 60 + crypto.rs - AES decryption pipeline (PBKDF2, key extraction, decrypt) 61 + model.rs - Train, StationStop, delay status types 62 + api.rs - Async HTTP client for Amtrak endpoints 63 + geo.rs - Embedded state boundary + route line data 64 + app.rs - Application state (viewport, selection, search) 65 + ui.rs - Ratatui rendering (map canvas, sidebar, search bar) 66 + main.rs - Event loop and terminal setup 67 + data/ 68 + us-states.json - Simplified US state boundaries (Natural Earth, public domain) 69 + routes.json - Simplified Amtrak route geometry 70 + ``` 71 + 72 + ## Tests 73 + 74 + ``` 75 + cargo test 76 + ``` 77 + 78 + 41 tests covering crypto roundtrips, GeoJSON parsing, viewport math, search 79 + filtering, and UI rendering (via ratatui's `TestBackend`). 80 + 81 + ## License 82 + 83 + MIT
+1
data/routes.json
··· 1 + {"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-71.055,42.352],[-71.09,42.336],[-71.118,42.295],[-71.155,42.194],[-71.155,42.155],[-71.19,42.117],[-71.209,42.048],[-71.304,41.913],[-71.383,41.892],[-71.417,41.842],[-71.413,41.829],[-71.437,41.822],[-71.442,41.806],[-71.422,41.783],[-71.454,41.696],[-71.446,41.663],[-71.53,41.504],[-71.618,41.449],[-71.657,41.451],[-71.708,41.433],[-71.721,41.413],[-71.769,41.388],[-71.824,41.384],[-71.845,41.361],[-71.899,41.336],[-71.966,41.351],[-71.997,41.323],[-72.092,41.363],[-72.103,41.335],[-72.133,41.337],[-72.165,41.316],[-72.191,41.324],[-72.277,41.291],[-72.355,41.311],[-72.496,41.275],[-72.569,41.29],[-72.732,41.265],[-72.873,41.283],[-72.886,41.323],[-72.932,41.293]]},"properties":{"name":"Acela"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-72.926,41.298],[-73.035,41.225],[-73.119,41.203],[-73.145,41.185],[-73.184,41.185],[-73.188,41.17],[-73.229,41.165],[-73.259,41.142],[-73.321,41.12],[-73.376,41.118],[-73.436,41.079],[-73.515,41.07],[-73.557,41.036],[-73.632,41.021],[-73.776,40.922],[-73.819,40.862],[-73.879,40.836],[-73.926,40.788],[-73.906,40.753],[-73.958,40.741],[-73.994,40.751]]},"properties":{"name":"Acela"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-75.182,39.956],[-75.322,39.881],[-75.343,39.856],[-75.435,39.815],[-75.494,39.755],[-75.767,39.666],[-75.843,39.607],[-75.928,39.611],[-75.974,39.58],[-76.101,39.549],[-76.158,39.515],[-76.217,39.45],[-76.338,39.397],[-76.384,39.35],[-76.477,39.328],[-76.531,39.299],[-76.621,39.311],[-76.649,39.302],[-76.69,39.257],[-76.696,39.121],[-76.707,39.085],[-76.741,39.044],[-76.908,38.92],[-76.98,38.918],[-77.006,38.898]]},"properties":{"name":"Acela"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.994,40.751],[-74.049,40.773],[-74.163,40.736],[-74.274,40.608],[-74.355,40.542],[-74.451,40.493],[-74.532,40.388],[-74.774,40.203],[-74.796,40.159],[-74.848,40.109],[-74.923,40.09],[-75.057,40.014],[-75.165,39.994],[-75.198,39.973],[-75.182,39.956]]},"properties":{"name":"Acela"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.739,42.649],[-73.785,42.387],[-73.77,42.296],[-73.799,42.252],[-73.834,42.23],[-73.91,42.118],[-73.956,41.908],[-73.926,41.86],[-73.948,41.773],[-73.936,41.75],[-73.947,41.582],[-73.985,41.519],[-73.984,41.455],[-73.943,41.393],[-73.98,41.318],[-73.931,41.291],[-73.939,41.274],[-73.921,41.246],[-73.929,41.232],[-73.89,41.202],[-73.872,41.168],[-73.865,41.079],[-73.92,40.887],[-74.001,40.754]]},"properties":{"name":"Adirondack"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.942,42.816],[-73.957,42.796],[-73.826,42.696],[-73.757,42.672],[-73.739,42.649]]},"properties":{"name":"Adirondack"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.942,42.816],[-73.87,42.919],[-73.809,43.088],[-73.712,43.132],[-73.501,43.348],[-73.483,43.422],[-73.455,43.435],[-73.415,43.506],[-73.403,43.559],[-73.432,43.586],[-73.422,43.621],[-73.433,43.64],[-73.372,43.728],[-73.363,43.769],[-73.401,43.817],[-73.388,43.85],[-73.391,43.887],[-73.418,43.909],[-73.42,43.981],[-73.456,44.002],[-73.461,44.032],[-73.436,44.108],[-73.458,44.181],[-73.446,44.223],[-73.388,44.289],[-73.418,44.408],[-73.398,44.454],[-73.421,44.484],[-73.386,44.516],[-73.448,44.568],[-73.442,44.594],[-73.454,44.635],[-73.443,44.696],[-73.463,44.727],[-73.467,44.771],[-73.506,44.833],[-73.412,44.917],[-73.371,44.996],[-73.254,45.303],[-73.407,45.375],[-73.458,45.437],[-73.418,45.481],[-73.462,45.506],[-73.554,45.485],[-73.566,45.5]]},"properties":{"name":"Adirondack"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-122.687,45.628],[-122.693,45.68],[-122.744,45.752],[-122.739,45.806],[-122.757,45.929],[-122.843,45.996],[-122.871,46.089],[-122.915,46.129],[-122.912,46.168],[-122.897,46.189],[-122.904,46.219],[-122.885,46.253],[-122.963,46.4],[-122.965,46.433],[-122.933,46.502],[-122.944,46.518],[-122.937,46.541],[-122.908,46.577],[-122.967,46.631],[-122.972,46.674],[-122.931,46.762],[-122.863,46.806],[-122.866,46.876],[-122.852,46.948],[-122.822,46.979],[-122.719,47.026],[-122.691,47.057],[-122.691,47.076],[-122.57,47.111],[-122.497,47.154],[-122.486,47.222],[-122.472,47.232],[-122.399,47.24],[-122.349,47.202],[-122.27,47.188],[-122.228,47.216],[-122.24,47.463],[-122.332,47.564],[-122.329,47.598]]},"properties":{"name":"Amtrak Cascades"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-122.686,45.629],[-122.752,45.569],[-122.676,45.529]]},"properties":{"name":"Amtrak Cascades"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-122.676,45.529],[-122.646,45.497],[-122.632,45.447],[-122.573,45.424],[-122.568,45.398],[-122.6,45.362],[-122.654,45.331],[-122.658,45.289],[-122.675,45.27],[-122.752,45.236],[-122.932,45.081],[-122.983,45.021],[-123.028,44.935],[-123.026,44.915],[-122.955,44.848],[-122.937,44.767],[-122.947,44.735],[-122.997,44.734],[-123.009,44.716],[-123.053,44.701],[-123.067,44.646],[-123.107,44.625],[-123.108,44.329],[-123.165,44.28],[-123.169,44.253],[-123.202,44.228],[-123.199,44.18],[-123.15,44.085],[-123.092,44.055]]},"properties":{"name":"Amtrak Cascades"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-122.197,47.974],[-122.215,47.98],[-122.306,47.946],[-122.335,47.853],[-122.395,47.802],[-122.373,47.728],[-122.38,47.709],[-122.403,47.697],[-122.405,47.672],[-122.381,47.65],[-122.381,47.635],[-122.329,47.598]]},"properties":{"name":"Amtrak Cascades"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-123.097,49.274],[-123.053,49.258],[-122.973,49.262],[-122.882,49.235],[-122.897,49.211],[-122.887,49.204],[-122.94,49.156],[-122.921,49.14],[-122.921,49.116],[-122.893,49.094],[-122.86,49.089],[-122.86,49.063],[-122.884,49.047],[-122.873,49.028],[-122.761,49.005],[-122.59,48.881],[-122.591,48.85],[-122.563,48.814],[-122.564,48.792],[-122.484,48.752],[-122.519,48.712],[-122.494,48.692],[-122.489,48.67],[-122.499,48.658],[-122.419,48.594],[-122.371,48.528],[-122.337,48.511],[-122.324,48.451],[-122.338,48.427],[-122.344,48.322],[-122.365,48.26],[-122.336,48.231],[-122.243,48.195],[-122.183,48.121],[-122.174,48.089],[-122.189,48.015],[-122.178,47.985],[-122.197,47.975]]},"properties":{"name":"Amtrak Cascades"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-81.149,32.083],[-81.126,32.05],[-81.152,32.026],[-81.726,31.721],[-81.887,31.601],[-82.028,31.007],[-82.001,30.785],[-81.943,30.723],[-81.843,30.58],[-81.728,30.368],[-81.681,30.331],[-81.721,30.301],[-81.719,30.275],[-81.697,30.244],[-81.714,30.162],[-81.755,30.136],[-81.771,30.108],[-81.76,30.068],[-81.685,29.996],[-81.619,29.843],[-81.662,29.702],[-81.64,29.656],[-81.641,29.63],[-81.685,29.602],[-81.64,29.528],[-81.57,29.481],[-81.51,29.373],[-81.459,29.217],[-81.36,29.137],[-81.364,29.051],[-81.332,28.979],[-81.338,28.875],[-81.324,28.859],[-81.325,28.83],[-81.292,28.809],[-81.292,28.785],[-81.314,28.761]]},"properties":{"name":"Auto Train"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-78.279,35.533],[-78.335,35.51],[-78.39,35.461],[-78.518,35.41],[-78.702,35.189],[-78.88,35.065],[-78.922,35.019],[-78.948,34.963],[-78.984,34.938],[-79.199,34.674],[-79.363,34.429],[-79.53,34.209],[-79.762,34.199],[-79.744,33.903],[-80.018,33.164],[-80.043,32.961],[-79.996,32.867],[-80.045,32.852],[-80.107,32.793],[-80.188,32.771],[-80.323,32.755],[-80.473,32.762],[-80.571,32.737],[-80.835,32.702],[-80.932,32.586],[-81.077,32.293],[-81.187,32.198],[-81.185,32.164],[-81.149,32.083]]},"properties":{"name":"Auto Train"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-77.22,38.708],[-77.25,38.656],[-77.262,38.565],[-77.309,38.505],[-77.328,38.449],[-77.411,38.352],[-77.449,38.34],[-77.448,38.302],[-77.461,38.295],[-77.452,38.261],[-77.434,38.245],[-77.438,38.199],[-77.457,38.163],[-77.38,38.073],[-77.363,37.965],[-77.439,37.955],[-77.469,37.915],[-77.457,37.834],[-77.501,37.694],[-77.508,37.645],[-77.496,37.617]]},"properties":{"name":"Auto Train"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-77.496,37.617],[-77.473,37.579],[-77.496,37.525],[-77.451,37.463],[-77.454,37.375],[-77.402,37.31],[-77.408,37.27],[-77.435,37.232],[-77.42,37.201],[-77.393,37.019],[-77.404,36.929],[-77.466,36.816],[-77.476,36.772],[-77.545,36.682],[-77.557,36.645],[-77.53,36.545],[-77.554,36.465],[-77.606,36.416],[-77.617,36.366],[-77.595,36.321],[-77.657,36.19],[-77.703,36.147],[-77.707,36.115],[-77.772,36.005],[-77.825,35.873],[-77.932,35.678],[-78.279,35.533]]},"properties":{"name":"Auto Train"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-87.639,41.879],[-87.638,41.86],[-87.961,41.796],[-88.074,41.798],[-88.132,41.78],[-88.292,41.768],[-88.331,41.747],[-88.379,41.698],[-89.111,41.556],[-89.262,41.466],[-89.458,41.388],[-89.508,41.392],[-89.727,41.303],[-89.78,41.302],[-89.94,41.242],[-89.999,41.187],[-90.161,41.114],[-90.238,41.066],[-90.367,40.941]]},"properties":{"name":"California Zephyr"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-121.502,38.585],[-121.542,38.588],[-121.738,38.543],[-121.959,38.29],[-122.04,38.245],[-122.1,38.133],[-122.105,38.09],[-122.131,38.063],[-122.118,38.028],[-122.137,38.02]]},"properties":{"name":"California Zephyr"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-121.083,38.904],[-121.086,38.887],[-121.107,38.878],[-121.209,38.861],[-121.23,38.801],[-121.26,38.761],[-121.302,38.739],[-121.448,38.599],[-121.451,38.584],[-121.502,38.585]]},"properties":{"name":"California Zephyr"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-122.135,38.02],[-122.165,38.027],[-122.191,38.052],[-122.239,38.058],[-122.298,38.011],[-122.353,38.001],[-122.36,37.947],[-122.309,37.893],[-122.292,37.841]]},"properties":{"name":"California Zephyr"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-121.083,38.904],[-121.089,38.932],[-121.043,38.944],[-120.988,38.995],[-120.967,39.031],[-120.969,39.074],[-120.944,39.123],[-120.931,39.113],[-120.931,39.125],[-120.902,39.137],[-120.878,39.171],[-120.833,39.191],[-120.829,39.208],[-120.763,39.2],[-120.75,39.234],[-120.713,39.257],[-120.712,39.248],[-120.7,39.252],[-120.673,39.298],[-120.625,39.319],[-120.587,39.329],[-120.529,39.296],[-120.391,39.324],[-120.294,39.3],[-120.244,39.315],[-120.269,39.292],[-120.229,39.315],[-120.131,39.342],[-120.125,39.361],[-120.111,39.362],[-120.117,39.371],[-120.094,39.385],[-120.071,39.367],[-120.021,39.378],[-120.033,39.427],[-120.006,39.45],[-119.993,39.514],[-119.982,39.52],[-119.887,39.507],[-119.777,39.534],[-119.659,39.51],[-119.624,39.516],[-119.586,39.547],[-119.477,39.565],[-119.458,39.589],[-119.376,39.589],[-119.291,39.617],[-119.109,39.592],[-119.075,39.563],[-119.034,39.567],[-118.796,39.706],[-118.77,39.754],[-118.771,39.878],[-118.693,39.946],[-118.656,40.052],[-118.506,40.135],[-118.463,40.193],[-118.399,40.238],[-118.315,40.343],[-118.288,40.455],[-118.298,40.488],[-118.281,40.56],[-118.246,40.61],[-118.199,40.646],[-118.068,40.685],[-118.034,40.741],[-118.018,40.798],[-117.984,40.843],[-117.932,40.882],[-117.766,40.931],[-117.691,41.012],[-117.622,41.028],[-117.558,41.018],[-117.486,40.956],[-117.416,40.964],[-117.328,41.001],[-117.142,40.806],[-116.899,40.618],[-116.805,40.614],[-116.759,40.655],[-116.717,40.672],[-116.639,40.675],[-116.532,40.702],[-116.516,40.698],[-116.485,40.602],[-116.462,40.579],[-116.356,40.567],[-116.314,40.58],[-116.221,40.58],[-116.169,40.634],[-116.141,40.695],[-116.066,40.725],[-115.979,40.714],[-115.927,40.763],[-115.763,40.826],[-115.687,40.917],[-115.659,40.933],[-115.606,40.944],[-115.552,40.929],[-115.484,40.932],[-115.458,40.945],[-115.416,41.004],[-115.352,41.044],[-115.134,41.128],[-115.068,41.137],[-114.975,41.12],[-114.966,41.071],[-114.976,41.018],[-114.944,40.95],[-114.891,40.896],[-114.646,40.793],[-114.601,40.754],[-114.592,40.732],[-114.566,40.734],[-114.541,40.781],[-114.376,40.912],[-114.345,40.922],[-114.287,40.912],[-114.269,40.945],[-114.287,40.902],[-114.268,40.891],[-114.247,40.832],[-114.224,40.805],[-114.14,40.762],[-114.142,40.705],[-114.059,40.735],[-114.014,40.738],[-113.203,40.72],[-113.072,40.703],[-113.021,40.724],[-112.99,40.762],[-112.961,40.767],[-112.928,40.809],[-112.879,40.839],[-112.78,40.766],[-112.636,40.745],[-112.371,40.668],[-112.188,40.729],[-112.15,40.769],[-112.098,40.769],[-112.077,40.753],[-112.035,40.751],[-111.913,40.769],[-111.896,40.665],[-111.907,40.615],[-111.9,40.565],[-111.904,40.511],[-111.932,40.459],[-111.854,40.383],[-111.8,40.368],[-111.675,40.228],[-111.631,40.205],[-111.605,40.148],[-111.615,40.128],[-111.568,40.06],[-111.504,40.027],[-111.493,39.998],[-111.405,39.996],[-111.29,39.952],[-111.228,39.955],[-111.198,39.945],[-111.197,39.931],[-111.165,39.931],[-111.19,39.914],[-111.118,39.932],[-111.069,39.921],[-111.007,39.839],[-110.961,39.837],[-110.937,39.818],[-110.942,39.805],[-110.923,39.804],[-110.927,39.786],[-110.865,39.729],[-110.868,39.707],[-110.853,39.685],[-110.859,39.644],[-110.82,39.602],[-110.603,39.46],[-110.553,39.46],[-110.439,39.377],[-110.404,39.378],[-110.384,39.348],[-110.389,39.323],[-110.374,39.281],[-110.341,39.255],[-110.357,39.206],[-110.3,39.027],[-110.244,38.996],[-110.111,38.988],[-110.031,38.958],[-109.999,38.961],[-109.93,38.924],[-109.723,38.972],[-109.688,38.956],[-109.641,38.967],[-109.542,38.961],[-109.454,38.936],[-109.362,38.946],[-109.34,38.966],[-109.28,38.975],[-109.251,39.012],[-109.211,39.033],[-109.177,39.077],[-109.114,39.084],[-109.076,39.121],[-109.039,39.119],[-108.996,39.157],[-108.958,39.159],[-108.96,39.173],[-108.94,39.178],[-108.932,39.205],[-108.885,39.225],[-108.825,39.205],[-108.566,39.061],[-108.325,39.116],[-108.304,39.17],[-108.258,39.211],[-108.275,39.235],[-108.258,39.241],[-108.245,39.312],[-108.161,39.357],[-108.019,39.473],[-107.978,39.476],[-107.932,39.5],[-107.898,39.5],[-107.882,39.488],[-107.875,39.507],[-107.824,39.527],[-107.705,39.547],[-107.627,39.546],[-107.537,39.571],[-107.441,39.575],[-107.399,39.557],[-107.352,39.559],[-107.326,39.548],[-107.273,39.566],[-107.243,39.557],[-107.121,39.624],[-107.057,39.642],[-107.07,39.658],[-107.071,39.698],[-107.048,39.701],[-107.023,39.722],[-107.019,39.75],[-106.994,39.783],[-106.903,39.869],[-106.877,39.882],[-106.858,39.877],[-106.848,39.897],[-106.8,39.897],[-106.782,39.915],[-106.716,39.904],[-106.678,39.859],[-106.642,39.856],[-106.608,39.908],[-106.58,39.916],[-106.571,39.943],[-106.52,39.969],[-106.514,39.992],[-106.398,40.056],[-106.35,40.048],[-106.226,40.068],[-106.136,40.051],[-106.102,40.081],[-105.999,40.109],[-105.887,40.073],[-105.878,40.029],[-105.833,39.992],[-105.816,39.942],[-105.763,39.888],[-105.517,39.917],[-105.48,39.91],[-105.442,39.938],[-105.346,39.93],[-105.31,39.908],[-105.286,39.924],[-105.274,39.885],[-105.284,39.878],[-105.259,39.852],[-105.249,39.854],[-105.259,39.863],[-105.204,39.86],[-105.141,39.844],[-105.098,39.812],[-105.002,39.803],[-104.996,39.762]]},"properties":{"OBJECTID":68,"cartodb_id":17,"name":"California Zephyr","cmsid":"1237608341980","segmentid":"33","north":41.25095,"south":38.903554,"east":-90.363018,"west":-121.092785,"created_at":"2015-07-31T00:00:00Z","updated_at":"2015-07-31T00:00:00Z","Shape__Length":37.597484883556035,"routeName":"California Zephyr"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-105.001,39.754],[-104.996,39.757]]},"properties":{"OBJECTID":68,"cartodb_id":17,"name":"California Zephyr","cmsid":"1237608341980","segmentid":"33","north":41.25095,"south":38.903554,"east":-90.363018,"west":-121.092785,"created_at":"2015-07-31T00:00:00Z","updated_at":"2015-07-31T00:00:00Z","Shape__Length":37.597484883556035,"routeName":"California Zephyr"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-104.996,39.757],[-104.996,39.762]]},"properties":{"OBJECTID":68,"cartodb_id":17,"name":"California Zephyr","cmsid":"1237608341980","segmentid":"33","north":41.25095,"south":38.903554,"east":-90.363018,"west":-121.092785,"created_at":"2015-07-31T00:00:00Z","updated_at":"2015-07-31T00:00:00Z","Shape__Length":37.597484883556035,"routeName":"California Zephyr"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-104.996,39.757],[-104.993,39.764]]},"properties":{"OBJECTID":68,"cartodb_id":17,"name":"California Zephyr","cmsid":"1237608341980","segmentid":"33","north":41.25095,"south":38.903554,"east":-90.363018,"west":-121.092785,"created_at":"2015-07-31T00:00:00Z","updated_at":"2015-07-31T00:00:00Z","Shape__Length":37.597484883556035,"routeName":"California Zephyr"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-104.996,39.762],[-104.993,39.764]]},"properties":{"OBJECTID":68,"cartodb_id":17,"name":"California Zephyr","cmsid":"1237608341980","segmentid":"33","north":41.25095,"south":38.903554,"east":-90.363018,"west":-121.092785,"created_at":"2015-07-31T00:00:00Z","updated_at":"2015-07-31T00:00:00Z","Shape__Length":37.597484883556035,"routeName":"California Zephyr"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-104.993,39.764],[-104.601,40.102],[-104.534,40.102],[-104.473,40.126],[-104.439,40.165],[-104.195,40.178],[-104.019,40.25],[-103.936,40.242],[-103.566,40.264],[-103.515,40.255],[-103.343,40.161],[-103.047,40.155],[-102.549,40.109],[-102.504,40.116],[-102.323,40.102],[-102.242,40.079],[-102.129,40.086],[-102.025,40.058],[-101.935,40.016],[-101.696,40.043],[-101.588,40.033],[-101.538,40.044],[-101.486,40.08],[-101.375,40.122],[-101.019,40.179],[-100.816,40.231],[-100.689,40.2],[-100.585,40.197],[-100.494,40.226],[-100.306,40.248],[-100.026,40.306],[-99.856,40.299],[-99.564,40.227],[-99.528,40.334],[-99.488,40.353],[-99.399,40.429],[-98.934,40.507],[-98.665,40.621],[-98.564,40.595],[-98.436,40.582],[-98.245,40.59],[-98.11,40.617],[-97.882,40.6],[-97.835,40.622],[-97.193,40.661],[-96.97,40.624],[-96.884,40.683],[-96.834,40.761],[-96.752,40.775],[-96.71,40.821],[-96.452,40.953],[-96.382,41.022],[-96.281,41.089],[-96.271,41.12],[-96.213,41.171],[-96.173,41.185],[-96.106,41.175],[-96.039,41.199],[-95.989,41.193],[-95.933,41.251],[-95.914,41.233],[-95.931,41.216],[-95.929,41.195],[-95.899,41.18],[-95.886,41.153],[-95.89,41.109],[-95.933,41.063],[-95.923,41.048],[-95.89,41.041],[-95.872,41.001],[-95.831,41.016],[-95.767,41.017],[-95.696,41.057],[-95.563,41.005],[-95.493,41.026],[-95.237,41.002],[-95.197,41.01],[-94.972,40.925],[-94.785,40.965],[-94.684,41.005],[-94.626,41.01],[-94.567,41.039],[-94.391,41.046],[-94.336,41.058],[-94.287,41.039],[-94.089,41.019],[-93.916,41.055],[-93.819,41.023],[-93.745,41.043],[-93.685,41.015],[-93.563,41.004],[-93.477,41.027],[-93.352,41.04],[-93.243,40.973],[-93.152,40.988],[-93.013,40.964],[-92.949,40.975],[-92.863,41.034],[-92.813,41.03],[-92.769,41.044],[-92.719,41.08],[-92.647,41.093],[-92.535,41.09],[-92.404,41.01],[-92.328,40.991],[-92.172,40.992],[-92.008,41.015],[-91.852,41.01],[-91.799,40.995],[-91.549,40.971],[-91.402,40.93],[-91.262,40.83],[-91.132,40.831],[-91.096,40.797],[-90.96,40.865],[-90.804,40.853],[-90.651,40.907],[-90.607,40.891],[-90.468,40.889],[-90.363,40.945]]},"properties":{"OBJECTID":68,"cartodb_id":17,"name":"California Zephyr","cmsid":"1237608341980","segmentid":"33","north":41.25095,"south":38.903554,"east":-90.363018,"west":-121.092785,"created_at":"2015-07-31T00:00:00Z","updated_at":"2015-07-31T00:00:00Z","Shape__Length":37.597484883556035,"routeName":"California Zephyr"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-121.502,38.585],[-121.542,38.588],[-121.738,38.543],[-121.959,38.29],[-122.04,38.245],[-122.1,38.133],[-122.105,38.09],[-122.131,38.063],[-122.118,38.028],[-122.137,38.02]]},"properties":{"name":"Capitol Corridor"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-121.903,37.33],[-121.941,37.355],[-121.977,37.421],[-121.974,37.481],[-122.033,37.525],[-122.005,37.561],[-121.97,37.572],[-122.016,37.593],[-122.214,37.766],[-122.305,37.806]]},"properties":{"name":"Capitol Corridor"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-121.083,38.904],[-121.086,38.887],[-121.107,38.878],[-121.209,38.861],[-121.23,38.801],[-121.26,38.761],[-121.302,38.739],[-121.448,38.599],[-121.451,38.584],[-121.502,38.585]]},"properties":{"name":"Capitol Corridor"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-122.137,38.02],[-122.169,38.029],[-122.189,38.051],[-122.24,38.058],[-122.297,38.011],[-122.355,37.999],[-122.359,37.945],[-122.31,37.895],[-122.292,37.841],[-122.305,37.806],[-122.272,37.793]]},"properties":{"name":"Capitol Corridor"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-81.696,41.506],[-81.738,41.487],[-81.885,41.375],[-82.097,41.37],[-82.343,41.419],[-82.494,41.371],[-82.688,41.438],[-82.761,41.444],[-82.834,41.498],[-83.114,41.521],[-83.434,41.569],[-83.548,41.638],[-84.894,41.426],[-85.548,41.463],[-85.638,41.492],[-85.865,41.601],[-85.972,41.681],[-86.107,41.661],[-86.245,41.663],[-86.29,41.679],[-86.501,41.705],[-86.754,41.604],[-86.9,41.599],[-87.077,41.614]]},"properties":{"name":"Capitol Limited"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-87.639,41.879],[-87.634,41.783],[-87.417,41.634],[-87.322,41.605],[-87.16,41.62],[-87.074,41.614]]},"properties":{"name":"Capitol Limited"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-81.696,41.506],[-81.658,41.509],[-81.627,41.479],[-81.627,41.449],[-81.542,41.395],[-81.501,41.302],[-81.464,41.256],[-81.243,41.143],[-81.205,41.1],[-81.143,40.98],[-81.099,40.925],[-80.9,40.922],[-80.797,40.861],[-80.736,40.885],[-80.67,40.883],[-80.628,40.866],[-80.595,40.832],[-80.482,40.841],[-80.41,40.865],[-80.395,40.816],[-80.329,40.815],[-80.324,40.749],[-80.29,40.701],[-80.253,40.681],[-80.234,40.644],[-80.232,40.587],[-80.19,40.537],[-80.076,40.501],[-80.009,40.45],[-79.957,40.461],[-79.946,40.402],[-79.895,40.417],[-79.846,40.385],[-79.838,40.359],[-79.869,40.343],[-79.834,40.314],[-79.79,40.316],[-79.804,40.296],[-79.774,40.289],[-79.796,40.278],[-79.787,40.265],[-79.804,40.235],[-79.765,40.228],[-79.769,40.208],[-79.756,40.191],[-79.777,40.182],[-79.743,40.156],[-79.742,40.135],[-79.759,40.114],[-79.711,40.086],[-79.725,40.073],[-79.697,40.052],[-79.688,40.062],[-79.638,40.038],[-79.614,40.049],[-79.599,40.042],[-79.582,39.975],[-79.502,39.964],[-79.471,39.915],[-79.487,39.906],[-79.475,39.904],[-79.476,39.89],[-79.494,39.872],[-79.46,39.847],[-79.423,39.856],[-79.357,39.812],[-79.335,39.81],[-79.309,39.828],[-79.26,39.832],[-79.229,39.852],[-79.23,39.882],[-79.16,39.913],[-79.134,39.906],[-79.113,39.88],[-79.064,39.868],[-79.048,39.856],[-79.044,39.832],[-79.025,39.817],[-78.996,39.818],[-78.978,39.797],[-78.939,39.835],[-78.851,39.809],[-78.802,39.845],[-78.758,39.824],[-78.736,39.833],[-78.721,39.826],[-78.723,39.778],[-78.777,39.714],[-78.787,39.67],[-78.762,39.656],[-78.767,39.636],[-78.733,39.621],[-78.727,39.565],[-78.563,39.517],[-78.468,39.517],[-78.434,39.542],[-78.425,39.567],[-78.396,39.582],[-78.394,39.604],[-78.371,39.613],[-78.379,39.625],[-78.36,39.624],[-78.353,39.64],[-78.267,39.618],[-78.223,39.662],[-78.231,39.673],[-78.191,39.69],[-78.104,39.679],[-78.055,39.651],[-78.011,39.598],[-77.984,39.59],[-77.963,39.462],[-77.908,39.442],[-77.878,39.371],[-77.812,39.363],[-77.727,39.324],[-77.671,39.329],[-77.603,39.305],[-77.559,39.308],[-77.537,39.273],[-77.442,39.224],[-77.183,39.14],[-77.136,39.075],[-77.004,38.958],[-76.994,38.927],[-77.006,38.897]]},"properties":{"name":"Capitol Limited"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-86.16,39.763],[-86.091,39.766],[-85.925,39.731],[-85.453,39.61],[-85.306,39.628],[-85.205,39.661],[-85.089,39.639],[-85.014,39.673],[-84.996,39.641],[-84.951,39.652],[-84.818,39.57],[-84.745,39.504],[-84.734,39.464],[-84.562,39.396],[-84.547,39.363],[-84.467,39.3],[-84.457,39.276],[-84.465,39.218],[-84.544,39.149],[-84.539,39.102],[-84.505,39.071],[-84.475,39.109],[-84.455,39.112],[-84.439,39.098],[-84.427,39.049],[-84.349,39.029],[-84.318,39.008],[-84.29,38.946],[-84.242,38.883],[-84.237,38.833],[-84.218,38.804],[-84.092,38.768],[-83.95,38.781],[-83.872,38.756],[-83.839,38.711],[-83.799,38.697],[-83.757,38.644],[-83.657,38.625],[-83.612,38.68],[-83.527,38.697],[-83.457,38.666],[-83.353,38.644],[-83.319,38.602],[-83.286,38.595],[-83.243,38.623],[-83.159,38.61],[-83.016,38.724],[-82.963,38.721],[-82.901,38.737],[-82.88,38.727],[-82.883,38.684],[-82.845,38.583],[-82.8,38.557],[-82.696,38.531],[-82.675,38.5],[-82.618,38.47],[-82.599,38.405],[-82.56,38.392],[-82.313,38.429],[-82.267,38.409],[-82.18,38.444],[-82.081,38.411],[-81.981,38.441],[-81.867,38.445],[-81.841,38.39],[-81.772,38.372],[-81.738,38.35],[-81.671,38.363],[-81.574,38.313],[-81.573,38.252],[-81.511,38.199],[-81.482,38.195],[-81.441,38.217],[-81.418,38.2],[-81.381,38.209],[-81.367,38.188],[-81.31,38.177],[-81.273,38.13],[-81.223,38.122],[-81.209,38.156],[-81.197,38.159],[-81.179,38.15],[-81.177,38.134],[-81.155,38.138],[-81.147,38.114],[-81.126,38.119],[-81.102,38.102],[-81.068,38.052],[-81.034,38.045],[-81.02,38.004],[-81.026,37.959],[-81.083,37.963],[-81.056,37.921],[-81.024,37.911],[-81.063,37.893],[-81.071,37.858],[-81.047,37.856],[-81.057,37.842],[-81.05,37.834],[-80.964,37.833],[-80.892,37.778],[-80.92,37.742],[-80.889,37.723],[-80.875,37.688],[-80.892,37.666],[-80.88,37.652],[-80.828,37.66],[-80.801,37.642],[-80.758,37.652],[-80.736,37.645],[-80.731,37.676],[-80.698,37.676],[-80.668,37.697],[-80.669,37.728],[-80.618,37.716],[-80.554,37.747],[-80.499,37.722],[-80.429,37.777],[-80.356,37.768],[-80.295,37.791],[-80.273,37.752],[-80.228,37.742],[-80.21,37.762],[-80.194,37.756],[-80.13,37.786],[-80.039,37.808],[-79.993,37.796],[-79.988,37.761],[-79.914,37.778],[-79.8,37.816],[-79.746,37.847],[-79.641,37.956],[-79.547,38.008],[-79.491,37.988],[-79.4,38.071],[-79.287,38.11],[-79.276,38.152],[-79.243,38.186],[-79.185,38.149],[-79.057,38.145],[-78.995,38.099],[-78.927,38.097],[-78.876,38.071],[-78.853,38.03],[-78.718,38.077],[-78.619,38.055],[-78.535,38.055],[-78.494,38.032]]},"properties":{"name":"Cardinal"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-77.006,38.898],[-77.007,38.885],[-77.032,38.883],[-77.048,38.863],[-77.047,38.824],[-77.058,38.809],[-77.106,38.807]]},"properties":{"name":"Cardinal"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-86.16,39.763],[-86.295,39.737],[-86.317,39.812],[-86.413,39.853],[-86.545,39.888],[-86.899,40.044],[-86.913,40.299],[-86.936,40.344],[-86.933,40.382],[-86.896,40.411],[-86.861,40.491],[-86.836,40.516],[-86.867,40.592],[-86.878,40.86],[-87.094,40.942],[-87.158,40.944],[-87.411,41.271],[-87.446,41.353],[-87.463,41.447],[-87.518,41.499],[-87.518,41.545],[-87.612,41.593],[-87.613,41.679],[-87.633,41.692],[-87.64,41.758],[-87.639,41.879]]},"properties":{"name":"Cardinal"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-77.106,38.807],[-77.166,38.793],[-77.326,38.801],[-77.371,38.782],[-77.431,38.78],[-77.459,38.753],[-77.503,38.743],[-77.616,38.677],[-77.809,38.53],[-77.993,38.473],[-77.995,38.435],[-78.056,38.329],[-78.105,38.27],[-78.11,38.245]]},"properties":{"name":"Cardinal"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-75.182,39.956],[-75.322,39.881],[-75.343,39.856],[-75.435,39.815],[-75.494,39.755],[-75.767,39.666],[-75.843,39.607],[-75.928,39.611],[-75.974,39.58],[-76.101,39.549],[-76.158,39.515],[-76.217,39.45],[-76.338,39.397],[-76.384,39.35],[-76.477,39.328],[-76.531,39.299],[-76.621,39.311],[-76.649,39.302],[-76.69,39.257],[-76.696,39.121],[-76.707,39.085],[-76.741,39.044],[-76.908,38.92],[-76.98,38.918],[-77.006,38.898]]},"properties":{"name":"Cardinal"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.994,40.751],[-74.049,40.773],[-74.163,40.736],[-74.274,40.608],[-74.355,40.542],[-74.451,40.493],[-74.532,40.388],[-74.774,40.203],[-74.796,40.159],[-74.848,40.109],[-74.923,40.09],[-75.057,40.014],[-75.165,39.994],[-75.198,39.973],[-75.182,39.956]]},"properties":{"name":"Cardinal"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-78.11,38.245],[-78.123,38.195],[-78.319,38.018],[-78.421,38.009],[-78.493,38.031]]},"properties":{"name":"Cardinal"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-86.16,39.763],[-86.295,39.737],[-86.317,39.812],[-86.413,39.853],[-86.545,39.888],[-86.899,40.044],[-86.913,40.299],[-86.936,40.344],[-86.933,40.382],[-86.896,40.411],[-86.861,40.491],[-86.836,40.516],[-86.867,40.592],[-86.878,40.86],[-87.094,40.942],[-87.158,40.944],[-87.411,41.271],[-87.446,41.353],[-87.463,41.447],[-87.518,41.499],[-87.518,41.545],[-87.612,41.593],[-87.613,41.679],[-87.633,41.692],[-87.64,41.758],[-87.639,41.879]]},"properties":{"name":"Hoosier State"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-79.785,36.069],[-79.871,36.052],[-79.915,36.003],[-79.968,35.967],[-80.012,35.954],[-80.068,35.889],[-80.254,35.819],[-80.282,35.803],[-80.307,35.76],[-80.346,35.728],[-80.397,35.72],[-80.427,35.689],[-80.496,35.652],[-80.563,35.577],[-80.614,35.542],[-80.627,35.484],[-80.588,35.393],[-80.596,35.347],[-80.647,35.317],[-80.715,35.31],[-80.747,35.266],[-80.824,35.24]]},"properties":{"name":"Carolinian / Piedmont"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-78.779,35.789],[-78.824,35.807],[-78.855,35.874],[-78.848,35.948],[-78.859,35.967],[-78.962,36.03],[-79.055,36.036],[-79.103,36.071],[-79.29,36.099],[-79.402,36.08],[-79.467,36.112],[-79.515,36.1],[-79.615,36.114],[-79.786,36.069]]},"properties":{"name":"Carolinian / Piedmont"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-78.648,35.777],[-78.694,35.795],[-78.779,35.789]]},"properties":{"name":"Carolinian / Piedmont"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-87.637,41.861],[-87.621,41.86],[-87.586,41.8],[-87.62,41.65],[-87.845,41.229],[-87.868,41.11],[-87.988,40.796],[-88.03,40.617],[-88.242,40.113],[-88.322,39.597],[-88.549,39.114],[-88.579,39.003],[-88.595,38.977],[-88.973,38.669],[-89.129,38.561],[-89.176,38.397],[-89.242,38.07],[-89.216,37.724]]},"properties":{"name":"City of New Orleans"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-89.215,37.719],[-89.211,37.681],[-89.223,37.652],[-89.207,37.621],[-89.226,37.563],[-89.255,37.537],[-89.264,37.509],[-89.255,37.466],[-89.23,37.451],[-89.219,37.42],[-89.194,37.411],[-89.165,37.366],[-89.177,37.351],[-89.162,37.307],[-89.183,37.286],[-89.211,37.204],[-89.196,37.168],[-89.202,37.101],[-89.19,37.023],[-89.166,37.025],[-89.151,37.003],[-89.117,36.994],[-89.018,36.883],[-88.995,36.635],[-88.883,36.517],[-88.896,36.488],[-89.006,36.407],[-89.049,36.356],[-89.189,36.259],[-89.192,36.204],[-89.25,36.123],[-89.382,36.027],[-89.396,35.858],[-89.409,35.832],[-89.435,35.822],[-89.46,35.791],[-89.512,35.771],[-89.638,35.586],[-89.643,35.553],[-89.844,35.399],[-89.923,35.303],[-90.001,35.244],[-90.036,35.202],[-90.061,35.137],[-90.06,35.101],[-90.096,35.067],[-90.155,34.949],[-90.218,34.901],[-90.244,34.729],[-90.234,34.644],[-90.195,34.533],[-90.192,34.468],[-90.275,34.375],[-90.28,33.857],[-90.3,33.836],[-90.244,33.769],[-90.206,33.761],[-90.227,33.695],[-90.165,33.539],[-90.217,33.465],[-90.208,33.406],[-90.24,33.341],[-90.222,33.281],[-90.221,33.178],[-90.267,33.114],[-90.319,33.086],[-90.331,33.033],[-90.326,32.984],[-90.375,32.931],[-90.398,32.862],[-90.445,32.823],[-90.484,32.754],[-90.471,32.734],[-90.416,32.713],[-90.343,32.618],[-90.261,32.415],[-90.187,32.33],[-90.226,32.2],[-90.272,32.119],[-90.333,32.052],[-90.4,31.899],[-90.393,31.873],[-90.409,31.796],[-90.386,31.715],[-90.457,31.536],[-90.442,31.464],[-90.463,31.385],[-90.45,31.325],[-90.467,31.284],[-90.445,31.175],[-90.472,31.114],[-90.467,31.01],[-90.512,30.922],[-90.507,30.657],[-90.397,30.267],[-90.432,30.163],[-90.426,30.103],[-90.399,30.072],[-90.264,29.981],[-90.108,29.968]]},"properties":{"name":"City of New Orleans"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-90.108,29.968],[-90.079,29.946]]},"properties":{"name":"City of New Orleans"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-123.092,44.055],[-123.043,44.039],[-122.941,44.036],[-122.827,43.908],[-122.745,43.907],[-122.685,43.863],[-122.63,43.845],[-122.622,43.826],[-122.56,43.808],[-122.523,43.782],[-122.526,43.761],[-122.493,43.76],[-122.479,43.747],[-122.431,43.75],[-122.442,43.742],[-122.436,43.731],[-122.384,43.733],[-122.281,43.707],[-122.232,43.681],[-122.219,43.651],[-122.318,43.692],[-122.254,43.664],[-122.218,43.631],[-122.159,43.624],[-122.143,43.605],[-122.094,43.611],[-122.057,43.6],[-122.052,43.584],[-121.955,43.538],[-121.976,43.517],[-121.942,43.485],[-121.95,43.439],[-121.912,43.436],[-121.861,43.408],[-121.859,43.392],[-121.805,43.347],[-121.817,43.325],[-121.778,43.2],[-121.802,43.112],[-121.819,42.836],[-121.834,42.786],[-121.816,42.671],[-121.831,42.651],[-121.825,42.623],[-121.859,42.613],[-121.885,42.519],[-121.866,42.436],[-121.815,42.386],[-121.829,42.345],[-121.817,42.273],[-121.773,42.229],[-121.771,42.19],[-121.823,42.132],[-121.89,42.004],[-121.918,41.996],[-121.911,41.973],[-122.003,41.836],[-122.0,41.729],[-121.985,41.686],[-121.95,41.671],[-121.963,41.648],[-122.008,41.642],[-122.079,41.672],[-122.102,41.649],[-122.183,41.639],[-122.194,41.63],[-122.186,41.542],[-122.226,41.513],[-122.288,41.506],[-122.335,41.461],[-122.332,41.429],[-122.385,41.36],[-122.323,41.328],[-122.308,41.299],[-122.315,41.284],[-122.266,41.254],[-122.304,41.267],[-122.266,41.246],[-122.28,41.233],[-122.27,41.212],[-122.297,41.17],[-122.291,41.152],[-122.316,41.147],[-122.325,41.111],[-122.392,41.043],[-122.391,41.029],[-122.411,41.015],[-122.406,41.002],[-122.439,40.962],[-122.436,40.945],[-122.382,40.914],[-122.374,40.899],[-122.39,40.869],[-122.347,40.825],[-122.328,40.825],[-122.313,40.79],[-122.331,40.723],[-122.393,40.675],[-122.398,40.632],[-122.388,40.605],[-122.4,40.594],[-122.375,40.501],[-122.244,40.405],[-122.316,40.35],[-122.328,40.31],[-122.312,40.257],[-122.27,40.233],[-122.245,40.202],[-122.242,40.179],[-122.136,40.031],[-122.103,40.026],[-121.961,39.784],[-121.859,39.734],[-121.817,39.694],[-121.623,39.169],[-121.592,39.159],[-121.6,39.139],[-121.561,39.109],[-121.467,38.618],[-121.479,38.586]]},"properties":{"name":"Coast Starlight"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-120.655,35.276],[-120.619,35.236],[-120.61,35.193],[-120.633,35.162],[-120.632,35.13],[-120.592,35.082],[-120.594,35.035],[-120.566,34.982],[-120.574,34.944],[-120.564,34.919],[-120.514,34.876],[-120.521,34.838],[-120.549,34.828],[-120.589,34.838],[-120.61,34.794],[-120.615,34.745],[-120.596,34.698],[-120.643,34.584],[-120.631,34.563],[-120.585,34.561],[-120.514,34.527],[-120.454,34.452],[-120.297,34.472],[-120.135,34.474],[-120.09,34.461],[-120.004,34.461],[-119.911,34.432],[-119.779,34.442],[-119.7,34.411],[-119.66,34.423],[-119.56,34.416],[-119.459,34.374],[-119.311,34.277],[-119.195,34.244],[-119.165,34.197],[-119.049,34.196],[-118.979,34.264],[-118.839,34.293],[-118.665,34.261],[-118.602,34.272],[-118.592,34.24],[-118.321,34.186],[-118.281,34.16],[-118.228,34.091],[-118.234,34.058]]},"properties":{"name":"Coast Starlight"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-121.903,37.33],[-121.87,37.296],[-121.751,37.226],[-121.639,37.119],[-121.542,36.973],[-121.548,36.919],[-121.562,36.898],[-121.629,36.911],[-121.641,36.892],[-121.746,36.894],[-121.757,36.85],[-121.739,36.83],[-121.755,36.794],[-121.734,36.732],[-121.703,36.699],[-121.652,36.678],[-121.555,36.603],[-121.416,36.482],[-121.323,36.422],[-121.255,36.401],[-121.234,36.364],[-121.203,36.35],[-121.156,36.296],[-121.12,36.21],[-121.065,36.186],[-121.031,36.134],[-120.923,36.057],[-120.833,35.881],[-120.761,35.856],[-120.751,35.81],[-120.695,35.763],[-120.688,35.579],[-120.71,35.534],[-120.651,35.496],[-120.604,35.429],[-120.605,35.393],[-120.641,35.369],[-120.626,35.321],[-120.655,35.341],[-120.681,35.323],[-120.692,35.33],[-120.655,35.276]]},"properties":{"name":"Coast Starlight"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-122.687,45.628],[-122.693,45.68],[-122.744,45.752],[-122.739,45.806],[-122.757,45.929],[-122.843,45.996],[-122.871,46.089],[-122.915,46.129],[-122.912,46.168],[-122.897,46.189],[-122.904,46.219],[-122.885,46.253],[-122.963,46.4],[-122.965,46.433],[-122.933,46.502],[-122.944,46.518],[-122.937,46.541],[-122.908,46.577],[-122.967,46.631],[-122.972,46.674],[-122.931,46.762],[-122.863,46.806],[-122.866,46.876],[-122.852,46.948],[-122.822,46.979],[-122.719,47.026],[-122.691,47.057],[-122.691,47.076],[-122.57,47.111],[-122.497,47.154],[-122.486,47.222],[-122.472,47.232],[-122.399,47.24],[-122.349,47.202],[-122.27,47.188],[-122.228,47.216],[-122.24,47.463],[-122.332,47.564],[-122.329,47.598]]},"properties":{"name":"Coast Starlight"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-122.686,45.629],[-122.752,45.569],[-122.676,45.529]]},"properties":{"name":"Coast Starlight"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-122.676,45.529],[-122.646,45.497],[-122.632,45.447],[-122.573,45.424],[-122.568,45.398],[-122.6,45.362],[-122.654,45.331],[-122.658,45.289],[-122.675,45.27],[-122.752,45.236],[-122.932,45.081],[-122.983,45.021],[-123.028,44.935],[-123.026,44.915],[-122.955,44.848],[-122.937,44.767],[-122.947,44.735],[-122.997,44.734],[-123.009,44.716],[-123.053,44.701],[-123.067,44.646],[-123.107,44.625],[-123.108,44.329],[-123.165,44.28],[-123.169,44.253],[-123.202,44.228],[-123.199,44.18],[-123.15,44.085],[-123.092,44.055]]},"properties":{"name":"Coast Starlight"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-121.502,38.585],[-121.542,38.588],[-121.738,38.543],[-121.959,38.29],[-122.04,38.245],[-122.1,38.133],[-122.105,38.09],[-122.131,38.063],[-122.118,38.028],[-122.137,38.02]]},"properties":{"name":"Coast Starlight"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-122.273,37.794],[-122.243,37.786],[-122.179,37.735],[-122.184,37.716],[-122.162,37.683],[-122.064,37.586],[-122.054,37.541],[-121.975,37.481],[-121.977,37.42],[-121.942,37.357],[-121.903,37.33]]},"properties":{"name":"Coast Starlight"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-122.137,38.02],[-122.169,38.029],[-122.189,38.051],[-122.24,38.058],[-122.297,38.011],[-122.355,37.999],[-122.359,37.945],[-122.31,37.895],[-122.292,37.841],[-122.305,37.806],[-122.272,37.793]]},"properties":{"name":"Coast Starlight"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-77.006,38.898],[-77.007,38.885],[-77.032,38.883],[-77.048,38.863],[-77.047,38.824],[-77.058,38.809],[-77.106,38.807]]},"properties":{"name":"Crescent"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-77.106,38.807],[-77.166,38.793],[-77.326,38.801],[-77.371,38.782],[-77.431,38.78],[-77.459,38.753],[-77.503,38.743],[-77.616,38.677],[-77.809,38.53],[-77.993,38.473],[-77.995,38.435],[-78.056,38.329],[-78.105,38.27],[-78.11,38.245]]},"properties":{"name":"Crescent"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-78.493,38.031],[-78.574,38.006],[-78.661,37.913],[-78.711,37.884],[-78.754,37.8],[-78.797,37.752],[-78.932,37.673],[-78.996,37.604],[-79.048,37.579],[-79.084,37.533],[-79.115,37.518],[-79.148,37.484],[-79.166,37.442],[-79.158,37.407]]},"properties":{"name":"Crescent"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-79.157,37.405],[-79.153,37.393],[-79.202,37.311],[-79.286,37.235],[-79.301,37.148],[-79.291,37.111],[-79.359,37.043],[-79.353,36.962],[-79.404,36.872],[-79.408,36.788],[-79.395,36.708],[-79.37,36.67],[-79.367,36.598],[-79.387,36.575],[-79.457,36.559],[-79.492,36.475],[-79.524,36.469],[-79.656,36.379],[-79.664,36.368],[-79.645,36.308],[-79.65,36.267],[-79.69,36.244],[-79.723,36.204],[-79.726,36.181],[-79.779,36.123],[-79.786,36.069],[-79.872,36.051],[-79.91,36.006],[-79.966,35.968],[-80.011,35.955],[-80.071,35.887],[-80.13,35.874],[-80.284,35.802],[-80.306,35.761],[-80.344,35.73],[-80.396,35.721],[-80.424,35.69],[-80.513,35.638],[-80.565,35.576],[-80.611,35.546],[-80.626,35.482],[-80.59,35.399],[-80.591,35.355],[-80.649,35.317],[-80.718,35.308],[-80.745,35.268],[-80.862,35.222],[-80.9,35.222],[-81.05,35.244],[-81.07,35.234],[-81.127,35.278],[-81.208,35.262],[-81.246,35.288],[-81.305,35.282],[-81.351,35.231],[-81.489,35.137],[-81.618,35.079],[-81.646,35.077],[-81.724,35.018],[-81.797,35.021],[-81.827,34.994],[-81.882,34.983],[-81.942,34.952],[-81.966,34.965],[-82.081,34.946],[-82.108,34.961],[-82.15,34.936],[-82.224,34.936],[-82.263,34.918],[-82.311,34.917],[-82.409,34.876],[-82.431,34.848],[-82.49,34.851],[-82.555,34.825],[-82.62,34.835],[-82.707,34.776],[-82.75,34.766],[-82.756,34.742],[-82.814,34.695],[-82.864,34.691],[-82.919,34.713],[-82.95,34.684],[-82.994,34.686],[-83.085,34.661],[-83.159,34.662],[-83.308,34.578],[-83.443,34.556],[-83.538,34.504],[-83.665,34.388],[-83.723,34.381],[-83.768,34.355],[-83.847,34.257],[-83.997,34.136],[-84.086,34.04],[-84.219,33.936],[-84.326,33.876],[-84.378,33.809],[-84.415,33.785],[-84.482,33.806],[-84.535,33.84],[-84.697,33.791],[-84.726,33.762],[-84.806,33.731],[-84.914,33.729],[-84.978,33.742],[-85.017,33.732],[-85.04,33.751],[-85.185,33.705],[-85.242,33.731],[-85.265,33.726],[-85.279,33.742],[-85.313,33.733],[-85.35,33.743],[-85.376,33.73],[-85.482,33.726],[-85.488,33.712],[-85.561,33.685],[-85.595,33.646],[-85.64,33.649],[-85.642,33.662],[-85.678,33.673],[-85.789,33.615],[-85.824,33.616],[-85.83,33.645],[-85.849,33.658],[-85.897,33.654],[-85.929,33.625],[-86.0,33.606],[-86.068,33.62],[-86.194,33.61],[-86.234,33.589],[-86.295,33.582],[-86.337,33.593],[-86.36,33.584],[-86.423,33.591],[-86.451,33.573],[-86.478,33.574],[-86.528,33.541],[-86.653,33.563],[-86.708,33.538],[-86.751,33.538],[-86.883,33.469],[-87.08,33.26],[-87.168,33.198],[-87.305,33.159],[-87.399,33.167],[-87.418,33.185],[-87.454,33.183],[-87.499,33.204],[-87.558,33.197],[-87.6,33.036],[-87.677,32.935],[-87.75,32.874],[-87.825,32.845],[-87.866,32.847],[-88.124,32.693],[-88.166,32.656],[-88.187,32.578],[-88.32,32.474],[-88.336,32.448],[-88.399,32.417],[-88.574,32.415],[-88.659,32.391],[-88.733,32.352],[-88.767,32.292],[-88.771,32.216],[-88.825,32.181],[-88.876,32.09],[-88.924,31.927],[-88.944,31.925],[-88.98,31.892],[-89.015,31.8],[-89.119,31.724],[-89.125,31.696],[-89.279,31.507],[-89.286,31.443],[-89.267,31.377],[-89.277,31.335],[-89.334,31.277],[-89.373,31.256],[-89.389,31.187],[-89.405,31.177],[-89.435,31.088],[-89.447,31.001],[-89.474,30.974],[-89.535,30.836],[-89.57,30.795],[-89.602,30.707],[-89.634,30.672],[-89.695,30.47],[-89.787,30.27],[-89.928,30.09],[-89.981,30.051],[-90.045,30.028],[-90.044,29.987],[-90.056,29.986]]},"properties":{"name":"Crescent"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-75.182,39.956],[-75.322,39.881],[-75.343,39.856],[-75.435,39.815],[-75.494,39.755],[-75.767,39.666],[-75.843,39.607],[-75.928,39.611],[-75.974,39.58],[-76.101,39.549],[-76.158,39.515],[-76.217,39.45],[-76.338,39.397],[-76.384,39.35],[-76.477,39.328],[-76.531,39.299],[-76.621,39.311],[-76.649,39.302],[-76.69,39.257],[-76.696,39.121],[-76.707,39.085],[-76.741,39.044],[-76.908,38.92],[-76.98,38.918],[-77.006,38.898]]},"properties":{"name":"Crescent"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.994,40.751],[-74.049,40.773],[-74.163,40.736],[-74.274,40.608],[-74.355,40.542],[-74.451,40.493],[-74.532,40.388],[-74.774,40.203],[-74.796,40.159],[-74.848,40.109],[-74.923,40.09],[-75.057,40.014],[-75.165,39.994],[-75.198,39.973],[-75.182,39.956]]},"properties":{"name":"Crescent"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-78.11,38.245],[-78.204,38.218],[-78.286,38.17],[-78.336,38.173],[-78.429,38.106],[-78.493,38.031]]},"properties":{"name":"Crescent"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-90.056,29.986],[-90.113,29.989],[-90.108,29.968]]},"properties":{"name":"Crescent"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-69.966,43.911],[-70.04,43.905],[-70.102,43.856],[-70.251,43.778],[-70.304,43.711],[-70.28,43.647],[-70.292,43.654]]},"properties":{"name":"Downeaster"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-70.291,43.654],[-70.279,43.646],[-70.294,43.638],[-70.303,43.597],[-70.381,43.509],[-70.462,43.496],[-70.493,43.47],[-70.5,43.417],[-70.571,43.337],[-70.593,43.323],[-70.727,43.307],[-70.928,43.166],[-70.945,43.092],[-70.935,43.057],[-70.945,42.998],[-71.113,42.825],[-71.086,42.77],[-71.135,42.706],[-71.162,42.696],[-71.142,42.668],[-71.16,42.635],[-71.175,42.549],[-71.127,42.48],[-71.145,42.432],[-71.061,42.366]]},"properties":{"name":"Downeaster"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-122.686,45.629],[-122.752,45.569],[-122.676,45.529]]},"properties":{"name":"Empire Builder"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-117.443,47.651],[-117.432,47.613],[-117.507,47.55],[-117.518,47.514],[-117.616,47.464],[-117.794,47.429],[-117.923,47.302],[-118.022,47.296],[-118.076,47.258],[-118.281,47.205],[-118.412,47.1],[-118.425,47.055],[-118.533,47.007],[-118.577,46.969],[-118.67,46.963],[-118.712,46.941],[-118.731,46.904],[-118.758,46.896],[-118.805,46.84],[-118.834,46.753],[-118.882,46.706],[-118.863,46.662],[-118.944,46.603],[-119.009,46.58],[-119.0,46.563],[-119.016,46.528],[-119.014,46.461],[-119.12,46.325],[-119.084,46.233],[-119.099,46.204],[-118.955,46.08],[-118.956,46.038],[-119.033,45.973],[-119.171,45.935],[-119.264,45.948],[-119.45,45.921],[-119.58,45.937],[-119.638,45.921],[-119.699,45.885],[-119.777,45.866],[-119.849,45.867],[-119.879,45.841],[-119.965,45.83],[-120.156,45.776],[-120.233,45.729],[-120.446,45.704],[-120.506,45.709],[-120.536,45.734],[-120.61,45.752],[-120.91,45.657],[-121.076,45.656],[-121.17,45.608],[-121.189,45.614],[-121.212,45.672],[-121.348,45.71],[-121.397,45.697],[-121.525,45.729],[-121.704,45.699],[-121.833,45.712],[-121.909,45.676],[-121.914,45.654],[-121.989,45.64],[-122.214,45.562],[-122.26,45.559],[-122.399,45.584],[-122.446,45.578],[-122.686,45.629]]},"properties":{"name":"Empire Builder"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-93.085,44.947],[-93.146,44.932],[-93.186,44.955],[-93.185,44.97],[-93.243,44.992],[-93.269,45.029],[-93.269,45.096],[-93.285,45.134],[-93.372,45.204],[-93.497,45.243],[-93.586,45.315],[-93.794,45.34],[-93.936,45.429],[-93.997,45.445],[-94.14,45.558],[-94.2,45.621],[-94.18,45.679],[-94.193,45.722],[-94.3,45.835],[-94.373,45.993],[-94.586,46.147],[-94.79,46.353],[-94.957,46.377],[-95.191,46.461],[-95.266,46.469],[-95.447,46.543],[-95.576,46.597],[-95.657,46.661],[-95.703,46.729],[-95.754,46.748],[-95.813,46.805],[-96.052,46.88],[-96.248,46.907],[-96.311,46.883],[-96.325,46.857],[-96.353,46.846],[-96.451,46.873],[-96.753,46.874],[-96.812,46.888],[-96.934,47.054],[-97.059,47.393],[-97.111,47.679],[-97.111,47.917],[-97.519,47.918],[-97.634,47.904],[-97.665,47.924],[-97.72,47.93],[-97.801,47.989],[-97.945,47.997],[-98.066,48.025],[-98.622,48.067],[-98.854,48.106],[-99.217,48.276],[-99.471,48.295],[-99.792,48.361],[-99.942,48.371],[-100.314,48.362],[-100.922,48.254],[-101.219,48.235],[-101.285,48.241],[-101.361,48.219],[-101.43,48.247],[-101.543,48.253],[-101.89,48.349],[-102.251,48.337],[-102.422,48.311],[-102.557,48.314],[-102.705,48.343],[-102.78,48.385],[-102.989,48.399],[-103.081,48.388],[-103.24,48.296],[-103.407,48.286],[-103.477,48.245],[-103.504,48.203],[-103.584,48.144],[-103.678,48.136],[-103.745,48.094],[-103.849,48.065],[-103.93,48.012],[-103.992,47.999],[-104.055,48.006],[-104.136,48.061],[-104.183,48.077],[-104.192,48.091],[-104.172,48.119],[-104.258,48.159],[-104.339,48.14],[-104.47,48.151],[-104.572,48.132],[-104.618,48.147],[-104.786,48.139],[-104.868,48.158],[-105.023,48.109],[-105.059,48.108],[-105.207,48.108],[-105.335,48.135],[-105.966,48.048],[-106.212,48.077],[-106.309,48.128],[-106.395,48.137],[-106.43,48.156],[-106.587,48.162],[-106.626,48.191],[-106.745,48.242],[-106.845,48.324],[-106.945,48.37],[-107.011,48.372],[-107.263,48.424],[-107.391,48.472],[-107.463,48.468],[-107.557,48.403],[-107.667,48.379],[-107.813,48.36],[-108.05,48.366],[-108.291,48.407],[-108.339,48.435],[-108.455,48.452],[-108.91,48.564],[-109.246,48.596],[-109.382,48.593],[-109.518,48.56],[-109.638,48.569],[-109.662,48.554],[-110.187,48.584],[-110.34,48.559],[-110.833,48.56],[-110.865,48.555],[-110.913,48.518],[-111.159,48.503],[-111.239,48.468],[-111.365,48.477],[-111.441,48.462],[-111.618,48.482],[-111.753,48.477],[-111.891,48.522],[-112.153,48.566],[-112.34,48.642],[-112.62,48.632],[-112.795,48.604],[-112.826,48.585],[-112.897,48.572],[-112.946,48.542],[-113.026,48.531],[-113.041,48.544],[-113.116,48.517],[-113.175,48.46],[-113.215,48.452],[-113.217,48.42],[-113.249,48.41],[-113.283,48.367],[-113.365,48.304],[-113.417,48.296],[-113.434,48.28],[-113.451,48.285],[-113.454,48.268],[-113.489,48.243],[-113.554,48.234],[-113.606,48.268],[-113.648,48.352],[-113.73,48.419],[-113.791,48.424],[-113.862,48.492],[-113.943,48.502],[-114.011,48.491],[-114.019,48.472],[-114.046,48.458],[-114.062,48.4],[-114.2,48.379],[-114.358,48.414],[-114.39,48.437],[-114.395,48.463],[-114.419,48.472],[-114.491,48.462],[-114.633,48.592],[-114.8,48.684],[-114.842,48.693],[-114.957,48.599],[-114.945,48.45],[-114.911,48.41],[-114.915,48.4],[-115.033,48.353],[-115.039,48.304],[-115.072,48.283],[-115.28,48.234],[-115.305,48.277],[-115.304,48.322],[-115.321,48.362],[-115.39,48.359],[-115.436,48.372],[-115.466,48.414],[-115.535,48.391],[-115.665,48.445],[-115.768,48.454],[-115.862,48.439],[-115.903,48.486],[-115.941,48.505],[-115.99,48.583],[-116.046,48.607],[-116.104,48.676],[-116.134,48.685],[-116.143,48.714],[-116.17,48.716],[-116.2,48.696],[-116.312,48.695],[-116.347,48.656],[-116.387,48.644],[-116.382,48.628],[-116.402,48.603],[-116.393,48.559],[-116.405,48.536],[-116.44,48.519],[-116.497,48.423],[-116.548,48.379],[-116.57,48.293],[-116.548,48.284],[-116.523,48.244],[-116.535,48.216],[-116.592,48.182],[-116.648,48.025],[-116.698,47.988],[-116.725,47.913],[-116.827,47.848],[-117.092,47.714],[-117.415,47.656]]},"properties":{"name":"Empire Builder"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-117.415,47.656],[-117.454,47.647],[-117.491,47.681],[-117.571,47.687],[-117.852,47.536],[-117.989,47.496],[-118.154,47.529],[-118.245,47.493],[-118.273,47.441],[-118.409,47.362],[-118.654,47.333],[-118.734,47.334],[-118.767,47.348],[-118.794,47.332],[-118.977,47.404],[-119.228,47.433],[-119.341,47.414],[-119.399,47.384],[-119.443,47.383],[-119.538,47.333],[-119.581,47.277],[-119.629,47.259],[-119.915,47.233],[-119.966,47.249],[-119.965,47.273],[-120.001,47.232],[-120.048,47.228],[-120.083,47.241],[-120.08,47.329],[-120.094,47.348],[-120.133,47.369],[-120.17,47.365],[-120.282,47.388],[-120.337,47.458],[-120.407,47.48],[-120.463,47.52],[-120.566,47.545],[-120.648,47.614],[-120.637,47.712],[-120.701,47.71],[-120.74,47.729],[-120.75,47.762],[-120.854,47.785],[-120.999,47.77],[-121.157,47.708],[-121.278,47.712],[-121.295,47.705],[-121.299,47.688],[-121.319,47.71],[-121.373,47.709],[-121.399,47.732],[-121.448,47.746],[-121.52,47.804],[-121.567,47.811],[-121.557,47.821],[-121.602,47.817],[-121.618,47.835],[-121.661,47.835],[-121.721,47.866],[-121.855,47.851],[-121.972,47.858],[-122.168,47.926],[-122.194,47.95],[-122.199,47.979],[-122.215,47.98],[-122.306,47.946],[-122.335,47.853],[-122.395,47.802],[-122.373,47.728],[-122.38,47.709],[-122.403,47.697],[-122.405,47.672],[-122.381,47.65],[-122.381,47.635],[-122.329,47.598]]},"properties":{"name":"Empire Builder"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-87.917,43.034],[-87.909,43.001],[-87.927,42.97],[-87.927,42.835],[-87.906,42.724],[-87.905,42.561],[-87.932,42.468],[-87.9,42.291],[-87.848,42.162],[-87.72,41.91],[-87.687,41.888],[-87.641,41.888],[-87.639,41.879]]},"properties":{"name":"Empire Builder"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-87.917,43.034],[-87.964,43.031],[-88.034,43.058],[-88.076,43.041],[-88.128,43.069],[-88.478,43.105],[-88.574,43.131],[-88.761,43.215],[-88.844,43.303],[-88.936,43.333],[-89.018,43.343],[-89.067,43.393],[-89.261,43.464],[-89.279,43.485],[-89.417,43.521],[-89.448,43.547],[-89.75,43.605],[-89.841,43.679],[-90.089,43.803],[-90.184,43.881],[-90.425,43.974],[-90.581,44.008],[-90.693,43.992],[-90.818,43.928],[-90.966,43.893],[-91.105,43.89],[-91.142,43.866],[-91.204,43.86],[-91.279,43.834],[-91.438,43.988],[-91.66,44.052],[-91.75,44.097],[-91.833,44.171],[-91.934,44.222],[-92.001,44.335],[-92.236,44.424],[-92.324,44.501],[-92.397,44.518],[-92.425,44.543],[-92.521,44.571],[-92.581,44.571],[-92.792,44.725],[-92.845,44.74],[-92.882,44.779],[-92.99,44.85],[-93.027,44.933],[-93.085,44.947]]},"properties":{"name":"Empire Builder"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-79.056,43.11],[-78.951,43.126],[-78.926,43.117],[-78.92,43.068],[-78.868,43.015],[-78.882,43.008],[-78.901,42.915],[-78.878,42.879],[-78.727,42.907]]},"properties":{"name":"Empire Service"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.739,42.649],[-73.785,42.387],[-73.77,42.296],[-73.799,42.252],[-73.834,42.23],[-73.91,42.118],[-73.956,41.908],[-73.926,41.86],[-73.948,41.773],[-73.936,41.75],[-73.947,41.582],[-73.985,41.519],[-73.984,41.455],[-73.943,41.393],[-73.98,41.318],[-73.931,41.291],[-73.939,41.274],[-73.921,41.246],[-73.929,41.232],[-73.89,41.202],[-73.872,41.168],[-73.865,41.079],[-73.92,40.887],[-74.001,40.754]]},"properties":{"name":"Empire Service"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.942,42.816],[-73.957,42.796],[-73.826,42.696],[-73.757,42.672],[-73.739,42.649]]},"properties":{"name":"Empire Service"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.942,42.816],[-73.958,42.837],[-74.042,42.882],[-74.181,42.928],[-74.225,42.956],[-74.273,42.955],[-74.303,42.938],[-74.378,42.954],[-74.469,42.9],[-74.509,42.896],[-74.601,42.917],[-74.657,42.993],[-74.746,43.005],[-74.846,43.042],[-74.895,43.022],[-75.018,43.018],[-75.108,43.071],[-75.28,43.12],[-75.383,43.189],[-75.457,43.2],[-75.547,43.168],[-75.607,43.12],[-75.742,43.078],[-75.929,43.083],[-76.069,43.063],[-76.138,43.09],[-76.191,43.061],[-76.261,43.08],[-76.406,43.086],[-76.601,43.05],[-76.836,43.066],[-76.889,43.087],[-76.95,43.075],[-76.972,43.056],[-77.046,43.063],[-77.071,43.054],[-77.152,43.087],[-77.196,43.065],[-77.269,43.09],[-77.371,43.078],[-77.503,43.119],[-77.6,43.167],[-78.086,43.043],[-78.169,42.985],[-78.225,42.989],[-78.727,42.907]]},"properties":{"name":"Empire Service"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.404,43.547],[-73.453,43.439],[-73.483,43.423],[-73.492,43.363],[-73.514,43.331],[-73.713,43.131],[-73.81,43.086],[-73.876,42.91],[-73.944,42.814]]},"properties":{"name":"Ethan Allen Express"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-72.982,43.606],[-73.035,43.593],[-73.1,43.613],[-73.175,43.614],[-73.272,43.586],[-73.316,43.587],[-73.405,43.544]]},"properties":{"name":"Ethan Allen Express"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-72.982,43.606],[-73.011,43.604],[-73.035,43.632],[-73.036,43.709],[-73.054,43.719],[-73.086,43.794],[-73.122,43.803],[-73.16,43.875],[-73.167,44.013],[-73.19,44.103],[-73.183,44.129],[-73.208,44.161],[-73.253,44.188],[-73.263,44.302],[-73.253,44.359],[-73.229,44.381],[-73.214,44.427],[-73.22,44.476]]},"properties":{"name":"Ethan Allen Express"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.739,42.649],[-73.785,42.387],[-73.77,42.296],[-73.799,42.252],[-73.834,42.23],[-73.91,42.118],[-73.956,41.908],[-73.926,41.86],[-73.948,41.773],[-73.936,41.75],[-73.947,41.582],[-73.985,41.519],[-73.984,41.455],[-73.943,41.393],[-73.98,41.318],[-73.931,41.291],[-73.939,41.274],[-73.921,41.246],[-73.929,41.232],[-73.89,41.202],[-73.872,41.168],[-73.865,41.079],[-73.92,40.887],[-74.001,40.754]]},"properties":{"name":"Ethan Allen Express"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.942,42.816],[-73.957,42.796],[-73.826,42.696],[-73.757,42.672],[-73.739,42.649]]},"properties":{"name":"Ethan Allen Express"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-97.512,35.465],[-97.513,35.442],[-97.485,35.395],[-97.482,35.286],[-97.39,35.126],[-97.356,35.025],[-97.351,34.955],[-97.29,34.904],[-97.28,34.87],[-97.255,34.851],[-97.246,34.764],[-97.204,34.729],[-97.177,34.667],[-97.16,34.582],[-97.101,34.445],[-97.033,34.376],[-97.031,34.352],[-97.048,34.313],[-97.037,34.28],[-97.059,34.27],[-97.072,34.234],[-97.137,34.155],[-97.142,34.13],[-97.135,34.051],[-97.105,34.006],[-97.123,33.864],[-97.165,33.727],[-97.167,33.68],[-97.144,33.64],[-97.139,33.575],[-97.165,33.517],[-97.155,33.457],[-97.171,33.385],[-97.167,33.327],[-97.223,33.284],[-97.285,33.19],[-97.295,33.067],[-97.361,32.941],[-97.357,32.828],[-97.325,32.752]]},"properties":{"name":"Heartland Flyer"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-87.917,43.034],[-87.909,43.001],[-87.927,42.97],[-87.927,42.835],[-87.906,42.724],[-87.905,42.561],[-87.932,42.468],[-87.9,42.291],[-87.848,42.162],[-87.72,41.91],[-87.687,41.888],[-87.641,41.888],[-87.639,41.879]]},"properties":{"name":"Hiawatha"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-87.637,41.861],[-87.621,41.86],[-87.586,41.8],[-87.62,41.65],[-87.845,41.229],[-87.868,41.11],[-87.988,40.796],[-88.03,40.617],[-88.242,40.113],[-88.322,39.597],[-88.549,39.114],[-88.579,39.003],[-88.595,38.977],[-88.973,38.669],[-89.129,38.561],[-89.176,38.397],[-89.242,38.07],[-89.216,37.724]]},"properties":{"name":"Illinois Service"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-87.639,41.879],[-87.638,41.86],[-87.961,41.796],[-88.074,41.798],[-88.132,41.78],[-88.292,41.768],[-88.331,41.747],[-88.379,41.698],[-89.111,41.556],[-89.262,41.466],[-89.458,41.388],[-89.508,41.392],[-89.727,41.303],[-89.78,41.302],[-89.94,41.242],[-89.999,41.187],[-90.161,41.114],[-90.238,41.066],[-90.367,40.941]]},"properties":{"name":"Illinois Service"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-90.364,40.944],[-90.393,40.888],[-90.397,40.769],[-90.413,40.711],[-90.54,40.513],[-90.585,40.48],[-90.75,40.441],[-90.862,40.4],[-90.917,40.282],[-90.945,40.251],[-91.061,40.042],[-91.17,40.031],[-91.28,40.002],[-91.368,39.957]]},"properties":{"name":"Illinois Service"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-87.639,41.879],[-87.642,41.849],[-87.81,41.795],[-87.892,41.721],[-88.048,41.654],[-88.052,41.604],[-88.144,41.314],[-88.481,41.05],[-88.63,40.891],[-88.781,40.647],[-88.993,40.504],[-89.038,40.428],[-89.393,40.127],[-89.607,39.889],[-89.611,39.856],[-89.65,39.813],[-89.654,39.767],[-89.742,39.596],[-89.78,39.438],[-89.904,39.257],[-89.959,39.211],[-89.979,39.164],[-90.02,39.147],[-90.155,39.028],[-90.181,38.977],[-90.185,38.95],[-90.107,38.868],[-90.084,38.788],[-90.087,38.767],[-90.159,38.7],[-90.161,38.678],[-90.195,38.672],[-90.182,38.63],[-90.187,38.619],[-90.205,38.624]]},"properties":{"name":"Illinois Service"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-75.181,39.958],[-75.348,40.042],[-75.41,40.05],[-75.569,40.033],[-75.664,40.005],[-75.814,39.988],[-75.989,39.949],[-76.02,39.987],[-76.107,40.002],[-76.188,40.042],[-76.318,40.056],[-76.397,40.096],[-76.521,40.112],[-76.608,40.146],[-76.705,40.163],[-76.734,40.195],[-76.808,40.211],[-76.877,40.262]]},"properties":{"name":"Keystone"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.994,40.751],[-74.049,40.773],[-74.163,40.736],[-74.274,40.608],[-74.355,40.542],[-74.451,40.493],[-74.532,40.388],[-74.774,40.203],[-74.796,40.159],[-74.848,40.109],[-74.923,40.09],[-75.057,40.014],[-75.165,39.994],[-75.198,39.973],[-75.182,39.956]]},"properties":{"name":"Keystone"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-81.696,41.506],[-81.738,41.487],[-81.885,41.375],[-82.097,41.37],[-82.343,41.419],[-82.494,41.371],[-82.688,41.438],[-82.761,41.444],[-82.834,41.498],[-83.114,41.521],[-83.434,41.569],[-83.548,41.638],[-84.894,41.426],[-85.548,41.463],[-85.638,41.492],[-85.865,41.601],[-85.972,41.681],[-86.107,41.661],[-86.245,41.663],[-86.29,41.679],[-86.501,41.705],[-86.754,41.604],[-86.9,41.599],[-87.077,41.614]]},"properties":{"name":"Lake Shore Limited"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.254,42.451],[-73.308,42.44],[-73.398,42.349],[-73.424,42.357],[-73.448,42.382],[-73.451,42.41],[-73.472,42.418],[-73.536,42.411],[-73.545,42.392],[-73.607,42.358],[-73.62,42.415],[-73.669,42.449],[-73.696,42.485],[-73.745,42.611],[-73.742,42.64]]},"properties":{"name":"Lake Shore Limited"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-87.639,41.879],[-87.634,41.783],[-87.417,41.634],[-87.322,41.605],[-87.16,41.62],[-87.074,41.614]]},"properties":{"name":"Lake Shore Limited"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-71.055,42.351],[-71.178,42.358],[-71.247,42.346],[-71.268,42.336],[-71.293,42.298],[-71.436,42.274],[-71.456,42.26],[-71.623,42.275],[-71.724,42.235],[-71.748,42.236],[-71.773,42.271],[-71.853,42.233],[-71.87,42.195],[-71.903,42.194],[-71.922,42.176],[-71.99,42.176],[-72.009,42.221],[-72.04,42.226],[-72.098,42.208],[-72.162,42.232],[-72.19,42.213],[-72.256,42.208],[-72.26,42.173],[-72.279,42.144],[-72.351,42.16],[-72.472,42.151],[-72.562,42.128],[-72.606,42.097],[-72.661,42.093],[-72.813,42.156],[-72.829,42.18],[-72.849,42.185],[-72.858,42.223],[-72.958,42.262],[-72.995,42.313],[-73.009,42.304],[-73.086,42.335],[-73.114,42.381],[-73.114,42.426],[-73.143,42.464],[-73.164,42.47],[-73.254,42.451]]},"properties":{"name":"Lake Shore Limited"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.739,42.649],[-73.785,42.387],[-73.77,42.296],[-73.799,42.252],[-73.834,42.23],[-73.91,42.118],[-73.956,41.908],[-73.926,41.86],[-73.948,41.773],[-73.936,41.75],[-73.947,41.582],[-73.985,41.519],[-73.984,41.455],[-73.943,41.393],[-73.98,41.318],[-73.931,41.291],[-73.939,41.274],[-73.921,41.246],[-73.929,41.232],[-73.89,41.202],[-73.872,41.168],[-73.865,41.079],[-73.92,40.887],[-74.001,40.754]]},"properties":{"name":"Lake Shore Limited"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.942,42.816],[-73.957,42.796],[-73.826,42.696],[-73.757,42.672],[-73.739,42.649]]},"properties":{"name":"Lake Shore Limited"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.942,42.816],[-73.958,42.837],[-74.042,42.882],[-74.181,42.928],[-74.225,42.956],[-74.273,42.955],[-74.303,42.938],[-74.378,42.954],[-74.469,42.9],[-74.509,42.896],[-74.601,42.917],[-74.657,42.993],[-74.746,43.005],[-74.846,43.042],[-74.895,43.022],[-75.018,43.018],[-75.108,43.071],[-75.28,43.12],[-75.383,43.189],[-75.457,43.2],[-75.547,43.168],[-75.607,43.12],[-75.742,43.078],[-75.929,43.083],[-76.069,43.063],[-76.138,43.09],[-76.191,43.061],[-76.261,43.08],[-76.406,43.086],[-76.601,43.05],[-76.836,43.066],[-76.889,43.087],[-76.95,43.075],[-76.972,43.056],[-77.046,43.063],[-77.071,43.054],[-77.152,43.087],[-77.196,43.065],[-77.269,43.09],[-77.371,43.078],[-77.503,43.119],[-77.6,43.167],[-78.086,43.043],[-78.169,42.985],[-78.225,42.989],[-78.727,42.907]]},"properties":{"name":"Lake Shore Limited"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-78.727,42.907],[-78.838,42.884],[-78.85,42.858],[-78.834,42.817],[-78.844,42.779],[-79.125,42.564],[-79.346,42.483],[-79.437,42.403],[-79.683,42.277],[-80.099,42.116],[-80.263,42.04],[-80.387,42.011],[-80.514,41.955],[-80.652,41.929],[-80.996,41.789],[-81.236,41.737],[-81.389,41.65],[-81.693,41.507]]},"properties":{"name":"Lake Shore Limited"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.739,42.649],[-73.785,42.387],[-73.77,42.296],[-73.799,42.252],[-73.834,42.23],[-73.91,42.118],[-73.956,41.908],[-73.926,41.86],[-73.948,41.773],[-73.936,41.75],[-73.947,41.582],[-73.985,41.519],[-73.984,41.455],[-73.943,41.393],[-73.98,41.318],[-73.931,41.291],[-73.939,41.274],[-73.921,41.246],[-73.929,41.232],[-73.89,41.202],[-73.872,41.168],[-73.865,41.079],[-73.92,40.887],[-74.001,40.754]]},"properties":{"name":"Maple Leaf"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.942,42.816],[-73.957,42.796],[-73.826,42.696],[-73.757,42.672],[-73.739,42.649]]},"properties":{"name":"Maple Leaf"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.942,42.816],[-73.958,42.837],[-74.042,42.882],[-74.181,42.928],[-74.225,42.956],[-74.273,42.955],[-74.303,42.938],[-74.378,42.954],[-74.469,42.9],[-74.509,42.896],[-74.601,42.917],[-74.657,42.993],[-74.746,43.005],[-74.846,43.042],[-74.895,43.022],[-75.018,43.018],[-75.108,43.071],[-75.28,43.12],[-75.383,43.189],[-75.457,43.2],[-75.547,43.168],[-75.607,43.12],[-75.742,43.078],[-75.929,43.083],[-76.069,43.063],[-76.138,43.09],[-76.191,43.061],[-76.261,43.08],[-76.406,43.086],[-76.601,43.05],[-76.836,43.066],[-76.889,43.087],[-76.95,43.075],[-76.972,43.056],[-77.046,43.063],[-77.071,43.054],[-77.152,43.087],[-77.196,43.065],[-77.269,43.09],[-77.371,43.078],[-77.503,43.119],[-77.6,43.167],[-78.086,43.043],[-78.169,42.985],[-78.225,42.989],[-78.727,42.907]]},"properties":{"name":"Maple Leaf"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-79.38,43.644],[-79.464,43.638],[-79.554,43.587],[-79.785,43.356],[-79.886,43.293],[-79.887,43.274],[-79.52,43.188],[-79.189,43.137],[-79.107,43.143],[-79.069,43.109],[-78.941,43.125],[-78.922,43.111],[-78.92,43.068],[-78.868,43.015],[-78.886,42.992],[-78.897,42.897],[-78.858,42.876],[-78.803,42.898],[-78.727,42.907]]},"properties":{"name":"Maple Leaf"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-87.639,41.879],[-87.634,41.783],[-87.417,41.634],[-87.322,41.605],[-87.16,41.62],[-87.074,41.614]]},"properties":{"name":"Michigan Services"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-87.074,41.614],[-87.046,41.618],[-86.909,41.698],[-86.864,41.704],[-86.715,41.801],[-86.653,41.859],[-86.598,41.882],[-86.513,42.027],[-86.483,42.114],[-86.458,42.118],[-86.388,42.181],[-86.173,42.203],[-86.157,42.263],[-86.077,42.349],[-86.102,42.595],[-86.086,42.618],[-86.107,42.65],[-86.077,42.669],[-86.098,42.789],[-86.088,42.802],[-86.005,42.816],[-85.882,42.859],[-85.679,42.957]]},"properties":{"name":"Michigan Services"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-87.076,41.614],[-86.924,41.694],[-86.907,41.721],[-86.813,41.752],[-86.748,41.796],[-86.45,41.797],[-86.364,41.82],[-86.332,41.807],[-86.292,41.814],[-86.249,41.838],[-85.989,42.099],[-85.777,42.214],[-85.687,42.279],[-85.619,42.274],[-85.584,42.296],[-85.423,42.289],[-85.352,42.333],[-85.296,42.351],[-85.17,42.312]]},"properties":{"name":"Michigan Services"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-85.17,42.312],[-85.115,42.367],[-85.082,42.38],[-85.037,42.435],[-84.96,42.465],[-84.845,42.547],[-84.842,42.565],[-84.796,42.6],[-84.596,42.722],[-84.445,42.725],[-84.234,42.822],[-83.641,43.02],[-83.295,43.05],[-82.444,42.96]]},"properties":{"name":"Michigan Services"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-85.17,42.312],[-85.143,42.316],[-85.126,42.298],[-85.038,42.262],[-84.977,42.269],[-84.874,42.255],[-84.827,42.274],[-84.66,42.224],[-84.586,42.265],[-84.312,42.233],[-84.282,42.247],[-84.215,42.251],[-84.044,42.313],[-83.883,42.34],[-83.766,42.315],[-83.74,42.287],[-83.654,42.272],[-83.603,42.242],[-83.098,42.327],[-83.058,42.379],[-83.181,42.53],[-83.292,42.633]]},"properties":{"name":"Michigan Services"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-87.639,41.879],[-87.642,41.849],[-87.81,41.795],[-87.892,41.721],[-88.048,41.654],[-88.052,41.604],[-88.144,41.314],[-88.481,41.05],[-88.63,40.891],[-88.781,40.647],[-88.993,40.504],[-89.038,40.428],[-89.393,40.127],[-89.607,39.889],[-89.611,39.856],[-89.65,39.813],[-89.654,39.767],[-89.742,39.596],[-89.78,39.438],[-89.904,39.257],[-89.959,39.211],[-89.979,39.164],[-90.02,39.147],[-90.155,39.028],[-90.181,38.977],[-90.185,38.95],[-90.107,38.868],[-90.084,38.788],[-90.087,38.767],[-90.159,38.7],[-90.161,38.678],[-90.195,38.672],[-90.182,38.63],[-90.187,38.619],[-90.205,38.624]]},"properties":{"name":"Missouri River Runner"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-90.204,38.623],[-90.292,38.622],[-90.324,38.604],[-90.344,38.608],[-90.39,38.575],[-90.426,38.58],[-90.501,38.547],[-90.561,38.547],[-90.609,38.528],[-90.62,38.503],[-90.683,38.504],[-90.709,38.48],[-90.79,38.478],[-90.834,38.496],[-90.85,38.521],[-90.975,38.543],[-91.068,38.601],[-91.138,38.596],[-91.215,38.614],[-91.351,38.689],[-91.411,38.708],[-91.474,38.701],[-91.552,38.669],[-91.792,38.674],[-91.874,38.644],[-92.006,38.555],[-92.074,38.547],[-92.265,38.624],[-92.321,38.611],[-92.446,38.631],[-92.459,38.615],[-92.491,38.608],[-92.515,38.612],[-92.527,38.628],[-92.571,38.63],[-92.673,38.663],[-92.747,38.653],[-92.882,38.67],[-92.967,38.706],[-93.043,38.683],[-93.121,38.685],[-93.265,38.718],[-93.32,38.751],[-93.412,38.775],[-93.572,38.767],[-93.631,38.777],[-93.874,38.745],[-93.972,38.712],[-94.096,38.756],[-94.267,38.779],[-94.343,38.855],[-94.399,38.946],[-94.414,38.979],[-94.415,39.067],[-94.426,39.086],[-94.48,39.114],[-94.586,39.086]]},"properties":{"name":"Missouri River Runner"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-71.055,42.352],[-71.09,42.336],[-71.118,42.295],[-71.155,42.194],[-71.155,42.155],[-71.19,42.117],[-71.209,42.048],[-71.304,41.913],[-71.383,41.892],[-71.417,41.842],[-71.413,41.829],[-71.437,41.822],[-71.442,41.806],[-71.422,41.783],[-71.454,41.696],[-71.446,41.663],[-71.53,41.504],[-71.618,41.449],[-71.657,41.451],[-71.708,41.433],[-71.721,41.413],[-71.769,41.388],[-71.824,41.384],[-71.845,41.361],[-71.899,41.336],[-71.966,41.351],[-71.997,41.323],[-72.092,41.363],[-72.103,41.335],[-72.133,41.337],[-72.165,41.316],[-72.191,41.324],[-72.277,41.291],[-72.355,41.311],[-72.496,41.275],[-72.569,41.29],[-72.732,41.265],[-72.873,41.283],[-72.886,41.323],[-72.932,41.293]]},"properties":{"name":"Northeast Regional"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-72.594,42.106],[-72.58,42.079],[-72.605,42.0],[-72.606,41.946],[-72.627,41.935],[-72.628,41.881],[-72.649,41.814],[-72.745,41.705],[-72.771,41.586],[-72.816,41.507],[-72.811,41.483],[-72.904,41.315]]},"properties":{"name":"Northeast Regional"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-77.496,37.617],[-77.473,37.577],[-77.496,37.525],[-77.451,37.463],[-77.454,37.375],[-77.402,37.31],[-77.408,37.27],[-77.435,37.232],[-77.42,37.187],[-77.359,37.206],[-77.34,37.196],[-76.614,36.722],[-76.275,36.781],[-76.266,36.799],[-76.276,36.843]]},"properties":{"name":"Northeast Regional"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-77.006,38.898],[-77.007,38.885],[-77.032,38.883],[-77.048,38.863],[-77.047,38.824],[-77.058,38.809],[-77.106,38.807]]},"properties":{"name":"Northeast Regional"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-79.941,37.273],[-79.896,37.283],[-79.861,37.333],[-79.816,37.356],[-79.78,37.394],[-79.744,37.395],[-79.647,37.353],[-79.511,37.334],[-79.403,37.359],[-79.26,37.359],[-79.168,37.373],[-79.157,37.406]]},"properties":{"name":"Northeast Regional"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-77.106,38.807],[-77.166,38.793],[-77.326,38.801],[-77.371,38.782],[-77.431,38.78],[-77.459,38.753],[-77.503,38.743],[-77.616,38.677],[-77.809,38.53],[-77.993,38.473],[-77.995,38.435],[-78.056,38.329],[-78.105,38.27],[-78.11,38.245]]},"properties":{"name":"Northeast Regional"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-78.493,38.031],[-78.574,38.006],[-78.661,37.913],[-78.711,37.884],[-78.754,37.8],[-78.797,37.752],[-78.932,37.673],[-78.996,37.604],[-79.048,37.579],[-79.084,37.533],[-79.115,37.518],[-79.148,37.484],[-79.166,37.442],[-79.158,37.407]]},"properties":{"name":"Northeast Regional"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-77.472,37.578],[-77.431,37.555],[-77.429,37.532],[-77.394,37.505],[-77.228,37.469],[-77.183,37.48],[-76.851,37.386],[-76.82,37.395],[-76.773,37.376],[-76.713,37.278],[-76.582,37.205],[-76.517,37.123]]},"properties":{"name":"Northeast Regional"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-72.926,41.298],[-73.035,41.225],[-73.119,41.203],[-73.145,41.185],[-73.184,41.185],[-73.188,41.17],[-73.229,41.165],[-73.259,41.142],[-73.321,41.12],[-73.376,41.118],[-73.436,41.079],[-73.515,41.07],[-73.557,41.036],[-73.632,41.021],[-73.776,40.922],[-73.819,40.862],[-73.879,40.836],[-73.926,40.788],[-73.906,40.753],[-73.958,40.741],[-73.994,40.751]]},"properties":{"name":"Northeast Regional"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-77.106,38.807],[-77.153,38.793],[-77.223,38.698]]},"properties":{"name":"Northeast Regional"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-77.22,38.708],[-77.25,38.656],[-77.262,38.565],[-77.309,38.505],[-77.328,38.449],[-77.411,38.352],[-77.449,38.34],[-77.448,38.302],[-77.461,38.295],[-77.452,38.261],[-77.434,38.245],[-77.438,38.199],[-77.457,38.163],[-77.38,38.073],[-77.363,37.965],[-77.439,37.955],[-77.469,37.915],[-77.457,37.834],[-77.501,37.694],[-77.508,37.645],[-77.496,37.617]]},"properties":{"name":"Northeast Regional"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-75.182,39.956],[-75.322,39.881],[-75.343,39.856],[-75.435,39.815],[-75.494,39.755],[-75.767,39.666],[-75.843,39.607],[-75.928,39.611],[-75.974,39.58],[-76.101,39.549],[-76.158,39.515],[-76.217,39.45],[-76.338,39.397],[-76.384,39.35],[-76.477,39.328],[-76.531,39.299],[-76.621,39.311],[-76.649,39.302],[-76.69,39.257],[-76.696,39.121],[-76.707,39.085],[-76.741,39.044],[-76.908,38.92],[-76.98,38.918],[-77.006,38.898]]},"properties":{"name":"Northeast Regional"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.994,40.751],[-74.049,40.773],[-74.163,40.736],[-74.274,40.608],[-74.355,40.542],[-74.451,40.493],[-74.532,40.388],[-74.774,40.203],[-74.796,40.159],[-74.848,40.109],[-74.923,40.09],[-75.057,40.014],[-75.165,39.994],[-75.198,39.973],[-75.182,39.956]]},"properties":{"name":"Northeast Regional"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-78.11,38.245],[-78.204,38.218],[-78.286,38.17],[-78.336,38.173],[-78.429,38.106],[-78.493,38.031]]},"properties":{"name":"Northeast Regional"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-120.655,35.276],[-120.619,35.236],[-120.61,35.193],[-120.633,35.162],[-120.632,35.13],[-120.592,35.082],[-120.594,35.035],[-120.566,34.982],[-120.574,34.944],[-120.564,34.919],[-120.514,34.876],[-120.521,34.838],[-120.549,34.828],[-120.589,34.838],[-120.61,34.794],[-120.615,34.745],[-120.596,34.698],[-120.643,34.584],[-120.631,34.563],[-120.585,34.561],[-120.514,34.527],[-120.454,34.452],[-120.297,34.472],[-120.135,34.474],[-120.09,34.461],[-120.004,34.461],[-119.911,34.432],[-119.779,34.442],[-119.7,34.411],[-119.66,34.423],[-119.56,34.416],[-119.459,34.374],[-119.311,34.277],[-119.195,34.244],[-119.165,34.197],[-119.049,34.196],[-118.979,34.264],[-118.839,34.293],[-118.665,34.261],[-118.602,34.272],[-118.592,34.24],[-118.321,34.186],[-118.281,34.16],[-118.228,34.091],[-118.234,34.058]]},"properties":{"name":"Pacific Surfliner"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-117.924,33.868],[-117.911,33.864],[-117.896,33.811],[-117.857,33.796],[-117.853,33.742],[-117.677,33.607],[-117.664,33.501],[-117.681,33.464],[-117.593,33.386],[-117.49,33.327],[-117.337,33.144],[-117.27,32.988],[-117.26,32.937],[-117.213,32.89],[-117.174,32.878],[-117.234,32.841],[-117.199,32.753],[-117.17,32.716]]},"properties":{"name":"Pacific Surfliner"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-117.924,33.868],[-117.995,33.878],[-118.06,33.916],[-118.074,33.961],[-118.225,34.016],[-118.23,34.062]]},"properties":{"name":"Pacific Surfliner"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-75.181,39.958],[-75.348,40.042],[-75.41,40.05],[-75.569,40.033],[-75.664,40.005],[-75.814,39.988],[-75.989,39.949],[-76.02,39.987],[-76.107,40.002],[-76.188,40.042],[-76.318,40.056],[-76.397,40.096],[-76.521,40.112],[-76.608,40.146],[-76.705,40.163],[-76.734,40.195],[-76.808,40.211],[-76.877,40.262]]},"properties":{"name":"Pennsylvanian"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-76.877,40.262],[-76.893,40.326],[-76.923,40.335],[-76.935,40.356],[-77.017,40.361],[-77.029,40.384],[-77.015,40.406],[-77.018,40.427],[-77.036,40.47],[-77.095,40.488],[-77.11,40.469],[-77.124,40.471],[-77.138,40.52],[-77.173,40.563],[-77.209,40.562],[-77.328,40.522],[-77.379,40.531],[-77.404,40.558],[-77.41,40.589],[-77.444,40.608],[-77.538,40.577],[-77.58,40.588],[-77.596,40.562],[-77.647,40.538],[-77.658,40.513],[-77.72,40.506],[-77.738,40.455],[-77.866,40.381],[-77.941,40.393],[-77.932,40.435],[-78.018,40.487],[-78.041,40.563],[-78.16,40.612],[-78.234,40.668],[-78.319,40.627],[-78.379,40.536],[-78.432,40.491],[-78.486,40.499],[-78.474,40.491],[-78.48,40.475],[-78.578,40.478],[-78.65,40.401],[-78.793,40.366],[-78.849,40.378],[-78.861,40.353],[-78.911,40.329],[-78.941,40.346],[-78.942,40.363],[-78.964,40.373],[-78.992,40.412],[-79.019,40.417],[-79.056,40.381],[-79.12,40.373],[-79.123,40.394],[-79.151,40.397],[-79.155,40.41],[-79.22,40.42],[-79.311,40.327],[-79.368,40.326],[-79.419,40.301],[-79.491,40.317],[-79.55,40.304],[-79.622,40.331],[-79.709,40.335],[-79.779,40.4],[-79.86,40.399],[-79.886,40.418],[-79.888,40.446],[-79.918,40.46],[-79.958,40.461],[-79.992,40.445]]},"properties":{"name":"Pennsylvanian"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.994,40.751],[-74.049,40.773],[-74.163,40.736],[-74.274,40.608],[-74.355,40.542],[-74.451,40.493],[-74.532,40.388],[-74.774,40.203],[-74.796,40.159],[-74.848,40.109],[-74.923,40.09],[-75.057,40.014],[-75.165,39.994],[-75.198,39.973],[-75.182,39.956]]},"properties":{"name":"Pennsylvanian"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-121.286,37.945],[-121.246,37.949],[-120.962,37.771],[-120.948,37.759],[-120.899,37.621],[-120.672,37.422],[-120.505,37.318],[-120.444,37.295],[-120.329,37.295],[-120.288,37.263],[-120.183,37.178],[-120.078,37.025],[-119.912,36.854],[-119.799,36.776],[-119.772,36.714],[-119.751,36.696],[-119.739,36.555],[-119.685,36.431],[-119.664,36.409],[-119.645,36.221],[-119.376,35.85],[-119.331,35.719],[-119.325,35.548],[-119.122,35.378],[-119.008,35.372]]},"properties":{"name":"Gold Runner"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-122.131,38.022],[-122.014,38.049],[-121.742,38.008],[-121.7,37.994],[-121.623,37.94],[-121.353,37.934],[-121.286,37.945]]},"properties":{"name":"Gold Runner"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-121.501,38.585],[-121.436,38.578],[-121.413,38.54],[-121.272,38.158],[-121.283,37.975],[-121.274,37.947]]},"properties":{"name":"Gold Runner"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-122.137,38.02],[-122.169,38.029],[-122.189,38.051],[-122.24,38.058],[-122.297,38.011],[-122.355,37.999],[-122.359,37.945],[-122.31,37.895],[-122.292,37.841],[-122.305,37.806],[-122.272,37.793]]},"properties":{"name":"Gold Runner"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-77.006,38.898],[-77.007,38.885],[-77.032,38.883],[-77.048,38.863],[-77.047,38.824],[-77.058,38.809],[-77.106,38.807]]},"properties":{"name":"Silver Service / Palmetto"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-81.796,28.059],[-81.77,28.022],[-81.736,28.006],[-81.65,27.893],[-81.638,27.811],[-81.507,27.621],[-81.498,27.574],[-81.437,27.533],[-81.432,27.491],[-81.319,27.477],[-80.87,27.252],[-80.817,27.251],[-80.066,26.75],[-80.071,26.551],[-80.091,26.469],[-80.091,26.402],[-80.12,26.373],[-80.12,26.324],[-80.168,26.151],[-80.167,25.964],[-80.259,25.897],[-80.258,25.85]]},"properties":{"name":"Silver Service / Palmetto"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-81.149,32.083],[-81.126,32.05],[-81.152,32.026],[-81.726,31.721],[-81.887,31.601],[-82.028,31.007],[-82.001,30.785],[-81.943,30.723],[-81.843,30.58],[-81.728,30.368],[-81.681,30.331],[-81.721,30.301],[-81.719,30.275],[-81.697,30.244],[-81.714,30.162],[-81.755,30.136],[-81.771,30.108],[-81.76,30.068],[-81.685,29.996],[-81.619,29.843],[-81.662,29.702],[-81.64,29.656],[-81.641,29.63],[-81.685,29.602],[-81.64,29.528],[-81.57,29.481],[-81.51,29.373],[-81.459,29.217],[-81.36,29.137],[-81.364,29.051],[-81.332,28.979],[-81.338,28.875],[-81.324,28.859],[-81.325,28.83],[-81.292,28.809],[-81.292,28.785],[-81.314,28.761]]},"properties":{"name":"Silver Service / Palmetto"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-78.279,35.533],[-78.335,35.51],[-78.39,35.461],[-78.518,35.41],[-78.702,35.189],[-78.88,35.065],[-78.922,35.019],[-78.948,34.963],[-78.984,34.938],[-79.199,34.674],[-79.363,34.429],[-79.53,34.209],[-79.762,34.199],[-79.744,33.903],[-80.018,33.164],[-80.043,32.961],[-79.996,32.867],[-80.045,32.852],[-80.107,32.793],[-80.188,32.771],[-80.323,32.755],[-80.473,32.762],[-80.571,32.737],[-80.835,32.702],[-80.932,32.586],[-81.077,32.293],[-81.187,32.198],[-81.185,32.164],[-81.149,32.083]]},"properties":{"name":"Silver Service / Palmetto"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-81.796,28.059],[-81.873,28.045],[-81.938,28.049],[-82.45,27.953]]},"properties":{"name":"Silver Service / Palmetto"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-81.314,28.761],[-81.336,28.727],[-81.368,28.621],[-81.352,28.596],[-81.381,28.554],[-81.367,28.441],[-81.374,28.366],[-81.402,28.297],[-81.463,28.26],[-81.529,28.267],[-81.552,28.257],[-81.63,28.106],[-81.661,28.115],[-81.766,28.087],[-81.796,28.059],[-81.77,28.022],[-81.738,28.007]]},"properties":{"name":"Silver Service / Palmetto"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-78.779,35.789],[-78.832,35.758],[-78.886,35.703],[-79.042,35.613],[-79.087,35.612],[-79.123,35.549],[-79.171,35.511],[-79.177,35.442],[-79.196,35.39],[-79.261,35.312],[-79.284,35.253],[-79.4,35.168],[-79.481,35.055],[-79.561,35.024],[-79.569,34.995],[-79.6,34.977],[-79.616,34.943],[-79.683,34.909],[-79.694,34.886],[-79.752,34.844],[-79.768,34.795],[-79.817,34.766],[-79.837,34.726],[-79.882,34.693],[-79.965,34.66],[-80.019,34.588],[-80.061,34.564],[-80.109,34.561],[-80.173,34.519],[-80.244,34.492],[-80.278,34.444],[-80.408,34.388],[-80.445,34.359],[-80.527,34.335],[-80.614,34.266],[-80.616,34.253],[-80.684,34.212],[-80.72,34.21],[-80.748,34.182],[-80.814,34.162],[-80.969,34.053],[-81.014,34.05],[-81.058,34.016],[-81.037,33.989],[-81.057,33.969],[-81.057,33.925],[-81.082,33.888],[-81.077,33.871],[-81.093,33.866],[-81.104,33.815],[-81.1,33.711],[-81.116,33.682],[-81.1,33.641],[-81.107,33.584],[-81.127,33.542],[-81.118,33.504],[-81.147,33.296],[-81.175,33.228],[-81.236,32.974],[-81.245,32.609],[-81.254,32.542],[-81.271,32.509],[-81.223,32.206],[-81.153,32.09]]},"properties":{"name":"Silver Service / Palmetto"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-78.279,35.533],[-78.477,35.668],[-78.619,35.712],[-78.648,35.777]]},"properties":{"name":"Silver Service / Palmetto"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-77.106,38.807],[-77.153,38.793],[-77.223,38.698]]},"properties":{"name":"Silver Service / Palmetto"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-77.22,38.708],[-77.25,38.656],[-77.262,38.565],[-77.309,38.505],[-77.328,38.449],[-77.411,38.352],[-77.449,38.34],[-77.448,38.302],[-77.461,38.295],[-77.452,38.261],[-77.434,38.245],[-77.438,38.199],[-77.457,38.163],[-77.38,38.073],[-77.363,37.965],[-77.439,37.955],[-77.469,37.915],[-77.457,37.834],[-77.501,37.694],[-77.508,37.645],[-77.496,37.617]]},"properties":{"name":"Silver Service / Palmetto"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-77.496,37.617],[-77.473,37.579],[-77.496,37.525],[-77.451,37.463],[-77.454,37.375],[-77.402,37.31],[-77.408,37.27],[-77.435,37.232],[-77.42,37.201],[-77.393,37.019],[-77.404,36.929],[-77.466,36.816],[-77.476,36.772],[-77.545,36.682],[-77.557,36.645],[-77.53,36.545],[-77.554,36.465],[-77.606,36.416],[-77.617,36.366],[-77.595,36.321],[-77.657,36.19],[-77.703,36.147],[-77.707,36.115],[-77.772,36.005],[-77.825,35.873],[-77.932,35.678],[-78.279,35.533]]},"properties":{"name":"Silver Service / Palmetto"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-75.182,39.956],[-75.322,39.881],[-75.343,39.856],[-75.435,39.815],[-75.494,39.755],[-75.767,39.666],[-75.843,39.607],[-75.928,39.611],[-75.974,39.58],[-76.101,39.549],[-76.158,39.515],[-76.217,39.45],[-76.338,39.397],[-76.384,39.35],[-76.477,39.328],[-76.531,39.299],[-76.621,39.311],[-76.649,39.302],[-76.69,39.257],[-76.696,39.121],[-76.707,39.085],[-76.741,39.044],[-76.908,38.92],[-76.98,38.918],[-77.006,38.898]]},"properties":{"name":"Silver Service / Palmetto"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.994,40.751],[-74.049,40.773],[-74.163,40.736],[-74.274,40.608],[-74.355,40.542],[-74.451,40.493],[-74.532,40.388],[-74.774,40.203],[-74.796,40.159],[-74.848,40.109],[-74.923,40.09],[-75.057,40.014],[-75.165,39.994],[-75.198,39.973],[-75.182,39.956]]},"properties":{"name":"Silver Service / Palmetto"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-78.648,35.777],[-78.694,35.795],[-78.779,35.789]]},"properties":{"name":"Silver Service / Palmetto"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-90.362,40.946],[-90.464,40.89],[-90.525,40.889],[-91.012,40.712],[-91.168,40.637],[-91.369,40.616],[-91.555,40.538],[-91.598,40.509],[-91.642,40.521],[-91.655,40.503],[-91.825,40.408],[-92.022,40.363],[-92.09,40.31],[-92.283,40.2],[-92.293,40.177],[-92.397,40.113],[-92.484,40.032],[-92.608,39.996],[-92.711,39.9],[-92.797,39.866],[-92.827,39.828],[-92.85,39.828],[-92.96,39.7],[-93.031,39.664],[-93.243,39.515],[-93.315,39.483],[-93.454,39.369],[-93.531,39.335],[-93.824,39.275],[-94.029,39.192],[-94.174,39.189],[-94.185,39.176],[-94.219,39.205],[-94.258,39.203],[-94.547,39.091],[-94.635,39.072],[-94.709,39.087],[-94.804,39.04],[-94.859,39.05],[-94.923,38.98],[-95.029,38.976],[-95.106,38.948],[-95.183,38.95],[-95.239,38.975],[-95.259,39.007],[-95.416,39.059],[-95.661,39.055],[-95.689,38.985],[-95.688,38.934],[-95.7,38.909],[-95.688,38.877],[-95.7,38.855],[-95.695,38.819],[-95.737,38.782],[-95.827,38.753],[-95.818,38.644],[-95.854,38.588],[-95.875,38.582],[-95.914,38.531],[-95.984,38.517],[-96.167,38.401],[-96.381,38.406],[-96.5,38.384],[-96.561,38.4],[-96.618,38.398],[-96.696,38.328],[-96.781,38.277],[-96.87,38.245],[-96.913,38.245],[-97.134,38.145],[-97.214,38.153],[-97.298,38.074],[-97.378,38.024],[-97.528,38.006],[-97.937,38.057],[-98.084,37.97],[-98.344,37.974],[-98.491,37.958],[-98.765,37.995],[-98.83,37.975],[-99.374,37.935],[-99.433,37.913],[-99.75,37.855],[-99.906,37.775],[-99.951,37.765],[-99.964,37.748],[-100.077,37.755],[-100.202,37.79],[-100.418,37.814],[-100.528,37.859],[-100.679,37.883],[-100.829,37.956],[-101.042,37.996],[-101.213,37.96],[-101.344,37.891],[-101.386,37.882],[-101.568,37.941],[-101.927,38.025],[-102.073,38.047],[-102.247,38.05],[-102.454,38.092],[-102.75,38.084],[-102.954,38.048],[-103.096,38.078],[-103.125,38.063],[-103.351,38.057],[-103.508,37.989],[-103.558,37.988],[-103.821,37.786],[-103.883,37.723],[-104.001,37.673],[-104.05,37.575],[-104.101,37.553],[-104.196,37.483],[-104.284,37.314],[-104.359,37.298],[-104.542,37.152],[-104.523,37.125],[-104.524,37.051],[-104.484,37.016],[-104.49,36.979],[-104.439,36.896],[-104.459,36.853],[-104.462,36.749],[-104.499,36.639],[-104.534,36.587],[-104.545,36.495],[-104.564,36.465],[-104.561,36.416],[-104.6,36.376],[-104.593,36.361],[-104.625,36.308],[-104.635,36.243],[-104.682,36.136],[-104.696,36.023],[-104.863,35.817],[-104.918,35.815],[-104.956,35.789],[-104.979,35.795],[-105.09,35.699],[-105.176,35.67],[-105.209,35.607],[-105.213,35.558],[-105.25,35.512],[-105.218,35.432],[-105.226,35.403],[-105.287,35.386],[-105.398,35.398],[-105.458,35.375],[-105.517,35.414],[-105.602,35.43],[-105.674,35.483],[-105.701,35.54],[-105.753,35.559],[-105.771,35.584],[-105.818,35.555],[-105.918,35.449],[-106.01,35.396],[-106.131,35.44],[-106.255,35.509],[-106.352,35.515],[-106.436,35.443],[-106.464,35.391],[-106.501,35.367],[-106.572,35.266],[-106.64,35.117],[-106.675,34.938],[-106.709,34.898],[-106.844,34.822],[-106.856,34.775],[-106.886,34.763],[-106.923,34.785],[-106.968,34.779],[-107.03,34.81],[-107.059,34.837],[-107.091,34.905],[-107.322,35.025],[-107.341,35.047],[-107.337,35.065],[-107.432,35.04],[-107.747,35.073],[-107.758,35.102],[-107.885,35.164],[-107.916,35.222],[-107.981,35.276],[-107.987,35.308],[-108.022,35.358],[-108.121,35.387],[-108.275,35.411],[-108.352,35.445],[-108.405,35.498],[-108.487,35.499],[-108.607,35.533],[-108.733,35.532],[-108.896,35.494],[-108.988,35.428],[-109.101,35.306],[-109.168,35.277],[-109.239,35.271],[-109.332,35.215],[-109.45,35.182],[-109.496,35.145],[-109.635,35.08],[-109.79,34.986],[-110.004,34.953],[-110.129,34.895],[-110.25,34.904],[-110.298,34.934],[-110.379,34.959],[-110.537,34.971],[-110.912,35.089],[-110.973,35.098],[-111.181,35.188],[-111.382,35.194],[-111.445,35.217],[-111.486,35.205],[-111.573,35.221],[-111.636,35.193],[-111.738,35.195],[-111.834,35.236],[-111.922,35.255],[-112.016,35.23],[-112.04,35.246],[-112.112,35.238],[-112.155,35.25],[-112.24,35.309],[-112.252,35.33],[-112.292,35.349],[-112.322,35.348],[-112.365,35.312],[-112.431,35.312],[-112.452,35.344],[-112.454,35.388],[-112.475,35.397],[-112.622,35.362],[-112.654,35.339],[-112.673,35.304],[-112.763,35.292],[-112.963,35.341],[-112.994,35.385],[-113.16,35.467],[-113.18,35.492],[-113.265,35.515],[-113.353,35.512],[-113.369,35.526],[-113.408,35.53],[-113.485,35.503],[-113.514,35.475],[-113.598,35.458],[-113.632,35.438],[-113.665,35.375],[-113.688,35.36],[-113.719,35.366],[-113.798,35.417],[-113.839,35.396],[-114.034,35.184],[-114.062,35.186],[-114.059,35.166],[-114.08,35.157],[-114.107,35.106],[-114.159,34.813],[-114.242,34.749],[-114.305,34.737],[-114.441,34.744],[-114.48,34.72],[-114.518,34.722],[-114.603,34.84],[-114.673,34.867],[-114.703,34.892],[-114.757,34.89],[-114.8,34.944],[-114.929,34.919],[-115.056,34.922],[-115.19,34.807],[-115.227,34.745],[-115.312,34.682],[-115.384,34.581],[-115.473,34.517],[-115.648,34.531],[-115.958,34.604],[-116.008,34.638],[-116.014,34.682],[-116.081,34.726],[-116.302,34.719],[-116.401,34.798],[-116.925,34.866],[-117.015,34.903],[-117.079,34.894],[-117.171,34.845],[-117.204,34.808],[-117.317,34.755],[-117.347,34.668],[-117.345,34.628],[-117.33,34.581],[-117.285,34.531],[-117.278,34.506],[-117.279,34.469],[-117.308,34.393],[-117.322,34.373],[-117.389,34.35],[-117.414,34.328],[-117.444,34.336],[-117.486,34.316],[-117.456,34.292],[-117.453,34.273],[-117.468,34.254],[-117.373,34.197],[-117.303,34.128],[-117.304,34.106],[-117.327,34.103],[-117.334,34.015],[-117.386,33.946],[-117.452,33.906],[-117.533,33.879],[-117.587,33.89],[-117.675,33.874],[-117.74,33.884],[-117.792,33.862],[-117.915,33.868]]},"properties":{"name":"Southwest Chief"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-87.639,41.879],[-87.638,41.86],[-87.961,41.796],[-88.074,41.798],[-88.132,41.78],[-88.292,41.768],[-88.331,41.747],[-88.379,41.698],[-89.111,41.556],[-89.262,41.466],[-89.458,41.388],[-89.508,41.392],[-89.727,41.303],[-89.78,41.302],[-89.94,41.242],[-89.999,41.187],[-90.161,41.114],[-90.238,41.066],[-90.367,40.941]]},"properties":{"name":"Southwest Chief"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-117.922,33.868],[-117.994,33.877],[-118.057,33.913],[-118.061,33.946],[-118.076,33.962],[-118.225,34.016],[-118.228,34.062],[-118.234,34.056]]},"properties":{"name":"Southwest Chief"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-98.478,29.419],[-98.481,29.403],[-98.532,29.407],[-98.598,29.331],[-98.649,29.32],[-98.739,29.329],[-98.781,29.321],[-98.893,29.266],[-98.945,29.288],[-99.017,29.362],[-99.033,29.351],[-99.305,29.325],[-99.35,29.354],[-99.49,29.313],[-99.569,29.318],[-99.7,29.278],[-99.789,29.233],[-99.899,29.247],[-99.98,29.238],[-100.021,29.259],[-100.123,29.233],[-100.202,29.236],[-100.281,29.21],[-100.381,29.152],[-100.593,29.284],[-100.628,29.354],[-100.685,29.385],[-100.724,29.392],[-100.81,29.363],[-100.878,29.371],[-100.923,29.356],[-101.002,29.372],[-101.02,29.394],[-101.018,29.452],[-101.039,29.493],[-101.107,29.536],[-101.162,29.679],[-101.217,29.696],[-101.263,29.754],[-101.291,29.752],[-101.31,29.768],[-101.357,29.758],[-101.507,29.818],[-101.577,29.815],[-101.648,29.89],[-101.769,29.972],[-101.858,29.955],[-101.873,29.98],[-101.983,29.998],[-102.033,30.055],[-102.094,30.045],[-102.22,30.054],[-102.328,30.105],[-102.35,30.085],[-102.391,30.118],[-102.39,30.136],[-102.445,30.147],[-102.479,30.168],[-102.566,30.134],[-102.654,30.163],[-102.767,30.095],[-102.857,30.073],[-102.885,30.115],[-102.976,30.113],[-103.051,30.158],[-103.084,30.202],[-103.344,30.218],[-103.451,30.284],[-103.472,30.333],[-103.5,30.359],[-103.582,30.389],[-103.622,30.366],[-103.752,30.339],[-103.804,30.307],[-103.801,30.282],[-103.832,30.271],[-103.981,30.299],[-103.997,30.312],[-104.062,30.316],[-104.223,30.375],[-104.721,30.765],[-104.806,30.927],[-104.872,30.953],[-104.923,30.938],[-105.038,31.024],[-105.348,31.173],[-105.438,31.196],[-105.52,31.277],[-105.594,31.255],[-105.695,31.261],[-105.763,31.23],[-105.79,31.241],[-105.805,31.271],[-105.89,31.331],[-105.932,31.379],[-106.057,31.423],[-106.144,31.49],[-106.316,31.691],[-106.406,31.769],[-106.461,31.776],[-106.499,31.758],[-106.528,31.788],[-106.549,31.794],[-106.566,31.784],[-106.591,31.795],[-106.697,31.861],[-106.914,32.057],[-107.112,32.142],[-107.147,32.176],[-107.577,32.29],[-108.152,32.223],[-108.384,32.181],[-108.633,32.329],[-108.74,32.356],[-108.775,32.35],[-108.963,32.234],[-109.024,32.224],[-109.589,32.352],[-109.738,32.345],[-109.795,32.305],[-109.98,32.037],[-110.026,32.035],[-110.127,31.979],[-110.165,31.983],[-110.161,31.965],[-110.182,31.952],[-110.206,31.952],[-110.219,31.976],[-110.286,31.965],[-110.328,31.988],[-110.382,31.997],[-110.487,31.983],[-110.619,32.03],[-110.65,32.016],[-110.688,32.027],[-110.908,32.158],[-110.912,32.188],[-110.979,32.231],[-111.121,32.395],[-111.251,32.488],[-111.384,32.644],[-112.087,33.075],[-112.217,33.069],[-112.349,33.035],[-112.433,32.995],[-112.49,33.002],[-112.538,32.977],[-112.97,32.909],[-113.142,32.865],[-113.48,32.819],[-113.724,32.727],[-114.223,32.659],[-114.294,32.677],[-114.35,32.722],[-114.364,32.757],[-114.407,32.759],[-114.44,32.698],[-114.465,32.684],[-114.541,32.675],[-114.607,32.691],[-114.615,32.73],[-114.635,32.745],[-114.685,32.756],[-114.726,32.745],[-114.757,32.752],[-115.048,32.982],[-115.298,33.146],[-115.558,33.263],[-115.636,33.357],[-115.7,33.378],[-115.738,33.357],[-115.76,33.361],[-115.946,33.531],[-116.025,33.539],[-116.065,33.557],[-116.208,33.717],[-116.32,33.765],[-116.551,33.9],[-116.644,33.904],[-116.696,33.924],[-116.793,33.916],[-116.83,33.931],[-116.977,33.925],[-117.007,33.943],[-117.049,33.945],[-117.087,33.974],[-117.142,33.985],[-117.236,34.05],[-117.344,34.068],[-117.797,34.058],[-117.833,34.024],[-117.881,34.002],[-117.945,34.013],[-118.043,34.081],[-118.096,34.097],[-118.184,34.062],[-118.234,34.056]]},"properties":{"name":"Sunset Limited"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-90.078,29.946],[-90.111,29.97],[-90.172,29.971],[-90.183,29.965],[-90.163,29.939],[-90.174,29.917],[-90.275,29.94],[-90.423,29.889],[-90.453,29.867],[-90.459,29.835],[-90.579,29.747],[-90.76,29.768],[-91.031,29.667],[-91.116,29.669],[-91.2,29.694],[-91.286,29.677],[-91.388,29.705],[-91.495,29.778],[-91.594,29.876],[-91.818,30.002],[-91.968,30.156],[-92.022,30.235],[-92.306,30.236],[-92.435,30.182],[-92.55,30.179],[-92.623,30.194],[-92.681,30.237],[-93.496,30.237],[-93.654,30.151],[-93.749,30.148],[-93.741,30.097],[-93.75,30.09],[-94.034,30.103],[-94.1,30.075],[-94.534,30.021],[-94.762,30.057],[-94.877,30.061],[-95.093,29.881],[-95.353,29.766],[-95.437,29.784],[-95.449,29.764],[-95.45,29.653],[-95.546,29.616],[-95.586,29.629],[-95.738,29.596],[-95.793,29.563],[-96.108,29.529],[-96.322,29.581],[-96.403,29.655],[-96.46,29.677],[-96.488,29.705],[-96.641,29.694],[-96.785,29.702],[-96.866,29.671],[-96.951,29.686],[-97.255,29.679],[-97.337,29.704],[-97.462,29.668],[-97.529,29.662],[-97.672,29.681],[-97.734,29.657],[-97.844,29.645],[-97.967,29.583],[-98.032,29.594],[-98.253,29.56],[-98.44,29.436],[-98.476,29.436],[-98.478,29.419]]},"properties":{"name":"Sunset Limited"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-97.321,32.746],[-97.351,32.683],[-97.364,32.553],[-97.389,32.489],[-97.378,32.188],[-97.419,32.139],[-97.482,32.106],[-97.559,32.029],[-97.632,31.997],[-97.638,31.978],[-97.636,31.918],[-97.54,31.707],[-97.509,31.67],[-97.462,31.646],[-97.439,31.519],[-97.358,31.325],[-97.365,31.24],[-97.337,31.148],[-97.349,31.101],[-97.338,31.085],[-97.367,30.99],[-97.35,30.957],[-97.367,30.913],[-97.397,30.891],[-97.41,30.863],[-97.446,30.692],[-97.404,30.568],[-97.609,30.533],[-97.678,30.507],[-97.699,30.486],[-97.73,30.429],[-97.73,30.394],[-97.755,30.336],[-97.767,30.276],[-97.753,30.267],[-97.82,30.174],[-97.878,29.959],[-97.909,29.907],[-98.113,29.741],[-98.149,29.686],[-98.319,29.6],[-98.38,29.556],[-98.408,29.468],[-98.443,29.435]]},"properties":{"name":"Texas Eagle"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-87.639,41.879],[-87.642,41.849],[-87.81,41.795],[-87.892,41.721],[-88.048,41.654],[-88.052,41.604],[-88.144,41.314],[-88.481,41.05],[-88.63,40.891],[-88.781,40.647],[-88.993,40.504],[-89.038,40.428],[-89.393,40.127],[-89.607,39.889],[-89.611,39.856],[-89.65,39.813],[-89.654,39.767],[-89.742,39.596],[-89.78,39.438],[-89.904,39.257],[-89.959,39.211],[-89.979,39.164],[-90.02,39.147],[-90.155,39.028],[-90.181,38.977],[-90.185,38.95],[-90.107,38.868],[-90.084,38.788],[-90.087,38.767],[-90.159,38.7],[-90.161,38.678],[-90.195,38.672],[-90.182,38.63],[-90.187,38.619],[-90.205,38.624]]},"properties":{"name":"Texas Eagle"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-90.203,38.623],[-90.241,38.63],[-90.275,38.606],[-90.256,38.579],[-90.266,38.55],[-90.257,38.541],[-90.314,38.418],[-90.359,38.386],[-90.378,38.289],[-90.42,38.276],[-90.435,38.223],[-90.534,38.172],[-90.565,38.111],[-90.62,38.088],[-90.624,38.033],[-90.704,37.981],[-90.727,37.934],[-90.681,37.872],[-90.676,37.822],[-90.642,37.802],[-90.621,37.755],[-90.65,37.67],[-90.624,37.591],[-90.693,37.519],[-90.686,37.46],[-90.697,37.441],[-90.694,37.384],[-90.705,37.372],[-90.702,37.36],[-90.677,37.353],[-90.662,37.302],[-90.668,37.265],[-90.715,37.219],[-90.691,37.163],[-90.719,37.121],[-90.717,37.102],[-90.707,37.074],[-90.67,37.042],[-90.62,37.021],[-90.605,36.987],[-90.52,36.956],[-90.516,36.93],[-90.463,36.898],[-90.437,36.843],[-90.378,36.792],[-90.381,36.767],[-90.468,36.676],[-90.503,36.586],[-90.515,36.496],[-90.587,36.398],[-90.594,36.328],[-90.969,36.059],[-91.268,35.621],[-91.354,35.567],[-91.463,35.405],[-91.567,35.309],[-91.631,35.274],[-91.644,35.25],[-91.81,35.122],[-91.955,35.026],[-92.014,34.976],[-92.083,34.885],[-92.217,34.773],[-92.273,34.763],[-92.369,34.667],[-92.426,34.645],[-92.484,34.596],[-92.598,34.548],[-92.623,34.526],[-92.651,34.454],[-92.725,34.384],[-92.817,34.364],[-92.992,34.155],[-93.091,34.079],[-93.085,34.039],[-93.1,33.99],[-93.17,33.903],[-93.331,33.847],[-93.525,33.687],[-93.737,33.622],[-93.812,33.614],[-93.875,33.548],[-94.05,33.417],[-94.063,33.389],[-94.057,33.306],[-94.126,33.252],[-94.19,33.028],[-94.257,32.933],[-94.318,32.804],[-94.356,32.763],[-94.341,32.678],[-94.368,32.552],[-94.391,32.548],[-94.441,32.509],[-94.603,32.511],[-94.734,32.493],[-94.844,32.503],[-95.072,32.576],[-95.238,32.589],[-95.418,32.648],[-95.551,32.676],[-95.748,32.674],[-95.898,32.7],[-96.278,32.736],[-96.462,32.741],[-96.481,32.756],[-96.501,32.749],[-96.656,32.777],[-96.735,32.776],[-96.758,32.739],[-96.808,32.774],[-96.82,32.803],[-96.851,32.813],[-97.206,32.807],[-97.325,32.752]]},"properties":{"name":"Texas Eagle"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-98.478,29.419],[-98.481,29.403],[-98.532,29.407],[-98.598,29.331],[-98.649,29.32],[-98.739,29.329],[-98.781,29.321],[-98.893,29.266],[-98.945,29.288],[-99.017,29.362],[-99.033,29.351],[-99.305,29.325],[-99.35,29.354],[-99.49,29.313],[-99.569,29.318],[-99.7,29.278],[-99.789,29.233],[-99.899,29.247],[-99.98,29.238],[-100.021,29.259],[-100.123,29.233],[-100.202,29.236],[-100.281,29.21],[-100.381,29.152],[-100.593,29.284],[-100.628,29.354],[-100.685,29.385],[-100.724,29.392],[-100.81,29.363],[-100.878,29.371],[-100.923,29.356],[-101.002,29.372],[-101.02,29.394],[-101.018,29.452],[-101.039,29.493],[-101.107,29.536],[-101.162,29.679],[-101.217,29.696],[-101.263,29.754],[-101.291,29.752],[-101.31,29.768],[-101.357,29.758],[-101.507,29.818],[-101.577,29.815],[-101.648,29.89],[-101.769,29.972],[-101.858,29.955],[-101.873,29.98],[-101.983,29.998],[-102.033,30.055],[-102.094,30.045],[-102.22,30.054],[-102.328,30.105],[-102.35,30.085],[-102.391,30.118],[-102.39,30.136],[-102.445,30.147],[-102.479,30.168],[-102.566,30.134],[-102.654,30.163],[-102.767,30.095],[-102.857,30.073],[-102.885,30.115],[-102.976,30.113],[-103.051,30.158],[-103.084,30.202],[-103.344,30.218],[-103.451,30.284],[-103.472,30.333],[-103.5,30.359],[-103.582,30.389],[-103.622,30.366],[-103.752,30.339],[-103.804,30.307],[-103.801,30.282],[-103.832,30.271],[-103.981,30.299],[-103.997,30.312],[-104.062,30.316],[-104.223,30.375],[-104.721,30.765],[-104.806,30.927],[-104.872,30.953],[-104.923,30.938],[-105.038,31.024],[-105.348,31.173],[-105.438,31.196],[-105.52,31.277],[-105.594,31.255],[-105.695,31.261],[-105.763,31.23],[-105.79,31.241],[-105.805,31.271],[-105.89,31.331],[-105.932,31.379],[-106.057,31.423],[-106.144,31.49],[-106.316,31.691],[-106.406,31.769],[-106.461,31.776],[-106.499,31.758],[-106.528,31.788],[-106.549,31.794],[-106.566,31.784],[-106.591,31.795],[-106.697,31.861],[-106.914,32.057],[-107.112,32.142],[-107.147,32.176],[-107.577,32.29],[-108.152,32.223],[-108.384,32.181],[-108.633,32.329],[-108.74,32.356],[-108.775,32.35],[-108.963,32.234],[-109.024,32.224],[-109.589,32.352],[-109.738,32.345],[-109.795,32.305],[-109.98,32.037],[-110.026,32.035],[-110.127,31.979],[-110.165,31.983],[-110.161,31.965],[-110.182,31.952],[-110.206,31.952],[-110.219,31.976],[-110.286,31.965],[-110.328,31.988],[-110.382,31.997],[-110.487,31.983],[-110.619,32.03],[-110.65,32.016],[-110.688,32.027],[-110.908,32.158],[-110.912,32.188],[-110.979,32.231],[-111.121,32.395],[-111.251,32.488],[-111.384,32.644],[-112.087,33.075],[-112.217,33.069],[-112.349,33.035],[-112.433,32.995],[-112.49,33.002],[-112.538,32.977],[-112.97,32.909],[-113.142,32.865],[-113.48,32.819],[-113.724,32.727],[-114.223,32.659],[-114.294,32.677],[-114.35,32.722],[-114.364,32.757],[-114.407,32.759],[-114.44,32.698],[-114.465,32.684],[-114.541,32.675],[-114.607,32.691],[-114.615,32.73],[-114.635,32.745],[-114.685,32.756],[-114.726,32.745],[-114.757,32.752],[-115.048,32.982],[-115.298,33.146],[-115.558,33.263],[-115.636,33.357],[-115.7,33.378],[-115.738,33.357],[-115.76,33.361],[-115.946,33.531],[-116.025,33.539],[-116.065,33.557],[-116.208,33.717],[-116.32,33.765],[-116.551,33.9],[-116.644,33.904],[-116.696,33.924],[-116.793,33.916],[-116.83,33.931],[-116.977,33.925],[-117.007,33.943],[-117.049,33.945],[-117.087,33.974],[-117.142,33.985],[-117.236,34.05],[-117.344,34.068],[-117.797,34.058],[-117.833,34.024],[-117.881,34.002],[-117.945,34.013],[-118.043,34.081],[-118.096,34.097],[-118.184,34.062],[-118.234,34.056]]},"properties":{"name":"Texas Eagle"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-72.594,42.106],[-72.58,42.079],[-72.605,42.0],[-72.606,41.946],[-72.627,41.935],[-72.628,41.881],[-72.649,41.814],[-72.745,41.705],[-72.771,41.586],[-72.816,41.507],[-72.811,41.483],[-72.904,41.315]]},"properties":{"name":"Vermonter"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.086,44.811],[-73.106,44.773],[-73.081,44.737],[-73.091,44.709],[-73.083,44.676],[-73.105,44.647],[-73.12,44.58],[-73.134,44.57],[-73.11,44.487],[-73.033,44.46],[-72.989,44.401],[-72.772,44.348],[-72.609,44.259],[-72.597,44.239],[-72.633,44.206],[-72.674,44.115],[-72.728,44.115],[-72.758,44.026],[-72.747,43.972],[-72.7,43.933],[-72.657,43.92],[-72.64,43.875],[-72.645,43.841],[-72.627,43.825],[-72.574,43.828],[-72.555,43.814],[-72.531,43.824],[-72.489,43.787],[-72.452,43.781],[-72.432,43.726],[-72.393,43.677],[-72.317,43.647],[-72.336,43.617],[-72.328,43.603],[-72.383,43.577],[-72.401,43.513],[-72.384,43.488],[-72.393,43.409],[-72.378,43.377],[-72.378,43.328],[-72.425,43.252],[-72.449,43.151],[-72.433,43.119],[-72.444,43.102],[-72.438,43.089],[-72.469,43.053],[-72.455,43.014],[-72.471,42.98],[-72.532,42.955],[-72.525,42.916],[-72.555,42.881],[-72.557,42.852],[-72.518,42.765],[-72.49,42.757],[-72.461,42.722],[-72.479,42.689],[-72.511,42.668],[-72.552,42.666],[-72.606,42.604],[-72.592,42.565],[-72.642,42.348],[-72.604,42.272],[-72.626,42.231],[-72.601,42.206],[-72.615,42.149],[-72.596,42.104]]},"properties":{"name":"Vermonter"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-72.926,41.298],[-73.035,41.225],[-73.119,41.203],[-73.145,41.185],[-73.184,41.185],[-73.188,41.17],[-73.229,41.165],[-73.259,41.142],[-73.321,41.12],[-73.376,41.118],[-73.436,41.079],[-73.515,41.07],[-73.557,41.036],[-73.632,41.021],[-73.776,40.922],[-73.819,40.862],[-73.879,40.836],[-73.926,40.788],[-73.906,40.753],[-73.958,40.741],[-73.994,40.751]]},"properties":{"name":"Vermonter"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-75.182,39.956],[-75.322,39.881],[-75.343,39.856],[-75.435,39.815],[-75.494,39.755],[-75.767,39.666],[-75.843,39.607],[-75.928,39.611],[-75.974,39.58],[-76.101,39.549],[-76.158,39.515],[-76.217,39.45],[-76.338,39.397],[-76.384,39.35],[-76.477,39.328],[-76.531,39.299],[-76.621,39.311],[-76.649,39.302],[-76.69,39.257],[-76.696,39.121],[-76.707,39.085],[-76.741,39.044],[-76.908,38.92],[-76.98,38.918],[-77.006,38.898]]},"properties":{"name":"Vermonter"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.994,40.751],[-74.049,40.773],[-74.163,40.736],[-74.274,40.608],[-74.355,40.542],[-74.451,40.493],[-74.532,40.388],[-74.774,40.203],[-74.796,40.159],[-74.848,40.109],[-74.923,40.09],[-75.057,40.014],[-75.165,39.994],[-75.198,39.973],[-75.182,39.956]]},"properties":{"name":"Vermonter"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.254,42.451],[-73.308,42.44],[-73.398,42.349],[-73.424,42.357],[-73.448,42.382],[-73.451,42.41],[-73.472,42.418],[-73.536,42.411],[-73.545,42.392],[-73.607,42.358],[-73.62,42.415],[-73.669,42.449],[-73.696,42.485],[-73.745,42.611],[-73.742,42.64]]},"properties":{"name":"Berkshire flyer"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-73.739,42.649],[-73.785,42.387],[-73.77,42.296],[-73.799,42.252],[-73.834,42.23],[-73.91,42.118],[-73.956,41.908],[-73.926,41.86],[-73.948,41.773],[-73.936,41.75],[-73.947,41.582],[-73.985,41.519],[-73.984,41.455],[-73.943,41.393],[-73.98,41.318],[-73.931,41.291],[-73.939,41.274],[-73.921,41.246],[-73.929,41.232],[-73.89,41.202],[-73.872,41.168],[-73.865,41.079],[-73.92,40.887],[-74.001,40.754]]},"properties":{"name":"Berkshire flyer"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-87.639,41.879],[-87.642,41.849],[-87.81,41.795],[-87.892,41.721],[-88.048,41.654],[-88.052,41.604],[-88.144,41.314],[-88.481,41.05],[-88.63,40.891],[-88.781,40.647],[-88.993,40.504],[-89.038,40.428],[-89.393,40.127],[-89.607,39.889],[-89.611,39.856],[-89.65,39.813],[-89.654,39.767],[-89.742,39.596],[-89.78,39.438],[-89.904,39.257],[-89.959,39.211],[-89.979,39.164],[-90.02,39.147],[-90.155,39.028],[-90.181,38.977],[-90.185,38.95],[-90.107,38.868],[-90.084,38.788],[-90.087,38.767],[-90.159,38.7],[-90.161,38.678],[-90.195,38.672],[-90.182,38.63],[-90.187,38.619],[-90.205,38.624]]},"properties":{"name":"Lincoln Service Missouri River Runner"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-90.204,38.623],[-90.292,38.622],[-90.324,38.604],[-90.344,38.608],[-90.39,38.575],[-90.426,38.58],[-90.501,38.547],[-90.561,38.547],[-90.609,38.528],[-90.62,38.503],[-90.683,38.504],[-90.709,38.48],[-90.79,38.478],[-90.834,38.496],[-90.85,38.521],[-90.975,38.543],[-91.068,38.601],[-91.138,38.596],[-91.215,38.614],[-91.351,38.689],[-91.411,38.708],[-91.474,38.701],[-91.552,38.669],[-91.792,38.674],[-91.874,38.644],[-92.006,38.555],[-92.074,38.547],[-92.265,38.624],[-92.321,38.611],[-92.446,38.631],[-92.459,38.615],[-92.491,38.608],[-92.515,38.612],[-92.527,38.628],[-92.571,38.63],[-92.673,38.663],[-92.747,38.653],[-92.882,38.67],[-92.967,38.706],[-93.043,38.683],[-93.121,38.685],[-93.265,38.718],[-93.32,38.751],[-93.412,38.775],[-93.572,38.767],[-93.631,38.777],[-93.874,38.745],[-93.972,38.712],[-94.096,38.756],[-94.267,38.779],[-94.343,38.855],[-94.399,38.946],[-94.414,38.979],[-94.415,39.067],[-94.426,39.086],[-94.48,39.114],[-94.586,39.086]]},"properties":{"name":"Lincoln Service Missouri River Runner"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-81.696,41.506],[-81.738,41.487],[-81.885,41.375],[-82.097,41.37],[-82.343,41.419],[-82.494,41.371],[-82.688,41.438],[-82.761,41.444],[-82.834,41.498],[-83.114,41.521],[-83.434,41.569],[-83.548,41.638],[-84.894,41.426],[-85.548,41.463],[-85.638,41.492],[-85.865,41.601],[-85.972,41.681],[-86.107,41.661],[-86.245,41.663],[-86.29,41.679],[-86.501,41.705],[-86.754,41.604],[-86.9,41.599],[-87.077,41.614]]},"properties":{"name":"Floridian"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-77.006,38.898],[-77.007,38.885],[-77.032,38.883],[-77.048,38.863],[-77.047,38.824],[-77.058,38.809],[-77.106,38.807]]},"properties":{"name":"Floridian"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-81.796,28.059],[-81.77,28.022],[-81.736,28.006],[-81.65,27.893],[-81.638,27.811],[-81.507,27.621],[-81.498,27.574],[-81.437,27.533],[-81.432,27.491],[-81.319,27.477],[-80.87,27.252],[-80.817,27.251],[-80.066,26.75],[-80.071,26.551],[-80.091,26.469],[-80.091,26.402],[-80.12,26.373],[-80.12,26.324],[-80.168,26.151],[-80.167,25.964],[-80.259,25.897],[-80.258,25.85]]},"properties":{"name":"Floridian"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-81.149,32.083],[-81.126,32.05],[-81.152,32.026],[-81.726,31.721],[-81.887,31.601],[-82.028,31.007],[-82.001,30.785],[-81.943,30.723],[-81.843,30.58],[-81.728,30.368],[-81.681,30.331],[-81.721,30.301],[-81.719,30.275],[-81.697,30.244],[-81.714,30.162],[-81.755,30.136],[-81.771,30.108],[-81.76,30.068],[-81.685,29.996],[-81.619,29.843],[-81.662,29.702],[-81.64,29.656],[-81.641,29.63],[-81.685,29.602],[-81.64,29.528],[-81.57,29.481],[-81.51,29.373],[-81.459,29.217],[-81.36,29.137],[-81.364,29.051],[-81.332,28.979],[-81.338,28.875],[-81.324,28.859],[-81.325,28.83],[-81.292,28.809],[-81.292,28.785],[-81.314,28.761]]},"properties":{"name":"Floridian"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-81.796,28.059],[-81.873,28.045],[-81.938,28.049],[-82.45,27.953]]},"properties":{"name":"Floridian"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-87.639,41.879],[-87.634,41.783],[-87.417,41.634],[-87.322,41.605],[-87.16,41.62],[-87.074,41.614]]},"properties":{"name":"Floridian"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-81.314,28.761],[-81.336,28.727],[-81.368,28.621],[-81.352,28.596],[-81.381,28.554],[-81.367,28.441],[-81.374,28.366],[-81.402,28.297],[-81.463,28.26],[-81.529,28.267],[-81.552,28.257],[-81.63,28.106],[-81.661,28.115],[-81.766,28.087],[-81.796,28.059],[-81.77,28.022],[-81.738,28.007]]},"properties":{"name":"Floridian"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-78.779,35.789],[-78.832,35.758],[-78.886,35.703],[-79.042,35.613],[-79.087,35.612],[-79.123,35.549],[-79.171,35.511],[-79.177,35.442],[-79.196,35.39],[-79.261,35.312],[-79.284,35.253],[-79.4,35.168],[-79.481,35.055],[-79.561,35.024],[-79.569,34.995],[-79.6,34.977],[-79.616,34.943],[-79.683,34.909],[-79.694,34.886],[-79.752,34.844],[-79.768,34.795],[-79.817,34.766],[-79.837,34.726],[-79.882,34.693],[-79.965,34.66],[-80.019,34.588],[-80.061,34.564],[-80.109,34.561],[-80.173,34.519],[-80.244,34.492],[-80.278,34.444],[-80.408,34.388],[-80.445,34.359],[-80.527,34.335],[-80.614,34.266],[-80.616,34.253],[-80.684,34.212],[-80.72,34.21],[-80.748,34.182],[-80.814,34.162],[-80.969,34.053],[-81.014,34.05],[-81.058,34.016],[-81.037,33.989],[-81.057,33.969],[-81.057,33.925],[-81.082,33.888],[-81.077,33.871],[-81.093,33.866],[-81.104,33.815],[-81.1,33.711],[-81.116,33.682],[-81.1,33.641],[-81.107,33.584],[-81.127,33.542],[-81.118,33.504],[-81.147,33.296],[-81.175,33.228],[-81.236,32.974],[-81.245,32.609],[-81.254,32.542],[-81.271,32.509],[-81.223,32.206],[-81.153,32.09]]},"properties":{"name":"Floridian"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-78.279,35.533],[-78.477,35.668],[-78.619,35.712],[-78.648,35.777]]},"properties":{"name":"Floridian"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-81.696,41.506],[-81.658,41.509],[-81.627,41.479],[-81.627,41.449],[-81.542,41.395],[-81.501,41.302],[-81.464,41.256],[-81.243,41.143],[-81.205,41.1],[-81.143,40.98],[-81.099,40.925],[-80.9,40.922],[-80.797,40.861],[-80.736,40.885],[-80.67,40.883],[-80.628,40.866],[-80.595,40.832],[-80.482,40.841],[-80.41,40.865],[-80.395,40.816],[-80.329,40.815],[-80.324,40.749],[-80.29,40.701],[-80.253,40.681],[-80.234,40.644],[-80.232,40.587],[-80.19,40.537],[-80.076,40.501],[-80.009,40.45],[-79.957,40.461],[-79.946,40.402],[-79.895,40.417],[-79.846,40.385],[-79.838,40.359],[-79.869,40.343],[-79.834,40.314],[-79.79,40.316],[-79.804,40.296],[-79.774,40.289],[-79.796,40.278],[-79.787,40.265],[-79.804,40.235],[-79.765,40.228],[-79.769,40.208],[-79.756,40.191],[-79.777,40.182],[-79.743,40.156],[-79.742,40.135],[-79.759,40.114],[-79.711,40.086],[-79.725,40.073],[-79.697,40.052],[-79.688,40.062],[-79.638,40.038],[-79.614,40.049],[-79.599,40.042],[-79.582,39.975],[-79.502,39.964],[-79.471,39.915],[-79.487,39.906],[-79.475,39.904],[-79.476,39.89],[-79.494,39.872],[-79.46,39.847],[-79.423,39.856],[-79.357,39.812],[-79.335,39.81],[-79.309,39.828],[-79.26,39.832],[-79.229,39.852],[-79.23,39.882],[-79.16,39.913],[-79.134,39.906],[-79.113,39.88],[-79.064,39.868],[-79.048,39.856],[-79.044,39.832],[-79.025,39.817],[-78.996,39.818],[-78.978,39.797],[-78.939,39.835],[-78.851,39.809],[-78.802,39.845],[-78.758,39.824],[-78.736,39.833],[-78.721,39.826],[-78.723,39.778],[-78.777,39.714],[-78.787,39.67],[-78.762,39.656],[-78.767,39.636],[-78.733,39.621],[-78.727,39.565],[-78.563,39.517],[-78.468,39.517],[-78.434,39.542],[-78.425,39.567],[-78.396,39.582],[-78.394,39.604],[-78.371,39.613],[-78.379,39.625],[-78.36,39.624],[-78.353,39.64],[-78.267,39.618],[-78.223,39.662],[-78.231,39.673],[-78.191,39.69],[-78.104,39.679],[-78.055,39.651],[-78.011,39.598],[-77.984,39.59],[-77.963,39.462],[-77.908,39.442],[-77.878,39.371],[-77.812,39.363],[-77.727,39.324],[-77.671,39.329],[-77.603,39.305],[-77.559,39.308],[-77.537,39.273],[-77.442,39.224],[-77.183,39.14],[-77.136,39.075],[-77.004,38.958],[-76.994,38.927],[-77.006,38.897]]},"properties":{"name":"Floridian"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-77.106,38.807],[-77.153,38.793],[-77.223,38.698]]},"properties":{"name":"Floridian"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-77.22,38.708],[-77.25,38.656],[-77.262,38.565],[-77.309,38.505],[-77.328,38.449],[-77.411,38.352],[-77.449,38.34],[-77.448,38.302],[-77.461,38.295],[-77.452,38.261],[-77.434,38.245],[-77.438,38.199],[-77.457,38.163],[-77.38,38.073],[-77.363,37.965],[-77.439,37.955],[-77.469,37.915],[-77.457,37.834],[-77.501,37.694],[-77.508,37.645],[-77.496,37.617]]},"properties":{"name":"Floridian"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-77.496,37.617],[-77.473,37.579],[-77.496,37.525],[-77.451,37.463],[-77.454,37.375],[-77.402,37.31],[-77.408,37.27],[-77.435,37.232],[-77.42,37.201],[-77.393,37.019],[-77.404,36.929],[-77.466,36.816],[-77.476,36.772],[-77.545,36.682],[-77.557,36.645],[-77.53,36.545],[-77.554,36.465],[-77.606,36.416],[-77.617,36.366],[-77.595,36.321],[-77.657,36.19],[-77.703,36.147],[-77.707,36.115],[-77.772,36.005],[-77.825,35.873],[-77.932,35.678],[-78.279,35.533]]},"properties":{"name":"Floridian"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-78.648,35.777],[-78.694,35.795],[-78.779,35.789]]},"properties":{"name":"Floridian"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-90.056,29.986],[-90.035,30.002],[-89.804,30.062],[-89.525,30.2]]},"properties":{"OBJECTID":401,"cartodb_id":null,"name":"Amtrak Mardi Gras Service","cmsid":"1241245668166","segmentid":null,"north":null,"south":null,"east":null,"west":null,"created_at":null,"updated_at":null,"Shape__Length":2.264419412503505,"routeName":"Amtrak Mardi Gras Service"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-89.525,30.2],[-89.501,30.215]]},"properties":{"OBJECTID":401,"cartodb_id":null,"name":"Amtrak Mardi Gras Service","cmsid":"1241245668166","segmentid":null,"north":null,"south":null,"east":null,"west":null,"created_at":null,"updated_at":null,"Shape__Length":2.264419412503505,"routeName":"Amtrak Mardi Gras Service"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-89.501,30.215],[-89.525,30.2]]},"properties":{"OBJECTID":401,"cartodb_id":null,"name":"Amtrak Mardi Gras Service","cmsid":"1241245668166","segmentid":null,"north":null,"south":null,"east":null,"west":null,"created_at":null,"updated_at":null,"Shape__Length":2.264419412503505,"routeName":"Amtrak Mardi Gras Service"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-89.501,30.215],[-89.335,30.308],[-89.274,30.31],[-88.993,30.395],[-88.877,30.4],[-88.816,30.415],[-88.543,30.368],[-88.391,30.463],[-88.188,30.529],[-88.127,30.619],[-88.041,30.672],[-88.038,30.691]]},"properties":{"OBJECTID":401,"cartodb_id":null,"name":"Amtrak Mardi Gras Service","cmsid":"1241245668166","segmentid":null,"north":null,"south":null,"east":null,"west":null,"created_at":null,"updated_at":null,"Shape__Length":2.264419412503505,"routeName":"Amtrak Mardi Gras Service"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-90.056,29.986],[-90.113,29.989],[-90.108,29.968]]},"properties":{"name":"Amtrak Mardi Gras Service"}},{"type":"Feature","geometry":{"type":"LineString","coordinates":[[-90.108,29.968],[-90.079,29.946]]},"properties":{"name":"Amtrak Mardi Gras Service"}}]}
+1
data/us-states.json
··· 1 + {"type":"FeatureCollection","features":[{"type":"Feature","id":"01","properties":{"name":"Alabama"},"geometry":{"type":"Polygon","coordinates":[[[-87.36,35.0],[-85.61,34.98],[-85.18,32.86],[-85.07,32.58],[-84.96,32.42],[-85.0,32.32],[-84.89,32.26],[-85.06,32.14],[-85.05,32.01],[-85.14,31.84],[-85.04,31.54],[-85.11,31.28],[-85.0,31.0],[-87.6,31.0],[-87.63,30.87],[-87.41,30.67],[-87.45,30.51],[-87.37,30.43],[-87.52,30.28],[-87.66,30.25],[-87.91,30.41],[-87.93,30.66],[-88.01,30.69],[-88.1,30.5],[-88.14,30.32],[-88.39,30.37],[-88.47,31.9],[-88.1,34.89],[-88.2,35.0],[-87.36,35.0]]]}},{"type":"Feature","id":"02","properties":{"name":"Alaska"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-131.6,55.12],[-131.57,55.28],[-131.36,55.18],[-131.39,55.01],[-131.65,55.04],[-131.6,55.12]]],[[[-131.83,55.42],[-131.65,55.3],[-131.75,55.13],[-131.83,55.19],[-131.83,55.42]]],[[[-132.98,56.44],[-132.74,56.46],[-132.63,56.42],[-132.66,56.27],[-132.88,56.24],[-133.07,56.33],[-132.98,56.44]]],[[[-133.6,56.35],[-133.16,56.32],[-133.05,56.13],[-132.62,55.91],[-132.47,55.78],[-132.46,55.67],[-132.36,55.65],[-132.34,55.51],[-132.17,55.36],[-132.14,55.24],[-132.03,55.28],[-131.98,55.18],[-131.96,54.79],[-132.03,54.7],[-132.31,54.72],[-132.39,54.92],[-132.48,54.9],[-132.69,55.05],[-132.75,55.0],[-132.92,55.05],[-132.89,54.9],[-132.73,54.94],[-132.63,54.88],[-132.68,54.68],[-132.87,54.7],[-133.16,54.96],[-133.24,55.09],[-133.22,55.23],[-133.45,55.22],[-133.45,55.32],[-133.28,55.33],[-133.1,55.42],[-133.18,55.59],[-133.39,55.62],[-133.42,55.88],[-133.5,56.02],[-133.64,55.92],[-133.69,56.07],[-133.55,56.14],[-133.67,56.31],[-133.6,56.35]]],[[[-133.74,55.56],[-133.55,55.49],[-133.41,55.57],[-133.28,55.53],[-133.42,55.39],[-133.63,55.43],[-133.74,55.56]]],[[[-133.91,56.93],[-134.05,57.03],[-133.89,57.1],[-133.34,57.0],[-133.1,57.01],[-132.93,56.82],[-132.62,56.67],[-132.65,56.55],[-132.82,56.49],[-133.04,56.52],[-133.2,56.45],[-133.42,56.49],[-133.66,56.45],[-133.71,56.68],[-133.69,56.84],[-133.87,56.84],[-133.91,56.93]]],[[[-134.12,56.48],[-134.25,56.56],[-134.4,56.72],[-134.42,56.85],[-134.3,56.91],[-134.17,56.85],[-134.14,56.95],[-133.75,56.77],[-133.71,56.6],[-133.85,56.57],[-133.94,56.38],[-133.84,56.32],[-133.96,56.09],[-134.11,56.14],[-134.13,56.0],[-134.23,56.07],[-134.29,56.35],[-134.12,56.48]]],[[[-134.64,56.28],[-134.67,56.17],[-134.81,56.24],[-135.18,56.68],[-135.41,56.81],[-135.33,56.91],[-135.42,57.17],[-135.69,57.37],[-135.42,57.57],[-135.3,57.48],[-135.06,57.42],[-134.85,57.41],[-134.84,57.25],[-134.64,56.73],[-134.64,56.28]]],[[[-134.71,58.22],[-134.37,58.15],[-134.18,58.16],[-134.19,58.08],[-133.9,57.81],[-134.1,57.85],[-134.15,57.76],[-133.94,57.62],[-133.87,57.36],[-134.08,57.3],[-134.15,57.21],[-134.5,57.03],[-134.6,57.03],[-134.65,57.23],[-134.58,57.34],[-134.61,57.51],[-134.73,57.72],[-134.71,57.83],[-134.78,58.1],[-134.92,58.21],[-134.95,58.41],[-134.71,58.22]]],[[[-135.86,57.33],[-135.72,57.33],[-135.57,57.15],[-135.63,57.02],[-135.86,57.0],[-135.82,57.19],[-135.86,57.33]]],[[[-136.28,58.21],[-135.98,58.2],[-135.78,58.29],[-135.5,58.17],[-135.65,58.04],[-135.59,57.99],[-135.45,58.14],[-135.11,58.09],[-134.92,57.98],[-135.03,57.78],[-134.94,57.76],[-134.82,57.5],[-135.09,57.46],[-135.57,57.68],[-135.56,57.46],[-135.71,57.37],[-135.89,57.41],[-136.0,57.54],[-136.21,57.64],[-136.37,57.83],[-136.57,57.92],[-136.56,58.08],[-136.42,58.13],[-136.38,58.27],[-136.28,58.21]]],[[[-147.08,60.2],[-147.5,59.95],[-147.53,59.85],[-147.87,59.78],[-147.8,59.94],[-147.44,60.1],[-147.21,60.27],[-147.08,60.2]]],[[[-147.56,60.58],[-147.62,60.37],[-147.76,60.16],[-147.96,60.23],[-147.79,60.47],[-147.56,60.58]]],[[[-147.79,70.25],[-147.68,70.2],[-147.16,70.16],[-146.89,70.19],[-146.1,70.15],[-145.86,70.17],[-145.2,69.99],[-144.62,69.97],[-144.46,70.03],[-144.08,70.06],[-143.91,70.13],[-143.5,70.14],[-143.5,70.09],[-143.26,70.12],[-142.75,70.04],[-142.4,69.92],[-142.08,69.86],[-142.01,69.8],[-141.71,69.79],[-141.43,69.7],[-141.38,69.64],[-141.21,69.69],[-141.0,69.65],[-141.0,60.3],[-140.53,60.22],[-140.47,60.31],[-139.99,60.18],[-139.7,60.34],[-139.09,60.36],[-139.2,60.09],[-139.05,60.0],[-138.7,59.91],[-138.62,59.77],[-137.6,59.24],[-137.45,58.91],[-136.83,59.16],[-136.58,59.17],[-136.47,59.29],[-136.48,59.47],[-136.3,59.47],[-136.26,59.63],[-135.95,59.66],[-135.48,59.8],[-135.03,59.57],[-135.07,59.42],[-134.96,59.28],[-134.7,59.25],[-134.38,59.03],[-134.4,58.97],[-134.25,58.86],[-133.84,58.73],[-133.17,58.15],[-133.08,58.0],[-132.87,57.85],[-132.25,57.22],[-132.37,57.1],[-132.05,57.05],[-132.13,56.88],[-131.87,56.8],[-131.84,56.6],[-131.58,56.61],[-131.09,56.41],[-130.78,56.37],[-130.62,56.27],[-130.47,56.24],[-130.42,56.14],[-130.1,56.11],[-130.0,55.99],[-130.15,55.77],[-130.13,55.58],[-129.99,55.28],[-130.1,55.2],[-130.34,54.92],[-130.69,54.72],[-130.79,54.82],[-130.92,54.79],[-131.01,55.0],[-130.98,55.09],[-131.09,55.19],[-130.86,55.3],[-130.93,55.34],[-131.16,55.2],[-131.28,55.29],[-131.43,55.24],[-131.84,55.46],[-131.7,55.7],[-131.96,55.62],[-131.97,55.5],[-132.18,55.59],[-132.23,55.7],[-132.08,55.83],[-132.13,55.96],[-132.32,55.85],[-132.52,56.08],[-132.64,56.03],[-132.72,56.22],[-132.53,56.34],[-132.34,56.34],[-132.4,56.49],[-132.3,56.68],[-132.45,56.67],[-132.77,56.84],[-132.99,57.03],[-133.52,57.18],[-133.51,57.58],[-133.68,57.63],[-133.64,57.79],[-133.81,57.83],[-134.07,58.05],[-134.14,58.17],[-134.59,58.21],[-135.07,58.5],[-135.28,59.19],[-135.38,59.03],[-135.34,58.89],[-135.14,58.62],[-135.19,58.57],[-135.06,58.35],[-135.09,58.2],[-135.28,58.23],[-135.43,58.4],[-135.63,58.43],[-135.92,58.38],[-135.91,58.62],[-136.09,58.81],[-136.25,58.75],[-136.88,58.96],[-136.93,58.9],[-136.59,58.84],[-136.32,58.67],[-136.21,58.67],[-136.18,58.54],[-136.04,58.38],[-136.39,58.29],[-136.59,58.35],[-136.6,58.21],[-136.86,58.32],[-136.95,58.39],[-137.11,58.39],[-137.9,58.77],[-137.93,58.87],[-138.12,59.02],[-138.63,59.13],[-139.75,59.51],[-139.72,59.64],[-139.63,59.6],[-139.52,59.69],[-139.63,59.88],[-139.49,59.99],[-139.55,60.04],[-139.8,59.83],[-140.32,59.7],[-140.93,59.75],[-141.44,59.87],[-141.47,59.97],[-141.71,59.95],[-141.96,60.02],[-142.54,60.09],[-142.87,60.09],[-143.89,60.0],[-144.23,60.14],[-144.65,60.21],[-144.79,60.29],[-144.83,60.44],[-145.12,60.43],[-145.22,60.3],[-145.74,60.47],[-145.82,60.55],[-146.35,60.41],[-146.61,60.24],[-146.72,60.4],[-146.61,60.49],[-146.46,60.46],[-145.95,60.58],[-146.02,60.67],[-146.25,60.62],[-146.35,60.74],[-146.57,60.75],[-146.78,61.04],[-146.87,60.97],[-147.17,60.93],[-147.27,60.97],[-147.38,60.88],[-147.76,60.91],[-147.78,60.81],[-148.03,60.78],[-148.15,60.82],[-148.07,61.01],[-148.18,61.0],[-148.35,60.8],[-148.11,60.74],[-148.09,60.59],[-147.94,60.44],[-148.03,60.28],[-148.22,60.33],[-148.27,60.25],[-148.09,60.22],[-147.98,60.0],[-148.25,59.95],[-148.4,60.0],[-148.64,59.94],[-148.76,59.99],[-149.07,59.98],[-149.06,60.06],[-149.2,60.01],[-149.29,59.9],[-149.42,60.0],[-149.58,59.87],[-149.51,59.81],[-149.74,59.73],[-149.95,59.72],[-150.03,59.61],[-150.26,59.52],[-150.41,59.55],[-150.58,59.44],[-150.72,59.45],[-151.0,59.23],[-151.31,59.21],[-151.41,59.28],[-151.59,59.16],[-151.98,59.25],[-151.89,59.42],[-151.64,59.48],[-151.47,59.47],[-151.42,59.54],[-151.13,59.67],[-151.12,59.78],[-151.51,59.63],[-151.83,59.72],[-151.87,59.78],[-151.7,60.03],[-151.42,60.21],[-151.38,60.36],[-151.3,60.39],[-151.26,60.55],[-151.41,60.72],[-151.06,60.79],[-150.4,61.04],[-150.25,60.94],[-150.04,60.91],[-149.74,61.02],[-150.08,61.15],[-150.21,61.26],[-150.47,61.25],[-150.66,61.3],[-150.71,61.25],[-151.02,61.18],[-151.17,61.04],[-151.48,61.01],[-151.8,60.85],[-151.83,60.75],[-152.08,60.69],[-152.14,60.58],[-152.31,60.51],[-152.39,60.3],[-152.73,60.17],[-152.57,60.07],[-152.7,59.92],[-153.02,59.89],[-153.05,59.69],[-153.35,59.62],[-153.44,59.7],[-153.59,59.55],[-153.76,59.54],[-153.73,59.43],[-154.12,59.37],[-154.19,59.07],[-153.75,59.05],[-153.4,58.97],[-153.3,58.87],[-153.44,58.71],[-153.68,58.61],[-153.9,58.61],[-153.92,58.52],[-154.06,58.49],[-154.0,58.38],[-154.15,58.21],[-154.46,58.06],[-154.64,58.06],[-154.82,58.0],[-154.99,58.02],[-155.12,57.96],[-155.08,57.87],[-155.33,57.83],[-155.38,57.71],[-155.55,57.79],[-155.73,57.55],[-156.05,57.57],[-156.02,57.44],[-156.21,57.47],[-156.34,57.42],[-156.34,57.25],[-156.55,56.99],[-156.88,56.95],[-157.16,56.83],[-157.2,56.77],[-157.38,56.86],[-157.67,56.61],[-157.75,56.68],[-157.92,56.66],[-157.96,56.51],[-158.13,56.46],[-158.33,56.48],[-158.49,56.34],[-158.21,56.3],[-158.51,55.98],[-159.38,55.87],[-159.62,55.59],[-159.68,55.65],[-159.64,55.83],[-159.81,55.86],[-160.03,55.79],[-160.06,55.72],[-160.39,55.61],[-160.54,55.47],[-160.58,55.57],[-160.67,55.46],[-160.87,55.53],[-161.23,55.36],[-161.51,55.36],[-161.47,55.5],[-161.59,55.62],[-161.7,55.52],[-161.69,55.41],[-162.05,55.07],[-162.18,55.16],[-162.22,55.03],[-162.47,55.05],[-162.51,55.25],[-162.66,55.29],[-162.72,55.22],[-162.58,55.13],[-162.65,55.0],[-162.85,54.93],[-163.0,55.08],[-163.19,55.09],[-163.22,55.03],[-163.03,54.94],[-163.37,54.8],[-163.14,54.76],[-163.14,54.7],[-163.33,54.75],[-163.59,54.61],[-164.09,54.62],[-164.33,54.53],[-164.35,54.47],[-164.64,54.39],[-164.85,54.42],[-164.92,54.6],[-164.71,54.66],[-164.55,54.89],[-164.34,54.89],[-163.89,55.04],[-163.53,55.05],[-163.4,54.9],[-163.29,55.01],[-163.31,55.13],[-163.11,55.18],[-162.88,55.18],[-162.58,55.45],[-162.25,55.68],[-161.81,55.89],[-161.29,55.98],[-161.08,55.94],[-160.87,56.0],[-160.82,55.91],[-160.93,55.81],[-160.81,55.74],[-160.77,55.86],[-160.51,55.87],[-160.44,55.79],[-160.28,55.76],[-160.27,55.86],[-160.54,55.94],[-160.56,55.99],[-160.38,56.25],[-160.15,56.4],[-159.83,56.54],[-159.33,56.67],[-158.96,56.85],[-158.78,56.78],[-158.64,56.81],[-158.7,56.93],[-158.66,57.03],[-158.38,57.26],[-158.0,57.41],[-157.69,57.61],[-157.71,57.72],[-157.46,58.5],[-157.08,58.71],[-157.12,58.87],[-158.04,58.63],[-158.33,58.66],[-158.4,58.76],[-158.56,58.8],[-158.62,58.91],[-158.77,58.86],[-158.86,58.69],[-158.7,58.48],[-158.89,58.39],[-159.06,58.42],[-159.39,58.76],[-159.62,58.93],[-159.73,58.93],[-159.81,58.8],[-159.91,58.78],[-160.05,58.89],[-160.24,58.9],[-160.32,59.07],[-161.34,58.74],[-161.37,58.67],[-161.75,58.55],[-161.94,58.66],[-161.77,58.78],[-161.83,59.06],[-161.96,59.36],[-161.7,59.49],[-161.91,59.74],[-162.09,59.88],[-162.23,60.09],[-162.45,60.18],[-162.5,60.0],[-162.76,59.96],[-163.17,59.84],[-163.66,59.8],[-163.93,59.81],[-164.16,59.87],[-164.19,60.03],[-164.39,60.07],[-164.7,60.29],[-164.96,60.34],[-165.27,60.58],[-165.06,60.69],[-165.02,60.89],[-165.18,60.85],[-165.2,60.97],[-165.12,61.08],[-165.32,61.17],[-165.35,61.07],[-165.59,61.11],[-165.62,61.28],[-165.82,61.3],[-165.92,61.42],[-165.92,61.56],[-166.11,61.49],[-166.14,61.63],[-165.9,61.66],[-166.1,61.82],[-165.76,61.83],[-165.76,62.01],[-165.67,62.14],[-165.04,62.54],[-164.91,62.66],[-164.82,62.64],[-164.87,62.81],[-164.63,63.1],[-164.43,63.21],[-164.04,63.26],[-163.74,63.21],[-163.31,63.04],[-163.04,63.06],[-162.66,63.23],[-162.27,63.49],[-162.08,63.51],[-162.03,63.45],[-161.56,63.45],[-161.14,63.5],[-160.77,63.77],[-160.77,63.84],[-160.95,64.09],[-160.97,64.24],[-161.27,64.4],[-161.37,64.53],[-161.08,64.49],[-160.8,64.61],[-160.78,64.72],[-161.14,64.92],[-161.41,64.76],[-161.66,64.79],[-161.9,64.7],[-162.17,64.68],[-162.23,64.62],[-162.54,64.53],[-162.63,64.38],[-162.79,64.32],[-162.86,64.5],[-163.05,64.54],[-163.18,64.4],[-163.25,64.47],[-163.6,64.57],[-164.3,64.56],[-165.0,64.43],[-166.19,64.58],[-166.39,64.64],[-166.48,64.74],[-166.41,64.87],[-166.69,64.99],[-166.64,65.11],[-166.46,65.18],[-166.52,65.34],[-166.8,65.34],[-167.48,65.41],[-168.07,65.58],[-168.11,65.68],[-167.54,65.82],[-166.33,66.19],[-166.05,66.11],[-165.76,66.09],[-165.69,66.2],[-165.87,66.22],[-165.88,66.31],[-165.19,66.47],[-164.4,66.58],[-163.98,66.59],[-163.75,66.55],[-163.87,66.39],[-163.83,66.27],[-163.92,66.19],[-163.77,66.06],[-162.75,66.09],[-162.63,66.04],[-162.37,66.03],[-162.14,66.08],[-161.84,66.02],[-161.55,66.24],[-161.34,66.25],[-161.2,66.21],[-161.13,66.33],[-161.53,66.4],[-161.91,66.35],[-161.87,66.51],[-162.17,66.69],[-162.5,66.74],[-162.6,66.9],[-162.34,66.94],[-162.02,66.78],[-162.08,66.65],[-161.92,66.55],[-161.57,66.44],[-161.49,66.56],[-161.88,66.72],[-161.71,67.0],[-161.85,67.05],[-162.24,66.99],[-162.64,67.01],[-162.7,67.06],[-162.9,67.01],[-163.74,67.13],[-163.76,67.25],[-164.01,67.53],[-164.21,67.64],[-165.49,68.06],[-165.79,68.08],[-166.24,68.25],[-166.68,68.34],[-166.7,68.37],[-166.38,68.42],[-166.23,68.57],[-166.22,68.88],[-165.33,68.86],[-164.26,68.93],[-163.98,68.99],[-163.53,69.14],[-163.11,69.37],[-163.02,69.61],[-162.84,69.81],[-162.47,69.98],[-162.31,70.11],[-161.85,70.31],[-161.78,70.26],[-161.4,70.24],[-160.84,70.34],[-160.49,70.45],[-159.65,70.79],[-159.33,70.81],[-159.3,70.76],[-158.98,70.8],[-158.66,70.79],[-158.03,70.83],[-157.42,70.98],[-156.81,71.29],[-156.57,71.35],[-156.52,71.3],[-155.59,71.17],[-155.51,71.08],[-155.83,70.97],[-155.98,70.96],[-155.97,70.81],[-155.5,70.86],[-155.48,70.94],[-155.26,71.02],[-155.19,70.97],[-155.03,71.15],[-154.57,70.99],[-154.64,70.87],[-154.35,70.84],[-154.18,70.77],[-153.93,70.88],[-153.49,70.89],[-153.24,70.92],[-152.26,70.84],[-152.42,70.61],[-151.82,70.55],[-151.77,70.49],[-151.19,70.38],[-151.18,70.43],[-150.76,70.5],[-150.36,70.49],[-150.35,70.44],[-150.11,70.43],[-149.87,70.51],[-149.46,70.52],[-148.79,70.4],[-148.61,70.42],[-148.35,70.31],[-148.2,70.35],[-147.96,70.32],[-147.79,70.25]]],[[[-152.94,58.03],[-152.95,57.98],[-153.29,58.05],[-153.04,58.31],[-152.82,58.33],[-152.67,58.56],[-152.5,58.35],[-152.35,58.43],[-152.08,58.31],[-152.08,58.15],[-152.48,58.13],[-152.66,58.06],[-152.94,58.03]]],[[[-153.96,57.54],[-153.67,57.67],[-153.93,57.7],[-153.94,57.81],[-153.72,57.89],[-153.57,57.83],[-153.55,57.72],[-153.46,57.8],[-153.46,57.97],[-153.27,57.89],[-153.24,58.0],[-153.07,57.93],[-152.87,57.93],[-152.72,57.99],[-152.47,57.89],[-152.47,57.6],[-152.15,57.62],[-152.36,57.43],[-152.74,57.51],[-152.6,57.38],[-152.71,57.28],[-152.91,57.33],[-152.91,57.13],[-153.21,57.07],[-153.31,56.99],[-153.5,57.07],[-153.7,56.86],[-153.85,56.84],[-154.01,56.74],[-154.07,56.97],[-154.3,56.85],[-154.31,56.92],[-154.52,56.99],[-154.54,57.19],[-154.74,57.28],[-154.63,57.51],[-154.23,57.66],[-153.98,57.65],[-153.96,57.54]]],[[[-154.53,56.6],[-154.74,56.4],[-154.81,56.43],[-154.53,56.6]]],[[[-155.63,55.92],[-155.48,55.91],[-155.53,55.7],[-155.79,55.73],[-155.84,55.8],[-155.63,55.92]]],[[[-159.89,55.28],[-159.95,55.07],[-160.26,54.89],[-160.11,55.16],[-160.01,55.13],[-159.89,55.28]]],[[[-160.52,55.36],[-160.33,55.36],[-160.34,55.25],[-160.53,55.13],[-160.69,55.21],[-160.79,55.13],[-160.85,55.32],[-160.8,55.38],[-160.52,55.36]]],[[[-162.26,54.98],[-162.23,54.89],[-162.35,54.84],[-162.44,54.93],[-162.26,54.98]]],[[[-162.42,63.63],[-162.56,63.54],[-162.61,63.62],[-162.42,63.63]]],[[[-162.8,54.49],[-162.59,54.45],[-162.61,54.37],[-162.78,54.37],[-162.8,54.49]]],[[[-165.55,54.3],[-165.48,54.18],[-165.63,54.13],[-165.69,54.25],[-165.55,54.3]]],[[[-165.74,54.15],[-166.05,54.04],[-166.11,54.12],[-165.98,54.22],[-165.74,54.15]]],[[[-166.36,60.36],[-166.13,60.4],[-166.08,60.33],[-165.88,60.34],[-165.69,60.28],[-165.65,59.99],[-165.75,59.9],[-166.01,59.84],[-166.06,59.75],[-166.44,59.86],[-166.62,59.85],[-166.99,59.99],[-167.13,59.99],[-167.34,60.07],[-167.42,60.21],[-167.31,60.24],[-166.94,60.21],[-166.76,60.31],[-166.58,60.32],[-166.5,60.39],[-166.36,60.36]]],[[[-166.38,54.01],[-166.21,53.93],[-166.54,53.75],[-166.54,53.72],[-166.12,53.85],[-166.11,53.78],[-166.28,53.68],[-166.56,53.62],[-166.58,53.53],[-166.88,53.43],[-167.14,53.43],[-167.31,53.33],[-167.62,53.25],[-167.79,53.34],[-167.46,53.44],[-167.36,53.43],[-167.1,53.51],[-167.16,53.61],[-167.02,53.72],[-166.81,53.67],[-166.79,53.73],[-167.02,53.75],[-167.14,53.83],[-167.03,53.95],[-166.64,54.02],[-166.56,53.88],[-166.38,54.01]]],[[[-168.79,53.16],[-168.41,53.35],[-168.39,53.43],[-168.24,53.52],[-168.01,53.57],[-167.89,53.52],[-167.84,53.39],[-168.27,53.24],[-168.5,53.04],[-168.69,52.97],[-168.79,53.16]]],[[[-169.75,52.89],[-169.71,52.8],[-169.96,52.79],[-169.99,52.86],[-169.75,52.89]]],[[[-170.15,57.22],[-170.29,57.13],[-170.31,57.22],[-170.15,57.22]]],[[[-170.67,52.7],[-170.6,52.6],[-170.79,52.54],[-170.82,52.64],[-170.67,52.7]]],[[[-171.74,63.72],[-170.95,63.57],[-170.49,63.69],[-170.28,63.68],[-170.09,63.61],[-170.04,63.49],[-169.64,63.43],[-169.52,63.37],[-168.69,63.3],[-168.86,63.15],[-169.11,63.18],[-169.38,63.15],[-169.51,63.09],[-169.64,62.94],[-169.83,63.08],[-170.06,63.17],[-170.26,63.18],[-170.36,63.28],[-170.87,63.42],[-171.1,63.42],[-171.46,63.31],[-171.74,63.37],[-171.85,63.49],[-171.74,63.72]]],[[[-172.43,52.39],[-172.42,52.28],[-172.61,52.25],[-172.57,52.35],[-172.43,52.39]]],[[[-173.63,52.15],[-173.5,52.11],[-173.12,52.11],[-173.11,52.08],[-173.55,52.03],[-173.63,52.15]]],[[[-174.32,52.28],[-174.33,52.38],[-174.19,52.42],[-173.98,52.32],[-174.06,52.23],[-174.18,52.23],[-174.14,52.13],[-174.33,52.12],[-174.74,52.01],[-174.97,52.04],[-174.9,52.12],[-174.66,52.11],[-174.32,52.28]]],[[[-176.47,51.85],[-176.29,51.87],[-176.29,51.74],[-176.52,51.76],[-176.8,51.61],[-176.91,51.81],[-176.79,51.82],[-176.78,51.96],[-176.63,51.97],[-176.63,51.86],[-176.47,51.85]]],[[[-177.15,51.95],[-177.04,51.9],[-177.12,51.73],[-177.27,51.68],[-177.28,51.78],[-177.15,51.95]]],[[[-178.12,51.92],[-177.95,51.91],[-177.8,51.79],[-177.96,51.65],[-178.12,51.92]]],[[[-187.11,52.99],[-187.29,52.93],[-187.3,52.82],[-188.9,52.76],[-188.64,52.93],[-188.64,53.0],[-187.11,52.99]]]]}},{"type":"Feature","id":"04","properties":{"name":"Arizona"},"geometry":{"type":"Polygon","coordinates":[[[-109.04,37.0],[-109.05,31.33],[-111.07,31.33],[-114.82,32.49],[-114.72,32.72],[-114.52,32.76],[-114.47,32.84],[-114.52,33.03],[-114.66,33.03],[-114.73,33.41],[-114.52,33.55],[-114.5,33.7],[-114.54,33.93],[-114.42,34.11],[-114.26,34.17],[-114.14,34.31],[-114.33,34.45],[-114.47,34.71],[-114.63,34.88],[-114.63,35.0],[-114.57,35.14],[-114.6,35.32],[-114.68,35.52],[-114.74,36.1],[-114.37,36.14],[-114.25,36.02],[-114.15,36.03],[-114.05,36.2],[-114.05,37.0],[-109.04,37.0]]]}},{"type":"Feature","id":"05","properties":{"name":"Arkansas"},"geometry":{"type":"Polygon","coordinates":[[[-94.47,36.5],[-90.15,36.5],[-90.06,36.3],[-90.22,36.18],[-90.38,36.0],[-89.73,36.0],[-89.76,35.81],[-89.91,35.76],[-89.94,35.6],[-90.13,35.44],[-90.11,35.2],[-90.21,35.02],[-90.31,35.0],[-90.25,34.91],[-90.41,34.83],[-90.48,34.66],[-90.59,34.62],[-90.57,34.42],[-90.75,34.37],[-90.74,34.3],[-90.95,34.14],[-90.89,34.03],[-91.07,33.87],[-91.23,33.56],[-91.06,33.43],[-91.14,33.35],[-91.09,33.14],[-91.17,33.0],[-94.04,33.02],[-94.04,33.55],[-94.18,33.59],[-94.38,33.54],[-94.48,33.64],[-94.43,35.4],[-94.62,36.5],[-94.47,36.5]]]}},{"type":"Feature","id":"06","properties":{"name":"California"},"geometry":{"type":"Polygon","coordinates":[[[-123.23,42.01],[-120.0,42.0],[-120.0,39.0],[-117.5,37.22],[-115.85,35.97],[-114.63,35.0],[-114.63,34.88],[-114.47,34.71],[-114.33,34.45],[-114.14,34.31],[-114.26,34.17],[-114.42,34.11],[-114.54,33.93],[-114.5,33.7],[-114.52,33.55],[-114.73,33.41],[-114.66,33.03],[-114.52,33.03],[-114.47,32.84],[-114.52,32.76],[-114.72,32.72],[-117.13,32.54],[-117.25,32.67],[-117.25,32.88],[-117.33,33.12],[-117.47,33.3],[-117.78,33.54],[-118.18,33.76],[-118.26,33.7],[-118.41,33.74],[-118.39,33.84],[-118.57,34.04],[-118.8,34.0],[-119.22,34.15],[-119.28,34.27],[-119.56,34.42],[-119.88,34.41],[-120.14,34.48],[-120.47,34.45],[-120.65,34.58],[-120.61,34.86],[-120.67,34.9],[-120.63,35.1],[-120.89,35.25],[-120.91,35.45],[-121.0,35.46],[-121.17,35.64],[-121.28,35.67],[-121.33,35.78],[-121.72,36.2],[-121.9,36.32],[-121.94,36.64],[-121.86,36.61],[-121.79,36.8],[-121.93,36.98],[-122.11,36.96],[-122.34,37.12],[-122.42,37.24],[-122.4,37.36],[-122.52,37.52],[-122.52,37.78],[-122.33,37.78],[-122.41,38.15],[-122.49,38.11],[-122.5,37.93],[-122.7,37.89],[-122.94,38.03],[-122.98,38.27],[-123.13,38.45],[-123.33,38.57],[-123.74,38.96],[-123.69,39.03],[-123.82,39.37],[-123.76,39.55],[-123.85,39.83],[-124.11,40.11],[-124.36,40.26],[-124.41,40.44],[-124.16,40.88],[-124.11,41.03],[-124.16,41.14],[-124.07,41.44],[-124.15,41.72],[-124.26,41.78],[-124.21,42.0],[-123.23,42.01]]]}},{"type":"Feature","id":"08","properties":{"name":"Colorado"},"geometry":{"type":"Polygon","coordinates":[[[-107.92,41.0],[-102.05,41.0],[-102.04,36.99],[-109.04,37.0],[-109.05,41.0],[-107.92,41.0]]]}},{"type":"Feature","id":"09","properties":{"name":"Connecticut"},"geometry":{"type":"Polygon","coordinates":[[[-73.05,42.04],[-71.8,42.02],[-71.8,41.41],[-71.86,41.32],[-71.95,41.34],[-72.39,41.26],[-72.91,41.28],[-73.13,41.15],[-73.37,41.1],[-73.66,40.99],[-73.73,41.1],[-73.48,41.21],[-73.55,41.29],[-73.49,42.05],[-73.05,42.04]]]}},{"type":"Feature","id":"10","properties":{"name":"Delaware"},"geometry":{"type":"Polygon","coordinates":[[[-75.41,39.8],[-75.51,39.68],[-75.61,39.62],[-75.59,39.46],[-75.44,39.31],[-75.4,39.07],[-75.19,38.81],[-75.09,38.8],[-75.05,38.45],[-75.69,38.46],[-75.79,39.72],[-75.62,39.83],[-75.41,39.8]]]}},{"type":"Feature","id":"11","properties":{"name":"District of Columbia"},"geometry":{"type":"Polygon","coordinates":[[[-77.04,38.99],[-76.91,38.9],[-77.04,38.79],[-77.12,38.93],[-77.04,38.99]]]}},{"type":"Feature","id":"12","properties":{"name":"Florida"},"geometry":{"type":"Polygon","coordinates":[[[-85.5,31.0],[-85.0,31.0],[-84.87,30.71],[-82.22,30.57],[-82.17,30.36],[-82.05,30.36],[-82.0,30.56],[-82.04,30.75],[-81.95,30.83],[-81.72,30.75],[-81.44,30.71],[-81.38,30.27],[-81.26,29.79],[-80.97,29.15],[-80.52,28.46],[-80.59,28.41],[-80.57,28.09],[-80.38,27.74],[-80.09,27.02],[-80.03,26.8],[-80.15,25.74],[-80.24,25.72],[-80.34,25.47],[-80.3,25.38],[-80.5,25.2],[-80.57,25.24],[-80.76,25.16],[-81.08,25.12],[-81.17,25.22],[-81.13,25.38],[-81.35,25.82],[-81.53,25.9],[-81.68,25.84],[-81.8,26.09],[-81.83,26.29],[-82.04,26.52],[-82.09,26.67],[-82.06,26.88],[-82.17,26.92],[-82.15,26.79],[-82.25,26.76],[-82.57,27.3],[-82.69,27.44],[-82.39,27.84],[-82.59,27.82],[-82.72,27.69],[-82.85,27.89],[-82.68,28.43],[-82.64,28.89],[-82.76,29.0],[-82.8,29.15],[-82.99,29.18],[-83.22,29.42],[-83.4,29.52],[-83.41,29.67],[-83.54,29.72],[-83.64,29.89],[-84.02,30.1],[-84.36,30.06],[-84.34,29.9],[-84.45,29.93],[-84.87,29.74],[-85.31,29.7],[-85.3,29.81],[-85.4,29.94],[-85.92,30.24],[-86.3,30.36],[-86.63,30.4],[-87.52,30.28],[-87.37,30.43],[-87.45,30.51],[-87.41,30.67],[-87.63,30.87],[-87.6,31.0],[-85.5,31.0]]]}},{"type":"Feature","id":"13","properties":{"name":"Georgia"},"geometry":{"type":"Polygon","coordinates":[[[-83.11,35.0],[-83.32,34.79],[-83.34,34.68],[-83.01,34.47],[-82.9,34.49],[-82.75,34.27],[-82.71,34.15],[-82.56,33.94],[-82.33,33.82],[-82.19,33.63],[-81.93,33.46],[-81.94,33.35],[-81.76,33.16],[-81.49,33.01],[-81.43,32.84],[-81.42,32.63],[-81.28,32.56],[-81.12,32.29],[-81.12,32.12],[-80.89,32.03],[-81.13,31.69],[-81.18,31.52],[-81.28,31.36],[-81.29,31.21],[-81.4,31.13],[-81.44,30.71],[-81.72,30.75],[-81.95,30.83],[-82.04,30.75],[-82.0,30.56],[-82.05,30.36],[-82.17,30.36],[-82.22,30.57],[-84.87,30.71],[-85.11,31.28],[-85.04,31.54],[-85.14,31.84],[-85.05,32.01],[-85.06,32.14],[-84.89,32.26],[-85.0,32.32],[-84.96,32.42],[-85.07,32.58],[-85.18,32.86],[-85.61,34.98],[-83.11,35.0]]]}},{"type":"Feature","id":"15","properties":{"name":"Hawaii"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-155.63,18.95],[-155.88,19.04],[-155.92,19.12],[-155.89,19.35],[-156.06,19.73],[-155.93,19.86],[-155.83,20.03],[-155.9,20.15],[-155.88,20.27],[-155.6,20.13],[-155.28,20.02],[-155.09,19.87],[-155.09,19.74],[-154.81,19.52],[-154.98,19.35],[-155.3,19.27],[-155.51,19.13],[-155.63,18.95]]],[[[-156.59,21.03],[-156.47,20.89],[-156.32,20.95],[-156.0,20.79],[-156.05,20.65],[-156.38,20.58],[-156.45,20.61],[-156.46,20.78],[-156.63,20.82],[-156.7,20.92],[-156.59,21.03]]],[[[-156.98,21.21],[-157.08,21.11],[-157.31,21.11],[-157.24,21.22],[-156.98,21.21]]],[[[-157.95,21.7],[-157.84,21.46],[-157.9,21.33],[-158.11,21.3],[-158.25,21.58],[-158.13,21.59],[-157.95,21.7]]],[[[-159.47,22.23],[-159.35,22.22],[-159.3,22.11],[-159.33,21.97],[-159.45,21.87],[-159.76,21.99],[-159.73,22.15],[-159.47,22.23]]]]}},{"type":"Feature","id":"16","properties":{"name":"Idaho"},"geometry":{"type":"Polygon","coordinates":[[[-116.05,49.0],[-116.05,47.98],[-115.72,47.7],[-115.72,47.42],[-115.53,47.3],[-115.32,47.26],[-115.3,47.19],[-114.93,46.92],[-114.89,46.81],[-114.62,46.71],[-114.61,46.64],[-114.32,46.65],[-114.46,46.27],[-114.49,46.04],[-114.39,45.88],[-114.57,45.77],[-114.5,45.67],[-114.55,45.56],[-114.33,45.46],[-114.09,45.59],[-113.99,45.7],[-113.81,45.6],[-113.83,45.52],[-113.74,45.33],[-113.57,45.13],[-113.45,45.06],[-113.46,44.87],[-113.34,44.78],[-113.13,44.77],[-113.0,44.45],[-112.89,44.39],[-112.78,44.49],[-112.47,44.48],[-112.24,44.57],[-112.1,44.52],[-111.87,44.56],[-111.82,44.51],[-111.62,44.55],[-111.39,44.76],[-111.23,44.58],[-111.05,44.48],[-111.05,42.0],[-117.03,42.0],[-117.03,43.83],[-116.9,44.16],[-116.98,44.24],[-117.17,44.26],[-117.24,44.39],[-117.04,44.75],[-116.93,44.78],[-116.83,44.93],[-116.85,45.02],[-116.73,45.14],[-116.67,45.32],[-116.46,45.62],[-116.55,45.75],[-116.78,45.82],[-116.92,45.99],[-116.92,46.17],[-117.06,46.34],[-117.03,49.0],[-116.05,49.0]]]}},{"type":"Feature","id":"17","properties":{"name":"Illinois"},"geometry":{"type":"Polygon","coordinates":[[[-90.64,42.51],[-87.8,42.49],[-87.84,42.3],[-87.68,42.08],[-87.52,41.71],[-87.53,39.35],[-87.64,39.17],[-87.51,38.96],[-87.5,38.78],[-87.62,38.64],[-87.66,38.51],[-87.84,38.29],[-87.95,38.28],[-87.92,38.15],[-88.0,38.1],[-88.06,37.87],[-88.03,37.8],[-88.16,37.66],[-88.07,37.48],[-88.48,37.39],[-88.51,37.29],[-88.42,37.15],[-88.55,37.07],[-88.91,37.22],[-89.03,37.21],[-89.18,37.04],[-89.13,36.98],[-89.29,36.99],[-89.52,37.28],[-89.44,37.35],[-89.52,37.54],[-89.52,37.69],[-89.84,37.9],[-89.95,37.88],[-90.06,38.01],[-90.36,38.22],[-90.35,38.37],[-90.18,38.63],[-90.21,38.73],[-90.11,38.85],[-90.25,38.92],[-90.47,38.96],[-90.59,38.87],[-90.66,38.93],[-90.73,39.26],[-91.06,39.47],[-91.37,39.73],[-91.49,40.03],[-91.51,40.24],[-91.42,40.38],[-91.4,40.56],[-91.12,40.67],[-91.09,40.82],[-90.96,40.92],[-90.95,41.1],[-91.11,41.24],[-91.05,41.41],[-90.66,41.46],[-90.34,41.59],[-90.31,41.74],[-90.18,41.81],[-90.14,42.0],[-90.17,42.13],[-90.39,42.23],[-90.42,42.33],[-90.64,42.51]]]}},{"type":"Feature","id":"18","properties":{"name":"Indiana"},"geometry":{"type":"Polygon","coordinates":[[[-85.99,41.76],[-84.81,41.76],[-84.82,39.1],[-84.89,39.06],[-84.81,38.79],[-84.99,38.78],[-85.17,38.69],[-85.43,38.73],[-85.42,38.53],[-85.59,38.45],[-85.66,38.33],[-85.83,38.28],[-85.92,38.02],[-86.04,37.96],[-86.26,38.05],[-86.3,38.17],[-86.52,38.04],[-86.5,37.93],[-86.73,37.89],[-86.8,37.99],[-87.05,37.89],[-87.13,37.79],[-87.38,37.94],[-87.51,37.9],[-87.6,37.98],[-87.68,37.9],[-87.93,37.89],[-88.03,37.8],[-88.06,37.87],[-88.0,38.1],[-87.92,38.15],[-87.95,38.28],[-87.84,38.29],[-87.66,38.51],[-87.62,38.64],[-87.5,38.78],[-87.51,38.96],[-87.64,39.17],[-87.53,39.35],[-87.52,41.71],[-87.43,41.64],[-87.12,41.64],[-86.82,41.76],[-85.99,41.76]]]}},{"type":"Feature","id":"19","properties":{"name":"Iowa"},"geometry":{"type":"Polygon","coordinates":[[[-91.37,43.5],[-91.22,43.5],[-91.2,43.35],[-91.06,43.25],[-91.18,43.13],[-91.14,42.91],[-91.07,42.75],[-90.71,42.64],[-90.64,42.51],[-90.42,42.33],[-90.39,42.23],[-90.17,42.13],[-90.14,42.0],[-90.18,41.81],[-90.31,41.74],[-90.34,41.59],[-90.66,41.46],[-91.05,41.41],[-91.11,41.24],[-90.95,41.1],[-90.96,40.92],[-91.09,40.82],[-91.12,40.67],[-91.4,40.56],[-91.42,40.38],[-91.53,40.41],[-91.73,40.62],[-94.63,40.57],[-95.77,40.59],[-95.88,40.72],[-95.83,40.98],[-95.93,41.2],[-95.92,41.45],[-96.1,41.54],[-96.12,41.68],[-96.06,41.8],[-96.13,41.97],[-96.26,42.04],[-96.45,42.49],[-96.63,42.71],[-96.54,42.86],[-96.51,43.05],[-96.43,43.12],[-96.56,43.22],[-96.53,43.4],[-96.58,43.48],[-96.45,43.5],[-91.37,43.5]]]}},{"type":"Feature","id":"20","properties":{"name":"Kansas"},"geometry":{"type":"Polygon","coordinates":[[[-101.91,40.0],[-95.31,40.0],[-95.21,39.91],[-94.88,39.83],[-95.11,39.54],[-94.98,39.44],[-94.82,39.21],[-94.61,39.16],[-94.62,37.0],[-102.04,36.99],[-102.05,40.0],[-101.91,40.0]]]}},{"type":"Feature","id":"21","properties":{"name":"Kentucky"},"geometry":{"type":"Polygon","coordinates":[[[-83.9,38.77],[-83.68,38.63],[-83.52,38.7],[-83.14,38.63],[-83.03,38.73],[-82.89,38.76],[-82.85,38.59],[-82.73,38.56],[-82.59,38.42],[-82.62,38.12],[-82.5,37.93],[-82.34,37.78],[-82.29,37.67],[-82.1,37.55],[-81.97,37.54],[-82.35,37.27],[-82.72,37.12],[-82.72,37.04],[-82.87,36.98],[-82.88,36.89],[-83.07,36.85],[-83.14,36.74],[-83.69,36.58],[-86.59,36.66],[-87.85,36.63],[-88.07,36.68],[-88.05,36.5],[-89.42,36.5],[-89.36,36.62],[-89.22,36.58],[-89.13,36.98],[-89.18,37.04],[-89.03,37.21],[-88.91,37.22],[-88.55,37.07],[-88.42,37.15],[-88.51,37.29],[-88.48,37.39],[-88.07,37.48],[-88.16,37.66],[-87.93,37.89],[-87.68,37.9],[-87.6,37.98],[-87.51,37.9],[-87.38,37.94],[-87.13,37.79],[-87.05,37.89],[-86.8,37.99],[-86.73,37.89],[-86.5,37.93],[-86.52,38.04],[-86.3,38.17],[-86.26,38.05],[-86.04,37.96],[-85.92,38.02],[-85.83,38.28],[-85.66,38.33],[-85.59,38.45],[-85.42,38.53],[-85.43,38.73],[-85.17,38.69],[-84.99,38.78],[-84.81,38.79],[-84.89,39.06],[-84.82,39.1],[-84.43,39.1],[-84.23,38.9],[-84.22,38.81],[-83.9,38.77]]]}},{"type":"Feature","id":"22","properties":{"name":"Louisiana"},"geometry":{"type":"Polygon","coordinates":[[[-93.61,33.02],[-91.17,33.0],[-91.07,32.89],[-91.14,32.84],[-91.15,32.64],[-91.01,32.51],[-90.99,32.22],[-91.11,31.99],[-91.34,31.85],[-91.4,31.62],[-91.5,31.64],[-91.52,31.28],[-91.64,31.27],[-91.57,31.07],[-91.64,31.0],[-89.75,31.0],[-89.85,30.67],[-89.68,30.45],[-89.64,30.29],[-89.52,30.18],[-89.82,30.04],[-89.84,29.95],[-89.6,29.88],[-89.5,30.04],[-89.29,29.88],[-89.3,29.75],[-89.42,29.7],[-89.65,29.75],[-89.62,29.66],[-89.7,29.51],[-89.51,29.39],[-89.2,29.35],[-89.09,29.2],[-89.0,29.18],[-89.16,29.01],[-89.34,29.04],[-89.48,29.22],[-89.85,29.31],[-89.85,29.48],[-90.03,29.43],[-90.02,29.28],[-90.1,29.15],[-90.23,29.13],[-90.33,29.28],[-90.56,29.28],[-90.65,29.13],[-90.8,29.09],[-90.96,29.18],[-91.09,29.19],[-91.22,29.44],[-91.45,29.55],[-91.53,29.53],[-91.62,29.74],[-91.88,29.71],[-91.89,29.84],[-92.15,29.72],[-92.11,29.62],[-92.31,29.54],[-92.62,29.58],[-92.97,29.72],[-93.23,29.78],[-93.77,29.73],[-93.84,29.69],[-93.93,29.79],[-93.69,30.14],[-93.77,30.33],[-93.7,30.44],[-93.73,30.58],[-93.63,30.68],[-93.53,30.94],[-93.54,31.15],[-93.82,31.56],[-93.82,31.78],[-94.04,31.99],[-94.04,33.02],[-93.61,33.02]]]}},{"type":"Feature","id":"23","properties":{"name":"Maine"},"geometry":{"type":"Polygon","coordinates":[[[-70.7,43.06],[-70.82,43.13],[-70.81,43.23],[-70.97,43.34],[-71.08,45.3],[-70.65,45.44],[-70.72,45.51],[-70.56,45.66],[-70.39,45.74],[-70.42,45.8],[-70.26,45.89],[-70.31,46.06],[-70.21,46.33],[-70.06,46.42],[-70.0,46.69],[-69.23,47.46],[-69.04,47.43],[-69.03,47.24],[-68.9,47.18],[-68.58,47.29],[-68.38,47.29],[-68.23,47.36],[-67.95,47.2],[-67.79,47.07],[-67.8,45.68],[-67.46,45.6],[-67.51,45.49],[-67.42,45.38],[-67.49,45.28],[-67.35,45.13],[-67.16,45.16],[-66.98,44.8],[-67.19,44.65],[-67.31,44.71],[-67.41,44.6],[-67.55,44.62],[-67.57,44.53],[-67.75,44.54],[-68.05,44.33],[-68.12,44.48],[-68.22,44.49],[-68.17,44.33],[-68.4,44.25],[-68.46,44.38],[-68.57,44.31],[-68.83,44.31],[-68.83,44.46],[-68.98,44.43],[-68.96,44.32],[-69.1,44.1],[-69.07,44.04],[-69.26,43.92],[-69.44,43.97],[-69.55,43.84],[-69.71,43.82],[-69.83,43.72],[-69.99,43.74],[-70.03,43.85],[-70.25,43.68],[-70.19,43.57],[-70.36,43.53],[-70.37,43.44],[-70.56,43.32],[-70.7,43.06]]]}},{"type":"Feature","id":"24","properties":{"name":"Maryland"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-75.99,37.95],[-76.04,37.95],[-75.99,37.95]]],[[[-79.48,39.72],[-75.79,39.72],[-75.69,38.46],[-75.05,38.45],[-75.24,38.03],[-75.89,37.91],[-75.88,38.07],[-75.96,38.14],[-75.85,38.21],[-76.0,38.37],[-76.05,38.3],[-76.26,38.32],[-76.33,38.5],[-76.26,38.5],[-76.26,38.74],[-76.19,38.83],[-76.28,39.15],[-76.17,39.33],[-76.0,39.37],[-75.97,39.56],[-76.1,39.54],[-76.1,39.44],[-76.37,39.31],[-76.44,39.2],[-76.46,38.91],[-76.56,38.77],[-76.51,38.54],[-76.38,38.38],[-76.4,38.26],[-76.32,38.14],[-76.36,38.06],[-76.59,38.22],[-76.92,38.29],[-77.02,38.45],[-77.21,38.36],[-77.28,38.48],[-77.13,38.63],[-77.04,38.79],[-76.91,38.9],[-77.04,38.99],[-77.12,38.93],[-77.25,39.03],[-77.46,39.08],[-77.46,39.22],[-77.57,39.31],[-77.72,39.32],[-77.83,39.6],[-78.0,39.6],[-78.17,39.69],[-78.27,39.62],[-78.43,39.62],[-78.47,39.51],[-78.77,39.59],[-78.96,39.44],[-79.09,39.47],[-79.29,39.3],[-79.49,39.21],[-79.48,39.72]]]]}},{"type":"Feature","id":"25","properties":{"name":"Massachusetts"},"geometry":{"type":"Polygon","coordinates":[[[-70.92,42.89],[-70.82,42.87],[-70.78,42.7],[-70.82,42.55],[-70.98,42.42],[-70.99,42.27],[-70.77,42.25],[-70.64,42.09],[-70.66,41.96],[-70.55,41.93],[-70.54,41.81],[-70.26,41.72],[-69.94,41.81],[-70.01,41.67],[-70.48,41.55],[-70.66,41.55],[-70.76,41.64],[-70.93,41.61],[-70.93,41.54],[-71.12,41.5],[-71.2,41.68],[-71.33,41.78],[-71.38,42.02],[-73.49,42.05],[-73.51,42.09],[-73.27,42.75],[-71.3,42.7],[-71.19,42.79],[-70.92,42.89]]]}},{"type":"Feature","id":"26","properties":{"name":"Michigan"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-83.45,41.73],[-84.81,41.69],[-84.81,41.76],[-86.82,41.76],[-86.62,41.89],[-86.48,42.12],[-86.36,42.25],[-86.26,42.44],[-86.21,42.72],[-86.23,43.01],[-86.53,43.59],[-86.43,43.81],[-86.5,44.08],[-86.27,44.34],[-86.22,44.57],[-86.25,44.69],[-86.09,44.74],[-86.07,44.9],[-85.81,44.95],[-85.61,45.13],[-85.63,44.77],[-85.52,44.75],[-85.39,44.93],[-85.39,45.24],[-85.31,45.31],[-85.03,45.36],[-85.12,45.58],[-84.94,45.76],[-84.71,45.77],[-84.46,45.65],[-84.22,45.64],[-84.1,45.49],[-83.91,45.48],[-83.6,45.35],[-83.49,45.36],[-83.32,45.14],[-83.45,45.03],[-83.32,44.88],[-83.27,44.71],[-83.33,44.34],[-83.54,44.25],[-83.59,44.05],[-83.83,43.99],[-83.96,43.76],[-83.91,43.67],[-83.67,43.59],[-83.48,43.71],[-83.26,43.97],[-82.92,44.07],[-82.75,43.99],[-82.64,43.85],[-82.54,43.44],[-82.52,43.23],[-82.41,42.98],[-82.52,42.61],[-82.68,42.56],[-82.69,42.69],[-82.8,42.65],[-82.92,42.35],[-83.13,42.24],[-83.19,42.01],[-83.44,41.81],[-83.45,41.73]]],[[[-85.51,45.73],[-85.49,45.61],[-85.62,45.59],[-85.57,45.76],[-85.51,45.73]]],[[[-87.59,45.1],[-87.74,45.2],[-87.65,45.34],[-87.89,45.36],[-87.79,45.5],[-87.78,45.68],[-87.99,45.8],[-88.1,45.92],[-88.53,46.02],[-88.66,45.99],[-89.09,46.14],[-90.12,46.34],[-90.23,46.51],[-90.42,46.57],[-90.03,46.67],[-89.85,46.79],[-89.41,46.84],[-89.13,46.99],[-89.0,47.0],[-88.89,47.1],[-88.58,47.25],[-88.42,47.37],[-88.18,47.46],[-87.96,47.38],[-88.44,46.97],[-88.44,46.79],[-88.25,46.93],[-87.9,46.91],[-87.63,46.81],[-87.39,46.54],[-87.26,46.49],[-87.01,46.53],[-86.95,46.47],[-86.7,46.44],[-86.16,46.67],[-85.51,46.68],[-85.26,46.75],[-85.06,46.76],[-85.03,46.48],[-84.83,46.44],[-84.63,46.49],[-84.55,46.42],[-84.42,46.5],[-84.13,46.53],[-84.12,46.18],[-83.99,46.03],[-83.79,45.99],[-83.77,46.09],[-83.58,46.09],[-83.48,45.99],[-83.56,45.91],[-84.11,45.98],[-84.37,45.93],[-84.66,46.05],[-84.74,45.94],[-84.7,45.85],[-84.83,45.87],[-85.02,46.01],[-85.34,46.09],[-85.5,46.1],[-85.66,45.97],[-85.92,45.93],[-86.21,45.96],[-86.32,45.91],[-86.35,45.8],[-86.66,45.7],[-86.65,45.83],[-86.78,45.86],[-86.84,45.73],[-87.07,45.72],[-87.17,45.66],[-87.33,45.42],[-87.61,45.12],[-87.59,45.1]]],[[[-88.81,47.98],[-89.06,47.85],[-89.19,47.83],[-89.18,47.94],[-88.55,48.17],[-88.67,48.01],[-88.81,47.98]]]]}},{"type":"Feature","id":"27","properties":{"name":"Minnesota"},"geometry":{"type":"Polygon","coordinates":[[[-92.01,46.71],[-92.09,46.75],[-92.29,46.67],[-92.29,46.08],[-92.35,46.02],[-92.64,45.93],[-92.87,45.72],[-92.89,45.58],[-92.77,45.57],[-92.64,45.44],[-92.76,45.29],[-92.74,45.12],[-92.81,44.75],[-92.55,44.57],[-92.34,44.55],[-92.23,44.44],[-91.93,44.33],[-91.88,44.2],[-91.59,44.03],[-91.43,43.99],[-91.24,43.78],[-91.27,43.62],[-91.22,43.5],[-96.45,43.5],[-96.45,45.3],[-96.68,45.41],[-96.86,45.6],[-96.58,45.82],[-96.56,45.93],[-96.6,46.33],[-96.72,46.44],[-96.8,46.66],[-96.79,46.92],[-96.82,46.97],[-96.86,47.61],[-97.13,48.14],[-97.16,48.55],[-97.1,48.68],[-97.23,49.0],[-95.15,49.0],[-95.15,49.38],[-94.96,49.37],[-94.82,49.3],[-94.69,48.78],[-94.59,48.72],[-94.26,48.7],[-94.22,48.65],[-93.84,48.63],[-93.79,48.52],[-93.47,48.55],[-93.47,48.59],[-93.21,48.64],[-92.98,48.62],[-92.73,48.54],[-92.66,48.44],[-92.51,48.45],[-92.37,48.22],[-92.3,48.32],[-92.05,48.36],[-92.01,48.27],[-91.71,48.2],[-91.71,48.11],[-91.57,48.04],[-91.26,48.08],[-91.08,48.18],[-90.84,48.24],[-90.75,48.09],[-90.58,48.12],[-90.38,48.09],[-90.14,48.11],[-89.87,47.99],[-89.62,48.01],[-89.64,47.95],[-89.97,47.83],[-90.44,47.73],[-90.74,47.63],[-91.17,47.37],[-91.64,47.03],[-92.09,46.79],[-92.01,46.71]]]}},{"type":"Feature","id":"28","properties":{"name":"Mississippi"},"geometry":{"type":"Polygon","coordinates":[[[-88.47,35.0],[-88.2,35.0],[-88.1,34.89],[-88.47,31.9],[-88.39,30.37],[-88.5,30.32],[-88.74,30.35],[-88.84,30.41],[-89.08,30.37],[-89.42,30.25],[-89.52,30.18],[-89.64,30.29],[-89.68,30.45],[-89.85,30.67],[-89.75,31.0],[-91.64,31.0],[-91.57,31.07],[-91.64,31.27],[-91.52,31.28],[-91.5,31.64],[-91.4,31.62],[-91.34,31.85],[-91.11,31.99],[-90.99,32.22],[-91.01,32.51],[-91.15,32.64],[-91.14,32.84],[-91.07,32.89],[-91.17,33.0],[-91.09,33.14],[-91.14,33.35],[-91.06,33.43],[-91.23,33.56],[-91.07,33.87],[-90.89,34.03],[-90.95,34.14],[-90.74,34.3],[-90.75,34.37],[-90.57,34.42],[-90.59,34.62],[-90.48,34.66],[-90.41,34.83],[-90.25,34.91],[-90.31,35.0],[-88.47,35.0]]]}},{"type":"Feature","id":"29","properties":{"name":"Missouri"},"geometry":{"type":"Polygon","coordinates":[[[-91.83,40.61],[-91.73,40.62],[-91.53,40.41],[-91.42,40.38],[-91.51,40.24],[-91.49,40.03],[-91.37,39.73],[-91.06,39.47],[-90.73,39.26],[-90.66,38.93],[-90.59,38.87],[-90.47,38.96],[-90.25,38.92],[-90.11,38.85],[-90.21,38.73],[-90.18,38.63],[-90.35,38.37],[-90.36,38.22],[-90.06,38.01],[-89.95,37.88],[-89.84,37.9],[-89.52,37.69],[-89.52,37.54],[-89.44,37.35],[-89.52,37.28],[-89.29,36.99],[-89.13,36.98],[-89.22,36.58],[-89.36,36.62],[-89.42,36.5],[-89.54,36.5],[-89.53,36.25],[-89.73,36.0],[-90.38,36.0],[-90.22,36.18],[-90.06,36.3],[-90.15,36.5],[-94.62,36.5],[-94.61,39.16],[-94.82,39.21],[-94.98,39.44],[-95.11,39.54],[-94.88,39.83],[-95.21,39.91],[-95.55,40.26],[-95.77,40.59],[-94.63,40.57],[-91.83,40.61]]]}},{"type":"Feature","id":"30","properties":{"name":"Montana"},"geometry":{"type":"Polygon","coordinates":[[[-104.05,49.0],[-104.04,45.0],[-111.05,45.0],[-111.05,44.48],[-111.23,44.58],[-111.39,44.76],[-111.62,44.55],[-111.82,44.51],[-111.87,44.56],[-112.1,44.52],[-112.24,44.57],[-112.47,44.48],[-112.78,44.49],[-112.89,44.39],[-113.0,44.45],[-113.13,44.77],[-113.34,44.78],[-113.46,44.87],[-113.45,45.06],[-113.57,45.13],[-113.74,45.33],[-113.83,45.52],[-113.81,45.6],[-113.99,45.7],[-114.09,45.59],[-114.33,45.46],[-114.55,45.56],[-114.5,45.67],[-114.57,45.77],[-114.39,45.88],[-114.49,46.04],[-114.46,46.27],[-114.32,46.65],[-114.61,46.64],[-114.62,46.71],[-114.89,46.81],[-114.93,46.92],[-115.3,47.19],[-115.32,47.26],[-115.53,47.3],[-115.72,47.42],[-115.72,47.7],[-116.05,47.98],[-116.05,49.0],[-104.05,49.0]]]}},{"type":"Feature","id":"31","properties":{"name":"Nebraska"},"geometry":{"type":"Polygon","coordinates":[[[-103.32,43.0],[-98.5,43.0],[-98.47,42.95],[-97.95,42.77],[-97.83,42.87],[-97.22,42.84],[-96.69,42.66],[-96.63,42.52],[-96.45,42.49],[-96.26,42.04],[-96.13,41.97],[-96.06,41.8],[-96.12,41.68],[-96.1,41.54],[-95.92,41.45],[-95.93,41.2],[-95.83,40.98],[-95.88,40.72],[-95.55,40.26],[-95.31,40.0],[-102.05,40.0],[-102.05,41.0],[-104.05,41.0],[-104.05,43.0],[-103.32,43.0]]]}},{"type":"Feature","id":"32","properties":{"name":"Nevada"},"geometry":{"type":"Polygon","coordinates":[[[-117.03,42.0],[-114.04,42.0],[-114.05,36.2],[-114.15,36.03],[-114.25,36.02],[-114.37,36.14],[-114.74,36.1],[-114.68,35.52],[-114.6,35.32],[-114.57,35.14],[-114.63,35.0],[-115.85,35.97],[-117.5,37.22],[-120.0,39.0],[-120.0,42.0],[-117.03,42.0]]]}},{"type":"Feature","id":"33","properties":{"name":"New Hampshire"},"geometry":{"type":"Polygon","coordinates":[[[-71.08,45.3],[-70.97,43.34],[-70.81,43.23],[-70.82,43.13],[-70.7,43.06],[-70.82,42.87],[-70.92,42.89],[-71.19,42.79],[-71.3,42.7],[-72.46,42.73],[-72.54,42.81],[-72.53,42.95],[-72.45,43.01],[-72.46,43.15],[-72.38,43.57],[-72.2,43.77],[-72.12,43.99],[-72.03,44.08],[-72.03,44.32],[-71.7,44.42],[-71.54,44.59],[-71.63,44.75],[-71.49,44.91],[-71.5,45.01],[-71.36,45.27],[-71.13,45.24],[-71.08,45.3]]]}},{"type":"Feature","id":"34","properties":{"name":"New Jersey"},"geometry":{"type":"Polygon","coordinates":[[[-74.24,41.14],[-73.9,41.0],[-74.02,40.71],[-74.19,40.64],[-74.27,40.49],[-74.0,40.41],[-73.98,40.3],[-74.1,39.76],[-74.41,39.36],[-74.61,39.25],[-74.8,38.99],[-74.89,39.16],[-75.18,39.24],[-75.53,39.46],[-75.56,39.63],[-75.41,39.8],[-75.15,39.89],[-75.13,39.96],[-74.82,40.13],[-74.77,40.22],[-75.06,40.42],[-75.07,40.54],[-75.2,40.58],[-75.21,40.69],[-75.05,40.87],[-75.13,40.97],[-74.88,41.18],[-74.83,41.29],[-74.7,41.36],[-74.24,41.14]]]}},{"type":"Feature","id":"35","properties":{"name":"New Mexico"},"geometry":{"type":"Polygon","coordinates":[[[-107.42,37.0],[-103.0,37.0],[-103.0,36.5],[-103.04,36.5],[-103.07,32.0],[-106.62,32.0],[-106.64,31.9],[-106.53,31.79],[-108.21,31.79],[-108.21,31.33],[-109.05,31.33],[-109.04,37.0],[-107.42,37.0]]]}},{"type":"Feature","id":"36","properties":{"name":"New York"},"geometry":{"type":"Polygon","coordinates":[[[-73.34,45.01],[-73.33,44.8],[-73.39,44.62],[-73.29,44.44],[-73.32,44.25],[-73.44,44.04],[-73.35,43.77],[-73.4,43.69],[-73.25,43.52],[-73.27,42.75],[-73.51,42.09],[-73.49,42.05],[-73.55,41.29],[-73.48,41.21],[-73.73,41.1],[-73.66,40.99],[-73.23,40.91],[-73.14,40.97],[-72.77,40.97],[-72.59,41.0],[-72.28,41.16],[-72.26,41.04],[-72.1,40.99],[-72.47,40.85],[-73.24,40.63],[-73.56,40.58],[-73.78,40.59],[-73.94,40.54],[-74.02,40.71],[-73.9,41.0],[-74.7,41.36],[-74.74,41.43],[-74.89,41.44],[-75.07,41.61],[-75.05,41.75],[-75.17,41.87],[-75.25,41.86],[-75.36,42.0],[-79.76,42.0],[-79.76,42.27],[-79.15,42.55],[-79.05,42.69],[-78.85,42.78],[-78.93,42.95],[-79.01,42.99],[-79.07,43.26],[-78.49,43.38],[-77.76,43.34],[-77.53,43.23],[-77.39,43.28],[-76.96,43.27],[-76.7,43.34],[-76.42,43.52],[-76.24,43.53],[-76.23,43.8],[-76.14,43.96],[-76.36,44.07],[-76.31,44.2],[-75.91,44.37],[-75.76,44.51],[-75.28,44.85],[-74.83,45.02],[-74.15,44.99],[-73.34,45.01]]]}},{"type":"Feature","id":"37","properties":{"name":"North Carolina"},"geometry":{"type":"Polygon","coordinates":[[[-80.98,36.56],[-75.87,36.55],[-75.75,36.15],[-76.03,36.19],[-76.07,36.14],[-76.41,36.08],[-76.46,36.03],[-76.68,36.01],[-76.67,35.94],[-76.4,35.99],[-76.36,35.94],[-76.06,35.99],[-75.96,35.9],[-75.78,35.94],[-75.72,35.7],[-75.78,35.58],[-75.9,35.57],[-76.15,35.32],[-76.48,35.31],[-76.54,35.14],[-76.39,34.97],[-76.28,34.94],[-76.49,34.66],[-76.67,34.69],[-76.99,34.67],[-77.21,34.61],[-77.56,34.42],[-77.83,34.16],[-77.97,33.85],[-78.18,33.92],[-78.54,33.85],[-79.68,34.8],[-80.8,34.82],[-80.78,34.94],[-80.93,35.11],[-81.04,35.04],[-81.04,35.15],[-82.28,35.2],[-82.55,35.16],[-82.76,35.07],[-83.11,35.0],[-84.32,34.99],[-84.29,35.23],[-84.1,35.25],[-84.02,35.41],[-83.77,35.56],[-83.5,35.57],[-83.25,35.72],[-82.99,35.77],[-82.78,36.0],[-82.64,36.06],[-82.61,35.97],[-82.22,36.16],[-82.04,36.12],[-81.91,36.3],[-81.72,36.35],[-81.68,36.59],[-80.98,36.56]]]}},{"type":"Feature","id":"38","properties":{"name":"North Dakota"},"geometry":{"type":"Polygon","coordinates":[[[-97.23,49.0],[-97.1,48.68],[-97.16,48.55],[-97.13,48.14],[-96.86,47.61],[-96.82,46.97],[-96.79,46.92],[-96.8,46.66],[-96.72,46.44],[-96.6,46.33],[-96.56,45.93],[-104.05,45.94],[-104.05,49.0],[-97.23,49.0]]]}},{"type":"Feature","id":"39","properties":{"name":"Ohio"},"geometry":{"type":"Polygon","coordinates":[[[-80.52,41.98],[-80.52,40.64],[-80.67,40.58],[-80.6,40.47],[-80.6,40.32],[-80.74,40.08],[-80.83,39.71],[-81.22,39.39],[-81.35,39.34],[-81.46,39.41],[-81.57,39.27],[-81.69,39.27],[-81.81,39.08],[-81.78,38.97],[-81.89,38.87],[-82.04,39.03],[-82.22,38.79],[-82.17,38.63],[-82.29,38.58],[-82.33,38.45],[-82.59,38.42],[-82.73,38.56],[-82.85,38.59],[-82.89,38.76],[-83.03,38.73],[-83.14,38.63],[-83.52,38.7],[-83.68,38.63],[-83.9,38.77],[-84.22,38.81],[-84.23,38.9],[-84.43,39.1],[-84.82,39.1],[-84.81,41.69],[-83.45,41.73],[-83.07,41.6],[-82.93,41.51],[-82.84,41.59],[-82.62,41.43],[-82.48,41.38],[-82.01,41.51],[-81.74,41.49],[-81.44,41.67],[-81.01,41.85],[-80.52,41.98]]]}},{"type":"Feature","id":"40","properties":{"name":"Oklahoma"},"geometry":{"type":"Polygon","coordinates":[[[-100.09,37.0],[-94.62,37.0],[-94.62,36.5],[-94.43,35.4],[-94.48,33.64],[-94.87,33.75],[-94.97,33.86],[-95.22,33.96],[-95.29,33.87],[-95.55,33.88],[-95.6,33.93],[-95.84,33.83],[-95.94,33.89],[-96.15,33.84],[-96.35,33.69],[-96.42,33.77],[-96.63,33.85],[-96.85,33.85],[-96.92,33.96],[-97.17,33.74],[-97.26,33.86],[-97.37,33.82],[-97.46,33.91],[-97.69,33.98],[-97.87,33.85],[-97.95,33.99],[-98.09,34.0],[-98.17,34.11],[-98.36,34.16],[-98.49,34.06],[-98.57,34.15],[-98.77,34.14],[-98.99,34.22],[-99.19,34.21],[-99.26,34.4],[-99.58,34.42],[-99.7,34.38],[-99.92,34.57],[-100.0,34.56],[-100.0,36.5],[-103.0,36.5],[-103.0,37.0],[-100.09,37.0]]]}},{"type":"Feature","id":"41","properties":{"name":"Oregon"},"geometry":{"type":"Polygon","coordinates":[[[-123.21,46.17],[-123.12,46.19],[-122.9,46.08],[-122.81,45.96],[-122.76,45.66],[-122.25,45.55],[-121.81,45.71],[-121.54,45.73],[-121.22,45.67],[-121.18,45.6],[-120.64,45.75],[-120.51,45.7],[-120.21,45.73],[-119.96,45.82],[-119.53,45.91],[-119.13,45.93],[-118.99,46.0],[-116.92,45.99],[-116.78,45.82],[-116.55,45.75],[-116.46,45.62],[-116.67,45.32],[-116.73,45.14],[-116.85,45.02],[-116.83,44.93],[-116.93,44.78],[-117.04,44.75],[-117.24,44.39],[-117.17,44.26],[-116.98,44.24],[-116.9,44.16],[-117.03,43.83],[-117.03,42.0],[-124.21,42.0],[-124.36,42.12],[-124.43,42.44],[-124.42,42.66],[-124.55,42.84],[-124.45,43.0],[-124.38,43.27],[-124.24,43.56],[-124.17,43.81],[-124.06,44.66],[-124.08,44.77],[-123.98,45.14],[-123.94,45.66],[-123.99,45.94],[-123.95,46.11],[-123.55,46.26],[-123.37,46.15],[-123.21,46.17]]]}},{"type":"Feature","id":"42","properties":{"name":"Pennsylvania"},"geometry":{"type":"Polygon","coordinates":[[[-79.76,42.25],[-79.76,42.0],[-75.36,42.0],[-75.25,41.86],[-75.17,41.87],[-75.05,41.75],[-75.07,41.61],[-74.89,41.44],[-74.74,41.43],[-74.7,41.36],[-74.83,41.29],[-74.88,41.18],[-75.13,40.97],[-75.05,40.87],[-75.21,40.69],[-75.2,40.58],[-75.07,40.54],[-75.06,40.42],[-74.77,40.22],[-74.82,40.13],[-75.13,39.96],[-75.15,39.89],[-75.41,39.8],[-75.62,39.83],[-75.79,39.72],[-80.52,39.72],[-80.52,41.98],[-79.76,42.25]]]}},{"type":"Feature","id":"44","properties":{"name":"Rhode Island"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-71.2,41.68],[-71.12,41.5],[-71.32,41.47],[-71.2,41.68]]],[[[-71.53,42.02],[-71.38,42.02],[-71.33,41.78],[-71.22,41.71],[-71.34,41.73],[-71.45,41.58],[-71.48,41.37],[-71.86,41.32],[-71.8,41.41],[-71.8,42.01],[-71.53,42.02]]]]}},{"type":"Feature","id":"45","properties":{"name":"South Carolina"},"geometry":{"type":"Polygon","coordinates":[[[-82.76,35.07],[-82.55,35.16],[-82.28,35.2],[-81.04,35.15],[-81.04,35.04],[-80.93,35.11],[-80.78,34.94],[-80.8,34.82],[-79.68,34.8],[-78.54,33.85],[-78.72,33.8],[-78.94,33.64],[-79.15,33.38],[-79.19,33.17],[-79.36,33.01],[-79.58,33.01],[-79.63,32.89],[-79.87,32.76],[-80.0,32.61],[-80.21,32.55],[-80.43,32.4],[-80.45,32.33],[-80.66,32.25],[-80.89,32.03],[-81.12,32.12],[-81.12,32.29],[-81.28,32.56],[-81.42,32.63],[-81.43,32.84],[-81.49,33.01],[-81.76,33.16],[-81.94,33.35],[-81.93,33.46],[-82.19,33.63],[-82.33,33.82],[-82.56,33.94],[-82.71,34.15],[-82.75,34.27],[-82.9,34.49],[-83.01,34.47],[-83.34,34.68],[-83.32,34.79],[-83.11,35.0],[-82.76,35.07]]]}},{"type":"Feature","id":"46","properties":{"name":"South Dakota"},"geometry":{"type":"Polygon","coordinates":[[[-104.05,45.94],[-96.56,45.93],[-96.58,45.82],[-96.86,45.6],[-96.68,45.41],[-96.45,45.3],[-96.45,43.5],[-96.58,43.48],[-96.53,43.4],[-96.56,43.22],[-96.43,43.12],[-96.51,43.05],[-96.54,42.86],[-96.63,42.71],[-96.45,42.49],[-96.63,42.52],[-96.69,42.66],[-97.22,42.84],[-97.83,42.87],[-97.95,42.77],[-98.47,42.95],[-98.5,43.0],[-104.05,43.0],[-104.05,45.94]]]}},{"type":"Feature","id":"47","properties":{"name":"Tennessee"},"geometry":{"type":"Polygon","coordinates":[[[-88.05,36.5],[-88.07,36.68],[-87.85,36.63],[-86.59,36.66],[-84.54,36.59],[-81.68,36.59],[-81.72,36.35],[-81.91,36.3],[-82.04,36.12],[-82.22,36.16],[-82.61,35.97],[-82.64,36.06],[-82.78,36.0],[-82.99,35.77],[-83.25,35.72],[-83.5,35.57],[-83.77,35.56],[-84.02,35.41],[-84.1,35.25],[-84.29,35.23],[-84.32,34.99],[-90.31,35.0],[-90.21,35.02],[-90.11,35.2],[-90.13,35.44],[-89.94,35.6],[-89.91,35.76],[-89.76,35.81],[-89.73,36.0],[-89.53,36.25],[-89.54,36.5],[-88.05,36.5]]]}},{"type":"Feature","id":"48","properties":{"name":"Texas"},"geometry":{"type":"Polygon","coordinates":[[[-101.81,36.5],[-100.0,36.5],[-100.0,34.56],[-99.92,34.57],[-99.7,34.38],[-99.58,34.42],[-99.26,34.4],[-99.19,34.21],[-98.99,34.22],[-98.77,34.14],[-98.57,34.15],[-98.49,34.06],[-98.36,34.16],[-98.17,34.11],[-98.09,34.0],[-97.95,33.99],[-97.87,33.85],[-97.69,33.98],[-97.46,33.91],[-97.37,33.82],[-97.26,33.86],[-97.17,33.74],[-96.92,33.96],[-96.85,33.85],[-96.63,33.85],[-96.42,33.77],[-96.35,33.69],[-96.15,33.84],[-95.94,33.89],[-95.84,33.83],[-95.6,33.93],[-95.55,33.88],[-95.29,33.87],[-95.22,33.96],[-94.97,33.86],[-94.87,33.75],[-94.48,33.64],[-94.38,33.54],[-94.18,33.59],[-94.04,33.55],[-94.04,31.99],[-93.82,31.78],[-93.82,31.56],[-93.54,31.15],[-93.53,30.94],[-93.63,30.68],[-93.73,30.58],[-93.7,30.44],[-93.77,30.33],[-93.69,30.14],[-93.93,29.79],[-93.84,29.69],[-94.0,29.68],[-94.52,29.55],[-94.71,29.62],[-94.74,29.79],[-94.87,29.67],[-94.97,29.7],[-95.02,29.56],[-94.91,29.5],[-94.9,29.31],[-95.08,29.11],[-95.38,28.87],[-95.99,28.6],[-96.05,28.65],[-96.23,28.58],[-96.23,28.64],[-96.48,28.6],[-96.59,28.72],[-96.66,28.7],[-96.4,28.44],[-96.59,28.36],[-96.77,28.41],[-96.8,28.23],[-97.03,28.04],[-97.26,27.69],[-97.4,27.33],[-97.51,27.36],[-97.54,27.23],[-97.43,27.26],[-97.48,27.0],[-97.56,26.99],[-97.56,26.84],[-97.47,26.76],[-97.44,26.46],[-97.33,26.35],[-97.31,26.16],[-97.22,25.99],[-97.52,25.89],[-97.65,26.02],[-97.89,26.07],[-98.2,26.06],[-98.47,26.22],[-98.67,26.24],[-98.82,26.37],[-99.03,26.41],[-99.17,26.54],[-99.27,26.84],[-99.45,27.02],[-99.42,27.17],[-99.51,27.34],[-99.48,27.48],[-99.61,27.64],[-99.71,27.66],[-99.88,27.8],[-99.93,27.98],[-100.08,28.14],[-100.3,28.28],[-100.4,28.58],[-100.5,28.66],[-100.63,28.91],[-100.67,29.1],[-100.8,29.24],[-101.01,29.37],[-101.06,29.46],[-101.26,29.54],[-101.41,29.75],[-101.85,29.8],[-102.11,29.79],[-102.34,29.87],[-102.39,29.77],[-102.63,29.73],[-102.81,29.52],[-102.92,29.19],[-102.98,29.18],[-103.12,28.99],[-103.28,28.98],[-103.53,29.14],[-104.15,29.38],[-104.27,29.51],[-104.51,29.64],[-104.68,29.92],[-104.69,30.18],[-104.86,30.39],[-104.9,30.57],[-105.01,30.69],[-105.39,30.86],[-105.6,31.09],[-105.77,31.17],[-105.95,31.36],[-106.21,31.47],[-106.38,31.73],[-106.53,31.79],[-106.64,31.9],[-106.62,32.0],[-103.07,32.0],[-103.04,36.5],[-101.81,36.5]]]}},{"type":"Feature","id":"49","properties":{"name":"Utah"},"geometry":{"type":"Polygon","coordinates":[[[-112.16,42.0],[-111.05,42.0],[-111.05,41.0],[-109.05,41.0],[-109.04,37.0],[-114.05,37.0],[-114.04,42.0],[-112.16,42.0]]]}},{"type":"Feature","id":"50","properties":{"name":"Vermont"},"geometry":{"type":"Polygon","coordinates":[[[-71.5,45.01],[-71.49,44.91],[-71.63,44.75],[-71.54,44.59],[-71.7,44.42],[-72.03,44.32],[-72.03,44.08],[-72.12,43.99],[-72.2,43.77],[-72.38,43.57],[-72.46,43.15],[-72.45,43.01],[-72.53,42.95],[-72.54,42.81],[-72.46,42.73],[-73.27,42.75],[-73.25,43.52],[-73.4,43.69],[-73.35,43.77],[-73.44,44.04],[-73.32,44.25],[-73.29,44.44],[-73.39,44.62],[-73.33,44.8],[-73.34,45.01],[-71.5,45.01]]]}},{"type":"Feature","id":"51","properties":{"name":"Virginia"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-75.4,38.01],[-75.24,38.03],[-75.38,37.86],[-75.51,37.8],[-75.59,37.57],[-75.8,37.2],[-75.97,37.12],[-76.03,37.26],[-75.94,37.56],[-75.67,37.95],[-75.4,38.01]]],[[[-76.02,37.95],[-75.99,37.95],[-76.04,37.95],[-76.02,37.95]]],[[[-78.35,39.46],[-77.83,39.13],[-77.72,39.32],[-77.57,39.31],[-77.46,39.22],[-77.46,39.08],[-77.25,39.03],[-77.12,38.93],[-77.04,38.79],[-77.13,38.63],[-77.25,38.59],[-77.33,38.45],[-77.28,38.34],[-77.01,38.37],[-76.96,38.22],[-76.61,38.15],[-76.51,38.02],[-76.24,37.89],[-76.36,37.61],[-76.25,37.39],[-76.38,37.29],[-76.4,37.16],[-76.27,37.08],[-76.41,36.96],[-76.62,37.12],[-76.67,37.07],[-76.49,36.95],[-75.99,36.92],[-75.87,36.55],[-79.51,36.54],[-83.67,36.6],[-83.14,36.74],[-83.07,36.85],[-82.88,36.89],[-82.87,36.98],[-82.72,37.04],[-82.72,37.12],[-82.35,37.27],[-81.97,37.54],[-81.99,37.45],[-81.85,37.29],[-81.68,37.2],[-81.55,37.21],[-81.36,37.34],[-81.23,37.24],[-80.97,37.29],[-80.51,37.48],[-80.47,37.42],[-80.3,37.51],[-80.29,37.69],[-80.18,37.85],[-80.0,38.0],[-79.92,38.18],[-79.72,38.36],[-79.65,38.59],[-79.48,38.46],[-79.31,38.41],[-79.21,38.5],[-79.0,38.85],[-78.87,38.76],[-78.4,39.17],[-78.35,39.46]]]]}},{"type":"Feature","id":"53","properties":{"name":"Washington"},"geometry":{"type":"MultiPolygon","coordinates":[[[[-117.03,49.0],[-117.06,46.34],[-116.92,46.17],[-116.92,45.99],[-118.99,46.0],[-119.13,45.93],[-119.53,45.91],[-119.96,45.82],[-120.21,45.73],[-120.51,45.7],[-120.64,45.75],[-121.18,45.6],[-121.22,45.67],[-121.54,45.73],[-121.81,45.71],[-122.25,45.55],[-122.76,45.66],[-122.81,45.96],[-122.9,46.08],[-123.12,46.19],[-123.37,46.15],[-123.55,46.26],[-123.73,46.3],[-123.87,46.24],[-124.07,46.33],[-124.03,46.46],[-123.9,46.54],[-124.1,46.74],[-124.24,47.29],[-124.32,47.36],[-124.43,47.74],[-124.62,47.89],[-124.71,48.18],[-124.6,48.38],[-123.98,48.16],[-123.7,48.17],[-123.42,48.12],[-123.16,48.17],[-123.04,48.08],[-122.8,48.09],[-122.64,47.87],[-122.52,47.88],[-122.49,47.59],[-122.42,47.32],[-122.32,47.35],[-122.42,47.58],[-122.4,47.8],[-122.23,48.03],[-122.36,48.12],[-122.37,48.29],[-122.47,48.47],[-122.42,48.6],[-122.49,48.75],[-122.65,48.78],[-122.8,48.89],[-122.76,49.0],[-117.03,49.0]]],[[[-122.72,48.31],[-122.59,48.35],[-122.61,48.15],[-122.77,48.23],[-122.72,48.31]]],[[[-123.03,48.58],[-122.92,48.72],[-122.77,48.56],[-122.81,48.42],[-123.04,48.46],[-123.03,48.58]]]]}},{"type":"Feature","id":"54","properties":{"name":"West Virginia"},"geometry":{"type":"Polygon","coordinates":[[[-80.52,40.64],[-80.52,39.72],[-79.48,39.72],[-79.49,39.21],[-79.29,39.3],[-79.09,39.47],[-78.96,39.44],[-78.77,39.59],[-78.47,39.51],[-78.43,39.62],[-78.27,39.62],[-78.17,39.69],[-78.0,39.6],[-77.83,39.6],[-77.72,39.32],[-77.83,39.13],[-78.35,39.46],[-78.4,39.17],[-78.87,38.76],[-79.0,38.85],[-79.21,38.5],[-79.31,38.41],[-79.48,38.46],[-79.65,38.59],[-79.72,38.36],[-79.92,38.18],[-80.0,38.0],[-80.18,37.85],[-80.29,37.69],[-80.3,37.51],[-80.47,37.42],[-80.51,37.48],[-80.97,37.29],[-81.23,37.24],[-81.36,37.34],[-81.55,37.21],[-81.68,37.2],[-81.85,37.29],[-81.99,37.45],[-81.97,37.54],[-82.1,37.55],[-82.29,37.67],[-82.34,37.78],[-82.5,37.93],[-82.62,38.12],[-82.59,38.42],[-82.33,38.45],[-82.29,38.58],[-82.17,38.63],[-82.22,38.79],[-82.04,39.03],[-81.89,38.87],[-81.78,38.97],[-81.81,39.08],[-81.69,39.27],[-81.57,39.27],[-81.46,39.41],[-81.35,39.34],[-81.22,39.39],[-80.83,39.71],[-80.74,40.08],[-80.6,40.32],[-80.6,40.47],[-80.67,40.58],[-80.52,40.64]]]}},{"type":"Feature","id":"55","properties":{"name":"Wisconsin"},"geometry":{"type":"Polygon","coordinates":[[[-90.42,46.57],[-90.23,46.51],[-90.12,46.34],[-89.09,46.14],[-88.66,45.99],[-88.53,46.02],[-88.1,45.92],[-87.99,45.8],[-87.78,45.68],[-87.79,45.5],[-87.89,45.36],[-87.65,45.34],[-87.74,45.2],[-87.59,45.1],[-87.63,44.97],[-87.82,44.95],[-87.98,44.72],[-88.04,44.56],[-87.93,44.54],[-87.78,44.64],[-87.61,44.84],[-87.4,44.91],[-87.24,45.17],[-87.03,45.22],[-87.05,45.09],[-87.19,44.97],[-87.47,44.55],[-87.55,44.32],[-87.54,44.16],[-87.64,44.1],[-87.74,43.88],[-87.7,43.69],[-87.79,43.56],[-87.91,43.25],[-87.89,43.0],[-87.76,42.78],[-87.8,42.49],[-90.64,42.51],[-90.71,42.64],[-91.07,42.75],[-91.14,42.91],[-91.18,43.13],[-91.06,43.25],[-91.2,43.35],[-91.22,43.5],[-91.27,43.62],[-91.24,43.78],[-91.43,43.99],[-91.59,44.03],[-91.88,44.2],[-91.93,44.33],[-92.23,44.44],[-92.34,44.55],[-92.55,44.57],[-92.81,44.75],[-92.74,45.12],[-92.76,45.29],[-92.64,45.44],[-92.77,45.57],[-92.89,45.58],[-92.87,45.72],[-92.64,45.93],[-92.35,46.02],[-92.29,46.08],[-92.29,46.67],[-92.09,46.75],[-92.01,46.71],[-91.79,46.69],[-91.09,46.86],[-90.84,46.96],[-90.75,46.89],[-90.89,46.75],[-90.56,46.58],[-90.42,46.57]]]}},{"type":"Feature","id":"56","properties":{"name":"Wyoming"},"geometry":{"type":"Polygon","coordinates":[[[-109.08,45.0],[-104.06,45.0],[-104.05,41.0],[-111.05,41.0],[-111.05,45.0],[-109.08,45.0]]]}},{"type":"Feature","id":"72","properties":{"name":"Puerto Rico"},"geometry":{"type":"Polygon","coordinates":[[[-66.45,17.98],[-66.77,18.01],[-66.92,17.93],[-66.99,17.97],[-67.21,17.96],[-67.15,18.19],[-67.27,18.36],[-67.09,18.52],[-65.84,18.43],[-65.63,18.37],[-65.63,18.2],[-65.73,18.19],[-65.83,18.02],[-66.23,17.93],[-66.45,17.98]]]}}]}
+83
src/api.rs
··· 1 + // Amtrak API client - fetch and decrypt train data 2 + 3 + use anyhow::{Context, Result}; 4 + use serde::Deserialize; 5 + 6 + use crate::crypto::{self, DecryptionKeys}; 7 + use crate::model::{self, Train}; 8 + 9 + const TRAINS_URL: &str = "https://maps.amtrak.com/services/MapDataService/trains/getTrainsData"; 10 + const ROUTES_URL: &str = "https://maps.amtrak.com/rttl/js/RoutesList.json"; 11 + const ROUTES_V_URL: &str = "https://maps.amtrak.com/rttl/js/RoutesList.v.json"; 12 + 13 + #[derive(Deserialize)] 14 + struct RouteEntry { 15 + #[serde(default)] 16 + #[serde(alias = "ZoomLevel")] 17 + zoom_level: Option<u32>, 18 + } 19 + 20 + #[derive(Deserialize)] 21 + struct RoutesVConfig { 22 + arr: Vec<String>, 23 + s: Vec<String>, 24 + v: Vec<String>, 25 + } 26 + 27 + pub async fn fetch_decryption_keys(client: &reqwest::Client) -> Result<DecryptionKeys> { 28 + // Fetch route list to calculate masterZoom 29 + let routes: Vec<RouteEntry> = client 30 + .get(ROUTES_URL) 31 + .send() 32 + .await 33 + .context("failed to fetch routes list")? 34 + .json() 35 + .await 36 + .context("failed to parse routes list")?; 37 + 38 + let master_zoom = routes 39 + .iter() 40 + .map(|r| r.zoom_level.unwrap_or(0)) 41 + .sum::<u32>() as usize; 42 + 43 + // Fetch keys config 44 + let config: RoutesVConfig = client 45 + .get(ROUTES_V_URL) 46 + .send() 47 + .await 48 + .context("failed to fetch routes config")? 49 + .json() 50 + .await 51 + .context("failed to parse routes config")?; 52 + 53 + let public_key = config 54 + .arr 55 + .get(master_zoom) 56 + .context("masterZoom index out of bounds")? 57 + .clone(); 58 + 59 + let salt = crypto::extract_salt(&config.s); 60 + let iv = crypto::extract_iv(&config.v); 61 + 62 + Ok(DecryptionKeys { 63 + public_key, 64 + salt, 65 + iv, 66 + }) 67 + } 68 + 69 + pub async fn fetch_trains(client: &reqwest::Client, keys: &DecryptionKeys) -> Result<Vec<Train>> { 70 + let encrypted = client 71 + .get(TRAINS_URL) 72 + .send() 73 + .await 74 + .context("failed to fetch train data")? 75 + .text() 76 + .await 77 + .context("failed to read train data response")?; 78 + 79 + let json = crypto::decrypt_train_data(encrypted.trim(), keys) 80 + .context("failed to decrypt train data")?; 81 + 82 + model::parse_train_response(&json) 83 + }
+521
src/app.rs
··· 1 + // Application state - viewport, selected train, etc. 2 + 3 + use crate::geo::GeoLayers; 4 + use crate::model::{DelayStatus, Train, TrainState}; 5 + 6 + #[derive(Debug, Clone, PartialEq, Eq)] 7 + pub enum InputMode { 8 + Normal, 9 + Search, 10 + } 11 + 12 + pub struct Viewport { 13 + pub x_min: f64, // west longitude 14 + pub x_max: f64, // east longitude 15 + pub y_min: f64, // south latitude 16 + pub y_max: f64, // north latitude 17 + } 18 + 19 + impl Viewport { 20 + pub fn conus() -> Self { 21 + Self { 22 + x_min: -130.0, 23 + x_max: -60.0, 24 + y_min: 22.0, 25 + y_max: 52.0, 26 + } 27 + } 28 + 29 + pub fn width(&self) -> f64 { 30 + self.x_max - self.x_min 31 + } 32 + 33 + pub fn height(&self) -> f64 { 34 + self.y_max - self.y_min 35 + } 36 + 37 + pub fn center(&self) -> (f64, f64) { 38 + ( 39 + (self.x_min + self.x_max) / 2.0, 40 + (self.y_min + self.y_max) / 2.0, 41 + ) 42 + } 43 + 44 + pub fn zoom_in(&mut self) { 45 + let (cx, cy) = self.center(); 46 + let new_w = self.width() * 0.7; 47 + let new_h = self.height() * 0.7; 48 + self.x_min = cx - new_w / 2.0; 49 + self.x_max = cx + new_w / 2.0; 50 + self.y_min = cy - new_h / 2.0; 51 + self.y_max = cy + new_h / 2.0; 52 + } 53 + 54 + pub fn zoom_out(&mut self) { 55 + let (cx, cy) = self.center(); 56 + let new_w = self.width() / 0.7; 57 + let new_h = self.height() / 0.7; 58 + self.x_min = cx - new_w / 2.0; 59 + self.x_max = cx + new_w / 2.0; 60 + self.y_min = cy - new_h / 2.0; 61 + self.y_max = cy + new_h / 2.0; 62 + } 63 + 64 + pub fn pan(&mut self, dx_frac: f64, dy_frac: f64) { 65 + let dx = self.width() * dx_frac; 66 + let dy = self.height() * dy_frac; 67 + self.x_min += dx; 68 + self.x_max += dx; 69 + self.y_min += dy; 70 + self.y_max += dy; 71 + } 72 + 73 + pub fn contains(&self, lon: f64, lat: f64) -> bool { 74 + lon >= self.x_min && lon <= self.x_max && lat >= self.y_min && lat <= self.y_max 75 + } 76 + 77 + pub fn center_on(&mut self, lon: f64, lat: f64, width: f64, height: f64) { 78 + self.x_min = lon - width / 2.0; 79 + self.x_max = lon + width / 2.0; 80 + self.y_min = lat - height / 2.0; 81 + self.y_max = lat + height / 2.0; 82 + } 83 + } 84 + 85 + pub struct App { 86 + pub viewport: Viewport, 87 + pub trains: Vec<Train>, 88 + pub geo: Option<GeoLayers>, 89 + pub selected_index: Option<usize>, 90 + pub should_quit: bool, 91 + pub last_update: Option<std::time::Instant>, 92 + pub status_message: String, 93 + pub input_mode: InputMode, 94 + pub search_query: String, 95 + } 96 + 97 + impl Default for App { 98 + fn default() -> Self { 99 + Self::new() 100 + } 101 + } 102 + 103 + impl App { 104 + pub fn new() -> Self { 105 + let geo = GeoLayers::load().ok(); 106 + Self { 107 + viewport: Viewport::conus(), 108 + trains: Vec::new(), 109 + geo, 110 + selected_index: None, 111 + should_quit: false, 112 + last_update: None, 113 + status_message: "Loading...".into(), 114 + input_mode: InputMode::Normal, 115 + search_query: String::new(), 116 + } 117 + } 118 + 119 + fn matches_search(&self, train: &Train) -> bool { 120 + if self.search_query.is_empty() { 121 + return true; 122 + } 123 + let q = self.search_query.to_lowercase(); 124 + train.number.to_lowercase().contains(&q) 125 + || train.route_name.to_lowercase().contains(&q) 126 + || train.origin.to_lowercase().contains(&q) 127 + || train.destination.to_lowercase().contains(&q) 128 + } 129 + 130 + pub fn visible_trains(&self) -> Vec<&Train> { 131 + self.trains 132 + .iter() 133 + .filter(|t| t.state == TrainState::Active) 134 + .filter(|t| self.search_query.is_empty() || self.matches_search(t)) 135 + .filter(|t| self.viewport.contains(t.lon, t.lat)) 136 + .collect() 137 + } 138 + 139 + pub fn selected_train(&self) -> Option<&Train> { 140 + let visible = self.visible_trains(); 141 + self.selected_index.and_then(|i| visible.get(i).copied()) 142 + } 143 + 144 + pub fn select_next(&mut self) { 145 + let count = self.visible_trains().len(); 146 + if count == 0 { 147 + self.selected_index = None; 148 + return; 149 + } 150 + self.selected_index = Some(match self.selected_index { 151 + None => 0, 152 + Some(i) => (i + 1) % count, 153 + }); 154 + } 155 + 156 + pub fn select_prev(&mut self) { 157 + let count = self.visible_trains().len(); 158 + if count == 0 { 159 + self.selected_index = None; 160 + return; 161 + } 162 + self.selected_index = Some(match self.selected_index { 163 + None => count - 1, 164 + Some(0) => count - 1, 165 + Some(i) => i - 1, 166 + }); 167 + } 168 + 169 + pub fn active_train_count(&self) -> usize { 170 + self.trains 171 + .iter() 172 + .filter(|t| t.state == TrainState::Active) 173 + .count() 174 + } 175 + 176 + pub fn delayed_train_count(&self) -> usize { 177 + self.trains 178 + .iter() 179 + .filter(|t| t.state == TrainState::Active) 180 + .filter(|t| t.delay_status() == DelayStatus::Late) 181 + .count() 182 + } 183 + 184 + pub fn jump_to_selected(&mut self) { 185 + let visible = self.visible_trains(); 186 + if let Some(train) = self.selected_index.and_then(|i| visible.get(i).copied()) { 187 + let lon = train.lon; 188 + let lat = train.lat; 189 + // Zoom to roughly a 15x10 degree window centered on the train 190 + self.viewport.center_on(lon, lat, 15.0, 10.0); 191 + } 192 + } 193 + 194 + pub fn enter_search(&mut self) { 195 + self.input_mode = InputMode::Search; 196 + self.search_query.clear(); 197 + } 198 + 199 + pub fn exit_search(&mut self) { 200 + self.input_mode = InputMode::Normal; 201 + self.search_query.clear(); 202 + self.selected_index = None; 203 + } 204 + 205 + pub fn search_push(&mut self, c: char) { 206 + self.search_query.push(c); 207 + self.selected_index = None; 208 + } 209 + 210 + pub fn search_pop(&mut self) { 211 + self.search_query.pop(); 212 + self.selected_index = None; 213 + } 214 + 215 + pub fn reset_view(&mut self) { 216 + self.viewport = Viewport::conus(); 217 + self.selected_index = None; 218 + self.search_query.clear(); 219 + self.input_mode = InputMode::Normal; 220 + } 221 + } 222 + 223 + #[cfg(test)] 224 + mod tests { 225 + use super::*; 226 + 227 + fn make_train(num: &str, lon: f64, lat: f64, state: TrainState) -> Train { 228 + make_train_named(num, "Test", lon, lat, state) 229 + } 230 + 231 + fn make_train_named(num: &str, route: &str, lon: f64, lat: f64, state: TrainState) -> Train { 232 + Train { 233 + number: num.into(), 234 + route_name: route.into(), 235 + origin: "A".into(), 236 + destination: "B".into(), 237 + lon, 238 + lat, 239 + velocity: 50.0, 240 + heading: "N".into(), 241 + state, 242 + status_msg: String::new(), 243 + stations: vec![], 244 + } 245 + } 246 + 247 + // --- Viewport --- 248 + 249 + #[test] 250 + fn test_viewport_conus_defaults() { 251 + let vp = Viewport::conus(); 252 + assert!(vp.width() > 60.0); // US is about 70 degrees wide 253 + assert!(vp.height() > 25.0); 254 + let (cx, cy) = vp.center(); 255 + assert!(cx < -80.0 && cx > -110.0); // roughly center of US 256 + assert!(cy > 30.0 && cy < 45.0); 257 + } 258 + 259 + #[test] 260 + fn test_viewport_zoom_in_shrinks_bounds() { 261 + let mut vp = Viewport::conus(); 262 + let old_w = vp.width(); 263 + let old_h = vp.height(); 264 + let old_center = vp.center(); 265 + vp.zoom_in(); 266 + assert!(vp.width() < old_w); 267 + assert!(vp.height() < old_h); 268 + // Center should stay the same 269 + let new_center = vp.center(); 270 + assert!((new_center.0 - old_center.0).abs() < 0.001); 271 + assert!((new_center.1 - old_center.1).abs() < 0.001); 272 + } 273 + 274 + #[test] 275 + fn test_viewport_zoom_out_expands_bounds() { 276 + let mut vp = Viewport::conus(); 277 + let old_w = vp.width(); 278 + vp.zoom_out(); 279 + assert!(vp.width() > old_w); 280 + } 281 + 282 + #[test] 283 + fn test_viewport_pan() { 284 + let mut vp = Viewport::conus(); 285 + let old_center = vp.center(); 286 + vp.pan(0.1, 0.0); // pan right 10% 287 + let new_center = vp.center(); 288 + assert!(new_center.0 > old_center.0); 289 + assert!((new_center.1 - old_center.1).abs() < 0.001); 290 + } 291 + 292 + #[test] 293 + fn test_viewport_contains() { 294 + let vp = Viewport::conus(); 295 + // Chicago should be visible 296 + assert!(vp.contains(-87.6, 41.8)); 297 + // London should not 298 + assert!(!vp.contains(0.0, 51.5)); 299 + } 300 + 301 + // --- App --- 302 + 303 + #[test] 304 + fn test_visible_trains_filters_by_viewport_and_state() { 305 + let mut app = App::new(); 306 + app.trains = vec![ 307 + make_train("1", -87.6, 41.8, TrainState::Active), // Chicago - visible 308 + make_train("2", 0.0, 51.5, TrainState::Active), // London - out of bounds 309 + make_train("3", -90.0, 38.0, TrainState::Completed), // STL completed - filtered 310 + make_train("4", -118.0, 34.0, TrainState::Active), // LA - visible 311 + ]; 312 + 313 + let visible = app.visible_trains(); 314 + assert_eq!(visible.len(), 2); 315 + assert_eq!(visible[0].number, "1"); 316 + assert_eq!(visible[1].number, "4"); 317 + } 318 + 319 + #[test] 320 + fn test_select_next_wraps() { 321 + let mut app = App::new(); 322 + app.trains = vec![ 323 + make_train("1", -87.6, 41.8, TrainState::Active), 324 + make_train("2", -90.0, 38.0, TrainState::Active), 325 + ]; 326 + 327 + assert_eq!(app.selected_index, None); 328 + app.select_next(); 329 + assert_eq!(app.selected_index, Some(0)); 330 + app.select_next(); 331 + assert_eq!(app.selected_index, Some(1)); 332 + app.select_next(); 333 + assert_eq!(app.selected_index, Some(0)); // wraps 334 + } 335 + 336 + #[test] 337 + fn test_select_prev_wraps() { 338 + let mut app = App::new(); 339 + app.trains = vec![ 340 + make_train("1", -87.6, 41.8, TrainState::Active), 341 + make_train("2", -90.0, 38.0, TrainState::Active), 342 + ]; 343 + 344 + app.select_prev(); 345 + assert_eq!(app.selected_index, Some(1)); // starts from end 346 + app.select_prev(); 347 + assert_eq!(app.selected_index, Some(0)); 348 + app.select_prev(); 349 + assert_eq!(app.selected_index, Some(1)); // wraps 350 + } 351 + 352 + #[test] 353 + fn test_train_counts() { 354 + let mut app = App::new(); 355 + app.trains = vec![ 356 + make_train("1", -87.6, 41.8, TrainState::Active), 357 + make_train("2", -90.0, 38.0, TrainState::Active), 358 + make_train("3", -118.0, 34.0, TrainState::Completed), 359 + ]; 360 + assert_eq!(app.active_train_count(), 2); 361 + } 362 + 363 + // --- Jump to train --- 364 + 365 + #[test] 366 + fn test_jump_to_train_centers_viewport() { 367 + let mut app = App::new(); 368 + app.trains = vec![ 369 + make_train("785", -118.6, 34.2, TrainState::Active), // LA area 370 + make_train("316", -90.2, 38.6, TrainState::Active), // St Louis 371 + ]; 372 + app.selected_index = Some(0); 373 + 374 + app.jump_to_selected(); 375 + 376 + let (cx, cy) = app.viewport.center(); 377 + assert!( 378 + (cx - (-118.6)).abs() < 0.1, 379 + "center x should be near -118.6, got {}", 380 + cx 381 + ); 382 + assert!( 383 + (cy - 34.2).abs() < 0.1, 384 + "center y should be near 34.2, got {}", 385 + cy 386 + ); 387 + // Should have zoomed in (smaller than CONUS default of 70 degrees) 388 + assert!( 389 + app.viewport.width() < 30.0, 390 + "should be zoomed in, width={}", 391 + app.viewport.width() 392 + ); 393 + } 394 + 395 + #[test] 396 + fn test_jump_to_train_no_selection_is_noop() { 397 + let mut app = App::new(); 398 + app.trains = vec![make_train("1", -87.6, 41.8, TrainState::Active)]; 399 + let old_center = app.viewport.center(); 400 + app.jump_to_selected(); // no selection 401 + assert_eq!(app.viewport.center(), old_center); 402 + } 403 + 404 + // --- Input mode --- 405 + 406 + #[test] 407 + fn test_input_mode_starts_normal() { 408 + let app = App::new(); 409 + assert_eq!(app.input_mode, InputMode::Normal); 410 + } 411 + 412 + #[test] 413 + fn test_enter_search_mode() { 414 + let mut app = App::new(); 415 + app.enter_search(); 416 + assert_eq!(app.input_mode, InputMode::Search); 417 + assert_eq!(app.search_query, ""); 418 + } 419 + 420 + #[test] 421 + fn test_exit_search_clears_query() { 422 + let mut app = App::new(); 423 + app.enter_search(); 424 + app.search_query = "acela".into(); 425 + app.exit_search(); 426 + assert_eq!(app.input_mode, InputMode::Normal); 427 + assert_eq!(app.search_query, ""); 428 + } 429 + 430 + // --- Search filtering --- 431 + 432 + #[test] 433 + fn test_search_filters_by_train_number() { 434 + let mut app = App::new(); 435 + app.trains = vec![ 436 + make_train("785", -118.6, 34.2, TrainState::Active), 437 + make_train("316", -90.2, 38.6, TrainState::Active), 438 + make_train("2121", -87.6, 41.8, TrainState::Active), 439 + ]; 440 + app.search_query = "78".into(); 441 + 442 + let visible = app.visible_trains(); 443 + assert_eq!(visible.len(), 1); 444 + assert_eq!(visible[0].number, "785"); 445 + } 446 + 447 + #[test] 448 + fn test_search_filters_by_route_name_case_insensitive() { 449 + let mut app = App::new(); 450 + app.trains = vec![ 451 + make_train_named("785", "Pacific Surfliner", -118.6, 34.2, TrainState::Active), 452 + make_train_named( 453 + "316", 454 + "Missouri River Runner", 455 + -90.2, 456 + 38.6, 457 + TrainState::Active, 458 + ), 459 + make_train_named("171", "Acela", -73.9, 40.7, TrainState::Active), 460 + ]; 461 + app.search_query = "acela".into(); 462 + 463 + let visible = app.visible_trains(); 464 + assert_eq!(visible.len(), 1); 465 + assert_eq!(visible[0].number, "171"); 466 + } 467 + 468 + #[test] 469 + fn test_search_filters_by_origin_dest() { 470 + let mut app = App::new(); 471 + app.trains = vec![ 472 + make_train("1", -87.6, 41.8, TrainState::Active), 473 + make_train("2", -90.0, 38.0, TrainState::Active), 474 + ]; 475 + // Both have origin "A", destination "B" 476 + app.search_query = "A".into(); 477 + assert_eq!(app.visible_trains().len(), 2); 478 + 479 + app.search_query = "zzz".into(); 480 + assert_eq!(app.visible_trains().len(), 0); 481 + } 482 + 483 + #[test] 484 + fn test_empty_search_shows_all() { 485 + let mut app = App::new(); 486 + app.trains = vec![ 487 + make_train("1", -87.6, 41.8, TrainState::Active), 488 + make_train("2", -90.0, 38.0, TrainState::Active), 489 + ]; 490 + app.search_query = "".into(); 491 + assert_eq!(app.visible_trains().len(), 2); 492 + } 493 + 494 + #[test] 495 + fn test_selection_resets_when_search_changes() { 496 + let mut app = App::new(); 497 + app.trains = vec![ 498 + make_train("785", -118.6, 34.2, TrainState::Active), 499 + make_train("316", -90.2, 38.6, TrainState::Active), 500 + ]; 501 + app.selected_index = Some(1); 502 + app.search_push('7'); // type a character 503 + assert_eq!( 504 + app.selected_index, None, 505 + "selection should reset on search change" 506 + ); 507 + } 508 + 509 + #[test] 510 + fn test_search_backspace() { 511 + let mut app = App::new(); 512 + app.search_query = "ace".into(); 513 + app.search_pop(); 514 + assert_eq!(app.search_query, "ac"); 515 + app.search_pop(); 516 + app.search_pop(); 517 + assert_eq!(app.search_query, ""); 518 + app.search_pop(); // should not panic on empty 519 + assert_eq!(app.search_query, ""); 520 + } 521 + }
+264
src/crypto.rs
··· 1 + // Amtrak data decryption 2 + // 3 + // The encrypted response from getTrainsData uses AES-CBC with PBKDF2-SHA1 key derivation. 4 + // Keys (salt, IV, public key) are fetched from RoutesList.v.json and RoutesList.json. 5 + // The last 88 bytes of the response are an encrypted segment containing the private key. 6 + 7 + use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit}; 8 + use anyhow::{Context, Result}; 9 + use base64::{engine::general_purpose::STANDARD as BASE64, Engine}; 10 + 11 + type Aes128CbcEnc = cbc::Encryptor<aes::Aes128>; 12 + type Aes128CbcDec = cbc::Decryptor<aes::Aes128>; 13 + 14 + pub struct RouteInfo { 15 + pub zoom_level: u32, 16 + } 17 + 18 + pub struct DecryptionKeys { 19 + pub public_key: String, 20 + pub salt: String, 21 + pub iv: String, 22 + } 23 + 24 + /// Sum of fibonacci(0..=9) = 88. This is the length of the encrypted key segment 25 + /// appended to the end of the response. 26 + pub fn master_segment() -> usize { 27 + let mut fib = vec![0usize, 1]; 28 + for i in 2..=9 { 29 + fib.push(fib[i - 2] + fib[i - 1]); 30 + } 31 + fib.iter().sum() 32 + } 33 + 34 + /// All salt entries in the config have length 8, so picking any element 35 + /// and using its length as the index always yields index 8. 36 + pub fn extract_salt(salt_array: &[String]) -> String { 37 + let idx = salt_array[0].len(); // always 8 38 + salt_array[idx].clone() 39 + } 40 + 41 + /// All IV entries in the config have length 32, so picking any element 42 + /// and using its length as the index always yields index 32. 43 + pub fn extract_iv(iv_array: &[String]) -> String { 44 + let idx = iv_array[0].len(); // always 32 45 + iv_array[idx].clone() 46 + } 47 + 48 + /// masterZoom = sum of all ZoomLevel values from the route list. 49 + /// Used as the index into the arr[] to get the public key. 50 + pub fn calculate_master_zoom(routes: &[RouteInfo]) -> u32 { 51 + routes.iter().map(|r| r.zoom_level).sum() 52 + } 53 + 54 + /// Derive an AES-128 key from a password using PBKDF2-HMAC-SHA1 (1000 iterations). 55 + fn derive_key(password: &str, salt_hex: &str) -> [u8; 16] { 56 + let salt = hex::decode(salt_hex).expect("invalid salt hex"); 57 + let mut key = [0u8; 16]; 58 + pbkdf2::pbkdf2_hmac::<sha1::Sha1>(password.as_bytes(), &salt, 1000, &mut key); 59 + key 60 + } 61 + 62 + /// Decrypt base64-encoded AES-128-CBC ciphertext. 63 + pub fn decrypt_aes( 64 + ciphertext_b64: &str, 65 + password: &str, 66 + salt_hex: &str, 67 + iv_hex: &str, 68 + ) -> Result<String> { 69 + let ciphertext = BASE64.decode(ciphertext_b64).context("invalid base64")?; 70 + let iv = hex::decode(iv_hex).context("invalid IV hex")?; 71 + let key = derive_key(password, salt_hex); 72 + 73 + let mut buf = ciphertext.clone(); 74 + let plaintext = Aes128CbcDec::new_from_slices(&key, &iv) 75 + .context("invalid key/iv length")? 76 + .decrypt_padded_mut::<Pkcs7>(&mut buf) 77 + .map_err(|e| anyhow::anyhow!("decryption failed: {}", e))?; 78 + 79 + String::from_utf8(plaintext.to_vec()).context("decrypted data is not valid UTF-8") 80 + } 81 + 82 + /// Encrypt plaintext to base64-encoded AES-128-CBC (for tests). 83 + pub fn encrypt_aes(plaintext: &str, password: &str, salt_hex: &str, iv_hex: &str) -> String { 84 + let iv = hex::decode(iv_hex).expect("invalid IV hex"); 85 + let key = derive_key(password, salt_hex); 86 + 87 + // Buffer needs room for plaintext + up to 16 bytes of PKCS7 padding 88 + let mut buf = vec![0u8; plaintext.len() + 16]; 89 + buf[..plaintext.len()].copy_from_slice(plaintext.as_bytes()); 90 + 91 + let ciphertext = Aes128CbcEnc::new_from_slices(&key, &iv) 92 + .expect("invalid key/iv length") 93 + .encrypt_padded_mut::<Pkcs7>(&mut buf, plaintext.len()) 94 + .expect("encryption failed"); 95 + 96 + BASE64.encode(ciphertext) 97 + } 98 + 99 + /// Split the encrypted response into main data and the key segment. 100 + pub fn split_encrypted_response(blob: &str) -> (&str, &str) { 101 + let seg_len = master_segment(); 102 + let split_at = blob.len() - seg_len; 103 + (&blob[..split_at], &blob[split_at..]) 104 + } 105 + 106 + /// Extract the private key from the decrypted segment (format: "uuid|timestamp"). 107 + pub fn extract_private_key(decrypted_segment: &str) -> &str { 108 + decrypted_segment 109 + .split('|') 110 + .next() 111 + .expect("segment should contain '|'") 112 + } 113 + 114 + /// Decrypt train data using a known segment length (for testing with non-88 segments). 115 + pub fn decrypt_train_data_with_segment_len( 116 + encrypted_blob: &str, 117 + keys: &DecryptionKeys, 118 + segment_len: usize, 119 + ) -> Result<String> { 120 + let split_at = encrypted_blob.len() - segment_len; 121 + let main_data = &encrypted_blob[..split_at]; 122 + let key_segment = &encrypted_blob[split_at..]; 123 + 124 + // Step 1: Decrypt the key segment using the public key 125 + let decrypted_segment = decrypt_aes(key_segment, &keys.public_key, &keys.salt, &keys.iv) 126 + .context("failed to decrypt key segment")?; 127 + 128 + // Step 2: Extract the private key 129 + let private_key = extract_private_key(&decrypted_segment); 130 + 131 + // Step 3: Decrypt the main data using the private key 132 + decrypt_aes(main_data, private_key, &keys.salt, &keys.iv) 133 + .context("failed to decrypt main train data") 134 + } 135 + 136 + /// Decrypt train data from the raw API response. 137 + pub fn decrypt_train_data(encrypted_blob: &str, keys: &DecryptionKeys) -> Result<String> { 138 + decrypt_train_data_with_segment_len(encrypted_blob, keys, master_segment()) 139 + } 140 + 141 + #[cfg(test)] 142 + mod tests { 143 + use super::*; 144 + 145 + // --- Fibonacci / master segment --- 146 + 147 + #[test] 148 + fn test_master_segment_is_88() { 149 + // The master segment length is the sum of fibonacci(0..=9) 150 + // which equals 0+1+1+2+3+5+8+13+21+34 = 88 151 + assert_eq!(master_segment(), 88); 152 + } 153 + 154 + // --- Key extraction --- 155 + 156 + #[test] 157 + fn test_extract_keys_from_config() { 158 + // All salt entries have length 8, so any random pick -> index 8 159 + let salt_array: Vec<String> = (0..20).map(|i| format!("{:08x}", i * 12345678)).collect(); 160 + assert_eq!(salt_array[0].len(), 8); 161 + let salt = extract_salt(&salt_array); 162 + assert_eq!(salt, salt_array[8]); 163 + 164 + // All IV entries have length 32, so any random pick -> index 32 165 + let iv_array: Vec<String> = (0..50) 166 + .map(|i| format!("{:032x}", i * 12345678u128)) 167 + .collect(); 168 + assert_eq!(iv_array[0].len(), 32); 169 + let iv = extract_iv(&iv_array); 170 + assert_eq!(iv, iv_array[32]); 171 + } 172 + 173 + #[test] 174 + fn test_master_zoom_calculation() { 175 + // masterZoom is the sum of all ZoomLevel values from the route list 176 + let routes = vec![ 177 + RouteInfo { zoom_level: 6 }, 178 + RouteInfo { zoom_level: 5 }, 179 + RouteInfo { zoom_level: 4 }, 180 + ]; 181 + assert_eq!(calculate_master_zoom(&routes), 15); 182 + } 183 + 184 + // --- AES decryption --- 185 + 186 + #[test] 187 + fn test_aes_decrypt_roundtrip() { 188 + // Encrypt something with known params, then decrypt it 189 + let plaintext = "hello amtrak"; 190 + let password = "test-password-uuid"; 191 + let salt_hex = "9a3686ac"; 192 + let iv_hex = "c6eb2f7f5c4740c1a2f708fefd947d39"; 193 + 194 + let encrypted = encrypt_aes(plaintext, password, salt_hex, iv_hex); 195 + let decrypted = decrypt_aes(&encrypted, password, salt_hex, iv_hex).unwrap(); 196 + assert_eq!(decrypted, plaintext); 197 + } 198 + 199 + #[test] 200 + fn test_decrypt_aes_bad_data_returns_error() { 201 + let result = decrypt_aes( 202 + "not-valid-base64!!!", 203 + "key", 204 + "9a3686ac", 205 + "c6eb2f7f5c4740c1a2f708fefd947d39", 206 + ); 207 + assert!(result.is_err()); 208 + } 209 + 210 + // --- Full pipeline --- 211 + 212 + #[test] 213 + fn test_split_encrypted_response() { 214 + // Given a blob of length 200, the last 88 chars are the encrypted key segment 215 + let blob = "A".repeat(112) + &"B".repeat(88); 216 + let (main_data, key_segment) = split_encrypted_response(&blob); 217 + assert_eq!(main_data.len(), 112); 218 + assert_eq!(key_segment.len(), 88); 219 + assert_eq!(key_segment, "B".repeat(88)); 220 + } 221 + 222 + #[test] 223 + fn test_extract_private_key_from_decrypted_segment() { 224 + // The decrypted segment is "private-key-uuid|2026-03-15T11:05:05.000Z" 225 + let segment = "37b1b306-903f-4ae7-ae93-ceb9e267215d|2026-03-15T11:05:05.000Z"; 226 + let key = extract_private_key(segment); 227 + assert_eq!(key, "37b1b306-903f-4ae7-ae93-ceb9e267215d"); 228 + } 229 + 230 + #[test] 231 + fn test_full_decrypt_pipeline_with_known_data() { 232 + // Create a mini encrypted blob using known keys, then decrypt it 233 + let salt_hex = "9a3686ac"; 234 + let iv_hex = "c6eb2f7f5c4740c1a2f708fefd947d39"; 235 + let public_key = "69af143c-e8cf-47f8-bf09-fc1f61e5cc33"; 236 + let private_key = "37b1b306-903f-4ae7-ae93-ceb9e267215d"; 237 + 238 + // The actual train JSON (simplified) 239 + let train_json = r#"{"type":"FeatureCollection","features":[]}"#; 240 + 241 + // Encrypt the train data with the private key 242 + let encrypted_main = encrypt_aes(train_json, private_key, salt_hex, iv_hex); 243 + 244 + // Create the key segment: "private_key|timestamp" 245 + let key_segment_plain = format!("{}|2026-03-15T11:05:05.000Z", private_key); 246 + let encrypted_segment = encrypt_aes(&key_segment_plain, public_key, salt_hex, iv_hex); 247 + 248 + // Combine: encrypted_main + encrypted_segment (segment must be exactly 88 chars) 249 + // Note: in reality the segment is always 88 chars. For this test we verify the pipeline 250 + // works with our split/decrypt logic. 251 + let combined = format!("{}{}", encrypted_main, encrypted_segment); 252 + 253 + let keys = DecryptionKeys { 254 + public_key: public_key.to_string(), 255 + salt: salt_hex.to_string(), 256 + iv: iv_hex.to_string(), 257 + }; 258 + 259 + let segment_len = encrypted_segment.len(); 260 + let result = decrypt_train_data_with_segment_len(&combined, &keys, segment_len); 261 + assert!(result.is_ok(), "decrypt failed: {:?}", result.err()); 262 + assert_eq!(result.unwrap(), train_json); 263 + } 264 + }
+147
src/geo.rs
··· 1 + // Geographic data layers: state boundaries and route lines 2 + // Parsed from embedded GeoJSON at compile time. 3 + 4 + use anyhow::{Context, Result}; 5 + 6 + /// A line segment as a series of (lon, lat) points. 7 + #[derive(Debug, Clone)] 8 + pub struct LineSegment { 9 + pub points: Vec<(f64, f64)>, 10 + pub name: String, 11 + } 12 + 13 + /// All static geographic layers for rendering. 14 + #[derive(Debug, Clone)] 15 + pub struct GeoLayers { 16 + pub state_boundaries: Vec<LineSegment>, 17 + pub routes: Vec<LineSegment>, 18 + } 19 + 20 + // Embed the data files at compile time 21 + const STATES_JSON: &str = include_str!("../data/us-states.json"); 22 + const ROUTES_JSON: &str = include_str!("../data/routes.json"); 23 + 24 + impl GeoLayers { 25 + pub fn load() -> Result<Self> { 26 + let state_boundaries = parse_states(STATES_JSON)?; 27 + let routes = parse_routes(ROUTES_JSON)?; 28 + Ok(Self { 29 + state_boundaries, 30 + routes, 31 + }) 32 + } 33 + } 34 + 35 + fn extract_ring(coords: &serde_json::Value) -> Vec<(f64, f64)> { 36 + coords 37 + .as_array() 38 + .unwrap_or(&vec![]) 39 + .iter() 40 + .filter_map(|c| { 41 + let arr = c.as_array()?; 42 + Some((arr[0].as_f64()?, arr[1].as_f64()?)) 43 + }) 44 + .collect() 45 + } 46 + 47 + fn parse_states(json: &str) -> Result<Vec<LineSegment>> { 48 + let data: serde_json::Value = 49 + serde_json::from_str(json).context("failed to parse states JSON")?; 50 + let features = data["features"].as_array().context("no features array")?; 51 + 52 + let mut segments = Vec::new(); 53 + for feat in features { 54 + let name = feat["properties"]["name"] 55 + .as_str() 56 + .unwrap_or("") 57 + .to_string(); 58 + let geom = &feat["geometry"]; 59 + 60 + match geom["type"].as_str() { 61 + Some("Polygon") => { 62 + for ring in geom["coordinates"].as_array().unwrap_or(&vec![]) { 63 + let points = extract_ring(ring); 64 + if points.len() >= 2 { 65 + segments.push(LineSegment { 66 + points, 67 + name: name.clone(), 68 + }); 69 + } 70 + } 71 + } 72 + Some("MultiPolygon") => { 73 + for poly in geom["coordinates"].as_array().unwrap_or(&vec![]) { 74 + for ring in poly.as_array().unwrap_or(&vec![]) { 75 + let points = extract_ring(ring); 76 + if points.len() >= 2 { 77 + segments.push(LineSegment { 78 + points, 79 + name: name.clone(), 80 + }); 81 + } 82 + } 83 + } 84 + } 85 + _ => {} 86 + } 87 + } 88 + Ok(segments) 89 + } 90 + 91 + fn parse_routes(json: &str) -> Result<Vec<LineSegment>> { 92 + let data: serde_json::Value = 93 + serde_json::from_str(json).context("failed to parse routes JSON")?; 94 + let features = data["features"].as_array().context("no features array")?; 95 + 96 + let mut segments = Vec::new(); 97 + for feat in features { 98 + let name = feat["properties"]["name"] 99 + .as_str() 100 + .unwrap_or("") 101 + .to_string(); 102 + let coords = &feat["geometry"]["coordinates"]; 103 + let points = extract_ring(coords); 104 + if points.len() >= 2 { 105 + segments.push(LineSegment { points, name }); 106 + } 107 + } 108 + Ok(segments) 109 + } 110 + 111 + #[cfg(test)] 112 + mod tests { 113 + use super::*; 114 + 115 + #[test] 116 + fn test_load_geo_layers() { 117 + let layers = GeoLayers::load().unwrap(); 118 + assert!( 119 + layers.state_boundaries.len() > 40, 120 + "should have ~52 state boundaries, got {}", 121 + layers.state_boundaries.len() 122 + ); 123 + assert!( 124 + layers.routes.len() > 100, 125 + "should have 100+ route segments, got {}", 126 + layers.routes.len() 127 + ); 128 + } 129 + 130 + #[test] 131 + fn test_state_boundaries_have_valid_coords() { 132 + let layers = GeoLayers::load().unwrap(); 133 + for seg in &layers.state_boundaries { 134 + for &(lon, lat) in &seg.points { 135 + assert!(lon > -200.0 && lon < 200.0, "bad lon: {}", lon); 136 + assert!(lat > -90.0 && lat < 90.0, "bad lat: {}", lat); 137 + } 138 + } 139 + } 140 + 141 + #[test] 142 + fn test_routes_have_names() { 143 + let layers = GeoLayers::load().unwrap(); 144 + let named = layers.routes.iter().filter(|r| !r.name.is_empty()).count(); 145 + assert!(named > 100, "most routes should have names"); 146 + } 147 + }
+6
src/lib.rs
··· 1 + pub mod api; 2 + pub mod app; 3 + pub mod crypto; 4 + pub mod geo; 5 + pub mod model; 6 + pub mod ui;
+170
src/main.rs
··· 1 + use std::io; 2 + use std::time::{Duration, Instant}; 3 + 4 + use crossterm::{ 5 + event::{self, Event, KeyCode, KeyModifiers}, 6 + execute, 7 + terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, 8 + }; 9 + use ratatui::{backend::CrosstermBackend, Terminal}; 10 + 11 + use amtracker::{ 12 + api, 13 + app::{App, InputMode}, 14 + ui, 15 + }; 16 + 17 + const REFRESH_INTERVAL: Duration = Duration::from_secs(60); 18 + 19 + #[tokio::main] 20 + async fn main() -> anyhow::Result<()> { 21 + // Terminal setup 22 + enable_raw_mode()?; 23 + let mut stdout = io::stdout(); 24 + execute!(stdout, EnterAlternateScreen)?; 25 + let backend = CrosstermBackend::new(stdout); 26 + let mut terminal = Terminal::new(backend)?; 27 + 28 + let result = run(&mut terminal).await; 29 + 30 + // Restore terminal 31 + disable_raw_mode()?; 32 + execute!(terminal.backend_mut(), LeaveAlternateScreen)?; 33 + terminal.show_cursor()?; 34 + 35 + result 36 + } 37 + 38 + async fn run(terminal: &mut Terminal<CrosstermBackend<io::Stdout>>) -> anyhow::Result<()> { 39 + let mut app = App::new(); 40 + let client = reqwest::Client::new(); 41 + 42 + // Initial data fetch 43 + app.status_message = "Fetching keys...".into(); 44 + terminal.draw(|f| ui::draw(f, &app))?; 45 + 46 + let keys = api::fetch_decryption_keys(&client).await?; 47 + app.status_message = "Fetching trains...".into(); 48 + terminal.draw(|f| ui::draw(f, &app))?; 49 + 50 + match api::fetch_trains(&client, &keys).await { 51 + Ok(trains) => { 52 + let count = trains.len(); 53 + app.trains = trains; 54 + app.last_update = Some(Instant::now()); 55 + app.status_message = format!("Loaded {} trains", count); 56 + } 57 + Err(e) => { 58 + app.status_message = format!("Error: {}", e); 59 + } 60 + } 61 + 62 + loop { 63 + terminal.draw(|f| ui::draw(f, &app))?; 64 + 65 + // Poll for events with a short timeout so we can check for refresh 66 + if event::poll(Duration::from_millis(250))? { 67 + if let Event::Key(key) = event::read()? { 68 + match app.input_mode { 69 + InputMode::Search => match key.code { 70 + KeyCode::Esc => app.exit_search(), 71 + KeyCode::Enter => { 72 + // Accept search, switch to normal mode but keep filter 73 + app.input_mode = InputMode::Normal; 74 + if !app.visible_trains().is_empty() { 75 + app.select_next(); 76 + } 77 + } 78 + KeyCode::Backspace => app.search_pop(), 79 + KeyCode::Char(c) => app.search_push(c), 80 + KeyCode::Tab => app.select_next(), 81 + KeyCode::BackTab => app.select_prev(), 82 + _ => {} 83 + }, 84 + InputMode::Normal => match (key.code, key.modifiers) { 85 + (KeyCode::Char('q'), _) | (KeyCode::Char('c'), KeyModifiers::CONTROL) => { 86 + app.should_quit = true; 87 + } 88 + 89 + // Zoom 90 + (KeyCode::Char('+') | KeyCode::Char('='), _) => app.viewport.zoom_in(), 91 + (KeyCode::Char('-'), _) => app.viewport.zoom_out(), 92 + 93 + // Pan 94 + (KeyCode::Left, _) => app.viewport.pan(-0.15, 0.0), 95 + (KeyCode::Right, _) => app.viewport.pan(0.15, 0.0), 96 + (KeyCode::Up, _) => app.viewport.pan(0.0, 0.15), 97 + (KeyCode::Down, _) => app.viewport.pan(0.0, -0.15), 98 + 99 + // Selection 100 + (KeyCode::Tab, _) => app.select_next(), 101 + (KeyCode::BackTab, _) => app.select_prev(), 102 + 103 + // Jump to selected train 104 + (KeyCode::Enter, _) => { 105 + if app.selected_index.is_some() { 106 + app.jump_to_selected(); 107 + } else { 108 + app.select_next(); 109 + } 110 + } 111 + 112 + // Search 113 + (KeyCode::Char('/'), _) => app.enter_search(), 114 + 115 + // Reset view 116 + (KeyCode::Char('0'), _) => app.reset_view(), 117 + 118 + (KeyCode::Esc, _) => { 119 + app.selected_index = None; 120 + app.search_query.clear(); 121 + } 122 + 123 + // Manual refresh 124 + (KeyCode::Char('r'), _) => { 125 + app.status_message = "Refreshing...".into(); 126 + terminal.draw(|f| ui::draw(f, &app))?; 127 + match api::fetch_trains(&client, &keys).await { 128 + Ok(trains) => { 129 + let count = trains.len(); 130 + app.trains = trains; 131 + app.last_update = Some(Instant::now()); 132 + app.status_message = format!("Refreshed: {} trains", count); 133 + } 134 + Err(e) => { 135 + app.status_message = format!("Refresh error: {}", e); 136 + } 137 + } 138 + } 139 + 140 + _ => {} 141 + }, 142 + } 143 + } 144 + } 145 + 146 + if app.should_quit { 147 + break; 148 + } 149 + 150 + // Auto-refresh 151 + if let Some(last) = app.last_update { 152 + if last.elapsed() >= REFRESH_INTERVAL { 153 + match api::fetch_trains(&client, &keys).await { 154 + Ok(trains) => { 155 + let count = trains.len(); 156 + app.trains = trains; 157 + app.last_update = Some(Instant::now()); 158 + app.status_message = format!("Auto-refresh: {} trains", count); 159 + } 160 + Err(e) => { 161 + app.status_message = format!("Refresh error: {}", e); 162 + app.last_update = Some(Instant::now()); // don't spam retries 163 + } 164 + } 165 + } 166 + } 167 + } 168 + 169 + Ok(()) 170 + }
+309
src/model.rs
··· 1 + // Train data model - parsed from Amtrak's decrypted GeoJSON 2 + 3 + use anyhow::{Context, Result}; 4 + use serde::Deserialize; 5 + 6 + #[derive(Debug, Clone, PartialEq, Eq)] 7 + pub enum TrainState { 8 + Active, 9 + Completed, 10 + Predeparture, 11 + Unknown, 12 + } 13 + 14 + impl TrainState { 15 + pub fn parse(s: &str) -> Self { 16 + match s { 17 + "Active" => Self::Active, 18 + "Completed" => Self::Completed, 19 + "Predeparture" => Self::Predeparture, 20 + _ => Self::Unknown, 21 + } 22 + } 23 + } 24 + 25 + #[derive(Debug, Clone, PartialEq, Eq)] 26 + pub enum DelayStatus { 27 + OnTime, 28 + Early, 29 + Late, 30 + Unknown, 31 + } 32 + 33 + #[derive(Debug, Clone, Deserialize)] 34 + struct StationJson { 35 + code: String, 36 + #[serde(default)] 37 + postcmnt: Option<String>, 38 + #[serde(default)] 39 + estarrcmnt: Option<String>, 40 + #[serde(default)] 41 + estdepcmnt: Option<String>, 42 + } 43 + 44 + #[derive(Debug, Clone)] 45 + pub struct StationStop { 46 + pub code: String, 47 + pub comment: Option<String>, 48 + } 49 + 50 + impl StationStop { 51 + pub fn from_json_str(s: &str) -> Result<Self> { 52 + let parsed: StationJson = 53 + serde_json::from_str(s).context("failed to parse station JSON")?; 54 + // Use the most relevant comment: postcmnt (actual) > estarrcmnt (estimated) > estdepcmnt 55 + let comment = parsed.postcmnt.or(parsed.estarrcmnt).or(parsed.estdepcmnt); 56 + Ok(Self { 57 + code: parsed.code, 58 + comment, 59 + }) 60 + } 61 + 62 + pub fn delay_status(&self) -> DelayStatus { 63 + match &self.comment { 64 + None => DelayStatus::Unknown, 65 + Some(c) => { 66 + let upper = c.to_uppercase(); 67 + if upper.contains("LATE") { 68 + DelayStatus::Late 69 + } else if upper.contains("EARLY") { 70 + DelayStatus::Early 71 + } else if upper.contains("ON TIME") { 72 + DelayStatus::OnTime 73 + } else { 74 + DelayStatus::Unknown 75 + } 76 + } 77 + } 78 + } 79 + } 80 + 81 + #[derive(Debug, Clone)] 82 + pub struct Train { 83 + pub number: String, 84 + pub route_name: String, 85 + pub origin: String, 86 + pub destination: String, 87 + pub lon: f64, 88 + pub lat: f64, 89 + pub velocity: f64, 90 + pub heading: String, 91 + pub state: TrainState, 92 + pub status_msg: String, 93 + pub stations: Vec<StationStop>, 94 + } 95 + 96 + impl Train { 97 + pub fn from_feature(feature: &geojson::Feature) -> Result<Self> { 98 + let geometry = feature 99 + .geometry 100 + .as_ref() 101 + .context("feature has no geometry")?; 102 + 103 + let (lon, lat) = match &geometry.value { 104 + geojson::Value::Point(coords) => (coords[0], coords[1]), 105 + _ => anyhow::bail!("expected Point geometry"), 106 + }; 107 + 108 + let props = feature 109 + .properties 110 + .as_ref() 111 + .context("feature has no properties")?; 112 + 113 + let get_str = |key: &str| -> String { 114 + props 115 + .get(key) 116 + .and_then(|v| v.as_str()) 117 + .unwrap_or("") 118 + .to_string() 119 + }; 120 + 121 + let velocity: f64 = get_str("Velocity").parse().unwrap_or(0.0); 122 + 123 + // Parse station stops from Station1..Station45 124 + let mut stations = Vec::new(); 125 + for i in 1..=45 { 126 + let key = format!("Station{}", i); 127 + if let Some(val) = props.get(&key).and_then(|v| v.as_str()) { 128 + if let Ok(stop) = StationStop::from_json_str(val) { 129 + stations.push(stop); 130 + } 131 + } 132 + } 133 + 134 + Ok(Train { 135 + number: get_str("TrainNum"), 136 + route_name: get_str("RouteName"), 137 + origin: get_str("OrigCode"), 138 + destination: get_str("DestCode"), 139 + lon, 140 + lat, 141 + velocity, 142 + heading: get_str("Heading"), 143 + state: TrainState::parse(&get_str("TrainState")), 144 + status_msg: get_str("StatusMsg").trim().to_string(), 145 + stations, 146 + }) 147 + } 148 + 149 + pub fn delay_status(&self) -> DelayStatus { 150 + if self.stations.is_empty() { 151 + return DelayStatus::Unknown; 152 + } 153 + // If any station shows late, the train is late 154 + let mut found_status = false; 155 + for stop in &self.stations { 156 + match stop.delay_status() { 157 + DelayStatus::Late => return DelayStatus::Late, 158 + DelayStatus::OnTime | DelayStatus::Early => found_status = true, 159 + DelayStatus::Unknown => {} 160 + } 161 + } 162 + if found_status { 163 + DelayStatus::OnTime 164 + } else { 165 + DelayStatus::Unknown 166 + } 167 + } 168 + } 169 + 170 + pub fn parse_train_response(json: &str) -> Result<Vec<Train>> { 171 + let collection: geojson::FeatureCollection = 172 + serde_json::from_str(json).context("failed to parse GeoJSON")?; 173 + 174 + let mut trains = Vec::new(); 175 + for feature in &collection.features { 176 + match Train::from_feature(feature) { 177 + Ok(train) => trains.push(train), 178 + Err(e) => eprintln!("skipping feature: {}", e), 179 + } 180 + } 181 + Ok(trains) 182 + } 183 + 184 + #[cfg(test)] 185 + mod tests { 186 + use super::*; 187 + 188 + #[test] 189 + fn test_parse_train_from_geojson_feature() { 190 + let feature_json = r#"{ 191 + "type": "Feature", 192 + "geometry": { "type": "Point", "coordinates": [-118.614, 34.264] }, 193 + "properties": { 194 + "TrainNum": "785", 195 + "RouteName": "Pacific Surfliner", 196 + "OrigCode": "SAN", 197 + "DestCode": "GTA", 198 + "Velocity": "42.37", 199 + "Heading": "SW", 200 + "TrainState": "Active", 201 + "StatusMsg": " ", 202 + "LastValTS": "3/15/2026 8:03:20 PM", 203 + "OrigSchDep": "3/15/2026 4:01:00 PM", 204 + "Station1": "{\"code\":\"SAN\",\"tz\":\"P\",\"bus\":false,\"postcmnt\":\"ON TIME\"}", 205 + "Station2": "{\"code\":\"OSD\",\"tz\":\"P\",\"bus\":false,\"estarrcmnt\":\"04 MI LATE\"}" 206 + } 207 + }"#; 208 + 209 + let feature: geojson::Feature = serde_json::from_str(feature_json).unwrap(); 210 + let train = Train::from_feature(&feature).unwrap(); 211 + 212 + assert_eq!(train.number, "785"); 213 + assert_eq!(train.route_name, "Pacific Surfliner"); 214 + assert_eq!(train.origin, "SAN"); 215 + assert_eq!(train.destination, "GTA"); 216 + assert!((train.lon - (-118.614)).abs() < 0.001); 217 + assert!((train.lat - 34.264).abs() < 0.001); 218 + assert!((train.velocity - 42.37).abs() < 0.01); 219 + assert_eq!(train.heading, "SW"); 220 + assert_eq!(train.state, TrainState::Active); 221 + } 222 + 223 + #[test] 224 + fn test_parse_train_state() { 225 + assert_eq!(TrainState::parse("Active"), TrainState::Active); 226 + assert_eq!(TrainState::parse("Completed"), TrainState::Completed); 227 + assert_eq!(TrainState::parse("Predeparture"), TrainState::Predeparture); 228 + assert_eq!(TrainState::parse("SomethingElse"), TrainState::Unknown); 229 + } 230 + 231 + #[test] 232 + fn test_train_delay_status() { 233 + // Extract delay info from station data 234 + let station_json = r#"{"code":"SBA","tz":"P","bus":false,"estarrcmnt":"12 MI LATE"}"#; 235 + let stop = StationStop::from_json_str(station_json).unwrap(); 236 + assert_eq!(stop.code, "SBA"); 237 + assert_eq!(stop.delay_status(), DelayStatus::Late); 238 + 239 + let on_time_json = r#"{"code":"LAX","tz":"P","bus":false,"postcmnt":"ON TIME"}"#; 240 + let stop2 = StationStop::from_json_str(on_time_json).unwrap(); 241 + assert_eq!(stop2.delay_status(), DelayStatus::OnTime); 242 + 243 + let early_json = r#"{"code":"GTA","tz":"P","bus":false,"estarrcmnt":"04 MI EARLY"}"#; 244 + let stop3 = StationStop::from_json_str(early_json).unwrap(); 245 + assert_eq!(stop3.delay_status(), DelayStatus::Early); 246 + } 247 + 248 + #[test] 249 + fn test_train_overall_delay_status() { 250 + let mut train = Train { 251 + number: "1".into(), 252 + route_name: "Test".into(), 253 + origin: "A".into(), 254 + destination: "B".into(), 255 + lon: 0.0, 256 + lat: 0.0, 257 + velocity: 0.0, 258 + heading: "N".into(), 259 + state: TrainState::Active, 260 + status_msg: String::new(), 261 + stations: vec![], 262 + }; 263 + 264 + // No stations -> Unknown 265 + assert_eq!(train.delay_status(), DelayStatus::Unknown); 266 + 267 + // All on time -> OnTime 268 + train.stations.push(StationStop { 269 + code: "A".into(), 270 + comment: Some("ON TIME".into()), 271 + }); 272 + assert_eq!(train.delay_status(), DelayStatus::OnTime); 273 + 274 + // Any late -> Late 275 + train.stations.push(StationStop { 276 + code: "B".into(), 277 + comment: Some("15 MI LATE".into()), 278 + }); 279 + assert_eq!(train.delay_status(), DelayStatus::Late); 280 + } 281 + 282 + #[test] 283 + fn test_parse_full_response() { 284 + let json = r#"{ 285 + "type": "FeatureCollection", 286 + "features": [ 287 + { 288 + "type": "Feature", 289 + "geometry": { "type": "Point", "coordinates": [-90.0, 38.0] }, 290 + "properties": { 291 + "TrainNum": "316", 292 + "RouteName": "Missouri River Runner", 293 + "OrigCode": "KCY", 294 + "DestCode": "STL", 295 + "Velocity": "0", 296 + "Heading": "E", 297 + "TrainState": "Completed", 298 + "StatusMsg": " " 299 + } 300 + } 301 + ] 302 + }"#; 303 + 304 + let trains = parse_train_response(json).unwrap(); 305 + assert_eq!(trains.len(), 1); 306 + assert_eq!(trains[0].number, "316"); 307 + assert_eq!(trains[0].state, TrainState::Completed); 308 + } 309 + }
+418
src/ui.rs
··· 1 + // UI rendering with ratatui 2 + 3 + use ratatui::{ 4 + layout::{Constraint, Direction, Layout, Rect}, 5 + style::{Color, Modifier, Style}, 6 + text::{Line, Span}, 7 + widgets::{ 8 + canvas::{Canvas, Line as CanvasLine, Map, MapResolution}, 9 + Block, Borders, List, ListItem, Paragraph, 10 + }, 11 + Frame, 12 + }; 13 + 14 + use crate::app::App; 15 + use crate::geo::LineSegment; 16 + use crate::model::{DelayStatus, TrainState}; 17 + 18 + fn delay_color(status: &DelayStatus) -> Color { 19 + match status { 20 + DelayStatus::OnTime | DelayStatus::Early => Color::Green, 21 + DelayStatus::Late => Color::Red, 22 + DelayStatus::Unknown => Color::Yellow, 23 + } 24 + } 25 + 26 + pub fn draw(frame: &mut Frame, app: &App) { 27 + // Main layout: map on top/left, info panel on right 28 + let main_chunks = Layout::default() 29 + .direction(Direction::Horizontal) 30 + .constraints([Constraint::Percentage(70), Constraint::Percentage(30)]) 31 + .split(frame.area()); 32 + 33 + draw_map(frame, app, main_chunks[0]); 34 + draw_sidebar(frame, app, main_chunks[1]); 35 + } 36 + 37 + struct TrainDot { 38 + lon: f64, 39 + lat: f64, 40 + number: String, 41 + color: Color, 42 + } 43 + 44 + /// Convert LineSegments into owned (x1,y1,x2,y2,color) tuples for the paint closure. 45 + fn segments_to_lines(segments: &[LineSegment], color: Color) -> Vec<(f64, f64, f64, f64, Color)> { 46 + let mut lines = Vec::new(); 47 + for seg in segments { 48 + for pair in seg.points.windows(2) { 49 + lines.push((pair[0].0, pair[0].1, pair[1].0, pair[1].1, color)); 50 + } 51 + } 52 + lines 53 + } 54 + 55 + fn draw_map(frame: &mut Frame, app: &App, area: Rect) { 56 + let vp = &app.viewport; 57 + let vp_width = vp.width(); 58 + 59 + // Collect train dots into owned data for the 'static paint closure 60 + let dots: Vec<TrainDot> = app 61 + .trains 62 + .iter() 63 + .filter(|t| t.state == TrainState::Active) 64 + .map(|t| TrainDot { 65 + lon: t.lon, 66 + lat: t.lat, 67 + number: t.number.clone(), 68 + color: delay_color(&t.delay_status()), 69 + }) 70 + .collect(); 71 + 72 + // Pre-compute geo layer lines for the closure 73 + let state_lines = app 74 + .geo 75 + .as_ref() 76 + .map(|g| segments_to_lines(&g.state_boundaries, Color::Rgb(50, 50, 70))) 77 + .unwrap_or_default(); 78 + 79 + let route_lines = app 80 + .geo 81 + .as_ref() 82 + .map(|g| segments_to_lines(&g.routes, Color::Rgb(60, 80, 140))) 83 + .unwrap_or_default(); 84 + 85 + let canvas = Canvas::default() 86 + .block( 87 + Block::default() 88 + .title(format!( 89 + " amtracker — {} active trains ", 90 + app.active_train_count() 91 + )) 92 + .borders(Borders::ALL) 93 + .border_style(Style::default().fg(Color::DarkGray)), 94 + ) 95 + .x_bounds([vp.x_min, vp.x_max]) 96 + .y_bounds([vp.y_min, vp.y_max]) 97 + .paint(move |ctx| { 98 + // Layer 1: Coastlines (built-in ratatui world map) 99 + ctx.draw(&Map { 100 + resolution: MapResolution::High, 101 + color: Color::Rgb(40, 40, 50), 102 + }); 103 + 104 + // Layer 2: State boundaries 105 + for &(x1, y1, x2, y2, color) in &state_lines { 106 + ctx.draw(&CanvasLine { 107 + x1, 108 + y1, 109 + x2, 110 + y2, 111 + color, 112 + }); 113 + } 114 + 115 + // Layer 3: Amtrak routes 116 + for &(x1, y1, x2, y2, color) in &route_lines { 117 + ctx.draw(&CanvasLine { 118 + x1, 119 + y1, 120 + x2, 121 + y2, 122 + color, 123 + }); 124 + } 125 + 126 + // Layer 4: Train markers — use ◆ character for visibility 127 + for dot in &dots { 128 + ctx.print( 129 + dot.lon, 130 + dot.lat, 131 + Span::styled( 132 + "◆", 133 + Style::default().fg(dot.color).add_modifier(Modifier::BOLD), 134 + ), 135 + ); 136 + ctx.print( 137 + dot.lon + vp_width * 0.012, 138 + dot.lat, 139 + Span::styled(dot.number.clone(), Style::default().fg(dot.color)), 140 + ); 141 + } 142 + }) 143 + .marker(ratatui::symbols::Marker::Braille); 144 + 145 + frame.render_widget(canvas, area); 146 + } 147 + 148 + fn draw_sidebar(frame: &mut Frame, app: &App, area: Rect) { 149 + let search_height = if app.input_mode == crate::app::InputMode::Search { 150 + 3 151 + } else { 152 + 0 153 + }; 154 + let sidebar_chunks = Layout::default() 155 + .direction(Direction::Vertical) 156 + .constraints([ 157 + Constraint::Length(3), // status bar 158 + Constraint::Length(search_height), // search bar (conditional) 159 + Constraint::Min(10), // train list or detail 160 + Constraint::Length(5), // help 161 + ]) 162 + .split(area); 163 + 164 + draw_status_bar(frame, app, sidebar_chunks[0]); 165 + if app.input_mode == crate::app::InputMode::Search { 166 + draw_search_bar(frame, app, sidebar_chunks[1]); 167 + } 168 + draw_train_panel(frame, app, sidebar_chunks[2]); 169 + draw_help(frame, sidebar_chunks[3]); 170 + } 171 + 172 + fn draw_search_bar(frame: &mut Frame, app: &App, area: Rect) { 173 + let input = Paragraph::new(Line::from(vec![ 174 + Span::styled("/ ", Style::default().fg(Color::Yellow)), 175 + Span::raw(&app.search_query), 176 + Span::styled( 177 + "_", 178 + Style::default() 179 + .fg(Color::Yellow) 180 + .add_modifier(Modifier::SLOW_BLINK), 181 + ), 182 + ])) 183 + .block( 184 + Block::default() 185 + .title(" Search ") 186 + .borders(Borders::ALL) 187 + .border_style(Style::default().fg(Color::Yellow)), 188 + ); 189 + frame.render_widget(input, area); 190 + } 191 + 192 + fn draw_status_bar(frame: &mut Frame, app: &App, area: Rect) { 193 + let delayed = app.delayed_train_count(); 194 + let status = if delayed > 0 { 195 + format!( 196 + "{} active | {} delayed | {}", 197 + app.active_train_count(), 198 + delayed, 199 + &app.status_message 200 + ) 201 + } else { 202 + format!( 203 + "{} active | {}", 204 + app.active_train_count(), 205 + &app.status_message 206 + ) 207 + }; 208 + 209 + let paragraph = Paragraph::new(status).block( 210 + Block::default() 211 + .title(" Status ") 212 + .borders(Borders::ALL) 213 + .border_style(Style::default().fg(Color::DarkGray)), 214 + ); 215 + frame.render_widget(paragraph, area); 216 + } 217 + 218 + fn draw_train_panel(frame: &mut Frame, app: &App, area: Rect) { 219 + if let Some(train) = app.selected_train() { 220 + // Detail view for selected train 221 + let delay = train.delay_status(); 222 + let color = delay_color(&delay); 223 + 224 + let lines = vec![ 225 + Line::from(vec![ 226 + Span::styled("Train ", Style::default().fg(Color::White)), 227 + Span::styled( 228 + format!("#{}", train.number), 229 + Style::default().fg(color).add_modifier(Modifier::BOLD), 230 + ), 231 + ]), 232 + Line::from(format!("Route: {}", train.route_name)), 233 + Line::from(format!("{} → {}", train.origin, train.destination)), 234 + Line::from(format!( 235 + "Speed: {:.0} mph | Heading: {}", 236 + train.velocity, train.heading 237 + )), 238 + Line::from(Span::styled( 239 + format!("{:?}", delay), 240 + Style::default().fg(color), 241 + )), 242 + Line::from(""), 243 + Line::from(Span::styled( 244 + "Stations:", 245 + Style::default().add_modifier(Modifier::BOLD), 246 + )), 247 + ]; 248 + 249 + let mut all_lines = lines; 250 + for stop in &train.stations { 251 + let stop_color = delay_color(&stop.delay_status()); 252 + let comment = stop.comment.as_deref().unwrap_or("--"); 253 + all_lines.push(Line::from(vec![ 254 + Span::styled( 255 + format!(" {} ", stop.code), 256 + Style::default().fg(Color::White), 257 + ), 258 + Span::styled(comment, Style::default().fg(stop_color)), 259 + ])); 260 + } 261 + 262 + let paragraph = Paragraph::new(all_lines).block( 263 + Block::default() 264 + .title(" Train Detail ") 265 + .borders(Borders::ALL) 266 + .border_style(Style::default().fg(color)), 267 + ); 268 + frame.render_widget(paragraph, area); 269 + } else { 270 + // Train list view 271 + let visible = app.visible_trains(); 272 + let items: Vec<ListItem> = visible 273 + .iter() 274 + .enumerate() 275 + .map(|(i, train)| { 276 + let color = delay_color(&train.delay_status()); 277 + let selected = app.selected_index == Some(i); 278 + let marker = if selected { "▸" } else { " " }; 279 + ListItem::new(Line::from(vec![ 280 + Span::raw(marker), 281 + Span::styled( 282 + format!("#{:<5}", train.number), 283 + Style::default().fg(color).add_modifier(Modifier::BOLD), 284 + ), 285 + Span::styled( 286 + format!(" {}", train.route_name), 287 + Style::default().fg(Color::White), 288 + ), 289 + ])) 290 + }) 291 + .collect(); 292 + 293 + let list = List::new(items).block( 294 + Block::default() 295 + .title(format!(" Trains ({}) ", visible.len())) 296 + .borders(Borders::ALL) 297 + .border_style(Style::default().fg(Color::DarkGray)), 298 + ); 299 + frame.render_widget(list, area); 300 + } 301 + } 302 + 303 + fn draw_help(frame: &mut Frame, area: Rect) { 304 + let help_text = vec![ 305 + Line::from("←↑↓→ pan | +/- zoom | ↹/⇧↹ select"), 306 + Line::from("Enter: jump | /: search | 0: reset | q: quit"), 307 + ]; 308 + let paragraph = Paragraph::new(help_text).block( 309 + Block::default() 310 + .title(" Help ") 311 + .borders(Borders::ALL) 312 + .border_style(Style::default().fg(Color::DarkGray)), 313 + ); 314 + frame.render_widget(paragraph, area); 315 + } 316 + 317 + #[cfg(test)] 318 + mod tests { 319 + use super::*; 320 + use crate::model::TrainState; 321 + use ratatui::{backend::TestBackend, Terminal}; 322 + 323 + fn make_test_train(num: &str, lon: f64, lat: f64) -> crate::model::Train { 324 + crate::model::Train { 325 + number: num.into(), 326 + route_name: "Test Route".into(), 327 + origin: "AAA".into(), 328 + destination: "BBB".into(), 329 + lon, 330 + lat, 331 + velocity: 55.0, 332 + heading: "NE".into(), 333 + state: TrainState::Active, 334 + status_msg: String::new(), 335 + stations: vec![], 336 + } 337 + } 338 + 339 + #[test] 340 + fn test_ui_renders_without_panic() { 341 + let backend = TestBackend::new(120, 40); 342 + let mut terminal = Terminal::new(backend).unwrap(); 343 + let app = App::new(); 344 + 345 + terminal 346 + .draw(|frame| draw(frame, &app)) 347 + .expect("draw should not panic"); 348 + } 349 + 350 + #[test] 351 + fn test_ui_renders_with_trains() { 352 + let backend = TestBackend::new(120, 40); 353 + let mut terminal = Terminal::new(backend).unwrap(); 354 + 355 + let mut app = App::new(); 356 + app.trains = vec![ 357 + make_test_train("785", -118.6, 34.2), 358 + make_test_train("316", -90.2, 38.6), 359 + ]; 360 + app.status_message = "Updated just now".into(); 361 + 362 + terminal 363 + .draw(|frame| draw(frame, &app)) 364 + .expect("draw should not panic with trains"); 365 + } 366 + 367 + #[test] 368 + fn test_ui_renders_with_selected_train() { 369 + let backend = TestBackend::new(120, 40); 370 + let mut terminal = Terminal::new(backend).unwrap(); 371 + 372 + let mut app = App::new(); 373 + app.trains = vec![ 374 + make_test_train("785", -118.6, 34.2), 375 + make_test_train("316", -90.2, 38.6), 376 + ]; 377 + app.selected_index = Some(0); 378 + 379 + terminal 380 + .draw(|frame| draw(frame, &app)) 381 + .expect("draw should not panic with selection"); 382 + } 383 + 384 + #[test] 385 + fn test_ui_contains_train_count_in_title() { 386 + let backend = TestBackend::new(120, 40); 387 + let mut terminal = Terminal::new(backend).unwrap(); 388 + 389 + let mut app = App::new(); 390 + app.trains = vec![ 391 + make_test_train("1", -87.6, 41.8), 392 + make_test_train("2", -90.0, 38.0), 393 + ]; 394 + 395 + terminal.draw(|frame| draw(frame, &app)).unwrap(); 396 + 397 + // Check that the buffer contains "2 active" 398 + let buf = terminal.backend().buffer().clone(); 399 + let content: String = buf 400 + .content 401 + .iter() 402 + .map(|cell| cell.symbol().chars().next().unwrap_or(' ')) 403 + .collect(); 404 + assert!( 405 + content.contains("2 active"), 406 + "buffer should contain '2 active trains', got: ...{}...", 407 + &content[..200.min(content.len())] 408 + ); 409 + } 410 + 411 + #[test] 412 + fn test_delay_colors() { 413 + assert_eq!(delay_color(&DelayStatus::OnTime), Color::Green); 414 + assert_eq!(delay_color(&DelayStatus::Early), Color::Green); 415 + assert_eq!(delay_color(&DelayStatus::Late), Color::Red); 416 + assert_eq!(delay_color(&DelayStatus::Unknown), Color::Yellow); 417 + } 418 + }