Ticket #3671: store-ways-in-quadbuckets.patch
File store-ways-in-quadbuckets.patch, 37.2 KB (added by , 15 years ago) |
---|
-
(a) core/src/org/openstreetmap/josm/data/osm/DataSet.java~store-ways-in-quadbuckets vs. (b) core-dave/src/org/openstreetmap/josm/data/osm/DataSet.java
--- core-dave/src/org/openstreetmap/josm/data/osm/DataSet.java | 6 core-dave/src/org/openstreetmap/josm/data/osm/QuadBuckets.java | 573 ++++++---- 2 files changed, 391 insertions(+), 188 deletions(-) diff -puN src/org/openstreetmap/josm/data/osm/DataSet.java~store-ways-in-quadbuckets src/org/openstreetmap/josm/data/osm/DataSet.java
a b 1 1 // License: GPL. Copyright 2007 by Immanuel Scholz and others 2 2 package org.openstreetmap.josm.data.osm; 3 3 import org.openstreetmap.josm.tools.ReverseLookup; 4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 6 6 import java.awt.geom.Area; … … public class DataSet implements Cloneabl 38 38 * All nodes goes here, even when included in other data (ways etc). This enables the instant 39 39 * conversion of the whole DataSet by iterating over this data structure. 40 40 */ 41 public Collection<Node> nodes = new QuadBuckets();41 public QuadBuckets<Node> nodes = new QuadBuckets<Node>(); 42 42 43 43 /** 44 44 * All ways (Streets etc.) in the DataSet. 45 45 * 46 46 * The way nodes are stored only in the way list. 47 47 */ 48 public Collection<Way> ways = new LinkedList<Way>();48 public QuadBuckets<Way> ways = new QuadBuckets<Way>(); 49 49 50 50 /** 51 51 * All relations/relationships -
(a) core/src/org/openstreetmap/josm/data/osm/QuadBuckets.java~store-ways-in-quadbuckets vs. (b) core-dave/src/org/openstreetmap/josm/data/osm/QuadBuckets.java
diff -puN src/org/openstreetmap/josm/data/osm/QuadBuckets.java~store-ways-in-quadbuckets src/org/openstreetmap/josm/data/osm/QuadBuckets.java
a b 1 1 package org.openstreetmap.josm.data.osm; 2 import java.lang.reflect.Array; 2 3 import java.util.ArrayList; 3 4 import java.util.Collection; 4 5 import java.util.Collections; … … import java.util.TreeMap; 8 9 9 10 import org.openstreetmap.josm.data.coor.EastNorth; 10 11 import org.openstreetmap.josm.data.osm.Node; 12 import org.openstreetmap.josm.data.osm.Way; 13 import org.openstreetmap.josm.data.osm.OsmPrimitive; 11 14 import org.openstreetmap.josm.tools.Pair; 12 15 13 16 import java.nio.MappedByteBuffer; … … import org.openstreetmap.josm.data.coor. 34 37 import org.openstreetmap.josm.data.osm.visitor.Visitor; 35 38 36 39 37 public class QuadBuckets implements Collection<Node>40 public class QuadBuckets<T extends OsmPrimitive> implements Collection<T> 38 41 { 39 42 public static boolean debug = false; 40 43 static boolean consistency_testing = false; … … public class QuadBuckets implements Coll 79 82 // has to be a power of 2 80 83 public static int TILES_PER_LEVEL_SHIFT = 2; 81 84 public static int TILES_PER_LEVEL = 1<<TILES_PER_LEVEL_SHIFT; 85 // Maybe this should just be a Rectangle?? 82 86 class BBox 83 87 { 84 private double xmin ;85 private double xmax ;86 private double ymin ;87 private double ymax ;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; 88 92 void sanity() 89 93 { 90 94 if (xmin < -180.0) … … public class QuadBuckets implements Coll 106 110 } 107 111 public String toString() 108 112 { 109 return "[ " + xmin + " -> " + xmax + ", "+110 113 return "[ x: " + xmin + " -> " + xmax + 114 ", y: " + ymin + " -> " + ymax + " ]"; 111 115 } 112 116 double min(double a, double b) 113 117 { … … public class QuadBuckets implements Coll 121 125 return a; 122 126 return b; 123 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 } 124 135 public BBox(LatLon a, LatLon b) 125 136 { 126 xmin = min(a.lon(), b.lon()); 127 xmax = max(a.lon(), b.lon()); 128 ymin = min(a.lat(), b.lat()); 129 ymax = max(a.lat(), b.lat()); 137 add(a); 138 add(b); 130 139 sanity(); 131 140 } 132 141 public BBox(double a_x, double a_y, double b_x, double b_y) … … public class QuadBuckets implements Coll 137 146 ymax = max(a_y, b_y); 138 147 sanity(); 139 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 } 140 159 public double height() 141 160 { 142 161 return ymax-ymin; … … public class QuadBuckets implements Coll 179 198 { 180 199 return this.inside(b) || b.inside(this); 181 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 } 182 214 } 183 215 class QBLevel 184 216 { … … public class QuadBuckets implements Coll 186 218 long quad; 187 219 QBLevel parent; 188 220 189 public List< Node> content;221 public List<T> content; 190 222 public QBLevel children[]; 223 public String toString() 224 { 225 return super.toString()+ "["+level+"]: " + bbox; 226 } 191 227 public QBLevel(QBLevel parent) 192 228 { 193 229 init(parent); 194 230 } 195 String quads( Node n)231 String quads(T o) 196 232 { 197 return Long.toHexString(QuadTiling.quadTile(n.getCoor())); 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; 198 296 } 199 297 void split() 200 298 { … … public class QuadBuckets implements Coll 205 303 if (children != null) { 206 304 abort("overwrote children"); 207 305 } 208 children = new QBLevel[QuadTiling.TILES_PER_LEVEL]; 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); 209 312 // deferring allocation of children until use 210 313 // seems a bit faster 211 314 //for (int i = 0; i < TILES_PER_LEVEL; i++) 212 315 // children[i] = new QBLevel(this, i); 213 List< Node> tmpcontent = content;316 List<T> tmpcontent = content; 214 317 content = null; 215 for (Node n : tmpcontent) { 216 int new_index = QuadTiling.index(n, level); 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 } 217 325 if (children[new_index] == null) 218 326 children[new_index] = new QBLevel(this, new_index); 219 327 QBLevel child = children[new_index]; 220 328 if (debug) 221 out("putting "+ n+"(q:"+quads(n)+") into ["+new_index+"] " + child.bbox());222 child.add( n);329 out("putting "+o+"(q:"+quads(o)+") into ["+new_index+"] " + child.bbox()); 330 child.add(o); 223 331 } 224 332 } 225 void add_leaf(Node n) 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) 226 344 { 227 LatLon c = n.getCoor();228 345 QBLevel ret = this; 229 if (content == null) { 230 if (debug) 231 out(" new content array"); 232 // I chose a LinkedList because we do not do 233 // any real random access to this. We generally 234 // just run through the whole thing all at once 235 content = new LinkedList<Node>(); 236 } 237 content.add(n); 346 add_content(o); 238 347 if (content.size() > MAX_OBJECTS_PER_LEVEL) { 239 348 if (debug) 240 349 out("["+level+"] deciding to split"); … … public class QuadBuckets implements Coll 254 363 } 255 364 return; 256 365 } 257 if (debug) { 258 out(" plain content put now have " + content.size()); 259 if (content.contains(c)) 260 out(" and I already have that one"); 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); 261 385 } 386 return b; 387 //return new BBox(w); 262 388 } 263 389 264 List<Node> search(BBox bbox, LatLon point, double radius)390 boolean matches(T o, BBox search_bbox) 265 391 { 266 if (isLeaf()) 267 return search_leaf(bbox, point, radius); 268 return search_branch(bbox, point, radius); 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; 269 404 } 270 private List< Node> search_leaf(BBox bbox, LatLon point, double radius)405 private List<T> search_contents(BBox search_bbox) 271 406 { 407 String size = "null"; 408 if (content != null) 409 size = ""+content.size(); 272 410 if (debug) 273 out("searching contents in tile " + this.bbox() + " for " +bbox);411 out("searching contents (size: "+size+") for " + search_bbox); 274 412 /* 275 413 * It is possible that this was created in a split 276 414 * but never got any content populated. … … public class QuadBuckets implements Coll 283 421 // 284 422 // the iterator's calls to ArrayList.size() were showing up in 285 423 // some profiles, so I'm trying a LinkedList instead 286 List<Node> ret = new LinkedList<Node>(); 287 for (Node n : content) { 288 // This supports two modes: one where the bbox is just 289 // an outline of the point/radius combo, and one where 290 // it is *just* the bbox. If it is *just* the bbox, 291 // don't consider the points below 292 if (point == null) { 293 ret.add(n); 294 continue; 295 } 296 LatLon c = n.getCoor(); 297 // is greatCircleDistance really necessary? 298 double d = c.greatCircleDistance(point); 299 if (debug) 300 out("[" + level + "] Checking coor: " + c + " dist: " + d + " vs. " + radius + " " + quads(n)); 301 if (d > radius) 302 continue; 303 if (debug) 304 out("hit in quad: "+Long.toHexString(this.quad)+"\n"); 305 //if (ret == null) 306 // ret = new LinkedList<Node>(); 307 ret.add(n); 424 List<T> ret = new LinkedList<T>(); 425 for (T o : content) { 426 if (matches(o, search_bbox)) 427 ret.add(o); 308 428 } 309 429 if (debug) 310 430 out("done searching quad " + Long.toHexString(this.quad)); … … public class QuadBuckets implements Coll 354 474 } 355 475 return null; 356 476 } 357 QBLevel nextLeaf() 477 boolean hasContent() 478 { 479 if (content == null) 480 return false; 481 return true; 482 } 483 QBLevel nextContentNode() 358 484 { 359 485 QBLevel next = this; 360 486 if (this.isLeaf()) { … … public class QuadBuckets implements Coll 380 506 // leaf. So, walk back down the tree 381 507 // and find the first leaf 382 508 while (!next.isLeaf()) { 509 if (next.hasContent() && next != this) 510 break; 383 511 if (debug) 384 out("["+next.level+"] next node is a branch, moving down...");512 out("["+next.level+"] next node ("+next+") is a branch (content: "+next.hasContent()+"), moving down..."); 385 513 for (QBLevel child : next.children) { 386 514 if (child == null) 387 515 continue; … … public class QuadBuckets implements Coll 416 544 continue; 417 545 ret += l.size(); 418 546 } 547 if (content != null) 548 ret += content.size(); 419 549 if (debug) 420 550 out("["+level+"] branch size: " + ret); 421 551 return ret; 422 552 } 423 boolean contains( Noden)553 boolean contains(T n) 424 554 { 425 555 QBLevel res = find_exact(n); 426 556 if (res == null) 427 557 return false; 428 558 return true; 429 559 } 430 QBLevel find_exact( Noden)560 QBLevel find_exact(T n) 431 561 { 432 562 if (isLeaf()) 433 563 return find_exact_leaf(n); 434 564 return find_exact_branch(n); 435 565 } 436 QBLevel find_exact_leaf( Noden)566 QBLevel find_exact_leaf(T n) 437 567 { 438 568 QBLevel ret = null; 439 569 if (content != null && content.contains(n)) 440 570 ret = this; 441 571 return ret; 442 572 } 443 QBLevel find_exact_branch( Noden)573 QBLevel find_exact_branch(T n) 444 574 { 445 575 QBLevel ret = null; 446 576 for (QBLevel l : children) { … … public class QuadBuckets implements Coll 452 582 } 453 583 return ret; 454 584 } 455 boolean add( Noden)585 boolean add(T n) 456 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 } 457 595 if (isLeaf()) 458 add_ leaf(n);596 add_to_leaf(n); 459 597 else 460 add_ branch(n);598 add_to_branch(n); 461 599 return true; 462 600 } 463 QBLevel add_ branch(Noden)601 QBLevel add_to_branch(T n) 464 602 { 465 LatLon c = n.getCoor(); 466 int index = QuadTiling.index(n, level); 467 if (debug) 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) { 468 611 out("[" + level + "]: " + n + 469 612 " index " + index + " levelquad:" + this.quads() + " level bbox:" + this.bbox()); 470 if (debug)471 613 out(" put in child["+index+"]"); 614 } 472 615 if (children[index] == null) 473 616 children[index] = new QBLevel(this, index); 474 617 children[index].add(n); 475 /* this is broken at the moment because we need to handle the null n.getCoor()476 if (consistency_testing && !children[index].bbox().bounds(n.getCoor())) {477 out("target child["+index+"] does not bound " + children[index].bbox() + " " + c);478 for (int i = 0; i < children.length; i++) {479 QBLevel ch = children[i];480 if (ch == null)481 continue;482 out("child["+i+"] quad: " + ch.quads() + " bounds: " + ch.bbox.bounds(n.getCoor()));483 }484 out(" coor quad: " + quads(n));485 abort("");486 }*/487 488 if (consistency_testing) {489 for (int i = 0; i < children.length; i++) {490 QBLevel ch = children[i];491 if (ch == null)492 continue;493 if (ch.bbox().bounds(c) && i != index) {494 out("multible bounding?? expected: " + index + " but saw at " + i + " " + ch.quads());495 out("child["+i+"] bbox: " + ch.bbox());496 }497 }498 }499 618 return this; 500 619 } 501 private List< Node> search_branch(BBox bbox, LatLon point, double radius)620 private List<T> search(BBox search_bbox) 502 621 { 503 List< Node> ret = null;622 List<T> ret = null; 504 623 if (debug) 505 624 System.out.print("[" + level + "] qb bbox: " + this.bbox() + " "); 506 if (!this.bbox().intersects( bbox)) {625 if (!this.bbox().intersects(search_bbox)) { 507 626 if (debug) { 508 627 out("miss " + Long.toHexString(this.quad)); 509 628 //QuadTiling.tile2xy(this.quad); 510 629 } 511 630 return ret; 512 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"); 513 639 if (debug) 514 640 out("hit " + this.quads()); 515 641 516 642 if (debug) 517 643 out("[" + level + "] not leaf, moving down"); 518 int child_nr = 0; 519 for (QBLevel q : children) { 644 for (int i = 0; i < children.length; i++) { 645 QBLevel q = children[i]; 646 if (debug) 647 out("child["+i+"]: " + q); 520 648 if (q == null) 521 649 continue; 522 child_nr++;523 650 if (debug) 524 System.out.print( child_nr+": ");525 List< Node> coors = q.search(bbox, point, radius);651 System.out.print(i+": "); 652 List<T> coors = q.search(search_bbox); 526 653 if (coors == null) 527 654 continue; 528 655 if (ret == null) 529 656 ret = coors; 530 657 else 531 658 ret.addAll(coors); 532 if (q.bbox().bounds( bbox)) {659 if (q.bbox().bounds(search_bbox)) { 533 660 search_cache = q; 534 661 // optimization: do not continue searching 535 662 // other tiles if this one wholly contained … … public class QuadBuckets implements Coll 550 677 public void init(QBLevel parent) 551 678 { 552 679 this.parent = parent; 553 if (parent != null) 680 if (parent == null) 681 this.level = 0; 682 else 554 683 this.level = parent.level + 1; 555 684 this.quad = 0; 556 685 } … … public class QuadBuckets implements Coll 568 697 public QBLevel(QBLevel parent, int parent_index) 569 698 { 570 699 this.init(parent); 571 this.level = parent.level+1;572 this.parent = parent;573 700 int shift = (QuadBuckets.NR_LEVELS - level) * 2; 574 701 long mult = 1; 575 702 // Java blows the big one. It seems to wrap when … … public class QuadBuckets implements Coll 644 771 { 645 772 root = new QBLevel(null); 646 773 search_cache = null; 647 if (debug) 774 if (debug) { 648 775 out("QuadBuckets() cleared: " + this); 776 out("root: " + root + " level: " + root.level + " bbox: " + root.bbox()); 777 } 649 778 } 650 public boolean add( Noden)779 public boolean add(T n) 651 780 { 652 781 if (debug) 653 out( this + "QuadBuckets() add: " + n + " size now: " + this.size());782 out("QuadBuckets() add: " + n + " size now: " + this.size()); 654 783 int before_size = -1; 655 784 if (consistency_testing) 656 785 before_size = root.size(); 657 boolean ret = root.add(n); 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); 658 793 if (consistency_testing) { 659 794 int end_size = root.size(); 660 795 if (ret) … … public class QuadBuckets implements Coll 662 797 if (before_size != end_size) 663 798 abort("size inconsistency before add: " + before_size + " after: " + end_size); 664 799 } 800 if (debug) 801 out("QuadBuckets() add: " + n + " size after: " + this.size()); 665 802 return ret; 666 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 } 667 814 public void unsupported() 668 815 { 669 816 out("unsupported operation"); 670 817 throw new UnsupportedOperationException(); 671 818 } 672 public boolean retainAll(Collection nodes)819 public boolean retainAll(Collection objects) 673 820 { 674 for ( Node n: this) {675 if ( nodes.contains(n))821 for (T o : this) { 822 if (objects.contains(o)) 676 823 continue; 677 if (!this.remove( n))824 if (!this.remove(o)) 678 825 return false; 679 826 } 680 827 return true; 681 828 } 682 public boolean removeAll(Collection nodes) 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) 683 838 { 684 for (Object o : nodes) {685 if (! (o instanceof Node))839 for (Object o : objects) { 840 if (!canStore(o)) 686 841 return false; 687 Node n = (Node)o; 688 if (!this.remove(n)) 842 if (!this.remove(o)) 689 843 return false; 690 844 } 691 845 return true; 692 846 } 693 public boolean addAll(Collection nodes)847 public boolean addAll(Collection objects) 694 848 { 695 for (Object o : nodes) {696 if (! (o instanceof Node))849 for (Object o : objects) { 850 if (!canStore(o)) 697 851 return false; 698 Node n = (Node)o; 699 if (!this.add(n)) 852 if (!this.add(convert(o))) 700 853 return false; 701 854 } 702 855 return true; 703 856 } 704 public boolean containsAll(Collection nodes)857 public boolean containsAll(Collection objects) 705 858 { 706 859 boolean ret = true; 707 for (Object o : nodes) {708 if (! (o instanceof Node))860 for (Object o : objects) { 861 if (!canStore(o)) 709 862 return false; 710 Node n = (Node)o; 711 if (!this.contains(n)) 863 if (!this.contains(o)) 712 864 return false; 713 865 } 714 866 return true; 715 867 } 716 868 private void check_type(Object o) 717 869 { 718 if ( o instanceof Node)870 if (canStore(o)) 719 871 return; 720 872 unsupported(); 721 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 } 722 881 public boolean remove(Object o) 723 882 { 724 883 check_type(o); 725 return this.remove( (Node)o);884 return this.remove(convert(o)); 726 885 } 727 public boolean remove( Noden)886 public boolean remove(T n) 728 887 { 729 888 QBLevel bucket = root.find_exact(n); 730 889 if (!bucket.isLeaf()) 731 890 abort("found branch where leaf expected"); 732 return bucket.content.remove(n); 891 boolean ret = bucket.remove_content(n); 892 return ret; 733 893 } 734 894 public boolean contains(Object o) 735 895 { 736 if (! (o instanceof Node))896 if (!canStore(o)) 737 897 return false; 738 QBLevel bucket = root.find_exact( (Node)o);898 QBLevel bucket = root.find_exact(convert(o)); 739 899 if (bucket == null) 740 900 return false; 741 901 return true; 742 902 } 743 public ArrayList< Node> toArrayList()903 public ArrayList<T> toArrayList() 744 904 { 745 ArrayList< Node> a = new ArrayList<Node>();746 for ( Noden : this)905 ArrayList<T> a = new ArrayList<T>(); 906 for (T n : this) 747 907 a.add(n); 748 out("returning array list with size: " + a.size()); 908 if (debug) 909 out("returning array list with size: " + a.size()); 749 910 return a; 750 911 } 751 912 public Object[] toArray() … … public class QuadBuckets implements Coll 756 917 { 757 918 return this.toArrayList().toArray(template); 758 919 } 759 class QuadBucketIterator implements Iterator< Node>920 class QuadBucketIterator implements Iterator<T> 760 921 { 761 QBLevel current_ leaf;762 int index_in_leaf;922 QBLevel current_node; 923 int content_index; 763 924 int iterated_over; 764 QBLevel next_ leaf(QBLevel q)925 QBLevel next_content_node(QBLevel q) 765 926 { 766 927 if (q == null) 767 928 return null; 768 929 QBLevel orig = q; 769 QBLevel next = q.nextLeaf(); 770 if (consistency_testing && (orig == next)) 930 QBLevel next = q.nextContentNode(); 931 //if (consistency_testing && (orig == next)) 932 if (orig == next) 771 933 abort("got same leaf back leaf: " + q.isLeaf()); 772 934 return next; 773 935 } 774 public QuadBucketIterator(QuadBuckets qb)936 public QuadBucketIterator(QuadBuckets<T> qb) 775 937 { 776 938 if (debug) 777 out(this + " is a new iterator qb .root: " + qb.root+ " size: " + qb.size());778 if (qb.root.isLeaf() )779 current_ leaf= qb.root;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; 780 942 else 781 current_ leaf = next_leaf(qb.root);943 current_node = next_content_node(qb.root); 782 944 if (debug) 783 out("\titerator first leaf: " + current_ leaf);945 out("\titerator first leaf: " + current_node); 784 946 iterated_over = 0; 785 947 } 786 948 public boolean hasNext() … … public class QuadBuckets implements Coll 792 954 } 793 955 return true; 794 956 } 795 Nodepeek()957 T peek() 796 958 { 797 if (current_ leaf== null) {959 if (current_node == null) { 798 960 if (debug) 799 961 out("null current leaf, nowhere to go"); 800 962 return null; 801 963 } 802 while((current_ leaf.content == null) ||803 ( index_in_leaf >= current_leaf.content.size())) {964 while((current_node.content == null) || 965 (content_index >= current_node.content.size())) { 804 966 if (debug) 805 967 out("moving to next leaf"); 806 index_in_leaf= 0;807 current_ leaf = next_leaf(current_leaf);808 if (current_ leaf== null)968 content_index = 0; 969 current_node = next_content_node(current_node); 970 if (current_node == null) 809 971 break; 810 972 } 811 if (current_ leaf == null || current_leaf.content == null) {973 if (current_node == null || current_node.content == null) { 812 974 if (debug) 813 out("late nowhere to go " + current_ leaf);975 out("late nowhere to go " + current_node); 814 976 return null; 815 977 } 816 return current_ leaf.content.get(index_in_leaf);978 return current_node.content.get(content_index); 817 979 } 818 public Nodenext()980 public T next() 819 981 { 820 Noderet = peek();821 index_in_leaf++;982 T ret = peek(); 983 content_index++; 822 984 if (debug) 823 out("iteration["+iterated_over+"] " + index_in_leaf + " leaf: " + current_leaf);985 out("iteration["+iterated_over+"] " + content_index + " leaf: " + current_node); 824 986 iterated_over++; 825 987 if (ret == null) { 826 988 if (debug) … … public class QuadBuckets implements Coll 834 996 // 1. Back up to the thing we just returned 835 997 // 2. move the index back since we removed 836 998 // an element 837 index_in_leaf--; 838 current_leaf.content.remove(index_in_leaf); 999 content_index--; 1000 T object = peek(); 1001 current_node.content.remove(content_index); 839 1002 } 840 1003 } 841 public Iterator< Node> iterator()1004 public Iterator<T> iterator() 842 1005 { 843 1006 return new QuadBucketIterator(this); 844 1007 } … … public class QuadBuckets implements Coll 850 1013 out(this + " QuadBuckets size: " + ret); 851 1014 return ret; 852 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 } 853 1026 public boolean isEmpty() 854 1027 { 855 1028 if (this.size() == 0) … … public class QuadBuckets implements Coll 867 1040 out("search bbox after sanity: " + bbox); 868 1041 return bbox; 869 1042 } 870 List<Node> search(LatLon point, double radius) 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) 871 1049 { 872 return this.search( search_to_bbox(point, radius), point, radius);1050 return this.search(n.getCoor(), radius); 873 1051 } 874 List<Node> search(BBox bbox)1052 public List<T> search(LatLon point, double radius) 875 1053 { 876 return this.search(bbox, null, -1); 1054 if (point == null) 1055 return Collections.emptyList(); 1056 return this.search(search_to_bbox(point, radius)); 877 1057 } 878 public List< Node> search(LatLon b1, LatLon b2)1058 public List<T> search(LatLon b1, LatLon b2) 879 1059 { 880 1060 BBox bbox = new BBox(b1.lon(), b1.lat(), b2.lon(), b2.lat()); 881 1061 bbox.sanity(); 882 1062 return this.search(bbox); 883 1063 } 884 List< Node> search(BBox bbox, LatLon point, double radius)1064 List<T> search(BBox search_bbox) 885 1065 { 886 1066 if (debug) { 887 out("qb root search at " + point + " around: " + radius);1067 out("qb root search at " + search_bbox); 888 1068 out("root bbox: " + root.bbox()); 889 1069 } 890 List< Node> ret = null;1070 List<T> ret; 891 1071 // Doing this cuts down search cost on a real-life data 892 1072 // set by about 25% 893 1073 boolean cache_searches = true; … … public class QuadBuckets implements Coll 897 1077 // Walk back up the tree when the last 898 1078 // search spot can not cover the current 899 1079 // search 900 while (!search_cache.bbox().bounds(bbox)) { 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 } 901 1086 search_cache = search_cache.parent; 1087 if (debug) 1088 out("new search_cache: " + search_cache); 902 1089 } 903 1090 } else { 904 1091 search_cache = root; 905 1092 } 906 return search_cache.search(bbox, point, radius); 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; 907 1110 } 908 1111 } -
src/org/openstreetmap/josm/data/coor/QuadTiling.java
97 97 int total_shift = TILES_PER_LEVEL_SHIFT*(NR_LEVELS-level-1); 98 98 return (int)(mask & (quad >> total_shift)); 99 99 } 100 static public int index( Node n, int level)100 static public int index(LatLon coor, int level) 101 101 { 102 LatLon coor = n.getCoor();103 102 // The nodes that don't return coordinates will all get 104 103 // stuck in a single tile. Hopefully there are not too 105 104 // many of them 106 105 if (coor == null) 107 106 return 0; 108 long quad = coorToTile( n.getCoor());107 long quad = coorToTile(coor); 109 108 return index(level, quad); 110 109 } 111 110 }