1
0
mirror of git://projects.qi-hardware.com/nanomap.git synced 2024-11-22 12:51:34 +02:00
nanomap/monav/contractionhierarchies/compressedgraph.h
2010-11-17 22:05:12 +01:00

601 lines
17 KiB
C++

/*
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