空间树rtree原理见: rtree原理
rtree的原始论文下载:R-trees a dynamic index structure for spatial searching
rtree的代码根据the GRASS Development Team的原始代码整理, 原来的多个文件合并成了现在的两个文件,
向先行者致敬!
rtree.h
/****************************************************************************
* MODULE: R-Tree library
*
* AUTHOR(S): Antonin Guttman - original code
* Daniel Green ([email protected]) - major clean-up
* and implementation of bounding spheres
*
* PURPOSE: Multidimensional index
*
* COPYRIGHT: (C) 2001 by the GRASS Development Team
*
* This program is free software under the GNU General Public
* License (>=v2). Read the file COPYING that comes with GRASS
* for details.
*
* LAST MODIFICATION: Hyforthy 2013
*****************************************************************************/
#ifndef __RTREE_H__
#define __RTREE_H__
#define NUMDIMS 3 /* number of dimensions */
#define NUMSIDES 2*NUMDIMS
/* typedef float RectReal; */
typedef double RectReal;
struct Rect
{
RectReal boundary[NUMSIDES]; /* xmin,ymin,...,xmax,ymax,... */
};
struct Node;
typedef int (*SearchHitCallback) (int id, void *arg);
struct Node *RTreeNewIndex(void);
int RTreeSearch(struct Node *, struct Rect *, SearchHitCallback, void *);
int RTreeInsertRect(struct Rect *, int, struct Node **, int depth);
int RTreeDeleteRect(struct Rect *, int, struct Node **);
void RTreeDestroyNode(struct Node *);
#endif
/****************************************************************************
* MODULE: R-Tree library
*
* AUTHOR(S): Antonin Guttman - original code
* Daniel Green ([email protected]) - major clean-up
* and implementation of bounding spheres
*
* PURPOSE: Multidimensional index
*
* COPYRIGHT: (C) 2001 by the GRASS Development Team
*
* This program is free software under the GNU General Public
* License (>=v2). Read the file COPYING that comes with GRASS
* for details.
*
* LAST MODIFICATION: Hyforthy 2013
*****************************************************************************/
#include
#include
#include
#include "assert.h"
#include "rtree.h"
/*-----------------------------------------------------------------------------
| Definitions and global variables.
-----------------------------------------------------------------------------*/
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#define METHODS 1
#define BIG_NUM (FLT_MAX/4.0)
#define Undefined(x) ((x)->boundary[0] > (x)->boundary[NUMDIMS])
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
/* PGSIZE is normally the natural page size of the machine */
#define PGSIZE 512
struct Branch
{
struct Rect rect;
struct Node *child;
};
/* max branching factor of a node */
#define MAXCARD (int)((PGSIZE-(2*sizeof(int))) / sizeof(struct Branch))
struct Node
{
int count;
int level; /* 0 is leaf, others positive */
struct Branch branch[MAXCARD];
};
struct ListNode
{
struct ListNode *next;
struct Node *node;
};
// splitnode
struct PartitionVars {
int partition[MAXCARD + 1]; /// partition[i]表示id为i的branch被分配到的group id
int total, minfill; /// total表示待分配的branch总数,minfill表示结点所需的最少孩子数
int taken[MAXCARD + 1]; /// taken[i]表示id为i的branch分配与否
int count[2]; /// count[i]表示id为i的group已包含的branch数
struct Rect cover[2]; /// cover[i]表示id为i的group的矩形空间
RectReal area[2]; /// area[i]表示id为i的group的矩形空间大小
};
int NODECARD = MAXCARD;
int LEAFCARD = MAXCARD;
/* balance criteria for node splitting */
/* NOTE: can be changed if needed. */
#define MinNodeFill (NODECARD / 2)
#define MinLeafFill (LEAFCARD / 2)
#define MAXKIDS(n) ((n)->level > 0 ? NODECARD : LEAFCARD)
#define MINFILL(n) ((n)->level > 0 ? MinNodeFill : MinLeafFill)
struct Branch BranchBuf[MAXCARD + 1];
int BranchCount;
struct Rect CoverSplit;
RectReal CoverSplitArea;
/* variables for finding a partition */
struct PartitionVars Partitions[METHODS];
static int set_max(int *which, int new_max)
{
if (2 > new_max || new_max > MAXCARD)
return 0;
*which = new_max;
return 1;
}
static int RTreeSetNodeMax(int new_max)
{
return set_max(&NODECARD, new_max);
}
static int RTreeSetLeafMax(int new_max)
{
return set_max(&LEAFCARD, new_max);
}
static int RTreeGetNodeMax(void)
{
return NODECARD;
}
static int RTreeGetLeafMax(void)
{
return LEAFCARD;
}
/*-----------------------------------------------------------------------------
| Initialize a rectangle to have all 0 coordinates.
-----------------------------------------------------------------------------*/
static void RTreeInitRect(struct Rect *R)
{
register struct Rect *r = R;
register int i;
for (i = 0; i < NUMSIDES; i++)
r->boundary[i] = (RectReal) 0;
}
/*-----------------------------------------------------------------------------
| Return a rect whose first low side is higher than its opposite side -
| interpreted as an undefined rect.
-----------------------------------------------------------------------------*/
static struct Rect RTreeNullRect(void)
{
struct Rect r;
register int i;
r.boundary[0] = (RectReal) 1;
r.boundary[NUMDIMS] = (RectReal) - 1;
for (i = 1; i < NUMDIMS; i++)
r.boundary[i] = r.boundary[i + NUMDIMS] = (RectReal) 0;
return r;
}
#if 0
/*-----------------------------------------------------------------------------
| Fills in random coordinates in a rectangle.
| The low side is guaranteed to be less than the high side.
-----------------------------------------------------------------------------*/
static void RTreeRandomRect(struct Rect *R)
{
register struct Rect *r = R;
register int i;
register RectReal width;
for (i = 0; i < NUMDIMS; i++) {
/* width from 1 to 1000 / 4, more small ones
*/
width = drand48() * (1000 / 4) + 1;
/* sprinkle a given size evenly but so they stay in [0,100]
*/
r->boundary[i] = drand48() * (1000 - width); /* low side */
r->boundary[i + NUMDIMS] = r->boundary[i] + width; /* high side */
}
}
/*-----------------------------------------------------------------------------
| Fill in the boundaries for a random search rectangle.
| Pass in a pointer to a rect that contains all the data,
| and a pointer to the rect to be filled in.
| Generated rect is centered randomly anywhere in the data area,
| and has size from 0 to the size of the data area in each dimension,
| i.e. search rect can stick out beyond data area.
-----------------------------------------------------------------------------*/
static void RTreeSearchRect(struct Rect *Search, struct Rect *Data)
{
register struct Rect *search = Search, *data = Data;
register int i, j;
register RectReal size, center;
assert(search);
assert(data);
for (i = 0; i < NUMDIMS; i++) {
j = i + NUMDIMS; /* index for high side boundary */
if (data->boundary[i] > -BIG_NUM && data->boundary[j] < BIG_NUM) {
size = (drand48() * (data->boundary[j] -
data->boundary[i] + 1)) / 2;
center = data->boundary[i] + drand48() *
(data->boundary[j] - data->boundary[i] + 1);
search->boundary[i] = center - size / 2;
search->boundary[j] = center + size / 2;
}
else { /* some open boundary, search entire dimension */
search->boundary[i] = -BIG_NUM;
search->boundary[j] = BIG_NUM;
}
}
}
#endif
static void RTreeTabIn(int depth)
{
int i;
for (i = 0; i < depth; i++)
putchar('\t');
}
/*-----------------------------------------------------------------------------
| Print out the data for a rectangle.
-----------------------------------------------------------------------------*/
static void RTreePrintRect(struct Rect *R, int depth)
{
register struct Rect *r = R;
register int i;
assert(r);
RTreeTabIn(depth);
fprintf(stdout, "rect:\n");
for (i = 0; i < NUMDIMS; i++) {
RTreeTabIn(depth + 1);
fprintf(stdout, "%f\t%f\n", r->boundary[i], r->boundary[i + NUMDIMS]);
}
}
/*-----------------------------------------------------------------------------
| Calculate the n-dimensional volume of a rectangle
-----------------------------------------------------------------------------*/
static RectReal RTreeRectVolume(struct Rect *R)
{
register struct Rect *r = R;
register int i;
register RectReal volume = (RectReal) 1;
assert(r);
if (Undefined(r))
return (RectReal) 0;
for (i = 0; i < NUMDIMS; i++)
volume *= r->boundary[i + NUMDIMS] - r->boundary[i];
assert(volume >= 0.0);
return volume;
}
/*-----------------------------------------------------------------------------
| Define the NUMDIMS-dimensional volume the unit sphere in that dimension into
| the symbol "UnitSphereVolume"
| Note that if the gamma function is available in the math library and if the
| compiler supports static initialization using functions, this is
| easily computed for any dimension. If not, the value can be precomputed and
| taken from a table. The following code can do it either way.
-----------------------------------------------------------------------------*/
#ifdef gamma
/* computes the volume of an N-dimensional sphere. */
/* derived from formule in "Regular Polytopes" by H.S.M Coxeter */
static double sphere_volume(double dimension)
{
double log_gamma, log_volume;
log_gamma = gamma(dimension / 2.0 + 1);
log_volume = dimension / 2.0 * log(M_PI) - log_gamma;
return exp(log_volume);
}
static const double UnitSphereVolume = sphere_volume(NUMDIMS);
#else
/* Precomputed volumes of the unit spheres for the first few dimensions */
static const double UnitSphereVolumes[] = {
0.000000, /* dimension 0 */
2.000000, /* dimension 1 */
3.141593, /* dimension 2 */
4.188790, /* dimension 3 */
4.934802, /* dimension 4 */
5.263789, /* dimension 5 */
5.167713, /* dimension 6 */
4.724766, /* dimension 7 */
4.058712, /* dimension 8 */
3.298509, /* dimension 9 */
2.550164, /* dimension 10 */
1.884104, /* dimension 11 */
1.335263, /* dimension 12 */
0.910629, /* dimension 13 */
0.599265, /* dimension 14 */
0.381443, /* dimension 15 */
0.235331, /* dimension 16 */
0.140981, /* dimension 17 */
0.082146, /* dimension 18 */
0.046622, /* dimension 19 */
0.025807, /* dimension 20 */
};
#if NUMDIMS > 20
# error "not enough precomputed sphere volumes"
#endif
#define UnitSphereVolume UnitSphereVolumes[NUMDIMS]
#endif
/*-----------------------------------------------------------------------------
| Calculate the n-dimensional volume of the bounding sphere of a rectangle
-----------------------------------------------------------------------------*/
#if 0
/*
* A fast approximation to the volume of the bounding sphere for the
* given Rect. By Paul B.
*/
static RectReal RTreeRectSphericalVolume(struct Rect *R)
{
register struct Rect *r = R;
register int i;
RectReal maxsize = (RectReal) 0, c_size;
assert(r);
if (Undefined(r))
return (RectReal) 0;
for (i = 0; i < NUMDIMS; i++) {
c_size = r->boundary[i + NUMDIMS] - r->boundary[i];
if (c_size > maxsize)
maxsize = c_size;
}
return (RectReal) (pow(maxsize / 2, NUMDIMS) * UnitSphereVolume);
}
#endif
/*
* The exact volume of the bounding sphere for the given Rect.
*/
static RectReal RTreeRectSphericalVolume(struct Rect * R)
{
register struct Rect *r = R;
register int i;
register double sum_of_squares = 0, radius;
assert(r);
if (Undefined(r))
return (RectReal) 0;
for (i = 0; i < NUMDIMS; i++) {
double half_extent = (r->boundary[i + NUMDIMS] - r->boundary[i]) / 2;
sum_of_squares += half_extent * half_extent;
}
radius = sqrt(sum_of_squares);
return (RectReal) (pow(radius, NUMDIMS) * UnitSphereVolume);
}
/*-----------------------------------------------------------------------------
| Calculate the n-dimensional surface area of a rectangle
-----------------------------------------------------------------------------*/
static RectReal RTreeRectSurfaceArea(struct Rect * R)
{
register struct Rect *r = R;
register int i, j;
register RectReal sum = (RectReal) 0;
assert(r);
if (Undefined(r))
return (RectReal) 0;
for (i = 0; i < NUMDIMS; i++) {
RectReal face_area = (RectReal) 1;
for (j = 0; j < NUMDIMS; j++)
/* exclude i extent from product in this dimension */
if (i != j) {
RectReal j_extent = r->boundary[j + NUMDIMS] - r->boundary[j];
face_area *= j_extent;
}
sum += face_area;
}
return 2 * sum;
}
/*-----------------------------------------------------------------------------
| Combine two rectangles, make one that includes both.
-----------------------------------------------------------------------------*/
static struct Rect RTreeCombineRect(struct Rect *R, struct Rect *Rr)
{
register struct Rect *r = R, *rr = Rr;
register int i, j;
struct Rect new_rect;
assert(r && rr);
if (Undefined(r))
return *rr;
if (Undefined(rr))
return *r;
for (i = 0; i < NUMDIMS; i++) {
new_rect.boundary[i] = MIN(r->boundary[i], rr->boundary[i]);
j = i + NUMDIMS;
new_rect.boundary[j] = MAX(r->boundary[j], rr->boundary[j]);
}
return new_rect;
}
/*-----------------------------------------------------------------------------
| Decide whether two rectangles overlap.
-----------------------------------------------------------------------------*/
static int RTreeOverlap(struct Rect *R, struct Rect *S)
{
register struct Rect *r = R, *s = S;
register int i, j;
assert(r && s);
for (i = 0; i < NUMDIMS; i++) {
j = i + NUMDIMS; /* index for high sides */
if (r->boundary[i] > s->boundary[j] ||
s->boundary[i] > r->boundary[j]) {
return FALSE;
}
}
return TRUE;
}
/*-----------------------------------------------------------------------------
| Decide whether rectangle r is contained in rectangle s.
-----------------------------------------------------------------------------*/
static int RTreeContained(struct Rect *R, struct Rect *S)
{
register struct Rect *r = R, *s = S;
register int i, j, result;
assert(r && s); /* same as in RTreeOverlap() */
/* undefined rect is contained in any other */
if (Undefined(r))
return TRUE;
/* no rect (except an undefined one) is contained in an undef rect */
if (Undefined(s))
return FALSE;
result = TRUE;
for (i = 0; i < NUMDIMS; i++) {
j = i + NUMDIMS; /* index for high sides */
result = result && r->boundary[i] >= s->boundary[i]
&& r->boundary[j] <= s->boundary[j];
}
return result;
}
/* Initialize one branch cell in a node. */
static void RTreeInitBranch(struct Branch *b)
{
RTreeInitRect(&(b->rect));
b->child = NULL;
}
/* Initialize a Node structure. */
static void RTreeInitNode(struct Node *N)
{
register struct Node *n = N;
register int i;
n->count = 0;
n->level = -1;
for (i = 0; i < MAXCARD; i++)
RTreeInitBranch(&(n->branch[i]));
}
/* Make a new node and initialize to have all branch cells empty. */
static struct Node *RTreeNewNode(void)
{
register struct Node *n;
/* n = new Node; */
n = (struct Node *)malloc(sizeof(struct Node));
assert(n);
RTreeInitNode(n);
return n;
}
static void RTreeFreeNode(struct Node *p)
{
assert(p);
/* delete p; */
free(p);
}
static void RTreePrintNode(struct Node *n, int depth);
static void RTreePrintBranch(struct Branch *b, int depth)
{
RTreePrintRect(&(b->rect), depth);
RTreePrintNode(b->child, depth);
}
/* Print out the data in a node. */
static void RTreePrintNode(struct Node *n, int depth)
{
int i;
assert(n);
RTreeTabIn(depth);
fprintf(stdout, "node");
if (n->level == 0)
fprintf(stdout, " LEAF");
else if (n->level > 0)
fprintf(stdout, " NONLEAF");
else
fprintf(stdout, " TYPE=?");
fprintf(stdout, " level=%d count=%d address=%o\n", n->level, n->count,
(unsigned)n);
for (i = 0; i < n->count; i++) {
if (n->level == 0) {
/* RTreeTabIn(depth); */
/* fprintf (stdout, "\t%d: data = %d\n", i, n->branch[i].child); */
}
else {
RTreeTabIn(depth);
fprintf(stdout, "branch %d\n", i);
RTreePrintBranch(&n->branch[i], depth + 1);
}
}
}
/*
* Find the smallest rectangle that includes all rectangles in
* branches of a node.
*/
static struct Rect RTreeNodeCover(struct Node *N)
{
register struct Node *n = N;
register int i, first_time = 1;
struct Rect r;
assert(n);
RTreeInitRect(&r);
for (i = 0; i < MAXKIDS(n); i++)
if (n->branch[i].child) {
if (first_time) {
r = n->branch[i].rect;
first_time = 0;
}
else
r = RTreeCombineRect(&r, &(n->branch[i].rect));
}
return r;
}
/*
* Pick a branch. Pick the one that will need the smallest increase
* in area to accomodate the new rectangle. This will result in the
* least total area for the covering rectangles in the current node.
* In case of a tie, pick the one which was smaller before, to get
* the best resolution when searching.
*/
static int RTreePickBranch(struct Rect *R, struct Node *N)
{
register struct Rect *r = R;
register struct Node *n = N;
register struct Rect *rr;
register int i, first_time = 1;
RectReal increase, bestIncr = (RectReal) - 1, area, bestArea = 0;
int best = 0;
struct Rect tmp_rect;
assert(r && n);
// best是这样的孩子:添加矩形R后其矩形空间增加值最少
for (i = 0; i < MAXKIDS(n); i++) {
if (n->branch[i].child) {
rr = &n->branch[i].rect;
area = RTreeRectSphericalVolume(rr);
tmp_rect = RTreeCombineRect(r, rr);
increase = RTreeRectSphericalVolume(&tmp_rect) - area;
if (increase < bestIncr || first_time) {
best = i;
bestArea = area;
bestIncr = increase;
first_time = 0;
}
else if (increase == bestIncr && area < bestArea) {
best = i;
bestArea = area;
bestIncr = increase;
}
}
}
return best;
}
/*-----------------------------------------------------------------------------
| Load branch buffer with branches from full node plus the extra branch.
-----------------------------------------------------------------------------*/
static void RTreeGetBranches(struct Node *n, struct Branch *b)
{
int i;
assert(n);
assert(b);
/* load the branch buffer */
for (i = 0; i < MAXKIDS(n); i++) {
assert(n->branch[i].child); /* n should have every entry full */
BranchBuf[i] = n->branch[i];
}
BranchBuf[MAXKIDS(n)] = *b;
BranchCount = MAXKIDS(n) + 1;
/* calculate rect containing all in the set */
CoverSplit = BranchBuf[0].rect;
for (i = 1; i < MAXKIDS(n) + 1; i++) {
CoverSplit = RTreeCombineRect(&CoverSplit, &BranchBuf[i].rect);
}
CoverSplitArea = RTreeRectSphericalVolume(&CoverSplit);
RTreeInitNode(n);
}
/*-----------------------------------------------------------------------------
| Put a branch in one of the groups.
-----------------------------------------------------------------------------*/
static void RTreeClassify(int i, int group, struct PartitionVars *p)
{
assert(p);
assert(!p->taken[i]);
p->partition[i] = group;
p->taken[i] = TRUE;
if (p->count[group] == 0)
p->cover[group] = BranchBuf[i].rect;
else
p->cover[group] =
RTreeCombineRect(&BranchBuf[i].rect, &p->cover[group]);
p->area[group] = RTreeRectSphericalVolume(&p->cover[group]);
p->count[group]++;
}
/*-----------------------------------------------------------------------------
| Pick two rects from set to be the first elements of the two groups.
| Pick the two that waste the most area if covered by a single rectangle.
-----------------------------------------------------------------------------*/
static void RTreePickSeeds(struct PartitionVars *p)
{
int i, j, seed0 = 0, seed1 = 0;
RectReal worst, waste, area[MAXCARD + 1];
for (i = 0; i < p->total; i++)
area[i] = RTreeRectSphericalVolume(&BranchBuf[i].rect);
worst = -CoverSplitArea - 1;
for (i = 0; i < p->total - 1; i++) {
for (j = i + 1; j < p->total; j++) {
struct Rect one_rect;
one_rect = RTreeCombineRect(&BranchBuf[i].rect,
&BranchBuf[j].rect);
waste = RTreeRectSphericalVolume(&one_rect) - area[i] - area[j];
if (waste > worst) {
worst = waste;
seed0 = i;
seed1 = j;
}
}
}
RTreeClassify(seed0, 0, p);
RTreeClassify(seed1, 1, p);
}
/*-----------------------------------------------------------------------------
| Initialize a PartitionVars structure.
-----------------------------------------------------------------------------*/
static void RTreeInitPVars(struct PartitionVars *p, int maxrects, int minfill)
{
int i;
assert(p);
p->count[0] = p->count[1] = 0;
p->cover[0] = p->cover[1] = RTreeNullRect();
p->area[0] = p->area[1] = (RectReal) 0;
p->total = maxrects;
p->minfill = minfill;
for (i = 0; i < maxrects; i++) {
p->taken[i] = FALSE;
p->partition[i] = -1;
}
}
/*-----------------------------------------------------------------------------
| Print out data for a partition from PartitionVars struct.
-----------------------------------------------------------------------------*/
static void RTreePrintPVars(struct PartitionVars *p)
{
int i;
assert(p);
fprintf(stdout, "\npartition:\n");
for (i = 0; i < p->total; i++) {
fprintf(stdout, "%3d\t", i);
}
fprintf(stdout, "\n");
for (i = 0; i < p->total; i++) {
if (p->taken[i])
fprintf(stdout, " t\t");
else
fprintf(stdout, "\t");
}
fprintf(stdout, "\n");
for (i = 0; i < p->total; i++) {
fprintf(stdout, "%3d\t", p->partition[i]);
}
fprintf(stdout, "\n");
fprintf(stdout, "count[0] = %d area = %f\n", p->count[0], p->area[0]);
fprintf(stdout, "count[1] = %d area = %f\n", p->count[1], p->area[1]);
if (p->area[0] + p->area[1] > 0) {
fprintf(stdout, "total area = %f effectiveness = %3.2f\n",
p->area[0] + p->area[1],
(float)CoverSplitArea / (p->area[0] + p->area[1]));
}
fprintf(stdout, "cover[0]:\n");
RTreePrintRect(&p->cover[0], 0);
fprintf(stdout, "cover[1]:\n");
RTreePrintRect(&p->cover[1], 0);
}
/*-----------------------------------------------------------------------------
| Method #0 for choosing a partition:
| As the seeds for the two groups, pick the two rects that would waste the
| most area if covered by a single rectangle, i.e. evidently the worst pair
| to have in the same group.
| Of the remaining, one at a time is chosen to be put in one of the two groups.
| The one chosen is the one with the greatest difference in area expansion
| depending on which group - the rect most strongly attracted to one group
| and repelled from the other.
| If one group gets too full (more would force other group to violate min
| fill requirement) then other group gets the rest.
| These last are the ones that can go in either group most easily.
-----------------------------------------------------------------------------*/
static void RTreeMethodZero(struct PartitionVars *p, int minfill)
{
int i;
RectReal biggestDiff;
int group, chosen = 0, betterGroup = 0;
assert(p);
RTreeInitPVars(p, BranchCount, minfill);
RTreePickSeeds(p);
while (p->count[0] + p->count[1] < p->total
&& p->count[0] < p->total - p->minfill
&& p->count[1] < p->total - p->minfill) {
biggestDiff = (RectReal) - 1.;
for (i = 0; i < p->total; i++) {
if (!p->taken[i]) {
struct Rect *r, rect_0, rect_1;
RectReal growth0, growth1, diff;
r = &BranchBuf[i].rect;
rect_0 = RTreeCombineRect(r, &p->cover[0]);
rect_1 = RTreeCombineRect(r, &p->cover[1]);
growth0 = RTreeRectSphericalVolume(&rect_0) - p->area[0];
growth1 = RTreeRectSphericalVolume(&rect_1) - p->area[1];
diff = growth1 - growth0;
if (diff >= 0)
group = 0;
else {
group = 1;
diff = -diff;
}
if (diff > biggestDiff) {
biggestDiff = diff;
chosen = i;
betterGroup = group;
}
else if (diff == biggestDiff &&
p->count[group] < p->count[betterGroup]) {
chosen = i;
betterGroup = group;
}
}
}
RTreeClassify(chosen, betterGroup, p);
}
/* if one group too full, put remaining rects in the other */
if (p->count[0] + p->count[1] < p->total) {
if (p->count[0] >= p->total - p->minfill)
group = 1;
else
group = 0;
for (i = 0; i < p->total; i++) {
if (!p->taken[i])
RTreeClassify(i, group, p);
}
}
assert(p->count[0] + p->count[1] == p->total);
assert(p->count[0] >= p->minfill && p->count[1] >= p->minfill);
}
static void RTreeSplitNode(struct Node *n, struct Branch *b, struct Node **nn);
/*
* Add a branch to a node. Split the node if necessary.
* Returns 0 if node not split. Old node updated.
* Returns 1 if node split, sets *new_node to address of new node.
* Old node updated, becomes one of two.
*/
static int RTreeAddBranch(struct Branch *B, struct Node *N, struct Node **New_node)
{
register struct Branch *b = B;
register struct Node *n = N;
register struct Node **new_node = New_node;
register int i;
assert(b);
assert(n);
if (n->count < MAXKIDS(n)) { /* split won't be necessary */
for (i = 0; i < MAXKIDS(n); i++) { /* find empty branch */
if (n->branch[i].child == NULL) {
n->branch[i] = *b;
n->count++;
break;
}
}
return 0;
}
else {
assert(new_node);
RTreeSplitNode(n, b, new_node);
return 1;
}
}
/*-----------------------------------------------------------------------------
| Copy branches from the buffer into two nodes according to the partition.
-----------------------------------------------------------------------------*/
static void RTreeLoadNodes(struct Node *n, struct Node *q,
struct PartitionVars *p)
{
int i;
assert(n);
assert(q);
assert(p);
for (i = 0; i < p->total; i++) {
assert(p->partition[i] == 0 || p->partition[i] == 1);
if (p->partition[i] == 0)
RTreeAddBranch(&BranchBuf[i], n, NULL);
else if (p->partition[i] == 1)
RTreeAddBranch(&BranchBuf[i], q, NULL);
}
}
/*-----------------------------------------------------------------------------
| Split a node.
| Divides the nodes branches and the extra one between two nodes.
| Old node is one of the new ones, and one really new one is created.
| Tries more than one method for choosing a partition, uses best result.
-----------------------------------------------------------------------------*/
static void RTreeSplitNode(struct Node *n, struct Branch *b, struct Node **nn)
{
struct PartitionVars *p;
int level;
assert(n);
assert(b);
/* load all the branches into a buffer, initialize old node */
level = n->level;
RTreeGetBranches(n, b);
/* find partition */
p = &Partitions[0];
/* Note: can't use MINFILL(n) below since n was cleared by GetBranches() */
RTreeMethodZero(p, level > 0 ? MinNodeFill : MinLeafFill);
/*
* put branches from buffer into 2 nodes
* according to chosen partition
*/
*nn = RTreeNewNode();
(*nn)->level = n->level = level;
RTreeLoadNodes(n, *nn, p);
assert(n->count + (*nn)->count == p->total);
}
/* Disconnect a dependent node. */
static void RTreeDisconnectBranch(struct Node *n, int i)
{
assert(n && i >= 0 && i < MAXKIDS(n));
assert(n->branch[i].child);
RTreeInitBranch(&(n->branch[i]));
n->count--;
}
/* Destroy (free) node recursively. */
void RTreeDestroyNode(struct Node *n)
{
int i;
if (!n)
return;
if (n->level > 0) { /* it is not leaf -> destroy childs */
for (i = 0; i < NODECARD; i++) {
if (n->branch[i].child) {
RTreeDestroyNode(n->branch[i].child);
}
}
}
/* Free this node */
RTreeFreeNode(n);
}
/* Make a new index, empty. Consists of a single node. */
struct Node *RTreeNewIndex(void)
{
struct Node *x;
x = RTreeNewNode();
x->level = 0; /* leaf */
return x;
}
/*
* Search in an index tree or subtree for all data retangles that
* overlap the argument rectangle.
* Return the number of qualifying data rects.
*/
int RTreeSearch(struct Node *N, struct Rect *R, SearchHitCallback shcb,
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;
assert(n);
assert(n->level >= 0);
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);
}
}
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 */
}
}
return hitCount;
}
/*
* 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.
* 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.
*/
static int RTreeInsertRect2(struct Rect *r,
struct Node *child, struct Node *n, struct Node **new_node,
int level)
{
/*
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;
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 */
n->branch[i].rect = RTreeNodeCover(n->branch[i].child);
b.child = n2;
b.rect = RTreeNodeCover(n2);
return RTreeAddBranch(&b, n, new_node);
}
}
/* Have reached level for insertion. Add rect, split if necessary */
else if (n->level == level) {
b.rect = *r;
b.child = child;
/* child field of leaves contains tid(the id of tuple) of data record */
return RTreeAddBranch(&b, n, new_node);
}
else {
/* Not supposed to happen */
assert(FALSE);
return 0;
}
}
static 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 Branch b;
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);
b.child = newnode;
RTreeAddBranch(&b, newroot, NULL);
*root = newroot;
result = 1;
}
else
result = 0;
return result;
}
/*
* Insert a data rectangle into an index structure.
* RTreeInsertRect provides for splitting the root;
* returns 1 if root was split, 0 if it was not.
* 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.
* RTreeInsertRect2 does the recursion.
*/
int RTreeInsertRect(struct Rect *R, int Tid, struct Node **Root, int Level)
{
assert(Level == 0);
return RTreeInsertRect1(R, (struct Node *) Tid, Root, Level);
}
/*
* Allocate space for a node in the list used in DeletRect to
* store Nodes that are too empty.
*/
static struct ListNode *RTreeNewListNode(void)
{
return (struct ListNode *)malloc(sizeof(struct ListNode));
/* return new ListNode; */
}
static void RTreeFreeListNode(struct ListNode *p)
{
free(p);
/* delete(p); */
}
/*
* Add a node to the reinsertion list. All its branches will later
* be reinserted into the index structure.
*/
static void RTreeReInsert(struct Node *n, struct ListNode **ee)
{
register struct ListNode *l;
l = RTreeNewListNode();
l->node = n;
l->next = *ee;
*ee = l;
}
/*
* Delete a rectangle from non-root part of an index structure.
* Called by RTreeDeleteRect. Descends tree recursively,
* merges branches on the way back up.
* Returns 1 if record not found, 0 if success.
*/
static int
RTreeDeleteRect2(struct Rect *R, struct Node *Child, struct Node *N,
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;
}
}
}
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;
}
}
return 1;
}
}
static 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;
struct ListNode *reInsertList = NULL;
register struct ListNode *e;
assert(r && nn);
assert(*nn);
assert(child);
if (!RTreeDeleteRect2(r, child, *nn, &reInsertList)) {
/* found and deleted a data item */
/* reinsert any branches from eliminated nodes */
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);
}
}
e = reInsertList;
reInsertList = reInsertList->next;
RTreeFreeNode(e->node);
RTreeFreeListNode(e);
}
/* 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)
break;
}
assert(tmp_nptr);
RTreeFreeNode(*nn);
*nn = tmp_nptr;
}
return 0;
}
else {
return 1;
}
}
/*
* 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.
* Returns 1 if record not found, 0 if success.
* RTreeDeleteRect provides for eliminating the root.
*/
int RTreeDeleteRect(struct Rect *R, int Tid, struct Node **Nn)
{
/* 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);
}
test.c
/****************************************************************************
* MODULE: R-Tree library
*
* AUTHOR(S): Antonin Guttman - original code
* Daniel Green ([email protected]) - major clean-up
* and implementation of bounding spheres
*
* PURPOSE: Multidimensional index
*
* COPYRIGHT: (C) 2001 by the GRASS Development Team
*
* This program is free software under the GNU General Public
* License (>=v2). Read the file COPYING that comes with GRASS
* for details.
*
* LAST MODIFICATION: Hyforthy 2013
*****************************************************************************/
#include
#include "rtree.h"
struct Rect rects[] = {
{{0, 0, 0, 2, 2, 0}}, /* xmin, ymin, zmin, xmax, ymax, zmax (for 3 dimensional RTree) */
{{5, 5, 0, 7, 7, 0}},
{{8, 5, 0, 9, 6, 0}},
{{7, 1, 0, 9, 2, 0}},
{{1, 1, 0, 9, 2, 0}},
{{2, 1, 0, 9, 2, 0}},
{{3, 1, 0, 9, 2, 0}},
{{4, 1, 0, 9, 2, 0}},
{{6, 1, 0, 9, 2, 0}},
{{8, 1, 0, 9, 2, 0}},
{{9, 1, 0, 9, 2, 0}},
{{1, 1, 0, 9, 2, 0}},
{{2, 2, 0, 9, 2, 0}},
{{3, 3, 0, 9, 20, 0}},
{{4, 5, 0, 9, 20, 0}},
{{6, 7, 0, 9, 20, 0}},
{{8, 10, 0, 9, 20, 0}},
{{9, 11, 0, 9, 20, 0}},
{{1, 1, 1, 9, 2, 10}},
{{2, 2, 2, 9, 2, 10}},
{{3, 3, 3, 9, 12, 10}},
{{4, 5, 4, 9, 12, 10}},
{{6, 7, 5, 9, 12, 10}},
{{8, 10, 6, 9, 12, 10}},
{{9, 11, 7, 9, 12, 10}},
{{1, 1, 1, 1, 2, 4}},
{{2, 2, 2, 9, 2, 7}},
{{3, 3, 3, 4, 4, 4}},
{{4, 5, 4, 10, 10, 10}},
{{6, 7, 5, 20, 20, 10}},
{{8, 10, 6, 15, 11, 10}},
{{9, 11, 7, 10, 12, 18}},
{{3, 5, 0, 19, 6, 1}},
{{3, 5, 0, 9, 6, 1}},
{{3, 1, 0, 8, 10, 1}},
{{3, 5, 0, 9, 6, 1}}
};
int nrects = sizeof(rects) / sizeof(rects[0]);
struct Rect search_rect = {
{6, 4, 0, 10, 6, 0} /* search will find above rects that this one overlaps */
};
int MySearchCallback(int id, void *arg)
{
/* Note: -1 to make up for the +1 when data was inserted */
fprintf(stdout, "Hit data rect %d\n", id - 1);
return 1; /* keep going */
}
int main()
{
struct Node *root = RTreeNewIndex();
int i, nhits;
fprintf(stdout, "nrects = %d\n", nrects);
/*
* Insert all the data rects.
* Notes about the arguments:
* parameter 1 is the rect being inserted,
* parameter 2 is its ID. NOTE: *** ID MUST NEVER BE ZERO ***, hence the +1,
* parameter 3 is the root of the tree. Note: its address is passed
* because it can change as a result of this call, therefore no other parts
* of this code should stash its address since it could change undernieth.
* parameter 4 is always zero which means to add from the root.
*/
for (i = 0; i < nrects; i++)
RTreeInsertRect(&rects[i], i + 1, &root, 0); /* i+1 is rect ID. Note: root can change */
nhits = RTreeSearch(root, &search_rect, MySearchCallback, 0);
fprintf(stdout, "Search resulted in %d hits\n", nhits);
fprintf(stdout, "------------------- delete -------------------\n");
RTreeDeleteRect(&rects[34], 35, &root);
nhits = RTreeSearch(root, &search_rect, MySearchCallback, 0);
fprintf(stdout, "Search resulted in %d hits\n", nhits);
RTreeDestroyNode(root);
return 0;
}