Difference between revisions of "User:Jdev/Code/R Tree"

From Robowiki
Jump to navigation Jump to search
(R-Tree implementation for Range Search)
 
(intersection calculation optimised a little)
Line 1: Line 1:
 
Tomcat's simplified implementation of R(ectangle)-Tree [http://en.wikipedia.org/wiki/R-tree]. Feel free to adopt and use it
 
Tomcat's simplified implementation of R(ectangle)-Tree [http://en.wikipedia.org/wiki/R-tree]. Feel free to adopt and use it
 
<syntaxhighlight>
 
<syntaxhighlight>
package lxx.utils.r_tree;
+
public class RTree {
 
+
    private static final int CHILDREN_COUNT = 3;
import lxx.ts_log.attributes.Attribute;
+
     private final RTree parent;
import lxx.utils.IntervalDouble;
 
 
 
import java.util.Arrays;
 
 
 
public class RTree<E extends RTreeEntry> {
 
     private final RTree<E> parent;
 
 
     private final Attribute[] dimensions;
 
     private final Attribute[] dimensions;
 
     private final IntervalDouble[] coveredRange;
 
     private final IntervalDouble[] coveredRange;
  
     private E[] entries = (E[]) new RTreeEntry[32];
+
     private RTreeEntry[] entries = new RTreeEntry[32];
 
     private int nextEntryIdx;
 
     private int nextEntryIdx;
  
     private RTree<E>[] children;
+
     private RTree[] children;
 +
 
 
     private int nextChild = -1;
 
     private int nextChild = -1;
 +
    private Intersection intersection;
 +
 
     private int splitDimensionIdx = -1;
 
