Files
secondo/include/MMRTree.h

1850 lines
40 KiB
C
Raw Normal View History

2026-01-23 17:03:45 +08:00
/*
----
This file is part of SECONDO.
Copyright (C) 2008, University in Hagen,
Faculty of Mathematics and Computer Science,
Database Systems for New Applications.
SECONDO is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
SECONDO is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with SECONDO; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
----
//paragraph [1] Title: [{\Large \bf \begin{center}] [\end{center}}]
//paragraph [10] Footnote: [{\footnote{] [}}]
//[TOC] [\tableofcontents]
//[_] [\_]
[1] A Main Memory based R-Tree implementation
[TOC]
*/
#ifndef MYRTREE_H
#define MYRTREE_H
/*
1 Preparations
This section contains __includes__,
__namespaces__, and
__forward declarations__.
*/
#include <string.h>
#include <iostream>
#include <sstream>
#include <map>
#include <set>
#include <vector>
#include <utility>
#include <stack>
#include "Algebras/Rectangle/RectangleAlgebra.h"
namespace mmrtree {
template<unsigned dim, class T> class Node;
/*
2 Declaration of class RTree
*/
template<unsigned dim, class T> class RtreeT{
public:
/*
2.1 Constructor
This constructor creates a new empty RTRee with ~min~ and ~max~ as
parameters for the minimum/ maximum number of entries within a node.
*/
RtreeT(const int min, const int max);
/*
2.2 Destructor
*/
~RtreeT(){
if(root){
root->destroy();
delete root;
root = 0;
}
}
Rectangle<dim> getBBox() const;
/*
2.2 ~insert~
Inserts a box together with an id into the R-Tree.
*/
void insert(const Rectangle<dim>& box, const T& id);
/*
2.3 ~findAll~
Returns all object's ids stored in the tree where the box
intersects ~box~.
*/
void findAll(const Rectangle<dim>& box, std::set<T>& res)const;
void findSimple(const Rectangle<dim>& box,
std::vector<std::pair<Rectangle<dim>,T> >& res)const;
/*
2.4 ~findAllExact~
Returns all object ids stored within the tree whose corresponding
rectangle is AlmostEqual to ~box~
*/
void findAllExact(const Rectangle<dim>& box, std::set<T>& res)const;
void findSimpleExact(const Rectangle<dim>& box,
std::vector<std::pair<Rectangle<dim>,T> >& res)const;
/*
2.4 ~erase~
Erases an entry of ~id~ found at positions intersecting by box.
If the object id is multiple stored, only the first instance will
be removed from the tree. If an object was deleted, the result
will be __true__; otherwise (i.e.
if no object with given id was present at the specified position),
the result will be __false__.
*/
bool erase(const Rectangle<dim>& box, const T& id);
/*
2.4 ~printStats~
Prints some statistical information about this tree to ~o~.
*/
std::ostream& printStats(std::ostream& o)const;
/*
2.5 ~printAsRel~
This function writes the nodes of the tree into ~o~
as a relation in nested list format.
*/
void printAsRel(std::ostream& o)const;
/*
2.6 ~printAsTree~
This function writes the content of the tree into ~o~.
The result can be read from Secondo's Javagui.
*/
void printAsTree(std::ostream& o) const;
/*
~noNodes~
Computes the number of non-object-nodes within the tree.
*/
int noNodes() const;
/*
~noLeaves~
Returns the number of leaves within the tree.
*/
int noLeaves() const;
/*
~noObjects~
Computes the number of stored objects. If an objects is
multiple stored, each instance is count.
*/
int noObjects() const;
/*
~height~
Computes the height of the tree rooted by ~root~.
*/
int height() const;
/*
~usedMem~
Returns the memory used by this structure in bytes
*/
size_t usedMem() const;
/*
~guessSize~
Returns the estimated size required for this tree for a given number of entries.
If the boolean parameter is set to true, the maximum size is returned,
otherwise the average size
*/
size_t guessSize(size_t entries, bool maxSize){
// size of object nodes
size_t sizeON = sizeof(int) + sizeof(int) + sizeof(long) + sizeof(T)
+ sizeof(Rectangle<dim>) + sizeof(void*);
// size of inner nodes
size_t sizeIn = sizeON + sizeof(void*) * max; // sons array
size_t noLeafs = maxSize ? entries/min : entries*2/(min+max);
size_t fanout = maxSize? min : (min+max) / 2;
size_t noI = 0; // number of inner nodes
while(noLeafs > 0){
noI += noLeafs;
noLeafs /= fanout;
}
return entries*sizeON + noI * sizeIn;
}
/*
1.1 An iterator class
This class can be used to traverse the tree without collecting all
results intersecting a given rectangle.
*/
class iterator{
public:
/*
1.1.1 Constructor
This constrfuctor should only be used by the RTreeT class.
*/
iterator(Node<dim,T>* root, const Rectangle<dim>& r) : path(), box(r){
if(root!=0 && root->box.Intersects(r)){
init(root);
}
}
iterator(Node<dim,T>* root): path(), box(false){
if(root!=0){
box = root->box;
init(root);
}
}
/*
Returns the next element from this tree. If no more elements are
avialable, 0 is returned. The caller must not destroy the
pointeri returned.
*/
T const * next() {
while(!path.empty()){
std::pair< Node<dim,T>*, int> t = path.top();
path.pop();
Node<dim,T>* node = t.first;
int pos = t.second;
pos++;
// try to find an intersecting entry
while(pos < node->count){
if(node->sons[pos]->box.Intersects(this->box)){
t.second = pos;
path.push(t);
return &(node->sons[pos]->id);
} else {
pos++;
}
}
computeNext();
}
return 0;
}
/*
Returns an id for the node of the element last retrieved.
*/
uintptr_t getNodeId() {
return path.empty() ? 0 : reinterpret_cast<uintptr_t>(path.top().first);
}
private:
// the current stack
std::stack<std::pair<Node<dim,T>*, int> > path;
// the search rectangle
Rectangle<dim> box;
// initialitzes the stack
void init(Node<dim,T>* root){
if(root->isLeaf()){
std::pair<Node<dim,T>*, int> p(root,-1);
path.push(p);
} else {
std::pair<Node<dim,T>*, int> p(root,-1);
path.push(p);
computeNext();
}
}
/*
~computeNext~
This function searches the next leaf having an entry with a box intersecting
the searchbox.
*/
void computeNext(){
bool found = false;
while(!path.empty() && !found){
// get the topmost entry
std::pair<Node<dim,T>*, int> t = path.top();
path.pop(); // temporary remove topmost element (must be updated)
Node<dim,T>* n = t.first;
int pos = t.second;
// use the next pos
pos++;
bool changed = false;
while(pos<n->count && !changed){
Node<dim,T>* son = n->sons[pos];
if(son->box.Intersects(this->box)){ // try this path
std::pair<Node<dim,T>*,int> p(son,-1);
changed = true;
t.second = pos; // save new state
path.push(t);
path.push(p);
found = son->isLeaf();
} else {
pos++;
}
}
}
}
};
/*
~find~
This function returns an iterator which returns all entries with a
box intersecting ~r~. The caller is responsible to destroy the
iterator after using it. Whenever the tree is changed (inserting
or deleting objects), this iterator is invalid and will crash when
used anyway.
*/
iterator* find(const Rectangle<dim>& r) const{
return new iterator(root, r);
}
/*
~entries~
Returns an iterator traversing all entries of the tree.
*/
iterator* entries() const {
return new iterator(root);
}
RtreeT<dim,T>* clone(){
RtreeT<dim,T>* res = new RtreeT<dim,T>(min,max);
if(root){
res->root = root->clone();
}
return res;
}
/*
2.6 private part
*/
private:
/*
~Data Members~
*/
int min; // minimum number of entries within a node
int max; // maximum number of entries within a node
Node<dim, T>* root; // root node
/*
~getListString~
This function produces a string according to the nested list
representation of a rectangle.
*/
std::string getListString(const Rectangle<dim>& rect) const ;
/*
~printAsRelRec~
Prints the content of the subtree given by root as relation to
~o~.
*/
void printAsRelRec(Node<dim,T>* root, std::ostream& o,
const int level, int& nodeId, const int father) const;
/*
~printAsTreeRec~
Function supporting the public function ~printAsTree~.
*/
void printAsTreeRec(const Node<dim,T>* root, std::ostream& o)const;
/*
~noNodes~
Computes the number of non-object-nodes within the tree.
*/
int noNodes(const Node<dim,T>* root) const;
/*
~noLeaves~
Returns the number of leaves within the tree rooted by ~root~.
*/
int noLeaves(const Node<dim,T>* root) const;
/*
~noObjects~
Computes the number of stored objects. If an object is
multiple stored, each instance is count.
*/
int noObjects(const Node<dim,T>* root) const;
/*
~height~
Computes the height of the tree rooted by ~root~.
*/
int height(const Node<dim,T>* root) const;
/*
~insert~
Inserts a set of subtrees at the specified levels.
This function supports the ~erase~ function.
*/
void insert(const std::set<std::pair < int , Node<dim,T>* > >& Q);
/*
Inserts a node at the specified level. If the tree grows, true is
returned. If a leaf should be inserted, just set level to -1.
*/
bool insertNodeAtLevel(int level, Node<dim,T>* node);
/*
~insertRecAtLevel~
Function supporting the ~insertNodeAtLevel~ function.
*/
std::pair<Node<dim,T>*, Node<dim,T>* >*
insertRecAtLevel(Node<dim,T>*& root, Node<dim,T>* node,
const int targetLevel, const int currentLevel);
/*
~findAllRec~
Searches in the subtree given by root for objects whose
bounding box intersects ~box~ and collect them in ~res~.
*/
void findAllRec(const Node<dim,T>* root,
const Rectangle<dim>& box,
std::set<T>& res)const;
void findSimpleRec(const Node<dim,T>* root,
const Rectangle<dim>& box,
std::vector< std::pair<Rectangle<dim>,T> >& res)const;
/*
~findAllRecExact~
Searches in the subtree given by root for objects whose
bounding box contains ~box~ and collect the object ids
whose corresponding rectangle is equals to ~box~.
*/
void findAllRecExact(const Node<dim,T>* root,
const Rectangle<dim>& box,
std::set<T>& res)const;
void findSimpleRecExact(const Node<dim,T>* root,
const Rectangle<dim>& box,
std::vector<std::pair<Rectangle<dim>,T> >& res)const;
/*
Erases one occurence of id.
If ~root~ is 0, nothing is do. Otherwise, if root is underflowed by
erasing the entry, root is deleted and the remaining subtrees are
inserted into ~Q~. An exception is the root of the tree which is not
removed even if 0 entries left in the root.
*/
bool eraseRec(Node<dim,T>*& root,const Rectangle<dim>& box, const T& id,
std::set<std::pair < int, Node<dim, T>*> >& Q, int level);
/*
~checkTree~
This function checks the complete tree for RTree properties.
- all leaves are on the same level
- all nodes have at most max sons
- all nodes without the root have at least min nodes
- the bounding box a each node is the union of the boxes of its sons
*/
bool checkTree(const bool print = true)const;
bool checkBox(const Node<dim,T>* root, const bool print,const int level)const;
bool checkSonNumber(const Node<dim,T>* root,
const bool print,
const int level) const;
bool checkLeafLevel(const Node<dim,T>* root, const bool print) const;
int getHeight(const Node<dim,T>* root)const;
};
template<unsigned dim>
class Rtree: public RtreeT<dim,long>{
public:
Rtree(const int min, const int max): RtreeT<dim,long>(min,max){}
};
/*
1 Definition of the Class ~Node~
This class represents a single node within an R-tree.
There is no distinction between nodes holding the objects ids,
leafs nodes and inner nodes. All nodes are represented by this
class. This class should only be used within the R-tree class.
For this reason, all members are private and the R-Tree class
is declared as a friend of this class.
*/
template<unsigned dim, class T>
class Node{
friend class RtreeT<dim,T>;
friend class RtreeT<dim,T>::iterator;
private:
/*
1.1 Constructors
This constructor creates a leaf node.
*/
Node(const Rectangle<dim>& abox, const T& _id):
min(-1), max(-1),count(-1),id(_id), box(abox), sons(0){ }
/*
Copy constructor.
*/
Node(const Node& src):min(src.min), max(src.max),
count(src.count), id(src.id), box(src.box){
if(src.sons){
sons = new Node[max+1];
for(int i=0;i<count;i++){
sons[i] = src.sons[i];
}
} else {
sons = 0;
}
}
/*
1.2 The assignment operator.
*/
Node& operator=(const Node& src){
this->min = src.min;
this->max = src.max;
this->count = src.count;
this->id = src.id;
this->box = src.box;
if(src.sons){
sons = new Node[max+1];
for(int i=0;i<count;i++){
sons[i] = src.sons[i];
}
} else {
sons = 0;
}
}
/*
1.3 Destructor
The destructor does not remove any sons of the node. Just the
array managing the sons is removed. If the complete subtree should
be removed, use a combination of destroy and delete.
*/
~Node(){
if(sons){
delete [] sons;
}
}
/*
1.4 getLabel
*/
std::string getLabel() const{
std::stringstream s;
if(max <0){ // a content node
s << "' o: " << id << "'";
} else { // an inner node
s << "' (";
for(unsigned int i=0;i<dim;i++){
if(i!=0){
s << ", ";
}
s << box.MinD(i);
}
s << " ) -> (";
for(unsigned int i=0;i<dim;i++){
if(i!=0){
s << ", ";
}
s << box.MaxD(i);
}
s << ") '";
}
return s.str();
}
/*
1.4 ~destroy~
Deletes the subtrees rooted by this node.
*/
void destroy(){
if(sons){
for(int i=0;i<count; i++){
sons[i]->destroy();
delete sons[i];
}
delete[] sons;
count = 0;
sons = 0;
}
}
/*
~usedMem~
Returns the memory allocated for this subtree;
*/
ssize_t usedMem() const{
if(sons==0){
return sizeof(*this);
}
size_t sum = 0;
for(int i=0;i<count;i++){
sum += sons[i]->usedMem();
}
return sum + sizeof(*this) + sizeof(sons);
}
Node<dim,T>* clone(){
Node<dim,T>* res = new Node<dim,T>(box,id);
res->min = min;
res->max = max;
res->count = count;
if(sons){
res->sons = new Node<dim,T>*[max+1];
for(int i=0;i<count;i++){
res->sons[i] = sons[i]->clone();
}
}
return res;
}
/*
1.4 Data members
*/
int min; // minimum count of entries
int max; // maximum count of entries <0 for "object nodes"
long count; // current count of entries
T id; // content
Rectangle<dim> box; // the bounding box
Node<dim,T>** sons; // array of sons, 0 for object nodes
/*
1.5 Private functions
~Constructor~
This constructor constructs an empty inner node.
*/
Node(int min1, int max1):
min(min1), max(max1), count(0), id(0), box(false) {
sons = new Node<dim,T>*[max+1];
}
/*
~append~
Appends a new entry to this node. If the node overflows,
the result will be false. Not usuable for "object nodes".
*/
bool append(Node* entry){
assert(count <= max);
assert(entry->box.IsDefined());
sons[count] = entry;
if(count == 0){
this->box = entry->box;
} else {
this->box = this->box.Union(entry->box);
}
count++;
return count <= max;
}
/*
~checkBox~
This function is for debugging purposes. It checks whether the
box stored within this node corresponds to the union of all
boxes of all sons.
*/
bool checkBox(bool print = true) const{
if(max<0){ // an object node
bool res = box.IsDefined();
if(print && !res){
cout << "undefined object node" << endl;
}
return res;
}
if(count == 0){
bool res = !box.IsDefined();
if(print && !res){
cout << "box defined but count == 0" << endl;
}
return res;
}
if(count < 0){
if(print){
cout << "count < 0 " << endl;
}
return false;
}
if(!box.IsDefined()){
if(print){
cout << "count >0 but box is undefined" << endl;
}
return false;
}
Rectangle<dim> abox = sons[0]->box;
for(int i=0;i<count;i++){
abox = abox.Union(sons[i]->box);
}
bool res = abox == box;
if(print && !res){
cout << "boxes differ";
cout << "computed box = "; abox.Print(cout); cout << endl;
cout << "stored box = "; box.Print(cout) ; cout << endl;
}
return res;
}
/*
~recomputeBox~
Sets the box to the union of all boxes of the sons.
*/
void recomputeBox() {
if(max<0){ // an object node
return;
}
if(count == 0){
box.SetDefined(false);
} else {
box = sons[0]->box;
for(int i=0;i<count;i++){
box = box.Union(sons[i]->box);
}
}
}
/*
~selectFittestSon~
Returns the index of the son which is the best one for
searching further the leaf for including ~box~.
*/
unsigned int selectFittestSon(const Rectangle<dim>& box)const{
assert(max>0 && count >0);
// initialize best fit index to be 0
double area = sons[0]->box.Area();
Rectangle<dim> b = sons[0]->box;
double extend = b.Union(box).Area() - area;
int index = 0;
for(int i=1;i<count;i++){
double area2 = sons[i]->box.Area();
Rectangle<dim> b2 = sons[i]->box;
double extend2 = b2.Union(box).Area() - area2;
if(extend2 < extend){
extend = extend2;
area = area2;
index = i;
} else if((extend2 == extend) && (area2 < area)){
extend = extend2;
area = area2;
index = i;
} else if((extend2 == extend) && (area2 == area) &&
(sons[i]->count < sons[index]->count)){
extend = extend2;
area = area2;
index = i;
}
}
return index;
}
/*
~pickSeeds~
Returns the indexes for the seeds using quadratic split.
*/
std::pair<unsigned int,unsigned int> pickSeeds() const{
std::pair<int, int> res;
double d = 0;
for(int i=0;i<count;i++){
for(int j=i+1;j<count;j++){
double d2 = (sons[i]->box.Union(sons[j]->box)).Area() -
(sons[i]->box.Area() + sons[j]->box.Area());
if(d2>d){
res.first = i;
res.second = j;
d = d2;
}
}
}
if(d==0){ // all boxes are the same
res.first = 0;
res.second = count-1;
}
return res;
}
/*
~pickNext~
returns the next index for the quadratic split algorithm.
*/
std::pair<unsigned int,unsigned int>
pickNext(const Node* grpone,const Node* grptwo) const{
assert(count>0);
double d1 = sons[0]->box.Union(grpone->box).Area();
double d2 = sons[0]->box.Union(grptwo->box).Area();
unsigned int index = 0;
unsigned int bestgrp = -1;
double d = std::abs(d1-d2);
for(int i=1;i<count;i++){
d1 = sons[i]->box.Union(grpone->box).Area();
d2 = sons[i]->box.Union(grptwo->box).Area();
double d3 = std::abs(d1-d2);
if(d3>d){
d = d3;
index = i;
double a1 = grpone->box.Area();
double a2 = grptwo->box.Area();
d1 = d1 - a1;
d2 = d2 - a2;
if(d1!=d2){
bestgrp = d1<d2?1:2;
} else if(a1!=a2){
bestgrp = a1<a2?1:2;
} else if(grpone->count!=grptwo->count){
bestgrp = grpone->count<grptwo->count? 1:2;
} else { // all criterions failed
bestgrp = 1;
}
}
}
std::pair<unsigned int , unsigned int> res;
res.first = index;
res.second = bestgrp;
return res;
}
/*
~Remove~
Removes the entry at position entry from this node.
If there is an underflow, the result will be false.
*/
bool remove(int index, bool updateBox = false){
for(int i=index;i<count-1;i++){
sons[i] = sons[i+1];
}
sons[count-1] = 0;
count--;
if(updateBox){
if(count==0){
box.SetDefined(false);
} else {
box = sons[0]->box;
for(int i=1;i < count; i++){
box = box.Union(sons[i]->box);
}
}
}
return count >= min;
}
/*
~Split~
Splits this node into two ones. After calling this function, this node will
be empty and all contained elements was distributed to the new nodes.
*/
std::pair<Node*, Node*> split(){
std::pair<Node<dim,T>*, Node<dim,T>* > res = quadraticSplit();
return res;
}
/*
~isLeaf~
This function checks whether this node is a leaf, i.e. whether the sons
are objects nodes.
*/
bool isLeaf() const{
assert(max>0);
if(count==0){
return true;
}
return sons[0]->max < 0;
}
/*
~quadraticSplit~
Implementation of the quadratic split algorithm.
*/
std::pair<Node*, Node*> quadraticSplit(){
std::pair<int, int> seeds = pickSeeds();
int index1 = seeds.first;
int index2 = seeds.second;
Node* node1 = new Node(min,max);
Node* node2 = new Node(min,max);
node1->append(sons[index1]);
node2->append(sons[index2]);
this->remove(std::max(index1,index2));
this->remove(std::min(index1,index2));
while(count > 0){
if(count + node1->count == min){ // all entries to node1
for(int i=0;i<count;i++){
node1->append(sons[i]);
}
count = 0;
}else if(count + node2->count == min){ // all entries to node2
for(int i=0;i<count;i++){
node2->append(sons[i]);
}
count = 0;
} else {
std::pair<unsigned int, unsigned int> next = pickNext(node1,node2);
if(next.second == 1){
node1->append(sons[next.first]);
} else {
node2->append(sons[next.first]);
}
remove(next.first);
}
}
return std::make_pair(node1,node2);
} // end of quadraticsSplit
/*
~Print~
Prints information about this node to ~o~.
*/
void Print( std::ostream& o){
o << "[min = " << min
<< ", max = " << max
<< ", count = " << count
<< ", box = " << box
<< ", sons = " << sons;
}
}; // end of class Node
/*
2 Implementation of the class RTree
2.1 Constructor
This constructor creates a new empty RTRee with ~min~ and ~max~ as
parameters for the minimum/ maximum number of entries within a node.
*/
template<unsigned dim, class T>
RtreeT<dim,T>::RtreeT(const int min, const int max){
assert(max>=2);
assert(min>0);
assert(min<=max/2);
this->min=min;
this->max = max;
root = 0;
}
/*
2.2 ~insert~
Inserts a box together with an id into the R-Tree.
*/
template<unsigned dim, class T>
void RtreeT<dim,T>::insert(const Rectangle<dim>& box, const T& id){
if(!box.IsDefined()){ // ignore undefined rectangles
return;
}
if(!root){
root = new Node<dim,T>(min,max);
}
Node<dim,T>* obj = new Node<dim,T>(box,id);
insertNodeAtLevel(-1,obj);
}
/*
2.3 ~findAll~
Returns all object's ids stored in the tree where the box
intersects ~box~.
*/
template<unsigned dim, class T>
void RtreeT<dim,T>::findAll(const Rectangle<dim>& box, std::set<T>& res)const{
res.clear();
if(box.IsDefined()){
findAllRec(root,box,res);
}
}
template<unsigned dim, class T>
void RtreeT<dim,T>::findSimple(const Rectangle<dim>& box,
std::vector<std::pair<Rectangle<dim>, T> >& res)const{
res.clear();
if(box.IsDefined()){
findSimpleRec(root,box,res);
}
}
/*
2.3 ~findAllExact~
Returns all object's ids stored in the tree where the box
is equals to ~box~.
*/
template<unsigned dim, class T>
void RtreeT<dim,T>::findAllExact(const Rectangle<dim>& box,
std::set<T>& res)const{
res.clear();
if(box.IsDefined()){
findAllRecExact(root,box,res);
}
}
template<unsigned dim, class T>
void RtreeT<dim,T>::findSimpleExact(const Rectangle<dim>& box,
std::vector<std::pair<Rectangle<dim>,T> >& res)const{
res.clear();
if(box.IsDefined()){
findSimpleRecExact(root,box,res);
}
}
/*
2.4 ~erase~
Erases the entries of ~id~ found at positions intersecting by box.
*/
template<unsigned dim, class T>
bool RtreeT<dim,T>::erase(const Rectangle<dim>& box, const T& id){
if(!box.IsDefined()){
return false;
}
std::set<std::pair < int , Node<dim,T>*> > Q;
Q.clear();
if(eraseRec(root,box,id, Q,0)){
if(root->count==0 && root->isLeaf()){ // last entry removed
delete root;
root = 0;
}
if(Q.size() > 0){
insert(Q);
}
if((root!=0) && (root->count == 1) &&(!root->isLeaf())){
// root has only a single entry and can be replaced by
// its son
Node<dim,T>* victim = root;
root = root->sons[0];
delete victim;
}
return true;
} else {
return false;
}
}
/*
2.4 ~printStats~
Prints some statistical information about this tree to ~o~.
*/
template<unsigned dim, class T>
std::ostream& RtreeT<dim,T>::printStats(std::ostream& o)const{
o << "Tree[" << endl
<< " min = " << min << endl
<< " max = " << max << endl
<< " nodes = " << noNodes(root) << endl
<< " leafs = " << noLeaves(root) << endl
<< " height = " << height(root) << endl
<< " objects = " << noObjects(root) << endl
<< " usedMem = " << usedMem() << endl
<< " ] ";
return o;
}
/*
2.5 ~printAsRel~
This function writes the nested list representation of this tree to ~o~.
*/
template<unsigned dim, class T>
void RtreeT<dim,T>::printAsRel(std::ostream& o) const{
o << " ( (rel (tuple ("
<< " ( level int )"
<< " ( box rectangle) "
<< " ( id int )"
<< " ( father int) "
<< " )))"
<< " ( ";
int level = 0;
int nodeId = 0;
int father = -1;
printAsRelRec(root,o,level, nodeId, father);
o << " ))";
}
/*
~getListString~
This function produces a string according to the nested list
representation of a rectangle.
*/
template<unsigned dim, class T>
std::string RtreeT<dim,T>::getListString(const Rectangle<dim>& rect)const{
std::stringstream res;
res << "(" ;
for(unsigned int i=0;i<dim; i++){
res << " " << rect.MinD(i) << " " << rect.MaxD(i);
}
res << ")";
return res.str();
}
/*
~printAsRelRec~
Prints the content of the subtree given by root as relation to o.
*/
template<unsigned dim, class T>
void RtreeT<dim,T>::printAsRelRec(Node<dim,T>* root, std::ostream& o,
const int level, int& nodeId, const int father) const{
if(!root){
return;
} else if (root->max<0){
o << "("
<< level << " "
<< getListString(root->box) << " "
<< nodeId++ << " "
<< father
<< " )" << endl;
} else {
int myId = nodeId;
nodeId++;
o << "("
<< level << " "
<< getListString(root->box) << " "
<< myId << " "
<< father
<< " )" << endl;
for(int i=0;i<root->count; i++){
printAsRelRec( root->sons[i],o, level+1, nodeId, myId);
}
}
}
/*
~printAsTree~
*/
template<unsigned dim, class T>
void RtreeT<dim,T>::printAsTree(std::ostream& o) const {
o << "( tree " << endl;
printAsTreeRec(root,o);
o << ")";
}
template<unsigned dim, class T>
void RtreeT<dim, T>::printAsTreeRec(const Node<dim,T>* root,
std::ostream& o)const{
if(!root){
o << "()" << endl;
} else {
o << "("
<< root->getLabel() << " " ;
if(root->max >0){ // not an object node
o << "(";
for(int i=0;i<root->count ; i++){
printAsTreeRec(root->sons[i],o);
}
o << ")";
} else {
o << "()" ;
}
o << ")" << endl;
}
}
/*
~noNodes~
Computes the number of non-object-nodes within the tree.
*/
template<unsigned dim, class T>
int RtreeT<dim,T>::noNodes()const{
return noNodes(root);
}
template<unsigned dim, class T>
int RtreeT<dim,T>::noNodes(const Node<dim,T>* root) const{
if(!root){
return 0;
}
if(root->isLeaf()){
return 1;
}else {
int sum = 1;
for(int i=0;i<root->count;i++){
sum += noNodes(root->sons[i]);
}
return sum;
}
}
/*
~noLeaves~
Returns the number of leaves within the tree.
*/
template<unsigned dim, class T>
int RtreeT<dim,T>::noLeaves()const {
return noLeaves(root);
}
template<unsigned dim, class T>
int RtreeT<dim,T>::noLeaves(const Node<dim,T>* root)const{
if(!root){
return 0;
}
if(root->isLeaf()){
return 1;
}else {
int sum = 0;
for(int i=0;i<root->count;i++){
sum += noLeaves(root->sons[i]);
}
return sum;
}
}
/*
~noObjects~
Computes the number of stored objects. If an object is
multiple stored, each instance is count.
*/
template<unsigned dim, class T>
int RtreeT<dim,T>::noObjects()const{
return noObjects(root);
}
template<unsigned dim, class T>
int RtreeT<dim,T>::noObjects(const Node<dim,T>* root)const{
if(!root){
return 0;
}
if(root->isLeaf()){
return root->count;
}else {
int sum = 0;
for(int i=0;i<root->count;i++){
sum += noObjects(root->sons[i]);
}
return sum;
}
}
/*
~height~
Computes the height of the tree.
*/
template<unsigned dim, class T>
int RtreeT<dim,T>::height() const{
return height(root);
}
template<unsigned dim,class T>
int RtreeT<dim,T>::height(const Node<dim,T>* root) const{
if(!root){
return -1;
}
int h = 0;
const Node<dim,T>* node = root;
while(!node->isLeaf()){
h++;
node = node->sons[0];
}
return h;
}
/*
~usedMem~
*/
template<unsigned dim, class T>
size_t RtreeT<dim,T>::usedMem() const{
if(!root){
return sizeof(*this);
} else {
return sizeof(*this) + root->usedMem();
}
}
/*
~insert~
Inserts a set of subtrees at the specified levels. This function supports the
~erase~ function.
*/
template<unsigned dim, class T>
void RtreeT<dim,T>::insert(
const std::set<std::pair < int , Node<dim,T>* > >& Q){
int levelDiff = 0; // store the grow of the tree
typename std::set<std::pair < int , Node<dim,T>*> >::iterator it;
for(it = Q.begin(); it!=Q.end(); it++){
std::pair<int, Node<dim,T>*> node = *it;
int level = node.first<0?node.first:node.first+levelDiff;
if(insertNodeAtLevel(level, node.second)){
levelDiff++;
}
}
}
/*
Inserts a node at the specified level. If the tree grows, true is
returned.
*/
template<unsigned dim, class T>
bool RtreeT<dim,T>::insertNodeAtLevel(int level, Node<dim,T>* node){
if(!root){
root = new Node<dim,T>(min,max);
}
std::pair<Node<dim,T>*, Node<dim,T>* >* res =
insertRecAtLevel(root,node, level,0);
if(!res){ // tree does not grow
return false;
} else {
delete root;
root = new Node<dim,T>(min,max);
root->append(res->first);
root->append(res->second);
delete res;
return true;
}
}
template<unsigned dim, class T>
std::pair<Node<dim,T>*, Node<dim,T>* >*
RtreeT<dim,T>::insertRecAtLevel(Node<dim,T>*& root, Node<dim,T>* node,
const int targetLevel, const int currentLevel){
if(root->isLeaf() || (targetLevel == currentLevel) ){
if(root->append(node)){ // no overflow
return 0;
} else { // overflow
std::pair<Node<dim,T>*, Node<dim,T>*> res = root->split();
delete root;
root = 0;
return new std::pair<Node<dim,T>*, Node<dim,T>*>(res);
}
} else { // not the target node
int index = root->selectFittestSon(node->box);
std::pair<Node<dim,T>*, Node<dim,T>*>* res;
res = insertRecAtLevel(root->sons[index], node,
targetLevel, currentLevel+1);
if(!res){ // son was not split
root->box = root->box.Union(node->box);
return 0;
}else {
root->sons[index] = res->first; // replace old son by a split node
root->box = root->box.Union(res->first->box);
if(root->append(res->second)){ // no overflow
delete res;
return 0;
} else {
delete res;
res = new std::pair<Node<dim,T>*, Node<dim,T>*>(root->split());
delete root;
root = 0;
return res;
}
}
}
}
/*
~findAllRec~
Searches in the subtree given by root for objects whose
bounding box intersects ~box~ and collect them in ~res~.
*/
template<unsigned dim, class T>
void RtreeT<dim,T>::findAllRec(const Node<dim,T>* root,
const Rectangle<dim>& box,
std::set<T>& res)const{
if(!root){
return;
} else if(root->isLeaf()){
for(int i = 0; i<root->count; i++){
if(root->sons[i]->box.Intersects(box)){
res.insert(root->sons[i]->id);
}
}
} else {
for(int i =0; i < root->count; i++){
if(root->sons[i]->box.Intersects(box)){
findAllRec(root->sons[i],box,res);
}
}
}
}
template<unsigned dim, class T>
void RtreeT<dim,T>::findSimpleRec(const Node<dim,T>* root,
const Rectangle<dim>& box,
std::vector<std::pair<Rectangle<dim>,T> >& res)const{
if(!root){
return;
} else if(root->isLeaf()){
for(int i = 0; i<root->count; i++){
if(root->sons[i]->box.Intersects(box)){
std::pair<Rectangle<dim>,T> p(root->sons[i]->box, root->sons[i]->id);
res.push_back(p);
}
}
} else {
for(int i =0; i < root->count; i++){
if(root->sons[i]->box.Intersects(box)){
findSimpleRec(root->sons[i],box,res);
}
}
}
}
/*
~findAllRecExact~
Searches in the subtree given by root for objects whose
bounding box is equals to ~box~ and collect them in ~res~.
*/
template<unsigned dim, class T>
void RtreeT<dim,T>::findAllRecExact(const Node<dim,T>* root,
const Rectangle<dim>& box,
std::set<T>& res)const{
if(!root){
return;
} else if(root->isLeaf()){
for(int i = 0; i<root->count; i++){
if(root->sons[i]->box.AlmostEqual(box)){
res.insert(root->sons[i]->id);
}
}
} else {
for(int i =0; i < root->count; i++){
if(root->sons[i]->box.Contains(box)){
findAllRec(root->sons[i],box,res);
}
}
}
}
template<unsigned dim, class T>
void RtreeT<dim,T>::findSimpleRecExact(const Node<dim,T>* root,
const Rectangle<dim>& box,
std::vector<std::pair<Rectangle<dim>,T> >& res)const{
if(!root){
return;
} else if(root->isLeaf()){
for(int i = 0; i<root->count; i++){
if(root->sons[i]->box.AlmostEqual(box)){
std::pair<Rectangle<dim>,T> p(root->sons[i]->box,root->sons[i]->id);
res.push_back(p);
}
}
} else {
for(int i =0; i < root->count; i++){
if(root->sons[i]->box.Contains(box)){
findSimpleRec(root->sons[i],box,res);
}
}
}
}
/*
Erases one occurence of ~id~.
If ~root~ is 0, nothing is do. Otherwise, if root is underflowed by
erasing the entry, root is deleted and the remaining subtrees are
inserted into Q. An exception is the root of the tree which is not
removed even if 0 entries left in the root.
*/
template<unsigned dim, class T>
bool RtreeT<dim,T>::eraseRec(Node<dim,T>*& root,
const Rectangle<dim>& box,
const T& id,
std::set<std::pair < int, Node<dim,T>*> >& Q,
int level){
if(!root){ // tree is empty, doe nothing
return false;
} else if(root->isLeaf()){
// try find the object node having the corresponding id
int index = -1;
for(int i=0;i<root->count && index < 0;i++){
if(root->sons[i]->id == id){
index = i;
}
}
if(index < 0){ // id not found within this node
return false;
}
Node<dim,T>* victim = root->sons[index];
bool under = !root->remove(index,true);
delete victim;
if( under && (root != this->root)){ // an underflow
// insert all remaining leaves into Q
for(int i=0;i<root->count; i++){
Q.insert(std::make_pair(-1, root->sons[i]));
}
delete root; // delete the underflowed node
root = 0;
}
return true; // deletion successful
} else { // root is an inner node
int index = -1;
for(int i=0;i<root->count && index <0 ; i++){
if(root->sons[i]->box.Intersects(box)){
if(eraseRec(root->sons[i], box, id, Q, level + 1)){
index = i;
}
}
}
if(index < 0){ // id not found in this subtree
return false;
}
if(root->sons[index]){ // no underflow
root->recomputeBox();
return true;
}
bool under = !root->remove(index,true);
if( !under || (root == this->root)){ // no underflow
return true;
}
// an underflow in root because deletion of son[index]
// insert remaining subtrees into Q
for(int i=0;i<root->count;i++){
Q.insert(std::make_pair(level, root->sons[i]));
}
delete root;
root = 0;
return true;
}
}
/*
~checkTree~
This function checks the complete tree for RTree properties.
- all leaves are on the same level
- all nodes have at most max sons
- all nodes without the root have at least min nodes
- the bounding box a each node is the union of the boxes of its sons
*/
template<unsigned dim, class T>
bool RtreeT<dim,T>::checkTree(const bool print/* = true*/)const{
return checkLeafLevel(root, print) &&
checkSonNumber (root,print,0 ) &&
checkBox(root,print,0);
}
template<unsigned dim, class T>
bool RtreeT<dim,T>::checkBox(const Node<dim,T>* root,
const bool print,
const int level)const{
if(!root) {
return true;
} else {
if(!root->checkBox(print)){
if(print){
cout << "Wrong boxes at level " << level << endl;
cout << "There are " << root->count << "entries" << endl;
}
return false;
}
if(!root->isLeaf()){
bool wrong = true;
for(int i=0;i<root->count && wrong;i++){
wrong = checkBox(root->sons[i],print,level+1);
}
return wrong;
}
return true;
}
}
template<unsigned dim, class T>
bool RtreeT<dim,T>::checkSonNumber(const Node<dim,T>* root,
const bool print,
const int level) const{
if(!root){ // empty tree all ok
return true;
} else {
if(root->count > max){ // too much entries
if(print){
cout << "Node with more than " << max << " entries found " << endl;
cout << " problem at level " << level << endl;
}
return false;
}
if((level>0) ){ // not the root node
if((root->count) < (this->min) ){ // too less entries
if(print){
cout << "Node with less than " << min << " entries found " << endl;
cout << "Problem at Level " << level << endl;
}
return false;
}
}
if(!root->isLeaf()){
bool ok = true;
for(int i=0;i<root->count && ok; i++){
ok = checkSonNumber(root->sons[i],print,level + 1);
}
return ok;
}
return true;
}
}
template<unsigned dim, class T>
bool RtreeT<dim,T>::checkLeafLevel(const Node<dim,T>* root,
const bool print) const{
if(!root) { // empty tree
return true;
} else if(root->isLeaf()){ // a leave has correct height
return true;
} else { // not a leaf
int h = height(root->sons[0]);
bool ok = true;
for(int i=1; i<root->count && ok ; i++){
ok = h == height(root->sons[i]);
}
for(int i=0;i<root->count && ok; i++){
ok = checkLeafLevel(root->sons[i],print);
}
if(!ok && print){
cout << "Leaves on different levels found " << endl;
}
return ok;
}
}
template<unsigned dim, class T>
Rectangle<dim> RtreeT<dim,T>::getBBox() const{
if(root) return root->box;
return Rectangle<dim>(false);
}
} // end of namespace
#endif