3700 lines
97 KiB
C++
3700 lines
97 KiB
C++
/*
|
|
//paragraph [1] Title: [{\Large \bf \begin{center}] [\end{center}}]
|
|
//paragraph [10] Footnote: [{\footnote{] [}}]
|
|
//[TOC] [\tableofcontents]
|
|
|
|
{\Large \bf Anhang D: RTree-Template }
|
|
|
|
[1] Header-File of TMRTree
|
|
|
|
2012, June Jianqiu
|
|
|
|
[TOC]
|
|
|
|
0 Overview
|
|
|
|
This header file implements a disk-resident representation of a R-Tree.
|
|
Setting some parameters the R-Tree-behaviour of Guttman or the R[*]-Tree
|
|
of Kriegel et al. can be selected.
|
|
|
|
The R-Tree is implemented as a template to satisfy the usage with various
|
|
dimensions. The desired dimensions are passed as a parameter to the template.
|
|
|
|
1 Defines and Includes
|
|
|
|
*/
|
|
|
|
#ifndef __TMRTREE_H__
|
|
#define __TMRTREE_H__
|
|
|
|
#include "stdarg.h"
|
|
|
|
#ifdef SECONDO_WIN32
|
|
#define Rectangle SecondoRectangle
|
|
#endif
|
|
|
|
#include <iostream>
|
|
#include <stack>
|
|
#include <limits>
|
|
#include <string.h>
|
|
|
|
|
|
#include "SpatialAlgebra.h"
|
|
#include "RelationAlgebra.h"
|
|
#include "Algebra.h"
|
|
#include "NestedList.h"
|
|
#include "QueryProcessor.h"
|
|
#include "RectangleAlgebra.h"
|
|
#include "StandardTypes.h"
|
|
#include "BTreeAlgebra.h"
|
|
#include "TemporalAlgebra.h"
|
|
#include "AlmostEqual.h"
|
|
#include "RTreeAlgebra.h"
|
|
#include "GeneralType.h"
|
|
|
|
extern NestedList* nl;
|
|
extern QueryProcessor* qp;
|
|
|
|
#define BBox Rectangle
|
|
#define BBoxSet RectangleSet
|
|
|
|
|
|
/*
|
|
4 Class TMTreeNode
|
|
This is a node in the TMR-Tree.
|
|
we can not simply derive this tmrtreenode from rtreenode class because
|
|
of the following two functions: read() and write(). we have to store the tm
|
|
value after count and leaf.
|
|
|
|
*/
|
|
template<unsigned dim, class LeafInfo>
|
|
class TM_RTreeNode
|
|
{
|
|
public:
|
|
|
|
TM_RTreeNode( const bool leaf, const int min, const int max );
|
|
TM_RTreeNode( const TM_RTreeNode<dim, LeafInfo>& n );
|
|
~TM_RTreeNode();
|
|
|
|
static int SizeOfEmptyNode() {
|
|
// return sizeof( bool ) + sizeof( int );
|
|
return sizeof(bool) + sizeof(int) + sizeof(long);// plus tm
|
|
};
|
|
|
|
int Size() const;
|
|
|
|
int EntryCount() const { return count; }
|
|
|
|
int MaxEntries() const { return maxEntries; }
|
|
|
|
int MinEntries() const { return minEntries; }
|
|
|
|
bool IsLeaf() const { return leaf; }
|
|
|
|
|
|
R_TreeEntry<dim>& operator[] ( int index ) const {
|
|
assert( index >= 0 );
|
|
assert(index <= maxEntries );
|
|
assert(index < count);
|
|
return *entries[ index ];
|
|
}
|
|
/*
|
|
Returns entry given by index.
|
|
|
|
*/
|
|
R_TreeLeafEntry<dim,LeafInfo>* GetLeafEntry(const int index) const;
|
|
R_TreeInternalEntry<dim>* GetInternalEntry(const int index) const;
|
|
|
|
BBox<dim> BoundingBox() const;
|
|
BBox<dim> BoundingBox(int entryid) const
|
|
{
|
|
assert(entryid >= 0 && entryid <= maxEntries);
|
|
return entries[entryid]->box;
|
|
}
|
|
|
|
void Clear()
|
|
{
|
|
for( int i = 0; i <= maxEntries; i++ )
|
|
{
|
|
if(entries[i]){
|
|
delete entries[ i ];
|
|
entries[ i ] = 0;
|
|
}
|
|
}
|
|
count = 0;
|
|
modified = true;
|
|
}
|
|
/*
|
|
Clears all entries.
|
|
|
|
*/
|
|
|
|
TM_RTreeNode<dim, LeafInfo>& operator = ( const TM_RTreeNode<dim, LeafInfo>&);
|
|
/*
|
|
Assignment operator between nodes.
|
|
|
|
*/
|
|
|
|
bool Remove( int );
|
|
/*
|
|
Removes the given entry from the node. Returns true if successful
|
|
or false if underflow (The entry is deleted regardless).
|
|
|
|
*/
|
|
|
|
bool Insert( const R_TreeEntry<dim>& e );
|
|
/*
|
|
Adds ~e~ to this node if possible. Returns ~true~ if successful,
|
|
i.e., if there is enough room in the node, or ~false~ if the insertion
|
|
caused an overflow. In the latter case, the entry is inserted,
|
|
but the node should be split by whoever called the insert method.
|
|
|
|
*/
|
|
|
|
void Split(TM_RTreeNode<dim, LeafInfo>& n1,
|
|
TM_RTreeNode<dim, LeafInfo>& n2 );
|
|
/*
|
|
Splits this node in two: ~n1~ and ~n2~, which should be empty nodes.
|
|
|
|
*/
|
|
|
|
void UpdateBox( BBox<dim>& box, SmiRecordId pointer );
|
|
/*
|
|
Update entry corresponding to ~pointer~ to have bounding box ~box~.
|
|
|
|
*/
|
|
|
|
void Read( SmiRecordFile *file, const SmiRecordId pointer );
|
|
void Read( SmiRecord& record );
|
|
/*
|
|
Reads this node from an ~SmiRecordFile~ at position ~id~.
|
|
|
|
*/
|
|
|
|
void Write( SmiRecordFile *file, const SmiRecordId pointer );
|
|
void Write( SmiRecord& record );
|
|
/*
|
|
Writes this node to an ~SmiRecordFile~ at position ~id~
|
|
|
|
*/
|
|
void SetInternal( int minEntries, int maxEntries )
|
|
{
|
|
assert( count == 0 );
|
|
leaf = false;
|
|
this->minEntries = minEntries;
|
|
this->maxEntries = maxEntries;
|
|
modified = true;
|
|
}
|
|
/*
|
|
Converts a leaf node to an internal one. The node must be empty.
|
|
|
|
*/
|
|
|
|
//////////////////////////////////////////////////
|
|
void SetModified(){modified = true;}
|
|
//////////////////////////////////////////////////////
|
|
|
|
long GetTMValue(){return tm;}
|
|
void SetTMValue(long m){tm = m; modified = true;}
|
|
|
|
private:
|
|
bool leaf;
|
|
/*
|
|
Flag that tells whether this is a leaf node
|
|
|
|
*/
|
|
|
|
int minEntries;
|
|
/*
|
|
Min \# of entries per node.
|
|
|
|
*/
|
|
|
|
int maxEntries;
|
|
/*
|
|
Max \# of entries per node.
|
|
|
|
*/
|
|
|
|
int count;
|
|
/*
|
|
Number of entries in this node.
|
|
|
|
*/
|
|
|
|
R_TreeEntry<dim>** entries;
|
|
/*
|
|
Array of entries.
|
|
|
|
*/
|
|
|
|
bool modified;
|
|
/*
|
|
A flag that indicates when a node is modified. This avoids
|
|
writing unnecessarily.
|
|
|
|
*/
|
|
|
|
void LinearPickSeeds( int& seed1, int& seed2 ) const;
|
|
/*
|
|
Implements the linear ~PickSeeds~ algorithm of Guttman.
|
|
Linear algorithm that selects 2 seed entries to use as anchors
|
|
for the node splitting algorithm. The entry numbers are returned
|
|
in ~seed1~ and ~seed2~.
|
|
|
|
*/
|
|
|
|
void QuadraticPickSeeds( int& seed1, int& seed2 ) const;
|
|
/*
|
|
Implementation of the quadratic 'PickSeeds' Algorithm of Guttman.
|
|
Quadratic algorithm that selects 2 seed entries to use as anchors
|
|
for the node splitting algorithm. The entry numbers are returned
|
|
in ~seed1~ and ~seed2~.
|
|
|
|
*/
|
|
|
|
int QuadraticPickNext( BBox<dim>& b1, BBox<dim>& b2 ) const;
|
|
/*
|
|
Returns the entry position that should be assigned next to one of the
|
|
two groups with bounding boxes ~b1~ and ~b2~, respectively.
|
|
(Algorithm ~PickNext~ of Guttman)
|
|
|
|
*/
|
|
|
|
long tm; ///////////transportation mode
|
|
};
|
|
|
|
|
|
/*
|
|
The constructors for TMRtree node
|
|
minentries: 24 maxentries: 62
|
|
|
|
*/
|
|
template<unsigned dim, class LeafInfo>
|
|
TM_RTreeNode<dim, LeafInfo>::TM_RTreeNode( const bool leaf,
|
|
const int min,
|
|
const int max ) :
|
|
leaf( leaf ),
|
|
minEntries( min ),
|
|
maxEntries( max ),
|
|
count( 0 ),
|
|
entries( new R_TreeEntry<dim>*[ max + 1 ] ),
|
|
modified( true ), tm(-1)
|
|
{
|
|
for( int i = 0; i <= maxEntries; i++ ){
|
|
entries[ i ] = 0;
|
|
}
|
|
// cout<<MinEntries()<<" "<<MaxEntries()<<endl;
|
|
}
|
|
|
|
template<unsigned dim, class LeafInfo>
|
|
TM_RTreeNode<dim, LeafInfo>::
|
|
TM_RTreeNode(const TM_RTreeNode<dim, LeafInfo>& node):
|
|
leaf( node.leaf ),
|
|
minEntries( node.minEntries ),
|
|
maxEntries( node.maxEntries ),
|
|
count( node.count ),
|
|
entries( new R_TreeEntry<dim>*[ node.maxEntries + 1 ] ),
|
|
modified( true ), tm(node.tm)
|
|
{
|
|
int i;
|
|
for( i = 0; i < node.EntryCount(); i++ )
|
|
{
|
|
if( leaf )
|
|
entries[ i ] =
|
|
new R_TreeLeafEntry<dim, LeafInfo>( (R_TreeLeafEntry<dim,
|
|
LeafInfo>&)*node.entries[ i ] );
|
|
else
|
|
entries[ i ] =
|
|
new R_TreeInternalEntry<dim>( (
|
|
R_TreeInternalEntry<dim>&)*node.entries[ i ] );
|
|
}
|
|
for( ; i <= node.maxEntries; i++ ){
|
|
entries[ i ] = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
4.2 The destructor
|
|
|
|
*/
|
|
template<unsigned dim, class LeafInfo>
|
|
TM_RTreeNode<dim, LeafInfo>::~TM_RTreeNode()
|
|
{
|
|
for( int i = 0; i <= count; i++ ){
|
|
delete entries[ i ];
|
|
}
|
|
delete []entries;
|
|
}
|
|
|
|
template<unsigned dim, class LeafInfo>
|
|
int TM_RTreeNode<dim, LeafInfo>::Size() const
|
|
{
|
|
int size = SizeOfEmptyNode();
|
|
|
|
if( leaf )
|
|
size += R_TreeLeafEntry<dim, LeafInfo>::Size() * maxEntries;
|
|
else
|
|
size += R_TreeInternalEntry<dim>::Size() * maxEntries;
|
|
|
|
return size;
|
|
}
|
|
|
|
template<unsigned dim, class LeafInfo>
|
|
TM_RTreeNode<dim, LeafInfo>& TM_RTreeNode<dim, LeafInfo>::operator=
|
|
(const TM_RTreeNode<dim, LeafInfo>& node )
|
|
{
|
|
|
|
// delete old entries
|
|
for(int i=0;i<=count; i++){
|
|
delete entries[i];
|
|
entries[i] = 0;
|
|
}
|
|
|
|
leaf = node.leaf;
|
|
count = node.count;
|
|
modified = true;
|
|
|
|
// if there are a change in size, resize the entries array
|
|
if(maxEntries!=node.maxEntries){
|
|
delete[] entries;
|
|
entries = new R_TreeEntry<dim>*[node.maxEntries +1];
|
|
for(int i=node.count; i<=node.maxEntries; i++){
|
|
entries[i] = 0;
|
|
}
|
|
}
|
|
|
|
for( int i = 0; i < node.count; i++ ) {
|
|
if( leaf ){
|
|
entries[ i ] = new R_TreeLeafEntry<dim, LeafInfo>
|
|
( (R_TreeLeafEntry<dim, LeafInfo>&)*node.entries[ i ] );
|
|
} else {
|
|
entries[ i ] = new R_TreeInternalEntry<dim>
|
|
( (R_TreeInternalEntry<dim>&)*node.entries[ i ] );
|
|
}
|
|
}
|
|
|
|
tm = node.tm;
|
|
return *this;
|
|
}
|
|
|
|
template<unsigned dim, class LeafInfo>
|
|
bool TM_RTreeNode<dim, LeafInfo>::Remove( int index )
|
|
{
|
|
assert( index >= 0 && index < count );
|
|
|
|
delete entries[ index ];
|
|
entries[ index ] = entries[ count - 1 ];
|
|
entries[ count - 1 ] = 0;
|
|
count -= 1;
|
|
|
|
modified = true;
|
|
return (count >= minEntries);
|
|
}
|
|
|
|
/*
|
|
4.3 Method Insert
|
|
|
|
*/
|
|
template<unsigned dim, class LeafInfo>
|
|
bool TM_RTreeNode<dim, LeafInfo>::Insert( const R_TreeEntry<dim>& ent )
|
|
{
|
|
assert( count <= maxEntries );
|
|
|
|
if( leaf )
|
|
entries[ count++ ] = new R_TreeLeafEntry<dim, LeafInfo>
|
|
( (R_TreeLeafEntry<dim, LeafInfo>&)ent );
|
|
else
|
|
entries[ count++ ] = new R_TreeInternalEntry<dim>
|
|
( (R_TreeInternalEntry<dim>&)ent );
|
|
modified = true;
|
|
|
|
return (count <= maxEntries);
|
|
}
|
|
|
|
/*
|
|
4.3 Method LinearPickSeeds
|
|
|
|
*/
|
|
template<unsigned dim, class LeafInfo>
|
|
void TM_RTreeNode<dim, LeafInfo>::LinearPickSeeds(int& seed1,int& seed2 ) const
|
|
{
|
|
assert( EntryCount() == MaxEntries() + 1 );
|
|
// This should be called only if the node has an overflow
|
|
|
|
double maxMinVal[ dim ];
|
|
double minMaxVal[ dim ];
|
|
double minVal[ dim ];
|
|
double maxVal[ dim ];
|
|
double sep[ dim ];
|
|
double maxSep = -std::numeric_limits<double>::max();
|
|
int maxMinNode[ dim ];
|
|
int minMaxNode[ dim ];
|
|
int bestD = -1;
|
|
|
|
for( unsigned i = 0; i < dim; i++ )
|
|
{
|
|
maxMinVal[i] = -std::numeric_limits<double>::max();
|
|
minMaxVal[i] = std::numeric_limits<double>::max();
|
|
minVal[i] = std::numeric_limits<double>::max();
|
|
maxVal[i] = -std::numeric_limits<double>::max();
|
|
maxMinNode[i] = -1;
|
|
minMaxNode[i] = -1;
|
|
}
|
|
|
|
for( int i = 0; i < EntryCount(); i++ )
|
|
{
|
|
for( unsigned d = 0; d < dim; d++ )
|
|
{
|
|
if( entries[ i ]->box.MinD( d ) > maxMinVal[ d ] )
|
|
{
|
|
maxMinVal[ d ] = entries[ i ]->box.MinD( d );
|
|
maxMinNode[ d ] = i;
|
|
}
|
|
|
|
if( entries[ i ]->box.MinD( d ) < minVal[ d ] )
|
|
minVal[ d ] = entries[ i ]->box.MinD( d );
|
|
|
|
if( entries[ i ]->box.MaxD( d ) < minMaxVal[ d ] )
|
|
{
|
|
minMaxVal[ d ] = entries[ i ]->box.MaxD( d );
|
|
minMaxNode[ d ] = i;
|
|
}
|
|
|
|
if( entries[ i ]->box.MaxD( d ) > maxVal[ d ] )
|
|
maxVal[ d ] = entries[ i ]->box.MaxD( d );
|
|
}
|
|
}
|
|
|
|
for( unsigned d = 0; d < dim; d++ )
|
|
{
|
|
assert( maxMinNode[ d ] != -1 && minMaxNode[ d ] != -1 );
|
|
assert( maxVal[ d ] > minVal[ d ] );
|
|
sep[ d ] = double( maxMinVal[ d ] - minMaxVal[ d ] )
|
|
/ (maxVal[ d ] - minVal[ d ]);
|
|
if( sep[ d ] > maxSep )
|
|
{
|
|
bestD = d;
|
|
maxSep = sep[ d ];
|
|
}
|
|
}
|
|
|
|
assert( bestD != -1 );
|
|
seed1 = maxMinNode[ bestD ];
|
|
seed2 = minMaxNode[ bestD ];
|
|
|
|
if( seed1 == seed2 )
|
|
{
|
|
if( seed2 == 0 )
|
|
seed2++;
|
|
else
|
|
seed2--;
|
|
}
|
|
}
|
|
|
|
/*
|
|
4.4 Method QuadraticPickSeeds
|
|
|
|
*/
|
|
template<unsigned dim, class LeafInfo>
|
|
void TM_RTreeNode<dim, LeafInfo>::QuadraticPickSeeds( int& seed1,
|
|
int& seed2 ) const
|
|
{
|
|
assert( EntryCount() == MaxEntries() + 1 );
|
|
// This should be called only if the node has an overflow
|
|
|
|
double bestWaste = -std::numeric_limits<double>::max();
|
|
double *area = new double[ MaxEntries() + 1 ]; // Compute areas just once
|
|
int i;
|
|
|
|
for( i = 0; i < EntryCount(); i++ )
|
|
{
|
|
int j;
|
|
|
|
area[ i ] = entries[ i ]->box.Area();
|
|
|
|
for( j = 0; j < i; ++j )
|
|
{
|
|
double totalArea = entries[ i ]->box.Union( entries[ j ]->box ).Area();
|
|
double waste = totalArea - area[ i ] - area[ j ];
|
|
|
|
if( waste > bestWaste )
|
|
{
|
|
seed1 = i;
|
|
seed2 = j;
|
|
bestWaste = waste;
|
|
}
|
|
}
|
|
}
|
|
|
|
delete [] area;
|
|
}
|
|
|
|
/*
|
|
4.5 Method QuadraticPickNext
|
|
|
|
*/
|
|
template<unsigned dim, class LeafInfo>
|
|
int TM_RTreeNode<dim, LeafInfo>::QuadraticPickNext( BBox<dim>& b1,
|
|
BBox<dim>& b2 ) const
|
|
{
|
|
double area1 = b1.Area();
|
|
double area2 = b2.Area();
|
|
double bestDiff = -1;
|
|
int besti = -1;
|
|
|
|
for( int i = 0; i < count; i++ )
|
|
{
|
|
double d1 = b1.Union( entries[ i ]->box ).Area() - area1;
|
|
double d2 = b2.Union( entries[ i ]->box ).Area() - area2;
|
|
double diff = fabs( d1 - d2 );
|
|
|
|
assert( d1 >= 0 && d2 >= 0 );
|
|
|
|
if( diff > bestDiff )
|
|
{
|
|
bestDiff = diff;
|
|
besti = i;
|
|
}
|
|
}
|
|
|
|
assert( besti >= 0 );
|
|
|
|
return besti;
|
|
}
|
|
|
|
|
|
/*
|
|
4.6 Method Split
|
|
|
|
*/
|
|
template<unsigned dim, class LeafInfo>
|
|
void TM_RTreeNode<dim, LeafInfo>::Split( TM_RTreeNode<dim,
|
|
LeafInfo>& n1,
|
|
TM_RTreeNode<dim,
|
|
LeafInfo>& n2 )
|
|
// Splits this node in two: n1 and n2, which should be empty nodes.
|
|
{
|
|
assert( EntryCount() == MaxEntries() + 1 );
|
|
// Make sure this node is ready to be split
|
|
|
|
assert( n1.EntryCount() == 0 && n2.EntryCount() == 0 );
|
|
assert( n1.MinEntries() == MinEntries() && n1.MaxEntries() == MaxEntries() );
|
|
assert( n2.MinEntries() == MinEntries() && n2.MaxEntries() == MaxEntries() );
|
|
// Make sure n1 and n2 are ok
|
|
|
|
if( do_axis_split )
|
|
{ // Do R[*]-Tree style split
|
|
int *sortedEntry[ 2*dim ] = { NULL }; // Arrays of sorted entries
|
|
struct StatStruct
|
|
{
|
|
double margin;
|
|
double overlap;
|
|
double area;
|
|
} *stat = new StatStruct[ dim*dim*(MaxEntries() + 2 - 2*MinEntries()) ],
|
|
*pstat = stat; // Array of distribution statistics
|
|
double minMarginSum = std::numeric_limits<double>::max();
|
|
int minMarginAxis = -1;
|
|
|
|
for( unsigned d = 0; d < dim; d++ )
|
|
{ // Compute sorted lists.
|
|
// Sort entries numbers by minimum value of axis 'd'.
|
|
int* psort = sortedEntry[ 2*d ] = new int[ MaxEntries() + 1 ];
|
|
SortedArray sort( MaxEntries() + 1 );
|
|
int i;
|
|
|
|
for( i = 0; i <= MaxEntries(); i++ )
|
|
sort.push( i, entries[ i ]->box.MinD( d ) );
|
|
|
|
for( i = 0; i <= MaxEntries(); i++ )
|
|
*psort++ = sort.pop();
|
|
|
|
assert( sort.empty() );
|
|
|
|
// Sort entries numbers by maximum value of axis 'd'
|
|
psort = sortedEntry[ 2*d + 1 ] = new int[ MaxEntries() + 1 ];
|
|
for( i = 0; i <= MaxEntries(); i++ )
|
|
sort.push( i, entries[ i ]->box.MaxD( d ) );
|
|
|
|
for( i = 0; i <= MaxEntries(); i++ )
|
|
*psort++ = sort.pop();
|
|
|
|
assert( sort.empty() );
|
|
}
|
|
|
|
// Compute statistics for the various distributions
|
|
for( unsigned d = 0; d < dim; d++ )
|
|
{ // Sum margins over all distributions correspondig to one axis
|
|
double marginSum = 0.0;
|
|
|
|
for( unsigned minMax = 0; minMax < 2; minMax++ )
|
|
{ // psort points to one of the sorted arrays of entries
|
|
int* psort = sortedEntry[ 2*d + minMax ];
|
|
// Start by computing the cummulative bounding boxes of the
|
|
// 'MaxEntries()-MinEntries()+1' entries of each end of the scale
|
|
BBox<dim> *b1 = new BBox<dim>[ MaxEntries() + 1 ];
|
|
BBox<dim> *b2 = new BBox<dim>[ MaxEntries() + 1 ];
|
|
int i, splitPoint;
|
|
|
|
b1[ 0 ] = entries[ psort[ 0 ] ]->box;
|
|
b2[ 0 ] = entries[ psort[ MaxEntries() ] ]->box;
|
|
|
|
for( i = 1; i <= MaxEntries(); i++ )
|
|
{
|
|
b1[i] = b1[i-1].Union(entries[psort[i]]->box );
|
|
b2[i] = b2[i-1].Union(entries[psort[MaxEntries()-i]]->box);
|
|
}
|
|
|
|
// Now compute the statistics for the
|
|
// MaxEntries() - 2*MinEntries() + 2 distributions
|
|
for( splitPoint = MinEntries() - 1;
|
|
splitPoint <= MaxEntries() - MinEntries();
|
|
splitPoint++ )
|
|
{
|
|
BBox<dim>& box1 = b1[ splitPoint ];
|
|
BBox<dim>& box2 = b2[ MaxEntries() - splitPoint - 1 ];
|
|
|
|
pstat->margin = box1.Perimeter() + box2.Perimeter();
|
|
pstat->overlap = box1.Intersection( box2 ).Area();
|
|
pstat->area = box1.Area() + box2.Area();
|
|
marginSum += pstat->margin;
|
|
pstat += 1;
|
|
|
|
assert( pstat - stat <=
|
|
(int)(dim*dim*(MaxEntries() + 2 - 2*MinEntries())) );
|
|
}
|
|
|
|
delete [] b2;
|
|
delete [] b1;
|
|
}
|
|
|
|
if( marginSum < minMarginSum )
|
|
{
|
|
minMarginSum = marginSum;
|
|
minMarginAxis = d;
|
|
}
|
|
}
|
|
|
|
assert( pstat - stat == 2*(int)dim*(MaxEntries() + 2 - 2*MinEntries()));
|
|
|
|
// At this point we have in minMarginAxis the axis on which we will
|
|
// split. Choose the distribution with minimum overlap,
|
|
// breaking ties by choosing the distribution with minimum Area
|
|
{
|
|
double minOverlap = std::numeric_limits<double>::max();
|
|
double minArea = std::numeric_limits<double>::max();
|
|
int minSplitPoint = -1;
|
|
int *sort = 0;
|
|
int d = minMarginAxis;
|
|
|
|
pstat = &stat[ 2*d*(MaxEntries() + 2 - 2*MinEntries()) ];
|
|
for( unsigned minMax = 0; minMax < 2; minMax++ )
|
|
{
|
|
int *psort = sortedEntry[ 2*d + minMax ];
|
|
int splitPoint;
|
|
|
|
for( splitPoint = MinEntries() - 1;
|
|
splitPoint <= MaxEntries()-MinEntries();
|
|
splitPoint++, pstat++ )
|
|
if( pstat->overlap < minOverlap ||
|
|
(pstat->overlap == minOverlap && pstat->area < minArea) )
|
|
{
|
|
minOverlap = pstat->overlap;
|
|
minArea = pstat->area;
|
|
minSplitPoint = splitPoint;
|
|
sort = psort;
|
|
}
|
|
}
|
|
|
|
assert( sort != 0 );
|
|
assert( pstat - stat == (d + 1 )*2*(MaxEntries() + 2 - 2*MinEntries()) );
|
|
|
|
// Picked distribution; now put the corresponding entries in the
|
|
// two split blocks
|
|
for( int i = 0; i <= minSplitPoint; i++ )
|
|
n1.Insert( *entries[ sort[ i ] ] );
|
|
|
|
for( int i = minSplitPoint + 1; i <= MaxEntries(); i++ )
|
|
n2.Insert( *entries[ sort[ i ] ] );
|
|
|
|
assert( AlmostEqual(
|
|
n1.BoundingBox().Intersection( n2.BoundingBox() ).Area(),
|
|
minOverlap ));
|
|
|
|
// Deallocate the sortedEntry arrays
|
|
for( unsigned i = 0; i < 2*dim; i++)
|
|
delete [] sortedEntry[ i ];
|
|
|
|
delete [] stat;
|
|
}
|
|
}
|
|
else
|
|
{ // Do regular R-tree split
|
|
int seed1, seed2; // Pick seeds
|
|
|
|
if( do_quadratic_split )
|
|
QuadraticPickSeeds( seed1, seed2 );
|
|
else
|
|
{
|
|
assert( do_linear_split );
|
|
|
|
LinearPickSeeds( seed1, seed2 );
|
|
}
|
|
|
|
// Put the two seeds in n1 and n2 and mark them
|
|
BBox<dim> box1 = entries[ seed1 ]->box;
|
|
BBox<dim> box2 = entries[ seed2 ]->box;
|
|
n1.Insert( *entries[ seed1 ] );
|
|
n2.Insert( *entries[ seed2 ] );
|
|
|
|
// Make sure that we delete entries from end of the array first
|
|
if( seed1 > seed2 )
|
|
{
|
|
Remove( seed1 );
|
|
Remove( seed2 );
|
|
}
|
|
else
|
|
{
|
|
Remove( seed2 );
|
|
Remove( seed1 );
|
|
}
|
|
|
|
{ // Successively choose a not yet assigned entry and put it into n1 or n2
|
|
int i = 0;
|
|
int notAssigned = EntryCount();
|
|
|
|
assert( notAssigned == MaxEntries() - 1 );
|
|
while( notAssigned > 0 )
|
|
{
|
|
if( n1.EntryCount() + notAssigned == n1.MinEntries() )
|
|
{ // Insert all remaining entries in n1
|
|
for( i = 0; i < EntryCount() ; i++, notAssigned-- ){
|
|
n1.Insert( *entries[ i ] );
|
|
delete entries[i];
|
|
entries[i] = 0;
|
|
}
|
|
count = 0;
|
|
assert( notAssigned == 0 );
|
|
}
|
|
else if( n2.EntryCount() + notAssigned == n2.MinEntries() )
|
|
{ // Insert all remaining entries in n2
|
|
for( i = 0; i < EntryCount(); ++i, notAssigned-- ){
|
|
n2.Insert( *entries[ i ] );
|
|
delete entries[i];
|
|
entries[i] = 0;
|
|
}
|
|
count = 0;
|
|
assert( notAssigned == 0 );
|
|
}
|
|
else
|
|
{
|
|
BBox<dim> union1, union2;
|
|
if( do_quadratic_split )
|
|
i = QuadraticPickNext( box1, box2 );
|
|
else
|
|
{
|
|
assert( do_linear_split );
|
|
i = 0;
|
|
}
|
|
|
|
union1 = box1.Union( entries[ i ]->box );
|
|
union2 = box2.Union( entries[ i ]->box );
|
|
|
|
if( union1.Area() - box1.Area() < union2.Area() - box2.Area() )
|
|
{
|
|
n1.Insert( *entries[ i ] );
|
|
box1 = union1;
|
|
}
|
|
else
|
|
{
|
|
n2.Insert( *entries[ i ] );
|
|
box2 = union2;
|
|
}
|
|
|
|
Remove( i );
|
|
notAssigned--;
|
|
}
|
|
}
|
|
|
|
assert( notAssigned == 0 );
|
|
assert( EntryCount() == 0 );
|
|
}
|
|
}
|
|
|
|
assert( n1.EntryCount() + n2.EntryCount() == MaxEntries() + 1 );
|
|
assert( n1.EntryCount() >= MinEntries() && n1.EntryCount() <= MaxEntries() );
|
|
assert( n2.EntryCount() >= MinEntries() && n2.EntryCount() <= MaxEntries() );
|
|
modified = true;
|
|
}
|
|
|
|
/*
|
|
4.7 Method BoundingBox
|
|
|
|
*/
|
|
template<unsigned dim, class LeafInfo>
|
|
BBox<dim> TM_RTreeNode<dim, LeafInfo>::BoundingBox() const
|
|
{
|
|
if( count == 0 )
|
|
return BBox<dim>( false );
|
|
else
|
|
{
|
|
BBox<dim> result = entries[ 0 ]->box;
|
|
int i;
|
|
|
|
for( i = 1; i < count; i++ )
|
|
result = result.Union( entries[ i ]->box );
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/*
|
|
4.8 Method UpdateBox
|
|
|
|
*/
|
|
template<unsigned dim, class LeafInfo>
|
|
void TM_RTreeNode<dim, LeafInfo>::UpdateBox( BBox<dim>& b, SmiRecordId pointer)
|
|
{
|
|
assert( !leaf );
|
|
modified = true;
|
|
|
|
for( int i = 0; i < count; i++ )
|
|
if( ((R_TreeInternalEntry<dim>*)entries[ i ])->pointer == pointer )
|
|
{
|
|
entries[ i ]->box = b;
|
|
|
|
return;
|
|
}
|
|
|
|
// Should never reach this point
|
|
assert( 0 );
|
|
}
|
|
|
|
template<unsigned dim, class LeafInfo>
|
|
void TM_RTreeNode<dim, LeafInfo>::Read( SmiRecordFile *file,
|
|
const SmiRecordId pointer )
|
|
{
|
|
SmiRecord record;
|
|
int RecordSelected = file->SelectRecord( pointer, record, SmiFile::ReadOnly );
|
|
assert( RecordSelected );
|
|
Read( record );
|
|
}
|
|
|
|
template<unsigned dim, class LeafInfo>
|
|
void TM_RTreeNode<dim, LeafInfo>::Read( SmiRecord& record )
|
|
{
|
|
int offset = 0;
|
|
char buffer[Size() + 1];
|
|
memset( buffer, 0, Size() + 1 );
|
|
|
|
SmiSize readed = record.Read( buffer, Size(), offset );
|
|
|
|
// Reads leaf, count
|
|
memcpy( &leaf, buffer + offset, sizeof( leaf ) );
|
|
offset += sizeof( leaf );
|
|
memcpy( &count, buffer + offset, sizeof( count ) );
|
|
offset += sizeof( count );
|
|
|
|
///////////////read tm ///////////////////
|
|
memcpy( &tm, buffer + offset, sizeof( tm ) );
|
|
offset += sizeof( tm );
|
|
|
|
|
|
assert( count <= maxEntries );
|
|
|
|
// Now read the entry array.
|
|
for( int i = 0; i < count; i++ )
|
|
{
|
|
if( leaf )
|
|
entries[ i ] = new R_TreeLeafEntry<dim, LeafInfo>();
|
|
else
|
|
entries[ i ] = new R_TreeInternalEntry<dim>();
|
|
|
|
entries[ i ]->Read( buffer, offset );
|
|
}
|
|
|
|
assert(offset<=(int)readed); // otherwise some entries will be uninitialized
|
|
modified = false;
|
|
|
|
|
|
}
|
|
|
|
template<unsigned dim, class LeafInfo>
|
|
void TM_RTreeNode<dim, LeafInfo>::Write( SmiRecordFile *file,
|
|
const SmiRecordId pointer )
|
|
{
|
|
|
|
if( modified )
|
|
{
|
|
|
|
SmiRecord record;
|
|
int RecordSelected = file->SelectRecord( pointer, record, SmiFile::Update );
|
|
assert( RecordSelected );
|
|
Write( record );
|
|
}
|
|
}
|
|
|
|
template<unsigned dim, class LeafInfo>
|
|
void TM_RTreeNode<dim, LeafInfo>::Write( SmiRecord& record )
|
|
{
|
|
if( modified )
|
|
{
|
|
int offset = 0;
|
|
char buffer[Size() + 1];
|
|
memset( buffer, 0, Size() + 1 );
|
|
|
|
// Writes leaf, count
|
|
memcpy( buffer + offset, &leaf, sizeof( leaf ) );
|
|
offset += sizeof( leaf );
|
|
memcpy( buffer + offset, &count, sizeof( count ) );
|
|
offset += sizeof( count );
|
|
|
|
//////////////////////write tm ////////////////////////
|
|
memcpy( buffer + offset, &tm, sizeof( tm ) );
|
|
offset += sizeof( tm );
|
|
|
|
//cout << "TM_RTreeNode<dim, LeafInfo>::Write(): count/maxEntries = "
|
|
// << count << "/" << maxEntries << "." << endl;
|
|
assert( count <= maxEntries );
|
|
|
|
// Now write the entry array.
|
|
for( int i = 0; i < count; i++ )
|
|
entries[i]->Write( buffer, offset );
|
|
|
|
int RecordWritten = record.Write( buffer, Size(), 0 );
|
|
assert( RecordWritten );
|
|
modified = false;
|
|
}
|
|
}
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
R_TreeLeafEntry<dim,LeafInfo>*
|
|
TM_RTreeNode<dim,LeafInfo>::GetLeafEntry(const int index) const
|
|
{
|
|
assert(index >= 0 && index <= maxEntries);
|
|
return (R_TreeLeafEntry<dim,LeafInfo>*)entries[index];
|
|
}
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
R_TreeInternalEntry<dim>*
|
|
TM_RTreeNode<dim,LeafInfo>::GetInternalEntry(const int index) const
|
|
{
|
|
assert(index >= 0 && index < count);
|
|
return (R_TreeInternalEntry<dim>*)entries[index];
|
|
}
|
|
|
|
|
|
/*
|
|
Class tmbulkload
|
|
This class implements the TMR-Tree.
|
|
|
|
*/
|
|
|
|
template<unsigned dim, class LeafInfo>
|
|
class TM_BulkLoadInfo
|
|
{
|
|
public:
|
|
TM_RTreeNode<dim, LeafInfo> *node[MAX_PATH_SIZE];
|
|
bool skipLeaf;
|
|
int currentLevel;
|
|
int currentHeight;
|
|
int nodeCount;
|
|
int entryCount;
|
|
long levelEntryCounter[MAX_PATH_SIZE];
|
|
double levelDistanceSum[MAX_PATH_SIZE];
|
|
BBox<dim> levelLastBox[MAX_PATH_SIZE];
|
|
|
|
TM_BulkLoadInfo(const bool &leafSkipping = false) :
|
|
skipLeaf( leafSkipping ),
|
|
currentLevel( 0 ),
|
|
currentHeight( 0 ),
|
|
nodeCount( 0 ),
|
|
entryCount( 0 )
|
|
{
|
|
for(int i=0; i < MAX_PATH_SIZE; i++)
|
|
{
|
|
node[i] = NULL;
|
|
levelEntryCounter[i] = 0;
|
|
levelDistanceSum[i] = 0.0;
|
|
levelLastBox[i] = BBox<dim>(false);
|
|
}
|
|
};
|
|
|
|
virtual ~TM_BulkLoadInfo()
|
|
{
|
|
for(int i=0; i < MAX_PATH_SIZE; i++)
|
|
if(node[i] != NULL)
|
|
delete node[i];
|
|
};
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
///////////////////// TMRtree implementation /////////////////////////////
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
class TM_RTree
|
|
{
|
|
public:
|
|
/*
|
|
The first constructor. Creates an empty R-tree.
|
|
|
|
*/
|
|
TM_RTree( const int pageSize );
|
|
|
|
/*
|
|
Opens an existing R-tree.
|
|
|
|
*/
|
|
TM_RTree( SmiRecordFile *file );
|
|
|
|
|
|
TM_RTree( const SmiFileId fileId );
|
|
TM_RTree( SmiRecordFile *file,
|
|
const SmiRecordId headerRecordId );
|
|
TM_RTree( const SmiFileId fileId, bool update );
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
TM_RTree( const SmiFileId fileid,const int);
|
|
void OpenFile(const SmiFileId fileid){file->Open(fileid);}
|
|
void CloseFile(){file->Close();}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
void Clone(TM_RTree<dim,LeafInfo>*);
|
|
SmiRecordId DFVisit_Rtree(TM_RTree<dim,LeafInfo>*,
|
|
TM_RTreeNode<dim,LeafInfo>*);
|
|
|
|
|
|
/*
|
|
The destructor.
|
|
|
|
*/
|
|
~TM_RTree();
|
|
|
|
/*
|
|
Open and Save are used by NetworkAlgebra to save and open the rtree of network.
|
|
|
|
*/
|
|
bool Open( SmiRecord& valueRecord,
|
|
size_t& offset,
|
|
std::string typeInfo,
|
|
Word &value);
|
|
|
|
bool Save(SmiRecord& valueRecord,
|
|
size_t& offset);
|
|
inline static const std::string BasicType(){///!!! "tm-rtree" does not work
|
|
return "tmrtree";
|
|
}
|
|
|
|
static const bool checkType(ListExpr type){
|
|
return nl->IsEqual(type, "tmrtree" );
|
|
}
|
|
|
|
/*
|
|
Deletes the file of the R-Tree.
|
|
|
|
*/
|
|
inline void DeleteFile()
|
|
{
|
|
if(nodePtr){
|
|
delete nodePtr;
|
|
nodePtr=0;
|
|
}
|
|
file->Close();
|
|
file->Drop();
|
|
}
|
|
|
|
/*
|
|
Returns the ~SmiFileId~ of the R-Tree database file.
|
|
|
|
*/
|
|
inline SmiFileId FileId()
|
|
{
|
|
return file->GetFileId();
|
|
}
|
|
|
|
inline void GetNode(SmiRecordId id, TM_RTreeNode<dim, LeafInfo>& result) {
|
|
result.Read(file,id);
|
|
}
|
|
|
|
|
|
/*
|
|
Returns the ~SmiRecordId~ of the root node.
|
|
|
|
*/
|
|
inline SmiRecordId RootRecordId() const
|
|
{
|
|
return header.rootRecordId;
|
|
}
|
|
|
|
/*
|
|
Returns the ~SmiRecordId~ of the header node.
|
|
|
|
*/
|
|
inline SmiRecordId HeaderRecordId() const
|
|
{
|
|
return header.headerRecordId;
|
|
}
|
|
|
|
inline int MinEntries( int level ) const
|
|
{
|
|
return level == Height() ? header.minLeafEntries
|
|
: header.minInternalEntries;
|
|
}
|
|
|
|
inline int MinLeafEntries() const{
|
|
return header.minLeafEntries;
|
|
}
|
|
inline int MinInternalEntries() const{
|
|
return header.minInternalEntries;
|
|
}
|
|
|
|
/*
|
|
Returns the minimum number of entries per node.
|
|
|
|
*/
|
|
|
|
inline int MaxEntries( int level ) const
|
|
{
|
|
return level == Height() ? header.maxLeafEntries
|
|
: header.maxInternalEntries;
|
|
}
|
|
inline int MaxLeafEntries() const{
|
|
return header.maxLeafEntries;
|
|
}
|
|
inline int MaxInternalEntries() const{
|
|
return header.maxInternalEntries;
|
|
}
|
|
/*
|
|
Returns the maximum number of entries per node.
|
|
|
|
*/
|
|
|
|
inline int EntryCount() const
|
|
{
|
|
return header.entryCount;
|
|
}
|
|
/*
|
|
Return the total number of (leaf) entries in this tree.
|
|
|
|
*/
|
|
|
|
inline int NodeCount() const
|
|
{
|
|
return header.nodeCount;
|
|
}
|
|
/*
|
|
Returns the total number of nodes in this tree.
|
|
|
|
*/
|
|
|
|
inline int Height() const
|
|
{
|
|
return header.height;
|
|
}
|
|
/*
|
|
Returns the height of this tree.
|
|
|
|
*/
|
|
|
|
BBox<dim> BoundingBox();
|
|
/*
|
|
Returns the bounding box of this rtree.
|
|
|
|
*/
|
|
|
|
void Insert( const R_TreeLeafEntry<dim, LeafInfo>& );
|
|
/*
|
|
Inserts the given entry somewhere in the tree.
|
|
|
|
*/
|
|
|
|
bool Remove( const R_TreeLeafEntry<dim, LeafInfo>& );
|
|
/*
|
|
Deletes the given entry from the tree. Returns ~true~ if entry found
|
|
and successfully deleted, and ~false~ otherwise.
|
|
|
|
*/
|
|
|
|
bool First( const BBox<dim>& bx,
|
|
R_TreeLeafEntry<dim, LeafInfo>& result,
|
|
int replevel = -1 );
|
|
bool First( const BBoxSet<dim>& searchBoxSet,
|
|
R_TreeLeafEntry<dim, LeafInfo>& result );
|
|
bool Next( R_TreeLeafEntry<dim, LeafInfo>& result );
|
|
/*
|
|
Sets ~result~ to the (leaf) entry corresponding to the first/next
|
|
object whose bounding box overlaps ~bx~.
|
|
Returns ~true~ if a suitable entry was found and ~false~ if not.
|
|
Setting ~replevel~ to a value != -1 forces the search to return
|
|
entries at that level of the tree regardless of whether they
|
|
are at leaf nodes or not.
|
|
|
|
*/
|
|
|
|
TM_RTreeNode<dim, LeafInfo>& Root();
|
|
/*
|
|
Loads ~nodePtr~ with the root node and returns it.
|
|
|
|
*/
|
|
|
|
|
|
bool checkRecordId(SmiRecordId nodeId);
|
|
|
|
bool InitializeBulkLoad(const bool &leafSkipping = BULKLOAD_LEAF_SKIPPING);
|
|
|
|
/*
|
|
Verifies, that the R-tree is empty. Use this before calling
|
|
~InsertBulkLoad()~.
|
|
Data structures used during bulk loading will be initialized.
|
|
|
|
*/
|
|
|
|
|
|
/*
|
|
build the r-tree in a way for genmo units
|
|
|
|
*/
|
|
void TM_BulkLoad(const R_TreeEntry<dim>& entry, int , int);
|
|
void BulkLoad(const R_TreeEntry<dim>& entry);
|
|
/*
|
|
Insert an entry in the bulk loading mode. The R-tree will be
|
|
constructed bottom up.
|
|
|
|
*/
|
|
|
|
bool FinalizeBulkLoad();
|
|
/*
|
|
Call this after inserting the last entry in the bulk loading mode.
|
|
The root will be changed to point to the constructed R-tree.
|
|
Data structures used during bulk loading will be deleted.
|
|
|
|
*/
|
|
|
|
bool IntrospectFirst(IntrospectResult<dim>& result);
|
|
bool IntrospectNext(IntrospectResult<dim>& result);
|
|
bool IntrospectNextE(unsigned long& nodeid, BBox<dim>& box,unsigned long&
|
|
tupleid);
|
|
SmiRecordId SmiNodeId(int currlevel)
|
|
{
|
|
assert(currlevel >= 0 && currlevel <= Height());
|
|
return path[currlevel];
|
|
}
|
|
/*
|
|
The last two methods are used to produce a sequence of node decriptions, that
|
|
can be used to inspect the R-tree structure.
|
|
|
|
*/
|
|
|
|
void FirstDistanceScan( const BBox<dim>& box );
|
|
/*
|
|
FirstDistanceScan initializes the priority queue.
|
|
|
|
Implemented by NearestNeighborAlgebra.
|
|
|
|
*/
|
|
|
|
void LastDistanceScan( );
|
|
/*
|
|
LastDistanceScan deletes the priority queue of the distancescan
|
|
|
|
Implemented by NearestNeighborAlgebra.
|
|
|
|
*/
|
|
|
|
bool NextDistanceScan( const BBox<dim>& box, LeafInfo& result );
|
|
/*
|
|
NextDistanceScan returns true and fills the result with the
|
|
ID of the next tuple if there is a next tuple else it returns false
|
|
|
|
*/
|
|
|
|
|
|
TM_RTreeNode<dim, LeafInfo> *GetMyNode(SmiRecordId& address,
|
|
const bool leaf,
|
|
const int min, const int max )
|
|
{
|
|
return GetNode(address,leaf,min,max);
|
|
}
|
|
|
|
bool getFileStats( SmiStatResultType &result );
|
|
|
|
bool InitializeBLI(const bool& leafSkipping=BULKLOAD_LEAF_SKIPPING);
|
|
|
|
bool CalculateTM(Relation* rel, int);
|
|
long CalculateNodeTM(SmiRecordId nodeid, Relation* rel, int attr_pos);
|
|
///////////
|
|
void WriteNode(TM_RTreeNode<dim, LeafInfo>* node, SmiRecordId nodeid){
|
|
node->Write(file, nodeid);
|
|
}
|
|
private:
|
|
bool fileOwner;
|
|
SmiRecordFile *file;
|
|
/*
|
|
The record file of the R-Tree.
|
|
|
|
*/
|
|
struct Header
|
|
{
|
|
SmiRecordId headerRecordId; // Header node address.
|
|
SmiRecordId rootRecordId; // Root node address (Path[ 0 ]).
|
|
int minLeafEntries; // min # of entries per leaf node.
|
|
int maxLeafEntries; // max # of entries per leaf node.
|
|
int minInternalEntries; // min # of entries per internal node.
|
|
int maxInternalEntries; // max # of entries per internal node.
|
|
int nodeCount; // number of nodes in this tree.
|
|
int entryCount; // number of entries in this tree.
|
|
int height; // height of the tree.
|
|
|
|
Header() :
|
|
headerRecordId( 0 ), rootRecordId( 0 ),
|
|
minLeafEntries( 0 ), maxLeafEntries( 0 ),
|
|
minInternalEntries( 0 ), maxInternalEntries( 0 ),
|
|
nodeCount( 0 ), entryCount( 0 ), height( 0 )
|
|
{}
|
|
Header( SmiRecordId _headerRecordId, SmiRecordId _rootRecordId = 0,
|
|
int _minEntries = 0, int _maxEntries = 0,
|
|
int _minInternalEntries = 0, int _maxInternalEntries = 0,
|
|
int _nodeCount = 0, int _entryCount = 0,
|
|
int _nodeSize = 0, int _height = 0) :
|
|
headerRecordId( _headerRecordId ),
|
|
rootRecordId( _rootRecordId ),
|
|
minLeafEntries( _minEntries ),
|
|
maxLeafEntries( _maxEntries ),
|
|
minInternalEntries( _minInternalEntries ),
|
|
maxInternalEntries( _maxInternalEntries ),
|
|
nodeCount( _nodeCount ),
|
|
entryCount( _entryCount ),
|
|
height( _height )
|
|
{}
|
|
} header;
|
|
|
|
/*
|
|
The header of the R-Tree which will be written (read) to (from) the file.
|
|
|
|
*/
|
|
|
|
SmiRecordId path[ MAX_PATH_SIZE ];
|
|
/*
|
|
Addresses of all nodes in the current path.
|
|
|
|
*/
|
|
|
|
int pathEntry[ MAX_PATH_SIZE ];
|
|
/*
|
|
Indices of entries down the current path.
|
|
|
|
*/
|
|
|
|
int overflowFlag[ MAX_PATH_SIZE ];
|
|
/*
|
|
Flags used in Insert which control the forced reinsertion process.
|
|
|
|
*/
|
|
|
|
TM_RTreeNode<dim, LeafInfo> *nodePtr;
|
|
/*
|
|
The current node of the R-tree.
|
|
|
|
*/
|
|
|
|
int currLevel;
|
|
/*
|
|
Current level (of ~nodePtr~).
|
|
|
|
*/
|
|
|
|
int currEntry;
|
|
/*
|
|
Current entry within ~nodePtr~.
|
|
|
|
*/
|
|
|
|
int reportLevel;
|
|
/*
|
|
Report level for first/next.
|
|
|
|
*/
|
|
|
|
BBox<dim> searchBox;
|
|
BBoxSet<dim> searchBoxSet;
|
|
/*
|
|
Bounding box for first/next.
|
|
|
|
*/
|
|
|
|
enum SearchType { NoSearch, BoxSearch, BoxSetSearch };
|
|
SearchType searchType;
|
|
/*
|
|
A flag that tells whether we're in the middle of a First/Next
|
|
scan of the tree.
|
|
|
|
*/
|
|
|
|
void PutNode( const SmiRecordId address, TM_RTreeNode<dim, LeafInfo> **node);
|
|
void PutNode( SmiRecord& record, TM_RTreeNode<dim, LeafInfo> **node );
|
|
/*
|
|
Writes the node ~node~ at file position ~address~.
|
|
Also deletes the node.
|
|
|
|
*/
|
|
|
|
TM_RTreeNode<dim, LeafInfo> *GetNode( const SmiRecordId address,
|
|
const bool leaf,
|
|
const int min, const int max );
|
|
TM_RTreeNode<dim, LeafInfo> *GetNode( SmiRecord& record, const bool leaf,
|
|
const int min, const int max );
|
|
/*
|
|
Reads a node at file position ~address~. This function creates a new
|
|
node that must be deleted somewhere.
|
|
|
|
*/
|
|
|
|
void WriteHeader();
|
|
/*
|
|
Writes the header of this tree.
|
|
|
|
*/
|
|
|
|
void ReadHeader();
|
|
/*
|
|
Reads the header of this rtree.
|
|
|
|
*/
|
|
|
|
void InsertEntry( const R_TreeEntry<dim>& );
|
|
/*
|
|
Inserts given entry in current node.
|
|
|
|
*/
|
|
|
|
void LocateBestNode( const R_TreeEntry<dim>& ent, int level );
|
|
/*
|
|
Locates the "best" node of level ~level~ to insert ~ent~.
|
|
|
|
*/
|
|
|
|
bool FindEntry( const R_TreeLeafEntry<dim, LeafInfo>& ent );
|
|
/*
|
|
Finds the given entry ~ent~ in the tree. If successful, ~true~ is
|
|
returned and ~currEntry~ and ~nodePtr~ are set to point to the
|
|
found entry. Otherwise, ~false~ is returned.
|
|
|
|
*/
|
|
|
|
void UpdateBox();
|
|
/*
|
|
Updates "bottom-up" bounding boxes of nodes in path
|
|
(i.e., from leaf to root).
|
|
|
|
*/
|
|
|
|
void GotoLevel( int level );
|
|
/*
|
|
Loads the node at the given ~level~ (must be in the current path).
|
|
|
|
*/
|
|
|
|
void DownLevel( int entryno );
|
|
/*
|
|
Loads the child node of the current node given by ~entryno~.
|
|
|
|
*/
|
|
|
|
void UpLevel();
|
|
/*
|
|
Loads the father node.
|
|
|
|
*/
|
|
|
|
/*
|
|
Private InsertBulkLoad method. Additionally gets the TreeNode to insert into.
|
|
|
|
*/
|
|
void InsertBulkLoad(TM_RTreeNode<dim, LeafInfo> *node,
|
|
const R_TreeEntry<dim>& entry);
|
|
|
|
void TM_InsertBulkLoad(TM_RTreeNode<dim, LeafInfo> *node,
|
|
const R_TreeEntry<dim>& entry, int, int);
|
|
bool bulkMode;
|
|
|
|
/*
|
|
true, iff in bulk loading mode
|
|
|
|
*/
|
|
|
|
TM_BulkLoadInfo<dim, LeafInfo> *bli;
|
|
/*
|
|
Info maintained during bulk loading
|
|
|
|
*/
|
|
|
|
long nodeIdCounter;
|
|
/*
|
|
A counter used in ~Introspect~ routines
|
|
|
|
*/
|
|
|
|
long nodeId[ MAX_PATH_SIZE ];
|
|
/*
|
|
An array to save the nodeIds of the path during the ~Introspect~ routines
|
|
|
|
*/
|
|
|
|
|
|
bool distanceFlag;
|
|
/*
|
|
true, after a call of FirstDistanceScan
|
|
Used by NearestNeighborAlgebra.
|
|
|
|
*/
|
|
unsigned long noentrynode;//noentryin currnode
|
|
};
|
|
|
|
/*
|
|
5.1 The constructors
|
|
|
|
*/
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
TM_RTree<dim, LeafInfo>::TM_RTree( const int pageSize ) :
|
|
fileOwner( true ),
|
|
file( new SmiRecordFile( true, pageSize ) ),
|
|
header(),
|
|
nodePtr( NULL ),
|
|
currLevel( -1 ),
|
|
currEntry( -1 ),
|
|
reportLevel( -1 ),
|
|
searchBox( false ),
|
|
searchBoxSet(),
|
|
searchType( NoSearch ),
|
|
bulkMode( false ),
|
|
bli( NULL ),
|
|
nodeIdCounter( 0 )
|
|
{
|
|
|
|
file->Create();
|
|
|
|
// Calculating maxEntries e minEntries
|
|
int nodeEmptySize = TM_RTreeNode<dim, LeafInfo>::SizeOfEmptyNode();
|
|
int leafEntrySize = sizeof( R_TreeLeafEntry<dim, LeafInfo> ),
|
|
internalEntrySize = sizeof( R_TreeInternalEntry<dim> );
|
|
|
|
int maxLeaf = ( pageSize - nodeEmptySize ) / leafEntrySize,
|
|
maxInternal = ( pageSize - nodeEmptySize ) / internalEntrySize;
|
|
|
|
header.maxLeafEntries = maxLeaf;
|
|
header.minLeafEntries = (int)(maxLeaf * 0.4);
|
|
|
|
header.maxInternalEntries = maxInternal;
|
|
header.minInternalEntries = (int)(maxInternal * 0.4);
|
|
|
|
assert( header.maxLeafEntries >= 2*header.minLeafEntries &&
|
|
header.minLeafEntries > 0 );
|
|
assert( header.maxInternalEntries >= 2*header.minInternalEntries &&
|
|
header.minInternalEntries > 0 );
|
|
|
|
// initialize overflowflag array
|
|
for( int i = 0; i < MAX_PATH_SIZE; i++ )
|
|
{
|
|
overflowFlag[ i ] = 0;
|
|
nodeId[ i ] = 0;
|
|
}
|
|
|
|
nodePtr = new TM_RTreeNode<dim, LeafInfo>( true,
|
|
MinEntries( 0 ),
|
|
MaxEntries( 0 ) );
|
|
|
|
// Creating a new page for the R-Tree header.
|
|
SmiRecord headerRecord;
|
|
int AppendedRecord = file->AppendRecord( header.headerRecordId,
|
|
headerRecord );
|
|
|
|
assert( AppendedRecord );
|
|
assert( header.headerRecordId == 1 );
|
|
|
|
// Creating the root node.
|
|
SmiRecordId rootRecno;
|
|
SmiRecord rootRecord;
|
|
AppendedRecord = file->AppendRecord( rootRecno, rootRecord );
|
|
assert( AppendedRecord );
|
|
header.rootRecordId = path[ 0 ] = rootRecno;
|
|
header.nodeCount = 1;
|
|
nodePtr->Write( rootRecord );
|
|
|
|
currLevel = 0;
|
|
}
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
TM_RTree<dim, LeafInfo>::TM_RTree( SmiRecordFile *file ) :
|
|
fileOwner( false ),
|
|
file( file ),
|
|
header(),
|
|
nodePtr( NULL ),
|
|
currLevel( -1 ),
|
|
currEntry( -1 ),
|
|
reportLevel( -1 ),
|
|
searchBox( false ),
|
|
searchBoxSet(),
|
|
searchType( NoSearch ),
|
|
bulkMode( false ),
|
|
bli( NULL ),
|
|
nodeIdCounter( 0 )
|
|
{
|
|
|
|
// Calculating maxEntries e minEntries
|
|
int nodeEmptySize = TM_RTreeNode<dim, LeafInfo>::SizeOfEmptyNode();
|
|
int leafEntrySize = sizeof( R_TreeLeafEntry<dim, LeafInfo> ),
|
|
internalEntrySize = sizeof( R_TreeInternalEntry<dim> );
|
|
|
|
int maxLeaf = ( file->GetRecordLength() - nodeEmptySize ) /
|
|
leafEntrySize,
|
|
maxInternal = ( file->GetRecordLength() - nodeEmptySize ) /
|
|
internalEntrySize;
|
|
|
|
header.maxLeafEntries = maxLeaf;
|
|
header.minLeafEntries = (int)(maxLeaf * 0.4);
|
|
|
|
header.maxInternalEntries = maxInternal;
|
|
header.minInternalEntries = (int)(maxInternal * 0.4);
|
|
|
|
assert( header.maxLeafEntries >= 2*header.minLeafEntries &&
|
|
header.minLeafEntries > 0 );
|
|
assert( header.maxInternalEntries >= 2*header.minInternalEntries &&
|
|
header.minInternalEntries > 0 );
|
|
|
|
|
|
// initialize overflowflag array
|
|
for( int i = 0; i < MAX_PATH_SIZE; i++ )
|
|
{
|
|
overflowFlag[ i ] = 0;
|
|
nodeId[ i ] = 0;
|
|
}
|
|
|
|
nodePtr = new TM_RTreeNode<dim, LeafInfo>( true,
|
|
MinEntries( 0 ),
|
|
MaxEntries( 0 ) );
|
|
|
|
// Creating a new page for the R-Tree header.
|
|
SmiRecord headerRecord;
|
|
int AppendedRecord =
|
|
file->AppendRecord( header.headerRecordId, headerRecord );
|
|
assert( AppendedRecord );
|
|
|
|
|
|
// Creating the root node.
|
|
SmiRecordId rootRecno;
|
|
SmiRecord rootRecord;
|
|
|
|
AppendedRecord = file->AppendRecord( rootRecno, rootRecord );
|
|
assert( AppendedRecord );
|
|
header.rootRecordId = path[ 0 ] = rootRecno;
|
|
header.nodeCount = 1;
|
|
|
|
nodePtr->Write( rootRecord );
|
|
|
|
currLevel = 0;
|
|
}
|
|
|
|
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
TM_RTree<dim, LeafInfo>::TM_RTree( const SmiFileId fileid ) :
|
|
fileOwner( true ),
|
|
file( new SmiRecordFile( true ) ),
|
|
header( 1 ),
|
|
nodePtr( NULL ),
|
|
currLevel( -1 ),
|
|
currEntry( -1 ),
|
|
reportLevel( -1 ),
|
|
searchBox( false ),
|
|
searchBoxSet(),
|
|
searchType( NoSearch ),
|
|
nodeIdCounter( 0 )
|
|
{
|
|
|
|
file->Open( fileid );
|
|
|
|
// initialize overflowflag array
|
|
for( int i = 0; i < MAX_PATH_SIZE; i++ )
|
|
{
|
|
overflowFlag[ i ] = 0;
|
|
nodeId[ i ] = 0;
|
|
}
|
|
|
|
ReadHeader();
|
|
assert( header.maxLeafEntries >= 2*header.minLeafEntries &&
|
|
header.minLeafEntries > 0 );
|
|
assert( header.maxInternalEntries >= 2*header.minInternalEntries &&
|
|
header.minInternalEntries > 0 );
|
|
|
|
currLevel = 0;
|
|
|
|
nodePtr = GetNode( RootRecordId(),
|
|
currLevel == Height(),
|
|
MinEntries( currLevel ),
|
|
MaxEntries( currLevel ) );
|
|
path[ 0 ] = header.rootRecordId;
|
|
}
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
TM_RTree<dim, LeafInfo>::TM_RTree( SmiRecordFile *file,
|
|
const SmiRecordId headerRecordId ) :
|
|
fileOwner( false ),
|
|
file( file ),
|
|
header( headerRecordId ),
|
|
nodePtr( NULL ),
|
|
currLevel( -1 ),
|
|
currEntry( -1 ),
|
|
reportLevel( -1 ),
|
|
searchBox( false ),
|
|
searchBoxSet(),
|
|
searchType( NoSearch ),
|
|
nodeIdCounter( 0 )
|
|
{
|
|
// cout<<"222"<<endl;
|
|
// initialize overflowflag array
|
|
for( int i = 0; i < MAX_PATH_SIZE; i++ )
|
|
{
|
|
overflowFlag[ i ] = 0;
|
|
nodeId[ i ] = 0;
|
|
}
|
|
|
|
ReadHeader();
|
|
assert( header.maxLeafEntries >= 2*header.minLeafEntries &&
|
|
header.minLeafEntries > 0 );
|
|
assert( header.maxInternalEntries >= 2*header.minInternalEntries &&
|
|
header.minInternalEntries > 0 );
|
|
|
|
currLevel = 0;
|
|
|
|
nodePtr = GetNode( RootRecordId(),
|
|
currLevel == Height(),
|
|
MinEntries( currLevel ),
|
|
MaxEntries( currLevel ) );
|
|
path[ 0 ] = header.rootRecordId;
|
|
}
|
|
|
|
/*
|
|
Open an existing R-Tree file and create a new R-Tree on it
|
|
|
|
*/
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
TM_RTree<dim, LeafInfo>::TM_RTree( const SmiFileId fileid,const int pageSize ) :
|
|
fileOwner( true ),
|
|
file( new SmiRecordFile( true ) ),
|
|
header(),
|
|
nodePtr( NULL ),
|
|
currLevel( -1 ),
|
|
currEntry( -1 ),
|
|
reportLevel( -1 ),
|
|
searchBox( false ),
|
|
searchBoxSet(),
|
|
searchType( NoSearch ),
|
|
bulkMode(false),
|
|
bli(NULL),
|
|
nodeIdCounter( 0 )
|
|
{
|
|
// cout<<"333"<<endl;
|
|
file->Open(fileid);
|
|
// Calculating maxEntries e minEntries
|
|
int nodeEmptySize = TM_RTreeNode<dim, LeafInfo>::SizeOfEmptyNode();
|
|
int leafEntrySize = sizeof( R_TreeLeafEntry<dim, LeafInfo> ),
|
|
internalEntrySize = sizeof( R_TreeInternalEntry<dim> );
|
|
|
|
int maxLeaf = ( pageSize - nodeEmptySize ) / leafEntrySize,
|
|
maxInternal = ( pageSize - nodeEmptySize ) / internalEntrySize;
|
|
|
|
header.maxLeafEntries = maxLeaf;
|
|
header.minLeafEntries = (int)(maxLeaf * 0.4);
|
|
|
|
header.maxInternalEntries = maxInternal;
|
|
header.minInternalEntries = (int)(maxInternal * 0.4);
|
|
|
|
assert( header.maxLeafEntries >= 2*header.minLeafEntries &&
|
|
header.minLeafEntries > 0 );
|
|
assert( header.maxInternalEntries >= 2*header.minInternalEntries &&
|
|
header.minInternalEntries > 0 );
|
|
|
|
// initialize overflowflag array
|
|
for( int i = 0; i < MAX_PATH_SIZE; i++ )
|
|
{
|
|
overflowFlag[ i ] = 0;
|
|
nodeId[ i ] = 0;
|
|
}
|
|
|
|
nodePtr = new TM_RTreeNode<dim, LeafInfo>( true,
|
|
MinEntries( 0 ),
|
|
MaxEntries( 0 ) );
|
|
|
|
// Creating a new page for the R-Tree header.
|
|
SmiRecord headerRecord;
|
|
int AppendedRecord = file->AppendRecord( header.headerRecordId,
|
|
headerRecord );
|
|
assert( AppendedRecord );
|
|
|
|
// Creating the root node.
|
|
SmiRecordId rootRecno;
|
|
SmiRecord rootRecord;
|
|
AppendedRecord = file->AppendRecord( rootRecno, rootRecord );
|
|
assert( AppendedRecord );
|
|
header.rootRecordId = path[ 0 ] = rootRecno;
|
|
header.nodeCount = 1;
|
|
nodePtr->Write( rootRecord );
|
|
|
|
currLevel = 0;
|
|
|
|
}
|
|
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
TM_RTree<dim, LeafInfo>::TM_RTree( const SmiFileId fileid, bool update ) :
|
|
fileOwner( true ),
|
|
file( new SmiRecordFile( true ) ),
|
|
header(1),
|
|
nodePtr( NULL ),
|
|
currLevel( -1 ),
|
|
currEntry( -1 ),
|
|
reportLevel( -1 ),
|
|
searchBox( false ),
|
|
searchBoxSet(),
|
|
searchType( NoSearch ),
|
|
bulkMode(false),
|
|
bli(NULL),
|
|
nodeIdCounter( 0 )
|
|
{
|
|
// cout<<"444"<<endl;
|
|
file->Open( fileid );
|
|
// initialize overflowflag array
|
|
for( int i = 0; i < MAX_PATH_SIZE; i++ )
|
|
{
|
|
overflowFlag[ i ] = 0;
|
|
nodeId[ i ] = 0;
|
|
}
|
|
|
|
ReadHeader();
|
|
assert( header.maxLeafEntries >= 2*header.minLeafEntries &&
|
|
header.minLeafEntries > 0 );
|
|
assert( header.maxInternalEntries >= 2*header.minInternalEntries &&
|
|
header.minInternalEntries > 0 );
|
|
|
|
currLevel = 0;
|
|
|
|
nodePtr = GetNode( RootRecordId(),
|
|
currLevel == Height(),
|
|
MinEntries( currLevel ),
|
|
MaxEntries( currLevel ) );
|
|
path[ 0 ] = header.rootRecordId;
|
|
|
|
}
|
|
|
|
/*
|
|
5.2 The destructor
|
|
|
|
*/
|
|
template <unsigned dim, class LeafInfo>
|
|
TM_RTree<dim, LeafInfo>::~TM_RTree()
|
|
{
|
|
// cout<<"~R_Tree()"<<endl;
|
|
|
|
if( file->IsOpen() )
|
|
{
|
|
if( nodePtr != NULL )
|
|
PutNode( path[ currLevel ], &nodePtr );
|
|
|
|
WriteHeader();
|
|
|
|
if( fileOwner )
|
|
file->Close();
|
|
}
|
|
if( fileOwner )
|
|
delete file;
|
|
}
|
|
|
|
/*
|
|
5.3 Reading and writing the header
|
|
|
|
*/
|
|
template <unsigned dim, class LeafInfo>
|
|
void TM_RTree<dim, LeafInfo>::ReadHeader()
|
|
{
|
|
|
|
SmiRecord record;
|
|
|
|
int RecordSelected =
|
|
file->SelectRecord( header.headerRecordId,
|
|
record,
|
|
SmiFile::ReadOnly );
|
|
assert( RecordSelected );
|
|
|
|
int RecordRead = record.Read( &header, sizeof( Header ), 0 )
|
|
== sizeof( Header );
|
|
assert( RecordRead );
|
|
}
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
void TM_RTree<dim, LeafInfo>::WriteHeader()
|
|
{
|
|
SmiRecord record;
|
|
int RecordSelected =
|
|
file->SelectRecord( header.headerRecordId,
|
|
record,
|
|
SmiFile::Update );
|
|
assert( RecordSelected );
|
|
int RecordWritten =
|
|
record.Write( &header, sizeof( Header ), 0 ) == sizeof( Header );
|
|
assert( RecordWritten );
|
|
}
|
|
|
|
/*
|
|
5.4 Method PutNode: Putting node to disk
|
|
|
|
*/
|
|
template <unsigned dim, class LeafInfo>
|
|
void TM_RTree<dim, LeafInfo>::PutNode( const SmiRecordId recno,
|
|
TM_RTreeNode<dim, LeafInfo> **node )
|
|
{
|
|
assert( file->IsOpen() );
|
|
(*node)->Write( file, recno );
|
|
delete *node;
|
|
*node = NULL;
|
|
}
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
void TM_RTree<dim, LeafInfo>::PutNode( SmiRecord& record,
|
|
TM_RTreeNode<dim, LeafInfo> **node )
|
|
{
|
|
(*node)->Write( record );
|
|
delete *node;
|
|
*node = NULL;
|
|
}
|
|
|
|
/*
|
|
5.5 Method GetNode: Getting node from disk
|
|
|
|
*/
|
|
template <unsigned dim, class LeafInfo>
|
|
TM_RTreeNode<dim, LeafInfo> *TM_RTree<dim, LeafInfo>::GetNode(
|
|
const SmiRecordId recno,
|
|
const bool leaf,
|
|
const int min,
|
|
const int max )
|
|
{
|
|
assert( file->IsOpen() );
|
|
TM_RTreeNode<dim, LeafInfo> *node =
|
|
new TM_RTreeNode<dim, LeafInfo>( leaf, min, max );
|
|
node->Read( file, recno );
|
|
return node;
|
|
}
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
TM_RTreeNode<dim, LeafInfo> *TM_RTree<dim, LeafInfo>::GetNode(
|
|
SmiRecord& record,
|
|
const bool leaf,
|
|
const int min,
|
|
const int max )
|
|
{
|
|
TM_RTreeNode<dim, LeafInfo> *node =
|
|
new TM_RTreeNode<dim, LeafInfo>( leaf, min, max );
|
|
node->Read( record );
|
|
return node;
|
|
}
|
|
|
|
/*
|
|
return BoundingBox
|
|
|
|
*/
|
|
template <unsigned dim, class LeafInfo>
|
|
BBox<dim> TM_RTree<dim, LeafInfo>::BoundingBox()
|
|
{
|
|
if( currLevel == 0 )
|
|
return nodePtr->BoundingBox();
|
|
else
|
|
{
|
|
TM_RTreeNode<dim, LeafInfo> *tmp =
|
|
GetNode( RootRecordId(),
|
|
0,
|
|
MinEntries(currLevel),
|
|
MaxEntries(currLevel) );
|
|
BBox<dim> result = tmp->BoundingBox();
|
|
delete tmp;
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Method Insert
|
|
|
|
*/
|
|
template <unsigned dim, class LeafInfo>
|
|
void TM_RTree<dim,LeafInfo>::Insert(const R_TreeLeafEntry<dim,LeafInfo>& entry)
|
|
{
|
|
searchType = NoSearch;
|
|
|
|
LocateBestNode( entry, Height() );
|
|
InsertEntry( entry );
|
|
header.entryCount++;
|
|
}
|
|
|
|
/*
|
|
Method LocateBestNode
|
|
|
|
*/
|
|
template <unsigned dim, class LeafInfo>
|
|
void TM_RTree<dim, LeafInfo>::LocateBestNode( const R_TreeEntry<dim>& entry,
|
|
int level )
|
|
{ GotoLevel( 0 );
|
|
|
|
// Top down search for a node of level 'level'
|
|
while( currLevel < level )
|
|
{
|
|
int bestNode = -1;
|
|
if( currLevel + 1 == Height() && minimize_leafnode_overlap )
|
|
{ // Best node is the one that gives minimum overlap. However,
|
|
// we should only take into consideration the k nodes that
|
|
// result in least enlargement, where k is given by
|
|
// leafnode_subset_max.
|
|
SortedArray enlargeList( MaxEntries(currLevel) + 1 );
|
|
int i, j, k;
|
|
|
|
for( i = 0; i < nodePtr->EntryCount(); i++ )
|
|
{
|
|
R_TreeEntry<dim> &son = (*nodePtr)[ i ];
|
|
enlargeList.push( i,
|
|
son.box.Union( entry.box ).Area() - son.box.Area() );
|
|
}
|
|
|
|
if( enlargeList.headPri() == 0.0 )
|
|
bestNode = enlargeList.pop();
|
|
// ...No need to do the overlap enlargement tests
|
|
else
|
|
{
|
|
double bestEnlargement = std::numeric_limits<double>::max(),
|
|
bestoverlap = std::numeric_limits<double>::max();
|
|
|
|
// Now compute the overlap enlargements
|
|
for( k = 0; !enlargeList.empty() && k < leafnode_subset_max; k++ )
|
|
{
|
|
double enlargement = enlargeList.headPri(),
|
|
overlapBefore = 0.0,
|
|
overlapAfter = 0.0,
|
|
overlap;
|
|
i = enlargeList.pop();
|
|
BBox<dim> boxBefore = (*nodePtr)[ i ].box;
|
|
BBox<dim> boxAfter = boxBefore.Union( entry.box );
|
|
|
|
for( j = 0; j < nodePtr->EntryCount(); ++j )
|
|
if( j == i )
|
|
continue;
|
|
else
|
|
{
|
|
R_TreeEntry<dim> &son = (*nodePtr)[ j ];
|
|
overlapBefore += boxBefore.Intersection( son.box ).Area();
|
|
overlapAfter += boxAfter.Intersection( son.box ).Area();
|
|
}
|
|
|
|
overlap = overlapAfter - overlapBefore;
|
|
|
|
if( overlap < bestoverlap ||
|
|
(overlap == bestoverlap && enlargement < bestEnlargement) )
|
|
{
|
|
bestoverlap = overlap;
|
|
bestEnlargement = enlargement;
|
|
bestNode = i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
double bestEnlargement = std::numeric_limits<double>::max();
|
|
int i;
|
|
|
|
for( i = 0; i < nodePtr->EntryCount(); i++ )
|
|
{
|
|
R_TreeEntry<dim> &son = (*nodePtr)[ i ];
|
|
double enlargement = son.box.Union( entry.box ).Area() - son.box.Area();
|
|
|
|
if( enlargement < bestEnlargement )
|
|
{
|
|
bestNode = i;
|
|
bestEnlargement = enlargement;
|
|
}
|
|
}
|
|
}
|
|
|
|
assert( bestNode != -1 );
|
|
DownLevel( bestNode );
|
|
}
|
|
}
|
|
|
|
/*
|
|
5.9 Method GotoLevel
|
|
|
|
*/
|
|
template <unsigned dim, class LeafInfo>
|
|
void TM_RTree<dim, LeafInfo>::GotoLevel( int level )
|
|
{
|
|
if( currLevel == level )
|
|
{
|
|
if( nodePtr == NULL )
|
|
nodePtr = GetNode( path[ currLevel ],
|
|
Height() == level,
|
|
MinEntries(currLevel),
|
|
MaxEntries(currLevel) );
|
|
}
|
|
else
|
|
{
|
|
assert( level >= 0 && level <= Height() );
|
|
|
|
if( nodePtr != NULL )
|
|
PutNode( path[ currLevel ], &nodePtr );
|
|
|
|
currLevel = level;
|
|
nodePtr = GetNode( path[ currLevel ],
|
|
Height() == level,
|
|
MinEntries(currLevel),
|
|
MaxEntries(currLevel) );
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
Method DownLevel
|
|
|
|
*/
|
|
template <unsigned dim, class LeafInfo>
|
|
void TM_RTree<dim, LeafInfo>::DownLevel( int entryNo )
|
|
{
|
|
assert( currLevel != Height() );
|
|
assert( nodePtr != 0 );
|
|
assert( entryNo >= 0 && entryNo < nodePtr->EntryCount() );
|
|
|
|
pathEntry[ currLevel ] = entryNo;
|
|
path[ currLevel+1 ] =
|
|
((R_TreeInternalEntry<dim>&)(*nodePtr)[ entryNo ]).pointer;
|
|
PutNode( path[ currLevel ], &nodePtr );
|
|
currLevel += 1;
|
|
nodePtr = GetNode( path[ currLevel ],
|
|
Height() == currLevel,
|
|
MinEntries(currLevel),
|
|
MaxEntries(currLevel) );
|
|
}
|
|
|
|
|
|
/*
|
|
InsertEntry
|
|
|
|
*/
|
|
template <unsigned dim, class LeafInfo>
|
|
void TM_RTree<dim, LeafInfo>::InsertEntry( const R_TreeEntry<dim>& entry )
|
|
{
|
|
assert( file->IsOpen() );
|
|
|
|
if( nodePtr->Insert( entry ) ){
|
|
UpdateBox();
|
|
} else {
|
|
if( !do_forced_reinsertion || currLevel == 0 ||
|
|
overflowFlag[ Height() - currLevel ] )
|
|
{ // Node splitting is necessary
|
|
TM_RTreeNode<dim, LeafInfo> *n1 =
|
|
new TM_RTreeNode<dim, LeafInfo>
|
|
(nodePtr->IsLeaf(), MinEntries(currLevel), MaxEntries(currLevel) );
|
|
TM_RTreeNode<dim, LeafInfo> *n2 =
|
|
new TM_RTreeNode<dim, LeafInfo>
|
|
(nodePtr->IsLeaf(), MinEntries(currLevel), MaxEntries(currLevel) );
|
|
|
|
nodePtr->Split( *n1, *n2 );
|
|
|
|
// Write split nodes and update parent
|
|
if( currLevel == 0)
|
|
{ // splitting root node
|
|
nodePtr->Clear();
|
|
nodePtr->SetInternal( header.minInternalEntries,
|
|
header.maxInternalEntries );
|
|
|
|
BBox<dim> n1Box( n1->BoundingBox() );
|
|
SmiRecordId node1recno;
|
|
SmiRecord node1record;
|
|
int RecordAppended = file->AppendRecord( node1recno, node1record );
|
|
assert( RecordAppended );
|
|
PutNode( node1record, &n1 ); // deletes n1
|
|
int EntryInserted =
|
|
nodePtr->Insert( R_TreeInternalEntry<dim>(n1Box,node1recno));
|
|
assert(EntryInserted);
|
|
|
|
BBox<dim> n2Box( n2->BoundingBox() );
|
|
SmiRecordId node2recno;
|
|
SmiRecord node2record;
|
|
RecordAppended = file->AppendRecord( node2recno, node2record );
|
|
assert( RecordAppended );
|
|
PutNode( node2record, &n2 ); // deletes n2
|
|
EntryInserted =
|
|
nodePtr->Insert(R_TreeInternalEntry<dim>(n2Box,node2recno));
|
|
assert(EntryInserted);
|
|
header.height += 1;
|
|
header.nodeCount += 2;
|
|
}
|
|
else
|
|
{ // splitting non-root node
|
|
SmiRecordId newNoderecno;
|
|
SmiRecord newNoderecord;
|
|
int RecordAppended = file->AppendRecord( newNoderecno, newNoderecord );
|
|
assert( RecordAppended );
|
|
R_TreeInternalEntry<dim> newEntry( n2->BoundingBox(), newNoderecno );
|
|
PutNode( newNoderecord, &n2 ); // deletes n2
|
|
|
|
header.nodeCount++;
|
|
|
|
// Copy all entries from n1 to nodePtr
|
|
nodePtr->Clear();
|
|
*nodePtr = *n1;
|
|
delete n1;
|
|
|
|
UpdateBox();
|
|
UpLevel();
|
|
InsertEntry( newEntry );
|
|
}
|
|
}
|
|
else
|
|
{ // Do forced reinsertion instead of split
|
|
int reinsertLevel = Height() - currLevel;
|
|
|
|
// Avoid reinserting at this level
|
|
overflowFlag[ reinsertLevel ] = 1;
|
|
|
|
// Compute the center of the node
|
|
BBox<dim> nodeBox = nodePtr->BoundingBox();
|
|
double nodeCenter[ dim ];
|
|
|
|
for( unsigned i = 0; i < dim; i++ )
|
|
nodeCenter[ i ] = (nodeBox.MinD( i ) + nodeBox.MaxD( i ))/2;
|
|
|
|
// Make list sorted by distance from the center of each
|
|
// entry bounding box to the center of the bounding box
|
|
// of all entries.
|
|
// NOTE: We use CHESSBOARD metric for the distance
|
|
SortedArray distSort( MaxEntries(currLevel) + 1 );
|
|
|
|
for( int i = 0; i < nodePtr->EntryCount(); i++ )
|
|
{
|
|
double entryDistance = 0.0;
|
|
|
|
for( unsigned j = 0; j < dim; j++ )
|
|
{
|
|
double centerEntry = ((*nodePtr)[ i ].box.MinD( j ) +
|
|
(*nodePtr)[ i ].box.MaxD( j ))/2;
|
|
double dist = fabs( centerEntry - nodeCenter[ j ] );
|
|
|
|
if( dist > entryDistance )
|
|
entryDistance = dist;
|
|
}
|
|
|
|
distSort.push( i, entryDistance );
|
|
}
|
|
|
|
{ // Write node with the entries that will stay
|
|
int maxEntries = MaxEntries(currLevel),
|
|
minEntries = MinEntries(currLevel);
|
|
|
|
R_TreeEntry<dim> **tmp = new R_TreeEntry<dim>*[ maxEntries + 1 ];
|
|
int *keepFlag = new int[ maxEntries + 1 ];
|
|
bool leaf = nodePtr->IsLeaf();
|
|
int deleteCount, n = 0;
|
|
|
|
for( int i = 0; i <= maxEntries; i++ )
|
|
{
|
|
keepFlag[ i ] = 0;
|
|
tmp[i] = 0;
|
|
}
|
|
|
|
deleteCount = (forced_reinsertion_percent * maxEntries) / 100;
|
|
|
|
assert( maxEntries - deleteCount >= minEntries );
|
|
|
|
for( int i = maxEntries-deleteCount; i >= 0; i-- )
|
|
keepFlag[ distSort.pop() ] = 1;
|
|
|
|
for( int i = maxEntries; i >= 0; i-- )
|
|
if( !keepFlag[ i ] )
|
|
{
|
|
if( leaf )
|
|
tmp[ i ] = new R_TreeLeafEntry<dim, LeafInfo>
|
|
( (R_TreeLeafEntry<dim, LeafInfo>&)(*nodePtr)[ i ] );
|
|
else
|
|
tmp[ i ] = new R_TreeInternalEntry<dim>
|
|
( (R_TreeInternalEntry<dim>&)(*nodePtr)[ i ] );
|
|
n++;
|
|
nodePtr->Remove( i );
|
|
}
|
|
|
|
assert( n == deleteCount );
|
|
UpdateBox();
|
|
|
|
// Reinsert remaining entries( using "close reinsert" --
|
|
// see R*tree paper, pg 327)
|
|
while( !distSort.empty() )
|
|
{
|
|
int entryNo = distSort.pop();
|
|
R_TreeEntry<dim> *reinsertEntry = tmp[ entryNo ];
|
|
|
|
assert( !keepFlag[ entryNo ] );
|
|
|
|
LocateBestNode( *reinsertEntry, Height() - reinsertLevel );
|
|
InsertEntry( *reinsertEntry );
|
|
}
|
|
|
|
// Reset flag so that other insertion operations may cause the
|
|
// forced reinsertion process to take place
|
|
overflowFlag[ reinsertLevel ] = 0;
|
|
|
|
for( int i = 0; i <= maxEntries; i++ )
|
|
delete tmp[i];
|
|
delete [] tmp;
|
|
delete [] keepFlag;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
5.12 Method UpLevel
|
|
|
|
*/
|
|
template <unsigned dim, class LeafInfo>
|
|
void TM_RTree<dim, LeafInfo>::UpLevel()
|
|
{
|
|
assert( currLevel > 0 );
|
|
|
|
if( nodePtr != NULL )
|
|
PutNode( path[ currLevel ], &nodePtr );
|
|
|
|
currLevel -= 1;
|
|
nodePtr = GetNode( path[ currLevel ],
|
|
Height() == currLevel,
|
|
MinEntries(currLevel),
|
|
MaxEntries(currLevel) );
|
|
}
|
|
|
|
|
|
/*
|
|
5.13 Method UpdateBox
|
|
|
|
*/
|
|
template <unsigned dim, class LeafInfo>
|
|
void TM_RTree<dim, LeafInfo>::UpdateBox()
|
|
{
|
|
// Save where we were before
|
|
int formerlevel = currLevel;
|
|
|
|
for( int l = currLevel; l > 0; l-- )
|
|
{
|
|
// Compute bounding box of child
|
|
BBox<dim> box = nodePtr->BoundingBox();
|
|
|
|
// Update 'father' node
|
|
UpLevel();
|
|
nodePtr->UpdateBox( box, path[ l ] );
|
|
}
|
|
|
|
// Return to where we were before
|
|
GotoLevel( formerlevel );
|
|
}
|
|
|
|
/*
|
|
5.14 Method FindEntry
|
|
|
|
*/
|
|
template <unsigned dim, class LeafInfo>
|
|
bool TM_RTree<dim, LeafInfo>::FindEntry(const R_TreeLeafEntry<dim,
|
|
LeafInfo>& entry )
|
|
{
|
|
|
|
// First see if the current entry is the one that is being sought,
|
|
// as is the case with many searches followed by Delete
|
|
if( currLevel == Height() &&
|
|
currEntry >= 0 && currEntry < nodePtr->EntryCount() &&
|
|
nodePtr != 0 &&
|
|
(*nodePtr)[ currEntry ].box == entry.box &&
|
|
((R_TreeLeafEntry<dim, LeafInfo>&)(*nodePtr)[ currEntry ]).info
|
|
== entry.info )
|
|
return true;
|
|
|
|
// Load root node
|
|
GotoLevel( 0 );
|
|
|
|
// Init search params
|
|
searchBox = entry.box;
|
|
currEntry = 0;
|
|
|
|
// Search the tree until no more subtrees are found in the root
|
|
while( currEntry < nodePtr->EntryCount() || currLevel > 0 )
|
|
{
|
|
if( currEntry >= nodePtr->EntryCount())
|
|
{ // All entries or subtrees of the current node were
|
|
// examined. Go up on the tree
|
|
UpLevel();
|
|
currEntry = pathEntry[ currLevel ];
|
|
currEntry++;
|
|
}
|
|
else // Try another subtree or entry
|
|
if( currLevel == Height() ) // This is a leaf node. Search all entries
|
|
while( currEntry < nodePtr->EntryCount() )
|
|
{
|
|
if( (*nodePtr)[ currEntry ].box == entry.box &&
|
|
((R_TreeLeafEntry<dim, LeafInfo>&)(*nodePtr)[ currEntry ]).
|
|
info == entry.info )
|
|
return true; // Found it
|
|
|
|
currEntry++;
|
|
}
|
|
else // This is an internal node. Search all subtrees
|
|
while( currEntry < nodePtr->EntryCount() )
|
|
if( (*nodePtr)[ currEntry ].box.Intersects( searchBox ) )
|
|
{ // Found a possible subtree. Go down
|
|
DownLevel( currEntry );
|
|
currEntry = 0;
|
|
|
|
break;
|
|
}
|
|
else
|
|
currEntry++;
|
|
// No subtree was found. Go up
|
|
}
|
|
|
|
// Entry Not found
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
5.15 Method First
|
|
|
|
*/
|
|
template <unsigned dim, class LeafInfo>
|
|
bool TM_RTree<dim, LeafInfo>::First( const BBox<dim>& box,
|
|
R_TreeLeafEntry<dim,
|
|
LeafInfo>& result,
|
|
int replevel )
|
|
{
|
|
// Remember that we have started a scan of the R_Tree
|
|
searchType = BoxSearch;
|
|
|
|
// Init search params
|
|
searchBox = box;
|
|
reportLevel = replevel;
|
|
|
|
// Load root node
|
|
GotoLevel( 0 );
|
|
|
|
// Finish with the actual search
|
|
currEntry = -1;
|
|
return Next( result );
|
|
}
|
|
|
|
template<unsigned dim, class LeafInfo>
|
|
bool TM_RTree<dim, LeafInfo>::First( const BBoxSet<dim>& boxSet,
|
|
R_TreeLeafEntry<dim, LeafInfo>& result )
|
|
{
|
|
// Remember that we have started a scan of the R_Tree
|
|
searchType = BoxSetSearch;
|
|
|
|
// Init search params
|
|
searchBoxSet = boxSet;
|
|
|
|
// Load root node
|
|
GotoLevel( 0 );
|
|
|
|
// Finish with the actual search
|
|
currEntry = -1;
|
|
return Next( result );
|
|
}
|
|
|
|
/*
|
|
5.16 Method Next
|
|
|
|
*/
|
|
template <unsigned dim, class LeafInfo>
|
|
bool TM_RTree<dim, LeafInfo>::Next( R_TreeLeafEntry<dim, LeafInfo>& result )
|
|
{
|
|
// Next can be called only after a 'First' or a 'Next' operation
|
|
assert( searchType != NoSearch );
|
|
|
|
bool retcode = false;
|
|
|
|
while( currEntry < nodePtr->EntryCount() || currLevel > 0 )
|
|
if( currEntry >= nodePtr->EntryCount())
|
|
{ // All entries in this node were examined. Go up the tree
|
|
// Find entry in father node corresponding to this node.
|
|
UpLevel();
|
|
assert( pathEntry[ currLevel ] < nodePtr->EntryCount() );
|
|
currEntry = pathEntry[ currLevel ];
|
|
}
|
|
else
|
|
{ // Search next entry / subtree in this node
|
|
currEntry++;
|
|
|
|
if( currEntry < nodePtr->EntryCount() )
|
|
{
|
|
if( searchType == BoxSearch ?
|
|
searchBox.Intersects( (*nodePtr)[ currEntry ].box ) :
|
|
searchBoxSet.Intersects( (*nodePtr)[ currEntry ].box ) )
|
|
{
|
|
if( nodePtr->IsLeaf() || currLevel == reportLevel)
|
|
{ // Found an appropriate entry
|
|
result = (R_TreeLeafEntry<dim, LeafInfo>&)(*nodePtr)[ currEntry ];
|
|
retcode = true;
|
|
break;
|
|
}
|
|
else
|
|
{ // Go down the tree
|
|
DownLevel( currEntry );
|
|
currEntry = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return retcode;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
Variants of ~First~ and ~Next~ for introspection into the R-tree.
|
|
|
|
The complete r-tree will be iterated, but only all entries on replevel
|
|
will be returned, unless replevel == -1.
|
|
|
|
*/
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
bool TM_RTree<dim, LeafInfo>::IntrospectFirst( IntrospectResult<dim>& result)
|
|
{
|
|
// create result for root
|
|
result = IntrospectResult<dim>
|
|
(
|
|
0,
|
|
0,
|
|
BoundingBox(),
|
|
-1,
|
|
nodePtr->IsLeaf(),
|
|
nodePtr->MinEntries(),
|
|
nodePtr->MaxEntries(),
|
|
nodePtr->EntryCount()
|
|
);
|
|
|
|
// Load root node
|
|
GotoLevel( 0 );
|
|
nodeIdCounter = 0;
|
|
nodeId[currLevel] = 0;
|
|
|
|
|
|
// Remember that we have started a scan of the R_Tree
|
|
searchType = BoxSearch;
|
|
currEntry = -1;
|
|
for(int i = 0; i < MAX_PATH_SIZE; i++)
|
|
{
|
|
pathEntry[i] = -1;
|
|
nodeId[i] = -1;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
bool TM_RTree<dim, LeafInfo>::IntrospectNextE(unsigned long& nodeid,BBox<dim>&
|
|
box,unsigned long& tupleid )
|
|
{
|
|
// Next can be called only after a 'IntrospectFirst'
|
|
// or a 'IntrospectNext' operation
|
|
assert( searchType == BoxSearch );
|
|
|
|
bool retcode = false;
|
|
|
|
pathEntry[currLevel]++;
|
|
// printf("pathEntry %d\n",pathEntry[currLevel]);
|
|
// printf("EntryCount() %d\n",nodePtr->EntryCount());
|
|
// printf("currLevel %d\n",currLevel);
|
|
while( pathEntry[currLevel] < nodePtr->EntryCount() || currLevel > 0 )
|
|
{
|
|
if( pathEntry[currLevel] >= nodePtr->EntryCount())
|
|
{ // All entries in this node were examined. Go up the tree
|
|
// Find entry in father node corresponding to this node.
|
|
nodeId[currLevel] = -1;
|
|
pathEntry[ currLevel ] = -1;
|
|
UpLevel();
|
|
assert( pathEntry[ currLevel ] < nodePtr->EntryCount() );
|
|
pathEntry[currLevel]++;
|
|
}
|
|
else
|
|
{ // Search next entry / subtree in this node
|
|
// pathEntry[currLevel]++;
|
|
|
|
if( nodeId[ currLevel ] < 0)
|
|
nodeId[ currLevel ] = nodeIdCounter;
|
|
if( pathEntry[currLevel] < nodePtr->EntryCount() )
|
|
{ // produce result
|
|
if( nodePtr->IsLeaf() )
|
|
{ // Found leaf node
|
|
// get complete node
|
|
// printf("leaf\n");
|
|
R_TreeLeafEntry<dim, LeafInfo> entry =
|
|
(R_TreeLeafEntry<dim, LeafInfo>&)
|
|
(*nodePtr)[ pathEntry[ currLevel ] ];
|
|
//
|
|
box = entry.box;
|
|
tupleid = entry.info;
|
|
nodeid = path[currLevel];
|
|
retcode = true;
|
|
break;
|
|
}
|
|
else // internal node
|
|
{ // Found internal node
|
|
// printf("internal node\n");
|
|
R_TreeInternalEntry<dim> entry =
|
|
(R_TreeInternalEntry<dim>&) (*nodePtr)[pathEntry[ currLevel]];
|
|
// printf("pathEntry currLevel %d, %d\n",currLevel,pathEntry[currLevel]);
|
|
DownLevel( pathEntry[currLevel] );
|
|
nodeId[currLevel] = ++nodeIdCounter; // set nodeId
|
|
// printf("pathEntry currLevel %d, %d\n",currLevel,pathEntry[currLevel]);
|
|
pathEntry[currLevel]++;
|
|
// pathEntry[currLevel] = -1; // reset for next iteration
|
|
} // end else
|
|
|
|
} //end if
|
|
} // end else
|
|
} // end while
|
|
// pathEntry[currLevel] += nodePtr->EntryCount();
|
|
return retcode;
|
|
}
|
|
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
bool TM_RTree<dim, LeafInfo>::IntrospectNext( IntrospectResult<dim>& result )
|
|
{
|
|
// Next can be called only after a 'IntrospectFirst'
|
|
// or a 'IntrospectNext' operation
|
|
assert( searchType == BoxSearch );
|
|
|
|
bool retcode = false;
|
|
|
|
pathEntry[currLevel]++;
|
|
while( pathEntry[currLevel] < nodePtr->EntryCount() || currLevel > 0 )
|
|
{
|
|
if( pathEntry[currLevel] >= nodePtr->EntryCount())
|
|
{ // All entries in this node were examined. Go up the tree
|
|
// Find entry in father node corresponding to this node.
|
|
nodeId[currLevel] = -1;
|
|
pathEntry[ currLevel ] = -1;
|
|
UpLevel();
|
|
assert( pathEntry[ currLevel ] < nodePtr->EntryCount() );
|
|
pathEntry[currLevel]++;
|
|
}
|
|
else
|
|
{ // Search next entry / subtree in this node
|
|
// pathEntry[currLevel]++;
|
|
if( nodeId[ currLevel ] < 0)
|
|
nodeId[ currLevel ] = nodeIdCounter;
|
|
if( pathEntry[currLevel] < nodePtr->EntryCount() )
|
|
{ // produce result
|
|
if( nodePtr->IsLeaf() )
|
|
{ // Found leaf node
|
|
// get complete node
|
|
R_TreeLeafEntry<dim, LeafInfo> entry =
|
|
(R_TreeLeafEntry<dim, LeafInfo>&)
|
|
(*nodePtr)[ pathEntry[ currLevel ] ];
|
|
result = IntrospectResult<dim>
|
|
(
|
|
currLevel+1, // the entries shall have bigger levels
|
|
++nodeIdCounter, // and get their own node numbers
|
|
entry.box,
|
|
nodeId[currLevel],
|
|
true, // use some
|
|
1, // resonable standard
|
|
1, // values for
|
|
1 // these attributes
|
|
);
|
|
}
|
|
else // internal node
|
|
{ // Found internal node
|
|
DownLevel( pathEntry[currLevel] );
|
|
nodeId[currLevel] = ++nodeIdCounter; // set nodeId
|
|
R_TreeInternalEntry<dim> entry =
|
|
(R_TreeInternalEntry<dim>&) (*nodePtr)[ 0 ];
|
|
result = IntrospectResult<dim>
|
|
(
|
|
currLevel,
|
|
nodeIdCounter,
|
|
nodePtr->BoundingBox(),
|
|
nodeId[currLevel-1], // currLevel >= 1 (DownLevel)
|
|
nodePtr->IsLeaf(),
|
|
nodePtr->MinEntries(),
|
|
nodePtr->MaxEntries(),
|
|
nodePtr->EntryCount()
|
|
);
|
|
// pathEntry[currLevel] = -1; // reset for next iteration
|
|
} // end else
|
|
retcode = true;
|
|
break;
|
|
} //end if
|
|
} // end else
|
|
} // end while
|
|
return retcode;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
Method Remove
|
|
|
|
*/
|
|
template <unsigned dim, class LeafInfo>
|
|
bool TM_RTree<dim, LeafInfo>::Remove( const R_TreeLeafEntry<dim,
|
|
LeafInfo>& entry )
|
|
{
|
|
assert( file->IsOpen() );
|
|
|
|
searchType = NoSearch;
|
|
|
|
// First locate the entry in the tree
|
|
if( !FindEntry( entry ) )
|
|
return false;
|
|
else
|
|
{ // Create a list of nodes whose entries must be reinserted
|
|
std::stack<int> reinsertLevelList;
|
|
std::stack<TM_RTreeNode<dim, LeafInfo>*> reinsertNodeList;
|
|
BBox<dim> sonBox( false );
|
|
|
|
// remove leaf node entry
|
|
nodePtr->Remove( currEntry );
|
|
header.entryCount -= 1;
|
|
|
|
while( currLevel > 0 )
|
|
{
|
|
int underflow = nodePtr->EntryCount() < MinEntries( currEntry );
|
|
|
|
if( underflow )
|
|
{ // Current node has underflow. Save it for later reinsertion
|
|
TM_RTreeNode<dim, LeafInfo>* nodePtrcopy = new TM_RTreeNode<dim, LeafInfo>
|
|
( *nodePtr );
|
|
|
|
reinsertNodeList.push( nodePtrcopy );
|
|
reinsertLevelList.push( currLevel );
|
|
|
|
// Remove node from the tree
|
|
nodePtr->Clear();
|
|
int RecordDeleted = file->DeleteRecord( path[ currLevel ] );
|
|
assert( RecordDeleted );
|
|
}
|
|
else
|
|
sonBox = nodePtr->BoundingBox();
|
|
|
|
// Find entry corresponding to this node in father node
|
|
UpLevel();
|
|
currEntry = pathEntry[ currLevel ];
|
|
|
|
// Adjust father node
|
|
if( underflow ) // remove corresponding entry in father node
|
|
nodePtr->Remove( currEntry );
|
|
else // Adjust father node entry bounding box
|
|
(*nodePtr)[ currEntry ].box = sonBox;
|
|
}
|
|
|
|
// Reinsert entries in every node of reinsertNodeList
|
|
while( !reinsertNodeList.empty() )
|
|
{
|
|
TM_RTreeNode<dim, LeafInfo>* tmp = reinsertNodeList.top();
|
|
int level = reinsertLevelList.top(), i;
|
|
reinsertNodeList.pop();
|
|
reinsertLevelList.pop();
|
|
|
|
for( i = 0; i < tmp->EntryCount(); i++ )
|
|
{
|
|
assert( level <= Height() );
|
|
|
|
LocateBestNode( (*tmp)[ i ], level );
|
|
InsertEntry( (*tmp)[ i ] );
|
|
}
|
|
|
|
delete tmp;
|
|
}
|
|
|
|
// See if root node now has only one son
|
|
if( Height() == 0 ) // we are done
|
|
return true;
|
|
|
|
// Load root node
|
|
GotoLevel( 0 );
|
|
|
|
assert( !nodePtr->IsLeaf());
|
|
|
|
if( nodePtr->EntryCount() == 1 )
|
|
{ // root node has only one son
|
|
long newRoot = ((R_TreeInternalEntry<dim>&)(*nodePtr)[ 0 ]).pointer;
|
|
|
|
// Remove former root node from the tree
|
|
file->DeleteRecord( RootRecordId() );
|
|
delete nodePtr; nodePtr = NULL;
|
|
|
|
// Retrieve new root
|
|
header.rootRecordId = path[ 0 ] = newRoot;
|
|
GotoLevel( 0 );
|
|
// Tree has diminished in height()
|
|
header.height -= 1;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
bool TM_RTree<dim, LeafInfo>::InitializeBulkLoad(const bool &leafSkipping)
|
|
{
|
|
assert( NodeCount() == 1 );
|
|
|
|
if( bulkMode || bli != NULL )
|
|
{
|
|
return false;
|
|
}
|
|
bulkMode = true;
|
|
bli = new TM_BulkLoadInfo<dim, LeafInfo>(leafSkipping);
|
|
return true;
|
|
};
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
void TM_RTree<dim, LeafInfo>::TM_BulkLoad(const R_TreeEntry<dim>& entry, int m,
|
|
int last_m)
|
|
{
|
|
if( bli->node[0] != NULL ) {
|
|
assert(bulkMode == true);
|
|
}
|
|
bli->currentLevel = 0;
|
|
if( bli->node[0] == NULL ) { // create a fresh leaf node
|
|
bli->node[0] = new TM_RTreeNode<dim,LeafInfo>(true,
|
|
header.minLeafEntries,
|
|
header.maxLeafEntries);
|
|
bli->nodeCount++;
|
|
}
|
|
|
|
TM_InsertBulkLoad(bli->node[0], entry, m, last_m);
|
|
bli->entryCount++;
|
|
return;
|
|
}
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
void TM_RTree<dim, LeafInfo>::BulkLoad(const R_TreeEntry<dim>& entry)
|
|
{
|
|
if( bli->node[0] != NULL ) {
|
|
assert(bulkMode == true);
|
|
}
|
|
bli->currentLevel = 0;
|
|
if( bli->node[0] == NULL ) { // create a fresh leaf node
|
|
bli->node[0] = new TM_RTreeNode<dim,LeafInfo>(true,
|
|
header.minLeafEntries,
|
|
header.maxLeafEntries);
|
|
bli->nodeCount++;
|
|
}
|
|
|
|
InsertBulkLoad(bli->node[0], entry);
|
|
bli->entryCount++;
|
|
return;
|
|
}
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
void TM_RTree<dim, LeafInfo>::InsertBulkLoad(TM_RTreeNode<dim, LeafInfo> *node,
|
|
const R_TreeEntry<dim>& entry)
|
|
{
|
|
assert( bulkMode == true );
|
|
assert( node != NULL );
|
|
|
|
if( !bli->levelLastBox[bli->currentLevel].IsDefined() ) {
|
|
// initialize when called for the first time
|
|
bli->levelLastBox[bli->currentLevel] = entry.box;
|
|
}
|
|
// trace the distance to the last entry inserted into this node:
|
|
double dist = entry.box.Distance(bli->levelLastBox[bli->currentLevel]);
|
|
bli->levelEntryCounter[bli->currentLevel]++;
|
|
bli->levelDistanceSum[bli->currentLevel] += dist;
|
|
bli->levelLastBox[bli->currentLevel] = entry.box;
|
|
// update average distance of all entries on this level
|
|
double avgDist = bli->levelDistanceSum[bli->currentLevel]
|
|
/ (MAX(bli->levelEntryCounter[bli->currentLevel],2) - 1);
|
|
|
|
// cout << "Level = " << bli->currentLevel << endl
|
|
// << " dist = " << dist
|
|
// << " avgDist = " << avgDist
|
|
// << " #Entries =" << bli->node[bli->currentLevel]->EntryCount()
|
|
// << "/" << bli->node[bli->currentLevel]->MaxEntries()
|
|
// << endl;
|
|
|
|
if( ( bli->node[bli->currentLevel]->EntryCount() <
|
|
bli->node[bli->currentLevel]->MaxEntries() // fits into node
|
|
)
|
|
&&
|
|
( !bli->skipLeaf // standard case
|
|
|| ( dist <= (avgDist * BULKLOAD_TOLERANCE) ) // distance OK
|
|
|| ( bli->node[bli->currentLevel]->EntryCount() <=
|
|
bli->node[bli->currentLevel]->MinEntries() *
|
|
BULKLOAD_MIN_ENTRIES_FACTOR ) // too few entries
|
|
|| ( bli->levelEntryCounter[bli->currentLevel] <=
|
|
BULKLOAD_INITIAL_SEQ_LENGTH)
|
|
)
|
|
)
|
|
{ // insert the entry into this (already existing) node
|
|
// cout << " --> Inserting here!" << endl;
|
|
bli->node[bli->currentLevel]->Insert(entry);
|
|
return;
|
|
} // else: node is already full (or distance to large)...
|
|
|
|
// cout << " --> Passing upwards..." << endl;
|
|
// Write node[currentLevel] to disk
|
|
// Request SMI for a fresh record (and its Id):
|
|
assert(file->IsOpen());
|
|
SmiRecordId recId;
|
|
SmiRecord rec;
|
|
bool RecordAppended = file->AppendRecord(recId, rec);
|
|
assert(RecordAppended);
|
|
// write the current node into the fresh record
|
|
bli->node[bli->currentLevel]->Write(file, recId);
|
|
|
|
// if no father node exists, create one
|
|
if( bli->node[bli->currentLevel+1] == NULL ) {
|
|
assert (bli->currentHeight == bli->currentLevel);
|
|
bli->currentHeight++; // increase height reached
|
|
bli->node[bli->currentLevel+1] =
|
|
new TM_RTreeNode<dim,LeafInfo>(false,
|
|
header.minInternalEntries,
|
|
header.maxInternalEntries);
|
|
bli->nodeCount++;
|
|
}
|
|
|
|
// change to father
|
|
bli->currentLevel++;
|
|
|
|
// Insert son into father (by recursive call)
|
|
InsertBulkLoad(
|
|
bli->node[bli->currentLevel],
|
|
R_TreeInternalEntry<dim>(
|
|
bli->node[bli->currentLevel-1]->BoundingBox(),
|
|
recId ) );
|
|
|
|
// delete node and create a new node instead
|
|
bli->currentLevel--; // change back to original level
|
|
delete bli->node[bli->currentLevel];
|
|
bli->node[bli->currentLevel] = NULL;
|
|
if(bli->currentLevel == 0) { // create a leaf node
|
|
bli->node[bli->currentLevel] =
|
|
new TM_RTreeNode<dim,LeafInfo>(true,
|
|
header.minLeafEntries,
|
|
header.maxLeafEntries);
|
|
} else { // create an internal node
|
|
bli->node[bli->currentLevel] =
|
|
new TM_RTreeNode<dim,LeafInfo>(false,
|
|
header.minInternalEntries,
|
|
header.maxInternalEntries);
|
|
}
|
|
|
|
bli->nodeCount++;
|
|
|
|
// finally, insert the original entry passed as argument
|
|
bli->node[bli->currentLevel]->Insert(entry);
|
|
return;
|
|
};
|
|
|
|
/*
|
|
for each leaf node, only insert tuples with the same transportation mode
|
|
|
|
*/
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
void TM_RTree<dim, LeafInfo>::TM_InsertBulkLoad(
|
|
TM_RTreeNode<dim, LeafInfo> *node,
|
|
const R_TreeEntry<dim>& entry,
|
|
int m, int last_m)
|
|
{
|
|
assert( bulkMode == true );
|
|
assert( node != NULL );
|
|
|
|
if( !bli->levelLastBox[bli->currentLevel].IsDefined() ) {
|
|
// initialize when called for the first time
|
|
bli->levelLastBox[bli->currentLevel] = entry.box;
|
|
}
|
|
// trace the distance to the last entry inserted into this node:
|
|
double dist = entry.box.Distance(bli->levelLastBox[bli->currentLevel]);
|
|
bli->levelEntryCounter[bli->currentLevel]++;
|
|
bli->levelDistanceSum[bli->currentLevel] += dist;
|
|
bli->levelLastBox[bli->currentLevel] = entry.box;
|
|
|
|
if( bli->node[bli->currentLevel]->EntryCount() <
|
|
bli->node[bli->currentLevel]->MaxEntries() && m == last_m)
|
|
|
|
{ // insert the entry into this (already existing) node
|
|
// cout << " --> Inserting here!" << endl;
|
|
bli->node[bli->currentLevel]->Insert(entry);
|
|
return;
|
|
} // else: node is already full (or distance to large)...
|
|
|
|
// cout << " --> Passing upwards..." << endl;
|
|
// Write node[currentLevel] to disk
|
|
// Request SMI for a fresh record (and its Id):
|
|
|
|
|
|
assert(file->IsOpen());
|
|
SmiRecordId recId;
|
|
SmiRecord rec;
|
|
bool RecordAppended = file->AppendRecord(recId, rec);
|
|
assert(RecordAppended);
|
|
// write the current node into the fresh record
|
|
bli->node[bli->currentLevel]->Write(file, recId);
|
|
|
|
// if no father node exists, create one
|
|
if( bli->node[bli->currentLevel+1] == NULL ) {
|
|
assert (bli->currentHeight == bli->currentLevel);
|
|
bli->currentHeight++; // increase height reached
|
|
bli->node[bli->currentLevel+1] =
|
|
new TM_RTreeNode<dim,LeafInfo>(false,
|
|
header.minInternalEntries,
|
|
header.maxInternalEntries);
|
|
bli->nodeCount++;
|
|
}
|
|
|
|
// change to father
|
|
bli->currentLevel++;
|
|
|
|
// Insert son into father (by recursive call)
|
|
InsertBulkLoad(
|
|
bli->node[bli->currentLevel],
|
|
R_TreeInternalEntry<dim>(
|
|
bli->node[bli->currentLevel-1]->BoundingBox(),
|
|
recId ) );
|
|
|
|
// delete node and create a new node instead
|
|
bli->currentLevel--; // change back to original level
|
|
delete bli->node[bli->currentLevel];
|
|
bli->node[bli->currentLevel] = NULL;
|
|
if(bli->currentLevel == 0) { // create a leaf node
|
|
bli->node[bli->currentLevel] =
|
|
new TM_RTreeNode<dim,LeafInfo>(true,
|
|
header.minLeafEntries,
|
|
header.maxLeafEntries);
|
|
} else { // create an internal node
|
|
bli->node[bli->currentLevel] =
|
|
new TM_RTreeNode<dim,LeafInfo>(false,
|
|
header.minInternalEntries,
|
|
header.maxInternalEntries);
|
|
}
|
|
bli->nodeCount++;
|
|
|
|
// finally, insert the original entry passed as argument
|
|
bli->node[bli->currentLevel]->Insert(entry);
|
|
return;
|
|
};
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
SmiRecordId TM_RTree<dim, LeafInfo>::DFVisit_Rtree(
|
|
TM_RTree<dim,LeafInfo>* rtree_in,
|
|
TM_RTreeNode<dim,LeafInfo>* node)
|
|
{
|
|
if(node->IsLeaf()){
|
|
SmiRecordId recordid;
|
|
SmiRecord record;
|
|
int AppendRecord =
|
|
file->AppendRecord(recordid,record);
|
|
assert(AppendRecord);
|
|
node->SetModified();
|
|
node->Write(record);
|
|
return recordid;
|
|
}else{
|
|
TM_RTreeNode<dim,LeafInfo>* new_n =
|
|
new TM_RTreeNode<dim,LeafInfo>(node->IsLeaf(),MinEntries(0),MaxEntries(0));
|
|
|
|
for(int i = 0;i < node->EntryCount();i++){
|
|
R_TreeInternalEntry<dim> e =
|
|
(R_TreeInternalEntry<dim>&)(*node)[i];
|
|
TM_RTreeNode<dim,LeafInfo>* n = rtree_in->GetMyNode(
|
|
e.pointer,false,rtree_in->MinEntries(0),rtree_in->MaxEntries(0));
|
|
SmiRecordId recid;
|
|
recid = DFVisit_Rtree(rtree_in,n);
|
|
new_n->Insert(R_TreeInternalEntry<dim>(e.box,recid));
|
|
delete n;
|
|
}
|
|
SmiRecordId recordid;
|
|
SmiRecord record;
|
|
int AppendRecord =
|
|
file->AppendRecord(recordid,record);
|
|
assert(AppendRecord);
|
|
new_n->SetModified();
|
|
new_n->Write(record);
|
|
delete new_n;
|
|
return recordid;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Copy an R-Tree, 1) Using BulkLoad 2) DF Traversal
|
|
The key issue is for different files, the recordId is different and can't be
|
|
controled (which is returned by berkeleydb library function)
|
|
|
|
*/
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
void TM_RTree<dim, LeafInfo>::Clone(TM_RTree<dim,LeafInfo>* rtree_in)
|
|
{
|
|
/* assert(InitializeBulkLoad());
|
|
BBox<dim> searchbox(rtree_in->BoundingBox());
|
|
R_TreeLeafEntry<dim,LeafInfo> e;
|
|
if(rtree_in->First(searchbox,e)){
|
|
InsertBulkLoad(e);
|
|
while(rtree_in->Next(e)){
|
|
InsertBulkLoad(e);
|
|
}
|
|
}
|
|
assert(FinalizeBulkLoad());*/
|
|
|
|
////////////////////// root /////////////////////////////
|
|
SmiRecordId root_id = rtree_in->RootRecordId();
|
|
|
|
TM_RTreeNode<dim,LeafInfo>* rootnode = rtree_in->GetMyNode(
|
|
root_id,false,rtree_in->MinEntries(0),rtree_in->MaxEntries(0));
|
|
|
|
root_id = DFVisit_Rtree(rtree_in,rootnode);
|
|
|
|
header.nodeCount = rtree_in->NodeCount();
|
|
header.entryCount = rtree_in->EntryCount();
|
|
header.height = rtree_in->Height();
|
|
header.rootRecordId = root_id;
|
|
delete rootnode;
|
|
}
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
bool TM_RTree<dim, LeafInfo>::FinalizeBulkLoad()
|
|
{
|
|
if ( bulkMode != true ) {
|
|
return false;
|
|
}
|
|
// write all nodes to disk and delete them from memory
|
|
|
|
// the array bli->node[i] contains all nodes, that need to be flushed outputs
|
|
// onto disk. This is done bottom-up (starting at the leaf-level): Each node
|
|
// is written to disk and then inserted into the node one level above.
|
|
// During insertion, ancestor nodes may be changed, written to disk or being
|
|
// replaced by other nodes.
|
|
bool finished = false;
|
|
for(int i=0; i < MAX_PATH_SIZE; i++) { // level == 0 means the leaf level
|
|
if( !finished
|
|
&& i <= bli->currentHeight
|
|
&& bli->node[i] != NULL) { // need to write the node
|
|
// request SMI for a fresh record
|
|
assert(file->IsOpen());
|
|
SmiRecordId recId;
|
|
SmiRecord rec;
|
|
int RecordAppended = file->AppendRecord(recId, rec);
|
|
assert(RecordAppended);
|
|
// write current node to that record
|
|
bli->node[i]->Write(rec);
|
|
|
|
if( i < bli->currentHeight ) { // Insert a non-root node into its father
|
|
assert( i+1 <= bli->currentHeight );
|
|
assert( bli->node[i+1] != NULL );
|
|
if(i == 0) {// leaf level
|
|
assert( bli->node[i]->IsLeaf() == true );
|
|
} else { // inner level
|
|
assert( bli->node[i]->IsLeaf() == false );
|
|
// create an entry for this node to insert into the father
|
|
}
|
|
// create an entry for this node to insert into the father
|
|
R_TreeInternalEntry<dim> entry( bli->node[i]->BoundingBox(),
|
|
recId );
|
|
bli->currentLevel = i+1;
|
|
InsertBulkLoad(bli->node[i+1],entry);
|
|
} else {// ( i == bli->currentHeight): replace the root node
|
|
assert( i == bli->currentHeight );
|
|
|
|
// Verify, that the current rtree is empty:
|
|
assert( NodeCount() == 1 );
|
|
assert( EntryCount() == 0 );
|
|
SmiRecordId newRootId = recId;
|
|
|
|
// Remove old root node from the tree
|
|
file->DeleteRecord( RootRecordId() );
|
|
delete nodePtr;
|
|
nodePtr = NULL;
|
|
currLevel = 0;
|
|
currEntry = -1;
|
|
reportLevel = -1;
|
|
searchBox = false;
|
|
searchType = NoSearch;
|
|
|
|
// Set new root to topmost node from bulkloading
|
|
header.nodeCount = bli->nodeCount;
|
|
header.entryCount = bli->entryCount;
|
|
header.height = bli->currentHeight;
|
|
header.rootRecordId = newRootId;
|
|
path[ 0 ] = newRootId;
|
|
|
|
WriteHeader();
|
|
finished = true;
|
|
}
|
|
}
|
|
// ALWAYS: delete the current node from memory
|
|
if( bli->node[i] != NULL ) {
|
|
delete bli->node[i];
|
|
bli->node[i] = NULL;
|
|
}
|
|
} // end for
|
|
delete bli;
|
|
bli = NULL;
|
|
|
|
|
|
if(!finished){
|
|
// happens if no things are do do above
|
|
WriteHeader();
|
|
}
|
|
// re-initialize the rtree
|
|
ReadHeader();
|
|
assert( header.maxLeafEntries >= 2*header.minLeafEntries &&
|
|
header.minLeafEntries > 0 );
|
|
assert( header.maxInternalEntries >= 2*header.minInternalEntries &&
|
|
header.minInternalEntries > 0 );
|
|
currLevel = 0;
|
|
//assert(nodePtr==0);
|
|
nodePtr = GetNode( RootRecordId(),
|
|
currLevel == Height(),
|
|
MinEntries( currLevel ),
|
|
MaxEntries( currLevel ) );
|
|
path[ 0 ] = header.rootRecordId;
|
|
|
|
return true;
|
|
};
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
bool TM_RTree<dim, LeafInfo>::getFileStats( SmiStatResultType &result )
|
|
{
|
|
result = file->GetFileStatistics(SMI_STATS_EAGER);
|
|
std::stringstream fileid;
|
|
fileid << file->GetFileId();
|
|
result.push_back(std::pair<std::string,std::string>("FilePurpose",
|
|
"SecondaryRtreeIndexFile"));
|
|
result.push_back(std::pair<std::string,std::string>("FileId",fileid.str()));
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
6 Template functions for the type constructors
|
|
|
|
6.1 ~Out~-function
|
|
|
|
It does not make sense to have an R-Tree as an independent value
|
|
since the record ids stored in it become obsolete as soon as
|
|
the underlying relation is deleted. Therefore this function
|
|
outputs will show only some statistics about the tree.
|
|
|
|
*/
|
|
template <unsigned dim>
|
|
ListExpr OutTMRTree(ListExpr typeInfo, Word value)
|
|
{
|
|
if( nl->BoolValue(nl->Fourth(typeInfo)) == true )
|
|
{
|
|
ListExpr bboxList, appendList;
|
|
TM_RTree<dim, TwoLayerLeafInfo> *rtree =
|
|
(TM_RTree<dim, TwoLayerLeafInfo> *)value.addr;
|
|
|
|
bboxList = nl->OneElemList(
|
|
nl->RealAtom( rtree->BoundingBox().MinD( 0 ) ));
|
|
appendList = bboxList;
|
|
appendList = nl->Append(appendList,
|
|
nl->RealAtom( rtree->BoundingBox().MaxD( 0 ) ));
|
|
|
|
for( unsigned i = 1; i < dim; i++)
|
|
{
|
|
appendList = nl->Append(appendList,
|
|
nl->RealAtom( rtree->BoundingBox().MinD( i ) ));
|
|
appendList = nl->Append(appendList,
|
|
nl->RealAtom( rtree->BoundingBox().MaxD( i ) ));
|
|
}
|
|
|
|
return nl->FiveElemList(
|
|
nl->StringAtom( "TM-RTree statistics" ),
|
|
nl->TwoElemList( nl->StringAtom( "Height" ),
|
|
nl->IntAtom( rtree->Height() ) ),
|
|
nl->TwoElemList( nl->StringAtom( "# of (leaf) entries" ),
|
|
nl->IntAtom( rtree->EntryCount() ) ),
|
|
nl->TwoElemList( nl->StringAtom( "# of nodes" ),
|
|
nl->IntAtom( rtree->NodeCount() ) ),
|
|
nl->TwoElemList( nl->StringAtom( "Bounding Box" ), bboxList ) );
|
|
}
|
|
else
|
|
{
|
|
ListExpr bboxList, appendList;
|
|
TM_RTree<dim, TupleId> *rtree = (TM_RTree<dim, TupleId> *)value.addr;
|
|
|
|
bboxList = nl->OneElemList(
|
|
nl->RealAtom( rtree->BoundingBox().MinD( 0 ) ));
|
|
appendList = bboxList;
|
|
appendList = nl->Append(appendList,
|
|
nl->RealAtom( rtree->BoundingBox().MaxD( 0 ) ));
|
|
|
|
for( unsigned i = 1; i < dim; i++)
|
|
{
|
|
appendList = nl->Append(appendList,
|
|
nl->RealAtom( rtree->BoundingBox().MinD( i ) ));
|
|
appendList = nl->Append(appendList,
|
|
nl->RealAtom( rtree->BoundingBox().MaxD( i ) ));
|
|
}
|
|
|
|
return nl->FiveElemList(
|
|
nl->StringAtom( "TM-RTree statistics" ),
|
|
nl->TwoElemList( nl->StringAtom( "Height" ),
|
|
nl->IntAtom( rtree->Height() ) ),
|
|
nl->TwoElemList( nl->StringAtom( "# of (leaf) entries" ),
|
|
nl->IntAtom( rtree->EntryCount() ) ),
|
|
nl->TwoElemList( nl->StringAtom( "# of nodes" ),
|
|
nl->IntAtom( rtree->NodeCount() ) ),
|
|
nl->TwoElemList( nl->StringAtom( "Bounding Box" ), bboxList ) );
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
6.2 ~In~-function
|
|
|
|
Reading an R-Tree from a list does not make sense because an R-Tree
|
|
is not an independent value. Therefore calling this function leads
|
|
to program abort.
|
|
|
|
*/
|
|
template <unsigned dim>
|
|
Word InTMRTree( ListExpr typeInfo, ListExpr value,
|
|
int errorPos, ListExpr& errorInfo, bool& correct )
|
|
{
|
|
correct = false;
|
|
return SetWord(Address(0));
|
|
}
|
|
|
|
/*
|
|
6.3 ~Create~-function
|
|
|
|
*/
|
|
template <unsigned dim>
|
|
Word CreateTMRTree( const ListExpr typeInfo )
|
|
{
|
|
|
|
if( nl->BoolValue(nl->Fourth(typeInfo)) == true ){
|
|
return SetWord( new TM_RTree<dim, TwoLayerLeafInfo>( 4000 ) );
|
|
}else{
|
|
return SetWord( new TM_RTree<dim, TupleId>( 4000 ) );
|
|
}
|
|
}
|
|
|
|
/*
|
|
6.4 ~Close~-function
|
|
|
|
*/
|
|
template <unsigned dim>
|
|
void CloseTMRTree( const ListExpr typeInfo, Word& w )
|
|
{
|
|
if( nl->BoolValue(nl->Fourth(typeInfo)) == true )
|
|
{
|
|
TM_RTree<dim, TwoLayerLeafInfo>* rtree = (TM_RTree<dim,
|
|
TwoLayerLeafInfo>*)w.addr;
|
|
delete rtree;
|
|
}
|
|
else
|
|
{
|
|
TM_RTree<dim, TupleId>* rtree = (TM_RTree<dim, TupleId>*)w.addr;
|
|
delete rtree;
|
|
}
|
|
}
|
|
|
|
/*
|
|
6.5 ~Clone~-function
|
|
|
|
Not implemented yet.
|
|
|
|
implemented by Jianqiu xu --- 2009.11.30
|
|
|
|
*/
|
|
template <unsigned dim>
|
|
Word CloneTMRTree( const ListExpr typeInfo, const Word& w )
|
|
{
|
|
/////////////// new implementation ////////////////////////////
|
|
TM_RTree<dim,TupleId>* rtree = (TM_RTree<dim,TupleId>*)w.addr;
|
|
TM_RTree<dim,TupleId>* newrtree =
|
|
new TM_RTree<dim,TupleId>(4000);
|
|
|
|
newrtree->Clone(rtree);
|
|
return SetWord( newrtree);
|
|
|
|
//////////////// original version ////////////////////////////////////////
|
|
// return SetWord( Address(0) );
|
|
}
|
|
|
|
/*
|
|
6.6 ~Delete~-function
|
|
|
|
*/
|
|
template <unsigned dim>
|
|
void DeleteTMRTree( const ListExpr typeInfo, Word& w )
|
|
{
|
|
|
|
if (nl->ListLength(typeInfo) == 4)
|
|
{
|
|
if( nl->BoolValue(nl->Fourth(typeInfo)) == true )
|
|
{
|
|
TM_RTree<dim, TwoLayerLeafInfo>* rtree = (TM_RTree<dim,
|
|
TwoLayerLeafInfo>*)w.addr;
|
|
rtree->DeleteFile();
|
|
delete rtree;
|
|
return;
|
|
}
|
|
}
|
|
|
|
TM_RTree<dim, TupleId>* rtree = (TM_RTree<dim, TupleId>*)w.addr;
|
|
rtree->DeleteFile();
|
|
delete rtree;
|
|
|
|
}
|
|
|
|
/*
|
|
6.7 ~Cast~-function
|
|
|
|
*/
|
|
template <unsigned dim>
|
|
void* CastTMRTree( void* addr)
|
|
{
|
|
return ( 0 );
|
|
}
|
|
|
|
/*
|
|
6.8 ~Open~-function
|
|
|
|
*/
|
|
template <unsigned dim>
|
|
bool OpenTMRTree( SmiRecord& valueRecord,
|
|
size_t& offset,
|
|
const ListExpr typeInfo,
|
|
Word& value )
|
|
{
|
|
|
|
SmiFileId fileid;
|
|
valueRecord.Read( &fileid, sizeof( SmiFileId ), offset );
|
|
offset += sizeof( SmiFileId );
|
|
|
|
if( nl->BoolValue(nl->Fourth(typeInfo)) == true )
|
|
{
|
|
TM_RTree<dim, TwoLayerLeafInfo> *rtree =
|
|
new TM_RTree<dim, TwoLayerLeafInfo>( fileid );
|
|
value = SetWord( rtree );
|
|
}
|
|
else
|
|
{
|
|
TM_RTree<dim, TupleId> *rtree = new TM_RTree<dim, TupleId>( fileid );
|
|
value = SetWord( rtree );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
6.9 ~Save~-function
|
|
|
|
*/
|
|
|
|
template <unsigned dim>
|
|
bool SaveTMRTree( SmiRecord& valueRecord,
|
|
size_t& offset,
|
|
const ListExpr typeInfo,
|
|
Word& value )
|
|
{
|
|
|
|
SmiFileId fileId;
|
|
|
|
if( nl->BoolValue(nl->Fourth(typeInfo)) == true )
|
|
{
|
|
TM_RTree<dim, TwoLayerLeafInfo> *rtree =
|
|
(TM_RTree<dim, TwoLayerLeafInfo> *)value.addr;
|
|
fileId = rtree->FileId();
|
|
}
|
|
else
|
|
{
|
|
assert(value.addr);
|
|
TM_RTree<dim, TupleId> *rtree = (TM_RTree<dim, TupleId> *)value.addr;
|
|
fileId = rtree->FileId();
|
|
|
|
}
|
|
valueRecord.Write( &fileId, sizeof( SmiFileId ), offset );
|
|
offset += sizeof( SmiFileId );
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
6.10 ~SizeOf~-function
|
|
|
|
*/
|
|
template <unsigned dim>
|
|
int SizeOfTMRTree()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
bool TM_RTree<dim, LeafInfo>::Open(SmiRecord& valueRecord,
|
|
size_t& offset,
|
|
std::string typeInfo,
|
|
Word &value)
|
|
{
|
|
SmiFileId fileId;
|
|
size_t n = sizeof(SmiFileId);
|
|
valueRecord.Read(&fileId, n, offset);
|
|
offset += n;
|
|
value = SetWord(new TM_RTree<dim, LeafInfo> (fileId));
|
|
return true;
|
|
};
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
bool TM_RTree<dim, LeafInfo>::Save(SmiRecord& valueRecord,
|
|
size_t& offset)
|
|
{
|
|
const size_t n=sizeof(SmiFileId);
|
|
SmiFileId fileId= this->FileId();
|
|
if (valueRecord.Write(&fileId, n, offset) != n) return false;
|
|
offset += n;
|
|
return true;
|
|
};
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
bool TM_RTree<dim,LeafInfo>::InitializeBLI(const bool& leafSkipping)
|
|
{
|
|
if(bulkMode)cout<<"bulkMode"<<endl;
|
|
if(bli)cout<<"bli"<<endl;
|
|
// if(bulkMode || bli != NULL)
|
|
// return false;
|
|
bulkMode = true;
|
|
bli = new TM_BulkLoadInfo<dim,LeafInfo>(leafSkipping);
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
calculate the transportation mode for each node
|
|
from bottom to up
|
|
|
|
*/
|
|
template <unsigned dim, class LeafInfo>
|
|
bool TM_RTree<dim,LeafInfo>::CalculateTM(Relation* rel, int attr_pos)
|
|
{
|
|
|
|
SmiRecordId node_id = RootRecordId();
|
|
long tm = CalculateNodeTM(node_id, rel, attr_pos);
|
|
|
|
TM_RTreeNode<3, TupleId>* node =
|
|
GetMyNode(node_id,false, MinEntries(0), MaxEntries(0));
|
|
|
|
////////////write the value ///////////////
|
|
node->SetTMValue(tm);
|
|
node->Write(file, node_id);
|
|
|
|
delete node;
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
recursively calling the son node
|
|
very, very, very important!!!.
|
|
from original genmo, pos = max - m;
|
|
but here, we direct use the enum value of mode as bit index
|
|
|
|
*/
|
|
template <unsigned dim, class LeafInfo>
|
|
long TM_RTree<dim,LeafInfo>::CalculateNodeTM(SmiRecordId nodeid,
|
|
Relation* rel, int attr_pos)
|
|
{
|
|
TM_RTreeNode<3, TupleId>* node = GetMyNode(nodeid,false,
|
|
MinEntries(0), MaxEntries(0));
|
|
|
|
if(node->IsLeaf()){
|
|
|
|
int pos = -1;
|
|
// bitset<ARR_SIZE(str_tm)> modebits;
|
|
std::bitset<TM_SUM_NO> modebits;//extension to a pair of modes
|
|
modebits.reset();
|
|
|
|
for(int j = 0;j < node->EntryCount();j++){
|
|
|
|
R_TreeLeafEntry<3, TupleId> e =
|
|
(R_TreeLeafEntry<3, TupleId>&)(*node)[j];
|
|
Tuple* tuple = rel->GetTuple(e.info, false);
|
|
int m = ((CcInt*)tuple->GetAttribute(attr_pos))->GetIntval();//bit index
|
|
tuple->DeleteIfAllowed();
|
|
// // cout<<"j "<<j<<" tm "<<GetTMStr(m)<<endl;
|
|
|
|
pos = m;
|
|
assert(0 <= pos && pos <= (int)(TM_SUM_NO - 1));
|
|
|
|
modebits.set(pos, 1);//set the value for each entry:general method
|
|
}
|
|
/////////checking for leaf node, mode should be the same///////////////
|
|
|
|
/* bitset<ARR_SIZE(str_tm)> modebits;
|
|
modebits.reset();
|
|
modebits.set(pos, 1);*/
|
|
long tm = modebits.to_ulong();////////bit value to an integer
|
|
// cout<<"leaf node "<<nodeid<<endl;
|
|
// cout<<tm<<" "<<modebits.to_string()<<" "<<GetModeStringExt(tm)<<endl;
|
|
|
|
node->SetTMValue(tm);
|
|
node->Write(file, nodeid);
|
|
delete node;
|
|
|
|
return tm;
|
|
|
|
}else{
|
|
// bitset<ARR_SIZE(str_tm)> modebits;
|
|
std::bitset<TM_SUM_NO> modebits; //extension to a pair of modes
|
|
modebits.reset();
|
|
for(int j = 0;j < node->EntryCount();j++){
|
|
|
|
R_TreeInternalEntry<3> e =
|
|
(R_TreeInternalEntry<3>&)(*node)[j];
|
|
long son_tm = CalculateNodeTM(e.pointer, rel, attr_pos);
|
|
|
|
// bitset<ARR_SIZE(str_tm)> m_bit(son_tm);
|
|
std::bitset<TM_SUM_NO> m_bit(son_tm);
|
|
|
|
// ///////////// union value of each son tm to tm//////////////
|
|
// /* cout<<"new one "<<m_bit.to_string()
|
|
// <<" before "<<modebits.to_string()<<endl;*/
|
|
modebits = modebits | m_bit;
|
|
// // cout<<"after"<<modebits.to_string()<<endl;
|
|
//
|
|
}
|
|
|
|
long tm = modebits.to_ulong();
|
|
// cout<<"non leaf node "<<nodeid<<endl;
|
|
// cout<<modebits.to_ulong()<<" "<<modebits.to_string()
|
|
// <<" "<<GetModeString(tm)<<endl;
|
|
node->SetTMValue(tm);
|
|
node->Write(file, nodeid);
|
|
delete node;
|
|
return tm;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
after the TMRtree declaration
|
|
|
|
*/
|
|
|
|
template <unsigned dim, class LeafInfo>
|
|
TM_RTreeNode<dim, LeafInfo>& TM_RTree<dim, LeafInfo>::Root()
|
|
// Loads nodeptr with the root node
|
|
{
|
|
GotoLevel( 0 );
|
|
|
|
return *nodePtr;
|
|
}
|
|
|
|
#endif
|