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

Last change on this file since 2286 was 2286, checked in by stoecker, 15 years ago

applied #367 - patch by Dave Hansen - NPE

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