|
@@ -5,178 +5,331 @@
|
|
* AUTHOR(S): Antonin Guttman - original code
|
|
* AUTHOR(S): Antonin Guttman - original code
|
|
* Daniel Green (green@superliminal.com) - major clean-up
|
|
* Daniel Green (green@superliminal.com) - major clean-up
|
|
* and implementation of bounding spheres
|
|
* and implementation of bounding spheres
|
|
|
|
+* Markus Metz - R*-tree
|
|
*
|
|
*
|
|
* PURPOSE: Multidimensional index
|
|
* PURPOSE: Multidimensional index
|
|
*
|
|
*
|
|
-* COPYRIGHT: (C) 2001 by the GRASS Development Team
|
|
|
|
|
|
+* COPYRIGHT: (C) 2009 by the GRASS Development Team
|
|
*
|
|
*
|
|
* This program is free software under the GNU General Public
|
|
* This program is free software under the GNU General Public
|
|
* License (>=v2). Read the file COPYING that comes with GRASS
|
|
* License (>=v2). Read the file COPYING that comes with GRASS
|
|
* for details.
|
|
* for details.
|
|
*****************************************************************************/
|
|
*****************************************************************************/
|
|
|
|
|
|
-#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <stdlib.h>
|
|
-#include "assert.h"
|
|
|
|
|
|
+#include <assert.h>
|
|
#include "index.h"
|
|
#include "index.h"
|
|
#include "card.h"
|
|
#include "card.h"
|
|
|
|
|
|
-/* Make a new index, empty. Consists of a single node. */
|
|
|
|
-struct Node *RTreeNewIndex(void)
|
|
|
|
|
|
+/* stack used for non-recursive insertion/deletion */
|
|
|
|
+struct stack
|
|
{
|
|
{
|
|
- struct Node *x;
|
|
|
|
|
|
+ struct Node *sn; /* stack node */
|
|
|
|
+ int branch_id; /* branch no to follow down */
|
|
|
|
+};
|
|
|
|
|
|
- x = RTreeNewNode();
|
|
|
|
- x->level = 0; /* leaf */
|
|
|
|
- return x;
|
|
|
|
|
|
+/*
|
|
|
|
+ * Make a new index, empty.
|
|
|
|
+ * ndims number of dimensions
|
|
|
|
+ * returns pointer to RTree structure
|
|
|
|
+ */
|
|
|
|
+struct RTree *RTreeNewIndex(int ndims)
|
|
|
|
+{
|
|
|
|
+ struct RTree *new_rtree;
|
|
|
|
+ struct Node *n;
|
|
|
|
+
|
|
|
|
+ new_rtree = (struct RTree *)malloc(sizeof(struct RTree));
|
|
|
|
+
|
|
|
|
+ new_rtree->ndims = ndims;
|
|
|
|
+ new_rtree->nsides = 2 * ndims;
|
|
|
|
+
|
|
|
|
+ new_rtree->nodesize = sizeof(struct Node);
|
|
|
|
+ new_rtree->branchsize = sizeof(struct Branch);
|
|
|
|
+ new_rtree->rectsize = sizeof(struct Rect);
|
|
|
|
+
|
|
|
|
+ /* nodecard and leafcard can be adjusted, must NOT be larger than MAXCARD */
|
|
|
|
+ new_rtree->nodecard = MAXCARD;
|
|
|
|
+ new_rtree->leafcard = MAXCARD;
|
|
|
|
+ /* NOTE: min fill can be changed if needed. */
|
|
|
|
+ new_rtree->min_node_fill = (new_rtree->nodecard - 1) / 2;
|
|
|
|
+ new_rtree->min_leaf_fill = (new_rtree->leafcard - 1) / 2;
|
|
|
|
+
|
|
|
|
+ n = RTreeNewNode(new_rtree);
|
|
|
|
+ new_rtree->n_levels = n->level = 0; /* leaf */
|
|
|
|
+ new_rtree->root = n;
|
|
|
|
+
|
|
|
|
+ new_rtree->n_nodes = 1;
|
|
|
|
+ new_rtree->n_leafs = 0;
|
|
|
|
+
|
|
|
|
+ return new_rtree;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+void RTreeFreeIndex(struct RTree *t)
|
|
|
|
+{
|
|
|
|
+ assert(t);
|
|
|
|
+
|
|
|
|
+ if (t->root)
|
|
|
|
+ RTreeDestroyNode(t->root, t->nodecard);
|
|
|
|
+
|
|
|
|
+ free(t);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
- * Search in an index tree or subtree for all data retangles that
|
|
|
|
|
|
+ * Search in an index tree for all data retangles that
|
|
* overlap the argument rectangle.
|
|
* overlap the argument rectangle.
|
|
* Return the number of qualifying data rects.
|
|
* Return the number of qualifying data rects.
|
|
*/
|
|
*/
|
|
-int RTreeSearch(struct Node *N, struct Rect *R, SearchHitCallback shcb,
|
|
|
|
|
|
+int RTreeSearch(struct RTree *t, struct Rect *r, SearchHitCallback shcb,
|
|
void *cbarg)
|
|
void *cbarg)
|
|
{
|
|
{
|
|
- register struct Node *n = N;
|
|
|
|
- register struct Rect *r = R; /* NOTE: Suspected bug was R sent in as Node* and cast to Rect* here. */
|
|
|
|
-
|
|
|
|
- /* Fix not yet tested. */
|
|
|
|
- register int hitCount = 0;
|
|
|
|
- register int i;
|
|
|
|
|
|
+ struct Node *n;
|
|
|
|
+ int hitCount = 0, found;
|
|
|
|
+ int i;
|
|
|
|
+ struct stack s[MAXLEVEL];
|
|
|
|
+ int top = 0;
|
|
|
|
|
|
- assert(n);
|
|
|
|
- assert(n->level >= 0);
|
|
|
|
assert(r);
|
|
assert(r);
|
|
-
|
|
|
|
- if (n->level > 0) { /* this is an internal node in the tree */
|
|
|
|
- for (i = 0; i < NODECARD; i++)
|
|
|
|
- if (n->branch[i].child && RTreeOverlap(r, &n->branch[i].rect)) {
|
|
|
|
- hitCount += RTreeSearch(n->branch[i].child, r, shcb, cbarg);
|
|
|
|
|
|
+ assert(t);
|
|
|
|
+
|
|
|
|
+ /* stack size of t->n_levels + 1 is enough because of depth first search */
|
|
|
|
+ /* only one node per level on stack at any given time */
|
|
|
|
+
|
|
|
|
+ /* add root node position to stack */
|
|
|
|
+ s[top].sn = t->root;
|
|
|
|
+ s[top].branch_id = i = 0;
|
|
|
|
+ n = s[top].sn;
|
|
|
|
+
|
|
|
|
+ while (top >= 0) {
|
|
|
|
+ n = s[top].sn;
|
|
|
|
+ if (s[top].sn->level > 0) { /* this is an internal node in the tree */
|
|
|
|
+ found = 1;
|
|
|
|
+ for (i = s[top].branch_id; i < t->nodecard; i++) {
|
|
|
|
+ if (s[top].sn->branch[i].child &&
|
|
|
|
+ RTreeOverlap(r, &(s[top].sn->branch[i].rect), t)) {
|
|
|
|
+ s[top++].branch_id = i + 1;
|
|
|
|
+ /* add next node to stack */
|
|
|
|
+ s[top].sn = n->branch[i].child;
|
|
|
|
+ s[top].branch_id = 0;
|
|
|
|
+ found = 0;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- }
|
|
|
|
- else { /* this is a leaf node */
|
|
|
|
-
|
|
|
|
- for (i = 0; i < LEAFCARD; i++)
|
|
|
|
- if (n->branch[i].child && RTreeOverlap(r, &n->branch[i].rect)) {
|
|
|
|
- hitCount++;
|
|
|
|
- if (shcb) /* call the user-provided callback */
|
|
|
|
- if (!shcb((int)n->branch[i].child, cbarg))
|
|
|
|
- return hitCount; /* callback wants to terminate search early */
|
|
|
|
|
|
+ if (found) {
|
|
|
|
+ /* nothing else found, go back up */
|
|
|
|
+ s[top].branch_id = t->nodecard;
|
|
|
|
+ top--;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ else { /* this is a leaf node */
|
|
|
|
+ for (i = 0; i < t->leafcard; i++) {
|
|
|
|
+ if (s[top].sn->branch[i].child &&
|
|
|
|
+ RTreeOverlap(r, &(s[top].sn->branch[i].rect), t)) {
|
|
|
|
+ hitCount++;
|
|
|
|
+ if (shcb) { /* call the user-provided callback */
|
|
|
|
+ if (!shcb((int)s[top].sn->branch[i].child, cbarg)) {
|
|
|
|
+ /* callback wants to terminate search early */
|
|
|
|
+ return hitCount;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
+ top--;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
+
|
|
return hitCount;
|
|
return hitCount;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Free ListBranch
|
|
|
|
+ */
|
|
|
|
+static void RTreeFreeListBranch(struct ListBranch *p)
|
|
|
|
+{
|
|
|
|
+ free(p);
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Inserts a new data rectangle into the index structure.
|
|
* Inserts a new data rectangle into the index structure.
|
|
- * Recursively descends tree, propagates splits back up.
|
|
|
|
- * Returns 0 if node was not split. Old node updated.
|
|
|
|
- * If node was split, returns 1 and sets the pointer pointed to by
|
|
|
|
- * new_node to point to the new node. Old node updated to become one of two.
|
|
|
|
|
|
+ * Non-recursively descends tree.
|
|
|
|
+ * Returns 0 if node was not split and nothing was removed.
|
|
|
|
+ * Returns 1 if root node was split. Old node updated to become one of two.
|
|
|
|
+ * Returns 2 if branches need to be reinserted.
|
|
* The level argument specifies the number of steps up from the leaf
|
|
* The level argument specifies the number of steps up from the leaf
|
|
* level to insert; e.g. a data rectangle goes in at level = 0.
|
|
* level to insert; e.g. a data rectangle goes in at level = 0.
|
|
*/
|
|
*/
|
|
-static int RTreeInsertRect2(struct Rect *r,
|
|
|
|
- struct Node *child, struct Node *n, struct Node **new_node,
|
|
|
|
- int level)
|
|
|
|
|
|
+static int RTreeInsertRect2(struct Rect *r, struct Node *child, int level,
|
|
|
|
+ struct Node **newnode, struct RTree *t,
|
|
|
|
+ struct ListBranch **ee, int *overflow)
|
|
{
|
|
{
|
|
- /*
|
|
|
|
- register struct Rect *r = R;
|
|
|
|
- register int tid = Tid;
|
|
|
|
- register struct Node *n = N, **new_node = New_node;
|
|
|
|
- register int level = Level;
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- register int i;
|
|
|
|
|
|
+ int i;
|
|
struct Branch b;
|
|
struct Branch b;
|
|
- struct Node *n2;
|
|
|
|
-
|
|
|
|
- assert(r && n && new_node);
|
|
|
|
- assert(level >= 0 && level <= n->level);
|
|
|
|
-
|
|
|
|
- /* Still above level for insertion, go down tree recursively */
|
|
|
|
- if (n->level > level) {
|
|
|
|
- i = RTreePickBranch(r, n);
|
|
|
|
- if (!RTreeInsertRect2(r, child, n->branch[i].child, &n2, level)) {
|
|
|
|
- /* child was not split */
|
|
|
|
- n->branch[i].rect = RTreeCombineRect(r, &(n->branch[i].rect));
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
- else { /* child was split */
|
|
|
|
|
|
+ struct Node *n, *n2;
|
|
|
|
+ struct Rect *cover;
|
|
|
|
+ struct stack s[MAXLEVEL];
|
|
|
|
+ int top = 0, down = 0;
|
|
|
|
+ int result;
|
|
|
|
|
|
- n->branch[i].rect = RTreeNodeCover(n->branch[i].child);
|
|
|
|
- b.child = n2;
|
|
|
|
- b.rect = RTreeNodeCover(n2);
|
|
|
|
- return RTreeAddBranch(&b, n, new_node);
|
|
|
|
- }
|
|
|
|
|
|
+ assert(r && newnode && t);
|
|
|
|
+
|
|
|
|
+ /* add root node to stack */
|
|
|
|
+ s[top].sn = t->root;
|
|
|
|
+
|
|
|
|
+ /* go down to level of insertion */
|
|
|
|
+ while (s[top].sn->level > level) {
|
|
|
|
+ n = s[top].sn;
|
|
|
|
+ i = RTreePickBranch(r, n, t);
|
|
|
|
+ s[top++].branch_id = i;
|
|
|
|
+ /* add next node to stack */
|
|
|
|
+ s[top].sn = n->branch[i].child;
|
|
}
|
|
}
|
|
|
|
|
|
- /* Have reached level for insertion. Add rect, split if necessary */
|
|
|
|
- else if (n->level == level) {
|
|
|
|
|
|
+ /* Have reached level for insertion. Remove p rectangles or split */
|
|
|
|
+ if (s[top].sn->level == level) {
|
|
b.rect = *r;
|
|
b.rect = *r;
|
|
- b.child = child;
|
|
|
|
/* child field of leaves contains tid of data record */
|
|
/* child field of leaves contains tid of data record */
|
|
- return RTreeAddBranch(&b, n, new_node);
|
|
|
|
|
|
+ b.child = child;
|
|
|
|
+ /* add branch, may split node or remove branches */
|
|
|
|
+ cover = &(s[top - 1].sn->branch[s[top - 1].branch_id].rect);
|
|
|
|
+ result = RTreeAddBranch(&b, s[top].sn, &n2, ee, cover, overflow, t);
|
|
|
|
+ /* update node count */
|
|
|
|
+ if (result == 1) {
|
|
|
|
+ t->n_nodes++;
|
|
|
|
+ }
|
|
}
|
|
}
|
|
else {
|
|
else {
|
|
/* Not supposed to happen */
|
|
/* Not supposed to happen */
|
|
assert(FALSE);
|
|
assert(FALSE);
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /* go back up */
|
|
|
|
+ while (top) {
|
|
|
|
+ down = top--;
|
|
|
|
+ i = s[top].branch_id;
|
|
|
|
+ if (result == 0) { /* branch was added */
|
|
|
|
+ s[top].sn->branch[i].rect =
|
|
|
|
+ RTreeCombineRect(r, &(s[top].sn->branch[i].rect), t);
|
|
|
|
+ }
|
|
|
|
+ else if (result == 2) { /* branches were removed */
|
|
|
|
+ /* get node cover of previous node */
|
|
|
|
+ s[top].sn->branch[i].rect = RTreeNodeCover(s[down].sn, t);
|
|
|
|
+ }
|
|
|
|
+ else if (result == 1) { /* node was split */
|
|
|
|
+ /* get node cover of previous node */
|
|
|
|
+ s[top].sn->branch[i].rect = RTreeNodeCover(s[down].sn, t);
|
|
|
|
+ /* add new branch for new node previously added by RTreeAddBranch() */
|
|
|
|
+ b.child = n2;
|
|
|
|
+ b.rect = RTreeNodeCover(b.child, t);
|
|
|
|
+
|
|
|
|
+ /* add branch, may split node or remove branches */
|
|
|
|
+ cover = &(s[top - 1].sn->branch[s[top - 1].branch_id].rect);
|
|
|
|
+ result =
|
|
|
|
+ RTreeAddBranch(&b, s[top].sn, &n2, ee, cover, overflow, t);
|
|
|
|
+
|
|
|
|
+ /* update node count */
|
|
|
|
+ if (result == 1) {
|
|
|
|
+ t->n_nodes++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ *newnode = n2;
|
|
|
|
+
|
|
|
|
+ return result;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
* Insert a data rectangle into an index structure.
|
|
* Insert a data rectangle into an index structure.
|
|
- * RTreeInsertRect provides for splitting the root;
|
|
|
|
|
|
+ * RTreeInsertRect1 provides for splitting the root;
|
|
* returns 1 if root was split, 0 if it was not.
|
|
* returns 1 if root was split, 0 if it was not.
|
|
* The level argument specifies the number of steps up from the leaf
|
|
* The level argument specifies the number of steps up from the leaf
|
|
* level to insert; e.g. a data rectangle goes in at level = 0.
|
|
* level to insert; e.g. a data rectangle goes in at level = 0.
|
|
- * RTreeInsertRect2 does the recursion.
|
|
|
|
|
|
+ * RTreeInsertRect2 does the actual insertion.
|
|
*/
|
|
*/
|
|
-int RTreeInsertRect(struct Rect *R, int Tid, struct Node **Root, int Level)
|
|
|
|
|
|
+static int RTreeInsertRect1(struct Rect *r, struct Node *child, int level,
|
|
|
|
+ struct RTree *t)
|
|
{
|
|
{
|
|
- assert(Level == 0);
|
|
|
|
- return RTreeInsertRect1(R, (struct Node *) Tid, Root, Level);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-int RTreeInsertRect1(struct Rect *R, struct Node *Child, struct Node **Root, int Level)
|
|
|
|
-{
|
|
|
|
- register struct Rect *r = R;
|
|
|
|
- register struct Node *child = Child;
|
|
|
|
- register struct Node **root = Root;
|
|
|
|
- register int level = Level;
|
|
|
|
- register int i;
|
|
|
|
- register struct Node *newroot;
|
|
|
|
struct Node *newnode;
|
|
struct Node *newnode;
|
|
|
|
+ struct Node *newroot;
|
|
struct Branch b;
|
|
struct Branch b;
|
|
|
|
+ struct ListBranch *reInsertList = NULL;
|
|
|
|
+ struct ListBranch *e;
|
|
int result;
|
|
int result;
|
|
-
|
|
|
|
- assert(r && root);
|
|
|
|
- assert(level >= 0 && level <= (*root)->level);
|
|
|
|
- for (i = 0; i < NUMDIMS; i++) {
|
|
|
|
- assert(r->boundary[i] <= r->boundary[NUMDIMS + i]);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (RTreeInsertRect2(r, child, *root, &newnode, level)) { /* root split */
|
|
|
|
- newroot = RTreeNewNode(); /* grow a new root, & tree taller */
|
|
|
|
- newroot->level = (*root)->level + 1;
|
|
|
|
- b.rect = RTreeNodeCover(*root);
|
|
|
|
- b.child = *root;
|
|
|
|
- RTreeAddBranch(&b, newroot, NULL);
|
|
|
|
- b.rect = RTreeNodeCover(newnode);
|
|
|
|
|
|
+ int i, overflow[50];
|
|
|
|
+
|
|
|
|
+ /* R*-tree forced reinsertion: for each level only once */
|
|
|
|
+ for (i = 0; i < 50; i++)
|
|
|
|
+ overflow[i] = 1;
|
|
|
|
+
|
|
|
|
+ result =
|
|
|
|
+ RTreeInsertRect2(r, child, level, &newnode, t, &reInsertList,
|
|
|
|
+ overflow);
|
|
|
|
+ if (result == 1) { /* root split */
|
|
|
|
+ /* grow a new root, & tree taller */
|
|
|
|
+ newroot = RTreeNewNode(t);
|
|
|
|
+ newroot->level = ++t->n_levels;
|
|
|
|
+ /* branch for old root */
|
|
|
|
+ b.rect = RTreeNodeCover(t->root, t);
|
|
|
|
+ b.child = t->root;
|
|
|
|
+ RTreeAddBranch(&b, newroot, NULL, NULL, NULL, NULL, t);
|
|
|
|
+ /* branch for new node created by RTreeInsertRect2() */
|
|
|
|
+ b.rect = RTreeNodeCover(newnode, t);
|
|
b.child = newnode;
|
|
b.child = newnode;
|
|
- RTreeAddBranch(&b, newroot, NULL);
|
|
|
|
- *root = newroot;
|
|
|
|
- result = 1;
|
|
|
|
|
|
+ RTreeAddBranch(&b, newroot, NULL, NULL, NULL, NULL, t);
|
|
|
|
+ /* set new root node */
|
|
|
|
+ t->root = newroot;
|
|
|
|
+ t->n_nodes++;
|
|
|
|
+ }
|
|
|
|
+ else if (result == 2) { /* branches were removed */
|
|
|
|
+ while (reInsertList) {
|
|
|
|
+ /* get next branch in list */
|
|
|
|
+ b = reInsertList->b;
|
|
|
|
+ level = reInsertList->level;
|
|
|
|
+ e = reInsertList;
|
|
|
|
+ reInsertList = reInsertList->next;
|
|
|
|
+ RTreeFreeListBranch(e);
|
|
|
|
+ /* reinsert branches */
|
|
|
|
+ result =
|
|
|
|
+ RTreeInsertRect2(&(b.rect), b.child, level, &newnode, t,
|
|
|
|
+ &reInsertList, overflow);
|
|
|
|
+
|
|
|
|
+ if (result == 1) { /* root split */
|
|
|
|
+ /* grow a new root, & tree taller */
|
|
|
|
+ newroot = RTreeNewNode(t);
|
|
|
|
+ newroot->level = ++t->n_levels;
|
|
|
|
+ /* branch for old root */
|
|
|
|
+ b.rect = RTreeNodeCover(t->root, t);
|
|
|
|
+ b.child = t->root;
|
|
|
|
+ RTreeAddBranch(&b, newroot, NULL, NULL, NULL, NULL, t);
|
|
|
|
+ /* branch for new node created by RTreeInsertRect2() */
|
|
|
|
+ b.rect = RTreeNodeCover(newnode, t);
|
|
|
|
+ b.child = newnode;
|
|
|
|
+ RTreeAddBranch(&b, newroot, NULL, NULL, NULL, NULL, t);
|
|
|
|
+ /* set new root node */
|
|
|
|
+ t->root = newroot;
|
|
|
|
+ t->n_nodes++;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- else
|
|
|
|
- result = 0;
|
|
|
|
|
|
|
|
return result;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * Insert a data rectangle into an RTree index structure.
|
|
|
|
+ * r pointer to rectangle
|
|
|
|
+ * tid data id stored with rectangle, must be > 0
|
|
|
|
+ * t RTree where rectangle should be inserted
|
|
|
|
+ */
|
|
|
|
+int RTreeInsertRect(struct Rect *r, int tid, struct RTree *t)
|
|
|
|
+{
|
|
|
|
+ assert(r && t);
|
|
|
|
+
|
|
|
|
+ t->n_leafs++;
|
|
|
|
+
|
|
|
|
+ return RTreeInsertRect1(r, (struct Node *)tid, 0, t);
|
|
|
|
+}
|
|
|
|
+
|
|
/*
|
|
/*
|
|
* Allocate space for a node in the list used in DeletRect to
|
|
* Allocate space for a node in the list used in DeletRect to
|
|
* store Nodes that are too empty.
|
|
* store Nodes that are too empty.
|
|
@@ -190,14 +343,13 @@ static struct ListNode *RTreeNewListNode(void)
|
|
static void RTreeFreeListNode(struct ListNode *p)
|
|
static void RTreeFreeListNode(struct ListNode *p)
|
|
{
|
|
{
|
|
free(p);
|
|
free(p);
|
|
- /* delete(p); */
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
* Add a node to the reinsertion list. All its branches will later
|
|
* Add a node to the reinsertion list. All its branches will later
|
|
* be reinserted into the index structure.
|
|
* be reinserted into the index structure.
|
|
*/
|
|
*/
|
|
-static void RTreeReInsert(struct Node *n, struct ListNode **ee)
|
|
|
|
|
|
+static void RTreeReInsertNode(struct Node *n, struct ListNode **ee)
|
|
{
|
|
{
|
|
register struct ListNode *l;
|
|
register struct ListNode *l;
|
|
|
|
|
|
@@ -209,94 +361,123 @@ static void RTreeReInsert(struct Node *n, struct ListNode **ee)
|
|
|
|
|
|
/*
|
|
/*
|
|
* Delete a rectangle from non-root part of an index structure.
|
|
* Delete a rectangle from non-root part of an index structure.
|
|
- * Called by RTreeDeleteRect. Descends tree recursively,
|
|
|
|
|
|
+ * Called by RTreeDeleteRect. Descends tree non-recursively,
|
|
* merges branches on the way back up.
|
|
* merges branches on the way back up.
|
|
* Returns 1 if record not found, 0 if success.
|
|
* Returns 1 if record not found, 0 if success.
|
|
*/
|
|
*/
|
|
static int
|
|
static int
|
|
-RTreeDeleteRect2(struct Rect *R, struct Node *Child, struct Node *N,
|
|
|
|
- struct ListNode **Ee)
|
|
|
|
|
|
+RTreeDeleteRect2(struct Rect *r, struct Node *child, struct RTree *t,
|
|
|
|
+ struct ListNode **ee)
|
|
{
|
|
{
|
|
- register struct Rect *r = R;
|
|
|
|
- register struct Node *child = Child;
|
|
|
|
- register struct Node *n = N;
|
|
|
|
- register struct ListNode **ee = Ee;
|
|
|
|
- register int i;
|
|
|
|
-
|
|
|
|
- assert(r && n && ee);
|
|
|
|
- assert(child);
|
|
|
|
- assert(n->level >= 0);
|
|
|
|
-
|
|
|
|
- if (n->level > 0) { /* not a leaf node */
|
|
|
|
- for (i = 0; i < NODECARD; i++) {
|
|
|
|
- if (n->branch[i].child && RTreeOverlap(r, &(n->branch[i].rect))) {
|
|
|
|
- if (!RTreeDeleteRect2(r, child, n->branch[i].child, ee)) {
|
|
|
|
- if (n->branch[i].child->count >= MinNodeFill) {
|
|
|
|
- n->branch[i].rect =
|
|
|
|
- RTreeNodeCover(n->branch[i].child);
|
|
|
|
- }
|
|
|
|
- else {
|
|
|
|
- /* not enough entries in child, eliminate child node */
|
|
|
|
- RTreeReInsert(n->branch[i].child, ee);
|
|
|
|
- RTreeDisconnectBranch(n, i);
|
|
|
|
- }
|
|
|
|
- return 0;
|
|
|
|
|
|
+ int i, notfound = 1;
|
|
|
|
+ struct Node *n;
|
|
|
|
+ struct stack s[MAXLEVEL];
|
|
|
|
+ int top = 0, down = 0;
|
|
|
|
+ int minfill;
|
|
|
|
+
|
|
|
|
+ assert(r && ee && t);
|
|
|
|
+
|
|
|
|
+ /* add root node position to stack */
|
|
|
|
+ s[top].sn = t->root;
|
|
|
|
+ s[top].branch_id = 0;
|
|
|
|
+ n = s[top].sn;
|
|
|
|
+
|
|
|
|
+ while (notfound) {
|
|
|
|
+ /* go down to level 0, remember path */
|
|
|
|
+ if (s[top].sn->level > 0) {
|
|
|
|
+ n = s[top].sn;
|
|
|
|
+ for (i = s[top].branch_id; i < t->nodecard; i++) {
|
|
|
|
+ if (n->branch[i].child &&
|
|
|
|
+ RTreeOverlap(r, &(n->branch[i].rect), t)) {
|
|
|
|
+ s[top++].branch_id = i + 1;
|
|
|
|
+ /* add next node to stack */
|
|
|
|
+ s[top].sn = n->branch[i].child;
|
|
|
|
+ s[top].branch_id = 0;
|
|
|
|
+
|
|
|
|
+ notfound = 0;
|
|
|
|
+ break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+ if (notfound) {
|
|
|
|
+ /* nothing else found, go back up */
|
|
|
|
+ s[top].branch_id = t->nodecard;
|
|
|
|
+ top--;
|
|
|
|
+ }
|
|
|
|
+ else /* found a way down but not yet the item */
|
|
|
|
+ notfound = 1;
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ for (i = 0; i < t->leafcard; i++) {
|
|
|
|
+ if (s[top].sn->branch[i].child && s[top].sn->branch[i].child == child) { /* found item */
|
|
|
|
+ RTreeDisconnectBranch(s[top].sn, i, t);
|
|
|
|
+ t->n_leafs--;
|
|
|
|
+ notfound = 0;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (notfound) /* continue searching */
|
|
|
|
+ top--;
|
|
}
|
|
}
|
|
- return 1;
|
|
|
|
}
|
|
}
|
|
- else { /* a leaf node */
|
|
|
|
|
|
|
|
- for (i = 0; i < LEAFCARD; i++) {
|
|
|
|
- if (n->branch[i].child &&
|
|
|
|
- n->branch[i].child == child) {
|
|
|
|
- RTreeDisconnectBranch(n, i);
|
|
|
|
- return 0;
|
|
|
|
- }
|
|
|
|
|
|
+ if (notfound) {
|
|
|
|
+ return notfound;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* go back up */
|
|
|
|
+ while (top) {
|
|
|
|
+ down = top;
|
|
|
|
+ top--;
|
|
|
|
+ n = s[top].sn;
|
|
|
|
+ i = s[top].branch_id - 1;
|
|
|
|
+ assert(s[down].sn->level == s[top].sn->level - 1);
|
|
|
|
+
|
|
|
|
+ minfill = (s[down].sn->level ? t->min_node_fill : t->min_leaf_fill);
|
|
|
|
+ if (s[down].sn->count >= minfill) {
|
|
|
|
+ /* just update node cover */
|
|
|
|
+ s[top].sn->branch[i].rect = RTreeNodeCover(s[down].sn, t);
|
|
|
|
+ }
|
|
|
|
+ else {
|
|
|
|
+ /* not enough entries in child, eliminate child node */
|
|
|
|
+ RTreeReInsertNode(s[top].sn->branch[i].child, ee);
|
|
|
|
+ RTreeDisconnectBranch(s[top].sn, i, t);
|
|
}
|
|
}
|
|
- return 1;
|
|
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ return notfound;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|
|
|
|
+ * should be called by RTreeDeleteRect() only
|
|
|
|
+ *
|
|
* Delete a data rectangle from an index structure.
|
|
* Delete a data rectangle from an index structure.
|
|
- * Pass in a pointer to a Rect, the tid of the record, ptr to ptr to root node.
|
|
|
|
|
|
+ * Pass in a pointer to a Rect, the tid of the record, ptr RTree.
|
|
* Returns 1 if record not found, 0 if success.
|
|
* Returns 1 if record not found, 0 if success.
|
|
- * RTreeDeleteRect provides for eliminating the root.
|
|
|
|
|
|
+ * RTreeDeleteRect1 provides for eliminating the root.
|
|
*/
|
|
*/
|
|
-int RTreeDeleteRect(struct Rect *R, int Tid, struct Node **Nn)
|
|
|
|
|
|
+static int RTreeDeleteRect1(struct Rect *r, struct Node *child,
|
|
|
|
+ struct RTree *t)
|
|
{
|
|
{
|
|
- /* wrapper not really needed, but restricts compile warnings to rtree lib */
|
|
|
|
- /* this way it's easier to fix if necessary? */
|
|
|
|
- return RTreeDeleteRect1(R, (struct Node *) Tid, Nn);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-int RTreeDeleteRect1(struct Rect *R, struct Node *Child, struct Node **Nn)
|
|
|
|
-{
|
|
|
|
- register struct Rect *r = R;
|
|
|
|
- register struct Node *child = Child;
|
|
|
|
- register struct Node **nn = Nn;
|
|
|
|
- register int i;
|
|
|
|
- struct Node *tmp_nptr = NULL;
|
|
|
|
|
|
+ int i, maxkids;
|
|
|
|
+ struct Node *n;
|
|
struct ListNode *reInsertList = NULL;
|
|
struct ListNode *reInsertList = NULL;
|
|
- register struct ListNode *e;
|
|
|
|
|
|
+ struct ListNode *e;
|
|
|
|
|
|
- assert(r && nn);
|
|
|
|
- assert(*nn);
|
|
|
|
- assert(child);
|
|
|
|
|
|
+ assert(r);
|
|
|
|
+ assert(t);
|
|
|
|
|
|
- if (!RTreeDeleteRect2(r, child, *nn, &reInsertList)) {
|
|
|
|
|
|
+ if (!RTreeDeleteRect2(r, child, t, &reInsertList)) {
|
|
/* found and deleted a data item */
|
|
/* found and deleted a data item */
|
|
|
|
|
|
/* reinsert any branches from eliminated nodes */
|
|
/* reinsert any branches from eliminated nodes */
|
|
while (reInsertList) {
|
|
while (reInsertList) {
|
|
- tmp_nptr = reInsertList->node;
|
|
|
|
- for (i = 0; i < MAXKIDS(tmp_nptr); i++) {
|
|
|
|
- if (tmp_nptr->branch[i].child) {
|
|
|
|
- RTreeInsertRect1(&(tmp_nptr->branch[i].rect),
|
|
|
|
- tmp_nptr->branch[i].child,
|
|
|
|
- nn, tmp_nptr->level);
|
|
|
|
|
|
+ t->n_nodes--;
|
|
|
|
+ n = reInsertList->node;
|
|
|
|
+ maxkids = (n->level > 0 ? t->nodecard : t->leafcard);
|
|
|
|
+ for (i = 0; i < maxkids; i++) {
|
|
|
|
+ if (n->branch[i].child) {
|
|
|
|
+ RTreeInsertRect1(&(n->branch[i].rect),
|
|
|
|
+ n->branch[i].child, n->level, t);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
e = reInsertList;
|
|
e = reInsertList;
|
|
@@ -306,15 +487,15 @@ int RTreeDeleteRect1(struct Rect *R, struct Node *Child, struct Node **Nn)
|
|
}
|
|
}
|
|
|
|
|
|
/* check for redundant root (not leaf, 1 child) and eliminate */
|
|
/* check for redundant root (not leaf, 1 child) and eliminate */
|
|
- if ((*nn)->count == 1 && (*nn)->level > 0) {
|
|
|
|
- for (i = 0; i < NODECARD; i++) {
|
|
|
|
- tmp_nptr = (*nn)->branch[i].child;
|
|
|
|
- if (tmp_nptr)
|
|
|
|
|
|
+ n = t->root;
|
|
|
|
+
|
|
|
|
+ if (n->count == 1 && n->level > 0) {
|
|
|
|
+ for (i = 0; i < t->nodecard; i++) {
|
|
|
|
+ if (n->branch[i].child)
|
|
break;
|
|
break;
|
|
}
|
|
}
|
|
- assert(tmp_nptr);
|
|
|
|
- RTreeFreeNode(*nn);
|
|
|
|
- *nn = tmp_nptr;
|
|
|
|
|
|
+ t->root = n->branch[i].child;
|
|
|
|
+ RTreeFreeNode(n);
|
|
}
|
|
}
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
@@ -322,3 +503,20 @@ int RTreeDeleteRect1(struct Rect *R, struct Node *Child, struct Node **Nn)
|
|
return 1;
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * Delete a data rectangle from an index structure.
|
|
|
|
+ * Pass in a pointer to a Rect, the tid of the record, ptr RTree.
|
|
|
|
+ * Returns 1 if record not found, 0 if success.
|
|
|
|
+ * RTreeDeleteRect1 provides for eliminating the root.
|
|
|
|
+ *
|
|
|
|
+ * RTreeDeleteRect() should be called by external functions instead of
|
|
|
|
+ * RTreeDeleteRect1()
|
|
|
|
+ * wrapper for RTreeDeleteRect1 not really needed, but restricts
|
|
|
|
+ * compile warnings to rtree lib
|
|
|
|
+ * this way it's easier to fix if necessary?
|
|
|
|
+ */
|
|
|
|
+int RTreeDeleteRect(struct Rect *r, int tid, struct RTree *t)
|
|
|
|
+{
|
|
|
|
+ return RTreeDeleteRect1(r, (struct Node *)tid, t);
|
|
|
|
+}
|