add QuadTree implementing SpatialIndex

- add SpatialIndex interface
This commit is contained in:
Hannes Janetzek 2014-06-10 19:12:40 +02:00
parent 4e94a8f269
commit b460b4ebaf
7 changed files with 749 additions and 278 deletions

View File

@ -0,0 +1,234 @@
package org.oscim.utils;
import static java.lang.System.currentTimeMillis;
import static java.lang.System.out;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.junit.Assert;
import org.junit.Test;
import org.oscim.core.Box;
import org.oscim.utils.SpatialIndex.SearchCb;
public class QuadTreeTest {
final static Random rand = new Random((long) (Math.PI * 10000000));
public class Item {
final int val;
final Box bbox;;
Item(Box bbox, int val) {
this.val = val;
this.bbox = new Box(bbox);
}
@Override
public String toString() {
return String.valueOf(val) + ' ' + bbox;
}
}
ArrayList<Item> fillRandomTree(SpatialIndex<Item> q, int numItems) {
Box box = new Box();
ArrayList<Item> items = new ArrayList<Item>(numItems + 16);
for (int i = 0; i < numItems; i++) {
box.minX = (int) (rand.nextDouble() * 10000 - 5000);
box.minY = (int) (rand.nextDouble() * 10000 - 5000);
box.maxX = (int) (box.minX + rand.nextDouble() * 500);
box.maxY = (int) (box.minY + rand.nextDouble() * 500);
Item it = new Item(box, i);
q.insert(box, it);
items.add(it);
}
return items;
}
@Test
public void shouldWork0() {
SpatialIndex<Item> q = new QuadTree<Item>(Short.MAX_VALUE + 1, 16);
//SpatialIndex<Item> q = new RTree<Item>();
int numItems = 10000;
List<Item> items = fillRandomTree(q, numItems);
final int[] found = { 0 };
final int[] matched = { 0 };
for (Item it : items) {
int f = matched[0];
q.search(it.bbox, new SearchCb<Item>() {
@Override
public boolean call(Item item, Object context) {
found[0]++;
if (context == item) {
matched[0]++;
return false;
}
return true;
}
}, it);
if (f == matched[0])
out.println((it.bbox.maxX - it.bbox.minX)
+ " x " + (it.bbox.maxY - it.bbox.minY)
+ " ==> " + it);
}
//out.println("m:" + matched[0] + " f:" + found[0]);
Assert.assertEquals(numItems, matched[0]);
Assert.assertEquals(numItems, q.size());
}
@Test
public void shouldWork1() {
long time = currentTimeMillis();
for (int i = 0; i < 10; i++) {
shouldWork0();
}
long now = currentTimeMillis();
out.println("==>" + (now - time) / 10.0f + "ms");
}
@Test
public void shouldWork6() {
SpatialIndex<Item> q = new QuadTree<Item>(Short.MAX_VALUE + 1, 16);
Box box = new Box(-4184.653317773969,
3183.6174297948446,
-4088.3197324911957,
3222.7770427421046);
Item it = new Item(box, 1);
q.insert(box, it);
q.search(it.bbox, new SearchCb<Item>() {
@Override
public boolean call(Item item, Object context) {
out.println("==> " + item + " " + (context == item));
return true;
}
}, it);
Assert.assertEquals(1, q.size());
}
@Test
public void shouldWork7() {
SpatialIndex<Item> q = new QuadTree<Item>(Short.MAX_VALUE + 1, 14);
//SpatialIndex<Item> q = new RTree<Item>();
int numItems = 10000;
List<Item> items = fillRandomTree(q, numItems);
Assert.assertEquals(numItems, q.size());
int cnt = numItems;
for (Item it : items) {
if (!q.remove(it.bbox, it)) {
out.println((it.bbox.maxX - it.bbox.minX)
+ " x " + (it.bbox.maxY - it.bbox.minY)
+ " ==> " + it);
q.search(it.bbox, new SearchCb<Item>() {
@Override
public boolean call(Item item, Object context) {
if (context == item) {
out.println("found...");
return false;
}
return true;
}
}, it);
}
Assert.assertEquals(--cnt, q.size());
}
items = fillRandomTree(q, numItems);
Assert.assertEquals(numItems, q.size());
cnt = numItems;
for (Item it : items) {
if (!q.remove(it.bbox, it))
out.println((it.bbox.maxX - it.bbox.minX)
+ " x " + (it.bbox.maxY - it.bbox.minY)
+ " => " + it);
Assert.assertEquals(--cnt, q.size());
}
Assert.assertEquals(0, q.size());
out.println("");
}
@Test
public void shouldWork8() {
SpatialIndex<Item> q = new QuadTree<Item>(Short.MAX_VALUE + 1, 16);
//SpatialIndex<Item> q = new RTree<Item>();
int numItems = 10000;
List<Item> items = fillRandomTree(q, numItems);
final int[] found = { 0 };
final int[] matched = { 0 };
for (Item it : items) {
int f = matched[0];
int cnt = 0;
for (Item it2 : items) {
if (it2.bbox.overlap(it.bbox))
cnt++;
}
found[0] = 0;
q.search(it.bbox, new SearchCb<Item>() {
@Override
public boolean call(Item item, Object context) {
found[0]++;
if (context == item) {
matched[0]++;
//return false;
}
return true;
}
}, it);
if (found[0] != cnt) {
out.println("not found " + (found[0] - cnt));
}
//Assert.assertEquals(cnt, found[0]);
if (f == matched[0])
out.println((it.bbox.maxX - it.bbox.minX)
+ " x " + (it.bbox.maxY - it.bbox.minY)
+ " ==> " + it);
}
//out.println("m:" + matched[0] + " f:" + found[0]);
Assert.assertEquals(numItems, matched[0]);
Assert.assertEquals(numItems, q.size());
}
public static void main(String[] args) {
QuadTreeTest qt = new QuadTreeTest();
long time = currentTimeMillis();
for (int i = 0; i < 100; i++) {
qt.shouldWork0();
long now = currentTimeMillis();
out.println("==>" + (now - time));
time = now;
}
}
}

