Compare commits
456 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| caf908669f | |||
| 39c10e2b6a | |||
| 295164302a | |||
| 9a65d480a8 | |||
| b5a1bf6a84 | |||
| d72c48500b | |||
| 8b2be2639e | |||
| 7295811965 | |||
| a3000019db | |||
| 44235d427d | |||
| 18b4f44605 | |||
| 17a5b7a363 | |||
| 8d008f9dcc | |||
| d32734af0b | |||
| d68f72ec6d | |||
| 37f80e027d | |||
| 7585dec359 | |||
| b98a67223b | |||
| 7c17344a42 | |||
| 3279ce3ea9 | |||
| 7e52e2c691 | |||
| 4e75bd1de6 | |||
| b5d2e56faf | |||
| fb1857f7cc | |||
| 885d45e9b8 | |||
| 070050a48e | |||
| 8c0db16b13 | |||
| 99641bd55c | |||
| 14c999fc82 | |||
| 95bcfa3dce | |||
| 5e345fa7b0 | |||
| 8578a1c101 | |||
| 4100b05fd9 | |||
| e7439e92ed | |||
| d874b51419 | |||
| cebef471a5 | |||
| be9b21edc4 | |||
| 37e07025f7 | |||
| f66c1e4a4f | |||
| 9a6cf7b991 | |||
| caf8cd701e | |||
| eca2c0c5ef | |||
| 11653ef58f | |||
| d05638f64f | |||
| d80586e405 | |||
| a62e757099 | |||
| 274189ee7e | |||
| 18c7aa7f89 | |||
| 735b4e2db1 | |||
| f7326ca7f1 | |||
| 647f054714 | |||
| b6ddccc793 | |||
| 1e54b3e69a | |||
| 8f3de7d022 | |||
| d1436cce38 | |||
| 97afa231ca | |||
| 34e4ead885 | |||
| afaca23f12 | |||
| c6f6a7baed | |||
| 25ac07f8a6 | |||
| 1bac501cbd | |||
| f8135c56b7 | |||
| eb9630251f | |||
| 08dd04a0d6 | |||
| a5c4d2102c | |||
| 3fcabb642e | |||
| 63e8793c7f | |||
| 2a6275cf38 | |||
| 43438dbfab | |||
| eae9e95c66 | |||
| 77c6a06c15 | |||
| 3f49351d38 | |||
| c1e0387f3b | |||
| 52430b0180 | |||
| cc737e54d4 | |||
| a9f9857ab8 | |||
| d7f82db8bd | |||
| 4e2be10574 | |||
| 4e3f975221 | |||
| 5274af7730 | |||
| 1fb34be11e | |||
| 5e1f621c50 | |||
| 9091c8702d | |||
| a2389d741e | |||
| 7541a614b0 | |||
| 2f2b1f874a | |||
| c3125c2e5e | |||
| 3a8ed01b8f | |||
| 35a2dad993 | |||
| 9f04305c12 | |||
| a69d378b84 | |||
| a9ce528744 | |||
| aae7027ac6 | |||
| 0745341d49 | |||
| 68ecc270f7 | |||
| 5132c84dfa | |||
| 0ec648217a | |||
| e603a41fb6 | |||
| 89e4d4ac30 | |||
| 789cb99292 | |||
| 0f440610b6 | |||
| cf8de234be | |||
| cd03ad9207 | |||
| b1f2f2786c | |||
| 1db1b4e5ae | |||
| 3ec4487297 | |||
| 88bd5aefe0 | |||
| a73d79b138 | |||
| cd9a01bdf0 | |||
| 2f65370d19 | |||
| 54de6d4afb | |||
| 858adeb9db | |||
| 109279d0b5 | |||
| c4fb8f74ac | |||
| 528d0a2899 | |||
| faba474def | |||
| 7dc17ae693 | |||
| 482edd0b02 | |||
| d1b723e5e1 | |||
| 89c9ca183f | |||
| 54d8a362fc | |||
| 0c4c1ce878 | |||
| 816657347f | |||
| 8533b5d0a2 | |||
| 2523f845d2 | |||
| 30cffd2563 | |||
| bcb39b9e69 | |||
| a5c94c4630 | |||
| b5813ae18b | |||
| 4919f72060 | |||
| c8b0f96251 | |||
| 3bcd262099 | |||
| ff3de43ec6 | |||
| 79083e7585 | |||
| 29fb07446a | |||
| a816630f2f | |||
| 24ca42c017 | |||
| 39fe92ebc4 | |||
| 905b0331b8 | |||
| 88a8d9043a | |||
| d05e097a78 | |||
| 2d5d1f4a29 | |||
| 85c1a74c1b | |||
| f7e81a8d22 | |||
| 61bc95dc9a | |||
| 53e9d70d3d | |||
| 11dbf03467 | |||
| 4f23dfef64 | |||
| 83fca53d04 | |||
| e93735903e | |||
| ddc7e8b8c7 | |||
| 5377e82ca0 | |||
| fdb46f5445 | |||
| a76cf5cad4 | |||
| 96f5c1c735 | |||
| 307c9ae9c5 | |||
| 83b5774d1a | |||
| ae81a8d118 | |||
| 77ae0d8ef4 | |||
| 2e8744a034 | |||
| f29f6c65a5 | |||
| 4bbf53ce62 | |||
| 9878372866 | |||
| 302255807c | |||
| c60c3fcd3c | |||
| ccf40b53eb | |||
| e3d97ec41b | |||
| cdad977341 | |||
| 717d25cad0 | |||
| 9ffe6ccb6a | |||
| af98879303 | |||
| 57809439a8 | |||
| 44e03627c3 | |||
| 13f8c973f4 | |||
| 00e0d46e92 | |||
| 7fee51081f | |||
| 6b99e44177 | |||
| 87bc397ee0 | |||
| ac3ccf1d7a | |||
| a8ad4695aa | |||
| dae6d1c730 | |||
| 132c25fd3b | |||
| 943a0203f2 | |||
| 6e8a1e0e31 | |||
| 108e1ad5e6 | |||
| a79231c666 | |||
| 97bd93f9bc | |||
| a37528362d | |||
| 4883c6c197 | |||
| 5ef8afe7c4 | |||
| 229cddb5c6 | |||
| bb9cb9a1ef | |||
| eca141eebc | |||
| e7a4070339 | |||
| 9551eff4fb | |||
| 4639187180 | |||
| 5b621d8fe9 | |||
| e3b648ea72 | |||
| 676333ab5a | |||
| bd7aa1cc7b | |||
| 88cfb538bd | |||
| b844634a85 | |||
| c93e948bc8 | |||
| dafb0f7e37 | |||
| d531a1f255 | |||
| 39d6527ea4 | |||
| ffb21d613e | |||
| 46afc9fb08 | |||
| f2a3041ce7 | |||
| d761d0fa9d | |||
| 5f6cac9155 | |||
| 860e73a633 | |||
| 9ad7d9db8f | |||
| fba77ff459 | |||
| 8d5c42d7f8 | |||
| 408dc7358b | |||
| ff5ac3a109 | |||
| 8f637b95ef | |||
| fadaf7ec5c | |||
| 8b8e0eb9ef | |||
| 3bfb274d24 | |||
| 0d3e8914ae | |||
| 5cebe65f38 | |||
| 023c655c6e | |||
| 8bae431503 | |||
| 87eab2b242 | |||
| 86ec00867a | |||
| 2df5d58027 | |||
| 5f327f450d | |||
| 10bd19f4e1 | |||
| 4962678776 | |||
| 82ef4170cb | |||
| fca4c19406 | |||
| bf7bab8ea3 | |||
| ce73bd7501 | |||
| 0239365964 | |||
| 1ff580d9eb | |||
| db9b65d7ed | |||
| c0f33222ea | |||
| 0328057a9d | |||
| d531c5beb5 | |||
| 243fc2664a | |||
| 61b8b320e3 | |||
| 689b447990 | |||
| 1cbf2ab0d7 | |||
| 5c0ed9229d | |||
| 0a29df80d0 | |||
| d2b6ac722b | |||
| 2784e273bc | |||
| 0332b11793 | |||
| 9de3a5a586 | |||
| 5642dc00e1 | |||
| 75f80029f6 | |||
| a93738dd79 | |||
| 4f78634994 | |||
| 1f7ddc865f | |||
| 2c6e68dda7 | |||
| d29a27cca7 | |||
| 90bd34d8ac | |||
| fb2a414839 | |||
| 68c210d184 | |||
| 4e01f75be7 | |||
| 9514c4b811 | |||
| 51d0b94e90 | |||
| 66a3da0694 | |||
| b5562485ab | |||
| c62e1ac5fe | |||
| df6e229cfc | |||
| 92b467ae77 | |||
| 8502b62c77 | |||
| 6b91ae4585 | |||
| 43c2e3a291 | |||
| 08854d9f6b | |||
| 571e6be9ce | |||
| b5aa1554c0 | |||
| 68b02a2348 | |||
| 08661558f2 | |||
| ed608f9f13 | |||
| ac6583906d | |||
| 878e837679 | |||
| 3a421f04ad | |||
| 929793f7a8 | |||
| 5217307132 | |||
| 06ce09d0d7 | |||
| 010627b75a | |||
| 26966f5cdb | |||
| 9be6c1c795 | |||
| 3d44f3eb64 | |||
| b6cf33b353 | |||
| 7437bad834 | |||
| 78df3ae23a | |||
| b5c92f20b4 | |||
| 4fef3195b9 | |||
| 03a53c4fdb | |||
| a60c5f651a | |||
| ddd6d800fc | |||
| 5f711c5e03 | |||
| 417f02d30a | |||
| 654ca2de2b | |||
| 846bb11cc0 | |||
| 29977c4b88 | |||
| 445f7c9a92 | |||
| 0e44126df6 | |||
| 5347b285e5 | |||
| df4ead91b6 | |||
| 73dc97b95d | |||
| 6b6ed949b4 | |||
| cd51481ce1 | |||
| 09ad2ff699 | |||
| df4768132e | |||
| 247dee0966 | |||
| 3db44b3ce1 | |||
| e695e269b1 | |||
| 7562795b4b | |||
| 951d6d9311 | |||
| 7d5cb718cf | |||
| be34eebda7 | |||
| 229812cd95 | |||
| 4c5f8f1ce3 | |||
| f231484cc9 | |||
| 9d1d1a3a54 | |||
| 30369c7701 | |||
| f1f2541e4d | |||
| 2b639a5a14 | |||
| da3789f2ce | |||
| 50373d0a94 | |||
| 72fc514370 | |||
| dae3152c9c | |||
| a2b096b096 | |||
| 6a3fe6edeb | |||
| d2c532e4d5 | |||
| 75ba542c38 | |||
| 221080e281 | |||
| d0547f3d69 | |||
| e48b47f1ec | |||
| 164c9333ef | |||
| e32ba24b6a | |||
| 46a690dfc2 | |||
| e9cfe94dfd | |||
| cf46fd79b0 | |||
| d38adbe7f1 | |||
| 85bc62948b | |||
| f663df87d4 | |||
| edfa70aff0 | |||
| 7584c78c25 | |||
| 1ecca0db74 | |||
| bfd2a8aee2 | |||
| f45af2ba72 | |||
| 17310a9d2b | |||
| e176115273 | |||
| 73ab331506 | |||
| 03772fd85b | |||
| 545162cd4c | |||
| 054bffc0f6 | |||
| ddad5a0523 | |||
| b1ee58b8e8 | |||
| 0f1f99dbf2 | |||
| 5692d43abb | |||
| 512aabc7a4 | |||
| 3b885a492c | |||
| e91058c2eb | |||
| 7e77a1f9cd | |||
| f35d87f685 | |||
| 5637c2f92a | |||
| 94cfb4aad7 | |||
| 676f64b0ef | |||
| 4c47d5b70e | |||
| affaa106d1 | |||
| 76488c58e8 | |||
| 586e8b4c4b | |||
| 2acfe2591f | |||
| 8f06b2fa31 | |||
| c36fc9e49f | |||
| ee56d0051f | |||
| 1de5496e9a | |||
| 61a8b6820f | |||
| 4a9698844e | |||
| 27ff707d54 | |||
| 53aa2f54e5 | |||
| be836dc950 | |||
| 9c13a8a6dd | |||
| 41e5a52509 | |||
| 6cefcf568b | |||
| a07efcc4b7 | |||
| 10ea331909 | |||
| 94c8c61a05 | |||
| 43bbf953c1 | |||
| effee46011 | |||
| 891b068551 | |||
| 32a357c434 | |||
| 6fcc6722c4 | |||
| f9f4fa2972 | |||
| 1ba915cbed | |||
| e79e36bea9 | |||
| cc44fef25d | |||
| 0875c2d557 | |||
| 027c0e46ee | |||
| 34b67d0621 | |||
| b3fded0725 | |||
| dc889cfe80 | |||
| a9b76e8050 | |||
| 897f328b45 | |||
| 8148fad464 | |||
| 95dddcf522 | |||
| d8701a7e41 | |||
| 3bdfd1437b | |||
| 106155a97a | |||
| 59abeccc3e | |||
| 4f883aaeda | |||
| 77df6616a9 | |||
| 171815c9b7 | |||
| b87d6f3c66 | |||
| 52d1c1abfc | |||
| a96f932016 | |||
| 85388b3112 | |||
| 65351959fe | |||
| d07dd71078 | |||
| 5a67a79569 | |||
| 25ecd5072f | |||
| 7c05cf9980 | |||
| 051b710484 | |||
| ae0e6d6516 | |||
| 048c04d0f1 | |||
| 38fd46565e | |||
| 2acad805b3 | |||
| 7f63a84295 | |||
| 1f8235ec98 | |||
| ec0108553f | |||
| 92963ced1f | |||
| 5052302f97 | |||
| a83b790225 | |||
| e8baf75ec2 | |||
| e38d473fdd | |||
| 49a088ac87 | |||
| 51cc0bb209 | |||
| 4ce7739453 | |||
| a02dafb8d7 | |||
| 5c13171e29 | |||
| 681f3c46e7 | |||
| 13a88e712f | |||
| 4177ab6df9 | |||
| 60d472ac56 | |||
| dd6c87e12d | |||
| 27a6bcd7ce | |||
| 94d50c1fdd | |||
| 604024e3e3 | |||
| 4df82206b4 | |||
| 72e314d1c0 | |||
| 61c19405fd | |||
| 4351e8850a | |||
| fa5a042071 | |||
| 18b278fe1d | |||
| eead597c9e | |||
| cea7c28626 | |||
| d4a64d2168 | |||
| a44a309de5 |
@@ -1,2 +1,72 @@
|
||||
/.settings
|
||||
/.project
|
||||
# Compiled source #
|
||||
###################
|
||||
*.com
|
||||
*.class
|
||||
*.dll
|
||||
*.exe
|
||||
*.o
|
||||
*.so
|
||||
|
||||
# Packages #
|
||||
############
|
||||
# it's better to unpack these files and commit the raw source
|
||||
# git has its own built in compression methods
|
||||
*.7z
|
||||
*.dmg
|
||||
*.gz
|
||||
*.iso
|
||||
*.jar
|
||||
*.rar
|
||||
*.tar
|
||||
*.zip
|
||||
|
||||
# Logs and databases #
|
||||
######################
|
||||
*.log
|
||||
*.sql
|
||||
*.sqlite
|
||||
|
||||
# OS generated files #
|
||||
######################
|
||||
.DS_Store?
|
||||
ehthumbs.db
|
||||
Icon?
|
||||
Thumbs.db
|
||||
|
||||
# SCons related files #
|
||||
#######################
|
||||
SconsBuilder*
|
||||
.scon*
|
||||
.build
|
||||
|
||||
# Eclipse related files #
|
||||
#########################
|
||||
.setting*
|
||||
.scb
|
||||
.cproject
|
||||
.project
|
||||
|
||||
# stxxl related files #
|
||||
#######################
|
||||
.stxxl
|
||||
stxxl.log
|
||||
stxxl.errlog
|
||||
|
||||
# compiled protobuffers #
|
||||
#########################
|
||||
DataStructures/pbf-proto/*.pb.h
|
||||
DataStructures/pbf-proto/*.pb.cc
|
||||
|
||||
# External Libs #
|
||||
#################
|
||||
lib/
|
||||
|
||||
# Visual Studio Temp + build Files #
|
||||
####################################
|
||||
vcprojects/*.user
|
||||
vcprojects/*.ncb
|
||||
vcprojects/*.suo
|
||||
vcprojects/Debug/
|
||||
vcprojects/Release/
|
||||
bin/
|
||||
bin-debug/
|
||||
@@ -0,0 +1,10 @@
|
||||
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,126 @@
|
||||
/*
|
||||
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 DOUGLASPEUCKER_H_
|
||||
#define DOUGLASPEUCKER_H_
|
||||
|
||||
#include <cfloat>
|
||||
#include <stack>
|
||||
|
||||
/*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.
|
||||
* 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. };
|
||||
|
||||
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;
|
||||
|
||||
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));
|
||||
|
||||
}
|
||||
// point lies in between
|
||||
return (p-x)*(p-x) + (q-y)*(q-y);
|
||||
}
|
||||
|
||||
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) );
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* DOUGLASPEUCKER_H_ */
|
||||
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
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 POLYLINECOMPRESSOR_H_
|
||||
#define POLYLINECOMPRESSOR_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "../DataStructures/ExtractorStructs.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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
numberToEncode += 63;
|
||||
output += (static_cast<char> (numberToEncode));
|
||||
if(92 == numberToEncode)
|
||||
output += (static_cast<char> (numberToEncode));
|
||||
}
|
||||
|
||||
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 += "\"";
|
||||
|
||||
}
|
||||
|
||||
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 += "\"";
|
||||
}
|
||||
|
||||
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 += "]";
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* POLYLINECOMPRESSOR_H_ */
|
||||
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
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
|
||||
@@ -0,0 +1,570 @@
|
||||
/*
|
||||
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 CONTRACTOR_H_INCLUDED
|
||||
#define CONTRACTOR_H_INCLUDED
|
||||
#ifdef _GLIBCXX_PARALLEL
|
||||
#include <parallel/algorithm>
|
||||
#else
|
||||
#include <algorithm>
|
||||
#endif
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "../DataStructures/DynamicGraph.h"
|
||||
#include "../DataStructures/Percent.h"
|
||||
#include "../DataStructures/BinaryHeap.h"
|
||||
#include <ctime>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <stack>
|
||||
#include <limits>
|
||||
#ifdef _OPEMP
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
class Contractor {
|
||||
|
||||
private:
|
||||
|
||||
struct _EdgeBasedContractorEdgeData {
|
||||
unsigned distance;
|
||||
unsigned originalEdges;
|
||||
unsigned via;
|
||||
unsigned nameID;
|
||||
bool shortcut;
|
||||
bool forward;
|
||||
bool backward;
|
||||
short turnInstruction;
|
||||
} data;
|
||||
|
||||
struct _HeapData {
|
||||
bool target;
|
||||
_HeapData() : target(false) {}
|
||||
_HeapData( bool t ) : target(t) {}
|
||||
};
|
||||
|
||||
typedef DynamicGraph< _EdgeBasedContractorEdgeData > _DynamicGraph;
|
||||
typedef BinaryHeap< NodeID, NodeID, int, _HeapData> _Heap;
|
||||
typedef _DynamicGraph::InputEdge _ImportEdge;
|
||||
|
||||
struct _ThreadData {
|
||||
_Heap heap;
|
||||
std::vector< _ImportEdge > insertedEdges;
|
||||
std::vector< NodeID > neighbours;
|
||||
_ThreadData( NodeID nodes ): heap( nodes ) {
|
||||
}
|
||||
};
|
||||
|
||||
struct _PriorityData {
|
||||
int depth;
|
||||
NodeID bias;
|
||||
_PriorityData() : depth(0), bias(0) { }
|
||||
};
|
||||
|
||||
struct _ContractionInformation {
|
||||
int edgesDeleted;
|
||||
int edgesAdded;
|
||||
int originalEdgesDeleted;
|
||||
int originalEdgesAdded;
|
||||
_ContractionInformation() {
|
||||
edgesAdded = edgesDeleted = originalEdgesAdded = originalEdgesDeleted = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct _NodePartitionor {
|
||||
bool operator()( std::pair< NodeID, bool > nodeData ) {
|
||||
return !nodeData.second;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
template< class InputEdge >
|
||||
Contractor( int nodes, std::vector< InputEdge >& inputEdges, unsigned eqf = 2, unsigned oqf = 1, unsigned df = 1) : edgeQuotionFactor(eqf), originalQuotientFactor(oqf), depthFactor(df) {
|
||||
|
||||
std::vector< _ImportEdge > edges;
|
||||
edges.reserve( 2 * inputEdges.size() );
|
||||
for ( typename std::vector< InputEdge >::const_iterator i = inputEdges.begin(), e = inputEdges.end(); i != e; ++i ) {
|
||||
_ImportEdge edge;
|
||||
edge.source = i->source();
|
||||
edge.target = i->target();
|
||||
|
||||
edge.data.distance = (std::max)((int)i->weight(), 1 );
|
||||
assert( edge.data.distance > 0 );
|
||||
#ifndef NDEBUG
|
||||
if ( edge.data.distance > 24 * 60 * 60 * 10 ) {
|
||||
std::cout << "Edge Weight too large -> May lead to invalid CH" << std::endl;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
edge.data.shortcut = false;
|
||||
edge.data.nameID = i->getNameIDOfTurnTarget();
|
||||
edge.data.via = i->via();
|
||||
edge.data.turnInstruction = i->turnInstruction();
|
||||
edge.data.forward = i->isForward();
|
||||
edge.data.backward = i->isBackward();
|
||||
edge.data.originalEdges = 1;
|
||||
edges.push_back( edge );
|
||||
std::swap( edge.source, edge.target );
|
||||
edge.data.forward = i->isBackward();
|
||||
edge.data.backward = i->isForward();
|
||||
edges.push_back( edge );
|
||||
}
|
||||
//remove data from memory
|
||||
inputEdges.clear();
|
||||
std::vector< InputEdge >().swap( inputEdges );
|
||||
|
||||
#ifdef _GLIBCXX_PARALLEL
|
||||
__gnu_parallel::sort( edges.begin(), edges.end() );
|
||||
#else
|
||||
sort( edges.begin(), edges.end() );
|
||||
#endif
|
||||
NodeID edge = 0;
|
||||
for ( NodeID i = 0; i < edges.size(); ) {
|
||||
const NodeID source = edges[i].source;
|
||||
const NodeID target = edges[i].target;
|
||||
const NodeID via = edges[i].data.via;
|
||||
const short turnType = edges[i].data.turnInstruction;
|
||||
//remove eigenloops
|
||||
if ( source == target ) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
_ImportEdge forwardEdge;
|
||||
_ImportEdge backwardEdge;
|
||||
forwardEdge.source = backwardEdge.source = source;
|
||||
forwardEdge.target = backwardEdge.target = target;
|
||||
forwardEdge.data.forward = backwardEdge.data.backward = true;
|
||||
forwardEdge.data.backward = backwardEdge.data.forward = false;
|
||||
forwardEdge.data.turnInstruction = backwardEdge.data.turnInstruction = turnType;
|
||||
forwardEdge.data.nameID = backwardEdge.data.nameID = edges[i].data.nameID;
|
||||
forwardEdge.data.shortcut = backwardEdge.data.shortcut = false;
|
||||
forwardEdge.data.via = backwardEdge.data.via = via;
|
||||
forwardEdge.data.originalEdges = backwardEdge.data.originalEdges = 1;
|
||||
forwardEdge.data.distance = backwardEdge.data.distance = std::numeric_limits< int >::max();
|
||||
//remove parallel edges
|
||||
while ( i < edges.size() && edges[i].source == source && edges[i].target == target ) {
|
||||
if ( edges[i].data.forward )
|
||||
forwardEdge.data.distance = std::min( edges[i].data.distance, forwardEdge.data.distance );
|
||||
if ( edges[i].data.backward )
|
||||
backwardEdge.data.distance = std::min( edges[i].data.distance, backwardEdge.data.distance );
|
||||
i++;
|
||||
}
|
||||
//merge edges (s,t) and (t,s) into bidirectional edge
|
||||
if ( forwardEdge.data.distance == backwardEdge.data.distance ) {
|
||||
if ( (int)forwardEdge.data.distance != std::numeric_limits< int >::max() ) {
|
||||
forwardEdge.data.backward = true;
|
||||
edges[edge++] = forwardEdge;
|
||||
}
|
||||
} else { //insert seperate edges
|
||||
if ( ((int)forwardEdge.data.distance) != std::numeric_limits< int >::max() ) {
|
||||
edges[edge++] = forwardEdge;
|
||||
}
|
||||
if ( (int)backwardEdge.data.distance != std::numeric_limits< int >::max() ) {
|
||||
edges[edge++] = backwardEdge;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cout << "ok" << std::endl << "merged " << edges.size() - edge << " edges out of " << edges.size() << std::endl;
|
||||
edges.resize( edge );
|
||||
_graph.reset( new _DynamicGraph( nodes, edges ) );
|
||||
std::vector< _ImportEdge >().swap( edges );
|
||||
}
|
||||
|
||||
~Contractor() { }
|
||||
|
||||
void Run() {
|
||||
const NodeID numberOfNodes = _graph->GetNumberOfNodes();
|
||||
Percent p (numberOfNodes);
|
||||
|
||||
unsigned maxThreads = omp_get_max_threads();
|
||||
std::vector < _ThreadData* > threadData;
|
||||
for ( unsigned threadNum = 0; threadNum < maxThreads; ++threadNum ) {
|
||||
threadData.push_back( new _ThreadData( numberOfNodes ) );
|
||||
}
|
||||
std::cout << "Contractor is using " << maxThreads << " threads" << std::endl;
|
||||
|
||||
NodeID levelID = 0;
|
||||
std::vector< std::pair< NodeID, bool > > remainingNodes( numberOfNodes );
|
||||
std::vector< double > nodePriority( numberOfNodes );
|
||||
std::vector< _PriorityData > nodeData( numberOfNodes );
|
||||
|
||||
//initialize the variables
|
||||
#pragma omp parallel for schedule ( guided )
|
||||
for ( int x = 0; x < ( int ) numberOfNodes; ++x )
|
||||
remainingNodes[x].first = x;
|
||||
std::random_shuffle( remainingNodes.begin(), remainingNodes.end() );
|
||||
for ( int x = 0; x < ( int ) numberOfNodes; ++x )
|
||||
nodeData[remainingNodes[x].first].bias = x;
|
||||
|
||||
std::cout << "initializing elimination PQ ..." << std::flush;
|
||||
#pragma omp parallel
|
||||
{
|
||||
_ThreadData* data = threadData[omp_get_thread_num()];
|
||||
#pragma omp parallel for schedule ( guided )
|
||||
for ( int x = 0; x < ( int ) numberOfNodes; ++x ) {
|
||||
nodePriority[x] = _Evaluate( data, &nodeData[x], x );
|
||||
}
|
||||
}
|
||||
std::cout << "ok" << std::endl << "preprocessing ..." << std::flush;
|
||||
|
||||
while ( levelID < numberOfNodes ) {
|
||||
const int last = ( int ) remainingNodes.size();
|
||||
#pragma omp parallel
|
||||
{
|
||||
//determine independent node set
|
||||
_ThreadData* const data = threadData[omp_get_thread_num()];
|
||||
#pragma omp for schedule ( guided )
|
||||
for ( int i = 0; i < last; ++i ) {
|
||||
const NodeID node = remainingNodes[i].first;
|
||||
remainingNodes[i].second = _IsIndependent( nodePriority, nodeData, data, node );
|
||||
}
|
||||
}
|
||||
_NodePartitionor functor;
|
||||
const std::vector < std::pair < NodeID, bool > >::const_iterator first = stable_partition( remainingNodes.begin(), remainingNodes.end(), functor );
|
||||
const int firstIndependent = first - remainingNodes.begin();
|
||||
//contract independent nodes
|
||||
#pragma omp parallel
|
||||
{
|
||||
_ThreadData* data = threadData[omp_get_thread_num()];
|
||||
#pragma omp for schedule ( guided ) nowait
|
||||
for ( int position = firstIndependent ; position < last; ++position ) {
|
||||
NodeID x = remainingNodes[position].first;
|
||||
_Contract< false > ( data, x );
|
||||
nodePriority[x] = -1;
|
||||
}
|
||||
std::sort( data->insertedEdges.begin(), data->insertedEdges.end() );
|
||||
}
|
||||
#pragma omp parallel
|
||||
{
|
||||
_ThreadData* data = threadData[omp_get_thread_num()];
|
||||
#pragma omp for schedule ( guided ) nowait
|
||||
for ( int position = firstIndependent ; position < last; ++position ) {
|
||||
NodeID x = remainingNodes[position].first;
|
||||
_DeleteIncomingEdges( data, x );
|
||||
}
|
||||
}
|
||||
//insert new edges
|
||||
for ( unsigned threadNum = 0; threadNum < maxThreads; ++threadNum ) {
|
||||
_ThreadData& data = *threadData[threadNum];
|
||||
for ( int i = 0; i < ( int ) data.insertedEdges.size(); ++i ) {
|
||||
const _ImportEdge& edge = data.insertedEdges[i];
|
||||
_graph->InsertEdge( edge.source, edge.target, edge.data );
|
||||
}
|
||||
std::vector< _ImportEdge >().swap( data.insertedEdges );
|
||||
}
|
||||
//update priorities
|
||||
#pragma omp parallel
|
||||
{
|
||||
_ThreadData* data = threadData[omp_get_thread_num()];
|
||||
#pragma omp for schedule ( guided ) nowait
|
||||
for ( int position = firstIndependent ; position < last; ++position ) {
|
||||
NodeID x = remainingNodes[position].first;
|
||||
_UpdateNeighbours( &nodePriority, &nodeData, data, x );
|
||||
}
|
||||
}
|
||||
//remove contracted nodes from the pool
|
||||
levelID += last - firstIndependent;
|
||||
remainingNodes.resize( firstIndependent );
|
||||
std::vector< std::pair< NodeID, bool > >( remainingNodes ).swap( remainingNodes );
|
||||
p.printStatus(levelID);
|
||||
}
|
||||
|
||||
for ( unsigned threadNum = 0; threadNum < maxThreads; threadNum++ ) {
|
||||
delete threadData[threadNum];
|
||||
}
|
||||
}
|
||||
|
||||
template< class Edge >
|
||||
void GetEdges( std::vector< Edge >& edges ) {
|
||||
NodeID numberOfNodes = _graph->GetNumberOfNodes();
|
||||
for ( NodeID node = 0; node < numberOfNodes; ++node ) {
|
||||
for ( _DynamicGraph::EdgeIterator edge = _graph->BeginEdges( node ), endEdges = _graph->EndEdges( node ); edge < endEdges; edge++ ) {
|
||||
const NodeID target = _graph->GetTarget( edge );
|
||||
const _EdgeBasedContractorEdgeData& data = _graph->GetEdgeData( edge );
|
||||
Edge newEdge;
|
||||
newEdge.source = node;
|
||||
newEdge.target = target;
|
||||
newEdge.data.distance = data.distance;
|
||||
newEdge.data.shortcut = data.shortcut;
|
||||
newEdge.data.via = data.via;
|
||||
newEdge.data.nameID1 = data.nameID;
|
||||
newEdge.data.turnInstruction = data.turnInstruction;
|
||||
newEdge.data.forward = data.forward;
|
||||
newEdge.data.backward = data.backward;
|
||||
edges.push_back( newEdge );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void _Dijkstra( const int maxDistance, const unsigned numTargets, const int maxNodes, _ThreadData* data ){
|
||||
|
||||
_Heap& heap = data->heap;
|
||||
|
||||
int nodes = 0;
|
||||
unsigned targetsFound = 0;
|
||||
while ( heap.Size() > 0 ) {
|
||||
const NodeID node = heap.DeleteMin();
|
||||
const int distance = heap.GetKey( node );
|
||||
if ( nodes++ > maxNodes )
|
||||
return;
|
||||
//Destination settled?
|
||||
if ( distance > maxDistance )
|
||||
return;
|
||||
|
||||
if ( heap.GetData( node ).target ) {
|
||||
targetsFound++;
|
||||
if ( targetsFound >= numTargets )
|
||||
return;
|
||||
}
|
||||
|
||||
//iterate over all edges of node
|
||||
for ( _DynamicGraph::EdgeIterator edge = _graph->BeginEdges( node ), endEdges = _graph->EndEdges( node ); edge != endEdges; ++edge ) {
|
||||
const _EdgeBasedContractorEdgeData& data = _graph->GetEdgeData( edge );
|
||||
if ( !data.forward )
|
||||
continue;
|
||||
const NodeID to = _graph->GetTarget( edge );
|
||||
const int toDistance = distance + data.distance;
|
||||
|
||||
//New Node discovered -> Add to Heap + Node Info Storage
|
||||
if ( !heap.WasInserted( to ) )
|
||||
heap.Insert( to, toDistance, _HeapData() );
|
||||
|
||||
//Found a shorter Path -> Update distance
|
||||
else if ( toDistance < heap.GetKey( to ) ) {
|
||||
heap.DecreaseKey( to, toDistance );
|
||||
//heap.GetData( to ).hops = hops + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double _Evaluate( _ThreadData* const data, _PriorityData* const nodeData, NodeID node ){
|
||||
_ContractionInformation stats;
|
||||
|
||||
//perform simulated contraction
|
||||
_Contract< true > ( data, node, &stats );
|
||||
|
||||
// Result will contain the priority
|
||||
if ( stats.edgesDeleted == 0 || stats.originalEdgesDeleted == 0 )
|
||||
return depthFactor * nodeData->depth;
|
||||
return edgeQuotionFactor * ((( double ) stats.edgesAdded ) / stats.edgesDeleted ) + originalQuotientFactor * ((( double ) stats.originalEdgesAdded ) / stats.originalEdgesDeleted ) + depthFactor * nodeData->depth;
|
||||
}
|
||||
|
||||
template< bool Simulate > bool _Contract( _ThreadData* data, NodeID node, _ContractionInformation* stats = NULL ) {
|
||||
_Heap& heap = data->heap;
|
||||
int insertedEdgesSize = data->insertedEdges.size();
|
||||
std::vector< _ImportEdge >& insertedEdges = data->insertedEdges;
|
||||
|
||||
for ( _DynamicGraph::EdgeIterator inEdge = _graph->BeginEdges( node ), endInEdges = _graph->EndEdges( node ); inEdge != endInEdges; ++inEdge ) {
|
||||
const _EdgeBasedContractorEdgeData& inData = _graph->GetEdgeData( inEdge );
|
||||
const NodeID source = _graph->GetTarget( inEdge );
|
||||
if ( Simulate ) {
|
||||
assert( stats != NULL );
|
||||
stats->edgesDeleted++;
|
||||
stats->originalEdgesDeleted += inData.originalEdges;
|
||||
}
|
||||
if ( !inData.backward )
|
||||
continue;
|
||||
|
||||
heap.Clear();
|
||||
heap.Insert( source, 0, _HeapData() );
|
||||
if ( node != source )
|
||||
heap.Insert( node, inData.distance, _HeapData() );
|
||||
int maxDistance = 0;
|
||||
unsigned numTargets = 0;
|
||||
|
||||
for ( _DynamicGraph::EdgeIterator outEdge = _graph->BeginEdges( node ), endOutEdges = _graph->EndEdges( node ); outEdge != endOutEdges; ++outEdge ) {
|
||||
const _EdgeBasedContractorEdgeData& outData = _graph->GetEdgeData( outEdge );
|
||||
if ( !outData.forward )
|
||||
continue;
|
||||
const NodeID target = _graph->GetTarget( outEdge );
|
||||
const int pathDistance = inData.distance + outData.distance;
|
||||
maxDistance = std::max( maxDistance, pathDistance );
|
||||
if ( !heap.WasInserted( target ) ) {
|
||||
heap.Insert( target, pathDistance, _HeapData( true ) );
|
||||
numTargets++;
|
||||
} else if ( pathDistance < heap.GetKey( target ) ) {
|
||||
heap.DecreaseKey( target, pathDistance );
|
||||
}
|
||||
}
|
||||
|
||||
if( Simulate )
|
||||
_Dijkstra( maxDistance, numTargets, 500, data );
|
||||
else
|
||||
_Dijkstra( maxDistance, numTargets, 1000, data );
|
||||
|
||||
for ( _DynamicGraph::EdgeIterator outEdge = _graph->BeginEdges( node ), endOutEdges = _graph->EndEdges( node ); outEdge != endOutEdges; ++outEdge ) {
|
||||
const _EdgeBasedContractorEdgeData& outData = _graph->GetEdgeData( outEdge );
|
||||
if ( !outData.forward )
|
||||
continue;
|
||||
const NodeID target = _graph->GetTarget( outEdge );
|
||||
const int pathDistance = inData.distance + outData.distance;
|
||||
const int distance = heap.GetKey( target );
|
||||
|
||||
if ( pathDistance <= distance ) {
|
||||
if ( Simulate ) {
|
||||
assert( stats != NULL );
|
||||
stats->edgesAdded+=2;
|
||||
stats->originalEdgesAdded += 2* ( outData.originalEdges + inData.originalEdges );
|
||||
} else {
|
||||
_ImportEdge newEdge;
|
||||
newEdge.source = source;
|
||||
newEdge.target = target;
|
||||
newEdge.data.distance = pathDistance;
|
||||
newEdge.data.forward = true;
|
||||
newEdge.data.backward = false;
|
||||
newEdge.data.via = node;
|
||||
newEdge.data.shortcut = true;
|
||||
newEdge.data.turnInstruction = inData.turnInstruction;
|
||||
newEdge.data.originalEdges = outData.originalEdges + inData.originalEdges;
|
||||
insertedEdges.push_back( newEdge );
|
||||
std::swap( newEdge.source, newEdge.target );
|
||||
newEdge.data.forward = false;
|
||||
newEdge.data.backward = true;
|
||||
insertedEdges.push_back( newEdge );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( !Simulate ) {
|
||||
for ( int i = insertedEdgesSize, iend = insertedEdges.size(); i < iend; i++ ) {
|
||||
bool found = false;
|
||||
for ( int other = i + 1 ; other < iend ; ++other ) {
|
||||
if ( insertedEdges[other].source != insertedEdges[i].source )
|
||||
continue;
|
||||
if ( insertedEdges[other].target != insertedEdges[i].target )
|
||||
continue;
|
||||
if ( insertedEdges[other].data.distance != insertedEdges[i].data.distance )
|
||||
continue;
|
||||
if ( insertedEdges[other].data.shortcut != insertedEdges[i].data.shortcut )
|
||||
continue;
|
||||
insertedEdges[other].data.forward |= insertedEdges[i].data.forward;
|
||||
insertedEdges[other].data.backward |= insertedEdges[i].data.backward;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if ( !found )
|
||||
insertedEdges[insertedEdgesSize++] = insertedEdges[i];
|
||||
}
|
||||
insertedEdges.resize( insertedEdgesSize );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _DeleteIncomingEdges( _ThreadData* data, NodeID node ) {
|
||||
std::vector< NodeID >& neighbours = data->neighbours;
|
||||
neighbours.clear();
|
||||
|
||||
//find all neighbours
|
||||
for ( _DynamicGraph::EdgeIterator e = _graph->BeginEdges( node ) ; e < _graph->EndEdges( node ) ; ++e ) {
|
||||
const NodeID u = _graph->GetTarget( e );
|
||||
if ( u == node )
|
||||
continue;
|
||||
neighbours.push_back( u );
|
||||
}
|
||||
//eliminate duplicate entries ( forward + backward edges )
|
||||
std::sort( neighbours.begin(), neighbours.end() );
|
||||
neighbours.resize( std::unique( neighbours.begin(), neighbours.end() ) - neighbours.begin() );
|
||||
|
||||
for ( int i = 0, e = ( int ) neighbours.size(); i < e; ++i ) {
|
||||
const NodeID u = neighbours[i];
|
||||
_graph->DeleteEdgesTo( u, node );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _UpdateNeighbours( std::vector< double >* priorities, std::vector< _PriorityData >* const nodeData, _ThreadData* const data, NodeID node ) {
|
||||
std::vector< NodeID >& neighbours = data->neighbours;
|
||||
neighbours.clear();
|
||||
|
||||
//find all neighbours
|
||||
for ( _DynamicGraph::EdgeIterator e = _graph->BeginEdges( node ) ; e < _graph->EndEdges( node ) ; ++e ) {
|
||||
const NodeID u = _graph->GetTarget( e );
|
||||
if ( u == node )
|
||||
continue;
|
||||
neighbours.push_back( u );
|
||||
( *nodeData )[u].depth = (std::max)(( *nodeData )[node].depth + 1, ( *nodeData )[u].depth );
|
||||
}
|
||||
//eliminate duplicate entries ( forward + backward edges )
|
||||
std::sort( neighbours.begin(), neighbours.end() );
|
||||
neighbours.resize( std::unique( neighbours.begin(), neighbours.end() ) - neighbours.begin() );
|
||||
|
||||
for ( int i = 0, e = ( int ) neighbours.size(); i < e; ++i ) {
|
||||
const NodeID u = neighbours[i];
|
||||
( *priorities )[u] = _Evaluate( data, &( *nodeData )[u], u );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _IsIndependent( const std::vector< double >& priorities, const std::vector< _PriorityData >& nodeData, _ThreadData* const data, NodeID node ) {
|
||||
const double priority = priorities[node];
|
||||
|
||||
std::vector< NodeID >& neighbours = data->neighbours;
|
||||
neighbours.clear();
|
||||
|
||||
for ( _DynamicGraph::EdgeIterator e = _graph->BeginEdges( node ) ; e < _graph->EndEdges( node ) ; ++e ) {
|
||||
const NodeID target = _graph->GetTarget( e );
|
||||
const double targetPriority = priorities[target];
|
||||
assert( targetPriority >= 0 );
|
||||
//found a neighbour with lower priority?
|
||||
if ( priority > targetPriority )
|
||||
return false;
|
||||
//tie breaking
|
||||
if ( priority == targetPriority && nodeData[node].bias < nodeData[target].bias )
|
||||
return false;
|
||||
neighbours.push_back( target );
|
||||
}
|
||||
|
||||
std::sort( neighbours.begin(), neighbours.end() );
|
||||
neighbours.resize( std::unique( neighbours.begin(), neighbours.end() ) - neighbours.begin() );
|
||||
|
||||
//examine all neighbours that are at most 2 hops away
|
||||
for ( std::vector< NodeID >::const_iterator i = neighbours.begin(), lastNode = neighbours.end(); i != lastNode; ++i ) {
|
||||
const NodeID u = *i;
|
||||
|
||||
for ( _DynamicGraph::EdgeIterator e = _graph->BeginEdges( u ) ; e < _graph->EndEdges( u ) ; ++e ) {
|
||||
const NodeID target = _graph->GetTarget( e );
|
||||
|
||||
const double targetPriority = priorities[target];
|
||||
assert( targetPriority >= 0 );
|
||||
//found a neighbour with lower priority?
|
||||
if ( priority > targetPriority )
|
||||
return false;
|
||||
//tie breaking
|
||||
if ( priority == targetPriority && nodeData[node].bias < nodeData[target].bias )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
boost::shared_ptr<_DynamicGraph> _graph;
|
||||
unsigned edgeQuotionFactor;
|
||||
unsigned originalQuotientFactor;
|
||||
unsigned depthFactor;
|
||||
};
|
||||
|
||||
#endif // CONTRACTOR_H_INCLUDED
|
||||
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#ifdef _GLIBCXX_PARALLEL
|
||||
#include <parallel/algorithm>
|
||||
#else
|
||||
#include <algorithm>
|
||||
#endif
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include "EdgeBasedGraphFactory.h"
|
||||
|
||||
template<>
|
||||
EdgeBasedGraphFactory::EdgeBasedGraphFactory(int nodes, std::vector<NodeBasedEdge> & inputEdges, std::vector<_Restriction> & irs, std::vector<NodeInfo> & nI)
|
||||
: inputRestrictions(irs), inputNodeInfoList(nI) {
|
||||
|
||||
#ifdef _GLIBCXX_PARALLEL
|
||||
__gnu_parallel::sort(inputRestrictions.begin(), inputRestrictions.end(), CmpRestrictionByFrom);
|
||||
#else
|
||||
std::sort(inputRestrictions.begin(), inputRestrictions.end(), CmpRestrictionByFrom);
|
||||
#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();
|
||||
|
||||
if(edge.source == edge.target)
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#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.");
|
||||
}
|
||||
|
||||
template<>
|
||||
void EdgeBasedGraphFactory::GetEdgeBasedEdges( std::vector< EdgeBasedEdge >& outputEdgeList ) {
|
||||
|
||||
GUARANTEE(0 == outputEdgeList.size(), "Vector passed to EdgeBasedGraphFactory::GetEdgeBasedEdges(..) is not empty");
|
||||
GUARANTEE(0 != edgeBasedEdges.size(), "No edges in edge based graph");
|
||||
|
||||
edgeBasedEdges.swap(outputEdgeList);
|
||||
}
|
||||
|
||||
void EdgeBasedGraphFactory::GetEdgeBasedNodes( std::vector< EdgeBasedNode> & nodes) {
|
||||
for(unsigned i = 0; i < edgeBasedNodes.size(); ++i)
|
||||
nodes.push_back(edgeBasedNodes[i]);
|
||||
}
|
||||
|
||||
void EdgeBasedGraphFactory::Run() {
|
||||
INFO("Generating Edge based representation of input data");
|
||||
|
||||
std::vector<_Restriction>::iterator restrictionIterator = inputRestrictions.begin();
|
||||
Percent p(_nodeBasedGraph->GetNumberOfNodes());
|
||||
int numberOfResolvedRestrictions(0);
|
||||
int nodeBasedEdgeCounter(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());
|
||||
}
|
||||
|
||||
//incorporate turn costs, this is just a simple model and can (read: must) be extended
|
||||
double angle = GetAngleBetweenTwoEdges(inputNodeInfoList[u], inputNodeInfoList[v], inputNodeInfoList[w]);
|
||||
|
||||
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);
|
||||
|
||||
//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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
_NodeBasedDynamicGraph::EdgeData & data1 = _nodeBasedGraph->GetEdgeData(edge1);
|
||||
_NodeBasedDynamicGraph::EdgeData & data2 = _nodeBasedGraph->GetEdgeData(edge2);
|
||||
|
||||
double angle = GetAngleBetweenTwoEdges(inputNodeInfoList[u], inputNodeInfoList[v], inputNodeInfoList[w]);
|
||||
|
||||
//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;
|
||||
}
|
||||
}
|
||||
//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 roundabout, we skip it.
|
||||
if(data1.nameID == data2.nameID)
|
||||
return TurnInstructions.NoTurn;
|
||||
|
||||
return TurnInstructions.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;
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This class constructs the edge base representation of a graph from a given node based edge list
|
||||
*/
|
||||
|
||||
#ifndef EDGEBASEDGRAPHFACTORY_H_
|
||||
#define EDGEBASEDGRAPHFACTORY_H_
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#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"
|
||||
|
||||
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;
|
||||
|
||||
struct _EdgeBasedEdgeData {
|
||||
int distance;
|
||||
unsigned via;
|
||||
unsigned nameID;
|
||||
bool forward;
|
||||
bool backward;
|
||||
short turnInstruction;
|
||||
};
|
||||
|
||||
typedef DynamicGraph< _NodeBasedEdgeData > _NodeBasedDynamicGraph;
|
||||
typedef _NodeBasedDynamicGraph::InputEdge _NodeBasedEdge;
|
||||
|
||||
public:
|
||||
struct EdgeBasedNode {
|
||||
bool operator<(const EdgeBasedNode & other) const {
|
||||
return other.id < id;
|
||||
}
|
||||
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;
|
||||
|
||||
std::vector<_Restriction> & inputRestrictions;
|
||||
std::vector<NodeInfo> & inputNodeInfoList;
|
||||
|
||||
std::vector<EdgeBasedEdge> edgeBasedEdges;
|
||||
std::vector<EdgeBasedNode> edgeBasedNodes;
|
||||
|
||||
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();
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
#endif /* EDGEBASEDGRAPHFACTORY_H_ */
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
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_ */
|
||||
@@ -0,0 +1,293 @@
|
||||
/*
|
||||
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 BINARYHEAP_H_INCLUDED
|
||||
#define BINARYHEAP_H_INCLUDED
|
||||
|
||||
//Not compatible with non contiguous node ids
|
||||
|
||||
#include <cassert>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <google/dense_hash_map>
|
||||
#include <google/sparse_hash_map>
|
||||
#include <google/sparsetable>
|
||||
|
||||
template< typename NodeID, typename Key >
|
||||
class ArrayStorage {
|
||||
public:
|
||||
|
||||
ArrayStorage( size_t size ) : positions( new Key[size] ) {
|
||||
memset(positions, 0, size*sizeof(Key));
|
||||
}
|
||||
|
||||
~ArrayStorage() {
|
||||
delete[] positions;
|
||||
}
|
||||
|
||||
Key &operator[]( NodeID node ) {
|
||||
return positions[node];
|
||||
}
|
||||
|
||||
void Clear() {}
|
||||
|
||||
private:
|
||||
Key* positions;
|
||||
};
|
||||
|
||||
template< typename NodeID, typename Key >
|
||||
class MapStorage {
|
||||
public:
|
||||
|
||||
MapStorage( size_t size = 0 ) {}
|
||||
|
||||
Key &operator[]( NodeID node ) {
|
||||
return nodes[node];
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
nodes.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
std::map< NodeID, Key > nodes;
|
||||
|
||||
};
|
||||
|
||||
template< typename NodeID, typename Key >
|
||||
class DenseStorage {
|
||||
public:
|
||||
|
||||
DenseStorage( size_t size = 0 ) { nodes.set_empty_key(UINT_MAX); }
|
||||
|
||||
Key &operator[]( NodeID node ) {
|
||||
return nodes[node];
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
nodes.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
google::dense_hash_map< NodeID, Key > nodes;
|
||||
};
|
||||
|
||||
template< typename NodeID, typename Key >
|
||||
class SparseStorage {
|
||||
public:
|
||||
|
||||
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:
|
||||
typedef Weight WeightType;
|
||||
typedef Data DataType;
|
||||
|
||||
BinaryHeap( size_t maxID )
|
||||
: nodeIndex( maxID ) {
|
||||
Clear();
|
||||
}
|
||||
|
||||
void Clear() {
|
||||
heap.resize( 1 );
|
||||
insertedNodes.clear();
|
||||
heap[0].weight = std::numeric_limits< Weight >::min();
|
||||
nodeIndex.Clear();
|
||||
}
|
||||
|
||||
Key Size() const {
|
||||
return ( Key )( heap.size() - 1 );
|
||||
}
|
||||
|
||||
void Insert( NodeID node, Weight weight, const Data &data ) {
|
||||
HeapElement element;
|
||||
element.index = ( NodeID ) insertedNodes.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 );
|
||||
CheckHeap();
|
||||
}
|
||||
|
||||
Data& GetData( NodeID node ) {
|
||||
const Key index = nodeIndex[node];
|
||||
return insertedNodes[index].data;
|
||||
}
|
||||
|
||||
Weight& GetKey( NodeID node ) {
|
||||
const Key index = nodeIndex[node];
|
||||
return insertedNodes[index].weight;
|
||||
}
|
||||
|
||||
bool WasRemoved( NodeID node ) {
|
||||
assert( WasInserted( node ) );
|
||||
const Key index = nodeIndex[node];
|
||||
return insertedNodes[index].key == 0;
|
||||
}
|
||||
|
||||
bool WasInserted( NodeID node ) {
|
||||
const Key index = nodeIndex[node];
|
||||
if ( index >= ( Key ) insertedNodes.size() )
|
||||
return false;
|
||||
return insertedNodes[index].node == node;
|
||||
}
|
||||
|
||||
NodeID Min() const {
|
||||
assert( heap.size() > 1 );
|
||||
return insertedNodes[heap[1].index].node;
|
||||
}
|
||||
|
||||
NodeID DeleteMin() {
|
||||
assert( heap.size() > 1 );
|
||||
const Key removedIndex = heap[1].index;
|
||||
heap[1] = heap[heap.size()-1];
|
||||
heap.pop_back();
|
||||
if ( heap.size() > 1 )
|
||||
Downheap( 1 );
|
||||
insertedNodes[removedIndex].key = 0;
|
||||
CheckHeap();
|
||||
return insertedNodes[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 DecreaseKey( NodeID node, Weight weight ) {
|
||||
assert( UINT_MAX != node );
|
||||
const Key index = nodeIndex[node];
|
||||
Key key = insertedNodes[index].key;
|
||||
assert ( key >= 0 );
|
||||
|
||||
insertedNodes[index].weight = weight;
|
||||
heap[key].weight = weight;
|
||||
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 ) {
|
||||
}
|
||||
|
||||
NodeID node;
|
||||
Key key;
|
||||
Weight weight;
|
||||
Data data;
|
||||
};
|
||||
struct HeapElement {
|
||||
Key index;
|
||||
Weight weight;
|
||||
};
|
||||
|
||||
std::vector< HeapNode > insertedNodes;
|
||||
std::vector< HeapElement > heap;
|
||||
IndexStorage nodeIndex;
|
||||
|
||||
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() ) {
|
||||
const Key nextKeyOther = nextKey + 1;
|
||||
if ( ( nextKeyOther < ( Key ) heap.size() )&& ( heap[nextKey].weight > heap[nextKeyOther].weight) )
|
||||
nextKey = nextKeyOther;
|
||||
|
||||
if ( weight <= heap[nextKey].weight )
|
||||
break;
|
||||
|
||||
heap[key] = heap[nextKey];
|
||||
insertedNodes[heap[key].index].key = key;
|
||||
key = nextKey;
|
||||
nextKey <<= 1;
|
||||
}
|
||||
heap[key].index = droppingIndex;
|
||||
heap[key].weight = weight;
|
||||
insertedNodes[droppingIndex].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 );
|
||||
heap[key] = heap[nextKey];
|
||||
insertedNodes[heap[key].index].key = key;
|
||||
key = nextKey;
|
||||
nextKey >>= 1;
|
||||
}
|
||||
heap[key].index = risingIndex;
|
||||
heap[key].weight = weight;
|
||||
insertedNodes[risingIndex].key = key;
|
||||
}
|
||||
|
||||
void CheckHeap() {
|
||||
#ifndef NDEBUG
|
||||
for ( Key i = 2; i < (Key) heap.size(); ++i ) {
|
||||
assert( heap[i].weight >= heap[i >> 1].weight );
|
||||
}
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
#endif //#ifndef BINARYHEAP_H_INCLUDED
|
||||
@@ -0,0 +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.
|
||||
|
||||
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 CONCURRENTQUEUE_H_INCLUDED
|
||||
#define CONCURRENTQUEUE_H_INCLUDED
|
||||
|
||||
#include <queue>
|
||||
#include <boost/signals2/mutex.hpp>
|
||||
|
||||
#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 {
|
||||
|
||||
public:
|
||||
ConcurrentQueue(const size_t max_size)
|
||||
: max_queue_size(max_size) {
|
||||
}
|
||||
|
||||
void push(Data const& data) {
|
||||
if (size_exceeded()) {
|
||||
boost::mutex::scoped_lock qf_lock(queue_full_mutex);
|
||||
queue_full_cv.wait(qf_lock);
|
||||
}
|
||||
|
||||
boost::mutex::scoped_lock lock(queue_mutex);
|
||||
internal_queue.push(data);
|
||||
lock.unlock();
|
||||
queue_cv.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()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
popped_value=internal_queue.front();
|
||||
internal_queue.pop();
|
||||
queue_full_cv.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;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif //#ifndef CONCURRENTQUEUE_H_INCLUDED
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
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 DYNAMICGRAPH_H_INCLUDED
|
||||
#define DYNAMICGRAPH_H_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
|
||||
template< typename EdgeDataT>
|
||||
class DynamicGraph {
|
||||
public:
|
||||
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 )
|
||||
{
|
||||
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;
|
||||
}
|
||||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~DynamicGraph(){ }
|
||||
|
||||
unsigned GetNumberOfNodes() const
|
||||
{
|
||||
return m_numNodes;
|
||||
}
|
||||
|
||||
unsigned GetNumberOfEdges() const
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
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];
|
||||
--m_numEdges;
|
||||
--node.edges;
|
||||
const unsigned last = node.firstEdge + node.edges;
|
||||
//swap with last edge
|
||||
m_edges[e] = m_edges[last];
|
||||
makeDummy( last );
|
||||
}
|
||||
|
||||
//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 );
|
||||
}
|
||||
}
|
||||
|
||||
#pragma omp atomic
|
||||
m_numEdges -= deleted;
|
||||
m_nodes[source].edges -= deleted;
|
||||
|
||||
return deleted;
|
||||
}
|
||||
|
||||
//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 );
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
#endif // DYNAMICGRAPH_H_INCLUDED
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
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_ */
|
||||
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
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_ */
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
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_ */
|
||||
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
Created on: 18.11.2010
|
||||
Author: dennis
|
||||
*/
|
||||
|
||||
#ifndef HASHTABLE_H_
|
||||
#define HASHTABLE_H_
|
||||
|
||||
#include <boost/unordered_map.hpp>
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool Holds(const keyT& key) {
|
||||
if(table.find(key) == table.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_ */
|
||||
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
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 EDGE_H
|
||||
#define EDGE_H
|
||||
|
||||
#include <cassert>
|
||||
|
||||
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());
|
||||
}
|
||||
return (target() < e.target());
|
||||
}
|
||||
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) :
|
||||
_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 name() const { return _name; }
|
||||
EdgeWeight weight() const {return _weight; }
|
||||
|
||||
short type() const { assert(_type >= 0); return _type; }
|
||||
bool isBackward() const { return backward; }
|
||||
bool isForward() const { return forward; }
|
||||
bool isLocatable() const { return _type != 14; }
|
||||
bool isRoundabout() const { return _roundabout; }
|
||||
|
||||
NodeID _source;
|
||||
NodeID _target;
|
||||
NodeID _name;
|
||||
EdgeWeight _weight;
|
||||
bool forward;
|
||||
bool backward;
|
||||
short _type;
|
||||
bool _roundabout;
|
||||
};
|
||||
|
||||
class EdgeBasedEdge {
|
||||
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());
|
||||
}
|
||||
return (target() < e.target());
|
||||
}
|
||||
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) { }
|
||||
|
||||
/** 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.
|
||||
|
||||
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); }
|
||||
|
||||
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; }
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
typedef NodeBasedEdge ImportEdge;
|
||||
|
||||
#endif // EDGE_H
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
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 INPUTREADERFACTORY_H
|
||||
#define INPUTREADERFACTORY_H
|
||||
|
||||
#include <bzlib.h>
|
||||
#include <libxml/xmlreader.h>
|
||||
|
||||
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 read = 0;
|
||||
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) {
|
||||
return read;
|
||||
} 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++) {
|
||||
context->unused[i] = unusedTmp[i];
|
||||
}
|
||||
BZ2_bzReadClose(&context->error, context->bz2);
|
||||
if(BZ_OK != context->error) { cerr << "Could not BZ2_bzReadClose" << endl; exit(-1);};
|
||||
context->error = BZ_STREAM_END; // set to the stream end for next call to this function
|
||||
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); }
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
int closeBz2Stream( void *pointer )
|
||||
{
|
||||
BZ2Context* context = (BZ2Context*) pointer;
|
||||
fclose( context->file );
|
||||
delete context;
|
||||
return 0;
|
||||
}
|
||||
|
||||
xmlTextReaderPtr inputReaderFactory( const char* name )
|
||||
{
|
||||
std::string inputName(name);
|
||||
|
||||
if(inputName.find(".osm.bz2")!=string::npos)
|
||||
{
|
||||
BZ2Context* context = new BZ2Context();
|
||||
context->error = false;
|
||||
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 ) {
|
||||
delete context;
|
||||
return NULL;
|
||||
}
|
||||
return xmlReaderForIO( readFromBz2Stream, closeBz2Stream, (void*) context, NULL, NULL, 0 );
|
||||
} else {
|
||||
return xmlNewTextReaderFilename(name);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // INPUTREADERFACTORY_H
|
||||
@@ -0,0 +1,128 @@
|
||||
#include <climits>
|
||||
#include <google/sparse_hash_map>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
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();
|
||||
};
|
||||
|
||||
inline bool exists( const Key &key ) const {
|
||||
return _index.find( key ) != _index.end();
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,635 @@
|
||||
/*
|
||||
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,68 @@
|
||||
/*
|
||||
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
|
||||
@@ -0,0 +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.
|
||||
|
||||
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,535 @@
|
||||
/*
|
||||
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_ */
|
||||
@@ -0,0 +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.
|
||||
|
||||
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 PERCENT_H
|
||||
#define PERCENT_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);
|
||||
}
|
||||
|
||||
/** 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 (currentValue + 1 == _maxValue)
|
||||
std::cout << " 100%" << std::endl;
|
||||
}
|
||||
|
||||
void printIncrement()
|
||||
{
|
||||
#pragma omp atomic
|
||||
_current_value++;
|
||||
printStatus(_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 << "% ";
|
||||
}
|
||||
else {
|
||||
std::cout << ".";
|
||||
}
|
||||
std::cout.flush();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // PERCENT_H
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
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 PHANTOMNODES_H_
|
||||
#define PHANTOMNODES_H_
|
||||
|
||||
#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;
|
||||
location.Reset();
|
||||
}
|
||||
bool isBidirected() const {
|
||||
return weight2 != INT_MAX;
|
||||
}
|
||||
};
|
||||
|
||||
struct PhantomNodes {
|
||||
PhantomNode startPhantom;
|
||||
PhantomNode targetPhantom;
|
||||
void Reset() {
|
||||
startPhantom.Reset();
|
||||
targetPhantom.Reset();
|
||||
}
|
||||
|
||||
bool PhantomsAreOnSameNodeBasedEdge() const {
|
||||
return (startPhantom.edgeBasedNode == targetPhantom.edgeBasedNode);
|
||||
}
|
||||
|
||||
bool AtLeastOnePhantomNodeIsUINTMAX() const {
|
||||
return !(startPhantom.edgeBasedNode == UINT_MAX || targetPhantom.edgeBasedNode == UINT_MAX);
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
return out;
|
||||
}
|
||||
|
||||
struct NodesOfEdge {
|
||||
NodeID edgeBasedNode;
|
||||
double ratio;
|
||||
_Coordinate projectedPoint;
|
||||
};
|
||||
|
||||
#endif /* PHANTOMNODES_H_ */
|
||||
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
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 SEARCHENGINE_H_
|
||||
#define SEARCHENGINE_H_
|
||||
|
||||
#include <climits>
|
||||
#include <deque>
|
||||
|
||||
#include <boost/thread.hpp>
|
||||
|
||||
#include "BinaryHeap.h"
|
||||
#include "PhantomNodes.h"
|
||||
#include "../Util/StringUtil.h"
|
||||
#include "../typedefs.h"
|
||||
|
||||
struct _HeapData {
|
||||
NodeID parent;
|
||||
_HeapData( NodeID p ) : parent(p) { }
|
||||
};
|
||||
|
||||
typedef boost::thread_specific_ptr<BinaryHeap< NodeID, NodeID, int, _HeapData > > HeapPtr;
|
||||
|
||||
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_ */
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
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 SEGMENTINFORMATION_H_
|
||||
#define SEGMENTINFORMATION_H_
|
||||
|
||||
#include <climits>
|
||||
|
||||
struct SegmentInformation {
|
||||
_Coordinate location;
|
||||
NodeID nameID;
|
||||
unsigned length;
|
||||
unsigned duration;
|
||||
short turnInstruction;
|
||||
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) {}
|
||||
};
|
||||
|
||||
#endif /* SEGMENTINFORMATION_H_ */
|
||||
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
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 STATICGRAPH_H_INCLUDED
|
||||
#define STATICGRAPH_H_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
#ifdef _GLIBCXX_PARALLEL
|
||||
#include <parallel/algorithm>
|
||||
#else
|
||||
#include <algorithm>
|
||||
#endif
|
||||
|
||||
#include "../typedefs.h"
|
||||
#include "ImportEdge.h"
|
||||
|
||||
template< typename EdgeDataT>
|
||||
class StaticGraph {
|
||||
public:
|
||||
typedef NodeID NodeIterator;
|
||||
typedef NodeID EdgeIterator;
|
||||
typedef EdgeDataT EdgeData;
|
||||
class InputEdge {
|
||||
public:
|
||||
EdgeDataT data;
|
||||
NodeIterator source;
|
||||
NodeIterator target;
|
||||
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);
|
||||
EdgeIterator edge = 0;
|
||||
EdgeIterator position = 0;
|
||||
for ( NodeIterator node = 0; node <= _numNodes; ++node ) {
|
||||
EdgeIterator lastEdge = edge;
|
||||
while ( edge < _numEdges && graph[edge].source == node )
|
||||
++edge;
|
||||
_nodes[node].firstEdge = position; //=edge
|
||||
position += edge - lastEdge; //remove
|
||||
}
|
||||
_edges.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);
|
||||
edge++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned GetNumberOfNodes() const {
|
||||
return _numNodes;
|
||||
}
|
||||
|
||||
unsigned GetNumberOfEdges() const {
|
||||
return _numEdges;
|
||||
}
|
||||
|
||||
unsigned GetOutDegree( const NodeIterator &n ) const {
|
||||
return BeginEdges(n)-EndEdges(n) - 1;
|
||||
}
|
||||
|
||||
inline NodeIterator GetTarget( const EdgeIterator &e ) const {
|
||||
return NodeIterator( _edges[e].target );
|
||||
}
|
||||
|
||||
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++ ) {
|
||||
const NodeID target = GetTarget(edge);
|
||||
const EdgeWeight weight = GetEdgeData(edge).distance;
|
||||
if(target == to && weight < smallestWeight) {
|
||||
smallestEdge = edge; smallestWeight = weight;
|
||||
}
|
||||
}
|
||||
return smallestEdge;
|
||||
}
|
||||
|
||||
EdgeIterator FindEdgeInEitherDirection( const NodeIterator &from, const NodeIterator &to ) const {
|
||||
EdgeIterator tmp = FindEdge( from, to );
|
||||
return (UINT_MAX != 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)
|
||||
result = true;
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
#endif // STATICGRAPH_H_INCLUDED
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
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.
|
||||
|
||||
KD Tree coded by Christian Vetter, Monav Project
|
||||
|
||||
*/
|
||||
#ifndef STATICKDTREE_H_INCLUDED
|
||||
#define STATICKDTREE_H_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
#ifdef _GLIBCXX_PARALLEL
|
||||
#include <parallel/algorithm>
|
||||
#else
|
||||
#include <algorithm>
|
||||
#endif
|
||||
#include <stack>
|
||||
#include <limits>
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
T min[k];
|
||||
T max[k];
|
||||
};
|
||||
|
||||
struct NoData {};
|
||||
|
||||
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 ) {
|
||||
double temp = (double)left[i] - (double)right[i];
|
||||
result += temp * temp;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
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] )
|
||||
nearest[dim] = box.min[dim];
|
||||
else if ( point[dim] > box.max[dim] )
|
||||
nearest[dim] = box.max[dim];
|
||||
else
|
||||
nearest[dim] = point[dim];
|
||||
}
|
||||
return operator() ( point, nearest );
|
||||
}
|
||||
};
|
||||
|
||||
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] )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
StaticKDTree( std::vector< InputPoint > * points ){
|
||||
assert( k > 0 );
|
||||
assert ( points->size() > 0 );
|
||||
size = points->size();
|
||||
kdtree = new InputPoint[size];
|
||||
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] )
|
||||
boundingBox.min[dim] = kdtree[i].coordinates[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() ) {
|
||||
Tree tree = s.top();
|
||||
s.pop();
|
||||
|
||||
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 ) );
|
||||
}
|
||||
}
|
||||
|
||||
~StaticKDTree(){
|
||||
delete[] kdtree;
|
||||
}
|
||||
|
||||
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() ) {
|
||||
NNTree tree = s.top();
|
||||
s.pop();
|
||||
|
||||
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 ) {
|
||||
nearestDistance = newDistance;
|
||||
*result = kdtree[i];
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Iterator middle = tree.left + ( tree.right - tree.left ) / 2;
|
||||
|
||||
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 );
|
||||
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 );
|
||||
}
|
||||
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 );
|
||||
}
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
private:
|
||||
typedef unsigned Iterator;
|
||||
struct Tree {
|
||||
Iterator left;
|
||||
Iterator right;
|
||||
unsigned dimension;
|
||||
Tree() {}
|
||||
Tree( Iterator l, Iterator r, unsigned d ): left( l ), right( r ), dimension( d ) {}
|
||||
};
|
||||
struct NNTree {
|
||||
Iterator left;
|
||||
Iterator right;
|
||||
unsigned dimension;
|
||||
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 ) {}
|
||||
};
|
||||
class Less {
|
||||
public:
|
||||
Less( unsigned d ) {
|
||||
dimension = d;
|
||||
assert( dimension < k );
|
||||
}
|
||||
|
||||
bool operator() ( const InputPoint& left, const InputPoint& right ) {
|
||||
assert( dimension < k );
|
||||
return left.coordinates[dimension] < right.coordinates[dimension];
|
||||
}
|
||||
private:
|
||||
unsigned dimension;
|
||||
};
|
||||
|
||||
BoundingBox< k, T > boundingBox;
|
||||
InputPoint* kdtree;
|
||||
Iterator size;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // STATICKDTREE_H_INCLUDED
|
||||
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
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 TURNINSTRUCTIONS_H_
|
||||
#define TURNINSTRUCTIONS_H_
|
||||
|
||||
#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;
|
||||
// }
|
||||
//
|
||||
|
||||
};
|
||||
|
||||
static TurnInstructionsClass TurnInstructions;
|
||||
|
||||
#endif /* TURNINSTRUCTIONS_H_ */
|
||||
@@ -0,0 +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.
|
||||
|
||||
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_ */
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
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,53 @@
|
||||
/** 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
/** 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];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
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 BASE_DESCRIPTOR_H_
|
||||
#define BASE_DESCRIPTOR_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), encodeGeometry(false), z(18) {}
|
||||
bool instructions;
|
||||
bool geometry;
|
||||
bool encodeGeometry;
|
||||
unsigned short z;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
#endif /* BASE_DESCRIPTOR_H_ */
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#include "DescriptionFactory.h"
|
||||
|
||||
DescriptionFactory::DescriptionFactory() { }
|
||||
|
||||
DescriptionFactory::~DescriptionFactory() { }
|
||||
|
||||
double DescriptionFactory::GetAngleBetweenCoordinates() const {
|
||||
return 0.;//GetAngleBetweenTwoEdges(previousCoordinate, currentCoordinate, nextCoordinate);
|
||||
}
|
||||
|
||||
void DescriptionFactory::SetStartSegment(const PhantomNode & _startPhantom) {
|
||||
startPhantom = _startPhantom;
|
||||
AppendSegment(_startPhantom.location, _PathData(0, _startPhantom.nodeBasedEdgeNameID, 10, _startPhantom.weight1));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +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.
|
||||
|
||||
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 DESCRIPTIONFACTORY_H_
|
||||
#define DESCRIPTIONFACTORY_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "../typedefs.h"
|
||||
#include "../Algorithms/DouglasPeucker.h"
|
||||
#include "../Algorithms/PolylineCompressor.h"
|
||||
#include "../DataStructures/ExtractorStructs.h"
|
||||
#include "../DataStructures/SegmentInformation.h"
|
||||
|
||||
/* 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);
|
||||
|
||||
};
|
||||
|
||||
#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 += "]";
|
||||
// }
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
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 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;
|
||||
|
||||
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>";
|
||||
|
||||
for(unsigned segmentIdx = 0; segmentIdx < rawRoute.routeSegments.size(); segmentIdx++) {
|
||||
BOOST_FOREACH(_PathData pathData, rawRoute.routeSegments[segmentIdx]) {
|
||||
sEngine.GetCoordinatesForNodeID(pathData.node, current);
|
||||
|
||||
convertInternalLatLonToString(current.lat, tmp);
|
||||
reply.content += "<rtept lat=\"" + tmp + "\" ";
|
||||
convertInternalLatLonToString(current.lon, tmp);
|
||||
reply.content += "lon=\"" + tmp + "\"></rtept>";
|
||||
}
|
||||
}
|
||||
convertInternalLatLonToString(phantomNodes.targetPhantom.location.lat, tmp);
|
||||
reply.content += "<rtept lat=\"" + tmp + "\" ";
|
||||
convertInternalLatLonToString(phantomNodes.targetPhantom.location.lon, tmp);
|
||||
reply.content += "lon=\"" + tmp + "\"></rtept>";
|
||||
}
|
||||
reply.content += "</rte></gpx>";
|
||||
}
|
||||
};
|
||||
#endif /* GPX_DESCRIPTOR_H_ */
|
||||
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
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_ */
|
||||
@@ -0,0 +1,9 @@
|
||||
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+
|
||||
@@ -0,0 +1,44 @@
|
||||
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.
|
||||
@@ -0,0 +1,6 @@
|
||||
- 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.
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
/*
|
||||
* 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];
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
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();
|
||||
}
|
||||
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
* 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');
|
||||
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
/*
|
||||
* 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 )
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 934 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 831 B |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1,70 @@
|
||||
<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>
|
||||
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* 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"
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
This is the OpenLayers Version 2.10
|
||||
|
After Width: | Height: | Size: 42 B |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 451 B |
|
After Width: | Height: | Size: 451 B |
|
After Width: | Height: | Size: 249 B |
|
After Width: | Height: | Size: 992 B |
|
After Width: | Height: | Size: 831 B |
|
After Width: | Height: | Size: 967 B |
|
After Width: | Height: | Size: 606 B |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 484 B |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 285 B |
|
After Width: | Height: | Size: 481 B |
|
After Width: | Height: | Size: 453 B |
|
After Width: | Height: | Size: 359 B |
|
After Width: | Height: | Size: 489 B |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 463 B |
@@ -0,0 +1,10 @@
|
||||
.olLayerGoogleCopyright {
|
||||
right: 3px;
|
||||
bottom: 2px;
|
||||
left: auto;
|
||||
}
|
||||
.olLayerGooglePoweredBy {
|
||||
left: 2px;
|
||||
bottom: 2px;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
.olControlZoomPanel div {
|
||||
background-image: url(img/zoom-panel-NOALPHA.png);
|
||||
}
|
||||
.olControlPanPanel div {
|
||||
background-image: url(img/pan-panel-NOALPHA.png);
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 42 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.4 KiB |