Compare commits
2427 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7d7cce5c72 | |||
| 0c66f84555 | |||
| 044e41c079 | |||
| 8dc631e13c | |||
| d93b4feb99 | |||
| 0b873f590c | |||
| f52d637f58 | |||
| d1fdc7061f | |||
| 2822382797 | |||
| 35c9021bdf | |||
| 493b13364f | |||
| a8ff3231a8 | |||
| c2a750a690 | |||
| 6a9541833a | |||
| 9117b45899 | |||
| 812cf36d52 | |||
| 4aa7420d6a | |||
| 1802839a22 | |||
| 9a2d701e2e | |||
| 69ad3f3365 | |||
| d53eb881c2 | |||
| e28e45f38e | |||
| bf6ca22b00 | |||
| b8acbae3e8 | |||
| 4ec9f2c00f | |||
| 4fc329a1eb | |||
| 0574a60bc2 | |||
| e490c4afed | |||
| d028a30f87 | |||
| 75a2d4d00a | |||
| a122a1e8c7 | |||
| 8fe09c85b6 | |||
| bc951de2a5 | |||
| c970cd13cc | |||
| 8983c0f927 | |||
| a47467f29b | |||
| ef206eb4d9 | |||
| da5eec1c5f | |||
| a69b3535a5 | |||
| f4c23f3259 | |||
| e36e9fd629 | |||
| d2f19353ed | |||
| 2d498cb88b | |||
| a80815d57a | |||
| acab77f4f8 | |||
| bddad0c57c | |||
| 3968349480 | |||
| 3ae17761b3 | |||
| 9a28bdbf76 | |||
| e769821e0f | |||
| 9b68821f05 | |||
| b2adb22b2d | |||
| 981941edf4 | |||
| 111dea89a9 | |||
| 2720f4de9c | |||
| 21645643b0 | |||
| c6a07acd90 | |||
| 1816e6607e | |||
| 37dd05a9b5 | |||
| 529997de9b | |||
| 4f37270300 | |||
| faf9c96442 | |||
| f7e09686e5 | |||
| 8eef8204e7 | |||
| 8b8e08f0f5 | |||
| a6bfec22b5 | |||
| d05c4fa9ed | |||
| 07231d3706 | |||
| 584ba10726 | |||
| 24a61dc650 | |||
| 7e7aa6aaee | |||
| 35c5be6475 | |||
| fcf3eb6193 | |||
| 5226b200e7 | |||
| 5f57152156 | |||
| 633ef26054 | |||
| 285bd4f542 | |||
| e706fb973f | |||
| 8dc7afed4e | |||
| 0c7f996de6 | |||
| 985a8e9f97 | |||
| 5e26e4c22d | |||
| 00e27e4b5c | |||
| b3ec9c9323 | |||
| 84ffedd95d | |||
| 455dc26a5d | |||
| 0fccd0f0d2 | |||
| 7e639d6bc1 | |||
| 694ce9d04f | |||
| aebe8572f9 | |||
| c98eed612a | |||
| 5f0ee417b1 | |||
| a6b4658138 | |||
| 1a57aff0f4 | |||
| afc0bac232 | |||
| ac1c59b724 | |||
| 1f9806386e | |||
| 1b67120704 | |||
| 5579388896 | |||
| 49c972983d | |||
| 3cb34f8036 | |||
| 3c5b2286a3 | |||
| 845972daa5 | |||
| e00ef38305 | |||
| 22d0861f6d | |||
| 5265f38c35 | |||
| d13cd4d4b3 | |||
| 79d33d669c | |||
| 439f6e62bc | |||
| 3ef054bbac | |||
| 5a13c6cc3f | |||
| b7750ff742 | |||
| 2c0fa2a9f6 | |||
| 881a57bf8d | |||
| c924b2067c | |||
| fdd0c8470f | |||
| 1960c38468 | |||
| 3074a0146e | |||
| f060dfda40 | |||
| b0ead129ca | |||
| abe9c4d53c | |||
| 15a95ff425 | |||
| 37b8f97d60 | |||
| 88a4bb4d12 | |||
| 50594febc7 | |||
| 9587923e55 | |||
| 2850a074ea | |||
| bed5c8002f | |||
| cc982a578d | |||
| e12ad48822 | |||
| 6abbb06ff6 | |||
| 3d68769503 | |||
| bd316e7e98 | |||
| 3f4c4d675b | |||
| 4b7e5b7445 | |||
| 996dbdde00 | |||
| db67f2ddf1 | |||
| 7b9b2fd23a | |||
| 700747801c | |||
| cb30f4668e | |||
| 9710f39cad | |||
| e3244dd649 | |||
| abd20776a2 | |||
| 5d8b4cb261 | |||
| 9ee615020f | |||
| acef734643 | |||
| 0ff7ac6aad | |||
| 859502c504 | |||
| 9c2926ef8a | |||
| 3188bc5aba | |||
| bf9d9512a0 | |||
| ba03f99e09 | |||
| 8e89f80588 | |||
| 07e245eb02 | |||
| ea12c6fde6 | |||
| 38c7d07e50 | |||
| a0a835bbe6 | |||
| c09e897dab | |||
| c5a3937c80 | |||
| 8e6cc09d2f | |||
| dee3529f14 | |||
| bf5d83d228 | |||
| c2b7336817 | |||
| fc6017c0dd | |||
| 821cc3a177 | |||
| 8939cef020 | |||
| fb17653651 | |||
| f8a07c7a03 | |||
| eb89436df3 | |||
| c1e7ba7118 | |||
| ffddea75a4 | |||
| 380111a604 | |||
| dd4461d473 | |||
| 2706a0bec1 | |||
| 46b207e08e | |||
| d3eda80969 | |||
| 66fb5fc9c8 | |||
| 2d3fb858ad | |||
| 7bf74c0917 | |||
| 5b22dffa6f | |||
| 3ca9420801 | |||
| 3c948a82c0 | |||
| c33c6188a8 | |||
| 0d8f2e1b18 | |||
| 17ed4f908c | |||
| 946bfb9a26 | |||
| 6c2c48a611 | |||
| 1f36cf9242 | |||
| 3ddac9ac5d | |||
| 1b22b348b0 | |||
| f68721af07 | |||
| d53b7de5ec | |||
| b25f3891c8 | |||
| ca58c9ce09 | |||
| 7a2a535f87 | |||
| 17d220187a | |||
| d3a4857826 | |||
| e64fec15b0 | |||
| 3d5f566d86 | |||
| c87b63cb24 | |||
| cd95f9310b | |||
| 297128c19c | |||
| 25a385c940 | |||
| 181e814139 | |||
| 52e5483dfb | |||
| 56f6c795ed | |||
| 5d8cfa465b | |||
| b6ca4a7a46 | |||
| a45508a96b | |||
| a45d274847 | |||
| e3cc896a42 | |||
| 99a47ae87b | |||
| b1388b6f30 | |||
| 2cddf90e49 | |||
| 85148d1283 | |||
| 7b0b378abc | |||
| a1ecab2f95 | |||
| b7704f0c7f | |||
| d6962f3a09 | |||
| e44ebe0743 | |||
| f02ec41fbc | |||
| d0b4ffd154 | |||
| 265af1f790 | |||
| 241d6b482e | |||
| 45f0af2afc | |||
| 7955066d5c | |||
| ec3f9695cd | |||
| 018c144c76 | |||
| 56e88b2a56 | |||
| a2bdc5d8d9 | |||
| 1b257f7547 | |||
| 5d6b05979d | |||
| 538f8d040a | |||
| 7d52aa1272 | |||
| d316202834 | |||
| 0167dfc3c9 | |||
| 02dc3dee04 | |||
| a5d3bc9578 | |||
| cfbc6b0441 | |||
| 8c5f8e995e | |||
| b77dd6699d | |||
| 9a153708e6 | |||
| e0027a78e1 | |||
| 51e8113a69 | |||
| 78f5753a3a | |||
| 16ca8da438 | |||
| ca6515c58a | |||
| 2ec952032a | |||
| b0b67a0cdc | |||
| 0eed39cdf1 | |||
| ace59411cb | |||
| ef7619d664 | |||
| 0f8a32f38c | |||
| c6a58ff1b4 | |||
| 45f751720a | |||
| ca016e2818 | |||
| 5da01946b8 | |||
| 8b3002a685 | |||
| ede5cca2e7 | |||
| d0e158ca07 | |||
| 09dc21af31 | |||
| 2435fadfbd | |||
| edef9c11f7 | |||
| 2c9edcaf23 | |||
| 18861d58b5 | |||
| 9b3dab8055 | |||
| 7a6a5f6612 | |||
| f468fcc2b6 | |||
| 5d93c68790 | |||
| fa04706484 | |||
| d09be5a80e | |||
| a4d6e5c9cc | |||
| 4f85fd28cf | |||
| 39914cd933 | |||
| 038e8cc8b8 | |||
| db5fd5506d | |||
| f2be495e95 | |||
| 6d8465a04d | |||
| 4bb5270f25 | |||
| 0f06c71796 | |||
| 5b8d8a83dd | |||
| 8ec3d549a6 | |||
| 394e369b54 | |||
| 899ab9ddc0 | |||
| 8ae467985f | |||
| 9ccc8a7404 | |||
| 0b89a9d554 | |||
| 752fb880be | |||
| 549bcb502b | |||
| 44077cb007 | |||
| 7b5902a580 | |||
| 2861bacd2a | |||
| 776ac3bb2a | |||
| b429d9f509 | |||
| 26397e4692 | |||
| 48d23194af | |||
| bcaea1a617 | |||
| 12b43d206c | |||
| 9cc49f6ff3 | |||
| 87f036e538 | |||
| fdebec6448 | |||
| ba37836e24 | |||
| b60cfd9294 | |||
| bc0665cd9f | |||
| 0dbe5e6593 | |||
| 9b33aaa11a | |||
| 024b78da7c | |||
| 2a64297506 | |||
| 09c76939f1 | |||
| 0b3f3bdf92 | |||
| 3b29eeb6b6 | |||
| 985270bb02 | |||
| 6b91d6692f | |||
| eca09e6c81 | |||
| 874c579f86 | |||
| a0bddab169 | |||
| b679a94930 | |||
| 5bde545ce3 | |||
| e68c750389 | |||
| f16cb3c52d | |||
| f7d5b0db9c | |||
| ba0b664e3f | |||
| 149d037824 | |||
| d0349d9b0d | |||
| c71c8b0047 | |||
| 3be644265b | |||
| cd6874ca60 | |||
| 7083978f9d | |||
| 4d132489c1 | |||
| b5170ed1fd | |||
| cc915dbef2 | |||
| 9a5bf1ee95 | |||
| a14b6af5c0 | |||
| ec372ad01d | |||
| 8a412ef69e | |||
| c978364f49 | |||
| 61d2a99dd4 | |||
| fdba916d83 | |||
| a12209e61d | |||
| fc6607ce9e | |||
| 62ccbc7490 | |||
| b2b36984e1 | |||
| c10208407d | |||
| 64720c2d2e | |||
| da81e4839a | |||
| 25013afdd2 | |||
| 678829ab28 | |||
| d8dffa9d71 | |||
| be8810077a | |||
| ee4ecb9783 | |||
| eaed8572f0 | |||
| e91e6fb068 | |||
| cb17a0a49b | |||
| 248df9ca2d | |||
| fa615ed9f4 | |||
| 65aa4af6d9 | |||
| df56ad476e | |||
| 8e20fa89e8 | |||
| 76058729e0 | |||
| 88a0cc8c9c | |||
| 2ea45c0c58 | |||
| bf27f41f52 | |||
| 853f6012d5 | |||
| 14ad02777f | |||
| c1eb00f6d5 | |||
| e5adaf974d | |||
| 727a29600d | |||
| a5ebdb9243 | |||
| 2ef37ee798 | |||
| c23575786c | |||
| 5a0d693c93 | |||
| c6902528b3 | |||
| f90993be86 | |||
| e7db076648 | |||
| 6d52a7d3d4 | |||
| 3368b492b9 | |||
| 1222aa6d25 | |||
| 67efd150d4 | |||
| 9894f2e053 | |||
| 3a1a51ac46 | |||
| a1b5429f4e | |||
| b11e39554f | |||
| a92c764945 | |||
| f581396f1d | |||
| 6814926f05 | |||
| b8f882dba4 | |||
| 783e8edf71 | |||
| 966f1d654e | |||
| 02adaac468 | |||
| 2b2a0d685f | |||
| 6f2ec17640 | |||
| 6c158f5a1b | |||
| df83dfdfe8 | |||
| 165c252fc8 | |||
| d21f63d327 | |||
| 05c33bee78 | |||
| 762dd17512 | |||
| 5f7065848e | |||
| 65e020a627 | |||
| 793e477898 | |||
| a5232f857e | |||
| 0ef7a72b33 | |||
| 717787ff62 | |||
| 2d599a5190 | |||
| e73145d558 | |||
| 220e7748a9 | |||
| 72f41c5b4f | |||
| 775627473a | |||
| ba2356d2ba | |||
| 179f53b21b | |||
| 3ccd3b5650 | |||
| 224655f8cb | |||
| fd96c7c488 | |||
| 45c96f73c2 | |||
| c5893ef6e8 | |||
| 8b1f09d302 | |||
| cb1b824a75 | |||
| b0ce9e4af7 | |||
| aae3637e0c | |||
| 33faa2f252 | |||
| 482d79ef4b | |||
| 6bdd517c43 | |||
| a337109618 | |||
| 0ae5ace520 | |||
| a4306cddef | |||
| 1f97c5518e | |||
| 1823a4da36 | |||
| d60caf7095 | |||
| 9e10b94339 | |||
| 7048dd754d | |||
| de29e17d95 | |||
| c299989ff7 | |||
| 0b655ea6a1 | |||
| 538827942a | |||
| 8bd5f69e04 | |||
| c470ea9fa1 | |||
| c1806476af | |||
| af41c9f6e4 | |||
| 823e8d24b5 | |||
| 4028c0b24f | |||
| 2e4ff30103 | |||
| 121dcca7e3 | |||
| 5adfe2d46b | |||
| 03f4aaa2d6 | |||
| d67c3f36ff | |||
| e199d30beb | |||
| 27e2de2b1f | |||
| 8a6d07342e | |||
| ab39457fe1 | |||
| e6933ea413 | |||
| 422e0c44d5 | |||
| ad03b409ab | |||
| 38ae213260 | |||
| 6a0a59896e | |||
| 187cb56364 | |||
| 1fe96d0d22 | |||
| 52b859b3e6 | |||
| 9483b781e2 | |||
| 791f475168 | |||
| 6b69d6dce2 | |||
| 720abbc81e | |||
| e3183cfd16 | |||
| e07423f260 | |||
| ecfda146b2 | |||
| f923f508f5 | |||
| b465dabe77 | |||
| d8d6b91257 | |||
| 0946fac11f | |||
| c50b69c654 | |||
| 32bf99ba40 | |||
| 0baa8215ef | |||
| 85a007d87b | |||
| 7b14de13ec | |||
| 117ebe1c32 | |||
| 2b010811d8 | |||
| 4b94f1fa83 | |||
| d73f3ebd1b | |||
| 4c48cda4cd | |||
| 9e64ccdbf2 | |||
| aedf9d3a91 | |||
| 0ee94a5729 | |||
| 2130630293 | |||
| 445cf5d42c | |||
| 08f13a1776 | |||
| f406dc51fa | |||
| 7580777e43 | |||
| ea30005762 | |||
| 7794cd6274 | |||
| 45a4fe44f7 | |||
| 1d225078eb | |||
| 533caf8c0f | |||
| e6c25ae290 | |||
| 264077acac | |||
| 0ff18881b6 | |||
| 9e0e536dbc | |||
| 1a9e54ba33 | |||
| 9378af1057 | |||
| cd6075a884 | |||
| 1da3a00b92 | |||
| 275961f088 | |||
| f14fac3f7d | |||
| ce784e0491 | |||
| 65ad3bb941 | |||
| b5b4280c3b | |||
| 0649f6e607 | |||
| 22c2efded9 | |||
| 7825279405 | |||
| 2623c7023c | |||
| 2138fdfadf | |||
| 40517e3010 | |||
| 1341214044 | |||
| 34e2ef13cf | |||
| 058219528d | |||
| f4bd175616 | |||
| fea0c07e1c | |||
| f684fa9a7b | |||
| 2855b94aa8 | |||
| 9958aaae4f | |||
| 289e5ada2c | |||
| 648eed6532 | |||
| d94955d51e | |||
| 046fe93f1f | |||
| ce60af5029 | |||
| dbe70ffc8a | |||
| 8acf081be3 | |||
| c5f6065280 | |||
| 07509ba5c9 | |||
| 8b3c8f7093 | |||
| c3e0b68399 | |||
| f0dd1fdfc7 | |||
| 5fd8c0938a | |||
| b3f4aa4504 | |||
| 4718cf6cc0 | |||
| ac07c78664 | |||
| 4a3db7e2c3 | |||
| a23158f986 | |||
| 838322299a | |||
| 41f3f53540 | |||
| a26d982f52 | |||
| 5e4121ac58 | |||
| 81d8c8f9bf | |||
| 0c34addfa7 | |||
| 6a68fe6a4f | |||
| 0d246a4422 | |||
| 991c9f2f8b | |||
| ffd36b5489 | |||
| b9149658a8 | |||
| 21ff3fbccd | |||
| 90efbefbec | |||
| 8f89d75edc | |||
| 5d082f53ae | |||
| 591d79ba49 | |||
| 5ee6ede824 | |||
| 095a58b1bd | |||
| fd2f000075 | |||
| bb244646df | |||
| 238dd07cf6 | |||
| aea5e446e5 | |||
| 203beff4d2 | |||
| cb6f4ad646 | |||
| f7b72dd136 | |||
| 96aff57275 | |||
| 4b46d07380 | |||
| 204f7c918e | |||
| b1c83e6588 | |||
| c55bd11377 | |||
| 885a9df356 | |||
| 0a373018c6 | |||
| 8c4c1a56fd | |||
| de790c7c19 | |||
| 3c2bf76103 | |||
| 13bc41fc36 | |||
| 758027e2e0 | |||
| 25b352aeab | |||
| b030fe7db2 | |||
| 9b09c9f0c3 | |||
| de73809bb9 | |||
| 7144f69683 | |||
| c3396fa6fd | |||
| 924a41a2f4 | |||
| a492bf3885 | |||
| 662714fca3 | |||
| 4fc4e93ab5 | |||
| d0b5929a9e | |||
| 58dc98460b | |||
| e0dd4848b0 | |||
| cef01f5dbc | |||
| 88af9c545b | |||
| cd1f45e248 | |||
| d5eee3138f | |||
| fdfe0ce63d | |||
| ba6641ed9a | |||
| fb13e3ddb8 | |||
| ca17efd764 | |||
| dd104a49f6 | |||
| b849d008d2 | |||
| 1e09935501 | |||
| d7e558350b | |||
| 04afe8fbf1 | |||
| 392726bad3 | |||
| 19285d61ab | |||
| 015b29e8a3 | |||
| aff61dbc3e | |||
| 37f5d755f1 | |||
| 98cd3465d3 | |||
| 009f08dca3 | |||
| 2edf4906a4 | |||
| ee6c15b997 | |||
| 03df07e4b6 | |||
| 501f2f8267 | |||
| e949677c48 | |||
| b2512915ae | |||
| dc9db06cc8 | |||
| cb5931aaeb | |||
| da17e55657 | |||
| f47d944349 | |||
| 682079adc6 | |||
| f0fb97e67c | |||
| 6e5058f95f | |||
| 31b8997518 | |||
| 76a9723268 | |||
| 5fe79764de | |||
| 186859fcf0 | |||
| b545d60453 | |||
| f68a046080 | |||
| f77fa5b34c | |||
| 38bf0c276c | |||
| ef5c8c24a8 | |||
| 4e8fb92e07 | |||
| 4f1ff5b24c | |||
| 22abc27b6b | |||
| 19f6d885fe | |||
| b19a633441 | |||
| 997dcad799 | |||
| e35efd001c | |||
| 50d6b10be4 | |||
| 5ef7ea794a | |||
| e70485f1fd | |||
| f55f8a3629 | |||
| fbbd6caf97 | |||
| bde3f95ae9 | |||
| a5c406a4dd | |||
| 58a57543ce | |||
| e32bc5e186 | |||
| 7a5f45ac94 | |||
| 657f7577bf | |||
| 48355447f9 | |||
| c8f7e6331b | |||
| 40bb27c95f | |||
| 78f8124c63 | |||
| 94d5bcbf1c | |||
| ad5d8c6f9a | |||
| 4911601b24 | |||
| cb86d3d829 | |||
| 24c6314f1a | |||
| 02e2b3bd47 | |||
| ec84b3a0ca | |||
| 4455e816aa | |||
| 3df89c0dfe | |||
| f5d22c904f | |||
| 2233c44d36 | |||
| d5f6c1c15c | |||
| e36a27307b | |||
| cf527ed819 | |||
| a8e8833a8d | |||
| 349df0dc94 | |||
| 0e33f638e6 | |||
| 50ccfbf554 | |||
| e2c093856c | |||
| 992458ae4b | |||
| 8c16686150 | |||
| 7cbadfbd12 | |||
| 0e43697ee0 | |||
| 7de27df309 | |||
| b06db1ba39 | |||
| 5fe24cb689 | |||
| 733d1384a4 | |||
| 99e9d0d023 | |||
| 2bd1e46ab9 | |||
| cabaad4b17 | |||
| 8b6fe691ed | |||
| ccf9efdaa5 | |||
| bf66bec4c7 | |||
| 3319709526 | |||
| 7ceab5c88c | |||
| 94d4789e68 | |||
| b13985d550 | |||
| dc4a3e9b89 | |||
| d538c35532 | |||
| cfd26321b5 | |||
| 61d955f3de | |||
| a9f54c44e9 | |||
| 125877035f | |||
| 8f4b0c8078 | |||
| 1863e85bf5 | |||
| 488c6099fc | |||
| 67abc6d2cf | |||
| c56a57c0ba | |||
| 30b2c1ad61 | |||
| 5e279363e4 | |||
| abb7509f03 | |||
| 1cd60c05f3 | |||
| 510cc22484 | |||
| 3da664236c | |||
| 7e35a7fe0c | |||
| 58ef7db5d6 | |||
| f9de4a394c | |||
| dced5816df | |||
| 690ac740d2 | |||
| 89d0412e22 | |||
| a305752faf | |||
| 469b139e89 | |||
| 8f5d9c4816 | |||
| 7102c77e0b | |||
| 115ae21f1f | |||
| 8d3fdf45b9 | |||
| 1e7e5ea57c | |||
| 7c8b91456f | |||
| dc960f30f5 | |||
| ba12e31280 | |||
| 027a4081a2 | |||
| 190def17e5 | |||
| 9730526d33 | |||
| c87c843308 | |||
| 6453cdf0d6 | |||
| 5f80c33b24 | |||
| a33542c9b5 | |||
| 3a1d98648f | |||
| 4ec79d61d0 | |||
| b4f055f80c | |||
| 6bf35c679f | |||
| 3349d43c00 | |||
| ced961d24c | |||
| 419f708d87 | |||
| bbc1c04042 | |||
| 7a6865637d | |||
| ae96420925 | |||
| 2822812d0d | |||
| c8ab430491 | |||
| bb87f28e80 | |||
| 6db671215a | |||
| 64566ec6cf | |||
| faaf97ef62 | |||
| 67d6efed21 | |||
| 571d9bd610 | |||
| 4144698540 | |||
| 9bf6bf2c78 | |||
| cc9236b8db | |||
| 0103b59e61 | |||
| e9f6531db7 | |||
| 62c2c750ea | |||
| 3e5e3175e0 | |||
| 54c5af30d9 | |||
| ca4a3361c0 | |||
| 47d302221d | |||
| ed215657e6 | |||
| 3e63b14192 | |||
| 86433ab3a8 | |||
| 7214800d93 | |||
| 29f298fa4a | |||
| ef35bc77cb | |||
| 6ae880f61e | |||
| 0d1149310c | |||
| 51d32bb8d3 | |||
| 49a8980dea | |||
| 9fe1680c56 | |||
| b7db65a023 | |||
| 4c02002265 | |||
| d0198649f2 | |||
| 32c1fd082d | |||
| 363bf42b95 | |||
| a1ab2232f3 | |||
| cf69f43166 | |||
| 9833438733 | |||
| 67c5e3966a | |||
| aa0927911d | |||
| 13c9e1ed58 | |||
| 19a457ab7d | |||
| 0926bff0cc | |||
| 53a0bc963a | |||
| a2819d822a | |||
| 175112d97d | |||
| dced47024f | |||
| ffc361406e | |||
| 852c648235 | |||
| 1197b96c49 | |||
| f21fe81e16 | |||
| 593dfd0108 | |||
| aaf0fb9d12 | |||
| 02e9f8aef3 | |||
| 424055c04e | |||
| 02daf17326 | |||
| a666f0d52e | |||
| 27a760c58c | |||
| 0ba12f48c6 | |||
| eaaf54d886 | |||
| 4b3d634fe9 | |||
| f77a699ea4 | |||
| e989e38cc2 | |||
| 6a900ed6e7 | |||
| 29e6b85f79 | |||
| 2641408d53 | |||
| 5b79226183 | |||
| 98f9f0cd48 | |||
| cf7e107ad1 | |||
| ab66c1aa1b | |||
| 5b03366909 | |||
| 7824e1446a | |||
| 59cf1a4937 | |||
| 252113c838 | |||
| e2f84d1acd | |||
| 204dcecb28 | |||
| 70bbfbea12 | |||
| 2221a0a908 | |||
| 203b215093 | |||
| 76ee84dae8 | |||
| 79e1c87a01 | |||
| ed208114be | |||
| 7dd711b37c | |||
| 4852802d66 | |||
| c495b1faab | |||
| eb5bd8e6ac | |||
| ff7dcf9c17 | |||
| 2b12a16c84 | |||
| 4d00b2ebbb | |||
| 1ba5abbaef | |||
| b87a98bbda | |||
| f76361a345 | |||
| f25bab9555 | |||
| ed78b8429f | |||
| 94e9dd8f28 | |||
| 90215ca0dd | |||
| 34eb647fcb | |||
| 83205290de | |||
| 02fd528c32 | |||
| e05193f46d | |||
| 14e2d900b5 | |||
| ba4290340d | |||
| 5a04700bae | |||
| 999f50dce9 | |||
| 9522e6e714 | |||
| 7836ea4479 | |||
| 37e8ab8de1 | |||
| 34048497e7 | |||
| 5197422482 | |||
| d9987c2734 | |||
| c9671b4712 | |||
| bbf03e3060 | |||
| e35a78e3cf | |||
| e318be6a88 | |||
| 1dab0ebc25 | |||
| 687e58e9d4 | |||
| 993a4aa675 | |||
| c991510b0f | |||
| c8025bf004 | |||
| 9f5767a80c | |||
| ffb05d6271 | |||
| 2dd2d3dd7b | |||
| be29c7fca0 | |||
| 2c23a3c0b3 | |||
| 37e3ead8e9 | |||
| b018ea7b1b | |||
| d6cdca35d4 | |||
| a09d150b47 | |||
| 7cf46afc48 | |||
| 056c95670f | |||
| 35dffe43d9 | |||
| 19c26f0733 | |||
| 388ff10ff5 | |||
| 8c7be8e45f | |||
| 579aa67cde | |||
| 725f86a0d2 | |||
| 50a6ef18d2 | |||
| 1b3e924450 | |||
| a33e08e299 | |||
| 45dee5a666 | |||
| 642473244e | |||
| e92f344335 | |||
| 7951795ea4 | |||
| 101fbcca90 | |||
| d562132a4a | |||
| 7152ab39ff | |||
| 39c5279f5d | |||
| fef0e16f9c | |||
| fb1361b00d | |||
| 64c75c8b57 | |||
| f4e015a255 | |||
| 242b68c488 | |||
| 9452c7e0c7 | |||
| ab1d3d1ced | |||
| 41df92bb93 | |||
| 427db3f29a | |||
| b5600bdd7f | |||
| a2153b668a | |||
| 7d52c82c3b | |||
| 092f1a4959 | |||
| 21bee7e40c | |||
| a9ac0ac725 | |||
| 5dc1ed6696 | |||
| b55f66e69c | |||
| ab637e22b1 | |||
| fa665c5494 | |||
| e91b75a1bb | |||
| 4c6f85fd25 | |||
| f09e9b7790 | |||
| b51d3da7e5 | |||
| 861dbd5977 | |||
| 5001fcdd53 | |||
| eeb47dc724 | |||
| 5556dff11e | |||
| 18b57fa6aa | |||
| 12bb4d75fd | |||
| 411d8d4d98 | |||
| 748df0b21a | |||
| 8d98316a89 | |||
| c6b45a8e09 | |||
| d42920674a | |||
| c66fced161 | |||
| d3dee115ab | |||
| 77aa965533 | |||
| 29499f21c5 | |||
| 18d8968ae7 | |||
| 467cf73635 | |||
| 457519eae3 | |||
| 2a1c24763d | |||
| 92196dc207 | |||
| f73723f9af | |||
| 26ab8755b6 | |||
| d91382208d | |||
| 5f90ed8b3e | |||
| 644fad8355 | |||
| c4904cc365 | |||
| 613e38d7f8 | |||
| dee7c339b3 | |||
| b80cda66a9 | |||
| 411bcbcbde | |||
| 352bf8839b | |||
| 18f438a528 | |||
| 076944da5d | |||
| acbba95032 | |||
| 75f77783ff | |||
| 87e0f074e8 | |||
| 2211c69455 | |||
| a7449c913c | |||
| aaec0e641b | |||
| 5afed2d396 | |||
| 843348338a | |||
| 061ebc7942 | |||
| 822c1c97e3 | |||
| 65df6aa4a0 | |||
| 32719690ac | |||
| f83748fdc2 | |||
| a354d7c00b | |||
| 41f7312263 | |||
| d3804d2c2c | |||
| 96184b83a3 | |||
| 9bcc057176 | |||
| 20f3213477 | |||
| bf8505a285 | |||
| 770a07cc28 | |||
| 3f80f52ca5 | |||
| 52f6e149ec | |||
| 6893df9103 | |||
| 60546c9788 | |||
| 12f91564fb | |||
| ca448e682a | |||
| 2b077d140f | |||
| 0f58ff8356 | |||
| 940b740b24 | |||
| 0f258f94a8 | |||
| fd7b22f639 | |||
| ca3464512d | |||
| f59cb6417f | |||
| 74ccb53607 | |||
| e4c6e98099 | |||
| cfab0fa245 | |||
| 08d861e87c | |||
| 73234e7782 | |||
| de8d28ea5f | |||
| ed7b478ee0 | |||
| ff9a216990 | |||
| 58c94159b1 | |||
| 7a1bd4d53a | |||
| 5d7c23c62a | |||
| d91d911051 | |||
| cd0cab465d | |||
| a45b887c5b | |||
| f965b7129b | |||
| e894ce00c9 | |||
| 333aba8be6 | |||
| e9d93ae210 | |||
| 043c8be747 | |||
| 1be67518df | |||
| 4bf1987bb7 | |||
| ae45eed2b1 | |||
| 572a9393aa | |||
| 7579c41a35 | |||
| 4e589c2575 | |||
| 026da34ce9 | |||
| 0daf49a1da | |||
| f57e4c6c14 | |||
| 14bd1d01f2 | |||
| 918e20b164 | |||
| 6a0e90ef90 | |||
| 71fe8ed80d | |||
| 581c9c570b | |||
| b25fe3d127 | |||
| fc4aef6d89 | |||
| 6b4fa6a40d | |||
| 6756eea209 | |||
| bf3cd37b49 | |||
| cc7caa9e16 | |||
| 633060562f | |||
| cc48546f62 | |||
| 96cc44e521 | |||
| f2d9e4b2df | |||
| 049c7ad1f0 | |||
| 226dad651b | |||
| 0977cabc79 | |||
| 0b5e8a086e | |||
| e024ea5ee1 | |||
| 6f71092aa4 | |||
| edce44df97 | |||
| 54551d9f37 | |||
| 8521b5501f | |||
| 396dc21903 | |||
| 5e2b0ba46c | |||
| cce5d775de | |||
| 248a239c7b | |||
| 83655e9aef | |||
| 973d115edf | |||
| aaa9f89550 | |||
| 8afad5614a | |||
| a9fdabf926 | |||
| b5b262c722 | |||
| d1e1190cbd | |||
| 9812eaaca7 | |||
| 4520e04d37 | |||
| 5679b5862f | |||
| e72dc38d77 | |||
| 71fcfa4935 | |||
| c4463036c3 | |||
| b343a17b29 | |||
| 0b0fb249bf | |||
| 8d689974a7 | |||
| 375c81d38d | |||
| 0ef4f36d44 | |||
| c85f6c1228 | |||
| 0cabc81693 | |||
| 12b4fff81e | |||
| 27b6627110 | |||
| 387014dd37 | |||
| d5c91b9bda | |||
| 370f4f6257 | |||
| 49a665488e | |||
| 29da133726 | |||
| 1194bb8ae3 | |||
| 7599124aa0 | |||
| 01d2d91ecc | |||
| 6980d19214 | |||
| ac14a7b0da | |||
| 23f00ab533 | |||
| 39122bd61d | |||
| c5824765f9 | |||
| 547e942c66 | |||
| 2ab04e7e2f | |||
| a08fef172e | |||
| a04f77e7e0 | |||
| 4900f3e54d | |||
| 34ee6411d5 | |||
| 5abe7bc7e5 | |||
| a48aef4039 | |||
| e1198f29ce | |||
| 329e619c8c | |||
| 9a9e1b46ef | |||
| cc94ab6411 | |||
| e67541e82f | |||
| d907be264b | |||
| 003c1df53e | |||
| 6470838065 | |||
| a178555838 | |||
| f844e9e702 | |||
| 8753dbdb0f | |||
| 1bd24f7042 | |||
| 66d58bf047 | |||
| 1e5c4b0d79 | |||
| 711d402f17 | |||
| e19f54a378 | |||
| 5294fc4767 | |||
| 7ed3a1df04 | |||
| 2d45354f50 | |||
| 9f6d13f4c3 | |||
| 57a31d0ec9 | |||
| da277d9816 | |||
| 22dabc5180 | |||
| 47f11fc3a6 | |||
| 91a2e56215 | |||
| e6681af9d0 | |||
| d9e3c43c91 | |||
| f84de4ac21 | |||
| 3089231476 | |||
| 0f94fb9d6d | |||
| 01097f7225 | |||
| a25e4e1ed8 | |||
| 9e75595754 | |||
| f6d5945b59 | |||
| 250eb20851 | |||
| d2b00c4995 | |||
| 89c73d89b4 | |||
| fcd63bf9eb | |||
| 9a0cf6a609 | |||
| 6c32d97b23 | |||
| c9e40d3e59 | |||
| dc920a7e39 | |||
| 07262387e0 | |||
| 97e2ac689e | |||
| 286a80d03b | |||
| 3aaf31fdb9 | |||
| 71ae5764cc | |||
| 2e41ed153b | |||
| 9ee422e6ba | |||
| 8a008d1808 | |||
| d78faea737 | |||
| 3cf72e93e6 | |||
| caa02bbca1 | |||
| 33e64aee6f | |||
| d17150270e | |||
| 463a297f0c | |||
| 407d73a735 | |||
| b5bfdb56dc | |||
| 58b7ef8431 | |||
| 2211dfb741 | |||
| 1d6e602473 | |||
| a6c52fd154 | |||
| 7a5bceae4c | |||
| 6f3e7f9e56 | |||
| fb9822b507 | |||
| d0db09cb92 | |||
| 189fbb3f7e | |||
| 887c95fe2a | |||
| fdbeb951eb | |||
| 9d23dee3fc | |||
| 70b8cef318 | |||
| e1620b8fa2 | |||
| dabc9589e9 | |||
| da98900e3a | |||
| b584ba9149 | |||
| d24ba6d13d | |||
| aba078a9d8 | |||
| 161487d6c2 | |||
| d851dd7196 | |||
| cf29621aa7 | |||
| 9e46577f43 | |||
| b7980b83df | |||
| 0a0bed7ae0 | |||
| e42b8fc37d | |||
| b01e30acfd | |||
| 311c50df9f | |||
| c2f9467f61 | |||
| 06c8656b9e | |||
| 4748bca8a4 | |||
| 916387748c | |||
| 216d7dcb86 | |||
| 23f2c7e6f5 | |||
| c8e0e5eca2 | |||
| 3167d3f451 | |||
| beb9c1ba74 | |||
| dcd88260e9 | |||
| 5f4f631fc5 | |||
| dc6fda03a1 | |||
| 764ad81537 | |||
| e26c41095f | |||
| d7bd78d612 | |||
| c50b4c72d7 | |||
| 124e555ed0 | |||
| aeba3aa209 | |||
| e50b5202b8 | |||
| 32c7578629 | |||
| 5bc5e0e8e9 | |||
| 7ee4692e84 | |||
| 679688705d | |||
| 83f0a2c094 | |||
| 504817058b | |||
| 45754b27de | |||
| a75ae8bbef | |||
| c3e6dc69bb | |||
| 0285bb6ea5 | |||
| a542292ce2 | |||
| 0765ebf735 | |||
| e86e8c37a8 | |||
| 149a42cc94 | |||
| 8de4613a3e | |||
| 60950c57e3 | |||
| 4727bb5c9b | |||
| 1e1e254897 | |||
| df5455121c | |||
| 0e2570b204 | |||
| f57519b909 | |||
| 088393ca12 | |||
| 375dc63db3 | |||
| 503324162f | |||
| 5445b64376 | |||
| 7fe997d38c | |||
| d7673dad7b | |||
| 44eed9282b | |||
| 0eeb94b24c | |||
| ef221e1c6c | |||
| 75cdefa4fe | |||
| a609a1c6c2 | |||
| f4906a97b9 | |||
| 21ffd441fa | |||
| a82383d78e | |||
| b405029f0f | |||
| db7d4f56b3 | |||
| 3151197471 | |||
| e06fe6935a | |||
| e7cec83a4c | |||
| e5f0ad4f0b | |||
| cabbe70e47 | |||
| b86c3681bb | |||
| 0f8239bc8e | |||
| 13f5baf608 | |||
| 4f5d7f79bd | |||
| 23899613c3 | |||
| db46a915cc | |||
| b591aa013d | |||
| c0db8b71dd | |||
| 444bf41fa4 | |||
| 54302a53e1 | |||
| 63059cfab3 | |||
| ec7d3a9885 | |||
| 960b55fd6c | |||
| 3067ba60ae | |||
| c9a756eddc | |||
| d4ea5fc74f | |||
| 92e52f1a5d | |||
| ef1d75db83 | |||
| f03d1a5d27 | |||
| 8a55b706ca | |||
| 357133677a | |||
| 1b6c660a8f | |||
| 111e68831e | |||
| d9f6732ce2 | |||
| f2b81ba86c | |||
| 2542112301 | |||
| a45215ddf2 | |||
| cb28b2de07 | |||
| bf0b43aa24 | |||
| 811d6deb41 | |||
| bcdd9374c7 | |||
| f70174d1d1 | |||
| 4a39a4af1c | |||
| 0367399c89 | |||
| c5db4dc15f | |||
| 31fbfa3a70 | |||
| 3515ddcfe6 | |||
| 90303d5952 | |||
| 9a02421091 | |||
| 4961b819f3 | |||
| 61c2ea9225 | |||
| f5f7269f70 | |||
| 03f1430ddc | |||
| c3dab15eb6 | |||
| a4f6ec516b | |||
| 1ca213cec6 | |||
| b782c8560c | |||
| d2e68068bf | |||
| 129a846393 | |||
| 15308931e9 | |||
| 4dffeedd1c | |||
| d0c4111429 | |||
| f579b6f971 | |||
| f5124de327 | |||
| 9ab86ae2bf | |||
| 51a7d3ff50 | |||
| 80b9afc14d | |||
| 92ae8f37ba | |||
| df1f828488 | |||
| d8c97da087 | |||
| 9d29e5d87a | |||
| bf62147802 | |||
| 34735b8aad | |||
| 932b2b9acf | |||
| 7ab4472865 | |||
| e1e79206f9 | |||
| 7ebe6de022 | |||
| b234760e92 | |||
| 3119e4b82b | |||
| 2c3f05e6f1 | |||
| 5b5da8698e | |||
| c3c0c8d881 | |||
| 9de5e31a7a | |||
| dbf15c2db2 | |||
| c9c900b364 | |||
| 5c07204351 | |||
| 828b370ea1 | |||
| de947d8b17 | |||
| 8cb5e68b1a | |||
| 99aef2a178 | |||
| fff1460dfb | |||
| e3b619fd54 | |||
| 29e363e7fb | |||
| c209245b0e | |||
| 4194ce3095 | |||
| a0e9f59e04 | |||
| c940c2722e | |||
| 5c84c12f40 | |||
| 1bcacfab74 | |||
| 1f5f8a76fb | |||
| cfa8b1f0dd | |||
| aecbcdd390 | |||
| 74729a372b | |||
| 63d8abe32f | |||
| 2c397bfa0b | |||
| bfef8f39b7 | |||
| 4430cbc3cb | |||
| ae20bac3c5 | |||
| d9a26c4062 | |||
| 48cb374d94 | |||
| 9d6bd91279 | |||
| 163cfda282 | |||
| 72cda375c8 | |||
| ecb4a08655 | |||
| 05c50bc64f | |||
| 747e4a7061 | |||
| 2b8b876713 | |||
| f13694b539 | |||
| 648f9c9723 | |||
| 0a6c37b726 | |||
| b43a51f912 | |||
| fa328c5a78 | |||
| 728bcc2b10 | |||
| 941903a243 | |||
| 4c8579b340 | |||
| 17416a09f1 | |||
| fdda21b114 | |||
| c75ae957f1 | |||
| 8c678126f1 | |||
| 2b0590f9bd | |||
| a31992aac7 | |||
| 829d2505e3 | |||
| df30498365 | |||
| 5d553bf9f2 | |||
| 03e3673dbb | |||
| 25b8b37f00 | |||
| 665f97e782 | |||
| 48a007febd | |||
| 155e0fada6 | |||
| fe2f1d32b5 | |||
| 2fcbb19e72 | |||
| 06a50d637a | |||
| 811b33e31a | |||
| 54c83ee940 | |||
| 7406e83dde | |||
| dc2c7d5339 | |||
| aaa25e5d48 | |||
| b757bd0ea2 | |||
| dd971d6b7d | |||
| c07966408b | |||
| 2557956b68 | |||
| 735260d21b | |||
| e5b0e43e18 | |||
| aa42b2494c | |||
| f057054172 | |||
| c39314c643 | |||
| ae018b759c | |||
| 556b498e06 | |||
| 19f1110421 | |||
| cc73ed19b3 | |||
| ccb7cc40df | |||
| 6d61e950d6 | |||
| 438c6e616c | |||
| 8d2396b81f | |||
| 9588ef00a5 | |||
| 411603ea03 | |||
| 67addfdb37 | |||
| 3afcd31f61 | |||
| 7ac901cb08 | |||
| fcdee8f5d5 | |||
| a7c32dfa95 | |||
| 36f3c0f77a | |||
| dad4981a57 | |||
| df53357ef1 | |||
| 85e333127a | |||
| d52d86ae82 | |||
| f62293275f | |||
| af490bae8e | |||
| edf5a0f677 | |||
| 5eecd0a57d | |||
| 1fc11a6b06 | |||
| 3516538813 | |||
| e3af8cb2e8 | |||
| 16cd822555 | |||
| 5938368a09 | |||
| 50f865b81c | |||
| 96cd09471b | |||
| cf6c1e97bb | |||
| f557e1efb4 | |||
| 8893fd1656 | |||
| 081831e6ea | |||
| 46c3ce0e34 | |||
| 6031a45c68 | |||
| ca64887cba | |||
| 3f1d67ca4c | |||
| ff09af2812 | |||
| 42b68fa834 | |||
| 2cc2c967d1 | |||
| bec4e4437d | |||
| f9abfbf68a | |||
| 53af4ee39f | |||
| af5f2f85da | |||
| ea83231da5 | |||
| c4693602ef | |||
| 3c8dd85966 | |||
| 0399022d25 | |||
| c6840496c0 | |||
| f7657280b6 | |||
| f88a4baf79 | |||
| b051f715e6 | |||
| 81a05c89e4 | |||
| cc55b016e9 | |||
| ae8e51074e | |||
| 313b93169d | |||
| db148741e9 | |||
| 1ecad20a0d | |||
| 22c5c539c2 | |||
| d59e526e1e | |||
| 94fa0047db | |||
| 26e2f9ddd1 | |||
| 23254b4f24 | |||
| 9a44f16846 | |||
| cad0069be2 | |||
| f315a013b8 | |||
| f780aa6160 | |||
| 0f03beb2b5 | |||
| 7e39c43896 | |||
| 906d28fa5d | |||
| 4d017f5d49 | |||
| 6410314b0b | |||
| 1c0202e790 | |||
| 918c978551 | |||
| 3d1a85bbea | |||
| 1a442c36e9 | |||
| a4e322f085 | |||
| 0b1d268b09 | |||
| c2be6bc019 | |||
| 9e11e832da | |||
| 348e656671 | |||
| 54cdf6d6f2 | |||
| 7c5ca8f199 | |||
| ee42d94e2d | |||
| 91baa9dfd7 | |||
| 572b176401 | |||
| 97f1dc0749 | |||
| d67ac1a708 | |||
| 2707001a3a | |||
| bb1064ac42 | |||
| 436cc692da | |||
| 76decf2e8b | |||
| ccdd0f599a | |||
| c65fed1d38 | |||
| c36ec7c95b | |||
| 305ce04284 | |||
| 034ce137a9 | |||
| 73c81a0a6c | |||
| 7544727f7a | |||
| 961a379f79 | |||
| 9ffcaa5550 | |||
| 2dbae6ce51 | |||
| f7505e8877 | |||
| c453f8e7a9 | |||
| 3a4695713d | |||
| 7d9d0878de | |||
| 26729ca482 | |||
| 405600783e | |||
| 5b5e9296b3 | |||
| dd5d520b53 | |||
| b3d517943a | |||
| 59ca59d431 | |||
| 78c92be14b | |||
| adf0c726ef | |||
| 9ecfed4e56 | |||
| 9db884f371 | |||
| e0c211085c | |||
| 555bfaf37a | |||
| bcf4af4a2f | |||
| 6a71163912 | |||
| cb2fcf4f52 | |||
| 061d78c681 | |||
| 0ffa973ec6 | |||
| 03de87c213 | |||
| 59481dd762 | |||
| 86f4aebead | |||
| a92950d234 | |||
| ccdc6f1a63 | |||
| 62cc8f456c | |||
| f7c6b9a0f7 | |||
| 01a6e3c619 | |||
| 6948d56e5d | |||
| 6373b3e49c | |||
| 91dc1b6639 | |||
| 6317b08090 | |||
| aae0547cca | |||
| 4f9c422e2f | |||
| 3595ac08f7 | |||
| 5339f440bd | |||
| 86d7442a5f | |||
| e6e5626a2c | |||
| 4a52dd1c5b | |||
| dd1302e8b1 | |||
| 7b4b7232fc | |||
| b069725df0 | |||
| aa9d8c773f | |||
| c68a03d05c | |||
| cbea651cf8 | |||
| b4ee345966 | |||
| 296e7ccb08 | |||
| 43bb53e789 | |||
| 384be58230 | |||
| 46d1a87b89 | |||
| 7c8bf18cc6 | |||
| 35255d052d | |||
| cf5c776990 | |||
| 9961172d70 | |||
| c1c46544a6 | |||
| fc24dbf9b7 | |||
| 7f311551dd | |||
| 1a6c01769e | |||
| 9da4e18099 | |||
| b19e2fbafe | |||
| 7f69857376 | |||
| a64420d700 | |||
| 2ccd3da5b3 | |||
| 845c73af73 | |||
| f7d79209dc | |||
| 0017aef89a | |||
| 7449f81ed4 | |||
| 724e96f0c1 | |||
| 8158e7f1c5 | |||
| 7d7baa70a9 | |||
| 2e3947cc6d | |||
| 54774726b2 | |||
| 2af9fcad68 | |||
| fd79e81fe5 | |||
| 2b5e110719 | |||
| 864c2d9f49 | |||
| 764fe533e8 | |||
| f1f59d770e | |||
| 950baaa9fc | |||
| 5b3aacb4b2 | |||
| d6adc56b3a | |||
| dbe1a4255c | |||
| eed20b2271 | |||
| e70adbb1cb | |||
| 06c22a478f | |||
| 565b0e97d1 | |||
| 00168cb12f | |||
| ffdaa71086 | |||
| 5de2aa1cbf | |||
| 64988ed831 | |||
| fa050ad616 | |||
| d2458f3169 | |||
| 551af0e45a | |||
| 4ac5440a4a | |||
| 7c54d4e62f | |||
| 8ab5915983 | |||
| 877865a4b1 | |||
| 7cf4d37595 | |||
| 80add9f820 | |||
| 12a35c1214 | |||
| 9379d2462e | |||
| c5e8bed1fb | |||
| 4aa7ade32c | |||
| c4ed218a7a | |||
| ff417fac01 | |||
| 0f4207f85e | |||
| f4431ed837 | |||
| 0235cf5d8e | |||
| 8fc8a4e303 | |||
| 943c15927a | |||
| 28f0782b17 | |||
| 09e331a76b | |||
| ce43b09991 | |||
| f9ab8426ce | |||
| b869184c10 | |||
| 68e39b8053 | |||
| 6574436c33 | |||
| 8e023418e4 | |||
| bf94866ec0 | |||
| cba3c46820 | |||
| d4db5cca1c | |||
| e4fc633f68 | |||
| 9857fb7696 | |||
| 199b1cbcf8 | |||
| 1d38ba78e8 | |||
| c9ce018b9f | |||
| 9b4e31c5ea | |||
| 1e7a5e881e | |||
| 102e87bcc0 | |||
| 604043e3d8 | |||
| 10513784c7 | |||
| 5e3c8cdd8e | |||
| 5a5b4eb1f7 | |||
| e839cb4b8c | |||
| 5c3dad0c61 | |||
| f4a1d2df96 | |||
| cb6104e0ac | |||
| 00ed039621 | |||
| b247c20dbf | |||
| 81d26d7702 | |||
| 0f106c627b | |||
| ac06a2ef03 | |||
| 2ea5714d95 | |||
| a51c7416d5 | |||
| 2231765c9e | |||
| 18c948a211 | |||
| ec56478a3c | |||
| 7e9614b9ec | |||
| 7f7055a9e7 | |||
| ae106a3a90 | |||
| 29344f55ae | |||
| da6b750378 | |||
| 96c872ac37 | |||
| 51f67f3fa0 | |||
| d3dce3cc25 | |||
| 411d4331d4 | |||
| 18aff1ae40 | |||
| 3ff7febde0 | |||
| f7fdb8b54e | |||
| cc7f761c3e | |||
| b10b716733 | |||
| a255f222ef | |||
| b0f035bf3c | |||
| e41d52570e | |||
| 824724630f | |||
| 85c5da8512 | |||
| d79ebabe03 | |||
| f9cab44bc1 | |||
| 636f69b29f | |||
| 86a5bbac51 | |||
| b182cd1fa4 | |||
| 6f5fc14f59 | |||
| f1d47328e9 | |||
| 9a865384a1 | |||
| 7379c8c387 | |||
| 07f644f7d3 | |||
| 07302c3df8 | |||
| 33bd546588 | |||
| a790ffc2d1 | |||
| 6003ae7406 | |||
| 3c6121d192 | |||
| 12500e4721 | |||
| 166133a496 | |||
| 06914b13e0 | |||
| 36856fd646 | |||
| 870ed96c24 | |||
| aed5848f5a | |||
| 210f586d49 | |||
| 94657a3258 | |||
| 7e9eaaddc4 | |||
| bd3f069f47 | |||
| a2c9b5b6d1 | |||
| c4be1d1423 | |||
| f9d336ff88 | |||
| 2c08adab24 | |||
| 5faf8d6951 | |||
| b51f98fe0c | |||
| 0e3d2cf68d | |||
| 868d6ec776 | |||
| e45b7b4b14 | |||
| 3353e482ef | |||
| 11d4d3ec30 | |||
| 7fda7f1f9b | |||
| 7752f1ade0 | |||
| aeff6c8caa | |||
| 954710d6cc | |||
| 1c50c55a42 | |||
| 504368b293 | |||
| 03f8845998 | |||
| 7d0bd77643 | |||
| 07b6073dcf | |||
| 1ec49c3914 | |||
| f575da2ca8 | |||
| 591a06b25e | |||
| cbe48decd1 | |||
| 6f8148950e | |||
| ac68ff192e | |||
| 629cccc6e1 | |||
| 763cc86043 | |||
| a82c9d55b3 | |||
| 9fc3ed939a | |||
| 90b07cc6b0 | |||
| ac15c787cc | |||
| b5ed992e94 | |||
| 7493d8aa49 | |||
| 2c61d0a79e | |||
| 55e58375eb | |||
| 24321d860c | |||
| e8c7f7b5da | |||
| 263dcbd188 | |||
| 10cf41d138 | |||
| f4fd9dbd38 | |||
| 10b650caf4 | |||
| 75561b8278 | |||
| d89be86e4f | |||
| 6c218960e0 | |||
| 92d4b40379 | |||
| 4c0203c108 | |||
| 8b2629c273 | |||
| c19c97804a | |||
| 313466941b | |||
| 48c6145bdf | |||
| 9dd45cceab | |||
| b6303626b5 | |||
| d1d800025d | |||
| 57dc1e03b1 | |||
| ad5b96d6f2 | |||
| b5f2a98e89 | |||
| 3a706cade7 | |||
| d9af615db9 | |||
| f40550b18c | |||
| 3ec4a049c5 | |||
| dc86007fa9 | |||
| 64d14da0fa | |||
| c83ab666d7 | |||
| 599b96ed95 | |||
| 38ba10ddad | |||
| 8794db633b | |||
| c0287c0f2f | |||
| 6fe931fe4c | |||
| 680d3e7d92 | |||
| c5b1cea58e | |||
| 11d45f5a10 | |||
| 5c737709e2 | |||
| 7282e543ca | |||
| ea90627e02 | |||
| b95596d6b7 | |||
| 7400e60599 | |||
| 6eab47d768 | |||
| 3f039f59de | |||
| 2a89aeecd8 | |||
| 386d4c7e72 | |||
| df9a8a32eb | |||
| fa9eeb95b6 | |||
| 13069632be | |||
| a502da7c0f | |||
| 0b2df9892d | |||
| c6dc476704 | |||
| c5ebef3499 | |||
| 8e756a2476 | |||
| 5e1dc11000 | |||
| bcddc10b6b | |||
| 8e31b8860c | |||
| 794c3490fe | |||
| 216157e00d | |||
| 8e6b7766a1 | |||
| c1a08af00d | |||
| 59025ca2da | |||
| 17fab42f25 | |||
| 4b00557fda | |||
| 30465f08ac | |||
| 6fb2a41a0a | |||
| b70b74080e | |||
| 230fec9cdb | |||
| 17ca6b1e07 | |||
| 628f40f822 | |||
| 0994a7a20d | |||
| 66ff69355d | |||
| 04f71e65ee | |||
| 2b0e46caf2 | |||
| 549ce9a4d4 | |||
| 50d3c2c9d1 | |||
| 82caa357cd | |||
| 217fc4a0ae | |||
| ed8bc7ae58 | |||
| 87ff3f9b9b | |||
| 1bef65bf2c | |||
| b39507efc8 | |||
| 263409ed91 | |||
| 7610af3378 | |||
| 97f04eae91 | |||
| a14db578e2 | |||
| 62f31f3e2d | |||
| db9292ee86 | |||
| 1ad274ed2e | |||
| 57d6af5486 | |||
| 8b482e433a | |||
| d4352b84ac | |||
| b23a89d5a8 | |||
| 681d05f8f4 | |||
| 81b9e3b4cb | |||
| 2798c5a48d | |||
| 77c9f96f44 | |||
| 098454c875 | |||
| 6f00e8c936 | |||
| 3c32cafc9c | |||
| 585558a9ae | |||
| 6635d239b0 | |||
| f14c76dbca | |||
| 0fb747834b | |||
| 11175748bb | |||
| 49b7fffd66 | |||
| e69777d20a | |||
| dde4a496f6 | |||
| 48cb43cc79 | |||
| 4229b22f86 | |||
| 958e28050e | |||
| eae9e4bab7 | |||
| c6dfc7723b | |||
| 334f02d7f8 | |||
| 4c02542dc5 | |||
| 84ffec81ec | |||
| 3673b1a324 | |||
| 8cd28276ea | |||
| 2185bb3327 | |||
| 2d64dca565 | |||
| fad13506a7 | |||
| ffed740fdf | |||
| c0eb1d6967 | |||
| e2b208280e | |||
| f9279a3646 | |||
| e0ff8894a2 | |||
| c97d00c399 | |||
| c16c2adeda | |||
| c4326ffd0b | |||
| 85369d6310 | |||
| bd6f2db1d1 | |||
| 02c11caf7f | |||
| 0d087fcbce | |||
| 9a3735aa21 | |||
| 63229c9b3a | |||
| 17bc61767b | |||
| 5ec198b761 | |||
| 8e15a32229 | |||
| 3c903ddffd | |||
| 28f8edf459 | |||
| dc029ebefd | |||
| bb09cac7f4 | |||
| 937a3f88a4 | |||
| 352ee956cd | |||
| a735d467bf | |||
| 71d148caa8 | |||
| 01dc1af5f4 | |||
| dccb9f2424 | |||
| df819d20b5 | |||
| 518ea7ed01 | |||
| 8abbe8db4a | |||
| e36a517e86 | |||
| 7e5d5a6d90 | |||
| 51c0b129c9 | |||
| 96ba31cc2b | |||
| 74cc50f52b | |||
| 75cd287ab0 | |||
| 09088e71b5 | |||
| 6228c2c89b | |||
| 44b625520b | |||
| 1c415933da | |||
| ac588911ba | |||
| c14ceff9b3 | |||
| e3062cf3a2 | |||
| b448177278 | |||
| bcc7c41140 | |||
| 71d184c39b | |||
| 5423cff7a8 | |||
| aeb701f52e | |||
| b16db9fe68 | |||
| 2faf9ad1b6 | |||
| a8a0d1b216 | |||
| aef6d1795e | |||
| ae8dfcb55e | |||
| 9e6ac95e4f | |||
| 7965e29d49 | |||
| 6d823c62f4 | |||
| cc72409980 | |||
| beadb241a8 | |||
| 86dbcc8df2 | |||
| 4a89cfee3d | |||
| 837e91f56d | |||
| 8050fec64f | |||
| 3e0b4ee1e5 | |||
| eac900c6b8 | |||
| 117e8380e2 | |||
| 69f1bfb337 | |||
| b78e9ce02c | |||
| 3fd2725122 | |||
| 6f7cb3d9f2 | |||
| db0bb3d749 | |||
| e055211960 | |||
| 1b581b1c17 | |||
| d335e7d5e7 | |||
| f5857da6f4 | |||
| aad484b7f7 | |||
| b3ae4f5c7b | |||
| 428783e183 | |||
| d26d3bb574 | |||
| 7bd4e213c2 | |||
| bdcbb8670c | |||
| 3edf4799d3 | |||
| 62c12e7473 | |||
| e0f51dd5e9 | |||
| 7222231abf | |||
| 91080f0690 | |||
| 4c2c1f519c | |||
| 0eb5d8db4e | |||
| e43d27e86d | |||
| 87df312a9d | |||
| 1412c5db42 | |||
| 4c58674393 | |||
| 1fdfac4aaf | |||
| e1fe363268 | |||
| a127c705f2 | |||
| 827b9e4be2 | |||
| 417fcde9db | |||
| 818c66e774 | |||
| f69076f55c | |||
| ebafd25e77 | |||
| b055334e73 | |||
| f112f944aa | |||
| 62951f3ab9 | |||
| 3e25f85eb6 | |||
| e544233abb | |||
| d6fd898101 | |||
| ad610ac904 | |||
| 70c4b293ce | |||
| 41e7dbf205 | |||
| 2901e6891c | |||
| a911089913 | |||
| ac1925d4b9 | |||
| 5d2c2a3107 | |||
| bab2c80857 | |||
| d3d90e10a5 | |||
| 4336931f46 | |||
| 58dfd29a3a | |||
| 66c23b5843 | |||
| ea1fb0a078 | |||
| 0381e0dd9d | |||
| 595579a6c2 | |||
| 2143df2e4f | |||
| c86060f5bb | |||
| 4f2e9e8442 | |||
| 411803919d | |||
| ebc3d09f0f | |||
| c2ab16f984 | |||
| 3f3da4ee0b | |||
| 922c218394 | |||
| d5cb1fa15f | |||
| 2cf175cf91 | |||
| 0276cf579a | |||
| 5dd1211f46 | |||
| 1b98bd2c68 | |||
| 7261ce3b7f | |||
| 80a9317d8b | |||
| 7467e11147 | |||
| 6f78bd7a85 | |||
| 03276ad386 | |||
| 9c1caba2fa | |||
| 5ef3b12673 | |||
| cf1e2fa930 | |||
| 14e110178a | |||
| 4e2650d6e7 | |||
| f60f676563 | |||
| 7fddfd7a54 | |||
| fd88aba8a1 | |||
| f95c040bed | |||
| 459ebc7508 | |||
| 9b17543021 | |||
| 1326c5a01f | |||
| f01670db80 | |||
| bddac87321 | |||
| 176fa301d3 | |||
| f2ce4f0ff1 | |||
| a1affa6330 | |||
| 2c14d34f59 | |||
| 4a299fd4ff | |||
| cfec837278 | |||
| 1bf5ca2bec | |||
| 61efd41194 | |||
| 5ebc4b392f | |||
| 4236f92de2 | |||
| 7f0d40f459 | |||
| 6918c7fe1f | |||
| fa62f70cdf | |||
| 8ddb3fc6ad | |||
| df68c3ad48 | |||
| b7bcc401e7 | |||
| 82ead0a405 | |||
| 5ff2fc9250 | |||
| bbad30c6e0 | |||
| a962deaffd | |||
| b353df83ee | |||
| 134c02a762 | |||
| 63c6c06436 | |||
| 1b52f67e5d | |||
| e29b19805c | |||
| 7194fe02d1 | |||
| 863b9829ca | |||
| 666371099e | |||
| 0388860ea1 | |||
| eb90be2ec3 | |||
| 0d0a27d1c2 | |||
| 56c81dd890 | |||
| 00502e0476 | |||
| 2eb251bd7f | |||
| cbddbb598f | |||
| bb5b6fef26 | |||
| 4eb379a912 | |||
| 6769f83d5b | |||
| 358f12d910 | |||
| fe35fefc75 | |||
| 5ed9c41d5e | |||
| 9776158cac | |||
| 486eed4875 | |||
| f6225c0baa | |||
| db2047264c | |||
| 68f0cb7684 | |||
| 57b3f42b03 | |||
| a0a0caad38 | |||
| 30e76b4a32 | |||
| b474d7ec77 | |||
| ad3ec9d856 | |||
| 9f63e5da96 | |||
| 2e878d8f2e | |||
| bfaab86330 | |||
| 78b4d715d5 | |||
| 6dfea376ee | |||
| f0188c428f | |||
| 2987b1d425 | |||
| 86128892d7 | |||
| a8bdc59d5a | |||
| ac6ba24861 | |||
| 5b247a3699 | |||
| 580a760aa1 | |||
| f5da6a0e5f | |||
| a394973762 | |||
| 4f73b4725a | |||
| 71b90ab216 | |||
| c4f213f64e | |||
| cacf8f17d3 | |||
| 819c1729f1 | |||
| 765b1cb3a5 | |||
| 908d92fa5c | |||
| 98ef2e18d4 | |||
| f9e1657dcc | |||
| 0a4aa11d09 | |||
| 200028f328 | |||
| 276071b179 | |||
| 26662b7190 | |||
| 8fdfff3014 | |||
| 7e0fb433e4 | |||
| 0b83310100 | |||
| 8c2bd8bd88 | |||
| 44af60563d | |||
| bb98bc92ef | |||
| 3c08b89923 | |||
| 7baef59b79 | |||
| 6fe580bd75 | |||
| 91d875c157 | |||
| 7fa409a72f | |||
| d8927ec5f8 | |||
| 15751ae1a1 | |||
| bda8431d50 | |||
| 492cb727df | |||
| d4536ba242 | |||
| 876a551cd8 | |||
| 41eb07e35b | |||
| 231c593bb3 | |||
| 3b01d785a5 | |||
| 0c0049f8cc | |||
| d7c44e2bbd | |||
| 0504923689 | |||
| 7e47553c88 | |||
| f8761ecea0 | |||
| ff0eae40ea | |||
| 9d6402aad4 | |||
| 1173ac17ee | |||
| fed4a83a8f | |||
| 8343703115 | |||
| 85eb59eb40 | |||
| e11bd5d99f | |||
| f359927b09 | |||
| 70825abb1d | |||
| c4dc85f607 | |||
| 7e8de26657 | |||
| b3570f57ee | |||
| 3c62aa02d4 | |||
| 58e88d43a9 | |||
| 78ade5b7d7 | |||
| 969d5c8558 | |||
| 539199a7e9 | |||
| 60ffe55565 | |||
| dd03c6b168 | |||
| f53c0d7dfa | |||
| 7872354b49 | |||
| 1ee470f5b4 | |||
| 04e6ca9763 | |||
| 80219a31e2 | |||
| 0555dc4bc3 | |||
| 4401b58f45 | |||
| 8d2d3bd73c | |||
| c7dfed8f29 | |||
| 92f9a29de1 | |||
| ff50cfb4da | |||
| 086e10671f | |||
| 68c1c0a6a6 | |||
| 811d77eccb | |||
| f87450efd6 | |||
| e1a8cad552 | |||
| c0141a9744 | |||
| 221de3343a | |||
| 4c0a54f95f | |||
| 1ce21556a1 | |||
| e104017d4c | |||
| 8377e411cb | |||
| b16f3f91d9 | |||
| c669d46efb | |||
| 03deda313a | |||
| 7983063d05 | |||
| e412366bb2 | |||
| 7035d17290 | |||
| b9e0cb6be1 | |||
| 0a78a50eff | |||
| 323bf67037 | |||
| 9ffcec2fcb | |||
| 964c0425bf | |||
| 19208289dc | |||
| a5c5f4c02c | |||
| 53aede83dc | |||
| e4080aba05 | |||
| 2549f7254a | |||
| 87f4341866 | |||
| 3f6cc725d6 | |||
| a558f447cf | |||
| bd2c99d9ec | |||
| 1a224e2adc | |||
| c64aaab193 | |||
| 556e487a15 | |||
| 30d72543b9 | |||
| af0def36c2 | |||
| 6d8f99832f | |||
| 6a9b05e126 | |||
| c45e2cbb82 | |||
| 4d0c02de2d | |||
| e43019a1df | |||
| 4d19793d69 | |||
| 15358a2e72 | |||
| 73f0e6e825 | |||
| 17738c3861 | |||
| 3712868831 | |||
| a0a71d6e05 | |||
| 1116608389 | |||
| a9b9775fe4 | |||
| 75353852fd | |||
| 64ab2da43b | |||
| 6d74ae1b84 | |||
| 26f3c51cf0 | |||
| b192fb9403 | |||
| 24e72bc6cc | |||
| 9dfbd5f671 | |||
| a65339d011 | |||
| 88890a1914 | |||
| 3ea1461839 | |||
| 164e1be018 | |||
| fffb9c8a2c | |||
| f877788cf1 | |||
| 5940e40d22 | |||
| 9c894fb8b0 | |||
| 29b30bb69c | |||
| 9b340a507b | |||
| 0465753bdf | |||
| 08ebe94005 | |||
| 7afd6d32b7 | |||
| f41495dc7a | |||
| b253721df4 | |||
| 8200f889fb | |||
| ec9a7f8165 | |||
| e83891b4fc | |||
| 91c9cb2114 | |||
| 303d571d23 | |||
| f7cc34c807 | |||
| 8b2c0030d0 | |||
| c273351d4a | |||
| 0cd06be13a | |||
| ee64b2f5fd | |||
| f8499e1113 | |||
| 305f4dc5d0 | |||
| 3e73710245 | |||
| 77d5c44664 | |||
| 22e1e6c92f | |||
| e70bcfb343 | |||
| 93bccf36ba | |||
| 1720a2b123 | |||
| b0ecb869f2 | |||
| 58359fe4ac | |||
| 9fdb6eb2c5 | |||
| 9b80d44873 | |||
| 9673980fae | |||
| ec579fb6be | |||
| 3cc3d4fd3b | |||
| 63f4aaf7dd | |||
| 9776373784 | |||
| f8c0c468a9 | |||
| 3e3c5f72e9 | |||
| f332f39ea6 | |||
| 39be1ec116 | |||
| c3bab284ba | |||
| 28581de0ee | |||
| 2bd749b0f7 | |||
| 9586bc26c7 | |||
| e8ef6c8b8a | |||
| 300f7370c8 | |||
| 8d83ce47e9 | |||
| ade2ecdb8e | |||
| 8839a5eb3f | |||
| d61ece7891 | |||
| f736bb9c9a | |||
| f8fb79a838 | |||
| 5ad4992816 | |||
| 02e15bd3d2 | |||
| 25d15fe2af | |||
| fdb4198c0d | |||
| 3f7437fec2 | |||
| ed0733e125 | |||
| cc3255702f | |||
| b53f3b1b5c | |||
| 795d2b264c | |||
| ab961486d9 | |||
| f35a82e36a | |||
| f841a7a25a | |||
| d01f350e94 | |||
| 0e589ecbcd | |||
| 1f381d790f | |||
| e6ce9d3d29 | |||
| a88ad71be6 | |||
| 48daf38f2b | |||
| 7d42330352 | |||
| dfd227cfc9 | |||
| 568dd9bf3c | |||
| 3507a805a9 | |||
| e248517535 | |||
| 0984a669ce | |||
| 15f2b21b62 | |||
| 4aaecfb4f9 | |||
| e6941df981 | |||
| 632f344db5 | |||
| 1f1b472568 | |||
| 20fa2f8738 | |||
| d3bf394b16 | |||
| 887092bacd | |||
| 61aa095722 | |||
| 7aa2700d8f | |||
| e842c7b28b | |||
| 091cd57f68 | |||
| 045b1e0cb8 | |||
| e7bc232eb1 | |||
| 4eaaf91794 | |||
| 9542ab0482 | |||
| f427550bb7 | |||
| ef9c3c8431 | |||
| 9eef17506d | |||
| 801490b9fc | |||
| 169988b3c4 | |||
| 695fecddeb | |||
| 429eb316d0 | |||
| a99a26aeab | |||
| ae93423116 | |||
| b897b5d0e2 | |||
| 1744e69214 | |||
| e034733ac6 | |||
| ac41c3b06c | |||
| 3c8cea2af6 | |||
| 99e8f36901 | |||
| eeea5b0e81 | |||
| b210b22975 | |||
| 84b35f47a1 | |||
| 0727ff09fa | |||
| fe416526f2 | |||
| 3c10844067 | |||
| 94bd6e22ff | |||
| d11524781e | |||
| 8d16c047cc | |||
| f94ebf5296 | |||
| 8cddaf39c4 | |||
| 7ce75733d0 | |||
| e8af613aaa | |||
| 96f2358d28 | |||
| bb8645dd18 | |||
| 12c547126e | |||
| 78d6a25d98 | |||
| 2c4621011d | |||
| 7a6c36d43c | |||
| d9f0d92262 | |||
| cc91ee1c52 | |||
| 0dc77b056a | |||
| c793672d63 | |||
| 5136853ce0 | |||
| 9da873330a | |||
| bd67e7a4ed | |||
| 9eb73f9306 | |||
| 5e275b3935 | |||
| c98f19c5a6 | |||
| 66b4f65d84 | |||
| b85898c5da | |||
| f1332c7c6a | |||
| 8a47c87a8a | |||
| 88b2d8a8ef | |||
| 2fd3ebc8fb | |||
| 204f522ea4 | |||
| 1f986598f1 | |||
| 6996b24c44 | |||
| b44e36e7ba | |||
| 7c41206c6d | |||
| ea6c0353be | |||
| 8a665bc044 | |||
| f68d53ead6 | |||
| 93b1ff1c5d | |||
| d41b0f28b4 | |||
| c686d28675 | |||
| 448095ff98 | |||
| 835bf436e4 | |||
| 6da047005a | |||
| 571824415c | |||
| bef3aad423 | |||
| 7d21a4e0fb | |||
| e9258a238e | |||
| a21376f6ce | |||
| 664f133c4b | |||
| f4cf3596c5 | |||
| d4eebd7c0d | |||
| 70256146fc | |||
| 057e3a936e | |||
| 0aab2510d3 | |||
| ed826e4451 | |||
| 8efb9a38ea | |||
| 8e1d69ac8b | |||
| ee1b3afdcf | |||
| 62aab1cec6 | |||
| 44c07e9504 | |||
| 1c74f84647 | |||
| 1741ce7ec9 | |||
| 53d87eb9be | |||
| 6e7bb505c1 | |||
| e1f137d59a | |||
| 6d7dd2cf39 | |||
| ebf3eea004 | |||
| 913c2e5713 | |||
| d2b5b8d854 | |||
| 5c10d2fae0 | |||
| 738c3eae91 | |||
| 83968bc1cd | |||
| 4cd615ba2e | |||
| ac1908d464 | |||
| 95d8d44259 | |||
| d57be5244c | |||
| 5ef4909bdd | |||
| 83e160d14d | |||
| 9a2e11c0d7 | |||
| 41b381f567 | |||
| ecb979a14b | |||
| 84bba8aa20 | |||
| b133beed5e | |||
| 90502a0c15 | |||
| f5226b2228 | |||
| ad77d6cfec | |||
| a098e38c5a | |||
| a7e7773d85 | |||
| 0df0c85fea | |||
| fdd85a6328 | |||
| 1409d0e06e | |||
| 4765409fe7 | |||
| dad24f1106 | |||
| 9ea39d280c | |||
| a04eb2bba1 | |||
| e849d18c1b | |||
| 57868f38ef | |||
| 85f9b398da | |||
| e081cf1c3d | |||
| 7c85b2f85a | |||
| ec3bcb43a8 | |||
| 3a984668d6 | |||
| b08104367e | |||
| 1fb97f0080 | |||
| 79a989e504 | |||
| d7f8eafe2d | |||
| 6e74ac9bd2 | |||
| 82c2e9978f | |||
| e04ef5a030 | |||
| 0906a0fe1c | |||
| d07994bd73 | |||
| e8699d4337 | |||
| aa0ce0fb88 | |||
| 435efc9886 | |||
| e64e45015a | |||
| 8e9461ea6a | |||
| 1262600895 | |||
| 8589436158 | |||
| 3767ffd79a | |||
| d81c632ef2 | |||
| 18abdd0cd6 | |||
| 0cad039615 | |||
| abfb49818f | |||
| afe9157d65 | |||
| fe12ba23b0 | |||
| 4f54c90a95 | |||
| 045922fb56 | |||
| 4f5678fc95 | |||
| 3986b30b00 | |||
| 7be723782a | |||
| f601664620 | |||
| bd2080fdb5 | |||
| b4585f24bb | |||
| 21c9132902 | |||
| 9abb317e6d | |||
| 5236fb8c0e | |||
| fb661ab6f6 | |||
| 9d22fca5e7 | |||
| eb1d629ea7 | |||
| fd3ce305f4 | |||
| 928e1178b1 | |||
| ff5e34ee70 | |||
| e8d52c0f01 | |||
| 275808d0d5 | |||
| 55488b1fb6 | |||
| e50e6ada6b | |||
| f511af0f0c | |||
| f10bf842c6 | |||
| e3ad54996f | |||
| aac6f45f54 |
@@ -0,0 +1,54 @@
|
||||
---
|
||||
Language: Cpp
|
||||
# BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -2
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
AlignEscapedNewlinesLeft: false
|
||||
AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: true
|
||||
AlwaysBreakTemplateDeclarations: false
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
BreakBeforeBinaryOperators: false
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BinPackParameters: false
|
||||
ColumnLimit: 100
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
DerivePointerBinding: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
IndentCaseLabels: false
|
||||
MaxEmptyLinesToKeep: 1
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
NamespaceIndentation: None
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyExcessCharacter: 1000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PointerBindsToType: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
Cpp11BracedListStyle: true
|
||||
Standard: Cpp11
|
||||
IndentWidth: 4
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
BreakBeforeBraces: Allman
|
||||
IndentFunctionDeclarationAfterType: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInAngles: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
ContinuationIndentWidth: 4
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ]
|
||||
SpaceBeforeParens: ControlStatements
|
||||
...
|
||||
|
||||
@@ -28,16 +28,16 @@
|
||||
|
||||
# OS generated files #
|
||||
######################
|
||||
.DS_Store?
|
||||
.DS_Store
|
||||
ehthumbs.db
|
||||
Icon?
|
||||
Thumbs.db
|
||||
|
||||
# SCons related files #
|
||||
# build related files #
|
||||
#######################
|
||||
SconsBuilder*
|
||||
.scon*
|
||||
.build
|
||||
/build/
|
||||
/Util/UUID.cpp
|
||||
/Util/GitDescription.cpp
|
||||
|
||||
# Eclipse related files #
|
||||
#########################
|
||||
@@ -54,19 +54,39 @@ stxxl.errlog
|
||||
|
||||
# compiled protobuffers #
|
||||
#########################
|
||||
DataStructures/pbf-proto/*.pb.h
|
||||
DataStructures/pbf-proto/*.pb.cc
|
||||
/DataStructures/pbf-proto/*.pb.h
|
||||
/DataStructures/pbf-proto/*.pb.cc
|
||||
|
||||
# External Libs #
|
||||
#################
|
||||
lib/
|
||||
/lib/
|
||||
/win/lib
|
||||
|
||||
# Visual Studio Temp + build Files #
|
||||
####################################
|
||||
vcprojects/*.user
|
||||
vcprojects/*.ncb
|
||||
vcprojects/*.suo
|
||||
vcprojects/Debug/
|
||||
vcprojects/Release/
|
||||
bin/
|
||||
bin-debug/
|
||||
/win/*.user
|
||||
/win/*.ncb
|
||||
/win/*.suo
|
||||
/win/Debug/
|
||||
/win/Release/
|
||||
/win/bin/
|
||||
/win/bin-debug/
|
||||
/osrm-extract
|
||||
/osrm-io-benchmark
|
||||
/osrm-components
|
||||
/osrm-routed
|
||||
/osrm-datastore
|
||||
/osrm-prepare
|
||||
/osrm-unlock-all
|
||||
/osrm-cli
|
||||
/nohup.out
|
||||
|
||||
# Sandbox folder #
|
||||
###################
|
||||
/sandbox/
|
||||
|
||||
/test/profile.lua
|
||||
|
||||
# Deprecated config file #
|
||||
##########################
|
||||
/server.ini
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
language: cpp
|
||||
compiler:
|
||||
- gcc
|
||||
# - clang
|
||||
# Make sure CMake is installed
|
||||
install:
|
||||
- sudo apt-add-repository -y ppa:ubuntu-toolchain-r/test
|
||||
- sudo add-apt-repository -y ppa:boost-latest/ppa
|
||||
- sudo apt-get update >/dev/null
|
||||
- sudo apt-get -q install libprotoc-dev libprotobuf7 libprotobuf-dev libosmpbf-dev libbz2-dev libstxxl-dev libstxxl1 libxml2-dev libzip-dev lua5.1 liblua5.1-0-dev rubygems
|
||||
- sudo apt-get -q install g++-4.7
|
||||
- sudo apt-get install libboost1.54-all-dev
|
||||
#luabind
|
||||
- curl https://gist.githubusercontent.com/DennisOSRM/f2eb7b948e6fe1ae319e/raw/install-luabind.sh | sudo bash
|
||||
#osmosis
|
||||
- curl -s https://gist.githubusercontent.com/DennisOSRM/803a64a9178ec375069f/raw/ | sudo bash
|
||||
before_script:
|
||||
- rvm use 1.9.3
|
||||
- gem install bundler
|
||||
- bundle install
|
||||
- mkdir build
|
||||
- cd build
|
||||
- cmake .. $CMAKEOPTIONS
|
||||
script:
|
||||
- make -j 2
|
||||
- cd ..
|
||||
- cucumber -p verify
|
||||
after_script:
|
||||
# - cd ..
|
||||
# - cucumber -p verify
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- develop
|
||||
cache:
|
||||
- bundler
|
||||
- apt
|
||||
env:
|
||||
- CMAKEOPTIONS="-DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_COMPILER=g++-4.7" OSRM_PORT=5000 OSRM_TIMEOUT=60
|
||||
- CMAKEOPTIONS="-DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER=g++-4.7" OSRM_PORT=5010 OSRM_TIMEOUT=60
|
||||
notifications:
|
||||
irc:
|
||||
channels:
|
||||
- irc.oftc.net#osrm
|
||||
on_success: change
|
||||
on_failure: always
|
||||
use_notice: true
|
||||
skip_join: false
|
||||
|
||||
recipients:
|
||||
- dennis@mapbox.com
|
||||
email:
|
||||
on_success: change
|
||||
on_failure: always
|
||||
@@ -1,10 +0,0 @@
|
||||
The following people contributed code to the Open Source Routing Machine:
|
||||
|
||||
Christian Vetter
|
||||
Dennis Luxen
|
||||
Ruslan Krenzler
|
||||
Frederik Ramm
|
||||
Bharath Vissapragada
|
||||
Pascal Neis
|
||||
Sasa Ivetic
|
||||
Emil Tin
|
||||
@@ -0,0 +1,140 @@
|
||||
#ifndef __BFS_COMPONENT_EXPLORER_H__
|
||||
#define __BFS_COMPONENT_EXPLORER_H__
|
||||
|
||||
#include "../typedefs.h"
|
||||
#include "../DataStructures/DynamicGraph.h"
|
||||
#include "../DataStructures/RestrictionMap.h"
|
||||
|
||||
#include <queue>
|
||||
#include <unordered_set>
|
||||
|
||||
// Explores the components of the given graph while respecting turn restrictions
|
||||
// and barriers.
|
||||
template <typename GraphT> class BFSComponentExplorer
|
||||
{
|
||||
public:
|
||||
BFSComponentExplorer(const GraphT &dynamicGraph,
|
||||
const RestrictionMap &restrictions,
|
||||
const std::unordered_set<NodeID> &barrier_nodes)
|
||||
: m_graph(dynamicGraph), m_restriction_map(restrictions), m_barrier_nodes(barrier_nodes)
|
||||
{
|
||||
BOOST_ASSERT(m_graph.GetNumberOfNodes() > 0);
|
||||
}
|
||||
|
||||
/*!
|
||||
* Returns the size of the component that the node belongs to.
|
||||
*/
|
||||
inline unsigned int GetComponentSize(NodeID node)
|
||||
{
|
||||
BOOST_ASSERT(node < m_component_index_list.size());
|
||||
|
||||
return m_component_index_size[m_component_index_list[node]];
|
||||
}
|
||||
|
||||
inline unsigned int GetNumberOfComponents() { return m_component_index_size.size(); }
|
||||
|
||||
/*!
|
||||
* Computes the component sizes.
|
||||
*/
|
||||
void run()
|
||||
{
|
||||
std::queue<std::pair<NodeID, NodeID>> bfs_queue;
|
||||
unsigned current_component = 0;
|
||||
|
||||
BOOST_ASSERT(m_component_index_list.empty());
|
||||
BOOST_ASSERT(m_component_index_size.empty());
|
||||
|
||||
unsigned num_nodes = m_graph.GetNumberOfNodes();
|
||||
|
||||
m_component_index_list.resize(num_nodes, std::numeric_limits<unsigned>::max());
|
||||
|
||||
BOOST_ASSERT(num_nodes > 0);
|
||||
|
||||
// put unexplorered node with parent pointer into queue
|
||||
for (NodeID node = 0; node < num_nodes; ++node)
|
||||
{
|
||||
if (std::numeric_limits<unsigned>::max() == m_component_index_list[node])
|
||||
{
|
||||
unsigned size = ExploreComponent(bfs_queue, node, current_component);
|
||||
|
||||
// push size into vector
|
||||
m_component_index_size.emplace_back(size);
|
||||
++current_component;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/*!
|
||||
* Explores the current component that starts at node using BFS.
|
||||
*/
|
||||
inline unsigned ExploreComponent(std::queue<std::pair<NodeID, NodeID>> &bfs_queue,
|
||||
NodeID node,
|
||||
unsigned current_component)
|
||||
{
|
||||
bfs_queue.emplace(node, node);
|
||||
// mark node as read
|
||||
m_component_index_list[node] = current_component;
|
||||
|
||||
unsigned current_component_size = 1;
|
||||
|
||||
while (!bfs_queue.empty())
|
||||
{
|
||||
// fetch element from BFS queue
|
||||
std::pair<NodeID, NodeID> current_queue_item = bfs_queue.front();
|
||||
bfs_queue.pop();
|
||||
|
||||
const NodeID v = current_queue_item.first; // current node
|
||||
const NodeID u = current_queue_item.second; // parent
|
||||
// increment size counter of current component
|
||||
++current_component_size;
|
||||
const bool is_barrier_node = (m_barrier_nodes.find(v) != m_barrier_nodes.end());
|
||||
if (!is_barrier_node)
|
||||
{
|
||||
const NodeID to_node_of_only_restriction =
|
||||
m_restriction_map.CheckForEmanatingIsOnlyTurn(u, v);
|
||||
|
||||
for (auto e2 : m_graph.GetAdjacentEdgeRange(v))
|
||||
{
|
||||
const NodeID w = m_graph.GetTarget(e2);
|
||||
|
||||
if (to_node_of_only_restriction != std::numeric_limits<unsigned>::max() &&
|
||||
w != to_node_of_only_restriction)
|
||||
{
|
||||
// At an only_-restriction but not at the right turn
|
||||
continue;
|
||||
}
|
||||
|
||||
if (u != w)
|
||||
{
|
||||
// only add an edge if turn is not a U-turn except
|
||||
// when it is at the end of a dead-end street.
|
||||
if (!m_restriction_map.CheckIfTurnIsRestricted(u, v, w))
|
||||
{
|
||||
// only add an edge if turn is not prohibited
|
||||
if (std::numeric_limits<unsigned>::max() == m_component_index_list[w])
|
||||
{
|
||||
// insert next (node, parent) only if w has
|
||||
// not yet been explored
|
||||
// mark node as read
|
||||
m_component_index_list[w] = current_component;
|
||||
bfs_queue.emplace(w, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return current_component_size;
|
||||
}
|
||||
|
||||
std::vector<unsigned> m_component_index_list;
|
||||
std::vector<NodeID> m_component_index_size;
|
||||
|
||||
const GraphT &m_graph;
|
||||
const RestrictionMap &m_restriction_map;
|
||||
const std::unordered_set<NodeID> &m_barrier_nodes;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <osrm/Coordinate.h>
|
||||
|
||||
#include "DouglasPeucker.h"
|
||||
#include "../DataStructures/SegmentInformation.h"
|
||||
#include "../Util/SimpleLogger.h"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <limits>
|
||||
|
||||
DouglasPeucker::DouglasPeucker()
|
||||
: douglas_peucker_thresholds({262144., // z0
|
||||
131072., // z1
|
||||
65536., // z2
|
||||
32768., // z3
|
||||
16384., // z4
|
||||
8192., // z5
|
||||
4096., // z6
|
||||
2048., // z7
|
||||
960., // z8
|
||||
480., // z9
|
||||
240., // z10
|
||||
90., // z11
|
||||
50., // z12
|
||||
25., // z13
|
||||
15., // z14
|
||||
5., // z15
|
||||
.65, // z16
|
||||
.5, // z17
|
||||
.35 // z18
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
void DouglasPeucker::Run(std::vector<SegmentInformation> &input_geometry, const unsigned zoom_level)
|
||||
{
|
||||
input_geometry.front().necessary = true;
|
||||
input_geometry.back().necessary = true;
|
||||
|
||||
BOOST_ASSERT_MSG(!input_geometry.empty(), "geometry invalid");
|
||||
if (input_geometry.size() < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SimpleLogger().Write() << "input_geometry.size()=" << input_geometry.size();
|
||||
|
||||
{
|
||||
BOOST_ASSERT_MSG(zoom_level < 19, "unsupported zoom level");
|
||||
unsigned left_border = 0;
|
||||
unsigned right_border = 1;
|
||||
// Sweep over array and identify those ranges that need to be checked
|
||||
do
|
||||
{
|
||||
if (!input_geometry[left_border].necessary)
|
||||
{
|
||||
SimpleLogger().Write() << "broken interval [" << left_border << "," << right_border << "]";
|
||||
}
|
||||
BOOST_ASSERT_MSG(input_geometry[left_border].necessary,
|
||||
"left border must be necessary");
|
||||
BOOST_ASSERT_MSG(input_geometry.back().necessary, "right border must be necessary");
|
||||
|
||||
if (input_geometry[right_border].necessary)
|
||||
{
|
||||
recursion_stack.emplace(left_border, right_border);
|
||||
left_border = right_border;
|
||||
}
|
||||
++right_border;
|
||||
} while (right_border < input_geometry.size());
|
||||
}
|
||||
while (!recursion_stack.empty())
|
||||
{
|
||||
// pop next element
|
||||
const GeometryRange pair = recursion_stack.top();
|
||||
recursion_stack.pop();
|
||||
BOOST_ASSERT_MSG(input_geometry[pair.first].necessary, "left border mus be necessary");
|
||||
BOOST_ASSERT_MSG(input_geometry[pair.second].necessary, "right border must be necessary");
|
||||
BOOST_ASSERT_MSG(pair.second < input_geometry.size(), "right border outside of geometry");
|
||||
BOOST_ASSERT_MSG(pair.first < pair.second, "left border on the wrong side");
|
||||
double max_distance = std::numeric_limits<double>::min();
|
||||
|
||||
unsigned farthest_element_index = pair.second;
|
||||
// find index idx of element with max_distance
|
||||
for (unsigned i = pair.first + 1; i < pair.second; ++i)
|
||||
{
|
||||
const double temp_dist = FixedPointCoordinate::ComputePerpendicularDistance(
|
||||
input_geometry[i].location,
|
||||
input_geometry[pair.first].location,
|
||||
input_geometry[pair.second].location);
|
||||
const double distance = std::abs(temp_dist);
|
||||
if (distance > douglas_peucker_thresholds[zoom_level] && distance > max_distance)
|
||||
{
|
||||
farthest_element_index = i;
|
||||
max_distance = distance;
|
||||
}
|
||||
}
|
||||
if (max_distance > douglas_peucker_thresholds[zoom_level])
|
||||
{
|
||||
// mark idx as necessary
|
||||
input_geometry[farthest_element_index].necessary = true;
|
||||
if (1 < (farthest_element_index - pair.first))
|
||||
{
|
||||
recursion_stack.emplace(pair.first, farthest_element_index);
|
||||
}
|
||||
if (1 < (pair.second - farthest_element_index))
|
||||
{
|
||||
recursion_stack.emplace(farthest_element_index, pair.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,126 +1,62 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef DOUGLASPEUCKER_H_
|
||||
#define DOUGLASPEUCKER_H_
|
||||
|
||||
#include <cfloat>
|
||||
#include <stack>
|
||||
#include <vector>
|
||||
|
||||
/*This class object computes the bitvector of indicating generalized input points
|
||||
* according to the (Ramer-)Douglas-Peucker algorithm.
|
||||
/* This class object computes the bitvector of indicating generalized input
|
||||
* points according to the (Ramer-)Douglas-Peucker algorithm.
|
||||
*
|
||||
* Input is vector of pairs. Each pair consists of the point information and a bit
|
||||
* indicating if the points is present in the generalization.
|
||||
* Input is vector of pairs. Each pair consists of the point information and a
|
||||
* bit indicating if the points is present in the generalization.
|
||||
* Note: points may also be pre-selected*/
|
||||
|
||||
//These thresholds are more or less heuristically chosen.
|
||||
static double DouglasPeuckerThresholds[19] = { 10240000., 512., 2560000., 1280000., 640000., 320000., 160000., 80000., 40000., 20000., 10000., 5000., 2400., 1200., 200, 16, 6, 3., 1. };
|
||||
struct FixedPointCoordinate;
|
||||
struct SegmentInformation;
|
||||
|
||||
template<class PointT>
|
||||
class DouglasPeucker {
|
||||
private:
|
||||
typedef std::pair<std::size_t, std::size_t> PairOfPoints;
|
||||
//Stack to simulate the recursion
|
||||
std::stack<PairOfPoints > recursionStack;
|
||||
class DouglasPeucker
|
||||
{
|
||||
private:
|
||||
std::vector<double> douglas_peucker_thresholds;
|
||||
|
||||
double ComputeDistance(const _Coordinate& inputPoint, const _Coordinate& source, const _Coordinate& target) {
|
||||
double r;
|
||||
const double x = (double)inputPoint.lat;
|
||||
const double y = (double)inputPoint.lon;
|
||||
const double a = (double)source.lat;
|
||||
const double b = (double)source.lon;
|
||||
const double c = (double)target.lat;
|
||||
const double d = (double)target.lon;
|
||||
double p,q,mX,nY;
|
||||
if(c != a) {
|
||||
const double m = (d-b)/(c-a); // slope
|
||||
// Projection of (x,y) on line joining (a,b) and (c,d)
|
||||
p = ((x + (m*y)) + (m*m*a - m*b))/(1 + m*m);
|
||||
q = b + m*(p - a);
|
||||
} else {
|
||||
p = c;
|
||||
q = y;
|
||||
}
|
||||
nY = (d*p - c*q)/(a*d - b*c);
|
||||
mX = (p - nY*a)/c;// These values are actually n/m+n and m/m+n , we neednot calculate the values of m an n as we are just interested in the ratio
|
||||
r = mX;
|
||||
if(r<=0){
|
||||
return ((b - y)*(b - y) + (a - x)*(a - x));
|
||||
}
|
||||
else if(r >= 1){
|
||||
return ((d - y)*(d - y) + (c - x)*(c - x));
|
||||
typedef std::pair<unsigned, unsigned> GeometryRange;
|
||||
// Stack to simulate the recursion
|
||||
std::stack<GeometryRange> recursion_stack;
|
||||
|
||||
}
|
||||
// point lies in between
|
||||
return (p-x)*(p-x) + (q-y)*(q-y);
|
||||
}
|
||||
double ComputeDistance(const FixedPointCoordinate &point,
|
||||
const FixedPointCoordinate &segA,
|
||||
const FixedPointCoordinate &segB) const;
|
||||
|
||||
public:
|
||||
void Run(std::vector<PointT> & inputVector, const unsigned zoomLevel) {
|
||||
{
|
||||
assert(zoomLevel < 19);
|
||||
assert(1 < inputVector.size());
|
||||
std::size_t leftBorderOfRange = 0;
|
||||
std::size_t rightBorderOfRange = 1;
|
||||
//Sweep linerarily over array and identify those ranges that need to be checked
|
||||
do {
|
||||
assert(inputVector[leftBorderOfRange].necessary);
|
||||
assert(inputVector[inputVector.size()-1].necessary);
|
||||
|
||||
if(inputVector[rightBorderOfRange].necessary) {
|
||||
recursionStack.push(std::make_pair(leftBorderOfRange, rightBorderOfRange));
|
||||
leftBorderOfRange = rightBorderOfRange;
|
||||
}
|
||||
++rightBorderOfRange;
|
||||
} while( rightBorderOfRange < inputVector.size());
|
||||
}
|
||||
while(!recursionStack.empty()) {
|
||||
//pop next element
|
||||
const PairOfPoints pair = recursionStack.top();
|
||||
recursionStack.pop();
|
||||
assert(inputVector[pair.first].necessary);
|
||||
assert(inputVector[pair.second].necessary);
|
||||
assert(pair.second < inputVector.size());
|
||||
assert(pair.first < pair.second);
|
||||
double maxDistance = -DBL_MAX;
|
||||
std::size_t indexOfFarthestElement = pair.second;
|
||||
//find index idx of element with maxDistance
|
||||
for(std::size_t i = pair.first+1; i < pair.second; ++i){
|
||||
double distance = std::fabs(ComputeDistance(inputVector[i].location, inputVector[pair.first].location, inputVector[pair.second].location));
|
||||
if(distance > DouglasPeuckerThresholds[zoomLevel] && distance > maxDistance) {
|
||||
indexOfFarthestElement = i;
|
||||
maxDistance = distance;
|
||||
}
|
||||
}
|
||||
if (maxDistance > DouglasPeuckerThresholds[zoomLevel]) {
|
||||
// mark idx as necessary
|
||||
inputVector[indexOfFarthestElement].necessary = true;
|
||||
if (1 < indexOfFarthestElement - pair.first) {
|
||||
recursionStack.push(std::make_pair(pair.first, indexOfFarthestElement) );
|
||||
}
|
||||
if (1 < pair.second - indexOfFarthestElement)
|
||||
recursionStack.push(std::make_pair(indexOfFarthestElement, pair.second) );
|
||||
}
|
||||
}
|
||||
}
|
||||
public:
|
||||
DouglasPeucker();
|
||||
void Run(std::vector<SegmentInformation> &input_geometry, const unsigned zoom_level);
|
||||
};
|
||||
|
||||
#endif /* DOUGLASPEUCKER_H_ */
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef EXTRACT_ROUTE_NAMES_H
|
||||
#define EXTRACT_ROUTE_NAMES_H
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct RouteNames
|
||||
{
|
||||
std::string shortest_path_name_1;
|
||||
std::string shortest_path_name_2;
|
||||
std::string alternative_path_name_1;
|
||||
std::string alternative_path_name_2;
|
||||
};
|
||||
|
||||
// construct routes names
|
||||
template <class DataFacadeT, class SegmentT> struct ExtractRouteNames
|
||||
{
|
||||
RouteNames operator()(std::vector<SegmentT> &shortest_path_segments,
|
||||
std::vector<SegmentT> &alternative_path_segments,
|
||||
const DataFacadeT *facade)
|
||||
{
|
||||
RouteNames route_names;
|
||||
|
||||
SegmentT shortest_segment_1, shortest_segment_2;
|
||||
SegmentT alternative_segment_1, alternative_segment_2;
|
||||
|
||||
auto length_comperator = [](SegmentT a, SegmentT b)
|
||||
{ return a.length > b.length; };
|
||||
auto name_id_comperator = [](SegmentT a, SegmentT b)
|
||||
{ return a.name_id < b.name_id; };
|
||||
|
||||
if (shortest_path_segments.empty())
|
||||
{
|
||||
return route_names;
|
||||
}
|
||||
|
||||
std::sort(shortest_path_segments.begin(), shortest_path_segments.end(), length_comperator);
|
||||
shortest_segment_1 = shortest_path_segments[0];
|
||||
if (!alternative_path_segments.empty())
|
||||
{
|
||||
std::sort(alternative_path_segments.begin(),
|
||||
alternative_path_segments.end(),
|
||||
length_comperator);
|
||||
alternative_segment_1 = alternative_path_segments[0];
|
||||
}
|
||||
std::vector<SegmentT> shortest_path_set_difference(shortest_path_segments.size());
|
||||
std::vector<SegmentT> alternative_path_set_difference(alternative_path_segments.size());
|
||||
std::set_difference(shortest_path_segments.begin(),
|
||||
shortest_path_segments.end(),
|
||||
alternative_path_segments.begin(),
|
||||
alternative_path_segments.end(),
|
||||
shortest_path_set_difference.begin(),
|
||||
name_id_comperator);
|
||||
int size_of_difference = shortest_path_set_difference.size();
|
||||
if (size_of_difference)
|
||||
{
|
||||
int i = 0;
|
||||
while (i < size_of_difference &&
|
||||
shortest_path_set_difference[i].name_id == shortest_path_segments[0].name_id)
|
||||
{
|
||||
++i;
|
||||
}
|
||||
if (i < size_of_difference)
|
||||
{
|
||||
shortest_segment_2 = shortest_path_set_difference[i];
|
||||
}
|
||||
}
|
||||
|
||||
std::set_difference(alternative_path_segments.begin(),
|
||||
alternative_path_segments.end(),
|
||||
shortest_path_segments.begin(),
|
||||
shortest_path_segments.end(),
|
||||
alternative_path_set_difference.begin(),
|
||||
name_id_comperator);
|
||||
size_of_difference = alternative_path_set_difference.size();
|
||||
if (size_of_difference)
|
||||
{
|
||||
int i = 0;
|
||||
while (i < size_of_difference &&
|
||||
alternative_path_set_difference[i].name_id ==
|
||||
alternative_path_segments[0].name_id)
|
||||
{
|
||||
++i;
|
||||
}
|
||||
if (i < size_of_difference)
|
||||
{
|
||||
alternative_segment_2 = alternative_path_set_difference[i];
|
||||
}
|
||||
}
|
||||
if (shortest_segment_1.position > shortest_segment_2.position)
|
||||
{
|
||||
std::swap(shortest_segment_1, shortest_segment_2);
|
||||
}
|
||||
if (alternative_segment_1.position > alternative_segment_2.position)
|
||||
{
|
||||
std::swap(alternative_segment_1, alternative_segment_2);
|
||||
}
|
||||
route_names.shortest_path_name_1 =
|
||||
facade->GetEscapedNameForNameID(shortest_segment_1.name_id);
|
||||
route_names.shortest_path_name_2 =
|
||||
facade->GetEscapedNameForNameID(shortest_segment_2.name_id);
|
||||
|
||||
route_names.alternative_path_name_1 =
|
||||
facade->GetEscapedNameForNameID(alternative_segment_1.name_id);
|
||||
route_names.alternative_path_name_2 =
|
||||
facade->GetEscapedNameForNameID(alternative_segment_2.name_id);
|
||||
|
||||
return route_names;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // EXTRACT_ROUTE_NAMES_H
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ITERATOR_BASED_CRC32_H
|
||||
#define ITERATOR_BASED_CRC32_H
|
||||
|
||||
#include "../Util/SimpleLogger.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#if defined(__x86_64__)
|
||||
#include <cpuid.h>
|
||||
#else
|
||||
#include <boost/crc.hpp> // for boost::crc_32_type
|
||||
|
||||
inline void __get_cpuid(int param, unsigned *eax, unsigned *ebx, unsigned *ecx, unsigned *edx)
|
||||
{
|
||||
*ecx = 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
template <class ContainerT> class IteratorbasedCRC32
|
||||
{
|
||||
private:
|
||||
typedef typename ContainerT::iterator IteratorType;
|
||||
unsigned crc;
|
||||
|
||||
bool use_SSE42_CRC_function;
|
||||
|
||||
#if !defined(__x86_64__)
|
||||
boost::crc_optimal<32, 0x1EDC6F41, 0x0, 0x0, true, true> CRC32_processor;
|
||||
#endif
|
||||
unsigned SoftwareBasedCRC32(char *str, unsigned len)
|
||||
{
|
||||
#if !defined(__x86_64__)
|
||||
CRC32_processor.process_bytes(str, len);
|
||||
return CRC32_processor.checksum();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// adapted from http://byteworm.com/2010/10/13/crc32/
|
||||
unsigned SSE42BasedCRC32(char *str, unsigned len)
|
||||
{
|
||||
#if defined(__x86_64__)
|
||||
unsigned q = len / sizeof(unsigned);
|
||||
unsigned r = len % sizeof(unsigned);
|
||||
unsigned *p = (unsigned *)str;
|
||||
|
||||
// crc=0;
|
||||
while (q--)
|
||||
{
|
||||
__asm__ __volatile__(".byte 0xf2, 0xf, 0x38, 0xf1, 0xf1;"
|
||||
: "=S"(crc)
|
||||
: "0"(crc), "c"(*p));
|
||||
++p;
|
||||
}
|
||||
|
||||
str = (char *)p;
|
||||
while (r--)
|
||||
{
|
||||
__asm__ __volatile__(".byte 0xf2, 0xf, 0x38, 0xf1, 0xf1;"
|
||||
: "=S"(crc)
|
||||
: "0"(crc), "c"(*str));
|
||||
++str;
|
||||
}
|
||||
#endif
|
||||
return crc;
|
||||
}
|
||||
|
||||
inline unsigned cpuid() const
|
||||
{
|
||||
unsigned eax = 0, ebx = 0, ecx = 0, edx = 0;
|
||||
// on X64 this calls hardware cpuid(.) instr. otherwise a dummy impl.
|
||||
__get_cpuid(1, &eax, &ebx, &ecx, &edx);
|
||||
return ecx;
|
||||
}
|
||||
|
||||
bool DetectNativeCRC32Support()
|
||||
{
|
||||
static const int SSE42_BIT = 0x00100000;
|
||||
const unsigned ecx = cpuid();
|
||||
const bool has_SSE42 = ecx & SSE42_BIT;
|
||||
if (has_SSE42)
|
||||
{
|
||||
SimpleLogger().Write() << "using hardware based CRC32 computation";
|
||||
}
|
||||
else
|
||||
{
|
||||
SimpleLogger().Write() << "using software based CRC32 computation";
|
||||
}
|
||||
return has_SSE42;
|
||||
}
|
||||
|
||||
public:
|
||||
IteratorbasedCRC32() : crc(0) { use_SSE42_CRC_function = DetectNativeCRC32Support(); }
|
||||
|
||||
unsigned operator()(IteratorType iter, const IteratorType end)
|
||||
{
|
||||
unsigned crc = 0;
|
||||
while (iter != end)
|
||||
{
|
||||
char *data = reinterpret_cast<char *>(&(*iter));
|
||||
|
||||
if (use_SSE42_CRC_function)
|
||||
{
|
||||
crc = SSE42BasedCRC32(data, sizeof(typename ContainerT::value_type));
|
||||
}
|
||||
else
|
||||
{
|
||||
crc = SoftwareBasedCRC32(data, sizeof(typename ContainerT::value_type));
|
||||
}
|
||||
++iter;
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* ITERATOR_BASED_CRC32_H */
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef OBJECTTOBASE64_H_
|
||||
#define OBJECTTOBASE64_H_
|
||||
|
||||
#include "../Util/StringUtil.h"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/archive/iterators/base64_from_binary.hpp>
|
||||
#include <boost/archive/iterators/binary_from_base64.hpp>
|
||||
#include <boost/archive/iterators/transform_width.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
typedef
|
||||
boost::archive::iterators::base64_from_binary<
|
||||
boost::archive::iterators::transform_width<const char *, 6, 8>
|
||||
> base64_t;
|
||||
|
||||
typedef
|
||||
boost::archive::iterators::transform_width<
|
||||
boost::archive::iterators::binary_from_base64<
|
||||
std::string::const_iterator>, 8, 6
|
||||
> binary_t;
|
||||
|
||||
template<class ObjectT>
|
||||
static void EncodeObjectToBase64(const ObjectT & object, std::string& encoded) {
|
||||
const char * char_ptr_to_object = (const char *)&object;
|
||||
std::vector<unsigned char> data(sizeof(object));
|
||||
std::copy(
|
||||
char_ptr_to_object,
|
||||
char_ptr_to_object + sizeof(ObjectT),
|
||||
data.begin()
|
||||
);
|
||||
|
||||
unsigned char number_of_padded_chars = 0; // is in {0,1,2};
|
||||
while(data.size() % 3 != 0) {
|
||||
++number_of_padded_chars;
|
||||
data.push_back(0x00);
|
||||
}
|
||||
|
||||
BOOST_ASSERT_MSG(
|
||||
0 == data.size() % 3,
|
||||
"base64 input data size is not a multiple of 3!"
|
||||
);
|
||||
encoded.resize(sizeof(ObjectT));
|
||||
encoded.assign(
|
||||
base64_t( &data[0] ),
|
||||
base64_t( &data[0] + (data.size() - number_of_padded_chars) )
|
||||
);
|
||||
replaceAll(encoded, "+", "-");
|
||||
replaceAll(encoded, "/", "_");
|
||||
}
|
||||
|
||||
template<class ObjectT>
|
||||
static void DecodeObjectFromBase64(const std::string& input, ObjectT & object) {
|
||||
try {
|
||||
std::string encoded(input);
|
||||
//replace "-" with "+" and "_" with "/"
|
||||
replaceAll(encoded, "-", "+");
|
||||
replaceAll(encoded, "_", "/");
|
||||
|
||||
std::copy (
|
||||
binary_t( encoded.begin() ),
|
||||
binary_t( encoded.begin() + encoded.length() - 1),
|
||||
(char *)&object
|
||||
);
|
||||
|
||||
} catch(...) { }
|
||||
}
|
||||
|
||||
#endif /* OBJECTTOBASE64_H_ */
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include "PolylineCompressor.h"
|
||||
#include "../Util/StringUtil.h"
|
||||
|
||||
//TODO: return vector of start indices for each leg
|
||||
|
||||
void PolylineCompressor::encodeVectorSignedNumber(std::vector<int> &numbers, std::string &output)
|
||||
const
|
||||
{
|
||||
const unsigned end = numbers.size();
|
||||
for (unsigned i = 0; i < end; ++i)
|
||||
{
|
||||
numbers[i] <<= 1;
|
||||
if (numbers[i] < 0)
|
||||
{
|
||||
numbers[i] = ~(numbers[i]);
|
||||
}
|
||||
}
|
||||
for (const int number: numbers)
|
||||
{
|
||||
encodeNumber(number, output);
|
||||
}
|
||||
}
|
||||
|
||||
void PolylineCompressor::encodeNumber(int number_to_encode, std::string &output) const
|
||||
{
|
||||
while (number_to_encode >= 0x20)
|
||||
{
|
||||
int next_value = (0x20 | (number_to_encode & 0x1f)) + 63;
|
||||
output += static_cast<char>(next_value);
|
||||
if (92 == next_value)
|
||||
{
|
||||
output += static_cast<char>(next_value);
|
||||
}
|
||||
number_to_encode >>= 5;
|
||||
}
|
||||
|
||||
number_to_encode += 63;
|
||||
output += static_cast<char>(number_to_encode);
|
||||
if (92 == number_to_encode)
|
||||
{
|
||||
output += static_cast<char>(number_to_encode);
|
||||
}
|
||||
}
|
||||
|
||||
JSON::String PolylineCompressor::printEncodedString(const std::vector<SegmentInformation> &polyline) const
|
||||
{
|
||||
std::string output;
|
||||
std::vector<int> delta_numbers;
|
||||
if (!polyline.empty())
|
||||
{
|
||||
FixedPointCoordinate last_coordinate = polyline[0].location;
|
||||
delta_numbers.emplace_back(last_coordinate.lat);
|
||||
delta_numbers.emplace_back(last_coordinate.lon);
|
||||
for (const auto & segment : polyline)
|
||||
{
|
||||
if (segment.necessary)
|
||||
{
|
||||
int lat_diff = segment.location.lat - last_coordinate.lat;
|
||||
int lon_diff = segment.location.lon - last_coordinate.lon;
|
||||
delta_numbers.emplace_back(lat_diff);
|
||||
delta_numbers.emplace_back(lon_diff);
|
||||
last_coordinate = segment.location;
|
||||
}
|
||||
}
|
||||
encodeVectorSignedNumber(delta_numbers, output);
|
||||
}
|
||||
JSON::String return_value(output);
|
||||
return return_value;
|
||||
}
|
||||
|
||||
JSON::String PolylineCompressor::printEncodedString(const std::vector<FixedPointCoordinate> &polyline) const
|
||||
{
|
||||
std::string output;
|
||||
std::vector<int> delta_numbers(2 * polyline.size());
|
||||
if (!polyline.empty())
|
||||
{
|
||||
delta_numbers[0] = polyline[0].lat;
|
||||
delta_numbers[1] = polyline[0].lon;
|
||||
for (unsigned i = 1; i < polyline.size(); ++i)
|
||||
{
|
||||
int lat_diff = polyline[i].lat - polyline[i - 1].lat;
|
||||
int lon_diff = polyline[i].lon - polyline[i - 1].lon;
|
||||
delta_numbers[(2 * i)] = (lat_diff);
|
||||
delta_numbers[(2 * i) + 1] = (lon_diff);
|
||||
}
|
||||
encodeVectorSignedNumber(delta_numbers, output);
|
||||
}
|
||||
JSON::String return_value(output);
|
||||
return return_value;
|
||||
}
|
||||
|
||||
|
||||
JSON::Array PolylineCompressor::printUnencodedString(const std::vector<FixedPointCoordinate> &polyline) const
|
||||
{
|
||||
JSON::Array json_geometry_array;
|
||||
for( const auto & coordinate : polyline)
|
||||
{
|
||||
std::string tmp, output;
|
||||
FixedPointCoordinate::convertInternalLatLonToString(coordinate.lat, tmp);
|
||||
output += (tmp + ",");
|
||||
FixedPointCoordinate::convertInternalLatLonToString(coordinate.lon, tmp);
|
||||
output += tmp;
|
||||
json_geometry_array.values.push_back(output);
|
||||
}
|
||||
return json_geometry_array;
|
||||
}
|
||||
|
||||
JSON::Array PolylineCompressor::printUnencodedString(const std::vector<SegmentInformation> &polyline) const
|
||||
{
|
||||
JSON::Array json_geometry_array;
|
||||
for( const auto & segment : polyline)
|
||||
{
|
||||
if (segment.necessary)
|
||||
{
|
||||
std::string tmp, output;
|
||||
FixedPointCoordinate::convertInternalLatLonToString(segment.location.lat, tmp);
|
||||
output += (tmp + ",");
|
||||
FixedPointCoordinate::convertInternalLatLonToString(segment.location.lon, tmp);
|
||||
output += tmp;
|
||||
json_geometry_array.values.push_back(output);
|
||||
}
|
||||
}
|
||||
return json_geometry_array;
|
||||
}
|
||||
@@ -1,134 +1,54 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef POLYLINECOMPRESSOR_H_
|
||||
#define POLYLINECOMPRESSOR_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "../DataStructures/ExtractorStructs.h"
|
||||
#include "../DataStructures/JSONContainer.h"
|
||||
#include "../DataStructures/SegmentInformation.h"
|
||||
#include "../Util/StringUtil.h"
|
||||
|
||||
class PolylineCompressor {
|
||||
private:
|
||||
inline void encodeVectorSignedNumber(vector<int> & numbers, string & output) {
|
||||
for(unsigned i = 0; i < numbers.size(); ++i) {
|
||||
numbers[i] <<= 1;
|
||||
if (numbers[i] < 0) {
|
||||
numbers[i] = ~(numbers[i]);
|
||||
}
|
||||
}
|
||||
for(unsigned i = 0; i < numbers.size(); ++i) {
|
||||
encodeNumber(numbers[i], output);
|
||||
}
|
||||
}
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
inline void encodeNumber(int numberToEncode, string & output) {
|
||||
while (numberToEncode >= 0x20) {
|
||||
int nextValue = (0x20 | (numberToEncode & 0x1f)) + 63;
|
||||
output += (static_cast<char> (nextValue));
|
||||
if(92 == nextValue)
|
||||
output += (static_cast<char> (nextValue));
|
||||
numberToEncode >>= 5;
|
||||
}
|
||||
class PolylineCompressor
|
||||
{
|
||||
private:
|
||||
void encodeVectorSignedNumber(std::vector<int> &numbers, std::string &output) const;
|
||||
|
||||
numberToEncode += 63;
|
||||
output += (static_cast<char> (numberToEncode));
|
||||
if(92 == numberToEncode)
|
||||
output += (static_cast<char> (numberToEncode));
|
||||
}
|
||||
void encodeNumber(int number_to_encode, std::string &output) const;
|
||||
|
||||
public:
|
||||
inline void printEncodedString(const vector<SegmentInformation>& polyline, string &output) {
|
||||
vector<int> deltaNumbers;
|
||||
output += "\"";
|
||||
if(!polyline.empty()) {
|
||||
_Coordinate lastCoordinate = polyline[0].location;
|
||||
deltaNumbers.push_back( lastCoordinate.lat );
|
||||
deltaNumbers.push_back( lastCoordinate.lon );
|
||||
for(unsigned i = 1; i < polyline.size(); ++i) {
|
||||
if(!polyline[i].necessary)
|
||||
continue;
|
||||
deltaNumbers.push_back(polyline[i].location.lat - lastCoordinate.lat);
|
||||
deltaNumbers.push_back(polyline[i].location.lon - lastCoordinate.lon);
|
||||
lastCoordinate = polyline[i].location;
|
||||
}
|
||||
encodeVectorSignedNumber(deltaNumbers, output);
|
||||
}
|
||||
output += "\"";
|
||||
public:
|
||||
JSON::String printEncodedString(const std::vector<SegmentInformation> &polyline) const;
|
||||
|
||||
}
|
||||
JSON::String printEncodedString(const std::vector<FixedPointCoordinate> &polyline) const;
|
||||
|
||||
inline void printEncodedString(const vector<_Coordinate>& polyline, string &output) {
|
||||
vector<int> deltaNumbers(2*polyline.size());
|
||||
output += "\"";
|
||||
if(!polyline.empty()) {
|
||||
deltaNumbers[0] = polyline[0].lat;
|
||||
deltaNumbers[1] = polyline[0].lon;
|
||||
for(unsigned i = 1; i < polyline.size(); ++i) {
|
||||
deltaNumbers[(2*i)] = (polyline[i].lat - polyline[i-1].lat);
|
||||
deltaNumbers[(2*i)+1] = (polyline[i].lon - polyline[i-1].lon);
|
||||
}
|
||||
encodeVectorSignedNumber(deltaNumbers, output);
|
||||
}
|
||||
output += "\"";
|
||||
}
|
||||
JSON::Array printUnencodedString(const std::vector<FixedPointCoordinate> &polyline) const;
|
||||
|
||||
inline void printUnencodedString(vector<_Coordinate> & polyline, string & output) {
|
||||
output += "[";
|
||||
string tmp;
|
||||
for(unsigned i = 0; i < polyline.size(); i++) {
|
||||
convertInternalLatLonToString(polyline[i].lat, tmp);
|
||||
output += "[";
|
||||
output += tmp;
|
||||
convertInternalLatLonToString(polyline[i].lon, tmp);
|
||||
output += ", ";
|
||||
output += tmp;
|
||||
output += "]";
|
||||
if( i < polyline.size()-1 ) {
|
||||
output += ",";
|
||||
}
|
||||
}
|
||||
output += "]";
|
||||
}
|
||||
|
||||
inline void printUnencodedString(vector<SegmentInformation> & polyline, string & output) {
|
||||
output += "[";
|
||||
string tmp;
|
||||
for(unsigned i = 0; i < polyline.size(); i++) {
|
||||
if(!polyline[i].necessary)
|
||||
continue;
|
||||
convertInternalLatLonToString(polyline[i].location.lat, tmp);
|
||||
output += "[";
|
||||
output += tmp;
|
||||
convertInternalLatLonToString(polyline[i].location.lon, tmp);
|
||||
output += ", ";
|
||||
output += tmp;
|
||||
output += "]";
|
||||
if( i < polyline.size()-1 ) {
|
||||
output += ",";
|
||||
}
|
||||
}
|
||||
output += "]";
|
||||
}
|
||||
JSON::Array printUnencodedString(const std::vector<SegmentInformation> &polyline) const;
|
||||
};
|
||||
|
||||
#endif /* POLYLINECOMPRESSOR_H_ */
|
||||
|
||||
@@ -0,0 +1,459 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef STRONGLYCONNECTEDCOMPONENTS_H_
|
||||
#define STRONGLYCONNECTEDCOMPONENTS_H_
|
||||
|
||||
#include "../typedefs.h"
|
||||
#include "../DataStructures/DeallocatingVector.h"
|
||||
#include "../DataStructures/DynamicGraph.h"
|
||||
#include "../DataStructures/ImportEdge.h"
|
||||
#include "../DataStructures/QueryNode.h"
|
||||
#include "../DataStructures/Percent.h"
|
||||
#include "../DataStructures/Restriction.h"
|
||||
#include "../DataStructures/TurnInstructions.h"
|
||||
|
||||
#include "../Util/SimpleLogger.h"
|
||||
#include "../Util/StdHashExtensions.h"
|
||||
|
||||
#include <osrm/Coordinate.h>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <gdal.h>
|
||||
#include <ogrsf_frmts.h>
|
||||
#else
|
||||
#include <gdal/gdal.h>
|
||||
#include <gdal/ogrsf_frmts.h>
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <memory>
|
||||
#include <stack>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
class TarjanSCC
|
||||
{
|
||||
private:
|
||||
struct TarjanNode
|
||||
{
|
||||
TarjanNode() : index(UINT_MAX), low_link(UINT_MAX), on_stack(false) {}
|
||||
unsigned index;
|
||||
unsigned low_link;
|
||||
bool on_stack;
|
||||
};
|
||||
|
||||
struct TarjanEdgeData
|
||||
{
|
||||
int distance;
|
||||
unsigned name_id : 31;
|
||||
bool shortcut : 1;
|
||||
short type;
|
||||
bool forward : 1;
|
||||
bool backward : 1;
|
||||
bool reversedEdge : 1;
|
||||
};
|
||||
|
||||
struct TarjanStackFrame
|
||||
{
|
||||
explicit TarjanStackFrame(NodeID v, NodeID parent) : v(v), parent(parent) {}
|
||||
NodeID v;
|
||||
NodeID parent;
|
||||
};
|
||||
|
||||
typedef DynamicGraph<TarjanEdgeData> TarjanDynamicGraph;
|
||||
typedef TarjanDynamicGraph::InputEdge TarjanEdge;
|
||||
typedef std::pair<NodeID, NodeID> RestrictionSource;
|
||||
typedef std::pair<NodeID, bool> restriction_target;
|
||||
typedef std::vector<restriction_target> EmanatingRestrictionsVector;
|
||||
typedef std::unordered_map<RestrictionSource, unsigned> RestrictionMap;
|
||||
|
||||
std::vector<NodeInfo> m_coordinate_list;
|
||||
std::vector<EmanatingRestrictionsVector> m_restriction_bucket_list;
|
||||
std::shared_ptr<TarjanDynamicGraph> m_node_based_graph;
|
||||
std::unordered_set<NodeID> m_barrier_node_list;
|
||||
std::unordered_set<NodeID> m_traffic_light_list;
|
||||
unsigned m_restriction_counter;
|
||||
RestrictionMap m_restriction_map;
|
||||
|
||||
public:
|
||||
TarjanSCC(int number_of_nodes,
|
||||
std::vector<NodeBasedEdge> &input_edges,
|
||||
std::vector<NodeID> &bn,
|
||||
std::vector<NodeID> &tl,
|
||||
std::vector<TurnRestriction> &irs,
|
||||
std::vector<NodeInfo> &nI)
|
||||
: m_coordinate_list(nI), m_restriction_counter(irs.size())
|
||||
{
|
||||
for (const TurnRestriction &restriction : irs)
|
||||
{
|
||||
std::pair<NodeID, NodeID> restrictionSource = {restriction.fromNode,
|
||||
restriction.viaNode};
|
||||
unsigned index;
|
||||
RestrictionMap::iterator restriction_iterator =
|
||||
m_restriction_map.find(restrictionSource);
|
||||
if (restriction_iterator == m_restriction_map.end())
|
||||
{
|
||||
index = m_restriction_bucket_list.size();
|
||||
m_restriction_bucket_list.resize(index + 1);
|
||||
m_restriction_map.emplace(restrictionSource, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
index = restriction_iterator->second;
|
||||
// Map already contains an is_only_*-restriction
|
||||
if (m_restriction_bucket_list.at(index).begin()->second)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (restriction.flags.isOnly)
|
||||
{
|
||||
// We are going to insert an is_only_*-restriction. There can be only one.
|
||||
m_restriction_bucket_list.at(index).clear();
|
||||
}
|
||||
}
|
||||
|
||||
m_restriction_bucket_list.at(index)
|
||||
.emplace_back(restriction.toNode, restriction.flags.isOnly);
|
||||
}
|
||||
|
||||
m_barrier_node_list.insert(bn.begin(), bn.end());
|
||||
m_traffic_light_list.insert(tl.begin(), tl.end());
|
||||
|
||||
DeallocatingVector<TarjanEdge> edge_list;
|
||||
for (const NodeBasedEdge &input_edge : input_edges)
|
||||
{
|
||||
if (input_edge.source() == input_edge.target())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
TarjanEdge edge;
|
||||
if (input_edge.isForward())
|
||||
{
|
||||
edge.source = input_edge.source();
|
||||
edge.target = input_edge.target();
|
||||
edge.data.forward = input_edge.isForward();
|
||||
edge.data.backward = input_edge.isBackward();
|
||||
}
|
||||
else
|
||||
{
|
||||
edge.source = input_edge.target();
|
||||
edge.target = input_edge.source();
|
||||
edge.data.backward = input_edge.isForward();
|
||||
edge.data.forward = input_edge.isBackward();
|
||||
}
|
||||
|
||||
edge.data.distance = (std::max)((int)input_edge.weight(), 1);
|
||||
BOOST_ASSERT(edge.data.distance > 0);
|
||||
edge.data.shortcut = false;
|
||||
// edge.data.roundabout = input_edge.isRoundabout();
|
||||
// edge.data.ignoreInGrid = input_edge.ignoreInGrid();
|
||||
edge.data.name_id = input_edge.name();
|
||||
edge.data.type = input_edge.type();
|
||||
// edge.data.isAccessRestricted = input_edge.isAccessRestricted();
|
||||
edge.data.reversedEdge = false;
|
||||
edge_list.push_back(edge);
|
||||
if (edge.data.backward)
|
||||
{
|
||||
std::swap(edge.source, edge.target);
|
||||
edge.data.forward = input_edge.isBackward();
|
||||
edge.data.backward = input_edge.isForward();
|
||||
edge.data.reversedEdge = true;
|
||||
edge_list.push_back(edge);
|
||||
}
|
||||
}
|
||||
std::vector<NodeBasedEdge>().swap(input_edges);
|
||||
BOOST_ASSERT_MSG(0 == input_edges.size() && 0 == input_edges.capacity(),
|
||||
"input edge vector not properly deallocated");
|
||||
|
||||
std::sort(edge_list.begin(), edge_list.end());
|
||||
|
||||
m_node_based_graph = std::make_shared<TarjanDynamicGraph>(number_of_nodes, edge_list);
|
||||
}
|
||||
|
||||
~TarjanSCC() { m_node_based_graph.reset(); }
|
||||
|
||||
void Run()
|
||||
{
|
||||
// remove files from previous run if exist
|
||||
DeleteFileIfExists("component.dbf");
|
||||
DeleteFileIfExists("component.shx");
|
||||
DeleteFileIfExists("component.shp");
|
||||
|
||||
Percent p(m_node_based_graph->GetNumberOfNodes());
|
||||
|
||||
OGRRegisterAll();
|
||||
|
||||
const char *pszDriverName = "ESRI Shapefile";
|
||||
OGRSFDriver *poDriver =
|
||||
OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName(pszDriverName);
|
||||
if (NULL == poDriver)
|
||||
{
|
||||
throw OSRMException("ESRI Shapefile driver not available");
|
||||
}
|
||||
OGRDataSource *poDS = poDriver->CreateDataSource("component.shp", NULL);
|
||||
|
||||
if (NULL == poDS)
|
||||
{
|
||||
throw OSRMException("Creation of output file failed");
|
||||
}
|
||||
|
||||
OGRLayer *poLayer = poDS->CreateLayer("component", NULL, wkbLineString, NULL);
|
||||
|
||||
if (NULL == poLayer)
|
||||
{
|
||||
throw OSRMException("Layer creation failed.");
|
||||
}
|
||||
|
||||
// The following is a hack to distinguish between stuff that happens
|
||||
// before the recursive call and stuff that happens after
|
||||
std::stack<std::pair<bool, TarjanStackFrame>> recursion_stack;
|
||||
// true = stuff before, false = stuff after call
|
||||
std::stack<NodeID> tarjan_stack;
|
||||
std::vector<unsigned> components_index(m_node_based_graph->GetNumberOfNodes(), UINT_MAX);
|
||||
std::vector<NodeID> component_size_vector;
|
||||
std::vector<TarjanNode> tarjan_node_list(m_node_based_graph->GetNumberOfNodes());
|
||||
unsigned component_index = 0, size_of_current_component = 0;
|
||||
int index = 0;
|
||||
NodeID last_node = m_node_based_graph->GetNumberOfNodes();
|
||||
for (NodeID node = 0; node < last_node; ++node)
|
||||
{
|
||||
if (UINT_MAX == components_index[node])
|
||||
{
|
||||
recursion_stack.emplace(true, TarjanStackFrame(node, node));
|
||||
}
|
||||
|
||||
while (!recursion_stack.empty())
|
||||
{
|
||||
const bool before_recursion = recursion_stack.top().first;
|
||||
TarjanStackFrame currentFrame = recursion_stack.top().second;
|
||||
NodeID v = currentFrame.v;
|
||||
recursion_stack.pop();
|
||||
|
||||
if (before_recursion)
|
||||
{
|
||||
// Mark frame to handle tail of recursion
|
||||
recursion_stack.emplace(false, currentFrame);
|
||||
|
||||
// Mark essential information for SCC
|
||||
tarjan_node_list[v].index = index;
|
||||
tarjan_node_list[v].low_link = index;
|
||||
tarjan_stack.push(v);
|
||||
tarjan_node_list[v].on_stack = true;
|
||||
++index;
|
||||
|
||||
// Traverse outgoing edges
|
||||
for (auto e2 : m_node_based_graph->GetAdjacentEdgeRange(v))
|
||||
{
|
||||
const TarjanDynamicGraph::NodeIterator vprime =
|
||||
m_node_based_graph->GetTarget(e2);
|
||||
if (UINT_MAX == tarjan_node_list[vprime].index)
|
||||
{
|
||||
recursion_stack.emplace(true, TarjanStackFrame(vprime, v));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (tarjan_node_list[vprime].on_stack &&
|
||||
tarjan_node_list[vprime].index < tarjan_node_list[v].low_link)
|
||||
{
|
||||
tarjan_node_list[v].low_link = tarjan_node_list[vprime].index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tarjan_node_list[currentFrame.parent].low_link =
|
||||
std::min(tarjan_node_list[currentFrame.parent].low_link,
|
||||
tarjan_node_list[v].low_link);
|
||||
// after recursion, lets do cycle checking
|
||||
// Check if we found a cycle. This is the bottom part of the recursion
|
||||
if (tarjan_node_list[v].low_link == tarjan_node_list[v].index)
|
||||
{
|
||||
NodeID vprime;
|
||||
do
|
||||
{
|
||||
vprime = tarjan_stack.top();
|
||||
tarjan_stack.pop();
|
||||
tarjan_node_list[vprime].on_stack = false;
|
||||
components_index[vprime] = component_index;
|
||||
++size_of_current_component;
|
||||
} while (v != vprime);
|
||||
|
||||
component_size_vector.emplace_back(size_of_current_component);
|
||||
|
||||
if (size_of_current_component > 1000)
|
||||
{
|
||||
SimpleLogger().Write() << "large component [" << component_index
|
||||
<< "]=" << size_of_current_component;
|
||||
}
|
||||
|
||||
++component_index;
|
||||
size_of_current_component = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SimpleLogger().Write() << "identified: " << component_size_vector.size()
|
||||
<< " many components, marking small components";
|
||||
|
||||
// TODO/C++11: prime candidate for lambda function
|
||||
// unsigned size_one_counter = 0;
|
||||
// for (unsigned i = 0, end = component_size_vector.size(); i < end; ++i)
|
||||
// {
|
||||
// if (1 == component_size_vector[i])
|
||||
// {
|
||||
// ++size_one_counter;
|
||||
// }
|
||||
// }
|
||||
unsigned size_one_counter = std::count_if(component_size_vector.begin(),
|
||||
component_size_vector.end(),
|
||||
[] (unsigned value) { return 1 == value;});
|
||||
|
||||
SimpleLogger().Write() << "identified " << size_one_counter << " SCCs of size 1";
|
||||
|
||||
uint64_t total_network_distance = 0;
|
||||
p.reinit(m_node_based_graph->GetNumberOfNodes());
|
||||
NodeID last_u_node = m_node_based_graph->GetNumberOfNodes();
|
||||
for (NodeID u = 0; u < last_u_node; ++u)
|
||||
{
|
||||
p.printIncrement();
|
||||
for (auto e1 : m_node_based_graph->GetAdjacentEdgeRange(u))
|
||||
{
|
||||
if (!m_node_based_graph->GetEdgeData(e1).reversedEdge)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const TarjanDynamicGraph::NodeIterator v = m_node_based_graph->GetTarget(e1);
|
||||
|
||||
total_network_distance +=
|
||||
100 * FixedPointCoordinate::ApproximateDistance(m_coordinate_list[u].lat,
|
||||
m_coordinate_list[u].lon,
|
||||
m_coordinate_list[v].lat,
|
||||
m_coordinate_list[v].lon);
|
||||
|
||||
if (SHRT_MAX != m_node_based_graph->GetEdgeData(e1).type)
|
||||
{
|
||||
BOOST_ASSERT(e1 != UINT_MAX);
|
||||
BOOST_ASSERT(u != UINT_MAX);
|
||||
BOOST_ASSERT(v != UINT_MAX);
|
||||
|
||||
const unsigned size_of_containing_component =
|
||||
std::min(component_size_vector[components_index[u]],
|
||||
component_size_vector[components_index[v]]);
|
||||
|
||||
// edges that end on bollard nodes may actually be in two distinct components
|
||||
if (size_of_containing_component < 10)
|
||||
{
|
||||
OGRLineString lineString;
|
||||
lineString.addPoint(m_coordinate_list[u].lon / COORDINATE_PRECISION,
|
||||
m_coordinate_list[u].lat / COORDINATE_PRECISION);
|
||||
lineString.addPoint(m_coordinate_list[v].lon / COORDINATE_PRECISION,
|
||||
m_coordinate_list[v].lat / COORDINATE_PRECISION);
|
||||
|
||||
OGRFeature *poFeature = OGRFeature::CreateFeature(poLayer->GetLayerDefn());
|
||||
|
||||
poFeature->SetGeometry(&lineString);
|
||||
if (OGRERR_NONE != poLayer->CreateFeature(poFeature))
|
||||
{
|
||||
throw OSRMException("Failed to create feature in shapefile.");
|
||||
}
|
||||
OGRFeature::DestroyFeature(poFeature);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
OGRDataSource::DestroyDataSource(poDS);
|
||||
std::vector<NodeID>().swap(component_size_vector);
|
||||
BOOST_ASSERT_MSG(0 == component_size_vector.size() && 0 == component_size_vector.capacity(),
|
||||
"component_size_vector not properly deallocated");
|
||||
|
||||
std::vector<NodeID>().swap(components_index);
|
||||
BOOST_ASSERT_MSG(0 == components_index.size() && 0 == components_index.capacity(),
|
||||
"icomponents_index not properly deallocated");
|
||||
|
||||
SimpleLogger().Write() << "total network distance: " << (uint64_t)total_network_distance /
|
||||
100 / 1000. << " km";
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned CheckForEmanatingIsOnlyTurn(const NodeID u, const NodeID v) const
|
||||
{
|
||||
std::pair<NodeID, NodeID> restriction_source = {u, v};
|
||||
RestrictionMap::const_iterator restriction_iterator =
|
||||
m_restriction_map.find(restriction_source);
|
||||
if (restriction_iterator != m_restriction_map.end())
|
||||
{
|
||||
const unsigned index = restriction_iterator->second;
|
||||
for (const RestrictionSource &restriction_target : m_restriction_bucket_list.at(index))
|
||||
{
|
||||
if (restriction_target.second)
|
||||
{
|
||||
return restriction_target.first;
|
||||
}
|
||||
}
|
||||
}
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
bool CheckIfTurnIsRestricted(const NodeID u, const NodeID v, const NodeID w) const
|
||||
{
|
||||
// only add an edge if turn is not a U-turn except it is the end of dead-end street.
|
||||
std::pair<NodeID, NodeID> restriction_source = {u, v};
|
||||
RestrictionMap::const_iterator restriction_iterator =
|
||||
m_restriction_map.find(restriction_source);
|
||||
if (restriction_iterator != m_restriction_map.end())
|
||||
{
|
||||
const unsigned index = restriction_iterator->second;
|
||||
for (const restriction_target &restriction_target : m_restriction_bucket_list.at(index))
|
||||
{
|
||||
if (w == restriction_target.first)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DeleteFileIfExists(const std::string &file_name) const
|
||||
{
|
||||
if (boost::filesystem::exists(file_name))
|
||||
{
|
||||
boost::filesystem::remove(file_name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* STRONGLYCONNECTEDCOMPONENTS_H_ */
|
||||
@@ -0,0 +1,282 @@
|
||||
cmake_minimum_required(VERSION 2.8)
|
||||
project(OSRM)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
include(CheckCXXCompilerFlag)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
|
||||
include(GetGitRevisionDescription)
|
||||
git_describe(GIT_DESCRIPTION)
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
|
||||
|
||||
set(bitness 32)
|
||||
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(bitness 64)
|
||||
message(STATUS "Building on a 64 bit system")
|
||||
else()
|
||||
message(WARNING "Building on a 32 bit system is unsupported")
|
||||
endif()
|
||||
|
||||
include_directories(${CMAKE_SOURCE_DIR}/Include/)
|
||||
|
||||
add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/Util/UUID.cpp UUID.cpp.alwaysbuild
|
||||
COMMAND ${CMAKE_COMMAND} -DSOURCE_DIR=${CMAKE_SOURCE_DIR}
|
||||
-P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/UUID-Config.cmake
|
||||
DEPENDS
|
||||
${CMAKE_SOURCE_DIR}/Util/UUID.cpp.in
|
||||
${CMAKE_SOURCE_DIR}/cmake/UUID-Config.cmake
|
||||
COMMENT "Configuring UUID.cpp"
|
||||
VERBATIM)
|
||||
|
||||
add_custom_target(UUIDConfigure DEPENDS ${CMAKE_SOURCE_DIR}/Util/UUID.cpp)
|
||||
|
||||
set(BOOST_COMPONENTS date_time filesystem iostreams program_options regex system thread)
|
||||
|
||||
configure_file(
|
||||
${CMAKE_SOURCE_DIR}/Util/GitDescription.cpp.in
|
||||
${CMAKE_SOURCE_DIR}/Util/GitDescription.cpp
|
||||
)
|
||||
file(GLOB ExtractorGlob Extractor/*.cpp)
|
||||
set(ExtractorSources extractor.cpp ${ExtractorGlob})
|
||||
add_executable(osrm-extract ${ExtractorSources})
|
||||
|
||||
file(GLOB PrepareGlob Contractor/*.cpp DataStructures/HilbertValue.cpp DataStructures/RestrictionMap.cpp)
|
||||
set(PrepareSources prepare.cpp ${PrepareGlob})
|
||||
add_executable(osrm-prepare ${PrepareSources})
|
||||
|
||||
file(GLOB ServerGlob Server/*.cpp)
|
||||
file(GLOB DescriptorGlob Descriptors/*.cpp)
|
||||
file(GLOB DatastructureGlob DataStructures/SearchEngineData.cpp DataStructures/RouteParameters.cpp)
|
||||
file(GLOB CoordinateGlob DataStructures/Coordinate.cpp)
|
||||
file(GLOB AlgorithmGlob Algorithms/*.cpp)
|
||||
file(GLOB HttpGlob Server/Http/*.cpp)
|
||||
file(GLOB LibOSRMGlob Library/*.cpp)
|
||||
|
||||
set(
|
||||
OSRMSources
|
||||
${LibOSRMGlob}
|
||||
${DescriptorGlob}
|
||||
${DatastructureGlob}
|
||||
${CoordinateGlob}
|
||||
${AlgorithmGlob}
|
||||
${HttpGlob}
|
||||
)
|
||||
add_library(COORDLIB STATIC ${CoordinateGlob})
|
||||
add_library(OSRM ${OSRMSources} Util/GitDescription.cpp Util/UUID.cpp)
|
||||
add_library(UUID STATIC Util/UUID.cpp)
|
||||
add_library(GITDESCRIPTION STATIC Util/GitDescription.cpp)
|
||||
add_dependencies(UUID UUIDConfigure)
|
||||
add_dependencies(GITDESCRIPTION GIT_DESCRIPTION)
|
||||
|
||||
add_executable(osrm-routed routed.cpp ${ServerGlob})
|
||||
set_target_properties(osrm-routed PROPERTIES COMPILE_FLAGS -DROUTED)
|
||||
add_executable(osrm-datastore datastore.cpp)
|
||||
|
||||
# Check the release mode
|
||||
if(NOT CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
set(CMAKE_BUILD_TYPE Release)
|
||||
endif()
|
||||
if(CMAKE_BUILD_TYPE MATCHES Debug)
|
||||
message(STATUS "Configuring OSRM in debug mode")
|
||||
if(NOT "${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||
message(STATUS "adding profiling flags")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fprofile-arcs -ftest-coverage -fno-inline")
|
||||
set(CMAKE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage -fno-inline")
|
||||
endif()
|
||||
endif()
|
||||
if(CMAKE_BUILD_TYPE MATCHES Release)
|
||||
message(STATUS "Configuring OSRM in release mode")
|
||||
# Check if LTO is available
|
||||
set(LTO_FLAGS "")
|
||||
CHECK_CXX_COMPILER_FLAG("-flto" HAS_LTO_FLAG)
|
||||
if (HAS_LTO_FLAG)
|
||||
set(LTO_FLAGS "${LTO_FLAGS} -flto")
|
||||
|
||||
# Since gcc 4.9 the LTO format is non-standart ('slim'), so we need to use the build-in tools
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND
|
||||
NOT "${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS "4.9.0")
|
||||
message(STATUS "Using gcc specific binutils for LTO.")
|
||||
set(CMAKE_AR "/usr/bin/gcc-ar")
|
||||
set(CMAKE_RANLIB "/usr/bin/gcc-ranlib")
|
||||
endif()
|
||||
endif (HAS_LTO_FLAG)
|
||||
endif()
|
||||
|
||||
# Configuring compilers
|
||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
|
||||
# using Clang
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wunreachable-code -Wno-unknown-pragmas -Wno-unneeded-internal-declaration -pedantic -fPIC")
|
||||
message(STATUS "OpenMP parallelization not available using clang++")
|
||||
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
# using GCC
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fopenmp -pedantic -fPIC")
|
||||
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")
|
||||
# using Intel C++
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-intel -wd10237 -Wall -openmp -ipo -fPIC")
|
||||
elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||
# using Visual Studio C++
|
||||
endif()
|
||||
|
||||
# disable partitioning of LTO process when possible (fixes Debian issues)
|
||||
set(LTO_PARTITION_FLAGS "")
|
||||
CHECK_CXX_COMPILER_FLAG("-flto-partition=none" HAS_LTO_PARTITION_FLAG)
|
||||
if (HAS_LTO_PARTITION_FLAG)
|
||||
set(LTO_PARTITION_FLAGS "${LTO_PARTITION_FLAGS} -flto-partition=none")
|
||||
endif (HAS_LTO_PARTITION_FLAG)
|
||||
|
||||
# Add Link-Time-Optimization flags, if supported (GCC >= 4.7) and enabled
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LTO_FLAGS}")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${LTO_FLAGS} ${LTO_PARTITION_FLAGS}")
|
||||
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${LTO_FLAGS} ${LTO_PARTITION_FLAGS}")
|
||||
|
||||
# Activate C++11
|
||||
ADD_DEFINITIONS(
|
||||
-std=c++11 # Or -std=c++0x
|
||||
# Other flags
|
||||
)
|
||||
|
||||
# Configuring other platform dependencies
|
||||
if(APPLE)
|
||||
set(CMAKE_OSX_ARCHITECTURES "x86_64")
|
||||
message(STATUS "Set Architecture to x64 on OS X")
|
||||
exec_program(uname ARGS -v OUTPUT_VARIABLE DARWIN_VERSION)
|
||||
string(REGEX MATCH "[0-9]+" DARWIN_VERSION ${DARWIN_VERSION})
|
||||
# if(DARWIN_VERSION GREATER 12 AND NOT OSXLIBSTD)
|
||||
# message(STATUS "Activating -std=c++11 flag for >= OS X 10.9")
|
||||
# set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
# endif()
|
||||
if(OSXLIBSTD)
|
||||
message(STATUS "linking against ${OSXLIBSTD}")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=${OSXLIBSTD}")
|
||||
elseif(DARWIN_VERSION GREATER 12)
|
||||
message(STATUS "linking against libc++")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT APPLE)
|
||||
target_link_libraries(osrm-datastore rt)
|
||||
target_link_libraries(OSRM rt)
|
||||
endif()
|
||||
|
||||
#Check Boost
|
||||
set(BOOST_MIN_VERSION "1.49.0")
|
||||
find_package(Boost ${BOOST_MIN_VERSION} COMPONENTS ${BOOST_COMPONENTS} REQUIRED)
|
||||
if(NOT Boost_FOUND)
|
||||
message(FATAL_ERROR "Fatal error: Boost (version >= 1.49.0) required.\n")
|
||||
endif()
|
||||
include_directories(${Boost_INCLUDE_DIRS})
|
||||
|
||||
target_link_libraries(OSRM ${Boost_LIBRARIES} COORDLIB)
|
||||
target_link_libraries(osrm-extract ${Boost_LIBRARIES} UUID GITDESCRIPTION COORDLIB)
|
||||
target_link_libraries(osrm-prepare ${Boost_LIBRARIES} UUID GITDESCRIPTION COORDLIB)
|
||||
target_link_libraries(osrm-routed ${Boost_LIBRARIES} OSRM UUID GITDESCRIPTION)
|
||||
target_link_libraries(osrm-datastore ${Boost_LIBRARIES} UUID GITDESCRIPTION COORDLIB)
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
target_link_libraries(osrm-extract ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
find_package(Lua52)
|
||||
if(NOT LUA52_FOUND)
|
||||
find_package(Lua51 REQUIRED)
|
||||
if(NOT APPLE)
|
||||
find_package(LuaJIT 5.1)
|
||||
endif()
|
||||
else()
|
||||
if(NOT APPLE)
|
||||
find_package(LuaJIT 5.2)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if( LUAJIT_FOUND )
|
||||
target_link_libraries(osrm-extract ${LUAJIT_LIBRARIES})
|
||||
target_link_libraries(osrm-prepare ${LUAJIT_LIBRARIES})
|
||||
else()
|
||||
target_link_libraries(osrm-extract ${LUA_LIBRARY})
|
||||
target_link_libraries(osrm-prepare ${LUA_LIBRARY})
|
||||
endif()
|
||||
include_directories(${LUA_INCLUDE_DIR})
|
||||
|
||||
find_package(LibXml2 REQUIRED)
|
||||
include_directories(${LIBXML2_INCLUDE_DIR})
|
||||
target_link_libraries(osrm-extract ${LIBXML2_LIBRARIES})
|
||||
|
||||
find_package( Luabind REQUIRED )
|
||||
include_directories(${LUABIND_INCLUDE_DIR})
|
||||
target_link_libraries(osrm-extract ${LUABIND_LIBRARY})
|
||||
target_link_libraries(osrm-prepare ${LUABIND_LIBRARY})
|
||||
|
||||
find_package( STXXL REQUIRED )
|
||||
include_directories(${STXXL_INCLUDE_DIR})
|
||||
target_link_libraries(OSRM ${STXXL_LIBRARY})
|
||||
target_link_libraries(osrm-extract ${STXXL_LIBRARY})
|
||||
target_link_libraries(osrm-prepare ${STXXL_LIBRARY})
|
||||
|
||||
find_package( OSMPBF REQUIRED )
|
||||
include_directories(${OSMPBF_INCLUDE_DIR})
|
||||
target_link_libraries(osrm-extract ${OSMPBF_LIBRARY})
|
||||
target_link_libraries(osrm-prepare ${OSMPBF_LIBRARY})
|
||||
|
||||
find_package(Protobuf REQUIRED)
|
||||
include_directories(${PROTOBUF_INCLUDE_DIRS})
|
||||
target_link_libraries(osrm-extract ${PROTOBUF_LIBRARY})
|
||||
target_link_libraries(osrm-prepare ${PROTOBUF_LIBRARY})
|
||||
|
||||
find_package(BZip2 REQUIRED)
|
||||
include_directories(${BZIP_INCLUDE_DIRS})
|
||||
target_link_libraries(osrm-extract ${BZIP2_LIBRARIES})
|
||||
|
||||
find_package(ZLIB REQUIRED)
|
||||
include_directories(${ZLIB_INCLUDE_DIRS})
|
||||
target_link_libraries(osrm-extract ${ZLIB_LIBRARY})
|
||||
target_link_libraries(osrm-routed ${ZLIB_LIBRARY})
|
||||
|
||||
if(WITH_TOOLS)
|
||||
message(STATUS "Activating OSRM internal tools")
|
||||
find_package(GDAL)
|
||||
if(GDAL_FOUND)
|
||||
add_executable(osrm-components Tools/components.cpp)
|
||||
include_directories(${GDAL_INCLUDE_DIR})
|
||||
target_link_libraries(
|
||||
osrm-components
|
||||
${GDAL_LIBRARIES} ${Boost_LIBRARIES} UUID GITDESCRIPTION COORDLIB)
|
||||
endif()
|
||||
add_executable(osrm-cli Tools/simpleclient.cpp)
|
||||
target_link_libraries(osrm-cli ${Boost_LIBRARIES} OSRM UUID GITDESCRIPTION)
|
||||
add_executable(osrm-io-benchmark Tools/io-benchmark.cpp)
|
||||
target_link_libraries(osrm-io-benchmark ${Boost_LIBRARIES} GITDESCRIPTION)
|
||||
add_executable(osrm-unlock-all Tools/unlock_all_mutexes.cpp)
|
||||
target_link_libraries(osrm-unlock-all ${Boost_LIBRARIES} GITDESCRIPTION)
|
||||
if(UNIX AND NOT APPLE)
|
||||
target_link_libraries(osrm-unlock-all rt)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
file(GLOB InstallGlob Include/osrm/*.h Library/OSRM.h)
|
||||
|
||||
# Add RPATH info to executables so that when they are run after being installed
|
||||
# (i.e., from /usr/local/bin/) the linker can find library dependencies. For
|
||||
# more info see http://www.cmake.org/Wiki/CMake_RPATH_handling
|
||||
set_property(TARGET osrm-extract PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
set_property(TARGET osrm-prepare PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
set_property(TARGET osrm-datastore PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
set_property(TARGET osrm-routed PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||
|
||||
install(FILES ${InstallGlob} DESTINATION include/osrm)
|
||||
install(TARGETS osrm-extract DESTINATION bin)
|
||||
install(TARGETS osrm-prepare DESTINATION bin)
|
||||
install(TARGETS osrm-datastore DESTINATION bin)
|
||||
install(TARGETS osrm-routed DESTINATION bin)
|
||||
install(TARGETS OSRM DESTINATION lib)
|
||||
list(GET Boost_LIBRARIES 1 BOOST_LIBRARY_FIRST)
|
||||
get_filename_component(BOOST_LIBRARY_LISTING "${BOOST_LIBRARY_FIRST}" PATH)
|
||||
set(BOOST_LIBRARY_LISTING "-L${BOOST_LIBRARY_LISTING}")
|
||||
foreach (lib ${Boost_LIBRARIES})
|
||||
get_filename_component(BOOST_LIBRARY_NAME "${lib}" NAME_WE)
|
||||
string(REPLACE "lib" "" BOOST_LIBRARY_NAME ${BOOST_LIBRARY_NAME})
|
||||
set(BOOST_LIBRARY_LISTING "${BOOST_LIBRARY_LISTING} -l${BOOST_LIBRARY_NAME}")
|
||||
endforeach ()
|
||||
|
||||
configure_file(${CMAKE_SOURCE_DIR}/cmake/pkgconfig.in libosrm.pc @ONLY)
|
||||
install(FILES ${PROJECT_BINARY_DIR}/libosrm.pc DESTINATION lib/pkgconfig)
|
||||
@@ -1,290 +0,0 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
|
||||
#ifndef CONTRACTIONCLEANUP_H_INCLUDED
|
||||
#define CONTRACTIONCLEANUP_H_INCLUDED
|
||||
|
||||
#ifdef _GLIBCXX_PARALLEL
|
||||
#include <parallel/algorithm>
|
||||
#else
|
||||
#include <algorithm>
|
||||
#endif
|
||||
#ifndef _WIN32
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#include "Contractor.h"
|
||||
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
class ContractionCleanup {
|
||||
private:
|
||||
|
||||
struct _CleanupHeapData {
|
||||
NodeID parent;
|
||||
_CleanupHeapData( NodeID p ) {
|
||||
parent = p;
|
||||
}
|
||||
};
|
||||
#ifdef _MANYCORES
|
||||
typedef BinaryHeap< NodeID, NodeID, int, _HeapData, DenseStorage<NodeID, NodeID> > _Heap;
|
||||
#else
|
||||
typedef BinaryHeap< NodeID, NodeID, int, _CleanupHeapData > _Heap;
|
||||
#endif
|
||||
struct _ThreadData {
|
||||
_Heap* _heapForward;
|
||||
_Heap* _heapBackward;
|
||||
_ThreadData( NodeID nodes ) {
|
||||
_heapBackward = new _Heap(nodes);
|
||||
_heapForward = new _Heap(nodes);
|
||||
}
|
||||
~_ThreadData()
|
||||
{
|
||||
delete _heapBackward;
|
||||
delete _heapForward;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
struct Edge {
|
||||
NodeID source;
|
||||
NodeID target;
|
||||
struct EdgeData {
|
||||
NodeID via;
|
||||
unsigned nameID1;
|
||||
unsigned nameID2;
|
||||
int distance;
|
||||
bool shortcut;
|
||||
bool forward;
|
||||
bool backward;
|
||||
short turnInstruction;
|
||||
} data;
|
||||
|
||||
//sorts by source and other attributes
|
||||
static bool CompareBySource( const Edge& left, const Edge& right ) {
|
||||
if ( left.source != right.source )
|
||||
return left.source < right.source;
|
||||
int l = ( left.data.forward ? -1 : 0 ) + ( left.data.backward ? -1 : 0 );
|
||||
int r = ( right.data.forward ? -1 : 0 ) + ( right.data.backward ? -1 : 0 );
|
||||
if ( l != r )
|
||||
return l < r;
|
||||
if ( left.target != right.target )
|
||||
return left.target < right.target;
|
||||
return left.data.distance < right.data.distance;
|
||||
}
|
||||
|
||||
bool operator== ( const Edge& right ) const {
|
||||
return ( source == right.source && target == right.target && data.distance == right.data.distance &&
|
||||
data.shortcut == right.data.shortcut && data.forward == right.data.forward && data.backward == right.data.backward
|
||||
&& data.via == right.data.via && data.nameID1 == right.data.nameID1 && data.nameID2 == right.data.nameID2
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
ContractionCleanup( int numNodes, const std::vector< Edge >& edges ) {
|
||||
_graph = edges;
|
||||
_numNodes = numNodes;
|
||||
}
|
||||
|
||||
~ContractionCleanup() {
|
||||
|
||||
}
|
||||
|
||||
void Run() {
|
||||
RemoveUselessShortcuts();
|
||||
}
|
||||
|
||||
template< class EdgeT >
|
||||
void GetData( std::vector< EdgeT >& edges ) {
|
||||
for ( int edge = 0, endEdges = ( int ) _graph.size(); edge != endEdges; ++edge ) {
|
||||
if(_graph[edge].data.forward || _graph[edge].data.backward) {
|
||||
EdgeT newEdge;
|
||||
newEdge.source = _graph[edge].source;
|
||||
newEdge.target = _graph[edge].target;
|
||||
newEdge.data = _graph[edge].data;
|
||||
edges.push_back( newEdge );
|
||||
}
|
||||
}
|
||||
#ifdef _GLIBCXX_PARALLEL
|
||||
__gnu_parallel::sort( edges.begin(), edges.end() );
|
||||
#else
|
||||
sort( edges.begin(), edges.end() );
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
double _Timestamp() {
|
||||
struct timeval tp;
|
||||
gettimeofday(&tp, NULL);
|
||||
return double(tp.tv_sec) + tp.tv_usec / 1000000.;
|
||||
}
|
||||
|
||||
void BuildOutgoingGraph() {
|
||||
//sort edges by source
|
||||
#ifdef _GLIBCXX_PARALLEL
|
||||
__gnu_parallel::sort( _graph.begin(), _graph.end(), Edge::CompareBySource );
|
||||
#else
|
||||
sort( _graph.begin(), _graph.end(), Edge::CompareBySource );
|
||||
#endif
|
||||
try {
|
||||
_firstEdge.resize( _numNodes + 1 );
|
||||
} catch(...) {
|
||||
cerr << "Not enough RAM on machine" << endl;
|
||||
return;
|
||||
}
|
||||
_firstEdge[0] = 0;
|
||||
for ( NodeID i = 0, node = 0; i < ( NodeID ) _graph.size(); i++ ) {
|
||||
while ( _graph[i].source != node )
|
||||
_firstEdge[++node] = i;
|
||||
if ( i == ( NodeID ) _graph.size() - 1 )
|
||||
while ( node < _numNodes )
|
||||
_firstEdge[++node] = ( int ) _graph.size();
|
||||
}
|
||||
}
|
||||
|
||||
void RemoveUselessShortcuts() {
|
||||
int maxThreads = omp_get_max_threads();
|
||||
std::vector < _ThreadData* > threadData;
|
||||
for ( int threadNum = 0; threadNum < maxThreads; ++threadNum ) {
|
||||
threadData.push_back( new _ThreadData( _numNodes ) );
|
||||
}
|
||||
|
||||
cout << "Scanning for useless shortcuts" << endl;
|
||||
BuildOutgoingGraph();
|
||||
#pragma omp parallel for
|
||||
for ( int i = 0; i < ( int ) _graph.size(); i++ ) {
|
||||
for ( unsigned edge = _firstEdge[_graph[i].source]; edge < _firstEdge[_graph[i].source + 1]; ++edge ) {
|
||||
if ( edge == (unsigned)i )
|
||||
continue;
|
||||
if ( _graph[edge].target != _graph[i].target )
|
||||
continue;
|
||||
if ( _graph[edge].data.distance < _graph[i].data.distance )
|
||||
continue;
|
||||
|
||||
_graph[edge].data.forward &= !_graph[i].data.forward;
|
||||
_graph[edge].data.backward &= !_graph[i].data.backward;
|
||||
}
|
||||
|
||||
if ( !_graph[i].data.forward && !_graph[i].data.backward )
|
||||
continue;
|
||||
|
||||
//only remove shortcuts
|
||||
if ( !_graph[i].data.shortcut )
|
||||
continue;
|
||||
|
||||
if ( _graph[i].data.forward ) {
|
||||
int result = _ComputeDistance( _graph[i].source, _graph[i].target, threadData[omp_get_thread_num()] );
|
||||
if ( result < _graph[i].data.distance ) {
|
||||
_graph[i].data.forward = false;
|
||||
}
|
||||
}
|
||||
if ( _graph[i].data.backward ) {
|
||||
int result = _ComputeDistance( _graph[i].target, _graph[i].source, threadData[omp_get_thread_num()] );
|
||||
|
||||
if ( result < _graph[i].data.distance ) {
|
||||
_graph[i].data.backward = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cout << "Removing edges" << endl;
|
||||
int useful = 0;
|
||||
for ( int i = 0; i < ( int ) _graph.size(); i++ ) {
|
||||
if ( !_graph[i].data.forward && !_graph[i].data.backward && _graph[i].data.shortcut )
|
||||
continue;
|
||||
_graph[useful] = _graph[i];
|
||||
useful++;
|
||||
}
|
||||
cout << "Removed " << _graph.size() - useful << " useless shortcuts" << endl;
|
||||
_graph.resize( useful );
|
||||
|
||||
for ( int threadNum = 0; threadNum < maxThreads; ++threadNum ) {
|
||||
delete threadData[threadNum];
|
||||
}
|
||||
}
|
||||
|
||||
void _ComputeStep( _Heap* heapForward, _Heap* heapBackward, bool forwardDirection, NodeID* middle, int* targetDistance ) {
|
||||
|
||||
const NodeID node = heapForward->DeleteMin();
|
||||
const int distance = heapForward->GetKey( node );
|
||||
|
||||
if ( heapBackward->WasInserted( node ) ) {
|
||||
const int newDistance = heapBackward->GetKey( node ) + distance;
|
||||
if ( newDistance < *targetDistance ) {
|
||||
*middle = node;
|
||||
*targetDistance = newDistance;
|
||||
}
|
||||
}
|
||||
|
||||
if ( distance > *targetDistance ) {
|
||||
heapForward->DeleteAll();
|
||||
return;
|
||||
}
|
||||
for ( int edge = _firstEdge[node], endEdges = _firstEdge[node + 1]; edge != endEdges; ++edge ) {
|
||||
const NodeID to = _graph[edge].target;
|
||||
const int edgeWeight = _graph[edge].data.distance;
|
||||
assert( edgeWeight > 0 );
|
||||
const int toDistance = distance + edgeWeight;
|
||||
|
||||
if ( (forwardDirection ? _graph[edge].data.forward : _graph[edge].data.backward ) ) {
|
||||
//New Node discovered -> Add to Heap + Node Info Storage
|
||||
if ( !heapForward->WasInserted( to ) )
|
||||
heapForward->Insert( to, toDistance, node );
|
||||
|
||||
//Found a shorter Path -> Update distance
|
||||
else if ( toDistance < heapForward->GetKey( to ) ) {
|
||||
heapForward->DecreaseKey( to, toDistance );
|
||||
//new parent
|
||||
heapForward->GetData( to ) = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int _ComputeDistance( NodeID source, NodeID target, _ThreadData * data, std::vector< NodeID >* path = NULL ) {
|
||||
data->_heapForward->Clear();
|
||||
data->_heapBackward->Clear();
|
||||
//insert source into heap
|
||||
data->_heapForward->Insert( source, 0, source );
|
||||
data->_heapBackward->Insert( target, 0, target );
|
||||
|
||||
int targetDistance = (std::numeric_limits< int >::max)();
|
||||
NodeID middle = (std::numeric_limits<NodeID>::max)();
|
||||
|
||||
while ( data->_heapForward->Size() + data->_heapBackward->Size() > 0 ) {
|
||||
if ( data->_heapForward->Size() > 0 ) {
|
||||
_ComputeStep( data->_heapForward, data->_heapBackward, true, &middle, &targetDistance );
|
||||
}
|
||||
|
||||
if ( data->_heapBackward->Size() > 0 ) {
|
||||
_ComputeStep( data->_heapBackward, data->_heapForward, false, &middle, &targetDistance );
|
||||
}
|
||||
}
|
||||
return targetDistance;
|
||||
}
|
||||
NodeID _numNodes;
|
||||
std::vector< Edge > _graph;
|
||||
std::vector< unsigned > _firstEdge;
|
||||
};
|
||||
|
||||
#endif // CONTRACTIONCLEANUP_H_INCLUDED
|
||||
@@ -1,234 +1,775 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
#ifdef _GLIBCXX_PARALLEL
|
||||
#include <parallel/algorithm>
|
||||
#else
|
||||
#include <algorithm>
|
||||
#endif
|
||||
#include <boost/foreach.hpp>
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include "EdgeBasedGraphFactory.h"
|
||||
#include "../Algorithms/BFSComponentExplorer.h"
|
||||
#include "../DataStructures/Percent.h"
|
||||
#include "../Util/ComputeAngle.h"
|
||||
#include "../Util/LuaUtil.h"
|
||||
#include "../Util/SimpleLogger.h"
|
||||
#include "../Util/TimingUtil.h"
|
||||
|
||||
template<>
|
||||
EdgeBasedGraphFactory::EdgeBasedGraphFactory(int nodes, std::vector<NodeBasedEdge> & inputEdges, std::vector<_Restriction> & irs, std::vector<NodeInfo> & nI)
|
||||
: inputRestrictions(irs), inputNodeInfoList(nI) {
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#ifdef _GLIBCXX_PARALLEL
|
||||
__gnu_parallel::sort(inputRestrictions.begin(), inputRestrictions.end(), CmpRestrictionByFrom);
|
||||
#else
|
||||
std::sort(inputRestrictions.begin(), inputRestrictions.end(), CmpRestrictionByFrom);
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
|
||||
EdgeBasedGraphFactory::EdgeBasedGraphFactory(
|
||||
const std::shared_ptr<NodeBasedDynamicGraph> &node_based_graph,
|
||||
std::unique_ptr<RestrictionMap> restriction_map,
|
||||
std::vector<NodeID> &barrier_node_list,
|
||||
std::vector<NodeID> &traffic_light_node_list,
|
||||
std::vector<NodeInfo> &m_node_info_list,
|
||||
SpeedProfileProperties &speed_profile)
|
||||
: speed_profile(speed_profile),
|
||||
m_number_of_edge_based_nodes(std::numeric_limits<unsigned>::max()),
|
||||
m_node_info_list(m_node_info_list), m_node_based_graph(node_based_graph),
|
||||
m_restriction_map(std::move(restriction_map)), max_id(0)
|
||||
{
|
||||
|
||||
// insert into unordered sets for fast lookup
|
||||
m_barrier_nodes.insert(barrier_node_list.begin(), barrier_node_list.end());
|
||||
m_traffic_lights.insert(traffic_light_node_list.begin(), traffic_light_node_list.end());
|
||||
}
|
||||
|
||||
void EdgeBasedGraphFactory::GetEdgeBasedEdges(DeallocatingVector<EdgeBasedEdge> &output_edge_list)
|
||||
{
|
||||
BOOST_ASSERT_MSG(0 == output_edge_list.size(), "Vector is not empty");
|
||||
m_edge_based_edge_list.swap(output_edge_list);
|
||||
}
|
||||
|
||||
void EdgeBasedGraphFactory::GetEdgeBasedNodes(std::vector<EdgeBasedNode> &nodes)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
for (const EdgeBasedNode &node : m_edge_based_node_list)
|
||||
{
|
||||
BOOST_ASSERT(m_node_info_list.at(node.u).lat != INT_MAX);
|
||||
BOOST_ASSERT(m_node_info_list.at(node.u).lon != INT_MAX);
|
||||
BOOST_ASSERT(m_node_info_list.at(node.v).lon != INT_MAX);
|
||||
BOOST_ASSERT(m_node_info_list.at(node.v).lat != INT_MAX);
|
||||
}
|
||||
#endif
|
||||
nodes.swap(m_edge_based_node_list);
|
||||
}
|
||||
|
||||
void
|
||||
EdgeBasedGraphFactory::InsertEdgeBasedNode(NodeID u, NodeID v, EdgeID e1, bool belongs_to_tiny_cc)
|
||||
{
|
||||
// merge edges together into one EdgeBasedNode
|
||||
BOOST_ASSERT(u != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(v != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(e1 != SPECIAL_EDGEID);
|
||||
|
||||
#ifndef NDEBUG
|
||||
// find forward edge id and
|
||||
const EdgeID e1b = m_node_based_graph->FindEdge(u, v);
|
||||
BOOST_ASSERT(e1 == e1b);
|
||||
#endif
|
||||
|
||||
std::vector< _NodeBasedEdge > edges;
|
||||
edges.reserve( 2 * inputEdges.size() );
|
||||
for ( typename std::vector< NodeBasedEdge >::const_iterator i = inputEdges.begin(), e = inputEdges.end(); i != e; ++i ) {
|
||||
_NodeBasedEdge edge;
|
||||
edge.source = i->source();
|
||||
edge.target = i->target();
|
||||
BOOST_ASSERT(e1 != SPECIAL_EDGEID);
|
||||
const EdgeData &forward_data = m_node_based_graph->GetEdgeData(e1);
|
||||
|
||||
if(edge.source == edge.target)
|
||||
// find reverse edge id and
|
||||
const EdgeID e2 = m_node_based_graph->FindEdge(v, u);
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (e2 == m_node_based_graph->EndEdges(v))
|
||||
{
|
||||
SimpleLogger().Write(logWARNING) << "Did not find edge (" << v << "," << u << ")";
|
||||
}
|
||||
#endif
|
||||
BOOST_ASSERT(e2 != SPECIAL_EDGEID);
|
||||
BOOST_ASSERT(e2 < m_node_based_graph->EndEdges(v));
|
||||
const EdgeData &reverse_data = m_node_based_graph->GetEdgeData(e2);
|
||||
|
||||
if (forward_data.edgeBasedNodeID == SPECIAL_NODEID &&
|
||||
reverse_data.edgeBasedNodeID == SPECIAL_NODEID)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(m_geometry_compressor.HasEntryForID(e1) ==
|
||||
m_geometry_compressor.HasEntryForID(e2));
|
||||
if (m_geometry_compressor.HasEntryForID(e1))
|
||||
{
|
||||
BOOST_ASSERT(m_geometry_compressor.HasEntryForID(e2));
|
||||
|
||||
// reconstruct geometry and put in each individual edge with its offset
|
||||
const std::vector<GeometryCompressor::CompressedNode> &forward_geometry =
|
||||
m_geometry_compressor.GetBucketReference(e1);
|
||||
const std::vector<GeometryCompressor::CompressedNode> &reverse_geometry =
|
||||
m_geometry_compressor.GetBucketReference(e2);
|
||||
BOOST_ASSERT(forward_geometry.size() == reverse_geometry.size());
|
||||
BOOST_ASSERT(0 != forward_geometry.size());
|
||||
|
||||
// reconstruct bidirectional edge with individual weights and put each into the NN index
|
||||
|
||||
std::vector<int> forward_dist_prefix_sum(forward_geometry.size(), 0);
|
||||
std::vector<int> reverse_dist_prefix_sum(reverse_geometry.size(), 0);
|
||||
|
||||
// quick'n'dirty prefix sum as std::partial_sum needs addtional casts
|
||||
// TODO: move to lambda function with C++11
|
||||
int temp_sum = 0;
|
||||
|
||||
for (unsigned i = 0; i < forward_geometry.size(); ++i)
|
||||
{
|
||||
forward_dist_prefix_sum[i] = temp_sum;
|
||||
temp_sum += forward_geometry[i].second;
|
||||
|
||||
BOOST_ASSERT(forward_data.distance >= temp_sum);
|
||||
}
|
||||
|
||||
temp_sum = 0;
|
||||
for (unsigned i = 0; i < reverse_geometry.size(); ++i)
|
||||
{
|
||||
temp_sum += reverse_geometry[reverse_geometry.size() - 1 - i].second;
|
||||
reverse_dist_prefix_sum[i] = reverse_data.distance - temp_sum;
|
||||
BOOST_ASSERT(reverse_data.distance >= temp_sum);
|
||||
}
|
||||
|
||||
BOOST_ASSERT(forward_geometry.size() == reverse_geometry.size());
|
||||
|
||||
const unsigned geometry_size = forward_geometry.size();
|
||||
BOOST_ASSERT(geometry_size > 1);
|
||||
NodeID current_edge_start_coordinate_id = u;
|
||||
|
||||
if (forward_data.edgeBasedNodeID != SPECIAL_NODEID)
|
||||
{
|
||||
max_id = std::max(forward_data.edgeBasedNodeID, max_id);
|
||||
}
|
||||
if (SPECIAL_NODEID != reverse_data.edgeBasedNodeID)
|
||||
{
|
||||
max_id = std::max(reverse_data.edgeBasedNodeID, max_id);
|
||||
}
|
||||
|
||||
// traverse arrays from start and end respectively
|
||||
for (unsigned i = 0; i < geometry_size; ++i)
|
||||
{
|
||||
BOOST_ASSERT(current_edge_start_coordinate_id ==
|
||||
reverse_geometry[geometry_size - 1 - i].first);
|
||||
const NodeID current_edge_target_coordinate_id = forward_geometry[i].first;
|
||||
BOOST_ASSERT(current_edge_target_coordinate_id != current_edge_start_coordinate_id);
|
||||
|
||||
// build edges
|
||||
m_edge_based_node_list.emplace_back(forward_data.edgeBasedNodeID,
|
||||
reverse_data.edgeBasedNodeID,
|
||||
current_edge_start_coordinate_id,
|
||||
current_edge_target_coordinate_id,
|
||||
forward_data.nameID,
|
||||
forward_geometry[i].second,
|
||||
reverse_geometry[i].second,
|
||||
forward_dist_prefix_sum[i],
|
||||
reverse_dist_prefix_sum[i],
|
||||
m_geometry_compressor.GetPositionForID(e1),
|
||||
i,
|
||||
belongs_to_tiny_cc);
|
||||
current_edge_start_coordinate_id = current_edge_target_coordinate_id;
|
||||
|
||||
BOOST_ASSERT(m_edge_based_node_list.back().IsCompressed());
|
||||
|
||||
BOOST_ASSERT(u != m_edge_based_node_list.back().u ||
|
||||
v != m_edge_based_node_list.back().v);
|
||||
|
||||
BOOST_ASSERT(u != m_edge_based_node_list.back().v ||
|
||||
v != m_edge_based_node_list.back().u);
|
||||
}
|
||||
|
||||
BOOST_ASSERT(current_edge_start_coordinate_id == v);
|
||||
BOOST_ASSERT(m_edge_based_node_list.back().IsCompressed());
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(!m_geometry_compressor.HasEntryForID(e2));
|
||||
|
||||
if (forward_data.edgeBasedNodeID != SPECIAL_NODEID)
|
||||
{
|
||||
BOOST_ASSERT(forward_data.forward);
|
||||
}
|
||||
if (reverse_data.edgeBasedNodeID != SPECIAL_NODEID)
|
||||
{
|
||||
BOOST_ASSERT(reverse_data.forward);
|
||||
}
|
||||
if (forward_data.edgeBasedNodeID == SPECIAL_NODEID)
|
||||
{
|
||||
BOOST_ASSERT(!forward_data.forward);
|
||||
}
|
||||
if (reverse_data.edgeBasedNodeID == SPECIAL_NODEID)
|
||||
{
|
||||
BOOST_ASSERT(!reverse_data.forward);
|
||||
}
|
||||
|
||||
BOOST_ASSERT(forward_data.edgeBasedNodeID != SPECIAL_NODEID ||
|
||||
reverse_data.edgeBasedNodeID != SPECIAL_NODEID);
|
||||
|
||||
m_edge_based_node_list.emplace_back(EdgeBasedNode(forward_data.edgeBasedNodeID,
|
||||
reverse_data.edgeBasedNodeID,
|
||||
u,
|
||||
v,
|
||||
forward_data.nameID,
|
||||
forward_data.distance,
|
||||
reverse_data.distance,
|
||||
0,
|
||||
0,
|
||||
SPECIAL_EDGEID,
|
||||
0,
|
||||
belongs_to_tiny_cc));
|
||||
BOOST_ASSERT(!m_edge_based_node_list.back().IsCompressed());
|
||||
}
|
||||
}
|
||||
|
||||
void EdgeBasedGraphFactory::FlushVectorToStream(
|
||||
std::ofstream &edge_data_file, std::vector<OriginalEdgeData> &original_edge_data_vector) const
|
||||
{
|
||||
edge_data_file.write((char *)&(original_edge_data_vector[0]),
|
||||
original_edge_data_vector.size() * sizeof(OriginalEdgeData));
|
||||
original_edge_data_vector.clear();
|
||||
}
|
||||
|
||||
void EdgeBasedGraphFactory::Run(const std::string &original_edge_data_filename,
|
||||
const std::string &geometry_filename,
|
||||
lua_State *lua_state)
|
||||
{
|
||||
|
||||
TIMER_START(geometry);
|
||||
CompressGeometry();
|
||||
TIMER_STOP(geometry);
|
||||
|
||||
TIMER_START(renumber);
|
||||
RenumberEdges();
|
||||
TIMER_STOP(renumber);
|
||||
|
||||
TIMER_START(generate_nodes);
|
||||
GenerateEdgeExpandedNodes();
|
||||
TIMER_STOP(generate_nodes);
|
||||
|
||||
TIMER_START(generate_edges);
|
||||
GenerateEdgeExpandedEdges(original_edge_data_filename, lua_state);
|
||||
TIMER_STOP(generate_edges);
|
||||
|
||||
m_geometry_compressor.SerializeInternalVector(geometry_filename);
|
||||
|
||||
SimpleLogger().Write() << "Timing statistics for edge-expanded graph:";
|
||||
SimpleLogger().Write() << "Geometry compression: " << TIMER_MSEC(geometry)*0.001 << "s";
|
||||
SimpleLogger().Write() << "Renumbering edges: " << TIMER_MSEC(renumber)*0.001 << "s";
|
||||
SimpleLogger().Write() << "Generating nodes: " << TIMER_MSEC(generate_nodes)*0.001 << "s";
|
||||
SimpleLogger().Write() << "Generating edges: " << TIMER_MSEC(generate_edges)*0.001 << "s";
|
||||
}
|
||||
|
||||
void EdgeBasedGraphFactory::CompressGeometry()
|
||||
{
|
||||
SimpleLogger().Write() << "Removing graph geometry while preserving topology";
|
||||
|
||||
const unsigned original_number_of_nodes = m_node_based_graph->GetNumberOfNodes();
|
||||
const unsigned original_number_of_edges = m_node_based_graph->GetNumberOfEdges();
|
||||
|
||||
Percent p(original_number_of_nodes);
|
||||
unsigned removed_node_count = 0;
|
||||
|
||||
for (NodeID v = 0; v < original_number_of_nodes; ++v)
|
||||
{
|
||||
p.printStatus(v);
|
||||
|
||||
// only contract degree 2 vertices
|
||||
if (2 != m_node_based_graph->GetOutDegree(v))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
edge.data.distance = (std::max)((int)i->weight(), 1 );
|
||||
assert( edge.data.distance > 0 );
|
||||
edge.data.shortcut = false;
|
||||
edge.data.roundabout = i->isRoundabout();
|
||||
edge.data.nameID = i->name();
|
||||
edge.data.type = i->type();
|
||||
edge.data.forward = i->isForward();
|
||||
edge.data.backward = i->isBackward();
|
||||
edge.data.edgeBasedNodeID = edges.size();
|
||||
edges.push_back( edge );
|
||||
std::swap( edge.source, edge.target );
|
||||
if( edge.data.backward ) {
|
||||
edge.data.forward = i->isBackward();
|
||||
edge.data.backward = i->isForward();
|
||||
edge.data.edgeBasedNodeID = edges.size();
|
||||
edges.push_back( edge );
|
||||
// don't contract barrier node
|
||||
if (m_barrier_nodes.end() != m_barrier_nodes.find(v))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if v is a via node for a turn restriction, i.e. a 'directed' barrier node
|
||||
if (m_restriction_map->IsNodeAViaNode(v))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool reverse_edge_order =
|
||||
!(m_node_based_graph->GetEdgeData(m_node_based_graph->BeginEdges(v)).forward);
|
||||
const EdgeID forward_e2 = m_node_based_graph->BeginEdges(v) + reverse_edge_order;
|
||||
BOOST_ASSERT(SPECIAL_EDGEID != forward_e2);
|
||||
const EdgeID reverse_e2 = m_node_based_graph->BeginEdges(v) + 1 - reverse_edge_order;
|
||||
BOOST_ASSERT(SPECIAL_EDGEID != reverse_e2);
|
||||
|
||||
const EdgeData &fwd_edge_data2 = m_node_based_graph->GetEdgeData(forward_e2);
|
||||
const EdgeData &rev_edge_data2 = m_node_based_graph->GetEdgeData(reverse_e2);
|
||||
|
||||
const NodeID w = m_node_based_graph->GetTarget(forward_e2);
|
||||
BOOST_ASSERT(SPECIAL_NODEID != w);
|
||||
BOOST_ASSERT(v != w);
|
||||
const NodeID u = m_node_based_graph->GetTarget(reverse_e2);
|
||||
BOOST_ASSERT(SPECIAL_NODEID != u);
|
||||
BOOST_ASSERT(u != v);
|
||||
|
||||
const EdgeID forward_e1 = m_node_based_graph->FindEdge(u, v);
|
||||
BOOST_ASSERT(m_node_based_graph->EndEdges(u) != forward_e1);
|
||||
BOOST_ASSERT(SPECIAL_EDGEID != forward_e1);
|
||||
BOOST_ASSERT(v == m_node_based_graph->GetTarget(forward_e1));
|
||||
const EdgeID reverse_e1 = m_node_based_graph->FindEdge(w, v);
|
||||
BOOST_ASSERT(SPECIAL_EDGEID != reverse_e1);
|
||||
BOOST_ASSERT(v == m_node_based_graph->GetTarget(reverse_e1));
|
||||
|
||||
const EdgeData &fwd_edge_data1 = m_node_based_graph->GetEdgeData(forward_e1);
|
||||
const EdgeData &rev_edge_data1 = m_node_based_graph->GetEdgeData(reverse_e1);
|
||||
|
||||
if ((m_node_based_graph->FindEdge(u, w) != m_node_based_graph->EndEdges(u)) ||
|
||||
(m_node_based_graph->FindEdge(w, u) != m_node_based_graph->EndEdges(w)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( // TODO: rename to IsCompatibleTo
|
||||
fwd_edge_data1.IsEqualTo(fwd_edge_data2) &&
|
||||
rev_edge_data1.IsEqualTo(rev_edge_data2))
|
||||
{
|
||||
// Get distances before graph is modified
|
||||
const int forward_weight1 = m_node_based_graph->GetEdgeData(forward_e1).distance;
|
||||
const int forward_weight2 = m_node_based_graph->GetEdgeData(forward_e2).distance;
|
||||
|
||||
BOOST_ASSERT(0 != forward_weight1);
|
||||
BOOST_ASSERT(0 != forward_weight2);
|
||||
|
||||
const int reverse_weight1 = m_node_based_graph->GetEdgeData(reverse_e1).distance;
|
||||
const int reverse_weight2 = m_node_based_graph->GetEdgeData(reverse_e2).distance;
|
||||
|
||||
BOOST_ASSERT(0 != reverse_weight1);
|
||||
BOOST_ASSERT(0 != forward_weight2);
|
||||
|
||||
const bool add_traffic_signal_penalty =
|
||||
(m_traffic_lights.find(v) != m_traffic_lights.end());
|
||||
|
||||
// add weight of e2's to e1
|
||||
m_node_based_graph->GetEdgeData(forward_e1).distance += fwd_edge_data2.distance;
|
||||
m_node_based_graph->GetEdgeData(reverse_e1).distance += rev_edge_data2.distance;
|
||||
if (add_traffic_signal_penalty)
|
||||
{
|
||||
m_node_based_graph->GetEdgeData(forward_e1).distance +=
|
||||
speed_profile.trafficSignalPenalty;
|
||||
m_node_based_graph->GetEdgeData(reverse_e1).distance +=
|
||||
speed_profile.trafficSignalPenalty;
|
||||
}
|
||||
|
||||
// extend e1's to targets of e2's
|
||||
m_node_based_graph->SetTarget(forward_e1, w);
|
||||
m_node_based_graph->SetTarget(reverse_e1, u);
|
||||
|
||||
// remove e2's (if bidir, otherwise only one)
|
||||
m_node_based_graph->DeleteEdge(v, forward_e2);
|
||||
m_node_based_graph->DeleteEdge(v, reverse_e2);
|
||||
|
||||
// update any involved turn restrictions
|
||||
m_restriction_map->FixupStartingTurnRestriction(u, v, w);
|
||||
m_restriction_map->FixupArrivingTurnRestriction(u, v, w);
|
||||
|
||||
m_restriction_map->FixupStartingTurnRestriction(w, v, u);
|
||||
m_restriction_map->FixupArrivingTurnRestriction(w, v, u);
|
||||
|
||||
// store compressed geometry in container
|
||||
m_geometry_compressor.CompressEdge(
|
||||
forward_e1,
|
||||
forward_e2,
|
||||
v,
|
||||
w,
|
||||
forward_weight1 +
|
||||
(add_traffic_signal_penalty ? speed_profile.trafficSignalPenalty : 0),
|
||||
forward_weight2);
|
||||
m_geometry_compressor.CompressEdge(
|
||||
reverse_e1,
|
||||
reverse_e2,
|
||||
v,
|
||||
u,
|
||||
reverse_weight1,
|
||||
reverse_weight2 +
|
||||
(add_traffic_signal_penalty ? speed_profile.trafficSignalPenalty : 0));
|
||||
++removed_node_count;
|
||||
|
||||
BOOST_ASSERT(m_node_based_graph->GetEdgeData(forward_e1).nameID ==
|
||||
m_node_based_graph->GetEdgeData(reverse_e1).nameID);
|
||||
}
|
||||
}
|
||||
SimpleLogger().Write() << "removed " << removed_node_count << " nodes";
|
||||
m_geometry_compressor.PrintStatistics();
|
||||
|
||||
unsigned new_node_count = 0;
|
||||
unsigned new_edge_count = 0;
|
||||
for (unsigned i = 0; i < m_node_based_graph->GetNumberOfNodes(); ++i)
|
||||
{
|
||||
if (m_node_based_graph->GetOutDegree(i) > 0)
|
||||
{
|
||||
++new_node_count;
|
||||
new_edge_count += (m_node_based_graph->EndEdges(i) - m_node_based_graph->BeginEdges(i));
|
||||
}
|
||||
}
|
||||
SimpleLogger().Write() << "new nodes: " << new_node_count << ", edges " << new_edge_count;
|
||||
SimpleLogger().Write() << "Node compression ratio: " << new_node_count /
|
||||
(double)original_number_of_nodes;
|
||||
SimpleLogger().Write() << "Edge compression ratio: " << new_edge_count /
|
||||
(double)original_number_of_edges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the id of the edge in the edge expanded graph (into the egde in the node based graph)
|
||||
*/
|
||||
void EdgeBasedGraphFactory::RenumberEdges()
|
||||
{
|
||||
// renumber edge based node IDs
|
||||
unsigned numbered_edges_count = 0;
|
||||
for (NodeID current_node = 0; current_node < m_node_based_graph->GetNumberOfNodes();
|
||||
++current_node)
|
||||
{
|
||||
for (EdgeID current_edge : m_node_based_graph->GetAdjacentEdgeRange(current_node))
|
||||
{
|
||||
EdgeData &edge_data = m_node_based_graph->GetEdgeData(current_edge);
|
||||
if (!edge_data.forward)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(numbered_edges_count < m_node_based_graph->GetNumberOfEdges());
|
||||
edge_data.edgeBasedNodeID = numbered_edges_count;
|
||||
++numbered_edges_count;
|
||||
|
||||
BOOST_ASSERT(SPECIAL_NODEID != edge_data.edgeBasedNodeID);
|
||||
}
|
||||
}
|
||||
m_number_of_edge_based_nodes = numbered_edges_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the nodes in the edge expanded graph from edges in the node-based graph.
|
||||
*/
|
||||
void EdgeBasedGraphFactory::GenerateEdgeExpandedNodes()
|
||||
{
|
||||
SimpleLogger().Write() << "Identifying components of the road network";
|
||||
|
||||
// Run a BFS on the undirected graph and identify small components
|
||||
BFSComponentExplorer<NodeBasedDynamicGraph> component_explorer(
|
||||
*m_node_based_graph, *m_restriction_map, m_barrier_nodes);
|
||||
|
||||
component_explorer.run();
|
||||
|
||||
SimpleLogger().Write() << "identified: " << component_explorer.GetNumberOfComponents()
|
||||
<< " many components";
|
||||
SimpleLogger().Write() << "generating edge-expanded nodes";
|
||||
|
||||
Percent p(m_node_based_graph->GetNumberOfNodes());
|
||||
|
||||
// loop over all edges and generate new set of nodes
|
||||
for (NodeID u = 0, end = m_node_based_graph->GetNumberOfNodes(); u < end; ++u)
|
||||
{
|
||||
BOOST_ASSERT(u != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(u < m_node_based_graph->GetNumberOfNodes());
|
||||
p.printIncrement();
|
||||
for (EdgeID e1 : m_node_based_graph->GetAdjacentEdgeRange(u))
|
||||
{
|
||||
const EdgeData &edge_data = m_node_based_graph->GetEdgeData(e1);
|
||||
if (edge_data.edgeBasedNodeID == SPECIAL_NODEID)
|
||||
{
|
||||
// continue;
|
||||
}
|
||||
BOOST_ASSERT(e1 != SPECIAL_EDGEID);
|
||||
const NodeID v = m_node_based_graph->GetTarget(e1);
|
||||
|
||||
BOOST_ASSERT(SPECIAL_NODEID != v);
|
||||
// pick only every other edge
|
||||
if (u > v)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(u < v);
|
||||
BOOST_ASSERT(edge_data.type != SHRT_MAX);
|
||||
|
||||
// Note: edges that end on barrier nodes or on a turn restriction
|
||||
// may actually be in two distinct components. We choose the smallest
|
||||
const unsigned size_of_component = std::min(component_explorer.GetComponentSize(u),
|
||||
component_explorer.GetComponentSize(v));
|
||||
|
||||
const bool component_is_tiny = (size_of_component < 1000);
|
||||
InsertEdgeBasedNode(u, v, e1, component_is_tiny);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef _GLIBCXX_PARALLEL
|
||||
__gnu_parallel::sort( edges.begin(), edges.end() );
|
||||
#else
|
||||
sort( edges.begin(), edges.end() );
|
||||
#endif
|
||||
|
||||
_nodeBasedGraph.reset(new _NodeBasedDynamicGraph( nodes, edges ));
|
||||
INFO("Converted " << inputEdges.size() << " node-based edges into " << _nodeBasedGraph->GetNumberOfEdges() << " edge-based nodes.");
|
||||
SimpleLogger().Write() << "Generated " << m_edge_based_node_list.size()
|
||||
<< " nodes in edge-expanded graph";
|
||||
}
|
||||
|
||||
template<>
|
||||
void EdgeBasedGraphFactory::GetEdgeBasedEdges( std::vector< EdgeBasedEdge >& outputEdgeList ) {
|
||||
/**
|
||||
* Actually it also generates OriginalEdgeData and serializes them...
|
||||
*/
|
||||
void
|
||||
EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(const std::string &original_edge_data_filename,
|
||||
lua_State *lua_state)
|
||||
{
|
||||
SimpleLogger().Write() << "generating edge-expanded edges";
|
||||
|
||||
GUARANTEE(0 == outputEdgeList.size(), "Vector passed to EdgeBasedGraphFactory::GetEdgeBasedEdges(..) is not empty");
|
||||
GUARANTEE(0 != edgeBasedEdges.size(), "No edges in edge based graph");
|
||||
unsigned node_based_edge_counter = 0;
|
||||
unsigned original_edges_counter = 0;
|
||||
|
||||
edgeBasedEdges.swap(outputEdgeList);
|
||||
}
|
||||
std::ofstream edge_data_file(original_edge_data_filename.c_str(), std::ios::binary);
|
||||
|
||||
void EdgeBasedGraphFactory::GetEdgeBasedNodes( std::vector< EdgeBasedNode> & nodes) {
|
||||
for(unsigned i = 0; i < edgeBasedNodes.size(); ++i)
|
||||
nodes.push_back(edgeBasedNodes[i]);
|
||||
}
|
||||
// writes a dummy value that is updated later
|
||||
edge_data_file.write((char *)&original_edges_counter, sizeof(unsigned));
|
||||
|
||||
void EdgeBasedGraphFactory::Run() {
|
||||
INFO("Generating Edge based representation of input data");
|
||||
std::vector<OriginalEdgeData> original_edge_data_vector;
|
||||
original_edge_data_vector.reserve(1024 * 1024);
|
||||
|
||||
std::vector<_Restriction>::iterator restrictionIterator = inputRestrictions.begin();
|
||||
Percent p(_nodeBasedGraph->GetNumberOfNodes());
|
||||
int numberOfResolvedRestrictions(0);
|
||||
int nodeBasedEdgeCounter(0);
|
||||
// Loop over all turns and generate new set of edges.
|
||||
// Three nested loop look super-linear, but we are dealing with a (kind of)
|
||||
// linear number of turns only.
|
||||
unsigned restricted_turns_counter = 0;
|
||||
unsigned skipped_uturns_counter = 0;
|
||||
unsigned skipped_barrier_turns_counter = 0;
|
||||
unsigned compressed = 0;
|
||||
|
||||
//Loop over all nodes u. Three nested loop look super-linear, but we are dealing with a number linear in the turns only.
|
||||
for(_NodeBasedDynamicGraph::NodeIterator u = 0; u < _nodeBasedGraph->GetNumberOfNodes(); ++u ) {
|
||||
//loop over all adjacent edge (u,v)
|
||||
while(restrictionIterator->fromNode < u && inputRestrictions.end() != restrictionIterator) {
|
||||
++restrictionIterator;
|
||||
}
|
||||
for(_NodeBasedDynamicGraph::EdgeIterator e1 = _nodeBasedGraph->BeginEdges(u); e1 < _nodeBasedGraph->EndEdges(u); ++e1) {
|
||||
++nodeBasedEdgeCounter;
|
||||
_NodeBasedDynamicGraph::NodeIterator v = _nodeBasedGraph->GetTarget(e1);
|
||||
//loop over all reachable edges (v,w)
|
||||
for(_NodeBasedDynamicGraph::EdgeIterator e2 = _nodeBasedGraph->BeginEdges(v); e2 < _nodeBasedGraph->EndEdges(v); ++e2) {
|
||||
_NodeBasedDynamicGraph::NodeIterator w = _nodeBasedGraph->GetTarget(e2);
|
||||
//if (u,v,w) is a forbidden turn, continue
|
||||
bool isTurnProhibited = false;
|
||||
if( u != w ) { //only add an edge if turn is not a U-turn
|
||||
if(u == restrictionIterator->fromNode) {
|
||||
std::vector<_Restriction>::iterator secondRestrictionIterator = restrictionIterator;
|
||||
do {
|
||||
if( v == secondRestrictionIterator->viaNode && w == secondRestrictionIterator->toNode) {
|
||||
isTurnProhibited = true;
|
||||
}
|
||||
++secondRestrictionIterator;
|
||||
} while(u == secondRestrictionIterator->fromNode);
|
||||
}
|
||||
if( !isTurnProhibited ) { //only add an edge if turn is not prohibited
|
||||
//new costs for edge based edge (e1, e2) = cost (e1) + tc(e1,e2)
|
||||
const _NodeBasedDynamicGraph::NodeIterator edgeBasedSource = _nodeBasedGraph->GetEdgeData(e1).edgeBasedNodeID;
|
||||
// INFO("edgeBasedSource: " << edgeBasedSource);
|
||||
if(edgeBasedSource > _nodeBasedGraph->GetNumberOfEdges()) {
|
||||
ERR("edgeBasedTarget" << edgeBasedSource << ">" << _nodeBasedGraph->GetNumberOfEdges());
|
||||
}
|
||||
const _NodeBasedDynamicGraph::NodeIterator edgeBasedTarget = _nodeBasedGraph->GetEdgeData(e2).edgeBasedNodeID;
|
||||
// INFO("edgeBasedTarget: " << edgeBasedTarget);
|
||||
if(edgeBasedTarget > _nodeBasedGraph->GetNumberOfEdges()) {
|
||||
ERR("edgeBasedTarget" << edgeBasedTarget << ">" << _nodeBasedGraph->GetNumberOfEdges());
|
||||
}
|
||||
Percent p(m_node_based_graph->GetNumberOfNodes());
|
||||
|
||||
//incorporate turn costs, this is just a simple model and can (read: must) be extended
|
||||
double angle = GetAngleBetweenTwoEdges(inputNodeInfoList[u], inputNodeInfoList[v], inputNodeInfoList[w]);
|
||||
for (NodeID u = 0, end = m_node_based_graph->GetNumberOfNodes(); u < end; ++u)
|
||||
{
|
||||
for (EdgeID e1 : m_node_based_graph->GetAdjacentEdgeRange(u))
|
||||
{
|
||||
if (!m_node_based_graph->GetEdgeData(e1).forward)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned distance = (int)( _nodeBasedGraph->GetEdgeData(e1).distance *(1+std::abs((angle-180.)/180.)));
|
||||
unsigned nameID = _nodeBasedGraph->GetEdgeData(e2).nameID;
|
||||
short turnInstruction = AnalyzeTurn(u, v, w);
|
||||
++node_based_edge_counter;
|
||||
const NodeID v = m_node_based_graph->GetTarget(e1);
|
||||
const NodeID to_node_of_only_restriction =
|
||||
m_restriction_map->CheckForEmanatingIsOnlyTurn(u, v);
|
||||
const bool is_barrier_node = (m_barrier_nodes.find(v) != m_barrier_nodes.end());
|
||||
|
||||
//create edge-based graph edge
|
||||
//EdgeBasedEdge(NodeID s, NodeID t, NodeID v, unsigned n1, EdgeWeight w, bool f, bool b, short ty)
|
||||
EdgeBasedEdge newEdge(edgeBasedSource, edgeBasedTarget, v, nameID, distance, true, false, turnInstruction);
|
||||
edgeBasedEdges.push_back(newEdge);
|
||||
EdgeBasedNode currentNode;
|
||||
for (EdgeID e2 : m_node_based_graph->GetAdjacentEdgeRange(v))
|
||||
{
|
||||
if (!m_node_based_graph->GetEdgeData(e2).forward)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const NodeID w = m_node_based_graph->GetTarget(e2);
|
||||
|
||||
if(_nodeBasedGraph->GetEdgeData(e1).type != 14) {
|
||||
currentNode.nameID = _nodeBasedGraph->GetEdgeData(e1).nameID;
|
||||
currentNode.lat1 = inputNodeInfoList[u].lat;
|
||||
currentNode.lon1 = inputNodeInfoList[u].lon;
|
||||
currentNode.lat2 = inputNodeInfoList[v].lat;
|
||||
currentNode.lon2 = inputNodeInfoList[v].lon;
|
||||
currentNode.id = edgeBasedSource;
|
||||
currentNode.weight = _nodeBasedGraph->GetEdgeData(e1).distance;
|
||||
edgeBasedNodes.push_back(currentNode);
|
||||
}
|
||||
} else {
|
||||
++numberOfResolvedRestrictions;
|
||||
if ((to_node_of_only_restriction != SPECIAL_NODEID) &&
|
||||
(w != to_node_of_only_restriction))
|
||||
{
|
||||
// We are at an only_-restriction but not at the right turn.
|
||||
++restricted_turns_counter;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_barrier_node)
|
||||
{
|
||||
if (u != w)
|
||||
{
|
||||
++skipped_barrier_turns_counter;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((u == w) && (m_node_based_graph->GetOutDegree(v) > 1))
|
||||
{
|
||||
++skipped_uturns_counter;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// only add an edge if turn is not a U-turn except when it is
|
||||
// at the end of a dead-end street
|
||||
if (m_restriction_map->CheckIfTurnIsRestricted(u, v, w) &&
|
||||
(to_node_of_only_restriction == SPECIAL_NODEID) &&
|
||||
(w != to_node_of_only_restriction))
|
||||
{
|
||||
++restricted_turns_counter;
|
||||
continue;
|
||||
}
|
||||
|
||||
// only add an edge if turn is not prohibited
|
||||
const EdgeData &edge_data1 = m_node_based_graph->GetEdgeData(e1);
|
||||
const EdgeData &edge_data2 = m_node_based_graph->GetEdgeData(e2);
|
||||
|
||||
BOOST_ASSERT(edge_data1.edgeBasedNodeID != edge_data2.edgeBasedNodeID);
|
||||
BOOST_ASSERT(edge_data1.forward);
|
||||
BOOST_ASSERT(edge_data2.forward);
|
||||
|
||||
// the following is the core of the loop.
|
||||
unsigned distance = edge_data1.distance;
|
||||
if (m_traffic_lights.find(v) != m_traffic_lights.end())
|
||||
{
|
||||
distance += speed_profile.trafficSignalPenalty;
|
||||
}
|
||||
const double angle = GetAngleBetweenThreeFixedPointCoordinates(
|
||||
m_node_info_list[u], m_node_info_list[v], m_node_info_list[w]);
|
||||
const int turn_penalty = GetTurnPenalty(angle, lua_state);
|
||||
TurnInstruction turn_instruction = AnalyzeTurn(u, v, w, angle);
|
||||
if (turn_instruction == TurnInstruction::UTurn)
|
||||
{
|
||||
distance += speed_profile.uTurnPenalty;
|
||||
}
|
||||
distance += turn_penalty;
|
||||
|
||||
const bool edge_is_compressed = m_geometry_compressor.HasEntryForID(e1);
|
||||
|
||||
if (edge_is_compressed)
|
||||
{
|
||||
++compressed;
|
||||
}
|
||||
|
||||
original_edge_data_vector.emplace_back(
|
||||
(edge_is_compressed ? m_geometry_compressor.GetPositionForID(e1) : v),
|
||||
edge_data1.nameID,
|
||||
turn_instruction,
|
||||
edge_is_compressed);
|
||||
|
||||
++original_edges_counter;
|
||||
|
||||
if (original_edge_data_vector.size() > 1024 * 1024 * 10)
|
||||
{
|
||||
FlushVectorToStream(edge_data_file, original_edge_data_vector);
|
||||
}
|
||||
|
||||
BOOST_ASSERT(SPECIAL_NODEID != edge_data1.edgeBasedNodeID);
|
||||
BOOST_ASSERT(SPECIAL_NODEID != edge_data2.edgeBasedNodeID);
|
||||
|
||||
m_edge_based_edge_list.emplace_back(EdgeBasedEdge(edge_data1.edgeBasedNodeID,
|
||||
edge_data2.edgeBasedNodeID,
|
||||
m_edge_based_edge_list.size(),
|
||||
distance,
|
||||
true,
|
||||
false));
|
||||
}
|
||||
}
|
||||
p.printIncrement();
|
||||
}
|
||||
std::sort(edgeBasedNodes.begin(), edgeBasedNodes.end());
|
||||
edgeBasedNodes.erase( std::unique(edgeBasedNodes.begin(), edgeBasedNodes.end()), edgeBasedNodes.end() );
|
||||
INFO("Node-based graph contains " << nodeBasedEdgeCounter << " edges");
|
||||
INFO("Edge-based graph contains " << edgeBasedEdges.size() << " edges, blowup is " << (double)edgeBasedEdges.size()/(double)nodeBasedEdgeCounter);
|
||||
INFO("Edge-based graph obeys " << numberOfResolvedRestrictions << " turn restrictions, " << (inputRestrictions.size() - numberOfResolvedRestrictions )<< " skipped.");
|
||||
INFO("Generated " << edgeBasedNodes.size() << " edge based nodes");
|
||||
FlushVectorToStream(edge_data_file, original_edge_data_vector);
|
||||
|
||||
edge_data_file.seekp(std::ios::beg);
|
||||
edge_data_file.write((char *)&original_edges_counter, sizeof(unsigned));
|
||||
edge_data_file.close();
|
||||
|
||||
SimpleLogger().Write() << "Generated " << m_edge_based_node_list.size() << " edge based nodes";
|
||||
SimpleLogger().Write() << "Node-based graph contains " << node_based_edge_counter << " edges";
|
||||
SimpleLogger().Write() << "Edge-expanded graph ...";
|
||||
SimpleLogger().Write() << " contains " << m_edge_based_edge_list.size() << " edges";
|
||||
SimpleLogger().Write() << " skips " << restricted_turns_counter << " turns, "
|
||||
"defined by "
|
||||
<< m_restriction_map->size() << " restrictions";
|
||||
SimpleLogger().Write() << " skips " << skipped_uturns_counter << " U turns";
|
||||
SimpleLogger().Write() << " skips " << skipped_barrier_turns_counter << " turns over barriers";
|
||||
}
|
||||
|
||||
short EdgeBasedGraphFactory::AnalyzeTurn(const NodeID u, const NodeID v, const NodeID w) const {
|
||||
_NodeBasedDynamicGraph::EdgeIterator edge1 = _nodeBasedGraph->FindEdge(u, v);
|
||||
_NodeBasedDynamicGraph::EdgeIterator edge2 = _nodeBasedGraph->FindEdge(v, w);
|
||||
int EdgeBasedGraphFactory::GetTurnPenalty(double angle, lua_State *lua_state) const
|
||||
{
|
||||
|
||||
_NodeBasedDynamicGraph::EdgeData & data1 = _nodeBasedGraph->GetEdgeData(edge1);
|
||||
_NodeBasedDynamicGraph::EdgeData & data2 = _nodeBasedGraph->GetEdgeData(edge2);
|
||||
if (speed_profile.has_turn_penalty_function)
|
||||
{
|
||||
try
|
||||
{
|
||||
// call lua profile to compute turn penalty
|
||||
return luabind::call_function<int>(lua_state, "turn_function", 180. - angle);
|
||||
}
|
||||
catch (const luabind::error &er) { SimpleLogger().Write(logWARNING) << er.what(); }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
double angle = GetAngleBetweenTwoEdges(inputNodeInfoList[u], inputNodeInfoList[v], inputNodeInfoList[w]);
|
||||
TurnInstruction EdgeBasedGraphFactory::AnalyzeTurn(const NodeID u,
|
||||
const NodeID v,
|
||||
const NodeID w,
|
||||
double angle)
|
||||
const
|
||||
{
|
||||
if (u == w)
|
||||
{
|
||||
return TurnInstruction::UTurn;
|
||||
}
|
||||
|
||||
//roundabouts need to be handled explicitely
|
||||
if(data1.roundabout && data2.roundabout) {
|
||||
//Is a turn possible? If yes, we stay on the roundabout!
|
||||
if( 1 == (_nodeBasedGraph->EndEdges(v) - _nodeBasedGraph->BeginEdges(v)) ) {
|
||||
//No turn possible.
|
||||
return TurnInstructions.NoTurn;
|
||||
} else {
|
||||
return TurnInstructions.StayOnRoundAbout;
|
||||
const EdgeID edge1 = m_node_based_graph->FindEdge(u, v);
|
||||
const EdgeID edge2 = m_node_based_graph->FindEdge(v, w);
|
||||
|
||||
const EdgeData &data1 = m_node_based_graph->GetEdgeData(edge1);
|
||||
const EdgeData &data2 = m_node_based_graph->GetEdgeData(edge2);
|
||||
|
||||
if (!data1.contraFlow && data2.contraFlow)
|
||||
{
|
||||
return TurnInstruction::EnterAgainstAllowedDirection;
|
||||
}
|
||||
if (data1.contraFlow && !data2.contraFlow)
|
||||
{
|
||||
return TurnInstruction::LeaveAgainstAllowedDirection;
|
||||
}
|
||||
|
||||
// roundabouts need to be handled explicitely
|
||||
if (data1.roundabout && data2.roundabout)
|
||||
{
|
||||
// Is a turn possible? If yes, we stay on the roundabout!
|
||||
if (1 == m_node_based_graph->GetDirectedOutDegree(v))
|
||||
{
|
||||
// No turn possible.
|
||||
return TurnInstruction::NoTurn;
|
||||
}
|
||||
return TurnInstruction::StayOnRoundAbout;
|
||||
}
|
||||
// Does turn start or end on roundabout?
|
||||
if (data1.roundabout || data2.roundabout)
|
||||
{
|
||||
// We are entering the roundabout
|
||||
if ((!data1.roundabout) && data2.roundabout)
|
||||
{
|
||||
return TurnInstruction::EnterRoundAbout;
|
||||
}
|
||||
// We are leaving the roundabout
|
||||
if (data1.roundabout && (!data2.roundabout))
|
||||
{
|
||||
return TurnInstruction::LeaveRoundAbout;
|
||||
}
|
||||
}
|
||||
//Does turn start or end on roundabout?
|
||||
if(data1.roundabout || data2.roundabout) {
|
||||
//We are entering the roundabout
|
||||
if( (!data1.roundabout) && data2.roundabout)
|
||||
return TurnInstructions.EnterRoundAbout;
|
||||
//We are leaving the roundabout
|
||||
if(data1.roundabout && (!data2.roundabout) )
|
||||
return TurnInstructions.LeaveRoundAbout;
|
||||
|
||||
// If street names stay the same and if we are certain that it is not a
|
||||
// a segment of a roundabout, we skip it.
|
||||
if (data1.nameID == data2.nameID)
|
||||
{
|
||||
// TODO: Here we should also do a small graph exploration to check for
|
||||
// more complex situations
|
||||
if (0 != data1.nameID)
|
||||
{
|
||||
return TurnInstruction::NoTurn;
|
||||
}
|
||||
else if (m_node_based_graph->GetOutDegree(v) <= 2)
|
||||
{
|
||||
return TurnInstruction::NoTurn;
|
||||
}
|
||||
}
|
||||
|
||||
//If street names stay the same and if we are certain that it is not a roundabout, we skip it.
|
||||
if(data1.nameID == data2.nameID)
|
||||
return TurnInstructions.NoTurn;
|
||||
|
||||
return TurnInstructions.GetTurnDirectionOfInstruction(angle);
|
||||
return TurnInstructionsClass::GetTurnDirectionOfInstruction(angle);
|
||||
}
|
||||
|
||||
unsigned EdgeBasedGraphFactory::GetNumberOfNodes() const {
|
||||
return edgeBasedEdges.size();
|
||||
}
|
||||
|
||||
EdgeBasedGraphFactory::~EdgeBasedGraphFactory() {
|
||||
}
|
||||
|
||||
/* Get angle of line segment (A,C)->(C,B), atan2 magic, formerly cosine theorem*/
|
||||
template<class CoordinateT>
|
||||
double EdgeBasedGraphFactory::GetAngleBetweenTwoEdges(const CoordinateT& A, const CoordinateT& C, const CoordinateT& B) const {
|
||||
const int v1x = A.lon - C.lon;
|
||||
const int v1y = A.lat - C.lat;
|
||||
const int v2x = B.lon - C.lon;
|
||||
const int v2y = B.lat - C.lat;
|
||||
|
||||
double angle = (atan2((double)v2y,v2x) - atan2((double)v1y,v1x) )*180/M_PI;
|
||||
while(angle < 0)
|
||||
angle += 360;
|
||||
|
||||
return angle;
|
||||
unsigned EdgeBasedGraphFactory::GetNumberOfEdgeBasedNodes() const
|
||||
{
|
||||
return m_number_of_edge_based_nodes;
|
||||
}
|
||||
|
||||
@@ -1,104 +1,128 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
/*
|
||||
* This class constructs the edge base representation of a graph from a given node based edge list
|
||||
*/
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
// This class constructs the edge-expanded routing graph
|
||||
|
||||
#ifndef EDGEBASEDGRAPHFACTORY_H_
|
||||
#define EDGEBASEDGRAPHFACTORY_H_
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include "../typedefs.h"
|
||||
#include "../DataStructures/DeallocatingVector.h"
|
||||
#include "../DataStructures/DynamicGraph.h"
|
||||
#include "../DataStructures/EdgeBasedNode.h"
|
||||
#include "../DataStructures/OriginalEdgeData.h"
|
||||
#include "../DataStructures/QueryNode.h"
|
||||
#include "../DataStructures/TurnInstructions.h"
|
||||
#include "../DataStructures/Restriction.h"
|
||||
#include "../DataStructures/NodeBasedGraph.h"
|
||||
#include "../DataStructures/RestrictionMap.h"
|
||||
#include "GeometryCompressor.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "../typedefs.h"
|
||||
#include "../DataStructures/DynamicGraph.h"
|
||||
#include "../DataStructures/ExtractorStructs.h"
|
||||
#include "../DataStructures/ImportEdge.h"
|
||||
#include "../DataStructures/Percent.h"
|
||||
#include "../DataStructures/TurnInstructions.h"
|
||||
struct lua_State;
|
||||
|
||||
class EdgeBasedGraphFactory {
|
||||
private:
|
||||
struct _NodeBasedEdgeData {
|
||||
int distance;
|
||||
unsigned edgeBasedNodeID;
|
||||
unsigned nameID;
|
||||
bool shortcut:1;
|
||||
bool forward:1;
|
||||
bool backward:1;
|
||||
bool roundabout:1;
|
||||
short type;
|
||||
} data;
|
||||
class EdgeBasedGraphFactory
|
||||
{
|
||||
public:
|
||||
EdgeBasedGraphFactory() = delete;
|
||||
EdgeBasedGraphFactory(const EdgeBasedGraphFactory &) = delete;
|
||||
|
||||
struct _EdgeBasedEdgeData {
|
||||
int distance;
|
||||
unsigned via;
|
||||
unsigned nameID;
|
||||
bool forward;
|
||||
bool backward;
|
||||
short turnInstruction;
|
||||
};
|
||||
struct SpeedProfileProperties;
|
||||
|
||||
typedef DynamicGraph< _NodeBasedEdgeData > _NodeBasedDynamicGraph;
|
||||
typedef _NodeBasedDynamicGraph::InputEdge _NodeBasedEdge;
|
||||
explicit EdgeBasedGraphFactory(const std::shared_ptr<NodeBasedDynamicGraph> &node_based_graph,
|
||||
std::unique_ptr<RestrictionMap> restricion_map,
|
||||
std::vector<NodeID> &barrier_node_list,
|
||||
std::vector<NodeID> &traffic_light_node_list,
|
||||
std::vector<NodeInfo> &m_node_info_list,
|
||||
SpeedProfileProperties &speed_profile);
|
||||
|
||||
public:
|
||||
struct EdgeBasedNode {
|
||||
bool operator<(const EdgeBasedNode & other) const {
|
||||
return other.id < id;
|
||||
void Run(const std::string &original_edge_data_filename,
|
||||
const std::string &geometry_filename,
|
||||
lua_State *lua_state);
|
||||
|
||||
void GetEdgeBasedEdges(DeallocatingVector<EdgeBasedEdge> &edges);
|
||||
|
||||
void GetEdgeBasedNodes(std::vector<EdgeBasedNode> &nodes);
|
||||
|
||||
TurnInstruction AnalyzeTurn(const NodeID u, const NodeID v, const NodeID w, double angle) const;
|
||||
|
||||
int GetTurnPenalty(double angle, lua_State *lua_state) const;
|
||||
|
||||
unsigned GetNumberOfEdgeBasedNodes() const;
|
||||
|
||||
struct SpeedProfileProperties
|
||||
{
|
||||
SpeedProfileProperties()
|
||||
: trafficSignalPenalty(0), uTurnPenalty(0), has_turn_penalty_function(false)
|
||||
{
|
||||
}
|
||||
bool operator==(const EdgeBasedNode & other) const {
|
||||
return id == other.id;
|
||||
}
|
||||
int lat1;
|
||||
int lat2;
|
||||
int lon1;
|
||||
int lon2;
|
||||
NodeID id;
|
||||
NodeID nameID;
|
||||
unsigned weight;
|
||||
};
|
||||
|
||||
private:
|
||||
boost::shared_ptr<_NodeBasedDynamicGraph> _nodeBasedGraph;
|
||||
int trafficSignalPenalty;
|
||||
int uTurnPenalty;
|
||||
bool has_turn_penalty_function;
|
||||
} speed_profile;
|
||||
|
||||
std::vector<_Restriction> & inputRestrictions;
|
||||
std::vector<NodeInfo> & inputNodeInfoList;
|
||||
private:
|
||||
typedef NodeBasedDynamicGraph::EdgeData EdgeData;
|
||||
|
||||
std::vector<EdgeBasedEdge> edgeBasedEdges;
|
||||
std::vector<EdgeBasedNode> edgeBasedNodes;
|
||||
unsigned m_number_of_edge_based_nodes;
|
||||
|
||||
template<class CoordinateT>
|
||||
double GetAngleBetweenTwoEdges(const CoordinateT& A, const CoordinateT& C, const CoordinateT& B) const;
|
||||
public:
|
||||
template< class InputEdgeT >
|
||||
explicit EdgeBasedGraphFactory(int nodes, std::vector<InputEdgeT> & inputEdges, std::vector<_Restriction> & inputRestrictions, std::vector<NodeInfo> & nI);
|
||||
virtual ~EdgeBasedGraphFactory();
|
||||
std::vector<NodeInfo> m_node_info_list;
|
||||
std::vector<EdgeBasedNode> m_edge_based_node_list;
|
||||
DeallocatingVector<EdgeBasedEdge> m_edge_based_edge_list;
|
||||
|
||||
void Run();
|
||||
template< class ImportEdgeT >
|
||||
void GetEdgeBasedEdges( std::vector< ImportEdgeT >& edges );
|
||||
void GetEdgeBasedNodes( std::vector< EdgeBasedNode> & nodes);
|
||||
short AnalyzeTurn(const NodeID u, const NodeID v, const NodeID w) const;
|
||||
unsigned GetNumberOfNodes() const;
|
||||
std::shared_ptr<NodeBasedDynamicGraph> m_node_based_graph;
|
||||
std::unordered_set<NodeID> m_barrier_nodes;
|
||||
std::unordered_set<NodeID> m_traffic_lights;
|
||||
|
||||
std::unique_ptr<RestrictionMap> m_restriction_map;
|
||||
|
||||
GeometryCompressor m_geometry_compressor;
|
||||
|
||||
void CompressGeometry();
|
||||
void RenumberEdges();
|
||||
void GenerateEdgeExpandedNodes();
|
||||
void GenerateEdgeExpandedEdges(const std::string &original_edge_data_filename,
|
||||
lua_State *lua_state);
|
||||
|
||||
void InsertEdgeBasedNode(NodeID u, NodeID v, EdgeID e1, bool belongsToTinyComponent);
|
||||
|
||||
void FlushVectorToStream(std::ofstream &edge_data_file,
|
||||
std::vector<OriginalEdgeData> &original_edge_data_vector) const;
|
||||
|
||||
unsigned max_id;
|
||||
};
|
||||
|
||||
#endif /* EDGEBASEDGRAPHFACTORY_H_ */
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include "GeometryCompressor.h"
|
||||
#include "../Util/SimpleLogger.h"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
int current_free_list_maximum = 0;
|
||||
int UniqueNumber() { return ++current_free_list_maximum; }
|
||||
|
||||
GeometryCompressor::GeometryCompressor()
|
||||
{
|
||||
m_free_list.reserve(100);
|
||||
IncreaseFreeList();
|
||||
}
|
||||
|
||||
void GeometryCompressor::IncreaseFreeList()
|
||||
{
|
||||
m_compressed_geometries.resize(m_compressed_geometries.size() + 100);
|
||||
for (unsigned i = 100; i > 0; --i)
|
||||
{
|
||||
m_free_list.emplace_back(current_free_list_maximum);
|
||||
++current_free_list_maximum;
|
||||
}
|
||||
}
|
||||
|
||||
bool GeometryCompressor::HasEntryForID(const EdgeID edge_id) const
|
||||
{
|
||||
return (m_edge_id_to_list_index_map.find(edge_id) != m_edge_id_to_list_index_map.end());
|
||||
}
|
||||
|
||||
unsigned GeometryCompressor::GetPositionForID(const EdgeID edge_id) const
|
||||
{
|
||||
auto map_iterator = m_edge_id_to_list_index_map.find(edge_id);
|
||||
BOOST_ASSERT(map_iterator != m_edge_id_to_list_index_map.end());
|
||||
BOOST_ASSERT(map_iterator->second < m_compressed_geometries.size());
|
||||
return map_iterator->second;
|
||||
}
|
||||
|
||||
void GeometryCompressor::SerializeInternalVector(const std::string &path) const
|
||||
{
|
||||
|
||||
boost::filesystem::fstream geometry_out_stream(path, std::ios::binary | std::ios::out);
|
||||
const unsigned number_of_compressed_geometries = m_compressed_geometries.size() + 1;
|
||||
BOOST_ASSERT(std::numeric_limits<unsigned>::max() != number_of_compressed_geometries);
|
||||
geometry_out_stream.write((char *)&number_of_compressed_geometries, sizeof(unsigned));
|
||||
|
||||
// write indices array
|
||||
unsigned prefix_sum_of_list_indices = 0;
|
||||
for (const auto &elem : m_compressed_geometries)
|
||||
{
|
||||
geometry_out_stream.write((char *)&prefix_sum_of_list_indices, sizeof(unsigned));
|
||||
|
||||
const std::vector<CompressedNode> ¤t_vector = elem;
|
||||
const unsigned unpacked_size = current_vector.size();
|
||||
BOOST_ASSERT(std::numeric_limits<unsigned>::max() != unpacked_size);
|
||||
prefix_sum_of_list_indices += unpacked_size;
|
||||
}
|
||||
// sentinel element
|
||||
geometry_out_stream.write((char *)&prefix_sum_of_list_indices, sizeof(unsigned));
|
||||
|
||||
// number of geometry entries to follow, it is the (inclusive) prefix sum
|
||||
geometry_out_stream.write((char *)&prefix_sum_of_list_indices, sizeof(unsigned));
|
||||
|
||||
unsigned control_sum = 0;
|
||||
// write compressed geometries
|
||||
for (auto &elem : m_compressed_geometries)
|
||||
{
|
||||
const std::vector<CompressedNode> ¤t_vector = elem;
|
||||
const unsigned unpacked_size = current_vector.size();
|
||||
control_sum += unpacked_size;
|
||||
BOOST_ASSERT(std::numeric_limits<unsigned>::max() != unpacked_size);
|
||||
for (const CompressedNode current_node : current_vector)
|
||||
{
|
||||
geometry_out_stream.write((char *)&(current_node.first), sizeof(NodeID));
|
||||
}
|
||||
}
|
||||
BOOST_ASSERT(control_sum == prefix_sum_of_list_indices);
|
||||
// all done, let's close the resource
|
||||
geometry_out_stream.close();
|
||||
}
|
||||
|
||||
void GeometryCompressor::CompressEdge(const EdgeID edge_id_1,
|
||||
const EdgeID edge_id_2,
|
||||
const NodeID via_node_id,
|
||||
const NodeID target_node_id,
|
||||
const EdgeWeight weight1,
|
||||
const EdgeWeight weight2)
|
||||
{
|
||||
// remove super-trivial geometries
|
||||
BOOST_ASSERT(SPECIAL_EDGEID != edge_id_1);
|
||||
BOOST_ASSERT(SPECIAL_EDGEID != edge_id_2);
|
||||
BOOST_ASSERT(SPECIAL_NODEID != via_node_id);
|
||||
BOOST_ASSERT(SPECIAL_NODEID != target_node_id);
|
||||
BOOST_ASSERT(INVALID_EDGE_WEIGHT != weight1);
|
||||
BOOST_ASSERT(INVALID_EDGE_WEIGHT != weight2);
|
||||
|
||||
// append list of removed edge_id plus via node to surviving edge id:
|
||||
// <surv_1, .. , surv_n, via_node_id, rem_1, .. rem_n
|
||||
//
|
||||
// General scheme:
|
||||
// 1. append via node id to list of edge_id_1
|
||||
// 2. find list for edge_id_2, if yes add all elements and delete it
|
||||
|
||||
// Add via node id. List is created if it does not exist
|
||||
if (!HasEntryForID(edge_id_1))
|
||||
{
|
||||
// create a new entry in the map
|
||||
if (0 == m_free_list.size())
|
||||
{
|
||||
// make sure there is a place to put the entries
|
||||
IncreaseFreeList();
|
||||
}
|
||||
BOOST_ASSERT(!m_free_list.empty());
|
||||
m_edge_id_to_list_index_map[edge_id_1] = m_free_list.back();
|
||||
m_free_list.pop_back();
|
||||
}
|
||||
|
||||
const auto iter = m_edge_id_to_list_index_map.find(edge_id_1);
|
||||
BOOST_ASSERT(iter != m_edge_id_to_list_index_map.end());
|
||||
const unsigned edge_bucket_id1 = iter->second;
|
||||
BOOST_ASSERT(edge_bucket_id1 == GetPositionForID(edge_id_1));
|
||||
BOOST_ASSERT(edge_bucket_id1 < m_compressed_geometries.size());
|
||||
|
||||
std::vector<CompressedNode> &edge_bucket_list1 = m_compressed_geometries[edge_bucket_id1];
|
||||
|
||||
if (edge_bucket_list1.empty())
|
||||
{
|
||||
edge_bucket_list1.emplace_back(via_node_id, weight1);
|
||||
}
|
||||
|
||||
BOOST_ASSERT(0 < edge_bucket_list1.size());
|
||||
BOOST_ASSERT(!edge_bucket_list1.empty());
|
||||
|
||||
if (HasEntryForID(edge_id_2))
|
||||
{
|
||||
// second edge is not atomic anymore
|
||||
const unsigned list_to_remove_index = GetPositionForID(edge_id_2);
|
||||
BOOST_ASSERT(list_to_remove_index < m_compressed_geometries.size());
|
||||
|
||||
std::vector<CompressedNode> &edge_bucket_list2 =
|
||||
m_compressed_geometries[list_to_remove_index];
|
||||
|
||||
// found an existing list, append it to the list of edge_id_1
|
||||
edge_bucket_list1.insert(
|
||||
edge_bucket_list1.end(), edge_bucket_list2.begin(), edge_bucket_list2.end());
|
||||
|
||||
// remove the list of edge_id_2
|
||||
m_edge_id_to_list_index_map.erase(edge_id_2);
|
||||
BOOST_ASSERT(m_edge_id_to_list_index_map.end() ==
|
||||
m_edge_id_to_list_index_map.find(edge_id_2));
|
||||
edge_bucket_list2.clear();
|
||||
BOOST_ASSERT(0 == edge_bucket_list2.size());
|
||||
m_free_list.emplace_back(list_to_remove_index);
|
||||
BOOST_ASSERT(list_to_remove_index == m_free_list.back());
|
||||
}
|
||||
else
|
||||
{
|
||||
// we are certain that the second edge is atomic.
|
||||
edge_bucket_list1.emplace_back(target_node_id, weight2);
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryCompressor::PrintStatistics() const
|
||||
{
|
||||
const uint64_t compressed_edges = m_compressed_geometries.size();
|
||||
BOOST_ASSERT(0 == compressed_edges % 2);
|
||||
BOOST_ASSERT(m_compressed_geometries.size() + m_free_list.size() > 0);
|
||||
|
||||
uint64_t number_of_compressed_geometries = 0;
|
||||
uint64_t longest_chain_length = 0;
|
||||
for (const std::vector<CompressedNode> ¤t_vector : m_compressed_geometries)
|
||||
{
|
||||
number_of_compressed_geometries += current_vector.size();
|
||||
longest_chain_length = std::max(longest_chain_length, (uint64_t)current_vector.size());
|
||||
}
|
||||
|
||||
SimpleLogger().Write() << "Geometry successfully removed:"
|
||||
"\n compressed edges: " << compressed_edges
|
||||
<< "\n compressed geometries: " << number_of_compressed_geometries
|
||||
<< "\n longest chain length: " << longest_chain_length
|
||||
<< "\n cmpr ratio: "
|
||||
<< ((float)compressed_edges /
|
||||
std::max(number_of_compressed_geometries, (uint64_t)1))
|
||||
<< "\n avg chain length: "
|
||||
<< (float)number_of_compressed_geometries /
|
||||
std::max((uint64_t)1, compressed_edges);
|
||||
}
|
||||
|
||||
const std::vector<GeometryCompressor::CompressedNode> &
|
||||
GeometryCompressor::GetBucketReference(const EdgeID edge_id) const
|
||||
{
|
||||
const unsigned index = m_edge_id_to_list_index_map.at(edge_id);
|
||||
return m_compressed_geometries.at(index);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include "../typedefs.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifndef GEOMETRY_COMPRESSOR_H
|
||||
#define GEOMETRY_COMPRESSOR_H
|
||||
|
||||
class GeometryCompressor
|
||||
{
|
||||
public:
|
||||
typedef std::pair<NodeID, EdgeWeight> CompressedNode;
|
||||
|
||||
GeometryCompressor();
|
||||
void CompressEdge(const EdgeID surviving_edge_id,
|
||||
const EdgeID removed_edge_id,
|
||||
const NodeID via_node_id,
|
||||
const NodeID target_node,
|
||||
const EdgeWeight weight1,
|
||||
const EdgeWeight weight2);
|
||||
|
||||
bool HasEntryForID(const EdgeID edge_id) const;
|
||||
void PrintStatistics() const;
|
||||
void SerializeInternalVector(const std::string &path) const;
|
||||
unsigned GetPositionForID(const EdgeID edge_id) const;
|
||||
const std::vector<GeometryCompressor::CompressedNode> &
|
||||
GetBucketReference(const EdgeID edge_id) const;
|
||||
|
||||
private:
|
||||
void IncreaseFreeList();
|
||||
std::vector<std::vector<CompressedNode>> m_compressed_geometries;
|
||||
std::vector<unsigned> m_free_list;
|
||||
std::unordered_map<EdgeID, unsigned> m_edge_id_to_list_index_map;
|
||||
};
|
||||
|
||||
#endif // GEOMETRY_COMPRESSOR_H
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include "TemporaryStorage.h"
|
||||
|
||||
StreamData::StreamData()
|
||||
: write_mode(true), temp_path(boost::filesystem::unique_path(temp_directory.append(
|
||||
TemporaryFilePattern.begin(), TemporaryFilePattern.end()))),
|
||||
temp_file(new boost::filesystem::fstream(
|
||||
temp_path, std::ios::in | std::ios::out | std::ios::trunc | std::ios::binary)),
|
||||
readWriteMutex(std::make_shared<boost::mutex>())
|
||||
{
|
||||
if (temp_file->fail())
|
||||
{
|
||||
throw OSRMException("temporary file could not be created");
|
||||
}
|
||||
}
|
||||
|
||||
TemporaryStorage::TemporaryStorage() { temp_directory = boost::filesystem::temp_directory_path(); }
|
||||
|
||||
TemporaryStorage &TemporaryStorage::GetInstance()
|
||||
{
|
||||
static TemporaryStorage static_instance;
|
||||
return static_instance;
|
||||
}
|
||||
|
||||
TemporaryStorage::~TemporaryStorage() { RemoveAll(); }
|
||||
|
||||
void TemporaryStorage::RemoveAll()
|
||||
{
|
||||
boost::mutex::scoped_lock lock(mutex);
|
||||
for (unsigned slot_id = 0; slot_id < stream_data_list.size(); ++slot_id)
|
||||
{
|
||||
DeallocateSlot(slot_id);
|
||||
}
|
||||
stream_data_list.clear();
|
||||
}
|
||||
|
||||
int TemporaryStorage::AllocateSlot()
|
||||
{
|
||||
boost::mutex::scoped_lock lock(mutex);
|
||||
try { stream_data_list.push_back(StreamData()); }
|
||||
catch (boost::filesystem::filesystem_error &e) { Abort(e); }
|
||||
CheckIfTemporaryDeviceFull();
|
||||
return stream_data_list.size() - 1;
|
||||
}
|
||||
|
||||
void TemporaryStorage::DeallocateSlot(const int slot_id)
|
||||
{
|
||||
try
|
||||
{
|
||||
StreamData &data = stream_data_list[slot_id];
|
||||
boost::mutex::scoped_lock lock(*data.readWriteMutex);
|
||||
if (!boost::filesystem::exists(data.temp_path))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (data.temp_file->is_open())
|
||||
{
|
||||
data.temp_file->close();
|
||||
}
|
||||
|
||||
boost::filesystem::remove(data.temp_path);
|
||||
}
|
||||
catch (boost::filesystem::filesystem_error &e) { Abort(e); }
|
||||
}
|
||||
|
||||
void TemporaryStorage::WriteToSlot(const int slot_id, char *pointer, const std::size_t size)
|
||||
{
|
||||
try
|
||||
{
|
||||
StreamData &data = stream_data_list[slot_id];
|
||||
BOOST_ASSERT(data.write_mode);
|
||||
|
||||
boost::mutex::scoped_lock lock(*data.readWriteMutex);
|
||||
BOOST_ASSERT_MSG(data.write_mode, "Writing after first read is not allowed");
|
||||
if (1073741824 < data.buffer.size())
|
||||
{
|
||||
data.temp_file->write(&data.buffer[0], data.buffer.size());
|
||||
// data.temp_file->write(pointer, size);
|
||||
data.buffer.clear();
|
||||
CheckIfTemporaryDeviceFull();
|
||||
}
|
||||
data.buffer.insert(data.buffer.end(), pointer, pointer + size);
|
||||
}
|
||||
catch (boost::filesystem::filesystem_error &e) { Abort(e); }
|
||||
}
|
||||
void TemporaryStorage::ReadFromSlot(const int slot_id, char *pointer, const std::size_t size)
|
||||
{
|
||||
try
|
||||
{
|
||||
StreamData &data = stream_data_list[slot_id];
|
||||
boost::mutex::scoped_lock lock(*data.readWriteMutex);
|
||||
if (data.write_mode)
|
||||
{
|
||||
data.write_mode = false;
|
||||
data.temp_file->write(&data.buffer[0], data.buffer.size());
|
||||
data.buffer.clear();
|
||||
data.temp_file->seekg(data.temp_file->beg);
|
||||
BOOST_ASSERT(data.temp_file->beg == data.temp_file->tellg());
|
||||
}
|
||||
BOOST_ASSERT(!data.write_mode);
|
||||
data.temp_file->read(pointer, size);
|
||||
}
|
||||
catch (boost::filesystem::filesystem_error &e) { Abort(e); }
|
||||
}
|
||||
|
||||
uint64_t TemporaryStorage::GetFreeBytesOnTemporaryDevice()
|
||||
{
|
||||
uint64_t value = -1;
|
||||
try
|
||||
{
|
||||
boost::filesystem::path p = boost::filesystem::temp_directory_path();
|
||||
boost::filesystem::space_info s = boost::filesystem::space(p);
|
||||
value = s.free;
|
||||
}
|
||||
catch (boost::filesystem::filesystem_error &e) { Abort(e); }
|
||||
return value;
|
||||
}
|
||||
|
||||
void TemporaryStorage::CheckIfTemporaryDeviceFull()
|
||||
{
|
||||
boost::filesystem::path p = boost::filesystem::temp_directory_path();
|
||||
boost::filesystem::space_info s = boost::filesystem::space(p);
|
||||
if ((1024 * 1024) > s.free)
|
||||
{
|
||||
throw OSRMException("temporary device is full");
|
||||
}
|
||||
}
|
||||
|
||||
boost::filesystem::fstream::pos_type TemporaryStorage::Tell(const int slot_id)
|
||||
{
|
||||
boost::filesystem::fstream::pos_type position;
|
||||
try
|
||||
{
|
||||
StreamData &data = stream_data_list[slot_id];
|
||||
boost::mutex::scoped_lock lock(*data.readWriteMutex);
|
||||
position = data.temp_file->tellp();
|
||||
}
|
||||
catch (boost::filesystem::filesystem_error &e) { Abort(e); }
|
||||
return position;
|
||||
}
|
||||
|
||||
void TemporaryStorage::Abort(const boost::filesystem::filesystem_error &e)
|
||||
{
|
||||
RemoveAll();
|
||||
throw OSRMException(e.what());
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef TEMPORARYSTORAGE_H_
|
||||
#define TEMPORARYSTORAGE_H_
|
||||
|
||||
#include "../Util/BoostFileSystemFix.h"
|
||||
#include "../Util/OSRMException.h"
|
||||
#include "../Util/SimpleLogger.h"
|
||||
#include "../typedefs.h"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
|
||||
struct StreamData
|
||||
{
|
||||
bool write_mode;
|
||||
boost::filesystem::path temp_path;
|
||||
std::shared_ptr<boost::filesystem::fstream> temp_file;
|
||||
std::shared_ptr<boost::mutex> readWriteMutex;
|
||||
std::vector<char> buffer;
|
||||
|
||||
StreamData();
|
||||
};
|
||||
|
||||
// This class implements a singleton file storage for temporary data.
|
||||
// temporary slots can be accessed by other objects through an int
|
||||
// On deallocation every slot gets deallocated
|
||||
//
|
||||
// Access is sequential, which means, that there is no random access
|
||||
// -> Data is written in first phase and reread in second.
|
||||
|
||||
static boost::filesystem::path temp_directory;
|
||||
static std::string TemporaryFilePattern("OSRM-%%%%-%%%%-%%%%");
|
||||
class TemporaryStorage
|
||||
{
|
||||
public:
|
||||
static TemporaryStorage &GetInstance();
|
||||
virtual ~TemporaryStorage();
|
||||
|
||||
int AllocateSlot();
|
||||
void DeallocateSlot(const int slot_id);
|
||||
void WriteToSlot(const int slot_id, char *pointer, const std::size_t size);
|
||||
void ReadFromSlot(const int slot_id, char *pointer, const std::size_t size);
|
||||
// returns the number of free bytes
|
||||
uint64_t GetFreeBytesOnTemporaryDevice();
|
||||
boost::filesystem::fstream::pos_type Tell(const int slot_id);
|
||||
void RemoveAll();
|
||||
|
||||
private:
|
||||
TemporaryStorage();
|
||||
TemporaryStorage(TemporaryStorage const &) {};
|
||||
|
||||
TemporaryStorage &operator=(TemporaryStorage const &) { return *this; }
|
||||
|
||||
void Abort(const boost::filesystem::filesystem_error &e);
|
||||
void CheckIfTemporaryDeviceFull();
|
||||
|
||||
// vector of file streams that is used to store temporary data
|
||||
boost::mutex mutex;
|
||||
std::vector<StreamData> stream_data_list;
|
||||
};
|
||||
|
||||
#endif /* TEMPORARYSTORAGE_H_ */
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
|
||||
#ifndef BASEPARSER_H_
|
||||
#define BASEPARSER_H_
|
||||
|
||||
template<typename NodeT, typename RestrictionT, typename WayT>
|
||||
class BaseParser {
|
||||
public:
|
||||
virtual ~BaseParser() {}
|
||||
virtual bool Init() = 0;
|
||||
virtual bool RegisterCallbacks(bool (*nodeCallbackPointer)(NodeT), bool (*restrictionCallbackPointer)(RestrictionT), bool (*wayCallbackPointer)(WayT), bool (*addressCallbackPointer)(NodeT, HashTable<std::string, std::string>)) = 0;
|
||||
virtual bool Parse() = 0;
|
||||
};
|
||||
|
||||
#endif /* BASEPARSER_H_ */
|
||||
@@ -1,293 +1,289 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
#ifndef BINARYHEAP_H_INCLUDED
|
||||
#define BINARYHEAP_H_INCLUDED
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
//Not compatible with non contiguous node ids
|
||||
*/
|
||||
|
||||
#ifndef BINARY_HEAP_H
|
||||
#define BINARY_HEAP_H
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <google/dense_hash_map>
|
||||
#include <google/sparse_hash_map>
|
||||
#include <google/sparsetable>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
template< typename NodeID, typename Key >
|
||||
class ArrayStorage {
|
||||
public:
|
||||
|
||||
ArrayStorage( size_t size ) : positions( new Key[size] ) {
|
||||
memset(positions, 0, size*sizeof(Key));
|
||||
template <typename NodeID, typename Key> class ArrayStorage
|
||||
{
|
||||
public:
|
||||
explicit ArrayStorage(size_t size) : positions(new Key[size])
|
||||
{
|
||||
memset(positions, 0, size * sizeof(Key));
|
||||
}
|
||||
|
||||
~ArrayStorage() {
|
||||
delete[] positions;
|
||||
}
|
||||
~ArrayStorage() { delete[] positions; }
|
||||
|
||||
Key &operator[]( NodeID node ) {
|
||||
return positions[node];
|
||||
}
|
||||
Key &operator[](NodeID node) { return positions[node]; }
|
||||
|
||||
void Clear() {}
|
||||
|
||||
private:
|
||||
Key* positions;
|
||||
private:
|
||||
Key *positions;
|
||||
};
|
||||
|
||||
template< typename NodeID, typename Key >
|
||||
class MapStorage {
|
||||
public:
|
||||
template <typename NodeID, typename Key> class MapStorage
|
||||
{
|
||||
public:
|
||||
explicit MapStorage(size_t) {}
|
||||
|
||||
MapStorage( size_t size = 0 ) {}
|
||||
Key &operator[](NodeID node) { return nodes[node]; }
|
||||
|
||||
Key &operator[]( NodeID node ) {
|
||||
return nodes[node];
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
nodes.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
std::map< NodeID, Key > nodes;
|
||||
void Clear() { nodes.clear(); }
|
||||
|
||||
private:
|
||||
std::map<NodeID, Key> nodes;
|
||||
};
|
||||
|
||||
template< typename NodeID, typename Key >
|
||||
class DenseStorage {
|
||||
public:
|
||||
template <typename NodeID, typename Key> class UnorderedMapStorage
|
||||
{
|
||||
public:
|
||||
explicit UnorderedMapStorage(size_t) { nodes.rehash(1000); }
|
||||
|
||||
DenseStorage( size_t size = 0 ) { nodes.set_empty_key(UINT_MAX); }
|
||||
Key &operator[](const NodeID node) { return nodes[node]; }
|
||||
|
||||
Key &operator[]( NodeID node ) {
|
||||
return nodes[node];
|
||||
Key const &operator[](const NodeID node) const
|
||||
{
|
||||
auto iter = nodes.find(node);
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
nodes.clear();
|
||||
}
|
||||
void Clear() { nodes.clear(); }
|
||||
|
||||
private:
|
||||
google::dense_hash_map< NodeID, Key > nodes;
|
||||
private:
|
||||
std::unordered_map<NodeID, Key> nodes;
|
||||
};
|
||||
|
||||
template< typename NodeID, typename Key >
|
||||
class SparseStorage {
|
||||
public:
|
||||
template <typename NodeID,
|
||||
typename Key,
|
||||
typename Weight,
|
||||
typename Data,
|
||||
typename IndexStorage = ArrayStorage<NodeID, NodeID>>
|
||||
class BinaryHeap
|
||||
{
|
||||
private:
|
||||
BinaryHeap(const BinaryHeap &right);
|
||||
void operator=(const BinaryHeap &right);
|
||||
|
||||
SparseStorage( size_t size = 0 ) { }
|
||||
|
||||
Key &operator[]( NodeID node ) {
|
||||
return nodes[node];
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
nodes.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
google::sparse_hash_map< NodeID, Key > nodes;
|
||||
};
|
||||
|
||||
template< typename NodeID, typename Key >
|
||||
class SparseTableStorage : public google::sparsetable<NodeID> {
|
||||
public:
|
||||
SparseTableStorage(size_t n) : google::sparsetable<NodeID>(n){ }
|
||||
void Clear() {
|
||||
google::sparsetable<NodeID>::clear();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename NodeID = unsigned>
|
||||
struct _SimpleHeapData {
|
||||
NodeID parent;
|
||||
_SimpleHeapData( NodeID p ) : parent(p) { }
|
||||
};
|
||||
|
||||
template < typename NodeID, typename Key, typename Weight, typename Data, typename IndexStorage = ArrayStorage<NodeID, NodeID> >
|
||||
class BinaryHeap {
|
||||
private:
|
||||
BinaryHeap( const BinaryHeap& right );
|
||||
void operator=( const BinaryHeap& right );
|
||||
public:
|
||||
public:
|
||||
typedef Weight WeightType;
|
||||
typedef Data DataType;
|
||||
|
||||
BinaryHeap( size_t maxID )
|
||||
: nodeIndex( maxID ) {
|
||||
Clear();
|
||||
explicit BinaryHeap(size_t maxID) : node_index(maxID) { Clear(); }
|
||||
|
||||
void Clear()
|
||||
{
|
||||
heap.resize(1);
|
||||
inserted_nodes.clear();
|
||||
heap[0].weight = std::numeric_limits<Weight>::min();
|
||||
node_index.Clear();
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
heap.resize( 1 );
|
||||
insertedNodes.clear();
|
||||
heap[0].weight = std::numeric_limits< Weight >::min();
|
||||
nodeIndex.Clear();
|
||||
}
|
||||
std::size_t Size() const { return (heap.size() - 1); }
|
||||
|
||||
Key Size() const {
|
||||
return ( Key )( heap.size() - 1 );
|
||||
}
|
||||
bool Empty() const { return 0 == Size(); }
|
||||
|
||||
void Insert( NodeID node, Weight weight, const Data &data ) {
|
||||
void Insert(NodeID node, Weight weight, const Data &data)
|
||||
{
|
||||
HeapElement element;
|
||||
element.index = ( NodeID ) insertedNodes.size();
|
||||
element.index = static_cast<NodeID>(inserted_nodes.size());
|
||||
element.weight = weight;
|
||||
const Key key = ( Key ) heap.size();
|
||||
heap.push_back( element );
|
||||
insertedNodes.push_back( HeapNode( node, key, weight, data ) );
|
||||
nodeIndex[node] = element.index;
|
||||
Upheap( key );
|
||||
const Key key = static_cast<Key>(heap.size());
|
||||
heap.emplace_back(element);
|
||||
inserted_nodes.emplace_back(node, key, weight, data);
|
||||
node_index[node] = element.index;
|
||||
Upheap(key);
|
||||
CheckHeap();
|
||||
}
|
||||
|
||||
Data& GetData( NodeID node ) {
|
||||
const Key index = nodeIndex[node];
|
||||
return insertedNodes[index].data;
|
||||
Data &GetData(NodeID node)
|
||||
{
|
||||
const Key index = node_index[node];
|
||||
return inserted_nodes[index].data;
|
||||
}
|
||||
|
||||
Weight& GetKey( NodeID node ) {
|
||||
const Key index = nodeIndex[node];
|
||||
return insertedNodes[index].weight;
|
||||
Data const &GetData(NodeID node) const
|
||||
{
|
||||
const Key index = node_index[node];
|
||||
return inserted_nodes[index].data;
|
||||
}
|
||||
|
||||
bool WasRemoved( NodeID node ) {
|
||||
assert( WasInserted( node ) );
|
||||
const Key index = nodeIndex[node];
|
||||
return insertedNodes[index].key == 0;
|
||||
Weight &GetKey(NodeID node)
|
||||
{
|
||||
const Key index = node_index[node];
|
||||
return inserted_nodes[index].weight;
|
||||
}
|
||||
|
||||
bool WasInserted( NodeID node ) {
|
||||
const Key index = nodeIndex[node];
|
||||
if ( index >= ( Key ) insertedNodes.size() )
|
||||
bool WasRemoved(const NodeID node)
|
||||
{
|
||||
BOOST_ASSERT(WasInserted(node));
|
||||
const Key index = node_index[node];
|
||||
return inserted_nodes[index].key == 0;
|
||||
}
|
||||
|
||||
bool WasInserted(const NodeID node)
|
||||
{
|
||||
const Key index = node_index[node];
|
||||
if (index >= static_cast<Key>(inserted_nodes.size()))
|
||||
{
|
||||
return false;
|
||||
return insertedNodes[index].node == node;
|
||||
}
|
||||
return inserted_nodes[index].node == node;
|
||||
}
|
||||
|
||||
NodeID Min() const {
|
||||
assert( heap.size() > 1 );
|
||||
return insertedNodes[heap[1].index].node;
|
||||
NodeID Min() const
|
||||
{
|
||||
BOOST_ASSERT(heap.size() > 1);
|
||||
return inserted_nodes[heap[1].index].node;
|
||||
}
|
||||
|
||||
NodeID DeleteMin() {
|
||||
assert( heap.size() > 1 );
|
||||
NodeID DeleteMin()
|
||||
{
|
||||
BOOST_ASSERT(heap.size() > 1);
|
||||
const Key removedIndex = heap[1].index;
|
||||
heap[1] = heap[heap.size()-1];
|
||||
heap[1] = heap[heap.size() - 1];
|
||||
heap.pop_back();
|
||||
if ( heap.size() > 1 )
|
||||
Downheap( 1 );
|
||||
insertedNodes[removedIndex].key = 0;
|
||||
if (heap.size() > 1)
|
||||
{
|
||||
Downheap(1);
|
||||
}
|
||||
inserted_nodes[removedIndex].key = 0;
|
||||
CheckHeap();
|
||||
return insertedNodes[removedIndex].node;
|
||||
return inserted_nodes[removedIndex].node;
|
||||
}
|
||||
|
||||
void DeleteAll() {
|
||||
for ( typename std::vector< HeapElement >::iterator i = heap.begin() + 1, iend = heap.end(); i != iend; ++i )
|
||||
insertedNodes[i->index].key = 0;
|
||||
heap.resize( 1 );
|
||||
heap[0].weight = (std::numeric_limits< Weight >::min)();
|
||||
void DeleteAll()
|
||||
{
|
||||
auto iend = heap.end();
|
||||
for (typename std::vector<HeapElement>::iterator i = heap.begin() + 1; i != iend; ++i)
|
||||
{
|
||||
inserted_nodes[i->index].key = 0;
|
||||
}
|
||||
heap.resize(1);
|
||||
heap[0].weight = (std::numeric_limits<Weight>::min)();
|
||||
}
|
||||
|
||||
void DecreaseKey( NodeID node, Weight weight ) {
|
||||
assert( UINT_MAX != node );
|
||||
const Key index = nodeIndex[node];
|
||||
Key key = insertedNodes[index].key;
|
||||
assert ( key >= 0 );
|
||||
void DecreaseKey(NodeID node, Weight weight)
|
||||
{
|
||||
BOOST_ASSERT(std::numeric_limits<NodeID>::max() != node);
|
||||
const Key &index = node_index[node];
|
||||
Key &key = inserted_nodes[index].key;
|
||||
BOOST_ASSERT(key >= 0);
|
||||
|
||||
insertedNodes[index].weight = weight;
|
||||
inserted_nodes[index].weight = weight;
|
||||
heap[key].weight = weight;
|
||||
Upheap( key );
|
||||
Upheap(key);
|
||||
CheckHeap();
|
||||
}
|
||||
|
||||
private:
|
||||
class HeapNode {
|
||||
public:
|
||||
HeapNode() {
|
||||
}
|
||||
HeapNode( NodeID n, Key k, Weight w, Data d )
|
||||
: node( n ), key( k ), weight( w ), data( d ) {
|
||||
}
|
||||
private:
|
||||
class HeapNode
|
||||
{
|
||||
public:
|
||||
HeapNode(NodeID n, Key k, Weight w, Data d) : node(n), key(k), weight(w), data(d) {}
|
||||
|
||||
NodeID node;
|
||||
Key key;
|
||||
Weight weight;
|
||||
Data data;
|
||||
};
|
||||
struct HeapElement {
|
||||
struct HeapElement
|
||||
{
|
||||
Key index;
|
||||
Weight weight;
|
||||
};
|
||||
|
||||
std::vector< HeapNode > insertedNodes;
|
||||
std::vector< HeapElement > heap;
|
||||
IndexStorage nodeIndex;
|
||||
std::vector<HeapNode> inserted_nodes;
|
||||
std::vector<HeapElement> heap;
|
||||
IndexStorage node_index;
|
||||
|
||||
void Downheap( Key key ) {
|
||||
void Downheap(Key key)
|
||||
{
|
||||
const Key droppingIndex = heap[key].index;
|
||||
const Weight weight = heap[key].weight;
|
||||
Key nextKey = key << 1;
|
||||
while ( nextKey < ( Key ) heap.size() ) {
|
||||
while (nextKey < static_cast<Key>(heap.size()))
|
||||
{
|
||||
const Key nextKeyOther = nextKey + 1;
|
||||
if ( ( nextKeyOther < ( Key ) heap.size() )&& ( heap[nextKey].weight > heap[nextKeyOther].weight) )
|
||||
if ((nextKeyOther < static_cast<Key>(heap.size())) &&
|
||||
(heap[nextKey].weight > heap[nextKeyOther].weight))
|
||||
{
|
||||
nextKey = nextKeyOther;
|
||||
|
||||
if ( weight <= heap[nextKey].weight )
|
||||
}
|
||||
if (weight <= heap[nextKey].weight)
|
||||
{
|
||||
break;
|
||||
|
||||
}
|
||||
heap[key] = heap[nextKey];
|
||||
insertedNodes[heap[key].index].key = key;
|
||||
inserted_nodes[heap[key].index].key = key;
|
||||
key = nextKey;
|
||||
nextKey <<= 1;
|
||||
}
|
||||
heap[key].index = droppingIndex;
|
||||
heap[key].weight = weight;
|
||||
insertedNodes[droppingIndex].key = key;
|
||||
inserted_nodes[droppingIndex].key = key;
|
||||
}
|
||||
|
||||
void Upheap( Key key ) {
|
||||
void Upheap(Key key)
|
||||
{
|
||||
const Key risingIndex = heap[key].index;
|
||||
const Weight weight = heap[key].weight;
|
||||
Key nextKey = key >> 1;
|
||||
while ( heap[nextKey].weight > weight ) {
|
||||
assert( nextKey != 0 );
|
||||
while (heap[nextKey].weight > weight)
|
||||
{
|
||||
BOOST_ASSERT(nextKey != 0);
|
||||
heap[key] = heap[nextKey];
|
||||
insertedNodes[heap[key].index].key = key;
|
||||
inserted_nodes[heap[key].index].key = key;
|
||||
key = nextKey;
|
||||
nextKey >>= 1;
|
||||
}
|
||||
heap[key].index = risingIndex;
|
||||
heap[key].weight = weight;
|
||||
insertedNodes[risingIndex].key = key;
|
||||
inserted_nodes[risingIndex].key = key;
|
||||
}
|
||||
|
||||
void CheckHeap() {
|
||||
void CheckHeap()
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
for ( Key i = 2; i < (Key) heap.size(); ++i ) {
|
||||
assert( heap[i].weight >= heap[i >> 1].weight );
|
||||
for (Key i = 2; i < (Key)heap.size(); ++i)
|
||||
{
|
||||
BOOST_ASSERT(heap[i].weight >= heap[i >> 1].weight);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#endif //#ifndef BINARYHEAP_H_INCLUDED
|
||||
#endif // BINARY_HEAP_H
|
||||
|
||||
@@ -1,98 +1,88 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
#ifndef CONCURRENTQUEUE_H_INCLUDED
|
||||
#define CONCURRENTQUEUE_H_INCLUDED
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <queue>
|
||||
#include <boost/signals2/mutex.hpp>
|
||||
*/
|
||||
|
||||
#ifndef CONCURRENT_QUEUE_H
|
||||
#define CONCURRENT_QUEUE_H
|
||||
|
||||
#include "../typedefs.h"
|
||||
|
||||
/*
|
||||
Concurrent Queue written by Anthony Williams:
|
||||
http://www.justsoftwaresolutions.co.uk/threading/implementing-a-thread-safe-queue-using-condition-variables.html
|
||||
*/
|
||||
template<typename Data>
|
||||
class ConcurrentQueue {
|
||||
#include <boost/circular_buffer.hpp>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
public:
|
||||
ConcurrentQueue(const size_t max_size)
|
||||
: max_queue_size(max_size) {
|
||||
template <typename Data> class ConcurrentQueue
|
||||
{
|
||||
public:
|
||||
explicit ConcurrentQueue(const size_t max_size) : m_internal_queue(max_size) {}
|
||||
|
||||
inline void push(const Data &data)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
m_not_full.wait(lock,
|
||||
[this]
|
||||
{ return m_internal_queue.size() < m_internal_queue.capacity(); });
|
||||
m_internal_queue.push_back(data);
|
||||
m_mutex.unlock();
|
||||
m_not_empty.notify_one();
|
||||
}
|
||||
|
||||
void push(Data const& data) {
|
||||
if (size_exceeded()) {
|
||||
boost::mutex::scoped_lock qf_lock(queue_full_mutex);
|
||||
queue_full_cv.wait(qf_lock);
|
||||
}
|
||||
inline bool empty() const { return m_internal_queue.empty(); }
|
||||
|
||||
boost::mutex::scoped_lock lock(queue_mutex);
|
||||
internal_queue.push(data);
|
||||
lock.unlock();
|
||||
queue_cv.notify_one();
|
||||
inline void wait_and_pop(Data &popped_value)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
m_not_empty.wait(lock,
|
||||
[this]
|
||||
{ return !m_internal_queue.empty(); });
|
||||
popped_value = m_internal_queue.front();
|
||||
m_internal_queue.pop_front();
|
||||
m_mutex.unlock();
|
||||
m_not_full.notify_one();
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return internal_queue.empty();
|
||||
}
|
||||
|
||||
bool try_pop(Data& popped_value) {
|
||||
boost::mutex::scoped_lock lock(queue_mutex);
|
||||
if(internal_queue.empty()) {
|
||||
inline bool try_pop(Data &popped_value)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(m_mutex);
|
||||
if (m_internal_queue.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
popped_value=internal_queue.front();
|
||||
internal_queue.pop();
|
||||
queue_full_cv.notify_one();
|
||||
popped_value = m_internal_queue.front();
|
||||
m_internal_queue.pop_front();
|
||||
m_mutex.unlock();
|
||||
m_not_full.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
void wait_and_pop(Data& popped_value) {
|
||||
boost::mutex::scoped_lock lock(queue_mutex);
|
||||
while(internal_queue.empty()) {
|
||||
queue_cv.wait(lock);
|
||||
}
|
||||
|
||||
popped_value=internal_queue.front();
|
||||
internal_queue.pop();
|
||||
queue_full_cv.notify_one();
|
||||
}
|
||||
|
||||
int size() const {
|
||||
return static_cast<int>(internal_queue.size());
|
||||
}
|
||||
|
||||
private:
|
||||
std::queue<Data> internal_queue;
|
||||
mutable boost::mutex queue_mutex;
|
||||
mutable boost::mutex queue_full_mutex;
|
||||
boost::condition_variable queue_cv;
|
||||
boost::condition_variable queue_full_cv;
|
||||
const size_t max_queue_size;
|
||||
|
||||
bool size_exceeded() const {
|
||||
return internal_queue.size() >= max_queue_size;
|
||||
}
|
||||
|
||||
private:
|
||||
boost::circular_buffer<Data> m_internal_queue;
|
||||
std::mutex m_mutex;
|
||||
std::condition_variable m_not_empty;
|
||||
std::condition_variable m_not_full;
|
||||
};
|
||||
|
||||
#endif //#ifndef CONCURRENTQUEUE_H_INCLUDED
|
||||
#endif // CONCURRENT_QUEUE_H
|
||||
|
||||
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <osrm/Coordinate.h>
|
||||
#include "../Util/MercatorUtil.h"
|
||||
#include "../Util/SimpleLogger.h"
|
||||
#include "../Util/StringUtil.h"
|
||||
#include "../Util/TrigonometryTables.h"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#ifndef NDEBUG
|
||||
#include <bitset>
|
||||
#endif
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
|
||||
FixedPointCoordinate::FixedPointCoordinate()
|
||||
: lat(std::numeric_limits<int>::min()), lon(std::numeric_limits<int>::min())
|
||||
{
|
||||
}
|
||||
|
||||
FixedPointCoordinate::FixedPointCoordinate(int lat, int lon) : lat(lat), lon(lon)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
if (0 != (std::abs(lat) >> 30))
|
||||
{
|
||||
std::bitset<32> y(lat);
|
||||
SimpleLogger().Write(logDEBUG) << "broken lat: " << lat << ", bits: " << y;
|
||||
}
|
||||
if (0 != (std::abs(lon) >> 30))
|
||||
{
|
||||
std::bitset<32> x(lon);
|
||||
SimpleLogger().Write(logDEBUG) << "broken lon: " << lon << ", bits: " << x;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void FixedPointCoordinate::Reset()
|
||||
{
|
||||
lat = std::numeric_limits<int>::min();
|
||||
lon = std::numeric_limits<int>::min();
|
||||
}
|
||||
bool FixedPointCoordinate::isSet() const
|
||||
{
|
||||
return (std::numeric_limits<int>::min() != lat) && (std::numeric_limits<int>::min() != lon);
|
||||
}
|
||||
bool FixedPointCoordinate::isValid() const
|
||||
{
|
||||
if (lat > 90 * COORDINATE_PRECISION || lat < -90 * COORDINATE_PRECISION ||
|
||||
lon > 180 * COORDINATE_PRECISION || lon < -180 * COORDINATE_PRECISION)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool FixedPointCoordinate::operator==(const FixedPointCoordinate &other) const
|
||||
{
|
||||
return lat == other.lat && lon == other.lon;
|
||||
}
|
||||
|
||||
double FixedPointCoordinate::ApproximateDistance(const int lat1,
|
||||
const int lon1,
|
||||
const int lat2,
|
||||
const int lon2)
|
||||
{
|
||||
BOOST_ASSERT(lat1 != std::numeric_limits<int>::min());
|
||||
BOOST_ASSERT(lon1 != std::numeric_limits<int>::min());
|
||||
BOOST_ASSERT(lat2 != std::numeric_limits<int>::min());
|
||||
BOOST_ASSERT(lon2 != std::numeric_limits<int>::min());
|
||||
double RAD = 0.017453292519943295769236907684886;
|
||||
double lt1 = lat1 / COORDINATE_PRECISION;
|
||||
double ln1 = lon1 / COORDINATE_PRECISION;
|
||||
double lt2 = lat2 / COORDINATE_PRECISION;
|
||||
double ln2 = lon2 / COORDINATE_PRECISION;
|
||||
double dlat1 = lt1 * (RAD);
|
||||
|
||||
double dlong1 = ln1 * (RAD);
|
||||
double dlat2 = lt2 * (RAD);
|
||||
double dlong2 = ln2 * (RAD);
|
||||
|
||||
double dLong = dlong1 - dlong2;
|
||||
double dLat = dlat1 - dlat2;
|
||||
|
||||
double aHarv = pow(sin(dLat / 2.0), 2.0) + cos(dlat1) * cos(dlat2) * pow(sin(dLong / 2.), 2);
|
||||
double cHarv = 2. * atan2(sqrt(aHarv), sqrt(1.0 - aHarv));
|
||||
// earth radius varies between 6,356.750-6,378.135 km (3,949.901-3,963.189mi)
|
||||
// The IUGG value for the equatorial radius is 6378.137 km (3963.19 miles)
|
||||
const double earth = 6372797.560856;
|
||||
return earth * cHarv;
|
||||
}
|
||||
|
||||
double FixedPointCoordinate::ApproximateDistance(const FixedPointCoordinate &c1,
|
||||
const FixedPointCoordinate &c2)
|
||||
{
|
||||
return ApproximateDistance(c1.lat, c1.lon, c2.lat, c2.lon);
|
||||
}
|
||||
|
||||
float FixedPointCoordinate::ApproximateEuclideanDistance(const FixedPointCoordinate &c1,
|
||||
const FixedPointCoordinate &c2)
|
||||
{
|
||||
return ApproximateEuclideanDistance(c1.lat, c1.lon, c2.lat, c2.lon);
|
||||
}
|
||||
|
||||
float FixedPointCoordinate::ApproximateEuclideanDistance(const int lat1,
|
||||
const int lon1,
|
||||
const int lat2,
|
||||
const int lon2)
|
||||
{
|
||||
BOOST_ASSERT(lat1 != std::numeric_limits<int>::min());
|
||||
BOOST_ASSERT(lon1 != std::numeric_limits<int>::min());
|
||||
BOOST_ASSERT(lat2 != std::numeric_limits<int>::min());
|
||||
BOOST_ASSERT(lon2 != std::numeric_limits<int>::min());
|
||||
|
||||
const float RAD = 0.017453292519943295769236907684886;
|
||||
const float float_lat1 = (lat1 / COORDINATE_PRECISION) * RAD;
|
||||
const float float_lon1 = (lon1 / COORDINATE_PRECISION) * RAD;
|
||||
const float float_lat2 = (lat2 / COORDINATE_PRECISION) * RAD;
|
||||
const float float_lon2 = (lon2 / COORDINATE_PRECISION) * RAD;
|
||||
|
||||
const float x = (float_lon2 - float_lon1) * cos((float_lat1 + float_lat2) / 2.);
|
||||
const float y = (float_lat2 - float_lat1);
|
||||
const float earth_radius = 6372797.560856;
|
||||
return sqrt(x * x + y * y) * earth_radius;
|
||||
}
|
||||
|
||||
float FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordinate &point,
|
||||
const FixedPointCoordinate &segA,
|
||||
const FixedPointCoordinate &segB)
|
||||
{
|
||||
const float x = lat2y(point.lat / COORDINATE_PRECISION);
|
||||
const float y = point.lon / COORDINATE_PRECISION;
|
||||
const float a = lat2y(segA.lat / COORDINATE_PRECISION);
|
||||
const float b = segA.lon / COORDINATE_PRECISION;
|
||||
const float c = lat2y(segB.lat / COORDINATE_PRECISION);
|
||||
const float d = segB.lon / COORDINATE_PRECISION;
|
||||
float p, q, nY;
|
||||
if (std::abs(a - c) > std::numeric_limits<float>::epsilon())
|
||||
{
|
||||
const float m = (d - b) / (c - a); // slope
|
||||
// Projection of (x,y) on line joining (a,b) and (c,d)
|
||||
p = ((x + (m * y)) + (m * m * a - m * b)) / (1. + m * m);
|
||||
q = b + m * (p - a);
|
||||
}
|
||||
else
|
||||
{
|
||||
p = c;
|
||||
q = y;
|
||||
}
|
||||
nY = (d * p - c * q) / (a * d - b * c);
|
||||
|
||||
// discretize the result to coordinate precision. it's a hack!
|
||||
if (std::abs(nY) < (1. / COORDINATE_PRECISION))
|
||||
{
|
||||
nY = 0.;
|
||||
}
|
||||
|
||||
float r = (p - nY * a) / c;
|
||||
if (std::isnan(r))
|
||||
{
|
||||
r = ((segB.lat == point.lat) && (segB.lon == point.lon)) ? 1. : 0.;
|
||||
}
|
||||
else if (std::abs(r) <= std::numeric_limits<float>::epsilon())
|
||||
{
|
||||
r = 0.;
|
||||
}
|
||||
else if (std::abs(r - 1.) <= std::numeric_limits<float>::epsilon())
|
||||
{
|
||||
r = 1.;
|
||||
}
|
||||
FixedPointCoordinate nearest_location;
|
||||
BOOST_ASSERT(!std::isnan(r));
|
||||
if (r <= 0.)
|
||||
{ // point is "left" of edge
|
||||
nearest_location.lat = segA.lat;
|
||||
nearest_location.lon = segA.lon;
|
||||
}
|
||||
else if (r >= 1.)
|
||||
{ // point is "right" of edge
|
||||
nearest_location.lat = segB.lat;
|
||||
nearest_location.lon = segB.lon;
|
||||
}
|
||||
else
|
||||
{ // point lies in between
|
||||
nearest_location.lat = y2lat(p) * COORDINATE_PRECISION;
|
||||
nearest_location.lon = q * COORDINATE_PRECISION;
|
||||
}
|
||||
BOOST_ASSERT(nearest_location.isValid());
|
||||
const float approximated_distance =
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(point, nearest_location);
|
||||
BOOST_ASSERT(0. <= approximated_distance);
|
||||
return approximated_distance;
|
||||
}
|
||||
|
||||
float FixedPointCoordinate::ComputePerpendicularDistance(const FixedPointCoordinate &coord_a,
|
||||
const FixedPointCoordinate &coord_b,
|
||||
const FixedPointCoordinate &query_location,
|
||||
FixedPointCoordinate &nearest_location,
|
||||
float &r)
|
||||
{
|
||||
BOOST_ASSERT(query_location.isValid());
|
||||
|
||||
const float x = lat2y(query_location.lat / COORDINATE_PRECISION);
|
||||
const float y = query_location.lon / COORDINATE_PRECISION;
|
||||
const float a = lat2y(coord_a.lat / COORDINATE_PRECISION);
|
||||
const float b = coord_a.lon / COORDINATE_PRECISION;
|
||||
const float c = lat2y(coord_b.lat / COORDINATE_PRECISION);
|
||||
const float d = coord_b.lon / COORDINATE_PRECISION;
|
||||
float p, q /*,mX*/, nY;
|
||||
if (std::abs(a - c) > std::numeric_limits<float>::epsilon())
|
||||
{
|
||||
const float m = (d - b) / (c - a); // slope
|
||||
// Projection of (x,y) on line joining (a,b) and (c,d)
|
||||
p = ((x + (m * y)) + (m * m * a - m * b)) / (1. + m * m);
|
||||
q = b + m * (p - a);
|
||||
}
|
||||
else
|
||||
{
|
||||
p = c;
|
||||
q = y;
|
||||
}
|
||||
nY = (d * p - c * q) / (a * d - b * c);
|
||||
|
||||
// discretize the result to coordinate precision. it's a hack!
|
||||
if (std::abs(nY) < (1. / COORDINATE_PRECISION))
|
||||
{
|
||||
nY = 0.;
|
||||
}
|
||||
|
||||
r = (p - nY * a) / c; // These values are actually n/m+n and m/m+n , we need
|
||||
// not calculate the explicit values of m an n as we
|
||||
// are just interested in the ratio
|
||||
if (std::isnan(r))
|
||||
{
|
||||
r = ((coord_b.lat == query_location.lat) && (coord_b.lon == query_location.lon)) ? 1. : 0.;
|
||||
}
|
||||
else if (std::abs(r) <= std::numeric_limits<float>::epsilon())
|
||||
{
|
||||
r = 0.;
|
||||
}
|
||||
else if (std::abs(r - 1.) <= std::numeric_limits<float>::epsilon())
|
||||
{
|
||||
r = 1.;
|
||||
}
|
||||
BOOST_ASSERT(!std::isnan(r));
|
||||
if (r <= 0.)
|
||||
{
|
||||
nearest_location = coord_a;
|
||||
}
|
||||
else if (r >= 1.)
|
||||
{
|
||||
nearest_location = coord_b;
|
||||
}
|
||||
else
|
||||
{
|
||||
// point lies in between
|
||||
nearest_location.lat = y2lat(p) * COORDINATE_PRECISION;
|
||||
nearest_location.lon = q * COORDINATE_PRECISION;
|
||||
}
|
||||
BOOST_ASSERT(nearest_location.isValid());
|
||||
|
||||
// TODO: Replace with euclidean approximation when k-NN search is done
|
||||
// const float approximated_distance = FixedPointCoordinate::ApproximateEuclideanDistance(
|
||||
const float approximated_distance =
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(query_location, nearest_location);
|
||||
BOOST_ASSERT(0. <= approximated_distance);
|
||||
return approximated_distance;
|
||||
}
|
||||
|
||||
void FixedPointCoordinate::convertInternalLatLonToString(const int value, std::string &output)
|
||||
{
|
||||
char buffer[12];
|
||||
buffer[11] = 0; // zero termination
|
||||
output = printInt<11, 6>(buffer, value);
|
||||
}
|
||||
|
||||
void FixedPointCoordinate::convertInternalCoordinateToString(const FixedPointCoordinate &coord,
|
||||
std::string &output)
|
||||
{
|
||||
std::string tmp;
|
||||
tmp.reserve(23);
|
||||
convertInternalLatLonToString(coord.lon, tmp);
|
||||
output = tmp;
|
||||
output += ",";
|
||||
convertInternalLatLonToString(coord.lat, tmp);
|
||||
output += tmp;
|
||||
}
|
||||
|
||||
void
|
||||
FixedPointCoordinate::convertInternalReversedCoordinateToString(const FixedPointCoordinate &coord,
|
||||
std::string &output)
|
||||
{
|
||||
std::string tmp;
|
||||
tmp.reserve(23);
|
||||
convertInternalLatLonToString(coord.lat, tmp);
|
||||
output = tmp;
|
||||
output += ",";
|
||||
convertInternalLatLonToString(coord.lon, tmp);
|
||||
output += tmp;
|
||||
}
|
||||
|
||||
void FixedPointCoordinate::Output(std::ostream &out) const
|
||||
{
|
||||
out << "(" << lat / COORDINATE_PRECISION << "," << lon / COORDINATE_PRECISION << ")";
|
||||
}
|
||||
|
||||
float FixedPointCoordinate::GetBearing(const FixedPointCoordinate &A, const FixedPointCoordinate &B)
|
||||
{
|
||||
const float delta_long = DegreeToRadian(B.lon / COORDINATE_PRECISION - A.lon / COORDINATE_PRECISION);
|
||||
const float lat1 = DegreeToRadian(A.lat / COORDINATE_PRECISION);
|
||||
const float lat2 = DegreeToRadian(B.lat / COORDINATE_PRECISION);
|
||||
const float y = sin(delta_long) * cos(lat2);
|
||||
const float x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(delta_long);
|
||||
float result = RadianToDegree(std::atan2(y, x));
|
||||
while (result < 0.f)
|
||||
{
|
||||
result += 360.f;
|
||||
}
|
||||
|
||||
while (result >= 360.f)
|
||||
{
|
||||
result -= 360.f;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
float FixedPointCoordinate::GetBearing(const FixedPointCoordinate &other) const
|
||||
{
|
||||
const float delta_long = DegreeToRadian(lon / COORDINATE_PRECISION - other.lon / COORDINATE_PRECISION);
|
||||
const float lat1 = DegreeToRadian(other.lat / COORDINATE_PRECISION);
|
||||
const float lat2 = DegreeToRadian(lat / COORDINATE_PRECISION);
|
||||
const float y = std::sin(delta_long) * std::cos(lat2);
|
||||
const float x = std::cos(lat1) * std::sin(lat2) - std::sin(lat1) * std::cos(lat2) * std::cos(delta_long);
|
||||
float result = RadianToDegree(std::atan2(y, x));
|
||||
|
||||
while (result < 0.f)
|
||||
{
|
||||
result += 360.f;
|
||||
}
|
||||
|
||||
while (result >= 360.f)
|
||||
{
|
||||
result -= 360.f;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
float FixedPointCoordinate::DegreeToRadian(const float degree)
|
||||
{
|
||||
return degree * (M_PI / 180.f);
|
||||
}
|
||||
|
||||
float FixedPointCoordinate::RadianToDegree(const float radian)
|
||||
{
|
||||
return radian * (180.f / M_PI);
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef DEALLOCATINGVECTOR_H_
|
||||
#define DEALLOCATINGVECTOR_H_
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
|
||||
template <typename ElementT,
|
||||
std::size_t bucketSizeC = 8388608 / sizeof(ElementT),
|
||||
bool DeallocateC = false>
|
||||
class DeallocatingVectorIterator : public std::iterator<std::random_access_iterator_tag, ElementT>
|
||||
{
|
||||
protected:
|
||||
class DeallocatingVectorIteratorState
|
||||
{
|
||||
private:
|
||||
// make constructors explicit, so we do not mix random access and deallocation iterators.
|
||||
DeallocatingVectorIteratorState();
|
||||
|
||||
public:
|
||||
explicit DeallocatingVectorIteratorState(const DeallocatingVectorIteratorState &r)
|
||||
: index(r.index), bucket_list(r.bucket_list)
|
||||
{
|
||||
}
|
||||
explicit DeallocatingVectorIteratorState(const std::size_t idx,
|
||||
std::vector<ElementT *> &input_list)
|
||||
: index(idx), bucket_list(input_list)
|
||||
{
|
||||
}
|
||||
std::size_t index;
|
||||
std::vector<ElementT *> &bucket_list;
|
||||
|
||||
inline bool operator!=(const DeallocatingVectorIteratorState &other)
|
||||
{
|
||||
return index != other.index;
|
||||
}
|
||||
|
||||
inline bool operator==(const DeallocatingVectorIteratorState &other)
|
||||
{
|
||||
return index == other.index;
|
||||
}
|
||||
|
||||
bool operator<(const DeallocatingVectorIteratorState &other) const
|
||||
{
|
||||
return index < other.index;
|
||||
}
|
||||
|
||||
bool operator>(const DeallocatingVectorIteratorState &other) const
|
||||
{
|
||||
return index > other.index;
|
||||
}
|
||||
|
||||
bool operator>=(const DeallocatingVectorIteratorState &other) const
|
||||
{
|
||||
return index >= other.index;
|
||||
}
|
||||
|
||||
// This is a hack to make assignment operator possible with reference member
|
||||
inline DeallocatingVectorIteratorState &operator=(const DeallocatingVectorIteratorState &a)
|
||||
{
|
||||
if (this != &a)
|
||||
{
|
||||
this->DeallocatingVectorIteratorState::
|
||||
~DeallocatingVectorIteratorState(); // explicit non-virtual destructor
|
||||
new (this) DeallocatingVectorIteratorState(a); // placement new
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
DeallocatingVectorIteratorState current_state;
|
||||
|
||||
public:
|
||||
typedef std::random_access_iterator_tag iterator_category;
|
||||
typedef typename std::iterator<std::random_access_iterator_tag, ElementT>::value_type
|
||||
value_type;
|
||||
typedef typename std::iterator<std::random_access_iterator_tag, ElementT>::difference_type
|
||||
difference_type;
|
||||
typedef typename std::iterator<std::random_access_iterator_tag, ElementT>::reference reference;
|
||||
typedef typename std::iterator<std::random_access_iterator_tag, ElementT>::pointer pointer;
|
||||
|
||||
DeallocatingVectorIterator() {}
|
||||
|
||||
template <typename T2>
|
||||
explicit DeallocatingVectorIterator(const DeallocatingVectorIterator<T2> &r)
|
||||
: current_state(r.current_state)
|
||||
{
|
||||
}
|
||||
|
||||
DeallocatingVectorIterator(std::size_t idx, std::vector<ElementT *> &input_list)
|
||||
: current_state(idx, input_list)
|
||||
{
|
||||
}
|
||||
explicit DeallocatingVectorIterator(const DeallocatingVectorIteratorState &r) : current_state(r) {}
|
||||
|
||||
template <typename T2>
|
||||
DeallocatingVectorIterator &operator=(const DeallocatingVectorIterator<T2> &r)
|
||||
{
|
||||
if (DeallocateC)
|
||||
{
|
||||
BOOST_ASSERT(false);
|
||||
}
|
||||
current_state = r.current_state;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline DeallocatingVectorIterator &operator++()
|
||||
{ // prefix
|
||||
++current_state.index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline DeallocatingVectorIterator &operator--()
|
||||
{ // prefix
|
||||
if (DeallocateC)
|
||||
{
|
||||
BOOST_ASSERT(false);
|
||||
}
|
||||
--current_state.index;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline DeallocatingVectorIterator operator++(int)
|
||||
{ // postfix
|
||||
DeallocatingVectorIteratorState my_state(current_state);
|
||||
current_state.index++;
|
||||
return DeallocatingVectorIterator(my_state);
|
||||
}
|
||||
inline DeallocatingVectorIterator operator--(int)
|
||||
{ // postfix
|
||||
if (DeallocateC)
|
||||
{
|
||||
BOOST_ASSERT(false);
|
||||
}
|
||||
DeallocatingVectorIteratorState my_state(current_state);
|
||||
current_state.index--;
|
||||
return DeallocatingVectorIterator(my_state);
|
||||
}
|
||||
|
||||
inline DeallocatingVectorIterator operator+(const difference_type &n) const
|
||||
{
|
||||
DeallocatingVectorIteratorState my_state(current_state);
|
||||
my_state.index += n;
|
||||
return DeallocatingVectorIterator(my_state);
|
||||
}
|
||||
|
||||
inline DeallocatingVectorIterator &operator+=(const difference_type &n)
|
||||
{
|
||||
current_state.index += n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline DeallocatingVectorIterator operator-(const difference_type &n) const
|
||||
{
|
||||
if (DeallocateC)
|
||||
{
|
||||
BOOST_ASSERT(false);
|
||||
}
|
||||
DeallocatingVectorIteratorState my_state(current_state);
|
||||
my_state.index -= n;
|
||||
return DeallocatingVectorIterator(my_state);
|
||||
}
|
||||
|
||||
inline DeallocatingVectorIterator &operator-=(const difference_type &n) const
|
||||
{
|
||||
if (DeallocateC)
|
||||
{
|
||||
BOOST_ASSERT(false);
|
||||
}
|
||||
current_state.index -= n;
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline reference operator*() const
|
||||
{
|
||||
std::size_t current_bucket = current_state.index / bucketSizeC;
|
||||
std::size_t current_index = current_state.index % bucketSizeC;
|
||||
return (current_state.bucket_list[current_bucket][current_index]);
|
||||
}
|
||||
|
||||
inline pointer operator->() const
|
||||
{
|
||||
std::size_t current_bucket = current_state.index / bucketSizeC;
|
||||
std::size_t current_index = current_state.index % bucketSizeC;
|
||||
return &(current_state.bucket_list[current_bucket][current_index]);
|
||||
}
|
||||
|
||||
inline bool operator!=(const DeallocatingVectorIterator &other)
|
||||
{
|
||||
return current_state != other.current_state;
|
||||
}
|
||||
|
||||
inline bool operator==(const DeallocatingVectorIterator &other)
|
||||
{
|
||||
return current_state == other.current_state;
|
||||
}
|
||||
|
||||
inline bool operator<(const DeallocatingVectorIterator &other) const
|
||||
{
|
||||
return current_state < other.current_state;
|
||||
}
|
||||
|
||||
inline bool operator>(const DeallocatingVectorIterator &other) const
|
||||
{
|
||||
return current_state > other.current_state;
|
||||
}
|
||||
|
||||
inline bool operator>=(const DeallocatingVectorIterator &other) const
|
||||
{
|
||||
return current_state >= other.current_state;
|
||||
}
|
||||
|
||||
difference_type operator-(const DeallocatingVectorIterator &other)
|
||||
{
|
||||
if (DeallocateC)
|
||||
{
|
||||
BOOST_ASSERT(false);
|
||||
}
|
||||
return current_state.index - other.current_state.index;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ElementT, std::size_t bucketSizeC = 8388608 / sizeof(ElementT)>
|
||||
class DeallocatingVector
|
||||
{
|
||||
private:
|
||||
std::size_t current_size;
|
||||
std::vector<ElementT *> bucket_list;
|
||||
|
||||
public:
|
||||
typedef ElementT value_type;
|
||||
typedef DeallocatingVectorIterator<ElementT, bucketSizeC, false> iterator;
|
||||
typedef DeallocatingVectorIterator<ElementT, bucketSizeC, false> const_iterator;
|
||||
|
||||
// this iterator deallocates all buckets that have been visited. Iterators to visited objects
|
||||
// become invalid.
|
||||
typedef DeallocatingVectorIterator<ElementT, bucketSizeC, true> deallocation_iterator;
|
||||
|
||||
DeallocatingVector() : current_size(0)
|
||||
{
|
||||
// initial bucket
|
||||
bucket_list.emplace_back(new ElementT[bucketSizeC]);
|
||||
}
|
||||
|
||||
~DeallocatingVector() { clear(); }
|
||||
|
||||
inline void swap(DeallocatingVector<ElementT, bucketSizeC> &other)
|
||||
{
|
||||
std::swap(current_size, other.current_size);
|
||||
bucket_list.swap(other.bucket_list);
|
||||
}
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
// Delete[]'ing ptr's to all Buckets
|
||||
for (unsigned i = 0; i < bucket_list.size(); ++i)
|
||||
{
|
||||
if (nullptr != bucket_list[i])
|
||||
{
|
||||
delete[] bucket_list[i];
|
||||
bucket_list[i] = nullptr;
|
||||
}
|
||||
}
|
||||
// Removing all ptrs from vector
|
||||
std::vector<ElementT *>().swap(bucket_list);
|
||||
current_size = 0;
|
||||
}
|
||||
|
||||
inline void push_back(const ElementT &element)
|
||||
{
|
||||
const std::size_t current_capacity = capacity();
|
||||
if (current_size == current_capacity)
|
||||
{
|
||||
bucket_list.push_back(new ElementT[bucketSizeC]);
|
||||
}
|
||||
|
||||
std::size_t current_index = size() % bucketSizeC;
|
||||
bucket_list.back()[current_index] = element;
|
||||
++current_size;
|
||||
}
|
||||
|
||||
inline void emplace_back(const ElementT &&element)
|
||||
{
|
||||
const std::size_t current_capacity = capacity();
|
||||
if (current_size == current_capacity)
|
||||
{
|
||||
bucket_list.push_back(new ElementT[bucketSizeC]);
|
||||
}
|
||||
|
||||
const std::size_t current_index = size() % bucketSizeC;
|
||||
bucket_list.back()[current_index] = element;
|
||||
++current_size;
|
||||
}
|
||||
|
||||
inline void reserve(const std::size_t) const
|
||||
{
|
||||
// don't do anything
|
||||
}
|
||||
|
||||
inline void resize(const std::size_t new_size)
|
||||
{
|
||||
if (new_size > current_size)
|
||||
{
|
||||
while (capacity() < new_size)
|
||||
{
|
||||
bucket_list.push_back(new ElementT[bucketSizeC]);
|
||||
}
|
||||
current_size = new_size;
|
||||
}
|
||||
if (new_size < current_size)
|
||||
{
|
||||
const std::size_t number_of_necessary_buckets = 1 + (new_size / bucketSizeC);
|
||||
|
||||
for (unsigned i = number_of_necessary_buckets; i < bucket_list.size(); ++i)
|
||||
{
|
||||
delete[] bucket_list[i];
|
||||
}
|
||||
bucket_list.resize(number_of_necessary_buckets);
|
||||
current_size = new_size;
|
||||
}
|
||||
}
|
||||
|
||||
inline std::size_t size() const { return current_size; }
|
||||
|
||||
inline std::size_t capacity() const { return bucket_list.size() * bucketSizeC; }
|
||||
|
||||
inline iterator begin() { return iterator(static_cast<std::size_t>(0), bucket_list); }
|
||||
|
||||
inline iterator end() { return iterator(size(), bucket_list); }
|
||||
|
||||
inline deallocation_iterator dbegin()
|
||||
{
|
||||
return deallocation_iterator(static_cast<std::size_t>(0), bucket_list);
|
||||
}
|
||||
|
||||
inline deallocation_iterator dend() { return deallocation_iterator(size(), bucket_list); }
|
||||
|
||||
inline const_iterator begin() const
|
||||
{
|
||||
return const_iterator(static_cast<std::size_t>(0), bucket_list);
|
||||
}
|
||||
|
||||
inline const_iterator end() const { return const_iterator(size(), bucket_list); }
|
||||
|
||||
inline ElementT &operator[](const std::size_t index)
|
||||
{
|
||||
std::size_t _bucket = index / bucketSizeC;
|
||||
std::size_t _index = index % bucketSizeC;
|
||||
return (bucket_list[_bucket][_index]);
|
||||
}
|
||||
|
||||
const inline ElementT &operator[](const std::size_t index) const
|
||||
{
|
||||
std::size_t _bucket = index / bucketSizeC;
|
||||
std::size_t _index = index % bucketSizeC;
|
||||
return (bucket_list[_bucket][_index]);
|
||||
}
|
||||
|
||||
inline ElementT &back()
|
||||
{
|
||||
std::size_t _bucket = current_size / bucketSizeC;
|
||||
std::size_t _index = current_size % bucketSizeC;
|
||||
return (bucket_list[_bucket][_index]);
|
||||
}
|
||||
|
||||
const inline ElementT &back() const
|
||||
{
|
||||
std::size_t _bucket = current_size / bucketSizeC;
|
||||
std::size_t _index = current_size % bucketSizeC;
|
||||
return (bucket_list[_bucket][_index]);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* DEALLOCATINGVECTOR_H_ */
|
||||
@@ -1,237 +1,281 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
#ifndef DYNAMICGRAPH_H_INCLUDED
|
||||
#define DYNAMICGRAPH_H_INCLUDED
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef DYNAMICGRAPH_H
|
||||
#define DYNAMICGRAPH_H
|
||||
|
||||
#include "../DataStructures/DeallocatingVector.h"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/range/irange.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
template< typename EdgeDataT>
|
||||
class DynamicGraph {
|
||||
public:
|
||||
typedef EdgeDataT EdgeData;
|
||||
typedef unsigned NodeIterator;
|
||||
typedef unsigned EdgeIterator;
|
||||
template <typename EdgeDataT> class DynamicGraph
|
||||
{
|
||||
public:
|
||||
typedef decltype(boost::irange(0u,0u)) EdgeRange;
|
||||
typedef EdgeDataT EdgeData;
|
||||
typedef unsigned NodeIterator;
|
||||
typedef unsigned EdgeIterator;
|
||||
|
||||
class InputEdge {
|
||||
public:
|
||||
NodeIterator source;
|
||||
NodeIterator target;
|
||||
EdgeDataT data;
|
||||
bool operator<( const InputEdge& right ) const {
|
||||
if ( source != right.source )
|
||||
return source < right.source;
|
||||
return target < right.target;
|
||||
}
|
||||
};
|
||||
|
||||
//Constructs an empty graph with a given number of nodes.
|
||||
DynamicGraph( int nodes ) : m_numNodes(nodes), m_numEdges(0) {
|
||||
m_nodes.reserve( m_numNodes );
|
||||
m_nodes.resize( m_numNodes );
|
||||
|
||||
m_edges.reserve( m_numNodes * 1.2 );
|
||||
m_edges.resize( m_numNodes );
|
||||
}
|
||||
DynamicGraph( int nodes, const std::vector< InputEdge > &graph )
|
||||
class InputEdge
|
||||
{
|
||||
public:
|
||||
NodeIterator source;
|
||||
NodeIterator target;
|
||||
EdgeDataT data;
|
||||
bool operator<(const InputEdge &right) const
|
||||
{
|
||||
m_numNodes = nodes;
|
||||
m_numEdges = ( EdgeIterator ) graph.size();
|
||||
m_nodes.reserve( m_numNodes );
|
||||
m_nodes.resize( m_numNodes );
|
||||
EdgeIterator edge = 0;
|
||||
EdgeIterator position = 0;
|
||||
for ( NodeIterator node = 0; node < m_numNodes; ++node ) {
|
||||
EdgeIterator lastEdge = edge;
|
||||
while ( edge < m_numEdges && graph[edge].source == node ) {
|
||||
++edge;
|
||||
}
|
||||
m_nodes[node].firstEdge = position;
|
||||
m_nodes[node].edges = edge - lastEdge;
|
||||
position += m_nodes[node].edges;
|
||||
if (source != right.source)
|
||||
return source < right.source;
|
||||
return target < right.target;
|
||||
}
|
||||
};
|
||||
|
||||
// Constructs an empty graph with a given number of nodes.
|
||||
explicit DynamicGraph(int32_t nodes) : m_numNodes(nodes), m_numEdges(0)
|
||||
{
|
||||
m_nodes.reserve(m_numNodes);
|
||||
m_nodes.resize(m_numNodes);
|
||||
|
||||
m_edges.reserve(m_numNodes * 1.1);
|
||||
m_edges.resize(m_numNodes);
|
||||
}
|
||||
|
||||
template <class ContainerT> DynamicGraph(const int32_t nodes, const ContainerT &graph)
|
||||
{
|
||||
m_numNodes = nodes;
|
||||
m_numEdges = (EdgeIterator)graph.size();
|
||||
m_nodes.reserve(m_numNodes + 1);
|
||||
m_nodes.resize(m_numNodes + 1);
|
||||
EdgeIterator edge = 0;
|
||||
EdgeIterator position = 0;
|
||||
for (NodeIterator node = 0; node < m_numNodes; ++node)
|
||||
{
|
||||
EdgeIterator lastEdge = edge;
|
||||
while (edge < m_numEdges && graph[edge].source == node)
|
||||
{
|
||||
++edge;
|
||||
}
|
||||
m_edges.reserve( position * 1.2 );
|
||||
m_edges.resize( position );
|
||||
edge = 0;
|
||||
for ( NodeIterator node = 0; node < m_numNodes; ++node ) {
|
||||
for ( EdgeIterator i = m_nodes[node].firstEdge, e = m_nodes[node].firstEdge + m_nodes[node].edges; i != e; ++i ) {
|
||||
m_edges[i].target = graph[edge].target;
|
||||
m_edges[i].data = graph[edge].data;
|
||||
GUARANTEE(graph[edge].data.distance > 0, "edge: " << edge << "(" << graph[edge].source << "," << graph[edge].target << ")=" << graph[edge].data.distance);
|
||||
edge++;
|
||||
m_nodes[node].firstEdge = position;
|
||||
m_nodes[node].edges = edge - lastEdge;
|
||||
position += m_nodes[node].edges;
|
||||
}
|
||||
m_nodes.back().firstEdge = position;
|
||||
m_edges.reserve(position * 1.1);
|
||||
m_edges.resize(position);
|
||||
edge = 0;
|
||||
for (NodeIterator node = 0; node < m_numNodes; ++node)
|
||||
{
|
||||
for (EdgeIterator i = m_nodes[node].firstEdge,
|
||||
e = m_nodes[node].firstEdge + m_nodes[node].edges;
|
||||
i != e;
|
||||
++i)
|
||||
{
|
||||
m_edges[i].target = graph[edge].target;
|
||||
m_edges[i].data = graph[edge].data;
|
||||
BOOST_ASSERT_MSG(graph[edge].data.distance > 0, "edge distance invalid");
|
||||
++edge;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~DynamicGraph() {}
|
||||
|
||||
unsigned GetNumberOfNodes() const { return m_numNodes; }
|
||||
|
||||
unsigned GetNumberOfEdges() const { return m_numEdges; }
|
||||
|
||||
unsigned GetOutDegree(const NodeIterator n) const { return m_nodes[n].edges; }
|
||||
|
||||
unsigned GetDirectedOutDegree(const NodeIterator n) const
|
||||
{
|
||||
unsigned degree = 0;
|
||||
for(EdgeIterator edge = BeginEdges(n); edge < EndEdges(n); ++edge)
|
||||
{
|
||||
if (GetEdgeData(edge).forward)
|
||||
{
|
||||
++degree;
|
||||
}
|
||||
}
|
||||
return degree;
|
||||
}
|
||||
|
||||
NodeIterator GetTarget(const EdgeIterator e) const { return NodeIterator(m_edges[e].target); }
|
||||
|
||||
void SetTarget(const EdgeIterator e, const NodeIterator n) { m_edges[e].target = n; }
|
||||
|
||||
EdgeDataT &GetEdgeData(const EdgeIterator e) { return m_edges[e].data; }
|
||||
|
||||
const EdgeDataT &GetEdgeData(const EdgeIterator e) const { return m_edges[e].data; }
|
||||
|
||||
EdgeIterator BeginEdges(const NodeIterator n) const
|
||||
{
|
||||
return EdgeIterator(m_nodes[n].firstEdge);
|
||||
}
|
||||
|
||||
EdgeIterator EndEdges(const NodeIterator n) const
|
||||
{
|
||||
return EdgeIterator(m_nodes[n].firstEdge + m_nodes[n].edges);
|
||||
}
|
||||
|
||||
EdgeRange GetAdjacentEdgeRange(const NodeIterator node) const
|
||||
{
|
||||
return boost::irange(BeginEdges(node), EndEdges(node));
|
||||
}
|
||||
|
||||
// adds an edge. Invalidates edge iterators for the source node
|
||||
EdgeIterator InsertEdge(const NodeIterator from, const NodeIterator to, const EdgeDataT &data)
|
||||
{
|
||||
Node &node = m_nodes[from];
|
||||
EdgeIterator newFirstEdge = node.edges + node.firstEdge;
|
||||
if (newFirstEdge >= m_edges.size() || !isDummy(newFirstEdge))
|
||||
{
|
||||
if (node.firstEdge != 0 && isDummy(node.firstEdge - 1))
|
||||
{
|
||||
node.firstEdge--;
|
||||
m_edges[node.firstEdge] = m_edges[node.firstEdge + node.edges];
|
||||
}
|
||||
else
|
||||
{
|
||||
EdgeIterator newFirstEdge = (EdgeIterator)m_edges.size();
|
||||
unsigned newSize = node.edges * 1.1 + 2;
|
||||
EdgeIterator requiredCapacity = newSize + m_edges.size();
|
||||
EdgeIterator oldCapacity = m_edges.capacity();
|
||||
if (requiredCapacity >= oldCapacity)
|
||||
{
|
||||
m_edges.reserve(requiredCapacity * 1.1);
|
||||
}
|
||||
m_edges.resize(m_edges.size() + newSize);
|
||||
for (EdgeIterator i = 0; i < node.edges; ++i)
|
||||
{
|
||||
m_edges[newFirstEdge + i] = m_edges[node.firstEdge + i];
|
||||
makeDummy(node.firstEdge + i);
|
||||
}
|
||||
for (EdgeIterator i = node.edges + 1; i < newSize; ++i)
|
||||
makeDummy(newFirstEdge + i);
|
||||
node.firstEdge = newFirstEdge;
|
||||
}
|
||||
}
|
||||
Edge &edge = m_edges[node.firstEdge + node.edges];
|
||||
edge.target = to;
|
||||
edge.data = data;
|
||||
++m_numEdges;
|
||||
++node.edges;
|
||||
return EdgeIterator(node.firstEdge + node.edges);
|
||||
}
|
||||
|
||||
// removes an edge. Invalidates edge iterators for the source node
|
||||
void DeleteEdge(const NodeIterator source, const EdgeIterator e)
|
||||
{
|
||||
Node &node = m_nodes[source];
|
||||
#pragma omp atomic
|
||||
--m_numEdges;
|
||||
--node.edges;
|
||||
BOOST_ASSERT(std::numeric_limits<unsigned>::max() != node.edges);
|
||||
const unsigned last = node.firstEdge + node.edges;
|
||||
BOOST_ASSERT(std::numeric_limits<unsigned>::max() != last);
|
||||
// swap with last edge
|
||||
m_edges[e] = m_edges[last];
|
||||
makeDummy(last);
|
||||
}
|
||||
|
||||
// removes all edges (source,target)
|
||||
int32_t DeleteEdgesTo(const NodeIterator source, const NodeIterator target)
|
||||
{
|
||||
int32_t deleted = 0;
|
||||
for (EdgeIterator i = BeginEdges(source), iend = EndEdges(source); i < iend - deleted; ++i)
|
||||
{
|
||||
if (m_edges[i].target == target)
|
||||
{
|
||||
do
|
||||
{
|
||||
deleted++;
|
||||
m_edges[i] = m_edges[iend - deleted];
|
||||
makeDummy(iend - deleted);
|
||||
} while (i < iend - deleted && m_edges[i].target == target);
|
||||
}
|
||||
}
|
||||
|
||||
~DynamicGraph(){ }
|
||||
#pragma omp atomic
|
||||
m_numEdges -= deleted;
|
||||
m_nodes[source].edges -= deleted;
|
||||
|
||||
unsigned GetNumberOfNodes() const
|
||||
{
|
||||
return m_numNodes;
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
|
||||
unsigned GetNumberOfEdges() const
|
||||
// searches for a specific edge
|
||||
EdgeIterator FindEdge(const NodeIterator from, const NodeIterator to) const
|
||||
{
|
||||
for (EdgeIterator i = BeginEdges(from), iend = EndEdges(from); i != iend; ++i)
|
||||
{
|
||||
return m_numEdges;
|
||||
}
|
||||
|
||||
unsigned GetOutDegree( const NodeIterator &n ) const
|
||||
{
|
||||
return m_nodes[n].edges;
|
||||
}
|
||||
|
||||
NodeIterator GetTarget( const EdgeIterator &e ) const
|
||||
{
|
||||
return NodeIterator( m_edges[e].target );
|
||||
}
|
||||
|
||||
EdgeDataT &GetEdgeData( const EdgeIterator &e )
|
||||
{
|
||||
return m_edges[e].data;
|
||||
}
|
||||
|
||||
const EdgeDataT &GetEdgeData( const EdgeIterator &e ) const
|
||||
{
|
||||
return m_edges[e].data;
|
||||
}
|
||||
|
||||
EdgeIterator BeginEdges( const NodeIterator &n ) const
|
||||
{
|
||||
//assert( EndEdges( n ) - EdgeIterator( _nodes[n].firstEdge ) <= 100 );
|
||||
return EdgeIterator( m_nodes[n].firstEdge );
|
||||
}
|
||||
|
||||
EdgeIterator EndEdges( const NodeIterator &n ) const
|
||||
{
|
||||
return EdgeIterator( m_nodes[n].firstEdge + m_nodes[n].edges );
|
||||
}
|
||||
|
||||
//adds an edge. Invalidates edge iterators for the source node
|
||||
EdgeIterator InsertEdge( const NodeIterator &from, const NodeIterator &to, const EdgeDataT &data )
|
||||
{
|
||||
Node &node = m_nodes[from];
|
||||
EdgeIterator newFirstEdge = node.edges + node.firstEdge;
|
||||
if ( newFirstEdge >= m_edges.size() || !isDummy( newFirstEdge ) ) {
|
||||
if ( node.firstEdge != 0 && isDummy( node.firstEdge - 1 ) ) {
|
||||
node.firstEdge--;
|
||||
m_edges[node.firstEdge] = m_edges[node.firstEdge + node.edges];
|
||||
} else {
|
||||
EdgeIterator newFirstEdge = ( EdgeIterator ) m_edges.size();
|
||||
unsigned newSize = node.edges * 1.2 + 2;
|
||||
EdgeIterator requiredCapacity = newSize + m_edges.size();
|
||||
EdgeIterator oldCapacity = m_edges.capacity();
|
||||
if ( requiredCapacity >= oldCapacity ) {
|
||||
m_edges.reserve( requiredCapacity * 1.1 );
|
||||
}
|
||||
m_edges.resize( m_edges.size() + newSize );
|
||||
for ( EdgeIterator i = 0; i < node.edges; ++i ) {
|
||||
m_edges[newFirstEdge + i ] = m_edges[node.firstEdge + i];
|
||||
makeDummy( node.firstEdge + i );
|
||||
}
|
||||
for ( EdgeIterator i = node.edges + 1; i < newSize; i++ )
|
||||
makeDummy( newFirstEdge + i );
|
||||
node.firstEdge = newFirstEdge;
|
||||
}
|
||||
if (to == m_edges[i].target)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
Edge &edge = m_edges[node.firstEdge + node.edges];
|
||||
edge.target = to;
|
||||
edge.data = data;
|
||||
m_numEdges++;
|
||||
node.edges++;
|
||||
return EdgeIterator( node.firstEdge + node.edges );
|
||||
}
|
||||
return EndEdges(from);
|
||||
}
|
||||
|
||||
//removes an edge. Invalidates edge iterators for the source node
|
||||
void DeleteEdge( const NodeIterator source, const EdgeIterator &e ) {
|
||||
Node &node = m_nodes[source];
|
||||
--m_numEdges;
|
||||
--node.edges;
|
||||
const unsigned last = node.firstEdge + node.edges;
|
||||
//swap with last edge
|
||||
m_edges[e] = m_edges[last];
|
||||
makeDummy( last );
|
||||
}
|
||||
protected:
|
||||
bool isDummy(const EdgeIterator edge) const
|
||||
{
|
||||
return m_edges[edge].target == (std::numeric_limits<NodeIterator>::max)();
|
||||
}
|
||||
|
||||
//removes all edges (source,target)
|
||||
int DeleteEdgesTo( const NodeIterator source, const NodeIterator target )
|
||||
{
|
||||
int deleted = 0;
|
||||
for ( EdgeIterator i = BeginEdges( source ), iend = EndEdges( source ); i < iend - deleted; ++i ) {
|
||||
if ( m_edges[i].target == target ) {
|
||||
do {
|
||||
deleted++;
|
||||
m_edges[i] = m_edges[iend - deleted];
|
||||
makeDummy( iend - deleted );
|
||||
} while ( i < iend - deleted && m_edges[i].target == target );
|
||||
}
|
||||
}
|
||||
void makeDummy(const EdgeIterator edge)
|
||||
{
|
||||
m_edges[edge].target = (std::numeric_limits<NodeIterator>::max)();
|
||||
}
|
||||
|
||||
#pragma omp atomic
|
||||
m_numEdges -= deleted;
|
||||
m_nodes[source].edges -= deleted;
|
||||
struct Node
|
||||
{
|
||||
// index of the first edge
|
||||
EdgeIterator firstEdge;
|
||||
// amount of edges
|
||||
unsigned edges;
|
||||
};
|
||||
|
||||
return deleted;
|
||||
}
|
||||
struct Edge
|
||||
{
|
||||
NodeIterator target;
|
||||
EdgeDataT data;
|
||||
};
|
||||
|
||||
//searches for a specific edge
|
||||
EdgeIterator FindEdge( const NodeIterator &from, const NodeIterator &to ) const
|
||||
{
|
||||
for ( EdgeIterator i = BeginEdges( from ), iend = EndEdges( from ); i != iend; ++i ) {
|
||||
if ( m_edges[i].target == to ) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return EndEdges( from );
|
||||
}
|
||||
NodeIterator m_numNodes;
|
||||
EdgeIterator m_numEdges;
|
||||
|
||||
protected:
|
||||
|
||||
bool isDummy( EdgeIterator edge ) const
|
||||
{
|
||||
return m_edges[edge].target == (std::numeric_limits< NodeIterator >::max)();
|
||||
}
|
||||
|
||||
void makeDummy( EdgeIterator edge )
|
||||
{
|
||||
m_edges[edge].target = (std::numeric_limits< NodeIterator >::max)();
|
||||
}
|
||||
|
||||
struct Node {
|
||||
//index of the first edge
|
||||
EdgeIterator firstEdge;
|
||||
//amount of edges
|
||||
unsigned edges;
|
||||
};
|
||||
|
||||
struct Edge {
|
||||
NodeIterator target;
|
||||
EdgeDataT data;
|
||||
};
|
||||
|
||||
NodeIterator m_numNodes;
|
||||
EdgeIterator m_numEdges;
|
||||
|
||||
std::vector< Node > m_nodes;
|
||||
std::vector< Edge > m_edges;
|
||||
std::vector<Node> m_nodes;
|
||||
DeallocatingVector<Edge> m_edges;
|
||||
};
|
||||
|
||||
#endif // DYNAMICGRAPH_H_INCLUDED
|
||||
#endif // DYNAMICGRAPH_H
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
#ifndef EDGE_BASED_NODE_H
|
||||
#define EDGE_BASED_NODE_H
|
||||
|
||||
#include "../Util/SimpleLogger.h"
|
||||
#include "../typedefs.h"
|
||||
|
||||
#include <osrm/Coordinate.h>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <limits>
|
||||
|
||||
struct EdgeBasedNode
|
||||
{
|
||||
|
||||
EdgeBasedNode() :
|
||||
forward_edge_based_node_id(SPECIAL_NODEID),
|
||||
reverse_edge_based_node_id(SPECIAL_NODEID),
|
||||
u(SPECIAL_NODEID),
|
||||
v(SPECIAL_NODEID),
|
||||
name_id(0),
|
||||
forward_weight(INVALID_EDGE_WEIGHT >> 1),
|
||||
reverse_weight(INVALID_EDGE_WEIGHT >> 1),
|
||||
forward_offset(0),
|
||||
reverse_offset(0),
|
||||
packed_geometry_id(SPECIAL_EDGEID),
|
||||
fwd_segment_position( std::numeric_limits<unsigned short>::max() ),
|
||||
is_in_tiny_cc(false)
|
||||
{ }
|
||||
|
||||
explicit EdgeBasedNode(
|
||||
NodeID forward_edge_based_node_id,
|
||||
NodeID reverse_edge_based_node_id,
|
||||
NodeID u,
|
||||
NodeID v,
|
||||
unsigned name_id,
|
||||
int forward_weight,
|
||||
int reverse_weight,
|
||||
int forward_offset,
|
||||
int reverse_offset,
|
||||
unsigned packed_geometry_id,
|
||||
unsigned short fwd_segment_position,
|
||||
bool belongs_to_tiny_component
|
||||
) :
|
||||
forward_edge_based_node_id(forward_edge_based_node_id),
|
||||
reverse_edge_based_node_id(reverse_edge_based_node_id),
|
||||
u(u),
|
||||
v(v),
|
||||
name_id(name_id),
|
||||
forward_weight(forward_weight),
|
||||
reverse_weight(reverse_weight),
|
||||
forward_offset(forward_offset),
|
||||
reverse_offset(reverse_offset),
|
||||
packed_geometry_id(packed_geometry_id),
|
||||
fwd_segment_position(fwd_segment_position),
|
||||
is_in_tiny_cc(belongs_to_tiny_component)
|
||||
{
|
||||
BOOST_ASSERT((forward_edge_based_node_id != SPECIAL_NODEID) ||
|
||||
(reverse_edge_based_node_id != SPECIAL_NODEID));
|
||||
}
|
||||
|
||||
static inline FixedPointCoordinate Centroid(const FixedPointCoordinate & a, const FixedPointCoordinate & b)
|
||||
{
|
||||
FixedPointCoordinate centroid;
|
||||
//The coordinates of the midpoint are given by:
|
||||
centroid.lat = (a.lat + b.lat)/2;
|
||||
centroid.lon = (a.lon + b.lon)/2;
|
||||
return centroid;
|
||||
}
|
||||
|
||||
bool IsCompressed() const
|
||||
{
|
||||
return packed_geometry_id != SPECIAL_EDGEID;
|
||||
}
|
||||
|
||||
NodeID forward_edge_based_node_id; // needed for edge-expanded graph
|
||||
NodeID reverse_edge_based_node_id; // needed for edge-expanded graph
|
||||
NodeID u; // indices into the coordinates array
|
||||
NodeID v; // indices into the coordinates array
|
||||
unsigned name_id; // id of the edge name
|
||||
int forward_weight; // weight of the edge
|
||||
int reverse_weight; // weight in the other direction (may be different)
|
||||
int forward_offset; // prefix sum of the weight up the edge TODO: short must suffice
|
||||
int reverse_offset; // prefix sum of the weight from the edge TODO: short must suffice
|
||||
unsigned packed_geometry_id; // if set, then the edge represents a packed geometry
|
||||
unsigned short fwd_segment_position; // segment id in a compressed geometry
|
||||
bool is_in_tiny_cc;
|
||||
};
|
||||
|
||||
#endif //EDGE_BASED_NODE_H
|
||||
@@ -1,196 +0,0 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
|
||||
#ifndef EXTRACTORCALLBACKS_H_
|
||||
#define EXTRACTORCALLBACKS_H_
|
||||
|
||||
#include <stxxl.h>
|
||||
#include <vector>
|
||||
#include "ExtractorStructs.h"
|
||||
|
||||
typedef stxxl::vector<NodeID> STXXLNodeIDVector;
|
||||
typedef stxxl::vector<_Node> STXXLNodeVector;
|
||||
typedef stxxl::vector<_Edge> STXXLEdgeVector;
|
||||
typedef stxxl::vector<_Address> STXXLAddressVector;
|
||||
typedef stxxl::vector<string> STXXLStringVector;
|
||||
typedef stxxl::vector<_RawRestrictionContainer> STXXLRestrictionsVector;
|
||||
typedef stxxl::vector<_WayIDStartAndEndEdge> STXXLWayIDStartEndVector;
|
||||
|
||||
|
||||
struct STXXLContainers {
|
||||
STXXLNodeIDVector usedNodeIDs;
|
||||
STXXLNodeVector allNodes;
|
||||
STXXLEdgeVector allEdges;
|
||||
STXXLAddressVector adressVector;
|
||||
STXXLStringVector nameVector;
|
||||
STXXLRestrictionsVector restrictionsVector;
|
||||
STXXLWayIDStartEndVector wayStartEndVector;
|
||||
};
|
||||
|
||||
class ExtractorCallbacks{
|
||||
private:
|
||||
Settings settings;
|
||||
StringMap * stringMap;
|
||||
STXXLContainers * externalMemory;
|
||||
|
||||
public:
|
||||
ExtractorCallbacks(STXXLContainers * ext, Settings set, StringMap * strMap){
|
||||
externalMemory = ext;
|
||||
settings = set;
|
||||
stringMap = strMap;
|
||||
}
|
||||
|
||||
~ExtractorCallbacks() {
|
||||
}
|
||||
|
||||
/** warning: caller needs to take care of synchronization! */
|
||||
inline bool adressFunction(_Node n, HashTable<std::string, std::string> &keyVals) {
|
||||
/*
|
||||
std::string housenumber(keyVals.Find("addr:housenumber"));
|
||||
std::string housename(keyVals.Find("addr:housename"));
|
||||
std::string street(keyVals.Find("addr:street"));
|
||||
std::string state(keyVals.Find("addr:state"));
|
||||
std::string country(keyVals.Find("addr:country"));
|
||||
std::string postcode(keyVals.Find("addr:postcode"));
|
||||
std::string city(keyVals.Find("addr:city"));
|
||||
|
||||
if(housenumber != "" || housename != "" || street != "") {
|
||||
if(housenumber == "")
|
||||
housenumber = housename;
|
||||
addressVector->push_back(_Address(n, housenumber, street, state, country, postcode, city));
|
||||
}
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
/** warning: caller needs to take care of synchronization! */
|
||||
inline bool nodeFunction(_Node &n) {
|
||||
externalMemory->allNodes.push_back(n);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool restrictionFunction(_RawRestrictionContainer &r) {
|
||||
externalMemory->restrictionsVector.push_back(r);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** warning: caller needs to take care of synchronization! */
|
||||
inline bool wayFunction(_Way &w) {
|
||||
|
||||
//Get the properties of the way.
|
||||
std::string highway( w.keyVals.Find("highway") );
|
||||
std::string name( w.keyVals.Find("name") );
|
||||
std::string ref( w.keyVals.Find("ref"));
|
||||
std::string oneway( w.keyVals.Find("oneway"));
|
||||
std::string junction( w.keyVals.Find("junction") );
|
||||
std::string route( w.keyVals.Find("route") );
|
||||
double maxspeed( atoi(w.keyVals.Find("maxspeed").c_str()) );
|
||||
std::string access( w.keyVals.Find("access") );
|
||||
std::string accessClass( w.keyVals.Find(settings.accessTag) );
|
||||
std::string man_made( w.keyVals.Find("man_made") );
|
||||
std::string barrier( w.keyVals.Find("barrier") );
|
||||
|
||||
//Save the name of the way if it has one, ref has precedence over name tag.
|
||||
if ( 0 < ref.length() )
|
||||
w.name = ref;
|
||||
else
|
||||
if ( 0 < name.length() )
|
||||
w.name = name;
|
||||
|
||||
if(junction == "roundabout") {
|
||||
w.roundabout = true;
|
||||
}
|
||||
|
||||
//Is the highway tag listed as usable way?
|
||||
if(0 < settings[highway]) {
|
||||
|
||||
if(0 != maxspeed)
|
||||
w.speed = maxspeed;
|
||||
else
|
||||
w.speed = settings[highway];
|
||||
w.useful = true;
|
||||
|
||||
//Okay, do we have access to that way?
|
||||
if(0 < access.size()) { //fastest way to check for non-empty string
|
||||
//If access is forbidden, we don't want to route there.
|
||||
if(access == "private" || access == "no" || access == "agricultural" || access == "forestry" || access == "delivery") { //Todo: this is still hard coded
|
||||
w.access = false;
|
||||
}
|
||||
}
|
||||
|
||||
if("yes" == accessClass)
|
||||
w.access = true;
|
||||
else if("no" == accessClass)
|
||||
w.access = false;
|
||||
|
||||
//Let's process oneway property, if speed profile obeys to it
|
||||
if(oneway != "no" && oneway != "false" && oneway != "0" && settings.obeyOneways) {
|
||||
//if oneway tag is in forward direction or if actually roundabout
|
||||
if(junction == "roundabout" || oneway == "yes" || oneway == "true" || oneway == "1") {
|
||||
w.direction = _Way::oneway;
|
||||
} else {
|
||||
if( oneway == "-1")
|
||||
w.direction = _Way::opposite;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//Is the route tag listed as usable way in the profile?
|
||||
if(settings[route] > 0 || settings[man_made] > 0) {
|
||||
w.useful = true;
|
||||
w.direction = _Way::oneway;
|
||||
w.speed = settings[route];
|
||||
w.direction = _Way::bidirectional;
|
||||
}
|
||||
}
|
||||
if ( w.useful && w.access && (1 < w.path.size()) ) { //Only true if the way is specified by the speed profile
|
||||
//TODO: type is not set, perhaps use a bimap'ed speed profile to do set the type correctly?
|
||||
w.type = 1;
|
||||
|
||||
//Get the unique identifier for the street name
|
||||
StringMap::const_iterator strit = stringMap->find(w.name);
|
||||
if(strit == stringMap->end()) {
|
||||
w.nameID = externalMemory->nameVector.size();
|
||||
externalMemory->nameVector.push_back(w.name);
|
||||
stringMap->insert(StringMap::value_type(w.name, w.nameID));
|
||||
} else {
|
||||
w.nameID = strit->second;
|
||||
}
|
||||
|
||||
GUARANTEE(w.id != UINT_MAX, "found way with unknown type");
|
||||
GUARANTEE(-1 != w.speed, "found way with unknown speed");
|
||||
|
||||
if ( w.direction == _Way::opposite ){
|
||||
std::reverse( w.path.begin(), w.path.end() );
|
||||
}
|
||||
|
||||
for(vector< NodeID >::size_type n = 0; n < w.path.size()-1; ++n) {
|
||||
externalMemory->allEdges.push_back(_Edge(w.path[n], w.path[n+1], w.type, w.direction, w.speed, w.nameID, w.roundabout));
|
||||
externalMemory->usedNodeIDs.push_back(w.path[n]);
|
||||
}
|
||||
externalMemory->usedNodeIDs.push_back(w.path[w.path.size()-1]);
|
||||
|
||||
//The following information is needed to identify start and end segments of restrictions
|
||||
externalMemory->wayStartEndVector.push_back(_WayIDStartAndEndEdge(w.id, w.path[0], w.path[1], w.path[w.path.size()-2], w.path[w.path.size()-1]));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* EXTRACTORCALLBACKS_H_ */
|
||||
@@ -1,359 +0,0 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
|
||||
#ifndef EXTRACTORSTRUCTS_H_
|
||||
#define EXTRACTORSTRUCTS_H_
|
||||
|
||||
#include <climits>
|
||||
#include <string>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include "../typedefs.h"
|
||||
#include "Util.h"
|
||||
|
||||
struct _PathData {
|
||||
_PathData(NodeID no, unsigned na, unsigned tu, unsigned dur) : node(no), nameID(na), durationOfSegment(dur), turnInstruction(tu) { }
|
||||
NodeID node;
|
||||
unsigned nameID;
|
||||
unsigned durationOfSegment;
|
||||
short turnInstruction;
|
||||
};
|
||||
|
||||
typedef boost::unordered_map<std::string, NodeID> StringMap;
|
||||
|
||||
struct _Node : NodeInfo{
|
||||
_Node(int _lat, int _lon, unsigned int _id) : NodeInfo(_lat, _lon, _id) {}
|
||||
_Node() {}
|
||||
|
||||
static _Node min_value() {
|
||||
return _Node(0,0,0);
|
||||
}
|
||||
static _Node max_value() {
|
||||
return _Node((numeric_limits<int>::max)(), (numeric_limits<int>::max)(), (numeric_limits<unsigned int>::max)());
|
||||
}
|
||||
NodeID key() const {
|
||||
return id;
|
||||
}
|
||||
};
|
||||
|
||||
struct _Coordinate {
|
||||
int lat;
|
||||
int lon;
|
||||
_Coordinate () : lat(INT_MIN), lon(INT_MIN) {}
|
||||
_Coordinate (int t, int n) : lat(t) , lon(n) {}
|
||||
void Reset() {
|
||||
lat = INT_MIN;
|
||||
lon = INT_MIN;
|
||||
}
|
||||
bool isSet() const {
|
||||
return (INT_MIN != lat) && (INT_MIN != lon);
|
||||
}
|
||||
};
|
||||
|
||||
inline ostream & operator<<(ostream & out, const _Coordinate & c){
|
||||
out << "(" << c.lat << "," << c.lon << ")";
|
||||
return out;
|
||||
}
|
||||
|
||||
struct _Way {
|
||||
_Way() : id(UINT_MAX), nameID(UINT_MAX) {
|
||||
|
||||
direction = _Way::notSure;
|
||||
speed = -1;
|
||||
type = -1;
|
||||
useful = false;
|
||||
access = true;
|
||||
roundabout = false;
|
||||
}
|
||||
|
||||
enum {
|
||||
notSure = 0, oneway, bidirectional, opposite
|
||||
} direction;
|
||||
unsigned id;
|
||||
unsigned nameID;
|
||||
std::string name;
|
||||
double speed;
|
||||
short type;
|
||||
bool useful;
|
||||
bool access;
|
||||
bool roundabout;
|
||||
std::vector< NodeID > path;
|
||||
HashTable<std::string, std::string> keyVals;
|
||||
};
|
||||
|
||||
struct _Address {
|
||||
_Address() {}
|
||||
_Address(_Node n, std::string h, std::string str, std::string sta, std::string p, std::string ci, std::string co) {
|
||||
node = n;
|
||||
housenumber = h;
|
||||
street = str;
|
||||
state = sta;
|
||||
postcode = p;
|
||||
city = ci;
|
||||
country = co;
|
||||
}
|
||||
_Node node;
|
||||
std::string housenumber;
|
||||
std::string street;
|
||||
std::string state;
|
||||
std::string postcode;
|
||||
std::string city;
|
||||
std::string country;
|
||||
};
|
||||
|
||||
struct _Relation {
|
||||
_Relation() : type(unknown){}
|
||||
enum {
|
||||
unknown = 0, ferry, turnRestriction
|
||||
} type;
|
||||
HashTable<std::string, std::string> keyVals;
|
||||
};
|
||||
|
||||
struct _Edge {
|
||||
_Edge() : start(0), target(0), type(0), direction(0), speed(0), nameID(0), isRoundabout(false) {};
|
||||
_Edge(NodeID s, NodeID t) : start(s), target(t), type(0), direction(0), speed(0), nameID(0), isRoundabout(false) { }
|
||||
_Edge(NodeID s, NodeID t, short tp, short d, double sp): start(s), target(t), type(tp), direction(d), speed(sp), nameID(0), isRoundabout(false) { }
|
||||
_Edge(NodeID s, NodeID t, short tp, short d, double sp, unsigned nid, bool isra): start(s), target(t), type(tp), direction(d), speed(sp), nameID(nid), isRoundabout(isra) { }
|
||||
NodeID start;
|
||||
NodeID target;
|
||||
short type;
|
||||
short direction;
|
||||
double speed;
|
||||
unsigned nameID;
|
||||
bool isRoundabout;
|
||||
|
||||
_Coordinate startCoord;
|
||||
_Coordinate targetCoord;
|
||||
|
||||
static _Edge min_value() {
|
||||
return _Edge(0,0);
|
||||
}
|
||||
static _Edge max_value() {
|
||||
return _Edge((numeric_limits<unsigned>::max)(), (numeric_limits<unsigned>::max)());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct _Restriction {
|
||||
NodeID viaNode;
|
||||
NodeID fromNode;
|
||||
NodeID toNode;
|
||||
struct bits { //mostly unused
|
||||
char isOnly:1;
|
||||
char unused1:1;
|
||||
char unused2:1;
|
||||
char unused3:1;
|
||||
char unused4:1;
|
||||
char unused5:1;
|
||||
char unused6:1;
|
||||
char unused7:1;
|
||||
} flags;
|
||||
|
||||
_Restriction(NodeID vn) : viaNode(vn), fromNode(UINT_MAX), toNode(UINT_MAX) { }
|
||||
_Restriction(bool isOnly = false) : viaNode(UINT_MAX), fromNode(UINT_MAX), toNode(UINT_MAX) {
|
||||
flags.isOnly = isOnly;
|
||||
}
|
||||
};
|
||||
|
||||
inline bool CmpRestrictionByFrom ( _Restriction a, _Restriction b) { return (a.fromNode < b.fromNode); }
|
||||
|
||||
struct _RawRestrictionContainer {
|
||||
_Restriction restriction;
|
||||
EdgeID fromWay;
|
||||
EdgeID toWay;
|
||||
unsigned viaWay;
|
||||
|
||||
_RawRestrictionContainer(EdgeID f, EdgeID t, NodeID vn, unsigned vw) : fromWay(f), toWay(t), viaWay(vw) { restriction.viaNode = vn;}
|
||||
_RawRestrictionContainer(bool isOnly = false) : fromWay(UINT_MAX), toWay(UINT_MAX), viaWay(UINT_MAX) { restriction.flags.isOnly = isOnly;}
|
||||
|
||||
static _RawRestrictionContainer min_value() {
|
||||
return _RawRestrictionContainer((numeric_limits<unsigned>::min)(), (numeric_limits<unsigned>::min)(), (numeric_limits<unsigned>::min)(), (numeric_limits<unsigned>::min)());
|
||||
}
|
||||
static _RawRestrictionContainer max_value() {
|
||||
return _RawRestrictionContainer((numeric_limits<unsigned>::max)(), (numeric_limits<unsigned>::max)(), (numeric_limits<unsigned>::max)(), (numeric_limits<unsigned>::max)());
|
||||
}
|
||||
};
|
||||
|
||||
struct CmpRestrictionContainerByFrom: public std::binary_function<_RawRestrictionContainer, _RawRestrictionContainer, bool> {
|
||||
typedef _RawRestrictionContainer value_type;
|
||||
bool operator () (const _RawRestrictionContainer & a, const _RawRestrictionContainer & b) const {
|
||||
return a.fromWay < b.fromWay;
|
||||
}
|
||||
value_type max_value() {
|
||||
return _RawRestrictionContainer::max_value();
|
||||
}
|
||||
value_type min_value() {
|
||||
return _RawRestrictionContainer::min_value();
|
||||
}
|
||||
};
|
||||
|
||||
struct CmpRestrictionContainerByTo: public std::binary_function<_RawRestrictionContainer, _RawRestrictionContainer, bool> {
|
||||
typedef _RawRestrictionContainer value_type;
|
||||
bool operator () (const _RawRestrictionContainer & a, const _RawRestrictionContainer & b) const {
|
||||
return a.toWay < b.toWay;
|
||||
}
|
||||
value_type max_value() {
|
||||
return _RawRestrictionContainer::max_value();
|
||||
}
|
||||
value_type min_value() {
|
||||
return _RawRestrictionContainer::min_value();
|
||||
}
|
||||
};
|
||||
|
||||
struct _WayIDStartAndEndEdge {
|
||||
unsigned wayID;
|
||||
NodeID firstStart;
|
||||
NodeID firstTarget;
|
||||
NodeID lastStart;
|
||||
NodeID lastTarget;
|
||||
_WayIDStartAndEndEdge() : wayID(UINT_MAX), firstStart(UINT_MAX), firstTarget(UINT_MAX), lastStart(UINT_MAX), lastTarget(UINT_MAX) {}
|
||||
_WayIDStartAndEndEdge(unsigned w, NodeID fs, NodeID ft, NodeID ls, NodeID lt) : wayID(w), firstStart(fs), firstTarget(ft), lastStart(ls), lastTarget(lt) {}
|
||||
|
||||
static _WayIDStartAndEndEdge min_value() {
|
||||
return _WayIDStartAndEndEdge((numeric_limits<unsigned>::min)(), (numeric_limits<unsigned>::min)(), (numeric_limits<unsigned>::min)(), (numeric_limits<unsigned>::min)(), (numeric_limits<unsigned>::min)());
|
||||
}
|
||||
static _WayIDStartAndEndEdge max_value() {
|
||||
return _WayIDStartAndEndEdge((numeric_limits<unsigned>::max)(), (numeric_limits<unsigned>::max)(), (numeric_limits<unsigned>::max)(), (numeric_limits<unsigned>::max)(), (numeric_limits<unsigned>::max)());
|
||||
}
|
||||
};
|
||||
|
||||
struct CmpWayStartAndEnd : public std::binary_function<_WayIDStartAndEndEdge, _WayIDStartAndEndEdge, bool> {
|
||||
typedef _WayIDStartAndEndEdge value_type;
|
||||
bool operator () (const _WayIDStartAndEndEdge & a, const _WayIDStartAndEndEdge & b) const {
|
||||
return a.wayID < b.wayID;
|
||||
}
|
||||
value_type max_value() {
|
||||
return _WayIDStartAndEndEdge::max_value();
|
||||
}
|
||||
value_type min_value() {
|
||||
return _WayIDStartAndEndEdge::min_value();
|
||||
}
|
||||
};
|
||||
|
||||
struct Settings {
|
||||
Settings() : obeyPollards(true), obeyOneways(true), useRestrictions(true), accessTag("motorcar") {}
|
||||
StringMap speedProfile;
|
||||
int operator[](const string & param) const {
|
||||
if(speedProfile.find(param) == speedProfile.end())
|
||||
return 0;
|
||||
else
|
||||
return speedProfile.at(param);
|
||||
}
|
||||
bool obeyPollards;
|
||||
bool obeyOneways;
|
||||
bool useRestrictions;
|
||||
string accessTag;
|
||||
|
||||
};
|
||||
|
||||
struct Cmp : public std::binary_function<NodeID, NodeID, bool> {
|
||||
typedef NodeID value_type;
|
||||
bool operator () (const NodeID & a, const NodeID & b) const {
|
||||
return a < b;
|
||||
}
|
||||
value_type max_value() {
|
||||
return 0xffffffff;
|
||||
}
|
||||
value_type min_value() {
|
||||
return 0x0;
|
||||
}
|
||||
};
|
||||
|
||||
struct CmpNodeByID : public std::binary_function<_Node, _Node, bool> {
|
||||
typedef _Node value_type;
|
||||
bool operator () (const _Node & a, const _Node & b) const {
|
||||
return a.id < b.id;
|
||||
}
|
||||
value_type max_value() {
|
||||
return _Node::max_value();
|
||||
}
|
||||
value_type min_value() {
|
||||
return _Node::min_value();
|
||||
}
|
||||
};
|
||||
|
||||
struct CmpEdgeByStartID : public std::binary_function<_Edge, _Edge, bool>
|
||||
{
|
||||
typedef _Edge value_type;
|
||||
bool operator () (const _Edge & a, const _Edge & b) const {
|
||||
return a.start < b.start;
|
||||
}
|
||||
value_type max_value() {
|
||||
return _Edge::max_value();
|
||||
}
|
||||
value_type min_value() {
|
||||
return _Edge::min_value();
|
||||
}
|
||||
};
|
||||
|
||||
struct CmpEdgeByTargetID : public std::binary_function<_Edge, _Edge, bool>
|
||||
{
|
||||
typedef _Edge value_type;
|
||||
bool operator () (const _Edge & a, const _Edge & b) const
|
||||
{
|
||||
return a.target < b.target;
|
||||
}
|
||||
value_type max_value()
|
||||
{
|
||||
return _Edge::max_value();
|
||||
}
|
||||
value_type min_value()
|
||||
{
|
||||
return _Edge::min_value();
|
||||
}
|
||||
};
|
||||
|
||||
inline double ApproximateDistance( const int lat1, const int lon1, const int lat2, const int lon2 ) {
|
||||
assert(lat1 != INT_MIN);
|
||||
assert(lon1 != INT_MIN);
|
||||
assert(lat2 != INT_MIN);
|
||||
assert(lon2 != INT_MIN);
|
||||
static const double DEG_TO_RAD = 0.017453292519943295769236907684886;
|
||||
///Earth's quatratic mean radius for WGS-84
|
||||
static const double EARTH_RADIUS_IN_METERS = 6372797.560856;
|
||||
double latitudeArc = ( lat1/100000. - lat2/100000. ) * DEG_TO_RAD;
|
||||
double longitudeArc = ( lon1/100000. - lon2/100000. ) * DEG_TO_RAD;
|
||||
double latitudeH = sin( latitudeArc * 0.5 );
|
||||
latitudeH *= latitudeH;
|
||||
double lontitudeH = sin( longitudeArc * 0.5 );
|
||||
lontitudeH *= lontitudeH;
|
||||
double tmp = cos( lat1/100000. * DEG_TO_RAD ) * cos( lat2/100000. * DEG_TO_RAD );
|
||||
double distanceArc = 2.0 * asin( sqrt( latitudeH + tmp * lontitudeH ) );
|
||||
return EARTH_RADIUS_IN_METERS * distanceArc;
|
||||
}
|
||||
|
||||
inline double ApproximateDistance(const _Coordinate &c1, const _Coordinate &c2) {
|
||||
return ApproximateDistance( c1.lat, c1.lon, c2.lat, c2.lon );
|
||||
}
|
||||
|
||||
inline string GetRandomString() {
|
||||
char s[128];
|
||||
static const char alphanum[] =
|
||||
"0123456789"
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz";
|
||||
|
||||
for (int i = 0; i < 127; ++i) {
|
||||
s[i] = alphanum[rand() % (sizeof(alphanum) - 1)];
|
||||
}
|
||||
s[127] = 0;
|
||||
return string(s);
|
||||
}
|
||||
|
||||
#endif /* EXTRACTORSTRUCTS_H_ */
|
||||
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
|
||||
#ifndef GRIDEDGE_H_
|
||||
#define GRIDEDGE_H_
|
||||
|
||||
struct _GridEdge {
|
||||
_GridEdge(NodeID n, NodeID na, int w, _Coordinate sc, _Coordinate tc) : edgeBasedNode(n), nameID(na), weight(w), startCoord(sc), targetCoord(tc) {}
|
||||
_GridEdge() : edgeBasedNode(UINT_MAX), nameID(UINT_MAX), weight(INT_MAX) {}
|
||||
NodeID edgeBasedNode;
|
||||
NodeID nameID;
|
||||
int weight;
|
||||
_Coordinate startCoord;
|
||||
_Coordinate targetCoord;
|
||||
};
|
||||
|
||||
struct GridEntry {
|
||||
GridEntry() : fileIndex(UINT_MAX), ramIndex(UINT_MAX){}
|
||||
GridEntry(_GridEdge e, unsigned f, unsigned r) : edge(e), fileIndex(f), ramIndex(r) {}
|
||||
_GridEdge edge;
|
||||
unsigned fileIndex;
|
||||
unsigned ramIndex;
|
||||
bool operator< ( const GridEntry& right ) const {
|
||||
return (edge.edgeBasedNode < right.edge.edgeBasedNode);
|
||||
}
|
||||
bool operator==( const GridEntry& right ) const {
|
||||
return right.edge.edgeBasedNode == edge.edgeBasedNode;
|
||||
}
|
||||
};
|
||||
|
||||
struct CompareGridEdgeDataByFileIndex
|
||||
{
|
||||
bool operator () (const GridEntry & a, const GridEntry & b) const
|
||||
{
|
||||
return a.fileIndex < b.fileIndex;
|
||||
}
|
||||
};
|
||||
|
||||
struct CompareGridEdgeDataByRamIndex
|
||||
{
|
||||
typedef GridEntry value_type;
|
||||
|
||||
bool operator () (const GridEntry & a, const GridEntry & b) const
|
||||
{
|
||||
return a.ramIndex < b.ramIndex;
|
||||
}
|
||||
value_type max_value()
|
||||
{
|
||||
GridEntry e;
|
||||
e.ramIndex = (1024*1024) - 1;
|
||||
return e;
|
||||
}
|
||||
value_type min_value()
|
||||
{
|
||||
GridEntry e;
|
||||
e.ramIndex = 0;
|
||||
return e;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* GRIDEDGE_H_ */
|
||||
@@ -1,78 +1,65 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
Created on: 18.11.2010
|
||||
Author: dennis
|
||||
*/
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef HASHTABLE_H_
|
||||
#define HASHTABLE_H_
|
||||
*/
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
#ifndef HASH_TABLE_H
|
||||
#define HASH_TABLE_H
|
||||
|
||||
template<typename keyT, typename valueT>
|
||||
class HashTable {
|
||||
typedef boost::unordered_map<keyT, valueT> MyHashTable;
|
||||
public:
|
||||
typedef typename boost::unordered_map<keyT, valueT>::const_iterator MyIterator;
|
||||
typedef MyIterator iterator;
|
||||
HashTable() { }
|
||||
HashTable(const unsigned size) {
|
||||
table.resize(size);
|
||||
}
|
||||
void Add(const keyT& key, const valueT& value){
|
||||
table[key] = value;
|
||||
}
|
||||
void Set(const keyT& key, const valueT& value){
|
||||
table[key] = value;
|
||||
}
|
||||
valueT Find(const keyT& key) {
|
||||
if(table.find(key) == table.end())
|
||||
return valueT();
|
||||
return table.find(key)->second;
|
||||
#include <unordered_map>
|
||||
|
||||
template <typename Key, typename Value> class HashTable : public std::unordered_map<Key, Value>
|
||||
{
|
||||
private:
|
||||
typedef std::unordered_map<Key, Value> super;
|
||||
|
||||
public:
|
||||
HashTable() : super() {}
|
||||
|
||||
explicit HashTable(const unsigned size) : super(size) {}
|
||||
|
||||
inline void Add(Key const &key, Value const &value) { super::emplace(key, value); }
|
||||
|
||||
inline const Value Find(Key const &key) const
|
||||
{
|
||||
auto iter = super::find(key);
|
||||
if (iter == super::end())
|
||||
{
|
||||
return Value();
|
||||
}
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
bool Holds(const keyT& key) {
|
||||
if(table.find(key) == table.end())
|
||||
inline const bool Holds(Key const &key) const
|
||||
{
|
||||
if (super::find(key) == super::end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void EraseAll() {
|
||||
if(table.size() > 0)
|
||||
table.clear();
|
||||
}
|
||||
|
||||
inline
|
||||
valueT & operator[] (keyT key) {
|
||||
return table[key];
|
||||
}
|
||||
unsigned Size() const {
|
||||
return table.size();
|
||||
}
|
||||
MyIterator begin() const {
|
||||
return table.begin();
|
||||
}
|
||||
MyIterator end() const {
|
||||
return table.end();
|
||||
}
|
||||
private:
|
||||
MyHashTable table;
|
||||
};
|
||||
|
||||
#endif /* HASHTABLE_H_ */
|
||||
#endif /* HASH_TABLE_H */
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include "HilbertValue.h"
|
||||
|
||||
#include <osrm/Coordinate.h>
|
||||
|
||||
uint64_t HilbertCode::operator()(const FixedPointCoordinate ¤t_coordinate) const
|
||||
{
|
||||
unsigned location[2];
|
||||
location[0] = current_coordinate.lat + (90 * COORDINATE_PRECISION);
|
||||
location[1] = current_coordinate.lon + (180 * COORDINATE_PRECISION);
|
||||
|
||||
TransposeCoordinate(location);
|
||||
return BitInterleaving(location[0], location[1]);
|
||||
}
|
||||
|
||||
uint64_t HilbertCode::BitInterleaving(const uint32_t latitude, const uint32_t longitude) const
|
||||
{
|
||||
uint64_t result = 0;
|
||||
for (int8_t index = 31; index >= 0; --index)
|
||||
{
|
||||
result |= (latitude >> index) & 1;
|
||||
result <<= 1;
|
||||
result |= (longitude >> index) & 1;
|
||||
if (0 != index)
|
||||
{
|
||||
result <<= 1;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void HilbertCode::TransposeCoordinate(uint32_t *X) const
|
||||
{
|
||||
uint32_t M = 1 << (32 - 1), P, Q, t;
|
||||
int i;
|
||||
// Inverse undo
|
||||
for (Q = M; Q > 1; Q >>= 1)
|
||||
{
|
||||
P = Q - 1;
|
||||
for (i = 0; i < 2; ++i)
|
||||
{
|
||||
|
||||
const bool condition = (X[i] & Q);
|
||||
if (condition)
|
||||
{
|
||||
X[0] ^= P; // invert
|
||||
}
|
||||
else
|
||||
{
|
||||
t = (X[0] ^ X[i]) & P;
|
||||
X[0] ^= t;
|
||||
X[i] ^= t;
|
||||
}
|
||||
} // exchange
|
||||
}
|
||||
// Gray encode
|
||||
for (i = 1; i < 2; ++i)
|
||||
{
|
||||
X[i] ^= X[i - 1];
|
||||
}
|
||||
t = 0;
|
||||
for (Q = M; Q > 1; Q >>= 1)
|
||||
{
|
||||
const bool condition = (X[2 - 1] & Q);
|
||||
if (condition)
|
||||
{
|
||||
t ^= Q - 1;
|
||||
}
|
||||
} // check if this for loop is wrong
|
||||
for (i = 0; i < 2; ++i)
|
||||
{
|
||||
X[i] ^= t;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef HILBERTVALUE_H_
|
||||
#define HILBERTVALUE_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// computes a 64 bit value that corresponds to the hilbert space filling curve
|
||||
|
||||
struct FixedPointCoordinate;
|
||||
|
||||
class HilbertCode
|
||||
{
|
||||
public:
|
||||
uint64_t operator()(const FixedPointCoordinate ¤t_coordinate) const;
|
||||
HilbertCode() {}
|
||||
HilbertCode(const HilbertCode &) = delete;
|
||||
|
||||
private:
|
||||
inline uint64_t BitInterleaving(const uint32_t a, const uint32_t b) const;
|
||||
inline void TransposeCoordinate(uint32_t *X) const;
|
||||
};
|
||||
|
||||
#endif /* HILBERTVALUE_H_ */
|
||||
@@ -1,37 +1,51 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
|
||||
#ifndef EDGE_H
|
||||
#define EDGE_H
|
||||
#ifndef IMPORT_EDGE_H
|
||||
#define IMPORT_EDGE_H
|
||||
|
||||
#include <cassert>
|
||||
#include "../Util/OSRMException.h"
|
||||
#include "../typedefs.h"
|
||||
|
||||
class NodeBasedEdge {
|
||||
public:
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
bool operator< (const NodeBasedEdge& e) const {
|
||||
if (source() == e.source()) {
|
||||
if (target() == e.target()) {
|
||||
if (weight() == e.weight()) {
|
||||
return (isForward() && isBackward() &&
|
||||
((! e.isForward()) || (! e.isBackward())));
|
||||
class NodeBasedEdge
|
||||
{
|
||||
|
||||
public:
|
||||
bool operator<(const NodeBasedEdge &e) const
|
||||
{
|
||||
if (source() == e.source())
|
||||
{
|
||||
if (target() == e.target())
|
||||
{
|
||||
if (weight() == e.weight())
|
||||
{
|
||||
return (isForward() && isBackward() && ((!e.isForward()) || (!e.isBackward())));
|
||||
}
|
||||
return (weight() < e.weight());
|
||||
}
|
||||
@@ -40,43 +54,77 @@ public:
|
||||
return (source() < e.source());
|
||||
}
|
||||
|
||||
/** Default constructor. target and weight are set to 0.*/
|
||||
NodeBasedEdge() :
|
||||
_source(0), _target(0), _name(0), _weight(0), forward(0), backward(0), _type(0), _roundabout(false) { assert(false); } //shall not be used.
|
||||
explicit NodeBasedEdge(NodeID s,
|
||||
NodeID t,
|
||||
NodeID n,
|
||||
EdgeWeight w,
|
||||
bool f,
|
||||
bool b,
|
||||
short ty,
|
||||
bool ra,
|
||||
bool ig,
|
||||
bool ar,
|
||||
bool cf,
|
||||
bool is_split)
|
||||
: _source(s), _target(t), _name(n), _weight(w), _type(ty), forward(f), backward(b),
|
||||
_roundabout(ra), _ignoreInGrid(ig), _accessRestricted(ar), _contraFlow(cf),
|
||||
is_split(is_split)
|
||||
{
|
||||
if (ty < 0)
|
||||
{
|
||||
throw OSRMException("negative edge type");
|
||||
}
|
||||
}
|
||||
|
||||
explicit NodeBasedEdge(NodeID s, NodeID t, NodeID n, EdgeWeight w, bool f, bool b, short ty, bool ra) :
|
||||
_source(s), _target(t), _name(n), _weight(w), forward(f), backward(b), _type(ty), _roundabout(ra) { assert(ty >= 0); }
|
||||
|
||||
NodeID target() const {return _target; }
|
||||
NodeID source() const {return _source; }
|
||||
NodeID target() const { return _target; }
|
||||
NodeID source() const { return _source; }
|
||||
NodeID name() const { return _name; }
|
||||
EdgeWeight weight() const {return _weight; }
|
||||
|
||||
short type() const { assert(_type >= 0); return _type; }
|
||||
EdgeWeight weight() const { return _weight; }
|
||||
short type() const
|
||||
{
|
||||
BOOST_ASSERT_MSG(_type >= 0, "type of ImportEdge invalid");
|
||||
return _type;
|
||||
}
|
||||
bool isBackward() const { return backward; }
|
||||
bool isForward() const { return forward; }
|
||||
bool isLocatable() const { return _type != 14; }
|
||||
bool isRoundabout() const { return _roundabout; }
|
||||
bool ignoreInGrid() const { return _ignoreInGrid; }
|
||||
bool isAccessRestricted() const { return _accessRestricted; }
|
||||
bool isContraFlow() const { return _contraFlow; }
|
||||
bool IsSplit() const { return is_split; }
|
||||
|
||||
// TODO: names need to be fixed.
|
||||
NodeID _source;
|
||||
NodeID _target;
|
||||
NodeID _name;
|
||||
EdgeWeight _weight;
|
||||
bool forward;
|
||||
bool backward;
|
||||
short _type;
|
||||
bool _roundabout;
|
||||
bool forward : 1;
|
||||
bool backward : 1;
|
||||
bool _roundabout : 1;
|
||||
bool _ignoreInGrid : 1;
|
||||
bool _accessRestricted : 1;
|
||||
bool _contraFlow : 1;
|
||||
bool is_split : 1;
|
||||
|
||||
private:
|
||||
NodeBasedEdge() {}
|
||||
};
|
||||
|
||||
class EdgeBasedEdge {
|
||||
public:
|
||||
class EdgeBasedEdge
|
||||
{
|
||||
|
||||
bool operator< (const EdgeBasedEdge& e) const {
|
||||
if (source() == e.source()) {
|
||||
if (target() == e.target()) {
|
||||
if (weight() == e.weight()) {
|
||||
return (isForward() && isBackward() &&
|
||||
((! e.isForward()) || (! e.isBackward())));
|
||||
public:
|
||||
bool operator<(const EdgeBasedEdge &e) const
|
||||
{
|
||||
if (source() == e.source())
|
||||
{
|
||||
if (target() == e.target())
|
||||
{
|
||||
if (weight() == e.weight())
|
||||
{
|
||||
return (isForward() && isBackward() && ((!e.isForward()) || (!e.isBackward())));
|
||||
}
|
||||
return (weight() < e.weight());
|
||||
}
|
||||
@@ -85,52 +133,46 @@ public:
|
||||
return (source() < e.source());
|
||||
}
|
||||
|
||||
template<class EdgeT>
|
||||
EdgeBasedEdge(const EdgeT & myEdge ) :
|
||||
_source(myEdge.source),
|
||||
_target(myEdge.target),
|
||||
_via(myEdge.data.via),
|
||||
_nameID1(myEdge.data.nameID),
|
||||
_weight(myEdge.data.distance),
|
||||
_forward(myEdge.data.forward),
|
||||
_backward(myEdge.data.backward),
|
||||
_turnInstruction(myEdge.data.turnInstruction) { }
|
||||
template <class EdgeT>
|
||||
explicit EdgeBasedEdge(const EdgeT &myEdge)
|
||||
: m_source(myEdge.source), m_target(myEdge.target), m_edgeID(myEdge.data.via),
|
||||
m_weight(myEdge.data.distance), m_forward(myEdge.data.forward),
|
||||
m_backward(myEdge.data.backward)
|
||||
{
|
||||
}
|
||||
|
||||
/** Default constructor. target and weight are set to 0.*/
|
||||
EdgeBasedEdge() :
|
||||
_source(0), _target(0), _via(0), _nameID1(0), _weight(0), _forward(0), _backward(0), _turnInstruction(0) { assert(false); } //shall not be used.
|
||||
EdgeBasedEdge()
|
||||
: m_source(0), m_target(0), m_edgeID(0), m_weight(0), m_forward(false), m_backward(false)
|
||||
{
|
||||
}
|
||||
|
||||
explicit EdgeBasedEdge(NodeID s, NodeID t, NodeID v, unsigned n1, EdgeWeight w, bool f, bool b, short ty) :
|
||||
_source(s), _target(t), _via(v), _nameID1(n1), _weight(w), _forward(f), _backward(b), _turnInstruction(ty) { assert(ty >= 0); }
|
||||
explicit EdgeBasedEdge(const NodeID s,
|
||||
const NodeID t,
|
||||
const NodeID v,
|
||||
const EdgeWeight w,
|
||||
const bool f,
|
||||
const bool b)
|
||||
: m_source(s), m_target(t), m_edgeID(v), m_weight(w), m_forward(f), m_backward(b)
|
||||
{
|
||||
}
|
||||
|
||||
NodeID target() const {return _target; }
|
||||
NodeID source() const {return _source; }
|
||||
EdgeWeight weight() const {return _weight; }
|
||||
NodeID via() const { return _via; }
|
||||
short turnInstruction() const { assert(_turnInstruction >= 0); return _turnInstruction; }
|
||||
bool isBackward() const { return _backward; }
|
||||
bool isForward() const { return _forward; }
|
||||
NodeID target() const { return m_target; }
|
||||
NodeID source() const { return m_source; }
|
||||
EdgeWeight weight() const { return m_weight; }
|
||||
NodeID id() const { return m_edgeID; }
|
||||
bool isBackward() const { return m_backward; }
|
||||
bool isForward() const { return m_forward; }
|
||||
|
||||
unsigned getNameIDOfTurnTarget() const { return _nameID1; }
|
||||
|
||||
NodeID _source;
|
||||
NodeID _target;
|
||||
NodeID _via;
|
||||
unsigned _nameID1;
|
||||
EdgeWeight _weight;
|
||||
bool _forward;
|
||||
bool _backward;
|
||||
short _turnInstruction;
|
||||
|
||||
};
|
||||
|
||||
struct MinimalEdgeData {
|
||||
public:
|
||||
EdgeWeight distance;
|
||||
bool forward;
|
||||
bool backward;
|
||||
private:
|
||||
NodeID m_source;
|
||||
NodeID m_target;
|
||||
NodeID m_edgeID;
|
||||
EdgeWeight m_weight : 30;
|
||||
bool m_forward : 1;
|
||||
bool m_backward : 1;
|
||||
};
|
||||
|
||||
typedef NodeBasedEdge ImportEdge;
|
||||
|
||||
#endif // EDGE_H
|
||||
#endif /* IMPORT_EDGE_H */
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef IMPORTNODE_H_
|
||||
#define IMPORTNODE_H_
|
||||
|
||||
#include "QueryNode.h"
|
||||
#include "../DataStructures/HashTable.h"
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
struct ExternalMemoryNode : NodeInfo
|
||||
{
|
||||
ExternalMemoryNode(int lat, int lon, unsigned int id, bool bollard, bool traffic_light)
|
||||
: NodeInfo(lat, lon, id), bollard(bollard), trafficLight(traffic_light)
|
||||
{
|
||||
}
|
||||
|
||||
ExternalMemoryNode() : bollard(false), trafficLight(false) {}
|
||||
|
||||
static ExternalMemoryNode min_value() { return ExternalMemoryNode(0, 0, 0, false, false); }
|
||||
|
||||
static ExternalMemoryNode max_value()
|
||||
{
|
||||
return ExternalMemoryNode(std::numeric_limits<int>::max(),
|
||||
std::numeric_limits<int>::max(),
|
||||
std::numeric_limits<unsigned>::max(),
|
||||
false,
|
||||
false);
|
||||
}
|
||||
|
||||
NodeID key() const { return id; }
|
||||
|
||||
bool bollard;
|
||||
bool trafficLight;
|
||||
};
|
||||
|
||||
struct ImportNode : public ExternalMemoryNode
|
||||
{
|
||||
HashTable<std::string, std::string> keyVals;
|
||||
|
||||
inline void Clear()
|
||||
{
|
||||
keyVals.clear();
|
||||
lat = 0;
|
||||
lon = 0;
|
||||
id = 0;
|
||||
bollard = false;
|
||||
trafficLight = false;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* IMPORTNODE_H_ */
|
||||
@@ -1,94 +1,123 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
#ifndef INPUTREADERFACTORY_H
|
||||
#define INPUTREADERFACTORY_H
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef INPUT_READER_FACTORY_H
|
||||
#define INPUT_READER_FACTORY_H
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <bzlib.h>
|
||||
#include <libxml/xmlreader.h>
|
||||
|
||||
struct BZ2Context {
|
||||
FILE* file;
|
||||
BZFILE* bz2;
|
||||
struct BZ2Context
|
||||
{
|
||||
FILE *file;
|
||||
BZFILE *bz2;
|
||||
int error;
|
||||
int nUnused;
|
||||
char unused[BZ_MAX_UNUSED];
|
||||
};
|
||||
|
||||
int readFromBz2Stream( void* pointer, char* buffer, int len ) {
|
||||
void *unusedTmpVoid=NULL;
|
||||
char *unusedTmp=NULL;
|
||||
BZ2Context* context = (BZ2Context*) pointer;
|
||||
int readFromBz2Stream(void *pointer, char *buffer, int len)
|
||||
{
|
||||
void *unusedTmpVoid = NULL;
|
||||
char *unusedTmp = NULL;
|
||||
BZ2Context *context = (BZ2Context *)pointer;
|
||||
int read = 0;
|
||||
while(0 == read && !(BZ_STREAM_END == context->error && 0 == context->nUnused && feof(context->file))) {
|
||||
while (0 == read &&
|
||||
!(BZ_STREAM_END == context->error && 0 == context->nUnused && feof(context->file)))
|
||||
{
|
||||
read = BZ2_bzRead(&context->error, context->bz2, buffer, len);
|
||||
if(BZ_OK == context->error) {
|
||||
if (BZ_OK == context->error)
|
||||
{
|
||||
return read;
|
||||
} else if(BZ_STREAM_END == context->error) {
|
||||
}
|
||||
else if (BZ_STREAM_END == context->error)
|
||||
{
|
||||
BZ2_bzReadGetUnused(&context->error, context->bz2, &unusedTmpVoid, &context->nUnused);
|
||||
if(BZ_OK != context->error) { cerr << "Could not BZ2_bzReadGetUnused" << endl; exit(-1);};
|
||||
unusedTmp = (char*)unusedTmpVoid;
|
||||
for(int i=0;i<context->nUnused;i++) {
|
||||
BOOST_ASSERT_MSG(BZ_OK == context->error, "Could not BZ2_bzReadGetUnused");
|
||||
unusedTmp = (char *)unusedTmpVoid;
|
||||
for (int i = 0; i < context->nUnused; i++)
|
||||
{
|
||||
context->unused[i] = unusedTmp[i];
|
||||
}
|
||||
BZ2_bzReadClose(&context->error, context->bz2);
|
||||
if(BZ_OK != context->error) { cerr << "Could not BZ2_bzReadClose" << endl; exit(-1);};
|
||||
BOOST_ASSERT_MSG(BZ_OK == context->error, "Could not BZ2_bzReadClose");
|
||||
context->error = BZ_STREAM_END; // set to the stream end for next call to this function
|
||||
if(0 == context->nUnused && feof(context->file)) {
|
||||
if (0 == context->nUnused && feof(context->file))
|
||||
{
|
||||
return read;
|
||||
} else {
|
||||
context->bz2 = BZ2_bzReadOpen(&context->error, context->file, 0, 0, context->unused, context->nUnused);
|
||||
if(NULL == context->bz2){ cerr << "Could not open file" << endl; exit(-1);};
|
||||
}
|
||||
} else { cerr << "Could not read bz2 file" << endl; exit(-1); }
|
||||
else
|
||||
{
|
||||
context->bz2 = BZ2_bzReadOpen(
|
||||
&context->error, context->file, 0, 0, context->unused, context->nUnused);
|
||||
BOOST_ASSERT_MSG(NULL != context->bz2, "Could not open file");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT_MSG(false, "Could not read bz2 file");
|
||||
}
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
int closeBz2Stream( void *pointer )
|
||||
int closeBz2Stream(void *pointer)
|
||||
{
|
||||
BZ2Context* context = (BZ2Context*) pointer;
|
||||
fclose( context->file );
|
||||
BZ2Context *context = (BZ2Context *)pointer;
|
||||
fclose(context->file);
|
||||
delete context;
|
||||
return 0;
|
||||
}
|
||||
|
||||
xmlTextReaderPtr inputReaderFactory( const char* name )
|
||||
xmlTextReaderPtr inputReaderFactory(const char *name)
|
||||
{
|
||||
std::string inputName(name);
|
||||
|
||||
if(inputName.find(".osm.bz2")!=string::npos)
|
||||
if (inputName.find(".osm.bz2") != std::string::npos)
|
||||
{
|
||||
BZ2Context* context = new BZ2Context();
|
||||
BZ2Context *context = new BZ2Context();
|
||||
context->error = false;
|
||||
context->file = fopen( name, "r" );
|
||||
context->file = fopen(name, "r");
|
||||
int error;
|
||||
context->bz2 = BZ2_bzReadOpen( &error, context->file, 0, 0, context->unused, context->nUnused );
|
||||
if ( context->bz2 == NULL || context->file == NULL ) {
|
||||
context->bz2 =
|
||||
BZ2_bzReadOpen(&error, context->file, 0, 0, context->unused, context->nUnused);
|
||||
if (context->bz2 == NULL || context->file == NULL)
|
||||
{
|
||||
delete context;
|
||||
return NULL;
|
||||
}
|
||||
return xmlReaderForIO( readFromBz2Stream, closeBz2Stream, (void*) context, NULL, NULL, 0 );
|
||||
} else {
|
||||
return xmlReaderForIO(readFromBz2Stream, closeBz2Stream, (void *)context, NULL, NULL, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
return xmlNewTextReaderFilename(name);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // INPUTREADERFACTORY_H
|
||||
#endif // INPUT_READER_FACTORY_H
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
// based on https://svn.apache.org/repos/asf/mesos/tags/release-0.9.0-incubating-RC0/src/common/json.hpp
|
||||
|
||||
#ifndef JSON_CONTAINER_H
|
||||
#define JSON_CONTAINER_H
|
||||
|
||||
#include "../Util/StringUtil.h"
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace JSON
|
||||
{
|
||||
|
||||
struct String;
|
||||
struct Number;
|
||||
struct Object;
|
||||
struct Array;
|
||||
struct True;
|
||||
struct False;
|
||||
struct Null;
|
||||
|
||||
typedef boost::variant<boost::recursive_wrapper<String>,
|
||||
boost::recursive_wrapper<Number>,
|
||||
boost::recursive_wrapper<Object>,
|
||||
boost::recursive_wrapper<Array>,
|
||||
boost::recursive_wrapper<True>,
|
||||
boost::recursive_wrapper<False>,
|
||||
boost::recursive_wrapper<Null> > Value;
|
||||
|
||||
struct String
|
||||
{
|
||||
String() {}
|
||||
String(const char *value) : value(value) {}
|
||||
String(const std::string &value) : value(value) {}
|
||||
std::string value;
|
||||
};
|
||||
|
||||
struct Number
|
||||
{
|
||||
Number() {}
|
||||
Number(double value) : value(value) {}
|
||||
double value;
|
||||
};
|
||||
|
||||
struct Object
|
||||
{
|
||||
std::unordered_map<std::string, Value> values;
|
||||
};
|
||||
|
||||
struct Array
|
||||
{
|
||||
std::vector<Value> values;
|
||||
};
|
||||
|
||||
struct True
|
||||
{
|
||||
};
|
||||
|
||||
struct False
|
||||
{
|
||||
};
|
||||
|
||||
struct Null
|
||||
{
|
||||
};
|
||||
|
||||
struct Renderer : boost::static_visitor<>
|
||||
{
|
||||
Renderer(std::ostream &_out) : out(_out) {}
|
||||
|
||||
void operator()(const String &string) const { out << "\"" << string.value << "\""; }
|
||||
|
||||
void operator()(const Number &number) const
|
||||
{
|
||||
out.precision(10);
|
||||
out << number.value;
|
||||
}
|
||||
|
||||
void operator()(const Object &object) const
|
||||
{
|
||||
out << "{";
|
||||
auto iterator = object.values.begin();
|
||||
while (iterator != object.values.end())
|
||||
{
|
||||
out << "\"" << (*iterator).first << "\":";
|
||||
boost::apply_visitor(Renderer(out), (*iterator).second);
|
||||
if (++iterator != object.values.end())
|
||||
{
|
||||
out << ",";
|
||||
}
|
||||
}
|
||||
out << "}";
|
||||
}
|
||||
|
||||
void operator()(const Array &array) const
|
||||
{
|
||||
out << "[";
|
||||
std::vector<Value>::const_iterator iterator;
|
||||
iterator = array.values.begin();
|
||||
while (iterator != array.values.end())
|
||||
{
|
||||
boost::apply_visitor(Renderer(out), *iterator);
|
||||
if (++iterator != array.values.end())
|
||||
{
|
||||
out << ",";
|
||||
}
|
||||
}
|
||||
out << "]";
|
||||
}
|
||||
|
||||
void operator()(const True &) const { out << "true"; }
|
||||
|
||||
void operator()(const False &) const { out << "false"; }
|
||||
|
||||
void operator()(const Null &) const { out << "null"; }
|
||||
|
||||
private:
|
||||
std::ostream &out;
|
||||
};
|
||||
|
||||
struct ArrayRenderer : boost::static_visitor<>
|
||||
{
|
||||
ArrayRenderer(std::vector<char> &_out) : out(_out) {}
|
||||
|
||||
void operator()(const String &string) const {
|
||||
out.push_back('\"');
|
||||
out.insert(out.end(), string.value.begin(), string.value.end());
|
||||
out.push_back('\"');
|
||||
}
|
||||
|
||||
void operator()(const Number &number) const
|
||||
{
|
||||
const std::string number_string = FixedDoubleToString(number.value);
|
||||
out.insert(out.end(), number_string.begin(), number_string.end());
|
||||
}
|
||||
|
||||
void operator()(const Object &object) const
|
||||
{
|
||||
out.push_back('{');
|
||||
auto iterator = object.values.begin();
|
||||
while (iterator != object.values.end())
|
||||
{
|
||||
out.push_back('\"');
|
||||
out.insert(out.end(), (*iterator).first.begin(), (*iterator).first.end());
|
||||
out.push_back('\"');
|
||||
out.push_back(':');
|
||||
|
||||
boost::apply_visitor(ArrayRenderer(out), (*iterator).second);
|
||||
if (++iterator != object.values.end())
|
||||
{
|
||||
out.push_back(',');
|
||||
}
|
||||
}
|
||||
out.push_back('}');
|
||||
}
|
||||
|
||||
void operator()(const Array &array) const
|
||||
{
|
||||
out.push_back('[');
|
||||
std::vector<Value>::const_iterator iterator;
|
||||
iterator = array.values.begin();
|
||||
while (iterator != array.values.end())
|
||||
{
|
||||
boost::apply_visitor(ArrayRenderer(out), *iterator);
|
||||
if (++iterator != array.values.end())
|
||||
{
|
||||
out.push_back(',');
|
||||
}
|
||||
}
|
||||
out.push_back(']');
|
||||
}
|
||||
|
||||
void operator()(const True &) const {
|
||||
const std::string temp("true");
|
||||
out.insert(out.end(), temp.begin(), temp.end());
|
||||
}
|
||||
|
||||
void operator()(const False &) const {
|
||||
const std::string temp("false");
|
||||
out.insert(out.end(), temp.begin(), temp.end());
|
||||
}
|
||||
|
||||
void operator()(const Null &) const {
|
||||
const std::string temp("null");
|
||||
out.insert(out.end(), temp.begin(), temp.end());
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<char> &out;
|
||||
};
|
||||
|
||||
inline void render(std::ostream &out, const Object &object)
|
||||
{
|
||||
Value value = object;
|
||||
boost::apply_visitor(Renderer(out), value);
|
||||
}
|
||||
|
||||
inline void render(std::vector<char> &out, const Object &object)
|
||||
{
|
||||
Value value = object;
|
||||
boost::apply_visitor(ArrayRenderer(out), value);
|
||||
}
|
||||
|
||||
} // namespace JSON
|
||||
|
||||
#endif // JSON_CONTAINER_H
|
||||
@@ -1,128 +1,97 @@
|
||||
#include <climits>
|
||||
#include <google/sparse_hash_map>
|
||||
#include <iostream>
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef LRUCACHE_H
|
||||
#define LRUCACHE_H
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
template < class T >
|
||||
struct Countfn {
|
||||
unsigned long operator()( const T &x ) { return 1; }
|
||||
};
|
||||
|
||||
template< class Key, class Data, class Sizefn = Countfn< Data > > class LRUCache {
|
||||
public:
|
||||
typedef std::list< std::pair< Key, Data > > List; ///< Main cache storage typedef
|
||||
typedef typename List::iterator List_Iter; ///< Main cache iterator
|
||||
typedef typename List::const_iterator List_cIter; ///< Main cache iterator (const)
|
||||
typedef std::vector< Key > Key_List; ///< List of keys
|
||||
typedef typename Key_List::iterator Key_List_Iter; ///< Main cache iterator
|
||||
typedef typename Key_List::const_iterator Key_List_cIter; ///< Main cache iterator (const)
|
||||
typedef google::sparse_hash_map< Key, List_Iter > Map; ///< Index typedef
|
||||
typedef std::pair< Key, List_Iter > Pair; ///< Pair of Map elements
|
||||
typedef typename Map::iterator Map_Iter; ///< Index iterator
|
||||
typedef typename Map::const_iterator Map_cIter; ///< Index iterator (const)
|
||||
|
||||
private:
|
||||
List _list; ///< Main cache storage
|
||||
Map _index; ///< Cache storage index
|
||||
unsigned long _max_size; ///< Maximum abstract size of the cache
|
||||
unsigned long _curr_size; ///< Current abstract size of the cache
|
||||
|
||||
public:
|
||||
LRUCache( const unsigned long Size ) : _max_size( Size ), _curr_size( 0 ) {
|
||||
_index.set_deleted_key(UINT_MAX);
|
||||
}
|
||||
|
||||
~LRUCache() { clear(); }
|
||||
|
||||
inline const unsigned long size( void ) const { return _curr_size; }
|
||||
|
||||
inline const unsigned long max_size( void ) const { return _max_size; }
|
||||
|
||||
/// Clears all storage and indices.
|
||||
void clear( void ) {
|
||||
_list.clear();
|
||||
_index.clear();
|
||||
template <typename KeyT, typename ValueT> class LRUCache
|
||||
{
|
||||
private:
|
||||
struct CacheEntry
|
||||
{
|
||||
CacheEntry(KeyT k, ValueT v) : key(k), value(v) {}
|
||||
KeyT key;
|
||||
ValueT value;
|
||||
};
|
||||
unsigned capacity;
|
||||
std::list<CacheEntry> itemsInCache;
|
||||
std::unordered_map<KeyT, typename std::list<CacheEntry>::iterator> positionMap;
|
||||
|
||||
inline bool exists( const Key &key ) const {
|
||||
return _index.find( key ) != _index.end();
|
||||
public:
|
||||
explicit LRUCache(unsigned c) : capacity(c) {}
|
||||
|
||||
bool Holds(KeyT key)
|
||||
{
|
||||
if (positionMap.find(key) != positionMap.end())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline void remove( const Key &key ) {
|
||||
Map_Iter miter = _index.find( key );
|
||||
if( miter == _index.end() ) return;
|
||||
_remove( miter );
|
||||
}
|
||||
|
||||
inline void touch( const Key &key ) {
|
||||
_touch( key );
|
||||
}
|
||||
|
||||
inline Data *fetch_ptr( const Key &key, bool touch = true ) {
|
||||
Map_Iter miter = _index.find( key );
|
||||
if( miter == _index.end() ) return NULL;
|
||||
_touch( key );
|
||||
return &(miter->second->second);
|
||||
}
|
||||
|
||||
inline bool fetch( const Key &key, Data &data, bool touch_data = true ) {
|
||||
Map_Iter miter = _index.find( key );
|
||||
if( miter == _index.end() ) return false;
|
||||
if( touch_data )
|
||||
_touch( key );
|
||||
data = miter->second->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void insert( const Key &key, const Data data ) {
|
||||
// Touch the key, if it exists, then replace the content.
|
||||
Map_Iter miter = _touch( key );
|
||||
if( miter != _index.end() )
|
||||
_remove( miter );
|
||||
|
||||
// Ok, do the actual insert at the head of the list
|
||||
_list.push_front( std::make_pair( key, data ) );
|
||||
List_Iter liter = _list.begin();
|
||||
|
||||
// Store the index
|
||||
_index.insert( std::make_pair( key, liter ) );
|
||||
_curr_size += Sizefn()( data );
|
||||
|
||||
// Check to see if we need to remove an element due to exceeding max_size
|
||||
while( _curr_size > _max_size ) {
|
||||
std::cout << "removing element " << std::endl;
|
||||
// Remove the last element.
|
||||
liter = _list.end();
|
||||
--liter;
|
||||
_remove( liter->first );
|
||||
void Insert(const KeyT key, ValueT &value)
|
||||
{
|
||||
itemsInCache.push_front(CacheEntry(key, value));
|
||||
positionMap.insert(std::make_pair(key, itemsInCache.begin()));
|
||||
if (itemsInCache.size() > capacity)
|
||||
{
|
||||
positionMap.erase(itemsInCache.back().key);
|
||||
itemsInCache.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
inline const Key_List get_all_keys( void ) {
|
||||
Key_List ret;
|
||||
for( List_cIter liter = _list.begin(); liter != _list.end(); liter++ )
|
||||
ret.push_back( liter->first );
|
||||
return ret;
|
||||
void Insert(const KeyT key, ValueT value)
|
||||
{
|
||||
itemsInCache.push_front(CacheEntry(key, value));
|
||||
positionMap.insert(std::make_pair(key, itemsInCache.begin()));
|
||||
if (itemsInCache.size() > capacity)
|
||||
{
|
||||
positionMap.erase(itemsInCache.back().key);
|
||||
itemsInCache.pop_back();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
inline Map_Iter _touch( const Key &key ) {
|
||||
Map_Iter miter = _index.find( key );
|
||||
if( miter == _index.end() ) return miter;
|
||||
// Move the found node to the head of the list.
|
||||
_list.splice( _list.begin(), _list, miter->second );
|
||||
return miter;
|
||||
}
|
||||
bool Fetch(const KeyT key, ValueT &result)
|
||||
{
|
||||
if (Holds(key))
|
||||
{
|
||||
CacheEntry e = *(positionMap.find(key)->second);
|
||||
result = e.value;
|
||||
|
||||
inline void _remove( const Map_Iter &miter ) {
|
||||
_curr_size -= Sizefn()( miter->second->second );
|
||||
_list.erase( miter->second );
|
||||
_index.erase( miter );
|
||||
}
|
||||
|
||||
inline void _remove( const Key &key ) {
|
||||
Map_Iter miter = _index.find( key );
|
||||
_remove( miter );
|
||||
// move to front
|
||||
itemsInCache.splice(positionMap.find(key)->second, itemsInCache, itemsInCache.begin());
|
||||
positionMap.find(key)->second = itemsInCache.begin();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
unsigned Size() const { return itemsInCache.size(); }
|
||||
};
|
||||
#endif // LRUCACHE_H
|
||||
|
||||
@@ -1,635 +0,0 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
|
||||
#ifndef NNGRID_H_
|
||||
#define NNGRID_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <stxxl.h>
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <math.h>
|
||||
#endif
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <google/dense_hash_map>
|
||||
|
||||
#include "ExtractorStructs.h"
|
||||
#include "GridEdge.h"
|
||||
#include "LRUCache.h"
|
||||
#include "Percent.h"
|
||||
#include "PhantomNodes.h"
|
||||
#include "Util.h"
|
||||
#include "StaticGraph.h"
|
||||
|
||||
static const unsigned MAX_CACHE_ELEMENTS = 1000;
|
||||
|
||||
namespace NNGrid{
|
||||
static unsigned GetFileIndexForLatLon(const int lt, const int ln) {
|
||||
double lat = lt/100000.;
|
||||
double lon = ln/100000.;
|
||||
|
||||
double x = ( lon + 180.0 ) / 360.0;
|
||||
double y = ( lat + 180.0 ) / 360.0;
|
||||
|
||||
assert( x<=1.0 && x >= 0);
|
||||
assert( y<=1.0 && y >= 0);
|
||||
|
||||
unsigned line = 1073741824.0*y;
|
||||
line = line - (line % 32768);
|
||||
assert(line % 32768 == 0);
|
||||
unsigned column = 32768.*x;
|
||||
unsigned fileIndex = line+column;
|
||||
return fileIndex;
|
||||
}
|
||||
|
||||
static unsigned GetRAMIndexFromFileIndex(const int fileIndex) {
|
||||
unsigned fileLine = fileIndex / 32768;
|
||||
fileLine = fileLine / 32;
|
||||
fileLine = fileLine * 1024;
|
||||
unsigned fileColumn = (fileIndex % 32768);
|
||||
fileColumn = fileColumn / 32;
|
||||
unsigned ramIndex = fileLine + fileColumn;
|
||||
assert(ramIndex < 1024*1024);
|
||||
return ramIndex;
|
||||
}
|
||||
|
||||
static inline int signum(int x){
|
||||
return (x > 0) ? 1 : (x < 0) ? -1 : 0;
|
||||
}
|
||||
|
||||
static void GetIndicesByBresenhamsAlgorithm(int xstart,int ystart,int xend,int yend, std::vector<std::pair<unsigned, unsigned> > &indexList) {
|
||||
int x, y, t, dx, dy, incx, incy, pdx, pdy, ddx, ddy, es, el, err;
|
||||
|
||||
dx = xend - xstart;
|
||||
dy = yend - ystart;
|
||||
|
||||
incx = signum(dx);
|
||||
incy = signum(dy);
|
||||
if(dx<0) dx = -dx;
|
||||
if(dy<0) dy = -dy;
|
||||
|
||||
if (dx>dy) {
|
||||
pdx=incx; pdy=0;
|
||||
ddx=incx; ddy=incy;
|
||||
es =dy; el =dx;
|
||||
} else {
|
||||
pdx=0; pdy=incy;
|
||||
ddx=incx; ddy=incy;
|
||||
es =dx; el =dy;
|
||||
}
|
||||
x = xstart;
|
||||
y = ystart;
|
||||
err = el/2;
|
||||
{
|
||||
int fileIndex = (y-1)*32768 + x;
|
||||
int ramIndex = GetRAMIndexFromFileIndex(fileIndex);
|
||||
indexList.push_back(std::make_pair(fileIndex, ramIndex));
|
||||
}
|
||||
|
||||
for(t=0; t<el; ++t) {
|
||||
err -= es;
|
||||
if(err<0) {
|
||||
err += el;
|
||||
x += ddx;
|
||||
y += ddy;
|
||||
} else {
|
||||
x += pdx;
|
||||
y += pdy;
|
||||
}
|
||||
{
|
||||
int fileIndex = (y-1)*32768 + x;
|
||||
int ramIndex = GetRAMIndexFromFileIndex(fileIndex);
|
||||
indexList.push_back(std::make_pair(fileIndex, ramIndex));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void GetListOfIndexesForEdgeAndGridSize(_Coordinate& start, _Coordinate& target, std::vector<std::pair<unsigned, unsigned> > &indexList) {
|
||||
double lat1 = start.lat/100000.;
|
||||
double lon1 = start.lon/100000.;
|
||||
|
||||
double x1 = ( lon1 + 180.0 ) / 360.0;
|
||||
double y1 = ( lat1 + 180.0 ) / 360.0;
|
||||
|
||||
double lat2 = target.lat/100000.;
|
||||
double lon2 = target.lon/100000.;
|
||||
|
||||
double x2 = ( lon2 + 180.0 ) / 360.0;
|
||||
double y2 = ( lat2 + 180.0 ) / 360.0;
|
||||
|
||||
GetIndicesByBresenhamsAlgorithm(x1*32768, y1*32768, x2*32768, y2*32768, indexList);
|
||||
}
|
||||
|
||||
static boost::thread_specific_ptr<std::ifstream> localStream;
|
||||
|
||||
template<bool WriteAccess = false>
|
||||
class NNGrid {
|
||||
public:
|
||||
NNGrid() /*: cellCache(500), fileCache(500)*/ {
|
||||
ramIndexTable.resize((1024*1024), UINT_MAX);
|
||||
if( WriteAccess) {
|
||||
entries = new stxxl::vector<GridEntry>();
|
||||
}
|
||||
}
|
||||
|
||||
NNGrid(const char* rif, const char* _i, unsigned numberOfThreads = omp_get_num_procs()) /*: cellCache(500), fileCache(500) */{
|
||||
if(WriteAccess) {
|
||||
ERR("Not available in Write mode");
|
||||
}
|
||||
iif = std::string(_i);
|
||||
ramIndexTable.resize((1024*1024), UINT_MAX);
|
||||
ramInFile.open(rif, std::ios::in | std::ios::binary);
|
||||
entries = NULL;
|
||||
}
|
||||
|
||||
~NNGrid() {
|
||||
if(ramInFile.is_open()) ramInFile.close();
|
||||
|
||||
if (WriteAccess) {
|
||||
delete entries;
|
||||
}
|
||||
if(localStream.get() && localStream->is_open()) {
|
||||
localStream->close();
|
||||
}
|
||||
}
|
||||
|
||||
void OpenIndexFiles() {
|
||||
assert(ramInFile.is_open());
|
||||
|
||||
for(int i = 0; i < 1024*1024; ++i) {
|
||||
unsigned temp;
|
||||
ramInFile.read((char*)&temp, sizeof(unsigned));
|
||||
ramIndexTable[i] = temp;
|
||||
}
|
||||
ramInFile.close();
|
||||
}
|
||||
|
||||
template<typename EdgeT, typename NodeInfoT>
|
||||
void ConstructGrid(std::vector<EdgeT> & edgeList, vector<NodeInfoT> * int2ExtNodeMap, char * ramIndexOut, char * fileIndexOut) {
|
||||
Percent p(edgeList.size());
|
||||
for(NodeID i = 0; i < edgeList.size(); ++i) {
|
||||
p.printIncrement();
|
||||
EdgeT & edge = edgeList[i];
|
||||
|
||||
int slat = 100000*lat2y(edge.lat1/100000.);
|
||||
int slon = edge.lon1;
|
||||
int tlat = 100000*lat2y(edge.lat2/100000.);
|
||||
int tlon = edge.lon2;
|
||||
AddEdge( _GridEdge(
|
||||
edge.id, edge.nameID, edge.weight,
|
||||
_Coordinate(slat, slon),
|
||||
_Coordinate(tlat, tlon) )
|
||||
);
|
||||
}
|
||||
double timestamp = get_timestamp();
|
||||
//create index file on disk, old one is over written
|
||||
indexOutFile.open(fileIndexOut, std::ios::out | std::ios::binary | std::ios::trunc);
|
||||
cout << "sorting grid data consisting of " << entries->size() << " edges..." << flush;
|
||||
//sort entries
|
||||
stxxl::sort(entries->begin(), entries->end(), CompareGridEdgeDataByRamIndex(), 1024*1024*1024);
|
||||
cout << "ok in " << (get_timestamp() - timestamp) << "s" << endl;
|
||||
std::vector<GridEntry> entriesInFileWithRAMSameIndex;
|
||||
unsigned indexInRamTable = entries->begin()->ramIndex;
|
||||
unsigned lastPositionInIndexFile = 0;
|
||||
unsigned numberOfUsedCells = 0;
|
||||
unsigned maxNumberOfRAMCellElements = 0;
|
||||
cout << "writing data ..." << flush;
|
||||
p.reinit(entries->size());
|
||||
for(stxxl::vector<GridEntry>::iterator vt = entries->begin(); vt != entries->end(); ++vt) {
|
||||
p.printIncrement();
|
||||
if(vt->ramIndex != indexInRamTable) {
|
||||
unsigned numberOfBytesInCell = FillCell(entriesInFileWithRAMSameIndex, lastPositionInIndexFile);
|
||||
if(entriesInFileWithRAMSameIndex.size() > maxNumberOfRAMCellElements)
|
||||
maxNumberOfRAMCellElements = entriesInFileWithRAMSameIndex.size();
|
||||
|
||||
ramIndexTable[indexInRamTable] = lastPositionInIndexFile;
|
||||
lastPositionInIndexFile += numberOfBytesInCell;
|
||||
entriesInFileWithRAMSameIndex.clear();
|
||||
indexInRamTable = vt->ramIndex;
|
||||
numberOfUsedCells++;
|
||||
}
|
||||
entriesInFileWithRAMSameIndex.push_back(*vt);
|
||||
}
|
||||
/*unsigned numberOfBytesInCell = */FillCell(entriesInFileWithRAMSameIndex, lastPositionInIndexFile);
|
||||
ramIndexTable[indexInRamTable] = lastPositionInIndexFile;
|
||||
numberOfUsedCells++;
|
||||
entriesInFileWithRAMSameIndex.clear();
|
||||
|
||||
assert(entriesInFileWithRAMSameIndex.size() == 0);
|
||||
|
||||
for(int i = 0; i < 1024*1024; ++i) {
|
||||
if(ramIndexTable[i] != UINT_MAX) {
|
||||
numberOfUsedCells--;
|
||||
}
|
||||
}
|
||||
assert(numberOfUsedCells == 0);
|
||||
|
||||
//close index file
|
||||
indexOutFile.close();
|
||||
//Serialize RAM Index
|
||||
ofstream ramFile(ramIndexOut, std::ios::out | std::ios::binary | std::ios::trunc);
|
||||
//write 4 MB of index Table in RAM
|
||||
for(int i = 0; i < 1024*1024; ++i)
|
||||
ramFile.write((char *)&ramIndexTable[i], sizeof(unsigned) );
|
||||
//close ram index file
|
||||
ramFile.close();
|
||||
}
|
||||
|
||||
bool GetEdgeBasedStartNode(const _Coordinate& coord, NodesOfEdge& nodesOfEdge) {
|
||||
_Coordinate startCoord(100000*(lat2y(static_cast<double>(coord.lat)/100000.)), coord.lon);
|
||||
/** search for point on edge next to source */
|
||||
unsigned fileIndex = GetFileIndexForLatLon(startCoord.lat, startCoord.lon);
|
||||
std::vector<_GridEdge> candidates;
|
||||
|
||||
for(int j = -32768; j < (32768+1); j+=32768) {
|
||||
for(int i = -1; i < 2; i++){
|
||||
GetContentsOfFileBucket(fileIndex+i+j, candidates);
|
||||
}
|
||||
}
|
||||
_Coordinate tmp;
|
||||
double dist = numeric_limits<double>::max();
|
||||
BOOST_FOREACH(_GridEdge candidate, candidates) {
|
||||
double r = 0.;
|
||||
double tmpDist = ComputeDistance(startCoord, candidate.startCoord, candidate.targetCoord, tmp, &r);
|
||||
if(tmpDist < dist) {
|
||||
nodesOfEdge.edgeBasedNode = candidate.edgeBasedNode;
|
||||
nodesOfEdge.ratio = r;
|
||||
dist = tmpDist;
|
||||
nodesOfEdge.projectedPoint.lat = round(100000*(y2lat(static_cast<double>(tmp.lat)/100000.)));
|
||||
nodesOfEdge.projectedPoint.lon = tmp.lon;
|
||||
}
|
||||
}
|
||||
if(dist != (numeric_limits<double>::max)()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FindPhantomNodeForCoordinate( const _Coordinate & location, PhantomNode & resultNode) {
|
||||
bool foundNode = false;
|
||||
_Coordinate startCoord(100000*(lat2y(static_cast<double>(location.lat)/100000.)), location.lon);
|
||||
/** search for point on edge close to source */
|
||||
unsigned fileIndex = GetFileIndexForLatLon(startCoord.lat, startCoord.lon);
|
||||
std::vector<_GridEdge> candidates;
|
||||
|
||||
for(int j = -32768; j < (32768+1); j+=32768) {
|
||||
for(int i = -1; i < 2; i++){
|
||||
GetContentsOfFileBucket(fileIndex+i+j, candidates);
|
||||
}
|
||||
}
|
||||
|
||||
_GridEdge smallestEdge;
|
||||
_Coordinate tmp, newEndpoint;
|
||||
double dist = (numeric_limits<double>::max)();
|
||||
BOOST_FOREACH(_GridEdge candidate, candidates) {
|
||||
double r = 0.;
|
||||
double tmpDist = ComputeDistance(startCoord, candidate.startCoord, candidate.targetCoord, tmp, &r);
|
||||
if(DoubleEpsilonCompare(dist, tmpDist) && 1 == std::abs((int)candidate.edgeBasedNode-(int)resultNode.edgeBasedNode)) {
|
||||
resultNode.weight2 = candidate.weight;
|
||||
/* if(resultNode.weight1 != resultNode.weight2) {
|
||||
ERR("w1: " << resultNode.weight1 << ", w2: " << resultNode.weight2);
|
||||
assert(false);
|
||||
}*/
|
||||
if(candidate.edgeBasedNode < resultNode.edgeBasedNode) {
|
||||
resultNode.edgeBasedNode = candidate.edgeBasedNode;
|
||||
std::swap(resultNode.weight1, resultNode.weight2);
|
||||
}
|
||||
}
|
||||
if(tmpDist < dist) {
|
||||
resultNode.Reset();
|
||||
resultNode.edgeBasedNode = candidate.edgeBasedNode;
|
||||
resultNode.nodeBasedEdgeNameID = candidate.nameID;
|
||||
resultNode.weight1 = candidate.weight;
|
||||
dist = tmpDist;
|
||||
resultNode.location.lat = round(100000*(y2lat(static_cast<double>(tmp.lat)/100000.)));
|
||||
resultNode.location.lon = tmp.lon;
|
||||
foundNode = true;
|
||||
smallestEdge = candidate;
|
||||
newEndpoint = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
// INFO("startcoord: " << smallestEdge.startCoord << ", tgtcoord" << smallestEdge.targetCoord << "result: " << newEndpoint);
|
||||
// INFO("length of old edge: " << LengthOfVector(smallestEdge.startCoord, smallestEdge.targetCoord));
|
||||
// INFO("Length of new edge: " << LengthOfVector(smallestEdge.startCoord, newEndpoint));
|
||||
// assert(!resultNode.isBidirected || (resultNode.weight1 == resultNode.weight2));
|
||||
// if(resultNode.weight1 != resultNode.weight2) {
|
||||
// INFO("-> Weight1: " << resultNode.weight1 << ", weight2: " << resultNode.weight2);
|
||||
// INFO("-> node: " << resultNode.edgeBasedNode << ", bidir: " << (resultNode.isBidirected ? "yes" : "no"));
|
||||
// }
|
||||
|
||||
double ratio = std::min(1., LengthOfVector(smallestEdge.startCoord, newEndpoint)/LengthOfVector(smallestEdge.startCoord, smallestEdge.targetCoord) );
|
||||
assert(ratio >= 0 && ratio <=1);
|
||||
// INFO("Old weight1: " << resultNode.weight1 << ", old weight2: " << resultNode.weight2);
|
||||
resultNode.weight1 *= ratio;
|
||||
if(INT_MAX != resultNode.weight2) {
|
||||
resultNode.weight2 *= (1-ratio);
|
||||
// INFO("New weight1: " << resultNode.weight1 << ", new weight2: " << resultNode.weight2);
|
||||
}
|
||||
return foundNode;
|
||||
}
|
||||
|
||||
bool FindRoutingStarts(const _Coordinate& start, const _Coordinate& target, PhantomNodes & routingStarts) {
|
||||
routingStarts.Reset();
|
||||
return (FindPhantomNodeForCoordinate( start, routingStarts.startPhantom) &&
|
||||
FindPhantomNodeForCoordinate( target, routingStarts.targetPhantom) );
|
||||
}
|
||||
|
||||
void FindNearestCoordinateOnEdgeInNodeBasedGraph(const _Coordinate& inputCoordinate, _Coordinate& outputCoordinate) {
|
||||
unsigned fileIndex = GetFileIndexForLatLon(100000*(lat2y(static_cast<double>(inputCoordinate.lat)/100000.)), inputCoordinate.lon);
|
||||
std::vector<_GridEdge> candidates;
|
||||
for(int j = -32768; j < (32768+1); j+=32768) {
|
||||
for(int i = -1; i < 2; i++) {
|
||||
GetContentsOfFileBucket(fileIndex+i+j, candidates);
|
||||
}
|
||||
}
|
||||
_Coordinate tmp;
|
||||
double dist = (numeric_limits<double>::max)();
|
||||
BOOST_FOREACH(_GridEdge candidate, candidates) {
|
||||
double r = 0.;
|
||||
double tmpDist = ComputeDistance(inputCoordinate, candidate.startCoord, candidate.targetCoord, tmp, &r);
|
||||
if(tmpDist < dist) {
|
||||
dist = tmpDist;
|
||||
outputCoordinate = tmp;
|
||||
}
|
||||
}
|
||||
outputCoordinate.lat = 100000*(y2lat(static_cast<double>(outputCoordinate.lat)/100000.));
|
||||
}
|
||||
|
||||
void FindNearestPointOnEdge(const _Coordinate& inputCoordinate, _Coordinate& outputCoordinate) {
|
||||
_Coordinate startCoord(100000*(lat2y(static_cast<double>(inputCoordinate.lat)/100000.)), inputCoordinate.lon);
|
||||
unsigned fileIndex = GetFileIndexForLatLon(startCoord.lat, startCoord.lon);
|
||||
|
||||
std::vector<_GridEdge> candidates;
|
||||
for(int j = -32768; j < (32768+1); j+=32768) {
|
||||
for(int i = -1; i < 2; i++) {
|
||||
GetContentsOfFileBucket(fileIndex+i+j, candidates);
|
||||
}
|
||||
}
|
||||
_Coordinate tmp;
|
||||
double dist = (numeric_limits<double>::max)();
|
||||
BOOST_FOREACH(_GridEdge candidate, candidates) {
|
||||
double r = 0.;
|
||||
double tmpDist = ComputeDistance(startCoord, candidate.startCoord, candidate.targetCoord, tmp, &r);
|
||||
if(tmpDist < dist) {
|
||||
dist = tmpDist;
|
||||
outputCoordinate.lat = round(100000*(y2lat(static_cast<double>(tmp.lat)/100000.)));
|
||||
outputCoordinate.lon = tmp.lon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
inline double LengthOfVector(const _Coordinate & c1, const _Coordinate & c2) {
|
||||
double length1 = std::sqrt(c1.lat/100000.*c1.lat/100000. + c1.lon/100000.*c1.lon/100000.);
|
||||
double length2 = std::sqrt(c2.lat/100000.*c2.lat/100000. + c2.lon/100000.*c2.lon/100000.);
|
||||
return std::fabs(length1-length2);
|
||||
}
|
||||
|
||||
inline bool DoubleEpsilonCompare(const double d1, const double d2) {
|
||||
return (std::fabs(d1 - d2) < 0.000000001);
|
||||
}
|
||||
|
||||
unsigned FillCell(std::vector<GridEntry>& entriesWithSameRAMIndex, unsigned fileOffset ) {
|
||||
vector<char> * tmpBuffer = new vector<char>();
|
||||
tmpBuffer->resize(32*32*4096,0);
|
||||
unsigned indexIntoTmpBuffer = 0;
|
||||
unsigned numberOfWrittenBytes = 0;
|
||||
assert(indexOutFile.is_open());
|
||||
|
||||
vector<unsigned> cellIndex;
|
||||
cellIndex.resize(32*32,UINT_MAX);
|
||||
google::dense_hash_map< unsigned, unsigned > * cellMap = new google::dense_hash_map< unsigned, unsigned >(1024);
|
||||
cellMap->set_empty_key(UINT_MAX);
|
||||
|
||||
unsigned ramIndex = entriesWithSameRAMIndex.begin()->ramIndex;
|
||||
unsigned lineBase = ramIndex/1024;
|
||||
lineBase = lineBase*32*32768;
|
||||
unsigned columnBase = ramIndex%1024;
|
||||
columnBase=columnBase*32;
|
||||
|
||||
for(int i = 0; i < 32; i++) {
|
||||
for(int j = 0; j < 32; j++) {
|
||||
unsigned fileIndex = lineBase + i*32768 + columnBase+j;
|
||||
unsigned cellIndex = i*32+j;
|
||||
cellMap->insert(std::make_pair(fileIndex, cellIndex));
|
||||
}
|
||||
}
|
||||
|
||||
for(unsigned i = 0; i < entriesWithSameRAMIndex.size() -1; ++i) {
|
||||
assert(entriesWithSameRAMIndex[i].ramIndex== entriesWithSameRAMIndex[i+1].ramIndex);
|
||||
}
|
||||
|
||||
//sort & unique
|
||||
std::sort(entriesWithSameRAMIndex.begin(), entriesWithSameRAMIndex.end(), CompareGridEdgeDataByFileIndex());
|
||||
std::vector<GridEntry>::iterator uniqueEnd = std::unique(entriesWithSameRAMIndex.begin(), entriesWithSameRAMIndex.end());
|
||||
|
||||
//traverse each file bucket and write its contents to disk
|
||||
std::vector<GridEntry> entriesWithSameFileIndex;
|
||||
unsigned fileIndex = entriesWithSameRAMIndex.begin()->fileIndex;
|
||||
|
||||
for(std::vector<GridEntry>::iterator it = entriesWithSameRAMIndex.begin(); it != uniqueEnd; it++) {
|
||||
assert(cellMap->find(it->fileIndex) != cellMap->end() ); //asserting that file index belongs to cell index
|
||||
if(it->fileIndex != fileIndex) {
|
||||
// start in cellIndex vermerken
|
||||
int localFileIndex = entriesWithSameFileIndex.begin()->fileIndex;
|
||||
int localCellIndex = cellMap->find(localFileIndex)->second;
|
||||
/*int localRamIndex = */GetRAMIndexFromFileIndex(localFileIndex);
|
||||
assert(cellMap->find(entriesWithSameFileIndex.begin()->fileIndex) != cellMap->end());
|
||||
|
||||
cellIndex[localCellIndex] = indexIntoTmpBuffer + fileOffset;
|
||||
indexIntoTmpBuffer += FlushEntriesWithSameFileIndexToBuffer(entriesWithSameFileIndex, tmpBuffer, indexIntoTmpBuffer);
|
||||
}
|
||||
GridEntry data = *it;
|
||||
entriesWithSameFileIndex.push_back(data);
|
||||
fileIndex = it->fileIndex;
|
||||
}
|
||||
assert(cellMap->find(entriesWithSameFileIndex.begin()->fileIndex) != cellMap->end());
|
||||
int localFileIndex = entriesWithSameFileIndex.begin()->fileIndex;
|
||||
int localCellIndex = cellMap->find(localFileIndex)->second;
|
||||
/*int localRamIndex = */GetRAMIndexFromFileIndex(localFileIndex);
|
||||
|
||||
cellIndex[localCellIndex] = indexIntoTmpBuffer + fileOffset;
|
||||
indexIntoTmpBuffer += FlushEntriesWithSameFileIndexToBuffer(entriesWithSameFileIndex, tmpBuffer, indexIntoTmpBuffer);
|
||||
|
||||
assert(entriesWithSameFileIndex.size() == 0);
|
||||
|
||||
for(int i = 0; i < 32*32; i++) {
|
||||
indexOutFile.write((char *)&cellIndex[i], sizeof(unsigned));
|
||||
numberOfWrittenBytes += sizeof(unsigned);
|
||||
}
|
||||
|
||||
//write contents of tmpbuffer to disk
|
||||
for(unsigned i = 0; i < indexIntoTmpBuffer; i++) {
|
||||
indexOutFile.write(&(tmpBuffer->at(i)), sizeof(char));
|
||||
numberOfWrittenBytes += sizeof(char);
|
||||
}
|
||||
|
||||
delete tmpBuffer;
|
||||
delete cellMap;
|
||||
return numberOfWrittenBytes;
|
||||
}
|
||||
|
||||
unsigned FlushEntriesWithSameFileIndexToBuffer( std::vector<GridEntry> &vectorWithSameFileIndex, vector<char> * tmpBuffer, const unsigned index) {
|
||||
tmpBuffer->resize(tmpBuffer->size()+(sizeof(_GridEdge)*vectorWithSameFileIndex.size()) );
|
||||
unsigned counter = 0;
|
||||
unsigned max = UINT_MAX;
|
||||
|
||||
for(unsigned i = 0; i < vectorWithSameFileIndex.size()-1; i++) {
|
||||
assert( vectorWithSameFileIndex[i].fileIndex == vectorWithSameFileIndex[i+1].fileIndex );
|
||||
assert( vectorWithSameFileIndex[i].ramIndex == vectorWithSameFileIndex[i+1].ramIndex );
|
||||
}
|
||||
|
||||
sort( vectorWithSameFileIndex.begin(), vectorWithSameFileIndex.end() );
|
||||
vectorWithSameFileIndex.erase(unique(vectorWithSameFileIndex.begin(), vectorWithSameFileIndex.end()), vectorWithSameFileIndex.end());
|
||||
BOOST_FOREACH(GridEntry entry, vectorWithSameFileIndex) {
|
||||
char * data = (char *)&(entry.edge);
|
||||
for(unsigned i = 0; i < sizeof(_GridEdge); ++i) {
|
||||
tmpBuffer->at(index+counter) = data[i];
|
||||
++counter;
|
||||
}
|
||||
}
|
||||
char * umax = (char *) &max;
|
||||
for(unsigned i = 0; i < sizeof(unsigned); i++) {
|
||||
tmpBuffer->at(index+counter) = umax[i];
|
||||
counter++;
|
||||
}
|
||||
vectorWithSameFileIndex.clear();
|
||||
std::vector<GridEntry>().swap(vectorWithSameFileIndex);
|
||||
return counter;
|
||||
}
|
||||
|
||||
void GetContentsOfFileBucket(const unsigned fileIndex, std::vector<_GridEdge>& result) {
|
||||
unsigned ramIndex = GetRAMIndexFromFileIndex(fileIndex);
|
||||
unsigned startIndexInFile = ramIndexTable[ramIndex];
|
||||
if(startIndexInFile == UINT_MAX) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<unsigned> cellIndex;
|
||||
cellIndex.resize(32*32);
|
||||
google::dense_hash_map< unsigned, unsigned > cellMap(1024);
|
||||
cellMap.set_empty_key(UINT_MAX);
|
||||
|
||||
unsigned lineBase = ramIndex/1024;
|
||||
lineBase = lineBase*32*32768;
|
||||
unsigned columnBase = ramIndex%1024;
|
||||
columnBase=columnBase*32;
|
||||
|
||||
for(int i = 0; i < 32; i++) {
|
||||
for(int j = 0; j < 32; j++) {
|
||||
unsigned fileIndex = lineBase + i*32768 + columnBase+j;
|
||||
unsigned cellIndex = i*32+j;
|
||||
cellMap.insert(std::make_pair(fileIndex, cellIndex));
|
||||
}
|
||||
}
|
||||
if(!localStream.get() || !localStream->is_open()) {
|
||||
localStream.reset(new std::ifstream(iif.c_str(), std::ios::in | std::ios::binary));
|
||||
}
|
||||
localStream->seekg(startIndexInFile);
|
||||
localStream->read((char*) &cellIndex[0], 32*32*sizeof(unsigned));
|
||||
assert(cellMap.find(fileIndex) != cellMap.end());
|
||||
if(cellIndex[cellMap.find(fileIndex)->second] == UINT_MAX) {
|
||||
return;
|
||||
}
|
||||
const unsigned position = cellIndex[cellMap.find(fileIndex)->second] + 32*32*sizeof(unsigned) ;
|
||||
|
||||
localStream->seekg(position);
|
||||
_GridEdge gridEdge;
|
||||
do {
|
||||
localStream->read((char *)&(gridEdge), sizeof(_GridEdge));
|
||||
if(localStream->eof() || gridEdge.edgeBasedNode == UINT_MAX)
|
||||
break;
|
||||
result.push_back(gridEdge);
|
||||
} while(true);
|
||||
}
|
||||
|
||||
void AddEdge(_GridEdge edge) {
|
||||
std::vector<std::pair<unsigned, unsigned> > indexList;
|
||||
GetListOfIndexesForEdgeAndGridSize(edge.startCoord, edge.targetCoord, indexList);
|
||||
for(unsigned i = 0; i < indexList.size(); ++i) {
|
||||
entries->push_back(GridEntry(edge, indexList[i].first, indexList[i].second));
|
||||
}
|
||||
}
|
||||
|
||||
double ComputeDistance(const _Coordinate& inputPoint, const _Coordinate& source, const _Coordinate& target, _Coordinate& nearest, double *r) {
|
||||
|
||||
const double x = (double)inputPoint.lat;
|
||||
const double y = (double)inputPoint.lon;
|
||||
const double a = (double)source.lat;
|
||||
const double b = (double)source.lon;
|
||||
const double c = (double)target.lat;
|
||||
const double d = (double)target.lon;
|
||||
double p,q,mX,nY;
|
||||
if(c != a){
|
||||
const double m = (d-b)/(c-a); // slope
|
||||
// Projection of (x,y) on line joining (a,b) and (c,d)
|
||||
p = ((x + (m*y)) + (m*m*a - m*b))/(1 + m*m);
|
||||
q = b + m*(p - a);
|
||||
}
|
||||
else{
|
||||
p = c;
|
||||
q = y;
|
||||
}
|
||||
nY = (d*p - c*q)/(a*d - b*c);
|
||||
mX = (p - nY*a)/c;// These values are actually n/m+n and m/m+n , we neednot calculate the values of m an n as we are just interested in the ratio
|
||||
*r = mX;
|
||||
if(*r<=0){
|
||||
nearest.lat = source.lat;
|
||||
nearest.lon = source.lon;
|
||||
return ((b - y)*(b - y) + (a - x)*(a - x));
|
||||
}
|
||||
else if(*r >= 1){
|
||||
nearest.lat = target.lat;
|
||||
nearest.lon = target.lon;
|
||||
return ((d - y)*(d - y) + (c - x)*(c - x));
|
||||
|
||||
}
|
||||
// point lies in between
|
||||
nearest.lat = p;
|
||||
nearest.lon = q;
|
||||
return (p-x)*(p-x) + (q-y)*(q-y);
|
||||
}
|
||||
|
||||
ofstream indexOutFile;
|
||||
ifstream ramInFile;
|
||||
stxxl::vector<GridEntry> * entries;
|
||||
std::vector<unsigned> ramIndexTable; //4 MB for first level index in RAM
|
||||
std::string iif;
|
||||
// LRUCache<int,std::vector<unsigned> > cellCache;
|
||||
// LRUCache<int,std::vector<_Edge> > fileCache;
|
||||
};
|
||||
}
|
||||
|
||||
typedef NNGrid::NNGrid<false> ReadOnlyGrid;
|
||||
typedef NNGrid::NNGrid<true > WritableGrid;
|
||||
|
||||
#endif /* NNGRID_H_ */
|
||||
@@ -0,0 +1,110 @@
|
||||
#ifndef __NODE_BASED_GRAPH_H__
|
||||
#define __NODE_BASED_GRAPH_H__
|
||||
|
||||
#include "DynamicGraph.h"
|
||||
#include "ImportEdge.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
struct NodeBasedEdgeData
|
||||
{
|
||||
NodeBasedEdgeData()
|
||||
: distance(INVALID_EDGE_WEIGHT), edgeBasedNodeID(SPECIAL_NODEID),
|
||||
nameID(std::numeric_limits<unsigned>::max()), type(std::numeric_limits<short>::max()),
|
||||
isAccessRestricted(false), shortcut(false), forward(false), backward(false),
|
||||
roundabout(false), ignore_in_grid(false), contraFlow(false)
|
||||
{
|
||||
}
|
||||
|
||||
int distance;
|
||||
unsigned edgeBasedNodeID;
|
||||
unsigned nameID;
|
||||
short type;
|
||||
bool isAccessRestricted : 1;
|
||||
bool shortcut : 1;
|
||||
bool forward : 1;
|
||||
bool backward : 1;
|
||||
bool roundabout : 1;
|
||||
bool ignore_in_grid : 1;
|
||||
bool contraFlow : 1;
|
||||
|
||||
void SwapDirectionFlags()
|
||||
{
|
||||
bool temp_flag = forward;
|
||||
forward = backward;
|
||||
backward = temp_flag;
|
||||
}
|
||||
|
||||
bool IsEqualTo(const NodeBasedEdgeData &other) const
|
||||
{
|
||||
return (forward == other.forward) && (backward == other.backward) &&
|
||||
(nameID == other.nameID) && (ignore_in_grid == other.ignore_in_grid) &&
|
||||
(contraFlow == other.contraFlow);
|
||||
}
|
||||
};
|
||||
|
||||
typedef DynamicGraph<NodeBasedEdgeData> NodeBasedDynamicGraph;
|
||||
|
||||
// Factory method to create NodeBasedDynamicGraph from ImportEdges
|
||||
inline std::shared_ptr<NodeBasedDynamicGraph>
|
||||
NodeBasedDynamicGraphFromImportEdges(int number_of_nodes, std::vector<ImportEdge> &input_edge_list)
|
||||
{
|
||||
static_assert(sizeof(NodeBasedEdgeData) == 16, "changing node based edge data size changes memory consumption");
|
||||
std::sort(input_edge_list.begin(), input_edge_list.end());
|
||||
|
||||
// TODO: remove duplicate edges
|
||||
DeallocatingVector<NodeBasedDynamicGraph::InputEdge> edges_list;
|
||||
NodeBasedDynamicGraph::InputEdge edge;
|
||||
for (const ImportEdge &import_edge : input_edge_list)
|
||||
{
|
||||
// TODO: give ImportEdge a proper c'tor to use emplace_back's below
|
||||
if (!import_edge.isForward())
|
||||
{
|
||||
edge.source = import_edge.target();
|
||||
edge.target = import_edge.source();
|
||||
edge.data.backward = import_edge.isForward();
|
||||
edge.data.forward = import_edge.isBackward();
|
||||
}
|
||||
else
|
||||
{
|
||||
edge.source = import_edge.source();
|
||||
edge.target = import_edge.target();
|
||||
edge.data.forward = import_edge.isForward();
|
||||
edge.data.backward = import_edge.isBackward();
|
||||
}
|
||||
|
||||
if (edge.source == edge.target)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
edge.data.distance = (std::max)((int)import_edge.weight(), 1);
|
||||
BOOST_ASSERT(edge.data.distance > 0);
|
||||
edge.data.shortcut = false;
|
||||
edge.data.roundabout = import_edge.isRoundabout();
|
||||
edge.data.ignore_in_grid = import_edge.ignoreInGrid();
|
||||
edge.data.nameID = import_edge.name();
|
||||
edge.data.type = import_edge.type();
|
||||
edge.data.isAccessRestricted = import_edge.isAccessRestricted();
|
||||
edge.data.contraFlow = import_edge.isContraFlow();
|
||||
edges_list.push_back(edge);
|
||||
|
||||
if (!import_edge.IsSplit())
|
||||
{
|
||||
using std::swap; // enable ADL
|
||||
swap(edge.source, edge.target);
|
||||
edge.data.SwapDirectionFlags();
|
||||
edges_list.push_back(edge);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(edges_list.begin(), edges_list.end());
|
||||
auto graph = std::make_shared<NodeBasedDynamicGraph>(number_of_nodes, edges_list);
|
||||
|
||||
edges_list.clear();
|
||||
BOOST_ASSERT(0 == edges_list.size());
|
||||
|
||||
return graph;
|
||||
}
|
||||
|
||||
#endif // __NODE_BASED_GRAPH_H__
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
|
||||
#ifndef _NODE_COORDS_H
|
||||
#define _NODE_COORDS_H
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <climits>
|
||||
|
||||
#include "../typedefs.h"
|
||||
|
||||
template<typename NodeT>
|
||||
struct NodeCoords {
|
||||
typedef unsigned key_type; //type of NodeID
|
||||
typedef int value_type; //type of lat,lons
|
||||
|
||||
NodeCoords(int _lat, int _lon, NodeT _id) : lat(_lat), lon(_lon), id(_id) {}
|
||||
NodeCoords() : lat(INT_MAX), lon(INT_MAX), id(UINT_MAX) {}
|
||||
int lat;
|
||||
int lon;
|
||||
NodeT id;
|
||||
|
||||
static NodeCoords<NodeT> min_value()
|
||||
{
|
||||
return NodeCoords<NodeT>(-90*100000,-180*100000,numeric_limits<NodeT>::min());
|
||||
}
|
||||
static NodeCoords<NodeT> max_value()
|
||||
{
|
||||
return NodeCoords<NodeT>(90*100000, 180*100000, numeric_limits<NodeT>::max());
|
||||
}
|
||||
|
||||
value_type operator[](size_t n) const {
|
||||
switch(n) {
|
||||
case 1:
|
||||
return lat;
|
||||
break;
|
||||
case 0:
|
||||
return lon;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
return UINT_MAX;
|
||||
break;
|
||||
}
|
||||
assert(false);
|
||||
return UINT_MAX;
|
||||
}
|
||||
};
|
||||
|
||||
#endif //_NODE_COORDS_H
|
||||
@@ -1,90 +0,0 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
|
||||
#ifndef NODEINFORMATIONHELPDESK_H_
|
||||
#define NODEINFORMATIONHELPDESK_H_
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#include "../typedefs.h"
|
||||
#include "NNGrid.h"
|
||||
#include "PhantomNodes.h"
|
||||
|
||||
class NodeInformationHelpDesk{
|
||||
public:
|
||||
NodeInformationHelpDesk(const char* ramIndexInput, const char* fileIndexInput, unsigned _numberOfNodes) : numberOfNodes(_numberOfNodes) {
|
||||
readOnlyGrid = new ReadOnlyGrid(ramIndexInput,fileIndexInput);
|
||||
int2ExtNodeMap = new vector<_Coordinate>();
|
||||
int2ExtNodeMap->reserve(numberOfNodes);
|
||||
assert(0 == int2ExtNodeMap->size());
|
||||
}
|
||||
|
||||
~NodeInformationHelpDesk() {
|
||||
delete int2ExtNodeMap;
|
||||
delete readOnlyGrid;
|
||||
}
|
||||
void initNNGrid(ifstream& in) {
|
||||
while(!in.eof()) {
|
||||
NodeInfo b;
|
||||
in.read((char *)&b, sizeof(b));
|
||||
int2ExtNodeMap->push_back(_Coordinate(b.lat, b.lon));
|
||||
}
|
||||
in.close();
|
||||
readOnlyGrid->OpenIndexFiles();
|
||||
}
|
||||
|
||||
inline int getLatitudeOfNode(const NodeID node) const {
|
||||
return int2ExtNodeMap->at(node).lat;
|
||||
}
|
||||
|
||||
inline int getLongitudeOfNode(const NodeID node) const { return int2ExtNodeMap->at(node).lon; }
|
||||
|
||||
NodeID getNumberOfNodes() const { return numberOfNodes; }
|
||||
NodeID getNumberOfNodes2() const { return int2ExtNodeMap->size(); }
|
||||
|
||||
inline void FindNearestNodeCoordForLatLon(const _Coordinate& coord, _Coordinate& result) {
|
||||
readOnlyGrid->FindNearestCoordinateOnEdgeInNodeBasedGraph(coord, result);
|
||||
}
|
||||
bool FindPhantomNodeForCoordinate( const _Coordinate & location, PhantomNode & resultNode) {
|
||||
return readOnlyGrid->FindPhantomNodeForCoordinate(location, resultNode);
|
||||
}
|
||||
|
||||
inline bool FindRoutingStarts(const _Coordinate &start, const _Coordinate &target, PhantomNodes & phantomNodes) {
|
||||
readOnlyGrid->FindRoutingStarts(start, target, phantomNodes);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool GetStartAndDestNodesOfEdge(const _Coordinate& coord, NodesOfEdge& nodesOfEdge) {
|
||||
return readOnlyGrid->GetEdgeBasedStartNode(coord, nodesOfEdge);
|
||||
}
|
||||
|
||||
inline void FindNearestPointOnEdge(const _Coordinate & input, _Coordinate& output){
|
||||
readOnlyGrid->FindNearestPointOnEdge(input, output);
|
||||
}
|
||||
|
||||
private:
|
||||
vector<_Coordinate> * int2ExtNodeMap;
|
||||
ReadOnlyGrid * readOnlyGrid;
|
||||
unsigned numberOfNodes;
|
||||
};
|
||||
|
||||
#endif /*NODEINFORMATIONHELPDESK_H_*/
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ORIGINAL_EDGE_DATA_H
|
||||
#define ORIGINAL_EDGE_DATA_H
|
||||
|
||||
#include "TurnInstructions.h"
|
||||
#include "../typedefs.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
struct OriginalEdgeData
|
||||
{
|
||||
explicit OriginalEdgeData(NodeID via_node,
|
||||
unsigned name_id,
|
||||
TurnInstruction turn_instruction,
|
||||
bool compressed_geometry)
|
||||
: via_node(via_node), name_id(name_id), turn_instruction(turn_instruction),
|
||||
compressed_geometry(compressed_geometry)
|
||||
{
|
||||
}
|
||||
|
||||
OriginalEdgeData()
|
||||
: via_node(std::numeric_limits<unsigned>::max()),
|
||||
name_id(std::numeric_limits<unsigned>::max()),
|
||||
turn_instruction(TurnInstruction::NoTurn), compressed_geometry(false)
|
||||
{
|
||||
}
|
||||
|
||||
NodeID via_node;
|
||||
unsigned name_id;
|
||||
TurnInstruction turn_instruction;
|
||||
bool compressed_geometry;
|
||||
};
|
||||
|
||||
#endif // ORIGINAL_EDGE_DATA_H
|
||||
@@ -1,535 +0,0 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
|
||||
#ifndef PBFPARSER_H_
|
||||
#define PBFPARSER_H_
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#include "BaseParser.h"
|
||||
|
||||
#include "pbf-proto/fileformat.pb.h"
|
||||
#include "pbf-proto/osmformat.pb.h"
|
||||
#include "../typedefs.h"
|
||||
#include "HashTable.h"
|
||||
#include "ExtractorStructs.h"
|
||||
#include "ConcurrentQueue.h"
|
||||
|
||||
class PBFParser : public BaseParser<_Node, _RawRestrictionContainer, _Way> {
|
||||
|
||||
enum EntityType {
|
||||
TypeNode = 1,
|
||||
TypeWay = 2,
|
||||
TypeRelation = 4,
|
||||
TypeDenseNode = 8
|
||||
} ;
|
||||
|
||||
enum Endianness {
|
||||
LittleEndian = 1,
|
||||
BigEndian = 2
|
||||
};
|
||||
|
||||
struct _ThreadData {
|
||||
int currentGroupID;
|
||||
int currentEntityID;
|
||||
short entityTypeIndicator;
|
||||
|
||||
OSMPBF::BlobHeader PBFBlobHeader;
|
||||
OSMPBF::Blob PBFBlob;
|
||||
|
||||
OSMPBF::HeaderBlock PBFHeaderBlock;
|
||||
OSMPBF::PrimitiveBlock PBFprimitiveBlock;
|
||||
|
||||
std::vector<char> charBuffer;
|
||||
};
|
||||
|
||||
public:
|
||||
PBFParser(const char * fileName)
|
||||
: threadDataQueue( new ConcurrentQueue<_ThreadData*>(25) ) { /* Max 25 items in queue */
|
||||
GOOGLE_PROTOBUF_VERIFY_VERSION;
|
||||
input.open(fileName, std::ios::in | std::ios::binary);
|
||||
|
||||
if (!input) {
|
||||
std::cerr << fileName << ": File not found." << std::endl;
|
||||
}
|
||||
|
||||
blockCount = 0;
|
||||
groupCount = 0;
|
||||
|
||||
//Dummy initialization
|
||||
wayCallback = NULL; nodeCallback = NULL;
|
||||
addressCallback = NULL; restrictionCallback = NULL;
|
||||
}
|
||||
|
||||
bool RegisterCallbacks(bool (*nodeCallbackPointer)(_Node), bool (*restrictionCallbackPointer)(_RawRestrictionContainer), bool (*wayCallbackPointer)(_Way),bool (*addressCallbackPointer)(_Node, HashTable<std::string, std::string>) ) {
|
||||
nodeCallback = *nodeCallbackPointer;
|
||||
wayCallback = *wayCallbackPointer;
|
||||
restrictionCallback = *restrictionCallbackPointer;
|
||||
addressCallback = *addressCallbackPointer;
|
||||
return true;
|
||||
}
|
||||
|
||||
~PBFParser() {
|
||||
if(input.is_open())
|
||||
input.close();
|
||||
|
||||
// Clean up any leftover ThreadData objects in the queue
|
||||
_ThreadData* td;
|
||||
while (threadDataQueue->try_pop(td)) {
|
||||
delete td;
|
||||
}
|
||||
delete threadDataQueue;
|
||||
|
||||
google::protobuf::ShutdownProtobufLibrary();
|
||||
|
||||
#ifndef NDEBUG
|
||||
std::cout << "[info] blocks: " << blockCount << std::endl;
|
||||
std::cout << "[info] groups: " << groupCount << std::endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Init() {
|
||||
_ThreadData initData;
|
||||
/** read Header */
|
||||
if(!readPBFBlobHeader(input, &initData)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(readBlob(input, &initData)) {
|
||||
if(!initData.PBFHeaderBlock.ParseFromArray(&(initData.charBuffer[0]), initData.charBuffer.size() ) ) {
|
||||
std::cerr << "[error] Header not parseable!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
for(int i = 0; i < initData.PBFHeaderBlock.required_features_size(); i++) {
|
||||
const std::string& feature = initData.PBFHeaderBlock.required_features( i );
|
||||
bool supported = false;
|
||||
if ( feature == "OsmSchema-V0.6" )
|
||||
supported = true;
|
||||
else if ( feature == "DenseNodes" )
|
||||
supported = true;
|
||||
|
||||
if ( !supported ) {
|
||||
std::cerr << "[error] required feature not supported: " << feature.data() << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
std::cerr << "[error] blob not loaded!" << std::endl;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ReadData() {
|
||||
bool keepRunning = true;
|
||||
do {
|
||||
_ThreadData *threadData = new _ThreadData();
|
||||
keepRunning = readNextBlock(input, threadData);
|
||||
|
||||
if (keepRunning)
|
||||
threadDataQueue->push(threadData);
|
||||
else
|
||||
threadDataQueue->push(NULL); // No more data to read, parse stops when NULL encountered
|
||||
} while(keepRunning);
|
||||
}
|
||||
|
||||
void ParseData() {
|
||||
while (1) {
|
||||
_ThreadData *threadData;
|
||||
threadDataQueue->wait_and_pop(threadData);
|
||||
if (threadData == NULL) {
|
||||
cout << "Parse Data Thread Finished" << endl;
|
||||
threadDataQueue->push(NULL); // Signal end of data for other threads
|
||||
break;
|
||||
}
|
||||
|
||||
loadBlock(threadData);
|
||||
for(int i = 0; i < threadData->PBFprimitiveBlock.primitivegroup_size(); i++) {
|
||||
threadData->currentGroupID = i;
|
||||
loadGroup(threadData);
|
||||
|
||||
if(threadData->entityTypeIndicator == TypeNode)
|
||||
parseNode(threadData);
|
||||
if(threadData->entityTypeIndicator == TypeWay)
|
||||
parseWay(threadData);
|
||||
if(threadData->entityTypeIndicator == TypeRelation)
|
||||
parseRelation(threadData);
|
||||
if(threadData->entityTypeIndicator == TypeDenseNode)
|
||||
parseDenseNode(threadData);
|
||||
}
|
||||
|
||||
delete threadData;
|
||||
threadData = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool Parse() {
|
||||
// Start the read and parse threads
|
||||
boost::thread readThread(boost::bind(&PBFParser::ReadData, this));
|
||||
boost::thread parseThread(boost::bind(&PBFParser::ParseData, this));
|
||||
|
||||
// Wait for the threads to finish
|
||||
readThread.join();
|
||||
parseThread.join();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void parseDenseNode(_ThreadData * threadData) {
|
||||
const OSMPBF::DenseNodes& dense = threadData->PBFprimitiveBlock.primitivegroup( threadData->currentGroupID ).dense();
|
||||
int denseTagIndex = 0;
|
||||
int m_lastDenseID = 0;
|
||||
int m_lastDenseLatitude = 0;
|
||||
int m_lastDenseLongitude = 0;
|
||||
|
||||
for(int i = 0; i < dense.id_size(); i++) {
|
||||
HashTable<std::string, std::string> keyVals;
|
||||
m_lastDenseID += dense.id( i );
|
||||
m_lastDenseLatitude += dense.lat( i );
|
||||
m_lastDenseLongitude += dense.lon( i );
|
||||
_Node n;
|
||||
n.id = m_lastDenseID;
|
||||
n.lat = 100000*( ( double ) m_lastDenseLatitude * threadData->PBFprimitiveBlock.granularity() +threadData-> PBFprimitiveBlock.lat_offset() ) / NANO;
|
||||
n.lon = 100000*( ( double ) m_lastDenseLongitude * threadData->PBFprimitiveBlock.granularity() + threadData->PBFprimitiveBlock.lon_offset() ) / NANO;
|
||||
while (denseTagIndex < dense.keys_vals_size()) {
|
||||
int tagValue = dense.keys_vals( denseTagIndex );
|
||||
if(tagValue == 0) {
|
||||
denseTagIndex++;
|
||||
break;
|
||||
}
|
||||
int keyValue = dense.keys_vals ( denseTagIndex+1 );
|
||||
std::string key = threadData->PBFprimitiveBlock.stringtable().s(tagValue).data();
|
||||
std::string value = threadData->PBFprimitiveBlock.stringtable().s(keyValue).data();
|
||||
keyVals.Add(key, value);
|
||||
denseTagIndex += 2;
|
||||
}
|
||||
if(!(*addressCallback)(n, keyVals))
|
||||
std::cerr << "[PBFParser] adress not parsed" << std::endl;
|
||||
|
||||
if(!(*nodeCallback)(n))
|
||||
std::cerr << "[PBFParser] dense node not parsed" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void parseNode(_ThreadData * threadData) {
|
||||
_Node n;
|
||||
if(!(*nodeCallback)(n))
|
||||
std::cerr << "[PBFParser] simple node not parsed" << std::endl;
|
||||
}
|
||||
|
||||
void parseRelation(_ThreadData * threadData) {
|
||||
const OSMPBF::PrimitiveGroup& group = threadData->PBFprimitiveBlock.primitivegroup( threadData->currentGroupID );
|
||||
for(int i = 0; i < group.relations_size(); i++ ) {
|
||||
const OSMPBF::Relation& inputRelation = threadData->PBFprimitiveBlock.primitivegroup( threadData->currentGroupID ).relations(i);
|
||||
bool isRestriction = false;
|
||||
bool isOnlyRestriction = false;
|
||||
for(int k = 0; k < inputRelation.keys_size(); k++) {
|
||||
const std::string key = threadData->PBFprimitiveBlock.stringtable().s(inputRelation.keys(k));
|
||||
const std::string val = threadData->PBFprimitiveBlock.stringtable().s(inputRelation.vals(k));
|
||||
if ("type" == key && "restriction" == val) {
|
||||
isRestriction = true;
|
||||
}
|
||||
if ("restriction" == key) {
|
||||
if(val.find("only_") == 0)
|
||||
isOnlyRestriction = true;
|
||||
}
|
||||
|
||||
}
|
||||
if(isRestriction) {
|
||||
long long lastRef = 0;
|
||||
_RawRestrictionContainer currentRestrictionContainer(isOnlyRestriction);
|
||||
for(int rolesIndex = 0; rolesIndex < inputRelation.roles_sid_size(); rolesIndex++) {
|
||||
string role(threadData->PBFprimitiveBlock.stringtable().s( inputRelation.roles_sid( rolesIndex ) ).data());
|
||||
lastRef += inputRelation.memids(rolesIndex);
|
||||
|
||||
if(false == ("from" == role || "to" == role || "via" == role)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(inputRelation.types(rolesIndex)) {
|
||||
case 0: //node
|
||||
if("from" == role || "to" == role) //Only via should be a node
|
||||
continue;
|
||||
assert("via" == role);
|
||||
if(UINT_MAX != currentRestrictionContainer.viaWay)
|
||||
currentRestrictionContainer.viaWay = UINT_MAX;
|
||||
assert(UINT_MAX == currentRestrictionContainer.viaWay);
|
||||
currentRestrictionContainer.restriction.viaNode = lastRef;
|
||||
break;
|
||||
case 1: //way
|
||||
assert("from" == role || "to" == role || "via" == role);
|
||||
if("from" == role) {
|
||||
currentRestrictionContainer.fromWay = lastRef;
|
||||
}
|
||||
if ("to" == role) {
|
||||
currentRestrictionContainer.toWay = lastRef;
|
||||
}
|
||||
if ("via" == role) {
|
||||
assert(currentRestrictionContainer.restriction.toNode == UINT_MAX);
|
||||
currentRestrictionContainer.viaWay = lastRef;
|
||||
}
|
||||
break;
|
||||
case 2: //relation, not used. relations relating to relations are evil.
|
||||
continue;
|
||||
assert(false);
|
||||
break;
|
||||
|
||||
default: //should not happen
|
||||
cout << "unknown";
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// if(UINT_MAX != currentRestriction.viaNode) {
|
||||
// cout << "restr from " << currentRestriction.from << " via ";
|
||||
// cout << "node " << currentRestriction.viaNode;
|
||||
// cout << " to " << currentRestriction.to << endl;
|
||||
// }
|
||||
if(!(*restrictionCallback)(currentRestrictionContainer))
|
||||
std::cerr << "[PBFParser] relation not parsed" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void parseWay(_ThreadData * threadData) {
|
||||
if( threadData->PBFprimitiveBlock.primitivegroup( threadData->currentGroupID ).ways_size() > 0) {
|
||||
for(int i = 0; i < threadData->PBFprimitiveBlock.primitivegroup( threadData->currentGroupID ).ways_size(); i++) {
|
||||
const OSMPBF::Way& inputWay = threadData->PBFprimitiveBlock.primitivegroup( threadData->currentGroupID ).ways( i );
|
||||
_Way w;
|
||||
w.id = inputWay.id();
|
||||
unsigned pathNode(0);
|
||||
for(int i = 0; i < inputWay.refs_size(); i++) {
|
||||
pathNode += inputWay.refs(i);
|
||||
w.path.push_back(pathNode);
|
||||
}
|
||||
assert(inputWay.keys_size() == inputWay.vals_size());
|
||||
for(int i = 0; i < inputWay.keys_size(); i++) {
|
||||
const std::string key = threadData->PBFprimitiveBlock.stringtable().s(inputWay.keys(i));
|
||||
const std::string val = threadData->PBFprimitiveBlock.stringtable().s(inputWay.vals(i));
|
||||
w.keyVals.Add(key, val);
|
||||
}
|
||||
|
||||
if(!(*wayCallback)(w)) {
|
||||
std::cerr << "[PBFParser] way not parsed" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void loadGroup(_ThreadData * threadData) {
|
||||
groupCount++;
|
||||
|
||||
const OSMPBF::PrimitiveGroup& group = threadData->PBFprimitiveBlock.primitivegroup( threadData->currentGroupID );
|
||||
threadData->entityTypeIndicator = 0;
|
||||
if ( group.nodes_size() != 0 ) {
|
||||
threadData->entityTypeIndicator = TypeNode;
|
||||
}
|
||||
if ( group.ways_size() != 0 ) {
|
||||
threadData->entityTypeIndicator = TypeWay;
|
||||
}
|
||||
if ( group.relations_size() != 0 ) {
|
||||
threadData->entityTypeIndicator = TypeRelation;
|
||||
}
|
||||
if ( group.has_dense() ) {
|
||||
threadData->entityTypeIndicator = TypeDenseNode;
|
||||
assert( group.dense().id_size() != 0 );
|
||||
}
|
||||
assert( threadData->entityTypeIndicator != 0 );
|
||||
}
|
||||
|
||||
void loadBlock(_ThreadData * threadData) {
|
||||
blockCount++;
|
||||
threadData->currentGroupID = 0;
|
||||
threadData->currentEntityID = 0;
|
||||
}
|
||||
|
||||
/* Reverses Network Byte Order into something usable */
|
||||
inline unsigned swapEndian(unsigned x) {
|
||||
if(getMachineEndianness() == LittleEndian)
|
||||
return ( (x>>24) | ((x<<8) & 0x00FF0000) | ((x>>8) & 0x0000FF00) | (x<<24) );
|
||||
return x;
|
||||
}
|
||||
|
||||
bool readPBFBlobHeader(std::fstream& stream, _ThreadData * threadData) {
|
||||
int size(0);
|
||||
stream.read((char *)&size, sizeof(int));
|
||||
size = swapEndian(size);
|
||||
if(stream.eof()) {
|
||||
return false;
|
||||
}
|
||||
if ( size > MAX_BLOB_HEADER_SIZE || size < 0 ) {
|
||||
return false;
|
||||
}
|
||||
char *data = (char*)malloc(size);
|
||||
stream.read(data, size*sizeof(data[0]));
|
||||
|
||||
if ( !(threadData->PBFBlobHeader).ParseFromArray( data, size ) ){
|
||||
free(data);
|
||||
return false;
|
||||
}
|
||||
free(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unpackZLIB(std::fstream & stream, _ThreadData * threadData) {
|
||||
unsigned rawSize = threadData->PBFBlob.raw_size();
|
||||
char* unpackedDataArray = (char*)malloc(rawSize);
|
||||
z_stream compressedDataStream;
|
||||
compressedDataStream.next_in = ( unsigned char* ) threadData->PBFBlob.zlib_data().data();
|
||||
compressedDataStream.avail_in = threadData->PBFBlob.zlib_data().size();
|
||||
compressedDataStream.next_out = ( unsigned char* ) unpackedDataArray;
|
||||
compressedDataStream.avail_out = rawSize;
|
||||
compressedDataStream.zalloc = Z_NULL;
|
||||
compressedDataStream.zfree = Z_NULL;
|
||||
compressedDataStream.opaque = Z_NULL;
|
||||
int ret = inflateInit( &compressedDataStream );
|
||||
if ( ret != Z_OK ) {
|
||||
std::cerr << "[error] failed to init zlib stream" << std::endl;
|
||||
free(unpackedDataArray);
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = inflate( &compressedDataStream, Z_FINISH );
|
||||
if ( ret != Z_STREAM_END ) {
|
||||
std::cerr << "[error] failed to inflate zlib stream" << std::endl;
|
||||
std::cerr << "[error] Error type: " << ret << std::endl;
|
||||
free(unpackedDataArray);
|
||||
return false;
|
||||
}
|
||||
|
||||
ret = inflateEnd( &compressedDataStream );
|
||||
if ( ret != Z_OK ) {
|
||||
std::cerr << "[error] failed to deinit zlib stream" << std::endl;
|
||||
free(unpackedDataArray);
|
||||
return false;
|
||||
}
|
||||
|
||||
threadData->charBuffer.clear(); threadData->charBuffer.resize(rawSize);
|
||||
for(unsigned i = 0; i < rawSize; i++) {
|
||||
threadData->charBuffer[i] = unpackedDataArray[i];
|
||||
}
|
||||
free(unpackedDataArray);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool unpackLZMA(std::fstream & stream, _ThreadData * threadData) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool readBlob(std::fstream& stream, _ThreadData * threadData) {
|
||||
if(stream.eof())
|
||||
return false;
|
||||
|
||||
int size = threadData->PBFBlobHeader.datasize();
|
||||
if ( size < 0 || size > MAX_BLOB_SIZE ) {
|
||||
std::cerr << "[error] invalid Blob size:" << size << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
char* data = (char*)malloc(size);
|
||||
stream.read(data, sizeof(data[0])*size);
|
||||
|
||||
if ( !threadData->PBFBlob.ParseFromArray( data, size ) ) {
|
||||
std::cerr << "[error] failed to parse blob" << std::endl;
|
||||
free(data);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( threadData->PBFBlob.has_raw() ) {
|
||||
const std::string& data = threadData->PBFBlob.raw();
|
||||
threadData->charBuffer.clear();
|
||||
threadData->charBuffer.resize( data.size() );
|
||||
for ( unsigned i = 0; i < data.size(); i++ ) {
|
||||
threadData->charBuffer[i] = data[i];
|
||||
}
|
||||
} else if ( threadData->PBFBlob.has_zlib_data() ) {
|
||||
if ( !unpackZLIB(stream, threadData) ) {
|
||||
std::cerr << "[error] zlib data encountered that could not be unpacked" << std::endl;
|
||||
free(data);
|
||||
return false;
|
||||
}
|
||||
} else if ( threadData->PBFBlob.has_lzma_data() ) {
|
||||
if ( !unpackLZMA(stream, threadData) )
|
||||
std::cerr << "[error] lzma data encountered that could not be unpacked" << std::endl;
|
||||
free(data);
|
||||
return false;
|
||||
} else {
|
||||
std::cerr << "[error] Blob contains no data" << std::endl;
|
||||
free(data);
|
||||
return false;
|
||||
}
|
||||
free(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool readNextBlock(std::fstream& stream, _ThreadData * threadData) {
|
||||
if(stream.eof()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !readPBFBlobHeader(stream, threadData) )
|
||||
return false;
|
||||
|
||||
if ( threadData->PBFBlobHeader.type() != "OSMData" ) {
|
||||
std::cerr << "[error] invalid block type, found" << threadData->PBFBlobHeader.type().data() << "instead of OSMData" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( !readBlob(stream, threadData) )
|
||||
return false;
|
||||
|
||||
if ( !threadData->PBFprimitiveBlock.ParseFromArray( &(threadData->charBuffer[0]), threadData-> charBuffer.size() ) ) {
|
||||
std::cerr << "[error] failed to parse PrimitiveBlock" << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static Endianness getMachineEndianness() {
|
||||
int i(1);
|
||||
char *p = (char *) &i;
|
||||
if (p[0] == 1)
|
||||
return LittleEndian;
|
||||
return BigEndian;
|
||||
}
|
||||
|
||||
static const int NANO = 1000 * 1000 * 1000;
|
||||
static const int MAX_BLOB_HEADER_SIZE = 64 * 1024;
|
||||
static const int MAX_BLOB_SIZE = 32 * 1024 * 1024;
|
||||
|
||||
/* counting the number of read blocks and groups */
|
||||
unsigned groupCount;
|
||||
unsigned blockCount;
|
||||
|
||||
/* Function pointer for nodes */
|
||||
bool (*nodeCallback)(_Node);
|
||||
bool (*wayCallback)(_Way);
|
||||
bool (*restrictionCallback)(_RawRestrictionContainer);
|
||||
bool (*addressCallback)(_Node, HashTable<std::string, std::string>);
|
||||
/* the input stream to parse */
|
||||
std::fstream input;
|
||||
|
||||
/* ThreadData Queue */
|
||||
ConcurrentQueue < _ThreadData* >* threadDataQueue;
|
||||
};
|
||||
|
||||
#endif /* PBFPARSER_H_ */
|
||||
@@ -1,85 +1,98 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef PERCENT_H
|
||||
#define PERCENT_H
|
||||
|
||||
#include "../Util/OpenMPWrapper.h"
|
||||
#include <iostream>
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
class Percent
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Constructor.
|
||||
* @param maxValue the value that corresponds to 100%
|
||||
* @param step the progress is shown in steps of 'step' percent
|
||||
*/
|
||||
Percent(unsigned maxValue, unsigned step = 5) {
|
||||
reinit(maxValue, step);
|
||||
public:
|
||||
explicit Percent(unsigned max_value, unsigned step = 5) { reinit(max_value, step); }
|
||||
|
||||
// Reinitializes
|
||||
void reinit(unsigned max_value, unsigned step = 5)
|
||||
{
|
||||
m_max_value = max_value;
|
||||
m_current_value = 0;
|
||||
m_percent_interval = m_max_value / 100;
|
||||
m_next_threshold = m_percent_interval;
|
||||
m_last_percent = 0;
|
||||
m_step = step;
|
||||
}
|
||||
|
||||
/** Reinitializes this object. */
|
||||
void reinit(unsigned maxValue, unsigned step = 5) {
|
||||
_maxValue = maxValue;
|
||||
_current_value = 0;
|
||||
_intervalPercent = _maxValue / 100;
|
||||
_nextThreshold = _intervalPercent;
|
||||
_lastPercent = 0;
|
||||
_step = step;
|
||||
}
|
||||
|
||||
/** If there has been significant progress, display it. */
|
||||
void printStatus(unsigned currentValue) {
|
||||
if (currentValue >= _nextThreshold) {
|
||||
_nextThreshold += _intervalPercent;
|
||||
printPercent( currentValue / (double)_maxValue * 100 );
|
||||
// If there has been significant progress, display it.
|
||||
void printStatus(unsigned current_value)
|
||||
{
|
||||
if (current_value >= m_next_threshold)
|
||||
{
|
||||
m_next_threshold += m_percent_interval;
|
||||
printPercent(current_value / (double)m_max_value * 100);
|
||||
}
|
||||
if (currentValue + 1 == _maxValue)
|
||||
if (current_value + 1 == m_max_value)
|
||||
std::cout << " 100%" << std::endl;
|
||||
}
|
||||
|
||||
void printIncrement()
|
||||
{
|
||||
#pragma omp atomic
|
||||
_current_value++;
|
||||
printStatus(_current_value);
|
||||
++m_current_value;
|
||||
printStatus(m_current_value);
|
||||
}
|
||||
private:
|
||||
unsigned _current_value;
|
||||
unsigned _maxValue;
|
||||
unsigned _intervalPercent;
|
||||
unsigned _nextThreshold;
|
||||
unsigned _lastPercent;
|
||||
unsigned _step;
|
||||
|
||||
/** Displays the new progress. */
|
||||
void printPercent(double percent) {
|
||||
while (percent >= _lastPercent+_step) {
|
||||
_lastPercent+=_step;
|
||||
if (_lastPercent % 10 == 0) {
|
||||
std::cout << " " << _lastPercent << "% ";
|
||||
void printAddition(const unsigned addition)
|
||||
{
|
||||
#pragma omp atomic
|
||||
m_current_value += addition;
|
||||
printStatus(m_current_value);
|
||||
}
|
||||
|
||||
private:
|
||||
unsigned m_current_value;
|
||||
unsigned m_max_value;
|
||||
unsigned m_percent_interval;
|
||||
unsigned m_next_threshold;
|
||||
unsigned m_last_percent;
|
||||
unsigned m_step;
|
||||
|
||||
// Displays progress.
|
||||
void printPercent(double percent)
|
||||
{
|
||||
while (percent >= m_last_percent + m_step)
|
||||
{
|
||||
m_last_percent += m_step;
|
||||
if (m_last_percent % 10 == 0)
|
||||
{
|
||||
std::cout << " " << m_last_percent << "% ";
|
||||
}
|
||||
else {
|
||||
else
|
||||
{
|
||||
std::cout << ".";
|
||||
}
|
||||
std::cout.flush();
|
||||
|
||||
@@ -1,76 +1,180 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
#ifndef PHANTOMNODES_H_
|
||||
#define PHANTOMNODES_H_
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include "ExtractorStructs.h"
|
||||
*/
|
||||
|
||||
struct PhantomNode {
|
||||
PhantomNode() : edgeBasedNode(UINT_MAX), nodeBasedEdgeNameID(UINT_MAX), weight1(INT_MAX), weight2(INT_MAX) {}
|
||||
NodeID edgeBasedNode;
|
||||
unsigned nodeBasedEdgeNameID;
|
||||
int weight1;
|
||||
int weight2;
|
||||
_Coordinate location;
|
||||
void Reset() {
|
||||
edgeBasedNode = UINT_MAX;
|
||||
nodeBasedEdgeNameID = UINT_MAX;
|
||||
weight1 = INT_MAX;
|
||||
weight2 = INT_MAX;
|
||||
#ifndef PHANTOM_NODES_H
|
||||
#define PHANTOM_NODES_H
|
||||
|
||||
#include <osrm/Coordinate.h>
|
||||
#include "../Util/SimpleLogger.h"
|
||||
#include "../typedefs.h"
|
||||
|
||||
struct PhantomNode
|
||||
{
|
||||
PhantomNode() :
|
||||
forward_node_id(SPECIAL_NODEID),
|
||||
reverse_node_id(SPECIAL_NODEID),
|
||||
name_id(std::numeric_limits<unsigned>::max()),
|
||||
forward_weight(INVALID_EDGE_WEIGHT),
|
||||
reverse_weight(INVALID_EDGE_WEIGHT),
|
||||
forward_offset(0),
|
||||
reverse_offset(0),
|
||||
packed_geometry_id(SPECIAL_EDGEID),
|
||||
fwd_segment_position(0)
|
||||
{ }
|
||||
|
||||
NodeID forward_node_id;
|
||||
NodeID reverse_node_id;
|
||||
unsigned name_id;
|
||||
int forward_weight;
|
||||
int reverse_weight;
|
||||
int forward_offset;
|
||||
int reverse_offset;
|
||||
unsigned packed_geometry_id;
|
||||
FixedPointCoordinate location;
|
||||
unsigned short fwd_segment_position;
|
||||
|
||||
int GetForwardWeightPlusOffset() const
|
||||
{
|
||||
if (SPECIAL_NODEID == forward_node_id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
const int result = (forward_offset + forward_weight);
|
||||
return result;
|
||||
}
|
||||
|
||||
int GetReverseWeightPlusOffset() const
|
||||
{
|
||||
if (SPECIAL_NODEID == reverse_node_id)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
const int result = (reverse_offset + reverse_weight);
|
||||
return result;
|
||||
}
|
||||
|
||||
void Reset()
|
||||
{
|
||||
forward_node_id = SPECIAL_NODEID;
|
||||
name_id = SPECIAL_NODEID;
|
||||
forward_weight = INVALID_EDGE_WEIGHT;
|
||||
reverse_weight = INVALID_EDGE_WEIGHT;
|
||||
forward_offset = 0;
|
||||
reverse_offset = 0;
|
||||
location.Reset();
|
||||
}
|
||||
bool isBidirected() const {
|
||||
return weight2 != INT_MAX;
|
||||
|
||||
bool isBidirected() const
|
||||
{
|
||||
return (forward_node_id != SPECIAL_NODEID) &&
|
||||
(reverse_node_id != SPECIAL_NODEID);
|
||||
}
|
||||
|
||||
bool IsCompressed() const
|
||||
{
|
||||
return (forward_offset != 0) || (reverse_offset != 0);
|
||||
}
|
||||
|
||||
bool isValid(const unsigned numberOfNodes) const
|
||||
{
|
||||
return
|
||||
location.isValid() &&
|
||||
(
|
||||
(forward_node_id < numberOfNodes) ||
|
||||
(reverse_node_id < numberOfNodes)
|
||||
) &&
|
||||
(
|
||||
(forward_weight != INVALID_EDGE_WEIGHT) ||
|
||||
(reverse_weight != INVALID_EDGE_WEIGHT)
|
||||
) &&
|
||||
(name_id != std::numeric_limits<unsigned>::max()
|
||||
);
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return location.isValid() &&
|
||||
(name_id != std::numeric_limits<unsigned>::max());
|
||||
}
|
||||
|
||||
bool operator==(const PhantomNode & other) const
|
||||
{
|
||||
return location == other.location;
|
||||
}
|
||||
};
|
||||
|
||||
struct PhantomNodes {
|
||||
PhantomNode startPhantom;
|
||||
PhantomNode targetPhantom;
|
||||
void Reset() {
|
||||
startPhantom.Reset();
|
||||
targetPhantom.Reset();
|
||||
struct PhantomNodes
|
||||
{
|
||||
PhantomNode source_phantom;
|
||||
PhantomNode target_phantom;
|
||||
|
||||
void Reset()
|
||||
{
|
||||
source_phantom.Reset();
|
||||
target_phantom.Reset();
|
||||
}
|
||||
|
||||
bool PhantomsAreOnSameNodeBasedEdge() const {
|
||||
return (startPhantom.edgeBasedNode == targetPhantom.edgeBasedNode);
|
||||
bool PhantomsAreOnSameNodeBasedEdge() const
|
||||
{
|
||||
return (source_phantom.forward_node_id == target_phantom.forward_node_id);
|
||||
}
|
||||
|
||||
bool AtLeastOnePhantomNodeIsUINTMAX() const {
|
||||
return !(startPhantom.edgeBasedNode == UINT_MAX || targetPhantom.edgeBasedNode == UINT_MAX);
|
||||
bool AtLeastOnePhantomNodeIsInvalid() const
|
||||
{
|
||||
return ((source_phantom.forward_node_id == SPECIAL_NODEID) && (source_phantom.reverse_node_id == SPECIAL_NODEID)) ||
|
||||
((target_phantom.forward_node_id == SPECIAL_NODEID) && (target_phantom.reverse_node_id == SPECIAL_NODEID));
|
||||
}
|
||||
|
||||
bool PhantomNodesHaveEqualLocation() const
|
||||
{
|
||||
return source_phantom == target_phantom;
|
||||
}
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream &out, const PhantomNodes & pn){
|
||||
out << "Node1: " << pn.startPhantom.edgeBasedNode << std::endl;
|
||||
out << "Node2: " << pn.targetPhantom.edgeBasedNode << std::endl;
|
||||
out << "startCoord: " << pn.startPhantom.location << std::endl;
|
||||
out << "targetCoord: " << pn.targetPhantom.location << std::endl;
|
||||
inline std::ostream& operator<<(std::ostream &out, const PhantomNodes & pn)
|
||||
{
|
||||
out << "source_coord: " << pn.source_phantom.location << "\n";
|
||||
out << "target_coord: " << pn.target_phantom.location << std::endl;
|
||||
return out;
|
||||
}
|
||||
|
||||
struct NodesOfEdge {
|
||||
NodeID edgeBasedNode;
|
||||
double ratio;
|
||||
_Coordinate projectedPoint;
|
||||
};
|
||||
inline std::ostream& operator<<(std::ostream &out, const PhantomNode & pn)
|
||||
{
|
||||
out << "node1: " << pn.forward_node_id << ", " <<
|
||||
"node2: " << pn.reverse_node_id << ", " <<
|
||||
"name: " << pn.name_id << ", " <<
|
||||
"fwd-w: " << pn.forward_weight << ", " <<
|
||||
"rev-w: " << pn.reverse_weight << ", " <<
|
||||
"fwd-o: " << pn.forward_offset << ", " <<
|
||||
"rev-o: " << pn.reverse_offset << ", " <<
|
||||
"geom: " << pn.packed_geometry_id << ", " <<
|
||||
"pos: " << pn.fwd_segment_position << ", " <<
|
||||
"loc: " << pn.location;
|
||||
return out;
|
||||
}
|
||||
|
||||
#endif /* PHANTOMNODES_H_ */
|
||||
#endif // PHANTOM_NODES_H
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef QUERYEDGE_H_
|
||||
#define QUERYEDGE_H_
|
||||
|
||||
#include "../typedefs.h"
|
||||
|
||||
struct QueryEdge
|
||||
{
|
||||
NodeID source;
|
||||
NodeID target;
|
||||
struct EdgeData
|
||||
{
|
||||
NodeID id : 31;
|
||||
bool shortcut : 1;
|
||||
int distance : 30;
|
||||
bool forward : 1;
|
||||
bool backward : 1;
|
||||
} data;
|
||||
|
||||
bool operator<(const QueryEdge &right) const
|
||||
{
|
||||
if (source != right.source)
|
||||
{
|
||||
return source < right.source;
|
||||
}
|
||||
return target < right.target;
|
||||
}
|
||||
|
||||
bool operator==(const QueryEdge &right) const
|
||||
{
|
||||
return (source == right.source && target == right.target &&
|
||||
data.distance == right.data.distance && data.shortcut == right.data.shortcut &&
|
||||
data.forward == right.data.forward && data.backward == right.data.backward &&
|
||||
data.id == right.data.id);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* QUERYEDGE_H_ */
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef QUERY_NODE_H
|
||||
#define QUERY_NODE_H
|
||||
|
||||
#include "../typedefs.h"
|
||||
|
||||
#include <osrm/Coordinate.h>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <limits>
|
||||
|
||||
struct NodeInfo
|
||||
{
|
||||
typedef NodeID key_type; // type of NodeID
|
||||
typedef int value_type; // type of lat,lons
|
||||
|
||||
NodeInfo(int lat, int lon, NodeID id) : lat(lat), lon(lon), id(id) {}
|
||||
NodeInfo()
|
||||
: lat(std::numeric_limits<int>::max()), lon(std::numeric_limits<int>::max()),
|
||||
id(std::numeric_limits<unsigned>::max())
|
||||
{
|
||||
}
|
||||
|
||||
int lat;
|
||||
int lon;
|
||||
NodeID id;
|
||||
|
||||
static NodeInfo min_value()
|
||||
{
|
||||
return NodeInfo(-90 * COORDINATE_PRECISION,
|
||||
-180 * COORDINATE_PRECISION,
|
||||
std::numeric_limits<NodeID>::min());
|
||||
}
|
||||
|
||||
static NodeInfo max_value()
|
||||
{
|
||||
return NodeInfo(90 * COORDINATE_PRECISION,
|
||||
180 * COORDINATE_PRECISION,
|
||||
std::numeric_limits<NodeID>::max());
|
||||
}
|
||||
|
||||
value_type operator[](const std::size_t n) const
|
||||
{
|
||||
switch (n)
|
||||
{
|
||||
case 1:
|
||||
return lat;
|
||||
case 0:
|
||||
return lon;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
BOOST_ASSERT_MSG(false, "should not happen");
|
||||
return std::numeric_limits<unsigned>::max();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // QUERY_NODE_H
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef RAW_ROUTE_DATA_H
|
||||
#define RAW_ROUTE_DATA_H
|
||||
|
||||
#include "../DataStructures/PhantomNodes.h"
|
||||
#include "../DataStructures/TurnInstructions.h"
|
||||
#include "../typedefs.h"
|
||||
|
||||
#include <osrm/Coordinate.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <vector>
|
||||
|
||||
struct PathData
|
||||
{
|
||||
PathData()
|
||||
: node(std::numeric_limits<unsigned>::max()), name_id(std::numeric_limits<unsigned>::max()),
|
||||
segment_duration(std::numeric_limits<unsigned>::max()),
|
||||
turn_instruction(TurnInstruction::NoTurn)
|
||||
{
|
||||
}
|
||||
|
||||
PathData(NodeID node, unsigned name_id, TurnInstruction turn_instruction, unsigned segment_duration)
|
||||
: node(node), name_id(name_id), segment_duration(segment_duration), turn_instruction(turn_instruction)
|
||||
{
|
||||
}
|
||||
NodeID node;
|
||||
unsigned name_id;
|
||||
unsigned segment_duration;
|
||||
TurnInstruction turn_instruction;
|
||||
};
|
||||
|
||||
struct RawRouteData
|
||||
{
|
||||
std::vector<std::vector<PathData>> unpacked_path_segments;
|
||||
std::vector<PathData> unpacked_alternative;
|
||||
std::vector<PhantomNodes> segment_end_coordinates;
|
||||
std::vector<FixedPointCoordinate> raw_via_node_coordinates;
|
||||
unsigned check_sum;
|
||||
int shortest_path_length;
|
||||
int alternative_path_length;
|
||||
bool source_traversed_in_reverse;
|
||||
bool target_traversed_in_reverse;
|
||||
bool alt_source_traversed_in_reverse;
|
||||
bool alt_target_traversed_in_reverse;
|
||||
|
||||
RawRouteData()
|
||||
: check_sum(std::numeric_limits<unsigned>::max()),
|
||||
shortest_path_length(std::numeric_limits<int>::max()),
|
||||
alternative_path_length(std::numeric_limits<int>::max()),
|
||||
source_traversed_in_reverse(false), target_traversed_in_reverse(false),
|
||||
alt_source_traversed_in_reverse(false), alt_target_traversed_in_reverse(false)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#endif // RAW_ROUTE_DATA_H
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef RESTRICTION_H
|
||||
#define RESTRICTION_H
|
||||
|
||||
#include "../typedefs.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
struct TurnRestriction
|
||||
{
|
||||
NodeID viaNode;
|
||||
NodeID fromNode;
|
||||
NodeID toNode;
|
||||
struct Bits
|
||||
{ // mostly unused
|
||||
Bits()
|
||||
: isOnly(false), unused1(false), unused2(false), unused3(false), unused4(false),
|
||||
unused5(false), unused6(false), unused7(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool isOnly : 1;
|
||||
bool unused1 : 1;
|
||||
bool unused2 : 1;
|
||||
bool unused3 : 1;
|
||||
bool unused4 : 1;
|
||||
bool unused5 : 1;
|
||||
bool unused6 : 1;
|
||||
bool unused7 : 1;
|
||||
} flags;
|
||||
|
||||
explicit TurnRestriction(NodeID viaNode)
|
||||
: viaNode(viaNode), fromNode(std::numeric_limits<unsigned>::max()),
|
||||
toNode(std::numeric_limits<unsigned>::max())
|
||||
{
|
||||
}
|
||||
|
||||
explicit TurnRestriction(const bool isOnly = false)
|
||||
: viaNode(std::numeric_limits<unsigned>::max()),
|
||||
fromNode(std::numeric_limits<unsigned>::max()),
|
||||
toNode(std::numeric_limits<unsigned>::max())
|
||||
{
|
||||
flags.isOnly = isOnly;
|
||||
}
|
||||
};
|
||||
|
||||
struct InputRestrictionContainer
|
||||
{
|
||||
EdgeID fromWay;
|
||||
EdgeID toWay;
|
||||
unsigned viaNode;
|
||||
TurnRestriction restriction;
|
||||
|
||||
InputRestrictionContainer(EdgeID fromWay, EdgeID toWay, NodeID vn, unsigned vw)
|
||||
: fromWay(fromWay), toWay(toWay), viaNode(vw)
|
||||
{
|
||||
restriction.viaNode = vn;
|
||||
}
|
||||
explicit InputRestrictionContainer(bool isOnly = false)
|
||||
: fromWay(std::numeric_limits<unsigned>::max()),
|
||||
toWay(std::numeric_limits<unsigned>::max()), viaNode(std::numeric_limits<unsigned>::max())
|
||||
{
|
||||
restriction.flags.isOnly = isOnly;
|
||||
}
|
||||
|
||||
static InputRestrictionContainer min_value() { return InputRestrictionContainer(0, 0, 0, 0); }
|
||||
static InputRestrictionContainer max_value()
|
||||
{
|
||||
return InputRestrictionContainer(std::numeric_limits<unsigned>::max(),
|
||||
std::numeric_limits<unsigned>::max(),
|
||||
std::numeric_limits<unsigned>::max(),
|
||||
std::numeric_limits<unsigned>::max());
|
||||
}
|
||||
};
|
||||
|
||||
struct CmpRestrictionContainerByFrom
|
||||
{
|
||||
typedef InputRestrictionContainer value_type;
|
||||
inline bool operator()(const InputRestrictionContainer &a, const InputRestrictionContainer &b)
|
||||
const
|
||||
{
|
||||
return a.fromWay < b.fromWay;
|
||||
}
|
||||
inline value_type max_value() const { return InputRestrictionContainer::max_value(); }
|
||||
inline value_type min_value() const { return InputRestrictionContainer::min_value(); }
|
||||
};
|
||||
|
||||
struct CmpRestrictionContainerByTo
|
||||
{
|
||||
typedef InputRestrictionContainer value_type;
|
||||
inline bool operator()(const InputRestrictionContainer &a, const InputRestrictionContainer &b)
|
||||
const
|
||||
{
|
||||
return a.toWay < b.toWay;
|
||||
}
|
||||
value_type max_value() const { return InputRestrictionContainer::max_value(); }
|
||||
value_type min_value() const { return InputRestrictionContainer::min_value(); }
|
||||
};
|
||||
|
||||
#endif // RESTRICTION_H
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include "RestrictionMap.h"
|
||||
#include "NodeBasedGraph.h"
|
||||
|
||||
#include "../Util/SimpleLogger.h"
|
||||
|
||||
bool RestrictionMap::IsNodeAViaNode(const NodeID node) const
|
||||
{
|
||||
return m_no_turn_via_node_set.find(node) != m_no_turn_via_node_set.end();
|
||||
}
|
||||
|
||||
RestrictionMap::RestrictionMap(const std::shared_ptr<NodeBasedDynamicGraph> &graph,
|
||||
const std::vector<TurnRestriction> &input_restrictions_list)
|
||||
: m_count(0), m_graph(graph)
|
||||
{
|
||||
// decompose restriction consisting of a start, via and end node into a
|
||||
// a pair of starting edge and a list of all end nodes
|
||||
for (auto &restriction : input_restrictions_list)
|
||||
{
|
||||
m_restriction_start_nodes.insert(restriction.fromNode);
|
||||
m_no_turn_via_node_set.insert(restriction.viaNode);
|
||||
|
||||
std::pair<NodeID, NodeID> restriction_source = {restriction.fromNode, restriction.viaNode};
|
||||
|
||||
unsigned index;
|
||||
auto restriction_iter = m_restriction_map.find(restriction_source);
|
||||
if (restriction_iter == m_restriction_map.end())
|
||||
{
|
||||
index = m_restriction_bucket_list.size();
|
||||
m_restriction_bucket_list.resize(index + 1);
|
||||
m_restriction_map.emplace(restriction_source, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
index = restriction_iter->second;
|
||||
// Map already contains an is_only_*-restriction
|
||||
if (m_restriction_bucket_list.at(index).begin()->second)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (restriction.flags.isOnly)
|
||||
{
|
||||
// We are going to insert an is_only_*-restriction. There can be only one.
|
||||
m_count -= m_restriction_bucket_list.at(index).size();
|
||||
m_restriction_bucket_list.at(index).clear();
|
||||
}
|
||||
}
|
||||
++m_count;
|
||||
m_restriction_bucket_list.at(index)
|
||||
.emplace_back(restriction.toNode, restriction.flags.isOnly);
|
||||
}
|
||||
}
|
||||
|
||||
// Replace end v with w in each turn restriction containing u as via node
|
||||
void RestrictionMap::FixupArrivingTurnRestriction(const NodeID u, const NodeID v, const NodeID w)
|
||||
{
|
||||
BOOST_ASSERT(u != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(v != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(w != SPECIAL_NODEID);
|
||||
|
||||
if (!RestrictionStartsAtNode(u))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// find all potential start edges
|
||||
// it is more efficent to get a (small) list of potential start edges than iterating over all buckets
|
||||
std::vector<NodeID> predecessors;
|
||||
for (const EdgeID current_edge_id : m_graph->GetAdjacentEdgeRange(u))
|
||||
{
|
||||
const EdgeData &edge_data = m_graph->GetEdgeData(current_edge_id);
|
||||
const NodeID target = m_graph->GetTarget(current_edge_id);
|
||||
if (edge_data.backward && (v != target))
|
||||
{
|
||||
predecessors.push_back(target);
|
||||
}
|
||||
}
|
||||
|
||||
for (const NodeID x : predecessors)
|
||||
{
|
||||
auto restriction_iterator = m_restriction_map.find({x, u});
|
||||
if (restriction_iterator == m_restriction_map.end())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const unsigned index = restriction_iterator->second;
|
||||
auto &bucket = m_restriction_bucket_list.at(index);
|
||||
for (RestrictionTarget &restriction_target : bucket)
|
||||
{
|
||||
if (v == restriction_target.first)
|
||||
{
|
||||
restriction_target.first = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Replaces start edge (v, w) with (u, w). Only start node changes.
|
||||
void RestrictionMap::FixupStartingTurnRestriction(const NodeID u, const NodeID v, const NodeID w)
|
||||
{
|
||||
BOOST_ASSERT(u != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(v != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(w != SPECIAL_NODEID);
|
||||
|
||||
if (!RestrictionStartsAtNode(u))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto restriction_iterator = m_restriction_map.find({v, w});
|
||||
if (restriction_iterator != m_restriction_map.end())
|
||||
{
|
||||
const unsigned index = restriction_iterator->second;
|
||||
// remove old restriction start (v,w)
|
||||
m_restriction_map.erase(restriction_iterator);
|
||||
|
||||
// insert new restriction start (u,w) (point to index)
|
||||
RestrictionSource new_source = {u, w};
|
||||
m_restriction_map.emplace(new_source, index);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if edge (u, v) is the start of any turn restriction.
|
||||
// If so returns id of first target node.
|
||||
NodeID RestrictionMap::CheckForEmanatingIsOnlyTurn(const NodeID u, const NodeID v) const
|
||||
{
|
||||
BOOST_ASSERT(u != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(v != SPECIAL_NODEID);
|
||||
|
||||
if (!RestrictionStartsAtNode(u))
|
||||
{
|
||||
return SPECIAL_NODEID;
|
||||
}
|
||||
|
||||
auto restriction_iter = m_restriction_map.find({u, v});
|
||||
if (restriction_iter != m_restriction_map.end())
|
||||
{
|
||||
const unsigned index = restriction_iter->second;
|
||||
auto &bucket = m_restriction_bucket_list.at(index);
|
||||
for (const RestrictionSource &restriction_target : bucket)
|
||||
{
|
||||
if (restriction_target.second)
|
||||
{
|
||||
return restriction_target.first;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SPECIAL_NODEID;
|
||||
}
|
||||
|
||||
// Checks if turn <u,v,w> is actually a turn restriction.
|
||||
bool RestrictionMap::CheckIfTurnIsRestricted(const NodeID u, const NodeID v, const NodeID w) const
|
||||
{
|
||||
BOOST_ASSERT(u != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(v != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(w != SPECIAL_NODEID);
|
||||
|
||||
if (!RestrictionStartsAtNode(u))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto restriction_iter = m_restriction_map.find({u, v});
|
||||
if (restriction_iter != m_restriction_map.end())
|
||||
{
|
||||
const unsigned index = restriction_iter->second;
|
||||
const auto &bucket = m_restriction_bucket_list.at(index);
|
||||
for (const RestrictionTarget &restriction_target : bucket)
|
||||
{
|
||||
if ((w == restriction_target.first) && // target found
|
||||
(!restriction_target.second)) // and not an only_-restr.
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// check of node is the start of any restriction
|
||||
bool RestrictionMap::RestrictionStartsAtNode(const NodeID node) const
|
||||
{
|
||||
if (m_restriction_start_nodes.find(node) == m_restriction_start_nodes.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef __RESTRICTION_MAP_H__
|
||||
#define __RESTRICTION_MAP_H__
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "DynamicGraph.h"
|
||||
#include "Restriction.h"
|
||||
#include "NodeBasedGraph.h"
|
||||
#include "../Util/StdHashExtensions.h"
|
||||
#include "../typedefs.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
// Efficent look up if an edge is the start + via node of a TurnRestriction
|
||||
// EdgeBasedEdgeFactory decides by it if edges are inserted or geometry is compressed
|
||||
class RestrictionMap
|
||||
{
|
||||
public:
|
||||
RestrictionMap(const std::shared_ptr<NodeBasedDynamicGraph> &graph,
|
||||
const std::vector<TurnRestriction> &input_restrictions_list);
|
||||
|
||||
void FixupArrivingTurnRestriction(const NodeID u, const NodeID v, const NodeID w);
|
||||
void FixupStartingTurnRestriction(const NodeID u, const NodeID v, const NodeID w);
|
||||
NodeID CheckForEmanatingIsOnlyTurn(const NodeID u, const NodeID v) const;
|
||||
bool CheckIfTurnIsRestricted(const NodeID u, const NodeID v, const NodeID w) const;
|
||||
bool IsNodeAViaNode(const NodeID node) const;
|
||||
unsigned size() { return m_count; }
|
||||
|
||||
private:
|
||||
bool RestrictionStartsAtNode(const NodeID node) const;
|
||||
typedef std::pair<NodeID, NodeID> RestrictionSource;
|
||||
typedef std::pair<NodeID, bool> RestrictionTarget;
|
||||
typedef std::vector<RestrictionTarget> EmanatingRestrictionsVector;
|
||||
typedef NodeBasedDynamicGraph::EdgeData EdgeData;
|
||||
|
||||
unsigned m_count;
|
||||
std::shared_ptr<NodeBasedDynamicGraph> m_graph;
|
||||
//! index -> list of (target, isOnly)
|
||||
std::vector<EmanatingRestrictionsVector> m_restriction_bucket_list;
|
||||
//! maps (start, via) -> bucket index
|
||||
std::unordered_map<RestrictionSource, unsigned> m_restriction_map;
|
||||
std::unordered_set<NodeID> m_restriction_start_nodes;
|
||||
std::unordered_set<NodeID> m_no_turn_via_node_set;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include <osrm/RouteParameters.h>
|
||||
|
||||
#include <boost/fusion/container/vector.hpp>
|
||||
#include <boost/fusion/sequence/intrinsic.hpp>
|
||||
#include <boost/fusion/include/at_c.hpp>
|
||||
|
||||
RouteParameters::RouteParameters()
|
||||
: zoom_level(18), print_instructions(false), alternate_route(true), geometry(true),
|
||||
compression(true), deprecatedAPI(false), check_sum(-1)
|
||||
{
|
||||
}
|
||||
|
||||
void RouteParameters::setZoomLevel(const short level)
|
||||
{
|
||||
if (18 >= level && 0 <= level)
|
||||
{
|
||||
zoom_level = level;
|
||||
}
|
||||
}
|
||||
|
||||
void RouteParameters::setAlternateRouteFlag(const bool flag) { alternate_route = flag; }
|
||||
|
||||
void RouteParameters::setDeprecatedAPIFlag(const std::string &) { deprecatedAPI = true; }
|
||||
|
||||
void RouteParameters::setChecksum(const unsigned sum) { check_sum = sum; }
|
||||
|
||||
void RouteParameters::setInstructionFlag(const bool flag) { print_instructions = flag; }
|
||||
|
||||
void RouteParameters::setService(const std::string &service_string) { service = service_string; }
|
||||
|
||||
void RouteParameters::setOutputFormat(const std::string &format) { output_format = format; }
|
||||
|
||||
void RouteParameters::setJSONpParameter(const std::string ¶meter) { jsonp_parameter = parameter; }
|
||||
|
||||
void RouteParameters::addHint(const std::string &hint)
|
||||
{
|
||||
hints.resize(coordinates.size());
|
||||
if (!hints.empty())
|
||||
{
|
||||
hints.back() = hint;
|
||||
}
|
||||
}
|
||||
|
||||
void RouteParameters::setLanguage(const std::string &language_string) { language = language_string; }
|
||||
|
||||
void RouteParameters::setGeometryFlag(const bool flag) { geometry = flag; }
|
||||
|
||||
void RouteParameters::setCompressionFlag(const bool flag) { compression = flag; }
|
||||
|
||||
void RouteParameters::addCoordinate(const boost::fusion::vector<double, double> &transmitted_coordinates)
|
||||
{
|
||||
const int lat = COORDINATE_PRECISION * boost::fusion::at_c<0>(transmitted_coordinates);
|
||||
const int lon = COORDINATE_PRECISION * boost::fusion::at_c<1>(transmitted_coordinates);
|
||||
coordinates.emplace_back(lat, lon);
|
||||
}
|
||||
@@ -1,267 +1,60 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
#ifndef SEARCHENGINE_H_
|
||||
#define SEARCHENGINE_H_
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <climits>
|
||||
#include <deque>
|
||||
*/
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
#ifndef SEARCHENGINE_H
|
||||
#define SEARCHENGINE_H
|
||||
|
||||
#include "BinaryHeap.h"
|
||||
#include "PhantomNodes.h"
|
||||
#include "../Util/StringUtil.h"
|
||||
#include "../typedefs.h"
|
||||
#include "SearchEngineData.h"
|
||||
#include "../RoutingAlgorithms/AlternativePathRouting.h"
|
||||
#include "../RoutingAlgorithms/ManyToManyRouting.h"
|
||||
#include "../RoutingAlgorithms/ShortestPathRouting.h"
|
||||
|
||||
struct _HeapData {
|
||||
NodeID parent;
|
||||
_HeapData( NodeID p ) : parent(p) { }
|
||||
};
|
||||
#include <type_traits>
|
||||
|
||||
typedef boost::thread_specific_ptr<BinaryHeap< NodeID, NodeID, int, _HeapData > > HeapPtr;
|
||||
template <class DataFacadeT> class SearchEngine
|
||||
{
|
||||
private:
|
||||
DataFacadeT *facade;
|
||||
SearchEngineData engine_working_data;
|
||||
|
||||
public:
|
||||
ShortestPathRouting<DataFacadeT> shortest_path;
|
||||
AlternativeRouting<DataFacadeT> alternative_path;
|
||||
ManyToManyRouting<DataFacadeT> distance_table;
|
||||
|
||||
explicit SearchEngine(DataFacadeT *facade)
|
||||
: facade(facade), shortest_path(facade, engine_working_data),
|
||||
alternative_path(facade, engine_working_data), distance_table(facade, engine_working_data)
|
||||
{
|
||||
static_assert(!std::is_pointer<DataFacadeT>(), "don't instantiate with ptr type");
|
||||
static_assert(std::is_object<DataFacadeT>(), "don't instantiate with void, function, or reference");
|
||||
}
|
||||
|
||||
template<class EdgeData, class GraphT>
|
||||
class SearchEngine {
|
||||
private:
|
||||
const GraphT * _graph;
|
||||
NodeInformationHelpDesk * nodeHelpDesk;
|
||||
std::vector<string> * _names;
|
||||
static HeapPtr _forwardHeap;
|
||||
static HeapPtr _backwardHeap;
|
||||
inline double absDouble(double input) { if(input < 0) return input*(-1); else return input;}
|
||||
public:
|
||||
SearchEngine(GraphT * g, NodeInformationHelpDesk * nh, std::vector<string> * n = new std::vector<string>()) : _graph(g), nodeHelpDesk(nh), _names(n) {}
|
||||
~SearchEngine() {}
|
||||
|
||||
inline const void GetCoordinatesForNodeID(NodeID id, _Coordinate& result) const {
|
||||
result.lat = nodeHelpDesk->getLatitudeOfNode(id);
|
||||
result.lon = nodeHelpDesk->getLongitudeOfNode(id);
|
||||
}
|
||||
|
||||
inline void InitializeThreadLocalStorageIfNecessary() {
|
||||
if(!_forwardHeap.get())
|
||||
_forwardHeap.reset(new BinaryHeap< NodeID, NodeID, int, _HeapData >(nodeHelpDesk->getNumberOfNodes()));
|
||||
else
|
||||
_forwardHeap->Clear();
|
||||
|
||||
if(!_backwardHeap.get())
|
||||
_backwardHeap.reset(new BinaryHeap< NodeID, NodeID, int, _HeapData >(nodeHelpDesk->getNumberOfNodes()));
|
||||
else
|
||||
_backwardHeap->Clear();
|
||||
}
|
||||
|
||||
int ComputeRoute(PhantomNodes & phantomNodes, std::vector<_PathData> & path) {
|
||||
int _upperbound = INT_MAX;
|
||||
if(!phantomNodes.AtLeastOnePhantomNodeIsUINTMAX())
|
||||
return _upperbound;
|
||||
|
||||
InitializeThreadLocalStorageIfNecessary();
|
||||
NodeID middle = ( NodeID ) UINT_MAX;
|
||||
bool stOnSameEdge = false;
|
||||
//Handling the special case that origin and destination are on same edge and that the order is correct.
|
||||
if(phantomNodes.PhantomsAreOnSameNodeBasedEdge()){
|
||||
if(phantomNodes.startPhantom.isBidirected() && phantomNodes.targetPhantom.isBidirected()) {
|
||||
int weight = std::abs(phantomNodes.startPhantom.weight1 - phantomNodes.targetPhantom.weight1);
|
||||
return weight;
|
||||
} else if(phantomNodes.startPhantom.weight1 <= phantomNodes.targetPhantom.weight1){
|
||||
int weight = std::abs(phantomNodes.startPhantom.weight1 - phantomNodes.targetPhantom.weight1);
|
||||
return weight;
|
||||
} else {
|
||||
stOnSameEdge = true;
|
||||
}
|
||||
}
|
||||
|
||||
//insert start and/or target node of start edge
|
||||
_forwardHeap->Insert(phantomNodes.startPhantom.edgeBasedNode, -phantomNodes.startPhantom.weight1, phantomNodes.startPhantom.edgeBasedNode);
|
||||
if(phantomNodes.startPhantom.isBidirected() ) {
|
||||
_forwardHeap->Insert(phantomNodes.startPhantom.edgeBasedNode+1, -phantomNodes.startPhantom.weight2, phantomNodes.startPhantom.edgeBasedNode+1);
|
||||
}
|
||||
//insert start and/or target node of target edge id
|
||||
_backwardHeap->Insert(phantomNodes.targetPhantom.edgeBasedNode, -phantomNodes.targetPhantom.weight1, phantomNodes.targetPhantom.edgeBasedNode);
|
||||
if(phantomNodes.targetPhantom.isBidirected() ) {
|
||||
_backwardHeap->Insert(phantomNodes.targetPhantom.edgeBasedNode+1, -phantomNodes.targetPhantom.weight2, phantomNodes.targetPhantom.edgeBasedNode+1);
|
||||
}
|
||||
|
||||
while(_forwardHeap->Size() + _backwardHeap->Size() > 0){
|
||||
if(_forwardHeap->Size() > 0){
|
||||
_RoutingStep(_forwardHeap, _backwardHeap, true, &middle, &_upperbound, stOnSameEdge);
|
||||
}
|
||||
if(_backwardHeap->Size() > 0){
|
||||
_RoutingStep(_backwardHeap, _forwardHeap, false, &middle, &_upperbound, stOnSameEdge);
|
||||
}
|
||||
}
|
||||
if ( _upperbound == INT_MAX ) {
|
||||
return _upperbound;
|
||||
}
|
||||
NodeID pathNode = middle;
|
||||
deque<NodeID> packedPath;
|
||||
while(phantomNodes.startPhantom.edgeBasedNode != pathNode && (!phantomNodes.startPhantom.isBidirected() || phantomNodes.startPhantom.edgeBasedNode+1 != pathNode) ) {
|
||||
pathNode = _forwardHeap->GetData(pathNode).parent;
|
||||
packedPath.push_front(pathNode);
|
||||
}
|
||||
packedPath.push_back(middle);
|
||||
pathNode = middle;
|
||||
while(phantomNodes.targetPhantom.edgeBasedNode != pathNode && (!phantomNodes.targetPhantom.isBidirected() || phantomNodes.targetPhantom.edgeBasedNode+1 != pathNode)) {
|
||||
pathNode = _backwardHeap->GetData(pathNode).parent;
|
||||
packedPath.push_back(pathNode);
|
||||
}
|
||||
for(deque<NodeID>::size_type i = 0;i < packedPath.size() - 1;i++){
|
||||
_UnpackEdge(packedPath[i], packedPath[i + 1], path);
|
||||
}
|
||||
|
||||
return _upperbound;
|
||||
}
|
||||
|
||||
inline bool FindRoutingStarts(const _Coordinate & start, const _Coordinate & target, PhantomNodes & routingStarts) const {
|
||||
nodeHelpDesk->FindRoutingStarts(start, target, routingStarts);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool FindPhantomNodeForCoordinate(const _Coordinate & location, PhantomNode & result) const {
|
||||
return nodeHelpDesk->FindPhantomNodeForCoordinate(location, result);
|
||||
}
|
||||
|
||||
inline NodeID GetNameIDForOriginDestinationNodeID(NodeID s, NodeID t) const {
|
||||
if(s == t)
|
||||
return 0;
|
||||
|
||||
EdgeID e = _graph->FindEdge(s, t);
|
||||
if(e == UINT_MAX)
|
||||
e = _graph->FindEdge( t, s );
|
||||
if(UINT_MAX == e) {
|
||||
return 0;
|
||||
}
|
||||
assert(e != UINT_MAX);
|
||||
const EdgeData ed = _graph->GetEdgeData(e);
|
||||
return ed.via;
|
||||
}
|
||||
|
||||
inline std::string GetEscapedNameForNameID(const NodeID nameID) const {
|
||||
return ((nameID >= _names->size() || nameID == 0) ? std::string("") : HTMLEntitize(_names->at(nameID)));
|
||||
}
|
||||
|
||||
inline std::string GetEscapedNameForEdgeBasedEdgeID(const unsigned edgeID) const {
|
||||
|
||||
const unsigned nameID = _graph->GetEdgeData(edgeID).nameID1;
|
||||
return GetEscapedNameForNameID(nameID);
|
||||
}
|
||||
private:
|
||||
inline void _RoutingStep(HeapPtr & _forwardHeap, HeapPtr & _backwardHeap, const bool & forwardDirection, NodeID *middle, int *_upperbound, const bool stOnSameEdge) const {
|
||||
const NodeID node = _forwardHeap->DeleteMin();
|
||||
const int distance = _forwardHeap->GetKey(node);
|
||||
if(_backwardHeap->WasInserted(node) && (!stOnSameEdge || distance > 0) ){
|
||||
const int newDistance = _backwardHeap->GetKey(node) + distance;
|
||||
if(newDistance < *_upperbound){
|
||||
*middle = node;
|
||||
*_upperbound = newDistance;
|
||||
}
|
||||
}
|
||||
|
||||
if(distance > *_upperbound){
|
||||
_forwardHeap->DeleteAll();
|
||||
return;
|
||||
}
|
||||
|
||||
for ( typename GraphT::EdgeIterator edge = _graph->BeginEdges( node ); edge < _graph->EndEdges(node); edge++ ) {
|
||||
const EdgeData & data = _graph->GetEdgeData(edge);
|
||||
bool backwardDirectionFlag = (!forwardDirection) ? data.forward : data.backward;
|
||||
if(backwardDirectionFlag) {
|
||||
const NodeID to = _graph->GetTarget(edge);
|
||||
const int edgeWeight = data.distance;
|
||||
|
||||
assert( edgeWeight > 0 );
|
||||
|
||||
//Stalling
|
||||
if(_forwardHeap->WasInserted( to )) {
|
||||
if(_forwardHeap->GetKey( to ) + edgeWeight < distance) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for ( typename GraphT::EdgeIterator edge = _graph->BeginEdges( node ); edge < _graph->EndEdges(node); edge++ ) {
|
||||
const EdgeData & data = _graph->GetEdgeData(edge);
|
||||
bool forwardDirectionFlag = (forwardDirection ? data.forward : data.backward );
|
||||
if(forwardDirectionFlag) {
|
||||
|
||||
const NodeID to = _graph->GetTarget(edge);
|
||||
const int edgeWeight = data.distance;
|
||||
|
||||
assert( edgeWeight > 0 );
|
||||
const int toDistance = distance + edgeWeight;
|
||||
|
||||
//New Node discovered -> Add to Heap + Node Info Storage
|
||||
if ( !_forwardHeap->WasInserted( to ) ) {
|
||||
_forwardHeap->Insert( to, toDistance, node );
|
||||
}
|
||||
//Found a shorter Path -> Update distance
|
||||
else if ( toDistance < _forwardHeap->GetKey( to ) ) {
|
||||
_forwardHeap->GetData( to ).parent = node;
|
||||
_forwardHeap->DecreaseKey( to, toDistance );
|
||||
//new parent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline bool _UnpackEdge(const NodeID source, const NodeID target, std::vector<_PathData> & path) const {
|
||||
assert(source != target);
|
||||
//find edge first.
|
||||
typename GraphT::EdgeIterator smallestEdge = SPECIAL_EDGEID;
|
||||
int smallestWeight = INT_MAX;
|
||||
for(typename GraphT::EdgeIterator eit = _graph->BeginEdges(source);eit < _graph->EndEdges(source);eit++){
|
||||
const int weight = _graph->GetEdgeData(eit).distance;
|
||||
if(_graph->GetTarget(eit) == target && weight < smallestWeight && _graph->GetEdgeData(eit).forward){
|
||||
smallestEdge = eit;
|
||||
smallestWeight = weight;
|
||||
}
|
||||
}
|
||||
|
||||
if(smallestEdge == SPECIAL_EDGEID){
|
||||
for(typename GraphT::EdgeIterator eit = _graph->BeginEdges(target);eit < _graph->EndEdges(target);eit++){
|
||||
const int weight = _graph->GetEdgeData(eit).distance;
|
||||
if(_graph->GetTarget(eit) == source && weight < smallestWeight && _graph->GetEdgeData(eit).backward){
|
||||
smallestEdge = eit;
|
||||
smallestWeight = weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(smallestWeight != INT_MAX);
|
||||
|
||||
const EdgeData& ed = _graph->GetEdgeData(smallestEdge);
|
||||
if(ed.shortcut) {//unpack
|
||||
const NodeID middle = ed.via;
|
||||
_UnpackEdge(source, middle, path);
|
||||
_UnpackEdge(middle, target, path);
|
||||
return false;
|
||||
} else {
|
||||
assert(!ed.shortcut);
|
||||
path.push_back(_PathData(ed.via, ed.nameID1, ed.turnInstruction, ed.distance) );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
template<class EdgeData, class GraphT> HeapPtr SearchEngine<EdgeData, GraphT>::_forwardHeap;
|
||||
template<class EdgeData, class GraphT> HeapPtr SearchEngine<EdgeData, GraphT>::_backwardHeap;
|
||||
|
||||
#endif /* SEARCHENGINE_H_ */
|
||||
#endif // SEARCHENGINE_H
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include "SearchEngineData.h"
|
||||
|
||||
void SearchEngineData::InitializeOrClearFirstThreadLocalStorage(const unsigned number_of_nodes)
|
||||
{
|
||||
if (forwardHeap.get())
|
||||
{
|
||||
forwardHeap->Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
forwardHeap.reset(new QueryHeap(number_of_nodes));
|
||||
}
|
||||
|
||||
if (backwardHeap.get())
|
||||
{
|
||||
backwardHeap->Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
backwardHeap.reset(new QueryHeap(number_of_nodes));
|
||||
}
|
||||
}
|
||||
|
||||
void SearchEngineData::InitializeOrClearSecondThreadLocalStorage(const unsigned number_of_nodes)
|
||||
{
|
||||
if (forwardHeap2.get())
|
||||
{
|
||||
forwardHeap2->Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
forwardHeap2.reset(new QueryHeap(number_of_nodes));
|
||||
}
|
||||
|
||||
if (backwardHeap2.get())
|
||||
{
|
||||
backwardHeap2->Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
backwardHeap2.reset(new QueryHeap(number_of_nodes));
|
||||
}
|
||||
}
|
||||
|
||||
void SearchEngineData::InitializeOrClearThirdThreadLocalStorage(const unsigned number_of_nodes)
|
||||
{
|
||||
if (forwardHeap3.get())
|
||||
{
|
||||
forwardHeap3->Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
forwardHeap3.reset(new QueryHeap(number_of_nodes));
|
||||
}
|
||||
|
||||
if (backwardHeap3.get())
|
||||
{
|
||||
backwardHeap3->Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
backwardHeap3.reset(new QueryHeap(number_of_nodes));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef SEARCH_ENGINE_DATA_H
|
||||
#define SEARCH_ENGINE_DATA_H
|
||||
|
||||
#include "BinaryHeap.h"
|
||||
#include "QueryEdge.h"
|
||||
#include "StaticGraph.h"
|
||||
|
||||
#include "../typedefs.h"
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
struct HeapData
|
||||
{
|
||||
NodeID parent;
|
||||
/* explicit */ HeapData(NodeID p) : parent(p) {}
|
||||
};
|
||||
|
||||
struct SearchEngineData
|
||||
{
|
||||
typedef BinaryHeap<NodeID, NodeID, int, HeapData, UnorderedMapStorage<NodeID, int>> QueryHeap;
|
||||
typedef boost::thread_specific_ptr<QueryHeap> SearchEngineHeapPtr;
|
||||
|
||||
static SearchEngineHeapPtr forwardHeap;
|
||||
static SearchEngineHeapPtr backwardHeap;
|
||||
static SearchEngineHeapPtr forwardHeap2;
|
||||
static SearchEngineHeapPtr backwardHeap2;
|
||||
static SearchEngineHeapPtr forwardHeap3;
|
||||
static SearchEngineHeapPtr backwardHeap3;
|
||||
|
||||
void InitializeOrClearFirstThreadLocalStorage(const unsigned number_of_nodes);
|
||||
|
||||
void InitializeOrClearSecondThreadLocalStorage(const unsigned number_of_nodes);
|
||||
|
||||
void InitializeOrClearThirdThreadLocalStorage(const unsigned number_of_nodes);
|
||||
};
|
||||
|
||||
#endif // SEARCH_ENGINE_DATA_H
|
||||
@@ -1,39 +1,70 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
#ifndef SEGMENTINFORMATION_H_
|
||||
#define SEGMENTINFORMATION_H_
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <climits>
|
||||
*/
|
||||
|
||||
struct SegmentInformation {
|
||||
_Coordinate location;
|
||||
NodeID nameID;
|
||||
unsigned length;
|
||||
#ifndef SEGMENT_INFORMATION_H
|
||||
#define SEGMENT_INFORMATION_H
|
||||
|
||||
#include "TurnInstructions.h"
|
||||
|
||||
#include "../typedefs.h"
|
||||
|
||||
#include <osrm/Coordinate.h>
|
||||
|
||||
// Struct fits everything in one cache line
|
||||
struct SegmentInformation
|
||||
{
|
||||
FixedPointCoordinate location;
|
||||
NodeID name_id;
|
||||
unsigned duration;
|
||||
short turnInstruction;
|
||||
double length;
|
||||
short bearing; // more than enough [0..3600] fits into 12 bits
|
||||
TurnInstruction turn_instruction;
|
||||
bool necessary;
|
||||
SegmentInformation(const _Coordinate & loc, const NodeID nam, const unsigned len, const unsigned dur, const short tInstr, const bool nec) :
|
||||
location(loc), nameID(nam), length(len), duration(dur), turnInstruction(tInstr), necessary(nec) {}
|
||||
SegmentInformation(const _Coordinate & loc, const NodeID nam, const unsigned len, const unsigned dur, const short tInstr) :
|
||||
location(loc), nameID(nam), length(len), duration(dur), turnInstruction(tInstr), necessary(tInstr != 0) {}
|
||||
|
||||
explicit SegmentInformation(const FixedPointCoordinate &location,
|
||||
const NodeID name_id,
|
||||
const unsigned duration,
|
||||
const double length,
|
||||
const TurnInstruction turn_instruction,
|
||||
const bool necessary)
|
||||
: location(location), name_id(name_id), duration(duration), length(length), bearing(0),
|
||||
turn_instruction(turn_instruction), necessary(necessary)
|
||||
{
|
||||
}
|
||||
|
||||
explicit SegmentInformation(const FixedPointCoordinate &location,
|
||||
const NodeID name_id,
|
||||
const unsigned duration,
|
||||
const double length,
|
||||
const TurnInstruction turn_instruction)
|
||||
: location(location), name_id(name_id), duration(duration), length(length), bearing(0),
|
||||
turn_instruction(turn_instruction), necessary(turn_instruction != TurnInstruction::NoTurn)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* SEGMENTINFORMATION_H_ */
|
||||
#endif /* SEGMENT_INFORMATION_H */
|
||||
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef SHARED_MEMORY_FACTORY_H
|
||||
#define SHARED_MEMORY_FACTORY_H
|
||||
|
||||
#include "../Util/OSRMException.h"
|
||||
#include "../Util/SimpleLogger.h"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/interprocess/mapped_region.hpp>
|
||||
#include <boost/interprocess/xsi_shared_memory.hpp>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#endif
|
||||
|
||||
// #include <cstring>
|
||||
#include <cstdint>
|
||||
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
|
||||
struct OSRMLockFile
|
||||
{
|
||||
boost::filesystem::path operator()()
|
||||
{
|
||||
boost::filesystem::path temp_dir = boost::filesystem::temp_directory_path();
|
||||
boost::filesystem::path lock_file = temp_dir / "osrm.lock";
|
||||
return lock_file;
|
||||
}
|
||||
};
|
||||
|
||||
class SharedMemory
|
||||
{
|
||||
|
||||
// Remove shared memory on destruction
|
||||
class shm_remove
|
||||
{
|
||||
private:
|
||||
int m_shmid;
|
||||
bool m_initialized;
|
||||
|
||||
public:
|
||||
void SetID(int shmid)
|
||||
{
|
||||
m_shmid = shmid;
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
shm_remove() : m_shmid(INT_MIN), m_initialized(false) {}
|
||||
shm_remove(const shm_remove &) = delete;
|
||||
~shm_remove()
|
||||
{
|
||||
if (m_initialized)
|
||||
{
|
||||
SimpleLogger().Write(logDEBUG) << "automatic memory deallocation";
|
||||
if (!boost::interprocess::xsi_shared_memory::remove(m_shmid))
|
||||
{
|
||||
SimpleLogger().Write(logDEBUG) << "could not deallocate id " << m_shmid;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
void *Ptr() const { return region.get_address(); }
|
||||
|
||||
SharedMemory() = delete;
|
||||
SharedMemory(const SharedMemory &) = delete;
|
||||
|
||||
template <typename IdentifierT>
|
||||
SharedMemory(const boost::filesystem::path &lock_file,
|
||||
const IdentifierT id,
|
||||
const uint64_t size = 0,
|
||||
bool read_write = false,
|
||||
bool remove_prev = true)
|
||||
: key(lock_file.string().c_str(), id)
|
||||
{
|
||||
if (0 == size)
|
||||
{ // read_only
|
||||
shm = boost::interprocess::xsi_shared_memory(boost::interprocess::open_only, key);
|
||||
|
||||
region = boost::interprocess::mapped_region(
|
||||
shm,
|
||||
(read_write ? boost::interprocess::read_write : boost::interprocess::read_only));
|
||||
}
|
||||
else
|
||||
{ // writeable pointer
|
||||
// remove previously allocated mem
|
||||
if (remove_prev)
|
||||
{
|
||||
Remove(key);
|
||||
}
|
||||
shm = boost::interprocess::xsi_shared_memory(
|
||||
boost::interprocess::open_or_create, key, size);
|
||||
#ifdef __linux__
|
||||
if (-1 == shmctl(shm.get_shmid(), SHM_LOCK, 0))
|
||||
{
|
||||
if (ENOMEM == errno)
|
||||
{
|
||||
SimpleLogger().Write(logWARNING) << "could not lock shared memory to RAM";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
region = boost::interprocess::mapped_region(shm, boost::interprocess::read_write);
|
||||
|
||||
remover.SetID(shm.get_shmid());
|
||||
SimpleLogger().Write(logDEBUG) << "writeable memory allocated " << size << " bytes";
|
||||
}
|
||||
}
|
||||
|
||||
template <typename IdentifierT> static bool RegionExists(const IdentifierT id)
|
||||
{
|
||||
bool result = true;
|
||||
try
|
||||
{
|
||||
OSRMLockFile lock_file;
|
||||
boost::interprocess::xsi_key key(lock_file().string().c_str(), id);
|
||||
result = RegionExists(key);
|
||||
}
|
||||
catch (...) { result = false; }
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename IdentifierT> static bool Remove(const IdentifierT id)
|
||||
{
|
||||
OSRMLockFile lock_file;
|
||||
boost::interprocess::xsi_key key(lock_file().string().c_str(), id);
|
||||
return Remove(key);
|
||||
}
|
||||
|
||||
private:
|
||||
static bool RegionExists(const boost::interprocess::xsi_key &key)
|
||||
{
|
||||
bool result = true;
|
||||
try { boost::interprocess::xsi_shared_memory shm(boost::interprocess::open_only, key); }
|
||||
catch (...) { result = false; }
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool Remove(const boost::interprocess::xsi_key &key)
|
||||
{
|
||||
bool ret = false;
|
||||
try
|
||||
{
|
||||
SimpleLogger().Write(logDEBUG) << "deallocating prev memory";
|
||||
boost::interprocess::xsi_shared_memory xsi(boost::interprocess::open_only, key);
|
||||
ret = boost::interprocess::xsi_shared_memory::remove(xsi.get_shmid());
|
||||
}
|
||||
catch (const boost::interprocess::interprocess_exception &e)
|
||||
{
|
||||
if (e.get_error_code() != boost::interprocess::not_found_error)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
boost::interprocess::xsi_key key;
|
||||
boost::interprocess::xsi_shared_memory shm;
|
||||
boost::interprocess::mapped_region region;
|
||||
shm_remove remover;
|
||||
};
|
||||
|
||||
template <class LockFileT = OSRMLockFile> class SharedMemoryFactory_tmpl
|
||||
{
|
||||
public:
|
||||
template <typename IdentifierT>
|
||||
static SharedMemory *Get(const IdentifierT &id,
|
||||
const uint64_t size = 0,
|
||||
bool read_write = false,
|
||||
bool remove_prev = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
LockFileT lock_file;
|
||||
if (!boost::filesystem::exists(lock_file()))
|
||||
{
|
||||
if (0 == size)
|
||||
{
|
||||
throw OSRMException("lock file does not exist, exiting");
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::filesystem::ofstream ofs(lock_file());
|
||||
ofs.close();
|
||||
}
|
||||
}
|
||||
return new SharedMemory(lock_file(), id, size, read_write, remove_prev);
|
||||
}
|
||||
catch (const boost::interprocess::interprocess_exception &e)
|
||||
{
|
||||
SimpleLogger().Write(logWARNING) << "caught exception: " << e.what() << ", code "
|
||||
<< e.get_error_code();
|
||||
throw OSRMException(e.what());
|
||||
}
|
||||
}
|
||||
|
||||
SharedMemoryFactory_tmpl() = delete;
|
||||
SharedMemoryFactory_tmpl(const SharedMemoryFactory_tmpl &) = delete;
|
||||
};
|
||||
|
||||
typedef SharedMemoryFactory_tmpl<> SharedMemoryFactory;
|
||||
|
||||
#endif /* SHARED_MEMORY_POINTER_FACTORY_H */
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef SHARED_MEMORY_VECTOR_WRAPPER_H
|
||||
#define SHARED_MEMORY_VECTOR_WRAPPER_H
|
||||
|
||||
#include "../Util/SimpleLogger.h"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
template <typename DataT> class ShMemIterator : public std::iterator<std::input_iterator_tag, DataT>
|
||||
{
|
||||
DataT *p;
|
||||
|
||||
public:
|
||||
explicit ShMemIterator(DataT *x) : p(x) {}
|
||||
ShMemIterator(const ShMemIterator &mit) : p(mit.p) {}
|
||||
ShMemIterator &operator++()
|
||||
{
|
||||
++p;
|
||||
return *this;
|
||||
}
|
||||
ShMemIterator operator++(int)
|
||||
{
|
||||
ShMemIterator tmp(*this);
|
||||
operator++();
|
||||
return tmp;
|
||||
}
|
||||
ShMemIterator operator+(std::ptrdiff_t diff)
|
||||
{
|
||||
ShMemIterator tmp(p + diff);
|
||||
return tmp;
|
||||
}
|
||||
bool operator==(const ShMemIterator &rhs) { return p == rhs.p; }
|
||||
bool operator!=(const ShMemIterator &rhs) { return p != rhs.p; }
|
||||
DataT &operator*() { return *p; }
|
||||
};
|
||||
|
||||
template <typename DataT> class SharedMemoryWrapper
|
||||
{
|
||||
private:
|
||||
DataT *m_ptr;
|
||||
std::size_t m_size;
|
||||
|
||||
public:
|
||||
SharedMemoryWrapper() : m_ptr(nullptr), m_size(0) {}
|
||||
|
||||
SharedMemoryWrapper(DataT *ptr, std::size_t size) : m_ptr(ptr), m_size(size) {}
|
||||
|
||||
void swap(SharedMemoryWrapper<DataT> &other)
|
||||
{
|
||||
BOOST_ASSERT_MSG(m_size != 0 || other.size() != 0, "size invalid");
|
||||
std::swap(m_size, other.m_size);
|
||||
std::swap(m_ptr, other.m_ptr);
|
||||
}
|
||||
|
||||
DataT &at(const std::size_t index) { return m_ptr[index]; }
|
||||
|
||||
const DataT &at(const std::size_t index) const { return m_ptr[index]; }
|
||||
|
||||
ShMemIterator<DataT> begin() const { return ShMemIterator<DataT>(m_ptr); }
|
||||
|
||||
ShMemIterator<DataT> end() const { return ShMemIterator<DataT>(m_ptr + m_size); }
|
||||
|
||||
std::size_t size() const { return m_size; }
|
||||
|
||||
bool empty() const { return 0 == size(); }
|
||||
|
||||
DataT &operator[](const unsigned index)
|
||||
{
|
||||
BOOST_ASSERT_MSG(index < m_size, "invalid size");
|
||||
return m_ptr[index];
|
||||
}
|
||||
|
||||
const DataT &operator[](const unsigned index) const
|
||||
{
|
||||
BOOST_ASSERT_MSG(index < m_size, "invalid size");
|
||||
return m_ptr[index];
|
||||
}
|
||||
};
|
||||
|
||||
template <> class SharedMemoryWrapper<bool>
|
||||
{
|
||||
private:
|
||||
unsigned *m_ptr;
|
||||
std::size_t m_size;
|
||||
|
||||
public:
|
||||
SharedMemoryWrapper() : m_ptr(nullptr), m_size(0) {}
|
||||
|
||||
SharedMemoryWrapper(unsigned *ptr, std::size_t size) : m_ptr(ptr), m_size(size) {}
|
||||
|
||||
void swap(SharedMemoryWrapper<bool> &other)
|
||||
{
|
||||
BOOST_ASSERT_MSG(m_size != 0 || other.size() != 0, "size invalid");
|
||||
std::swap(m_size, other.m_size);
|
||||
std::swap(m_ptr, other.m_ptr);
|
||||
}
|
||||
|
||||
bool at(const std::size_t index) const
|
||||
{
|
||||
const unsigned bucket = index / 32;
|
||||
const unsigned offset = index % 32;
|
||||
return m_ptr[bucket] & (1 << offset);
|
||||
}
|
||||
|
||||
std::size_t size() const { return m_size; }
|
||||
|
||||
bool empty() const { return 0 == size(); }
|
||||
|
||||
bool operator[](const unsigned index)
|
||||
{
|
||||
BOOST_ASSERT_MSG(index < m_size, "invalid size");
|
||||
const unsigned bucket = index / 32;
|
||||
const unsigned offset = index % 32;
|
||||
return m_ptr[bucket] & (1 << offset);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename DataT, bool UseSharedMemory> struct ShM
|
||||
{
|
||||
typedef typename std::conditional<UseSharedMemory,
|
||||
SharedMemoryWrapper<DataT>,
|
||||
std::vector<DataT>>::type vector;
|
||||
};
|
||||
|
||||
#endif // SHARED_MEMORY_VECTOR_WRAPPER_H
|
||||
@@ -1,162 +1,229 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
#ifndef STATICGRAPH_H_INCLUDED
|
||||
#define STATICGRAPH_H_INCLUDED
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <vector>
|
||||
#ifdef _GLIBCXX_PARALLEL
|
||||
#include <parallel/algorithm>
|
||||
#else
|
||||
#include <algorithm>
|
||||
#endif
|
||||
*/
|
||||
|
||||
#ifndef STATIC_GRAPH_H
|
||||
#define STATIC_GRAPH_H
|
||||
|
||||
#include "../DataStructures/Percent.h"
|
||||
#include "../DataStructures/SharedMemoryVectorWrapper.h"
|
||||
#include "../Util/SimpleLogger.h"
|
||||
#include "../typedefs.h"
|
||||
#include "ImportEdge.h"
|
||||
|
||||
template< typename EdgeDataT>
|
||||
class StaticGraph {
|
||||
public:
|
||||
#include <boost/range/irange.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
template <typename EdgeDataT, bool UseSharedMemory = false> class StaticGraph
|
||||
{
|
||||
public:
|
||||
typedef decltype(boost::irange(0u,0u)) EdgeRange;
|
||||
typedef NodeID NodeIterator;
|
||||
typedef NodeID EdgeIterator;
|
||||
typedef EdgeDataT EdgeData;
|
||||
class InputEdge {
|
||||
public:
|
||||
class InputEdge
|
||||
{
|
||||
public:
|
||||
EdgeDataT data;
|
||||
NodeIterator source;
|
||||
NodeIterator target;
|
||||
bool operator<( const InputEdge& right ) const {
|
||||
if ( source != right.source )
|
||||
bool operator<(const InputEdge &right) const
|
||||
{
|
||||
if (source != right.source)
|
||||
{
|
||||
return source < right.source;
|
||||
}
|
||||
return target < right.target;
|
||||
}
|
||||
};
|
||||
|
||||
StaticGraph( const int nodes, std::vector< InputEdge > &graph ) {
|
||||
#ifdef _GLIBCXX_PARALLEL
|
||||
__gnu_parallel::sort( graph.begin(), graph.end() );
|
||||
#else
|
||||
std::sort( graph.begin(), graph.end() );
|
||||
#endif
|
||||
_numNodes = nodes;
|
||||
_numEdges = ( EdgeIterator ) graph.size();
|
||||
_nodes.resize( _numNodes + 1);
|
||||
struct NodeArrayEntry
|
||||
{
|
||||
// index of the first edge
|
||||
EdgeIterator first_edge;
|
||||
};
|
||||
|
||||
struct EdgeArrayEntry
|
||||
{
|
||||
NodeID target;
|
||||
EdgeDataT data;
|
||||
};
|
||||
|
||||
EdgeRange GetAdjacentEdgeRange(const NodeID node) const
|
||||
{
|
||||
return boost::irange(BeginEdges(node), EndEdges(node));
|
||||
}
|
||||
|
||||
StaticGraph(const int nodes, std::vector<InputEdge> &graph)
|
||||
{
|
||||
std::sort(graph.begin(), graph.end());
|
||||
number_of_nodes = nodes;
|
||||
number_of_edges = (EdgeIterator)graph.size();
|
||||
node_array.resize(number_of_nodes + 1);
|
||||
EdgeIterator edge = 0;
|
||||
EdgeIterator position = 0;
|
||||
for ( NodeIterator node = 0; node <= _numNodes; ++node ) {
|
||||
EdgeIterator lastEdge = edge;
|
||||
while ( edge < _numEdges && graph[edge].source == node )
|
||||
for (NodeIterator node = 0; node <= number_of_nodes; ++node)
|
||||
{
|
||||
EdgeIterator last_edge = edge;
|
||||
while (edge < number_of_edges && graph[edge].source == node)
|
||||
{
|
||||
++edge;
|
||||
_nodes[node].firstEdge = position; //=edge
|
||||
position += edge - lastEdge; //remove
|
||||
}
|
||||
node_array[node].first_edge = position; //=edge
|
||||
position += edge - last_edge; // remove
|
||||
}
|
||||
_edges.resize( position ); //(edge)
|
||||
edge_array.resize(position); //(edge)
|
||||
edge = 0;
|
||||
for ( NodeIterator node = 0; node < _numNodes; ++node ) {
|
||||
for ( EdgeIterator i = _nodes[node].firstEdge, e = _nodes[node+1].firstEdge; i != e; ++i ) {
|
||||
_edges[i].target = graph[edge].target;
|
||||
_edges[i].data = graph[edge].data;
|
||||
assert(_edges[i].data.distance > 0);
|
||||
for (NodeIterator node = 0; node < number_of_nodes; ++node)
|
||||
{
|
||||
EdgeIterator e = node_array[node + 1].first_edge;
|
||||
for (EdgeIterator i = node_array[node].first_edge; i != e; ++i)
|
||||
{
|
||||
edge_array[i].target = graph[edge].target;
|
||||
edge_array[i].data = graph[edge].data;
|
||||
assert(edge_array[i].data.distance > 0);
|
||||
edge++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned GetNumberOfNodes() const {
|
||||
return _numNodes;
|
||||
StaticGraph(typename ShM<NodeArrayEntry, UseSharedMemory>::vector &nodes,
|
||||
typename ShM<EdgeArrayEntry, UseSharedMemory>::vector &edges)
|
||||
{
|
||||
number_of_nodes = nodes.size() - 1;
|
||||
number_of_edges = edges.size();
|
||||
|
||||
node_array.swap(nodes);
|
||||
edge_array.swap(edges);
|
||||
|
||||
#ifndef NDEBUG
|
||||
Percent p(GetNumberOfNodes());
|
||||
for (unsigned u = 0; u < GetNumberOfNodes(); ++u)
|
||||
{
|
||||
for (auto eid : GetAdjacentEdgeRange(u))
|
||||
{
|
||||
const unsigned v = GetTarget(eid);
|
||||
const EdgeData &data = GetEdgeData(eid);
|
||||
if (data.shortcut)
|
||||
{
|
||||
const EdgeID first_edge_id = FindEdgeInEitherDirection(u, data.id);
|
||||
if (SPECIAL_EDGEID == first_edge_id)
|
||||
{
|
||||
SimpleLogger().Write(logWARNING) << "cannot find first segment of edge ("
|
||||
<< u << "," << data.id << "," << v
|
||||
<< "), eid: " << eid;
|
||||
BOOST_ASSERT(false);
|
||||
}
|
||||
const EdgeID second_edge_id = FindEdgeInEitherDirection(data.id, v);
|
||||
if (SPECIAL_EDGEID == second_edge_id)
|
||||
{
|
||||
SimpleLogger().Write(logWARNING) << "cannot find second segment of edge ("
|
||||
<< u << "," << data.id << "," << v
|
||||
<< "), eid: " << eid;
|
||||
BOOST_ASSERT(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
p.printIncrement();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned GetNumberOfEdges() const {
|
||||
return _numEdges;
|
||||
unsigned GetNumberOfNodes() const { return number_of_nodes -1; }
|
||||
|
||||
unsigned GetNumberOfEdges() const { return number_of_edges; }
|
||||
|
||||
unsigned GetOutDegree(const NodeIterator n) const { return BeginEdges(n) - EndEdges(n) - 1; }
|
||||
|
||||
inline NodeIterator GetTarget(const EdgeIterator e) const
|
||||
{
|
||||
return NodeIterator(edge_array[e].target);
|
||||
}
|
||||
|
||||
unsigned GetOutDegree( const NodeIterator &n ) const {
|
||||
return BeginEdges(n)-EndEdges(n) - 1;
|
||||
inline EdgeDataT &GetEdgeData(const EdgeIterator e) { return edge_array[e].data; }
|
||||
|
||||
const EdgeDataT &GetEdgeData(const EdgeIterator e) const { return edge_array[e].data; }
|
||||
|
||||
EdgeIterator BeginEdges(const NodeIterator n) const
|
||||
{
|
||||
return EdgeIterator(node_array.at(n).first_edge);
|
||||
}
|
||||
|
||||
inline NodeIterator GetTarget( const EdgeIterator &e ) const {
|
||||
return NodeIterator( _edges[e].target );
|
||||
EdgeIterator EndEdges(const NodeIterator n) const
|
||||
{
|
||||
return EdgeIterator(node_array.at(n + 1).first_edge);
|
||||
}
|
||||
|
||||
inline EdgeDataT &GetEdgeData( const EdgeIterator &e ) {
|
||||
return _edges[e].data;
|
||||
}
|
||||
|
||||
const EdgeDataT &GetEdgeData( const EdgeIterator &e ) const {
|
||||
return _edges[e].data;
|
||||
}
|
||||
|
||||
EdgeIterator BeginEdges( const NodeIterator &n ) const {
|
||||
return EdgeIterator( _nodes[n].firstEdge );
|
||||
}
|
||||
|
||||
EdgeIterator EndEdges( const NodeIterator &n ) const {
|
||||
return EdgeIterator( _nodes[n+1].firstEdge );
|
||||
}
|
||||
|
||||
//searches for a specific edge
|
||||
EdgeIterator FindEdge( const NodeIterator &from, const NodeIterator &to ) const {
|
||||
EdgeIterator smallestEdge = SPECIAL_EDGEID;
|
||||
EdgeWeight smallestWeight = UINT_MAX;
|
||||
for ( EdgeIterator edge = BeginEdges( from ); edge < EndEdges(from); edge++ ) {
|
||||
// searches for a specific edge
|
||||
EdgeIterator FindEdge(const NodeIterator from, const NodeIterator to) const
|
||||
{
|
||||
EdgeIterator smallest_edge = SPECIAL_EDGEID;
|
||||
EdgeWeight smallest_weight = INVALID_EDGE_WEIGHT;
|
||||
for (auto edge : GetAdjacentEdgeRange(from))
|
||||
{
|
||||
const NodeID target = GetTarget(edge);
|
||||
const EdgeWeight weight = GetEdgeData(edge).distance;
|
||||
if(target == to && weight < smallestWeight) {
|
||||
smallestEdge = edge; smallestWeight = weight;
|
||||
if (target == to && weight < smallest_weight)
|
||||
{
|
||||
smallest_edge = edge;
|
||||
smallest_weight = weight;
|
||||
}
|
||||
}
|
||||
return smallestEdge;
|
||||
return smallest_edge;
|
||||
}
|
||||
|
||||
EdgeIterator FindEdgeInEitherDirection( const NodeIterator &from, const NodeIterator &to ) const {
|
||||
EdgeIterator tmp = FindEdge( from, to );
|
||||
return (UINT_MAX != tmp ? tmp : FindEdge( to, from ));
|
||||
EdgeIterator FindEdgeInEitherDirection(const NodeIterator from, const NodeIterator to) const
|
||||
{
|
||||
EdgeIterator tmp = FindEdge(from, to);
|
||||
return (SPECIAL_NODEID != tmp ? tmp : FindEdge(to, from));
|
||||
}
|
||||
|
||||
EdgeIterator FindEdgeIndicateIfReverse( const NodeIterator &from, const NodeIterator &to, bool & result ) const {
|
||||
EdgeIterator tmp = FindEdge( from, to );
|
||||
if(UINT_MAX == tmp) {
|
||||
tmp = FindEdge( to, from );
|
||||
if(UINT_MAX != tmp)
|
||||
EdgeIterator
|
||||
FindEdgeIndicateIfReverse(const NodeIterator from, const NodeIterator to, bool &result) const
|
||||
{
|
||||
EdgeIterator current_iterator = FindEdge(from, to);
|
||||
if (SPECIAL_NODEID == current_iterator)
|
||||
{
|
||||
current_iterator = FindEdge(to, from);
|
||||
if (SPECIAL_NODEID != current_iterator)
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return tmp;
|
||||
return current_iterator;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
NodeIterator number_of_nodes;
|
||||
EdgeIterator number_of_edges;
|
||||
|
||||
struct _StrNode {
|
||||
//index of the first edge
|
||||
EdgeIterator firstEdge;
|
||||
};
|
||||
|
||||
struct _StrEdge {
|
||||
NodeID target;
|
||||
EdgeDataT data;
|
||||
};
|
||||
|
||||
NodeIterator _numNodes;
|
||||
EdgeIterator _numEdges;
|
||||
|
||||
std::vector< _StrNode > _nodes;
|
||||
std::vector< _StrEdge > _edges;
|
||||
typename ShM<NodeArrayEntry, UseSharedMemory>::vector node_array;
|
||||
typename ShM<EdgeArrayEntry, UseSharedMemory>::vector edge_array;
|
||||
};
|
||||
|
||||
#endif // STATICGRAPH_H_INCLUDED
|
||||
#endif // STATIC_GRAPH_H
|
||||
|
||||
@@ -1,48 +1,55 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
KD Tree coded by Christian Vetter, Monav Project
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
// KD Tree coded by Christian Vetter, Monav Project
|
||||
|
||||
*/
|
||||
#ifndef STATICKDTREE_H_INCLUDED
|
||||
#define STATICKDTREE_H_INCLUDED
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <vector>
|
||||
#ifdef _GLIBCXX_PARALLEL
|
||||
#include <parallel/algorithm>
|
||||
#else
|
||||
#include <algorithm>
|
||||
#endif
|
||||
#include <stack>
|
||||
#include <limits>
|
||||
|
||||
namespace KDTree {
|
||||
namespace KDTree
|
||||
{
|
||||
|
||||
#define KDTREE_BASESIZE (8)
|
||||
|
||||
template< unsigned k, typename T >
|
||||
class BoundingBox {
|
||||
public:
|
||||
BoundingBox() {
|
||||
for ( unsigned dim = 0; dim < k; ++dim ) {
|
||||
min[dim] = std::numeric_limits< T >::min();
|
||||
max[dim] = std::numeric_limits< T >::max();
|
||||
template <unsigned k, typename T> class BoundingBox
|
||||
{
|
||||
public:
|
||||
BoundingBox()
|
||||
{
|
||||
for (unsigned dim = 0; dim < k; ++dim)
|
||||
{
|
||||
min[dim] = std::numeric_limits<T>::min();
|
||||
max[dim] = std::numeric_limits<T>::max();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,106 +57,118 @@ public:
|
||||
T max[k];
|
||||
};
|
||||
|
||||
struct NoData {};
|
||||
struct NoData
|
||||
{
|
||||
};
|
||||
|
||||
template< unsigned k, typename T >
|
||||
class EuclidianMetric {
|
||||
public:
|
||||
double operator() ( const T left[k], const T right[k] ) {
|
||||
template <unsigned k, typename T> class EuclidianMetric
|
||||
{
|
||||
public:
|
||||
double operator()(const T left[k], const T right[k])
|
||||
{
|
||||
double result = 0;
|
||||
for ( unsigned i = 0; i < k; ++i ) {
|
||||
for (unsigned i = 0; i < k; ++i)
|
||||
{
|
||||
double temp = (double)left[i] - (double)right[i];
|
||||
result += temp * temp;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
double operator() ( const BoundingBox< k, T > &box, const T point[k] ) {
|
||||
double operator()(const BoundingBox<k, T> &box, const T point[k])
|
||||
{
|
||||
T nearest[k];
|
||||
for ( unsigned dim = 0; dim < k; ++dim ) {
|
||||
if ( point[dim] < box.min[dim] )
|
||||
for (unsigned dim = 0; dim < k; ++dim)
|
||||
{
|
||||
if (point[dim] < box.min[dim])
|
||||
nearest[dim] = box.min[dim];
|
||||
else if ( point[dim] > box.max[dim] )
|
||||
else if (point[dim] > box.max[dim])
|
||||
nearest[dim] = box.max[dim];
|
||||
else
|
||||
nearest[dim] = point[dim];
|
||||
}
|
||||
return operator() ( point, nearest );
|
||||
return operator()(point, nearest);
|
||||
}
|
||||
};
|
||||
|
||||
template < unsigned k, typename T, typename Data = NoData, typename Metric = EuclidianMetric< k, T > >
|
||||
class StaticKDTree {
|
||||
public:
|
||||
|
||||
struct InputPoint {
|
||||
template <unsigned k, typename T, typename Data = NoData, typename Metric = EuclidianMetric<k, T>>
|
||||
class StaticKDTree
|
||||
{
|
||||
public:
|
||||
struct InputPoint
|
||||
{
|
||||
T coordinates[k];
|
||||
Data data;
|
||||
bool operator==( const InputPoint& right )
|
||||
{
|
||||
for ( int i = 0; i < k; i++ ) {
|
||||
if ( coordinates[i] != right.coordinates[i] )
|
||||
bool operator==(const InputPoint &right)
|
||||
{
|
||||
for (int i = 0; i < k; i++)
|
||||
{
|
||||
if (coordinates[i] != right.coordinates[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
StaticKDTree( std::vector< InputPoint > * points ){
|
||||
assert( k > 0 );
|
||||
assert ( points->size() > 0 );
|
||||
explicit StaticKDTree(std::vector<InputPoint> *points)
|
||||
{
|
||||
BOOST_ASSERT(k > 0);
|
||||
BOOST_ASSERT(points->size() > 0);
|
||||
size = points->size();
|
||||
kdtree = new InputPoint[size];
|
||||
for ( Iterator i = 0; i != size; ++i ) {
|
||||
for (Iterator i = 0; i != size; ++i)
|
||||
{
|
||||
kdtree[i] = points->at(i);
|
||||
for ( unsigned dim = 0; dim < k; ++dim ) {
|
||||
if ( kdtree[i].coordinates[dim] < boundingBox.min[dim] )
|
||||
for (unsigned dim = 0; dim < k; ++dim)
|
||||
{
|
||||
if (kdtree[i].coordinates[dim] < boundingBox.min[dim])
|
||||
boundingBox.min[dim] = kdtree[i].coordinates[dim];
|
||||
if ( kdtree[i].coordinates[dim] > boundingBox.max[dim] )
|
||||
if (kdtree[i].coordinates[dim] > boundingBox.max[dim])
|
||||
boundingBox.max[dim] = kdtree[i].coordinates[dim];
|
||||
}
|
||||
}
|
||||
std::stack< Tree > s;
|
||||
s.push ( Tree ( 0, size, 0 ) );
|
||||
while ( !s.empty() ) {
|
||||
std::stack<Tree> s;
|
||||
s.push(Tree(0, size, 0));
|
||||
while (!s.empty())
|
||||
{
|
||||
Tree tree = s.top();
|
||||
s.pop();
|
||||
|
||||
if ( tree.right - tree.left < KDTREE_BASESIZE )
|
||||
if (tree.right - tree.left < KDTREE_BASESIZE)
|
||||
continue;
|
||||
|
||||
Iterator middle = tree.left + ( tree.right - tree.left ) / 2;
|
||||
#ifdef _GLIBCXX_PARALLEL
|
||||
__gnu_parallel::nth_element( kdtree + tree.left, kdtree + middle, kdtree + tree.right, Less( tree.dimension ) );
|
||||
#else
|
||||
std::nth_element( kdtree + tree.left, kdtree + middle, kdtree + tree.right, Less( tree.dimension ) );
|
||||
#endif
|
||||
s.push( Tree( tree.left, middle, ( tree.dimension + 1 ) % k ) );
|
||||
s.push( Tree( middle + 1, tree.right, ( tree.dimension + 1 ) % k ) );
|
||||
Iterator middle = tree.left + (tree.right - tree.left) / 2;
|
||||
std::nth_element(
|
||||
kdtree + tree.left, kdtree + middle, kdtree + tree.right, Less(tree.dimension));
|
||||
s.push(Tree(tree.left, middle, (tree.dimension + 1) % k));
|
||||
s.push(Tree(middle + 1, tree.right, (tree.dimension + 1) % k));
|
||||
}
|
||||
}
|
||||
|
||||
~StaticKDTree(){
|
||||
delete[] kdtree;
|
||||
}
|
||||
~StaticKDTree() { delete[] kdtree; }
|
||||
|
||||
bool NearestNeighbor( InputPoint* result, const InputPoint& point ) {
|
||||
bool NearestNeighbor(InputPoint *result, const InputPoint &point)
|
||||
{
|
||||
Metric distance;
|
||||
bool found = false;
|
||||
double nearestDistance = std::numeric_limits< T >::max();
|
||||
std::stack< NNTree > s;
|
||||
s.push ( NNTree ( 0, size, 0, boundingBox ) );
|
||||
while ( !s.empty() ) {
|
||||
double nearestDistance = std::numeric_limits<T>::max();
|
||||
std::stack<NNTree> s;
|
||||
s.push(NNTree(0, size, 0, boundingBox));
|
||||
while (!s.empty())
|
||||
{
|
||||
NNTree tree = s.top();
|
||||
s.pop();
|
||||
|
||||
if ( distance( tree.box, point.coordinates ) >= nearestDistance )
|
||||
if (distance(tree.box, point.coordinates) >= nearestDistance)
|
||||
continue;
|
||||
|
||||
if ( tree.right - tree.left < KDTREE_BASESIZE ) {
|
||||
for ( unsigned i = tree.left; i < tree.right; i++ ) {
|
||||
double newDistance = distance( kdtree[i].coordinates, point.coordinates );
|
||||
if ( newDistance < nearestDistance ) {
|
||||
if (tree.right - tree.left < KDTREE_BASESIZE)
|
||||
{
|
||||
for (unsigned i = tree.left; i < tree.right; i++)
|
||||
{
|
||||
double newDistance = distance(kdtree[i].coordinates, point.coordinates);
|
||||
if (newDistance < nearestDistance)
|
||||
{
|
||||
nearestDistance = newDistance;
|
||||
*result = kdtree[i];
|
||||
found = true;
|
||||
@@ -158,73 +177,84 @@ public:
|
||||
continue;
|
||||
}
|
||||
|
||||
Iterator middle = tree.left + ( tree.right - tree.left ) / 2;
|
||||
Iterator middle = tree.left + (tree.right - tree.left) / 2;
|
||||
|
||||
double newDistance = distance( kdtree[middle].coordinates, point.coordinates );
|
||||
if ( newDistance < nearestDistance ) {
|
||||
double newDistance = distance(kdtree[middle].coordinates, point.coordinates);
|
||||
if (newDistance < nearestDistance)
|
||||
{
|
||||
nearestDistance = newDistance;
|
||||
*result = kdtree[middle];
|
||||
found = true;
|
||||
}
|
||||
|
||||
Less comperator( tree.dimension );
|
||||
if ( !comperator( point, kdtree[middle] ) ) {
|
||||
NNTree first( middle + 1, tree.right, ( tree.dimension + 1 ) % k, tree.box );
|
||||
NNTree second( tree.left, middle, ( tree.dimension + 1 ) % k, tree.box );
|
||||
Less comperator(tree.dimension);
|
||||
if (!comperator(point, kdtree[middle]))
|
||||
{
|
||||
NNTree first(middle + 1, tree.right, (tree.dimension + 1) % k, tree.box);
|
||||
NNTree second(tree.left, middle, (tree.dimension + 1) % k, tree.box);
|
||||
first.box.min[tree.dimension] = kdtree[middle].coordinates[tree.dimension];
|
||||
second.box.max[tree.dimension] = kdtree[middle].coordinates[tree.dimension];
|
||||
s.push( second );
|
||||
s.push( first );
|
||||
s.push(second);
|
||||
s.push(first);
|
||||
}
|
||||
else {
|
||||
NNTree first( middle + 1, tree.right, ( tree.dimension + 1 ) % k, tree.box );
|
||||
NNTree second( tree.left, middle, ( tree.dimension + 1 ) % k, tree.box );
|
||||
else
|
||||
{
|
||||
NNTree first(middle + 1, tree.right, (tree.dimension + 1) % k, tree.box);
|
||||
NNTree second(tree.left, middle, (tree.dimension + 1) % k, tree.box);
|
||||
first.box.min[tree.dimension] = kdtree[middle].coordinates[tree.dimension];
|
||||
second.box.max[tree.dimension] = kdtree[middle].coordinates[tree.dimension];
|
||||
s.push( first );
|
||||
s.push( second );
|
||||
s.push(first);
|
||||
s.push(second);
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
private:
|
||||
private:
|
||||
typedef unsigned Iterator;
|
||||
struct Tree {
|
||||
struct Tree
|
||||
{
|
||||
Iterator left;
|
||||
Iterator right;
|
||||
unsigned dimension;
|
||||
Tree() {}
|
||||
Tree( Iterator l, Iterator r, unsigned d ): left( l ), right( r ), dimension( d ) {}
|
||||
Tree(Iterator l, Iterator r, unsigned d) : left(l), right(r), dimension(d) {}
|
||||
};
|
||||
struct NNTree {
|
||||
struct NNTree
|
||||
{
|
||||
Iterator left;
|
||||
Iterator right;
|
||||
unsigned dimension;
|
||||
BoundingBox< k, T > box;
|
||||
BoundingBox<k, T> box;
|
||||
NNTree() {}
|
||||
NNTree( Iterator l, Iterator r, unsigned d, const BoundingBox< k, T >& b ): left( l ), right( r ), dimension( d ), box ( b ) {}
|
||||
NNTree(Iterator l, Iterator r, unsigned d, const BoundingBox<k, T> &b)
|
||||
: left(l), right(r), dimension(d), box(b)
|
||||
{
|
||||
}
|
||||
};
|
||||
class Less {
|
||||
public:
|
||||
Less( unsigned d ) {
|
||||
class Less
|
||||
{
|
||||
public:
|
||||
explicit Less(unsigned d)
|
||||
{
|
||||
dimension = d;
|
||||
assert( dimension < k );
|
||||
BOOST_ASSERT(dimension < k);
|
||||
}
|
||||
|
||||
bool operator() ( const InputPoint& left, const InputPoint& right ) {
|
||||
assert( dimension < k );
|
||||
bool operator()(const InputPoint &left, const InputPoint &right)
|
||||
{
|
||||
BOOST_ASSERT(dimension < k);
|
||||
return left.coordinates[dimension] < right.coordinates[dimension];
|
||||
}
|
||||
private:
|
||||
|
||||
private:
|
||||
unsigned dimension;
|
||||
};
|
||||
|
||||
BoundingBox< k, T > boundingBox;
|
||||
InputPoint* kdtree;
|
||||
BoundingBox<k, T> boundingBox;
|
||||
InputPoint *kdtree;
|
||||
Iterator size;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // STATICKDTREE_H_INCLUDED
|
||||
|
||||
@@ -0,0 +1,781 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef STATICRTREE_H
|
||||
#define STATICRTREE_H
|
||||
|
||||
#include "DeallocatingVector.h"
|
||||
#include "HilbertValue.h"
|
||||
#include "PhantomNodes.h"
|
||||
#include "QueryNode.h"
|
||||
#include "SharedMemoryFactory.h"
|
||||
#include "SharedMemoryVectorWrapper.h"
|
||||
|
||||
#include "../Util/MercatorUtil.h"
|
||||
#include "../Util/OSRMException.h"
|
||||
#include "../Util/SimpleLogger.h"
|
||||
#include "../typedefs.h"
|
||||
|
||||
#include <osrm/Coordinate.h>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// tuning parameters
|
||||
const static uint32_t RTREE_BRANCHING_FACTOR = 64;
|
||||
const static uint32_t RTREE_LEAF_NODE_SIZE = 1024;
|
||||
|
||||
static boost::thread_specific_ptr<boost::filesystem::ifstream> thread_local_rtree_stream;
|
||||
|
||||
// Implements a static, i.e. packed, R-tree
|
||||
template <class DataT,
|
||||
class CoordinateListT = std::vector<FixedPointCoordinate>,
|
||||
bool UseSharedMemory = false>
|
||||
class StaticRTree
|
||||
{
|
||||
public:
|
||||
struct RectangleInt2D
|
||||
{
|
||||
RectangleInt2D() : min_lon(INT_MAX), max_lon(INT_MIN), min_lat(INT_MAX), max_lat(INT_MIN) {}
|
||||
|
||||
int32_t min_lon, max_lon;
|
||||
int32_t min_lat, max_lat;
|
||||
|
||||
inline void InitializeMBRectangle(const std::array<DataT, RTREE_LEAF_NODE_SIZE> &objects,
|
||||
const uint32_t element_count,
|
||||
const std::vector<NodeInfo> &coordinate_list)
|
||||
{
|
||||
for (uint32_t i = 0; i < element_count; ++i)
|
||||
{
|
||||
min_lon = std::min(min_lon,
|
||||
std::min(coordinate_list.at(objects[i].u).lon,
|
||||
coordinate_list.at(objects[i].v).lon));
|
||||
max_lon = std::max(max_lon,
|
||||
std::max(coordinate_list.at(objects[i].u).lon,
|
||||
coordinate_list.at(objects[i].v).lon));
|
||||
|
||||
min_lat = std::min(min_lat,
|
||||
std::min(coordinate_list.at(objects[i].u).lat,
|
||||
coordinate_list.at(objects[i].v).lat));
|
||||
max_lat = std::max(max_lat,
|
||||
std::max(coordinate_list.at(objects[i].u).lat,
|
||||
coordinate_list.at(objects[i].v).lat));
|
||||
}
|
||||
}
|
||||
|
||||
inline void AugmentMBRectangle(const RectangleInt2D &other)
|
||||
{
|
||||
min_lon = std::min(min_lon, other.min_lon);
|
||||
max_lon = std::max(max_lon, other.max_lon);
|
||||
min_lat = std::min(min_lat, other.min_lat);
|
||||
max_lat = std::max(max_lat, other.max_lat);
|
||||
}
|
||||
|
||||
inline FixedPointCoordinate Centroid() const
|
||||
{
|
||||
FixedPointCoordinate centroid;
|
||||
// The coordinates of the midpoints are given by:
|
||||
// x = (x1 + x2) /2 and y = (y1 + y2) /2.
|
||||
centroid.lon = (min_lon + max_lon) / 2;
|
||||
centroid.lat = (min_lat + max_lat) / 2;
|
||||
return centroid;
|
||||
}
|
||||
|
||||
inline bool Intersects(const RectangleInt2D &other) const
|
||||
{
|
||||
FixedPointCoordinate upper_left(other.max_lat, other.min_lon);
|
||||
FixedPointCoordinate upper_right(other.max_lat, other.max_lon);
|
||||
FixedPointCoordinate lower_right(other.min_lat, other.max_lon);
|
||||
FixedPointCoordinate lower_left(other.min_lat, other.min_lon);
|
||||
|
||||
return (Contains(upper_left) || Contains(upper_right) || Contains(lower_right) ||
|
||||
Contains(lower_left));
|
||||
}
|
||||
|
||||
inline float GetMinDist(const FixedPointCoordinate &location) const
|
||||
{
|
||||
bool is_contained = Contains(location);
|
||||
if (is_contained)
|
||||
{
|
||||
return 0.;
|
||||
}
|
||||
|
||||
float min_dist = std::numeric_limits<float>::max();
|
||||
min_dist = std::min(min_dist,
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(
|
||||
location.lat, location.lon, max_lat, min_lon));
|
||||
min_dist = std::min(min_dist,
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(
|
||||
location.lat, location.lon, max_lat, max_lon));
|
||||
min_dist = std::min(min_dist,
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(
|
||||
location.lat, location.lon, min_lat, max_lon));
|
||||
min_dist = std::min(min_dist,
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(
|
||||
location.lat, location.lon, min_lat, min_lon));
|
||||
return min_dist;
|
||||
}
|
||||
|
||||
inline float GetMinMaxDist(const FixedPointCoordinate &location) const
|
||||
{
|
||||
float min_max_dist = std::numeric_limits<float>::max();
|
||||
// Get minmax distance to each of the four sides
|
||||
FixedPointCoordinate upper_left(max_lat, min_lon);
|
||||
FixedPointCoordinate upper_right(max_lat, max_lon);
|
||||
FixedPointCoordinate lower_right(min_lat, max_lon);
|
||||
FixedPointCoordinate lower_left(min_lat, min_lon);
|
||||
|
||||
min_max_dist = std::min(
|
||||
min_max_dist,
|
||||
std::max(
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(location, upper_left),
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(location, upper_right)));
|
||||
|
||||
min_max_dist = std::min(
|
||||
min_max_dist,
|
||||
std::max(
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(location, upper_right),
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(location, lower_right)));
|
||||
|
||||
min_max_dist = std::min(
|
||||
min_max_dist,
|
||||
std::max(FixedPointCoordinate::ApproximateEuclideanDistance(location, lower_right),
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(location, lower_left)));
|
||||
|
||||
min_max_dist = std::min(
|
||||
min_max_dist,
|
||||
std::max(FixedPointCoordinate::ApproximateEuclideanDistance(location, lower_left),
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(location, upper_left)));
|
||||
return min_max_dist;
|
||||
}
|
||||
|
||||
inline bool Contains(const FixedPointCoordinate &location) const
|
||||
{
|
||||
const bool lats_contained = (location.lat > min_lat) && (location.lat < max_lat);
|
||||
const bool lons_contained = (location.lon > min_lon) && (location.lon < max_lon);
|
||||
return lats_contained && lons_contained;
|
||||
}
|
||||
|
||||
inline friend std::ostream &operator<<(std::ostream &out, const RectangleInt2D &rect)
|
||||
{
|
||||
out << rect.min_lat / COORDINATE_PRECISION << "," << rect.min_lon / COORDINATE_PRECISION
|
||||
<< " " << rect.max_lat / COORDINATE_PRECISION << ","
|
||||
<< rect.max_lon / COORDINATE_PRECISION;
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
typedef RectangleInt2D RectangleT;
|
||||
|
||||
struct TreeNode
|
||||
{
|
||||
TreeNode() : child_count(0), child_is_on_disk(false) {}
|
||||
RectangleT minimum_bounding_rectangle;
|
||||
uint32_t child_count : 31;
|
||||
bool child_is_on_disk : 1;
|
||||
uint32_t children[RTREE_BRANCHING_FACTOR];
|
||||
};
|
||||
|
||||
private:
|
||||
struct WrappedInputElement
|
||||
{
|
||||
explicit WrappedInputElement(const uint32_t _array_index, const uint64_t _hilbert_value)
|
||||
: m_array_index(_array_index), m_hilbert_value(_hilbert_value)
|
||||
{
|
||||
}
|
||||
|
||||
WrappedInputElement() : m_array_index(UINT_MAX), m_hilbert_value(0) {}
|
||||
|
||||
uint32_t m_array_index;
|
||||
uint64_t m_hilbert_value;
|
||||
|
||||
inline bool operator<(const WrappedInputElement &other) const
|
||||
{
|
||||
return m_hilbert_value < other.m_hilbert_value;
|
||||
}
|
||||
};
|
||||
|
||||
struct LeafNode
|
||||
{
|
||||
LeafNode() : object_count(0) {}
|
||||
uint32_t object_count;
|
||||
std::array<DataT, RTREE_LEAF_NODE_SIZE> objects;
|
||||
};
|
||||
|
||||
struct QueryCandidate
|
||||
{
|
||||
explicit QueryCandidate(const uint32_t n_id, const float dist)
|
||||
: node_id(n_id), min_dist(dist)
|
||||
{
|
||||
}
|
||||
QueryCandidate() : node_id(UINT_MAX), min_dist(std::numeric_limits<float>::max()) {}
|
||||
uint32_t node_id;
|
||||
float min_dist;
|
||||
inline bool operator<(const QueryCandidate &other) const
|
||||
{
|
||||
return min_dist < other.min_dist;
|
||||
}
|
||||
};
|
||||
|
||||
typename ShM<TreeNode, UseSharedMemory>::vector m_search_tree;
|
||||
uint64_t m_element_count;
|
||||
const std::string m_leaf_node_filename;
|
||||
std::shared_ptr<CoordinateListT> m_coordinate_list;
|
||||
|
||||
public:
|
||||
StaticRTree() = delete;
|
||||
StaticRTree(const StaticRTree &) = delete;
|
||||
|
||||
// Construct a packed Hilbert-R-Tree with Kamel-Faloutsos algorithm [1]
|
||||
explicit StaticRTree(std::vector<DataT> &input_data_vector,
|
||||
const std::string tree_node_filename,
|
||||
const std::string leaf_node_filename,
|
||||
const std::vector<NodeInfo> &coordinate_list)
|
||||
: m_element_count(input_data_vector.size()), m_leaf_node_filename(leaf_node_filename)
|
||||
{
|
||||
SimpleLogger().Write() << "constructing r-tree of " << m_element_count
|
||||
<< " edge elements build on-top of " << coordinate_list.size()
|
||||
<< " coordinates";
|
||||
|
||||
std::chrono::time_point<std::chrono::steady_clock> time0 = std::chrono::steady_clock::now();
|
||||
std::vector<WrappedInputElement> input_wrapper_vector(m_element_count);
|
||||
|
||||
HilbertCode get_hilbert_number;
|
||||
|
||||
// generate auxiliary vector of hilbert-values
|
||||
#pragma omp parallel for schedule(guided)
|
||||
for (uint64_t element_counter = 0; element_counter < m_element_count; ++element_counter)
|
||||
{
|
||||
input_wrapper_vector[element_counter].m_array_index = element_counter;
|
||||
// Get Hilbert-Value for centroid in mercartor projection
|
||||
DataT const ¤t_element = input_data_vector[element_counter];
|
||||
FixedPointCoordinate current_centroid =
|
||||
DataT::Centroid(FixedPointCoordinate(coordinate_list.at(current_element.u).lat,
|
||||
coordinate_list.at(current_element.u).lon),
|
||||
FixedPointCoordinate(coordinate_list.at(current_element.v).lat,
|
||||
coordinate_list.at(current_element.v).lon));
|
||||
current_centroid.lat =
|
||||
COORDINATE_PRECISION * lat2y(current_centroid.lat / COORDINATE_PRECISION);
|
||||
|
||||
uint64_t current_hilbert_value = get_hilbert_number(current_centroid);
|
||||
input_wrapper_vector[element_counter].m_hilbert_value = current_hilbert_value;
|
||||
}
|
||||
|
||||
// open leaf file
|
||||
boost::filesystem::ofstream leaf_node_file(leaf_node_filename, std::ios::binary);
|
||||
leaf_node_file.write((char *)&m_element_count, sizeof(uint64_t));
|
||||
|
||||
// sort the hilbert-value representatives
|
||||
std::sort(input_wrapper_vector.begin(), input_wrapper_vector.end());
|
||||
std::vector<TreeNode> tree_nodes_in_level;
|
||||
|
||||
// pack M elements into leaf node and write to leaf file
|
||||
uint64_t processed_objects_count = 0;
|
||||
while (processed_objects_count < m_element_count)
|
||||
{
|
||||
|
||||
LeafNode current_leaf;
|
||||
TreeNode current_node;
|
||||
// SimpleLogger().Write() << "reading " << tree_size << " tree nodes in " <<
|
||||
// (sizeof(TreeNode)*tree_size) << " bytes";
|
||||
for (uint32_t current_element_index = 0; RTREE_LEAF_NODE_SIZE > current_element_index;
|
||||
++current_element_index)
|
||||
{
|
||||
if (m_element_count > (processed_objects_count + current_element_index))
|
||||
{
|
||||
uint32_t index_of_next_object =
|
||||
input_wrapper_vector[processed_objects_count + current_element_index]
|
||||
.m_array_index;
|
||||
current_leaf.objects[current_element_index] =
|
||||
input_data_vector[index_of_next_object];
|
||||
++current_leaf.object_count;
|
||||
}
|
||||
}
|
||||
|
||||
// generate tree node that resemble the objects in leaf and store it for next level
|
||||
current_node.minimum_bounding_rectangle.InitializeMBRectangle(
|
||||
current_leaf.objects, current_leaf.object_count, coordinate_list);
|
||||
current_node.child_is_on_disk = true;
|
||||
current_node.children[0] = tree_nodes_in_level.size();
|
||||
tree_nodes_in_level.emplace_back(current_node);
|
||||
|
||||
// write leaf_node to leaf node file
|
||||
leaf_node_file.write((char *)¤t_leaf, sizeof(current_leaf));
|
||||
processed_objects_count += current_leaf.object_count;
|
||||
}
|
||||
|
||||
// close leaf file
|
||||
leaf_node_file.close();
|
||||
|
||||
uint32_t processing_level = 0;
|
||||
while (1 < tree_nodes_in_level.size())
|
||||
{
|
||||
std::vector<TreeNode> tree_nodes_in_next_level;
|
||||
uint32_t processed_tree_nodes_in_level = 0;
|
||||
while (processed_tree_nodes_in_level < tree_nodes_in_level.size())
|
||||
{
|
||||
TreeNode parent_node;
|
||||
// pack RTREE_BRANCHING_FACTOR elements into tree_nodes each
|
||||
for (uint32_t current_child_node_index = 0;
|
||||
RTREE_BRANCHING_FACTOR > current_child_node_index;
|
||||
++current_child_node_index)
|
||||
{
|
||||
if (processed_tree_nodes_in_level < tree_nodes_in_level.size())
|
||||
{
|
||||
TreeNode ¤t_child_node =
|
||||
tree_nodes_in_level[processed_tree_nodes_in_level];
|
||||
// add tree node to parent entry
|
||||
parent_node.children[current_child_node_index] = m_search_tree.size();
|
||||
m_search_tree.emplace_back(current_child_node);
|
||||
// augment MBR of parent
|
||||
parent_node.minimum_bounding_rectangle.AugmentMBRectangle(
|
||||
current_child_node.minimum_bounding_rectangle);
|
||||
// increase counters
|
||||
++parent_node.child_count;
|
||||
++processed_tree_nodes_in_level;
|
||||
}
|
||||
}
|
||||
tree_nodes_in_next_level.emplace_back(parent_node);
|
||||
}
|
||||
tree_nodes_in_level.swap(tree_nodes_in_next_level);
|
||||
++processing_level;
|
||||
}
|
||||
BOOST_ASSERT_MSG(1 == tree_nodes_in_level.size(), "tree broken, more than one root node");
|
||||
// last remaining entry is the root node, store it
|
||||
m_search_tree.emplace_back(tree_nodes_in_level[0]);
|
||||
|
||||
// reverse and renumber tree to have root at index 0
|
||||
std::reverse(m_search_tree.begin(), m_search_tree.end());
|
||||
|
||||
#pragma omp parallel for schedule(guided)
|
||||
for (uint32_t i = 0; i < m_search_tree.size(); ++i)
|
||||
{
|
||||
TreeNode ¤t_tree_node = m_search_tree[i];
|
||||
for (uint32_t j = 0; j < current_tree_node.child_count; ++j)
|
||||
{
|
||||
const uint32_t old_id = current_tree_node.children[j];
|
||||
const uint32_t new_id = m_search_tree.size() - old_id - 1;
|
||||
current_tree_node.children[j] = new_id;
|
||||
}
|
||||
}
|
||||
|
||||
// open tree file
|
||||
boost::filesystem::ofstream tree_node_file(tree_node_filename, std::ios::binary);
|
||||
|
||||
uint32_t size_of_tree = m_search_tree.size();
|
||||
BOOST_ASSERT_MSG(0 < size_of_tree, "tree empty");
|
||||
tree_node_file.write((char *)&size_of_tree, sizeof(uint32_t));
|
||||
tree_node_file.write((char *)&m_search_tree[0], sizeof(TreeNode) * size_of_tree);
|
||||
// close tree node file.
|
||||
tree_node_file.close();
|
||||
std::chrono::time_point<std::chrono::steady_clock> time1 = std::chrono::steady_clock::now();
|
||||
std::chrono::duration<double> elapsed_seconds = time1 - time0;
|
||||
SimpleLogger().Write() << "finished r-tree construction in " << (elapsed_seconds.count())
|
||||
<< " seconds";
|
||||
}
|
||||
|
||||
// Read-only operation for queries
|
||||
explicit StaticRTree(const boost::filesystem::path &node_file,
|
||||
const boost::filesystem::path &leaf_file,
|
||||
const std::shared_ptr<CoordinateListT> coordinate_list)
|
||||
: m_leaf_node_filename(leaf_file.string())
|
||||
{
|
||||
// open tree node file and load into RAM.
|
||||
m_coordinate_list = coordinate_list;
|
||||
|
||||
if (!boost::filesystem::exists(node_file))
|
||||
{
|
||||
throw OSRMException("ram index file does not exist");
|
||||
}
|
||||
if (0 == boost::filesystem::file_size(node_file))
|
||||
{
|
||||
throw OSRMException("ram index file is empty");
|
||||
}
|
||||
boost::filesystem::ifstream tree_node_file(node_file, std::ios::binary);
|
||||
|
||||
uint32_t tree_size = 0;
|
||||
tree_node_file.read((char *)&tree_size, sizeof(uint32_t));
|
||||
|
||||
m_search_tree.resize(tree_size);
|
||||
tree_node_file.read((char *)&m_search_tree[0], sizeof(TreeNode) * tree_size);
|
||||
tree_node_file.close();
|
||||
// open leaf node file and store thread specific pointer
|
||||
if (!boost::filesystem::exists(leaf_file))
|
||||
{
|
||||
throw OSRMException("mem index file does not exist");
|
||||
}
|
||||
if (0 == boost::filesystem::file_size(leaf_file))
|
||||
{
|
||||
throw OSRMException("mem index file is empty");
|
||||
}
|
||||
|
||||
boost::filesystem::ifstream leaf_node_file(leaf_file, std::ios::binary);
|
||||
leaf_node_file.read((char *)&m_element_count, sizeof(uint64_t));
|
||||
leaf_node_file.close();
|
||||
|
||||
// SimpleLogger().Write() << tree_size << " nodes in search tree";
|
||||
// SimpleLogger().Write() << m_element_count << " elements in leafs";
|
||||
}
|
||||
|
||||
explicit StaticRTree(TreeNode *tree_node_ptr,
|
||||
const uint32_t number_of_nodes,
|
||||
const boost::filesystem::path &leaf_file,
|
||||
std::shared_ptr<CoordinateListT> coordinate_list)
|
||||
: m_search_tree(tree_node_ptr, number_of_nodes), m_leaf_node_filename(leaf_file.string()),
|
||||
m_coordinate_list(coordinate_list)
|
||||
{
|
||||
// open leaf node file and store thread specific pointer
|
||||
if (!boost::filesystem::exists(leaf_file))
|
||||
{
|
||||
throw OSRMException("mem index file does not exist");
|
||||
}
|
||||
if (0 == boost::filesystem::file_size(leaf_file))
|
||||
{
|
||||
throw OSRMException("mem index file is empty");
|
||||
}
|
||||
|
||||
boost::filesystem::ifstream leaf_node_file(leaf_file, std::ios::binary);
|
||||
leaf_node_file.read((char *)&m_element_count, sizeof(uint64_t));
|
||||
leaf_node_file.close();
|
||||
|
||||
if (thread_local_rtree_stream.get())
|
||||
{
|
||||
thread_local_rtree_stream->close();
|
||||
}
|
||||
|
||||
// SimpleLogger().Write() << tree_size << " nodes in search tree";
|
||||
// SimpleLogger().Write() << m_element_count << " elements in leafs";
|
||||
}
|
||||
// Read-only operation for queries
|
||||
|
||||
bool LocateClosestEndPointForCoordinate(const FixedPointCoordinate &input_coordinate,
|
||||
FixedPointCoordinate &result_coordinate,
|
||||
const unsigned zoom_level)
|
||||
{
|
||||
bool ignore_tiny_components = (zoom_level <= 14);
|
||||
DataT nearest_edge;
|
||||
float min_dist = std::numeric_limits<float>::max();
|
||||
float min_max_dist = std::numeric_limits<float>::max();
|
||||
bool found_a_nearest_edge = false;
|
||||
|
||||
// initialize queue with root element
|
||||
std::priority_queue<QueryCandidate> traversal_queue;
|
||||
float current_min_dist =
|
||||
m_search_tree[0].minimum_bounding_rectangle.GetMinDist(input_coordinate);
|
||||
traversal_queue.emplace(0, current_min_dist);
|
||||
|
||||
while (!traversal_queue.empty())
|
||||
{
|
||||
const QueryCandidate current_query_node = traversal_queue.top();
|
||||
traversal_queue.pop();
|
||||
|
||||
const bool prune_downward = (current_query_node.min_dist >= min_max_dist);
|
||||
const bool prune_upward = (current_query_node.min_dist >= min_dist);
|
||||
if (!prune_downward && !prune_upward)
|
||||
{ // downward pruning
|
||||
TreeNode ¤t_tree_node = m_search_tree[current_query_node.node_id];
|
||||
if (current_tree_node.child_is_on_disk)
|
||||
{
|
||||
LeafNode current_leaf_node;
|
||||
LoadLeafFromDisk(current_tree_node.children[0], current_leaf_node);
|
||||
for (uint32_t i = 0; i < current_leaf_node.object_count; ++i)
|
||||
{
|
||||
DataT const ¤t_edge = current_leaf_node.objects[i];
|
||||
if (ignore_tiny_components && current_edge.is_in_tiny_cc)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float current_minimum_distance =
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(
|
||||
input_coordinate.lat,
|
||||
input_coordinate.lon,
|
||||
m_coordinate_list->at(current_edge.u).lat,
|
||||
m_coordinate_list->at(current_edge.u).lon);
|
||||
if (current_minimum_distance < min_dist)
|
||||
{
|
||||
// found a new minimum
|
||||
min_dist = current_minimum_distance;
|
||||
result_coordinate.lat = m_coordinate_list->at(current_edge.u).lat;
|
||||
result_coordinate.lon = m_coordinate_list->at(current_edge.u).lon;
|
||||
found_a_nearest_edge = true;
|
||||
}
|
||||
|
||||
current_minimum_distance =
|
||||
FixedPointCoordinate::ApproximateEuclideanDistance(
|
||||
input_coordinate.lat,
|
||||
input_coordinate.lon,
|
||||
m_coordinate_list->at(current_edge.v).lat,
|
||||
m_coordinate_list->at(current_edge.v).lon);
|
||||
|
||||
if (current_minimum_distance < min_dist)
|
||||
{
|
||||
// found a new minimum
|
||||
min_dist = current_minimum_distance;
|
||||
result_coordinate.lat = m_coordinate_list->at(current_edge.v).lat;
|
||||
result_coordinate.lon = m_coordinate_list->at(current_edge.v).lon;
|
||||
found_a_nearest_edge = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// traverse children, prune if global mindist is smaller than local one
|
||||
for (uint32_t i = 0; i < current_tree_node.child_count; ++i)
|
||||
{
|
||||
const int32_t child_id = current_tree_node.children[i];
|
||||
const TreeNode &child_tree_node = m_search_tree[child_id];
|
||||
const RectangleT &child_rectangle =
|
||||
child_tree_node.minimum_bounding_rectangle;
|
||||
const float current_min_dist =
|
||||
child_rectangle.GetMinDist(input_coordinate);
|
||||
const float current_min_max_dist =
|
||||
child_rectangle.GetMinMaxDist(input_coordinate);
|
||||
if (current_min_max_dist < min_max_dist)
|
||||
{
|
||||
min_max_dist = current_min_max_dist;
|
||||
}
|
||||
if (current_min_dist > min_max_dist)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (current_min_dist > min_dist)
|
||||
{ // upward pruning
|
||||
continue;
|
||||
}
|
||||
traversal_queue.emplace(child_id, current_min_dist);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found_a_nearest_edge;
|
||||
}
|
||||
|
||||
bool FindPhantomNodeForCoordinate(const FixedPointCoordinate &input_coordinate,
|
||||
PhantomNode &result_phantom_node,
|
||||
const unsigned zoom_level)
|
||||
{
|
||||
// SimpleLogger().Write() << "searching for coordinate " << input_coordinate;
|
||||
|
||||
const bool ignore_tiny_components = (zoom_level <= 14);
|
||||
DataT nearest_edge;
|
||||
|
||||
float min_dist = std::numeric_limits<float>::max();
|
||||
float min_max_dist = std::numeric_limits<float>::max();
|
||||
bool found_a_nearest_edge = false;
|
||||
|
||||
FixedPointCoordinate nearest, current_start_coordinate, current_end_coordinate;
|
||||
|
||||
// initialize queue with root element
|
||||
std::priority_queue<QueryCandidate> traversal_queue;
|
||||
float current_min_dist =
|
||||
m_search_tree[0].minimum_bounding_rectangle.GetMinDist(input_coordinate);
|
||||
traversal_queue.emplace(0, current_min_dist);
|
||||
|
||||
BOOST_ASSERT_MSG(std::numeric_limits<float>::epsilon() >
|
||||
(0. - traversal_queue.top().min_dist),
|
||||
"Root element in NN Search has min dist != 0.");
|
||||
|
||||
LeafNode current_leaf_node;
|
||||
while (!traversal_queue.empty())
|
||||
{
|
||||
const QueryCandidate current_query_node = traversal_queue.top();
|
||||
traversal_queue.pop();
|
||||
|
||||
const bool prune_downward = (current_query_node.min_dist >= min_max_dist);
|
||||
const bool prune_upward = (current_query_node.min_dist >= min_dist);
|
||||
if (!prune_downward && !prune_upward)
|
||||
{ // downward pruning
|
||||
const TreeNode ¤t_tree_node = m_search_tree[current_query_node.node_id];
|
||||
if (current_tree_node.child_is_on_disk)
|
||||
{
|
||||
LoadLeafFromDisk(current_tree_node.children[0], current_leaf_node);
|
||||
for (uint32_t i = 0; i < current_leaf_node.object_count; ++i)
|
||||
{
|
||||
DataT ¤t_edge = current_leaf_node.objects[i];
|
||||
if (ignore_tiny_components && current_edge.is_in_tiny_cc)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float current_ratio = 0.;
|
||||
const float current_perpendicular_distance =
|
||||
FixedPointCoordinate::ComputePerpendicularDistance(
|
||||
m_coordinate_list->at(current_edge.u),
|
||||
m_coordinate_list->at(current_edge.v),
|
||||
input_coordinate,
|
||||
nearest,
|
||||
current_ratio);
|
||||
|
||||
BOOST_ASSERT(0. <= current_perpendicular_distance);
|
||||
|
||||
if ((current_perpendicular_distance < min_dist) &&
|
||||
!EpsilonCompare(current_perpendicular_distance, min_dist))
|
||||
{ // found a new minimum
|
||||
min_dist = current_perpendicular_distance;
|
||||
// TODO: use assignment c'tor in PhantomNode
|
||||
result_phantom_node.forward_node_id =
|
||||
current_edge.forward_edge_based_node_id;
|
||||
result_phantom_node.reverse_node_id =
|
||||
current_edge.reverse_edge_based_node_id;
|
||||
result_phantom_node.name_id = current_edge.name_id;
|
||||
result_phantom_node.forward_weight = current_edge.forward_weight;
|
||||
result_phantom_node.reverse_weight = current_edge.reverse_weight;
|
||||
result_phantom_node.forward_offset = current_edge.forward_offset;
|
||||
result_phantom_node.reverse_offset = current_edge.reverse_offset;
|
||||
result_phantom_node.packed_geometry_id =
|
||||
current_edge.packed_geometry_id;
|
||||
result_phantom_node.fwd_segment_position =
|
||||
current_edge.fwd_segment_position;
|
||||
|
||||
result_phantom_node.location = nearest;
|
||||
current_start_coordinate.lat =
|
||||
m_coordinate_list->at(current_edge.u).lat;
|
||||
current_start_coordinate.lon =
|
||||
m_coordinate_list->at(current_edge.u).lon;
|
||||
current_end_coordinate.lat = m_coordinate_list->at(current_edge.v).lat;
|
||||
current_end_coordinate.lon = m_coordinate_list->at(current_edge.v).lon;
|
||||
nearest_edge = current_edge;
|
||||
found_a_nearest_edge = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// traverse children, prune if global mindist is smaller than local one
|
||||
for (uint32_t i = 0; i < current_tree_node.child_count; ++i)
|
||||
{
|
||||
const int32_t child_id = current_tree_node.children[i];
|
||||
TreeNode &child_tree_node = m_search_tree[child_id];
|
||||
RectangleT &child_rectangle = child_tree_node.minimum_bounding_rectangle;
|
||||
const float current_min_dist =
|
||||
child_rectangle.GetMinDist(input_coordinate);
|
||||
const float current_min_max_dist =
|
||||
child_rectangle.GetMinMaxDist(input_coordinate);
|
||||
if (current_min_max_dist < min_max_dist)
|
||||
{
|
||||
min_max_dist = current_min_max_dist;
|
||||
}
|
||||
if (current_min_dist > min_max_dist)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (current_min_dist > min_dist)
|
||||
{ // upward pruning
|
||||
continue;
|
||||
}
|
||||
traversal_queue.emplace(child_id, current_min_dist);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hack to fix rounding errors and wandering via nodes.
|
||||
if (1 == std::abs(input_coordinate.lon - result_phantom_node.location.lon))
|
||||
{
|
||||
result_phantom_node.location.lon = input_coordinate.lon;
|
||||
}
|
||||
if (1 == std::abs(input_coordinate.lat - result_phantom_node.location.lat))
|
||||
{
|
||||
result_phantom_node.location.lat = input_coordinate.lat;
|
||||
}
|
||||
|
||||
float ratio = 0.f;
|
||||
|
||||
if (found_a_nearest_edge)
|
||||
{
|
||||
const float distance_1 = FixedPointCoordinate::ApproximateEuclideanDistance(
|
||||
current_start_coordinate, result_phantom_node.location);
|
||||
|
||||
const float distance_2 = FixedPointCoordinate::ApproximateEuclideanDistance(
|
||||
current_start_coordinate, current_end_coordinate);
|
||||
|
||||
ratio = distance_1 / distance_2;
|
||||
ratio = std::min(1.f, ratio);
|
||||
|
||||
if (SPECIAL_NODEID != result_phantom_node.forward_node_id)
|
||||
{
|
||||
result_phantom_node.forward_weight *= ratio;
|
||||
}
|
||||
if (SPECIAL_NODEID != result_phantom_node.reverse_node_id)
|
||||
{
|
||||
result_phantom_node.reverse_weight *= (1. - ratio);
|
||||
}
|
||||
}
|
||||
return found_a_nearest_edge;
|
||||
}
|
||||
|
||||
private:
|
||||
inline void LoadLeafFromDisk(const uint32_t leaf_id, LeafNode &result_node)
|
||||
{
|
||||
if (!thread_local_rtree_stream.get() || !thread_local_rtree_stream->is_open())
|
||||
{
|
||||
thread_local_rtree_stream.reset(new boost::filesystem::ifstream(
|
||||
m_leaf_node_filename, std::ios::in | std::ios::binary));
|
||||
}
|
||||
if (!thread_local_rtree_stream->good())
|
||||
{
|
||||
thread_local_rtree_stream->clear(std::ios::goodbit);
|
||||
SimpleLogger().Write(logDEBUG) << "Resetting stale filestream";
|
||||
}
|
||||
uint64_t seek_pos = sizeof(uint64_t) + leaf_id * sizeof(LeafNode);
|
||||
thread_local_rtree_stream->seekg(seek_pos);
|
||||
thread_local_rtree_stream->read((char *)&result_node, sizeof(LeafNode));
|
||||
}
|
||||
|
||||
inline bool EdgesAreEquivalent(const FixedPointCoordinate &a,
|
||||
const FixedPointCoordinate &b,
|
||||
const FixedPointCoordinate &c,
|
||||
const FixedPointCoordinate &d) const
|
||||
{
|
||||
return (a == b && c == d) || (a == c && b == d) || (a == d && b == c);
|
||||
}
|
||||
|
||||
template<typename FloatT>
|
||||
inline bool EpsilonCompare(const FloatT d1, const FloatT d2) const
|
||||
{
|
||||
return (std::abs(d1 - d2) < std::numeric_limits<FloatT>::epsilon());
|
||||
}
|
||||
};
|
||||
|
||||
//[1] "On Packing R-Trees"; I. Kamel, C. Faloutsos; 1993; DOI: 10.1145/170088.170403
|
||||
//[2] "Nearest Neighbor Queries", N. Roussopulos et al; 1995; DOI: 10.1145/223784.223794
|
||||
|
||||
#endif // STATICRTREE_H
|
||||
@@ -1,154 +1,90 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
#ifndef TURNINSTRUCTIONS_H_
|
||||
#define TURNINSTRUCTIONS_H_
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <string>
|
||||
*/
|
||||
|
||||
//This is a hack until c++0x is available enough to use scoped enums
|
||||
struct TurnInstructionsClass {
|
||||
|
||||
const static short NoTurn = 0; //Give no instruction at all
|
||||
const static short GoStraight = 1; //Tell user to go straight!
|
||||
const static short TurnSlightRight = 2;
|
||||
const static short TurnRight = 3;
|
||||
const static short TurnSharpRight = 4;
|
||||
const static short UTurn = 5;
|
||||
const static short TurnSharpLeft = 6;
|
||||
const static short TurnLeft = 7;
|
||||
const static short TurnSlightLeft = 8;
|
||||
const static short ReachViaPoint = 9;
|
||||
const static short HeadOn = 10;
|
||||
const static short EnterRoundAbout = 11;
|
||||
const static short LeaveRoundAbout = 12;
|
||||
const static short StayOnRoundAbout = 13;
|
||||
|
||||
std::string TurnStrings[14];
|
||||
std::string Ordinals[11];
|
||||
|
||||
//This is a hack until c++0x is available enough to use initializer lists.
|
||||
TurnInstructionsClass(){
|
||||
TurnStrings [0] = "";
|
||||
TurnStrings [1] = "Continue";
|
||||
TurnStrings [2] = "Turn slight right";
|
||||
TurnStrings [3] = "Turn right";
|
||||
TurnStrings [4] = "Turn sharp right";
|
||||
TurnStrings [5] = "U-Turn";
|
||||
TurnStrings [6] = "Turn sharp left";
|
||||
TurnStrings [7] = "Turn left";
|
||||
TurnStrings [8] = "Turn slight left";
|
||||
TurnStrings [9] = "Reach via point";
|
||||
TurnStrings[10] = "Head";
|
||||
TurnStrings[11] = "Enter round-about";
|
||||
TurnStrings[12] = "Leave round-about";
|
||||
TurnStrings[13] = "Stay on round-about";
|
||||
|
||||
Ordinals[0] = "zeroth";
|
||||
Ordinals[1] = "first";
|
||||
Ordinals[2] = "second";
|
||||
Ordinals[3] = "third";
|
||||
Ordinals[4] = "fourth";
|
||||
Ordinals[5] = "fifth";
|
||||
Ordinals[6] = "sixth";
|
||||
Ordinals[7] = "seventh";
|
||||
Ordinals[8] = "eighth";
|
||||
Ordinals[9] = "nineth";
|
||||
Ordinals[10] = "tenth";
|
||||
};
|
||||
|
||||
static inline double GetTurnDirectionOfInstruction( const double angle ) {
|
||||
if(angle >= 23 && angle < 67) {
|
||||
return TurnSharpRight;
|
||||
}
|
||||
if (angle >= 67 && angle < 113) {
|
||||
return TurnRight;
|
||||
}
|
||||
if (angle >= 113 && angle < 158) {
|
||||
return TurnSlightRight;
|
||||
}
|
||||
if (angle >= 158 && angle < 202) {
|
||||
return GoStraight;
|
||||
}
|
||||
if (angle >= 202 && angle < 248) {
|
||||
return TurnSlightLeft;
|
||||
}
|
||||
if (angle >= 248 && angle < 292) {
|
||||
return TurnLeft;
|
||||
}
|
||||
if (angle >= 292 && angle < 336) {
|
||||
return TurnSharpLeft;
|
||||
}
|
||||
return 5;
|
||||
}
|
||||
|
||||
static inline bool TurnIsNecessary ( const short turnInstruction ) {
|
||||
if(NoTurn == turnInstruction || StayOnRoundAbout == turnInstruction)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// static inline void getDirectionOfInstruction(double angle, DirectionOfInstruction & dirInst) {
|
||||
// if(angle >= 23 && angle < 67) {
|
||||
// dirInst.direction = "southeast";
|
||||
// dirInst.shortDirection = "SE";
|
||||
// return;
|
||||
// }
|
||||
// if(angle >= 67 && angle < 113) {
|
||||
// dirInst.direction = "south";
|
||||
// dirInst.shortDirection = "S";
|
||||
// return;
|
||||
// }
|
||||
// if(angle >= 113 && angle < 158) {
|
||||
// dirInst.direction = "southwest";
|
||||
// dirInst.shortDirection = "SW";
|
||||
// return;
|
||||
// }
|
||||
// if(angle >= 158 && angle < 202) {
|
||||
// dirInst.direction = "west";
|
||||
// dirInst.shortDirection = "W";
|
||||
// return;
|
||||
// }
|
||||
// if(angle >= 202 && angle < 248) {
|
||||
// dirInst.direction = "northwest";
|
||||
// dirInst.shortDirection = "NW";
|
||||
// return;
|
||||
// }
|
||||
// if(angle >= 248 && angle < 292) {
|
||||
// dirInst.direction = "north";
|
||||
// dirInst.shortDirection = "N";
|
||||
// return;
|
||||
// }
|
||||
// if(angle >= 292 && angle < 336) {
|
||||
// dirInst.direction = "northeast";
|
||||
// dirInst.shortDirection = "NE";
|
||||
// return;
|
||||
// }
|
||||
// dirInst.direction = "East";
|
||||
// dirInst.shortDirection = "E";
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
#ifndef TURN_INSTRUCTIONS_H
|
||||
#define TURN_INSTRUCTIONS_H
|
||||
|
||||
enum class TurnInstruction : unsigned char
|
||||
{
|
||||
NoTurn = 0, GoStraight, TurnSlightRight, TurnRight, TurnSharpRight, UTurn,
|
||||
TurnSharpLeft, TurnLeft, TurnSlightLeft, ReachViaPoint, HeadOn, EnterRoundAbout,
|
||||
LeaveRoundAbout, StayOnRoundAbout, StartAtEndOfStreet, ReachedYourDestination,
|
||||
EnterAgainstAllowedDirection, LeaveAgainstAllowedDirection,
|
||||
InverseAccessRestrictionFlag = 127,
|
||||
AccessRestrictionFlag = 128,
|
||||
AccessRestrictionPenalty = 129
|
||||
};
|
||||
|
||||
static TurnInstructionsClass TurnInstructions;
|
||||
struct TurnInstructionsClass
|
||||
{
|
||||
TurnInstructionsClass() = delete;
|
||||
TurnInstructionsClass(const TurnInstructionsClass&) = delete;
|
||||
|
||||
#endif /* TURNINSTRUCTIONS_H_ */
|
||||
static inline TurnInstruction GetTurnDirectionOfInstruction(const double angle)
|
||||
{
|
||||
if (angle >= 23 && angle < 67)
|
||||
{
|
||||
return TurnInstruction::TurnSharpRight;
|
||||
}
|
||||
if (angle >= 67 && angle < 113)
|
||||
{
|
||||
return TurnInstruction::TurnRight;
|
||||
}
|
||||
if (angle >= 113 && angle < 158)
|
||||
{
|
||||
return TurnInstruction::TurnSlightRight;
|
||||
}
|
||||
if (angle >= 158 && angle < 202)
|
||||
{
|
||||
return TurnInstruction::GoStraight;
|
||||
}
|
||||
if (angle >= 202 && angle < 248)
|
||||
{
|
||||
return TurnInstruction::TurnSlightLeft;
|
||||
}
|
||||
if (angle >= 248 && angle < 292)
|
||||
{
|
||||
return TurnInstruction::TurnLeft;
|
||||
}
|
||||
if (angle >= 292 && angle < 336)
|
||||
{
|
||||
return TurnInstruction::TurnSharpLeft;
|
||||
}
|
||||
return TurnInstruction::UTurn;
|
||||
}
|
||||
|
||||
static inline bool TurnIsNecessary(const TurnInstruction turn_instruction)
|
||||
{
|
||||
if (TurnInstruction::NoTurn == turn_instruction || TurnInstruction::StayOnRoundAbout == turn_instruction)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* TURN_INSTRUCTIONS_H */
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
|
||||
#ifndef TIMEUTIL_H_
|
||||
#define TIMEUTIL_H_
|
||||
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <sys/timeb.h>
|
||||
#include <sys/types.h>
|
||||
#include <winsock.h>
|
||||
void gettimeofday(struct timeval* t,void* timezone)
|
||||
{ struct _timeb timebuffer;
|
||||
_ftime( &timebuffer );
|
||||
t->tv_sec=timebuffer.time;
|
||||
t->tv_usec=1000*timebuffer.millitm;
|
||||
}
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <boost/functional/hash.hpp>
|
||||
#else
|
||||
#include <tr1/functional_hash.h>
|
||||
#endif
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
/** Returns a timestamp (now) in seconds (incl. a fractional part). */
|
||||
static inline double get_timestamp() {
|
||||
struct timeval tp;
|
||||
gettimeofday(&tp, NULL);
|
||||
return double(tp.tv_sec) + tp.tv_usec / 1000000.;
|
||||
}
|
||||
|
||||
static inline double y2lat(double a) { return 180/M_PI * (2 * atan(exp(a*M_PI/180)) - M_PI/2); }
|
||||
static inline double lat2y(double a) { return 180/M_PI * log(tan(M_PI/4+a*(M_PI/180)/2)); }
|
||||
|
||||
static inline unsigned boost_thread_id_hash(boost::thread::id const& id) {
|
||||
std::stringstream ostr;
|
||||
ostr << id;
|
||||
std::tr1::hash<std::string> h;
|
||||
return h(ostr.str());
|
||||
}
|
||||
|
||||
#endif /* TIMEUTIL_H_ */
|
||||
@@ -1,217 +0,0 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
|
||||
#ifndef XMLPARSER_H_
|
||||
#define XMLPARSER_H_
|
||||
|
||||
#include <libxml/xmlreader.h>
|
||||
|
||||
#include "../typedefs.h"
|
||||
#include "BaseParser.h"
|
||||
#include "HashTable.h"
|
||||
#include "ExtractorStructs.h"
|
||||
#include "InputReaderFactory.h"
|
||||
|
||||
class XMLParser : public BaseParser<_Node, _RawRestrictionContainer, _Way> {
|
||||
public:
|
||||
XMLParser(const char * filename) {
|
||||
WARN("Parsing plain .osm/.osm.bz2 is deprecated. Switch to .pbf");
|
||||
inputReader = inputReaderFactory(filename);
|
||||
}
|
||||
virtual ~XMLParser() {}
|
||||
|
||||
bool RegisterCallbacks(bool (*nodeCallbackPointer)(_Node), bool (*restrictionCallbackPointer)(_RawRestrictionContainer), bool (*wayCallbackPointer)(_Way), bool (*addressCallbackPointer)(_Node, HashTable<std::string, std::string>) ) {
|
||||
nodeCallback = *nodeCallbackPointer;
|
||||
wayCallback = *wayCallbackPointer;
|
||||
restrictionCallback = *restrictionCallbackPointer;
|
||||
return true;
|
||||
}
|
||||
bool Init() {
|
||||
return (xmlTextReaderRead( inputReader ) == 1);
|
||||
}
|
||||
bool Parse() {
|
||||
while ( xmlTextReaderRead( inputReader ) == 1 ) {
|
||||
const int type = xmlTextReaderNodeType( inputReader );
|
||||
|
||||
//1 is Element
|
||||
if ( type != 1 )
|
||||
continue;
|
||||
|
||||
xmlChar* currentName = xmlTextReaderName( inputReader );
|
||||
if ( currentName == NULL )
|
||||
continue;
|
||||
|
||||
if ( xmlStrEqual( currentName, ( const xmlChar* ) "node" ) == 1 ) {
|
||||
_Node n = _ReadXMLNode( );
|
||||
if(!(*nodeCallback)(n))
|
||||
std::cerr << "[XMLParser] node not parsed" << std::endl;
|
||||
}
|
||||
|
||||
if ( xmlStrEqual( currentName, ( const xmlChar* ) "way" ) == 1 ) {
|
||||
string name;
|
||||
_Way way = _ReadXMLWay( );
|
||||
if(!(*wayCallback)(way)) {
|
||||
std::cerr << "[XMLParser] way not parsed" << std::endl;
|
||||
}
|
||||
}
|
||||
if ( xmlStrEqual( currentName, ( const xmlChar* ) "relation" ) == 1 ) {
|
||||
_Relation r;
|
||||
r.type = _Relation::unknown;
|
||||
//todo: parse relation
|
||||
}
|
||||
xmlFree( currentName );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
_Relation _ReadXMLRelation ( ) {
|
||||
_Relation relation;
|
||||
relation.type = _Relation::unknown;
|
||||
|
||||
return relation;
|
||||
}
|
||||
|
||||
_Way _ReadXMLWay( ) {
|
||||
_Way way;
|
||||
way.direction = _Way::notSure;
|
||||
way.speed = -1;
|
||||
way.type = -1;
|
||||
way.useful = false;
|
||||
way.access = true;
|
||||
// cout << "new way" << endl;
|
||||
if ( xmlTextReaderIsEmptyElement( inputReader ) != 1 ) {
|
||||
const int depth = xmlTextReaderDepth( inputReader );
|
||||
while ( xmlTextReaderRead( inputReader ) == 1 ) {
|
||||
const int childType = xmlTextReaderNodeType( inputReader );
|
||||
if ( childType != 1 && childType != 15 )
|
||||
continue;
|
||||
const int childDepth = xmlTextReaderDepth( inputReader );
|
||||
xmlChar* childName = xmlTextReaderName( inputReader );
|
||||
if ( childName == NULL )
|
||||
continue;
|
||||
|
||||
if ( depth == childDepth && childType == 15 && xmlStrEqual( childName, ( const xmlChar* ) "way" ) == 1 ) {
|
||||
xmlFree( childName );
|
||||
break;
|
||||
}
|
||||
if ( childType != 1 ) {
|
||||
xmlFree( childName );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( xmlStrEqual( childName, ( const xmlChar* ) "tag" ) == 1 ) {
|
||||
xmlChar* k = xmlTextReaderGetAttribute( inputReader, ( const xmlChar* ) "k" );
|
||||
xmlChar* value = xmlTextReaderGetAttribute( inputReader, ( const xmlChar* ) "v" );
|
||||
// cout << "->k=" << k << ", v=" << value << endl;
|
||||
if ( k != NULL && value != NULL ) {
|
||||
|
||||
way.keyVals.Add(std::string( (char *) k ), std::string( (char *) value));
|
||||
}
|
||||
if ( k != NULL )
|
||||
xmlFree( k );
|
||||
if ( value != NULL )
|
||||
xmlFree( value );
|
||||
} else if ( xmlStrEqual( childName, ( const xmlChar* ) "nd" ) == 1 ) {
|
||||
xmlChar* ref = xmlTextReaderGetAttribute( inputReader, ( const xmlChar* ) "ref" );
|
||||
if ( ref != NULL ) {
|
||||
way.path.push_back( atoi(( const char* ) ref ) );
|
||||
xmlFree( ref );
|
||||
}
|
||||
}
|
||||
xmlFree( childName );
|
||||
}
|
||||
}
|
||||
return way;
|
||||
}
|
||||
|
||||
|
||||
_Node _ReadXMLNode( ) {
|
||||
_Node node;
|
||||
|
||||
xmlChar* attribute = xmlTextReaderGetAttribute( inputReader, ( const xmlChar* ) "lat" );
|
||||
if ( attribute != NULL ) {
|
||||
node.lat = static_cast<NodeID>(100000.*atof(( const char* ) attribute ) );
|
||||
xmlFree( attribute );
|
||||
}
|
||||
attribute = xmlTextReaderGetAttribute( inputReader, ( const xmlChar* ) "lon" );
|
||||
if ( attribute != NULL ) {
|
||||
node.lon = static_cast<NodeID>(100000.*atof(( const char* ) attribute ));
|
||||
xmlFree( attribute );
|
||||
}
|
||||
attribute = xmlTextReaderGetAttribute( inputReader, ( const xmlChar* ) "id" );
|
||||
if ( attribute != NULL ) {
|
||||
node.id = atoi(( const char* ) attribute );
|
||||
xmlFree( attribute );
|
||||
}
|
||||
|
||||
if ( xmlTextReaderIsEmptyElement( inputReader ) != 1 ) {
|
||||
const int depth = xmlTextReaderDepth( inputReader );
|
||||
while ( xmlTextReaderRead( inputReader ) == 1 ) {
|
||||
const int childType = xmlTextReaderNodeType( inputReader );
|
||||
// 1 = Element, 15 = EndElement
|
||||
if ( childType != 1 && childType != 15 )
|
||||
continue;
|
||||
const int childDepth = xmlTextReaderDepth( inputReader );
|
||||
xmlChar* childName = xmlTextReaderName( inputReader );
|
||||
if ( childName == NULL )
|
||||
continue;
|
||||
|
||||
if ( depth == childDepth && childType == 15 && xmlStrEqual( childName, ( const xmlChar* ) "node" ) == 1 ) {
|
||||
xmlFree( childName );
|
||||
break;
|
||||
}
|
||||
if ( childType != 1 ) {
|
||||
xmlFree( childName );
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( xmlStrEqual( childName, ( const xmlChar* ) "tag" ) == 1 ) {
|
||||
xmlChar* k = xmlTextReaderGetAttribute( inputReader, ( const xmlChar* ) "k" );
|
||||
xmlChar* value = xmlTextReaderGetAttribute( inputReader, ( const xmlChar* ) "v" );
|
||||
if ( k != NULL && value != NULL ) {
|
||||
if ( xmlStrEqual( k, ( const xmlChar* ) "highway" ) == 1 ) {
|
||||
if ( xmlStrEqual( value, ( const xmlChar* ) "traffic_signals" ) == 1 ){
|
||||
//node.trafficSignal = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( k != NULL )
|
||||
xmlFree( k );
|
||||
if ( value != NULL )
|
||||
xmlFree( value );
|
||||
}
|
||||
|
||||
xmlFree( childName );
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
/* Input Reader */
|
||||
xmlTextReaderPtr inputReader;
|
||||
|
||||
/* Function pointer for nodes */
|
||||
bool (*nodeCallback)(_Node);
|
||||
bool (*wayCallback)(_Way);
|
||||
bool (*restrictionCallback)(_RawRestrictionContainer);
|
||||
|
||||
};
|
||||
|
||||
#endif /* XMLPARSER_H_ */
|
||||
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef XOR_FAST_HASH_H
|
||||
#define XOR_FAST_HASH_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
/*
|
||||
This is an implementation of Tabulation hashing, which has suprising properties like
|
||||
universality.
|
||||
The space requirement is 2*2^16 = 256 kb of memory, which fits into L2 cache.
|
||||
Evaluation boils down to 10 or less assembly instruction on any recent X86 CPU:
|
||||
|
||||
1: movq table2(%rip), %rdx
|
||||
2: movl %edi, %eax
|
||||
3: movzwl %di, %edi
|
||||
4: shrl $16, %eax
|
||||
5: movzwl %ax, %eax
|
||||
6: movzbl (%rdx,%rax), %eax
|
||||
7: movq table1(%rip), %rdx
|
||||
8: xorb (%rdx,%rdi), %al
|
||||
9: movzbl %al, %eax
|
||||
10: ret
|
||||
|
||||
*/
|
||||
class XORFastHash
|
||||
{ // 65k entries
|
||||
std::vector<unsigned short> table1;
|
||||
std::vector<unsigned short> table2;
|
||||
|
||||
public:
|
||||
XORFastHash()
|
||||
{
|
||||
table1.resize(2 << 16);
|
||||
table2.resize(2 << 16);
|
||||
for (unsigned i = 0; i < (2 << 16); ++i)
|
||||
{
|
||||
table1[i] = i;
|
||||
table2[i] = i;
|
||||
}
|
||||
std::random_shuffle(table1.begin(), table1.end());
|
||||
std::random_shuffle(table2.begin(), table2.end());
|
||||
}
|
||||
|
||||
inline unsigned short operator()(const unsigned originalValue) const
|
||||
{
|
||||
unsigned short lsb = ((originalValue) & 0xffff);
|
||||
unsigned short msb = (((originalValue) >> 16) & 0xffff);
|
||||
return table1[lsb] ^ table2[msb];
|
||||
}
|
||||
};
|
||||
|
||||
class XORMiniHash
|
||||
{ // 256 entries
|
||||
std::vector<unsigned char> table1;
|
||||
std::vector<unsigned char> table2;
|
||||
std::vector<unsigned char> table3;
|
||||
std::vector<unsigned char> table4;
|
||||
|
||||
public:
|
||||
XORMiniHash()
|
||||
{
|
||||
table1.resize(1 << 8);
|
||||
table2.resize(1 << 8);
|
||||
table3.resize(1 << 8);
|
||||
table4.resize(1 << 8);
|
||||
for (unsigned i = 0; i < (1 << 8); ++i)
|
||||
{
|
||||
table1[i] = i;
|
||||
table2[i] = i;
|
||||
table3[i] = i;
|
||||
table4[i] = i;
|
||||
}
|
||||
std::random_shuffle(table1.begin(), table1.end());
|
||||
std::random_shuffle(table2.begin(), table2.end());
|
||||
std::random_shuffle(table3.begin(), table3.end());
|
||||
std::random_shuffle(table4.begin(), table4.end());
|
||||
}
|
||||
unsigned char operator()(const unsigned originalValue) const
|
||||
{
|
||||
unsigned char byte1 = ((originalValue) & 0xff);
|
||||
unsigned char byte2 = ((originalValue >> 8) & 0xff);
|
||||
unsigned char byte3 = ((originalValue >> 16) & 0xff);
|
||||
unsigned char byte4 = ((originalValue >> 24) & 0xff);
|
||||
return table1[byte1] ^ table2[byte2] ^ table3[byte3] ^ table4[byte4];
|
||||
}
|
||||
};
|
||||
|
||||
#endif // XOR_FAST_HASH_H
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef XOR_FAST_HASH_STORAGE_H
|
||||
#define XOR_FAST_HASH_STORAGE_H
|
||||
|
||||
#include "XORFastHash.h"
|
||||
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
template <typename NodeID, typename Key> class XORFastHashStorage
|
||||
{
|
||||
public:
|
||||
struct HashCell
|
||||
{
|
||||
Key key;
|
||||
NodeID id;
|
||||
unsigned time;
|
||||
HashCell()
|
||||
: key(std::numeric_limits<unsigned>::max()), id(std::numeric_limits<unsigned>::max()),
|
||||
time(std::numeric_limits<unsigned>::max())
|
||||
{
|
||||
}
|
||||
|
||||
HashCell(const HashCell &other) : key(other.key), id(other.id), time(other.time) {}
|
||||
|
||||
inline operator Key() const { return key; }
|
||||
|
||||
inline void operator=(const Key &key_to_insert) { key = key_to_insert; }
|
||||
};
|
||||
|
||||
explicit XORFastHashStorage(size_t) : positions(2 << 16), current_timestamp(0) {}
|
||||
|
||||
inline HashCell &operator[](const NodeID node)
|
||||
{
|
||||
unsigned short position = fast_hasher(node);
|
||||
while ((positions[position].time == current_timestamp) && (positions[position].id != node))
|
||||
{
|
||||
++position %= (2 << 16);
|
||||
}
|
||||
|
||||
positions[position].id = node;
|
||||
positions[position].time = current_timestamp;
|
||||
return positions[position];
|
||||
}
|
||||
|
||||
inline void Clear()
|
||||
{
|
||||
++current_timestamp;
|
||||
if (std::numeric_limits<unsigned>::max() == current_timestamp)
|
||||
{
|
||||
positions.clear();
|
||||
positions.resize((2 << 16));
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
XORFastHashStorage() : positions(2 << 16), current_timestamp(0) {}
|
||||
std::vector<HashCell> positions;
|
||||
XORFastHash fast_hasher;
|
||||
unsigned current_timestamp;
|
||||
};
|
||||
|
||||
#endif // XOR_FAST_HASH_STORAGE_H
|
||||
@@ -1,53 +0,0 @@
|
||||
/** Copyright (c) 2010 Scott A. Crosby. <scott@sacrosby.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
option java_package = "crosby.binary";
|
||||
package OSMPBF;
|
||||
|
||||
//protoc --java_out=../.. fileformat.proto
|
||||
|
||||
|
||||
//
|
||||
// STORAGE LAYER: Storing primitives.
|
||||
//
|
||||
|
||||
message Blob {
|
||||
optional bytes raw = 1; // No compression
|
||||
optional int32 raw_size = 2; // When compressed, the uncompressed size
|
||||
|
||||
// Possible compressed versions of the data.
|
||||
optional bytes zlib_data = 3;
|
||||
|
||||
// PROPOSED feature for LZMA compressed data. SUPPORT IS NOT REQUIRED.
|
||||
optional bytes lzma_data = 4;
|
||||
|
||||
// Formerly used for bzip2 compressed data. Depreciated in 2010.
|
||||
optional bytes OBSOLETE_bzip2_data = 5 [deprecated=true]; // Don't reuse this tag number.
|
||||
}
|
||||
|
||||
/* A file contains an sequence of fileblock headers, each prefixed by
|
||||
their length in network byte order, followed by a data block
|
||||
containing the actual data. types staring with a "_" are reserved.
|
||||
*/
|
||||
|
||||
message BlobHeader {
|
||||
required string type = 1;
|
||||
optional bytes indexdata = 2;
|
||||
required int32 datasize = 3;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,226 +0,0 @@
|
||||
/** Copyright (c) 2010 Scott A. Crosby. <scott@sacrosby.com>
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as
|
||||
published by the Free Software Foundation, either version 3 of the
|
||||
License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
option java_package = "crosby.binary";
|
||||
package OSMPBF;
|
||||
|
||||
/* OSM Binary file format
|
||||
|
||||
This is the master schema file of the OSM binary file format. This
|
||||
file is designed to support limited random-access and future
|
||||
extendability.
|
||||
|
||||
A binary OSM file consists of a sequence of FileBlocks (please see
|
||||
fileformat.proto). The first fileblock contains a serialized instance
|
||||
of HeaderBlock, followed by a sequence of PrimitiveBlock blocks that
|
||||
contain the primitives.
|
||||
|
||||
Each primitiveblock is designed to be independently parsable. It
|
||||
contains a string table storing all strings in that block (keys and
|
||||
values in tags, roles in relations, usernames, etc.) as well as
|
||||
metadata containing the precision of coordinates or timestamps in that
|
||||
block.
|
||||
|
||||
A primitiveblock contains a sequence of primitive groups, each
|
||||
containing primitives of the same type (nodes, densenodes, ways,
|
||||
relations). Coordinates are stored in signed 64-bit integers. Lat&lon
|
||||
are measured in units <granularity> nanodegrees. The default of
|
||||
granularity of 100 nanodegrees corresponds to about 1cm on the ground,
|
||||
and a full lat or lon fits into 32 bits.
|
||||
|
||||
Converting an integer to a lattitude or longitude uses the formula:
|
||||
$OUT = IN * granularity / 10**9$. Many encoding schemes use delta
|
||||
coding when representing nodes and relations.
|
||||
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/* Contains the file header. */
|
||||
|
||||
message HeaderBlock {
|
||||
optional HeaderBBox bbox = 1;
|
||||
/* Additional tags to aid in parsing this dataset */
|
||||
repeated string required_features = 4;
|
||||
repeated string optional_features = 5;
|
||||
|
||||
optional string writingprogram = 16;
|
||||
optional string source = 17; // From the bbox field.
|
||||
}
|
||||
|
||||
|
||||
/** The bounding box field in the OSM header. BBOX, as used in the OSM
|
||||
header. Units are always in nanodegrees -- they do not obey
|
||||
granularity rules. */
|
||||
|
||||
message HeaderBBox {
|
||||
required sint64 left = 1;
|
||||
required sint64 right = 2;
|
||||
required sint64 top = 3;
|
||||
required sint64 bottom = 4;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
message PrimitiveBlock {
|
||||
required StringTable stringtable = 1;
|
||||
repeated PrimitiveGroup primitivegroup = 2;
|
||||
|
||||
// Granularity, units of nanodegrees, used to store coordinates in this block
|
||||
optional int32 granularity = 17 [default=100];
|
||||
// Offset value between the output coordinates coordinates and the granularity grid in unites of nanodegrees.
|
||||
optional int64 lat_offset = 19 [default=0];
|
||||
optional int64 lon_offset = 20 [default=0];
|
||||
|
||||
// Granularity of dates, normally represented in units of milliseconds since the 1970 epoch.
|
||||
optional int32 date_granularity = 18 [default=1000];
|
||||
|
||||
|
||||
// Proposed extension:
|
||||
//optional BBox bbox = XX;
|
||||
}
|
||||
|
||||
// Group of OSMPrimitives. All primitives in a group must be the same type.
|
||||
message PrimitiveGroup {
|
||||
repeated Node nodes = 1;
|
||||
optional DenseNodes dense = 2;
|
||||
repeated Way ways = 3;
|
||||
repeated Relation relations = 4;
|
||||
repeated ChangeSet changesets = 5;
|
||||
}
|
||||
|
||||
|
||||
/** String table, contains the common strings in each block.
|
||||
|
||||
Note that we reserve index '0' as a delimiter, so the entry at that
|
||||
index in the table is ALWAYS blank and unused.
|
||||
|
||||
*/
|
||||
message StringTable {
|
||||
repeated bytes s = 1;
|
||||
}
|
||||
|
||||
/* Optional metadata that may be included into each primitive. */
|
||||
message Info {
|
||||
optional int32 version = 1 [default = -1];
|
||||
optional int64 timestamp = 2;
|
||||
optional int64 changeset = 3;
|
||||
optional int32 uid = 4;
|
||||
optional uint32 user_sid = 5; // String IDs
|
||||
}
|
||||
|
||||
/** Optional metadata that may be included into each primitive. Special dense format used in DenseNodes. */
|
||||
message DenseInfo {
|
||||
repeated int32 version = 1 [packed = true];
|
||||
repeated sint64 timestamp = 2 [packed = true]; // DELTA coded
|
||||
repeated sint64 changeset = 3 [packed = true]; // DELTA coded
|
||||
repeated sint32 uid = 4 [packed = true]; // DELTA coded
|
||||
repeated sint32 user_sid = 5 [packed = true]; // String IDs for usernames. DELTA coded
|
||||
}
|
||||
|
||||
|
||||
// THIS IS STUB DESIGN FOR CHANGESETS. NOT USED RIGHT NOW.
|
||||
// TODO: REMOVE THIS?
|
||||
message ChangeSet {
|
||||
required int64 id = 1;
|
||||
//
|
||||
// // Parallel arrays.
|
||||
// repeated uint32 keys = 2 [packed = true]; // String IDs.
|
||||
// repeated uint32 vals = 3 [packed = true]; // String IDs.
|
||||
//
|
||||
// optional Info info = 4;
|
||||
|
||||
// optional int64 created_at = 8;
|
||||
// optional int64 closetime_delta = 9;
|
||||
// optional bool open = 10;
|
||||
// optional HeaderBBox bbox = 11;
|
||||
}
|
||||
|
||||
|
||||
message Node {
|
||||
required sint64 id = 1;
|
||||
// Parallel arrays.
|
||||
repeated uint32 keys = 2 [packed = true]; // String IDs.
|
||||
repeated uint32 vals = 3 [packed = true]; // String IDs.
|
||||
|
||||
optional Info info = 4; // May be omitted in omitmeta
|
||||
|
||||
required sint64 lat = 8;
|
||||
required sint64 lon = 9;
|
||||
}
|
||||
|
||||
/* Used to densly represent a sequence of nodes that do not have any tags.
|
||||
|
||||
We represent these nodes columnwise as five columns: ID's, lats, and
|
||||
lons, all delta coded. When metadata is not omitted,
|
||||
|
||||
We encode keys & vals for all nodes as a single array of integers
|
||||
containing key-stringid and val-stringid, using a stringid of 0 as a
|
||||
delimiter between nodes.
|
||||
|
||||
( (<keyid> <valid>)* '0' )*
|
||||
*/
|
||||
|
||||
message DenseNodes {
|
||||
repeated sint64 id = 1 [packed = true]; // DELTA coded
|
||||
|
||||
//repeated Info info = 4;
|
||||
optional DenseInfo denseinfo = 5;
|
||||
|
||||
repeated sint64 lat = 8 [packed = true]; // DELTA coded
|
||||
repeated sint64 lon = 9 [packed = true]; // DELTA coded
|
||||
|
||||
// Special packing of keys and vals into one array. May be empty if all nodes in this block are tagless.
|
||||
repeated int32 keys_vals = 10 [packed = true];
|
||||
}
|
||||
|
||||
|
||||
message Way {
|
||||
required int64 id = 1;
|
||||
// Parallel arrays.
|
||||
repeated uint32 keys = 2 [packed = true];
|
||||
repeated uint32 vals = 3 [packed = true];
|
||||
|
||||
optional Info info = 4;
|
||||
|
||||
repeated sint64 refs = 8 [packed = true]; // DELTA coded
|
||||
}
|
||||
|
||||
message Relation {
|
||||
enum MemberType {
|
||||
NODE = 0;
|
||||
WAY = 1;
|
||||
RELATION = 2;
|
||||
}
|
||||
required int64 id = 1;
|
||||
|
||||
// Parallel arrays.
|
||||
repeated uint32 keys = 2 [packed = true];
|
||||
repeated uint32 vals = 3 [packed = true];
|
||||
|
||||
optional Info info = 4;
|
||||
|
||||
// Parallel arrays
|
||||
repeated int32 roles_sid = 8 [packed = true];
|
||||
repeated sint64 memids = 9 [packed = true]; // DELTA encoded
|
||||
repeated MemberType types = 10 [packed = true];
|
||||
}
|
||||
|
||||
@@ -1,73 +1,63 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
#ifndef BASE_DESCRIPTOR_H_
|
||||
#define BASE_DESCRIPTOR_H_
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef BASE_DESCRIPTOR_H
|
||||
#define BASE_DESCRIPTOR_H
|
||||
|
||||
#include "../DataStructures/PhantomNodes.h"
|
||||
#include "../DataStructures/RawRouteData.h"
|
||||
#include "../typedefs.h"
|
||||
|
||||
#include <osrm/Reply.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../typedefs.h"
|
||||
#include "../DataStructures/ExtractorStructs.h"
|
||||
#include "../DataStructures/HashTable.h"
|
||||
#include "../Util/StringUtil.h"
|
||||
|
||||
#include "../Plugins/RawRouteData.h"
|
||||
|
||||
struct _RouteSummary {
|
||||
std::string lengthString;
|
||||
std::string durationString;
|
||||
std::string startName;
|
||||
std::string destName;
|
||||
_RouteSummary() : lengthString("0"), durationString("0"), startName("unknown street"), destName("unknown street") {}
|
||||
void BuildDurationAndLengthStrings(unsigned distance, unsigned time) {
|
||||
//compute distance/duration for route summary
|
||||
std::ostringstream s;
|
||||
s << 10*(round(distance/10.));
|
||||
lengthString = s.str();
|
||||
int travelTime = time/10 + 1;
|
||||
s.str("");
|
||||
s << travelTime;
|
||||
durationString = s.str();
|
||||
struct DescriptorConfig
|
||||
{
|
||||
DescriptorConfig() : instructions(true), geometry(true), encode_geometry(true), zoom_level(18)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct _DescriptorConfig {
|
||||
_DescriptorConfig() : instructions(true), geometry(true), encodeGeometry(false), z(18) {}
|
||||
bool instructions;
|
||||
bool geometry;
|
||||
bool encodeGeometry;
|
||||
unsigned short z;
|
||||
bool encode_geometry;
|
||||
unsigned short zoom_level;
|
||||
};
|
||||
|
||||
template<class SearchEngineT>
|
||||
class BaseDescriptor {
|
||||
public:
|
||||
BaseDescriptor() { }
|
||||
//Maybe someone can explain the pure virtual destructor thing to me (dennis)
|
||||
virtual ~BaseDescriptor() { }
|
||||
virtual void Run(http::Reply & reply, RawRouteData &rawRoute, PhantomNodes &phantomNodes, SearchEngineT &sEngine, unsigned distance) = 0;
|
||||
virtual void SetConfig(const _DescriptorConfig & config) = 0;
|
||||
template <class DataFacadeT> class BaseDescriptor
|
||||
{
|
||||
public:
|
||||
BaseDescriptor() {}
|
||||
// Maybe someone can explain the pure virtual destructor thing to me (dennis)
|
||||
virtual ~BaseDescriptor() {}
|
||||
virtual void Run(const RawRouteData &raw_route,
|
||||
const PhantomNodes &phantom_nodes,
|
||||
http::Reply &reply) = 0;
|
||||
virtual void SetConfig(const DescriptorConfig &config) = 0;
|
||||
};
|
||||
|
||||
#endif /* BASE_DESCRIPTOR_H_ */
|
||||
#endif // BASE_DESCRIPTOR_H
|
||||
|
||||
@@ -1,95 +1,80 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#include "DescriptionFactory.h"
|
||||
|
||||
DescriptionFactory::DescriptionFactory() { }
|
||||
DescriptionFactory::DescriptionFactory() : entireLength(0) {}
|
||||
|
||||
DescriptionFactory::~DescriptionFactory() { }
|
||||
|
||||
double DescriptionFactory::GetAngleBetweenCoordinates() const {
|
||||
return 0.;//GetAngleBetweenTwoEdges(previousCoordinate, currentCoordinate, nextCoordinate);
|
||||
void DescriptionFactory::SetStartSegment(const PhantomNode &source)
|
||||
{
|
||||
start_phantom = source;
|
||||
AppendSegment(source.location, PathData(0, source.name_id, TurnInstruction::HeadOn, source.forward_weight));
|
||||
}
|
||||
|
||||
void DescriptionFactory::SetStartSegment(const PhantomNode & _startPhantom) {
|
||||
startPhantom = _startPhantom;
|
||||
AppendSegment(_startPhantom.location, _PathData(0, _startPhantom.nodeBasedEdgeNameID, 10, _startPhantom.weight1));
|
||||
void DescriptionFactory::SetEndSegment(const PhantomNode &target)
|
||||
{
|
||||
target_phantom = target;
|
||||
path_description.emplace_back(
|
||||
target.location, target.name_id, 0, target.reverse_weight, TurnInstruction::NoTurn, true);
|
||||
}
|
||||
|
||||
void DescriptionFactory::SetEndSegment(const PhantomNode & _targetPhantom) {
|
||||
targetPhantom = _targetPhantom;
|
||||
pathDescription.push_back(SegmentInformation(_targetPhantom.location, _targetPhantom.nodeBasedEdgeNameID, 0, _targetPhantom.weight1, 0, true) );
|
||||
}
|
||||
|
||||
void DescriptionFactory::AppendSegment(const _Coordinate & coordinate, const _PathData & data ) {
|
||||
pathDescription.push_back(SegmentInformation(coordinate, data.nameID, 0, data.durationOfSegment, data.turnInstruction) );
|
||||
}
|
||||
|
||||
void DescriptionFactory::AppendEncodedPolylineString(std::string & output, bool isEncoded) {
|
||||
if(isEncoded)
|
||||
pc.printEncodedString(pathDescription, output);
|
||||
else
|
||||
pc.printUnencodedString(pathDescription, output);
|
||||
}
|
||||
|
||||
void DescriptionFactory::AppendEncodedPolylineString(std::string &output) {
|
||||
pc.printEncodedString(pathDescription, output);
|
||||
}
|
||||
|
||||
void DescriptionFactory::AppendUnencodedPolylineString(std::string &output) {
|
||||
pc.printUnencodedString(pathDescription, output);
|
||||
}
|
||||
|
||||
unsigned DescriptionFactory::Run(const unsigned zoomLevel) {
|
||||
if(0 == pathDescription.size())
|
||||
return 0;
|
||||
|
||||
unsigned entireLength = 0;
|
||||
/** starts at index 1 */
|
||||
pathDescription[0].length = 0;
|
||||
for(unsigned i = 1; i < pathDescription.size(); ++i) {
|
||||
pathDescription[i].length = ApproximateDistance(pathDescription[i-1].location, pathDescription[i].location);
|
||||
void DescriptionFactory::AppendSegment(const FixedPointCoordinate &coordinate,
|
||||
const PathData &path_point)
|
||||
{
|
||||
if ((1 == path_description.size()) && (path_description.back().location == coordinate))
|
||||
{
|
||||
path_description.back().name_id = path_point.name_id;
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned lengthOfSegment = 0;
|
||||
unsigned durationOfSegment = 0;
|
||||
unsigned indexOfSegmentBegin = 0;
|
||||
|
||||
for(unsigned i = 1; i < pathDescription.size(); ++i) {
|
||||
entireLength += pathDescription[i].length;
|
||||
lengthOfSegment += pathDescription[i].length;
|
||||
durationOfSegment += pathDescription[i].duration;
|
||||
pathDescription[indexOfSegmentBegin].length = lengthOfSegment;
|
||||
pathDescription[indexOfSegmentBegin].duration = durationOfSegment;
|
||||
if(pathDescription[i].turnInstruction != 0) {
|
||||
//INFO("Turn after " << lengthOfSegment << "m into way with name id " << segment.nameID);
|
||||
assert(pathDescription[i].necessary);
|
||||
lengthOfSegment = 0;
|
||||
durationOfSegment = 0;
|
||||
indexOfSegmentBegin = i;
|
||||
}
|
||||
}
|
||||
|
||||
//Generalize poly line
|
||||
dp.Run(pathDescription, zoomLevel);
|
||||
|
||||
//fix what needs to be fixed else
|
||||
return entireLength;
|
||||
path_description.emplace_back(coordinate,
|
||||
path_point.name_id,
|
||||
path_point.segment_duration,
|
||||
0,
|
||||
path_point.turn_instruction);
|
||||
}
|
||||
|
||||
JSON::Value DescriptionFactory::AppendEncodedPolylineString(const bool return_encoded)
|
||||
{
|
||||
if (return_encoded)
|
||||
{
|
||||
return polyline_compressor.printEncodedString(path_description);
|
||||
}
|
||||
return polyline_compressor.printUnencodedString(path_description);
|
||||
}
|
||||
|
||||
JSON::Value DescriptionFactory::AppendUnencodedPolylineString() const
|
||||
{
|
||||
return polyline_compressor.printUnencodedString(path_description);
|
||||
}
|
||||
|
||||
void DescriptionFactory::BuildRouteSummary(const double distance, const unsigned time)
|
||||
{
|
||||
summary.source_name_id = start_phantom.name_id;
|
||||
summary.target_name_id = target_phantom.name_id;
|
||||
summary.BuildDurationAndLengthStrings(distance, time);
|
||||
}
|
||||
|
||||
@@ -1,98 +1,205 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef DESCRIPTIONFACTORY_H_
|
||||
#define DESCRIPTIONFACTORY_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "../typedefs.h"
|
||||
#include "../Algorithms/DouglasPeucker.h"
|
||||
#include "../Algorithms/PolylineCompressor.h"
|
||||
#include "../DataStructures/ExtractorStructs.h"
|
||||
#include "../DataStructures/PhantomNodes.h"
|
||||
#include "../DataStructures/RawRouteData.h"
|
||||
#include "../DataStructures/SegmentInformation.h"
|
||||
#include "../DataStructures/TurnInstructions.h"
|
||||
#include "../Util/SimpleLogger.h"
|
||||
#include "../typedefs.h"
|
||||
|
||||
#include <osrm/Coordinate.h>
|
||||
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
/* This class is fed with all way segments in consecutive order
|
||||
* and produces the description plus the encoded polyline */
|
||||
|
||||
class DescriptionFactory {
|
||||
DouglasPeucker<SegmentInformation> dp;
|
||||
PolylineCompressor pc;
|
||||
PhantomNode startPhantom, targetPhantom;
|
||||
public:
|
||||
//I know, declaring this public is considered bad. I'm lazy
|
||||
std::vector <SegmentInformation> pathDescription;
|
||||
DescriptionFactory();
|
||||
virtual ~DescriptionFactory();
|
||||
double GetAngleBetweenCoordinates() const;
|
||||
void AppendEncodedPolylineString(std::string &output);
|
||||
void AppendUnencodedPolylineString(std::string &output);
|
||||
void AppendSegment(const _Coordinate & coordinate, const _PathData & data);
|
||||
void SetStartSegment(const PhantomNode & startPhantom);
|
||||
void SetEndSegment(const PhantomNode & startPhantom);
|
||||
void AppendEncodedPolylineString(std::string & output, bool isEncoded);
|
||||
unsigned Run(const unsigned zoomLevel);
|
||||
class DescriptionFactory
|
||||
{
|
||||
DouglasPeucker polyline_generalizer;
|
||||
PolylineCompressor polyline_compressor;
|
||||
PhantomNode start_phantom, target_phantom;
|
||||
|
||||
double DegreeToRadian(const double degree) const;
|
||||
double RadianToDegree(const double degree) const;
|
||||
|
||||
public:
|
||||
struct RouteSummary
|
||||
{
|
||||
unsigned distance;
|
||||
EdgeWeight duration;
|
||||
unsigned source_name_id;
|
||||
unsigned target_name_id;
|
||||
RouteSummary() : distance(0), duration(0), source_name_id(0), target_name_id(0) {}
|
||||
|
||||
void BuildDurationAndLengthStrings(const double raw_distance, const unsigned raw_duration)
|
||||
{
|
||||
// compute distance/duration for route summary
|
||||
distance = round(raw_distance);
|
||||
duration = round(raw_duration / 10.);
|
||||
}
|
||||
} summary;
|
||||
|
||||
double entireLength;
|
||||
|
||||
// I know, declaring this public is considered bad. I'm lazy
|
||||
std::vector<SegmentInformation> path_description;
|
||||
DescriptionFactory();
|
||||
JSON::Value AppendUnencodedPolylineString() const;
|
||||
void AppendSegment(const FixedPointCoordinate &coordinate, const PathData &data);
|
||||
void BuildRouteSummary(const double distance, const unsigned time);
|
||||
void SetStartSegment(const PhantomNode &start_phantom);
|
||||
void SetEndSegment(const PhantomNode &start_phantom);
|
||||
JSON::Value AppendEncodedPolylineString(const bool return_encoded);
|
||||
|
||||
template <class DataFacadeT> void Run(const DataFacadeT *facade, const unsigned zoomLevel)
|
||||
{
|
||||
if (path_description.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/** starts at index 1 */
|
||||
path_description[0].length = 0;
|
||||
for (unsigned i = 1; i < path_description.size(); ++i)
|
||||
{
|
||||
// move down names by one, q&d hack
|
||||
path_description[i - 1].name_id = path_description[i].name_id;
|
||||
path_description[i].length = FixedPointCoordinate::ApproximateEuclideanDistance(
|
||||
path_description[i - 1].location, path_description[i].location);
|
||||
}
|
||||
|
||||
/*Simplify turn instructions
|
||||
Input :
|
||||
10. Turn left on B 36 for 20 km
|
||||
11. Continue on B 35; B 36 for 2 km
|
||||
12. Continue on B 36 for 13 km
|
||||
|
||||
becomes:
|
||||
10. Turn left on B 36 for 35 km
|
||||
*/
|
||||
// TODO: rework to check only end and start of string.
|
||||
// stl string is way to expensive
|
||||
|
||||
// unsigned lastTurn = 0;
|
||||
// for(unsigned i = 1; i < path_description.size(); ++i) {
|
||||
// string1 = sEngine.GetEscapedNameForNameID(path_description[i].name_id);
|
||||
// if(TurnInstruction::GoStraight == path_description[i].turn_instruction) {
|
||||
// if(std::string::npos != string0.find(string1+";")
|
||||
// || std::string::npos != string0.find(";"+string1)
|
||||
// || std::string::npos != string0.find(string1+" ;")
|
||||
// || std::string::npos != string0.find("; "+string1)
|
||||
// ){
|
||||
// SimpleLogger().Write() << "->next correct: " << string0 << " contains " <<
|
||||
// string1;
|
||||
// for(; lastTurn != i; ++lastTurn)
|
||||
// path_description[lastTurn].name_id = path_description[i].name_id;
|
||||
// path_description[i].turn_instruction = TurnInstruction::NoTurn;
|
||||
// } else if(std::string::npos != string1.find(string0+";")
|
||||
// || std::string::npos != string1.find(";"+string0)
|
||||
// || std::string::npos != string1.find(string0+" ;")
|
||||
// || std::string::npos != string1.find("; "+string0)
|
||||
// ){
|
||||
// SimpleLogger().Write() << "->prev correct: " << string1 << " contains " <<
|
||||
// string0;
|
||||
// path_description[i].name_id = path_description[i-1].name_id;
|
||||
// path_description[i].turn_instruction = TurnInstruction::NoTurn;
|
||||
// }
|
||||
// }
|
||||
// if (TurnInstruction::NoTurn != path_description[i].turn_instruction) {
|
||||
// lastTurn = i;
|
||||
// }
|
||||
// string0 = string1;
|
||||
// }
|
||||
|
||||
double segment_length = 0.;
|
||||
unsigned segment_duration = 0;
|
||||
unsigned segment_start_index = 0;
|
||||
|
||||
for (unsigned i = 1; i < path_description.size(); ++i)
|
||||
{
|
||||
entireLength += path_description[i].length;
|
||||
segment_length += path_description[i].length;
|
||||
segment_duration += path_description[i].duration;
|
||||
path_description[segment_start_index].length = segment_length;
|
||||
path_description[segment_start_index].duration = segment_duration;
|
||||
|
||||
if (TurnInstruction::NoTurn != path_description[i].turn_instruction)
|
||||
{
|
||||
BOOST_ASSERT(path_description[i].necessary);
|
||||
segment_length = 0;
|
||||
segment_duration = 0;
|
||||
segment_start_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Post-processing to remove empty or nearly empty path segments
|
||||
if (std::numeric_limits<double>::epsilon() > path_description.back().length)
|
||||
{
|
||||
if (path_description.size() > 2)
|
||||
{
|
||||
path_description.pop_back();
|
||||
path_description.back().necessary = true;
|
||||
path_description.back().turn_instruction = TurnInstruction::NoTurn;
|
||||
target_phantom.name_id = (path_description.end() - 2)->name_id;
|
||||
}
|
||||
}
|
||||
if (std::numeric_limits<double>::epsilon() > path_description.front().length)
|
||||
{
|
||||
if (path_description.size() > 2)
|
||||
{
|
||||
path_description.erase(path_description.begin());
|
||||
path_description.front().turn_instruction = TurnInstruction::HeadOn;
|
||||
path_description.front().necessary = true;
|
||||
start_phantom.name_id = path_description.front().name_id;
|
||||
}
|
||||
}
|
||||
|
||||
// Generalize poly line
|
||||
polyline_generalizer.Run(path_description, zoomLevel);
|
||||
|
||||
// fix what needs to be fixed else
|
||||
for (unsigned i = 0; i < path_description.size() - 1 && path_description.size() >= 2; ++i)
|
||||
{
|
||||
if (path_description[i].necessary)
|
||||
{
|
||||
const double angle = path_description[i+1].location.GetBearing(path_description[i].location);
|
||||
path_description[i].bearing = angle * 10;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* DESCRIPTIONFACTORY_H_ */
|
||||
|
||||
//private:
|
||||
// void appendInstructionNameToString(const std::string & nameOfStreet, const std::string & instructionOrDirection, std::string &output, bool firstAdvice = false) {
|
||||
// output += "[";
|
||||
// if(config.instructions) {
|
||||
// output += "\"";
|
||||
// if(firstAdvice) {
|
||||
// output += "Head ";
|
||||
// }
|
||||
// output += instructionOrDirection;
|
||||
// output += "\",\"";
|
||||
// output += nameOfStreet;
|
||||
// output += "\",";
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// void appendInstructionLengthToString(unsigned length, std::string &output) {
|
||||
// if(config.instructions){
|
||||
// std::string tmpDistance;
|
||||
// intToString(10*(round(length/10.)), tmpDistance);
|
||||
// output += tmpDistance;
|
||||
// output += ",";
|
||||
// intToString(descriptionFactory.startIndexOfGeometry, tmp);
|
||||
// output += tmp;
|
||||
// output += ",";
|
||||
// intToString(descriptionFactory.durationOfInstruction, tmp);
|
||||
// output += tmp;
|
||||
// output += ",";
|
||||
// output += "\"";
|
||||
// output += tmpDistance;
|
||||
// output += "\",";
|
||||
// double angle = descriptionFactory.GetAngleBetweenCoordinates();
|
||||
// DirectionOfInstruction direction;
|
||||
// getDirectionOfInstruction(angle, direction);
|
||||
// output += "\"";
|
||||
// output += direction.shortDirection;
|
||||
// output += "\",";
|
||||
// std::stringstream numberString;
|
||||
// numberString << fixed << setprecision(2) << angle;
|
||||
// output += numberString.str();
|
||||
// }
|
||||
// output += "]";
|
||||
// }
|
||||
|
||||
@@ -1,67 +1,102 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
#ifndef GPX_DESCRIPTOR_H_
|
||||
#define GPX_DESCRIPTOR_H_
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef GPX_DESCRIPTOR_H
|
||||
#define GPX_DESCRIPTOR_H
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
#include "BaseDescriptor.h"
|
||||
|
||||
template<class SearchEngineT>
|
||||
class GPXDescriptor : public BaseDescriptor<SearchEngineT>{
|
||||
private:
|
||||
_DescriptorConfig config;
|
||||
_Coordinate current;
|
||||
template <class DataFacadeT> class GPXDescriptor : public BaseDescriptor<DataFacadeT>
|
||||
{
|
||||
private:
|
||||
DescriptorConfig config;
|
||||
FixedPointCoordinate current;
|
||||
DataFacadeT * facade;
|
||||
|
||||
std::string tmp;
|
||||
public:
|
||||
void SetConfig(const _DescriptorConfig& c) { config = c; }
|
||||
void Run(http::Reply & reply, RawRouteData &rawRoute, PhantomNodes &phantomNodes, SearchEngineT &sEngine, unsigned distance) {
|
||||
reply.content += ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||
reply.content += "<gpx xmlns=\"http://www.topografix.com/GPX/1/1\" "
|
||||
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
|
||||
"xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 gpx.xsd"
|
||||
"\">";
|
||||
reply.content += "<rte>";
|
||||
if(distance != UINT_MAX && rawRoute.routeSegments.size()) {
|
||||
convertInternalLatLonToString(phantomNodes.startPhantom.location.lat, tmp);
|
||||
reply.content += "<rtept lat=\"" + tmp + "\" ";
|
||||
convertInternalLatLonToString(phantomNodes.startPhantom.location.lon, tmp);
|
||||
reply.content += "lon=\"" + tmp + "\"></rtept>";
|
||||
void AddRoutePoint(const FixedPointCoordinate & coordinate, std::vector<char> & output)
|
||||
{
|
||||
const std::string route_point_head = "<rtept lat=\"";
|
||||
const std::string route_point_middle = " lon=\"";
|
||||
const std::string route_point_tail = "\"></rtept>";
|
||||
|
||||
for(unsigned segmentIdx = 0; segmentIdx < rawRoute.routeSegments.size(); segmentIdx++) {
|
||||
BOOST_FOREACH(_PathData pathData, rawRoute.routeSegments[segmentIdx]) {
|
||||
sEngine.GetCoordinatesForNodeID(pathData.node, current);
|
||||
std::string tmp;
|
||||
|
||||
convertInternalLatLonToString(current.lat, tmp);
|
||||
reply.content += "<rtept lat=\"" + tmp + "\" ";
|
||||
convertInternalLatLonToString(current.lon, tmp);
|
||||
reply.content += "lon=\"" + tmp + "\"></rtept>";
|
||||
FixedPointCoordinate::convertInternalLatLonToString(coordinate.lat, tmp);
|
||||
output.insert(output.end(), route_point_head.begin(), route_point_head.end());
|
||||
output.insert(output.end(), tmp.begin(), tmp.end());
|
||||
output.push_back('\"');
|
||||
|
||||
FixedPointCoordinate::convertInternalLatLonToString(coordinate.lon, tmp);
|
||||
output.insert(output.end(), route_point_middle.begin(), route_point_middle.end());
|
||||
output.insert(output.end(), tmp.begin(), tmp.end());
|
||||
output.insert(output.end(), route_point_tail.begin(), route_point_tail.end());
|
||||
}
|
||||
|
||||
public:
|
||||
GPXDescriptor(DataFacadeT *facade) : facade(facade) {}
|
||||
|
||||
void SetConfig(const DescriptorConfig &c) { config = c; }
|
||||
|
||||
// TODO: reorder parameters
|
||||
void Run(const RawRouteData &raw_route,
|
||||
const PhantomNodes &phantom_node_list,
|
||||
http::Reply &reply)
|
||||
{
|
||||
std::string header("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
|
||||
"<gpx creator=\"OSRM Routing Engine\" version=\"1.1\" "
|
||||
"xmlns=\"http://www.topografix.com/GPX/1/1\" "
|
||||
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
|
||||
"xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 gpx.xsd"
|
||||
"\">"
|
||||
"<metadata><copyright author=\"Project OSRM\"><license>Data (c)"
|
||||
" OpenStreetMap contributors (ODbL)</license></copyright>"
|
||||
"</metadata>"
|
||||
"<rte>");
|
||||
reply.content.insert(reply.content.end(), header.begin(), header.end());
|
||||
const bool found_route = (raw_route.shortest_path_length != INVALID_EDGE_WEIGHT) &&
|
||||
(!raw_route.unpacked_path_segments.front().empty());
|
||||
if (found_route)
|
||||
{
|
||||
AddRoutePoint(phantom_node_list.source_phantom.location, reply.content);
|
||||
|
||||
for (const std::vector<PathData> &path_data_vector : raw_route.unpacked_path_segments)
|
||||
{
|
||||
for (const PathData &path_data : path_data_vector)
|
||||
{
|
||||
const FixedPointCoordinate current_coordinate =
|
||||
facade->GetCoordinateOfNode(path_data.node);
|
||||
AddRoutePoint(current_coordinate, reply.content);
|
||||
}
|
||||
}
|
||||
convertInternalLatLonToString(phantomNodes.targetPhantom.location.lat, tmp);
|
||||
reply.content += "<rtept lat=\"" + tmp + "\" ";
|
||||
convertInternalLatLonToString(phantomNodes.targetPhantom.location.lon, tmp);
|
||||
reply.content += "lon=\"" + tmp + "\"></rtept>";
|
||||
AddRoutePoint(phantom_node_list.target_phantom.location, reply.content);
|
||||
|
||||
}
|
||||
reply.content += "</rte></gpx>";
|
||||
std::string footer("</rte></gpx>");
|
||||
reply.content.insert(reply.content.end(), footer.begin(), footer.end());
|
||||
}
|
||||
};
|
||||
#endif /* GPX_DESCRIPTOR_H_ */
|
||||
#endif // GPX_DESCRIPTOR_H
|
||||
|
||||
@@ -1,176 +1,356 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
|
||||
#ifndef JSON_DESCRIPTOR_H_
|
||||
#define JSON_DESCRIPTOR_H_
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include "BaseDescriptor.h"
|
||||
#include "DescriptionFactory.h"
|
||||
#include "../DataStructures/SegmentInformation.h"
|
||||
#include "../DataStructures/TurnInstructions.h"
|
||||
#include "../Util/StringUtil.h"
|
||||
|
||||
template<class SearchEngineT>
|
||||
class JSONDescriptor : public BaseDescriptor<SearchEngineT>{
|
||||
private:
|
||||
_DescriptorConfig config;
|
||||
_RouteSummary summary;
|
||||
DescriptionFactory descriptionFactory;
|
||||
std::string tmp;
|
||||
_Coordinate current;
|
||||
struct {
|
||||
int startIndex;
|
||||
int nameID;
|
||||
int leaveAtExit;
|
||||
} roundAbout;
|
||||
|
||||
public:
|
||||
JSONDescriptor() {}
|
||||
void SetConfig(const _DescriptorConfig & c) { config = c; }
|
||||
|
||||
void Run(http::Reply & reply, RawRouteData &rawRoute, PhantomNodes &phantomNodes, SearchEngineT &sEngine, unsigned durationOfTrip) {
|
||||
WriteHeaderToOutput(reply.content);
|
||||
if(durationOfTrip != INT_MAX && rawRoute.routeSegments.size() > 0) {
|
||||
summary.startName = sEngine.GetEscapedNameForNameID(phantomNodes.startPhantom.nodeBasedEdgeNameID);
|
||||
descriptionFactory.SetStartSegment(phantomNodes.startPhantom);
|
||||
summary.destName = sEngine.GetEscapedNameForNameID(phantomNodes.targetPhantom.nodeBasedEdgeNameID);
|
||||
reply.content += "0,"
|
||||
"\"status_message\": \"Found route between points\",";
|
||||
for(unsigned segmentIdx = 0; segmentIdx < rawRoute.routeSegments.size(); segmentIdx++) {
|
||||
const std::vector< _PathData > & path = rawRoute.routeSegments[segmentIdx];
|
||||
BOOST_FOREACH(_PathData pathData, path) {
|
||||
sEngine.GetCoordinatesForNodeID(pathData.node, current);
|
||||
descriptionFactory.AppendSegment(current, pathData );
|
||||
}
|
||||
//TODO: Add via points
|
||||
}
|
||||
descriptionFactory.SetEndSegment(phantomNodes.targetPhantom);
|
||||
} else {
|
||||
//We do not need to do much, if there is no route ;-)
|
||||
reply.content += "207,"
|
||||
"\"status_message\": \"Cannot find route between points\",";
|
||||
}
|
||||
|
||||
summary.BuildDurationAndLengthStrings(descriptionFactory.Run(config.z), durationOfTrip);
|
||||
|
||||
reply.content += "\"route_summary\": {"
|
||||
"\"total_distance\":";
|
||||
reply.content += summary.lengthString;
|
||||
reply.content += ","
|
||||
"\"total_time\":";
|
||||
reply.content += summary.durationString;
|
||||
reply.content += ","
|
||||
"\"start_point\":\"";
|
||||
reply.content += summary.startName;
|
||||
reply.content += "\","
|
||||
"\"end_point\":\"";
|
||||
reply.content += summary.destName;
|
||||
reply.content += "\"";
|
||||
reply.content += "},";
|
||||
reply.content += "\"route_geometry\": ";
|
||||
if(config.geometry) {
|
||||
if(config.encodeGeometry)
|
||||
descriptionFactory.AppendEncodedPolylineString(reply.content, config.encodeGeometry);
|
||||
} else {
|
||||
reply.content += "[]";
|
||||
}
|
||||
|
||||
reply.content += ","
|
||||
"\"route_instructions\": [";
|
||||
if(config.instructions) {
|
||||
//Segment information has following format:
|
||||
//["instruction","streetname",length,position,time,"length","earth_direction",azimuth]
|
||||
//Example: ["Turn left","High Street",200,4,10,"200m","NE",22.5]
|
||||
//See also: http://developers.cloudmade.com/wiki/navengine/JSON_format
|
||||
unsigned prefixSumOfNecessarySegments = 0;
|
||||
roundAbout.leaveAtExit = 0;
|
||||
roundAbout.nameID = 0;
|
||||
std::string tmpDist, tmpLength, tmp;
|
||||
//Fetch data from Factory and generate a string from it.
|
||||
BOOST_FOREACH(SegmentInformation segment, descriptionFactory.pathDescription) {
|
||||
if(TurnInstructions.TurnIsNecessary( segment.turnInstruction) ) {
|
||||
if(TurnInstructions.EnterRoundAbout == segment.turnInstruction) {
|
||||
roundAbout.nameID = segment.nameID;
|
||||
roundAbout.startIndex = prefixSumOfNecessarySegments;
|
||||
} else {
|
||||
if(0 != prefixSumOfNecessarySegments)
|
||||
reply.content += ",";
|
||||
|
||||
reply.content += "[\"";
|
||||
if(TurnInstructions.LeaveRoundAbout == segment.turnInstruction) {
|
||||
reply.content += TurnInstructions.TurnStrings[TurnInstructions.EnterRoundAbout];
|
||||
reply.content += " and leave at ";
|
||||
reply.content += TurnInstructions.Ordinals[roundAbout.leaveAtExit+1];
|
||||
reply.content += " exit";
|
||||
roundAbout.leaveAtExit = 0;
|
||||
} else {
|
||||
reply.content += TurnInstructions.TurnStrings[segment.turnInstruction];
|
||||
}
|
||||
reply.content += "\",\"";
|
||||
reply.content += sEngine.GetEscapedNameForNameID(segment.nameID);
|
||||
reply.content += "\",";
|
||||
intToString(segment.length, tmpDist);
|
||||
reply.content += tmpDist;
|
||||
reply.content += ",";
|
||||
intToString(prefixSumOfNecessarySegments, tmpLength);
|
||||
reply.content += tmpLength;
|
||||
reply.content += ",";
|
||||
intToString(segment.duration, tmp);
|
||||
reply.content += ",\"";
|
||||
reply.content += tmpLength;
|
||||
//TODO: fix heading
|
||||
reply.content += "\",\"NE\",22.5";
|
||||
reply.content += "]";
|
||||
}
|
||||
} else if(TurnInstructions.StayOnRoundAbout == segment.turnInstruction) {
|
||||
++roundAbout.leaveAtExit;
|
||||
}
|
||||
if(segment.necessary)
|
||||
++prefixSumOfNecessarySegments;
|
||||
}
|
||||
}
|
||||
reply.content += "],";
|
||||
//list all viapoints so that the client may display it
|
||||
reply.content += "\"via_points\":[";
|
||||
for(unsigned segmentIdx = 1; (true == config.geometry) && (segmentIdx < rawRoute.segmentEndCoordinates.size()); segmentIdx++) {
|
||||
if(segmentIdx > 1)
|
||||
reply.content += ",";
|
||||
reply.content += "[";
|
||||
if(rawRoute.segmentEndCoordinates[segmentIdx].startPhantom.location.isSet())
|
||||
convertInternalReversedCoordinateToString(rawRoute.segmentEndCoordinates[segmentIdx].startPhantom.location, tmp);
|
||||
else
|
||||
convertInternalReversedCoordinateToString(rawRoute.rawViaNodeCoordinates[segmentIdx], tmp);
|
||||
reply.content += tmp;
|
||||
reply.content += "]";
|
||||
}
|
||||
reply.content += "],"
|
||||
"\"transactionId\": \"OSRM Routing Engine JSON Descriptor (v0.2)\"";
|
||||
reply.content += "}";
|
||||
}
|
||||
|
||||
void WriteHeaderToOutput(std::string & output) {
|
||||
output += "{"
|
||||
"\"version\": 0.3,"
|
||||
"\"status\":";
|
||||
}
|
||||
};
|
||||
#endif /* JSON_DESCRIPTOR_H_ */
|
||||
/*
|
||||
|
||||
Copyright (c) 2013, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef JSON_DESCRIPTOR_H_
|
||||
#define JSON_DESCRIPTOR_H_
|
||||
|
||||
#include "BaseDescriptor.h"
|
||||
#include "DescriptionFactory.h"
|
||||
#include "../Algorithms/ObjectToBase64.h"
|
||||
#include "../Algorithms/ExtractRouteNames.h"
|
||||
#include "../DataStructures/JSONContainer.h"
|
||||
#include "../DataStructures/SegmentInformation.h"
|
||||
#include "../DataStructures/TurnInstructions.h"
|
||||
#include "../Util/Azimuth.h"
|
||||
#include "../Util/StringUtil.h"
|
||||
#include "../Util/TimingUtil.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
template <class DataFacadeT> class JSONDescriptor : public BaseDescriptor<DataFacadeT>
|
||||
{
|
||||
private:
|
||||
DataFacadeT *facade;
|
||||
DescriptorConfig config;
|
||||
DescriptionFactory description_factory, alternate_description_factory;
|
||||
FixedPointCoordinate current;
|
||||
unsigned entered_restricted_area_count;
|
||||
struct RoundAbout
|
||||
{
|
||||
RoundAbout() : start_index(INT_MAX), name_id(INT_MAX), leave_at_exit(INT_MAX) {}
|
||||
int start_index;
|
||||
int name_id;
|
||||
int leave_at_exit;
|
||||
} round_about;
|
||||
|
||||
struct Segment
|
||||
{
|
||||
Segment() : name_id(-1), length(-1), position(-1) {}
|
||||
Segment(int n, int l, int p) : name_id(n), length(l), position(p) {}
|
||||
int name_id;
|
||||
int length;
|
||||
int position;
|
||||
};
|
||||
std::vector<Segment> shortest_path_segments, alternative_path_segments;
|
||||
std::vector<unsigned> shortest_leg_end_indices, alternative_leg_end_indices;
|
||||
ExtractRouteNames<DataFacadeT, Segment> GenerateRouteNames;
|
||||
|
||||
|
||||
public:
|
||||
JSONDescriptor(DataFacadeT *facade) : facade(facade), entered_restricted_area_count(0)
|
||||
{
|
||||
shortest_leg_end_indices.emplace_back(0);
|
||||
alternative_leg_end_indices.emplace_back(0);
|
||||
}
|
||||
|
||||
void SetConfig(const DescriptorConfig &c) { config = c; }
|
||||
|
||||
unsigned DescribeLeg(const std::vector<PathData> route_leg, const PhantomNodes &leg_phantoms)
|
||||
{
|
||||
unsigned added_element_count = 0;
|
||||
// Get all the coordinates for the computed route
|
||||
FixedPointCoordinate current_coordinate;
|
||||
for (const PathData &path_data : route_leg)
|
||||
{
|
||||
current_coordinate = facade->GetCoordinateOfNode(path_data.node);
|
||||
description_factory.AppendSegment(current_coordinate, path_data);
|
||||
++added_element_count;
|
||||
}
|
||||
++added_element_count;
|
||||
BOOST_ASSERT((route_leg.size() + 1) == added_element_count);
|
||||
return added_element_count;
|
||||
}
|
||||
|
||||
void Run(const RawRouteData &raw_route,
|
||||
const PhantomNodes &phantom_nodes,
|
||||
http::Reply &reply)
|
||||
{
|
||||
JSON::Object json_result;
|
||||
|
||||
if (INVALID_EDGE_WEIGHT == raw_route.shortest_path_length)
|
||||
{
|
||||
// We do not need to do much, if there is no route ;-)
|
||||
json_result.values["status"] = 207;
|
||||
json_result.values["status_message"] = "Cannot find route between points";
|
||||
JSON::render(reply.content, json_result);
|
||||
return;
|
||||
}
|
||||
|
||||
// check if first segment is non-zero
|
||||
std::string road_name =
|
||||
facade->GetEscapedNameForNameID(phantom_nodes.source_phantom.name_id);
|
||||
|
||||
BOOST_ASSERT(raw_route.unpacked_path_segments.size() ==
|
||||
raw_route.segment_end_coordinates.size());
|
||||
|
||||
description_factory.SetStartSegment(phantom_nodes.source_phantom);
|
||||
json_result.values["status"] = 0;
|
||||
json_result.values["status_message"] = "Found route between points";
|
||||
|
||||
// for each unpacked segment add the leg to the description
|
||||
for (unsigned i = 0; i < raw_route.unpacked_path_segments.size(); ++i)
|
||||
{
|
||||
const int added_segments = DescribeLeg(raw_route.unpacked_path_segments[i],
|
||||
raw_route.segment_end_coordinates[i]);
|
||||
BOOST_ASSERT(0 < added_segments);
|
||||
shortest_leg_end_indices.emplace_back(added_segments + shortest_leg_end_indices.back());
|
||||
}
|
||||
description_factory.SetEndSegment(phantom_nodes.target_phantom);
|
||||
description_factory.Run(facade, config.zoom_level);
|
||||
|
||||
if (config.geometry)
|
||||
{
|
||||
JSON::Value route_geometry = description_factory.AppendEncodedPolylineString(config.encode_geometry);
|
||||
json_result.values["route_geometry"] = route_geometry;
|
||||
}
|
||||
if (config.instructions)
|
||||
{
|
||||
JSON::Array json_route_instructions;
|
||||
BuildTextualDescription(description_factory,
|
||||
json_route_instructions,
|
||||
raw_route.shortest_path_length,
|
||||
shortest_path_segments);
|
||||
json_result.values["route_instructions"] = json_route_instructions;
|
||||
}
|
||||
description_factory.BuildRouteSummary(description_factory.entireLength,
|
||||
raw_route.shortest_path_length);
|
||||
JSON::Object json_route_summary;
|
||||
json_route_summary.values["total_distance"] = description_factory.summary.distance;
|
||||
json_route_summary.values["total_time"] = description_factory.summary.duration;
|
||||
json_route_summary.values["start_point"] = facade->GetEscapedNameForNameID(description_factory.summary.source_name_id);
|
||||
json_route_summary.values["end_point"] = facade->GetEscapedNameForNameID(description_factory.summary.target_name_id);
|
||||
json_result.values["route_summary"] = json_route_summary;
|
||||
|
||||
BOOST_ASSERT(!raw_route.segment_end_coordinates.empty());
|
||||
|
||||
JSON::Array json_via_points_array;
|
||||
JSON::Array json_first_coordinate;
|
||||
json_first_coordinate.values.push_back(raw_route.segment_end_coordinates.front().source_phantom.location.lat/COORDINATE_PRECISION);
|
||||
json_first_coordinate.values.push_back(raw_route.segment_end_coordinates.front().source_phantom.location.lon/COORDINATE_PRECISION);
|
||||
json_via_points_array.values.push_back(json_first_coordinate);
|
||||
for (const PhantomNodes &nodes : raw_route.segment_end_coordinates)
|
||||
{
|
||||
std::string tmp;
|
||||
JSON::Array json_coordinate;
|
||||
json_coordinate.values.push_back(nodes.target_phantom.location.lat/COORDINATE_PRECISION);
|
||||
json_coordinate.values.push_back(nodes.target_phantom.location.lon/COORDINATE_PRECISION);
|
||||
json_via_points_array.values.push_back(json_coordinate);
|
||||
}
|
||||
json_result.values["via_points"] = json_via_points_array;
|
||||
|
||||
JSON::Array json_via_indices_array;
|
||||
json_via_indices_array.values.insert(json_via_indices_array.values.end(), shortest_leg_end_indices.begin(), shortest_leg_end_indices.end());
|
||||
json_result.values["via_indices"] = json_via_indices_array;
|
||||
|
||||
// only one alternative route is computed at this time, so this is hardcoded
|
||||
if (INVALID_EDGE_WEIGHT != raw_route.alternative_path_length)
|
||||
{
|
||||
json_result.values["found_alternative"] = JSON::True();
|
||||
alternate_description_factory.SetStartSegment(phantom_nodes.source_phantom);
|
||||
// Get all the coordinates for the computed route
|
||||
for (const PathData &path_data : raw_route.unpacked_alternative)
|
||||
{
|
||||
current = facade->GetCoordinateOfNode(path_data.node);
|
||||
alternate_description_factory.AppendSegment(current, path_data);
|
||||
}
|
||||
alternate_description_factory.Run(facade, config.zoom_level);
|
||||
|
||||
if (config.geometry)
|
||||
{
|
||||
JSON::Value alternate_geometry_string = alternate_description_factory.AppendEncodedPolylineString(config.encode_geometry);
|
||||
JSON::Array json_alternate_geometries_array;
|
||||
json_alternate_geometries_array.values.push_back(alternate_geometry_string);
|
||||
json_result.values["alternative_geometries"] = json_alternate_geometries_array;
|
||||
}
|
||||
// Generate instructions for each alternative (simulated here)
|
||||
JSON::Array json_alt_instructions;
|
||||
JSON::Array json_current_alt_instructions;
|
||||
if (config.instructions)
|
||||
{
|
||||
BuildTextualDescription(alternate_description_factory,
|
||||
json_current_alt_instructions,
|
||||
raw_route.alternative_path_length,
|
||||
alternative_path_segments);
|
||||
json_alt_instructions.values.push_back(json_current_alt_instructions);
|
||||
json_result.values["alternative_instructions"] = json_alt_instructions;
|
||||
}
|
||||
alternate_description_factory.BuildRouteSummary(
|
||||
alternate_description_factory.entireLength, raw_route.alternative_path_length);
|
||||
|
||||
JSON::Object json_alternate_route_summary;
|
||||
JSON::Array json_alternate_route_summary_array;
|
||||
json_alternate_route_summary.values["total_distance"] = alternate_description_factory.summary.distance;
|
||||
json_alternate_route_summary.values["total_time"] = alternate_description_factory.summary.duration;
|
||||
json_alternate_route_summary.values["start_point"] = facade->GetEscapedNameForNameID(alternate_description_factory.summary.source_name_id);
|
||||
json_alternate_route_summary.values["end_point"] = facade->GetEscapedNameForNameID(alternate_description_factory.summary.target_name_id);
|
||||
json_alternate_route_summary_array.values.push_back(json_alternate_route_summary);
|
||||
json_result.values["alternative_summaries"] = json_alternate_route_summary_array;
|
||||
|
||||
JSON::Array json_altenative_indices_array;
|
||||
json_altenative_indices_array.values.push_back(0);
|
||||
json_altenative_indices_array.values.push_back(alternate_description_factory.path_description.size());
|
||||
json_result.values["alternative_indices"] = json_altenative_indices_array;
|
||||
} else {
|
||||
json_result.values["found_alternative"] = JSON::False();
|
||||
}
|
||||
|
||||
// Get Names for both routes
|
||||
RouteNames route_names = GenerateRouteNames(shortest_path_segments, alternative_path_segments, facade);
|
||||
JSON::Array json_route_names;
|
||||
json_route_names.values.push_back(route_names.shortest_path_name_1);
|
||||
json_route_names.values.push_back(route_names.shortest_path_name_2);
|
||||
json_result.values["route_name"] = json_route_names;
|
||||
|
||||
if (INVALID_EDGE_WEIGHT != raw_route.alternative_path_length)
|
||||
{
|
||||
JSON::Array json_alternate_names_array;
|
||||
JSON::Array json_alternate_names;
|
||||
json_alternate_names.values.push_back(route_names.alternative_path_name_1);
|
||||
json_alternate_names.values.push_back(route_names.alternative_path_name_2);
|
||||
json_alternate_names_array.values.push_back(json_alternate_names);
|
||||
json_result.values["alternative_names"] = json_alternate_names_array;
|
||||
}
|
||||
|
||||
JSON::Object json_hint_object;
|
||||
json_hint_object.values["checksum"] = raw_route.check_sum;
|
||||
JSON::Array json_location_hint_array;
|
||||
std::string hint;
|
||||
for (unsigned i = 0; i < raw_route.segment_end_coordinates.size(); ++i)
|
||||
{
|
||||
EncodeObjectToBase64(raw_route.segment_end_coordinates[i].source_phantom, hint);
|
||||
json_location_hint_array.values.push_back(hint);
|
||||
}
|
||||
EncodeObjectToBase64(raw_route.segment_end_coordinates.back().target_phantom, hint);
|
||||
json_location_hint_array.values.push_back(hint);
|
||||
json_hint_object.values["locations"] = json_location_hint_array;
|
||||
json_result.values["hint_data"] = json_hint_object;
|
||||
|
||||
// render the content to the output array
|
||||
TIMER_START(route_render);
|
||||
JSON::render(reply.content, json_result);
|
||||
TIMER_STOP(route_render);
|
||||
SimpleLogger().Write(logDEBUG) << "rendering took: " << TIMER_MSEC(route_render);
|
||||
}
|
||||
|
||||
// TODO: reorder parameters
|
||||
inline void BuildTextualDescription(DescriptionFactory &description_factory,
|
||||
JSON::Array & json_instruction_array,
|
||||
const int route_length,
|
||||
std::vector<Segment> &route_segments_list)
|
||||
{
|
||||
// Segment information has following format:
|
||||
//["instruction id","streetname",length,position,time,"length","earth_direction",azimuth]
|
||||
unsigned necessary_segments_running_index = 0;
|
||||
round_about.leave_at_exit = 0;
|
||||
round_about.name_id = 0;
|
||||
std::string temp_dist, temp_length, temp_duration, temp_bearing, temp_instruction;
|
||||
|
||||
// Fetch data from Factory and generate a string from it.
|
||||
for (const SegmentInformation &segment : description_factory.path_description)
|
||||
{
|
||||
JSON::Array json_instruction_row;
|
||||
TurnInstruction current_instruction = segment.turn_instruction;
|
||||
entered_restricted_area_count += (current_instruction != segment.turn_instruction);
|
||||
if (TurnInstructionsClass::TurnIsNecessary(current_instruction))
|
||||
{
|
||||
if (TurnInstruction::EnterRoundAbout == current_instruction)
|
||||
{
|
||||
round_about.name_id = segment.name_id;
|
||||
round_about.start_index = necessary_segments_running_index;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string current_turn_instruction;
|
||||
if (TurnInstruction::LeaveRoundAbout == current_instruction)
|
||||
{
|
||||
temp_instruction = IntToString(as_integer(TurnInstruction::EnterRoundAbout));
|
||||
current_turn_instruction += temp_instruction;
|
||||
current_turn_instruction += "-";
|
||||
temp_instruction = IntToString(round_about.leave_at_exit + 1);
|
||||
current_turn_instruction += temp_instruction;
|
||||
round_about.leave_at_exit = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
temp_instruction = IntToString(as_integer(current_instruction));
|
||||
current_turn_instruction += temp_instruction;
|
||||
}
|
||||
json_instruction_row.values.push_back(current_turn_instruction);
|
||||
|
||||
json_instruction_row.values.push_back(facade->GetEscapedNameForNameID(segment.name_id));
|
||||
json_instruction_row.values.push_back(std::round(segment.length));
|
||||
json_instruction_row.values.push_back(necessary_segments_running_index);
|
||||
json_instruction_row.values.push_back(round(segment.duration / 10));
|
||||
json_instruction_row.values.push_back(IntToString(segment.length)+"m");
|
||||
int bearing_value = round(segment.bearing / 10.);
|
||||
json_instruction_row.values.push_back(Azimuth::Get(bearing_value));
|
||||
json_instruction_row.values.push_back(bearing_value);
|
||||
|
||||
route_segments_list.emplace_back(
|
||||
segment.name_id, segment.length, route_segments_list.size());
|
||||
json_instruction_array.values.push_back(json_instruction_row);
|
||||
}
|
||||
}
|
||||
else if (TurnInstruction::StayOnRoundAbout == current_instruction)
|
||||
{
|
||||
++round_about.leave_at_exit;
|
||||
}
|
||||
if (segment.necessary)
|
||||
{
|
||||
++necessary_segments_running_index;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: check if this in an invariant
|
||||
if (INVALID_EDGE_WEIGHT != route_length)
|
||||
{
|
||||
JSON::Array json_last_instruction_row;
|
||||
temp_instruction = IntToString(as_integer(TurnInstruction::ReachedYourDestination));
|
||||
json_last_instruction_row.values.push_back(temp_instruction);
|
||||
json_last_instruction_row.values.push_back("");
|
||||
json_last_instruction_row.values.push_back(0);
|
||||
json_last_instruction_row.values.push_back(necessary_segments_running_index - 1);
|
||||
json_last_instruction_row.values.push_back(0);
|
||||
json_last_instruction_row.values.push_back("0m");
|
||||
json_last_instruction_row.values.push_back(Azimuth::Get(0.0));
|
||||
json_last_instruction_row.values.push_back(0.);
|
||||
json_instruction_array.values.push_back(json_last_instruction_row);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* JSON_DESCRIPTOR_H_ */
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
Third Party Libraries:
|
||||
|
||||
Scons 1.3+
|
||||
Boost 1.37+
|
||||
sparsehash 1.4+
|
||||
stxxl 1.3.1+
|
||||
libz2-dev 1.0.5+
|
||||
protobuffer 2.3.0+
|
||||
zlib 1.2.3.4+
|
||||
@@ -1,44 +0,0 @@
|
||||
FAQ
|
||||
---
|
||||
|
||||
Q: What platforms does OSMR run on?
|
||||
A: Virtually any Unix-like platform with g++ installed. It has been developed
|
||||
under Linux and tested on MacOS X 10.6. It should run under Windows as well
|
||||
though the code will need some adjustments.
|
||||
|
||||
Q: What is the workflow to get the engine up and running
|
||||
A: Road network extraction->Preprocessing->Startup
|
||||
|
||||
Q: What does OSRM stand for?
|
||||
A: It is an abbreviation for Open Source Routing Machine.
|
||||
|
||||
Q: What does HSGR stand for?
|
||||
A: It is an abbreviation for Hierarchy Search GRaph.
|
||||
|
||||
Q: What is the .nodes file?
|
||||
A: It is a map that translates between internal and external Node IDs. Remember
|
||||
that external NodeIDs can be arbitrary and non-contigous. Internally the
|
||||
nodes are numbered from 0 to n-1.
|
||||
|
||||
Q: The routing engine crashes with a seg-fault
|
||||
A: Check the startup parameters.
|
||||
|
||||
Q: Something about the route is odd. I know that there is a better path
|
||||
A: Most probably it is missing data in the OSM file.
|
||||
|
||||
Q: I work for this company that would like to use the code, but we are hesistant
|
||||
because of the license.
|
||||
A: Contact me. Probably, we can work something out.
|
||||
|
||||
Q: How fast is this thing?
|
||||
A: Good question. Here is a number. The engine was able to handle more than
|
||||
2800 requests per Minute on the German road network with the travel time
|
||||
metric on a Core 2 Duo. This also includes transfer of data across a
|
||||
switched 100MBit/s LAN. So, I guess it's fair to say it's fast.
|
||||
|
||||
Q: What is the difference between extractNetwork and extractLargeNetwork?
|
||||
A: extractNetwork does all of its magic in RAM, while extractLargeNetwork uses the
|
||||
stxxl library to store the data in external memory. Use the first one for small
|
||||
networks, but beware of running out of RAM. Swapping is the kiss of death. For
|
||||
larger networks use extractLargeNetwork. It will be somewhat slower on the
|
||||
smaller files, but way faster with large ones like the planet file.
|
||||
@@ -1,6 +0,0 @@
|
||||
- extractorLargeNetwork finishes with:
|
||||
terminate called after throwing an instance of 'stxxl::resource_error'
|
||||
what(): Error in function void stxxl::mutex::lock() pthread_mutex_lock(&_mutex) Invalid argument
|
||||
|
||||
This ist a known, albeit odd behavior that is caused by a bug in stxxl. It can be ignored. Upgrade
|
||||
to version 1.3.1 of stxxl.
|
||||
@@ -1,159 +0,0 @@
|
||||
/*
|
||||
* Open Source Routing Machine (OSRM) - Web (GUI) Interface
|
||||
* Copyright (C) Pascal Neis, 2011
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU AFFERO General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
* or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Title: Geocode.js
|
||||
* Description: JS file for geocoding
|
||||
*
|
||||
* @author Pascal Neis, pascal@neis-one.org
|
||||
* @version 0.1 2011-05-15
|
||||
*/
|
||||
|
||||
//======================
|
||||
// OBJECTS
|
||||
var HOST_GEOCODE_URL = 'http://open.mapquestapi.com';
|
||||
var GEOCODE_POST = HOST_GEOCODE_URL + '/nominatim/v1/search?format=json&json_callback=showResultsGeocode';
|
||||
var searchType = 'search';
|
||||
|
||||
var isStartPointSet = false;
|
||||
var isEndPointSet = false;
|
||||
|
||||
//======================
|
||||
// FUNCTIONS
|
||||
/*
|
||||
* geocodeAddress()-Function to read out textfield and send request to a OSM nominatim geocoder
|
||||
*/
|
||||
function geocodeAddress(tf){
|
||||
var freeform;
|
||||
|
||||
if(tf == 'start'){
|
||||
freeform = document.getElementById('tfStartSearch').value;
|
||||
}
|
||||
if(tf == 'end'){
|
||||
freeform = document.getElementById('tfEndSearch').value;
|
||||
}
|
||||
|
||||
document.getElementById('information').style.visibility = 'visible';
|
||||
document.getElementById('information').innerHTML = '<p class="infoHL">One moment please ...</p>';
|
||||
|
||||
|
||||
var newURL = GEOCODE_POST + "&q="+freeform;
|
||||
var script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.src = newURL;
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
|
||||
/*
|
||||
* showResultsGeocode()-Function to show the geocode result in a div
|
||||
*/
|
||||
function showResultsGeocode(response) {
|
||||
var html = '';
|
||||
var lonlat = '';
|
||||
|
||||
if(response){
|
||||
html += '<p class="infoHL">Search Results:</p>';
|
||||
html += '<table>'
|
||||
var markername;
|
||||
if(getStatus()=='start'){
|
||||
markername = 'start'; isStartPointSet = true;
|
||||
}
|
||||
else if(getStatus()=='end'){
|
||||
markername = 'end'; isEndPointSet = true;
|
||||
}
|
||||
|
||||
for(var i=0; i < response.length; i++){
|
||||
var result = response[i]; var resultNum = i+1;
|
||||
//odd or even ?
|
||||
var rowstyle='geocodeResultOdd';
|
||||
if(i%2==0){ rowstyle='geocodeResultEven'; }
|
||||
|
||||
html += '<tr class="'+rowstyle+'">';
|
||||
html += '<td align="right" valign="top"><span class="routeSummarybold">'+resultNum+'</span></td>';
|
||||
html += '<td class="'+rowstyle+'">';
|
||||
if(result.display_name){
|
||||
var new_display_name = result.display_name;//.replace(/,/g, ",<br />")
|
||||
html += '<a href="#" onclick="javascript:setMarkerAndZoom(\''+markername+'\', new OpenLayers.LonLat('+result.lon+','+result.lat+'));">'+new_display_name.trim()+'</a>';
|
||||
}
|
||||
html += "</td></tr>";
|
||||
|
||||
//alert(result.lat + ", " + result.lon);
|
||||
if(lonlat == ''){
|
||||
lonlat = new OpenLayers.LonLat(result.lon,result.lat);
|
||||
}
|
||||
}
|
||||
html += '</table>';
|
||||
|
||||
setMarkerAndZoom(markername, lonlat);
|
||||
}
|
||||
|
||||
|
||||
switch (searchType) {
|
||||
case "search":
|
||||
document.getElementById('information').style.display = "";
|
||||
document.getElementById('information').innerHTML = html;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* setMarkerAndZoom()-Function to set a marker on the map and zoom
|
||||
*/
|
||||
function setMarkerAndZoom(markername,lonlat){
|
||||
setMarker(markername,lonlat);
|
||||
|
||||
//Hack - FIXME !
|
||||
lonlat.lon -= 500;
|
||||
//Set Center
|
||||
map.setCenter(lonlat, 15);
|
||||
}
|
||||
|
||||
/*
|
||||
* setMarker()-Function to set a marker on the map
|
||||
*/
|
||||
function setMarker(markername,lonlat){
|
||||
lonlat.transform(EPSG_4326, EPSG_900913);
|
||||
for(var i= 0; i<dragLayer.features.length; i++){
|
||||
if(dragLayer.features[i].name == markername){ dragLayer.removeFeatures([dragLayer.features[i]]); }
|
||||
}
|
||||
var point = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat));
|
||||
point.attributes = { icon: "img/start.png" };
|
||||
|
||||
if(markername == 'start'){
|
||||
point.attributes = { icon: "img/start.png" };
|
||||
}
|
||||
else if(markername == 'end'){
|
||||
point.attributes = { icon: "img/end.png" };
|
||||
}
|
||||
|
||||
point.name = markername;
|
||||
dragLayer.addFeatures([point]);
|
||||
}
|
||||
|
||||
/*
|
||||
* getMarkerByName()-Function to return the marker-object by a name
|
||||
*/
|
||||
function getMarkerByName(markerName){
|
||||
for(var i= 0; i<dragLayer.features.length; i++){
|
||||
if(dragLayer.features[i].name == markerName){
|
||||
return dragLayer.features[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,354 +0,0 @@
|
||||
/*
|
||||
* Open Source Routing Machine (OSRM) - Web (GUI) Interface
|
||||
* Copyright (C) Pascal Neis, 2011
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU AFFERO General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
* or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Title: Map.js
|
||||
* Description: JS file to show a map, for markers, left slide div, ...
|
||||
*
|
||||
* @author Pascal Neis, pascal@neis-one.org
|
||||
* @version 0.1.1 2011-06-09
|
||||
*/
|
||||
|
||||
//======================
|
||||
// OBJECTS
|
||||
//Map
|
||||
var map;
|
||||
|
||||
//Status object for what position actually a coordinate is searched
|
||||
var status = ''; //possible values [start,end]
|
||||
function getStatus(){ return status; }
|
||||
function setStatus(stat){ status = stat; }
|
||||
|
||||
//Indicator
|
||||
var ISDRAGGING = false;
|
||||
|
||||
//Layer
|
||||
var dragLayer;
|
||||
var vectorLayerRoute;
|
||||
var vectorLayerViaRoute;
|
||||
var markersLayer;
|
||||
|
||||
var selectFeature;
|
||||
//======================
|
||||
// FUNCTIONS
|
||||
/*
|
||||
* init()-Function to create the map div
|
||||
*/
|
||||
function init(){
|
||||
//Create Map
|
||||
map = new OpenLayers.Map ("map", {
|
||||
controls:[
|
||||
new OpenLayers.Control.Navigation(),
|
||||
new OpenLayers.Control.Permalink(),
|
||||
new OpenLayers.Control.LayerSwitcher()],
|
||||
maxExtent: new OpenLayers.Bounds(-20037508.34,-20037508.34,20037508.34,20037508.34),
|
||||
maxResolution: 156543.0399,
|
||||
numZoomLevels: 19,
|
||||
units: 'm',
|
||||
projection: new OpenLayers.Projection("EPSG:900913"),
|
||||
displayProjection: new OpenLayers.Projection("EPSG:4326")
|
||||
} );
|
||||
//Add the base layers.
|
||||
|
||||
var coverageLayer = new OpenLayers.Layer.Vector("Coverage", {
|
||||
style: {
|
||||
strokeColor: "#000000",
|
||||
strokeTransparency: 0.5,
|
||||
strokeWidth: 4
|
||||
}
|
||||
});
|
||||
if("map.project-osrm.org" == location.host) {
|
||||
var coveragePointList = [
|
||||
new OpenLayers.Geometry.Point(-11.99, 58.80),
|
||||
new OpenLayers.Geometry.Point(-11.99, 35.30),
|
||||
new OpenLayers.Geometry.Point(-7.57, 35.30),
|
||||
new OpenLayers.Geometry.Point(9.22, 38.24),
|
||||
new OpenLayers.Geometry.Point(15.38, 35.39),
|
||||
new OpenLayers.Geometry.Point(18.92, 40.02),
|
||||
new OpenLayers.Geometry.Point(15.70, 42.25),
|
||||
new OpenLayers.Geometry.Point(15.70, 55.40),
|
||||
new OpenLayers.Geometry.Point(30.31, 60.29),
|
||||
new OpenLayers.Geometry.Point(30.31, 71.27),
|
||||
new OpenLayers.Geometry.Point(21.01, 71.27),
|
||||
new OpenLayers.Geometry.Point(-11.99, 58.80)
|
||||
];
|
||||
|
||||
var line_string = new OpenLayers.Geometry.LineString(coveragePointList).transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913"));
|
||||
coverageLayer.addFeatures([new OpenLayers.Feature.Vector(line_string)]);
|
||||
|
||||
map.addLayer(coverageLayer);
|
||||
}
|
||||
|
||||
map.addLayer(new OpenLayers.Layer.OSM.Mapnik("Mapnik"));
|
||||
map.addLayer(new OpenLayers.Layer.OSM.MapQuest("MapQuest"));
|
||||
map.addLayer(new OpenLayers.Layer.OSM.Osmarender("Osmarender"));
|
||||
|
||||
//Add layer for the route
|
||||
vectorLayerRoute = new OpenLayers.Layer.Vector("Route",{maxResolution: 156543.0339});
|
||||
map.addLayer(vectorLayerRoute);
|
||||
|
||||
//Add layer for temporary via route
|
||||
vectorLayerViaRoute = new OpenLayers.Layer.Vector("ViaRoute",{maxResolution: 156543.0339});
|
||||
map.addLayer(vectorLayerViaRoute);
|
||||
|
||||
//Add Layerswitcher
|
||||
map.addControl(new OpenLayers.Control.LayerSwitcher());
|
||||
|
||||
//Add Panzoombar
|
||||
var panZoomBar = new OpenLayers.Control.PanZoomBar({id:'panzoombar',displayClass:'olControlPanZoomBar'})
|
||||
map.addControl(panZoomBar);
|
||||
//Customize the position of the panzoombar
|
||||
document.getElementById('panzoombar').style.left="400px";
|
||||
document.getElementById('panzoombar').style.top="25px";
|
||||
|
||||
//Create drag vector layer
|
||||
dragLayer = new OpenLayers.Layer.Vector(
|
||||
"Start/End Marker",{
|
||||
styleMap: new OpenLayers.StyleMap({
|
||||
// Set the external graphic and background graphic images.
|
||||
externalGraphic: '${icon}',
|
||||
// Makes sure the background graphic is placed correctly relative
|
||||
// to the external graphic.
|
||||
backgroundXOffset: -11,
|
||||
backgroundYOffset: -40,
|
||||
graphicXOffset: -11,
|
||||
graphicYOffset: -40,
|
||||
|
||||
// Set the z-indexes of both graphics to make sure the background
|
||||
// graphics stay in the background (shadows on top of markers looks
|
||||
// odd; let's not do that).
|
||||
graphicZIndex: 10,
|
||||
backgroundGraphicZIndex: 11,
|
||||
|
||||
pointRadius: 10,
|
||||
graphicWidth: 22,
|
||||
graphicHeight: 40
|
||||
}),
|
||||
rendererOptions: {zIndexing: true}
|
||||
}
|
||||
);
|
||||
// Add a drag feature control to move features around.
|
||||
var dragFeatures = new OpenLayers.Control.DragFeature(dragLayer,
|
||||
{ onDrag: function(feature, pixel){
|
||||
ISDRAGGING = true;
|
||||
if(feature.name == "start" || feature.name == "end") {
|
||||
feature.move(map.getLonLatFromPixel(pixel));
|
||||
if(!ISCALCULATING){ routing(true); }
|
||||
}
|
||||
if(feature.name == "via") {
|
||||
computeViaRoute(pixel, true);
|
||||
}
|
||||
if(feature.name == "viapoint" && feature.popup) {
|
||||
map.removePopup(feature.popup);
|
||||
computeViaRoute(pixel, true, feature.viaIndex);
|
||||
}
|
||||
},
|
||||
onComplete: function(feature, pixel){
|
||||
ISDRAGGING = false;
|
||||
if(feature.name == "start" || feature.name == "end") {
|
||||
feature.move(map.getLonLatFromPixel(pixel));
|
||||
routing(false);
|
||||
} else if(feature.name == "via") {
|
||||
console.log('finished via');
|
||||
//Erase temporary point from draglayer
|
||||
dragLayer.renderer.eraseFeatures([feature]);
|
||||
//delete temporary route from via route Layer
|
||||
vectorLayerViaRoute.removeFeatures(vectorLayerViaRoute.features);
|
||||
//compute via route
|
||||
computeViaRoute(pixel, false);
|
||||
feature.destroy();
|
||||
} else if(feature.name == "viapoint") {
|
||||
//Erase temporary point from draglayer
|
||||
dragLayer.renderer.eraseFeatures([feature]);
|
||||
//delete via point from vector
|
||||
viaPointsVector.splice(feature.viaIndex,1);
|
||||
//delete temporary route from via route Layer
|
||||
vectorLayerViaRoute.removeFeatures(vectorLayerViaRoute.features);
|
||||
//compute via route
|
||||
computeViaRoute(pixel, false);
|
||||
feature.destroy();
|
||||
}
|
||||
},
|
||||
onLeave: function(f){
|
||||
if(!ISCALCULATING){ routing(false); }}
|
||||
});
|
||||
map.addControl(dragFeatures);
|
||||
dragFeatures.activate();
|
||||
map.addLayers([dragLayer]);
|
||||
|
||||
//Add a marker layer
|
||||
markersLayer = new OpenLayers.Layer.Markers("Markers");
|
||||
map.addLayer(markersLayer);
|
||||
|
||||
//Add zoom event for rerouting
|
||||
map.events.on({zoomend: function(e) {reroute();distanceToRoute(null);}});
|
||||
|
||||
// Register Callback to evaluate distance to from mouse location to route on Mousemove
|
||||
map.events.on({mousemove: function(e) {distanceToRoute(e.xy);}});
|
||||
|
||||
// Set center of the map
|
||||
if (!map.getCenter()){
|
||||
map.setCenter(new OpenLayers.LonLat(600000, 6600000),6);
|
||||
}
|
||||
|
||||
//Check if the URL contains some GET parameter, e.g. for the route
|
||||
checkURL();
|
||||
|
||||
for(var i = 0; i < map.layers.length; i++) {
|
||||
map.layers[i].transitionEffect = 'resize';
|
||||
}
|
||||
|
||||
document.getElementById('map').oncontextmenu = function(e){
|
||||
e = e?e:window.event;
|
||||
if (e.preventDefault) e.preventDefault(); // For non-IE browsers.
|
||||
else return false; // For IE browsers.
|
||||
};
|
||||
|
||||
// A control class for capturing click events...
|
||||
OpenLayers.Control.Click = OpenLayers.Class(OpenLayers.Control, {
|
||||
|
||||
defaultHandlerOptions: {
|
||||
'single': true,
|
||||
'double': true,
|
||||
'pixelTolerance': 0,
|
||||
'stopSingle': false,
|
||||
'stopDouble': false
|
||||
},
|
||||
handleRightClicks:true,
|
||||
initialize: function(options) {
|
||||
this.handlerOptions = OpenLayers.Util.extend(
|
||||
{}, this.defaultHandlerOptions
|
||||
);
|
||||
OpenLayers.Control.prototype.initialize.apply(
|
||||
this, arguments
|
||||
);
|
||||
this.handler = new OpenLayers.Handler.Click(
|
||||
this, this.eventMethods, this.handlerOptions
|
||||
);
|
||||
},
|
||||
CLASS_NAME: "OpenLayers.Control.Click"
|
||||
|
||||
});
|
||||
|
||||
// Add an instance of the Click control that listens to various click events:
|
||||
var oClick = new OpenLayers.Control.Click({eventMethods:{
|
||||
'rightclick': function(e) {
|
||||
rightClick(e);
|
||||
},
|
||||
'click': function(e) {
|
||||
leftClick(e);
|
||||
},
|
||||
'dblclick': function(e) {
|
||||
dblClick(e);
|
||||
},
|
||||
'dblrightclick': function(e) {
|
||||
dblRightClick(e);
|
||||
}
|
||||
}});
|
||||
map.addControl(oClick);
|
||||
oClick.activate();
|
||||
|
||||
|
||||
selectFeature = new OpenLayers.Control.SelectFeature(
|
||||
[dragLayer],
|
||||
{
|
||||
clickout: true, toggle: false,
|
||||
multiple: false, hover: true,
|
||||
toggleKey: "ctrlKey", // ctrl key removes from selection
|
||||
multipleKey: "shiftKey", // shift key adds to selection
|
||||
onSelect: createPopup,
|
||||
onUnselect: destroyPopup
|
||||
|
||||
}
|
||||
);
|
||||
map.addControl(selectFeature);
|
||||
dragLayer.events.on({
|
||||
"featureselected": function(e) {
|
||||
featureSelected(e);
|
||||
},
|
||||
"featureunselected": function(e) {
|
||||
featureUnselected(e);
|
||||
}
|
||||
});
|
||||
|
||||
selectFeature.activate();
|
||||
}
|
||||
|
||||
//Helper Functions
|
||||
function checkReturn(textfieldname,e){
|
||||
var evt = e || window.event;
|
||||
if(!evt){ return; }
|
||||
|
||||
var key = 0;
|
||||
if (evt.keyCode) { key = evt.keyCode; }
|
||||
else if (typeof(evt.which)!= 'undefined') { key = evt.which; }
|
||||
if( key == 13 && textfieldname=='start'){ setStatus('start'); geocodeAddress('start'); return; }
|
||||
if( key == 13 && textfieldname=='end'){ setStatus('end'); geocodeAddress('end'); return; }
|
||||
}
|
||||
|
||||
function closeOpenDiv(name){
|
||||
if(document.getElementById(name).style.display == 'none'){ document.getElementById(name).style.display = ''; }
|
||||
else{ document.getElementById(name).style.display = 'none'; }
|
||||
}
|
||||
|
||||
//URL Functions
|
||||
function checkURL(){
|
||||
var getObjs = new Array();
|
||||
var getString = document.location.search.substr(1,document.location.search.length);
|
||||
if(getString != ''){
|
||||
var getArray=getString.split('&');
|
||||
console.log(getArray);
|
||||
for(i=0 ; i<getArray.length ; ++i){
|
||||
var v='';
|
||||
var vArr = getArray[i].split('=');
|
||||
if(vArr.length>1){ v = vArr[1]; }
|
||||
if("via" == vArr[0]) {
|
||||
var via_loc = unescape(v).split(',');
|
||||
for(var j=0; j<via_loc.length; j++) { via_loc[j] = parseFloat(via_loc[j]); }
|
||||
viaPointsVector.push(via_loc);
|
||||
} else {
|
||||
getObjs[unescape(vArr[0])]=unescape(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var fr = getObjectOfArray(getObjs, "fr");
|
||||
var to = getObjectOfArray(getObjs, "to");
|
||||
|
||||
if(fr != 'undefined' && to != 'undefined'){
|
||||
//From
|
||||
var fr_pos = fr.split(',');
|
||||
var fr_lonlat = new OpenLayers.LonLat(fr_pos[1],fr_pos[0]);
|
||||
setMarkerAndZoom('start', fr_lonlat);
|
||||
isStartPointSet = true;
|
||||
//To
|
||||
var to_pos = to.split(',');
|
||||
var to_lonlat = new OpenLayers.LonLat(to_pos[1],to_pos[0]);
|
||||
setMarker('end', to_lonlat);
|
||||
isEndPointSet = true;
|
||||
//Calculate the route
|
||||
routing(false);
|
||||
}
|
||||
}
|
||||
|
||||
function getObjectOfArray(objects, elementName){
|
||||
if(!objects[elementName]){ return 'undefined'; }
|
||||
return objects[elementName];
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
var selectedFeature = null;
|
||||
var popupCloseTimer = null;
|
||||
var popupToRemove = null;
|
||||
|
||||
function rightClick(e) {
|
||||
console.log("right click1");
|
||||
if(null == selectedFeature)
|
||||
return;
|
||||
//alert('rightclick at '+e.xy.x+','+e.xy.y);
|
||||
console.log("right click2");
|
||||
}
|
||||
|
||||
function dblClick(e) {
|
||||
//alert('dblclick at '+e.xy.x+','+e.xy.y);
|
||||
}
|
||||
|
||||
function dblRightClick(e) {
|
||||
//alert('dblrightclick at '+e.xy.x+','+e.xy.y);
|
||||
}
|
||||
|
||||
function leftClick(e) {
|
||||
//set start and target via clicks
|
||||
var lonlat = map.getLonLatFromViewPortPx(e.xy);
|
||||
var markername = "";
|
||||
|
||||
/* //routing shall not be done by left clicks
|
||||
* if (e.ctrlKey || e.altKey) {
|
||||
markername = "end";
|
||||
isEndPointSet = true;
|
||||
} else if(e.shiftKey) {
|
||||
markername = "start";
|
||||
isStartPointSet = true;
|
||||
}
|
||||
for(var i= 0; i<dragLayer.features.length; i++){
|
||||
if(dragLayer.features[i].name == markername){ dragLayer.removeFeatures([dragLayer.features[i]]); }
|
||||
}
|
||||
|
||||
var point = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(lonlat.lon, lonlat.lat));
|
||||
point.attributes = { icon: "img/start.png" };
|
||||
|
||||
if(markername == 'start'){
|
||||
point.attributes = { icon: "img/start.png" };
|
||||
}
|
||||
else if(markername == 'end'){
|
||||
point.attributes = { icon: "img/end.png" };
|
||||
}
|
||||
|
||||
point.name = markername;
|
||||
dragLayer.addFeatures([point]);
|
||||
reroute();
|
||||
*/
|
||||
}
|
||||
|
||||
function featureSelected(f) {
|
||||
if("viapoint" == f.feature.name) {
|
||||
// console.log('selected ' + f.feature.name);
|
||||
selectedFeature = f.feature;
|
||||
}
|
||||
}
|
||||
|
||||
function featureUnselected(f) {
|
||||
if("viapoint" == f.feature.name) {
|
||||
// console.log('unselected ' + f.feature.name);
|
||||
selectedFeature = null;
|
||||
}
|
||||
}
|
||||
|
||||
function createPopup(feature) {
|
||||
if("viapoint" != feature.name || ISDRAGGING)
|
||||
return;
|
||||
|
||||
if(popupCloseTimer != null) {
|
||||
clearInterval(popupCloseTimer);
|
||||
popupCloseTimer = null;
|
||||
map.removePopup(popupToRemove);
|
||||
popupToRemove = null;
|
||||
}
|
||||
var location = feature.geometry.getBounds().getCenterLonLat().clone();
|
||||
location.lon; location.lat;
|
||||
feature.popup = new OpenLayers.Popup.Anchored("ViaPointInfo",
|
||||
location,
|
||||
new OpenLayers.Size(16,16),
|
||||
'<div><a href="javascript:removeViaPoint('+ feature.viaIndex +')"><img src="img/cancel_small.png"/></a></div>',
|
||||
null,
|
||||
false,
|
||||
destroyPopup );
|
||||
feature.popup.backgroundColor = 'transparent';
|
||||
feature.popup.fixedRelativePosition = true;
|
||||
feature.popup.relativePosition = "tr";
|
||||
map.addPopup(feature.popup, true);
|
||||
}
|
||||
|
||||
function destroyPopup(feature) {
|
||||
if(feature.popup) {
|
||||
popupToRemove = feature.popup;
|
||||
popupCloseTimer = setTimeout("removePopup()",2000);
|
||||
}
|
||||
}
|
||||
|
||||
function removePopup() {
|
||||
if(null == popupToRemove)
|
||||
return;
|
||||
|
||||
map.removePopup(popupToRemove);
|
||||
popupToRemove = null;
|
||||
popupCloseTimer = null;
|
||||
}
|
||||
|
||||
function removeViaPoint(index) {
|
||||
for(var i = 0; i < map.popups.length; i++) {
|
||||
map.removePopup(map.popups[i]);
|
||||
}
|
||||
viaPointsVector.splice(index, 1);
|
||||
reroute();
|
||||
}
|
||||
@@ -1,366 +0,0 @@
|
||||
/*
|
||||
* Open Source Routing Machine (OSRM) - Web (GUI) Interface
|
||||
* Copyright (C) Pascal Neis, 2011
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU AFFERO General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
* or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Title: Route.js
|
||||
* Description: JS file for routing
|
||||
*
|
||||
* @author Pascal Neis, pascal@neis-one.org
|
||||
* @version 0.2 2011-06-23
|
||||
*/
|
||||
|
||||
//======================
|
||||
// OBJECTS
|
||||
//Map
|
||||
//var HOST_ROUTING_URL = 'http://localhost:5000/viaroute';
|
||||
var HOST_ROUTING_URL = 'http://routingdemo.geofabrik.de/route-via/';
|
||||
var HOST_WEBSITE = 'http://map.project-osrm.org/';//location.host
|
||||
|
||||
var ISCALCULATING = false;
|
||||
var EPSG_4326 = new OpenLayers.Projection("EPSG:4326");
|
||||
var EPSG_900913 = new OpenLayers.Projection("EPSG:900913");
|
||||
var allRoutePoints = [];
|
||||
//======================
|
||||
// FUNCTIONS
|
||||
/*
|
||||
* routing()-Function to create and send route request
|
||||
*/
|
||||
function routing(isDragRoute){
|
||||
//Check if startpoint is set
|
||||
if(!isStartPointSet){
|
||||
//alert("Please set your Start location first!");
|
||||
document.getElementById('information').innerHTML = '<p class="infoHLRed">Please set your Start location first!</p>';
|
||||
return;
|
||||
}
|
||||
//Check if endpoint is set
|
||||
if(!isEndPointSet){
|
||||
//alert("Please set your Destination first!");
|
||||
document.getElementById('information').innerHTML = '<p class="infoHLRed">Please set your Destination first!</p>';
|
||||
return;
|
||||
}
|
||||
|
||||
//Check if already a route is planning
|
||||
if(!isDragRoute){ ISCALCULATING = false; }
|
||||
if(ISCALCULATING){ return; }
|
||||
else{ ISCALCULATING = true; }
|
||||
|
||||
//Get Coordinates of the Start and Endpoint
|
||||
var startFeat = getMarkerByName('start');
|
||||
var endFeat = getMarkerByName('end');
|
||||
var from = new OpenLayers.LonLat(startFeat.geometry.x,startFeat.geometry.y).transform(new OpenLayers.Projection("EPSG:900913"), new OpenLayers.Projection("EPSG:4326"));
|
||||
var to = new OpenLayers.LonLat(endFeat.geometry.x,endFeat.geometry.y).transform(new OpenLayers.Projection("EPSG:900913"), new OpenLayers.Projection("EPSG:4326"));
|
||||
|
||||
//Send Request
|
||||
var script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
|
||||
var callBackFunction = 'showResultsRoute';
|
||||
var instructions = '&geomformat=cmp&instructions=true';
|
||||
if(isDragRoute){
|
||||
callBackFunction = 'showResultsDragRoute';
|
||||
instructions = '&geomformat=cmp&instructions=false';
|
||||
document.getElementById('information').innerHTML = '<p class="infoHL">Release mouse button to get Route Information!</p>(If no Route Summary is diplayed, press the Route!-button)';
|
||||
}
|
||||
|
||||
script.src = HOST_ROUTING_URL + "&start="+from.lat + ',' + from.lon + '&dest=' + to.lat + ',' + to.lon;
|
||||
for(var i = 0; i < viaPointsVector.length; i++) {
|
||||
script.src += ('&via=' + viaPointsVector[i][0] + ',' + viaPointsVector[i][1]);
|
||||
}
|
||||
script.src +='&z='+this.map.getZoom()+'&output=json&jsonp='+callBackFunction+instructions;
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
|
||||
/*
|
||||
* reroute()-Function
|
||||
*/
|
||||
function reroute() {
|
||||
if(!isStartPointSet || !isEndPointSet){ return; }
|
||||
routing(false);
|
||||
}
|
||||
|
||||
/*
|
||||
* showResultsDragRoute()-Function to show route result for drag-route
|
||||
*/
|
||||
function showResultsDragRoute(response) {
|
||||
if (response) {
|
||||
//Display Route
|
||||
showRouteGeometry(response);
|
||||
}
|
||||
markersLayer.clearMarkers();
|
||||
ISCALCULATING = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* showResultsRoute()-Function to show route result
|
||||
*/
|
||||
function showResultsRoute(response) {
|
||||
if (response) {
|
||||
//Display Route
|
||||
if(document.getElementById("cbNoNames").checked == true){
|
||||
showNoNameStreets(response);
|
||||
}
|
||||
else{
|
||||
showRouteGeometry(response);
|
||||
}
|
||||
|
||||
//Save Via Points that come with route
|
||||
var lengthOfArray = response.via_points.length;
|
||||
var i = 0;
|
||||
viaPointsVector.length = 0;
|
||||
viaPointsVector = response.via_points.slice(0);
|
||||
paintViaPoints();
|
||||
|
||||
//Create Link of the route
|
||||
var startFeat = getMarkerByName('start');
|
||||
var endFeat = getMarkerByName('end');
|
||||
var from = new OpenLayers.LonLat(startFeat.geometry.x,startFeat.geometry.y).transform(EPSG_900913,EPSG_4326);
|
||||
var to = new OpenLayers.LonLat(endFeat.geometry.x,endFeat.geometry.y).transform(EPSG_900913,EPSG_4326);
|
||||
var routelink = '<div id="routelink"><input name="routelink" type="submit" title="Get Link" onClick="createShortLink(\''+HOST_WEBSITE+'?fr='+from.lat.toFixed(6)+','+from.lon.toFixed(6)+'&to='+to.lat.toFixed(6)+','+to.lon.toFixed(6);
|
||||
for(i = 0; i < viaPointsVector.length; i++) {
|
||||
routelink += "&via=" + viaPointsVector[i][0] + "," + viaPointsVector[i][1];
|
||||
}
|
||||
routelink += '\');" value="Get Link"></div>';
|
||||
|
||||
//Link for the GPX Download
|
||||
var gpxLink = '(<a href="'+HOST_ROUTING_URL+'&start='+from.lat.toFixed(6)+','+from.lon.toFixed(6)+'&dest='+to.lat.toFixed(6)+','+to.lon.toFixed(6)+'&z='+this.map.getZoom();
|
||||
for(i = 0; i < viaPointsVector.length; i++) {
|
||||
gpxLink += "&via=" + viaPointsVector[i][0] + "," + viaPointsVector[i][1];
|
||||
}
|
||||
gpxLink += '&output=gpx">Get GPX File</a>)';;
|
||||
|
||||
//Show Route Summary
|
||||
var output = '<p class="routeSummaryHL">Some information about your Way <br> from \'<span class="routeSummaryHLlight">'+response.route_summary.start_point+'</span>\' to \'<span class="routeSummaryHLlight">'+response.route_summary.end_point+'</span>\'</p>';
|
||||
output += '<p class="routeSummary">Distance: <span class="routeSummarybold">'+response.route_summary.total_distance/1000+' km</span> - Duration: <span class="routeSummarybold">'+secondsToTime(response.route_summary.total_time)+'</span></p><p>'+routelink+'</p><p><span class="routeInstructionsHL">The Route-Instructions:</span> '+gpxLink+'</p>';
|
||||
//Show Route Instructions
|
||||
output += '<table>';
|
||||
lengthOfArray = response.route_instructions.length;
|
||||
for (i = 0; i < lengthOfArray; i++) {
|
||||
//console.log(response.route_instructions[i]);
|
||||
//odd or even ?
|
||||
var rowstyle='routeInstructionsOdd';
|
||||
if(i%2==0){ rowstyle='routeInstructionsEven'; }
|
||||
|
||||
var indexPos = response.route_instructions[i][3];
|
||||
//console.log('setting : ' + response.route_instructions[i] + ' at ' + allRoutePoints[indexPos]);
|
||||
var point = allRoutePoints[indexPos];
|
||||
|
||||
output += '<tr class="'+rowstyle+'"><td align="right" valign="top"><span class="routeSummarybold">'+(i+1)+'.</span></td><td class="'+rowstyle+'"><a href="#" class="nolinkStyle" onclick="setMapCenter(new OpenLayers.LonLat('+point.x+','+point.y+'));">'+response.route_instructions[i][0]+' on '+response.route_instructions[i][1]+' for '+getDistanceWithUnit(response.route_instructions[i][2])+'</a></td></tr>';
|
||||
}
|
||||
var rowstyle='routeInstructionsOdd';
|
||||
if(i%2==0){ rowstyle='routeInstructionsEven'; }
|
||||
if( lengthOfArray > 0) {
|
||||
var point = allRoutePoints[allRoutePoints.length-1];
|
||||
output += '<tr class="'+rowstyle+'"><td align="right" valign="top"><span class="routeSummarybold">'+(i+1)+'.</span></td><td class="'+rowstyle+'"><a href="#" class="nolinkStyle" onclick="setMapCenter(new OpenLayers.LonLat('+point.x+','+point.y+'));">You have reached your destination</a></td></tr>';
|
||||
}
|
||||
output += '</table>';
|
||||
//alert(vectorLayerRoute.features[0].geometry.getVertices());
|
||||
|
||||
document.getElementById('information').innerHTML = output;
|
||||
ISCALCULATING = false;
|
||||
ISCALCULATINGVIA = false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* showRouteGeometry()-Function to show route result
|
||||
*/
|
||||
function showRouteGeometry(response) {
|
||||
if (response) {
|
||||
allRoutePoints.length = 0;
|
||||
// now with compression of the route geometry
|
||||
var geometry = decodeRouteGeometry(response.route_geometry, 5);
|
||||
var lengthOfArray = geometry.length;
|
||||
var points = [];
|
||||
points.length = lengthOfArray;
|
||||
var points = [];
|
||||
//delete any previously displayed via route
|
||||
vectorLayerViaRoute.removeFeatures(vectorLayerViaRoute.features);
|
||||
vectorLayerRoute.removeAllFeatures();
|
||||
|
||||
//Create Route Layer for Display
|
||||
for (var i = 0; i < lengthOfArray; i++) {
|
||||
var point = new OpenLayers.Geometry.Point(geometry[i][1], geometry[i][0]).transform(EPSG_4326, EPSG_900913);
|
||||
allRoutePoints.push(point);
|
||||
points.push(point);
|
||||
|
||||
if(i % 1024 == 0 && i>0 || i==lengthOfArray-1){
|
||||
/* var feature2 = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString(points), null, {
|
||||
strokeColor: "#000066",
|
||||
strokeOpacity: 1,
|
||||
strokeWidth: 9
|
||||
});
|
||||
|
||||
var feature1 = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString(points), null, {
|
||||
strokeColor: "#ffffff",
|
||||
strokeOpacity: 0.75,
|
||||
strokeWidth: 7
|
||||
});
|
||||
*/
|
||||
|
||||
var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString(points), null, {
|
||||
strokeColor: "#0033ff",
|
||||
strokeOpacity: 0.7,
|
||||
strokeWidth: 5
|
||||
});
|
||||
// vectorLayerRoute.addFeatures([feature2]);
|
||||
// vectorLayerRoute.addFeatures([feature1]);
|
||||
vectorLayerRoute.addFeatures([feature]);
|
||||
points = [];
|
||||
points.push(point);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* showNoNameStreets()-Function to show route result
|
||||
*/
|
||||
function showNoNameStreets(response) {
|
||||
if (response) {
|
||||
allRoutePoints.length = 0;
|
||||
vectorLayerRoute.removeFeatures(vectorLayerRoute.features);
|
||||
// now with compression of the route geometry
|
||||
var geometry = decodeRouteGeometry(response.route_geometry, 5);
|
||||
var lengthOfArray = geometry.length;
|
||||
var points = [];
|
||||
points.length = lengthOfArray;
|
||||
|
||||
//Check if a instruction has no name !
|
||||
var colors = [];
|
||||
if(true){
|
||||
var instrucLength = response.route_instructions.length;
|
||||
for (var i = 0; i < instrucLength; i++) {
|
||||
var indexPos = response.route_instructions[i][3];
|
||||
var streetName = response.route_instructions[i][1];
|
||||
if(streetName == ''){ colors[indexPos] = "#FF00FF"; }
|
||||
else{ colors[indexPos] = "#0033ff"; }
|
||||
}
|
||||
}
|
||||
|
||||
//delete any previously displayed via route
|
||||
vectorLayerViaRoute.removeFeatures(vectorLayerViaRoute.features);
|
||||
vectorLayerRoute.removeAllFeatures();
|
||||
|
||||
//Create Route Layer for Display
|
||||
var color = "#0033ff";
|
||||
var changeColor = false;
|
||||
for (var i = 0; i < lengthOfArray; i++) {
|
||||
var point = new OpenLayers.Geometry.Point(geometry[i][1], geometry[i][0]).transform(EPSG_4326, EPSG_900913);
|
||||
points.push(point);
|
||||
allRoutePoints.push(point);
|
||||
|
||||
if(colors[i] != undefined){ changeColor=true;}
|
||||
|
||||
if(i % 1024 == 0 && i>0 || i==lengthOfArray-1 || changeColor){
|
||||
var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString(points), null, {
|
||||
strokeColor: color,
|
||||
strokeOpacity: 0.7,
|
||||
strokeWidth: 5
|
||||
});
|
||||
vectorLayerRoute.addFeatures([feature]);
|
||||
points = [];
|
||||
points.push(point);
|
||||
|
||||
if(colors[i] != undefined){ color = colors[i]; }
|
||||
changeColor=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* secondsToTime()-Function to transform seconds to a time string
|
||||
*/
|
||||
function secondsToTime(seconds){
|
||||
seconds = parseInt(seconds);
|
||||
minutes = parseInt(seconds/60);
|
||||
seconds = seconds%60;
|
||||
hours = parseInt(minutes/60);
|
||||
minutes = minutes%60;
|
||||
//return stunden+':'+minuten+':'+seconds;
|
||||
if(hours==0){
|
||||
return minutes+' min(s)';
|
||||
}
|
||||
else{
|
||||
return hours+' h '+minutes+' min(s)';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* getDistanceWithUnit()-Function to return a distance with units
|
||||
*/
|
||||
function getDistanceWithUnit(distance){
|
||||
distance = parseInt(distance);
|
||||
if(distance >= 1000){ return (parseInt(distance/1000))+' km'; }
|
||||
else{ return distance+' m'; }
|
||||
}
|
||||
|
||||
/*
|
||||
* setMapCenter()-Function to add a marker and center the map
|
||||
*/
|
||||
function setMapCenter(lonlat){
|
||||
//lonlat.transform(new OpenLayers.Projection("EPSG:4326"), new OpenLayers.Projection("EPSG:900913"));
|
||||
//console.log('zooming to :' + lonlat);
|
||||
|
||||
//Add Marker
|
||||
markersLayer.clearMarkers();
|
||||
var size = new OpenLayers.Size(21,25);
|
||||
var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
|
||||
var icon = new OpenLayers.Icon('img/marker.png',size,offset);
|
||||
markersLayer.addMarker(new OpenLayers.Marker(lonlat,icon));
|
||||
|
||||
//Hack - FIXME !
|
||||
map.setCenter(new OpenLayers.LonLat(lonlat.lon-200, lonlat.lat), 17);
|
||||
}
|
||||
|
||||
/*
|
||||
* decodeRouteGeometry()-Function to decode encoded Route Geometry
|
||||
*/
|
||||
function decodeRouteGeometry(encoded, precision) {
|
||||
precision = Math.pow(10, -precision);
|
||||
var len = encoded.length, index=0, lat=0, lng = 0, array = [];
|
||||
while (index < len) {
|
||||
var b, shift = 0, result = 0;
|
||||
do {
|
||||
b = encoded.charCodeAt(index++) - 63;
|
||||
result |= (b & 0x1f) << shift;
|
||||
shift += 5;
|
||||
} while (b >= 0x20);
|
||||
var dlat = ((result & 1) ? ~(result >> 1) : (result >> 1));
|
||||
lat += dlat;
|
||||
shift = 0;
|
||||
result = 0;
|
||||
do {
|
||||
b = encoded.charCodeAt(index++) - 63;
|
||||
result |= (b & 0x1f) << shift;
|
||||
shift += 5;
|
||||
} while (b >= 0x20);
|
||||
var dlng = ((result & 1) ? ~(result >> 1) : (result >> 1));
|
||||
lng += dlng;
|
||||
array.push([lat * precision, lng * precision]);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/*
|
||||
* createShortLink()-Function to "create" shortlink of a route
|
||||
*/
|
||||
function createShortLink(str){
|
||||
var script = document.createElement('script');
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Open Source Routing Machine (OSRM) - Web (GUI) Interface
|
||||
* Copyright (C) Pascal Neis, 2011
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU AFFERO General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
* or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Title: Slide.js
|
||||
* Description: Fixme
|
||||
*
|
||||
* @author Pascal Neis, pascal@neis-one.org
|
||||
* @version 0.1 2011-05-15
|
||||
*/
|
||||
|
||||
var timerlen = 5;
|
||||
var slideAniLen = 250;
|
||||
|
||||
var timerID = new Array();
|
||||
var startTime = new Array();
|
||||
var obj = new Array();
|
||||
var endWidth = new Array();
|
||||
var moving = new Array();
|
||||
var dir = new Array();
|
||||
|
||||
function toggleSlide(objname){
|
||||
document.getElementById('panzoombar').style.top="25px";
|
||||
document.getElementById('panzoombar').style.display = "none";
|
||||
document.getElementById('slide').style.display = "none";
|
||||
if(document.getElementById(objname).style.display == "none"){
|
||||
// div is hidden, so let's slide right
|
||||
slide_right(objname);
|
||||
}else{
|
||||
// div is not hidden, so slide left
|
||||
slide_left(objname);
|
||||
}
|
||||
}
|
||||
|
||||
function slide_right(objname){
|
||||
if(moving[objname]){ return; }
|
||||
if(document.getElementById(objname).style.display != "none"){
|
||||
return; // cannot slide right something that is already visible
|
||||
}
|
||||
moving[objname] = true; dir[objname] = "right"; startslide(objname);
|
||||
}
|
||||
|
||||
function slide_left(objname){
|
||||
if(moving[objname]){ return; }
|
||||
if(document.getElementById(objname).style.display == "none"){
|
||||
return; // cannot slide left something that is already hidden
|
||||
}
|
||||
moving[objname] = true; dir[objname] = "left"; startslide(objname);
|
||||
}
|
||||
|
||||
function startslide(objname){
|
||||
obj[objname] = document.getElementById(objname);
|
||||
endWidth[objname] = parseInt(obj[objname].style.width);
|
||||
startTime[objname] = (new Date()).getTime();
|
||||
if(dir[objname] == "right"){ obj[objname].style.width = "1px"; }
|
||||
obj[objname].style.display = "block";
|
||||
timerID[objname] = setInterval('slidetick(\'' + objname + '\');',timerlen);
|
||||
}
|
||||
|
||||
function slidetick(objname){
|
||||
var elapsed = (new Date()).getTime() - startTime[objname];
|
||||
if (elapsed > slideAniLen){ endSlide(objname); }
|
||||
else {
|
||||
var d = Math.round(elapsed / slideAniLen * endWidth[objname]);
|
||||
if(dir[objname] == "left"){ d = endWidth[objname] - d; }
|
||||
obj[objname].style.width = d + "px";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
function endSlide(objname){
|
||||
clearInterval(timerID[objname]);
|
||||
|
||||
if(dir[objname] == "left"){
|
||||
obj[objname].style.display = "none";
|
||||
document.getElementById('panzoombar').style.left="25px";
|
||||
document.getElementById('panzoombar').style.display = "";
|
||||
document.getElementById('slide').style.left="5px";
|
||||
document.getElementById('slide').innerHTML = "<a class=\"btn-slide\" href=\"javascript:;\" onmousedown=\"toggleSlide('data');\">>></a>";
|
||||
document.getElementById('slide').style.display = "";
|
||||
}
|
||||
else{
|
||||
document.getElementById('panzoombar').style.left="400px";
|
||||
document.getElementById('panzoombar').style.display = "";document.getElementById('slide').style.left="365px";
|
||||
document.getElementById('slide').innerHTML = "<a class=\"btn-slide\" href=\"javascript:;\" onmousedown=\"toggleSlide('data');\"><<</a>";
|
||||
document.getElementById('slide').style.display = "";
|
||||
}
|
||||
obj[objname].style.width = endWidth[objname] + "px";
|
||||
|
||||
delete(moving[objname]);
|
||||
delete(timerID[objname]);
|
||||
delete(startTime[objname]);
|
||||
delete(endWidth[objname]);
|
||||
delete(obj[objname]);
|
||||
delete(dir[objname]);
|
||||
return;
|
||||
}
|
||||
@@ -1,340 +0,0 @@
|
||||
/*
|
||||
* Open Source Routing Machine (OSRM) - Web (GUI) Interface
|
||||
* Copyright (C) Dennis Luxen, 2011
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU AFFERO General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
* or see http://www.gnu.org/licenses/agpl.txt.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Title: ViaRoute.js
|
||||
* Description: JS file for routing with via points
|
||||
*
|
||||
* @author Dennis Luxen, luxen@kit.edu
|
||||
* @version 0.1 June 2011
|
||||
*
|
||||
*/
|
||||
//======================
|
||||
// OBJECTS
|
||||
|
||||
//global timestamp to control frequency of mouse->route distance computations
|
||||
var timestamp = (new Date()).getTime();
|
||||
var ISCOMPUTINGDISTANCE = false;
|
||||
var ISCALCULATINGVIA = false;
|
||||
//var HASDELETEDTEMPORARYVIAROUTE = false;
|
||||
var nearestPoint = new OpenLayers.Geometry.Point(1,1);
|
||||
var nearestSegmentIndex = null;
|
||||
var viaPointsVector = [];
|
||||
var p0;
|
||||
|
||||
var temporaryViaStyle = {
|
||||
fillColor: "#ffffff",
|
||||
strokeColor: "#33ff33",
|
||||
strokeOpacity: 0.6,
|
||||
strokeWidth: 2,
|
||||
pointRadius: 4,
|
||||
pointerEvents: "visiblePainted"
|
||||
};
|
||||
|
||||
var permanentViaStyle = {
|
||||
fillColor: "#ffffff",
|
||||
strokeColor: "#0033ff",
|
||||
strokeOpacity: 0.6,
|
||||
strokeWidth: 2,
|
||||
pointRadius: 4,
|
||||
pointerEvents: "visiblePainted"
|
||||
};
|
||||
|
||||
//======================
|
||||
//FUNCTIONS
|
||||
function distanceBetweenPoint(x1, y1, x2, y2) {
|
||||
var a = x1 - x2;
|
||||
var b = y1 - y2;
|
||||
return Math.sqrt(a*a + b*b);
|
||||
}
|
||||
|
||||
|
||||
/* Distance from a point to a line or segment.
|
||||
x point's x coord
|
||||
y point's y coord
|
||||
x0 x coord of the line's A point
|
||||
y0 y coord of the line's A point
|
||||
x1 x coord of the line's B point
|
||||
y1 y coord of the line's B point
|
||||
o specifies if the distance should respect the limits of the segment
|
||||
(overLine = true) or if it should consider the segment as an infinite
|
||||
line (overLine = false), if false returns the distance from the point
|
||||
to the line, otherwise the distance from the point to the segment
|
||||
*/
|
||||
function dotLineLength(x, y, x0, y0, x1, y1, o){
|
||||
function lineLength(x, y, x0, y0){
|
||||
return Math.sqrt((x -= x0) * x + (y -= y0) * y);
|
||||
}
|
||||
if(o && !(o = function(x, y, x0, y0, x1, y1){
|
||||
if(!(x1 - x0)) return {x: x0, y: y};
|
||||
else if(!(y1 - y0)) return {x: x, y: y0};
|
||||
var left, tg = -1 / ((y1 - y0) / (x1 - x0));
|
||||
return {x: left = (x1 * (x * tg - y + y0) + x0 * (x * - tg + y - y1)) / (tg * (x1 - x0) + y0 - y1), y: tg * left - tg * x + y};
|
||||
}(x, y, x0, y0, x1, y1), o.x >= Math.min(x0, x1) && o.x <= Math.max(x0, x1) && o.y >= Math.min(y0, y1) && o.y <= Math.max(y0, y1))){
|
||||
var l1 = lineLength(x, y, x0, y0), l2 = lineLength(x, y, x1, y1);
|
||||
return l1 > l2 ? l2 : l1;
|
||||
}
|
||||
else {
|
||||
var a = y0 - y1, b = x1 - x0, c = x0 * y1 - y0 * x1;
|
||||
return Math.abs(a * x + b * y + c) / Math.sqrt(a * a + b * b);
|
||||
}
|
||||
};
|
||||
|
||||
function distanceToRoute(pixel) {
|
||||
if(!isStartPointSet || !isEndPointSet){
|
||||
return;
|
||||
}
|
||||
if(ISDRAGGING) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(ISCOMPUTINGDISTANCE) {
|
||||
return;
|
||||
}
|
||||
var tempTime = (new Date()).getTime()
|
||||
if( tempTime - timestamp < 25) {
|
||||
return;
|
||||
}
|
||||
timestamp = tempTime;
|
||||
if(ISCALCULATING) {
|
||||
var viaPointFeature = dragLayer.getFeatureBy("name", 'via');
|
||||
if(viaPointFeature != null) {
|
||||
dragLayer.removeFeatures(viaPointFeature);
|
||||
viaPointFeature.destroy;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(pixel == null) {
|
||||
pixel.x = window.event.clientX;
|
||||
pixel.y = window.event.clientY;
|
||||
}
|
||||
|
||||
var newLonLat = map.getLonLatFromPixel(pixel);
|
||||
var minimumDist = 20*Math.pow(2,(19- this.map.getZoom()));
|
||||
var minimumDistToVia = 20*Math.pow(2,(19- this.map.getZoom()));
|
||||
var indexToInsertViaPoint = 0;
|
||||
|
||||
var startFeat = getMarkerByName('start');
|
||||
var endFeat = getMarkerByName('end');
|
||||
var from = new OpenLayers.LonLat(startFeat.geometry.x,startFeat.geometry.y).transform(EPSG_900913, EPSG_4326);
|
||||
var to = new OpenLayers.LonLat(endFeat.geometry.x,endFeat.geometry.y).transform(EPSG_900913, EPSG_4326);
|
||||
|
||||
var mapExtent = map.getExtent();
|
||||
for(var i = 0; i < allRoutePoints.length-1; i++) {
|
||||
var p1 = allRoutePoints[i];
|
||||
var p2 = allRoutePoints[i+1];
|
||||
if(p1.x < mapExtent.left || p1.x > mapExtent.right || p1.y < mapExtent.bottom || p1.y > mapExtent.top) {
|
||||
if(p2.x < mapExtent.left || p2.x > mapExtent.right || p2.y < mapExtent.bottom || p2.y > mapExtent.top) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
//check if previous segment is closest to via point
|
||||
//if yes, then we have passed a via point.
|
||||
if(viaPointsVector.length > indexToInsertViaPoint && p0) {
|
||||
var viaPoint = new OpenLayers.LonLat(viaPointsVector[indexToInsertViaPoint][1],viaPointsVector[indexToInsertViaPoint][0]).transform(EPSG_4326, EPSG_900913);
|
||||
var dist2 = dotLineLength(viaPoint.lon, viaPoint.lat, p1.x, p1.y, p0.x, p0.y, true)
|
||||
if( 0 == dist2 ) {
|
||||
indexToInsertViaPoint++;
|
||||
}
|
||||
var tmpDist = distanceBetweenPoint(viaPoint.lat, viaPoint.lon, newLonLat.lat, newLonLat.lon);
|
||||
if(tmpDist < minimumDistToVia) {
|
||||
minimumDistToVia = tmpDist;
|
||||
}
|
||||
}
|
||||
|
||||
//continue if point out of view
|
||||
dist = dotLineLength(newLonLat.lon, newLonLat.lat, p1.x, p1.y, p2.x, p2.y, true);
|
||||
if(dist <= minimumDist) {
|
||||
nearestSegmentIndex = indexToInsertViaPoint;
|
||||
minimumDist = dist;
|
||||
var lonDiff = (p2.x - p1.x)
|
||||
var m = (p2.y - p1.y) / lonDiff;
|
||||
|
||||
var b = p1.y - (m * p1.x);
|
||||
var m2 = m*m;
|
||||
var x = (m * newLonLat.lat + newLonLat.lon - m * b) / (m2 + 1);
|
||||
var y = (m2 * newLonLat.lat + m * newLonLat.lon + b) / (m2 + 1);
|
||||
var r = (x - p1.x) / lonDiff;
|
||||
if ( r <= 1 && r >= 0 ) {
|
||||
nearestPoint.x = x;
|
||||
nearestPoint.y = y;
|
||||
} else {
|
||||
if( r < 0 ) {
|
||||
nearestPoint.x = p1.x;
|
||||
nearestPoint.y = p1.y;
|
||||
} if ( r > 1 ) {
|
||||
nearestPoint.x = p2.x;
|
||||
nearestPoint.y = p2.y;
|
||||
}
|
||||
}
|
||||
if(p2.x == p1.x) {
|
||||
nearestPoint.x = p1.x;
|
||||
nearestPoint.y = newLonLat.lat;
|
||||
}
|
||||
}
|
||||
}
|
||||
var zoomFactor = Math.pow(2,(19- this.map.getZoom()));
|
||||
var viaPointFeature = dragLayer.getFeatureBy("name", 'via');
|
||||
if(viaPointFeature != null) {
|
||||
dragLayer.removeFeatures(viaPointFeature);
|
||||
viaPointFeature.destroy;
|
||||
}
|
||||
|
||||
//too close to start or destination?
|
||||
if(Math.min(distanceBetweenPoint(startFeat.geometry.x, startFeat.geometry.y, newLonLat.lon, newLonLat.lat),
|
||||
distanceBetweenPoint(endFeat.geometry.x, endFeat.geometry.y, newLonLat.lon, newLonLat.lat)) < 10*zoomFactor) {
|
||||
ISCOMPUTINGDISTANCE = false;
|
||||
return;
|
||||
}
|
||||
|
||||
//Are we close to the route but sufficiently far away from any existing via point?
|
||||
if(minimumDist < 10*zoomFactor && minimumDistToVia > 5*zoomFactor) {
|
||||
viaPointFeature = new OpenLayers.Feature.Vector( OpenLayers.Geometry.Polygon.createRegularPolygon( nearestPoint, (1.5*zoomFactor), 15, 0 ), null, temporaryViaStyle );
|
||||
viaPointFeature.name = "via";
|
||||
dragLayer.addFeatures( viaPointFeature );
|
||||
//console.log('nearestSegmentIndex: ' + nearestSegmentIndex);
|
||||
}
|
||||
ISCOMPUTINGDISTANCE = false;
|
||||
p0 = p1;
|
||||
}
|
||||
|
||||
function computeViaRoute(pixel, isTemporary, skipViaPointsIndex) {
|
||||
if(ISCALCULATINGVIA && isTemporary)
|
||||
return;
|
||||
|
||||
if( nearestSegmentIndex == null ) {
|
||||
//console.log('nearestSegmentIndex: ' + nearestSegmentIndex);
|
||||
return;
|
||||
}
|
||||
|
||||
if(isTemporary == null) {
|
||||
isTemporary = false;
|
||||
}
|
||||
|
||||
//So, we jumped all fences
|
||||
ISCALCULATINGVIA = true;
|
||||
|
||||
var startFeat = getMarkerByName('start');
|
||||
var endFeat = getMarkerByName('end');
|
||||
var from = new OpenLayers.LonLat(startFeat.geometry.x,startFeat.geometry.y).transform(EPSG_900913, EPSG_4326);
|
||||
var to = new OpenLayers.LonLat(endFeat.geometry.x,endFeat.geometry.y).transform(EPSG_900913, EPSG_4326);
|
||||
|
||||
var coordinate = map.getLonLatFromPixel(pixel);
|
||||
var via = coordinate.transform(EPSG_900913, EPSG_4326);
|
||||
var viaNodeInserted = false;
|
||||
var newURL = HOST_ROUTING_URL + "&start="+from.lat + ',' + from.lon + '&dest=' + to.lat + ',' + to.lon;
|
||||
newURL += '&geomformat=cmp';
|
||||
for(var i = 0; i < viaPointsVector.length; i++) {
|
||||
if(i == nearestSegmentIndex) { //insert new via node here
|
||||
newURL += '&via=' + via.lat + ',' + via.lon;
|
||||
viaNodeInserted = true;
|
||||
}
|
||||
if(skipViaPointsIndex == i)
|
||||
continue;
|
||||
newURL += '&via=' + viaPointsVector[i][0] + ',' + viaPointsVector[i][1];
|
||||
}
|
||||
if(false == viaNodeInserted) {
|
||||
newURL += '&via=' + via.lat + ',' + via.lon;
|
||||
}
|
||||
newURL += '&output=json' + '&z=' + this.map.getZoom();
|
||||
if(!isTemporary) {
|
||||
newURL += '&instructions=true&jsonp=showResultsRoute';
|
||||
} else {
|
||||
newURL += '&instructions=false&jsonp=showResultsViaRoute';
|
||||
}
|
||||
//console.log(newURL);
|
||||
var script = document.createElement('script');
|
||||
script.id = 'JSONP';
|
||||
script.type = 'application/javascript';
|
||||
script.src = newURL;
|
||||
|
||||
document.body.appendChild(script);
|
||||
}
|
||||
|
||||
function showResultsViaRoute(response) {
|
||||
if (response) {
|
||||
var viaRouteVector = [];
|
||||
// now with compression of the route geometry
|
||||
var geometry = decodeRouteGeometry(response.route_geometry, 5);
|
||||
var lengthOfArray = geometry.length;
|
||||
var points = [];
|
||||
points.length = lengthOfArray;
|
||||
|
||||
//Create Route Layer for Display
|
||||
for (var i = 0; i < lengthOfArray; i++) {
|
||||
var point = new OpenLayers.Geometry.Point(geometry[i][1], geometry[i][0]).transform(EPSG_4326, EPSG_900913);
|
||||
allRoutePoints.push(point);
|
||||
points.push(point);
|
||||
|
||||
if(i % 1024 == 0 && i>0 || i==lengthOfArray-1){
|
||||
/* var feature1 = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString(points), null, {
|
||||
strokeColor: "#006600",
|
||||
strokeOpacity: 1,
|
||||
strokeWidth: 9
|
||||
});
|
||||
*/
|
||||
var feature = new OpenLayers.Feature.Vector(new OpenLayers.Geometry.LineString(points), null, {
|
||||
strokeColor: "#aaffaa",
|
||||
strokeOpacity: 0.8,
|
||||
strokeWidth: 7
|
||||
});
|
||||
// viaRouteVector.push(feature1);
|
||||
viaRouteVector.push(feature);
|
||||
points = [];
|
||||
points.push(point);
|
||||
}
|
||||
}
|
||||
vectorLayerViaRoute.removeFeatures(vectorLayerViaRoute.features);
|
||||
|
||||
for(var i = 0; i< viaRouteVector.length; i++) {
|
||||
vectorLayerViaRoute.addFeatures(viaRouteVector[i]);
|
||||
}
|
||||
viaRouteVector.length = 0;
|
||||
ISCALCULATINGVIA = false;
|
||||
}
|
||||
}
|
||||
|
||||
function paintViaPoints() {
|
||||
//remove all previous via points
|
||||
var featuresToRemove = [];
|
||||
for(var i = 0; i < dragLayer.features.length; i++) {
|
||||
if(dragLayer.features[i].name == "viapoint" || "via" == dragLayer.features[i].name ) {
|
||||
featuresToRemove.push(dragLayer.features[i]);
|
||||
}
|
||||
}
|
||||
dragLayer.removeFeatures(featuresToRemove);
|
||||
featuresToRemove.length = 0;
|
||||
|
||||
//paint all via points
|
||||
var zoomFactor = Math.pow(2,(19- this.map.getZoom()));
|
||||
//console.log('painting ' + viaPointsVector.length + ' via points');
|
||||
for(var i = 0; i < viaPointsVector.length; i++) {
|
||||
//console.log(viaPointsVector[i]);
|
||||
var viapoint = new OpenLayers.Geometry.Point(
|
||||
viaPointsVector[i][1],
|
||||
viaPointsVector[i][0]);
|
||||
viapoint.transform(EPSG_4326, EPSG_900913);
|
||||
var viaPointFeature = new OpenLayers.Feature.Vector( OpenLayers.Geometry.Polygon.createRegularPolygon( viapoint, (1.5*zoomFactor), 20, 0 ), null, permanentViaStyle );
|
||||
viaPointFeature.name = "viapoint";
|
||||
viaPointFeature.viaIndex = i;
|
||||
dragLayer.addFeatures( viaPointFeature )
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 934 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 831 B |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
@@ -1,70 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
|
||||
<title>OSRM Web(GUI)Interface</title>
|
||||
<meta name="description" content="OSRM Web(GUI)Interface"/>
|
||||
<meta name="language" content="en" />
|
||||
<meta name="robots" content="index,follow" />
|
||||
<meta name="author" content="Pascal Neis" />
|
||||
<meta name="revisit-After" content="7 days" />
|
||||
<meta name="distribution" content="global" />
|
||||
|
||||
<script src="ol/OpenLayers.js"></script>
|
||||
<script src="ol/OpenStreetMap.js"></script>
|
||||
<script src="Slides.js"></script>
|
||||
<script src="Map.js"></script>
|
||||
<script src="Route.js"></script>
|
||||
<script src="ViaRoute.js"></script>
|
||||
<script src="Geocode.js"></script>
|
||||
<script src="MouseClick.js"></script>
|
||||
<!-- <script src="osrm.js"></script> -->
|
||||
|
||||
<link rel="stylesheet" href="style.css" type="text/css" media="all" />
|
||||
</head>
|
||||
|
||||
<body onload="init();">
|
||||
|
||||
<div id="slide" class="slide"><a class="btn-slide" href="javascript:;" onmousedown="toggleSlide('data');"><<</a></div>
|
||||
<div id="data" style="width:350px;overflow:hidden;" class="data">
|
||||
<div id="inner" style="width:350px;overflow:hidden;">
|
||||
<div id="routing">
|
||||
<!-- http://map.project-osrm.org/osrm3_small.png - http://project-osrm.org/osrm3.png -->
|
||||
<img src="http://map.project-osrm.org/osrm3_small.png" style="margin-left:auto; margin-right:auto; width:200px; display:block; padding: 5px;" alt="">
|
||||
|
||||
<table width="100%">
|
||||
<tr id="StartPoint">
|
||||
<td><div class="textLabel"><img src="img/arrow-start.png" class="radioIcons" width="12" height="16" alt=""><span class="text"></span></div></td>
|
||||
<td><input name="tfStartSearch" style="width:220px" type="text" id="tfStartSearch" class="tfPoints" value="Berlin" title="Search your start point!" onKeyPress="checkReturn('start',event);" >
|
||||
<button name="enterStart" onclick="setStatus('start'); geocodeAddress('start');">Search</button></td>
|
||||
<td rowspan="2" valign="middle">
|
||||
<!-- <button name="switchStartEnd" disabled="true" id="switchStartEnd" type="button"
|
||||
value="Change Start and Endpoint"onClick="changeStartEnd();"><img src="img/changePos.gif" width="9" height="22" alt="Tausche Start- und Endpunkt"><br> --></button></td>
|
||||
</tr>
|
||||
<tr id="EndPoint">
|
||||
<td nowrap>
|
||||
<div class="textLabel"><img src="img/arrow-finish.png" class="radioIcons" width="12" height="16" alt=""><span class="text"></span></div>
|
||||
</td>
|
||||
<td id="tdEnd">
|
||||
<input name="tfEndSearch" style="width:220px" type="text" id="tfEndSearch" class="tfPoints" value="München, Altstadt" title="Search your end point!" onKeyPress="checkReturn('end',event);">
|
||||
<button name="enterEnd" onclick="setStatus('end'); geocodeAddress('end');">Search</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="calc_route" class="calc_route"><span class="options"><a href="#" onclick="closeOpenDiv('options');">Options</a></span>
|
||||
<select name="selectRoutePref" id="selectRoutePref" title="Select the route preference ..."><option value="Fastest" selected>Car (travel time)</option></select>
|
||||
<input name="Submit5" type="submit" class="calculate_button" id="Calculate" title="Route!" onClick="markersLayer.clearMarkers(); routing(false);" value="Route!"></div>
|
||||
|
||||
<div id="options" class="options_div" style="display:none"><input class="checkbox" type="checkbox" name="cbNoNames" id="cbNoNames" value="checkbox">Highlight No Name Streets in my Route</div>
|
||||
<div id="information" class="information"> <p class="infoHL">Demo Website for the <br><a target="_blank" href="http://project-osrm.org/">Open Source Routing Machine Project</a></p> </div>
|
||||
<div id="thx" class="thx">GUI v0.3 110722 - OSRM hosting by <a target="_blank" href="http://www.geofabrik.de/">Geofabrik</a> - Geocoder by <a target="_blank" href="http://developer.mapquest.com/web/products/open/nominatim/">MapQuest</a></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="map" class="map"></div>
|
||||
|
||||
<div class="copyright">© 2011 <a target="_blank" href="http://project-osrm.org/">Project OSRM</a> - Map data <a target="_blank" href="http://creativecommons.org/licenses/by-sa/2.0/">CC-By-SA</a> <a target="_blank" href="http://www.openstreetmap.org">by OpenStreetMap</a></div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,141 +0,0 @@
|
||||
/**
|
||||
* Namespace: Util.OSM
|
||||
*/
|
||||
OpenLayers.Util.OSM = {};
|
||||
|
||||
/**
|
||||
* Constant: MISSING_TILE_URL
|
||||
* {String} URL of image to display for missing tiles
|
||||
*/
|
||||
OpenLayers.Util.OSM.MISSING_TILE_URL = "http://www.openstreetmap.org/openlayers/img/404.png";
|
||||
|
||||
/**
|
||||
* Property: originalOnImageLoadError
|
||||
* {Function} Original onImageLoadError function.
|
||||
*/
|
||||
OpenLayers.Util.OSM.originalOnImageLoadError = OpenLayers.Util.onImageLoadError;
|
||||
|
||||
/**
|
||||
* Function: onImageLoadError
|
||||
*/
|
||||
OpenLayers.Util.onImageLoadError = function() {
|
||||
if (this.src.match(/^http:\/\/[abc]\.[a-z]+\.openstreetmap\.org\//)) {
|
||||
this.src = OpenLayers.Util.OSM.MISSING_TILE_URL;
|
||||
} else if (this.src.match(/^http:\/\/[def]\.tah\.openstreetmap\.org\//)) {
|
||||
// do nothing - this layer is transparent
|
||||
} else {
|
||||
OpenLayers.Util.OSM.originalOnImageLoadError;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Class: OpenLayers.Layer.OSM.Mapnik
|
||||
*
|
||||
* Inherits from:
|
||||
* - <OpenLayers.Layer.OSM>
|
||||
*/
|
||||
OpenLayers.Layer.OSM.Mapnik = OpenLayers.Class(OpenLayers.Layer.OSM, {
|
||||
/**
|
||||
* Constructor: OpenLayers.Layer.OSM.Mapnik
|
||||
*
|
||||
* Parameters:
|
||||
* name - {String}
|
||||
* options - {Object} Hashtable of extra options to tag onto the layer
|
||||
*/
|
||||
initialize: function(name, options) {
|
||||
var url = [
|
||||
"http://a.tile.openstreetmap.org/${z}/${x}/${y}.png",
|
||||
"http://b.tile.openstreetmap.org/${z}/${x}/${y}.png",
|
||||
"http://c.tile.openstreetmap.org/${z}/${x}/${y}.png"
|
||||
];
|
||||
options = OpenLayers.Util.extend({ numZoomLevels: 19, buffer: 0 }, options);
|
||||
var newArguments = [name, url, options];
|
||||
OpenLayers.Layer.OSM.prototype.initialize.apply(this, newArguments);
|
||||
},
|
||||
|
||||
CLASS_NAME: "OpenLayers.Layer.OSM.Mapnik"
|
||||
});
|
||||
|
||||
/**
|
||||
* Class: OpenLayers.Layer.OSM.Osmarender
|
||||
*
|
||||
* Inherits from:
|
||||
* - <OpenLayers.Layer.OSM>
|
||||
*/
|
||||
OpenLayers.Layer.OSM.Osmarender = OpenLayers.Class(OpenLayers.Layer.OSM, {
|
||||
/**
|
||||
* Constructor: OpenLayers.Layer.OSM.Osmarender
|
||||
*
|
||||
* Parameters:
|
||||
* name - {String}
|
||||
* options - {Object} Hashtable of extra options to tag onto the layer
|
||||
*/
|
||||
initialize: function(name, options) {
|
||||
var url = [
|
||||
"http://a.tah.openstreetmap.org/Tiles/tile/${z}/${x}/${y}.png",
|
||||
"http://b.tah.openstreetmap.org/Tiles/tile/${z}/${x}/${y}.png",
|
||||
"http://c.tah.openstreetmap.org/Tiles/tile/${z}/${x}/${y}.png"
|
||||
];
|
||||
options = OpenLayers.Util.extend({ numZoomLevels: 18, buffer: 0 }, options);
|
||||
var newArguments = [name, url, options];
|
||||
OpenLayers.Layer.OSM.prototype.initialize.apply(this, newArguments);
|
||||
},
|
||||
|
||||
CLASS_NAME: "OpenLayers.Layer.OSM.Osmarender"
|
||||
});
|
||||
|
||||
/**
|
||||
* Class: OpenLayers.Layer.OSM.CycleMap
|
||||
*
|
||||
* Inherits from:
|
||||
* - <OpenLayers.Layer.OSM>
|
||||
*/
|
||||
OpenLayers.Layer.OSM.CycleMap = OpenLayers.Class(OpenLayers.Layer.OSM, {
|
||||
/**
|
||||
* Constructor: OpenLayers.Layer.OSM.CycleMap
|
||||
*
|
||||
* Parameters:
|
||||
* name - {String}
|
||||
* options - {Object} Hashtable of extra options to tag onto the layer
|
||||
*/
|
||||
initialize: function(name, options) {
|
||||
var url = [
|
||||
"http://a.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
|
||||
"http://b.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png",
|
||||
"http://c.tile.opencyclemap.org/cycle/${z}/${x}/${y}.png"
|
||||
];
|
||||
options = OpenLayers.Util.extend({ numZoomLevels: 19, buffer: 0 }, options);
|
||||
var newArguments = [name, url, options];
|
||||
OpenLayers.Layer.OSM.prototype.initialize.apply(this, newArguments);
|
||||
},
|
||||
|
||||
CLASS_NAME: "OpenLayers.Layer.OSM.CycleMap"
|
||||
});
|
||||
|
||||
/**
|
||||
* Class: OpenLayers.Layer.OSM.MapQuest
|
||||
*
|
||||
* Inherits from:
|
||||
* - <OpenLayers.Layer.OSM>
|
||||
*/
|
||||
OpenLayers.Layer.OSM.MapQuest = OpenLayers.Class(OpenLayers.Layer.OSM, {
|
||||
/**
|
||||
* Constructor: OpenLayers.Layer.OSM.MapQuest
|
||||
*
|
||||
* Parameters:
|
||||
* name - {String}
|
||||
* options - {Object} Hashtable of extra options to tag onto the layer
|
||||
*/
|
||||
initialize: function(name, options) {
|
||||
var url = [
|
||||
"http://otile1.mqcdn.com/tiles/1.0.0/osm/${z}/${x}/${y}.png",
|
||||
"http://otile2.mqcdn.com/tiles/1.0.0/osm/${z}/${x}/${y}.png",
|
||||
"http://otile3.mqcdn.com/tiles/1.0.0/osm/${z}/${x}/${y}.png"
|
||||
];
|
||||
options = OpenLayers.Util.extend({ numZoomLevels: 19, buffer: 0 }, options);
|
||||
var newArguments = [name, url, options];
|
||||
OpenLayers.Layer.OSM.prototype.initialize.apply(this, newArguments);
|
||||
},
|
||||
|
||||
CLASS_NAME: "OpenLayers.Layer.OSM.MapQuest"
|
||||
});
|
||||
@@ -1 +0,0 @@
|
||||
This is the OpenLayers Version 2.10
|
||||