create() {
}
/**
- * Create an RTree by bulk loading, using the STR method. STR: a simple and
- * efficient algorithm for R-tree packing
+ * Create an RTree by bulk loading, using {@link PackAlgo}. Supported pack methods.
+ * 1. STR method. STR: a simple and efficient algorithm for R-tree packing
* http://ieeexplore.ieee.org/abstract/document/582015/
+ * 2. Hilbert method.
+ * http://repository.cmu.edu/cgi/viewcontent.cgi?article=1586&context=compsci
+ *
*
* Note: this method mutates the input entries, the internal order of the List
* may be changed.
*
*
- * @param entries
- * entries to be added to the r-tree
+ * @param entries entries to be added to the r-tree
* @return a loaded RTree
*/
@SuppressWarnings("unchecked")
- public RTree create(List> entries) {
+ public RTree create(List> entries, PackAlgo packAlgo) {
setDefaultCapacity();
Context context = new Context(minChildren.get(), maxChildren.get(),
selector, splitter, (Factory) factory);
- return packingSTR(entries, true, entries.size(), context);
+
+ switch (packAlgo) {
+ case STR:
+ return packingSTR(entries, true, entries.size(), context);
+ case HILBERT:
+ return packingHilbert(entries, true, entries.size(), context);
+ default:
+ return packingSTR(entries, true, entries.size(), context);
+ }
}
private void setDefaultCapacity() {
@@ -366,7 +378,7 @@ private void setDefaultCapacity() {
@SuppressWarnings("unchecked")
private RTree packingSTR(List extends HasGeometry> objects,
- boolean isLeaf, int size, Context context) {
+ boolean isLeaf, int size, Context context) {
int capacity = (int) Math.round(maxChildren.get() * loadingFactor);
int nodeCount = (int) Math.ceil(1.0 * objects.size() / capacity);
@@ -411,6 +423,47 @@ private RTree packingSTR(List extends HasGeometr
return packingSTR(nodes, false, size, context);
}
+ @SuppressWarnings("unchecked")
+ private RTree packingHilbert(List extends HasGeometry> objects,
+ boolean isLeaf, int size, Context context) {
+ int capacity = (int) Math.round(maxChildren.get() * loadingFactor);
+ int nodeCount = (int) Math.ceil(1.0 * objects.size() / capacity);
+
+ if (nodeCount == 0) {
+ return create();
+ } else if (nodeCount == 1) {
+ Node root;
+ if (isLeaf) {
+ root = context.factory().createLeaf((List>) objects, context);
+ } else {
+ root = context.factory().createNonLeaf((List>) objects, context);
+ }
+ return new RTree(of(root), size, context);
+ }
+
+ if (isLeaf) {
+ // 52 bit precision corresponds to ~1 feet.
+ Collections.sort(objects, new HilbertComparator((short)2, 52));
+ }
+
+ List> nodes = new ArrayList>(nodeCount);
+ for (int i = 0; i < objects.size(); i += capacity) {
+ if (isLeaf) {
+ List> entries = (List>) objects.subList(i, Math.min(objects.size(), i + capacity));
+ Node leaf = context.factory().createLeaf(entries, context);
+ nodes.add(leaf);
+ } else {
+ List> children = (List>) objects.subList(i, Math.min(objects.size(), i + capacity));
+ Node nonleaf = context.factory().createNonLeaf(children, context);
+ nodes.add(nonleaf);
+ }
+ }
+
+ return packingHilbert(nodes, false, size, context);
+ }
+
+
+
private static final class MidComparator implements Comparator {
private final short dimension; // leave space for multiple dimensions, 0 for x, 1 for y,
// ...
@@ -433,6 +486,37 @@ private double mid(HasGeometry o) {
}
}
+ private static final class HilbertComparator implements Comparator {
+ private final short dimension; // supporting N dimensions.
+ private final int bitPrecision; // maps this precision in distance.
+
+ public HilbertComparator(short dim, int bitPrecision) {
+ this.dimension = dim;
+ if (bitPrecision >= 0 && bitPrecision < 64) {
+ this.bitPrecision = bitPrecision;
+ } else {
+ this.bitPrecision = 24; // default to ~10000 meters
+ }
+ }
+
+ @Override
+ public int compare(HasGeometry o1, HasGeometry o2) {
+ Long h1 = twoDCHilbertIndex(o1);
+ Long h2 = twoDCHilbertIndex(o2);
+ //System.out.println("h1: " + h1 + ", h2: " + h2);
+ return Long.compare(h1, h2);
+ }
+
+ private Long twoDCHilbertIndex(HasGeometry o) {
+ Rectangle mbr = o.geometry().mbr();
+ HilbertCurve c = HilbertCurve.bits(bitPrecision).dimensions(dimension);
+ int centerX = (int) ((mbr.x1() + mbr.x2()) / 2);
+ int centerY = (int) ((mbr.y1() + mbr.y2()) / 2);
+ BigInteger index = c.index(centerX, centerY);
+ return index.longValue();
+ }
+ }
+
}
/**
diff --git a/src/test/java/com/github/davidmoten/rtree/BenchmarksRTree.java b/src/test/java/com/github/davidmoten/rtree/BenchmarksRTree.java
index 8cc2fef4..97a845c2 100644
--- a/src/test/java/com/github/davidmoten/rtree/BenchmarksRTree.java
+++ b/src/test/java/com/github/davidmoten/rtree/BenchmarksRTree.java
@@ -1,5 +1,6 @@
package com.github.davidmoten.rtree;
+import static com.github.davidmoten.rtree.PackAlgo.STR;
import static com.github.davidmoten.rtree.Utilities.entries1000;
import java.io.ByteArrayInputStream;
@@ -154,12 +155,12 @@ public RTree