mirror of
git://projects.qi-hardware.com/nanomap.git
synced 2024-12-03 19:41:52 +02:00
add monav plugins from http://code.google.com/p/monav/ and use them to calculate routes
This commit is contained in:
parent
2d6e62a111
commit
e18428cb23
40
app.pro
Normal file
40
app.pro
Normal file
@ -0,0 +1,40 @@
|
||||
TARGET = NanoMap
|
||||
TEMPLATE = app
|
||||
|
||||
QT += network
|
||||
|
||||
#LIBS += -Lmonav -lgpsgridclient -lcontractionhierarchiesclient
|
||||
INCLUDEPATH += monav
|
||||
|
||||
SOURCES += main.cpp \
|
||||
mainwidget.cpp \
|
||||
projection.cpp \
|
||||
abstractlayer.cpp \
|
||||
gpslayer.cpp \
|
||||
markerlayer.cpp \
|
||||
gpxlayer.cpp \
|
||||
poilayer.cpp \
|
||||
monavlayer.cpp \
|
||||
timelayer.cpp \
|
||||
batterylayer.cpp \
|
||||
mapwidget.cpp \
|
||||
markerlist.cpp \
|
||||
downloadwidget.cpp \
|
||||
routingwidget.cpp \
|
||||
gpsclient.cpp
|
||||
|
||||
HEADERS += mainwidget.h \
|
||||
projection.h \
|
||||
abstractlayer.h \
|
||||
gpslayer.h \
|
||||
markerlayer.h \
|
||||
gpxlayer.h \
|
||||
poilayer.h \
|
||||
monavlayer.h \
|
||||
timelayer.h \
|
||||
batterylayer.h \
|
||||
mapwidget.h \
|
||||
markerlist.h \
|
||||
downloadwidget.h \
|
||||
routingwidget.h \
|
||||
gpsclient.h
|
@ -28,6 +28,7 @@
|
||||
#include "gpslayer.h"
|
||||
#include "gpxlayer.h"
|
||||
#include "markerlayer.h"
|
||||
#include "monavlayer.h"
|
||||
#include "poilayer.h"
|
||||
#include "timelayer.h"
|
||||
|
||||
@ -63,6 +64,9 @@ MainWidget::MainWidget(QWidget *parent)
|
||||
l->load(QDir::homePath()+"/Maps/marker.list");
|
||||
m_map->addLayer(l, 3, "Marker");
|
||||
|
||||
l = new MonavLayer(m_map);
|
||||
m_map->addLayer(l, 2, "MoNav Routing");
|
||||
|
||||
l = new GpsLayer(m_map);
|
||||
m_map->addLayer(l, 1, "GPS-Position");
|
||||
|
||||
|
@ -40,8 +40,6 @@
|
||||
|
||||
MapWidget::MapWidget(QWidget *parent)
|
||||
: QWidget(parent),
|
||||
m_routeStart(),
|
||||
m_routeEnd(),
|
||||
m_usage(false),
|
||||
m_ui(true),
|
||||
m_zoomable(false),
|
||||
@ -302,7 +300,7 @@ void MapWidget::keyPressEvent(QKeyEvent *event)
|
||||
case Qt::Key_R:
|
||||
{
|
||||
if (event->modifiers() == Qt::NoModifier) {
|
||||
emit route(m_routeStart, m_routeEnd);
|
||||
// emit route(m_routeStart, m_routeEnd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -310,15 +308,6 @@ void MapWidget::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if (event->modifiers() == Qt::AltModifier) {
|
||||
m_takeScreenshot = true;
|
||||
} else if (event->modifiers() == Qt::NoModifier) {
|
||||
m_routeStart = geoPos();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Qt::Key_E:
|
||||
{
|
||||
if (event->modifiers() == Qt::NoModifier) {
|
||||
m_routeEnd = geoPos();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -374,19 +363,6 @@ void MapWidget::paintEvent(QPaintEvent *event)
|
||||
}
|
||||
}
|
||||
|
||||
painter.save();
|
||||
QPoint p = geo2screen(m_routeStart.x(), m_routeStart.y());
|
||||
QPolygon tri;
|
||||
tri << p << p+QPoint(-5, -9) << p+QPoint(5, -9) << p;
|
||||
painter.setBrush(Qt::red);
|
||||
painter.drawPolygon(tri);
|
||||
p = geo2screen(m_routeEnd.x(), m_routeEnd.y());
|
||||
tri.clear();
|
||||
tri << p << p+QPoint(-5, -9) << p+QPoint(5, -9) << p;
|
||||
painter.setBrush(Qt::blue);
|
||||
painter.drawPolygon(tri);
|
||||
painter.restore();
|
||||
|
||||
QMapIterator<int, AbstractLayer *> i(m_layer);
|
||||
while (i.hasNext()) {
|
||||
i.next();
|
||||
|
@ -74,7 +74,6 @@ private:
|
||||
void downloadTile(int x, int y, int level);
|
||||
void changeZoomLevel(int diff);
|
||||
|
||||
QPointF m_routeStart, m_routeEnd;
|
||||
bool m_usage, m_ui, m_zoomable;
|
||||
bool m_takeScreenshot;
|
||||
int m_screenshotNumber;
|
||||
|
242
monav/contractionhierarchies/binaryheap.h
Normal file
242
monav/contractionhierarchies/binaryheap.h
Normal file
@ -0,0 +1,242 @@
|
||||
/*
|
||||
Copyright 2010 Christian Vetter veaac.fdirct@gmail.com
|
||||
|
||||
This file is part of MoNav.
|
||||
|
||||
MoNav is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MoNav is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with MoNav. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BINARYHEAP_H_INCLUDED
|
||||
#define BINARYHEAP_H_INCLUDED
|
||||
|
||||
//Not compatible with non contiguous node ids
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <QHash>
|
||||
|
||||
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 )
|
||||
{
|
||||
}
|
||||
|
||||
Key &operator[]( NodeID node )
|
||||
{
|
||||
return nodes[node];
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
nodes.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
QHash< 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();
|
||||
nodeIndex.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
|
162
monav/contractionhierarchies/blockcache.h
Normal file
162
monav/contractionhierarchies/blockcache.h
Normal file
@ -0,0 +1,162 @@
|
||||
/*
|
||||
Copyright 2010 Christian Vetter veaac.fdirct@gmail.com
|
||||
|
||||
This file is part of MoNav.
|
||||
|
||||
MoNav is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MoNav is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with MoNav. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BLOCKCACHE_H_INCLUDED
|
||||
#define BLOCKCACHE_H_INCLUDED
|
||||
|
||||
#include <QFile>
|
||||
#include <QHash>
|
||||
#include <limits>
|
||||
#include <QtDebug>
|
||||
|
||||
// Block must have member function / variables:
|
||||
// variable id => block id
|
||||
// function void load( const unsigned char* buffer )
|
||||
template< class Block >
|
||||
class BlockCache{
|
||||
|
||||
public:
|
||||
|
||||
BlockCache()
|
||||
{
|
||||
m_cache = NULL;
|
||||
m_LRU = NULL;
|
||||
m_blocks = NULL;
|
||||
}
|
||||
|
||||
bool load( const QString& filename, int cacheBlocks, unsigned blockSize )
|
||||
{
|
||||
m_cacheBlocks = cacheBlocks;
|
||||
m_blockSize = blockSize;
|
||||
m_inputFile.setFileName( filename );
|
||||
if ( !m_inputFile.open( QIODevice::ReadOnly | QIODevice::Unbuffered ) ) {
|
||||
qCritical() << "failed to open file:" << m_inputFile.fileName();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_cache = new unsigned char[( m_cacheBlocks + 1 ) * m_blockSize];
|
||||
m_LRU = new LRUEntry[m_cacheBlocks];
|
||||
m_blocks = new Block[m_cacheBlocks];
|
||||
|
||||
m_firstLoaded = -1;
|
||||
m_lastLoaded = -1;
|
||||
m_loadedCount = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void unload ( )
|
||||
{
|
||||
m_inputFile.close();
|
||||
if ( m_cache != NULL )
|
||||
delete[] m_cache;
|
||||
if ( m_LRU != NULL )
|
||||
delete[] m_LRU;
|
||||
if ( m_blocks != NULL )
|
||||
delete[] m_blocks;
|
||||
m_cache = NULL;
|
||||
m_LRU = NULL;
|
||||
m_blocks = NULL;
|
||||
m_index.clear();
|
||||
}
|
||||
|
||||
const Block* getBlock( unsigned block )
|
||||
{
|
||||
int cacheID = m_index.value( block, -1 );
|
||||
if ( cacheID == -1 )
|
||||
return loadBlock( block );
|
||||
|
||||
useBlock( cacheID );
|
||||
return m_blocks + cacheID;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
const Block* loadBlock( unsigned block )
|
||||
{
|
||||
int freeBlock = m_loadedCount;
|
||||
// cache is full => select least recently used block
|
||||
if ( m_loadedCount == m_cacheBlocks ) {
|
||||
assert ( m_lastLoaded != -1 );
|
||||
freeBlock = m_lastLoaded;
|
||||
m_index.remove( m_blocks[freeBlock].id );
|
||||
useBlock( freeBlock );
|
||||
} else {
|
||||
//insert into the front of the list
|
||||
m_LRU[freeBlock].previousLoaded = -1;
|
||||
m_LRU[freeBlock].nextLoaded = m_firstLoaded;
|
||||
if ( m_firstLoaded != -1 )
|
||||
m_LRU[m_firstLoaded].previousLoaded = freeBlock;
|
||||
if ( m_lastLoaded == -1 )
|
||||
m_lastLoaded = freeBlock;
|
||||
m_firstLoaded = freeBlock;
|
||||
m_loadedCount++;
|
||||
}
|
||||
|
||||
//load block
|
||||
m_inputFile.seek( ( long long ) block * m_blockSize );
|
||||
m_inputFile.read( ( char* ) m_cache + freeBlock * m_blockSize, m_blockSize );
|
||||
m_blocks[freeBlock].load( block, m_cache + freeBlock * m_blockSize );
|
||||
m_index[block] = freeBlock;
|
||||
|
||||
return m_blocks + freeBlock;
|
||||
}
|
||||
|
||||
void useBlock( int cacheID )
|
||||
{
|
||||
assert( m_firstLoaded != -1 );
|
||||
if ( m_firstLoaded == cacheID )
|
||||
return;
|
||||
|
||||
LRUEntry& block = m_LRU[cacheID];
|
||||
|
||||
//remove block from the list to put it into the front
|
||||
if ( block.nextLoaded != -1 )
|
||||
m_LRU[block.nextLoaded].previousLoaded = block.previousLoaded;
|
||||
else
|
||||
m_lastLoaded = block.previousLoaded;
|
||||
|
||||
m_LRU[block.previousLoaded].nextLoaded = block.nextLoaded;
|
||||
|
||||
// insert block into the front
|
||||
m_LRU[m_firstLoaded].previousLoaded = cacheID;
|
||||
block.nextLoaded = m_firstLoaded;
|
||||
block.previousLoaded = -1;
|
||||
m_firstLoaded = cacheID;
|
||||
}
|
||||
|
||||
struct LRUEntry{
|
||||
int nextLoaded;
|
||||
int previousLoaded;
|
||||
};
|
||||
|
||||
Block* m_blocks;
|
||||
LRUEntry* m_LRU;
|
||||
unsigned char* m_cache;
|
||||
int m_firstLoaded;
|
||||
int m_lastLoaded;
|
||||
int m_loadedCount;
|
||||
int m_cacheBlocks;
|
||||
unsigned m_blockSize;
|
||||
QFile m_inputFile;
|
||||
QHash< unsigned, int > m_index;
|
||||
|
||||
};
|
||||
|
||||
#endif // BLOCKCACHE_H_INCLUDED
|
600
monav/contractionhierarchies/compressedgraph.h
Normal file
600
monav/contractionhierarchies/compressedgraph.h
Normal file
@ -0,0 +1,600 @@
|
||||
/*
|
||||
Copyright 2010 Christian Vetter veaac.fdirct@gmail.com
|
||||
|
||||
This file is part of MoNav.
|
||||
|
||||
MoNav is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MoNav is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with MoNav. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef COMPRESSEDGRAPH_H
|
||||
#define COMPRESSEDGRAPH_H
|
||||
|
||||
#include "interfaces/irouter.h"
|
||||
#include "utils/coordinates.h"
|
||||
#include "utils/bithelpers.h"
|
||||
#include "blockcache.h"
|
||||
#include <QString>
|
||||
#include <QFile>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
class CompressedGraph {
|
||||
|
||||
public :
|
||||
|
||||
typedef unsigned NodeIterator;
|
||||
|
||||
protected:
|
||||
|
||||
//TYPES
|
||||
|
||||
struct Block {
|
||||
struct Settings {
|
||||
// adress blocks from the adjacent blocks array
|
||||
unsigned char blockBits;
|
||||
// address an entry in the adjacent blocks array
|
||||
//unsigned char adjacentBlockBits; ==> can be computed from adjacentBlockcount bitsNeeded( count - 1 )
|
||||
// address an internal node with a shortcut's middle
|
||||
//unsigned char internalBits; ==> can be computed from nodeCount bitsNeeded( count - 1 );
|
||||
// address an external node in another block
|
||||
unsigned char externalBits;
|
||||
// address the first edge of a node
|
||||
unsigned char firstEdgeBits;
|
||||
// bits used for the short weight class
|
||||
unsigned char shortWeightBits;
|
||||
// bits uses for the long weight class
|
||||
unsigned char longWeightBits;
|
||||
// bits used for the difference ( x - minX )
|
||||
unsigned char xBits;
|
||||
// bits used for the difference ( y - minY )
|
||||
unsigned char yBits;
|
||||
// minimal x value
|
||||
unsigned minX;
|
||||
// minimal y value
|
||||
unsigned minY;
|
||||
// #nodes => used for the size of firstEdges
|
||||
unsigned nodeCount;
|
||||
// #adjacent blocks => used for the size of adjacentBlocks
|
||||
unsigned adjacentBlockCount;
|
||||
} settings;
|
||||
|
||||
unsigned char adjacentBlockBits;
|
||||
unsigned char internalBits;
|
||||
|
||||
unsigned edges;
|
||||
unsigned adjacentBlocks;
|
||||
unsigned firstEdges;
|
||||
unsigned nodeCoordinates;
|
||||
|
||||
unsigned id;
|
||||
const unsigned char* buffer;
|
||||
|
||||
void load( unsigned id, const unsigned char* buffer )
|
||||
{
|
||||
CompressedGraph::loadBlock( this, id, buffer );
|
||||
}
|
||||
};
|
||||
|
||||
struct PathBlock {
|
||||
|
||||
struct DataItem {
|
||||
unsigned a;
|
||||
unsigned b;
|
||||
|
||||
DataItem()
|
||||
{
|
||||
a = b = 0;
|
||||
}
|
||||
|
||||
DataItem( const IRouter::Node& node )
|
||||
{
|
||||
assert( bits_needed( node.coordinate.x ) < 32 );
|
||||
a = node.coordinate.x << 1;
|
||||
a |= 1;
|
||||
b = node.coordinate.y;
|
||||
}
|
||||
|
||||
DataItem( const IRouter::Edge& description )
|
||||
{
|
||||
a = description.name;
|
||||
a <<= 1;
|
||||
a |= description.branchingPossible ? 1 : 0;
|
||||
a <<= 1;
|
||||
b = description.type;
|
||||
b <<= 16;
|
||||
b |= description.length;
|
||||
b <<= 8;
|
||||
b |= encode_integer< 4, 4 >( description.seconds );
|
||||
}
|
||||
|
||||
bool isNode() const
|
||||
{
|
||||
return ( a & 1 ) == 1;
|
||||
}
|
||||
|
||||
bool isEdge() const
|
||||
{
|
||||
return ( a & 1 ) == 0;
|
||||
}
|
||||
|
||||
IRouter::Node toNode()
|
||||
{
|
||||
IRouter::Node node;
|
||||
node.coordinate = UnsignedCoordinate( a >> 1, b );
|
||||
return node;
|
||||
}
|
||||
|
||||
IRouter::Edge toEdge()
|
||||
{
|
||||
IRouter::Edge edge;
|
||||
edge.name = a >> 2;
|
||||
edge.branchingPossible = ( a & 2 ) == 2;
|
||||
edge.type = b >> 24;
|
||||
edge.length = ( b >> 8 ) & ( ( 1u << 16 ) -1 );
|
||||
edge.seconds = decode_integer< 4, 4 >( b & 255 );
|
||||
return edge;
|
||||
}
|
||||
};
|
||||
|
||||
unsigned id;
|
||||
const unsigned char* buffer;
|
||||
|
||||
void load( unsigned id, const unsigned char* buffer )
|
||||
{
|
||||
CompressedGraph::loadPathBlock( this, id, buffer );
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
// TYPES
|
||||
|
||||
struct Edge {
|
||||
NodeIterator source;
|
||||
NodeIterator target;
|
||||
struct Data {
|
||||
unsigned distance;
|
||||
bool shortcut : 1;
|
||||
bool forward : 1;
|
||||
bool backward : 1;
|
||||
bool unpacked : 1;
|
||||
bool reversed : 1;
|
||||
union {
|
||||
NodeIterator middle;
|
||||
unsigned id;
|
||||
};
|
||||
unsigned path;
|
||||
} data;
|
||||
|
||||
bool operator<( const Edge& right ) const {
|
||||
if ( source != right.source )
|
||||
return source < right.source;
|
||||
int l = ( data.forward ? -1 : 0 ) + ( data.backward ? 1 : 0 );
|
||||
int r = ( right.data.forward ? -1 : 0 ) + ( right.data.backward ? 1 : 0 );
|
||||
if ( l != r )
|
||||
return l < r;
|
||||
if ( target != right.target )
|
||||
return target < right.target;
|
||||
return data.distance < right.data.distance;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class EdgeIterator {
|
||||
|
||||
friend class CompressedGraph;
|
||||
|
||||
public:
|
||||
|
||||
EdgeIterator()
|
||||
{
|
||||
}
|
||||
|
||||
bool hasEdgesLeft()
|
||||
{
|
||||
return m_position < m_end;
|
||||
}
|
||||
|
||||
NodeIterator target() const { return m_target; }
|
||||
bool forward() const { return m_data.forward; }
|
||||
bool backward() const { return m_data.backward; }
|
||||
bool shortcut() const { return m_data.shortcut; }
|
||||
bool unpacked() const { return m_data.unpacked; }
|
||||
NodeIterator middle() const { return m_data.middle; }
|
||||
unsigned distance() const { return m_data.distance; }
|
||||
IRouter::Edge description() const { return IRouter::Edge( m_data.description.nameID, m_data.description.branchingPossible, m_data.description.type, 1, ( m_data.distance + 5 ) / 10 ); }
|
||||
#ifdef NDEBUG
|
||||
private:
|
||||
#endif
|
||||
|
||||
EdgeIterator( unsigned source, const Block& block, unsigned position, unsigned end ) :
|
||||
m_block( &block ), m_source( source ), m_position( position ), m_end( end )
|
||||
{
|
||||
}
|
||||
|
||||
const Block* m_block;
|
||||
NodeIterator m_target;
|
||||
NodeIterator m_source;
|
||||
unsigned m_position;
|
||||
unsigned m_end;
|
||||
|
||||
struct EdgeData {
|
||||
unsigned distance;
|
||||
bool shortcut : 1;
|
||||
bool forward : 1;
|
||||
bool backward : 1;
|
||||
bool unpacked : 1;
|
||||
bool reversed : 1;
|
||||
union {
|
||||
NodeIterator middle;
|
||||
struct {
|
||||
unsigned nameID : 30;
|
||||
bool branchingPossible : 1;
|
||||
unsigned type;
|
||||
} description;
|
||||
};
|
||||
unsigned path;
|
||||
} m_data;
|
||||
};
|
||||
|
||||
// FUNCTIONS
|
||||
|
||||
CompressedGraph()
|
||||
{
|
||||
m_loaded = false;
|
||||
}
|
||||
|
||||
~CompressedGraph()
|
||||
{
|
||||
if ( m_loaded )
|
||||
unloadGraph();
|
||||
}
|
||||
|
||||
bool loadGraph( QString filename, unsigned cacheSize )
|
||||
{
|
||||
if ( m_loaded )
|
||||
unloadGraph();
|
||||
QFile settingsFile( filename + "_config" );
|
||||
if ( !settingsFile.open( QIODevice::ReadOnly ) ) {
|
||||
qCritical() << "failed to open file:" << settingsFile.fileName();
|
||||
return false;
|
||||
}
|
||||
m_settings.read( settingsFile );
|
||||
if ( !m_blockCache.load( filename + "_edges", cacheSize / m_settings.blockSize / 2 + 1, m_settings.blockSize ) )
|
||||
return false;
|
||||
if ( !m_pathCache.load( filename + "_paths", cacheSize / m_settings.blockSize / 2 + 1, m_settings.blockSize ) )
|
||||
return false;
|
||||
m_loaded = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
EdgeIterator edges( NodeIterator node )
|
||||
{
|
||||
unsigned blockID = nodeToBlock( node );
|
||||
unsigned internal = nodeToInternal( node );
|
||||
const Block* block = getBlock( blockID );
|
||||
return unpackFirstEdges( *block, internal );
|
||||
}
|
||||
|
||||
EdgeIterator findEdge( NodeIterator source, NodeIterator target, unsigned id )
|
||||
{
|
||||
if ( source < target )
|
||||
std::swap( source, target );
|
||||
EdgeIterator e = edges( source );
|
||||
while ( e.hasEdgesLeft() ) {
|
||||
unpackNextEdge( &e );
|
||||
if ( e.target() != target )
|
||||
continue;
|
||||
if ( e.shortcut() )
|
||||
continue;
|
||||
if ( id != 0 ) {
|
||||
id--;
|
||||
continue;
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
assert( false );
|
||||
return e;
|
||||
}
|
||||
|
||||
void unpackNextEdge( EdgeIterator* edge )
|
||||
{
|
||||
const Block& block = *edge->m_block;
|
||||
EdgeIterator::EdgeData& edgeData = edge->m_data;
|
||||
const unsigned char* buffer = block.buffer + ( edge->m_position >> 3 );
|
||||
int offset = edge->m_position & 7;
|
||||
|
||||
// forward + backward flag
|
||||
bool forwardAndBackward = read_unaligned_unsigned( &buffer, 1, &offset ) != 0;
|
||||
if ( forwardAndBackward ) {
|
||||
edgeData.forward = true;
|
||||
edgeData.backward = true;
|
||||
} else {
|
||||
edgeData.forward = read_unaligned_unsigned( &buffer, 1, &offset ) != 0;
|
||||
edgeData.backward = !edgeData.forward;
|
||||
}
|
||||
|
||||
// target
|
||||
bool internalTarget = read_unaligned_unsigned( &buffer, 1, &offset ) != 0;
|
||||
if ( internalTarget ) {
|
||||
unsigned target = read_unaligned_unsigned( &buffer, bits_needed( edge->m_source ), &offset );
|
||||
edge->m_target = nodeFromDescriptor( block.id, target );
|
||||
} else {
|
||||
unsigned adjacentBlock = read_unaligned_unsigned( &buffer, block.adjacentBlockBits, &offset );
|
||||
unsigned target = read_unaligned_unsigned( &buffer, block.settings.externalBits, &offset );
|
||||
unsigned adjacentBlockPosition = block.adjacentBlocks + adjacentBlock * block.settings.blockBits;
|
||||
unsigned targetBlock = read_unaligned_unsigned( block.buffer + ( adjacentBlockPosition >> 3 ), block.settings.blockBits, adjacentBlockPosition & 7 );
|
||||
edge->m_target = nodeFromDescriptor( targetBlock, target );
|
||||
}
|
||||
|
||||
// weight
|
||||
bool longWeight = block.settings.shortWeightBits == block.settings.longWeightBits;
|
||||
if ( !longWeight )
|
||||
longWeight = read_unaligned_unsigned( &buffer, 1, &offset ) != 0;
|
||||
edgeData.distance = read_unaligned_unsigned( &buffer, longWeight ? block.settings.longWeightBits : block.settings.shortWeightBits, &offset );
|
||||
|
||||
// unpacked
|
||||
edgeData.unpacked = read_unaligned_unsigned( &buffer, 1, &offset ) != 0;
|
||||
if ( edgeData.unpacked ) {
|
||||
if ( forwardAndBackward )
|
||||
edgeData.reversed = read_unaligned_unsigned( &buffer, 1, &offset ) != 0;
|
||||
else
|
||||
edgeData.reversed = edgeData.backward;
|
||||
edgeData.path = read_unaligned_unsigned( &buffer, m_settings.pathBits, &offset );
|
||||
}
|
||||
|
||||
// shortcut
|
||||
edgeData.shortcut = read_unaligned_unsigned( &buffer, 1, &offset ) != 0;
|
||||
if ( edgeData.shortcut ) {
|
||||
if ( !edgeData.unpacked ) {
|
||||
unsigned middle = read_unaligned_unsigned( &buffer, block.internalBits, &offset );
|
||||
edgeData.middle = nodeFromDescriptor( block.id, middle );
|
||||
}
|
||||
}
|
||||
|
||||
// edge description
|
||||
if ( !edgeData.shortcut && !edgeData.unpacked ) {
|
||||
edgeData.description.type = read_unaligned_unsigned( &buffer, m_settings.typeBits, &offset );
|
||||
edgeData.description.nameID = read_unaligned_unsigned( &buffer, m_settings.nameBits, &offset );
|
||||
edgeData.description.branchingPossible = read_unaligned_unsigned( &buffer, 1, &offset );
|
||||
}
|
||||
|
||||
edge->m_position = ( buffer - block.buffer ) * 8 + offset;
|
||||
}
|
||||
|
||||
IRouter::Node node( NodeIterator node )
|
||||
{
|
||||
unsigned blockID = nodeToBlock( node );
|
||||
unsigned internal = nodeToInternal( node );
|
||||
const Block* block = getBlock( blockID );
|
||||
IRouter::Node result;
|
||||
unpackCoordinates( *block, internal, &result.coordinate );
|
||||
return result;
|
||||
}
|
||||
|
||||
unsigned numberOfNodes() const
|
||||
{
|
||||
return m_settings.numberOfNodes;
|
||||
}
|
||||
|
||||
unsigned numberOfEdges() const
|
||||
{
|
||||
return m_settings.numberOfEdges;
|
||||
}
|
||||
|
||||
template< class T, class S >
|
||||
void path( const EdgeIterator& edge, T path, S edges, bool forward )
|
||||
{
|
||||
assert( edge.unpacked() );
|
||||
unsigned pathBegin = path->size();
|
||||
unsigned edgesBegin = edges->size();
|
||||
int increase = edge.m_data.reversed ? -1 : 1;
|
||||
|
||||
IRouter::Node targetNode = node( edge.target() );
|
||||
unsigned pathID = edge.m_data.path;
|
||||
|
||||
if ( !forward ) {
|
||||
PathBlock::DataItem data = unpackPath( pathID );
|
||||
assert( data.isNode() );
|
||||
path->push_back( data.toNode().coordinate );
|
||||
}
|
||||
|
||||
pathID += increase;
|
||||
|
||||
while( true ) {
|
||||
PathBlock::DataItem data = unpackPath( pathID );
|
||||
if ( data.isEdge() ) {
|
||||
edges->push_back( data.toEdge() );
|
||||
pathID += increase;
|
||||
continue;
|
||||
}
|
||||
assert( data.isNode() );
|
||||
IRouter::Node node = data.toNode();
|
||||
if ( node.coordinate.x == targetNode.coordinate.x && node.coordinate.y == targetNode.coordinate.y )
|
||||
break;
|
||||
path->push_back( node.coordinate );
|
||||
pathID += increase;
|
||||
}
|
||||
|
||||
if ( forward ) {
|
||||
path->push_back( targetNode.coordinate );
|
||||
} else {
|
||||
std::reverse( path->begin() + pathBegin, path->end() );
|
||||
std::reverse( edges->begin() + edgesBegin, edges->end() );
|
||||
}
|
||||
|
||||
assert( edges->size() != ( int ) edgesBegin ); // at least one edge description has to be present
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
// TYPES
|
||||
|
||||
struct GlobalSettings {
|
||||
unsigned blockSize;
|
||||
unsigned char internalBits;
|
||||
unsigned char pathBits;
|
||||
unsigned char typeBits;
|
||||
unsigned char nameBits;
|
||||
unsigned numberOfNodes;
|
||||
unsigned numberOfEdges;
|
||||
|
||||
void read( QFile& in )
|
||||
{
|
||||
in.read( ( char* ) &blockSize, sizeof( blockSize ) );
|
||||
in.read( ( char* ) &internalBits, sizeof( internalBits ) );
|
||||
in.read( ( char* ) &pathBits, sizeof( pathBits ) );
|
||||
in.read( ( char* ) &typeBits, sizeof( typeBits ) );
|
||||
in.read( ( char* ) &nameBits, sizeof( nameBits ) );
|
||||
in.read( ( char* ) &numberOfNodes, sizeof( numberOfNodes ) );
|
||||
in.read( ( char* ) &numberOfEdges, sizeof( numberOfEdges ) );
|
||||
}
|
||||
|
||||
void write( QFile& out )
|
||||
{
|
||||
out.write( ( const char* ) &blockSize, sizeof( blockSize ) );
|
||||
out.write( ( const char* ) &internalBits, sizeof( internalBits ) );
|
||||
out.write( ( const char* ) &pathBits, sizeof( pathBits ) );
|
||||
out.write( ( const char* ) &typeBits, sizeof( typeBits ) );
|
||||
out.write( ( const char* ) &nameBits, sizeof( nameBits ) );
|
||||
out.write( ( const char* ) &numberOfNodes, sizeof( numberOfNodes ) );
|
||||
out.write( ( const char* ) &numberOfEdges, sizeof( numberOfEdges ) );
|
||||
}
|
||||
};
|
||||
|
||||
struct nodeDescriptor {
|
||||
unsigned block;
|
||||
unsigned node;
|
||||
};
|
||||
|
||||
// FUNCTIONS
|
||||
|
||||
PathBlock::DataItem unpackPath( unsigned position ) {
|
||||
unsigned blockID = position / ( m_settings.blockSize / 8 );
|
||||
unsigned internal = ( position % ( m_settings.blockSize / 8 ) ) * 8;
|
||||
const PathBlock* block = getPathBlock( blockID );
|
||||
PathBlock::DataItem data;
|
||||
data.a = *( ( unsigned* ) ( block->buffer + internal ) );
|
||||
data.b = *( ( unsigned* ) ( block->buffer + internal + 4 ) );
|
||||
return data;
|
||||
}
|
||||
|
||||
void unpackCoordinates( const Block& block, unsigned node, UnsignedCoordinate* result )
|
||||
{
|
||||
unsigned position = block.nodeCoordinates + ( block.settings.xBits + block.settings.yBits ) * node;
|
||||
const unsigned char* buffer = block.buffer + ( position >> 3 );
|
||||
int offset = position & 7;
|
||||
result->x = read_unaligned_unsigned( &buffer, block.settings.xBits, &offset ) + block.settings.minX;
|
||||
result->y = read_unaligned_unsigned( buffer, block.settings.yBits, offset ) + block.settings.minY;
|
||||
}
|
||||
|
||||
EdgeIterator unpackFirstEdges( const Block& block, unsigned node )
|
||||
{
|
||||
unsigned position = block.firstEdges + block.settings.firstEdgeBits * node;
|
||||
const unsigned char* buffer = block.buffer + ( position >> 3 );
|
||||
int offset = position & 7;
|
||||
unsigned begin = read_unaligned_unsigned( &buffer, block.settings.firstEdgeBits, &offset );
|
||||
unsigned end = read_unaligned_unsigned( buffer, block.settings.firstEdgeBits, offset );
|
||||
return EdgeIterator( node, block, begin + block.edges, end + block.edges );
|
||||
}
|
||||
|
||||
const Block* getBlock( unsigned block )
|
||||
{
|
||||
return m_blockCache.getBlock( block );
|
||||
}
|
||||
|
||||
const PathBlock* getPathBlock( unsigned block )
|
||||
{
|
||||
return m_pathCache.getBlock( block );
|
||||
}
|
||||
|
||||
unsigned nodeToBlock( NodeIterator node )
|
||||
{
|
||||
return node >> m_settings.internalBits;
|
||||
}
|
||||
|
||||
unsigned nodeToInternal( NodeIterator node )
|
||||
{
|
||||
return read_bits( node, m_settings.internalBits );
|
||||
}
|
||||
|
||||
NodeIterator nodeFromDescriptor( nodeDescriptor node )
|
||||
{
|
||||
NodeIterator result = ( node.block << m_settings.internalBits ) | node.node;
|
||||
assert( nodeToBlock( result ) == node.block );
|
||||
assert( nodeToInternal( result ) == node.node );
|
||||
return result;
|
||||
}
|
||||
|
||||
NodeIterator nodeFromDescriptor( unsigned block, unsigned node )
|
||||
{
|
||||
NodeIterator result = ( block << m_settings.internalBits ) | node;
|
||||
assert( nodeToBlock( result ) == block );
|
||||
assert( nodeToInternal( result ) == node );
|
||||
return result;
|
||||
}
|
||||
|
||||
static void loadBlock( Block* block, unsigned blockID, const unsigned char* blockBuffer )
|
||||
{
|
||||
const unsigned char* buffer = blockBuffer;
|
||||
int offset = 0;
|
||||
|
||||
// read settings
|
||||
block->settings.blockBits = read_unaligned_unsigned( &buffer, 8, &offset );
|
||||
block->settings.externalBits = read_unaligned_unsigned( &buffer, 8, &offset );
|
||||
block->settings.firstEdgeBits = read_unaligned_unsigned( &buffer, 8, &offset );
|
||||
block->settings.shortWeightBits = read_unaligned_unsigned( &buffer, 8, &offset );
|
||||
block->settings.longWeightBits = read_unaligned_unsigned( &buffer, 8, &offset );
|
||||
block->settings.xBits = read_unaligned_unsigned( &buffer, 8, &offset );
|
||||
block->settings.yBits = read_unaligned_unsigned( &buffer, 8, &offset );
|
||||
block->settings.minX = read_unaligned_unsigned( &buffer, 32, &offset );
|
||||
block->settings.minY = read_unaligned_unsigned( &buffer, 32, &offset );
|
||||
block->settings.nodeCount = read_unaligned_unsigned( &buffer, 32, &offset );
|
||||
block->settings.adjacentBlockCount = read_unaligned_unsigned( &buffer, 32, &offset );
|
||||
|
||||
// set other values
|
||||
block->internalBits = bits_needed( block->settings.nodeCount - 1 );
|
||||
block->adjacentBlockBits = bits_needed( block->settings.adjacentBlockCount - 1 );
|
||||
block->id = blockID;
|
||||
block->buffer = blockBuffer;
|
||||
|
||||
// compute offsets
|
||||
block->nodeCoordinates = ( buffer - blockBuffer ) * 8 + offset;
|
||||
block->adjacentBlocks = block->nodeCoordinates + ( block->settings.xBits + block->settings.yBits ) * block->settings.nodeCount;
|
||||
block->firstEdges = block->adjacentBlocks + block->settings.blockBits * block->settings.adjacentBlockCount;
|
||||
block->edges = block->firstEdges + block->settings.firstEdgeBits * ( block->settings.nodeCount + 1 );
|
||||
}
|
||||
|
||||
static void loadPathBlock( PathBlock* block, unsigned blockID, const unsigned char* blockBuffer )
|
||||
{
|
||||
block->id = blockID;
|
||||
block->buffer = blockBuffer;
|
||||
}
|
||||
|
||||
void unloadGraph()
|
||||
{
|
||||
m_blockCache.unload();
|
||||
m_pathCache.unload();
|
||||
}
|
||||
|
||||
// VARIABLES
|
||||
|
||||
GlobalSettings m_settings;
|
||||
BlockCache< Block > m_blockCache;
|
||||
BlockCache< PathBlock > m_pathCache;
|
||||
bool m_loaded;
|
||||
};
|
||||
|
||||
#endif // COMPRESSEDGRAPH_H
|
407
monav/contractionhierarchies/contractionhierarchiesclient.cpp
Normal file
407
monav/contractionhierarchies/contractionhierarchiesclient.cpp
Normal file
@ -0,0 +1,407 @@
|
||||
/*
|
||||
Copyright 2010 Christian Vetter veaac.fdirct@gmail.com
|
||||
|
||||
This file is part of MoNav.
|
||||
|
||||
MoNav is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MoNav is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY
|
||||
{
|
||||
}
|
||||
without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with MoNav. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "contractionhierarchiesclient.h"
|
||||
#include "utils/qthelpers.h"
|
||||
#include <QtDebug>
|
||||
#include <stack>
|
||||
#ifndef NOGUI
|
||||
#include <QMessageBox>
|
||||
#endif
|
||||
|
||||
ContractionHierarchiesClient::ContractionHierarchiesClient()
|
||||
{
|
||||
m_heapForward = NULL;
|
||||
m_heapBackward = NULL;
|
||||
}
|
||||
|
||||
ContractionHierarchiesClient::~ContractionHierarchiesClient()
|
||||
{
|
||||
unload();
|
||||
}
|
||||
|
||||
|
||||
QString ContractionHierarchiesClient::GetName()
|
||||
{
|
||||
return "Contraction Hierarchies";
|
||||
}
|
||||
|
||||
void ContractionHierarchiesClient::SetInputDirectory( const QString& dir )
|
||||
{
|
||||
m_directory = dir;
|
||||
}
|
||||
|
||||
void ContractionHierarchiesClient::ShowSettings()
|
||||
{
|
||||
#ifndef NOGUI
|
||||
QMessageBox::information( NULL, "Settings", "No settings available" );
|
||||
#endif
|
||||
}
|
||||
|
||||
void ContractionHierarchiesClient::unload()
|
||||
{
|
||||
if ( m_heapForward != NULL )
|
||||
delete m_heapForward;
|
||||
m_heapForward = NULL;
|
||||
if ( m_heapBackward != NULL )
|
||||
delete m_heapBackward;
|
||||
m_heapBackward = NULL;
|
||||
m_types.clear();
|
||||
}
|
||||
|
||||
bool ContractionHierarchiesClient::LoadData()
|
||||
{
|
||||
QString filename = fileInDirectory( m_directory,"Contraction Hierarchies" );
|
||||
unload();
|
||||
|
||||
if ( !m_graph.loadGraph( filename, 1024 * 1024 * 4 ) )
|
||||
return false;
|
||||
|
||||
m_namesFile.setFileName( filename + "_names" );
|
||||
if ( !openQFile( &m_namesFile, QIODevice::ReadOnly ) )
|
||||
return false;
|
||||
m_names = ( const char* ) m_namesFile.map( 0, m_namesFile.size() );
|
||||
if ( m_names == NULL )
|
||||
return false;
|
||||
m_namesFile.close();
|
||||
|
||||
m_heapForward = new Heap( m_graph.numberOfNodes() );
|
||||
m_heapBackward = new Heap( m_graph.numberOfNodes() );
|
||||
|
||||
QFile typeFile( filename + "_types" );
|
||||
if ( !openQFile( &typeFile, QIODevice::ReadOnly ) )
|
||||
return false;
|
||||
|
||||
QByteArray buffer = typeFile.readAll();
|
||||
QString types = QString::fromUtf8( buffer.constData() );
|
||||
m_types = types.split( ';' );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContractionHierarchiesClient::GetRoute( double* distance, QVector< Node>* pathNodes, QVector< Edge >* pathEdges, const IGPSLookup::Result& source, const IGPSLookup::Result& target )
|
||||
{
|
||||
m_heapForward->Clear();
|
||||
m_heapBackward->Clear();
|
||||
|
||||
*distance = computeRoute( source, target, pathNodes, pathEdges );
|
||||
if ( *distance == std::numeric_limits< int >::max() )
|
||||
return false;
|
||||
|
||||
// is it shorter to drive along the edge?
|
||||
if ( target.source == source.source && target.target == source.target && source.edgeID == target.edgeID ) {
|
||||
EdgeIterator targetEdge = m_graph.findEdge( target.source, target.target, target.edgeID );
|
||||
double onEdgeDistance = fabs( target.percentage - source.percentage ) * targetEdge.distance();
|
||||
if ( onEdgeDistance < *distance ) {
|
||||
if ( ( targetEdge.forward() && targetEdge.backward() ) || source.percentage < target.percentage ) {
|
||||
pathNodes->clear();
|
||||
pathEdges->clear();
|
||||
pathNodes->push_back( source.nearestPoint );
|
||||
|
||||
QVector< Node > tempNodes;
|
||||
if ( targetEdge.unpacked() )
|
||||
m_graph.path( targetEdge, &tempNodes, pathEdges, target.target == targetEdge.target() );
|
||||
else
|
||||
pathEdges->push_back( targetEdge.description() );
|
||||
|
||||
if ( target.previousWayCoordinates < source.previousWayCoordinates ) {
|
||||
for ( unsigned pathID = target.previousWayCoordinates; pathID < source.previousWayCoordinates; pathID++ )
|
||||
pathNodes->push_back( tempNodes[pathID - 1] );
|
||||
std::reverse( pathNodes->begin() + 1, pathNodes->end() );
|
||||
} else {
|
||||
for ( unsigned pathID = source.previousWayCoordinates; pathID < target.previousWayCoordinates; pathID++ )
|
||||
pathNodes->push_back( tempNodes[pathID - 1] );
|
||||
}
|
||||
|
||||
pathNodes->push_back( target.nearestPoint );
|
||||
pathEdges->front().length = pathNodes->size() - 1;
|
||||
*distance = onEdgeDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*distance /= 10;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContractionHierarchiesClient::GetName( QString* result, unsigned name )
|
||||
{
|
||||
*result = QString::fromUtf8( m_names + name );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContractionHierarchiesClient::GetNames( QVector< QString >* result, QVector< unsigned > names )
|
||||
{
|
||||
result->resize( names.size() );
|
||||
for ( int i = 0; i < names.size(); i++ )
|
||||
( *result )[i] = QString::fromUtf8( m_names + names[i] );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContractionHierarchiesClient::GetType( QString* result, unsigned type )
|
||||
{
|
||||
*result = m_types[type];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContractionHierarchiesClient::GetTypes( QVector< QString >* result, QVector< unsigned > types )
|
||||
{
|
||||
result->resize( types.size() );
|
||||
for ( int i = 0; i < types.size(); i++ )
|
||||
( *result )[i] = m_types[types[i]];
|
||||
return true;
|
||||
}
|
||||
|
||||
template< class EdgeAllowed, class StallEdgeAllowed >
|
||||
void ContractionHierarchiesClient::computeStep( Heap* heapForward, Heap* heapBackward, const EdgeAllowed& edgeAllowed, const StallEdgeAllowed& stallEdgeAllowed, NodeIterator* middle, int* targetDistance ) {
|
||||
|
||||
const NodeIterator node = heapForward->DeleteMin();
|
||||
const int distance = heapForward->GetKey( node );
|
||||
|
||||
if ( heapForward->GetData( node ).stalled )
|
||||
return;
|
||||
|
||||
if ( heapBackward->WasInserted( node ) && !heapBackward->GetData( node ).stalled ) {
|
||||
const int newDistance = heapBackward->GetKey( node ) + distance;
|
||||
if ( newDistance < *targetDistance ) {
|
||||
*middle = node;
|
||||
*targetDistance = newDistance;
|
||||
}
|
||||
}
|
||||
|
||||
if ( distance > *targetDistance ) {
|
||||
heapForward->DeleteAll();
|
||||
return;
|
||||
}
|
||||
for ( EdgeIterator edge = m_graph.edges( node ); edge.hasEdgesLeft(); ) {
|
||||
m_graph.unpackNextEdge( &edge );
|
||||
const NodeIterator to = edge.target();
|
||||
const int edgeWeight = edge.distance();
|
||||
assert( edgeWeight > 0 );
|
||||
const int toDistance = distance + edgeWeight;
|
||||
|
||||
if ( stallEdgeAllowed( edge.forward(), edge.backward() ) && heapForward->WasInserted( to ) ) {
|
||||
const int shorterDistance = heapForward->GetKey( to ) + edgeWeight;
|
||||
if ( shorterDistance < distance ) {
|
||||
//perform a bfs starting at node
|
||||
//only insert nodes when a sub-optimal path can be proven
|
||||
//insert node into the stall queue
|
||||
heapForward->GetKey( node ) = shorterDistance;
|
||||
heapForward->GetData( node ).stalled = true;
|
||||
m_stallQueue.push( node );
|
||||
|
||||
while ( !m_stallQueue.empty() ) {
|
||||
//get node from the queue
|
||||
const NodeIterator stallNode = m_stallQueue.front();
|
||||
m_stallQueue.pop();
|
||||
const int stallDistance = heapForward->GetKey( stallNode );
|
||||
|
||||
//iterate over outgoing edges
|
||||
for ( EdgeIterator stallEdge = m_graph.edges( stallNode ); stallEdge.hasEdgesLeft(); ) {
|
||||
m_graph.unpackNextEdge( &stallEdge );
|
||||
//is edge outgoing/reached/stalled?
|
||||
if ( !edgeAllowed( stallEdge.forward(), stallEdge.backward() ) )
|
||||
continue;
|
||||
const NodeIterator stallTo = stallEdge.target();
|
||||
if ( !heapForward->WasInserted( stallTo ) )
|
||||
continue;
|
||||
if ( heapForward->GetData( stallTo ).stalled == true )
|
||||
continue;
|
||||
|
||||
const int stallToDistance = stallDistance + stallEdge.distance();
|
||||
//sub-optimal path found -> insert stallTo
|
||||
if ( stallToDistance < heapForward->GetKey( stallTo ) ) {
|
||||
if ( heapForward->WasRemoved( stallTo ) )
|
||||
heapForward->GetKey( stallTo ) = stallToDistance;
|
||||
else
|
||||
heapForward->DecreaseKey( stallTo, stallToDistance );
|
||||
|
||||
m_stallQueue.push( stallTo );
|
||||
heapForward->GetData( stallTo ).stalled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( edgeAllowed( edge.forward(), edge.backward() ) ) {
|
||||
//New Node discovered -> Add to Heap + Node Info Storage
|
||||
if ( !heapForward->WasInserted( to ) )
|
||||
heapForward->Insert( to, toDistance, node );
|
||||
|
||||
//Found a shorter Path -> Update distance
|
||||
else if ( toDistance <= heapForward->GetKey( to ) ) {
|
||||
heapForward->DecreaseKey( to, toDistance );
|
||||
//new parent + unstall
|
||||
heapForward->GetData( to ).parent = node;
|
||||
heapForward->GetData( to ).stalled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ContractionHierarchiesClient::computeRoute( const IGPSLookup::Result& source, const IGPSLookup::Result& target, QVector< Node>* pathNodes, QVector< Edge >* pathEdges ) {
|
||||
EdgeIterator sourceEdge = m_graph.findEdge( source.source, source.target, source.edgeID );
|
||||
unsigned sourceWeight = sourceEdge.distance();
|
||||
EdgeIterator targetEdge = m_graph.findEdge( target.source, target.target, target.edgeID );
|
||||
unsigned targetWeight = targetEdge.distance();
|
||||
|
||||
//insert source into heap
|
||||
m_heapForward->Insert( source.target, sourceWeight - sourceWeight * source.percentage, source.target );
|
||||
if ( sourceEdge.backward() && sourceEdge.forward() && source.target != source.source )
|
||||
m_heapForward->Insert( source.source, sourceWeight * source.percentage, source.source );
|
||||
|
||||
//insert target into heap
|
||||
m_heapBackward->Insert( target.source, targetWeight * target.percentage, target.source );
|
||||
if ( targetEdge.backward() && targetEdge.forward() && target.target != target.source )
|
||||
m_heapBackward->Insert( target.target, targetWeight - targetWeight * target.percentage, target.target );
|
||||
|
||||
int targetDistance = std::numeric_limits< int >::max();
|
||||
NodeIterator middle = ( NodeIterator ) 0;
|
||||
AllowForwardEdge forward;
|
||||
AllowBackwardEdge backward;
|
||||
|
||||
while ( m_heapForward->Size() + m_heapBackward->Size() > 0 ) {
|
||||
|
||||
if ( m_heapForward->Size() > 0 )
|
||||
computeStep( m_heapForward, m_heapBackward, forward, backward, &middle, &targetDistance );
|
||||
|
||||
if ( m_heapBackward->Size() > 0 )
|
||||
computeStep( m_heapBackward, m_heapForward, backward, forward, &middle, &targetDistance );
|
||||
|
||||
}
|
||||
|
||||
if ( targetDistance == std::numeric_limits< int >::max() )
|
||||
return std::numeric_limits< int >::max();
|
||||
|
||||
std::stack< NodeIterator > stack;
|
||||
NodeIterator pathNode = middle;
|
||||
while ( true ) {
|
||||
NodeIterator parent = m_heapForward->GetData( pathNode ).parent;
|
||||
stack.push( pathNode );
|
||||
if ( parent == pathNode )
|
||||
break;
|
||||
pathNode = parent;
|
||||
}
|
||||
|
||||
pathNodes->push_back( source.nearestPoint );
|
||||
bool reverseSourceDescription = pathNode != source.target;
|
||||
if ( source.source == source.target && sourceEdge.backward() && sourceEdge.forward() && source.percentage < 0.5 )
|
||||
reverseSourceDescription = !reverseSourceDescription;
|
||||
if ( sourceEdge.unpacked() ) {
|
||||
bool unpackSourceForward = source.target != sourceEdge.target() ? reverseSourceDescription : !reverseSourceDescription;
|
||||
m_graph.path( sourceEdge, pathNodes, pathEdges, unpackSourceForward );
|
||||
if ( reverseSourceDescription ) {
|
||||
pathNodes->remove( 1, pathNodes->size() - 1 - source.previousWayCoordinates );
|
||||
} else {
|
||||
pathNodes->remove( 1, source.previousWayCoordinates - 1 );
|
||||
}
|
||||
} else {
|
||||
pathNodes->push_back( m_graph.node( pathNode ) );
|
||||
pathEdges->push_back( sourceEdge.description() );
|
||||
}
|
||||
pathEdges->front().length = pathNodes->size() - 1;
|
||||
|
||||
while ( stack.size() > 1 ) {
|
||||
const NodeIterator node = stack.top();
|
||||
stack.pop();
|
||||
unpackEdge( node, stack.top(), true, pathNodes, pathEdges );
|
||||
}
|
||||
|
||||
pathNode = middle;
|
||||
while ( true ) {
|
||||
NodeIterator parent = m_heapBackward->GetData( pathNode ).parent;
|
||||
if ( parent == pathNode )
|
||||
break;
|
||||
unpackEdge( parent, pathNode, false, pathNodes, pathEdges );
|
||||
pathNode = parent;
|
||||
}
|
||||
|
||||
int begin = pathNodes->size();
|
||||
bool reverseTargetDescription = pathNode != target.source;
|
||||
if ( target.source == target.target && targetEdge.backward() && targetEdge.forward() && target.percentage > 0.5 )
|
||||
reverseSourceDescription = !reverseSourceDescription;
|
||||
if ( targetEdge.unpacked() ) {
|
||||
bool unpackTargetForward = target.target != targetEdge.target() ? reverseTargetDescription : !reverseTargetDescription;
|
||||
m_graph.path( targetEdge, pathNodes, pathEdges, unpackTargetForward );
|
||||
if ( reverseTargetDescription ) {
|
||||
pathNodes->resize( pathNodes->size() - target.previousWayCoordinates );
|
||||
} else {
|
||||
pathNodes->resize( begin + target.previousWayCoordinates - 1 );
|
||||
}
|
||||
} else {
|
||||
pathEdges->push_back( targetEdge.description() );
|
||||
}
|
||||
pathNodes->push_back( target.nearestPoint );
|
||||
pathEdges->back().length = pathNodes->size() - begin;
|
||||
|
||||
return targetDistance;
|
||||
}
|
||||
|
||||
bool ContractionHierarchiesClient::unpackEdge( const NodeIterator source, const NodeIterator target, bool forward, QVector< Node >* pathNodes, QVector< Edge >* pathEdges ) {
|
||||
EdgeIterator shortestEdge;
|
||||
|
||||
unsigned distance = std::numeric_limits< unsigned >::max();
|
||||
for ( EdgeIterator edge = m_graph.edges( source ); edge.hasEdgesLeft(); ) {
|
||||
m_graph.unpackNextEdge( &edge );
|
||||
if ( edge.target() != target )
|
||||
continue;
|
||||
if ( forward && !edge.forward() )
|
||||
continue;
|
||||
if ( !forward && !edge.backward() )
|
||||
continue;
|
||||
if ( edge.distance() > distance )
|
||||
continue;
|
||||
distance = edge.distance();
|
||||
shortestEdge = edge;
|
||||
}
|
||||
|
||||
if ( shortestEdge.unpacked() ) {
|
||||
m_graph.path( shortestEdge, pathNodes, pathEdges, forward );
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( !shortestEdge.shortcut() ) {
|
||||
pathEdges->push_back( shortestEdge.description() );
|
||||
if ( forward )
|
||||
pathNodes->push_back( m_graph.node( target ).coordinate );
|
||||
else
|
||||
pathNodes->push_back( m_graph.node( source ).coordinate );
|
||||
return true;
|
||||
}
|
||||
|
||||
const NodeIterator middle = shortestEdge.middle();
|
||||
|
||||
if ( forward ) {
|
||||
unpackEdge( middle, source, false, pathNodes, pathEdges );
|
||||
unpackEdge( middle, target, true, pathNodes, pathEdges );
|
||||
return true;
|
||||
} else {
|
||||
unpackEdge( middle, target, false, pathNodes, pathEdges );
|
||||
unpackEdge( middle, source, true, pathNodes, pathEdges );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Q_EXPORT_PLUGIN2( contractionhierarchiesclient, ContractionHierarchiesClient )
|
||||
|
93
monav/contractionhierarchies/contractionhierarchiesclient.h
Normal file
93
monav/contractionhierarchies/contractionhierarchiesclient.h
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
Copyright 2010 Christian Vetter veaac.fdirct@gmail.com
|
||||
|
||||
This file is part of MoNav.
|
||||
|
||||
MoNav is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MoNav is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with MoNav. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CONTRACTIONHIERARCHIESCLIENT_H
|
||||
#define CONTRACTIONHIERARCHIESCLIENT_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
#include "interfaces/irouter.h"
|
||||
#include "binaryheap.h"
|
||||
#include "compressedgraph.h"
|
||||
#include <queue>
|
||||
|
||||
class ContractionHierarchiesClient : public QObject, public IRouter
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES( IRouter )
|
||||
public:
|
||||
ContractionHierarchiesClient();
|
||||
virtual ~ContractionHierarchiesClient();
|
||||
|
||||
virtual QString GetName();
|
||||
virtual void SetInputDirectory( const QString& dir );
|
||||
virtual void ShowSettings();
|
||||
virtual bool LoadData();
|
||||
virtual bool GetRoute( double* distance, QVector< Node>* pathNodes, QVector< Edge >* pathEdges, const IGPSLookup::Result& source, const IGPSLookup::Result& target );
|
||||
virtual bool GetName( QString* result, unsigned name );
|
||||
virtual bool GetNames( QVector< QString >* result, QVector< unsigned > names );
|
||||
virtual bool GetType( QString* result, unsigned type );
|
||||
virtual bool GetTypes( QVector< QString >* result, QVector< unsigned > types );
|
||||
|
||||
protected:
|
||||
struct HeapData {
|
||||
CompressedGraph::NodeIterator parent;
|
||||
bool stalled: 1;
|
||||
HeapData( CompressedGraph::NodeIterator p ) {
|
||||
parent = p;
|
||||
stalled = false;
|
||||
}
|
||||
};
|
||||
|
||||
class AllowForwardEdge {
|
||||
public:
|
||||
bool operator()( bool forward, bool /*backward*/ ) const {
|
||||
return forward;
|
||||
}
|
||||
};
|
||||
|
||||
class AllowBackwardEdge {
|
||||
public:
|
||||
bool operator()( bool /*forward*/, bool backward ) const {
|
||||
return backward;
|
||||
}
|
||||
};
|
||||
|
||||
typedef CompressedGraph::NodeIterator NodeIterator;
|
||||
typedef CompressedGraph::EdgeIterator EdgeIterator;
|
||||
typedef BinaryHeap< NodeIterator, int, int, HeapData, MapStorage< NodeIterator, unsigned > > Heap;
|
||||
|
||||
CompressedGraph m_graph;
|
||||
const char* m_names;
|
||||
QFile m_namesFile;
|
||||
Heap* m_heapForward;
|
||||
Heap* m_heapBackward;
|
||||
std::queue< NodeIterator > m_stallQueue;
|
||||
QString m_directory;
|
||||
QStringList m_types;
|
||||
|
||||
void unload();
|
||||
template< class EdgeAllowed, class StallEdgeAllowed >
|
||||
void computeStep( Heap* heapForward, Heap* heapBackward, const EdgeAllowed& edgeAllowed, const StallEdgeAllowed& stallEdgeAllowed, NodeIterator* middle, int* targetDistance );
|
||||
int computeRoute( const IGPSLookup::Result& source, const IGPSLookup::Result& target, QVector< Node>* pathNodes, QVector< Edge >* pathEdges );
|
||||
bool unpackEdge( const NodeIterator source, const NodeIterator target, bool forward, QVector< Node>* pathNodes, QVector< Edge >* pathEdges );
|
||||
|
||||
};
|
||||
|
||||
#endif // CONTRACTIONHIERARCHIESCLIENT_H
|
@ -0,0 +1,25 @@
|
||||
TEMPLATE = lib
|
||||
CONFIG += plugin
|
||||
#CONFIG += debug
|
||||
DESTDIR = ..
|
||||
unix {
|
||||
QMAKE_CXXFLAGS_RELEASE -= -O2
|
||||
QMAKE_CXXFLAGS_RELEASE += -O3 \
|
||||
-Wno-unused-function
|
||||
QMAKE_CXXFLAGS_DEBUG += -Wno-unused-function
|
||||
}
|
||||
|
||||
HEADERS += \
|
||||
utils/coordinates.h \
|
||||
utils/config.h \
|
||||
blockcache.h \
|
||||
binaryheap.h \
|
||||
interfaces/irouter.h \
|
||||
contractionhierarchiesclient.h \
|
||||
compressedgraph.h \
|
||||
interfaces/igpslookup.h \
|
||||
utils/bithelpers.h \
|
||||
utils/qthelpers.h
|
||||
|
||||
SOURCES += \
|
||||
contractionhierarchiesclient.cpp
|
659
monav/contractionhierarchies/contractor.h
Normal file
659
monav/contractionhierarchies/contractor.h
Normal file
@ -0,0 +1,659 @@
|
||||
/*
|
||||
Copyright 2010 Christian Vetter veaac.fdirct@gmail.com
|
||||
|
||||
This file is part of MoNav.
|
||||
|
||||
MoNav is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MoNav is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with MoNav. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CONTRACTOR_H_INCLUDED
|
||||
#define CONTRACTOR_H_INCLUDED
|
||||
#include <vector>
|
||||
#include <omp.h>
|
||||
#include <limits>
|
||||
#include "utils/qthelpers.h"
|
||||
#include "dynamicgraph.h"
|
||||
#include "binaryheap.h"
|
||||
#include "utils/config.h"
|
||||
|
||||
class Contractor {
|
||||
|
||||
public:
|
||||
|
||||
struct Witness {
|
||||
NodeID source;
|
||||
NodeID target;
|
||||
NodeID middle;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
struct _EdgeData {
|
||||
unsigned distance;
|
||||
unsigned originalEdges : 29;
|
||||
bool shortcut : 1;
|
||||
bool forward : 1;
|
||||
bool backward : 1;
|
||||
union {
|
||||
NodeID middle; // shortcut
|
||||
unsigned id; // original edge
|
||||
};
|
||||
} data;
|
||||
|
||||
struct _HeapData {
|
||||
};
|
||||
|
||||
typedef DynamicGraph< _EdgeData > _DynamicGraph;
|
||||
typedef BinaryHeap< NodeID, NodeID, unsigned, _HeapData > _Heap;
|
||||
typedef _DynamicGraph::InputEdge _ImportEdge;
|
||||
|
||||
struct _ThreadData {
|
||||
_Heap heap;
|
||||
std::vector< _ImportEdge > insertedEdges;
|
||||
std::vector< Witness > witnessList;
|
||||
std::vector< NodeID > neighbours;
|
||||
_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() const {
|
||||
qDebug( "%d\t%d\t%lf\t%lf\t%lf\t%lf\t%lf", iteration, nodes, independent, contraction, inserting, removing, updating );
|
||||
}
|
||||
};
|
||||
|
||||
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 {
|
||||
qDebug( "Iteration\tNodes\tIndependent\tContraction\tInserting\tRemoving\tUpdating" );
|
||||
}
|
||||
|
||||
void PrintSummary() const {
|
||||
PrintHeader();
|
||||
GetSum().PrintStatistics();
|
||||
}
|
||||
|
||||
void Print() const {
|
||||
PrintHeader();
|
||||
for ( int i = 0, e = ( int ) iterations.size(); i < e; ++i )
|
||||
iterations[i].PrintStatistics();
|
||||
}
|
||||
|
||||
void Insert( const _LogItem& data ) {
|
||||
iterations.push_back( data );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
template< class InputEdge >
|
||||
Contractor( int nodes, const std::vector< InputEdge >& inputEdges ) {
|
||||
std::vector< _ImportEdge > edges;
|
||||
edges.reserve( 2 * inputEdges.size() );
|
||||
int skippedLargeEdges = 0;
|
||||
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( i->distance * 10.0 + 0.5, 1.0 );
|
||||
if ( edge.data.distance > 24 * 60 * 60 * 10 ) {
|
||||
skippedLargeEdges++;
|
||||
continue;
|
||||
}
|
||||
edge.data.shortcut = false;
|
||||
edge.data.id = i - inputEdges.begin();
|
||||
edge.data.forward = true;
|
||||
edge.data.backward = i->bidirectional;
|
||||
edge.data.originalEdges = 1;
|
||||
|
||||
if ( edge.data.distance < 1 ) {
|
||||
qDebug() << edge.source << edge.target << edge.data.forward << edge.data.backward << edge.data.distance << edge.data.id << i->distance;
|
||||
}
|
||||
|
||||
if ( edge.source == edge.target ) {
|
||||
_loops.push_back( edge );
|
||||
continue;
|
||||
}
|
||||
|
||||
edges.push_back( edge );
|
||||
std::swap( edge.source, edge.target );
|
||||
edge.data.forward = i->bidirectional;
|
||||
edge.data.backward = true;
|
||||
edges.push_back( edge );
|
||||
}
|
||||
if ( skippedLargeEdges != 0 )
|
||||
qDebug( "Skipped %d edges with too large edge weight", skippedLargeEdges );
|
||||
std::sort( edges.begin(), edges.end() );
|
||||
|
||||
_graph = new _DynamicGraph( nodes, edges );
|
||||
|
||||
std::vector< _ImportEdge >().swap( edges );
|
||||
}
|
||||
|
||||
~Contractor() {
|
||||
delete _graph;
|
||||
}
|
||||
|
||||
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 ) );
|
||||
}
|
||||
qDebug( "%d nodes, %d edges", numberOfNodes, _graph->GetNumberOfEdges() );
|
||||
qDebug( "using %d threads", maxThreads );
|
||||
|
||||
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;
|
||||
|
||||
qDebug( "Initialise Elimination PQ... " );
|
||||
_LogItem statistics0;
|
||||
statistics0.updating = _Timestamp();
|
||||
statistics0.iteration = 0;
|
||||
#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 );
|
||||
}
|
||||
}
|
||||
qDebug( "done" );
|
||||
|
||||
statistics0.updating = _Timestamp() - statistics0.updating;
|
||||
log.Insert( statistics0 );
|
||||
|
||||
log.PrintHeader();
|
||||
statistics0.PrintStatistics();
|
||||
|
||||
while ( levelID < numberOfNodes ) {
|
||||
_LogItem statistics;
|
||||
statistics.iteration = iteration++;
|
||||
const int last = ( int ) remainingNodes.size();
|
||||
|
||||
//determine independent node set
|
||||
double timeLast = _Timestamp();
|
||||
#pragma omp parallel
|
||||
{
|
||||
_ThreadData* const data = threadData[omp_get_thread_num()];
|
||||
#pragma omp for schedule ( guided )
|
||||
for ( int i = 0; i < last; ++i ) {
|
||||
const NodeID node = remainingNodes[i].first;
|
||||
remainingNodes[i].second = _IsIndependent( nodePriority, nodeData, data, node );
|
||||
}
|
||||
}
|
||||
_NodePartitionor functor;
|
||||
const std::vector < std::pair < NodeID, bool > >::const_iterator first = stable_partition( remainingNodes.begin(), remainingNodes.end(), functor );
|
||||
const int firstIndependent = first - remainingNodes.begin();
|
||||
statistics.nodes = last - firstIndependent;
|
||||
statistics.independent += _Timestamp() - timeLast;
|
||||
timeLast = _Timestamp();
|
||||
|
||||
//contract independent nodes
|
||||
#pragma omp parallel
|
||||
{
|
||||
_ThreadData* const 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* const 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];
|
||||
_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* const 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();
|
||||
//qDebug( 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();
|
||||
qDebug( "Total Time: %lf s", log.GetSum().GetTotalTime() );
|
||||
|
||||
}
|
||||
|
||||
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;
|
||||
if ( data.shortcut )
|
||||
newEdge.data.middle = data.middle;
|
||||
else
|
||||
newEdge.data.id = data.id;
|
||||
newEdge.data.forward = data.forward;
|
||||
newEdge.data.backward = data.backward;
|
||||
edges->push_back( newEdge );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template< class Edge >
|
||||
void GetLoops( std::vector< Edge >* edges ) {
|
||||
for ( unsigned i = 0; i < _loops.size(); i++ ) {
|
||||
Edge newEdge;
|
||||
newEdge.source = _loops[i].source;
|
||||
newEdge.target = _loops[i].target;
|
||||
newEdge.data.distance = _loops[i].data.distance;
|
||||
newEdge.data.shortcut = _loops[i].data.shortcut;
|
||||
newEdge.data.id = _loops[i].data.id;
|
||||
newEdge.data.forward = _loops[i].data.forward;
|
||||
newEdge.data.backward = _loops[i].data.backward;
|
||||
edges->push_back( newEdge );
|
||||
}
|
||||
}
|
||||
|
||||
void GetWitnessList( std::vector< Witness >& list ) {
|
||||
list = _witnessList;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
double _Timestamp() {
|
||||
static Timer timer;
|
||||
return ( double ) timer.elapsed() / 1000;
|
||||
}
|
||||
|
||||
bool _ConstructCH( _DynamicGraph* _graph );
|
||||
|
||||
void _Dijkstra( const unsigned maxDistance, const int maxNodes, _ThreadData* const data ){
|
||||
|
||||
_Heap& heap = data->heap;
|
||||
|
||||
int nodes = 0;
|
||||
while ( heap.Size() > 0 ) {
|
||||
const NodeID node = heap.DeleteMin();
|
||||
const unsigned distance = heap.GetKey( node );
|
||||
if ( nodes++ > maxNodes )
|
||||
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 unsigned 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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double _Evaluate( _ThreadData* const data, _PriorityData* const nodeData, NodeID node ){
|
||||
_ContractionInformation stats;
|
||||
|
||||
//perform simulated contraction
|
||||
_Contract< true > ( data, node, &stats );
|
||||
|
||||
// Result will contain the priority
|
||||
double result;
|
||||
if ( stats.edgesDeleted == 0 || stats.originalEdgesDeleted == 0 )
|
||||
result = 1 * nodeData->depth;
|
||||
else
|
||||
result = 2 * ((( double ) stats.edgesAdded ) / stats.edgesDeleted ) + 1 * ((( double ) stats.originalEdgesAdded ) / stats.originalEdgesDeleted ) + 1 * nodeData->depth;
|
||||
assert( result >= 0 );
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
template< bool Simulate > bool _Contract( _ThreadData* const data, NodeID node, _ContractionInformation* const stats = NULL ) {
|
||||
_Heap& heap = data->heap;
|
||||
//std::vector< Witness >& witnessList = data->witnessList;
|
||||
int insertedEdgesSize = data->insertedEdges.size();
|
||||
std::vector< _ImportEdge >& insertedEdges = data->insertedEdges;
|
||||
|
||||
for ( _DynamicGraph::EdgeIterator inEdge = _graph->BeginEdges( node ), endInEdges = _graph->EndEdges( node ); inEdge != endInEdges; ++inEdge ) {
|
||||
const _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() );
|
||||
unsigned 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 unsigned 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 );
|
||||
}
|
||||
|
||||
if ( Simulate )
|
||||
_Dijkstra( maxDistance, 500, data );
|
||||
else
|
||||
_Dijkstra( maxDistance, 1000, 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;
|
||||
insertedEdges.push_back( newEdge );
|
||||
std::swap( newEdge.source, newEdge.target );
|
||||
newEdge.data.forward = false;
|
||||
newEdge.data.backward = true;
|
||||
insertedEdges.push_back( newEdge );
|
||||
}
|
||||
}
|
||||
/*else if ( !Simulate ) {
|
||||
Witness witness;
|
||||
witness.source = source;
|
||||
witness.target = target;
|
||||
witness.middle = node;
|
||||
witnessList.push_back( witness );
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
if ( !Simulate ) {
|
||||
for ( int i = insertedEdgesSize, iend = insertedEdges.size(); i < iend; i++ ) {
|
||||
bool found = false;
|
||||
for ( int other = i + 1 ; other < iend ; ++other ) {
|
||||
if ( insertedEdges[other].source != insertedEdges[i].source )
|
||||
continue;
|
||||
if ( insertedEdges[other].target != insertedEdges[i].target )
|
||||
continue;
|
||||
if ( insertedEdges[other].data.distance != insertedEdges[i].data.distance )
|
||||
continue;
|
||||
if ( insertedEdges[other].data.shortcut != insertedEdges[i].data.shortcut )
|
||||
continue;
|
||||
insertedEdges[other].data.forward |= insertedEdges[i].data.forward;
|
||||
insertedEdges[other].data.backward |= insertedEdges[i].data.backward;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if ( !found )
|
||||
insertedEdges[insertedEdgesSize++] = insertedEdges[i];
|
||||
}
|
||||
insertedEdges.resize( insertedEdgesSize );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _DeleteIncommingEdges( _ThreadData* const data, NodeID node ) {
|
||||
std::vector< NodeID >& neighbours = data->neighbours;
|
||||
neighbours.clear();
|
||||
|
||||
//find all neighbours
|
||||
for ( _DynamicGraph::EdgeIterator e = _graph->BeginEdges( node ) ; e < _graph->EndEdges( node ) ; ++e ) {
|
||||
const NodeID u = _graph->GetTarget( e );
|
||||
if ( u == node )
|
||||
continue;
|
||||
neighbours.push_back( u );
|
||||
}
|
||||
//eliminate duplicate entries ( forward + backward edges )
|
||||
std::sort( neighbours.begin(), neighbours.end() );
|
||||
neighbours.resize( std::unique( neighbours.begin(), neighbours.end() ) - neighbours.begin() );
|
||||
|
||||
for ( int i = 0, e = ( int ) neighbours.size(); i < e; ++i ) {
|
||||
const NodeID u = neighbours[i];
|
||||
_graph->DeleteEdgesTo( u, node );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _UpdateNeighbours( std::vector< double >* priorities, std::vector< _PriorityData >* const nodeData, _ThreadData* const data, NodeID node ) {
|
||||
std::vector< NodeID >& neighbours = data->neighbours;
|
||||
neighbours.clear();
|
||||
|
||||
//find all neighbours
|
||||
for ( _DynamicGraph::EdgeIterator e = _graph->BeginEdges( node ) ; e < _graph->EndEdges( node ) ; ++e ) {
|
||||
const NodeID u = _graph->GetTarget( e );
|
||||
if ( u == node )
|
||||
continue;
|
||||
neighbours.push_back( u );
|
||||
( *nodeData )[u].depth = std::max(( *nodeData )[node].depth + 1, ( *nodeData )[u].depth );
|
||||
}
|
||||
//eliminate duplicate entries ( forward + backward edges )
|
||||
std::sort( neighbours.begin(), neighbours.end() );
|
||||
neighbours.resize( std::unique( neighbours.begin(), neighbours.end() ) - neighbours.begin() );
|
||||
|
||||
for ( int i = 0, e = ( int ) neighbours.size(); i < e; ++i ) {
|
||||
const NodeID u = neighbours[i];
|
||||
( *priorities )[u] = _Evaluate( data, &( *nodeData )[u], u );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool _IsIndependent( const std::vector< double >& priorities, const std::vector< _PriorityData >& nodeData, _ThreadData* const data, NodeID node ) {
|
||||
const double priority = priorities[node];
|
||||
|
||||
std::vector< NodeID >& neighbours = data->neighbours;
|
||||
neighbours.clear();
|
||||
|
||||
for ( _DynamicGraph::EdgeIterator e = _graph->BeginEdges( node ) ; e < _graph->EndEdges( node ) ; ++e ) {
|
||||
const NodeID target = _graph->GetTarget( e );
|
||||
const double targetPriority = priorities[target];
|
||||
assert( targetPriority >= 0 );
|
||||
//found a neighbour with lower priority?
|
||||
if ( priority > targetPriority )
|
||||
return false;
|
||||
//tie breaking
|
||||
if ( priority == targetPriority && nodeData[node].bias < nodeData[target].bias )
|
||||
return false;
|
||||
neighbours.push_back( target );
|
||||
}
|
||||
|
||||
std::sort( neighbours.begin(), neighbours.end() );
|
||||
neighbours.resize( std::unique( neighbours.begin(), neighbours.end() ) - neighbours.begin() );
|
||||
|
||||
//examine all neighbours that are at most 2 hops away
|
||||
for ( std::vector< NodeID >::const_iterator i = neighbours.begin(), lastNode = neighbours.end(); i != lastNode; ++i ) {
|
||||
const NodeID u = *i;
|
||||
|
||||
for ( _DynamicGraph::EdgeIterator e = _graph->BeginEdges( u ) ; e < _graph->EndEdges( u ) ; ++e ) {
|
||||
const NodeID target = _graph->GetTarget( e );
|
||||
|
||||
const double targetPriority = priorities[target];
|
||||
assert( targetPriority >= 0 );
|
||||
//found a neighbour with lower priority?
|
||||
if ( priority > targetPriority )
|
||||
return false;
|
||||
//tie breaking
|
||||
if ( priority == targetPriority && nodeData[node].bias < nodeData[target].bias )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
_DynamicGraph* _graph;
|
||||
std::vector< Witness > _witnessList;
|
||||
std::vector< _ImportEdge > _loops;
|
||||
};
|
||||
|
||||
#endif // CONTRACTOR_H_INCLUDED
|
1
monav/contractionhierarchies/interfaces
Symbolic link
1
monav/contractionhierarchies/interfaces
Symbolic link
@ -0,0 +1 @@
|
||||
../interfaces/
|
1
monav/contractionhierarchies/utils
Symbolic link
1
monav/contractionhierarchies/utils
Symbolic link
@ -0,0 +1 @@
|
||||
../utils/
|
286
monav/gpsgrid/cell.h
Normal file
286
monav/gpsgrid/cell.h
Normal file
@ -0,0 +1,286 @@
|
||||
/*
|
||||
Copyright 2010 Christian Vetter veaac.fdirct@gmail.com
|
||||
|
||||
This file is part of MoNav.
|
||||
|
||||
MoNav is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MoNav is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with MoNav. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CELL_H
|
||||
#define CELL_H
|
||||
|
||||
#include "utils/config.h"
|
||||
#include "utils/coordinates.h"
|
||||
#include "utils/bithelpers.h"
|
||||
#include "utils/edgeconnector.h"
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
namespace gg
|
||||
{
|
||||
|
||||
class Cell {
|
||||
|
||||
public:
|
||||
|
||||
struct Edge {
|
||||
unsigned pathID;
|
||||
unsigned short pathLength;
|
||||
unsigned short edgeID;
|
||||
NodeID source;
|
||||
NodeID target;
|
||||
bool bidirectional;
|
||||
bool operator==( const Edge& right ) const {
|
||||
if ( source != right.source )
|
||||
return false;
|
||||
if ( target != right.target )
|
||||
return false;
|
||||
if ( bidirectional != right.bidirectional )
|
||||
return false;
|
||||
if ( edgeID != right.edgeID )
|
||||
return false;
|
||||
if ( pathLength != right.pathLength )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector< Edge > edges;
|
||||
std::vector< UnsignedCoordinate > coordinates;
|
||||
|
||||
bool operator==( const Cell& right ) const
|
||||
{
|
||||
if ( edges.size() != right.edges.size() )
|
||||
return false;
|
||||
for ( int i = 0; i < ( int ) edges.size(); i++ ) {
|
||||
if ( !( edges[i] == right.edges[i] ) )
|
||||
return false;
|
||||
for ( unsigned path = 0; path < edges[i].pathLength; path++ ) {
|
||||
if ( coordinates[path + edges[i].pathID].x != right.coordinates[path + right.edges[i].pathID].x )
|
||||
return false;
|
||||
if ( coordinates[path + edges[i].pathID].y != right.coordinates[path + right.edges[i].pathID].y )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t write( unsigned char* buffer, UnsignedCoordinate min, UnsignedCoordinate max )
|
||||
{
|
||||
unsigned char* const oldBuffer = buffer;
|
||||
|
||||
NodeID minID = std::numeric_limits< NodeID >::max();
|
||||
NodeID maxID = 0;
|
||||
unsigned short maxEdgeID = 0;
|
||||
unsigned short maxPathLength = 0;
|
||||
|
||||
{
|
||||
// build edge connector data structures
|
||||
std::vector< EdgeConnector< unsigned >::Edge > connectorEdges;
|
||||
std::vector< unsigned > resultSegments;
|
||||
std::vector< unsigned > resultSegmentDescriptions;
|
||||
std::vector< bool > resultReversed;
|
||||
|
||||
for ( unsigned i = 0; i < ( unsigned ) edges.size(); i++ ) {
|
||||
EdgeConnector< unsigned >::Edge newEdge;
|
||||
newEdge.source = edges[i].source;
|
||||
newEdge.target = edges[i].target;
|
||||
newEdge.reverseable = edges[i].bidirectional;
|
||||
connectorEdges.push_back( newEdge );
|
||||
}
|
||||
|
||||
EdgeConnector< unsigned >::run( &resultSegments, &resultSegmentDescriptions, &resultReversed, connectorEdges );
|
||||
|
||||
for ( unsigned i = 0; i < ( unsigned ) edges.size(); i++ ) {
|
||||
if ( resultReversed[i] ) {
|
||||
std::swap( edges[i].source, edges[i].target );
|
||||
std::reverse( coordinates.begin() + edges[i].pathID, coordinates.begin() + edges[i].pathID + edges[i].pathLength );
|
||||
}
|
||||
}
|
||||
|
||||
std::vector< Edge > reorderedEdges;
|
||||
for ( unsigned i = 0; i < ( unsigned ) resultSegmentDescriptions.size(); i++ )
|
||||
reorderedEdges.push_back( edges[resultSegmentDescriptions[i]] );
|
||||
|
||||
edges.swap( reorderedEdges );
|
||||
}
|
||||
|
||||
std::vector< NodeID > nodes;
|
||||
for ( std::vector< Edge >::iterator i = edges.begin(), e = edges.end(); i != e; ++i ) {
|
||||
minID = std::min( minID, i->source );
|
||||
minID = std::min( minID, i->target );
|
||||
maxID = std::max( maxID, i->source );
|
||||
maxID = std::max( maxID, i->target );
|
||||
maxID = std::max( maxID, i->target );
|
||||
maxEdgeID = std::max( maxEdgeID, i->edgeID );
|
||||
assert( i->pathLength > 1 );
|
||||
maxPathLength = std::max( maxPathLength, ( unsigned short ) ( i->pathLength - 2 ) );
|
||||
|
||||
nodes.push_back( i->source );
|
||||
nodes.push_back( i->target );
|
||||
}
|
||||
|
||||
std::sort( nodes.begin(), nodes.end() );
|
||||
nodes.resize( std::unique( nodes.begin(), nodes.end() ) - nodes.begin() );
|
||||
|
||||
const char xBits = bits_needed( max.x - min.x );
|
||||
const char yBits = bits_needed( max.y - min.y );
|
||||
const char idBits = bits_needed( maxID - minID );
|
||||
const char edgeIDBits = bits_needed( maxEdgeID );
|
||||
const char pathLengthBits = bits_needed( maxPathLength );
|
||||
|
||||
int offset = 0;
|
||||
write_unaligned_unsigned( &buffer, edges.size(), 32, &offset );
|
||||
write_unaligned_unsigned( &buffer, minID, 32, &offset );
|
||||
write_unaligned_unsigned( &buffer, idBits, 6, &offset );
|
||||
write_unaligned_unsigned( &buffer, edgeIDBits, 6, &offset );
|
||||
write_unaligned_unsigned( &buffer, pathLengthBits, 6, &offset );
|
||||
|
||||
NodeID lastTarget = std::numeric_limits< NodeID >::max();
|
||||
std::vector< UnsignedCoordinate > wayBuffer;
|
||||
for ( std::vector< Edge >::const_iterator i = edges.begin(), e = edges.end(); i != e; i++ ) {
|
||||
bool reuse = lastTarget == i->source;
|
||||
|
||||
write_unaligned_unsigned( &buffer, reuse ? 1 : 0, 1, &offset );
|
||||
if ( !reuse )
|
||||
write_unaligned_unsigned( &buffer, i->source - minID, idBits, &offset );
|
||||
write_unaligned_unsigned( &buffer, i->target - minID, idBits, &offset );
|
||||
write_unaligned_unsigned( &buffer, i->edgeID, edgeIDBits, &offset );
|
||||
write_unaligned_unsigned( &buffer, i->bidirectional ? 1 : 0, 1, &offset );
|
||||
write_unaligned_unsigned( &buffer, i->pathLength - 2, pathLengthBits, &offset );
|
||||
|
||||
assert( i->pathLength >= 2 );
|
||||
for ( int pathID = 1; pathID < i->pathLength - 1; pathID++ )
|
||||
wayBuffer.push_back( coordinates[pathID + i->pathID] );
|
||||
|
||||
lastTarget = i->target;
|
||||
}
|
||||
|
||||
std::vector< UnsignedCoordinate > nodeCoordinates( nodes.size() );
|
||||
for ( std::vector< Edge >::iterator i = edges.begin(), e = edges.end(); i != e; ++i ) {
|
||||
unsigned sourcePos = lower_bound( nodes.begin(), nodes.end(), i->source ) - nodes.begin();
|
||||
unsigned targetPos = lower_bound( nodes.begin(), nodes.end(), i->target ) - nodes.begin();
|
||||
nodeCoordinates[sourcePos] = coordinates[i->pathID];
|
||||
nodeCoordinates[targetPos] = coordinates[i->pathID + i->pathLength - 1];
|
||||
}
|
||||
|
||||
for ( std::vector< UnsignedCoordinate >::const_iterator i = nodeCoordinates.begin(), iend = nodeCoordinates.end(); i != iend; i++ ) {
|
||||
writeCoordinate( &buffer, &offset, i->x, min.x, max.x, xBits);
|
||||
writeCoordinate( &buffer, &offset, i->y, min.y, max.y, yBits);
|
||||
}
|
||||
|
||||
for ( std::vector< UnsignedCoordinate >::const_iterator i = wayBuffer.begin(), iend = wayBuffer.end(); i != iend; i++ ) {
|
||||
writeCoordinate( &buffer, &offset, i->x, min.x, max.x, xBits);
|
||||
writeCoordinate( &buffer, &offset, i->y, min.y, max.y, yBits);
|
||||
}
|
||||
|
||||
buffer += ( offset + 7 ) / 8;
|
||||
|
||||
return buffer - oldBuffer;
|
||||
}
|
||||
|
||||
size_t read( const unsigned char* buffer, UnsignedCoordinate min, UnsignedCoordinate max ) {
|
||||
const unsigned char* oldBuffer = buffer;
|
||||
|
||||
int offset = 0;
|
||||
|
||||
const char xBits = bits_needed( max.x - min.x );
|
||||
const char yBits = bits_needed( max.y - min.y );
|
||||
|
||||
unsigned numEdges = read_unaligned_unsigned( &buffer, 32, &offset );
|
||||
unsigned minID = read_unaligned_unsigned( &buffer, 32, &offset );
|
||||
unsigned idBits = read_unaligned_unsigned( &buffer, 6, &offset );
|
||||
unsigned edgeIDBits = read_unaligned_unsigned( &buffer, 6, &offset );
|
||||
unsigned pathLengthBits = read_unaligned_unsigned( &buffer, 6, &offset );
|
||||
|
||||
std::vector< NodeID > nodes;
|
||||
NodeID lastTarget = std::numeric_limits< NodeID >::max();
|
||||
for ( unsigned i = 0; i < numEdges; i++ ) {
|
||||
Edge edge;
|
||||
bool reuse = read_unaligned_unsigned( &buffer, 1, &offset ) == 1;
|
||||
if ( reuse )
|
||||
edge.source = lastTarget;
|
||||
else
|
||||
edge.source = read_unaligned_unsigned( &buffer, idBits, &offset ) + minID;
|
||||
edge.target = read_unaligned_unsigned( &buffer, idBits, &offset ) + minID;
|
||||
edge.edgeID = read_unaligned_unsigned( &buffer, edgeIDBits, &offset );
|
||||
edge.bidirectional = read_unaligned_unsigned( &buffer, 1, &offset ) == 1;
|
||||
edge.pathLength = read_unaligned_unsigned( &buffer, pathLengthBits, &offset );
|
||||
|
||||
edges.push_back( edge );
|
||||
|
||||
nodes.push_back( edge.source );
|
||||
nodes.push_back( edge.target );
|
||||
|
||||
lastTarget = edge.target;
|
||||
}
|
||||
assert( edges.size() != 0 );
|
||||
|
||||
std::sort( nodes.begin(), nodes.end() );
|
||||
nodes.resize( std::unique( nodes.begin(), nodes.end() ) - nodes.begin() );
|
||||
|
||||
std::vector< UnsignedCoordinate > nodeCoordinates( nodes.size() );
|
||||
for ( std::vector< UnsignedCoordinate >::iterator i = nodeCoordinates.begin(), iend = nodeCoordinates.end(); i != iend; i++ ) {
|
||||
readCoordinate( &buffer, &offset, &i->x, min.x, max.x, xBits);
|
||||
readCoordinate( &buffer, &offset, &i->y, min.y, max.y, yBits);
|
||||
}
|
||||
|
||||
for ( std::vector< Edge >::iterator i = edges.begin(), iend = edges.end(); i != iend; i++ ) {
|
||||
unsigned sourcePos = lower_bound( nodes.begin(), nodes.end(), i->source ) - nodes.begin();
|
||||
unsigned targetPos = lower_bound( nodes.begin(), nodes.end(), i->target ) - nodes.begin();
|
||||
if ( coordinates.size() == 0 || coordinates.back().x != nodeCoordinates[sourcePos].x || coordinates.back().y != nodeCoordinates[sourcePos].y )
|
||||
coordinates.push_back( nodeCoordinates[sourcePos] );
|
||||
i->pathID = coordinates.size() - 1;
|
||||
for ( int path = 0; path < i->pathLength; path++ ) {
|
||||
UnsignedCoordinate coordinate;
|
||||
readCoordinate( &buffer, &offset, &coordinate.x, min.x, max.x, xBits);
|
||||
readCoordinate( &buffer, &offset, &coordinate.y, min.y, max.y, yBits);
|
||||
coordinates.push_back( coordinate );
|
||||
}
|
||||
coordinates.push_back( nodeCoordinates[targetPos] );
|
||||
|
||||
i->pathLength += 2;
|
||||
}
|
||||
|
||||
buffer += ( offset + 7 ) / 8;
|
||||
|
||||
return buffer - oldBuffer;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void writeCoordinate( unsigned char** buffer, int* offset, unsigned value, unsigned min, unsigned max, char bits )
|
||||
{
|
||||
bool inside = !( value < min || value > max );
|
||||
write_unaligned_unsigned( buffer, inside ? 1 : 0, 1, offset );
|
||||
if ( inside )
|
||||
write_unaligned_unsigned( buffer, value - min, bits, offset );
|
||||
else
|
||||
write_unaligned_unsigned( buffer, value, 32, offset );
|
||||
}
|
||||
|
||||
void readCoordinate( const unsigned char** buffer, int* offset, unsigned* value, unsigned min, unsigned /*max*/, char bits )
|
||||
{
|
||||
bool inside = read_unaligned_unsigned( buffer, 1, offset ) == 1;
|
||||
if ( inside )
|
||||
*value = read_unaligned_unsigned( buffer, bits, offset ) + min;
|
||||
else
|
||||
*value = read_unaligned_unsigned( buffer, 32, offset );
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif // CELL_H
|
290
monav/gpsgrid/gpsgridclient.cpp
Normal file
290
monav/gpsgrid/gpsgridclient.cpp
Normal file
@ -0,0 +1,290 @@
|
||||
/*
|
||||
Copyright 2010 Christian Vetter veaac.fdirct@gmail.com
|
||||
|
||||
This file is part of MoNav.
|
||||
|
||||
MoNav is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MoNav is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with MoNav. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "gpsgridclient.h"
|
||||
#include <QtDebug>
|
||||
#include <QHash>
|
||||
#include <algorithm>
|
||||
#include "utils/qthelpers.h"
|
||||
#ifndef NOGUI
|
||||
#include <QInputDialog>
|
||||
#endif
|
||||
#include <QSettings>
|
||||
|
||||
GPSGridClient::GPSGridClient()
|
||||
{
|
||||
index = NULL;
|
||||
gridFile = NULL;
|
||||
QSettings settings( "MoNavClient" );
|
||||
settings.beginGroup( "GPS Grid" );
|
||||
cacheSize = settings.value( "cacheSize", 1 ).toInt();
|
||||
cache.setMaxCost( 1024 * 1024 * 3 * cacheSize / 4 );
|
||||
}
|
||||
|
||||
GPSGridClient::~GPSGridClient()
|
||||
{
|
||||
QSettings settings( "MoNavClient" );
|
||||
settings.beginGroup( "GPS Grid" );
|
||||
settings.setValue( "cacheSize", cacheSize );
|
||||
unload();
|
||||
}
|
||||
|
||||
void GPSGridClient::unload()
|
||||
{
|
||||
if ( index != NULL )
|
||||
delete index;
|
||||
index = NULL;
|
||||
if ( gridFile != NULL )
|
||||
delete gridFile;
|
||||
gridFile = NULL;
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
QString GPSGridClient::GetName()
|
||||
{
|
||||
return "GPS Grid";
|
||||
}
|
||||
|
||||
void GPSGridClient::SetInputDirectory( const QString& dir )
|
||||
{
|
||||
directory = dir;
|
||||
}
|
||||
|
||||
void GPSGridClient::ShowSettings()
|
||||
{
|
||||
#ifndef NOGUI
|
||||
bool ok = false;
|
||||
int result = QInputDialog::getInt( NULL, "Settings", "Enter Cache Size [MB]", cacheSize, 1, 1024, 1, &ok );
|
||||
if ( !ok )
|
||||
return;
|
||||
cacheSize = result;
|
||||
if ( index != NULL )
|
||||
index->SetCacheSize( 1024 * 1024 * cacheSize / 4 );
|
||||
cache.setMaxCost( 1024 * 1024 * 3 * cacheSize / 4 );
|
||||
#endif
|
||||
}
|
||||
|
||||
bool GPSGridClient::LoadData()
|
||||
{
|
||||
unload();
|
||||
QString filename = fileInDirectory( directory, "GPSGrid" );
|
||||
QFile configFile( filename + "_config" );
|
||||
if ( !openQFile( &configFile, QIODevice::ReadOnly ) )
|
||||
return false;
|
||||
|
||||
index = new gg::Index( filename + "_index" );
|
||||
index->SetCacheSize( 1024 * 1024 * cacheSize / 4 );
|
||||
|
||||
gridFile = new QFile( filename + "_grid" );
|
||||
if ( !gridFile->open( QIODevice::ReadOnly ) ) {
|
||||
qCritical() << "failed to open file: " << gridFile->fileName();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPSGridClient::GetNearestEdge( Result* result, const UnsignedCoordinate& coordinate, double radius, bool headingPenalty, double heading )
|
||||
{
|
||||
const GPSCoordinate gps = coordinate.ToProjectedCoordinate().ToGPSCoordinate();
|
||||
const GPSCoordinate gpsMoved( gps.latitude, gps.longitude + 1 );
|
||||
|
||||
const double meter = gps.ApproximateDistance( gpsMoved );
|
||||
double gridRadius = (( double ) UnsignedCoordinate( ProjectedCoordinate( gpsMoved ) ).x - coordinate.x ) / meter * radius;
|
||||
gridRadius *= gridRadius;
|
||||
heading = fmod( ( heading + 270 ) * 2.0 * M_PI / 360.0, 2 * M_PI );
|
||||
|
||||
static const int width = 32 * 32 * 32;
|
||||
|
||||
ProjectedCoordinate position = coordinate.ToProjectedCoordinate();
|
||||
NodeID yGrid = floor( position.y * width );
|
||||
NodeID xGrid = floor( position.x * width );
|
||||
|
||||
result->distance = gridRadius;
|
||||
QVector< UnsignedCoordinate > path;
|
||||
|
||||
checkCell( result, &path, xGrid - 1, yGrid - 1, coordinate, heading, headingPenalty );
|
||||
checkCell( result, &path, xGrid - 1, yGrid, coordinate, heading, headingPenalty );
|
||||
checkCell( result, &path, xGrid - 1, yGrid + 1, coordinate, heading, headingPenalty );
|
||||
|
||||
checkCell( result, &path, xGrid, yGrid - 1, coordinate, heading, headingPenalty );
|
||||
checkCell( result, &path, xGrid, yGrid, coordinate, heading, headingPenalty );
|
||||
checkCell( result, &path, xGrid, yGrid + 1, coordinate, heading, headingPenalty );
|
||||
|
||||
checkCell( result, &path, xGrid + 1, yGrid - 1, coordinate, heading, headingPenalty );
|
||||
checkCell( result, &path, xGrid + 1, yGrid, coordinate, heading, headingPenalty );
|
||||
checkCell( result, &path, xGrid + 1, yGrid + 1, coordinate, heading, headingPenalty );
|
||||
|
||||
if ( path.empty() )
|
||||
return false;
|
||||
|
||||
double length = 0;
|
||||
double lengthToNearest = 0;
|
||||
for ( int pathID = 1; pathID < path.size(); pathID++ ) {
|
||||
UnsignedCoordinate sourceCoord = path[pathID - 1];
|
||||
UnsignedCoordinate targetCoord = path[pathID];
|
||||
double xDiff = ( double ) sourceCoord.x - targetCoord.x;
|
||||
double yDiff = ( double ) sourceCoord.y - targetCoord.y;
|
||||
|
||||
double distance = sqrt( xDiff * xDiff + yDiff * yDiff );
|
||||
length += distance;
|
||||
if ( pathID < ( int ) result->previousWayCoordinates )
|
||||
lengthToNearest += distance;
|
||||
else if ( pathID == ( int ) result->previousWayCoordinates )
|
||||
lengthToNearest += result->percentage * distance;
|
||||
}
|
||||
if ( length == 0 )
|
||||
result->percentage = 0;
|
||||
else
|
||||
result->percentage = lengthToNearest / length;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GPSGridClient::checkCell( Result* result, QVector< UnsignedCoordinate >* path, NodeID gridX, NodeID gridY, const UnsignedCoordinate& coordinate, double heading, double headingPenalty ) {
|
||||
static const int width = 32 * 32 * 32;
|
||||
ProjectedCoordinate minPos( ( double ) gridX / width, ( double ) gridY / width );
|
||||
ProjectedCoordinate maxPos( ( double ) ( gridX + 1 ) / width, ( double ) ( gridY + 1 ) / width );
|
||||
UnsignedCoordinate min( minPos );
|
||||
UnsignedCoordinate max( maxPos );
|
||||
if ( distance( min, max, coordinate ) >= result->distance )
|
||||
return false;
|
||||
|
||||
qint64 cellNumber = ( qint64( gridX ) << 32 ) + gridY;
|
||||
if ( !cache.contains( cellNumber ) ) {
|
||||
qint64 position = index->GetIndex( gridX, gridY );
|
||||
if ( position == -1 )
|
||||
return true;
|
||||
gridFile->seek( position );
|
||||
int size;
|
||||
gridFile->read( (char* ) &size, sizeof( size ) );
|
||||
unsigned char* buffer = new unsigned char[size + 8]; // reading buffer + 4 bytes
|
||||
|
||||
gridFile->read( ( char* ) buffer, size );
|
||||
gg::Cell* cell = new gg::Cell();
|
||||
cell->read( buffer, min, max );
|
||||
cache.insert( cellNumber, cell, cell->edges.size() * sizeof( gg::Cell::Edge ) );
|
||||
delete[] buffer;
|
||||
}
|
||||
gg::Cell* cell = cache.object( cellNumber );
|
||||
if ( cell == NULL )
|
||||
return true;
|
||||
|
||||
UnsignedCoordinate nearestPoint;
|
||||
for ( std::vector< gg::Cell::Edge >::const_iterator i = cell->edges.begin(), e = cell->edges.end(); i != e; ++i ) {
|
||||
bool found = false;
|
||||
|
||||
for ( int pathID = 1; pathID < i->pathLength; pathID++ ) {
|
||||
UnsignedCoordinate sourceCoord = cell->coordinates[pathID + i->pathID - 1];
|
||||
UnsignedCoordinate targetCoord = cell->coordinates[pathID + i->pathID];
|
||||
double percentage = 0;
|
||||
|
||||
double d = distance( &nearestPoint, &percentage, sourceCoord, targetCoord, coordinate );
|
||||
if ( d + headingPenalty > result->distance )
|
||||
continue;
|
||||
|
||||
double xDiff = ( double ) targetCoord.x - sourceCoord.x;
|
||||
double yDiff = ( double ) targetCoord.y - sourceCoord.y;
|
||||
double direction = 0;
|
||||
if ( xDiff != 0 || yDiff != 0 )
|
||||
direction = fmod( atan2( yDiff, xDiff ), 2 * M_PI );
|
||||
else
|
||||
headingPenalty = 0;
|
||||
double penalty = fabs( direction - heading );
|
||||
if ( penalty > M_PI )
|
||||
penalty = 2 * M_PI - penalty;
|
||||
if ( i->bidirectional && penalty > M_PI / 2 )
|
||||
penalty = M_PI - penalty;
|
||||
penalty = penalty / M_PI * headingPenalty;
|
||||
d += penalty;
|
||||
|
||||
if ( d < result->distance ) {
|
||||
result->nearestPoint = nearestPoint;
|
||||
result->distance = d;
|
||||
result->previousWayCoordinates = pathID;
|
||||
result->percentage = percentage;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( found ) {
|
||||
result->source = i->source;
|
||||
result->target = i->target;
|
||||
result->edgeID = i->edgeID;
|
||||
path->clear();
|
||||
for ( int pathID = 0; pathID < i->pathLength; pathID++ )
|
||||
path->push_back( cell->coordinates[pathID + i->pathID] );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
double GPSGridClient::distance( UnsignedCoordinate* nearestPoint, double* percentage, const UnsignedCoordinate source, const UnsignedCoordinate target, const UnsignedCoordinate& coordinate ) {
|
||||
const double vY = ( double ) target.y - source.y;
|
||||
const double vX = ( double ) target.x - source.x;
|
||||
const double wY = ( double ) coordinate.y - source.y;
|
||||
const double wX = ( double ) coordinate.x - source.x;
|
||||
const double vLengthSquared = vY * vY + vX * vX;
|
||||
|
||||
double r = 0;
|
||||
if ( vLengthSquared != 0 )
|
||||
r = ( vX * wX + vY * wY ) / vLengthSquared;
|
||||
*percentage = r;
|
||||
|
||||
if ( r <= 0 ) {
|
||||
*nearestPoint = source;
|
||||
*percentage = 0;
|
||||
return wY * wY + wX * wX;
|
||||
} else if ( r >= 1 ) {
|
||||
*nearestPoint = target;
|
||||
*percentage = 1;
|
||||
const double dY = ( double ) coordinate.y - target.y;
|
||||
const double dX = ( double ) coordinate.x - target.x;
|
||||
return dY * dY + dX * dX;
|
||||
}
|
||||
|
||||
nearestPoint->x = source.x + r * vX;
|
||||
nearestPoint->y = source.y + r * vY;
|
||||
|
||||
const double dX = ( double ) nearestPoint->x - coordinate.x;
|
||||
const double dY = ( double ) nearestPoint->y - coordinate.y;
|
||||
|
||||
return dY * dY + dX * dX;
|
||||
}
|
||||
|
||||
double GPSGridClient::distance( const UnsignedCoordinate& min, const UnsignedCoordinate& max, const UnsignedCoordinate& coordinate ) {
|
||||
UnsignedCoordinate nearest = coordinate;
|
||||
|
||||
if ( coordinate.x <= min.x )
|
||||
nearest.x = min.x;
|
||||
else if ( coordinate.x >= max.x )
|
||||
nearest.x = max.x;
|
||||
|
||||
if ( coordinate.y <= min.y )
|
||||
nearest.y = min.y;
|
||||
else if ( coordinate.y >= max.y )
|
||||
nearest.y = max.y;
|
||||
|
||||
double xDiff = ( double ) coordinate.x - nearest.x;
|
||||
double yDiff = ( double ) coordinate.y - nearest.y;
|
||||
|
||||
return xDiff * xDiff + yDiff * yDiff;
|
||||
}
|
||||
|
||||
Q_EXPORT_PLUGIN2(gpsgridclient, GPSGridClient)
|
62
monav/gpsgrid/gpsgridclient.h
Normal file
62
monav/gpsgrid/gpsgridclient.h
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
Copyright 2010 Christian Vetter veaac.fdirct@gmail.com
|
||||
|
||||
This file is part of MoNav.
|
||||
|
||||
MoNav is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MoNav is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with MoNav. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef GPSGRIDCLIENT_H
|
||||
#define GPSGRIDCLIENT_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QFile>
|
||||
#include "interfaces/igpslookup.h"
|
||||
#include "cell.h"
|
||||
#include "table.h"
|
||||
#include <QCache>
|
||||
|
||||
class GPSGridClient : public QObject, public IGPSLookup
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES( IGPSLookup )
|
||||
public:
|
||||
GPSGridClient();
|
||||
virtual ~GPSGridClient();
|
||||
|
||||
virtual QString GetName();
|
||||
virtual void SetInputDirectory( const QString& dir );
|
||||
virtual void ShowSettings();
|
||||
virtual bool LoadData();
|
||||
virtual bool GetNearestEdge( Result* result, const UnsignedCoordinate& coordinate, double radius, bool headingPenalty, double heading );
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
protected:
|
||||
|
||||
void unload();
|
||||
double distance( UnsignedCoordinate* nearestPoint, double* percentage, const UnsignedCoordinate source, const UnsignedCoordinate target, const UnsignedCoordinate& coordinate );
|
||||
double distance( const UnsignedCoordinate& min, const UnsignedCoordinate& max, const UnsignedCoordinate& coordinate );
|
||||
bool checkCell( Result* result, QVector< UnsignedCoordinate >* path, NodeID gridX, NodeID gridY, const UnsignedCoordinate& coordinate, double heading, double headingPenalty );
|
||||
|
||||
long long cacheSize;
|
||||
QString directory;
|
||||
QFile* gridFile;
|
||||
QCache< qint64, gg::Cell > cache;
|
||||
gg::Index* index;
|
||||
};
|
||||
|
||||
#endif // GPSGRIDCLIENT_H
|
31
monav/gpsgrid/gpsgridclient.pro
Normal file
31
monav/gpsgrid/gpsgridclient.pro
Normal file
@ -0,0 +1,31 @@
|
||||
#-------------------------------------------------
|
||||
#
|
||||
# Project created by QtCreator 2010-06-25T08:48:06
|
||||
#
|
||||
#-------------------------------------------------
|
||||
|
||||
TEMPLATE = lib
|
||||
CONFIG += plugin
|
||||
#CONFIG += debug
|
||||
|
||||
DESTDIR = ..
|
||||
|
||||
HEADERS += \
|
||||
utils/coordinates.h \
|
||||
utils/config.h \
|
||||
cell.h \
|
||||
interfaces/igpslookup.h \
|
||||
gpsgridclient.h \
|
||||
table.h \
|
||||
utils/bithelpers.h \
|
||||
utils/qthelpers.h
|
||||
|
||||
unix {
|
||||
QMAKE_CXXFLAGS_RELEASE -= -O2
|
||||
QMAKE_CXXFLAGS_RELEASE += -O3 \
|
||||
-Wno-unused-function
|
||||
QMAKE_CXXFLAGS_DEBUG += -Wno-unused-function
|
||||
}
|
||||
|
||||
SOURCES += \
|
||||
gpsgridclient.cpp
|
1
monav/gpsgrid/interfaces
Symbolic link
1
monav/gpsgrid/interfaces
Symbolic link
@ -0,0 +1 @@
|
||||
../interfaces/
|
224
monav/gpsgrid/table.h
Normal file
224
monav/gpsgrid/table.h
Normal file
@ -0,0 +1,224 @@
|
||||
/*
|
||||
Copyright 2010 Christian Vetter veaac.fdirct@gmail.com
|
||||
|
||||
This file is part of MoNav.
|
||||
|
||||
MoNav is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MoNav is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with MoNav. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TABLE_H
|
||||
#define TABLE_H
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QCache>
|
||||
#include <QFile>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <QtDebug>
|
||||
|
||||
namespace gg {
|
||||
|
||||
template< class T, int size >
|
||||
struct IndexTable {
|
||||
T index[size * size];
|
||||
IndexTable()
|
||||
{
|
||||
for ( int i = 0; i < size * size; i++ )
|
||||
index[i] = -1;
|
||||
}
|
||||
IndexTable( const char* buffer )
|
||||
{
|
||||
Read( buffer );
|
||||
}
|
||||
|
||||
T GetIndex( int x, int y ) const
|
||||
{
|
||||
if ( x < 0 || x >= 32 )
|
||||
return -1;
|
||||
if ( y < 0 || y >= 32 )
|
||||
return -1;
|
||||
return index[x + y * size];
|
||||
}
|
||||
|
||||
void SetIndex( int x, int y, T data )
|
||||
{
|
||||
assert( x >= 0 );
|
||||
assert( x < 32 );
|
||||
assert( y >= 0 );
|
||||
assert( y < 32 );
|
||||
assert( index[x + y *size] == -1 );
|
||||
index[x + y * size] = data;
|
||||
}
|
||||
|
||||
static size_t Size()
|
||||
{
|
||||
return size * size * sizeof( T );
|
||||
}
|
||||
|
||||
void Write( char* buffer ) const
|
||||
{
|
||||
memcpy( buffer, index, size * size * sizeof( T ) );
|
||||
}
|
||||
|
||||
void Read( const char* buffer )
|
||||
{
|
||||
memcpy( index, buffer, size * size * sizeof( T ) );
|
||||
}
|
||||
|
||||
void Debug()
|
||||
{
|
||||
for ( int i = 0; i < size; i++ ) {
|
||||
QString row;
|
||||
for ( int j = 0; j < size; j++)
|
||||
row += GetIndex( i, j ) == -1 ? "." : "#";
|
||||
qDebug() << row;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct GridIndex {
|
||||
qint64 position;
|
||||
int x;
|
||||
int y;
|
||||
};
|
||||
|
||||
class Index {
|
||||
|
||||
public:
|
||||
Index( QString filename ) :
|
||||
file2( filename + "_2" ), file3( filename + "_3" )
|
||||
{
|
||||
QFile file1( filename + "_1" );
|
||||
file1.open( QIODevice::ReadOnly );
|
||||
top.Read( file1.read( top.Size() ).constData() );
|
||||
file2.open( QIODevice::ReadOnly );
|
||||
file3.open( QIODevice::ReadOnly );
|
||||
}
|
||||
|
||||
qint64 GetIndex( int x, int y ) {
|
||||
int topx = x / 32 / 32;
|
||||
int topy = y / 32 / 32;
|
||||
int middle = top.GetIndex( topx, topy );
|
||||
if ( middle == -1 )
|
||||
return -1;
|
||||
|
||||
int middlex = ( x / 32 ) % 32;
|
||||
int middley = ( y / 32 ) % 32;
|
||||
if ( !cache2.contains( middle ) ) {
|
||||
IndexTable< int, 32 >* newEntry;
|
||||
file2.seek( middle * newEntry->Size() );
|
||||
newEntry = new IndexTable< int, 32 >( file2.read( newEntry->Size() ) );
|
||||
cache2.insert( middle, newEntry, newEntry->Size() );
|
||||
}
|
||||
if ( !cache2.contains( middle ) )
|
||||
return -1;
|
||||
IndexTable< int, 32 >* middleTable = cache2.object( middle );
|
||||
int bottom = middleTable->GetIndex( middlex, middley );
|
||||
if ( bottom == -1 )
|
||||
return -1;
|
||||
|
||||
int bottomx = x % 32;
|
||||
int bottomy = y % 32;
|
||||
if ( !cache3.contains( bottom ) ) {
|
||||
IndexTable< qint64, 32 >* newEntry;
|
||||
file3.seek( bottom * newEntry->Size() );
|
||||
newEntry = new IndexTable< qint64, 32 >( file3.read( newEntry->Size() ) );
|
||||
cache3.insert( bottom, newEntry, newEntry->Size() );
|
||||
}
|
||||
if ( !cache3.contains( bottom ) )
|
||||
return -1;
|
||||
IndexTable< qint64, 32 >* bottomTable = cache3.object( bottom );
|
||||
qint64 position = bottomTable->GetIndex( bottomx, bottomy );
|
||||
return position;
|
||||
}
|
||||
|
||||
void SetCacheSize( long long size )
|
||||
{
|
||||
assert( size > 0 );
|
||||
cache2.setMaxCost( size / 4 );
|
||||
cache3.setMaxCost( 3 * size / 4 );
|
||||
}
|
||||
|
||||
static void Create( QString filename, const std::vector< GridIndex >& data )
|
||||
{
|
||||
gg::IndexTable< int, 32 > top;
|
||||
std::vector< gg::IndexTable< int, 32 > > middle;
|
||||
std::vector< gg::IndexTable< qint64, 32 > > bottom;
|
||||
|
||||
for ( std::vector< GridIndex >::const_iterator i = data.begin(), iend = data.end(); i != iend; i++ ) {
|
||||
int topx = i->x / 32 / 32;
|
||||
int topy = i->y / 32 / 32;
|
||||
int middleIndex = top.GetIndex( topx, topy );
|
||||
if ( middleIndex == -1 ) {
|
||||
middleIndex = middle.size();
|
||||
top.SetIndex( topx, topy, middleIndex );
|
||||
middle.push_back( gg::IndexTable< int, 32 >() );
|
||||
}
|
||||
|
||||
int middlex = ( i->x / 32 ) % 32;
|
||||
int middley = ( i->y / 32 ) % 32;
|
||||
int bottomIndex = middle[middleIndex].GetIndex( middlex, middley );
|
||||
if ( bottomIndex == -1 ) {
|
||||
bottomIndex = bottom.size();
|
||||
middle[middleIndex].SetIndex( middlex, middley, bottomIndex );
|
||||
bottom.push_back( IndexTable< qint64, 32 >() );
|
||||
}
|
||||
|
||||
int bottomx = i->x % 32;
|
||||
int bottomy = i->y % 32;
|
||||
bottom[bottomIndex].SetIndex( bottomx, bottomy, i->position );
|
||||
}
|
||||
|
||||
qDebug() << "GPS Grid: top index filled: " << ( double ) middle.size() * 100 / 32 / 32 << "%";
|
||||
qDebug() << "GPS Grid: middle tables: " << middle.size();
|
||||
qDebug() << "GPS Grid: middle index filled: " << ( double ) bottom.size() * 100 / middle.size() / 32 / 32 << "%";
|
||||
qDebug() << "GPS Grid: bottom tables: " << bottom.size();
|
||||
qDebug() << "GPS Grid: bottom index filled: " << ( double ) data.size() * 100 / bottom.size() / 32 / 32 << "%";
|
||||
qDebug() << "GPS Grid: grid cells: " << data.size();
|
||||
|
||||
QFile file1( filename + "_1" );
|
||||
QFile file2( filename + "_2" );
|
||||
QFile file3( filename + "_3" );
|
||||
file1.open( QIODevice::WriteOnly );
|
||||
file2.open( QIODevice::WriteOnly );
|
||||
file3.open( QIODevice::WriteOnly );
|
||||
|
||||
char* buffer = new char[IndexTable< qint64, 32 >::Size()];
|
||||
|
||||
top.Write( buffer );
|
||||
file1.write( buffer, IndexTable< int, 32 >::Size() );
|
||||
|
||||
for ( int i = 0; i < ( int ) middle.size(); i++ ) {
|
||||
middle[i].Write( buffer );
|
||||
file2.write( buffer, IndexTable< int, 32 >::Size() );
|
||||
}
|
||||
|
||||
for ( int i = 0; i < ( int ) bottom.size(); i++ ) {
|
||||
bottom[i].Write( buffer );
|
||||
file3.write( buffer, IndexTable< qint64, 32 >::Size() );
|
||||
}
|
||||
|
||||
delete[] buffer;
|
||||
}
|
||||
|
||||
private:
|
||||
QFile file2;
|
||||
QFile file3;
|
||||
IndexTable< int, 32 > top;
|
||||
QCache< int, IndexTable< int, 32 > > cache2;
|
||||
QCache< int, IndexTable< qint64, 32 > > cache3;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // TABLE_H
|
1
monav/gpsgrid/utils
Symbolic link
1
monav/gpsgrid/utils
Symbolic link
@ -0,0 +1 @@
|
||||
../utils/
|
50
monav/interfaces/iaddresslookup.h
Normal file
50
monav/interfaces/iaddresslookup.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
Copyright 2010 Christian Vetter veaac.fdirct@gmail.com
|
||||
|
||||
This file is part of MoNav.
|
||||
|
||||
MoNav is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MoNav is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with MoNav. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef IADDRESSLOOKUP_H
|
||||
#define IADDRESSLOOKUP_H
|
||||
|
||||
#include "utils/coordinates.h"
|
||||
#include <QtPlugin>
|
||||
#include <QVector>
|
||||
|
||||
class IAddressLookup
|
||||
{
|
||||
public:
|
||||
virtual ~IAddressLookup() {}
|
||||
|
||||
virtual QString GetName() = 0;
|
||||
virtual void SetInputDirectory( const QString& dir ) = 0;
|
||||
virtual void ShowSettings() = 0;
|
||||
virtual bool LoadData() = 0;
|
||||
// for a given user input's prefix get a list of place name suggestions as well as partial input suggestions
|
||||
virtual bool GetPlaceSuggestions( const QString& input, int amount, QStringList* suggestions, QStringList* inputSuggestions ) = 0;
|
||||
// for a given user input's prefix get a list of street name suggestions as well as partial input suggestions
|
||||
virtual bool GetStreetSuggestions( const QString& input, int amount, QStringList* suggestions, QStringList* inputSuggestions ) = 0;
|
||||
// for a given place name get a list of places and their coordinates
|
||||
virtual bool GetPlaceData( QString input, QVector< int >* placeIDs, QVector< UnsignedCoordinate >* placeCoordinates ) = 0;
|
||||
// selects a place by it's id
|
||||
virtual bool SelectPlace( int placeID ) = 0;
|
||||
// uses the selected place to provide street name suggestions and partial input suggestions
|
||||
virtual bool GetStreetData( QString input, QVector< int >* segmentLength, QVector< UnsignedCoordinate >* coordinates ) = 0;
|
||||
};
|
||||
|
||||
Q_DECLARE_INTERFACE( IAddressLookup, "monav.IAddressLookup/1.1" )
|
||||
|
||||
#endif // IADDRESSLOOKUP_H
|
60
monav/interfaces/igpslookup.h
Normal file
60
monav/interfaces/igpslookup.h
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
Copyright 2010 Christian Vetter veaac.fdirct@gmail.com
|
||||
|
||||
This file is part of MoNav.
|
||||
|
||||
MoNav is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MoNav is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with MoNav. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef IGPSLOOKUP_H
|
||||
#define IGPSLOOKUP_H
|
||||
|
||||
#include "utils/config.h"
|
||||
#include "utils/coordinates.h"
|
||||
#include <QtPlugin>
|
||||
#include <QVector>
|
||||
|
||||
class IGPSLookup
|
||||
{
|
||||
public:
|
||||
|
||||
struct Result {
|
||||
// source + target + edgeID uniquely identify an edge
|
||||
NodeID source;
|
||||
NodeID target;
|
||||
unsigned edgeID;
|
||||
// the nearest point on the edge
|
||||
UnsignedCoordinate nearestPoint;
|
||||
// the amount of way coordinates on the way before the nearest point
|
||||
unsigned previousWayCoordinates;
|
||||
// the position on the way
|
||||
double percentage;
|
||||
// the distance to the nearest point
|
||||
double distance;
|
||||
|
||||
};
|
||||
|
||||
virtual ~IGPSLookup() {}
|
||||
|
||||
virtual QString GetName() = 0;
|
||||
virtual void SetInputDirectory( const QString& dir ) = 0;
|
||||
virtual void ShowSettings() = 0;
|
||||
virtual bool LoadData() = 0;
|
||||
// gets the nearest routing edge; a heading penalty can be applied if the way's orientation differs greatly from the current heading.
|
||||
virtual bool GetNearestEdge( Result* result, const UnsignedCoordinate& coordinate, double radius, bool headingPenalty = 0, double heading = 0 ) = 0;
|
||||
};
|
||||
|
||||
Q_DECLARE_INTERFACE( IGPSLookup, "monav.IGPSLookup/1.1" )
|
||||
|
||||
#endif // IGPSLOOKUP_H
|
80
monav/interfaces/irouter.h
Normal file
80
monav/interfaces/irouter.h
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
Copyright 2010 Christian Vetter veaac.fdirct@gmail.com
|
||||
|
||||
This file is part of MoNav.
|
||||
|
||||
MoNav is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MoNav is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with MoNav. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef IROUTER_H
|
||||
#define IROUTER_H
|
||||
|
||||
#include "utils/config.h"
|
||||
#include "utils/coordinates.h"
|
||||
#include "interfaces/igpslookup.h"
|
||||
#include <QtPlugin>
|
||||
#include <QVector>
|
||||
|
||||
class IRouter
|
||||
{
|
||||
public:
|
||||
|
||||
struct Node {
|
||||
Node(){}
|
||||
Node( UnsignedCoordinate coord )
|
||||
{
|
||||
coordinate = coord;
|
||||
}
|
||||
|
||||
UnsignedCoordinate coordinate;
|
||||
};
|
||||
|
||||
struct Edge {
|
||||
Edge(){}
|
||||
Edge( unsigned name_, bool branchingPossible_, unsigned char type_, unsigned short length_, unsigned seconds_ )
|
||||
{
|
||||
name = name_;
|
||||
branchingPossible = branchingPossible_;
|
||||
type = type_;
|
||||
length = length_;
|
||||
seconds = seconds_;
|
||||
}
|
||||
unsigned name : 30; // name ID of the edge
|
||||
bool branchingPossible : 1; // is there more than one subsequent edge to traverse ( turing around and traversing this edge in the opposite direction does not count )
|
||||
unsigned char type; // type ID of the edge
|
||||
unsigned short length; // the amount of path nodes - 1 == amount of edges
|
||||
unsigned seconds;
|
||||
};
|
||||
|
||||
virtual ~IRouter() {}
|
||||
|
||||
virtual QString GetName() = 0;
|
||||
virtual void SetInputDirectory( const QString& dir ) = 0;
|
||||
virtual void ShowSettings() = 0;
|
||||
virtual bool LoadData() = 0;
|
||||
// computes the route between source and target and returns the distance in second
|
||||
virtual bool GetRoute( double* distance, QVector< Node>* pathNodes, QVector< Edge >* pathEdges, const IGPSLookup::Result& source, const IGPSLookup::Result& target ) = 0;
|
||||
// translate a name ID into the corresponding string
|
||||
virtual bool GetName( QString* result, unsigned name ) = 0;
|
||||
// translate a list of name IDs into the corresponding strings
|
||||
virtual bool GetNames( QVector< QString >* result, QVector< unsigned > names ) = 0;
|
||||
// translate a type ID into the corresponding description
|
||||
virtual bool GetType( QString* result, unsigned type ) = 0;
|
||||
// translate a list of type IDs into the corresponding descriptions
|
||||
virtual bool GetTypes( QVector< QString >* result, QVector< unsigned > types ) = 0;
|
||||
};
|
||||
|
||||
Q_DECLARE_INTERFACE( IRouter, "monav.IRouter/1.1" )
|
||||
|
||||
#endif // IROUTER_H
|
7
monav/monav.pro
Normal file
7
monav/monav.pro
Normal file
@ -0,0 +1,7 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
#CONFIG += debug
|
||||
|
||||
SUBDIRS = contractionhierarchies/contractionhierarchiesclient.pro \
|
||||
gpsgrid/gpsgridclient.pro \
|
||||
unicodetournamenttrie/unicodetournamenttrieclient.pro
|
1
monav/unicodetournamenttrie/interfaces
Symbolic link
1
monav/unicodetournamenttrie/interfaces
Symbolic link
@ -0,0 +1 @@
|
||||
../interfaces/
|
188
monav/unicodetournamenttrie/trie.h
Normal file
188
monav/unicodetournamenttrie/trie.h
Normal file
@ -0,0 +1,188 @@
|
||||
/*
|
||||
Copyright 2010 Christian Vetter veaac.fdirct@gmail.com
|
||||
|
||||
This file is part of MoNav.
|
||||
|
||||
MoNav is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MoNav is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with MoNav. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TRIE_H
|
||||
#define TRIE_H
|
||||
|
||||
#include <QString>
|
||||
#include <vector>
|
||||
#include "utils/coordinates.h"
|
||||
#include "utils/bithelpers.h"
|
||||
|
||||
namespace utt
|
||||
{
|
||||
|
||||
struct CityData {
|
||||
UnsignedCoordinate coordinate;
|
||||
|
||||
size_t GetSize() const {
|
||||
return 2 * sizeof( unsigned );
|
||||
}
|
||||
|
||||
void Write( char* buffer ) const {
|
||||
*( ( unsigned* ) buffer ) = coordinate.x;
|
||||
buffer += sizeof( unsigned );
|
||||
*( ( unsigned* ) buffer ) = coordinate.y;
|
||||
}
|
||||
|
||||
void Read( const char* buffer ) {
|
||||
coordinate.x = readUnaligned< unsigned >( buffer );
|
||||
buffer += sizeof( unsigned );
|
||||
coordinate.y = readUnaligned< unsigned >( buffer );
|
||||
}
|
||||
};
|
||||
|
||||
struct Data {
|
||||
unsigned start;
|
||||
unsigned short length;
|
||||
|
||||
size_t GetSize() const {
|
||||
return sizeof( unsigned ) + sizeof( unsigned short );
|
||||
}
|
||||
|
||||
void Write( char* buffer ) const {
|
||||
*( ( unsigned* ) buffer ) = start;
|
||||
buffer += sizeof( unsigned );
|
||||
*( ( unsigned short* ) buffer ) = length;
|
||||
}
|
||||
|
||||
void Read( const char* buffer ) {
|
||||
start = readUnaligned< unsigned >( buffer );
|
||||
buffer += sizeof( unsigned );
|
||||
length = readUnaligned< unsigned short >( buffer );
|
||||
}
|
||||
|
||||
bool operator==( const Data& right ) const {
|
||||
return start == right.start && length == right.length;
|
||||
}
|
||||
};
|
||||
|
||||
struct Label {
|
||||
QString string;
|
||||
unsigned index;
|
||||
unsigned importance;
|
||||
|
||||
bool operator<( const Label& right ) const {
|
||||
if ( importance != right.importance )
|
||||
return importance > right.importance;
|
||||
return string < right.string;
|
||||
}
|
||||
|
||||
bool operator==( const Label& right ) const {
|
||||
return string == right.string && importance == right.importance;
|
||||
}
|
||||
|
||||
size_t GetSize() const {
|
||||
size_t result = 0;
|
||||
result += sizeof( unsigned );
|
||||
result += sizeof( unsigned );
|
||||
result += strlen( string.toUtf8().constData() ) + 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Write( char* buffer ) const {
|
||||
*( ( unsigned* ) buffer ) = index;
|
||||
buffer += sizeof( unsigned );
|
||||
*( ( unsigned* ) buffer ) = importance;
|
||||
buffer += sizeof( unsigned );
|
||||
strcpy( buffer, string.toUtf8().constData() );
|
||||
}
|
||||
|
||||
void Read( const char* buffer ) {
|
||||
index = readUnaligned< unsigned >( buffer );
|
||||
buffer += sizeof( unsigned );
|
||||
importance = readUnaligned< unsigned >( buffer );
|
||||
buffer += sizeof( unsigned );
|
||||
string = QString::fromUtf8( buffer );
|
||||
}
|
||||
};
|
||||
|
||||
struct Node {
|
||||
std::vector< Data > dataList;
|
||||
std::vector< Label > labelList;
|
||||
|
||||
size_t GetSize() const {
|
||||
size_t result = 0;
|
||||
result += sizeof( short );
|
||||
if ( dataList.size() != 0 )
|
||||
result += sizeof( unsigned short );
|
||||
for ( int i = 0; i < ( int ) labelList.size(); i++ )
|
||||
result += labelList[i].GetSize();
|
||||
for ( int i = 0; i < ( int ) dataList.size(); i++ )
|
||||
result += dataList[i].GetSize();
|
||||
return result;
|
||||
}
|
||||
|
||||
void Write( char* buffer ) const {
|
||||
assert( ( int ) labelList.size() <= std::numeric_limits< short >::max() );
|
||||
assert( dataList.size() <= std::numeric_limits< unsigned short >::max() );
|
||||
*( ( short* ) buffer ) = labelList.size() * ( dataList.size() > 0 ? -1 : 1 );
|
||||
buffer += sizeof( short );
|
||||
if ( dataList.size() > 0 ) {
|
||||
*( ( unsigned short* ) buffer ) = dataList.size();
|
||||
buffer += sizeof( unsigned short );
|
||||
}
|
||||
for ( int i = 0; i < ( int ) labelList.size(); i++ ) {
|
||||
labelList[i].Write( buffer );
|
||||
buffer += labelList[i].GetSize();
|
||||
}
|
||||
for ( int i = 0; i < ( int ) dataList.size(); i++ ) {
|
||||
dataList[i].Write( buffer );
|
||||
buffer += dataList[i].GetSize();
|
||||
}
|
||||
}
|
||||
|
||||
void Read( const char* buffer ) {
|
||||
short labelSize = readUnaligned< short >( buffer );
|
||||
labelList.resize( labelSize >= 0 ? labelSize : -labelSize );
|
||||
buffer += sizeof( unsigned short );
|
||||
if ( labelSize <= 0 ) {
|
||||
dataList.resize( readUnaligned< unsigned short >( buffer ) );
|
||||
buffer += sizeof( unsigned short );
|
||||
}
|
||||
for( int i = 0; i < ( int ) labelList.size(); i++ ) {
|
||||
labelList[i].Read( buffer );
|
||||
buffer += labelList[i].GetSize();
|
||||
}
|
||||
for( int i = 0; i < ( int ) dataList.size(); i++ ) {
|
||||
dataList[i].Read( buffer );
|
||||
buffer += dataList[i].GetSize();
|
||||
}
|
||||
}
|
||||
|
||||
bool operator== ( const Node& right ) const {
|
||||
if ( dataList.size() != right.dataList.size() )
|
||||
return false;
|
||||
if ( labelList.size() != right.labelList.size() )
|
||||
return false;
|
||||
for ( int i = 0; i < ( int ) dataList.size(); ++i ) {
|
||||
if ( !( dataList[i] == right.dataList[i] ) )
|
||||
return false;
|
||||
}
|
||||
for ( int i = 0; i < ( int ) labelList.size(); ++i ) {
|
||||
if ( !( labelList[i] == right.labelList[i] ) )
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // TRIE_H
|
296
monav/unicodetournamenttrie/unicodetournamenttrieclient.cpp
Normal file
296
monav/unicodetournamenttrie/unicodetournamenttrieclient.cpp
Normal file
@ -0,0 +1,296 @@
|
||||
/*
|
||||
Copyright 2010 Christian Vetter veaac.fdirct@gmail.com
|
||||
|
||||
This file is part of MoNav.
|
||||
|
||||
MoNav is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MoNav is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY
|
||||
{
|
||||
|
||||
}
|
||||
without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with MoNav. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "unicodetournamenttrieclient.h"
|
||||
#include "utils/qthelpers.h"
|
||||
#include <QtDebug>
|
||||
#include <algorithm>
|
||||
#ifndef NOGUI
|
||||
#include <QMessageBox>
|
||||
#endif
|
||||
|
||||
UnicodeTournamentTrieClient::UnicodeTournamentTrieClient()
|
||||
{
|
||||
trieFile = NULL;
|
||||
subTrieFile = NULL;
|
||||
dataFile = NULL;
|
||||
trieData = NULL;
|
||||
subTrieData = NULL;
|
||||
placeID = -1;
|
||||
}
|
||||
|
||||
UnicodeTournamentTrieClient::~UnicodeTournamentTrieClient()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UnicodeTournamentTrieClient::unload()
|
||||
{
|
||||
if ( trieFile != NULL )
|
||||
delete trieFile;
|
||||
trieFile = NULL;
|
||||
if ( subTrieFile != NULL )
|
||||
delete subTrieFile;
|
||||
subTrieFile = NULL;
|
||||
if ( dataFile != NULL )
|
||||
delete dataFile;
|
||||
dataFile = NULL;
|
||||
}
|
||||
|
||||
QString UnicodeTournamentTrieClient::GetName()
|
||||
{
|
||||
return "Unicode Tournament Trie";
|
||||
}
|
||||
|
||||
void UnicodeTournamentTrieClient::SetInputDirectory( const QString& dir )
|
||||
{
|
||||
directory = dir;
|
||||
}
|
||||
|
||||
void UnicodeTournamentTrieClient::ShowSettings()
|
||||
{
|
||||
#ifndef NOGUI
|
||||
QMessageBox::information( NULL, "Settings", "No settings available" );
|
||||
#endif
|
||||
}
|
||||
|
||||
bool UnicodeTournamentTrieClient::LoadData()
|
||||
{
|
||||
unload();
|
||||
QString filename = fileInDirectory( directory, "Unicode Tournament Trie" );
|
||||
trieFile = new QFile( filename + "_main" );
|
||||
subTrieFile = new QFile( filename + "_sub" );
|
||||
dataFile = new QFile( filename + "_ways" );
|
||||
|
||||
if ( !openQFile( trieFile, QIODevice::ReadOnly ) )
|
||||
return false;
|
||||
if ( !openQFile( subTrieFile, QIODevice::ReadOnly ) )
|
||||
return false;
|
||||
if ( !openQFile( dataFile, QIODevice::ReadOnly ) )
|
||||
return false;
|
||||
|
||||
trieData = ( char* ) trieFile->map( 0, trieFile->size() );
|
||||
subTrieData = ( char* ) subTrieFile->map( 0, subTrieFile->size() );
|
||||
|
||||
if ( trieData == NULL ) {
|
||||
qDebug( "Failed to Memory Map trie data" );
|
||||
return false;
|
||||
}
|
||||
if ( subTrieData == NULL ) {
|
||||
qDebug( "Failed to Memory Map sub trie data" );
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnicodeTournamentTrieClient::find( const char* trie, unsigned* resultNode, QString* missingPrefix, QString prefix )
|
||||
{
|
||||
unsigned node = *resultNode;
|
||||
for ( int i = 0; i < ( int ) prefix.length(); ) {
|
||||
utt::Node element;
|
||||
element.Read( trie + node );
|
||||
bool found = false;
|
||||
for ( int c = 0; c < ( int ) element.labelList.size(); c++ ) {
|
||||
const utt::Label& label = element.labelList[c];
|
||||
bool equal = true;
|
||||
for ( int subIndex = 0; subIndex < ( int ) label.string.length(); ++subIndex ) {
|
||||
if ( i + subIndex >= ( int ) prefix.length() ) {
|
||||
*missingPrefix = label.string.mid( subIndex );
|
||||
break;
|
||||
}
|
||||
if ( label.string[subIndex] != prefix[i + subIndex] ) {
|
||||
equal = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !equal )
|
||||
continue;
|
||||
|
||||
i += label.string.length();
|
||||
node = label.index;
|
||||
found = true;
|
||||
break;
|
||||
|
||||
}
|
||||
if ( !found )
|
||||
return false;
|
||||
}
|
||||
|
||||
*resultNode = node;
|
||||
return true;
|
||||
}
|
||||
|
||||
int UnicodeTournamentTrieClient::getSuggestion( const char* trie, QStringList* resultNames, unsigned node, int count, const QString prefix )
|
||||
{
|
||||
std::vector< Suggestion > candidates( 1 );
|
||||
candidates[0].index = node;
|
||||
candidates[0].prefix = prefix;
|
||||
candidates[0].importance = std::numeric_limits< unsigned >::max();
|
||||
|
||||
while( count > 0 && candidates.size() > 0 ) {
|
||||
const Suggestion next = candidates[0];
|
||||
candidates[0] = candidates.back();
|
||||
candidates.pop_back();
|
||||
|
||||
utt::Node element;
|
||||
element.Read( trie + next.index );
|
||||
bool isThis = true;
|
||||
for ( std::vector< utt::Label >::const_iterator c = element.labelList.begin(), e = element.labelList.end(); c != e; ++c ) {
|
||||
assert( c->importance <= next.importance );
|
||||
if ( c->importance == next.importance )
|
||||
isThis = false;
|
||||
}
|
||||
if ( isThis && element.dataList.size() > 0 ) {
|
||||
assert( next.prefix.length() > 0 );
|
||||
QString suggestion = next.prefix[0].toUpper();
|
||||
for ( int i = 1; i < ( int ) next.prefix.length(); ++i ) {
|
||||
if ( suggestion[i - 1] == ' ' || suggestion[i - 1] == '-' )
|
||||
suggestion += next.prefix[i].toUpper();
|
||||
else
|
||||
suggestion += next.prefix[i];
|
||||
}
|
||||
resultNames->push_back( suggestion );
|
||||
count--;
|
||||
}
|
||||
for ( std::vector< utt::Label >::const_iterator c = element.labelList.begin(), e = element.labelList.end(); c != e; ++c ) {
|
||||
Suggestion nextEntry;
|
||||
nextEntry.prefix = next.prefix + c->string;
|
||||
nextEntry.index = c->index;
|
||||
nextEntry.importance = c->importance;
|
||||
candidates.push_back( nextEntry );
|
||||
}
|
||||
std::sort( candidates.begin(), candidates.end() );
|
||||
if ( ( int ) candidates.size() > count )
|
||||
candidates.resize( count );
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool UnicodeTournamentTrieClient::GetPlaceSuggestions( const QString& input, int amount, QStringList* suggestions, QStringList* inputSuggestions )
|
||||
{
|
||||
unsigned node = 0;
|
||||
QString prefix;
|
||||
QString name = input.toLower();
|
||||
|
||||
if ( !find( trieData, &node, &prefix, name ) )
|
||||
return false;
|
||||
|
||||
if ( prefix.length() == 0 ) {
|
||||
utt::Node element;
|
||||
element.Read( trieData + node );
|
||||
for ( std::vector< utt::Label >::const_iterator c = element.labelList.begin(), e = element.labelList.end(); c != e; ++c )
|
||||
inputSuggestions->push_back( input + c->string );
|
||||
}
|
||||
else {
|
||||
inputSuggestions->push_back( input + prefix );
|
||||
}
|
||||
getSuggestion( trieData, suggestions, node, amount, name + prefix );
|
||||
std::sort( inputSuggestions->begin(), inputSuggestions->end() );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnicodeTournamentTrieClient::SelectPlace( int id )
|
||||
{
|
||||
placeID = id;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnicodeTournamentTrieClient::GetStreetSuggestions( const QString& input, int amount, QStringList* suggestions, QStringList* inputSuggestions )
|
||||
{
|
||||
if ( placeID < 0 )
|
||||
return false;
|
||||
unsigned node = 0;
|
||||
QString prefix;
|
||||
QString name = input.toLower();
|
||||
|
||||
if ( !find( subTrieData + placeID, &node, &prefix, name ) )
|
||||
return false;
|
||||
|
||||
if ( prefix.length() == 0 ) {
|
||||
utt::Node element;
|
||||
element.Read( subTrieData + placeID + node );
|
||||
for ( std::vector< utt::Label >::const_iterator c = element.labelList.begin(), e = element.labelList.end(); c != e; ++c )
|
||||
inputSuggestions->push_back( input + c->string );
|
||||
}
|
||||
else {
|
||||
inputSuggestions->push_back( input + prefix );
|
||||
}
|
||||
getSuggestion( subTrieData + placeID, suggestions, node, amount, name + prefix );
|
||||
std::sort( inputSuggestions->begin(), inputSuggestions->end() );
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnicodeTournamentTrieClient::GetPlaceData( QString input, QVector< int >* placeIDs, QVector< UnsignedCoordinate >* placeCoordinates )
|
||||
{
|
||||
unsigned node = 0;
|
||||
QString prefix;
|
||||
QString name = input.toLower();
|
||||
if ( !find( trieData, &node, &prefix, name ) )
|
||||
return false;
|
||||
|
||||
utt::Node element;
|
||||
element.Read( trieData + node );
|
||||
|
||||
for ( std::vector< utt::Data >::const_iterator i = element.dataList.begin(), e = element.dataList.end(); i != e; ++i ) {
|
||||
utt::CityData data;
|
||||
data.Read( subTrieData + i->start );
|
||||
placeCoordinates->push_back( data.coordinate );
|
||||
placeIDs->push_back( i->start + data.GetSize() );
|
||||
}
|
||||
|
||||
return placeIDs->size() != 0;
|
||||
}
|
||||
|
||||
bool UnicodeTournamentTrieClient::GetStreetData( QString input, QVector< int >* segmentLength, QVector< UnsignedCoordinate >* coordinates )
|
||||
{
|
||||
if ( placeID < 0 )
|
||||
return false;
|
||||
unsigned node = 0;
|
||||
QString prefix;
|
||||
QString name = input.toLower();
|
||||
if ( !find( subTrieData + placeID, &node, &prefix, name ) )
|
||||
return false;
|
||||
|
||||
utt::Node element;
|
||||
element.Read( subTrieData + placeID + node );
|
||||
|
||||
for ( std::vector< utt::Data >::const_iterator i = element.dataList.begin(), e = element.dataList.end(); i != e; ++i ) {
|
||||
unsigned* buffer = new unsigned[i->length * 2];
|
||||
dataFile->seek( i->start * sizeof( unsigned ) * 2 );
|
||||
dataFile->read( ( char* ) buffer, i->length * 2 * sizeof( unsigned ) );
|
||||
for ( unsigned start = 0; start < i->length; ++start ) {
|
||||
UnsignedCoordinate temp;
|
||||
temp.x = buffer[start * 2];
|
||||
temp.y = buffer[start * 2 + 1];
|
||||
coordinates->push_back( temp );
|
||||
}
|
||||
delete[] buffer;
|
||||
segmentLength->push_back( coordinates->size() );
|
||||
}
|
||||
|
||||
return segmentLength->size() != 0;
|
||||
}
|
||||
|
||||
Q_EXPORT_PLUGIN2(unicodetournamenttrieclient, UnicodeTournamentTrieClient)
|
79
monav/unicodetournamenttrie/unicodetournamenttrieclient.h
Normal file
79
monav/unicodetournamenttrie/unicodetournamenttrieclient.h
Normal file
@ -0,0 +1,79 @@
|
||||
/*
|
||||
Copyright 2010 Christian Vetter veaac.fdirct@gmail.com
|
||||
|
||||
This file is part of MoNav.
|
||||
|
||||
MoNav is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MoNav is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with MoNav. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef UNICODETOURNAMENTTRIECLIENT_H
|
||||
#define UNICODETOURNAMENTTRIECLIENT_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QtPlugin>
|
||||
#include <QFile>
|
||||
#include "interfaces/iaddresslookup.h"
|
||||
#include "trie.h"
|
||||
|
||||
class UnicodeTournamentTrieClient : public QObject, public IAddressLookup
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_INTERFACES( IAddressLookup )
|
||||
public:
|
||||
|
||||
explicit UnicodeTournamentTrieClient();
|
||||
~UnicodeTournamentTrieClient();
|
||||
|
||||
virtual QString GetName();
|
||||
virtual void SetInputDirectory( const QString& dir );
|
||||
virtual void ShowSettings();
|
||||
virtual bool LoadData();
|
||||
virtual bool GetPlaceSuggestions( const QString& input, int amount, QStringList* suggestions, QStringList* inputSuggestions );
|
||||
virtual bool GetStreetSuggestions( const QString& input, int amount, QStringList* suggestions, QStringList* inputSuggestions );
|
||||
virtual bool SelectPlace( int placeID );
|
||||
virtual bool GetPlaceData( QString input, QVector< int >* placeIDs, QVector< UnsignedCoordinate >* placeCoordinates );
|
||||
virtual bool GetStreetData( QString input, QVector< int >* segmentLength, QVector< UnsignedCoordinate >* coordinates );
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
protected:
|
||||
|
||||
struct Suggestion {
|
||||
unsigned importance;
|
||||
unsigned index;
|
||||
QString prefix;
|
||||
|
||||
bool operator<( const Suggestion& right ) const {
|
||||
return importance > right.importance;
|
||||
}
|
||||
};
|
||||
|
||||
void unload();
|
||||
bool find( const char* trie, unsigned* resultNode, QString* missingPrefix, QString prefix );
|
||||
int getSuggestion( const char* trie, QStringList* resultNames, unsigned node, int count, const QString prefix );
|
||||
|
||||
QString directory;
|
||||
QFile* trieFile;
|
||||
QFile* subTrieFile;
|
||||
QFile* dataFile;
|
||||
const char* trieData;
|
||||
const char* subTrieData;
|
||||
|
||||
int placeID;
|
||||
|
||||
};
|
||||
|
||||
#endif // UNICODETOURNAMENTTRIECLIENT_H
|
25
monav/unicodetournamenttrie/unicodetournamenttrieclient.pro
Normal file
25
monav/unicodetournamenttrie/unicodetournamenttrieclient.pro
Normal file
@ -0,0 +1,25 @@
|
||||
#-------------------------------------------------
|
||||
#
|
||||
# Project created by QtCreator 2010-06-22T13:51:45
|
||||
#
|
||||
#-------------------------------------------------
|
||||
|
||||
DESTDIR = ..
|
||||
TEMPLATE = lib
|
||||
CONFIG += plugin
|
||||
|
||||
HEADERS += utils/coordinates.h \
|
||||
utils/config.h \
|
||||
interfaces/iaddresslookup.h \
|
||||
trie.h \
|
||||
unicodetournamenttrieclient.h \
|
||||
utils/qthelpers.h
|
||||
|
||||
unix {
|
||||
QMAKE_CXXFLAGS_RELEASE -= -O2
|
||||
QMAKE_CXXFLAGS_RELEASE += -O3 -Wno-unused-function
|
||||
QMAKE_CXXFLAGS_DEBUG += -Wno-unused-function
|
||||
}
|
||||
|
||||
SOURCES += \
|
||||
unicodetournamenttrieclient.cpp
|
1
monav/unicodetournamenttrie/utils
Symbolic link
1
monav/unicodetournamenttrie/utils
Symbolic link
@ -0,0 +1 @@
|
||||
../utils/
|
203
monav/utils/bithelpers.h
Normal file
203
monav/utils/bithelpers.h
Normal file
@ -0,0 +1,203 @@
|
||||
/*
|
||||
Copyright 2010 Christian Vetter veaac.fdirct@gmail.com
|
||||
|
||||
This file is part of MoNav.
|
||||
|
||||
MoNav is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MoNav is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with MoNav. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef BITHELPERS_H
|
||||
#define BITHELPERS_H
|
||||
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
||||
template< class T >
|
||||
static inline T readUnaligned( const char* buffer ) {
|
||||
T temp;
|
||||
memcpy( &temp, buffer, sizeof( T ) );
|
||||
return temp;
|
||||
}
|
||||
|
||||
// reads first bits to a max of 31 bits ( 31 because 1u << 32 is undefined )
|
||||
// offset has to be <8
|
||||
// safe with unaligned memory access
|
||||
static inline unsigned read_unaligned_unsigned( const unsigned char* buffer, int offset ){
|
||||
assert ( offset <= 7 );
|
||||
|
||||
const int diff = ( ( size_t ) buffer ) & 3;
|
||||
buffer -= diff;
|
||||
offset += 8 * diff;
|
||||
|
||||
unsigned temp = * ( unsigned * ) buffer;
|
||||
if ( offset == 0 )
|
||||
return temp;
|
||||
unsigned temp2 = * ( ( ( unsigned * ) buffer ) + 1);
|
||||
return ( temp >> offset ) | ( temp2 << ( 32 - offset ) );
|
||||
}
|
||||
|
||||
static inline unsigned read_unaligned_unsigned( const unsigned char** buffer, int bits, int* offset ){
|
||||
assert ( *offset <= 7 );
|
||||
|
||||
const int diff = ( ( size_t ) *buffer ) & 3;
|
||||
const unsigned char* alignedBuffer = *buffer - diff;
|
||||
int alignedOffset = *offset + 8 * diff;
|
||||
unsigned temp = * ( unsigned * ) alignedBuffer;
|
||||
unsigned temp2 = * ( ( ( unsigned * ) alignedBuffer ) + 1);
|
||||
unsigned result;
|
||||
if ( alignedOffset == 0 )
|
||||
result = temp;
|
||||
else
|
||||
result = ( temp >> alignedOffset ) | ( temp2 << ( 32 - alignedOffset ) );
|
||||
|
||||
*offset += bits;
|
||||
*buffer += ( *offset ) >> 3;
|
||||
*offset &= 7;
|
||||
|
||||
if ( bits == 32 )
|
||||
return result;
|
||||
|
||||
return result & ( ( 1u << bits ) - 1 );
|
||||
}
|
||||
|
||||
static inline unsigned read_unaligned_unsigned( const unsigned char* buffer, int bits, int offset ){
|
||||
assert ( offset <= 7 );
|
||||
|
||||
const int diff = ( ( size_t ) buffer ) & 3;
|
||||
const unsigned char* alignedBuffer = buffer - diff;
|
||||
int alignedOffset = offset + 8 * diff;
|
||||
unsigned temp = * ( unsigned * ) alignedBuffer;
|
||||
unsigned temp2 = * ( ( ( unsigned * ) alignedBuffer ) + 1);
|
||||
unsigned result;
|
||||
if ( alignedOffset == 0 )
|
||||
result = temp;
|
||||
else
|
||||
result = ( temp >> alignedOffset ) | ( temp2 << ( 32 - alignedOffset ) );
|
||||
|
||||
if ( bits == 32 )
|
||||
return result;
|
||||
|
||||
return result & ( ( 1u << bits ) - 1 );
|
||||
}
|
||||
|
||||
// writes #bits bits of data into the buffer at the offset
|
||||
// offset has to be <8, **buffer has to be zeroed
|
||||
// modifies buffer and offset to point after the inserted data
|
||||
static inline void write_unaligned_unsigned( unsigned char** buffer, unsigned data, int bits, int* offset ) {
|
||||
( ( unsigned* ) *buffer )[0] |= ( data << ( *offset ) );
|
||||
( ( unsigned* ) ( *buffer + 1 ) )[0] |= ( data >> ( 8 - *offset ) );
|
||||
|
||||
#ifndef NDEBUG
|
||||
const unsigned char* tempBuffer = *buffer;
|
||||
int tempOffset = *offset;
|
||||
unsigned tempData = read_unaligned_unsigned( &tempBuffer, bits, &tempOffset );
|
||||
assert( tempData == data );
|
||||
#endif
|
||||
|
||||
*offset += bits;
|
||||
*buffer += ( *offset ) >> 3;
|
||||
*offset &= 7;
|
||||
}
|
||||
|
||||
static inline unsigned read_bits ( unsigned data, char bits ) {
|
||||
if ( bits == 32 )
|
||||
return data;
|
||||
|
||||
return data & ( ( 1u << bits ) - 1 );
|
||||
}
|
||||
|
||||
static inline unsigned log2_rounded ( unsigned x ) {
|
||||
static const unsigned bit_position[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;
|
||||
|
||||
return bit_position[ ( x * 0x077CB531u ) >> 27];
|
||||
}
|
||||
|
||||
// computes log2_rounded( x + 1 ), works even up to x = 2^32 - 1
|
||||
static inline unsigned bits_needed( unsigned x )
|
||||
{
|
||||
static const unsigned bit_position[32] = {
|
||||
32, 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
|
||||
};
|
||||
//slower, maybe think of a better workaround
|
||||
if ( x == 0 )
|
||||
return 0;
|
||||
|
||||
//+1 and round up
|
||||
x |= x >> 1;
|
||||
x |= x >> 2;
|
||||
x |= x >> 4;
|
||||
x |= x >> 8;
|
||||
x |= x >> 16;
|
||||
++x;
|
||||
|
||||
return bit_position[ ( x * 0x077CB531u ) >> 27];
|
||||
}
|
||||
|
||||
template< int exponentBits, int significantBits >
|
||||
static unsigned decode_integer( unsigned x )
|
||||
{
|
||||
if ( x == ( ( 1u << ( exponentBits + significantBits ) ) - 1 ) )
|
||||
return 0;
|
||||
unsigned exponent = x >> significantBits;
|
||||
unsigned significant = x & ( ( 1u << significantBits ) - 1 );
|
||||
significant = ( significant << 1 ) | 1; // implicit 1
|
||||
return significant << exponent;
|
||||
}
|
||||
|
||||
template< int exponentBits, int significantBits >
|
||||
static unsigned encode_integer( unsigned x )
|
||||
{
|
||||
assert ( exponentBits > 0 );
|
||||
assert( significantBits > 0 );
|
||||
|
||||
static bool initialized = false;
|
||||
static const unsigned numEncoded = 1u << ( exponentBits + significantBits );
|
||||
typedef std::pair< unsigned, unsigned > Lookup;
|
||||
static Lookup lookup[numEncoded];
|
||||
|
||||
if ( !initialized ) {
|
||||
for ( unsigned value = 0; value < numEncoded; value++ )
|
||||
lookup[value] = Lookup( decode_integer< exponentBits, significantBits >( value ), value );
|
||||
std::sort( lookup, lookup + numEncoded );
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
Lookup* value = std::lower_bound( lookup, lookup + numEncoded, Lookup( x, 0 ) );
|
||||
|
||||
if ( value >= lookup + numEncoded - 1 )
|
||||
return lookup[numEncoded - 1].second;
|
||||
|
||||
unsigned diffFirst = x - value->first;
|
||||
unsigned diffSecond = ( value + 1 )->first - x;
|
||||
|
||||
if ( diffFirst < diffSecond )
|
||||
return value->second;
|
||||
else
|
||||
return ( value + 1 )->second;
|
||||
}
|
||||
|
||||
#endif // BITHELPERS_H
|
25
monav/utils/config.h
Normal file
25
monav/utils/config.h
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
Copyright 2010 Christian Vetter veaac.fdirct@gmail.com
|
||||
|
||||
This file is part of MoNav.
|
||||
|
||||
MoNav is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MoNav is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with MoNav. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_H_INCLUDED
|
||||
#define CONFIG_H_INCLUDED
|
||||
|
||||
typedef unsigned NodeID;
|
||||
|
||||
#endif // CONFIG_H_INCLUDED
|
289
monav/utils/coordinates.h
Normal file
289
monav/utils/coordinates.h
Normal file
@ -0,0 +1,289 @@
|
||||
/*
|
||||
Copyright 2010 Christian Vetter veaac.fdirct@gmail.com
|
||||
|
||||
This file is part of MoNav.
|
||||
|
||||
MoNav is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MoNav is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with MoNav. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef COORDINATES_H_INCLUDED
|
||||
#define COORDINATES_H_INCLUDED
|
||||
|
||||
#include <limits>
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
class GPSCoordinate {
|
||||
public:
|
||||
|
||||
GPSCoordinate()
|
||||
{
|
||||
latitude = longitude = std::numeric_limits< double >::max();
|
||||
}
|
||||
GPSCoordinate( double lat, double lon )
|
||||
{
|
||||
latitude = lat;
|
||||
longitude = lon;
|
||||
}
|
||||
|
||||
double Distance( const GPSCoordinate &right ) const
|
||||
{
|
||||
assert( fabs( latitude ) < 90 && fabs( right.latitude ) < 90 );
|
||||
//assert ( nicht antipodal, nicht an den Polen )
|
||||
|
||||
// convert inputs in degrees to radians:
|
||||
static const double DEG_TO_RAD = 0.017453292519943295769236907684886;
|
||||
double lat1 = latitude * DEG_TO_RAD;
|
||||
double lon1 = longitude * DEG_TO_RAD;
|
||||
double lat2 = right.latitude * DEG_TO_RAD;
|
||||
double lon2 = right.longitude * DEG_TO_RAD;
|
||||
|
||||
static const double a = 6378137;
|
||||
static const double b = 6356752.31424518;
|
||||
static const double f = ( a - b ) / a;
|
||||
|
||||
const double U1 = atan(( 1 - f ) * tan( lat1 ) );
|
||||
const double U2 = atan(( 1 - f ) * tan( lat2 ) );
|
||||
const double cosU1 = cos( U1 );
|
||||
const double cosU2 = cos( U2 );
|
||||
const double sinU1 = sin( U1 );
|
||||
const double sinU2 = sin( U2 );
|
||||
const double L = fabs( lon2 - lon1 );
|
||||
|
||||
double lambda = L;
|
||||
double lambdaOld;
|
||||
unsigned iterLimit = 50;
|
||||
while ( true ) {
|
||||
const double cosLambda = cos( lambda );
|
||||
const double sinLambda = sin( lambda );
|
||||
const double leftSinSigma = cosU2 * sinLambda;
|
||||
const double rightSinSigma = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda;
|
||||
const double sinSigma = sqrt( leftSinSigma * leftSinSigma + rightSinSigma * rightSinSigma );
|
||||
if ( sinSigma == 0 ) // Fast identisch
|
||||
return 0;
|
||||
const double cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
|
||||
const double sigma = atan2( sinSigma, cosSigma );
|
||||
const double sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
|
||||
const double cosSquareAlpha = 1 - sinAlpha * sinAlpha;
|
||||
double cos2sigmam = cosSigma - 2 * sinU1 * sinU2 / cosSquareAlpha;
|
||||
if ( cos2sigmam != cos2sigmam ) // NAN: Parellel zum Äquator
|
||||
cos2sigmam = 0;
|
||||
const double C = f / 16 * cosSquareAlpha * ( 4 + f * ( 4 - 3 * cosSquareAlpha ) );
|
||||
lambdaOld = lambda;
|
||||
lambda = L + ( 1 - C ) * f * sinAlpha * ( sigma + C * sinSigma * ( cos2sigmam + C * cosSigma * ( -1 + 2 * cos2sigmam * cos2sigmam ) ) );
|
||||
if ( fabs( lambda - lambdaOld ) < 1e-12 || --iterLimit == 0 ) {
|
||||
const double u2 = cosSquareAlpha * ( a * a - b * b ) / ( b * b );
|
||||
const double A = 1 + u2 / 16384 * ( 4096 + u2 * ( -768 + u2 * ( 320 - 175 * u2 ) ) );
|
||||
const double B = u2 / 1024 * ( 256 + u2 * ( -128 + u2 * ( 74 - 47 * u2 ) ) );
|
||||
const double deltasigma = B * sinSigma * ( cos2sigmam + B / 4 * ( cosSigma * ( -1 + 2 * cos2sigmam * cos2sigmam ) - B / 6 * cos2sigmam * ( -3 + 4 * sinSigma * sinSigma ) * ( -3 + 4 * cos2sigmam * cos2sigmam ) ) );
|
||||
return b * A * ( sigma - deltasigma );
|
||||
}
|
||||
}
|
||||
|
||||
//should never be reached
|
||||
return 0;
|
||||
}
|
||||
|
||||
double ApproximateDistance( const GPSCoordinate &right ) const
|
||||
{
|
||||
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 = ( latitude - right.latitude ) * DEG_TO_RAD;
|
||||
double longitudeArc = ( longitude - right.longitude ) * DEG_TO_RAD;
|
||||
double latitudeH = sin( latitudeArc * 0.5 );
|
||||
latitudeH *= latitudeH;
|
||||
double lontitudeH = sin( longitudeArc * 0.5 );
|
||||
lontitudeH *= lontitudeH;
|
||||
double tmp = cos( latitude * DEG_TO_RAD ) * cos( right.latitude * DEG_TO_RAD );
|
||||
double distanceArc = 2.0 * asin( sqrt( latitudeH + tmp * lontitudeH ) );
|
||||
return EARTH_RADIUS_IN_METERS * distanceArc;
|
||||
}
|
||||
|
||||
bool operator==( const GPSCoordinate& right ) const
|
||||
{
|
||||
return latitude == right.latitude && longitude == right.longitude;
|
||||
}
|
||||
|
||||
bool operator!=( const GPSCoordinate& right ) const
|
||||
{
|
||||
return !( *this == right );
|
||||
}
|
||||
|
||||
bool operator<( const GPSCoordinate& right ) const
|
||||
{
|
||||
if ( latitude != right.latitude )
|
||||
return latitude < right.latitude;
|
||||
return longitude < right.longitude;
|
||||
}
|
||||
|
||||
bool IsValid() const
|
||||
{
|
||||
return latitude != std::numeric_limits< double >::max() && longitude != std::numeric_limits< double >::max();
|
||||
}
|
||||
|
||||
double latitude, longitude;
|
||||
};
|
||||
|
||||
class ProjectedCoordinate {
|
||||
public:
|
||||
|
||||
ProjectedCoordinate()
|
||||
{
|
||||
x = y = std::numeric_limits< double >::max();
|
||||
}
|
||||
|
||||
ProjectedCoordinate( double xVal, double yVal )
|
||||
{
|
||||
x = xVal;
|
||||
y = yVal;
|
||||
}
|
||||
|
||||
ProjectedCoordinate( double xVal, double yVal, int zoom ) {
|
||||
x = xVal / ( 1u << zoom );
|
||||
y = yVal / ( 1u << zoom );
|
||||
}
|
||||
|
||||
explicit ProjectedCoordinate( const GPSCoordinate& gps )
|
||||
{
|
||||
x = ( gps.longitude + 180.0 ) / 360.0;
|
||||
y = ( 1.0 - log( tan( gps.latitude * M_PI / 180.0 ) + 1.0 / cos( gps.latitude * M_PI / 180.0 ) ) / M_PI ) / 2.0;
|
||||
}
|
||||
|
||||
GPSCoordinate ToGPSCoordinate() const
|
||||
{
|
||||
GPSCoordinate gps;
|
||||
gps.longitude = x * 360.0 - 180;
|
||||
const double n = M_PI - 2.0 * M_PI * y;
|
||||
gps.latitude = 180.0 / M_PI * atan( 0.5 * ( exp( n ) - exp( -n ) ) );
|
||||
return gps;
|
||||
}
|
||||
|
||||
bool operator==( const ProjectedCoordinate& right ) const
|
||||
{
|
||||
return x == right.x && y == right.y;
|
||||
}
|
||||
|
||||
bool operator!=( const ProjectedCoordinate& right ) const
|
||||
{
|
||||
return !( *this == right );
|
||||
}
|
||||
|
||||
bool operator<( const ProjectedCoordinate& right ) const
|
||||
{
|
||||
if ( x != right.x )
|
||||
return x < right.x;
|
||||
return y < right.y;
|
||||
}
|
||||
|
||||
bool IsValid() const
|
||||
{
|
||||
return x != std::numeric_limits< double >::max() && y != std::numeric_limits< double >::max();
|
||||
}
|
||||
|
||||
double x, y;
|
||||
};
|
||||
|
||||
class UnsignedCoordinate {
|
||||
public:
|
||||
|
||||
UnsignedCoordinate()
|
||||
{
|
||||
x = y = std::numeric_limits< unsigned >::max();
|
||||
}
|
||||
|
||||
UnsignedCoordinate( unsigned xVal, unsigned yVal )
|
||||
{
|
||||
x = xVal;
|
||||
y = yVal;
|
||||
}
|
||||
|
||||
explicit UnsignedCoordinate( ProjectedCoordinate tile )
|
||||
{
|
||||
x = floor( tile.x * ( 1u << 30 ) );
|
||||
y = floor( tile.y * ( 1u << 30 ) );
|
||||
}
|
||||
|
||||
explicit UnsignedCoordinate( GPSCoordinate gps )
|
||||
{
|
||||
*this = UnsignedCoordinate( ProjectedCoordinate( gps ) );
|
||||
}
|
||||
|
||||
GPSCoordinate ToGPSCoordinate() const
|
||||
{
|
||||
return ToProjectedCoordinate().ToGPSCoordinate();
|
||||
}
|
||||
|
||||
ProjectedCoordinate ToProjectedCoordinate() const
|
||||
{
|
||||
ProjectedCoordinate tile;
|
||||
tile.x = x;
|
||||
tile.y = y;
|
||||
tile.x /= ( 1u << 30 );
|
||||
tile.y /= ( 1u << 30 );
|
||||
return tile;
|
||||
}
|
||||
|
||||
unsigned GetTileX( int zoom ) const {
|
||||
if ( zoom == 0 )
|
||||
return 0;
|
||||
return x >> ( 30 - zoom );
|
||||
}
|
||||
unsigned GetTileY( int zoom ) const {
|
||||
if ( zoom == 0 )
|
||||
return 0;
|
||||
return y >> ( 30 - zoom );
|
||||
}
|
||||
unsigned GetTileSubX( int zoom, int precision ) const {
|
||||
assert( zoom + precision < 31 );
|
||||
assert( zoom + precision > 0 );
|
||||
const unsigned subX = ( x << zoom ) >> zoom;
|
||||
return subX >> ( 30 - precision - zoom );
|
||||
}
|
||||
unsigned GetTileSubY( int zoom, int precision ) const {
|
||||
assert( zoom + precision < 31 );
|
||||
assert( zoom + precision > 0 );
|
||||
const unsigned subY = ( y << zoom ) >> zoom;
|
||||
return subY >> ( 30 - precision - zoom );
|
||||
}
|
||||
|
||||
bool operator==( const UnsignedCoordinate& right ) const
|
||||
{
|
||||
return x == right.x && y == right.y;
|
||||
}
|
||||
|
||||
bool operator!=( const UnsignedCoordinate& right ) const
|
||||
{
|
||||
return !( *this == right );
|
||||
}
|
||||
|
||||
bool operator<( const UnsignedCoordinate& right ) const
|
||||
{
|
||||
if ( x != right.x )
|
||||
return x < right.x;
|
||||
return y < right.y;
|
||||
}
|
||||
|
||||
bool IsValid() const
|
||||
{
|
||||
return x != std::numeric_limits< unsigned >::max() && y != std::numeric_limits< unsigned >::max();
|
||||
}
|
||||
|
||||
unsigned x, y;
|
||||
};
|
||||
|
||||
#endif // COORDINATES_H_INCLUDED
|
135
monav/utils/edgeconnector.h
Normal file
135
monav/utils/edgeconnector.h
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
Copyright 2010 Christian Vetter veaac.fdirct@gmail.com
|
||||
|
||||
This file is part of MoNav.
|
||||
|
||||
MoNav is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MoNav is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with MoNav. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef EDGECONNECTOR_H
|
||||
#define EDGECONNECTOR_H
|
||||
|
||||
#include "utils/coordinates.h"
|
||||
#include <vector>
|
||||
#include <QMultiHash>
|
||||
#include <algorithm>
|
||||
|
||||
static uint qHash( const UnsignedCoordinate& key )
|
||||
{
|
||||
return qHash( key.x ) ^ qHash( key.y );
|
||||
}
|
||||
|
||||
template< class Node >
|
||||
class EdgeConnector
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
struct Edge {
|
||||
Node source;
|
||||
Node target;
|
||||
bool reverseable;
|
||||
};
|
||||
|
||||
static void run( std::vector< unsigned >* segments, std::vector< unsigned >* segmentDescriptions, std::vector< bool >* reversed, const std::vector< Edge >& edges )
|
||||
{
|
||||
// build node index
|
||||
QMultiHash< Node, unsigned > nodes;
|
||||
QMultiHash< Node, unsigned > backwardNodes;
|
||||
for ( unsigned i = 0; i < edges.size(); i++ ) {
|
||||
nodes.insert( edges[i].source, i );
|
||||
backwardNodes.insert( edges[i].target, i );
|
||||
if ( edges[i].reverseable ) {
|
||||
nodes.insert( edges[i].target, i );
|
||||
backwardNodes.insert( edges[i].source, i );
|
||||
}
|
||||
}
|
||||
|
||||
std::vector< bool > used( edges.size(), false );
|
||||
reversed->resize( edges.size(), false );
|
||||
for ( unsigned i = 0; i < edges.size(); i++ ) {
|
||||
if ( used[i] )
|
||||
continue;
|
||||
|
||||
unsigned lastSize = segmentDescriptions->size();
|
||||
|
||||
segmentDescriptions->push_back( i );
|
||||
|
||||
used[i] = true;
|
||||
removeEdge( &nodes, &backwardNodes, edges[i], i );
|
||||
|
||||
// chain edges forward
|
||||
Node lastNode = edges[i].target;
|
||||
while ( nodes.contains( lastNode ) ) {
|
||||
unsigned nextEdge = nodes.value( lastNode );
|
||||
assert( !used[nextEdge] );
|
||||
|
||||
if ( lastNode != edges[nextEdge].source ) {
|
||||
assert( lastNode == edges[nextEdge].target );
|
||||
assert( edges[nextEdge].reverseable );
|
||||
( *reversed )[nextEdge] = true;
|
||||
lastNode = edges[nextEdge].source;
|
||||
} else {
|
||||
lastNode = edges[nextEdge].target;
|
||||
}
|
||||
|
||||
segmentDescriptions->push_back( nextEdge );
|
||||
|
||||
used[nextEdge] = true;
|
||||
removeEdge( &nodes, &backwardNodes, edges[nextEdge], nextEdge );
|
||||
}
|
||||
|
||||
// chain edges backward
|
||||
std::vector< unsigned > backwardsPath;
|
||||
lastNode = edges[i].source;
|
||||
while ( backwardNodes.contains( lastNode ) ) {
|
||||
unsigned nextEdge = backwardNodes.value( lastNode );
|
||||
assert( !used[nextEdge] );
|
||||
|
||||
if ( lastNode != edges[nextEdge].target ) {
|
||||
assert( lastNode == edges[nextEdge].source );
|
||||
assert( edges[nextEdge].reverseable );
|
||||
( *reversed )[nextEdge] = true;
|
||||
lastNode = edges[nextEdge].target;
|
||||
} else {
|
||||
lastNode = edges[nextEdge].source;
|
||||
}
|
||||
|
||||
backwardsPath.push_back( nextEdge );
|
||||
|
||||
used[nextEdge] = true;
|
||||
removeEdge( &nodes, &backwardNodes, edges[nextEdge], nextEdge );
|
||||
}
|
||||
|
||||
segmentDescriptions->insert( segmentDescriptions->begin() + lastSize, backwardsPath.rbegin(), backwardsPath.rend() );
|
||||
|
||||
segments->push_back( segmentDescriptions->size() - lastSize );
|
||||
}
|
||||
|
||||
assert( segmentDescriptions->size() == edges.size() );
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
static void removeEdge( QMultiHash< Node, unsigned >* nodes, QMultiHash< Node, unsigned >* backwardsNodes, const Edge& edge, unsigned value )
|
||||
{
|
||||
nodes->remove( edge.source, value );
|
||||
nodes->remove( edge.target, value );
|
||||
backwardsNodes->remove( edge.target, value );
|
||||
backwardsNodes->remove( edge.source, value );
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif // EDGECONNECTOR_H
|
75
monav/utils/qthelpers.h
Normal file
75
monav/utils/qthelpers.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
Copyright 2010 Christian Vetter veaac.fdirct@gmail.com
|
||||
|
||||
This file is part of MoNav.
|
||||
|
||||
MoNav is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
MoNav is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with MoNav. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef QTHELPERS_H
|
||||
#define QTHELPERS_H
|
||||
|
||||
#include <QFile>
|
||||
#include <QtDebug>
|
||||
#include <QDataStream>
|
||||
#include <QTime>
|
||||
#include <QDir>
|
||||
|
||||
static inline QString fileInDirectory( QString directory, QString filename )
|
||||
{
|
||||
QDir dir( directory );
|
||||
return dir.filePath( filename );
|
||||
}
|
||||
|
||||
static inline bool openQFile( QFile* file, QIODevice::OpenMode mode )
|
||||
{
|
||||
if ( !file->open( mode ) ) {
|
||||
qCritical() << "could not open file:" << file->fileName() << "," << mode;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
class FileStream : public QDataStream {
|
||||
|
||||
public:
|
||||
|
||||
FileStream( QString filename ) : m_file( filename )
|
||||
{
|
||||
}
|
||||
|
||||
bool open( QIODevice::OpenMode mode )
|
||||
{
|
||||
if ( !openQFile( &m_file, mode ) )
|
||||
return false;
|
||||
setDevice( &m_file );
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
QFile m_file;
|
||||
};
|
||||
|
||||
class Timer : public QTime {
|
||||
|
||||
public:
|
||||
|
||||
Timer()
|
||||
{
|
||||
start();
|
||||
}
|
||||
};
|
||||
|
||||
#endif // QTHELPERS_H
|
186
monavlayer.cpp
Normal file
186
monavlayer.cpp
Normal file
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright 2010 Niels Kummerfeldt <niels.kummerfeldt@tu-harburg.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "monavlayer.h"
|
||||
|
||||
#include "mapwidget.h"
|
||||
#include "projection.h"
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QDir>
|
||||
#include <QtCore/QFile>
|
||||
#include <QtCore/QPluginLoader>
|
||||
#include <QtCore/QSettings>
|
||||
|
||||
MonavLayer::MonavLayer(MapWidget *map) :
|
||||
AbstractLayer(map),
|
||||
m_gpsLookup(0),
|
||||
m_router(0),
|
||||
m_loaded(true),
|
||||
m_routeStart(),
|
||||
m_routeEnd(),
|
||||
m_track(),
|
||||
m_trackOnScreen(),
|
||||
m_trackOffset(),
|
||||
m_zoomLevel(0)
|
||||
{
|
||||
QSettings set(QDir::homePath()+"/Maps/nanomap.conf", QSettings::NativeFormat);
|
||||
set.beginGroup("monav");
|
||||
QString dataDir = set.value("datadir").toString();
|
||||
QString routerLib = set.value("router",
|
||||
"/usr/lib/monav/libcontractionhierarchiesclient.so").toString();
|
||||
QString gpsLookupLib = set.value("gpslookup",
|
||||
"/usr/lib/monav/libgpsgridclient.so").toString();
|
||||
set.endGroup();
|
||||
|
||||
QSettings pluginSettings(dataDir+"/plugins.ini", QSettings::IniFormat);
|
||||
QString routerName = pluginSettings.value("router").toString();
|
||||
QString gpsLookupName = pluginSettings.value("gpsLookup").toString();
|
||||
|
||||
QPluginLoader rLoader(routerLib);
|
||||
QObject *plugin = rLoader.instance();
|
||||
if (plugin) {
|
||||
m_router = qobject_cast<IRouter*>(plugin);
|
||||
if (m_router) {
|
||||
m_router->SetInputDirectory(dataDir);
|
||||
m_loaded = m_loaded && m_router->LoadData();
|
||||
} else {
|
||||
m_loaded = false;
|
||||
}
|
||||
}
|
||||
QPluginLoader gLoader(gpsLookupLib);
|
||||
plugin = gLoader.instance();
|
||||
if (plugin) {
|
||||
m_gpsLookup = qobject_cast<IGPSLookup*>(plugin);
|
||||
if (m_gpsLookup) {
|
||||
m_gpsLookup->SetInputDirectory(dataDir);
|
||||
m_loaded = m_loaded && m_gpsLookup->LoadData();
|
||||
} else {
|
||||
m_loaded = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MonavLayer::zoom(int level)
|
||||
{
|
||||
m_zoomLevel = level;
|
||||
if (m_track.count() > 1) {
|
||||
int scale = 1 << level;
|
||||
m_trackOnScreen.clear();
|
||||
m_trackOffset = map()->raw2screen(m_track.first().x(), m_track.first().y(), scale);
|
||||
m_trackOnScreen << QPoint(0, 0);
|
||||
for (int i = 1; i < m_track.count(); ++i) {
|
||||
QPointF p = m_track.at(i);
|
||||
m_trackOnScreen << map()->raw2screen(p.x(), p.y(), scale) - m_trackOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MonavLayer::pan(const QPoint &move)
|
||||
{
|
||||
m_trackOffset += move;
|
||||
}
|
||||
|
||||
void MonavLayer::paint(QPainter *painter)
|
||||
{
|
||||
if (!m_loaded) {
|
||||
return;
|
||||
}
|
||||
if (m_trackOnScreen.count() > 1) {
|
||||
QPoint p1, p2 = m_trackOnScreen.first();
|
||||
for (int i = 1; i < m_trackOnScreen.count(); ++i) {
|
||||
p1 = m_trackOnScreen.at(i);
|
||||
painter->drawLine(p1 + m_trackOffset, p2 + m_trackOffset);
|
||||
p2 = p1;
|
||||
}
|
||||
}
|
||||
|
||||
QPoint p = map()->geo2screen(m_routeStart.x(), m_routeStart.y());
|
||||
QPolygon tri;
|
||||
tri << p << p+QPoint(-5, -9) << p+QPoint(5, -9) << p;
|
||||
painter->setBrush(Qt::red);
|
||||
painter->drawPolygon(tri);
|
||||
p = map()->geo2screen(m_routeEnd.x(), m_routeEnd.y());
|
||||
tri.clear();
|
||||
tri << p << p+QPoint(-5, -9) << p+QPoint(5, -9) << p;
|
||||
painter->setBrush(Qt::blue);
|
||||
painter->drawPolygon(tri);
|
||||
}
|
||||
|
||||
void MonavLayer::keyPressed(QKeyEvent *event)
|
||||
{
|
||||
switch (event->key()) {
|
||||
case Qt::Key_R:
|
||||
{
|
||||
if (event->modifiers() == Qt::NoModifier) {
|
||||
findRoute();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Qt::Key_S:
|
||||
{
|
||||
if (event->modifiers() == Qt::NoModifier) {
|
||||
m_routeStart = map()->geoPos();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Qt::Key_E:
|
||||
{
|
||||
if (event->modifiers() == Qt::NoModifier) {
|
||||
m_routeEnd = map()->geoPos();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MonavLayer::findRoute()
|
||||
{
|
||||
if (!m_loaded) {
|
||||
return;
|
||||
}
|
||||
|
||||
QVector<IRouter::Node> nodes;
|
||||
QVector<IRouter::Edge> edges;
|
||||
double lookupRadius = 1000.0;
|
||||
double dist;
|
||||
|
||||
UnsignedCoordinate startCoord(GPSCoordinate(m_routeStart.y(), m_routeStart.x()));
|
||||
IGPSLookup::Result startPos;
|
||||
if (!m_gpsLookup->GetNearestEdge(&startPos, startCoord, lookupRadius)) {
|
||||
qDebug() << "source not found";
|
||||
return;
|
||||
}
|
||||
UnsignedCoordinate endCoord(GPSCoordinate(m_routeEnd.y(), m_routeEnd.x()));
|
||||
IGPSLookup::Result endPos;
|
||||
if (!m_gpsLookup->GetNearestEdge(&endPos, endCoord, lookupRadius)) {
|
||||
qDebug() << "target not found";
|
||||
return;
|
||||
}
|
||||
if (m_router->GetRoute(&dist, &nodes, &edges, startPos, endPos)) {
|
||||
qDebug() << "route found";
|
||||
m_track.clear();
|
||||
for (int j = 0; j < nodes.size(); ++j) {
|
||||
GPSCoordinate c = nodes[j].coordinate.ToGPSCoordinate();
|
||||
m_track << QPointF(Projection::lon2rawx(c.longitude), Projection::lat2rawy(c.latitude));
|
||||
}
|
||||
zoom(m_zoomLevel);
|
||||
}
|
||||
}
|
||||
|
57
monavlayer.h
Normal file
57
monavlayer.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright 2010 Niels Kummerfeldt <niels.kummerfeldt@tu-harburg.de>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
||||
* Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#ifndef MONAV_LAYER_H
|
||||
#define MONAV_LAYER_H
|
||||
|
||||
#include "abstractlayer.h"
|
||||
|
||||
#include <QtGui/QPainter>
|
||||
|
||||
#include "interfaces/irouter.h"
|
||||
#include "interfaces/igpslookup.h"
|
||||
|
||||
class MonavLayer : public AbstractLayer
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MonavLayer(MapWidget *map);
|
||||
|
||||
virtual void zoom(int level);
|
||||
virtual void pan(const QPoint &move);
|
||||
virtual void keyPressed(QKeyEvent *event);
|
||||
|
||||
protected:
|
||||
virtual void paint(QPainter *painter);
|
||||
|
||||
private:
|
||||
void findRoute();
|
||||
|
||||
IGPSLookup *m_gpsLookup;
|
||||
IRouter *m_router;
|
||||
bool m_loaded;
|
||||
QPointF m_routeStart, m_routeEnd;
|
||||
QPolygonF m_track;
|
||||
QList<QPoint> m_trackOnScreen;
|
||||
QPoint m_trackOffset;
|
||||
int m_zoomLevel;
|
||||
|
||||
};
|
||||
|
||||
#endif // MONAV_LAYER_H
|
33
nanomap.pro
33
nanomap.pro
@ -1,33 +1,4 @@
|
||||
TARGET = NanoMap
|
||||
TEMPLATE = app
|
||||
TEMPLATE = subdirs
|
||||
|
||||
QT += network
|
||||
SUBDIRS = monav/monav.pro app.pro
|
||||
|
||||
SOURCES += main.cpp \
|
||||
mainwidget.cpp \
|
||||
projection.cpp \
|
||||
abstractlayer.cpp \
|
||||
gpslayer.cpp \
|
||||
markerlayer.cpp \
|
||||
gpxlayer.cpp \
|
||||
timelayer.cpp \
|
||||
batterylayer.cpp \
|
||||
mapwidget.cpp \
|
||||
markerlist.cpp \
|
||||
downloadwidget.cpp \
|
||||
routingwidget.cpp \
|
||||
gpsclient.cpp
|
||||
|
||||
HEADERS += mainwidget.h \
|
||||
projection.h \
|
||||
abstractlayer.h \
|
||||
gpslayer.h \
|
||||
markerlayer.h \
|
||||
gpxlayer.h \
|
||||
timelayer.h \
|
||||
batterylayer.h \
|
||||
mapwidget.h \
|
||||
markerlist.h \
|
||||
downloadwidget.h \
|
||||
routingwidget.h \
|
||||
gpsclient.h
|
||||
|
Loading…
Reference in New Issue
Block a user