     private int splitDimensionIdx = -1;
  
Line 27: Line 24:
 
     }
 
     }
  
     private RTree(RTree<E> parent, Attribute[] dimensions) {
+
     private RTree(RTree parent, Attribute[] dimensions) {
 
         this.parent = parent;
 
         this.parent = parent;
 
         this.dimensions = dimensions;
 
         this.dimensions = dimensions;
Line 36: Line 33:
 
     }
 
     }
  
     public void insert(E entry) {
+
     public void insert(RTreeEntry entry) {
 
         entryCount++;
 
         entryCount++;
 
         for (int i = 0; i < coveredRange.length; i++) {
 
         for (int i = 0; i < coveredRange.length; i++) {
Line 46: Line 43:
 
             if (nextEntryIdx == entries.length) {
 
             if (nextEntryIdx == entries.length) {
 
                 if (singular) {
 
                 if (singular) {
                     E[] newEntries = (E[]) new RTreeEntry[entries.length * 2];
+
                     RTreeEntry[] newEntries = new RTreeEntry[entries.length * 2];
 
                     System.arraycopy(entries, 0, newEntries, 0, entries.length);
 
                     System.arraycopy(entries, 0, newEntries, 0, entries.length);
 
                     entries = newEntries;
 
                     entries = newEntries;
Line 61: Line 58:
 
     private void split() {
 
     private void split() {
 
         splitDimensionIdx = getSplitDimensionIdx();
 
         splitDimensionIdx = getSplitDimensionIdx();
         children = new RTree[3];
+
         children = new RTree[CHILDREN_COUNT];
 
         for (int i = 0; i < children.length; i++) {
 
         for (int i = 0; i < children.length; i++) {
             children[i] = new RTree<E>(this, dimensions);
+
             children[i] = new RTree(this, dimensions);
 
         }
 
         }
 +
        //noinspection ForLoopReplaceableByForEach
 
         for (int i = 0; i < entries.length; i++) {
 
         for (int i = 0; i < entries.length; i++) {
 
             insert(entries[i]);
 
             insert(entries[i]);
Line 83: Line 81:
 
     }
 
     }
  
     private RTree<E> selectChild(RTreeEntry entry) {
+
     private RTree selectChild(RTreeEntry entry) {
 
         final IntervalDouble splitDimensionRange = coveredRange[splitDimensionIdx];
 
         final IntervalDouble splitDimensionRange = coveredRange[splitDimensionIdx];
 
         int idx = (int) ((entry.location.toArray()[dimensions[splitDimensionIdx].id] - splitDimensionRange.a) / (splitDimensionRange.getLength() * 1.05) * children.length);
 
         int idx = (int) ((entry.location.toArray()[dimensions[splitDimensionIdx].id] - splitDimensionRange.a) / (splitDimensionRange.getLength() * 1.05) * children.length);
Line 96: Line 94:
  
 
     private int rangeSearchImpl(IntervalDouble[] range, RTreeEntry[] result) {
 
     private int rangeSearchImpl(IntervalDouble[] range, RTreeEntry[] result) {
         RTree<E> cursor = this;
+
         RTree cursor = this;
 
         cursor.nextChild = 0;
 
         cursor.nextChild = 0;
 +
        cursor.intersection = null;
 
         int resultIdx = 0;
 
         int resultIdx = 0;
 
         do {
 
         do {
             final Intersection intersection = intersection(range, cursor.coveredRange);
+
             if (cursor.intersection == Intersection.NONE) {
            if (intersection == Intersection.NONE) {
 
 
                 cursor = cursor.parent;
 
                 cursor = cursor.parent;
 
             } else if (cursor.children == null) {
 
             } else if (cursor.children == null) {
                 if (intersection == Intersection.FULL) {
+
                 if (cursor.intersection == Intersection.FULL) {
 
                     System.arraycopy(cursor.entries, 0, result, resultIdx, cursor.nextEntryIdx);
 
                     System.arraycopy(cursor.entries, 0, result, resultIdx, cursor.nextEntryIdx);
 
                     resultIdx += cursor.nextEntryIdx;
 
                     resultIdx += cursor.nextEntryIdx;
Line 125: Line 123:
 
                     cursor = cursor.children[cursor.nextChild++];
 
                     cursor = cursor.children[cursor.nextChild++];
 
                     cursor.nextChild = 0;
 
                     cursor.nextChild = 0;
 +
                    cursor.intersection = (cursor.parent.intersection == Intersection.FULL)
 +
                            ? Intersection.FULL
 +
                            : intersection(range, cursor.coveredRange);
 
                 }
 
                 }
 
             }
 
             }

Revision as of 14:07, 21 December 2011

Tomcat's simplified implementation of R(ectangle)-Tree [1]. Feel free to adopt and use it

public class RTree {
    private static final int CHILDREN_COUNT = 3;
    private final RTree parent;
    private final Attribute[] dimensions;
    private final IntervalDouble[] coveredRange;

    private RTreeEntry[] entries = new RTreeEntry[32];
    private int nextEntryIdx;

    private RTree[] children;

    private int nextChild = -1;
    private Intersection intersection;

    private int splitDimensionIdx = -1;

    private boolean singular = true;
    private int entryCount;

    public RTree(Attribute[] dimensions) {
        this(null, dimensions);
    }

    private RTree(RTree parent, Attribute[] dimensions) {
        this.parent = parent;
        this.dimensions = dimensions;
        coveredRange = new IntervalDouble[dimensions.length];
        for (int i = 0; i < coveredRange.length; i++) {
            coveredRange[i] = new IntervalDouble();
        }
    }

    public void insert(RTreeEntry entry) {
        entryCount++;
        for (int i = 0; i < coveredRange.length; i++) {
            coveredRange[i].extend(entry.location.toArray()[dimensions[i].id]);
            singular &= coveredRange[i].a == coveredRange[i].b;
        }
        if (children == null) {
            entries[nextEntryIdx++] = entry;
            if (nextEntryIdx == entries.length) {
                if (singular) {
                    RTreeEntry[] newEntries = new RTreeEntry[entries.length * 2];
                    System.arraycopy(entries, 0, newEntries, 0, entries.length);
                    entries = newEntries;
                } else {
                    split();
                }
            }

        } else {
            selectChild(entry).insert(entry);
        }
    }

    private void split() {
        splitDimensionIdx = getSplitDimensionIdx();
        children = new RTree[CHILDREN_COUNT];
        for (int i = 0; i < children.length; i++) {
            children[i] = new RTree(this, dimensions);
        }
        //noinspection ForLoopReplaceableByForEach
        for (int i = 0; i < entries.length; i++) {
            insert(entries[i]);
        }
        entries = null;
    }

    private int getSplitDimensionIdx() {
        int bestDimensionIdx = 0;
        for (int i = 1; i < dimensions.length; i++) {
            if (coveredRange[i].getLength() / dimensions[i].maxRange.getLength() >
                    coveredRange[bestDimensionIdx].getLength() / dimensions[bestDimensionIdx].maxRange.getLength()) {
                bestDimensionIdx = i;
            }
        }

        return bestDimensionIdx;
    }

    private RTree selectChild(RTreeEntry entry) {
        final IntervalDouble splitDimensionRange = coveredRange[splitDimensionIdx];
        int idx = (int) ((entry.location.toArray()[dimensions[splitDimensionIdx].id] - splitDimensionRange.a) / (splitDimensionRange.getLength() * 1.05) * children.length);
        return children[idx];
    }

    public RTreeEntry[] rangeSearch(IntervalDouble[] range) {
        final RTreeEntry[] res = new RTreeEntry[entryCount];
        final int len = rangeSearchImpl(range, res);
        return Arrays.copyOf(res, len);
    }

    private int rangeSearchImpl(IntervalDouble[] range, RTreeEntry[] result) {
        RTree cursor = this;
        cursor.nextChild = 0;
        cursor.intersection = null;
        int resultIdx = 0;
        do {
            if (cursor.intersection == Intersection.NONE) {
                cursor = cursor.parent;
            } else if (cursor.children == null) {
                if (cursor.intersection == Intersection.FULL) {
                    System.arraycopy(cursor.entries, 0, result, resultIdx, cursor.nextEntryIdx);
                    resultIdx += cursor.nextEntryIdx;
                } else {
                    for (int i = 0; i < cursor.nextEntryIdx; i++) {
                        boolean matches = true;
                        for (int j = 0; j < cursor.dimensions.length && matches; j++) {
                            matches = range[cursor.dimensions[j].id].contains(cursor.entries[i].location.toArray()[cursor.dimensions[j].id]);
                        }
                        if (matches) {
                            result[resultIdx++] = cursor.entries[i];
                        }
                    }
                }
                cursor = cursor.parent;
            } else {
                if (cursor.nextChild == cursor.children.length) {
                    cursor = cursor.parent;
                } else {
                    cursor = cursor.children[cursor.nextChild++];
                    cursor.nextChild = 0;
                    cursor.intersection = (cursor.parent.intersection == Intersection.FULL)
                            ? Intersection.FULL
                            : intersection(range, cursor.coveredRange);
                }
            }
        } while (cursor != null);
        return resultIdx;
    }

    private Intersection intersection(IntervalDouble[] range, IntervalDouble[] coveringRange) {
        Intersection intersectionType = Intersection.FULL;
        for (int i = 0; i < coveringRange.length && intersectionType != Intersection.NONE; i++) {
            double intersection = range[dimensions[i].id].intersection(coveringRange[i]);
            if (intersection < 0) {
                intersectionType = Intersection.NONE;
            } else if (intersection >= 0 && intersection != coveringRange[i].getLength()) {
                intersectionType = Intersection.PARTIALLY;
            }
        }

        return intersectionType;
    }

    private enum Intersection {
        NONE,
        PARTIALLY,
        FULL
    }

}

package lxx.utils.r_tree;

import lxx.ts_log.TurnSnapshot;

public class RTreeEntry {

    public final TurnSnapshot location;

    public RTreeEntry(TurnSnapshot location) {
        this.location = location;
    }

}