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

Last change on this file since 2299 was 2299, checked in by jttt, 15 years ago

Fixed #3728 - "undo" does stupid things destroying data

File size: 35.4 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 if (content != null && content.contains(n)) {
576 return this;
577 }
578
579 QBLevel ret = null;
580 for (QBLevel l : children) {
581 if (l == null)
582 continue;
583 ret = l.find_exact(n);
584 if (ret != null)
585 break;
586 }
587 return ret;
588 }
589 boolean add(T n)
590 {
591 if (consistency_testing) {
592 if (!matches(n, this.bbox())) {
593 out("-----------------------------");
594 debug = true;
595 get_index(n, level);
596 abort("object " + n + " does not belong in node at level: " + level + " bbox: " + this.bbox());
597 }
598 }
599 if (isLeaf())
600 add_to_leaf(n);
601 else
602 add_to_branch(n);
603 return true;
604 }
605 QBLevel add_to_branch(T n)
606 {
607 int index = get_index(n, level);
608 if (index == -1) {
609 if (debug)
610 out("unable to get index for " + n + "at level: " + level + ", adding content to branch: " + this);
611 this.add_content(n);
612 return this;
613 }
614 if (debug) {
615 out("[" + level + "]: " + n +
616 " index " + index + " levelquad:" + this.quads() + " level bbox:" + this.bbox());
617 out(" put in child["+index+"]");
618 }
619 if (children[index] == null)
620 children[index] = new QBLevel(this, index);
621 children[index].add(n);
622 return this;
623 }
624 private List<T> search(BBox search_bbox)
625 {
626 List<T> ret = null;
627 if (debug)
628 System.out.print("[" + level + "] qb bbox: " + this.bbox() + " ");
629 if (!this.bbox().intersects(search_bbox)) {
630 if (debug) {
631 out("miss " + Long.toHexString(this.quad));
632 //QuadTiling.tile2xy(this.quad);
633 }
634 return ret;
635 }
636 if (this.hasContent())
637 ret = this.search_contents(search_bbox);
638 if (this.isLeaf()) {
639 return ret;
640 }
641 //if (this.hasContent())
642 // abort("branch had stuff");
643 if (debug)
644 out("hit " + this.quads());
645
646 if (debug)
647 out("[" + level + "] not leaf, moving down");
648 for (int i = 0; i < children.length; i++) {
649 QBLevel q = children[i];
650 if (debug)
651 out("child["+i+"]: " + q);
652 if (q == null)
653 continue;
654 if (debug)
655 System.out.print(i+": ");
656 List<T> coors = q.search(search_bbox);
657 if (coors == null)
658 continue;
659 if (ret == null)
660 ret = coors;
661 else
662 ret.addAll(coors);
663 if (q.bbox().bounds(search_bbox)) {
664 search_cache = q;
665 // optimization: do not continue searching
666 // other tiles if this one wholly contained
667 // what we were looking for.
668 if (coors.size() > 0 ) {
669 if (debug)
670 out("break early");
671 break;
672 }
673 }
674 }
675 return ret;
676 }
677 public String quads()
678 {
679 return Long.toHexString(quad);
680 }
681 public void init(QBLevel parent)
682 {
683 this.parent = parent;
684 if (parent == null)
685 this.level = 0;
686 else
687 this.level = parent.level + 1;
688 this.quad = 0;
689 }
690 int index_of(QBLevel find_this)
691 {
692 if (this.isLeaf())
693 return -1;
694
695 for (int i = 0; i < QuadTiling.TILES_PER_LEVEL; i++) {
696 if (children[i] == find_this)
697 return i;
698 }
699 return -1;
700 }
701 public QBLevel(QBLevel parent, int parent_index)
702 {
703 this.init(parent);
704 int shift = (QuadBuckets.NR_LEVELS - level) * 2;
705 long mult = 1;
706 // Java blows the big one. It seems to wrap when
707 // you shift by > 31
708 if (shift >= 30) {
709 shift -= 30;
710 mult = 1<<30;
711 }
712 long this_quadpart = mult * (parent_index << shift);
713 this.quad = parent.quad | this_quadpart;
714 if (debug)
715 out("new level["+this.level+"] bbox["+parent_index+"]: " + this.bbox()
716 + " coor: " + this.coor()
717 + " quadpart: " + Long.toHexString(this_quadpart)
718 + " quad: " + Long.toHexString(this.quad));
719 }
720 /*
721 * Surely we can calculate these efficiently instead of storing
722 */
723 double width = Double.NEGATIVE_INFINITY;
724 double width()
725 {
726 if (width != Double.NEGATIVE_INFINITY)
727 return this.width;
728 if (level == 0) {
729 width = this.bbox().width();
730 } else {
731 width = parent.width()/2;
732 }
733 return width;
734 }
735 double height()
736 {
737 return width()/2;
738 }
739 BBox bbox = null;
740 public BBox bbox()
741 {
742 if (bbox != null) {
743 bbox.sanity();
744 return bbox;
745 }
746 if (level == 0) {
747 bbox = new BBox(-180, 90, 180, -90);
748 } else {
749 LatLon bottom_left = this.coor();
750 double lat = bottom_left.lat() + this.height();
751 double lon = bottom_left.lon() + this.width();
752 LatLon top_right = new LatLon(lat, lon);
753 bbox = new BBox(bottom_left, top_right);
754 }
755 bbox.sanity();
756 return bbox;
757 }
758 /*
759 * This gives the coordinate of the bottom-left
760 * corner of the box
761 */
762 LatLon coor()
763 {
764 return QuadTiling.tile2LatLon(this.quad);
765 }
766 }
767
768 private QBLevel root;
769 private QBLevel search_cache;
770 public QuadBuckets()
771 {
772 clear();
773 }
774 public void clear()
775 {
776 root = new QBLevel(null);
777 search_cache = null;
778 if (debug) {
779 out("QuadBuckets() cleared: " + this);
780 out("root: " + root + " level: " + root.level + " bbox: " + root.bbox());
781 }
782 }
783 public boolean add(T n)
784 {
785 if (debug)
786 out("QuadBuckets() add: " + n + " size now: " + this.size());
787 int before_size = -1;
788 if (consistency_testing)
789 before_size = root.size();
790 boolean ret;
791 // A way with no nodes will have an infinitely large
792 // bounding box. Just stash it in the root node.
793 if (!n.isUsable())
794 ret = root.add_content(n);
795 else
796 ret = root.add(n);
797 if (consistency_testing) {
798 int end_size = root.size();
799 if (ret)
800 end_size--;
801 if (before_size != end_size)
802 abort("size inconsistency before add: " + before_size + " after: " + end_size);
803 }
804 if (debug)
805 out("QuadBuckets() add: " + n + " size after: " + this.size());
806 return ret;
807 }
808 public void reindex()
809 {
810 QBLevel newroot = new QBLevel(null);
811 Iterator<T> i = this.iterator();
812 while (i.hasNext()) {
813 T o = i.next();
814 newroot.add(o);
815 }
816 root = newroot;
817 }
818 public void unsupported()
819 {
820 out("unsupported operation");
821 throw new UnsupportedOperationException();
822 }
823 public boolean retainAll(Collection objects)
824 {
825 for (T o : this) {
826 if (objects.contains(o))
827 continue;
828 if (!this.remove(o))
829 return false;
830 }
831 return true;
832 }
833 boolean canStore(Object o)
834 {
835 if (o instanceof Way)
836 return true;
837 if (o instanceof Node)
838 return true;
839 return false;
840 }
841 public boolean removeAll(Collection objects)
842 {
843 for (Object o : objects) {
844 if (!canStore(o))
845 return false;
846 if (!this.remove(o))
847 return false;
848 }
849 return true;
850 }
851 public boolean addAll(Collection objects)
852 {
853 for (Object o : objects) {
854 if (!canStore(o))
855 return false;
856 if (!this.add(convert(o)))
857 return false;
858 }
859 return true;
860 }
861 public boolean containsAll(Collection objects)
862 {
863 boolean ret = true;
864 for (Object o : objects) {
865 if (!canStore(o))
866 return false;
867 if (!this.contains(o))
868 return false;
869 }
870 return true;
871 }
872 private void check_type(Object o)
873 {
874 if (canStore(o))
875 return;
876 unsupported();
877 }
878 // If anyone has suggestions for how to fix
879 // this properly, I'm listening :)
880 private T convert(Object raw)
881 {
882 //@SuppressWarnings("unchecked")
883 return (T)raw;
884 }
885 public boolean remove(Object o)
886 {
887 check_type(o);
888 return this.remove(convert(o));
889 }
890 public boolean remove(T n)
891 {
892 QBLevel bucket = root.find_exact(n);
893 if (bucket == null)
894 return false;
895 boolean ret = bucket.remove_content(n);
896 return ret;
897 }
898 public boolean contains(Object o)
899 {
900 if (!canStore(o))
901 return false;
902 QBLevel bucket = root.find_exact(convert(o));
903 if (bucket == null)
904 return false;
905 return true;
906 }
907 public ArrayList<T> toArrayList()
908 {
909 ArrayList<T> a = new ArrayList<T>();
910 for (T n : this)
911 a.add(n);
912 if (debug)
913 out("returning array list with size: " + a.size());
914 return a;
915 }
916 public Object[] toArray()
917 {
918 return this.toArrayList().toArray();
919 }
920 public <T> T[] toArray(T[] template)
921 {
922 return this.toArrayList().toArray(template);
923 }
924 class QuadBucketIterator implements Iterator<T>
925 {
926 QBLevel current_node;
927 int content_index;
928 int iterated_over;
929 QBLevel next_content_node(QBLevel q)
930 {
931 if (q == null)
932 return null;
933 QBLevel orig = q;
934 QBLevel next = q.nextContentNode();
935 //if (consistency_testing && (orig == next))
936 if (orig == next)
937 abort("got same leaf back leaf: " + q.isLeaf());
938 return next;
939 }
940 public QuadBucketIterator(QuadBuckets<T> qb)
941 {
942 if (debug)
943 out(this + " is a new iterator qb: " + qb + " size: " + qb.size());
944 if (qb.root.isLeaf() || qb.root.hasContent())
945 current_node = qb.root;
946 else
947 current_node = next_content_node(qb.root);
948 if (debug)
949 out("\titerator first leaf: " + current_node);
950 iterated_over = 0;
951 }
952 public boolean hasNext()
953 {
954 if (this.peek() == null) {
955 if (debug)
956 out(this + " no hasNext(), but iterated over so far: " + iterated_over);
957 return false;
958 }
959 return true;
960 }
961 T peek()
962 {
963 if (current_node == null) {
964 if (debug)
965 out("null current leaf, nowhere to go");
966 return null;
967 }
968 while((current_node.content == null) ||
969 (content_index >= current_node.content.size())) {
970 if (debug)
971 out("moving to next leaf");
972 content_index = 0;
973 current_node = next_content_node(current_node);
974 if (current_node == null)
975 break;
976 }
977 if (current_node == null || current_node.content == null) {
978 if (debug)
979 out("late nowhere to go " + current_node);
980 return null;
981 }
982 return current_node.content.get(content_index);
983 }
984 public T next()
985 {
986 T ret = peek();
987 content_index++;
988 if (debug)
989 out("iteration["+iterated_over+"] " + content_index + " leaf: " + current_node);
990 iterated_over++;
991 if (ret == null) {
992 if (debug)
993 out(this + " no next node, but iterated over so far: " + iterated_over);
994 }
995 return ret;
996 }
997 public void remove()
998 {
999 // two uses
1000 // 1. Back up to the thing we just returned
1001 // 2. move the index back since we removed
1002 // an element
1003 content_index--;
1004 T object = peek();
1005 current_node.content.remove(content_index);
1006 }
1007 }
1008 public Iterator<T> iterator()
1009 {
1010 return new QuadBucketIterator(this);
1011 }
1012 public int size()
1013 {
1014 // This can certainly by optimized
1015 int ret = root.size();
1016 if (debug)
1017 out(this + " QuadBuckets size: " + ret);
1018 return ret;
1019 }
1020 public int size_iter()
1021 {
1022 int count = 0;
1023 Iterator<T> i = this.iterator();
1024 while (i.hasNext()) {
1025 i.next();
1026 count++;
1027 }
1028 return count;
1029 }
1030 public boolean isEmpty()
1031 {
1032 if (this.size() == 0)
1033 return true;
1034 return false;
1035 }
1036 public BBox search_to_bbox(LatLon point, double radius)
1037 {
1038 BBox bbox = new BBox(point.lon() - radius, point.lat() - radius,
1039 point.lon() + radius, point.lat() + radius);
1040 if (debug)
1041 out("search bbox before sanity: " + bbox);
1042 bbox.sanity();
1043 if (debug)
1044 out("search bbox after sanity: " + bbox);
1045 return bbox;
1046 }
1047 List<T> search(Way w)
1048 {
1049 BBox way_bbox = new BBox(w);
1050 return this.search(way_bbox);
1051 }
1052 public List<T> search(Node n, double radius)
1053 {
1054 return this.search(n.getCoor(), radius);
1055 }
1056 public List<T> search(LatLon point, double radius)
1057 {
1058 if (point == null)
1059 return Collections.emptyList();
1060 return this.search(search_to_bbox(point, radius));
1061 }
1062 public List<T> search(LatLon b1, LatLon b2)
1063 {
1064 BBox bbox = new BBox(b1.lon(), b1.lat(), b2.lon(), b2.lat());
1065 bbox.sanity();
1066 return this.search(bbox);
1067 }
1068 List<T> search(BBox search_bbox)
1069 {
1070 if (debug) {
1071 out("qb root search at " + search_bbox);
1072 out("root bbox: " + root.bbox());
1073 }
1074 List<T> ret;
1075 // Doing this cuts down search cost on a real-life data
1076 // set by about 25%
1077 boolean cache_searches = true;
1078 if (cache_searches) {
1079 if (search_cache == null)
1080 search_cache = root;
1081 // Walk back up the tree when the last
1082 // search spot can not cover the current
1083 // search
1084 while (!search_cache.bbox().bounds(search_bbox)) {
1085 out("bbox: " + search_bbox);
1086 if (debug) {
1087 out("search_cache: " + search_cache + " level: " + search_cache.level);
1088 out("search_cache.bbox(): " + search_cache.bbox());
1089 }
1090 search_cache = search_cache.parent;
1091 if (debug)
1092 out("new search_cache: " + search_cache);
1093 }
1094 } else {
1095 search_cache = root;
1096 }
1097 ret = search_cache.search(search_bbox);
1098 if (ret == null)
1099 ret = new ArrayList<T>();
1100 // A way that spans this bucket may be stored in one
1101 // of the nodes which is a parent of the search cache
1102 QBLevel tmp = search_cache.parent;
1103 while (tmp != null) {
1104 List<T> content_result = tmp.search_contents(search_bbox);
1105 if (content_result != null)
1106 ret.addAll(content_result);
1107 tmp = tmp.parent;
1108 }
1109 if (ret == null || ret.size() == 0)
1110 return Collections.emptyList();
1111 if (debug)
1112 out("search of QuadBuckets for " + search_bbox + " ret len: " + ret.size());
1113 return ret;
1114 }
1115}
Note: See TracBrowser for help on using the repository browser.