source: josm/trunk/src/org/openstreetmap/josm/data/osm/QuadBuckets.java@ 2678

Last change on this file since 2678 was 2626, checked in by jttt, 14 years ago

Fixed some of the warnings found by FindBugs

File size: 33.0 KB
RevLine 
[2165]1package org.openstreetmap.josm.data.osm;
[2427]2
[2263]3import java.lang.reflect.Array;
[2165]4import java.util.ArrayList;
[2420]5import java.util.Arrays;
[2165]6import java.util.Collection;
7import java.util.Collections;
[2388]8import java.util.Iterator;
9import java.util.LinkedList;
10import java.util.List;
[2165]11
[2388]12import org.openstreetmap.josm.data.coor.LatLon;
[2165]13import org.openstreetmap.josm.data.coor.QuadTiling;
14
[2263]15public class QuadBuckets<T extends OsmPrimitive> implements Collection<T>
[2165]16{
17 public static boolean debug = false;
[2424]18 static boolean consistency_testing = false;
[2440]19 /*
20 * Functions prefixed with __ need locking before
21 * being called.
22 */
23 private Object split_lock = new Object();
[2165]24
25 static void abort(String s)
26 {
[2420]27 throw new AssertionError(s);
[2165]28 }
29 static void out(String s)
30 {
31 System.out.println(s);
32 }
33 // periodic output
34 long last_out = -1;
35 void pout(String s)
36 {
37 long now = System.currentTimeMillis();
38 if (now - last_out < 300)
39 return;
40 last_out = now;
41 System.out.print(s + "\r");
42 }
43 void pout(String s, int i, int total)
44 {
45 long now = System.currentTimeMillis();
46 if ((now - last_out < 300) &&
[2388]47 ((i+1) < total))
[2165]48 return;
49 last_out = now;
50 // cast to float to keep the output size down
51 System.out.print(s + " " + (float)((i+1)*100.0/total) + "% done \r");
52 }
53 // 24 levels of buckets gives us bottom-level
54 // buckets that are about 2 meters on a side.
55 // That should do. :)
56 public static int NR_LEVELS = 24;
57 public static double WORLD_PARTS = (1 << NR_LEVELS);
58
[2454]59 public static int MAX_OBJECTS_PER_LEVEL = 16;
[2165]60 class QBLevel
61 {
62 int level;
63 long quad;
64 QBLevel parent;
65
[2263]66 public List<T> content;
[2165]67 public QBLevel children[];
[2388]68 @Override
[2263]69 public String toString()
70 {
71 return super.toString()+ "["+level+"]: " + bbox;
72 }
[2165]73 public QBLevel(QBLevel parent)
74 {
75 init(parent);
76 }
[2425]77 synchronized boolean remove_content(T o)
[2263]78 {
79 boolean ret = this.content.remove(o);
[2388]80 if (this.content.size() == 0) {
[2263]81 this.content = null;
[2388]82 }
[2427]83 if (this.canRemove()) {
[2423]84 this.remove_from_parent();
[2427]85 }
[2263]86 return ret;
87 }
[2353]88 QBLevel[] newChildren()
89 {
90 // This is ugly and hackish. But, it seems to work,
91 // and using an ArrayList here seems to cost us
92 // a significant performance penalty -- 50% in my
93 // testing. Child access is one of the single
94 // hottest code paths in this entire class.
95 return (QBLevel[])Array.newInstance(this.getClass(), QuadTiling.TILES_PER_LEVEL);
96 }
[2263]97 // Get the correct index for the given primitive
98 // at the given level. If the primitive can not
99 // fit into a single quad at this level, return -1
100 int get_index(T o, int level)
101 {
[2388]102 if (debug) {
[2263]103 out("getting index for " + o + " at level: " + level);
[2388]104 }
[2427]105 int index = -1;
106 for (LatLon c : o.getBBox().points()) {
107 if (debug) {
108 out("getting index for point: " + c);
109 }
110 if (index == -1) {
111 index = QuadTiling.index(c, level);
[2388]112 if (debug) {
[2427]113 out("set initial index to: " + index);
[2388]114 }
[2427]115 continue;
116 }
117 int another_index = QuadTiling.index(c, level);
118 if (debug) {
119 out("other point index: " + another_index);
120 }
121 if (another_index != index) {
122 // This happens at level 0 sometimes when a way has no
123 // nodes present.
[2388]124 if (debug) {
[2427]125 out("primitive ("+o.getId()+") would have gone across two quads "
126 + another_index + "/" + index + " at level: " + level + " ");
[2388]127 }
[2427]128 return -1;
[2263]129 }
130 }
[2427]131 return index;
[2263]132 }
[2440]133 /*
134 * There is a race between this and qb.nextContentNode().
135 * If nextContentNode() runs into this bucket, it may
136 * attempt to null out 'children' because it thinks this
137 * is a dead end.
138 */
139 void __split()
[2165]140 {
[2388]141 if (debug) {
[2165]142 out("splitting "+this.bbox()+" level "+level+" with "
143 + content.size() + " entries (my dimensions: "
144 + this.bbox.width()+", "+this.bbox.height()+")");
[2388]145 }
[2165]146 // deferring allocation of children until use
147 // seems a bit faster
148 //for (int i = 0; i < TILES_PER_LEVEL; i++)
149 // children[i] = new QBLevel(this, i);
[2263]150 List<T> tmpcontent = content;
[2165]151 content = null;
[2263]152 for (T o : tmpcontent) {
153 int new_index = get_index(o, level);
154 if (new_index == -1) {
[2440]155 this.__add_content(o);
[2263]156 //out("adding content to branch: " + this);
157 continue;
158 }
[2450]159 if (children == null) {
[2442]160 children = newChildren();
[2450]161 }
[2388]162 if (children[new_index] == null) {
[2165]163 children[new_index] = new QBLevel(this, new_index);
[2388]164 }
[2165]165 QBLevel child = children[new_index];
[2388]166 if (debug) {
[2437]167 out("putting "+o+"(q:"+Long.toHexString(QuadTiling.quadTile(o.getBBox().points().get(0)))+") into ["+new_index+"] " + child.bbox());
[2388]168 }
[2263]169 child.add(o);
[2165]170 }
171 }
[2440]172 void split() {
173 synchronized(split_lock) {
174 __split();
175 }
176 }
177 boolean __add_content(T o)
[2165]178 {
[2263]179 boolean ret = false;
[2440]180 // The split_lock will keep two concurrent
181 // calls from overwriting content
[2388]182 if (content == null) {
[2380]183 content = new ArrayList<T>();
[2388]184 }
[2263]185 ret = content.add(o);
[2388]186 if (debug && !this.isLeaf()) {
[2263]187 pout("added "+o.getClass().getName()+" to non-leaf with size: " + content.size());
[2388]188 }
[2263]189 return ret;
190 }
[2440]191 void __add_to_leaf(T o)
[2263]192 {
[2440]193 __add_content(o);
[2165]194 if (content.size() > MAX_OBJECTS_PER_LEVEL) {
[2388]195 if (debug) {
[2165]196 out("["+level+"] deciding to split");
[2388]197 }
[2165]198 if (level >= NR_LEVELS-1) {
[2427]199 if (debug) {
[2423]200 out("can not split, but too deep: " + level + " size: " + content.size());
[2427]201 }
[2165]202 return;
203 }
204 int before_size = -1;
[2388]205 if (consistency_testing) {
[2165]206 before_size = this.size();
[2388]207 }
[2165]208 this.split();
209 if (consistency_testing) {
210 int after_size = this.size();
211 if (before_size != after_size) {
212 abort("["+level+"] split done before: " + before_size + " after: " + after_size);
213 }
214 }
215 return;
216 }
[2263]217 }
[2165]218
[2263]219 boolean matches(T o, BBox search_bbox)
[2165]220 {
[2442]221 // This can be optimized when o is a node
222 //return search_bbox.bounds(coor));
[2427]223 return o.getBBox().intersects(search_bbox);
[2165]224 }
[2263]225 private List<T> search_contents(BBox search_bbox)
[2165]226 {
[2388]227 if (debug) {
[2441]228 out("searching contents (size: " + content == null?"<null>":content.size() + ") for " + search_bbox);
[2388]229 }
[2165]230 /*
231 * It is possible that this was created in a split
232 * but never got any content populated.
233 */
234 if (content == null)
235 return null;
236 // We could delay allocating this. But, the way it is currently
237 // used, we almost always have a node in the area that we're
238 // searching since we're searching around other nodes.
239 //
240 // the iterator's calls to ArrayList.size() were showing up in
241 // some profiles, so I'm trying a LinkedList instead
[2263]242 List<T> ret = new LinkedList<T>();
243 for (T o : content) {
[2388]244 if (matches(o, search_bbox)) {
[2263]245 ret.add(o);
[2388]246 }
[2165]247 }
[2388]248 if (debug) {
[2165]249 out("done searching quad " + Long.toHexString(this.quad));
[2388]250 }
[2165]251 return ret;
252 }
253 /*
254 * This is stupid. I tried to have a QBLeaf and QBBranch
255 * class decending from a QBLevel. It's more than twice
256 * as slow. So, this throws OO out the window, but it
257 * is fast. Runtime type determination must be slow.
258 */
259 boolean isLeaf()
260 {
261 if (children == null)
262 return true;
263 return false;
264 }
265 QBLevel next_sibling()
266 {
267 boolean found_me = false;
268 if (parent == null)
269 return null;
270 int __nr = 0;
271 for (QBLevel sibling : parent.children) {
272 __nr++;
273 int nr = __nr-1;
274 if (sibling == null) {
[2388]275 if (debug) {
[2165]276 out("[" + this.level + "] null child nr: " + nr);
[2388]277 }
[2165]278 continue;
279 }
280 // We're looking for the *next* child
281 // after us.
282 if (sibling == this) {
[2388]283 if (debug) {
[2165]284 out("[" + this.level + "] I was child nr: " + nr);
[2388]285 }
[2165]286 found_me = true;
287 continue;
288 }
289 if (found_me) {
[2388]290 if (debug) {
[2165]291 out("[" + this.level + "] next sibling was child nr: " + nr);
[2388]292 }
[2165]293 return sibling;
294 }
[2388]295 if (debug) {
[2165]296 out("[" + this.level + "] nr: " + nr + " is before me, ignoring...");
[2388]297 }
[2165]298 }
299 return null;
300 }
[2263]301 boolean hasContent()
[2165]302 {
[2441]303 return content != null;
[2263]304 }
[2423]305 QBLevel nextSibling()
[2263]306 {
[2165]307 QBLevel next = this;
[2423]308 QBLevel sibling = next.next_sibling();
309 // Walk back up the tree to find the
310 // next sibling node. It may be either
311 // a leaf or branch.
312 while (sibling == null) {
313 if (debug) {
314 out("no siblings at level["+next.level+"], moving to parent");
[2427]315 }
[2423]316 next = next.parent;
317 if (next == null) {
318 break;
[2427]319 }
[2423]320 sibling = next.next_sibling();
[2165]321 }
[2423]322 next = sibling;
323 return next;
324 }
[2440]325 QBLevel firstChild()
326 {
327 QBLevel ret = null;
328 for (QBLevel child : this.children) {
[2441]329 if (child == null) {
[2440]330 continue;
[2441]331 }
[2440]332 ret = child;
333 break;
334 }
335 return ret;
336 }
[2449]337 QBLevel nextNode()
338 {
339 if (this.isLeaf())
340 return this.nextSibling();
341 return this.firstChild();
342 }
[2423]343 QBLevel nextContentNode()
344 {
[2449]345 QBLevel next = this.nextNode();
[2165]346 if (next == null)
[2449]347 return next;
348 if (next.hasContent())
349 return next;
350 return next.nextContentNode();
[2165]351 }
352 int size()
353 {
354 if (isLeaf())
355 return size_leaf();
356 return size_branch();
357 }
358 private int size_leaf()
359 {
360 if (content == null) {
[2388]361 if (debug) {
[2420]362 out("["+level+"] leaf size: null content, children? " + Arrays.toString(children));
[2388]363 }
[2165]364 return 0;
365 }
[2388]366 if (debug) {
[2165]367 out("["+level+"] leaf size: " + content.size());
[2388]368 }
[2165]369 return content.size();
370 }
371 private int size_branch()
372 {
373 int ret = 0;
374 for (QBLevel l : children) {
[2388]375 if (l == null) {
[2165]376 continue;
[2388]377 }
[2165]378 ret += l.size();
379 }
[2388]380 if (content != null) {
[2263]381 ret += content.size();
[2388]382 }
383 if (debug) {
[2165]384 out("["+level+"] branch size: " + ret);
[2388]385 }
[2165]386 return ret;
387 }
[2263]388 boolean contains(T n)
[2165]389 {
390 QBLevel res = find_exact(n);
391 if (res == null)
392 return false;
393 return true;
394 }
[2263]395 QBLevel find_exact(T n)
[2165]396 {
397 if (content != null && content.contains(n))
[2355]398 return this;
399 return find_exact_child(n);
[2165]400 }
[2355]401 private QBLevel find_exact_child(T n)
[2165]402 {
403 QBLevel ret = null;
[2355]404 if (children == null)
405 return ret;
[2165]406 for (QBLevel l : children) {
[2388]407 if (l == null) {
[2165]408 continue;
[2388]409 }
[2165]410 ret = l.find_exact(n);
[2388]411 if (ret != null) {
[2165]412 break;
[2388]413 }
[2165]414 }
415 return ret;
416 }
[2263]417 boolean add(T n)
[2165]418 {
[2263]419 if (consistency_testing) {
420 if (!matches(n, this.bbox())) {
421 out("-----------------------------");
422 debug = true;
423 get_index(n, level);
[2442]424 get_index(n, level-1);
425 int nr = 0;
426 for (QBLevel sibling : parent.children) {
427 out("sibling["+ (nr++) +"]: " + sibling.bbox() + " this: " + (this==sibling));
428 }
429 abort("\nobject " + n + " does not belong in node at level: " + level + " bbox: " + this.bbox());
[2263]430 }
431 }
[2440]432 synchronized (split_lock) {
433 if (isLeaf()) {
434 __add_to_leaf(n);
435 } else {
436 __add_to_branch(n);
437 }
[2388]438 }
[2165]439 return true;
440 }
[2440]441 QBLevel __add_to_branch(T n)
[2165]442 {
[2263]443 int index = get_index(n, level);
444 if (index == -1) {
[2388]445 if (debug) {
[2263]446 out("unable to get index for " + n + "at level: " + level + ", adding content to branch: " + this);
[2388]447 }
[2440]448 this.__add_content(n);
[2263]449 return this;
450 }
451 if (debug) {
[2165]452 out("[" + level + "]: " + n +
[2388]453 " index " + index + " levelquad:" + this.quads() + " level bbox:" + this.bbox());
[2165]454 out(" put in child["+index+"]");
[2263]455 }
[2388]456 if (children[index] == null) {
[2165]457 children[index] = new QBLevel(this, index);
[2388]458 }
[2165]459 children[index].add(n);
460 return this;
461 }
[2263]462 private List<T> search(BBox search_bbox)
[2165]463 {
[2263]464 List<T> ret = null;
[2388]465 if (debug) {
[2165]466 System.out.print("[" + level + "] qb bbox: " + this.bbox() + " ");
[2388]467 }
[2263]468 if (!this.bbox().intersects(search_bbox)) {
[2165]469 if (debug) {
470 out("miss " + Long.toHexString(this.quad));
471 //QuadTiling.tile2xy(this.quad);
472 }
473 return ret;
474 }
[2388]475 if (this.hasContent()) {
[2263]476 ret = this.search_contents(search_bbox);
[2388]477 }
478 if (this.isLeaf())
[2263]479 return ret;
480 //if (this.hasContent())
481 // abort("branch had stuff");
[2388]482 if (debug) {
[2165]483 out("hit " + this.quads());
[2388]484 }
[2165]485
[2388]486 if (debug) {
[2165]487 out("[" + level + "] not leaf, moving down");
[2388]488 }
[2263]489 for (int i = 0; i < children.length; i++) {
490 QBLevel q = children[i];
[2388]491 if (debug) {
[2263]492 out("child["+i+"]: " + q);
[2388]493 }
494 if (q == null) {
[2165]495 continue;
[2388]496 }
497 if (debug) {
[2263]498 System.out.print(i+": ");
[2388]499 }
[2263]500 List<T> coors = q.search(search_bbox);
[2388]501 if (coors == null) {
[2165]502 continue;
[2388]503 }
504 if (ret == null) {
[2165]505 ret = coors;
[2388]506 } else {
[2165]507 ret.addAll(coors);
[2388]508 }
[2263]509 if (q.bbox().bounds(search_bbox)) {
[2165]510 search_cache = q;
511 // optimization: do not continue searching
512 // other tiles if this one wholly contained
513 // what we were looking for.
514 if (coors.size() > 0 ) {
[2388]515 if (debug) {
[2165]516 out("break early");
[2388]517 }
[2165]518 break;
519 }
520 }
521 }
522 return ret;
523 }
524 public String quads()
525 {
526 return Long.toHexString(quad);
527 }
528 public void init(QBLevel parent)
529 {
[2388]530 this.parent = parent;
531 if (parent == null) {
532 this.level = 0;
533 } else {
534 this.level = parent.level + 1;
535 }
536 this.quad = 0;
[2165]537 }
538 int index_of(QBLevel find_this)
539 {
540 if (this.isLeaf())
541 return -1;
542
543 for (int i = 0; i < QuadTiling.TILES_PER_LEVEL; i++) {
544 if (children[i] == find_this)
545 return i;
546 }
547 return -1;
548 }
549 public QBLevel(QBLevel parent, int parent_index)
550 {
551 this.init(parent);
552 int shift = (QuadBuckets.NR_LEVELS - level) * 2;
553 long mult = 1;
554 // Java blows the big one. It seems to wrap when
555 // you shift by > 31
556 if (shift >= 30) {
557 shift -= 30;
558 mult = 1<<30;
559 }
560 long this_quadpart = mult * (parent_index << shift);
561 this.quad = parent.quad | this_quadpart;
[2388]562 if (debug) {
[2165]563 out("new level["+this.level+"] bbox["+parent_index+"]: " + this.bbox()
564 + " coor: " + this.coor()
565 + " quadpart: " + Long.toHexString(this_quadpart)
566 + " quad: " + Long.toHexString(this.quad));
[2388]567 }
[2165]568 }
569 /*
570 * Surely we can calculate these efficiently instead of storing
571 */
572 double width = Double.NEGATIVE_INFINITY;
573 double width()
574 {
575 if (width != Double.NEGATIVE_INFINITY)
576 return this.width;
577 if (level == 0) {
578 width = this.bbox().width();
579 } else {
580 width = parent.width()/2;
581 }
582 return width;
583 }
584 double height()
585 {
586 return width()/2;
587 }
588 BBox bbox = null;
589 public BBox bbox()
590 {
[2450]591 if (bbox != null)
[2165]592 return bbox;
593 if (level == 0) {
594 bbox = new BBox(-180, 90, 180, -90);
595 } else {
596 LatLon bottom_left = this.coor();
597 double lat = bottom_left.lat() + this.height();
598 double lon = bottom_left.lon() + this.width();
599 LatLon top_right = new LatLon(lat, lon);
600 bbox = new BBox(bottom_left, top_right);
601 }
602 return bbox;
603 }
604 /*
605 * This gives the coordinate of the bottom-left
606 * corner of the box
607 */
608 LatLon coor()
609 {
610 return QuadTiling.tile2LatLon(this.quad);
611 }
[2425]612 boolean hasChildren()
613 {
614 if (children == null)
615 return false;
616
617 for (QBLevel child : children) {
618 if (child != null)
619 return true;
620 }
621 return false;
622 }
[2423]623 void remove_from_parent()
624 {
625 if (parent == null)
626 return;
627
628 int nr_siblings = 0;
629 for (int i = 0; i < parent.children.length; i++) {
630 QBLevel sibling = parent.children[i];
[2427]631 if (sibling != null) {
[2423]632 nr_siblings++;
[2427]633 }
634 if (sibling != this) {
[2423]635 continue;
[2427]636 }
637 if (!canRemove()) {
[2626]638 abort("attempt to remove non-empty child: " + this.content + " " + Arrays.toString(this.children));
[2427]639 }
[2423]640 parent.children[i] = null;
641 nr_siblings--;
642 }
[2427]643 if (parent.canRemove()) {
[2423]644 parent.remove_from_parent();
[2427]645 }
[2423]646 }
647 boolean canRemove()
648 {
649 if (content != null && content.size() > 0)
650 return false;
[2425]651 if (this.hasChildren())
652 return false;
[2423]653 return true;
654 }
[2165]655 }
656
657 private QBLevel root;
658 private QBLevel search_cache;
659 public QuadBuckets()
660 {
661 clear();
662 }
663 public void clear()
664 {
665 root = new QBLevel(null);
666 search_cache = null;
[2263]667 if (debug) {
[2165]668 out("QuadBuckets() cleared: " + this);
[2263]669 out("root: " + root + " level: " + root.level + " bbox: " + root.bbox());
670 }
[2165]671 }
[2263]672 public boolean add(T n)
[2165]673 {
[2388]674 if (debug) {
[2263]675 out("QuadBuckets() add: " + n + " size now: " + this.size());
[2388]676 }
[2165]677 int before_size = -1;
[2388]678 if (consistency_testing) {
[2165]679 before_size = root.size();
[2388]680 }
[2263]681 boolean ret;
682 // A way with no nodes will have an infinitely large
683 // bounding box. Just stash it in the root node.
[2388]684 if (!n.isUsable()) {
[2440]685 synchronized (split_lock) {
686 ret = root.__add_content(n);
687 }
[2388]688 } else {
[2263]689 ret = root.add(n);
[2388]690 }
[2165]691 if (consistency_testing) {
692 int end_size = root.size();
[2388]693 if (ret) {
[2165]694 end_size--;
[2388]695 }
696 if (before_size != end_size) {
[2165]697 abort("size inconsistency before add: " + before_size + " after: " + end_size);
[2388]698 }
[2165]699 }
[2388]700 if (debug) {
[2263]701 out("QuadBuckets() add: " + n + " size after: " + this.size());
[2388]702 }
[2165]703 return ret;
704 }
[2263]705 public void reindex()
706 {
707 QBLevel newroot = new QBLevel(null);
708 Iterator<T> i = this.iterator();
709 while (i.hasNext()) {
710 T o = i.next();
711 newroot.add(o);
712 }
713 root = newroot;
714 }
[2165]715 public void unsupported()
716 {
717 out("unsupported operation");
718 throw new UnsupportedOperationException();
719 }
[2420]720 public boolean retainAll(Collection<?> objects)
[2165]721 {
[2263]722 for (T o : this) {
[2388]723 if (objects.contains(o)) {
[2197]724 continue;
[2388]725 }
[2263]726 if (!this.remove(o))
[2197]727 return false;
728 }
729 return true;
[2165]730 }
[2420]731 public boolean removeAll(Collection<?> objects)
[2263]732 {
[2427]733 boolean changed = false;
[2263]734 for (Object o : objects) {
[2427]735 changed = changed | remove(o);
[2197]736 }
[2427]737 return changed;
[2165]738 }
[2420]739 public boolean addAll(Collection<? extends T> objects)
[2165]740 {
[2427]741 boolean changed = false;
[2263]742 for (Object o : objects) {
[2427]743 changed = changed | this.add(convert(o));
[2197]744 }
[2427]745 return changed;
[2165]746 }
[2420]747 public boolean containsAll(Collection<?> objects)
[2165]748 {
[2263]749 for (Object o : objects) {
750 if (!this.contains(o))
[2197]751 return false;
752 }
753 return true;
[2165]754 }
[2263]755 // If anyone has suggestions for how to fix
756 // this properly, I'm listening :)
[2353]757 @SuppressWarnings("unchecked")
[2263]758 private T convert(Object raw)
759 {
760 return (T)raw;
761 }
[2165]762 public boolean remove(Object o)
763 {
[2263]764 return this.remove(convert(o));
[2165]765 }
[2355]766 public boolean remove_slow(T removeme)
[2165]767 {
[2355]768 boolean ret = false;
769 Iterator<T> i = this.iterator();
770 while (i.hasNext()) {
771 T o = i.next();
[2388]772 if (o != removeme) {
[2355]773 continue;
[2388]774 }
[2355]775 i.remove();
776 ret = true;
777 break;
778 }
[2388]779 if (debug) {
[2355]780 out("qb slow remove result: " + ret);
[2388]781 }
[2355]782 return ret;
783 }
784 public boolean remove(T o)
785 {
786 /*
787 * We first try a locational search
788 */
789 QBLevel bucket = root.find_exact(o);
790 /*
791 * That may fail because the object was
792 * moved or changed in some way, so we
793 * resort to an iterative search:
794 */
[2286]795 if (bucket == null)
[2355]796 return remove_slow(o);
797 boolean ret = bucket.remove_content(o);
[2388]798 if (debug) {
[2355]799 out("qb remove result: " + ret);
[2388]800 }
[2263]801 return ret;
[2165]802 }
803 public boolean contains(Object o)
804 {
[2263]805 QBLevel bucket = root.find_exact(convert(o));
[2165]806 if (bucket == null)
807 return false;
808 return true;
809 }
[2263]810 public ArrayList<T> toArrayList()
[2165]811 {
[2263]812 ArrayList<T> a = new ArrayList<T>();
[2388]813 for (T n : this) {
[2165]814 a.add(n);
[2388]815 }
816 if (debug) {
817 out("returning array list with size: " + a.size());
818 }
[2165]819 return a;
820 }
821 public Object[] toArray()
822 {
823 return this.toArrayList().toArray();
824 }
[2420]825 public <A> A[] toArray(A[] template)
[2165]826 {
827 return this.toArrayList().toArray(template);
828 }
[2263]829 class QuadBucketIterator implements Iterator<T>
[2165]830 {
[2263]831 QBLevel current_node;
832 int content_index;
[2165]833 int iterated_over;
[2263]834 QBLevel next_content_node(QBLevel q)
[2165]835 {
836 if (q == null)
837 return null;
838 QBLevel orig = q;
[2440]839 QBLevel next;
840 synchronized (split_lock) {
841 next = q.nextContentNode();
842 }
[2263]843 //if (consistency_testing && (orig == next))
[2388]844 if (orig == next) {
[2165]845 abort("got same leaf back leaf: " + q.isLeaf());
[2388]846 }
[2165]847 return next;
848 }
[2263]849 public QuadBucketIterator(QuadBuckets<T> qb)
[2165]850 {
[2388]851 if (debug) {
[2263]852 out(this + " is a new iterator qb: " + qb + " size: " + qb.size());
[2388]853 }
854 if (qb.root.isLeaf() || qb.root.hasContent()) {
[2263]855 current_node = qb.root;
[2388]856 } else {
[2263]857 current_node = next_content_node(qb.root);
[2388]858 }
859 if (debug) {
[2263]860 out("\titerator first leaf: " + current_node);
[2388]861 }
[2165]862 iterated_over = 0;
863 }
864 public boolean hasNext()
865 {
866 if (this.peek() == null) {
[2388]867 if (debug) {
868 out(this + " no hasNext(), but iterated over so far: " + iterated_over);
869 }
[2165]870 return false;
871 }
872 return true;
873 }
[2263]874 T peek()
[2165]875 {
[2263]876 if (current_node == null) {
[2388]877 if (debug) {
[2165]878 out("null current leaf, nowhere to go");
[2388]879 }
[2165]880 return null;
881 }
[2263]882 while((current_node.content == null) ||
[2388]883 (content_index >= current_node.content.size())) {
884 if (debug) {
[2165]885 out("moving to next leaf");
[2388]886 }
[2263]887 content_index = 0;
888 current_node = next_content_node(current_node);
[2388]889 if (current_node == null) {
[2165]890 break;
[2388]891 }
[2165]892 }
[2263]893 if (current_node == null || current_node.content == null) {
[2388]894 if (debug) {
[2263]895 out("late nowhere to go " + current_node);
[2388]896 }
[2165]897 return null;
898 }
[2263]899 return current_node.content.get(content_index);
[2165]900 }
[2263]901 public T next()
[2165]902 {
[2263]903 T ret = peek();
904 content_index++;
[2388]905 if (debug) {
[2263]906 out("iteration["+iterated_over+"] " + content_index + " leaf: " + current_node);
[2388]907 }
[2165]908 iterated_over++;
909 if (ret == null) {
[2388]910 if (debug) {
[2165]911 out(this + " no next node, but iterated over so far: " + iterated_over);
[2388]912 }
[2165]913 }
914 return ret;
915 }
916 public void remove()
917 {
[2197]918 // two uses
919 // 1. Back up to the thing we just returned
920 // 2. move the index back since we removed
921 // an element
[2263]922 content_index--;
[2423]923 T object = peek(); //TODO Is the call to peek() necessary?
924 current_node.remove_content(object);
[2165]925 }
926 }
[2263]927 public Iterator<T> iterator()
[2165]928 {
929 return new QuadBucketIterator(this);
930 }
931 public int size()
932 {
933 // This can certainly by optimized
934 int ret = root.size();
[2388]935 if (debug) {
[2165]936 out(this + " QuadBuckets size: " + ret);
[2388]937 }
[2165]938 return ret;
939 }
[2263]940 public int size_iter()
941 {
942 int count = 0;
943 Iterator<T> i = this.iterator();
944 while (i.hasNext()) {
945 i.next();
946 count++;
947 }
948 return count;
949 }
[2165]950 public boolean isEmpty()
951 {
952 if (this.size() == 0)
953 return true;
954 return false;
955 }
[2430]956 public static BBox search_to_bbox(LatLon point, double radius)
[2165]957 {
958 BBox bbox = new BBox(point.lon() - radius, point.lat() - radius,
[2441]959 point.lon() + radius, point.lat() + radius);
[2388]960 if (debug) {
[2165]961 out("search bbox before sanity: " + bbox);
[2388]962 }
963 if (debug) {
[2165]964 out("search bbox after sanity: " + bbox);
[2388]965 }
[2165]966 return bbox;
967 }
[2263]968 List<T> search(Way w)
[2165]969 {
[2263]970 BBox way_bbox = new BBox(w);
971 return this.search(way_bbox);
[2165]972 }
[2263]973 public List<T> search(Node n, double radius)
[2165]974 {
[2263]975 return this.search(n.getCoor(), radius);
[2165]976 }
[2263]977 public List<T> search(LatLon point, double radius)
[2165]978 {
[2263]979 if (point == null)
980 return Collections.emptyList();
981 return this.search(search_to_bbox(point, radius));
982 }
983 public List<T> search(LatLon b1, LatLon b2)
984 {
[2165]985 BBox bbox = new BBox(b1.lon(), b1.lat(), b2.lon(), b2.lat());
986 return this.search(bbox);
987 }
[2263]988 List<T> search(BBox search_bbox)
[2165]989 {
990 if (debug) {
[2263]991 out("qb root search at " + search_bbox);
[2165]992 out("root bbox: " + root.bbox());
993 }
[2263]994 List<T> ret;
[2165]995 // Doing this cuts down search cost on a real-life data
996 // set by about 25%
997 boolean cache_searches = true;
998 if (cache_searches) {
[2388]999 if (search_cache == null) {
[2165]1000 search_cache = root;
[2388]1001 }
[2165]1002 // Walk back up the tree when the last
1003 // search spot can not cover the current
1004 // search
[2263]1005 while (!search_cache.bbox().bounds(search_bbox)) {
[2388]1006 if (debug) {
[2355]1007 out("bbox: " + search_bbox);
[2388]1008 }
[2263]1009 if (debug) {
1010 out("search_cache: " + search_cache + " level: " + search_cache.level);
1011 out("search_cache.bbox(): " + search_cache.bbox());
1012 }
[2165]1013 search_cache = search_cache.parent;
[2388]1014 if (debug) {
[2263]1015 out("new search_cache: " + search_cache);
[2388]1016 }
[2165]1017 }
1018 } else {
1019 search_cache = root;
1020 }
[2441]1021
1022 // Save parent because search_cache might change during search call
1023 QBLevel tmp = search_cache.parent;
1024
[2263]1025 ret = search_cache.search(search_bbox);
[2388]1026 if (ret == null) {
[2263]1027 ret = new ArrayList<T>();
[2388]1028 }
[2441]1029
[2263]1030 // A way that spans this bucket may be stored in one
1031 // of the nodes which is a parent of the search cache
1032 while (tmp != null) {
1033 List<T> content_result = tmp.search_contents(search_bbox);
[2388]1034 if (content_result != null) {
[2263]1035 ret.addAll(content_result);
[2388]1036 }
[2263]1037 tmp = tmp.parent;
1038 }
[2388]1039 if (debug) {
[2263]1040 out("search of QuadBuckets for " + search_bbox + " ret len: " + ret.size());
[2388]1041 }
[2263]1042 return ret;
[2165]1043 }
[2441]1044
1045 public void printTree() {
1046 printTreeRecursive(root, 0);
1047 }
1048
1049 private void printTreeRecursive(QBLevel level, int indent) {
1050 if (level == null) {
1051 printIndented(indent, "<empty child>");
1052 return;
1053 }
1054 printIndented(indent, level);
1055 if (level.hasContent()) {
1056 for (T o:level.content) {
1057 printIndented(indent, o);
1058 }
1059 }
1060 if (level.children != null) {
1061 for (QBLevel child:level.children) {
1062 printTreeRecursive(child, indent + 2);
1063 }
1064 }
1065 }
1066
1067 private void printIndented(int indent, Object msg) {
1068 for (int i=0; i<indent; i++) {
1069 System.out.print(' ');
1070 }
1071 System.out.println(msg);
1072 }
[2165]1073}
Note: See TracBrowser for help on using the repository browser.