123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- /****************************************************************************
- * MODULE: R-Tree library
- *
- * AUTHOR(S): Antonin Guttman - original code
- * Daniel Green (green@superliminal.com) - 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.
- *****************************************************************************/
- #include <stdio.h>
- #include "assert.h"
- #include "index.h"
- #include "card.h"
- #include "split_q.h"
- struct Branch BranchBuf[MAXCARD + 1];
- int BranchCount;
- struct Rect CoverSplit;
- RectReal CoverSplitArea;
- /* variables for finding a partition */
- struct PartitionVars Partitions[METHODS];
- /*-----------------------------------------------------------------------------
- | 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);
- }
- /*-----------------------------------------------------------------------------
- | 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);
- }
- }
- /*-----------------------------------------------------------------------------
- | 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);
- }
- /*-----------------------------------------------------------------------------
- | 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.
- -----------------------------------------------------------------------------*/
- 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);
- }
|