Ticket #9002: quads.diff
File quads.diff, 18.6 KB (added by , 11 years ago) |
---|
-
src/org/openstreetmap/josm/data/coor/LatLon.java
35 35 public static final double MAX_SERVER_INV_PRECISION = 1e7; 36 36 public static final int MAX_SERVER_DIGITS = 7; 37 37 38 public static final LatLon ZERO = new LatLon(0, 0); 39 38 40 private static DecimalFormat cDmsMinuteFormatter = new DecimalFormat("00"); 39 41 private static DecimalFormat cDmsSecondFormatter = new DecimalFormat("00.0"); 40 42 private static DecimalFormat cDmMinuteFormatter = new DecimalFormat("00.000"); -
src/org/openstreetmap/josm/data/osm/BBox.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.data.osm; 3 3 4 import java.util.ArrayList;5 4 import java.util.Arrays; 6 import java.util.List;7 5 8 6 import org.openstreetmap.josm.data.Bounds; 9 7 import org.openstreetmap.josm.data.coor.LatLon; -
src/org/openstreetmap/josm/data/osm/QuadBuckets.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.data.osm; 3 3 4 import java.lang.reflect.Array;5 4 import java.util.ArrayList; 6 5 import java.util.Arrays; 7 6 import java.util.Collection; … … 19 18 * This class is (no longer) thread safe. 20 19 * 21 20 */ 22 public class QuadBuckets<T extends OsmPrimitive> implements Collection<T> 23 { 21 public class QuadBuckets<T extends OsmPrimitive> implements Collection<T> { 24 22 private static final boolean consistency_testing = false; 25 23 private static final int NW_INDEX = 1; 26 24 private static final int NE_INDEX = 3; … … 32 30 } 33 31 34 32 public static final int MAX_OBJECTS_PER_LEVEL = 16; 35 36 class QBLevel37 {38 final int level;33 34 static class QBLevel<T extends OsmPrimitive> { 35 private final int level; 36 private final int index; 39 37 private final BBox bbox; 40 final long quad;41 final QBLevelparent;38 private final long quad; 39 private final QBLevel<T> parent; 42 40 private boolean isLeaf = true; 43 41 44 public List<T> content; 45 public QBLevel nw, ne, sw, se; 42 private List<T> content; 43 // child order by index is sw, nw, se, ne 44 private QBLevel<T> nw, ne, sw, se; 45 private boolean hasChild; 46 46 47 private QBLevel getChild(int index) { 47 private final QuadBuckets<T> buckets; 48 49 private QBLevel<T> getChild(int index) { 48 50 switch (index) { 49 51 case NE_INDEX: 50 52 if (ne == null) { 51 ne = new QBLevel(this, index); 53 ne = new QBLevel<T>(this, index, buckets); 54 hasChild = true; 52 55 } 53 56 return ne; 54 57 case NW_INDEX: 55 58 if (nw == null) { 56 nw = new QBLevel(this, index); 59 nw = new QBLevel<T>(this, index, buckets); 60 hasChild = true; 57 61 } 58 62 return nw; 59 63 case SE_INDEX: 60 64 if (se == null) { 61 se = new QBLevel(this, index); 65 se = new QBLevel<T>(this, index, buckets); 66 hasChild = true; 62 67 } 63 68 return se; 64 69 case SW_INDEX: 65 70 if (sw == null) { 66 sw = new QBLevel(this, index); 71 sw = new QBLevel<T>(this, index, buckets); 72 hasChild = true; 67 73 } 68 74 return sw; 69 75 default: … … 71 77 } 72 78 } 73 79 74 private QBLevel[] getChildren() { 75 // This is ugly and hackish. But, it seems to work, 76 // and using an ArrayList here seems to cost us 77 // a significant performance penalty -- 50% in my 78 // testing. Child access is one of the single 79 // hottest code paths in this entire class. 80 @SuppressWarnings("unchecked") 81 QBLevel[] result = (QBLevel[]) Array.newInstance(this.getClass(), QuadTiling.TILES_PER_LEVEL); 82 result[NW_INDEX] = nw; 83 result[NE_INDEX] = ne; 84 result[SW_INDEX] = sw; 85 result[SE_INDEX] = se; 86 return result; 80 @SuppressWarnings("unchecked") 81 private QBLevel<T>[] getChildren() { 82 return new QBLevel[] {sw, nw, se, ne}; 87 83 } 88 84 89 85 @Override 90 public String toString() 91 return super.toString() + "["+level+"]: " + bbox();86 public String toString() { 87 return super.toString() + "[" + level + "]: " + bbox(); 92 88 } 93 89 94 90 /** 95 91 * Constructor for root node 96 92 */ 97 public QBLevel( ) {93 public QBLevel(final QuadBuckets<T> buckets) { 98 94 level = 0; 95 index = 0; 99 96 quad = 0; 100 97 parent = null; 101 98 bbox = new BBox(-180, 90, 180, -90); 99 this.buckets = buckets; 102 100 } 103 101 104 public QBLevel(QBLevel parent, int parent_index) {102 public QBLevel(QBLevel<T> parent, int parent_index, final QuadBuckets<T> buckets) { 105 103 this.parent = parent; 106 104 this.level = parent.level + 1; 105 this.index = parent_index; 106 this.buckets = buckets; 107 107 108 int shift = (QuadTiling.NR_LEVELS - level) * 2; 108 109 long mult = 1; 109 110 // Java blows the big one. It seems to wrap when you shift by > 31 110 111 if (shift >= 30) { 111 112 shift -= 30; 112 mult = 1 <<30;113 mult = 1 << 30; 113 114 } 114 115 long this_quadpart = mult * (parent_index << shift); 115 116 this.quad = parent.quad | this_quadpart; … … 124 125 return new BBox(bottom_left, top_right); 125 126 } 126 127 127 QBLevel findBucket(BBox bbox) {128 QBLevel<T> findBucket(BBox bbox) { 128 129 if (!hasChildren()) 129 130 return this; 130 131 else { … … 163 164 List<T> tmpcontent = content; 164 165 content = null; 165 166 166 for (T o : tmpcontent) {167 for (T o : tmpcontent) { 167 168 int index = o.getBBox().getIndex(level); 168 169 if (index == -1) { 169 170 __add_content(o); … … 183 184 ret = content.add(o); 184 185 return ret; 185 186 } 186 187 boolean matches(T o, BBox search_bbox) { 188 // This can be optimized when o is a node 187 188 boolean matches(final T o, final BBox search_bbox) { 189 if (o instanceof Node){ 190 final LatLon latLon = ((Node)o).getCoor(); 191 // node without coords -> bbox[0,0,0,0] 192 return search_bbox.bounds(latLon != null ? latLon : LatLon.ZERO); 193 } 189 194 return o.getBBox().intersects(search_bbox); 190 195 } 191 196 192 197 private void search_contents(BBox search_bbox, List<T> result) { 193 198 /* 194 199 * It is possible that this was created in a split … … 203 208 } 204 209 } 205 210 } 206 211 207 212 /* 208 213 * This is stupid. I tried to have a QBLeaf and QBBranch 209 * class de cending from a QBLevel. It's more than twice214 * class descending from a QBLevel. It's more than twice 210 215 * as slow. So, this throws OO out the window, but it 211 216 * is fast. Runtime type determination must be slow. 212 217 */ 213 218 boolean isLeaf() { 214 219 return isLeaf; 215 220 } 216 221 217 222 boolean hasChildren() { 218 return nw != null || ne != null || sw != null || se != null;223 return hasChild; 219 224 } 220 225 221 QBLevel next_sibling() { 222 boolean found_me = false; 223 if (parent == null) 224 return null; 225 for (QBLevel sibling : parent.getChildren()) { 226 if (sibling == null) { 227 continue; 228 } 229 // We're looking for the *next* child after us. 230 if (sibling == this) { 231 found_me = true; 232 continue; 233 } 234 if (found_me) 235 return sibling; 236 } 237 return null; 226 QBLevel<T> next_sibling() { 227 return (parent == null) ? null : parent.firstSiblingOf(this); 238 228 } 239 229 240 230 boolean hasContent() { 241 231 return content != null; 242 232 } 243 244 QBLevel nextSibling() {245 QBLevel next = this;246 QBLevel sibling = next.next_sibling();233 234 QBLevel<T> nextSibling() { 235 QBLevel<T> next = this; 236 QBLevel<T> sibling = next.next_sibling(); 247 237 // Walk back up the tree to find the 248 238 // next sibling node. It may be either 249 239 // a leaf or branch. … … 257 247 next = sibling; 258 248 return next; 259 249 } 260 261 QBLevel firstChild() { 262 QBLevel ret = null; 263 for (QBLevel child : getChildren()) { 264 if (child == null) { 265 continue; 266 } 267 ret = child; 268 break; 250 251 QBLevel<T> firstChild() { 252 if (sw != null) 253 return sw; 254 if (nw != null) 255 return nw; 256 if (se != null) 257 return se; 258 return ne; 259 } 260 261 QBLevel<T> firstSiblingOf(final QBLevel<T> child) { 262 switch (child.index) { 263 case SW_INDEX: 264 if (nw != null) 265 return nw; 266 case NW_INDEX: 267 if (se != null) 268 return se; 269 case SE_INDEX: 270 return ne; 269 271 } 270 return ret;272 return null; 271 273 } 272 273 QBLevel nextNode() {274 275 QBLevel<T> nextNode() { 274 276 if (!this.hasChildren()) 275 277 return this.nextSibling(); 276 278 return this.firstChild(); 277 279 } 278 279 QBLevel nextContentNode() {280 QBLevel next = this.nextNode();280 281 QBLevel<T> nextContentNode() { 282 QBLevel<T> next = this.nextNode(); 281 283 if (next == null) 282 284 return next; 283 285 if (next.hasContent()) … … 289 291 if (consistency_testing) { 290 292 if (!matches(o, this.bbox())) { 291 293 o.getBBox().getIndex(level); 292 o.getBBox().getIndex(level -1);294 o.getBBox().getIndex(level - 1); 293 295 int nr = 0; 294 296 abort("\nobject " + o + " does not belong in node at level: " + level + " bbox: " + this.bbox()); 295 297 } … … 308 310 if (!this.bbox().intersects(search_bbox)) 309 311 return; 310 312 else if (bbox().bounds(search_bbox)) { 311 search_cache = this;313 buckets.search_cache = this; 312 314 } 313 315 314 316 if (this.hasContent()) { … … 330 332 sw.search(search_bbox, result); 331 333 } 332 334 } 333 335 334 336 public String quads() { 335 337 return Long.toHexString(quad); 336 338 } 337 338 int index_of(QBLevel find_this) {339 QBLevel [] children = getChildren();339 340 int index_of(QBLevel<T> find_this) { 341 QBLevel<T>[] children = getChildren(); 340 342 for (int i = 0; i < QuadTiling.TILES_PER_LEVEL; i++) { 341 343 if (children[i] == find_this) 342 344 return i; 343 345 } 344 346 return -1; 345 347 } 346 348 347 349 double width() { 348 350 return bbox.width(); 349 351 } … … 355 357 public BBox bbox() { 356 358 return bbox; 357 359 } 358 360 359 361 /* 360 362 * This gives the coordinate of the bottom-left 361 363 * corner of the box … … 363 365 LatLon coor() { 364 366 return QuadTiling.tile2LatLon(this.quad); 365 367 } 366 368 367 369 void remove_from_parent() { 368 370 if (parent == null) 369 371 return; … … 386 388 parent.remove_from_parent(); 387 389 } 388 390 } 389 391 390 392 boolean canRemove() { 391 393 if (content != null && !content.isEmpty()) 392 394 return false; … … 396 398 } 397 399 } 398 400 399 private QBLevel root;400 private QBLevel search_cache;401 private QBLevel<T> root; 402 private QBLevel<T> search_cache; 401 403 private int size; 402 404 403 405 /** … … 406 408 public QuadBuckets() { 407 409 clear(); 408 410 } 409 411 410 412 @Override 411 public void clear() 412 root = new QBLevel ();413 public void clear() { 414 root = new QBLevel<T>(this); 413 415 search_cache = null; 414 416 size = 0; 415 417 } 416 418 417 419 @Override 418 420 public boolean add(T n) { 419 421 root.add(n); … … 432 434 } 433 435 return true; 434 436 } 435 437 436 438 @Override 437 439 public boolean removeAll(Collection<?> objects) { 438 440 boolean changed = false; … … 441 443 } 442 444 return changed; 443 445 } 444 446 445 447 @Override 446 448 public boolean addAll(Collection<? extends T> objects) { 447 449 boolean changed = false; … … 450 452 } 451 453 return changed; 452 454 } 453 455 454 456 @Override 455 457 public boolean containsAll(Collection<?> objects) { 456 458 for (Object o : objects) { … … 459 461 } 460 462 return true; 461 463 } 462 464 463 465 @Override 464 466 public boolean remove(Object o) { 465 @SuppressWarnings("unchecked") T t = (T) o; 467 @SuppressWarnings("unchecked") 468 T t = (T) o; 466 469 search_cache = null; // Search cache might point to one of removed buckets 467 QBLevel bucket = root.findBucket(t.getBBox());470 QBLevel<T> bucket = root.findBucket(t.getBBox()); 468 471 if (bucket.remove_content(t)) { 469 472 size--; 470 473 return true; 471 474 } else 472 475 return false; 473 476 } 474 477 475 478 @Override 476 479 public boolean contains(Object o) { 477 @SuppressWarnings("unchecked") T t = (T) o; 478 QBLevel bucket = root.findBucket(t.getBBox()); 480 @SuppressWarnings("unchecked") 481 T t = (T) o; 482 QBLevel<T> bucket = root.findBucket(t.getBBox()); 479 483 return bucket != null && bucket.content != null && bucket.content.contains(t); 480 484 } 481 485 … … 486 490 } 487 491 return a; 488 492 } 489 493 490 494 @Override 491 495 public Object[] toArray() { 492 496 return this.toArrayList().toArray(); 493 497 } 494 498 495 499 @Override 496 500 public <A> A[] toArray(A[] template) { 497 501 return this.toArrayList().toArray(template); 498 502 } 499 500 class QuadBucketIterator implements Iterator<T> 501 { 502 QBLevel current_node; 503 504 class QuadBucketIterator implements Iterator<T> { 505 QBLevel<T> current_node; 503 506 int content_index; 504 507 int iterated_over; 505 QBLevel next_content_node(QBLevel q) { 508 509 QBLevel<T> next_content_node(QBLevel<T> q) { 506 510 if (q == null) 507 511 return null; 508 QBLevel orig = q;509 QBLevel next;512 QBLevel<T> orig = q; 513 QBLevel<T> next; 510 514 next = q.nextContentNode(); 511 515 //if (consistency_testing && (orig == next)) 512 516 if (orig == next) { … … 514 518 } 515 519 return next; 516 520 } 517 521 518 522 public QuadBucketIterator(QuadBuckets<T> qb) { 519 523 if (!qb.root.hasChildren() || qb.root.hasContent()) { 520 524 current_node = qb.root; … … 523 527 } 524 528 iterated_over = 0; 525 529 } 526 530 527 531 @Override 528 532 public boolean hasNext() { 529 533 if (this.peek() == null) 530 534 return false; 531 535 return true; 532 536 } 533 537 534 538 T peek() { 535 539 if (current_node == null) 536 540 return null; 537 while((current_node.content == null) || 538 (content_index >= current_node.content.size())) { 541 while ((current_node.content == null) || (content_index >= current_node.content.size())) { 539 542 content_index = 0; 540 543 current_node = next_content_node(current_node); 541 544 if (current_node == null) { … … 546 549 return null; 547 550 return current_node.content.get(content_index); 548 551 } 549 552 550 553 @Override 551 554 public T next() { 552 555 T ret = peek(); … … 554 557 iterated_over++; 555 558 return ret; 556 559 } 557 560 558 561 @Override 559 562 public void remove() { 560 563 // two uses … … 566 569 current_node.remove_content(object); 567 570 } 568 571 } 569 572 570 573 @Override 571 574 public Iterator<T> iterator() { 572 575 return new QuadBucketIterator(this); … … 583 586 return true; 584 587 return false; 585 588 } 586 589 587 590 public List<T> search(BBox search_bbox) { 588 591 List<T> ret = new ArrayList<T>(); 589 592 // Doing this cuts down search cost on a real-life data set by about 25% … … 606 609 } 607 610 608 611 // Save parent because search_cache might change during search call 609 QBLevel tmp = search_cache.parent;612 QBLevel<T> tmp = search_cache.parent; 610 613 611 614 search_cache.search(search_bbox, ret); 612 615 … … 623 626 printTreeRecursive(root, 0); 624 627 } 625 628 626 private void printTreeRecursive(QBLevel level, int indent) {629 private void printTreeRecursive(QBLevel<T> level, int indent) { 627 630 if (level == null) { 628 631 printIndented(indent, "<empty child>"); 629 632 return; 630 633 } 631 634 printIndented(indent, level); 632 635 if (level.hasContent()) { 633 for (T o :level.content) {636 for (T o : level.content) { 634 637 printIndented(indent, o); 635 638 } 636 639 } 637 for (QBLevel child:level.getChildren()) {640 for (QBLevel<T> child : level.getChildren()) { 638 641 printTreeRecursive(child, indent + 2); 639 642 } 640 643 } 641 644 642 645 private void printIndented(int indent, Object msg) { 643 for (int i =0; i<indent; i++) {646 for (int i = 0; i < indent; i++) { 644 647 System.out.print(' '); 645 648 } 646 649 System.out.println(msg);