/* //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 #include #include #include #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 class TM_RTreeNode { public: TM_RTreeNode( const bool leaf, const int min, const int max ); TM_RTreeNode( const TM_RTreeNode& 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& operator[] ( int index ) const { assert( index >= 0 ); assert(index <= maxEntries ); assert(index < count); return *entries[ index ]; } /* Returns entry given by index. */ R_TreeLeafEntry* GetLeafEntry(const int index) const; R_TreeInternalEntry* GetInternalEntry(const int index) const; BBox BoundingBox() const; BBox 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& operator = ( const TM_RTreeNode&); /* 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& 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& n1, TM_RTreeNode& n2 ); /* Splits this node in two: ~n1~ and ~n2~, which should be empty nodes. */ void UpdateBox( BBox& 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** 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& b1, BBox& 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 TM_RTreeNode::TM_RTreeNode( const bool leaf, const int min, const int max ) : leaf( leaf ), minEntries( min ), maxEntries( max ), count( 0 ), entries( new R_TreeEntry*[ max + 1 ] ), modified( true ), tm(-1) { for( int i = 0; i <= maxEntries; i++ ){ entries[ i ] = 0; } // cout< TM_RTreeNode:: TM_RTreeNode(const TM_RTreeNode& node): leaf( node.leaf ), minEntries( node.minEntries ), maxEntries( node.maxEntries ), count( node.count ), entries( new R_TreeEntry*[ node.maxEntries + 1 ] ), modified( true ), tm(node.tm) { int i; for( i = 0; i < node.EntryCount(); i++ ) { if( leaf ) entries[ i ] = new R_TreeLeafEntry( (R_TreeLeafEntry&)*node.entries[ i ] ); else entries[ i ] = new R_TreeInternalEntry( ( R_TreeInternalEntry&)*node.entries[ i ] ); } for( ; i <= node.maxEntries; i++ ){ entries[ i ] = NULL; } } /* 4.2 The destructor */ template TM_RTreeNode::~TM_RTreeNode() { for( int i = 0; i <= count; i++ ){ delete entries[ i ]; } delete []entries; } template int TM_RTreeNode::Size() const { int size = SizeOfEmptyNode(); if( leaf ) size += R_TreeLeafEntry::Size() * maxEntries; else size += R_TreeInternalEntry::Size() * maxEntries; return size; } template TM_RTreeNode& TM_RTreeNode::operator= (const TM_RTreeNode& 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*[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 ( (R_TreeLeafEntry&)*node.entries[ i ] ); } else { entries[ i ] = new R_TreeInternalEntry ( (R_TreeInternalEntry&)*node.entries[ i ] ); } } tm = node.tm; return *this; } template bool TM_RTreeNode::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 bool TM_RTreeNode::Insert( const R_TreeEntry& ent ) { assert( count <= maxEntries ); if( leaf ) entries[ count++ ] = new R_TreeLeafEntry ( (R_TreeLeafEntry&)ent ); else entries[ count++ ] = new R_TreeInternalEntry ( (R_TreeInternalEntry&)ent ); modified = true; return (count <= maxEntries); } /* 4.3 Method LinearPickSeeds */ template void TM_RTreeNode::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::max(); int maxMinNode[ dim ]; int minMaxNode[ dim ]; int bestD = -1; for( unsigned i = 0; i < dim; i++ ) { maxMinVal[i] = -std::numeric_limits::max(); minMaxVal[i] = std::numeric_limits::max(); minVal[i] = std::numeric_limits::max(); maxVal[i] = -std::numeric_limits::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 void TM_RTreeNode::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::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 int TM_RTreeNode::QuadraticPickNext( BBox& b1, BBox& 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 void TM_RTreeNode::Split( TM_RTreeNode& n1, TM_RTreeNode& 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::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 *b1 = new BBox[ MaxEntries() + 1 ]; BBox *b2 = new BBox[ 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& box1 = b1[ splitPoint ]; BBox& 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::max(); double minArea = std::numeric_limits::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 box1 = entries[ seed1 ]->box; BBox 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 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 BBox TM_RTreeNode::BoundingBox() const { if( count == 0 ) return BBox( false ); else { BBox 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 void TM_RTreeNode::UpdateBox( BBox& b, SmiRecordId pointer) { assert( !leaf ); modified = true; for( int i = 0; i < count; i++ ) if( ((R_TreeInternalEntry*)entries[ i ])->pointer == pointer ) { entries[ i ]->box = b; return; } // Should never reach this point assert( 0 ); } template void TM_RTreeNode::Read( SmiRecordFile *file, const SmiRecordId pointer ) { SmiRecord record; int RecordSelected = file->SelectRecord( pointer, record, SmiFile::ReadOnly ); assert( RecordSelected ); Read( record ); } template void TM_RTreeNode::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(); else entries[ i ] = new R_TreeInternalEntry(); entries[ i ]->Read( buffer, offset ); } assert(offset<=(int)readed); // otherwise some entries will be uninitialized modified = false; } template void TM_RTreeNode::Write( SmiRecordFile *file, const SmiRecordId pointer ) { if( modified ) { SmiRecord record; int RecordSelected = file->SelectRecord( pointer, record, SmiFile::Update ); assert( RecordSelected ); Write( record ); } } template void TM_RTreeNode::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::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 R_TreeLeafEntry* TM_RTreeNode::GetLeafEntry(const int index) const { assert(index >= 0 && index <= maxEntries); return (R_TreeLeafEntry*)entries[index]; } template R_TreeInternalEntry* TM_RTreeNode::GetInternalEntry(const int index) const { assert(index >= 0 && index < count); return (R_TreeInternalEntry*)entries[index]; } /* Class tmbulkload This class implements the TMR-Tree. */ template class TM_BulkLoadInfo { public: TM_RTreeNode *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 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(false); } }; virtual ~TM_BulkLoadInfo() { for(int i=0; i < MAX_PATH_SIZE; i++) if(node[i] != NULL) delete node[i]; }; }; /////////////////////////////////////////////////////////////////////////// ///////////////////// TMRtree implementation ///////////////////////////// //////////////////////////////////////////////////////////////////////////// template 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*); SmiRecordId DFVisit_Rtree(TM_RTree*, TM_RTreeNode*); /* 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& 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 BoundingBox(); /* Returns the bounding box of this rtree. */ void Insert( const R_TreeLeafEntry& ); /* Inserts the given entry somewhere in the tree. */ bool Remove( const R_TreeLeafEntry& ); /* Deletes the given entry from the tree. Returns ~true~ if entry found and successfully deleted, and ~false~ otherwise. */ bool First( const BBox& bx, R_TreeLeafEntry& result, int replevel = -1 ); bool First( const BBoxSet& searchBoxSet, R_TreeLeafEntry& result ); bool Next( R_TreeLeafEntry& 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& 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& entry, int , int); void BulkLoad(const R_TreeEntry& 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& result); bool IntrospectNext(IntrospectResult& result); bool IntrospectNextE(unsigned long& nodeid, BBox& 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& 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& 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 *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* 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 *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 searchBox; BBoxSet 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 **node); void PutNode( SmiRecord& record, TM_RTreeNode **node ); /* Writes the node ~node~ at file position ~address~. Also deletes the node. */ TM_RTreeNode *GetNode( const SmiRecordId address, const bool leaf, const int min, const int max ); TM_RTreeNode *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& ); /* Inserts given entry in current node. */ void LocateBestNode( const R_TreeEntry& ent, int level ); /* Locates the "best" node of level ~level~ to insert ~ent~. */ bool FindEntry( const R_TreeLeafEntry& 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 *node, const R_TreeEntry& entry); void TM_InsertBulkLoad(TM_RTreeNode *node, const R_TreeEntry& entry, int, int); bool bulkMode; /* true, iff in bulk loading mode */ TM_BulkLoadInfo *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 TM_RTree::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::SizeOfEmptyNode(); int leafEntrySize = sizeof( R_TreeLeafEntry ), internalEntrySize = sizeof( R_TreeInternalEntry ); 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( 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 TM_RTree::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::SizeOfEmptyNode(); int leafEntrySize = sizeof( R_TreeLeafEntry ), internalEntrySize = sizeof( R_TreeInternalEntry ); 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( 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 TM_RTree::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 TM_RTree::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"<= 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 TM_RTree::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"<Open(fileid); // Calculating maxEntries e minEntries int nodeEmptySize = TM_RTreeNode::SizeOfEmptyNode(); int leafEntrySize = sizeof( R_TreeLeafEntry ), internalEntrySize = sizeof( R_TreeInternalEntry ); 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( 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 TM_RTree::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"<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 TM_RTree::~TM_RTree() { // cout<<"~R_Tree()"<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 void TM_RTree::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 void TM_RTree::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 void TM_RTree::PutNode( const SmiRecordId recno, TM_RTreeNode **node ) { assert( file->IsOpen() ); (*node)->Write( file, recno ); delete *node; *node = NULL; } template void TM_RTree::PutNode( SmiRecord& record, TM_RTreeNode **node ) { (*node)->Write( record ); delete *node; *node = NULL; } /* 5.5 Method GetNode: Getting node from disk */ template TM_RTreeNode *TM_RTree::GetNode( const SmiRecordId recno, const bool leaf, const int min, const int max ) { assert( file->IsOpen() ); TM_RTreeNode *node = new TM_RTreeNode( leaf, min, max ); node->Read( file, recno ); return node; } template TM_RTreeNode *TM_RTree::GetNode( SmiRecord& record, const bool leaf, const int min, const int max ) { TM_RTreeNode *node = new TM_RTreeNode( leaf, min, max ); node->Read( record ); return node; } /* return BoundingBox */ template BBox TM_RTree::BoundingBox() { if( currLevel == 0 ) return nodePtr->BoundingBox(); else { TM_RTreeNode *tmp = GetNode( RootRecordId(), 0, MinEntries(currLevel), MaxEntries(currLevel) ); BBox result = tmp->BoundingBox(); delete tmp; return result; } } /* Method Insert */ template void TM_RTree::Insert(const R_TreeLeafEntry& entry) { searchType = NoSearch; LocateBestNode( entry, Height() ); InsertEntry( entry ); header.entryCount++; } /* Method LocateBestNode */ template void TM_RTree::LocateBestNode( const R_TreeEntry& 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 &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::max(), bestoverlap = std::numeric_limits::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 boxBefore = (*nodePtr)[ i ].box; BBox boxAfter = boxBefore.Union( entry.box ); for( j = 0; j < nodePtr->EntryCount(); ++j ) if( j == i ) continue; else { R_TreeEntry &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::max(); int i; for( i = 0; i < nodePtr->EntryCount(); i++ ) { R_TreeEntry &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 void TM_RTree::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 void TM_RTree::DownLevel( int entryNo ) { assert( currLevel != Height() ); assert( nodePtr != 0 ); assert( entryNo >= 0 && entryNo < nodePtr->EntryCount() ); pathEntry[ currLevel ] = entryNo; path[ currLevel+1 ] = ((R_TreeInternalEntry&)(*nodePtr)[ entryNo ]).pointer; PutNode( path[ currLevel ], &nodePtr ); currLevel += 1; nodePtr = GetNode( path[ currLevel ], Height() == currLevel, MinEntries(currLevel), MaxEntries(currLevel) ); } /* InsertEntry */ template void TM_RTree::InsertEntry( const R_TreeEntry& 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 *n1 = new TM_RTreeNode (nodePtr->IsLeaf(), MinEntries(currLevel), MaxEntries(currLevel) ); TM_RTreeNode *n2 = new TM_RTreeNode (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 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(n1Box,node1recno)); assert(EntryInserted); BBox n2Box( n2->BoundingBox() ); SmiRecordId node2recno; SmiRecord node2record; RecordAppended = file->AppendRecord( node2recno, node2record ); assert( RecordAppended ); PutNode( node2record, &n2 ); // deletes n2 EntryInserted = nodePtr->Insert(R_TreeInternalEntry(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 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 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 **tmp = new R_TreeEntry*[ 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 ( (R_TreeLeafEntry&)(*nodePtr)[ i ] ); else tmp[ i ] = new R_TreeInternalEntry ( (R_TreeInternalEntry&)(*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 *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 void TM_RTree::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 void TM_RTree::UpdateBox() { // Save where we were before int formerlevel = currLevel; for( int l = currLevel; l > 0; l-- ) { // Compute bounding box of child BBox 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 bool TM_RTree::FindEntry(const R_TreeLeafEntry& 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&)(*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&)(*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 bool TM_RTree::First( const BBox& box, R_TreeLeafEntry& 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 bool TM_RTree::First( const BBoxSet& boxSet, R_TreeLeafEntry& 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 bool TM_RTree::Next( R_TreeLeafEntry& 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&)(*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 bool TM_RTree::IntrospectFirst( IntrospectResult& result) { // create result for root result = IntrospectResult ( 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 bool TM_RTree::IntrospectNextE(unsigned long& nodeid,BBox& 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 entry = (R_TreeLeafEntry&) (*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 entry = (R_TreeInternalEntry&) (*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 bool TM_RTree::IntrospectNext( IntrospectResult& 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 entry = (R_TreeLeafEntry&) (*nodePtr)[ pathEntry[ currLevel ] ]; result = IntrospectResult ( 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 entry = (R_TreeInternalEntry&) (*nodePtr)[ 0 ]; result = IntrospectResult ( 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 bool TM_RTree::Remove( const R_TreeLeafEntry& 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 reinsertLevelList; std::stack*> reinsertNodeList; BBox 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* nodePtrcopy = new TM_RTreeNode ( *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* 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&)(*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 bool TM_RTree::InitializeBulkLoad(const bool &leafSkipping) { assert( NodeCount() == 1 ); if( bulkMode || bli != NULL ) { return false; } bulkMode = true; bli = new TM_BulkLoadInfo(leafSkipping); return true; }; template void TM_RTree::TM_BulkLoad(const R_TreeEntry& 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(true, header.minLeafEntries, header.maxLeafEntries); bli->nodeCount++; } TM_InsertBulkLoad(bli->node[0], entry, m, last_m); bli->entryCount++; return; } template void TM_RTree::BulkLoad(const R_TreeEntry& 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(true, header.minLeafEntries, header.maxLeafEntries); bli->nodeCount++; } InsertBulkLoad(bli->node[0], entry); bli->entryCount++; return; } template void TM_RTree::InsertBulkLoad(TM_RTreeNode *node, const R_TreeEntry& 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(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( 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(true, header.minLeafEntries, header.maxLeafEntries); } else { // create an internal node bli->node[bli->currentLevel] = new TM_RTreeNode(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 void TM_RTree::TM_InsertBulkLoad( TM_RTreeNode *node, const R_TreeEntry& 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(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( 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(true, header.minLeafEntries, header.maxLeafEntries); } else { // create an internal node bli->node[bli->currentLevel] = new TM_RTreeNode(false, header.minInternalEntries, header.maxInternalEntries); } bli->nodeCount++; // finally, insert the original entry passed as argument bli->node[bli->currentLevel]->Insert(entry); return; }; template SmiRecordId TM_RTree::DFVisit_Rtree( TM_RTree* rtree_in, TM_RTreeNode* 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* new_n = new TM_RTreeNode(node->IsLeaf(),MinEntries(0),MaxEntries(0)); for(int i = 0;i < node->EntryCount();i++){ R_TreeInternalEntry e = (R_TreeInternalEntry&)(*node)[i]; TM_RTreeNode* 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(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 void TM_RTree::Clone(TM_RTree* rtree_in) { /* assert(InitializeBulkLoad()); BBox searchbox(rtree_in->BoundingBox()); R_TreeLeafEntry 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* 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 bool TM_RTree::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 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 bool TM_RTree::getFileStats( SmiStatResultType &result ) { result = file->GetFileStatistics(SMI_STATS_EAGER); std::stringstream fileid; fileid << file->GetFileId(); result.push_back(std::pair("FilePurpose", "SecondaryRtreeIndexFile")); result.push_back(std::pair("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 ListExpr OutTMRTree(ListExpr typeInfo, Word value) { if( nl->BoolValue(nl->Fourth(typeInfo)) == true ) { ListExpr bboxList, appendList; TM_RTree *rtree = (TM_RTree *)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 *rtree = (TM_RTree *)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 Word InTMRTree( ListExpr typeInfo, ListExpr value, int errorPos, ListExpr& errorInfo, bool& correct ) { correct = false; return SetWord(Address(0)); } /* 6.3 ~Create~-function */ template Word CreateTMRTree( const ListExpr typeInfo ) { if( nl->BoolValue(nl->Fourth(typeInfo)) == true ){ return SetWord( new TM_RTree( 4000 ) ); }else{ return SetWord( new TM_RTree( 4000 ) ); } } /* 6.4 ~Close~-function */ template void CloseTMRTree( const ListExpr typeInfo, Word& w ) { if( nl->BoolValue(nl->Fourth(typeInfo)) == true ) { TM_RTree* rtree = (TM_RTree*)w.addr; delete rtree; } else { TM_RTree* rtree = (TM_RTree*)w.addr; delete rtree; } } /* 6.5 ~Clone~-function Not implemented yet. implemented by Jianqiu xu --- 2009.11.30 */ template Word CloneTMRTree( const ListExpr typeInfo, const Word& w ) { /////////////// new implementation //////////////////////////// TM_RTree* rtree = (TM_RTree*)w.addr; TM_RTree* newrtree = new TM_RTree(4000); newrtree->Clone(rtree); return SetWord( newrtree); //////////////// original version //////////////////////////////////////// // return SetWord( Address(0) ); } /* 6.6 ~Delete~-function */ template void DeleteTMRTree( const ListExpr typeInfo, Word& w ) { if (nl->ListLength(typeInfo) == 4) { if( nl->BoolValue(nl->Fourth(typeInfo)) == true ) { TM_RTree* rtree = (TM_RTree*)w.addr; rtree->DeleteFile(); delete rtree; return; } } TM_RTree* rtree = (TM_RTree*)w.addr; rtree->DeleteFile(); delete rtree; } /* 6.7 ~Cast~-function */ template void* CastTMRTree( void* addr) { return ( 0 ); } /* 6.8 ~Open~-function */ template 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 *rtree = new TM_RTree( fileid ); value = SetWord( rtree ); } else { TM_RTree *rtree = new TM_RTree( fileid ); value = SetWord( rtree ); } return true; } /* 6.9 ~Save~-function */ template bool SaveTMRTree( SmiRecord& valueRecord, size_t& offset, const ListExpr typeInfo, Word& value ) { SmiFileId fileId; if( nl->BoolValue(nl->Fourth(typeInfo)) == true ) { TM_RTree *rtree = (TM_RTree *)value.addr; fileId = rtree->FileId(); } else { assert(value.addr); TM_RTree *rtree = (TM_RTree *)value.addr; fileId = rtree->FileId(); } valueRecord.Write( &fileId, sizeof( SmiFileId ), offset ); offset += sizeof( SmiFileId ); return true; } /* 6.10 ~SizeOf~-function */ template int SizeOfTMRTree() { return 0; } template bool TM_RTree::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 (fileId)); return true; }; template bool TM_RTree::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 bool TM_RTree::InitializeBLI(const bool& leafSkipping) { if(bulkMode)cout<<"bulkMode"<(leafSkipping); return true; } /* calculate the transportation mode for each node from bottom to up */ template bool TM_RTree::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 long TM_RTree::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 modebits; std::bitset 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 "< modebits; modebits.reset(); modebits.set(pos, 1);*/ long tm = modebits.to_ulong();////////bit value to an integer // cout<<"leaf node "<SetTMValue(tm); node->Write(file, nodeid); delete node; return tm; }else{ // bitset modebits; std::bitset 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 m_bit(son_tm); std::bitset m_bit(son_tm); // ///////////// union value of each son tm to tm////////////// // /* cout<<"new one "<SetTMValue(tm); node->Write(file, nodeid); delete node; return tm; } } /* after the TMRtree declaration */ template TM_RTreeNode& TM_RTree::Root() // Loads nodeptr with the root node { GotoLevel( 0 ); return *nodePtr; } #endif