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.renderer.elements.ElementLayers;
import org.oscim.utils.pool.Inlist;
import org.oscim.utils.quadtree.Node;
import org.oscim.utils.quadtree.TreeNode;
import org.oscim.utils.quadtree.QuadTree;
/**
@ -32,7 +32,7 @@ import org.oscim.utils.quadtree.QuadTree;
*/
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;
import java.util.Arrays;
import org.oscim.utils.SpatialIndex.SearchCb;
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.BoxNode;
import org.slf4j.Logger;
@ -12,28 +16,55 @@ import org.slf4j.LoggerFactory;
*
* ... 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);
static boolean dbg = false;
protected final int extents;
protected final int maxDepth;
private final static int MAX_STACK = 32;
public static class BoxNode<T extends BoxItem<?>> extends Node<BoxNode<T>, T> {
// for non-recursive traversal
BoxNode<T> next;
// TODO make final? or update to the actual used extent?
static class Stack<E> extends Inlist<Stack<E>> {
/** Top of stack index */
int tos;
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 y1;
public int x2;
public int y2;
//BoxItem<T> list;
public boolean overlaps(T it) {
return (x1 < it.x2) && (y1 < it.y2) && (it.x1 < x2) && (it.y1 < y2);
}
// inherits BoxItem<E> item;
@Override
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 BoxItem(int x1, int y1, int x2, int y2) {
this.x1 = x1;
this.x2 = x2;
this.y1 = y1;
this.x2 = x2;
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
public String toString() {
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 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,18 +113,15 @@ public abstract class BoxTree<Box extends BoxItem<E>, E> extends QuadTree<BoxNod
boolean process(T item);
}
// public class NodeVistor<T> implements Visitor<BoxNode<T>> {
//
// @Override
// public boolean process(BoxNode<T> item) {
//
// return false;
// }
//
// }
boolean isPowerOfTwo(int x) {
return ((x > 0) && (x & (x - 1)) == 0);
}
public BoxTree(int extents, int maxDepth) {
super();
if (!isPowerOfTwo(extents))
throw new IllegalArgumentException("Extents must be power of two!");
//size is -extents to +extents
this.root.x1 = -extents;
this.root.y1 = -extents;
@ -97,169 +133,231 @@ public abstract class BoxTree<Box extends BoxItem<E>, E> extends QuadTree<BoxNod
}
@Override
public BoxNode<Box> create() {
return new BoxNode<Box>();
public BoxNode<T> create() {
return new BoxNode<T>();
}
@Override
public void removeItem(Box item) {
public void removeItem(T item) {
}
@SuppressWarnings("unchecked")
public int query(Box box) {
if (box.x1 > box.x2 || box.y1 > box.y2)
throw new IllegalArgumentException();
Pool<Stack<BoxNode<T>>> stackPool = new Pool<Stack<BoxNode<T>>>() {
@Override
protected Stack<BoxNode<T>> createItem() {
return new Stack<BoxNode<T>>();
}
int x1 = box.x1;
int x2 = box.x2;
int y1 = box.y1;
int y2 = box.y2;
protected boolean clearItem(Stack<BoxNode<T>> item) {
if (item.tos != 0) {
item.tos = 0;
Arrays.fill(item.nodes, null);
}
return true;
};
};
BoxNode<Box> cur, c;
BoxNode<Box> stack = root;
int result = 0;
public boolean search(T box, SearchCb<E> cb, Object ctxt) {
BoxNode<T> n;
boolean drop = false;
Stack<BoxNode<T>> stack = stackPool.get();
stack.push(root);
O: while (stack != null) {
O: while (!stack.empty()) {
/** pop cur from stack */
cur = stack;
stack = stack.next;
n = stack.pop();
/** process overlapping items from cur node */
Box prev = cur.item;
for (Box it = cur.item; it != null; it = (Box) it.next) {
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;
/* process overlapping items from cur node */
for (BoxItem<E> it = n.item; it != null; it = it.next) {
if (it.overlaps(box)) {
if (!cb.call(it.item, ctxt))
break O;
}
if (result < 0) {
result = 0;
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;
}
}
prev = it;
}
/** put children on stack which overlap with box */
if ((c = cur.child00) != null &&
(x1 < c.x2) && (y1 < c.y2) &&
(c.x1 < x2) && (c.y1 < y2)) {
c.next = stack;
stack = c;
}
if ((c = cur.child01) != null &&
(x1 < c.x2) && (y1 < c.y2) &&
(c.x1 < x2) && (c.y1 < y2)) {
c.next = stack;
stack = c;
}
if ((c = cur.child10) != null &&
(x1 < c.x2) && (y1 < c.y2) &&
(c.x1 < x2) && (c.y1 < y2)) {
c.next = stack;
stack = c;
}
if ((c = cur.child11) != null &&
(x1 < c.x2) && (y1 < c.y2) &&
(c.x1 < x2) && (c.y1 < y2)) {
c.next = stack;
stack = c;
}
}
BoxNode<T> p = n.parent;
/** dont keep dangling references */
///* gwt optimizer found this cannot be reached :) */
while (stack != null)
stack = stack.next;
return drop ? 1 : 0;
}
public abstract boolean collectAll(BoxNode<Box> node);
public int all() {
return all(root);
}
public int all(BoxNode<Box> node) {
BoxNode<Box> cur, c;
BoxNode<Box> stack = node;
while (stack != null) {
cur = stack;
stack = stack.next;
if (cur.item != null && !collectAll(cur))
/* 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 ((c = cur.child00) != null) {
c.next = stack;
stack = c;
public interface SearchBoxCb<T extends BoxItem<?>> {
boolean call(T item);
}
if ((c = cur.child01) != null) {
c.next = stack;
stack = c;
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;
}
if ((c = cur.child10) != null) {
c.next = stack;
stack = c;
}
if ((c = cur.child11) != null) {
c.next = stack;
stack = c;
}
}
// dont keep dangling references
while (stack != null)
stack = stack.next;
BoxNode<T> p = n.parent;
return 0;
/* 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;
}
public abstract int process(Box box, Box it);
/* 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 true;
}
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 interface SearchNodeCb<E extends BoxNode<?>> {
boolean call(E item);
}
public boolean collect(SearchNodeCb<BoxNode<T>> cb) {
BoxNode<T> n;
Stack<BoxNode<T>> stack = stackPool.get();
stack.push(root);
while (!stack.empty()) {
n = stack.pop();
/* process overlapping items from cur node */
cb.call(n);
BoxNode<T> p = n.parent;
/* push next node on same level onto stack */
switch (n.id) {
case 0:
if (p.child01 != null) {
stack.push(p.child01);
break;
}
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);
return false;
}
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;
node.x1 = parent.x1;
node.y1 = parent.y1;
if (i == 0) {
// top-left
parent.child00 = node;
} else if (i == 1) {
// bottom-left
parent.child10 = node;
parent.child01 = node;
node.y1 += size;
} else if (i == 2) {
// top-right
parent.child01 = node;
parent.child10 = node;
node.x1 += size;
} else {
// bottom-right
parent.child11 = node;
node.x1 += 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.y2 = node.y1 + size;
node.parent = parent;
node.id = (byte) i;
return node;
}
public void insert(Box box) {
public void insert(T box) {
if (box.x1 > box.x2 || box.y1 > box.y2)
throw new IllegalArgumentException();
BoxNode<Box> cur = root;
BoxNode<Box> child = null;
if (box.next != 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 x2 = box.x2;
int y1 = box.y1;
int y2 = box.y2;
for (int level = 0; level <= maxDepth; level++) {
// half size of tile at current z
//int hsize = (extents >> level);
cur.refs++;
/* half size of tile at current level */
int hsize = (cur.x2 - cur.x1) >> 1;
// center of tile (shift by -extents)
//int cx = px + hsize - extents;
//int cy = py + hsize - extents;
/* center of tile */
int cx = cur.x1 + hsize;
int cy = cur.y1 + hsize;
child = null;
//int childPos = -1;
//log.debug(cx + ":" + cy + " " + hsize);
if (x2 <= cx) {
if (y2 <= cy) {
if (level < maxDepth) {
if (x2 < cx) {
if (y2 < cy) {
if ((child = cur.child00) == null)
child = create(cur, 0);
}
// should be else?
if (y1 >= cy) {
if ((child = cur.child10) == null)
} else if (y1 >= cy) {
if ((child = cur.child01) == null)
child = create(cur, 1);
idX++;
}
}
if (x1 >= cx) {
if (y2 <= cy) {
if ((child = cur.child01) == null)
if (y2 < cy) {
if ((child = cur.child10) == null)
child = create(cur, 2);
idY++;
}
if (y1 >= cy) {
} else if (y1 >= cy) {
if ((child = cur.child11) == null)
child = create(cur, 3);
idX++;
idY++;
}
}
//log.debug("child {}", child);
}
if (cur == minNode && child != null)
minNode = cur;
if (child == null || level == maxDepth) {
// push item onto list of this node
if (child == null) {
/* push item onto list of this node */
box.next = cur.item;
cur.item = box;
if (dbg)
log.debug("insert at: " + level + " / " + idX + ":"
+ idY + " -- " + x1 + ":" + y1
+ " /" + (x2) + "x" + (y2));
log.debug("insert: " + level
+ " cnt:" + Inlist.size(cur.item) + " " + x1 + ":" + y1
+ " /" + (x2) + "x" + (y2) + " " + box.item);
break;
}
cur = child;
}
}
public void setMinNode(int x1, int y1, int x2, int y2) {
/* TODO find lowest node that fully contains the region
* and set it as start for following queries */
public boolean remove(T box, E item) {
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++) {
/* 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;
}
public abstract boolean process(BoxNode<Box> nodes);
cur = child;
}
return false;
}
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() {
root.child00 = null;
@ -361,67 +562,10 @@ public abstract class BoxTree<Box extends BoxItem<E>, E> extends QuadTree<BoxNod
root.child10 = null;
root.child11 = null;
root.item = null;
//root = create();
//root.parent = root;
root.refs = 0;
}
static class Item extends BoxItem<Integer> {
public Item(int x1, int y1, int x2, int y2) {
super(x1, y1, x2, y2);
}
public Item() {
// TODO Auto-generated constructor stub
public int size() {
return root.refs;
}
}
// 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.
*/
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 T minNode;
//protected T minNode;
protected T pool;
public QuadTree() {
root = create();
root.id = -1;
root.parent = root;
}
@ -153,15 +154,15 @@ public abstract class QuadTree<T extends Node<T, E>, E> {
while (cur != root) {
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;
cur.refs--;
// if current node has no children
/* if current node has no children */
if (cur.refs == 0) {
// unhook from parent
/* unhook from parent */
switch (cur.id) {
case 0:
@ -178,7 +179,7 @@ public abstract class QuadTree<T extends Node<T, E>, E> {
break;
}
// add item back to pool
/* add item back to pool */
cur.parent = pool;
pool = cur;
}

View File

@ -16,36 +16,31 @@
*/
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;
/** top-left */
public T child00;
public T child10;
/** bottom-left */
public T child01;
/** top-right */
public T child10;
/** bottom-right */
public T child11;
/** payload */
public E item;
// id of this child relative to parent
byte id;
/** id of this child relative to parent */
int id;
// number of children and grandchildren
/** number of children and grandchildren */
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() {
return parent.item;
}