Changeset 11269 in josm
- Timestamp:
- 2016-11-17T19:58:39+01:00 (8 years ago)
- Location:
- trunk
- Files:
-
- 11 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/osm/BBox.java
r11237 r11269 20 20 * Constructs a new (invalid) BBox 21 21 */ 22 protected BBox() { } 23 22 public BBox() { } 24 23 25 24 /** … … 31 30 */ 32 31 public BBox(final double x, final double y) { 33 xmax = xmin = x; 34 ymax = ymin = y; 35 sanity(); 32 if (!Double.isNaN(x) && !Double.isNaN(y)) { 33 xmin = x; 34 ymin = y; 35 xmax = x; 36 ymax = y; 37 } 36 38 } 37 39 38 40 /** 39 41 * Constructs a new {@code BBox} defined by points <code>a</code> and <code>b</code>. 40 * Result is minimal BBox containing both points .42 * Result is minimal BBox containing both points if they are both valid, else undefined 41 43 * 42 44 * @param a first point … … 59 61 } 60 62 63 /** 64 * Create minimal BBox so that {@code this.bounds(ax,ay)} and {@code this.bounds(bx,by)} will both return true 65 * @param ax left or right X value (-180 .. 180) 66 * @param ay top or bottom Y value (-90 .. 90) 67 * @param bx left or right X value (-180 .. 180) 68 * @param by top or bottom Y value (-90 .. 90) 69 */ 61 70 public BBox(double ax, double ay, double bx, double by) { 71 if (Double.isNaN(ax) || Double.isNaN(ay) || Double.isNaN(bx) || Double.isNaN(by)) { 72 return; // use default which is an invalid BBox 73 } 62 74 63 75 if (ax > bx) { … … 76 88 ymin = ay; 77 89 } 78 79 sanity(); 80 } 81 90 } 91 92 /** 93 * Create BBox for all nodes of the way with known coordinates. 94 * If no node has a known coordinate, an invalid BBox is returned. 95 * @param w the way 96 */ 82 97 public BBox(Way w) { 83 for (Node n : w.getNodes()) { 84 LatLon coor = n.getCoor(); 85 if (coor == null) { 86 continue; 87 } 88 add(coor); 89 } 90 } 91 98 w.getNodes().forEach((n) -> add(n.getCoor())); 99 } 100 101 /** 102 * Create BBox for a node. An invalid BBox is returned if the coordinates are not known. 103 * @param n the node 104 */ 92 105 public BBox(Node n) { 93 LatLon coor = n.getCoor(); 94 if (coor == null) { 95 xmin = xmax = ymin = ymax = 0; 96 } else { 97 xmin = xmax = coor.lon(); 98 ymin = ymax = coor.lat(); 99 } 100 } 101 102 private void sanity() { 103 if (xmin < -180.0) { 104 xmin = -180.0; 105 } 106 if (xmax > 180.0) { 107 xmax = 180.0; 108 } 109 if (ymin < -90.0) { 110 ymin = -90.0; 111 } 112 if (ymax > 90.0) { 113 ymax = 90.0; 114 } 115 } 116 106 if (n.isLatLonKnown()) 107 add(n.getCoor()); 108 } 109 110 /** 111 * Add a point to an existing BBox. Extends this bbox if necessary so that this.bounds(c) will return true 112 * if c is a valid LatLon instance. 113 * @param c a LatLon point 114 */ 117 115 public final void add(LatLon c) { 118 add(c.lon(), c.lat()); 116 if (c != null && c.isValid()) 117 add(c.lon(), c.lat()); 119 118 } 120 119 … … 125 124 */ 126 125 public final void add(double x, double y) { 126 if (Double.isNaN(x) || Double.isNaN(y)) 127 return; 127 128 xmin = Math.min(xmin, x); 128 129 xmax = Math.max(xmax, x); 129 130 ymin = Math.min(ymin, y); 130 131 ymax = Math.max(ymax, y); 131 sanity(); 132 } 133 134 public final void add(BBox box) { 135 xmin = Math.min(xmin, box.xmin); 136 xmax = Math.max(xmax, box.xmax); 137 ymin = Math.min(ymin, box.ymin); 138 ymax = Math.max(ymax, box.ymax); 139 sanity(); 140 } 141 132 } 133 134 /** 135 * Extends this bbox to include the bbox other. Does nothing if other is not valid. 136 * @param other a bbox 137 */ 138 public final void add(BBox other) { 139 if (other.isValid()) { 140 xmin = Math.min(xmin, other.xmin); 141 xmax = Math.max(xmax, other.xmax); 142 ymin = Math.min(ymin, other.ymin); 143 ymax = Math.max(ymax, other.ymax); 144 } 145 } 146 147 /** 148 * Extends this bbox to include the bbox of the primitive extended by extraSpace. 149 * @param primitive an OSM primitive 150 * @param extraSpace the value to extend the primitives bbox. Unit is in LatLon degrees. 151 */ 142 152 public void addPrimitive(OsmPrimitive primitive, double extraSpace) { 143 153 BBox primBbox = primitive.getBBox(); … … 285 295 } 286 296 297 /** 298 * @return true if the bbox covers a part of the planets surface 299 * Height and width must be non-negative, but may (both) be 0. 300 */ 301 public boolean isValid() { 302 return (xmin <= xmax && ymin <= ymax); 303 } 304 305 /** 306 * @return true if the bbox covers a part of the planets surface 307 */ 308 public boolean isInWorld() { 309 return !(xmin < -180.0 || xmax > 180.0 || ymin < -90.0 || ymax > 90.0); 310 } 311 287 312 @Override 288 313 public String toString() { -
trunk/src/org/openstreetmap/josm/data/osm/DataSet.java
r11115 r11269 504 504 tr("Unable to add primitive {0} to the dataset because it is already included", primitive.toString())); 505 505 506 primitive.updatePosition(); // Set cached bbox for way and relation (required for reindexWay and reinexRelation to work properly) 506 allPrimitives.add(primitive); 507 primitive.setDataset(this); 508 primitive.updatePosition(); // Set cached bbox for way and relation (required for reindexWay and reindexRelation to work properly) 507 509 boolean success = false; 508 510 if (primitive instanceof Node) { … … 515 517 if (!success) 516 518 throw new RuntimeException("failed to add primitive: "+primitive); 517 allPrimitives.add(primitive);518 primitive.setDataset(this);519 519 firePrimitivesAdded(Collections.singletonList(primitive), false); 520 520 } finally { -
trunk/src/org/openstreetmap/josm/data/osm/Node.java
r10919 r11269 330 330 @Override 331 331 public BBox getBBox() { 332 return new BBox(this); 332 return new BBox(lon, lat); 333 } 334 335 @Override 336 protected void addToBBox(BBox box, Set<PrimitiveId> visited) { 337 box.add(lon, lat); 333 338 } 334 339 -
trunk/src/org/openstreetmap/josm/data/osm/OsmPrimitive.java
r11079 r11269 1417 1417 return false; 1418 1418 } 1419 1420 /** 1421 * If necessary, extend the bbox to contain this primitive 1422 * @param box a bbox instance 1423 * @param visited a set of visited members or null 1424 */ 1425 protected abstract void addToBBox(BBox box, Set<PrimitiveId> visited); 1419 1426 } -
trunk/src/org/openstreetmap/josm/data/osm/QuadBuckets.java
r11237 r11269 6 6 import java.util.Collection; 7 7 import java.util.Iterator; 8 import java.util.LinkedHashSet; 8 9 import java.util.List; 9 10 import java.util.NoSuchElementException; … … 167 168 168 169 boolean matches(final T o, final BBox searchBbox) { 169 if (o instanceof Node) {170 final LatLon latLon = ((Node) o).getCoor();171 // node without coords -> bbox[0,0,0,0]172 return searchBbox.bounds(latLon != null ? latLon : LatLon.ZERO);173 }174 170 return o.getBBox().intersects(searchBbox); 175 171 } … … 359 355 private QBLevel<T> searchCache; 360 356 private int size; 357 private Collection<T> invalidBBoxPrimitives; 361 358 362 359 /** … … 370 367 public final void clear() { 371 368 root = new QBLevel<>(); 369 invalidBBoxPrimitives = new LinkedHashSet<>(); 372 370 searchCache = null; 373 371 size = 0; … … 376 374 @Override 377 375 public boolean add(T n) { 378 root.add(n); 376 if (n.getBBox().isValid()) 377 root.add(n); 378 else 379 invalidBBoxPrimitives.add(n); 379 380 size++; 380 381 return true; … … 426 427 searchCache = null; // Search cache might point to one of removed buckets 427 428 QBLevel<T> bucket = root.findBucket(t.getBBox()); 428 if (bucket.removeContent(t)) { 429 boolean removed = bucket.removeContent(t); 430 if (!removed) 431 removed = invalidBBoxPrimitives.remove(o); 432 if (removed) 429 433 size--; 430 return true; 431 } else 432 return false; 434 return removed; 433 435 } 434 436 … … 437 439 @SuppressWarnings("unchecked") 438 440 T t = (T) o; 441 if (!t.getBBox().isValid()) 442 return invalidBBoxPrimitives.contains(o); 439 443 QBLevel<T> bucket = root.findBucket(t.getBBox()); 440 444 return bucket != null && bucket.content != null && bucket.content.contains(t); … … 466 470 private QBLevel<T> currentNode; 467 471 private int contentIndex; 472 private Iterator<T> invalidBBoxIterator = invalidBBoxPrimitives.iterator(); 473 boolean fromInvalidBBoxPrimitives; 468 474 QuadBuckets<T> qb; 469 475 … … 491 497 @Override 492 498 public boolean hasNext() { 493 if (this.peek() == null) 494 return false; 499 if (this.peek() == null) { 500 fromInvalidBBoxPrimitives = true; 501 return invalidBBoxIterator.hasNext(); 502 } 495 503 return true; 496 504 } … … 513 521 @Override 514 522 public T next() { 523 if (fromInvalidBBoxPrimitives) 524 return invalidBBoxIterator.next(); 515 525 T ret = peek(); 516 526 if (ret == null) … … 522 532 @Override 523 533 public void remove() { 524 // two uses 525 // 1. Back up to the thing we just returned 526 // 2. move the index back since we removed 527 // an element 528 contentIndex--; 529 T object = peek(); 530 if (currentNode.removeContent(object)) 534 if (fromInvalidBBoxPrimitives) { 535 invalidBBoxIterator.remove(); 531 536 qb.size--; 537 } else { 538 // two uses 539 // 1. Back up to the thing we just returned 540 // 2. move the index back since we removed 541 // an element 542 contentIndex--; 543 T object = peek(); 544 if (currentNode.removeContent(object)) 545 qb.size--; 546 547 } 532 548 } 533 549 } … … 555 571 public List<T> search(BBox searchBbox) { 556 572 List<T> ret = new ArrayList<>(); 573 if (!searchBbox.isValid()) { 574 return ret; 575 } 576 557 577 // Doing this cuts down search cost on a real-life data set by about 25% 558 578 if (searchCache == null) { -
trunk/src/org/openstreetmap/josm/data/osm/Relation.java
r11038 r11269 447 447 @Override 448 448 public BBox getBBox() { 449 RelationMember[] members = this.members; 450 451 if (members.length == 0) 452 return new BBox(0, 0, 0, 0); 453 if (getDataSet() == null) 454 return calculateBBox(new HashSet<PrimitiveId>()); 455 else { 456 if (bbox == null) { 457 bbox = calculateBBox(new HashSet<PrimitiveId>()); 458 } 459 if (bbox == null) 460 return new BBox(0, 0, 0, 0); // No real members 461 else 462 return new BBox(bbox); 463 } 464 } 465 466 private BBox calculateBBox(Set<PrimitiveId> visitedRelations) { 467 if (visitedRelations.contains(this)) 468 return null; 469 visitedRelations.add(this); 470 471 RelationMember[] members = this.members; 472 if (members.length == 0) 473 return null; 474 else { 475 BBox result = null; 476 for (RelationMember rm:members) { 477 BBox box = rm.isRelation() ? rm.getRelation().calculateBBox(visitedRelations) : rm.getMember().getBBox(); 478 if (box != null) { 479 if (result == null) { 480 result = box; 481 } else { 482 result.add(box); 483 } 484 } 485 } 486 return result; 449 if (getDataSet() != null && bbox != null) 450 return new BBox(bbox); // use cached value 451 452 BBox box = new BBox(); 453 addToBBox(box, new HashSet<PrimitiveId>()); 454 if (getDataSet() != null) 455 bbox = box; // set cache 456 return new BBox(box); 457 } 458 459 @Override 460 protected void addToBBox(BBox box, Set<PrimitiveId> visited) { 461 for (RelationMember rm : members) { 462 if (visited.add(rm.getMember())) 463 rm.getMember().addToBBox(box, visited); 487 464 } 488 465 } … … 490 467 @Override 491 468 public void updatePosition() { 492 bbox = calculateBBox(new HashSet<PrimitiveId>()); 469 bbox = null; // make sure that it is recalculated 470 bbox = getBBox(); 493 471 } 494 472 -
trunk/src/org/openstreetmap/josm/data/osm/Way.java
r10662 r11269 631 631 } 632 632 return new BBox(bbox); 633 } 634 635 @Override 636 protected void addToBBox(BBox box, Set<PrimitiveId> visited) { 637 box.add(getBBox()); 633 638 } 634 639 -
trunk/test/unit/org/openstreetmap/josm/data/osm/BBoxTest.java
r10945 r11269 2 2 package org.openstreetmap.josm.data.osm; 3 3 4 import static org.junit.Assert.assertFalse; 5 import static org.junit.Assert.assertTrue; 6 4 7 import org.junit.Rule; 5 8 import org.junit.Test; 9 import org.openstreetmap.josm.data.coor.LatLon; 6 10 import org.openstreetmap.josm.testutils.JOSMTestRules; 7 11 … … 31 35 .verify(); 32 36 } 37 38 /** 39 * Test LatLon constructor which might result in invalid bbox 40 */ 41 @Test 42 public void testLatLonConstructor() { 43 LatLon latLon1 = new LatLon(10, 20); 44 LatLon latLon2 = new LatLon(20, 10); 45 BBox b1 = new BBox(latLon1, latLon2); 46 BBox b2 = new BBox(latLon2, latLon1); 47 assertTrue(b1.bounds(latLon1)); 48 assertTrue(b2.bounds(latLon1)); 49 assertTrue(b1.bounds(latLon2)); 50 assertTrue(b2.bounds(latLon2)); 51 assertTrue(b2.bounds(b1)); 52 assertTrue(b1.bounds(b2)); 53 54 // invalid latlon values 55 LatLon invalid1 = new LatLon(-190, 340); 56 BBox b3 = new BBox(invalid1, latLon1); 57 BBox b4 = new BBox(latLon1, invalid1); 58 BBox b5 = new BBox(invalid1, invalid1); 59 // what should be the result? 60 assertTrue(b3.isValid()); 61 assertTrue(b4.isValid()); 62 assertTrue(b3.bounds(latLon1)); 63 assertTrue(b4.bounds(latLon1)); 64 assertTrue(b5.isValid()); 65 assertFalse(b5.isInWorld()); 66 } 67 33 68 } -
trunk/test/unit/org/openstreetmap/josm/data/osm/NodeTest.java
r10945 r11269 2 2 package org.openstreetmap.josm.data.osm; 3 3 4 import static org.junit.Assert.assertEquals; 4 5 import static org.junit.Assert.assertFalse; 5 6 import static org.junit.Assert.assertNotNull; 6 7 import static org.junit.Assert.assertNull; 8 import static org.junit.Assert.assertTrue; 7 9 8 10 import org.junit.Rule; … … 43 45 assertFalse(n.isOutsideDownloadArea()); 44 46 } 47 48 /** 49 * Test BBox calculation with Node 50 */ 51 @Test 52 public void testBBox() { 53 DataSet ds = new DataSet(); 54 Node n1 = new Node(1); 55 Node n2 = new Node(2); 56 Node n3 = new Node(3); 57 Node n4 = new Node(4); 58 n1.setIncomplete(true); 59 n2.setCoor(new LatLon(10, 10)); 60 n3.setCoor(new LatLon(20, 20)); 61 n4.setCoor(new LatLon(90, 180)); 62 ds.addPrimitive(n1); 63 ds.addPrimitive(n2); 64 ds.addPrimitive(n3); 65 ds.addPrimitive(n4); 66 67 assertFalse(n1.getBBox().isValid()); 68 assertTrue(n2.getBBox().isValid()); 69 assertTrue(n3.getBBox().isValid()); 70 assertTrue(n4.getBBox().isValid()); 71 BBox box1 = n1.getBBox(); 72 box1.add(n2.getCoor()); 73 assertTrue(box1.isValid()); 74 BBox box2 = n2.getBBox(); 75 box2.add(n1.getCoor()); 76 assertTrue(box2.isValid()); 77 assertEquals(box1, box2); 78 box1.add(n3.getCoor()); 79 assertTrue(box1.isValid()); 80 assertEquals(box1.getCenter(), new LatLon(15, 15)); 81 } 45 82 } -
trunk/test/unit/org/openstreetmap/josm/data/osm/QuadBucketsTest.java
r10945 r11269 9 9 import java.util.Iterator; 10 10 import java.util.List; 11 import java.util.Random; 11 12 12 13 import org.fest.reflect.core.Reflection; … … 175 176 } 176 177 Assert.assertEquals(0, qbWays.size()); 178 179 } 180 181 /** 182 * Add more data so that quad buckets tree has a few leaves 183 */ 184 @Test 185 public void testSplitsWithIncompleteData() { 186 DataSet ds = new DataSet(); 187 long nodeId = 1; 188 long wayId = 1; 189 final int NUM_COMPLETE_WAYS = 300; 190 final int NUM_INCOMPLETE_WAYS = 10; 191 final int NUM_NODES_PER_WAY = 20; 192 final int NUM_INCOMPLETE_NODES = 10; 193 194 // force splits in quad buckets 195 Random random = new Random(31); 196 for (int i = 0; i < NUM_COMPLETE_WAYS; i++) { 197 Way w = new Way(wayId++); 198 List<Node> nodes = new ArrayList<>(); 199 double center = random.nextDouble() * 10; 200 for (int j = 0; j < NUM_NODES_PER_WAY; j++) { 201 Node n = new Node(nodeId++); 202 double lat = random.nextDouble() * 0.001; 203 double lon = random.nextDouble() * 0.001; 204 n.setCoor(new LatLon(center + lat, center + lon)); 205 nodes.add(n); 206 ds.addPrimitive(n); 207 } 208 w.setNodes(nodes); 209 ds.addPrimitive(w); 210 } 211 Assert.assertEquals(NUM_COMPLETE_WAYS, ds.getWays().size()); 212 Assert.assertEquals(NUM_COMPLETE_WAYS * NUM_NODES_PER_WAY, ds.getNodes().size()); 213 214 // add some incomplete nodes 215 List<Node> incompleteNodes = new ArrayList<>(); 216 for (int i = 0; i < NUM_INCOMPLETE_NODES; i++) { 217 Node n = new Node(nodeId++); 218 incompleteNodes.add(n); 219 n.setIncomplete(true); 220 ds.addPrimitive(n); 221 } 222 Assert.assertEquals(NUM_COMPLETE_WAYS * NUM_NODES_PER_WAY + NUM_INCOMPLETE_NODES, ds.getNodes().size()); 223 // add some incomplete ways 224 List<Way> incompleteWays = new ArrayList<>(); 225 for (int i = 0; i < NUM_INCOMPLETE_WAYS; i++) { 226 Way w = new Way(wayId++); 227 incompleteWays.add(w); 228 w.setIncomplete(true); 229 ds.addPrimitive(w); 230 } 231 Assert.assertEquals(NUM_COMPLETE_WAYS + NUM_INCOMPLETE_WAYS, ds.getWays().size()); 232 233 BBox planet = new BBox(-180, -90, 180, 90); 234 // incomplete ways should not be found with search 235 Assert.assertEquals(NUM_COMPLETE_WAYS, ds.searchWays(planet).size()); 236 // incomplete ways are only retrieved via iterator or object reference 237 for (Way w : incompleteWays) { 238 Assert.assertTrue(ds.getWays().contains(w)); 239 } 240 241 QuadBuckets<Way> qb = new QuadBuckets<>(); 242 qb.addAll(ds.getWays()); 243 int count = qb.size(); 244 Assert.assertEquals(count, ds.getWays().size()); 245 Iterator<Way> iter = qb.iterator(); 246 while (iter.hasNext()) { 247 iter.next(); 248 iter.remove(); 249 count--; 250 Assert.assertEquals(count, qb.size()); 251 } 252 Assert.assertEquals(0, qb.size()); 177 253 } 178 254 } -
trunk/test/unit/org/openstreetmap/josm/data/osm/RelationTest.java
r10945 r11269 76 76 Assert.assertEquals(w1.getBBox(), r1.getBBox()); 77 77 Assert.assertEquals(w1.getBBox(), r2.getBBox()); 78 79 // create incomplete node and add it to the relation, this must not change the bbox 80 BBox oldBBox = r2.getBBox(); 81 Node n4 = new Node(); 82 n4.setIncomplete(true); 83 ds.addPrimitive(n4); 84 r2.addMember(new RelationMember("", n4)); 85 86 Assert.assertEquals(oldBBox, r2.getBBox()); 78 87 } 79 88
Note:
See TracChangeset
for help on using the changeset viewer.