Initial Import.
This commit is contained in:
parent
a44a309de5
commit
d4a64d2168
7
AUTHORS.TXT
Normal file
7
AUTHORS.TXT
Normal file
@ -0,0 +1,7 @@
|
||||
The following people contributed code to the Open Source Routing Machine:
|
||||
|
||||
Christian Vetter
|
||||
Dennis Luxen
|
||||
|
||||
Note:
|
||||
http server ripped from Boost http example 3
|
253
Contractor/BinaryHeap.h
Normal file
253
Contractor/BinaryHeap.h
Normal file
@ -0,0 +1,253 @@
|
||||
/*
|
||||
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 <vector>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <google/dense_hash_map>
|
||||
|
||||
template< typename NodeID, typename Key >
|
||||
class ArrayStorage {
|
||||
public:
|
||||
|
||||
ArrayStorage( size_t size )
|
||||
: positions( new Key[size] ) {}
|
||||
|
||||
~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 SparseStorage {
|
||||
public:
|
||||
|
||||
SparseStorage( 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, typename Weight, typename Data, typename IndexStorage = ArrayStorage< NodeID, Key > >
|
||||
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 = 0;
|
||||
}
|
||||
|
||||
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 = 0;
|
||||
}
|
||||
|
||||
void DecreaseKey( NodeID node, Weight weight ) {
|
||||
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() ) )
|
||||
if ( 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() {
|
||||
/*for ( Key i = 2; i < heap.size(); ++i ) {
|
||||
assert( heap[i].weight >= heap[i >> 1].weight );
|
||||
}*/
|
||||
}
|
||||
};
|
||||
|
||||
#endif //#ifndef BINARYHEAP_H_INCLUDED
|
310
Contractor/ContractionCleanup.h
Normal file
310
Contractor/ContractionCleanup.h
Normal file
@ -0,0 +1,310 @@
|
||||
/*
|
||||
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
|
||||
#include <sys/time.h>
|
||||
#include "Contractor.h"
|
||||
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
class ContractionCleanup {
|
||||
private:
|
||||
|
||||
struct _HeapData {
|
||||
NodeID parent;
|
||||
_HeapData( NodeID p ) {
|
||||
parent = p;
|
||||
}
|
||||
};
|
||||
typedef BinaryHeap< NodeID, NodeID, int, _HeapData > _Heap;
|
||||
|
||||
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 {
|
||||
int distance : 29;
|
||||
bool shortcut : 1;
|
||||
bool forward : 1;
|
||||
bool backward : 1;
|
||||
NodeID middle;
|
||||
} 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.middle == right.data.middle );
|
||||
}
|
||||
};
|
||||
|
||||
ContractionCleanup( int numNodes, const std::vector< Edge >& edges ) {
|
||||
_graph = edges;
|
||||
_numNodes = numNodes;
|
||||
}
|
||||
|
||||
~ContractionCleanup() {
|
||||
|
||||
}
|
||||
|
||||
void Run() {
|
||||
|
||||
double time = _Timestamp();
|
||||
|
||||
RemoveUselessShortcuts();
|
||||
|
||||
time = _Timestamp() - time;
|
||||
cout << "Postprocessing Time: " << time << " s" << endl;
|
||||
}
|
||||
|
||||
template< class Edge >
|
||||
void GetData( std::vector< Edge >& edges ) {
|
||||
for ( int edge = 0, endEdges = ( int ) _graph.size(); edge != endEdges; ++edge ) {
|
||||
Edge newEdge;
|
||||
newEdge.source = _graph[edge].source;
|
||||
newEdge.target = _graph[edge].target;
|
||||
|
||||
newEdge.data.distance = _graph[edge].data.distance;
|
||||
newEdge.data.shortcut = _graph[edge].data.shortcut;
|
||||
if ( newEdge.data.shortcut )
|
||||
newEdge.data.middle = _graph[edge].data.middle;
|
||||
newEdge.data.forward = _graph[edge].data.forward;
|
||||
newEdge.data.backward = _graph[edge].data.backward;
|
||||
edges.push_back( newEdge );
|
||||
}
|
||||
#ifdef _GLIBCXX_PARALLEL
|
||||
__gnu_parallel::sort( edges.begin(), edges.end() );
|
||||
#else
|
||||
sort( edges.begin(), edges.end() );
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
class AllowForwardEdge {
|
||||
public:
|
||||
bool operator()( const Edge& data ) const {
|
||||
return data.data.forward;
|
||||
}
|
||||
};
|
||||
|
||||
class AllowBackwardEdge {
|
||||
public:
|
||||
bool operator()( const Edge& data ) const {
|
||||
return data.data.backward;
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
_firstEdge.resize( _numNodes + 1 );
|
||||
_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 ( unsigned i = 0; i < ( unsigned ) _graph.size(); i++ ) {
|
||||
for ( unsigned edge = _firstEdge[_graph[i].source]; edge < _firstEdge[_graph[i].source + 1]; ++edge ) {
|
||||
if ( edge == 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;
|
||||
Contractor::Witness temp;
|
||||
temp.source = _graph[i].source;
|
||||
temp.target = _graph[i].target;
|
||||
temp.middle = _graph[i].data.middle;
|
||||
}
|
||||
}
|
||||
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;
|
||||
Contractor::Witness temp;
|
||||
temp.source = _graph[i].target;
|
||||
temp.target = _graph[i].source;
|
||||
temp.middle = _graph[i].data.middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cout << "Removing edges" << endl;
|
||||
int usefull = 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[usefull] = _graph[i];
|
||||
usefull++;
|
||||
}
|
||||
cout << "Removed " << _graph.size() - usefull << " useless shortcuts" << endl;
|
||||
_graph.resize( usefull );
|
||||
}
|
||||
|
||||
template< class EdgeAllowed, class StallEdgeAllowed > void _ComputeStep( _Heap* heapForward, _Heap* heapBackward, const EdgeAllowed& edgeAllowed, const StallEdgeAllowed& stallEdgeAllowed, 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 ( edgeAllowed( _graph[edge] ) ) {
|
||||
//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 = 0;
|
||||
AllowForwardEdge forward;
|
||||
AllowBackwardEdge backward;
|
||||
|
||||
while ( data->_heapForward->Size() + data->_heapBackward->Size() > 0 ) {
|
||||
|
||||
if ( data->_heapForward->Size() > 0 ) {
|
||||
_ComputeStep( data->_heapForward, data->_heapBackward, forward, backward, &middle, &targetDistance );
|
||||
}
|
||||
|
||||
if ( data->_heapBackward->Size() > 0 ) {
|
||||
_ComputeStep( data->_heapBackward, data->_heapForward, backward, forward, &middle, &targetDistance );
|
||||
}
|
||||
}
|
||||
|
||||
if ( targetDistance == std::numeric_limits< int >::max() )
|
||||
return std::numeric_limits< unsigned >::max();
|
||||
|
||||
return targetDistance;
|
||||
}
|
||||
NodeID _numNodes;
|
||||
std::vector< Edge > _graph;
|
||||
std::vector< unsigned > _firstEdge;
|
||||
};
|
||||
|
||||
#endif // CONTRACTIONCLEANUP_H_INCLUDED
|
726
Contractor/Contractor.h
Normal file
726
Contractor/Contractor.h
Normal file
@ -0,0 +1,726 @@
|
||||
/*
|
||||
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 "DynamicGraph.h"
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <stack>
|
||||
#include <limits>
|
||||
#include <omp.h>
|
||||
|
||||
class Contractor {
|
||||
|
||||
public:
|
||||
|
||||
struct Witness {
|
||||
NodeID source;
|
||||
NodeID target;
|
||||
NodeID middle;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
struct _EdgeData {
|
||||
int distance;
|
||||
unsigned originalEdges : 29;
|
||||
bool shortcut : 1;
|
||||
bool forward : 1;
|
||||
bool backward : 1;
|
||||
NodeID middle;
|
||||
} data;
|
||||
|
||||
struct _HeapData {
|
||||
//short hops;
|
||||
//_HeapData() {
|
||||
// hops = 0;
|
||||
//}
|
||||
//_HeapData( int h ) {
|
||||
// hops = h;
|
||||
//}
|
||||
};
|
||||
|
||||
typedef DynamicGraph< _EdgeData > _DynamicGraph;
|
||||
typedef BinaryHeap< NodeID, NodeID, int, _HeapData > _Heap;
|
||||
typedef _DynamicGraph::InputEdge _ImportEdge;
|
||||
|
||||
struct _ThreadData {
|
||||
_Heap heap;
|
||||
std::vector< _ImportEdge > insertedEdges;
|
||||
std::vector< Witness > witnessList;
|
||||
_ThreadData( NodeID nodes ): heap( nodes ) {
|
||||
}
|
||||
};
|
||||
|
||||
struct _PriorityData {
|
||||
int depth;
|
||||
NodeID bias;
|
||||
_PriorityData() {
|
||||
depth = 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;
|
||||
}
|
||||
};
|
||||
|
||||
struct _LogItem {
|
||||
unsigned iteration;
|
||||
NodeID nodes;
|
||||
double contraction;
|
||||
double independent;
|
||||
double inserting;
|
||||
double removing;
|
||||
double updating;
|
||||
|
||||
_LogItem() {
|
||||
iteration = nodes = contraction = independent = inserting = removing = updating = 0;
|
||||
}
|
||||
|
||||
double GetTotalTime() const {
|
||||
return contraction + independent + inserting + removing + updating;
|
||||
}
|
||||
|
||||
void PrintStatistics( int interation ) const {
|
||||
cout << iteration << "\t" << nodes << "\t" << independent << "\t" << contraction << "\t" << inserting << "\t" << removing << "\t" << updating << endl;
|
||||
}
|
||||
};
|
||||
|
||||
class _LogData {
|
||||
public:
|
||||
|
||||
std::vector < _LogItem > iterations;
|
||||
|
||||
unsigned GetNIterations() {
|
||||
return ( unsigned ) iterations.size();
|
||||
}
|
||||
|
||||
_LogItem GetSum() const {
|
||||
_LogItem sum;
|
||||
sum.iteration = ( unsigned ) iterations.size();
|
||||
|
||||
for ( int i = 0, e = ( int ) iterations.size(); i < e; ++i ) {
|
||||
sum.nodes += iterations[i].nodes;
|
||||
sum.contraction += iterations[i].contraction;
|
||||
sum.independent += iterations[i].independent;
|
||||
sum.inserting += iterations[i].inserting;
|
||||
sum.removing += iterations[i].removing;
|
||||
sum.updating += iterations[i].updating;
|
||||
}
|
||||
|
||||
return sum;
|
||||
}
|
||||
|
||||
void PrintHeader() const {
|
||||
cout << "Iteration\tNodes\tIndependent\tContraction\tInserting\tRemoving\tUpdating" << endl;
|
||||
}
|
||||
|
||||
void PrintSummary() const {
|
||||
PrintHeader();
|
||||
GetSum().PrintStatistics(( int ) iterations.size() );
|
||||
}
|
||||
|
||||
void Print() const {
|
||||
PrintHeader();
|
||||
for ( int i = 0, e = ( int ) iterations.size(); i < e; ++i ) {
|
||||
iterations[i].PrintStatistics( i );
|
||||
}
|
||||
}
|
||||
|
||||
void Insert( const _LogItem& data ) {
|
||||
iterations.push_back( data );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
template< class InputEdge >
|
||||
Contractor( int nodes, std::vector< InputEdge >& inputEdges ) {
|
||||
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 );
|
||||
if ( edge.data.distance > 24 * 60 * 60 * 10 ) {
|
||||
cout << "Edge Weight too large -> May lead to invalid CH" << endl;
|
||||
continue;
|
||||
}
|
||||
if ( edge.data.distance <= 0 ) {
|
||||
cout << "Edge Weight too small -> May lead to invalid CH or Crashes"<< endl;
|
||||
continue;
|
||||
}
|
||||
edge.data.shortcut = false;
|
||||
edge.data.middle = 0;
|
||||
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 );
|
||||
}
|
||||
std::vector< InputEdge >().swap( inputEdges ); //free memory
|
||||
#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;
|
||||
//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.middle = backwardEdge.data.middle = 0;
|
||||
forwardEdge.data.shortcut = backwardEdge.data.shortcut = false;
|
||||
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 ( forwardEdge.data.distance != std::numeric_limits< int >::max() ) {
|
||||
forwardEdge.data.backward = true;
|
||||
edges[edge++] = forwardEdge;
|
||||
}
|
||||
} else { //insert seperate edges
|
||||
if ( forwardEdge.data.distance != std::numeric_limits< int >::max() ) {
|
||||
edges[edge++] = forwardEdge;
|
||||
}
|
||||
if ( backwardEdge.data.distance != std::numeric_limits< int >::max() ) {
|
||||
edges[edge++] = backwardEdge;
|
||||
}
|
||||
}
|
||||
}
|
||||
cout << "Removed " << edges.size() - edge << " edges of " << edges.size() << endl;
|
||||
edges.resize( edge );
|
||||
_graph = new _DynamicGraph( nodes, edges );
|
||||
|
||||
std::vector< _ImportEdge >().swap( edges );
|
||||
}
|
||||
|
||||
~Contractor() {
|
||||
delete _graph;
|
||||
}
|
||||
|
||||
template< class InputEdge >
|
||||
void checkForAllOrigEdges(std::vector< InputEdge >& inputEdges)
|
||||
{
|
||||
for(unsigned int i = 0; i < inputEdges.size(); i++)
|
||||
{
|
||||
bool found = false;
|
||||
_DynamicGraph::EdgeIterator eit = _graph->BeginEdges(inputEdges[i].source());
|
||||
for(;eit<_graph->EndEdges(inputEdges[i].source()); eit++)
|
||||
{
|
||||
if(_graph->GetEdgeData(eit).distance = inputEdges[i].weight())
|
||||
found = true;
|
||||
}
|
||||
eit = _graph->BeginEdges(inputEdges[i].target());
|
||||
for(;eit<_graph->EndEdges(inputEdges[i].target()); eit++)
|
||||
{
|
||||
if(_graph->GetEdgeData(eit).distance = inputEdges[i].weight())
|
||||
found = true;
|
||||
}
|
||||
assert(found);
|
||||
}
|
||||
}
|
||||
|
||||
void Run() {
|
||||
const NodeID numberOfNodes = _graph->GetNumberOfNodes();
|
||||
_LogData log;
|
||||
|
||||
int maxThreads = omp_get_max_threads();
|
||||
std::vector < _ThreadData* > threadData;
|
||||
for ( int threadNum = 0; threadNum < maxThreads; ++threadNum ) {
|
||||
threadData.push_back( new _ThreadData( numberOfNodes ) );
|
||||
}
|
||||
cout << numberOfNodes << " nodes, " << _graph->GetNumberOfEdges() << " edges" << endl;
|
||||
cout << "using " << maxThreads << " threads" << endl;
|
||||
|
||||
NodeID levelID = 0;
|
||||
NodeID iteration = 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;
|
||||
|
||||
cout << "Initialise Elimination PQ... " << endl;
|
||||
_LogItem statistics0;
|
||||
statistics0.updating = _Timestamp();
|
||||
#pragma omp parallel
|
||||
{
|
||||
_ThreadData* data = threadData[omp_get_thread_num()];
|
||||
#pragma omp for schedule ( guided )
|
||||
for ( int x = 0; x < ( int ) numberOfNodes; ++x ) {
|
||||
nodePriority[x] = _Evaluate( data, &nodeData[x], x );
|
||||
}
|
||||
}
|
||||
cout << "done" << endl;
|
||||
|
||||
statistics0.updating = _Timestamp() - statistics0.updating;
|
||||
log.Insert( statistics0 );
|
||||
|
||||
log.PrintHeader();
|
||||
statistics0.PrintStatistics( 0 );
|
||||
|
||||
while ( levelID < numberOfNodes ) {
|
||||
_LogItem statistics;
|
||||
statistics.iteration = iteration++;
|
||||
const int last = ( int ) remainingNodes.size();
|
||||
|
||||
//determine independent node set
|
||||
double timeLast = _Timestamp();
|
||||
#pragma omp parallel for schedule ( guided )
|
||||
for ( int i = 0; i < last; ++i ) {
|
||||
const NodeID node = remainingNodes[i].first;
|
||||
remainingNodes[i].second = _IsIndependent( _graph, nodePriority, nodeData, 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();
|
||||
statistics.nodes = last - firstIndependent;
|
||||
statistics.independent += _Timestamp() - timeLast;
|
||||
timeLast = _Timestamp();
|
||||
|
||||
//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() );
|
||||
}
|
||||
statistics.contraction += _Timestamp() - timeLast;
|
||||
timeLast = _Timestamp();
|
||||
|
||||
#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;
|
||||
_DeleteIncommingEdges( data, x );
|
||||
}
|
||||
}
|
||||
statistics.removing += _Timestamp() - timeLast;
|
||||
timeLast = _Timestamp();
|
||||
|
||||
//insert new edges
|
||||
for ( int 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];
|
||||
bool found = false;
|
||||
for ( _DynamicGraph::EdgeIterator e = _graph->BeginEdges( edge.source ) ; e < _graph->EndEdges( edge.source ) ; ++e ) {
|
||||
const NodeID target = _graph->GetTarget( e );
|
||||
if ( target != edge.target )
|
||||
continue;
|
||||
_EdgeData& data = _graph->GetEdgeData( e );
|
||||
if ( data.distance != edge.data.distance )
|
||||
continue;
|
||||
if ( data.shortcut != edge.data.shortcut )
|
||||
continue;
|
||||
if ( data.middle != edge.data.middle )
|
||||
continue;
|
||||
data.forward |= edge.data.forward;
|
||||
data.backward |= edge.data.backward;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if ( !found )
|
||||
_graph->InsertEdge( edge.source, edge.target, edge.data );
|
||||
}
|
||||
std::vector< _ImportEdge >().swap( data.insertedEdges );
|
||||
}
|
||||
statistics.inserting += _Timestamp() - timeLast;
|
||||
timeLast = _Timestamp();
|
||||
|
||||
//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 );
|
||||
}
|
||||
}
|
||||
statistics.updating += _Timestamp() - timeLast;
|
||||
timeLast = _Timestamp();
|
||||
|
||||
//output some statistics
|
||||
statistics.PrintStatistics( iteration + 1 );
|
||||
//LogVerbose( wxT( "Printed" ) );
|
||||
|
||||
//remove contracted nodes from the pool
|
||||
levelID += last - firstIndependent;
|
||||
remainingNodes.resize( firstIndependent );
|
||||
std::vector< std::pair< NodeID, bool > >( remainingNodes ).swap( remainingNodes );
|
||||
log.Insert( statistics );
|
||||
}
|
||||
|
||||
for ( int threadNum = 0; threadNum < maxThreads; threadNum++ ) {
|
||||
// _witnessList.insert( _witnessList.end(), threadData[threadNum]->witnessList.begin(), threadData[threadNum]->witnessList.end() );
|
||||
delete threadData[threadNum];
|
||||
}
|
||||
|
||||
log.PrintSummary();
|
||||
cout << "Total Time: " << log.GetSum().GetTotalTime()<< " s" << endl;
|
||||
cout << "checking sanity of generated data ..." << flush;
|
||||
_CheckCH<_EdgeData>();
|
||||
cout << "ok" << endl;
|
||||
}
|
||||
|
||||
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 _EdgeData& data = _graph->GetEdgeData( edge );
|
||||
Edge newEdge;
|
||||
newEdge.source = node;
|
||||
newEdge.target = target;
|
||||
newEdge.data.distance = data.distance;
|
||||
newEdge.data.shortcut = data.shortcut;
|
||||
newEdge.data.middle = data.middle;
|
||||
newEdge.data.forward = data.forward;
|
||||
newEdge.data.backward = data.backward;
|
||||
edges.push_back( newEdge );
|
||||
}
|
||||
}
|
||||
}
|
||||
private:
|
||||
|
||||
double _Timestamp() {
|
||||
return time(NULL);
|
||||
}
|
||||
|
||||
bool _ConstructCH( _DynamicGraph* _graph );
|
||||
|
||||
void _Dijkstra( NodeID source, const int maxDistance, _ThreadData* data ){
|
||||
|
||||
_Heap& heap = data->heap;
|
||||
|
||||
int nodes = 0;
|
||||
while ( heap.Size() > 0 ) {
|
||||
const NodeID node = heap.DeleteMin();
|
||||
const int distance = heap.GetKey( node );
|
||||
//const int hops = heap.GetData( node ).hops;
|
||||
if ( nodes++ > 1000 )
|
||||
return;
|
||||
//if ( hops >= 5 )
|
||||
// return;
|
||||
//Destination settled?
|
||||
if ( distance > maxDistance )
|
||||
return;
|
||||
|
||||
//iterate over all edges of node
|
||||
for ( _DynamicGraph::EdgeIterator edge = _graph->BeginEdges( node ), endEdges = _graph->EndEdges( node ); edge != endEdges; ++edge ) {
|
||||
const _EdgeData& 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* data, _PriorityData* 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 1 * nodeData->depth;
|
||||
return 2 * ((( double ) stats.edgesAdded ) / stats.edgesDeleted ) + 1 * ((( double ) stats.originalEdgesAdded ) / stats.originalEdgesDeleted ) + 1 * nodeData->depth;
|
||||
}
|
||||
|
||||
template< class Edge >
|
||||
bool _CheckCH()
|
||||
{
|
||||
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 start = node;
|
||||
const NodeID target = _graph->GetTarget( edge );
|
||||
const _EdgeData& data = _graph->GetEdgeData( edge );
|
||||
const NodeID middle = data.middle;
|
||||
assert(start != target);
|
||||
if(data.shortcut)
|
||||
{
|
||||
if(_graph->FindEdge(start, middle) == SPECIAL_EDGEID && _graph->FindEdge(middle, start) == SPECIAL_EDGEID)
|
||||
{
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
if(_graph->FindEdge(middle, target) == SPECIAL_EDGEID && _graph->FindEdge(target, middle) == SPECIAL_EDGEID)
|
||||
{
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
assert(data.middle == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template< bool Simulate > bool _Contract( _ThreadData* data, NodeID node, _ContractionInformation* stats = NULL ) {
|
||||
_Heap& heap = data->heap;
|
||||
//std::vector< Witness >& witnessList = data->witnessList;
|
||||
|
||||
for ( _DynamicGraph::EdgeIterator inEdge = _graph->BeginEdges( node ), endInEdges = _graph->EndEdges( node ); inEdge != endInEdges; ++inEdge ) {
|
||||
const _EdgeData& 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;
|
||||
|
||||
for ( _DynamicGraph::EdgeIterator outEdge = _graph->BeginEdges( node ), endOutEdges = _graph->EndEdges( node ); outEdge != endOutEdges; ++outEdge ) {
|
||||
const _EdgeData& 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() );
|
||||
else if ( pathDistance < heap.GetKey( target ) )
|
||||
heap.DecreaseKey( target, pathDistance );
|
||||
}
|
||||
|
||||
_Dijkstra( source, maxDistance, data );
|
||||
|
||||
for ( _DynamicGraph::EdgeIterator outEdge = _graph->BeginEdges( node ), endOutEdges = _graph->EndEdges( node ); outEdge != endOutEdges; ++outEdge ) {
|
||||
const _EdgeData& 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.middle = node;
|
||||
newEdge.data.shortcut = true;
|
||||
newEdge.data.originalEdges = outData.originalEdges + inData.originalEdges;
|
||||
data->insertedEdges.push_back( newEdge );
|
||||
std::swap( newEdge.source, newEdge.target );
|
||||
newEdge.data.forward = false;
|
||||
newEdge.data.backward = true;
|
||||
data->insertedEdges.push_back( newEdge );
|
||||
}
|
||||
}
|
||||
/*else if ( !Simulate ) {
|
||||
Witness witness;
|
||||
witness.source = source;
|
||||
witness.target = target;
|
||||
witness.middle = node;
|
||||
witnessList.push_back( witness );
|
||||
}*/
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _DeleteIncommingEdges( _ThreadData* data, NodeID node ) {
|
||||
std::vector < NodeID > neighbours;
|
||||
|
||||
//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];
|
||||
//_DynamicGraph::EdgeIterator edge = _graph->FindEdge( u, node );
|
||||
//assert( edge != _graph->EndEdges( u ) );
|
||||
//while ( edge != _graph->EndEdges( u ) ) {
|
||||
// _graph->DeleteEdge( u, edge );
|
||||
// edge = _graph->FindEdge( u, node );
|
||||
//}
|
||||
_graph->DeleteEdgesTo( u, node );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _UpdateNeighbours( std::vector< double >* priorities, std::vector< _PriorityData >* nodeData, _ThreadData* data, NodeID node ) {
|
||||
std::vector < NodeID > neighbours;
|
||||
|
||||
//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 _DynamicGraph* _graph, const std::vector< double >& priorities, const std::vector< _PriorityData >& nodeData, NodeID node ) {
|
||||
const double priority = priorities[node];
|
||||
|
||||
std::vector< NodeID > neighbours;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
_DynamicGraph* _graph;
|
||||
};
|
||||
|
||||
#endif // CONTRACTOR_H_INCLUDED
|
229
Contractor/DynamicGraph.h
Normal file
229
Contractor/DynamicGraph.h
Normal file
@ -0,0 +1,229 @@
|
||||
/*
|
||||
open source routing machine
|
||||
Copyright (C) Dennis Luxen, others 2010
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU AFFERO General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
any later version.
|
||||
|
||||
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 "../typedefs.h"
|
||||
|
||||
// returns the smallest power of two that is at least as large as x
|
||||
static unsigned log2Rounded32( unsigned x ) {
|
||||
const unsigned bitPosition[32] = {
|
||||
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
|
||||
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
|
||||
};
|
||||
|
||||
//round up
|
||||
--x;
|
||||
x |= x >> 1;
|
||||
x |= x >> 2;
|
||||
x |= x >> 4;
|
||||
x |= x >> 8;
|
||||
x |= x >> 16;
|
||||
++x;
|
||||
//x is now a power of 2
|
||||
|
||||
//each power of two is mapped to a unique 5 bit sequence with ( x * 0x077CB531U ) >> 27
|
||||
return bitPosition[( x * 0x077CB531U ) >> 27];
|
||||
}
|
||||
/*
|
||||
static unsigned log2Rounded64( unsigned long long x ) {
|
||||
int upperLog = log2Rounded32( x >> 32 );
|
||||
if ( upperLog > 0 )
|
||||
return upperLog;
|
||||
return log2Rounded32( x );
|
||||
}
|
||||
*/
|
||||
|
||||
template< typename EdgeData>
|
||||
class DynamicGraph {
|
||||
public:
|
||||
typedef NodeID NodeIterator;
|
||||
typedef NodeID EdgeIterator;
|
||||
|
||||
class InputEdge {
|
||||
public:
|
||||
EdgeData data;
|
||||
NodeIterator source;
|
||||
NodeIterator target;
|
||||
bool operator<( const InputEdge& right ) const {
|
||||
if ( source != right.source )
|
||||
return source < right.source;
|
||||
return target < right.target;
|
||||
}
|
||||
};
|
||||
|
||||
DynamicGraph( int nodes, std::vector< InputEdge > &graph ) {
|
||||
|
||||
std::sort( graph.begin(), graph.end() );
|
||||
_numNodes = nodes;
|
||||
_numEdges = ( EdgeIterator ) graph.size();
|
||||
_nodes.resize( _numNodes );
|
||||
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;
|
||||
_nodes[node].edges = edge - lastEdge;
|
||||
_nodes[node].size = 1 << log2Rounded32( edge - lastEdge );
|
||||
position += _nodes[node].size;
|
||||
}
|
||||
_edges.resize( position );
|
||||
edge = 0;
|
||||
for ( NodeIterator node = 0; node < _numNodes; ++node ) {
|
||||
for ( EdgeIterator i = _nodes[node].firstEdge, e = _nodes[node].firstEdge + _nodes[node].edges; 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 _nodes[n].edges;
|
||||
}
|
||||
|
||||
NodeIterator GetTarget( const EdgeIterator &e ) const {
|
||||
return NodeIterator( _edges[e].target );
|
||||
}
|
||||
|
||||
EdgeData &GetEdgeData( const EdgeIterator &e ) {
|
||||
return _edges[e].data;
|
||||
}
|
||||
|
||||
const EdgeData &GetEdgeData( const EdgeIterator &e ) const {
|
||||
return _edges[e].data;
|
||||
}
|
||||
|
||||
EdgeIterator BeginEdges( const NodeIterator &n ) const {
|
||||
//assert( EndEdges( n ) - EdgeIterator( _nodes[n].firstEdge ) <= 100 );
|
||||
return EdgeIterator( _nodes[n].firstEdge );
|
||||
}
|
||||
|
||||
EdgeIterator EndEdges( const NodeIterator &n ) const {
|
||||
return EdgeIterator( _nodes[n].firstEdge + _nodes[n].edges );
|
||||
}
|
||||
|
||||
//adds an edge. Invalidates edge iterators for the source node
|
||||
EdgeIterator InsertEdge( const NodeIterator &from, const NodeIterator &to, const EdgeData &data ) {
|
||||
_StrNode &node = _nodes[from];
|
||||
if ( node.edges + 1 >= node.size ) {
|
||||
node.size *= 2;
|
||||
EdgeIterator newFirstEdge = ( EdgeIterator ) _edges.size();
|
||||
_edges.resize( _edges.size() + node.size );
|
||||
for ( unsigned i = 0; i < node.edges; ++i ) {
|
||||
_edges[newFirstEdge + i ] = _edges[node.firstEdge + i];
|
||||
}
|
||||
node.firstEdge = newFirstEdge;
|
||||
}
|
||||
_StrEdge &edge = _edges[node.firstEdge + node.edges];
|
||||
edge.target = to;
|
||||
edge.data = data;
|
||||
_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 ) {
|
||||
_StrNode &node = _nodes[source];
|
||||
--_numEdges;
|
||||
--node.edges;
|
||||
const unsigned last = node.firstEdge + node.edges;
|
||||
//swap with last edge
|
||||
_edges[e] = _edges[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 ( _edges[i].target == target ) {
|
||||
do {
|
||||
deleted++;
|
||||
_edges[i] = _edges[iend - deleted];
|
||||
} while ( i < iend - deleted && _edges[i].target == target );
|
||||
}
|
||||
}
|
||||
|
||||
#pragma omp atomic
|
||||
_numEdges -= deleted;
|
||||
_nodes[source].edges -= deleted;
|
||||
|
||||
return deleted;
|
||||
}
|
||||
|
||||
//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;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
struct _StrNode {
|
||||
//index of the first edge
|
||||
EdgeIterator firstEdge;
|
||||
//amount of edges
|
||||
unsigned edges;
|
||||
unsigned size;
|
||||
};
|
||||
|
||||
struct _StrEdge {
|
||||
NodeID target;
|
||||
EdgeData data;
|
||||
};
|
||||
|
||||
NodeIterator _numNodes;
|
||||
EdgeIterator _numEdges;
|
||||
|
||||
std::vector< _StrNode > _nodes;
|
||||
std::vector< _StrEdge > _edges;
|
||||
};
|
||||
|
||||
#endif // DYNAMICGRAPH_H_INCLUDED
|
97
Contractor/GraphLoader.h
Normal file
97
Contractor/GraphLoader.h
Normal file
@ -0,0 +1,97 @@
|
||||
/*
|
||||
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 CREATEGRAPH_H
|
||||
#define GRAPHLOADER_H
|
||||
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
|
||||
#include <google/dense_hash_map>
|
||||
|
||||
#ifdef _GLIBCXX_PARALLEL
|
||||
#include <parallel/algorithm>
|
||||
#else
|
||||
#include <algorithm>
|
||||
#endif
|
||||
|
||||
#include "../DataStructures/ImportEdge.h"
|
||||
#include "../typedefs.h"
|
||||
|
||||
typedef google::dense_hash_map<NodeID, NodeID> ExternalNodeMap;
|
||||
|
||||
template<typename EdgeT>
|
||||
inline NodeID readOSMRGraphFromStream(istream &in, vector<EdgeT>& edgeList, vector<NodeInfo> * int2ExtNodeMap) {
|
||||
NodeID n, source, target, id;
|
||||
EdgeID m;
|
||||
int dir, xcoord, ycoord;// direction (0 = open, 1 = forward, 2+ = open)
|
||||
ExternalNodeMap ext2IntNodeMap;
|
||||
ext2IntNodeMap.set_empty_key(UINT_MAX);
|
||||
in >> n;
|
||||
VERBOSE(cout << "Importing n = " << n << " nodes ..." << flush;)
|
||||
for (NodeID i=0; i<n;i++) {
|
||||
in >> id >> ycoord >> xcoord;
|
||||
int2ExtNodeMap->push_back(NodeInfo(xcoord, ycoord, id));
|
||||
ext2IntNodeMap.insert(make_pair(id, i));
|
||||
}
|
||||
in >> m;
|
||||
VERBOSE(cout << " and " << m << " edges ..." << flush;)
|
||||
|
||||
edgeList.reserve(m);
|
||||
for (EdgeID i=0; i<m; i++) {
|
||||
EdgeWeight weight;
|
||||
int length;
|
||||
in >> source >> target >> length >> dir >> weight;
|
||||
|
||||
assert(length > 0);
|
||||
assert(weight > 0);
|
||||
assert(0<=dir && dir<=2);
|
||||
|
||||
bool forward = true;
|
||||
bool backward = true;
|
||||
if (dir == 1) backward = false;
|
||||
if (dir == 2) forward = false;
|
||||
|
||||
if(length == 0)
|
||||
{ cerr << "loaded null length edge" << endl; exit(1); }
|
||||
|
||||
// translate the external NodeIDs to internal IDs
|
||||
ExternalNodeMap::iterator intNodeID = ext2IntNodeMap.find(source);
|
||||
if( intNodeID == ext2IntNodeMap.end()) { cerr << "unresolved source NodeID: " << source << endl; exit(0); }
|
||||
source = intNodeID->second;
|
||||
intNodeID = ext2IntNodeMap.find(target);
|
||||
if(intNodeID == ext2IntNodeMap.end()) { cerr << "unresolved target NodeID : " << target << endl; exit(0); }
|
||||
target = intNodeID->second;
|
||||
|
||||
if(source == UINT_MAX || target == UINT_MAX) { cerr << "nonexisting source or target" << endl; exit(0); }
|
||||
|
||||
EdgeT inputEdge(source, target, weight, forward, backward);
|
||||
edgeList.push_back(inputEdge);
|
||||
}
|
||||
ext2IntNodeMap.clear();
|
||||
cout << "ok" << endl;
|
||||
return n;
|
||||
}
|
||||
|
||||
#endif // CREATEGRAPH_H
|
212
Contractor/SearchEngine.h
Normal file
212
Contractor/SearchEngine.h
Normal file
@ -0,0 +1,212 @@
|
||||
/*
|
||||
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 "BinaryHeap.h"
|
||||
#include "DynamicGraph.h"
|
||||
#include "../typedefs.h"
|
||||
|
||||
struct _HeapData {
|
||||
NodeID parent;
|
||||
_HeapData( NodeID p ) : parent(p) { }
|
||||
};
|
||||
|
||||
typedef BinaryHeap< NodeID, int, int, _HeapData, MapStorage< NodeID, unsigned > > _Heap;
|
||||
|
||||
template<typename EdgeData, typename KDTST = NodeInformationHelpDesk>
|
||||
class SearchEngine {
|
||||
private:
|
||||
const DynamicGraph<EdgeData> * _graph;
|
||||
public:
|
||||
SearchEngine(DynamicGraph<EdgeData> * g, KDTST * k) : _graph(g), kdtree(k) {}
|
||||
~SearchEngine() {}
|
||||
|
||||
NodeInfo& getNodeInfo(NodeID id) const
|
||||
{
|
||||
return kdtree->getExternalNodeInfo(id);
|
||||
}
|
||||
|
||||
unsigned int numberOfNodes() const
|
||||
{
|
||||
return kdtree->getNumberOfNodes();
|
||||
}
|
||||
|
||||
unsigned int ComputeRoute(NodeID start, NodeID target, vector<NodeID> * path)
|
||||
{
|
||||
_Heap * _forwardHeap = new _Heap(kdtree->getNumberOfNodes());
|
||||
_Heap * _backwardHeap = new _Heap(kdtree->getNumberOfNodes());
|
||||
NodeID middle = ( NodeID ) 0;
|
||||
unsigned int _upperbound = std::numeric_limits<unsigned int>::max();
|
||||
_forwardHeap->Insert(start, 0, start);
|
||||
_backwardHeap->Insert(target, 0, target);
|
||||
|
||||
while(_forwardHeap->Size() + _backwardHeap->Size() > 0)
|
||||
{
|
||||
if ( _forwardHeap->Size() > 0 ) {
|
||||
_RoutingStep( _forwardHeap, _backwardHeap, true, &middle, &_upperbound );
|
||||
}
|
||||
if ( _backwardHeap->Size() > 0 ) {
|
||||
_RoutingStep( _backwardHeap, _forwardHeap, false, &middle, &_upperbound );
|
||||
}
|
||||
}
|
||||
|
||||
if ( _upperbound == std::numeric_limits< unsigned int >::max() )
|
||||
return _upperbound;
|
||||
|
||||
NodeID pathNode = middle;
|
||||
NodeID unpackEndNode = start;
|
||||
deque< NodeID > packedPath;
|
||||
|
||||
while ( pathNode != unpackEndNode ) {
|
||||
pathNode = _forwardHeap->GetData( pathNode ).parent;
|
||||
packedPath.push_front( pathNode );
|
||||
}
|
||||
|
||||
packedPath.push_back( middle );
|
||||
|
||||
pathNode = middle;
|
||||
unpackEndNode = target;
|
||||
|
||||
while ( pathNode != unpackEndNode ) {
|
||||
pathNode = _backwardHeap->GetData( pathNode ).parent;
|
||||
packedPath.push_back( pathNode );
|
||||
}
|
||||
|
||||
// for(deque<NodeID>::size_type i = 0; i < packedPath.size()-1; i++)
|
||||
// {
|
||||
// cout << packedPath[i] << endl;
|
||||
// }
|
||||
|
||||
// push start node explicitely
|
||||
path->push_back(packedPath[0]);
|
||||
for(deque<NodeID>::size_type i = 0; i < packedPath.size()-1; i++)
|
||||
{
|
||||
// path->push_back(*it);
|
||||
_UnpackEdge(packedPath[i], packedPath[i+1], path);
|
||||
}
|
||||
|
||||
packedPath.clear();
|
||||
delete _forwardHeap;
|
||||
delete _backwardHeap;
|
||||
|
||||
return _upperbound/10;
|
||||
}
|
||||
|
||||
unsigned int findNearestNodeForLatLon(const int lat, const int lon, NodeCoords<NodeID> * data) const
|
||||
{
|
||||
return kdtree->findNearestNodeIDForLatLon( lat, lon, data);
|
||||
}
|
||||
private:
|
||||
KDTST * kdtree;
|
||||
|
||||
void _RoutingStep(_Heap * _forwardHeap, _Heap *_backwardHeap, const bool& forwardDirection, NodeID * middle, unsigned int * _upperbound)
|
||||
{
|
||||
const NodeID node = _forwardHeap->DeleteMin();
|
||||
const unsigned int distance = _forwardHeap->GetKey( node );
|
||||
if ( _backwardHeap->WasInserted( node ) ) {
|
||||
const unsigned int newDistance = _backwardHeap->GetKey( node ) + distance;
|
||||
if ( newDistance < *_upperbound ) {
|
||||
*middle = node;
|
||||
*_upperbound = newDistance;
|
||||
}
|
||||
}
|
||||
if ( distance > *_upperbound ) {
|
||||
_forwardHeap->DeleteAll();
|
||||
return;
|
||||
}
|
||||
for ( typename DynamicGraph<EdgeData>::EdgeIterator edge = _graph->BeginEdges( node ); edge < _graph->EndEdges(node); edge++ ) {
|
||||
const NodeID to = _graph->GetTarget(edge);
|
||||
const int edgeWeight = _graph->GetEdgeData(edge).distance;
|
||||
|
||||
assert( edgeWeight > 0 );
|
||||
const int toDistance = distance + edgeWeight;
|
||||
|
||||
if(forwardDirection ? _graph->GetEdgeData(edge).forward : _graph->GetEdgeData(edge).backward )
|
||||
{
|
||||
//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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool _UnpackEdge( const NodeID source, const NodeID target, std::vector< NodeID >* path ) {
|
||||
assert(source != target);
|
||||
//find edge first.
|
||||
typename DynamicGraph<EdgeData>::EdgeIterator smallestEdge = SPECIAL_EDGEID;
|
||||
EdgeWeight smallestWeight = UINT_MAX;
|
||||
for(typename DynamicGraph<EdgeData>::EdgeIterator eit = _graph->BeginEdges(source); eit < _graph->EndEdges(source); eit++)
|
||||
{
|
||||
//const NodeID target = GetTarget(edge);
|
||||
const EdgeWeight 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 DynamicGraph<EdgeData>::EdgeIterator eit = _graph->BeginEdges(target); eit < _graph->EndEdges(target); eit++)
|
||||
{
|
||||
//const NodeID target = GetTarget(edge);
|
||||
const EdgeWeight weight = _graph->GetEdgeData(eit).distance;
|
||||
{
|
||||
if(_graph->GetTarget(eit) == source && weight < smallestWeight && _graph->GetEdgeData(eit).backward)
|
||||
{
|
||||
smallestEdge = eit; smallestWeight = weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
assert(smallestWeight != SPECIAL_EDGEID);
|
||||
|
||||
const EdgeData ed = _graph->GetEdgeData(smallestEdge);
|
||||
if(ed.shortcut)
|
||||
{//unpack
|
||||
const NodeID middle = ed.middle;
|
||||
_UnpackEdge(source, middle, path);
|
||||
_UnpackEdge(middle, target, path);
|
||||
return false;
|
||||
} else {
|
||||
assert(!ed.shortcut);
|
||||
path->push_back(target);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* SEARCHENGINE_H_ */
|
67
DataStructures/ImportEdge.h
Normal file
67
DataStructures/ImportEdge.h
Normal file
@ -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 EDGE_H
|
||||
#define EDGE_H
|
||||
|
||||
#include <cassert>
|
||||
|
||||
class Edge
|
||||
{
|
||||
public:
|
||||
|
||||
bool operator< (const Edge& 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.*/
|
||||
Edge() { assert(false); } //shall not be used.
|
||||
|
||||
explicit Edge(NodeID s, NodeID t, EdgeWeight w, bool f, bool b) : _source(s), _target(t), _weight(w), forward(f), backward(b) { }
|
||||
|
||||
NodeID target() const {return _target; }
|
||||
NodeID source() const {return _source;}
|
||||
EdgeWeight weight() const {return _weight; }
|
||||
|
||||
bool isBackward() const { return backward; }
|
||||
|
||||
bool isForward() const { return forward; }
|
||||
|
||||
private:
|
||||
NodeID _source;
|
||||
NodeID _target;
|
||||
EdgeWeight _weight:30;
|
||||
bool forward:1;
|
||||
bool backward:1;
|
||||
};
|
||||
|
||||
typedef Edge ImportEdge;
|
||||
|
||||
#endif // EDGE_H
|
66
DataStructures/NodeCoords.h
Normal file
66
DataStructures/NodeCoords.h
Normal file
@ -0,0 +1,66 @@
|
||||
/*
|
||||
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
|
||||
|
||||
|
||||
template<typename NodeT>
|
||||
struct NodeCoords {
|
||||
NodeCoords(int _lat, int _lon, NodeT _id) : lat(_lat), lon(_lon), id(_id) {}
|
||||
NodeCoords() : lat(UINT_MAX), lon(UINT_MAX), id(UINT_MAX) {}
|
||||
int lat;
|
||||
int lon;
|
||||
unsigned int id;
|
||||
};
|
||||
|
||||
struct duplet
|
||||
{
|
||||
typedef int value_type;
|
||||
|
||||
inline value_type operator[](int const N) const { return d[N]; }
|
||||
|
||||
inline bool operator==(duplet const& other) const
|
||||
{
|
||||
return this->d[0] == other.d[0] && this->d[1] == other.d[1];
|
||||
}
|
||||
|
||||
inline bool operator!=(duplet const& other) const
|
||||
{
|
||||
return this->d[0] != other.d[0] || this->d[1] != other.d[1];
|
||||
}
|
||||
|
||||
friend std::ostream & operator<<(std::ostream & o, duplet const& d)
|
||||
{
|
||||
return o << "(" << d[0] << "," << d[1] << ")";
|
||||
}
|
||||
duplet(unsigned _id, int x, int y)
|
||||
{
|
||||
id = _id;
|
||||
d[0] = x;
|
||||
d[1] = y;
|
||||
}
|
||||
unsigned int id;
|
||||
value_type d[2];
|
||||
};
|
||||
|
||||
inline double return_dup( duplet d, int k ) { return d[k]; }
|
||||
|
||||
#endif //_NODE_COORDS_H
|
141
DataStructures/NodeInformationHelpDesk.h
Normal file
141
DataStructures/NodeInformationHelpDesk.h
Normal file
@ -0,0 +1,141 @@
|
||||
/*
|
||||
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 KDTREE_H_
|
||||
#define NODEINFORMATIONHELPDESK_H_
|
||||
|
||||
#include <omp.h>
|
||||
|
||||
#include <climits>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <list>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../typedefs.h"
|
||||
|
||||
#include <kdtree++/kdtree.hpp>
|
||||
|
||||
typedef KDTree::KDTree<2, duplet, std::pointer_to_binary_function<duplet,int,double> > duplet_tree_type;
|
||||
|
||||
class NodeInformationHelpDesk{
|
||||
public:
|
||||
~NodeInformationHelpDesk();
|
||||
NodeInformationHelpDesk() { int2ExtNodeMap = new vector<NodeInfo>();}
|
||||
duplet_tree_type * initKDTree(ifstream& input);
|
||||
|
||||
NodeID getExternalNodeID(const NodeID node);
|
||||
NodeInfo& getExternalNodeInfo(const NodeID node);
|
||||
int getLatitudeOfNode(const NodeID node);
|
||||
int getLongitudeOfNode(const NodeID node);
|
||||
|
||||
NodeID getNumberOfNodes() const { return int2ExtNodeMap->size(); }
|
||||
|
||||
inline NodeID findNearestNodeIDForLatLon(const int lat, const int lon, NodeCoords<NodeID> * data) const
|
||||
{
|
||||
|
||||
duplet dup = *(kdtree->find_nearest(duplet(0, lat, lon)).first);
|
||||
data->id = dup.id;
|
||||
data->lat = dup.d[1];
|
||||
data->lon = dup.d[0];
|
||||
return data->id;
|
||||
}
|
||||
private:
|
||||
vector<NodeInfo> * int2ExtNodeMap;
|
||||
duplet_tree_type * kdtree;
|
||||
};
|
||||
|
||||
//////////////////
|
||||
//implementation//
|
||||
//////////////////
|
||||
|
||||
NodeInformationHelpDesk::~NodeInformationHelpDesk(){
|
||||
// delete graph;
|
||||
// delete calc;
|
||||
// delete c;
|
||||
}
|
||||
|
||||
/* @brief: initialize kd-tree and internal->external node id map
|
||||
*
|
||||
*/
|
||||
duplet_tree_type * NodeInformationHelpDesk::initKDTree(ifstream& in)
|
||||
{
|
||||
// NodeID i = 0;
|
||||
while(!in.eof())
|
||||
{
|
||||
NodeInfo b;
|
||||
in.read((char *)&b, sizeof(b));
|
||||
int2ExtNodeMap->push_back(b);
|
||||
// i++;
|
||||
}
|
||||
in.close();
|
||||
|
||||
duplet_tree_type * tree = new duplet_tree_type(std::ptr_fun(return_dup));
|
||||
NodeID id = 0;
|
||||
for(vector<NodeInfo>::iterator it = int2ExtNodeMap->begin(); it != int2ExtNodeMap->end(); it++)
|
||||
{
|
||||
duplet super_dupre(id, it->lat, it->lon);
|
||||
tree->insert(super_dupre);
|
||||
id++;
|
||||
}
|
||||
kdtree = tree;
|
||||
return tree;
|
||||
}
|
||||
|
||||
NodeID NodeInformationHelpDesk::getExternalNodeID(const NodeID node)
|
||||
{
|
||||
// google::dense_hash_map<NodeID, NodeInfo>::iterator temp = int2ExtNodeMap->find(node);
|
||||
// if(temp == int2ExtNodeMap->end())
|
||||
// return UINT_MAX;
|
||||
// return temp->second.id;
|
||||
return int2ExtNodeMap->at(node).id;
|
||||
}
|
||||
|
||||
NodeInfo& NodeInformationHelpDesk::getExternalNodeInfo(const NodeID node)
|
||||
{
|
||||
return int2ExtNodeMap->at(node);
|
||||
}
|
||||
|
||||
int NodeInformationHelpDesk::getLatitudeOfNode(const NodeID node)
|
||||
{
|
||||
// google::dense_hash_map<NodeID, NodeInfo>::iterator temp = int2ExtNodeMap->find(node);
|
||||
// if(temp == int2ExtNodeMap->end())
|
||||
// return UINT_MAX;
|
||||
// return temp->second.lat;
|
||||
return int2ExtNodeMap->at(node).lat;
|
||||
}
|
||||
|
||||
int NodeInformationHelpDesk::getLongitudeOfNode(const NodeID node)
|
||||
{
|
||||
// google::dense_hash_map<NodeID, NodeInfo>::iterator temp = int2ExtNodeMap->find(node);
|
||||
// if(temp == int2ExtNodeMap->end())
|
||||
// return UINT_MAX;
|
||||
// return temp->second.lon;
|
||||
return int2ExtNodeMap->at(node).lon;
|
||||
}
|
||||
|
||||
#endif /*KDTREE_H_*/
|
34
DataStructures/Util.h
Normal file
34
DataStructures/Util.h
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
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 <sys/time.h>
|
||||
|
||||
/** Returns a timestamp (now) in seconds (incl. a fractional part). */
|
||||
inline double get_timestamp()
|
||||
{
|
||||
struct timeval tp;
|
||||
gettimeofday(&tp, NULL);
|
||||
return double(tp.tv_sec) + tp.tv_usec / 1000000.;
|
||||
}
|
||||
|
||||
#endif /* TIMEUTIL_H_ */
|
5
Docs/3rdparty.txt
Normal file
5
Docs/3rdparty.txt
Normal file
@ -0,0 +1,5 @@
|
||||
Third Party Libraries:
|
||||
|
||||
Boost 1.37+
|
||||
kdtree++ 0.7+ (0.62 does not work with g++ 4.0+)
|
||||
sparsehash 1.4+
|
37
Docs/FAQ.txt
Normal file
37
Docs/FAQ.txt
Normal file
@ -0,0 +1,37 @@
|
||||
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.
|
8
Docs/Todo.txt
Normal file
8
Docs/Todo.txt
Normal file
@ -0,0 +1,8 @@
|
||||
TODO
|
||||
----
|
||||
|
||||
Start/Endpoints of queries can only start on nodes only. Instead of selecting the nearest node the nearest edge should be selected.
|
||||
|
||||
The server part is mostly adapted from the boost http examples. It should be replaced with a self-written front-end
|
||||
|
||||
The KD-Tree uses a lot of RAM. An own implementation might be better.
|
164
HttpServer/connection.h
Normal file
164
HttpServer/connection.h
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
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 HTTP_SERVER3_CONNECTION_HPP
|
||||
#define HTTP_CONNECTION_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/enable_shared_from_this.hpp>
|
||||
#include "reply.h"
|
||||
#include "request.h"
|
||||
#include "request_handler.h"
|
||||
#include "request_parser.h"
|
||||
|
||||
namespace http {
|
||||
|
||||
|
||||
/// Represents a single connection from a client.
|
||||
class connection
|
||||
: public boost::enable_shared_from_this<connection>,
|
||||
private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
/// Construct a connection with the given io_service.
|
||||
explicit connection(boost::asio::io_service& io_service,
|
||||
request_handler& handler);
|
||||
|
||||
/// Get the socket associated with the connection.
|
||||
boost::asio::ip::tcp::socket& socket();
|
||||
|
||||
/// Start the first asynchronous operation for the connection.
|
||||
void start();
|
||||
|
||||
private:
|
||||
/// Handle completion of a read operation.
|
||||
void handle_read(const boost::system::error_code& e,
|
||||
std::size_t bytes_transferred);
|
||||
|
||||
/// Handle completion of a write operation.
|
||||
void handle_write(const boost::system::error_code& e);
|
||||
|
||||
/// Strand to ensure the connection's handlers are not called concurrently.
|
||||
boost::asio::io_service::strand strand_;
|
||||
|
||||
/// Socket for the connection.
|
||||
boost::asio::ip::tcp::socket socket_;
|
||||
|
||||
/// The handler used to process the incoming request.
|
||||
request_handler& request_handler_;
|
||||
|
||||
/// Buffer for incoming data.
|
||||
boost::array<char, 8192> buffer_;
|
||||
|
||||
/// The incoming request.
|
||||
request request_;
|
||||
|
||||
/// The parser for the incoming request.
|
||||
request_parser request_parser_;
|
||||
|
||||
/// The reply to be sent back to the client.
|
||||
reply reply_;
|
||||
};
|
||||
|
||||
typedef boost::shared_ptr<connection> connection_ptr;
|
||||
|
||||
|
||||
connection::connection(boost::asio::io_service& io_service,
|
||||
request_handler& handler)
|
||||
: strand_(io_service),
|
||||
socket_(io_service),
|
||||
request_handler_(handler)
|
||||
{
|
||||
}
|
||||
|
||||
boost::asio::ip::tcp::socket& connection::socket()
|
||||
{
|
||||
return socket_;
|
||||
}
|
||||
|
||||
void connection::start()
|
||||
{
|
||||
socket_.async_read_some(boost::asio::buffer(buffer_),
|
||||
strand_.wrap(
|
||||
boost::bind(&connection::handle_read, shared_from_this(),
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred)));
|
||||
}
|
||||
|
||||
void connection::handle_read(const boost::system::error_code& e,
|
||||
std::size_t bytes_transferred)
|
||||
{
|
||||
if (!e)
|
||||
{
|
||||
boost::tribool result;
|
||||
boost::tie(result, boost::tuples::ignore) = request_parser_.parse(
|
||||
request_, buffer_.data(), buffer_.data() + bytes_transferred);
|
||||
|
||||
if (result)
|
||||
{
|
||||
request_handler_.handle_request(request_, reply_);
|
||||
boost::asio::async_write(socket_, reply_.to_buffers(),
|
||||
strand_.wrap(
|
||||
boost::bind(&connection::handle_write, shared_from_this(),
|
||||
boost::asio::placeholders::error)));
|
||||
}
|
||||
else if (!result)
|
||||
{
|
||||
reply_ = reply::stock_reply(reply::bad_request);
|
||||
boost::asio::async_write(socket_, reply_.to_buffers(),
|
||||
strand_.wrap(
|
||||
boost::bind(&connection::handle_write, shared_from_this(),
|
||||
boost::asio::placeholders::error)));
|
||||
}
|
||||
else
|
||||
{
|
||||
socket_.async_read_some(boost::asio::buffer(buffer_),
|
||||
strand_.wrap(
|
||||
boost::bind(&connection::handle_read, shared_from_this(),
|
||||
boost::asio::placeholders::error,
|
||||
boost::asio::placeholders::bytes_transferred)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void connection::handle_write(const boost::system::error_code& e)
|
||||
{
|
||||
if (!e)
|
||||
{
|
||||
// Initiate graceful connection closure.
|
||||
boost::system::error_code ignored_ec;
|
||||
socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
|
||||
}
|
||||
|
||||
// No new asynchronous operations are started. This means that all shared_ptr
|
||||
// references to the connection object will disappear and the object will be
|
||||
// destroyed automatically after this handler returns. The connection class's
|
||||
// destructor closes the socket.
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
|
||||
#endif /* CONNECTION_HPP_ */
|
37
HttpServer/header.h
Normal file
37
HttpServer/header.h
Normal file
@ -0,0 +1,37 @@
|
||||
/*
|
||||
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 HTTP_HEADER_HPP
|
||||
#define HTTP_HEADER_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace http {
|
||||
|
||||
|
||||
struct header
|
||||
{
|
||||
std::string name;
|
||||
std::string value;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
312
HttpServer/reply.h
Normal file
312
HttpServer/reply.h
Normal file
@ -0,0 +1,312 @@
|
||||
/*
|
||||
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 HTTP_SERVER3_REPLY_HPP
|
||||
#define HTTP_SERVER3_REPLY_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "header.h"
|
||||
|
||||
namespace http {
|
||||
|
||||
/// A reply to be sent to a client.
|
||||
struct reply
|
||||
{
|
||||
/// The status of the reply.
|
||||
enum status_type
|
||||
{
|
||||
ok = 200,
|
||||
created = 201,
|
||||
accepted = 202,
|
||||
no_content = 204,
|
||||
multiple_choices = 300,
|
||||
moved_permanently = 301,
|
||||
moved_temporarily = 302,
|
||||
not_modified = 304,
|
||||
bad_request = 400,
|
||||
unauthorized = 401,
|
||||
forbidden = 403,
|
||||
not_found = 404,
|
||||
internal_server_error = 500,
|
||||
not_implemented = 501,
|
||||
bad_gateway = 502,
|
||||
service_unavailable = 503
|
||||
} status;
|
||||
|
||||
/// The headers to be included in the reply.
|
||||
std::vector<header> headers;
|
||||
|
||||
/// The content to be sent in the reply.
|
||||
std::string content;
|
||||
|
||||
/// Convert the reply into a vector of buffers. The buffers do not own the
|
||||
/// underlying memory blocks, therefore the reply object must remain valid and
|
||||
/// not be changed until the write operation has completed.
|
||||
std::vector<boost::asio::const_buffer> to_buffers();
|
||||
|
||||
/// Get a stock reply.
|
||||
static reply stock_reply(status_type status);
|
||||
};
|
||||
|
||||
namespace status_strings {
|
||||
|
||||
const std::string ok =
|
||||
"HTTP/1.0 200 OK\r\n";
|
||||
const std::string created =
|
||||
"HTTP/1.0 201 Created\r\n";
|
||||
const std::string accepted =
|
||||
"HTTP/1.0 202 Accepted\r\n";
|
||||
const std::string no_content =
|
||||
"HTTP/1.0 204 No Content\r\n";
|
||||
const std::string multiple_choices =
|
||||
"HTTP/1.0 300 Multiple Choices\r\n";
|
||||
const std::string moved_permanently =
|
||||
"HTTP/1.0 301 Moved Permanently\r\n";
|
||||
const std::string moved_temporarily =
|
||||
"HTTP/1.0 302 Moved Temporarily\r\n";
|
||||
const std::string not_modified =
|
||||
"HTTP/1.0 304 Not Modified\r\n";
|
||||
const std::string bad_request =
|
||||
"HTTP/1.0 400 Bad Request\r\n";
|
||||
const std::string unauthorized =
|
||||
"HTTP/1.0 401 Unauthorized\r\n";
|
||||
const std::string forbidden =
|
||||
"HTTP/1.0 403 Forbidden\r\n";
|
||||
const std::string not_found =
|
||||
"HTTP/1.0 404 Not Found\r\n";
|
||||
const std::string internal_server_error =
|
||||
"HTTP/1.0 500 Internal Server Error\r\n";
|
||||
const std::string not_implemented =
|
||||
"HTTP/1.0 501 Not Implemented\r\n";
|
||||
const std::string bad_gateway =
|
||||
"HTTP/1.0 502 Bad Gateway\r\n";
|
||||
const std::string service_unavailable =
|
||||
"HTTP/1.0 503 Service Unavailable\r\n";
|
||||
|
||||
boost::asio::const_buffer to_buffer(reply::status_type status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case reply::ok:
|
||||
return boost::asio::buffer(ok);
|
||||
case reply::created:
|
||||
return boost::asio::buffer(created);
|
||||
case reply::accepted:
|
||||
return boost::asio::buffer(accepted);
|
||||
case reply::no_content:
|
||||
return boost::asio::buffer(no_content);
|
||||
case reply::multiple_choices:
|
||||
return boost::asio::buffer(multiple_choices);
|
||||
case reply::moved_permanently:
|
||||
return boost::asio::buffer(moved_permanently);
|
||||
case reply::moved_temporarily:
|
||||
return boost::asio::buffer(moved_temporarily);
|
||||
case reply::not_modified:
|
||||
return boost::asio::buffer(not_modified);
|
||||
case reply::bad_request:
|
||||
return boost::asio::buffer(bad_request);
|
||||
case reply::unauthorized:
|
||||
return boost::asio::buffer(unauthorized);
|
||||
case reply::forbidden:
|
||||
return boost::asio::buffer(forbidden);
|
||||
case reply::not_found:
|
||||
return boost::asio::buffer(not_found);
|
||||
case reply::internal_server_error:
|
||||
return boost::asio::buffer(internal_server_error);
|
||||
case reply::not_implemented:
|
||||
return boost::asio::buffer(not_implemented);
|
||||
case reply::bad_gateway:
|
||||
return boost::asio::buffer(bad_gateway);
|
||||
case reply::service_unavailable:
|
||||
return boost::asio::buffer(service_unavailable);
|
||||
default:
|
||||
return boost::asio::buffer(internal_server_error);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace status_strings
|
||||
|
||||
namespace misc_strings {
|
||||
|
||||
const char name_value_separator[] = { ':', ' ' };
|
||||
const char crlf[] = { '\r', '\n' };
|
||||
|
||||
} // namespace misc_strings
|
||||
|
||||
std::vector<boost::asio::const_buffer> reply::to_buffers()
|
||||
{
|
||||
std::vector<boost::asio::const_buffer> buffers;
|
||||
buffers.push_back(status_strings::to_buffer(status));
|
||||
for (std::size_t i = 0; i < headers.size(); ++i)
|
||||
{
|
||||
header& h = headers[i];
|
||||
buffers.push_back(boost::asio::buffer(h.name));
|
||||
buffers.push_back(boost::asio::buffer(misc_strings::name_value_separator));
|
||||
buffers.push_back(boost::asio::buffer(h.value));
|
||||
buffers.push_back(boost::asio::buffer(misc_strings::crlf));
|
||||
}
|
||||
buffers.push_back(boost::asio::buffer(misc_strings::crlf));
|
||||
buffers.push_back(boost::asio::buffer(content));
|
||||
return buffers;
|
||||
}
|
||||
|
||||
namespace stock_replies {
|
||||
|
||||
const char ok[] = "";
|
||||
const char created[] =
|
||||
"<html>"
|
||||
"<head><title>Created</title></head>"
|
||||
"<body><h1>201 Created</h1></body>"
|
||||
"</html>";
|
||||
const char accepted[] =
|
||||
"<html>"
|
||||
"<head><title>Accepted</title></head>"
|
||||
"<body><h1>202 Accepted</h1></body>"
|
||||
"</html>";
|
||||
const char no_content[] =
|
||||
"<html>"
|
||||
"<head><title>No Content</title></head>"
|
||||
"<body><h1>204 Content</h1></body>"
|
||||
"</html>";
|
||||
const char multiple_choices[] =
|
||||
"<html>"
|
||||
"<head><title>Multiple Choices</title></head>"
|
||||
"<body><h1>300 Multiple Choices</h1></body>"
|
||||
"</html>";
|
||||
const char moved_permanently[] =
|
||||
"<html>"
|
||||
"<head><title>Moved Permanently</title></head>"
|
||||
"<body><h1>301 Moved Permanently</h1></body>"
|
||||
"</html>";
|
||||
const char moved_temporarily[] =
|
||||
"<html>"
|
||||
"<head><title>Moved Temporarily</title></head>"
|
||||
"<body><h1>302 Moved Temporarily</h1></body>"
|
||||
"</html>";
|
||||
const char not_modified[] =
|
||||
"<html>"
|
||||
"<head><title>Not Modified</title></head>"
|
||||
"<body><h1>304 Not Modified</h1></body>"
|
||||
"</html>";
|
||||
const char bad_request[] =
|
||||
"<html>"
|
||||
"<head><title>Bad Request</title></head>"
|
||||
"<body><h1>400 Bad Request</h1></body>"
|
||||
"</html>";
|
||||
const char unauthorized[] =
|
||||
"<html>"
|
||||
"<head><title>Unauthorized</title></head>"
|
||||
"<body><h1>401 Unauthorized</h1></body>"
|
||||
"</html>";
|
||||
const char forbidden[] =
|
||||
"<html>"
|
||||
"<head><title>Forbidden</title></head>"
|
||||
"<body><h1>403 Forbidden</h1></body>"
|
||||
"</html>";
|
||||
const char not_found[] =
|
||||
"<html>"
|
||||
"<head><title>Not Found</title></head>"
|
||||
"<body><h1>404 Not Found</h1></body>"
|
||||
"</html>";
|
||||
const char internal_server_error[] =
|
||||
"<html>"
|
||||
"<head><title>Internal Server Error</title></head>"
|
||||
"<body><h1>500 Internal Server Error</h1></body>"
|
||||
"</html>";
|
||||
const char not_implemented[] =
|
||||
"<html>"
|
||||
"<head><title>Not Implemented</title></head>"
|
||||
"<body><h1>501 Not Implemented</h1></body>"
|
||||
"</html>";
|
||||
const char bad_gateway[] =
|
||||
"<html>"
|
||||
"<head><title>Bad Gateway</title></head>"
|
||||
"<body><h1>502 Bad Gateway</h1></body>"
|
||||
"</html>";
|
||||
const char service_unavailable[] =
|
||||
"<html>"
|
||||
"<head><title>Service Unavailable</title></head>"
|
||||
"<body><h1>503 Service Unavailable</h1></body>"
|
||||
"</html>";
|
||||
|
||||
std::string to_string(reply::status_type status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case reply::ok:
|
||||
return ok;
|
||||
case reply::created:
|
||||
return created;
|
||||
case reply::accepted:
|
||||
return accepted;
|
||||
case reply::no_content:
|
||||
return no_content;
|
||||
case reply::multiple_choices:
|
||||
return multiple_choices;
|
||||
case reply::moved_permanently:
|
||||
return moved_permanently;
|
||||
case reply::moved_temporarily:
|
||||
return moved_temporarily;
|
||||
case reply::not_modified:
|
||||
return not_modified;
|
||||
case reply::bad_request:
|
||||
return bad_request;
|
||||
case reply::unauthorized:
|
||||
return unauthorized;
|
||||
case reply::forbidden:
|
||||
return forbidden;
|
||||
case reply::not_found:
|
||||
return not_found;
|
||||
case reply::internal_server_error:
|
||||
return internal_server_error;
|
||||
case reply::not_implemented:
|
||||
return not_implemented;
|
||||
case reply::bad_gateway:
|
||||
return bad_gateway;
|
||||
case reply::service_unavailable:
|
||||
return service_unavailable;
|
||||
default:
|
||||
return internal_server_error;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace stock_replies
|
||||
|
||||
reply reply::stock_reply(reply::status_type status)
|
||||
{
|
||||
reply rep;
|
||||
rep.status = status;
|
||||
rep.content = stock_replies::to_string(status);
|
||||
rep.headers.resize(2);
|
||||
rep.headers[0].name = "Content-Length";
|
||||
rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size());
|
||||
rep.headers[1].name = "Content-Type";
|
||||
rep.headers[1].value = "text/html";
|
||||
return rep;
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER3_REPLY_HPP
|
42
HttpServer/request.h
Normal file
42
HttpServer/request.h
Normal file
@ -0,0 +1,42 @@
|
||||
/*
|
||||
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 HTTP_REQUEST_HPP
|
||||
#define HTTP_REQUEST_HPP
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "header.h"
|
||||
|
||||
namespace http {
|
||||
|
||||
/// A request received from a client.
|
||||
struct request
|
||||
{
|
||||
std::string method;
|
||||
std::string uri;
|
||||
int http_version_major;
|
||||
int http_version_minor;
|
||||
std::vector<header> headers;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // HTTP_REQUEST_HPP
|
170
HttpServer/request_handler.h
Normal file
170
HttpServer/request_handler.h
Normal file
@ -0,0 +1,170 @@
|
||||
/*
|
||||
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 HTTP_ROUTER_REQUEST_HANDLER_HPP
|
||||
#define HTTP_ROUTER_REQUEST_HANDLER_HPP
|
||||
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include "reply.h"
|
||||
#include "request.h"
|
||||
|
||||
#include "../typedefs.h"
|
||||
|
||||
namespace http {
|
||||
|
||||
struct reply;
|
||||
struct request;
|
||||
|
||||
/// The common handler for all incoming requests.
|
||||
class request_handler : private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
/// Construct with a directory containing files to be served.
|
||||
explicit request_handler(SearchEngine<EdgeData> * s) : sEngine(s){}
|
||||
|
||||
/// Handle a request and produce a reply.
|
||||
void handle_request(const request& req, reply& rep){
|
||||
try {
|
||||
std::string request(req.uri);
|
||||
std::string command;
|
||||
std::size_t first_amp_pos = request.find_first_of("&");
|
||||
command = request.substr(1,first_amp_pos-1);
|
||||
// cout << "command: " << command << endl;
|
||||
if(command == "locate")
|
||||
{
|
||||
std::size_t last_amp_pos = request.find_last_of("&");
|
||||
int lat = static_cast<int>(100000*atof(request.substr(first_amp_pos+1, request.length()-last_amp_pos-1).c_str()));
|
||||
int lon = static_cast<int>(100000*atof(request.substr(last_amp_pos+1).c_str()));
|
||||
NodeCoords<NodeID> * data = new NodeCoords<NodeID>();
|
||||
NodeID start = sEngine->findNearestNodeForLatLon(lat, lon, data);
|
||||
|
||||
rep.status = reply::ok;
|
||||
rep.content.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||
rep.content.append("<kml xmlns=\"http://www.opengis.net/kml/2.2\">");
|
||||
rep.content.append("<Placemark>");
|
||||
|
||||
std::stringstream out1;
|
||||
out1 << setprecision(10);
|
||||
out1 << "<name>Nearest Place in map to " << lat/100000. << "," << lon/10000. << ": node with id " << start << "</name>";
|
||||
rep.content.append(out1.str());
|
||||
rep.content.append("<Point>");
|
||||
|
||||
std::stringstream out2;
|
||||
out2 << setprecision(10);
|
||||
out2 << "<coordinates>" << data->lat / 100000. << "," << data->lon / 100000. << "</coordinates>";
|
||||
rep.content.append(out2.str());
|
||||
rep.content.append("</Point>");
|
||||
rep.content.append("</Placemark>");
|
||||
rep.content.append("</kml>");
|
||||
|
||||
rep.headers.resize(3);
|
||||
rep.headers[0].name = "Content-Length";
|
||||
rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size());
|
||||
rep.headers[1].name = "Content-Type";
|
||||
rep.headers[1].value = "application/vnd.google-earth.kml+xml";
|
||||
rep.headers[2].name = "Content-Disposition";
|
||||
rep.headers[2].value = "attachment; filename=\"placemark.kml\"";
|
||||
delete data;
|
||||
return;
|
||||
}
|
||||
if(command == "route")
|
||||
{
|
||||
//http://localhost:5000/route&45.1427&12.14144&54.8733&8.59438
|
||||
std::size_t second_amp_pos = request.find_first_of("&", first_amp_pos+1);
|
||||
std::size_t third_amp_pos = request.find_first_of("&", second_amp_pos+1);
|
||||
std::size_t fourth_amp_pos = request.find_last_of("&");
|
||||
|
||||
int lat1 = static_cast<int>(100000*atof(request.substr(first_amp_pos+1, request.length()-second_amp_pos-1).c_str()));
|
||||
int lon1 = static_cast<int>(100000*atof(request.substr(second_amp_pos+1, request.length()-third_amp_pos-1).c_str()));
|
||||
int lat2 = static_cast<int>(100000*atof(request.substr(third_amp_pos+1, request.length()-fourth_amp_pos-1).c_str()));
|
||||
int lon2 = static_cast<int>(100000*atof(request.substr(fourth_amp_pos+1).c_str()));
|
||||
|
||||
NodeCoords<NodeID> * startData = new NodeCoords<NodeID>();
|
||||
NodeCoords<NodeID> * targetData = new NodeCoords<NodeID>();
|
||||
vector<NodeID> * path = new vector<NodeID>();
|
||||
NodeID start = sEngine->findNearestNodeForLatLon(lat1, lon1, startData);
|
||||
NodeID target = sEngine->findNearestNodeForLatLon(lat2, lon2, targetData);
|
||||
unsigned int distance = sEngine->ComputeRoute(start, target, path);
|
||||
|
||||
rep.status = reply::ok;
|
||||
rep.content.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
||||
rep.content.append("<kml xmlns=\"http://www.opengis.net/kml/2.2\">");
|
||||
rep.content.append("<Document>");
|
||||
rep.content.append("<Placemark>");
|
||||
rep.content.append("<name>OSM Routing Engine (c) Dennis Luxen and others </name>");
|
||||
std::stringstream out1;
|
||||
out1 << std::setprecision(10);
|
||||
out1 << "<description>Route from " << lat1/100000. << "," << lon1/100000. << " to " << lat2/100000. << "," << lon2/100000. << "</description>" << " ";
|
||||
rep.content.append(out1.str());
|
||||
rep.content.append("<LineString>");
|
||||
rep.content.append("<extrude>1</extrude>");
|
||||
rep.content.append("<tessellate>1</tessellate>");
|
||||
rep.content.append("<altitudeMode>absolute</altitudeMode>");
|
||||
rep.content.append("<coordinates>\n");
|
||||
if(distance != std::numeric_limits<unsigned int>::max())
|
||||
{ //A route has been found
|
||||
for(vector<NodeID>::iterator it = path->begin(); it != path->end(); it++)
|
||||
{
|
||||
NodeInfo info = sEngine-> getNodeInfo(*it);
|
||||
stringstream nodeout;
|
||||
nodeout << std::setprecision(10);
|
||||
nodeout << info.lon/100000. << "," << info.lat/100000. << " " << endl;
|
||||
rep.content.append(nodeout.str());
|
||||
}
|
||||
}
|
||||
rep.content.append("</coordinates>");
|
||||
rep.content.append("</LineString>");
|
||||
rep.content.append("</Placemark>");
|
||||
rep.content.append("</Document>");
|
||||
rep.content.append("</kml>");
|
||||
|
||||
rep.headers.resize(3);
|
||||
rep.headers[0].name = "Content-Length";
|
||||
rep.headers[0].value = boost::lexical_cast<std::string>(rep.content.size());
|
||||
rep.headers[1].name = "Content-Type";
|
||||
rep.headers[1].value = "application/vnd.google-earth.kml+xml";
|
||||
rep.headers[2].name = "Content-Disposition";
|
||||
rep.headers[2].value = "attachment; filename=\"route.kml\"";
|
||||
return;
|
||||
}
|
||||
rep = reply::stock_reply(reply::not_found);
|
||||
return;
|
||||
} catch(std::exception& e)
|
||||
{
|
||||
//TODO: log exception somewhere
|
||||
rep = reply::stock_reply(reply::internal_server_error);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
//SearchEngine object that is queried
|
||||
SearchEngine<EdgeData> * sEngine;
|
||||
};
|
||||
|
||||
} // namespace ROUTER
|
||||
|
||||
#endif // HTTP_ROUTER_REQUEST_HANDLER_HPP
|
420
HttpServer/request_parser.h
Normal file
420
HttpServer/request_parser.h
Normal file
@ -0,0 +1,420 @@
|
||||
/*
|
||||
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 HTTP_REQUEST_PARSER_HPP
|
||||
#define HTTP_REQUEST_PARSER_HPP
|
||||
|
||||
#include <boost/logic/tribool.hpp>
|
||||
#include <boost/tuple/tuple.hpp>
|
||||
|
||||
namespace http {
|
||||
|
||||
struct request;
|
||||
|
||||
/// Parser for incoming requests.
|
||||
class request_parser
|
||||
{
|
||||
public:
|
||||
/// Construct ready to parse the request method.
|
||||
request_parser();
|
||||
|
||||
/// Reset to initial parser state.
|
||||
void reset();
|
||||
|
||||
/// Parse some data. The tribool return value is true when a complete request
|
||||
/// has been parsed, false if the data is invalid, indeterminate when more
|
||||
/// data is required. The InputIterator return value indicates how much of the
|
||||
/// input has been consumed.
|
||||
template <typename InputIterator>
|
||||
boost::tuple<boost::tribool, InputIterator> parse(request& req,
|
||||
InputIterator begin, InputIterator end)
|
||||
{
|
||||
while (begin != end)
|
||||
{
|
||||
boost::tribool result = consume(req, *begin++);
|
||||
if (result || !result)
|
||||
return boost::make_tuple(result, begin);
|
||||
}
|
||||
boost::tribool result = boost::indeterminate;
|
||||
return boost::make_tuple(result, begin);
|
||||
}
|
||||
|
||||
private:
|
||||
/// Handle the next character of input.
|
||||
boost::tribool consume(request& req, char input);
|
||||
|
||||
/// Check if a byte is an HTTP character.
|
||||
static bool is_char(int c);
|
||||
|
||||
/// Check if a byte is an HTTP control character.
|
||||
static bool is_ctl(int c);
|
||||
|
||||
/// Check if a byte is defined as an HTTP tspecial character.
|
||||
static bool is_tspecial(int c);
|
||||
|
||||
/// Check if a byte is a digit.
|
||||
static bool is_digit(int c);
|
||||
|
||||
/// The current state of the parser.
|
||||
enum state
|
||||
{
|
||||
method_start,
|
||||
method,
|
||||
uri_start,
|
||||
uri,
|
||||
http_version_h,
|
||||
http_version_t_1,
|
||||
http_version_t_2,
|
||||
http_version_p,
|
||||
http_version_slash,
|
||||
http_version_major_start,
|
||||
http_version_major,
|
||||
http_version_minor_start,
|
||||
http_version_minor,
|
||||
expecting_newline_1,
|
||||
header_line_start,
|
||||
header_lws,
|
||||
header_name,
|
||||
space_before_header_value,
|
||||
header_value,
|
||||
expecting_newline_2,
|
||||
expecting_newline_3
|
||||
} state_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace http
|
||||
|
||||
#include "request.h"
|
||||
|
||||
namespace http {
|
||||
|
||||
request_parser::request_parser()
|
||||
: state_(method_start)
|
||||
{
|
||||
}
|
||||
|
||||
void request_parser::reset()
|
||||
{
|
||||
state_ = method_start;
|
||||
}
|
||||
|
||||
boost::tribool request_parser::consume(request& req, char input)
|
||||
{
|
||||
switch (state_)
|
||||
{
|
||||
case method_start:
|
||||
if (!is_char(input) || is_ctl(input) || is_tspecial(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
state_ = method;
|
||||
req.method.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case method:
|
||||
if (input == ' ')
|
||||
{
|
||||
state_ = uri;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.method.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case uri_start:
|
||||
if (is_ctl(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
state_ = uri;
|
||||
req.uri.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case uri:
|
||||
if (input == ' ')
|
||||
{
|
||||
state_ = http_version_h;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (is_ctl(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.uri.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case http_version_h:
|
||||
if (input == 'H')
|
||||
{
|
||||
state_ = http_version_t_1;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_t_1:
|
||||
if (input == 'T')
|
||||
{
|
||||
state_ = http_version_t_2;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_t_2:
|
||||
if (input == 'T')
|
||||
{
|
||||
state_ = http_version_p;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_p:
|
||||
if (input == 'P')
|
||||
{
|
||||
state_ = http_version_slash;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_slash:
|
||||
if (input == '/')
|
||||
{
|
||||
req.http_version_major = 0;
|
||||
req.http_version_minor = 0;
|
||||
state_ = http_version_major_start;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_major_start:
|
||||
if (is_digit(input))
|
||||
{
|
||||
req.http_version_major = req.http_version_major * 10 + input - '0';
|
||||
state_ = http_version_major;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_major:
|
||||
if (input == '.')
|
||||
{
|
||||
state_ = http_version_minor_start;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (is_digit(input))
|
||||
{
|
||||
req.http_version_major = req.http_version_major * 10 + input - '0';
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_minor_start:
|
||||
if (is_digit(input))
|
||||
{
|
||||
req.http_version_minor = req.http_version_minor * 10 + input - '0';
|
||||
state_ = http_version_minor;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case http_version_minor:
|
||||
if (input == '\r')
|
||||
{
|
||||
state_ = expecting_newline_1;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (is_digit(input))
|
||||
{
|
||||
req.http_version_minor = req.http_version_minor * 10 + input - '0';
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case expecting_newline_1:
|
||||
if (input == '\n')
|
||||
{
|
||||
state_ = header_line_start;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case header_line_start:
|
||||
if (input == '\r')
|
||||
{
|
||||
state_ = expecting_newline_3;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (!req.headers.empty() && (input == ' ' || input == '\t'))
|
||||
{
|
||||
state_ = header_lws;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.headers.push_back(header());
|
||||
req.headers.back().name.push_back(input);
|
||||
state_ = header_name;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case header_lws:
|
||||
if (input == '\r')
|
||||
{
|
||||
state_ = expecting_newline_2;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (input == ' ' || input == '\t')
|
||||
{
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (is_ctl(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
state_ = header_value;
|
||||
req.headers.back().value.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case header_name:
|
||||
if (input == ':')
|
||||
{
|
||||
state_ = space_before_header_value;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (!is_char(input) || is_ctl(input) || is_tspecial(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.headers.back().name.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case space_before_header_value:
|
||||
if (input == ' ')
|
||||
{
|
||||
state_ = header_value;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case header_value:
|
||||
if (input == '\r')
|
||||
{
|
||||
state_ = expecting_newline_2;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else if (is_ctl(input))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
req.headers.back().value.push_back(input);
|
||||
return boost::indeterminate;
|
||||
}
|
||||
case expecting_newline_2:
|
||||
if (input == '\n')
|
||||
{
|
||||
state_ = header_line_start;
|
||||
return boost::indeterminate;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
case expecting_newline_3:
|
||||
return (input == '\n');
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool request_parser::is_char(int c)
|
||||
{
|
||||
return c >= 0 && c <= 127;
|
||||
}
|
||||
|
||||
bool request_parser::is_ctl(int c)
|
||||
{
|
||||
return (c >= 0 && c <= 31) || (c == 127);
|
||||
}
|
||||
|
||||
bool request_parser::is_tspecial(int c)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '(': case ')': case '<': case '>': case '@':
|
||||
case ',': case ';': case ':': case '\\': case '"':
|
||||
case '/': case '[': case ']': case '?': case '=':
|
||||
case '{': case '}': case ' ': case '\t':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool request_parser::is_digit(int c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_SERVER3_REQUEST_PARSER_HPP
|
124
HttpServer/server.h
Normal file
124
HttpServer/server.h
Normal file
@ -0,0 +1,124 @@
|
||||
/*
|
||||
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 HTTP_ROUTER_SERVER_HPP
|
||||
#define HTTP_ROUTER_SERVER_HPP
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
#include <climits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "../typedefs.h"
|
||||
|
||||
#include "connection.h"
|
||||
#include "request_handler.h"
|
||||
|
||||
namespace http {
|
||||
|
||||
/// The top-level class of the HTTP server.
|
||||
class server: private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
/// Construct the server to listen on the specified TCP address and port, and
|
||||
/// serve up files from the given directory.
|
||||
explicit server(const std::string& address, const std::string& port, std::size_t thread_pool_size, SearchEngine<EdgeData, NodeInformationHelpDesk> * s)
|
||||
: thread_pool_size_(thread_pool_size),
|
||||
acceptor_(io_service_),
|
||||
new_connection_(new connection(io_service_, request_handler_)),
|
||||
request_handler_(s),
|
||||
sEngine(s)
|
||||
{
|
||||
// Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
|
||||
boost::asio::ip::tcp::resolver resolver(io_service_);
|
||||
boost::asio::ip::tcp::resolver::query query(address, port);
|
||||
boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
|
||||
|
||||
acceptor_.open(endpoint.protocol());
|
||||
acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
|
||||
acceptor_.bind(endpoint);
|
||||
acceptor_.listen();
|
||||
acceptor_.async_accept(new_connection_->socket(), boost::bind(&server::handle_accept, this, boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
/// Run the server's io_service loop.
|
||||
void run()
|
||||
{
|
||||
// Create a pool of threads to run all of the io_services.
|
||||
std::vector<boost::shared_ptr<boost::thread> > threads;
|
||||
for (std::size_t i = 0; i < thread_pool_size_; ++i)
|
||||
{
|
||||
boost::shared_ptr<boost::thread> thread(new boost::thread(
|
||||
boost::bind(&boost::asio::io_service::run, &io_service_)));
|
||||
threads.push_back(thread);
|
||||
}
|
||||
|
||||
// Wait for all threads in the pool to exit.
|
||||
for (std::size_t i = 0; i < threads.size(); ++i)
|
||||
threads[i]->join();
|
||||
}
|
||||
|
||||
/// Stop the server.
|
||||
void stop()
|
||||
{
|
||||
io_service_.stop();
|
||||
}
|
||||
|
||||
private:
|
||||
/// Handle completion of an asynchronous accept operation.
|
||||
void handle_accept(const boost::system::error_code& e)
|
||||
{
|
||||
if (!e)
|
||||
{
|
||||
new_connection_->start();
|
||||
new_connection_.reset(new connection(io_service_, request_handler_));
|
||||
acceptor_.async_accept(new_connection_->socket(),
|
||||
boost::bind(&server::handle_accept, this,
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
}
|
||||
|
||||
/// The number of threads that will call io_service::run().
|
||||
std::size_t thread_pool_size_;
|
||||
|
||||
/// The io_service used to perform asynchronous operations.
|
||||
boost::asio::io_service io_service_;
|
||||
|
||||
/// Acceptor used to listen for incoming connections.
|
||||
boost::asio::ip::tcp::acceptor acceptor_;
|
||||
|
||||
/// The next connection to be accepted.
|
||||
connection_ptr new_connection_;
|
||||
|
||||
/// The handler for all incoming requests.
|
||||
request_handler request_handler_;
|
||||
|
||||
/// The object to query the Routing Engine
|
||||
SearchEngine<EdgeData> * sEngine;
|
||||
|
||||
};
|
||||
|
||||
} // namespace http
|
||||
|
||||
#endif // HTTP_ROUTER_SERVER_HPP
|
661
LICENCE.TXT
Normal file
661
LICENCE.TXT
Normal file
@ -0,0 +1,661 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
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
|
||||
(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 Affero 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, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
6
Makefile
Normal file
6
Makefile
Normal file
@ -0,0 +1,6 @@
|
||||
all:
|
||||
g++ -O3 -Wall -Wno-deprecated -fopenmp -march=native -lboost_regex-mt -lboost_iostreams-mt -lboost_thread-mt -lboost_system-mt -o routed routed.cpp -DNDEBUG
|
||||
g++ createHierarchy.cpp -fopenmp -Wno-deprecated -o createHierarchy -O3 -march=native -DNDEBUG
|
||||
g++ extractNetwork.cpp -fopenmp -Wno-deprecated -o extractNetwork -O3 -march=native -I/usr/include/libxml2/ -lxml2 -DNDEBUG
|
||||
clean:
|
||||
rm -rf createHierarchy routed extractNetwork *.o *.gch
|
47
README.TXT
Normal file
47
README.TXT
Normal file
@ -0,0 +1,47 @@
|
||||
Compilation
|
||||
---
|
||||
|
||||
Compiling the source code is easy. If you are running a decent linux
|
||||
installing dependencies and running make should suffic. Make sure the following
|
||||
dependencies are installed:
|
||||
|
||||
- Boost 1.37+
|
||||
- kdtree++ 0.7+ (0.62 does not work with g++ 4.0+)
|
||||
- sparsehash 1.4+
|
||||
- g++ 4.4+
|
||||
- libxml2
|
||||
- make
|
||||
|
||||
Once the dependencies are properly installed running 'make' should build the
|
||||
binaries. The Makefile has been built for Ubuntu 10.04, but it should work
|
||||
under any recent Linux. It is possible to build and run the binaries under
|
||||
OS X, but for the time being manual compilation is required. Nevertheless,
|
||||
the code should compile under any recent flavor of UNIX.
|
||||
|
||||
Running the Server
|
||||
---
|
||||
Running the server requires preprocessing data from Openstreetmap. To do so you
|
||||
you need to acquire an .osm file. Beware of the world file, because you need
|
||||
serious computing power to preprocess it. Start with a smaller country. The
|
||||
preprocessing runs in three steps, all done by seperate programs.
|
||||
|
||||
'extractNetwork file.osm' extracts the road network of an osm file. This is
|
||||
necessary, because the osm data is not made to support fast routing out of the
|
||||
box. The output of the step is a file called 'file.osmr'
|
||||
|
||||
'createHierarchy file.osmr' preprocesses the road network and computes additional
|
||||
information that is exploited later to speed up the path computation. The output
|
||||
of this step consists of two file 'file.osmr.hsgr' and 'file.osm.hsgr'. The first
|
||||
file is the so-called hierarchy that speeds up the path computation while the
|
||||
latter one carries (among other things) geographical information.
|
||||
|
||||
'routed file.osmr.hsgr file.osm.hsgr' starts the server on TCP Port 5000. The
|
||||
server communicates over http and can be queried by any browser or http-capable
|
||||
command line tool. The server responds with KML-formatted output.Assume the
|
||||
server is installed on machine localhost and a map containing the Netherlands
|
||||
has been installed. Computing a route from Amsterdam to Den Haag can be done by
|
||||
calling
|
||||
http://localhost:5000/route&52.370197&4.890444&52.048167&4.3175
|
||||
which gives a shortest (fastest) route between the two points. To locate a
|
||||
point in the map that is nearest to a given coordinate use the following call
|
||||
http://localhost:5000/locate&52.048167&4.3175
|
130
createHierarchy.cpp
Normal file
130
createHierarchy.cpp
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
//g++ createHierarchy.cpp -fopenmp -Wno-deprecated -o createHierarchy -O3 -march=native -DNDEBUG
|
||||
|
||||
#define VERBOSE(x) x
|
||||
#define VERBOSE2(x)
|
||||
|
||||
#include <climits>
|
||||
#include <fstream>
|
||||
#include <istream>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
#include "typedefs.h"
|
||||
#include "Contractor/GraphLoader.h"
|
||||
#include "Contractor/BinaryHeap.h"
|
||||
#include "Contractor/Contractor.h"
|
||||
#include "Contractor/ContractionCleanup.h"
|
||||
#include "Contractor/DynamicGraph.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
typedef ContractionCleanup::Edge::EdgeData EdgeData;
|
||||
typedef DynamicGraph<EdgeData>::InputEdge GraphEdge;
|
||||
|
||||
vector<NodeInfo> * int2ExtNodeMap = new vector<NodeInfo>();
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
if(argc <= 1)
|
||||
{
|
||||
cerr << "usage: " << endl << argv[0] << " <osmr-data>" << endl;
|
||||
exit(-1);
|
||||
}
|
||||
cout << "preprocessing data from input file " << argv[1] << endl;
|
||||
|
||||
ifstream in;
|
||||
in.open (argv[1]);
|
||||
if (!in.is_open()) {
|
||||
cerr << "Cannot open " << argv[1] << endl; exit(-1);
|
||||
}
|
||||
vector<ImportEdge> edgeList;
|
||||
const NodeID n = readOSMRGraphFromStream(in, edgeList, int2ExtNodeMap);
|
||||
in.close();
|
||||
|
||||
char nodeOut[1024];
|
||||
char edgeOut[1024];
|
||||
strcpy(nodeOut, argv[1]);
|
||||
strcpy(edgeOut, argv[1]);
|
||||
strcat(nodeOut, ".nodes");
|
||||
strcat(edgeOut, ".hsgr");
|
||||
ofstream mapOutFile(nodeOut, ios::binary);
|
||||
|
||||
//Serializing the node map.
|
||||
for(NodeID i = 0; i < int2ExtNodeMap->size(); i++)
|
||||
{
|
||||
//mapOutFile.write((char *)&(i), sizeof(NodeID));
|
||||
mapOutFile.write((char *)&(int2ExtNodeMap->at(i)), sizeof(NodeInfo));
|
||||
}
|
||||
mapOutFile.close();
|
||||
int2ExtNodeMap->clear();
|
||||
|
||||
Contractor* contractor = new Contractor( n, edgeList );
|
||||
contractor->Run();
|
||||
|
||||
contractor->checkForAllOrigEdges(edgeList);
|
||||
|
||||
std::vector< ContractionCleanup::Edge > contractedEdges;
|
||||
contractor->GetEdges( contractedEdges );
|
||||
|
||||
ContractionCleanup * cleanup = new ContractionCleanup(n, contractedEdges);
|
||||
cleanup->Run();
|
||||
|
||||
std::vector< GraphEdge> cleanedEdgeList;
|
||||
cleanup->GetData(cleanedEdgeList);
|
||||
|
||||
ofstream edgeOutFile(edgeOut, ios::binary);
|
||||
|
||||
//Serializing the edge list.
|
||||
for(std::vector< GraphEdge>::iterator it = cleanedEdgeList.begin(); it != cleanedEdgeList.end(); it++)
|
||||
{
|
||||
int distance= it->data.distance;
|
||||
assert(distance > 0);
|
||||
bool shortcut= it->data.shortcut;
|
||||
bool forward= it->data.forward;
|
||||
bool backward= it->data.backward;
|
||||
NodeID middle= it->data.middle;
|
||||
NodeID source = it->source;
|
||||
NodeID target = it->target;
|
||||
|
||||
edgeOutFile.write((char *)&(distance), sizeof(int));
|
||||
edgeOutFile.write((char *)&(shortcut), sizeof(bool));
|
||||
edgeOutFile.write((char *)&(forward), sizeof(bool));
|
||||
edgeOutFile.write((char *)&(backward), sizeof(bool));
|
||||
edgeOutFile.write((char *)&(middle), sizeof(NodeID));
|
||||
|
||||
edgeOutFile.write((char *)&(source), sizeof(NodeID));
|
||||
edgeOutFile.write((char *)&(target), sizeof(NodeID));
|
||||
}
|
||||
edgeOutFile.close();
|
||||
cleanedEdgeList.clear();
|
||||
|
||||
delete cleanup;
|
||||
delete contractor;
|
||||
delete int2ExtNodeMap;
|
||||
}
|
480
extractNetwork.cpp
Normal file
480
extractNetwork.cpp
Normal file
@ -0,0 +1,480 @@
|
||||
/*
|
||||
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 <cassert>
|
||||
#include <climits>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <libxml/xmlreader.h>
|
||||
|
||||
#include "typedefs.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
struct _Node : NodeInfo{
|
||||
bool trafficSignal:1;
|
||||
};
|
||||
|
||||
struct _Way {
|
||||
std::vector< NodeID > path;
|
||||
enum {
|
||||
notSure = 0, oneway, bidirectional, opposite
|
||||
} direction;
|
||||
double maximumSpeed;
|
||||
bool usefull:1;
|
||||
bool access:1;
|
||||
short type;
|
||||
};
|
||||
|
||||
typedef google::dense_hash_map<NodeID, _Node> NodeMap;
|
||||
|
||||
struct _Stats {
|
||||
NodeID numberOfNodes;
|
||||
NodeID numberOfEdges;
|
||||
NodeID numberOfWays;
|
||||
// NodeID numberOfPlaces;
|
||||
// NodeID numberOfOutlines;
|
||||
NodeID numberOfMaxspeed;
|
||||
// NodeID numberOfZeroSpeed;
|
||||
// NodeID numberOfDefaultCitySpeed;
|
||||
// NodeID numberOfCityEdges;
|
||||
};
|
||||
|
||||
struct Settings {
|
||||
struct SpeedProfile {
|
||||
vector< double > speed;
|
||||
vector< string > names;
|
||||
} speedProfile;
|
||||
vector<string> accessList;
|
||||
int trafficLightPenalty;
|
||||
int indexInAccessListOf( const string & key)
|
||||
{
|
||||
for(int i = 0; i< accessList.size(); i++)
|
||||
{
|
||||
if(accessList[i] == key)
|
||||
return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
|
||||
_Way _ReadXMLWay( xmlTextReaderPtr& inputReader );
|
||||
_Node _ReadXMLNode( xmlTextReaderPtr& inputReader );
|
||||
double ApproximateDistance( const int lat1, const int lon1, const int lat2, const int lon2 );
|
||||
|
||||
_Stats stats;
|
||||
Settings settings;
|
||||
NodeMap AllNodes;
|
||||
|
||||
vector<NodeID> SignalNodes;
|
||||
vector<NodeID> UsedNodes;
|
||||
vector<_Way> UsedWays;
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
if(argc <= 1)
|
||||
{
|
||||
cerr << "usage: " << endl << argv[0] << " <file.osm>" << endl;
|
||||
exit(-1);
|
||||
}
|
||||
cout << "reading input file. This may take some time ..." << flush;
|
||||
/*
|
||||
Default Speed Profile:
|
||||
motorway 120
|
||||
motorway_link 80
|
||||
trunk 100
|
||||
trunk_link 80
|
||||
secondary 100
|
||||
secondary_link 50
|
||||
primary 100
|
||||
primary_link 50
|
||||
tertiary 100
|
||||
unclassified 50
|
||||
residential 50
|
||||
living_street 30
|
||||
service 20
|
||||
*/
|
||||
|
||||
string names[13] = { "motorway", "motorway_link", "trunk", "trunk_link", "secondary", "secondary_link", "primary", "primary_link", "tertiary", "unclassified", "residential", "living_street", "service" };
|
||||
double speeds[13] = { 120, 80, 100, 80, 100, 50, 100, 50, 100, 50, 50 , 30, 20};
|
||||
settings.speedProfile.names.insert(settings.speedProfile.names.begin(), names, names+13);
|
||||
settings.speedProfile.speed.insert(settings.speedProfile.speed.begin(), speeds, speeds+13);
|
||||
|
||||
AllNodes.set_empty_key(UINT_MAX);
|
||||
xmlTextReaderPtr inputReader = xmlNewTextReaderFilename( argv[1] );
|
||||
ofstream nodeFile("_nodes", ios::binary);
|
||||
ofstream wayFile("_ways", ios::binary);
|
||||
try {
|
||||
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 ) {
|
||||
stats.numberOfNodes++;
|
||||
_Node node = _ReadXMLNode( inputReader );
|
||||
AllNodes.insert(make_pair(node.id, node) );
|
||||
|
||||
if ( node.trafficSignal )
|
||||
SignalNodes.push_back( node.id );
|
||||
|
||||
}
|
||||
else if ( xmlStrEqual( currentName, ( const xmlChar* ) "way" ) == 1 ) {
|
||||
stats.numberOfWays++;
|
||||
_Way way = _ReadXMLWay( inputReader );
|
||||
|
||||
if ( way.usefull && way.access && way.path.size() ) {
|
||||
for ( unsigned i = 0; i < way.path.size(); ++i ) {
|
||||
UsedNodes.push_back( way.path[i] );
|
||||
}
|
||||
|
||||
if ( way.direction == _Way::opposite )
|
||||
std::reverse( way.path.begin(), way.path.end() );
|
||||
|
||||
stats.numberOfEdges += ( int ) way.path.size() - 1;
|
||||
|
||||
UsedWays.push_back(way);
|
||||
}
|
||||
}
|
||||
xmlFree( currentName );
|
||||
}
|
||||
sort(UsedNodes.begin(), UsedNodes.end());
|
||||
UsedNodes.erase(unique(UsedNodes.begin(), UsedNodes.end()), UsedNodes.end() );
|
||||
sort(SignalNodes.begin(), SignalNodes.end());
|
||||
SignalNodes.erase(unique(SignalNodes.begin(), SignalNodes.end()), SignalNodes.end() );
|
||||
cout << "ok" << endl;
|
||||
cout << endl << "Statistics: " << endl;
|
||||
cout << "All Nodes: " << stats.numberOfNodes << endl;
|
||||
cout << "Used Nodes: " << UsedNodes.size() << endl;
|
||||
cout << "Number of Ways: " << stats.numberOfWays << endl;
|
||||
cout << "Edges in graph: " << stats.numberOfEdges << endl;
|
||||
cout << "Number of ways with maxspeed information: " << stats.numberOfMaxspeed << endl;
|
||||
cout << "Number of nodes with traffic lights: " << SignalNodes.size() << endl;
|
||||
cout << "finished loading data" << endl;
|
||||
cout << "calculated edge weights and writing to disk ..." << flush;
|
||||
string name(argv[1]);
|
||||
int pos=name.find(".osm"); // pos=9
|
||||
if(pos!=string::npos)
|
||||
{
|
||||
//replace
|
||||
name.replace(pos, 5, ".osrm");
|
||||
} else {
|
||||
name.append(".osrm");
|
||||
}
|
||||
|
||||
|
||||
ofstream fout;
|
||||
fout.open(name.c_str());
|
||||
|
||||
fout << UsedNodes.size() << endl;
|
||||
for(vector<NodeID>::size_type i = 0; i < UsedNodes.size(); i++)
|
||||
{
|
||||
NodeMap::iterator it = AllNodes.find(UsedNodes[i]);
|
||||
assert(it!=AllNodes.end());
|
||||
fout << UsedNodes[i] << " " << it->second.lon << " " << it->second.lat << "\n" << flush;
|
||||
}
|
||||
UsedNodes.clear();
|
||||
fout << stats.numberOfEdges << endl;
|
||||
for(vector<_Way>::size_type i = 0; i < UsedWays.size(); i++)
|
||||
{
|
||||
vector< NodeID > & path = UsedWays[i].path;
|
||||
double speed = UsedWays[i].maximumSpeed;
|
||||
assert(UsedWays[i].type > -1 || UsedWays[i].maximumSpeed != -1);
|
||||
assert(path.size()>0);
|
||||
|
||||
for(vector< NodeID >::size_type n = 0; n < path.size()-1; n++)
|
||||
{
|
||||
//insert path[n], path[n+1]
|
||||
NodeMap::iterator startit = AllNodes.find(path[n]);
|
||||
if(startit == AllNodes.end())
|
||||
{
|
||||
cerr << "Node " << path[n] << " missing albeit referenced in way. Edge skipped" << endl;
|
||||
continue;
|
||||
}
|
||||
NodeMap::iterator targetit = AllNodes.find(path[n+1]);
|
||||
|
||||
if(targetit == AllNodes.end())
|
||||
{
|
||||
cerr << "Node << " << path[n+1] << "missing albeit reference in a way. Edge skipped" << endl;
|
||||
continue;
|
||||
}
|
||||
double distance = ApproximateDistance(startit->second.lat, startit->second.lon, targetit->second.lat, targetit->second.lon);
|
||||
if(speed == -1)
|
||||
speed = settings.speedProfile.speed[UsedWays[i].type];
|
||||
double weight = ( distance * 10. ) / (speed / 3.6);
|
||||
double intWeight = max(1, (int) weight);
|
||||
switch(UsedWays[i].direction)
|
||||
{
|
||||
case _Way::notSure:
|
||||
fout << startit->first << " " << targetit->first << " " << max(1, (int)distance) << " " << 0 << " " << intWeight << "\n";
|
||||
break;
|
||||
case _Way::oneway:
|
||||
fout << startit->first << " " << targetit->first << " " << max(1, (int)distance) << " " << 1 << " " << intWeight << "\n";
|
||||
break;
|
||||
case _Way::bidirectional:
|
||||
fout << startit->first << " " << targetit->first << " " << max(1, (int)distance) << " " << 0 << " " << intWeight << "\n";
|
||||
break;
|
||||
case _Way::opposite:
|
||||
fout << startit->first << " " << targetit->first << " " << max(1, (int)distance) << " " << 1 << " " << intWeight << "\n";
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fout.close();
|
||||
cout << "ok" << endl;
|
||||
} catch ( const std::exception& e ) {
|
||||
cerr << "Caught Execption:" << e.what() << endl;
|
||||
return false;
|
||||
}
|
||||
AllNodes.clear();
|
||||
SignalNodes.clear();
|
||||
UsedWays.clear();
|
||||
xmlFreeTextReader(inputReader);
|
||||
return true;
|
||||
}
|
||||
|
||||
_Way _ReadXMLWay( xmlTextReaderPtr& inputReader ) {
|
||||
_Way way;
|
||||
way.direction = _Way::notSure;
|
||||
way.maximumSpeed = -1;
|
||||
way.type = -1;
|
||||
way.usefull = false;
|
||||
way.access = true;
|
||||
|
||||
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" );
|
||||
if ( k != NULL && value != NULL ) {
|
||||
if ( xmlStrEqual( k, ( const xmlChar* ) "oneway" ) == 1 ) {
|
||||
if ( xmlStrEqual( value, ( const xmlChar* ) "no" ) == 1 || xmlStrEqual( value, ( const xmlChar* ) "false" ) == 1 || xmlStrEqual( value, ( const xmlChar* ) "0" ) == 1 )
|
||||
way.direction = _Way::bidirectional;
|
||||
else if ( xmlStrEqual( value, ( const xmlChar* ) "yes" ) == 1 || xmlStrEqual( value, ( const xmlChar* ) "true" ) == 1 || xmlStrEqual( value, ( const xmlChar* ) "1" ) == 1 )
|
||||
way.direction = _Way::oneway;
|
||||
else if ( xmlStrEqual( value, ( const xmlChar* ) "-1" ) == 1 )
|
||||
way.direction = _Way::opposite;
|
||||
} else if ( xmlStrEqual( k, ( const xmlChar* ) "junction" ) == 1 ) {
|
||||
if ( xmlStrEqual( value, ( const xmlChar* ) "roundabout" ) == 1 ) {
|
||||
if ( way.direction == _Way::notSure ) {
|
||||
way.direction = _Way::oneway;
|
||||
}
|
||||
if ( way.maximumSpeed == -1 )
|
||||
way.maximumSpeed = 10;
|
||||
way.usefull = true;
|
||||
}
|
||||
} else if ( xmlStrEqual( k, ( const xmlChar* ) "highway" ) == 1 ) {
|
||||
string name( ( const char* ) value );
|
||||
for ( int i = 0; i < settings.speedProfile.names.size(); i++ ) {
|
||||
if ( name == settings.speedProfile.names[i] ) {
|
||||
way.type = i;
|
||||
way.usefull = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( name == "motorway" ) {
|
||||
if ( way.direction == _Way::notSure ) {
|
||||
way.direction = _Way::oneway;
|
||||
}
|
||||
} else if ( name == "motorway_link" ) {
|
||||
if ( way.direction == _Way::notSure ) {
|
||||
way.direction = _Way::oneway;
|
||||
}
|
||||
}
|
||||
} else if ( xmlStrEqual( k, ( const xmlChar* ) "maxspeed" ) == 1 ) {
|
||||
double maxspeed = atof(( const char* ) value );
|
||||
|
||||
xmlChar buffer[100];
|
||||
xmlStrPrintf( buffer, 100, ( const xmlChar* ) "%.lf", maxspeed );
|
||||
if ( xmlStrEqual( value, buffer ) == 1 ) {
|
||||
way.maximumSpeed = maxspeed;
|
||||
stats.numberOfMaxspeed++;
|
||||
} else {
|
||||
xmlStrPrintf( buffer, 100, ( const xmlChar* ) "%.lf kmh", maxspeed );
|
||||
if ( xmlStrEqual( value, buffer ) == 1 ) {
|
||||
way.maximumSpeed = maxspeed;
|
||||
stats.numberOfMaxspeed++;
|
||||
} else {
|
||||
xmlStrPrintf( buffer, 100, ( const xmlChar* ) "%.lfkmh", maxspeed );
|
||||
if ( xmlStrEqual( value, buffer ) == 1 ) {
|
||||
way.maximumSpeed = maxspeed;
|
||||
stats.numberOfMaxspeed++;
|
||||
} else {
|
||||
xmlStrPrintf( buffer, 100, ( const xmlChar* ) "%.lf km/h", maxspeed );
|
||||
if ( xmlStrEqual( value, buffer ) == 1 ) {
|
||||
way.maximumSpeed = maxspeed;
|
||||
stats.numberOfMaxspeed++;
|
||||
} else {
|
||||
xmlStrPrintf( buffer, 100, ( const xmlChar* ) "%.lfkm/h", maxspeed );
|
||||
if ( xmlStrEqual( value, buffer ) == 1 ) {
|
||||
way.maximumSpeed = maxspeed;
|
||||
stats.numberOfMaxspeed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// string key( ( const char* ) k );
|
||||
// int index = -1;// settings.accessList.indexOf( key );
|
||||
// if ( index != -1 ) { //&& index < way.accessPriority ) {
|
||||
if ( xmlStrEqual( value, ( const xmlChar* ) "private" ) == 1
|
||||
|| xmlStrEqual( value, ( const xmlChar* ) "no" ) == 1
|
||||
|| xmlStrEqual( value, ( const xmlChar* ) "agricultural" ) == 1
|
||||
|| xmlStrEqual( value, ( const xmlChar* ) "forestry" ) == 1
|
||||
|| xmlStrEqual( value, ( const xmlChar* ) "delivery" ) == 1
|
||||
) {
|
||||
way.access = false;
|
||||
}
|
||||
else if ( xmlStrEqual( value, ( const xmlChar* ) "yes" ) == 1
|
||||
|| xmlStrEqual( value, ( const xmlChar* ) "designated" ) == 1
|
||||
|| xmlStrEqual( value, ( const xmlChar* ) "official" ) == 1
|
||||
|| xmlStrEqual( value, ( const xmlChar* ) "permissive" ) == 1
|
||||
) {
|
||||
way.access = true;
|
||||
}
|
||||
// }
|
||||
}
|
||||
|
||||
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( xmlTextReaderPtr& inputReader ) {
|
||||
_Node node;
|
||||
node.trafficSignal = false;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
double ApproximateDistance( const int lat1, const int lon1, const int lat2, const int lon2 ) {
|
||||
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;
|
||||
}
|
179
routed.cpp
Normal file
179
routed.cpp
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
#include <climits>
|
||||
#include <fstream>
|
||||
#include <istream>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#endif
|
||||
|
||||
#include "typedefs.h"
|
||||
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include "HttpServer/server.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
//typedef google::dense_hash_map<NodeID, NodeInfo> NodeMap;
|
||||
typedef ContractionCleanup::Edge::EdgeData EdgeData;
|
||||
typedef DynamicGraph<EdgeData>::InputEdge GraphEdge;
|
||||
typedef http::server server;
|
||||
|
||||
//NodeMap * int2ExtNodeMap = new NodeMap();
|
||||
|
||||
/*
|
||||
* TODO: Description of command line arguments
|
||||
*/
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
double time; // timemeasure
|
||||
|
||||
if(argc < 2)
|
||||
{
|
||||
cerr << "Correct usage:" << endl << argv[0] << " <hsgr data> <nodes data>" << endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (argv[0] == 0) {
|
||||
cerr << "Missing data files!" << endl;
|
||||
return -1;
|
||||
}
|
||||
ifstream in(argv[1], ios::binary);
|
||||
ifstream in2(argv[2], ios::binary);
|
||||
NodeInformationHelpDesk * kdtreeService = new NodeInformationHelpDesk();
|
||||
|
||||
time = get_timestamp();
|
||||
cout << "deserializing edge data from " << argv[1] << " ..." << flush;
|
||||
|
||||
std::vector< GraphEdge> edgelist;
|
||||
while(!in.eof())
|
||||
{
|
||||
GraphEdge g;
|
||||
EdgeData e;
|
||||
|
||||
int distance;
|
||||
bool shortcut;
|
||||
bool forward;
|
||||
bool backward;
|
||||
NodeID middle;
|
||||
NodeID source;
|
||||
NodeID target;
|
||||
|
||||
in.read((char *)&(distance), sizeof(int));
|
||||
assert(distance > 0);
|
||||
in.read((char *)&(shortcut), sizeof(bool));
|
||||
in.read((char *)&(forward), sizeof(bool));
|
||||
in.read((char *)&(backward), sizeof(bool));
|
||||
in.read((char *)&(middle), sizeof(NodeID));
|
||||
in.read((char *)&(source), sizeof(NodeID));
|
||||
in.read((char *)&(target), sizeof(NodeID));
|
||||
e.backward = backward; e.distance = distance; e.forward = forward; e.middle = middle; e.shortcut = shortcut;
|
||||
g.data = e; g.source = source; g.target = target;
|
||||
|
||||
edgelist.push_back(g);
|
||||
}
|
||||
|
||||
in.close();
|
||||
cout << "in " << get_timestamp() - time << "s" << endl;
|
||||
cout << "search graph has " << edgelist.size() << " edges" << endl;
|
||||
time = get_timestamp();
|
||||
cout << "deserializing node map and building kd-tree ..." << flush;
|
||||
kdtreeService->initKDTree(in2);
|
||||
cout << "in " << get_timestamp() - time << "s" << endl;
|
||||
|
||||
time = get_timestamp();
|
||||
cout << "building search graph ..." << flush;
|
||||
DynamicGraph<EdgeData> * graph = new DynamicGraph<EdgeData>(kdtreeService->getNumberOfNodes()-1, edgelist);
|
||||
cout << "checking sanity ..." << flush;
|
||||
NodeID numberOfNodes = graph->GetNumberOfNodes();
|
||||
for ( NodeID node = 0; node < numberOfNodes; ++node ) {
|
||||
for ( DynamicGraph<EdgeData>::EdgeIterator edge = graph->BeginEdges( node ), endEdges = graph->EndEdges( node ); edge != endEdges; ++edge ) {
|
||||
const NodeID start = node;
|
||||
const NodeID target = graph->GetTarget( edge );
|
||||
const EdgeData& data = graph->GetEdgeData( edge );
|
||||
const NodeID middle = data.middle;
|
||||
assert(start != target);
|
||||
if(data.shortcut)
|
||||
{
|
||||
if(graph->FindEdge(start, middle) == SPECIAL_EDGEID && graph->FindEdge(middle, start) == SPECIAL_EDGEID)
|
||||
{
|
||||
assert(false);
|
||||
cerr << "hierarchy broken" << endl; exit(-1);
|
||||
}
|
||||
if(graph->FindEdge(middle, target) == SPECIAL_EDGEID && graph->FindEdge(target, middle) == SPECIAL_EDGEID)
|
||||
{
|
||||
assert(false);
|
||||
cerr << "hierarchy broken" << endl; exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cout << "ok" << endl;
|
||||
|
||||
SearchEngine<EdgeData> * sEngine = new SearchEngine<EdgeData>(graph, kdtreeService);
|
||||
cout << "in " << get_timestamp() - time << "s" << endl;
|
||||
|
||||
time = get_timestamp();
|
||||
try {
|
||||
// Block all signals for background thread.
|
||||
sigset_t new_mask;
|
||||
sigfillset(&new_mask);
|
||||
sigset_t old_mask;
|
||||
pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask);
|
||||
|
||||
cout << "starting web server ..." << flush;
|
||||
// Run server in background thread.
|
||||
server s("0.0.0.0", "5000", omp_get_num_procs(), sEngine);
|
||||
boost::thread t(boost::bind(&http::server::run, &s));
|
||||
cout << "ok" << endl;
|
||||
|
||||
// Restore previous signals.
|
||||
pthread_sigmask(SIG_SETMASK, &old_mask, 0);
|
||||
|
||||
// Wait for signal indicating time to shut down.
|
||||
sigset_t wait_mask;
|
||||
sigemptyset(&wait_mask);
|
||||
sigaddset(&wait_mask, SIGINT);
|
||||
sigaddset(&wait_mask, SIGQUIT);
|
||||
sigaddset(&wait_mask, SIGTERM);
|
||||
pthread_sigmask(SIG_BLOCK, &wait_mask, 0);
|
||||
int sig = 0;
|
||||
sigwait(&wait_mask, &sig);
|
||||
// Stop the server.
|
||||
s.stop();
|
||||
t.join();
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
std::cerr << "exception: " << e.what() << "\n";
|
||||
}
|
||||
cout << "graceful shutdown after " << get_timestamp() - time << "s" << endl;
|
||||
delete kdtreeService;
|
||||
return 0;
|
||||
}
|
46
typedefs.h
Normal file
46
typedefs.h
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
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 TYPEDEFS_H_
|
||||
#define TYPEDEFS_H_
|
||||
|
||||
using namespace std;
|
||||
|
||||
#define VERBOSE(x) x
|
||||
#define VERBOSE2(x)
|
||||
|
||||
typedef unsigned int NodeID;
|
||||
typedef unsigned int EdgeID;
|
||||
typedef unsigned int EdgeWeight;
|
||||
static const NodeID SPECIAL_NODEID = UINT_MAX;
|
||||
static const EdgeID SPECIAL_EDGEID = UINT_MAX;
|
||||
|
||||
#include "DataStructures/NodeCoords.h"
|
||||
typedef NodeCoords<NodeID> NodeInfo;
|
||||
#include "DataStructures/Util.h"
|
||||
#include "DataStructures/NodeInformationHelpDesk.h"
|
||||
#include "Contractor/BinaryHeap.h"
|
||||
#include "Contractor/Contractor.h"
|
||||
#include "Contractor/ContractionCleanup.h"
|
||||
typedef ContractionCleanup::Edge::EdgeData EdgeData;
|
||||
#include "Contractor/DynamicGraph.h"
|
||||
#include "Contractor/SearchEngine.h"
|
||||
|
||||
#endif /* TYPEDEFS_H_ */
|
Loading…
Reference in New Issue
Block a user