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