View File

@ -21,7 +21,7 @@ import org.oscim.layers.tile.vector.VectorTileLoader;
import org.oscim.layers.tile.vector.labeling.LabelTileLoaderHook; import org.oscim.layers.tile.vector.labeling.LabelTileLoaderHook;
import org.oscim.renderer.elements.ElementLayers; import org.oscim.renderer.elements.ElementLayers;
import org.oscim.utils.pool.Inlist; import org.oscim.utils.pool.Inlist;
import org.oscim.utils.quadtree.Node; import org.oscim.utils.quadtree.TreeNode;
import org.oscim.utils.quadtree.QuadTree; import org.oscim.utils.quadtree.QuadTree;
/** /**
@ -32,7 +32,7 @@ import org.oscim.utils.quadtree.QuadTree;
*/ */
public class MapTile extends Tile { public class MapTile extends Tile {
public static class TileNode extends Node<TileNode, MapTile> { public static class TileNode extends TreeNode<TileNode, MapTile> {
} }

View File

@ -0,0 +1,76 @@
package org.oscim.utils;
import java.util.List;
import org.oscim.core.Box;
import org.oscim.utils.pool.Pool;
import org.oscim.utils.quadtree.BoxTree;
import org.oscim.utils.quadtree.BoxTree.BoxItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class QuadTree<T> extends BoxTree<BoxItem<T>, T> implements SpatialIndex<T> {
public QuadTree(int extents, int maxDepth) {
super(extents, maxDepth);
}
static final Logger log = LoggerFactory.getLogger(QuadTree.class);
final Pool<BoxItem<T>> boxPool = new Pool<BoxItem<T>>() {
@Override
protected BoxItem<T> createItem() {
return new BoxItem<T>();
}
};
private BoxItem<T> getBox(Box box) {
BoxItem<T> it = boxPool.get();
it.x1 = (int) box.minX;
it.y1 = (int) box.minY;
it.x2 = (int) box.maxX;
it.y2 = (int) box.maxY;
return it;
}
@Override
public void insert(Box box, T item) {
insert(new BoxItem<T>(box, item));
}
@Override
public boolean remove(Box box, T item) {
BoxItem<T> bbox = getBox(box);
boolean ok = remove(bbox, item);
boxPool.release(bbox);
return ok;
}
static class CollectCb<T> implements SearchCb<T> {
@SuppressWarnings("unchecked")
@Override
public boolean call(T item, Object context) {
List<T> l = (List<T>) context;
l.add(item);
return true;
}
}
final CollectCb<T> collectCb = new CollectCb<T>();
@Override
public List<T> search(Box bbox, List<T> results) {
BoxItem<T> box = getBox(bbox);
search(box, collectCb, results);
boxPool.release(box);
return results;
}
@Override
public int search(Box bbox, SearchCb<T> cb, Object context) {
BoxItem<T> box = getBox(bbox);
search(box, cb, context);
boxPool.release(box);
return 0;
}
}

View File

@ -0,0 +1,21 @@
package org.oscim.utils;
import java.util.List;
import org.oscim.core.Box;
public interface SpatialIndex<T> {
public interface SearchCb<T> {
boolean call(T item, Object context);
}
public void insert(Box box, T item);
public boolean remove(Box box, T item);
public List<T> search(Box bbox, List<T> results);
public int search(Box bbox, SearchCb<T> cb, Object context);
public int size();
}

View File

@ -1,6 +1,10 @@
package org.oscim.utils.quadtree; package org.oscim.utils.quadtree;
import java.util.Arrays;
import org.oscim.utils.SpatialIndex.SearchCb;
import org.oscim.utils.pool.Inlist; import org.oscim.utils.pool.Inlist;
import org.oscim.utils.pool.Pool;
import org.oscim.utils.quadtree.BoxTree.BoxItem; import org.oscim.utils.quadtree.BoxTree.BoxItem;
import org.oscim.utils.quadtree.BoxTree.BoxNode; import org.oscim.utils.quadtree.BoxTree.BoxNode;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -12,28 +16,55 @@ import org.slf4j.LoggerFactory;
* *
* ... in case this generic isnt obvious at first sight. * ... in case this generic isnt obvious at first sight.
* */ * */
public abstract class BoxTree<Box extends BoxItem<E>, E> extends QuadTree<BoxNode<Box>, Box> { public class BoxTree<T extends BoxItem<E>, E> extends TileIndex<BoxNode<T>, T> {
final static Logger log = LoggerFactory.getLogger(BoxTree.class); final static Logger log = LoggerFactory.getLogger(BoxTree.class);
static boolean dbg = false; static boolean dbg = false;
protected final int extents; protected final int extents;
protected final int maxDepth; protected final int maxDepth;
private final static int MAX_STACK = 32;
public static class BoxNode<T extends BoxItem<?>> extends Node<BoxNode<T>, T> { static class Stack<E> extends Inlist<Stack<E>> {
// for non-recursive traversal /** Top of stack index */
BoxNode<T> next; int tos;
// TODO make final? or update to the actual used extent?
final E[] nodes;
@SuppressWarnings("unchecked")
Stack() {
nodes = (E[]) new BoxNode[MAX_STACK];
}
void push(E node) {
nodes[tos] = node;
tos++;
}
/** Pop element off iteration stack (For internal use only) */
E pop() {
// assert (tos > 0);
nodes[tos--] = null;
return (E) nodes[tos];
}
E node() {
return (E) nodes[tos];
}
boolean empty() {
return tos <= 0;
}
}
public static class BoxNode<T extends BoxItem<?>> extends TreeNode<BoxNode<T>, T> {
// TODO this is redundant - use tile ids
public int x1; public int x1;
public int y1; public int y1;
public int x2; public int x2;
public int y2; public int y2;
//BoxItem<T> list; // inherits BoxItem<E> item;
public boolean overlaps(T it) {
return (x1 < it.x2) && (y1 < it.y2) && (it.x1 < x2) && (it.y1 < y2);
}
@Override @Override
public String toString() { public String toString() {
@ -44,11 +75,19 @@ public abstract class BoxTree<Box extends BoxItem<E>, E> extends QuadTree<BoxNod
public static class BoxItem<T> extends Inlist<BoxItem<T>> { public static class BoxItem<T> extends Inlist<BoxItem<T>> {
public BoxItem(int x1, int y1, int x2, int y2) { public BoxItem(int x1, int y1, int x2, int y2) {
this.x1 = x1; this.x1 = x1;
this.x2 = x2;
this.y1 = y1; this.y1 = y1;
this.x2 = x2;
this.y2 = y2; this.y2 = y2;
} }
public BoxItem(org.oscim.core.Box box, T item) {
this.x1 = (int) box.minX;
this.y1 = (int) box.minY;
this.x2 = (int) box.maxX;
this.y2 = (int) box.maxY;
this.item = item;
}
@Override @Override
public String toString() { public String toString() {
return x1 + "," + y1 + "/" + x2 + "," + y2; return x1 + "," + y1 + "/" + x2 + "," + y2;
@ -66,7 +105,7 @@ public abstract class BoxTree<Box extends BoxItem<E>, E> extends QuadTree<BoxNod
public int y2; public int y2;
public boolean overlaps(BoxItem<T> it) { public boolean overlaps(BoxItem<T> it) {
return (x1 < it.x2) && (it.x1 < x2) && (y1 < it.y2) && (it.y1 < y2); return !((x1 > it.x2) || (it.x1 > x2) || (y1 > it.y2) || (it.y1 > y2));
} }
} }
@ -74,19 +113,16 @@ public abstract class BoxTree<Box extends BoxItem<E>, E> extends QuadTree<BoxNod
boolean process(T item); boolean process(T item);
} }
// public class NodeVistor<T> implements Visitor<BoxNode<T>> { boolean isPowerOfTwo(int x) {
// return ((x > 0) && (x & (x - 1)) == 0);
// @Override }
// public boolean process(BoxNode<T> item) {
//
// return false;
// }
//
// }
public BoxTree(int extents, int maxDepth) { public BoxTree(int extents, int maxDepth) {
super(); super();
// size is -extents to +extents if (!isPowerOfTwo(extents))
throw new IllegalArgumentException("Extents must be power of two!");
//size is -extents to +extents
this.root.x1 = -extents; this.root.x1 = -extents;
this.root.y1 = -extents; this.root.y1 = -extents;
this.root.x2 = extents; this.root.x2 = extents;
@ -97,169 +133,231 @@ public abstract class BoxTree<Box extends BoxItem<E>, E> extends QuadTree<BoxNod
} }
@Override @Override
public BoxNode<Box> create() { public BoxNode<T> create() {
return new BoxNode<Box>(); return new BoxNode<T>();
} }
@Override @Override
public void removeItem(Box item) { public void removeItem(T item) {
} }
@SuppressWarnings("unchecked") Pool<Stack<BoxNode<T>>> stackPool = new Pool<Stack<BoxNode<T>>>() {
public int query(Box box) { @Override
if (box.x1 > box.x2 || box.y1 > box.y2) protected Stack<BoxNode<T>> createItem() {
throw new IllegalArgumentException(); return new Stack<BoxNode<T>>();
}
int x1 = box.x1; protected boolean clearItem(Stack<BoxNode<T>> item) {
int x2 = box.x2; if (item.tos != 0) {
int y1 = box.y1; item.tos = 0;
int y2 = box.y2; Arrays.fill(item.nodes, null);
}
return true;
};
};
BoxNode<Box> cur, c; public boolean search(T box, SearchCb<E> cb, Object ctxt) {
BoxNode<Box> stack = root; BoxNode<T> n;
int result = 0;
boolean drop = false; Stack<BoxNode<T>> stack = stackPool.get();
stack.push(root);
O: while (stack != null) { O: while (!stack.empty()) {
/** pop cur from stack */ n = stack.pop();
cur = stack;
stack = stack.next;
/** process overlapping items from cur node */ /* process overlapping items from cur node */
Box prev = cur.item; for (BoxItem<E> it = n.item; it != null; it = it.next) {
if (it.overlaps(box)) {
for (Box it = cur.item; it != null; it = (Box) it.next) { if (!cb.call(it.item, ctxt))
if ((x1 <= it.x2) && (x2 >= it.x1) &&
(y1 <= it.y2) && (y2 >= it.y1)) {
result = process(box, it);
if (result > 0) {
if (dbg)
log.debug("{} overlap {} {}", result, box, it);
drop = true;
break O; break O;
}
}
BoxNode<T> p = n.parent;
/* push next node on same level onto stack */
switch (n.id) {
case 0:
if (overlaps(p.child01, box)) {
stack.push(p.child01);
break;
} }
case 1:
if (overlaps(p.child10, box)) {
stack.push(p.child10);
break;
}
case 2:
if (overlaps(p.child11, box)) {
stack.push(p.child11);
break;
}
default:
break;
}
/* push next level child onto stack */
if (overlaps(n.child00, box))
stack.push(n.child00);
else if (overlaps(n.child01, box))
stack.push(n.child01);
else if (overlaps(n.child10, box))
stack.push(n.child10);
else if (overlaps(n.child11, box))
stack.push(n.child11);
}
stackPool.release(stack);
return false;
}
if (result < 0) { public interface SearchBoxCb<T extends BoxItem<?>> {
result = 0; boolean call(T item);
if (dbg) }
log.debug("remove overlap {} {}", box, it);
// remove this itemchild = cur.child11;
//cur.item = Inlist.remove(cur.item, it);
if (it == cur.item)
prev = cur.item = it;
else
prev.next = it.next;
continue; public boolean search(T box, SearchBoxCb<T> cb) {
BoxNode<T> n;
BoxNode<T> start = getNode(box, false);
if (start == null)
return false;
Stack<BoxNode<T>> stack = stackPool.get();
stack.push(start);
while (!stack.empty()) {
n = stack.pop();
/* process overlapping items from cur node */
for (BoxItem<E> it = n.item; it != null; it = it.next) {
if (it.overlaps(box)) {
@SuppressWarnings("unchecked")
T item = (T) it;
if (!cb.call(item)) {
stackPool.release(stack);
return false;
} }
} }
prev = it;
} }
/** put children on stack which overlap with box */ BoxNode<T> p = n.parent;
if ((c = cur.child00) != null &&
(x1 < c.x2) && (y1 < c.y2) && /* push next node on same level onto stack */
(c.x1 < x2) && (c.y1 < y2)) { switch (n.id) {
c.next = stack; case 0:
stack = c; if (overlaps(p.child01, box)) {
} stack.push(p.child01);
if ((c = cur.child01) != null && break;
(x1 < c.x2) && (y1 < c.y2) && }
(c.x1 < x2) && (c.y1 < y2)) { case 1:
c.next = stack; if (overlaps(p.child10, box)) {
stack = c; stack.push(p.child10);
} break;
if ((c = cur.child10) != null && }
(x1 < c.x2) && (y1 < c.y2) && case 2:
(c.x1 < x2) && (c.y1 < y2)) { if (overlaps(p.child11, box)) {
c.next = stack; stack.push(p.child11);
stack = c; break;
} }
if ((c = cur.child11) != null && default:
(x1 < c.x2) && (y1 < c.y2) && break;
(c.x1 < x2) && (c.y1 < y2)) {
c.next = stack;
stack = c;
} }
/* push next level child onto stack */
if (overlaps(n.child00, box))
stack.push(n.child00);
else if (overlaps(n.child01, box))
stack.push(n.child01);
else if (overlaps(n.child10, box))
stack.push(n.child10);
else if (overlaps(n.child11, box))
stack.push(n.child11);
} }
stackPool.release(stack);
/** dont keep dangling references */ return true;
///* gwt optimizer found this cannot be reached :) */
while (stack != null)
stack = stack.next;
return drop ? 1 : 0;
} }
public abstract boolean collectAll(BoxNode<Box> node); private static boolean overlaps(BoxNode<?> a, BoxItem<?> b) {
return a != null && !((a.x1 > b.x2) || (b.x1 > a.x2) || (a.y1 > b.y2) || (b.y1 > a.y2));
public int all() {
return all(root);
} }
public int all(BoxNode<Box> node) { public interface SearchNodeCb<E extends BoxNode<?>> {
boolean call(E item);
}
BoxNode<Box> cur, c; public boolean collect(SearchNodeCb<BoxNode<T>> cb) {
BoxNode<Box> stack = node; BoxNode<T> n;
while (stack != null) { Stack<BoxNode<T>> stack = stackPool.get();
cur = stack;
stack = stack.next;
if (cur.item != null && !collectAll(cur)) stack.push(root);
break;
if ((c = cur.child00) != null) { while (!stack.empty()) {
c.next = stack; n = stack.pop();
stack = c;
} /* process overlapping items from cur node */
if ((c = cur.child01) != null) { cb.call(n);
c.next = stack;
stack = c; BoxNode<T> p = n.parent;
}
if ((c = cur.child10) != null) { /* push next node on same level onto stack */
c.next = stack; switch (n.id) {
stack = c; case 0:
} if (p.child01 != null) {
if ((c = cur.child11) != null) { stack.push(p.child01);
c.next = stack; break;
stack = c; }
case 1:
if (p.child10 != null) {
stack.push(p.child10);
break;
}
case 2:
if (p.child11 != null) {
stack.push(p.child11);
break;
}
default:
break;
} }
/* push next level child onto stack */
if (n.child00 != null)
stack.push(n.child00);
else if (n.child01 != null)
stack.push(n.child01);
else if (n.child10 != null)
stack.push(n.child10);
else if (n.child11 != null)
stack.push(n.child11);
} }
stackPool.release(stack);
// dont keep dangling references return false;
while (stack != null)
stack = stack.next;
return 0;
} }
public abstract int process(Box box, Box it); public BoxNode<T> create(BoxNode<T> parent, int i) {
BoxNode<T> node;
if (pool != null) {
node = pool;
pool = pool.parent;
} else
node = new BoxNode<T>();
node.parent = parent;
public BoxNode<Box> create(BoxNode<Box> parent, int i) {
BoxNode<Box> node = new BoxNode<Box>();
int size = (parent.x2 - parent.x1) >> 1; int size = (parent.x2 - parent.x1) >> 1;
node.x1 = parent.x1; node.x1 = parent.x1;
node.y1 = parent.y1; node.y1 = parent.y1;
if (i == 0) { if (i == 0) {
// top-left
parent.child00 = node; parent.child00 = node;
} else if (i == 1) { } else if (i == 1) {
// bottom-left parent.child01 = node;
parent.child10 = node;
node.y1 += size; node.y1 += size;
} else if (i == 2) { } else if (i == 2) {
// top-right parent.child10 = node;
parent.child01 = node;
node.x1 += size; node.x1 += size;
} else { } else {
// bottom-right
parent.child11 = node; parent.child11 = node;
node.x1 += size; node.x1 += size;
node.y1 += size; node.y1 += size;
@ -267,93 +365,196 @@ public abstract class BoxTree<Box extends BoxItem<E>, E> extends QuadTree<BoxNod
node.x2 = node.x1 + size; node.x2 = node.x1 + size;
node.y2 = node.y1 + size; node.y2 = node.y1 + size;
node.id = (byte) i;
node.parent = parent;
return node; return node;
} }
public void insert(Box box) { public void insert(T box) {
if (box.x1 > box.x2 || box.y1 > box.y2) if (box.x1 > box.x2 || box.y1 > box.y2)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
BoxNode<Box> cur = root; if (box.next != null)
BoxNode<Box> child = null; throw new IllegalStateException("BoxItem is list");
BoxNode<T> cur = root;
BoxNode<T> child = null;
// tile position in tree
//int px = 0, py = 0;
int idX = 0, idY = 0;
int x1 = box.x1; int x1 = box.x1;
int x2 = box.x2; int x2 = box.x2;
int y1 = box.y1; int y1 = box.y1;
int y2 = box.y2; int y2 = box.y2;
for (int level = 0; level <= maxDepth; level++) { for (int level = 0; level <= maxDepth; level++) {
// half size of tile at current z cur.refs++;
//int hsize = (extents >> level);
/* half size of tile at current level */
int hsize = (cur.x2 - cur.x1) >> 1; int hsize = (cur.x2 - cur.x1) >> 1;
// center of tile (shift by -extents) /* center of tile */
//int cx = px + hsize - extents;
//int cy = py + hsize - extents;
int cx = cur.x1 + hsize; int cx = cur.x1 + hsize;
int cy = cur.y1 + hsize; int cy = cur.y1 + hsize;
child = null; child = null;
//int childPos = -1;
//log.debug(cx + ":" + cy + " " + hsize); if (level < maxDepth) {
if (x2 <= cx) { if (x2 < cx) {
if (y2 <= cy) { if (y2 < cy) {
if ((child = cur.child00) == null) if ((child = cur.child00) == null)
child = create(cur, 0); child = create(cur, 0);
} else if (y1 >= cy) {
if ((child = cur.child01) == null)
child = create(cur, 1);
}
} }
// should be else? if (x1 >= cx) {
if (y1 >= cy) { if (y2 < cy) {
if ((child = cur.child10) == null) if ((child = cur.child10) == null)
child = create(cur, 1); child = create(cur, 2);
idX++; } else if (y1 >= cy) {
if ((child = cur.child11) == null)
child = create(cur, 3);
}
} }
} }
if (x1 >= cx) {
if (y2 <= cy) {
if ((child = cur.child01) == null)
child = create(cur, 2);
idY++;
}
if (y1 >= cy) {
if ((child = cur.child11) == null)
child = create(cur, 3);
idX++;
idY++;
}
}
//log.debug("child {}", child);
if (cur == minNode && child != null) if (child == null) {
minNode = cur; /* push item onto list of this node */
if (child == null || level == maxDepth) {
// push item onto list of this node
box.next = cur.item; box.next = cur.item;
cur.item = box; cur.item = box;
if (dbg) if (dbg)
log.debug("insert at: " + level + " / " + idX + ":" log.debug("insert: " + level
+ idY + " -- " + x1 + ":" + y1 + " cnt:" + Inlist.size(cur.item) + " " + x1 + ":" + y1
+ " /" + (x2) + "x" + (y2)); + " /" + (x2) + "x" + (y2) + " " + box.item);
break; break;
} }
cur = child; cur = child;
} }
} }
public void setMinNode(int x1, int y1, int x2, int y2) { public boolean remove(T box, E item) {
/* TODO find lowest node that fully contains the region if (box.x1 > box.x2 || box.y1 > box.y2)
* and set it as start for following queries */ throw new IllegalArgumentException();
BoxNode<T> cur = root;
BoxNode<T> child = null;
int x1 = box.x1;
int x2 = box.x2;
int y1 = box.y1;
int y2 = box.y2;
for (int level = 0; level <= maxDepth; level++) {
/* half size of tile at current level */
int hsize = (cur.x2 - cur.x1) >> 1;
/* center of tile */
int cx = cur.x1 + hsize;
int cy = cur.y1 + hsize;
child = null;
if (level < maxDepth) {
if (x2 < cx) {
if (y2 < cy) {
if ((child = cur.child00) == null)
return false;
} else if (y1 >= cy) {
if ((child = cur.child01) == null)
return false;
}
} else if (x1 >= cx) {
if (y2 < cy) {
if ((child = cur.child10) == null)
return false;
} else if (y1 >= cy) {
if ((child = cur.child11) == null)
return false;
}
}
}
if (child == null) {
/* push item onto list of this node */
BoxItem<E> prev = cur.item;
for (BoxItem<E> it = cur.item; it != null; it = it.next) {
if (it.item == item) {
if (dbg)
log.debug("remove: " + level
+ " cnt:" + Inlist.size(cur.item) + " " + x1 + ":" + y1
+ " /" + (x2) + "x" + (y2) + " " + item);
if (cur.item == it) {
// FUNKY GENERICS...
@SuppressWarnings("unchecked")
T b = (T) it.next;
cur.item = b;
} else
prev.next = it.next;
remove(cur);
return true;
}
prev = it;
}
return false;
}
cur = child;
}
return false;
} }
public abstract boolean process(BoxNode<Box> nodes); public BoxNode<T> getNode(T box, boolean create) {
if (box.x1 > box.x2 || box.y1 > box.y2)
throw new IllegalArgumentException();
BoxNode<T> cur = root;
BoxNode<T> child = null;
int x1 = box.x1;
int x2 = box.x2;
int y1 = box.y1;
int y2 = box.y2;
for (int level = 0; level <= maxDepth; level++) {
cur.refs++;
/* half size of tile at current z */
int hsize = (cur.x2 - cur.x1) >> 1;
/* center of tile (shift by -extents) */
int cx = cur.x1 + hsize;
int cy = cur.y1 + hsize;
child = null;
if (x2 < cx) {
if (y2 < cy) {
if ((child = cur.child00) == null && create)
child = create(cur, 0);
} else if (y1 >= cy) {
if ((child = cur.child01) == null && create)
child = create(cur, 1);
}
}
if (x1 >= cx) {
if (y2 < cy) {
if ((child = cur.child10) == null && create)
child = create(cur, 2);
} else if (y1 >= cy) {
if ((child = cur.child11) == null && create)
child = create(cur, 3);
}
}
if (child == null || level == maxDepth)
return cur;
cur = child;
}
return null;
}
public void clear() { public void clear() {
root.child00 = null; root.child00 = null;
@ -361,67 +562,10 @@ public abstract class BoxTree<Box extends BoxItem<E>, E> extends QuadTree<BoxNod
root.child10 = null; root.child10 = null;
root.child11 = null; root.child11 = null;
root.item = null; root.item = null;
root.refs = 0;
//root = create();
//root.parent = root;
} }
static class Item extends BoxItem<Integer> { public int size() {
public Item(int x1, int y1, int x2, int y2) { return root.refs;
super(x1, y1, x2, y2);
}
public Item() {
// TODO Auto-generated constructor stub
}
} }
// static {
// System.setProperty(org.slf4j.impl.SimpleLogger.DEFAULT_LOG_LEVEL_KEY, "TRACE");
//}
// public static void main(String[] args) {
//
// BoxTree<Item, Integer> tree = new BoxTree<Item, Integer>(4096, 12) {
//
// @Override
// public int process(Item box, Item it) {
// System.out.println("found ... " + box + "\t\t" + it);
// return 1;
// }
//
// @Override
// public boolean process(BoxNode<Item> nodes) {
// System.out.println("found ... ");
// //for (BoxItem it = nodes.item; it != null; it = it.next) {
// // System.out.println("it: " + it.x1 + "/" + it.y1);
// //}
//
// // TODO Auto-generated method stub
// return false;
// }
//
// @Override
// public boolean collectAll(BoxNode<Item> nodes) {
// for (Item it = nodes.item; it != null; it = (Item) it.next) {
// System.out.println("all: " + it);
// }
// return false;
// }
//
// };
// //[VtmAsyncExecutor] DEBUG org.oscim.utils.quadtree.BoxTree - insert at: 8 / 9:0 -- -631:266 /106x187
//
// tree.insert(new Item(-631, 266, -631 + 106, 266 + 187));
//
// //tree.insert(new Item(-40, -40, -32, -32));
// // tree.insert(new Item(-60, -60, -40, -40));
// // tree.insert(new Item(100, 10, 200, 100));
// // tree.insert(new Item(100, 200, 200, 300));
// // tree.insert(new Item(100, 200, 1000, 1000));
// //
// // tree.query(new Item(-100, -100, 10, 10));
// //tree.query(new Item(10, 10, 100, 100));
//
// tree.all();
// }
} }

View File

@ -19,16 +19,17 @@ package org.oscim.utils.quadtree;
/** /**
* A quad tree for the standard map tiling schema. * A quad tree for the standard map tiling schema.
*/ */
public abstract class QuadTree<T extends Node<T, E>, E> { public abstract class QuadTree<T extends TreeNode<T, E>, E> {
protected final T root; protected final T root;
protected T minNode; //protected T minNode;
protected T pool; protected T pool;
public QuadTree() { public QuadTree() {
root = create(); root = create();
root.id = -1;
root.parent = root; root.parent = root;
} }
@ -153,15 +154,15 @@ public abstract class QuadTree<T extends Node<T, E>, E> {
while (cur != root) { while (cur != root) {
if (cur == null) if (cur == null)
throw new IllegalArgumentException("item not in index"); throw new IllegalStateException("Item not in index");
// keep pointer to parent /* keep pointer to parent */
next = cur.parent; next = cur.parent;
cur.refs--; cur.refs--;
// if current node has no children /* if current node has no children */
if (cur.refs == 0) { if (cur.refs == 0) {
// unhook from parent /* unhook from parent */
switch (cur.id) { switch (cur.id) {
case 0: case 0:
@ -178,7 +179,7 @@ public abstract class QuadTree<T extends Node<T, E>, E> {
break; break;
} }
// add item back to pool /* add item back to pool */
cur.parent = pool; cur.parent = pool;
pool = cur; pool = cur;
} }

View File

@ -16,36 +16,31 @@
*/ */
package org.oscim.utils.quadtree; package org.oscim.utils.quadtree;
public class Node<T extends Node<T, E>, E> { public class TreeNode<T extends TreeNode<T, E>, E> {
public T parent; public T parent;
/** top-left */
public T child00; public T child00;
public T child10;
/** bottom-left */
public T child01; public T child01;
/** top-right */
public T child10;
/** bottom-right */
public T child11; public T child11;
/** payload */
public E item; public E item;
// id of this child relative to parent /** id of this child relative to parent */
byte id; int id;
// number of children and grandchildren /** number of children and grandchildren */
int refs = 0; int refs = 0;
// public byte getId() {
// if (parent.child00 == this)
// return 0;
// if (parent.child01 == this)
// return 1;
// if (parent.child10 == this)
// return 2;
// if (parent.child00 == this)
// return 3;
//
// // is root node
// //if (parent == this)
// return -1;
// }
public E parent() { public E parent() {
return parent.item; return parent.item;
} }