Ticket #13289: improve_MultipolygonBuilder_v3.patch
File improve_MultipolygonBuilder_v3.patch, 9.9 KB (added by , 9 years ago) |
---|
-
src/org/openstreetmap/josm/data/osm/MultipolygonBuilder.java
11 11 import java.util.ArrayList; 12 12 import java.util.Collection; 13 13 import java.util.Collections; 14 import java.util.HashMap; 14 15 import java.util.HashSet; 15 16 import java.util.List; 16 17 import java.util.Map; … … 40 41 Utils.newForkJoinPool("multipolygon_creation.numberOfThreads", "multipolygon-builder-%d", Thread.NORM_PRIORITY); 41 42 42 43 /** 44 * Helper class to avoid unneeded costly intersection calculations. 45 * If the intersection between polygons a and b was calculated we also know 46 * the result of intersection between b and a. The lookup in the hash tables is 47 * much faster than the intersection calculation. 48 */ 49 private static class IntersectionMatrix { 50 private final Map<JoinedPolygon, Map<JoinedPolygon, PolygonIntersection>> results; 51 private long countTest; 52 private long countMiss; 53 54 IntersectionMatrix(Collection<JoinedPolygon> polygons) { 55 results = new HashMap<>(polygons.size()); 56 } 57 58 /** 59 * Compute the reverse result of the intersection test done by {@code 60 Geometry.polygonIntersection(Area a1, Area a2)} 61 * @param intersection the intersection result for polygons a1 and a2 (in that order) 62 * @return the intersection result for a2 and a1 63 */ 64 private PolygonIntersection getReverseIntersectionResult(PolygonIntersection intersection) { 65 if (intersection == PolygonIntersection.FIRST_INSIDE_SECOND) 66 return PolygonIntersection.SECOND_INSIDE_FIRST; 67 else if (intersection == PolygonIntersection.SECOND_INSIDE_FIRST) 68 return PolygonIntersection.FIRST_INSIDE_SECOND; 69 return intersection; 70 } 71 72 /** 73 * Store the result of the intersection test done by {@code 74 Geometry.polygonIntersection(Area a1, Area a2)} 75 * @param a1 first polygon 76 * @param a2 second polygon 77 * @param intersection result of {@code Geometry.polygonIntersection(a1,a2)} 78 */ 79 private synchronized void updateMap(JoinedPolygon a1, JoinedPolygon a2, PolygonIntersection intersection) { 80 Map<JoinedPolygon, PolygonIntersection> subMap = results.get(a1); 81 if (subMap == null) { 82 subMap = new HashMap<>(); 83 results.put(a1, subMap); 84 } 85 subMap.put(a2, intersection); 86 } 87 88 /** 89 * Store the result of intersection between two polygons 90 * @param a1 first polygon 91 * @param a2 second polygon 92 * @param intersection result of {@code Geometry.polygonIntersection(a1,a2)} 93 */ 94 public void storeResult(JoinedPolygon a1, JoinedPolygon a2, PolygonIntersection intersection) { 95 updateMap(a1, a2, intersection); 96 updateMap(a2, a1, getReverseIntersectionResult(intersection)); 97 } 98 99 /** 100 * Check if the intersection result is available. 101 * @param a1 first polygon 102 * @param a2 second polygon 103 * @return the result {@code Geometry.polygonIntersection(a1,a2)} or null if this was not yet stored 104 */ 105 public synchronized PolygonIntersection checkIfKnownIntersection(JoinedPolygon a1, JoinedPolygon a2) { 106 countTest++; 107 Map<JoinedPolygon, PolygonIntersection> subMap = results.get(a1); 108 PolygonIntersection res = null; 109 if (subMap != null) 110 res = subMap.get(a2); 111 if (res == null) 112 countMiss++; 113 return res; 114 } 115 116 @Override 117 public String toString() { 118 return "IntersectionMatrix [countTest=" + countTest + ", countMiss=" + countMiss + "]"; 119 } 120 121 } 122 123 /** 43 124 * Represents one polygon that consists of multiple ways. 44 125 */ 45 126 public static class JoinedPolygon { … … 291 372 return null; 292 373 } 293 374 294 private static Pair<Boolean, List<JoinedPolygon>> findInnerWaysCandidates(JoinedPolygon outerWay, Collection<JoinedPolygon> boundaryWays) { 375 private static Pair<Boolean, List<JoinedPolygon>> findInnerWaysCandidates(IntersectionMatrix cache, 376 JoinedPolygon outerWay, Collection<JoinedPolygon> boundaryWays) { 295 377 boolean outerGood = true; 296 378 List<JoinedPolygon> innerCandidates = new ArrayList<>(); 297 379 … … 303 385 // Preliminary computation on bounds. If bounds do not intersect, no need to do a costly area intersection 304 386 if (outerWay.bounds.intersects(innerWay.bounds)) { 305 387 // Bounds intersection, let's see in detail 306 PolygonIntersection intersection = Geometry.polygonIntersection(outerWay.area, innerWay.area); 388 PolygonIntersection intersection = cache.checkIfKnownIntersection(outerWay, innerWay); 389 if (intersection == null) { 390 intersection = Geometry.polygonIntersection(outerWay.area, innerWay.area); 391 cache.storeResult(outerWay, innerWay, intersection); 392 } 307 393 308 394 if (intersection == PolygonIntersection.FIRST_INSIDE_SECOND) { 309 395 outerGood = false; // outer is inside another polygon … … 326 412 * @return the outermostWay, or {@code null} if intersection found. 327 413 */ 328 414 private static List<PolygonLevel> findOuterWaysMultiThread(List<JoinedPolygon> boundaryWays) { 329 return THREAD_POOL.invoke(new Worker(boundaryWays, 0, boundaryWays.size(), new ArrayList<PolygonLevel>(), 415 IntersectionMatrix im = new IntersectionMatrix(boundaryWays); 416 List<PolygonLevel> res = THREAD_POOL.invoke(new Worker(im, boundaryWays, 0, boundaryWays.size(), new ArrayList<PolygonLevel>(), 330 417 Math.max(32, boundaryWays.size() / THREAD_POOL.getParallelism() / 3))); 418 if (!boundaryWays.isEmpty()) 419 Main.trace(im.toString()); 420 return res; 331 421 } 332 422 333 423 private static class Worker extends RecursiveTask<List<PolygonLevel>> { … … 340 430 private final int to; 341 431 private final transient List<PolygonLevel> output; 342 432 private final int directExecutionTaskSize; 433 private final IntersectionMatrix cache; 343 434 344 Worker(List<JoinedPolygon> input, int from, int to, List<PolygonLevel> output, int directExecutionTaskSize) { 435 Worker(IntersectionMatrix cache, List<JoinedPolygon> input, int from, int to, List<PolygonLevel> output, int directExecutionTaskSize) { 436 this.cache = cache; 345 437 this.input = input; 346 438 this.from = from; 347 439 this.to = to; … … 352 444 /** 353 445 * Collects outer way and corresponding inner ways from all boundaries. 354 446 * @param level nesting level 447 * @param cache cache that tracks previously calculated results 355 448 * @param boundaryWays boundary ways 356 449 * @return the outermostWay, or {@code null} if intersection found. 357 450 */ 358 private static List<PolygonLevel> findOuterWaysRecursive(int level, List<JoinedPolygon> boundaryWays) {451 private static List<PolygonLevel> findOuterWaysRecursive(int level, IntersectionMatrix cache, List<JoinedPolygon> boundaryWays) { 359 452 360 453 final List<PolygonLevel> result = new ArrayList<>(); 361 454 362 455 for (JoinedPolygon outerWay : boundaryWays) { 363 if (processOuterWay(level, boundaryWays, result, outerWay) == null) {456 if (processOuterWay(level, cache, boundaryWays, result, outerWay) == null) { 364 457 return null; 365 458 } 366 459 } … … 368 461 return result; 369 462 } 370 463 371 private static List<PolygonLevel> processOuterWay(int level, List<JoinedPolygon> boundaryWays,464 private static List<PolygonLevel> processOuterWay(int level, IntersectionMatrix cache, List<JoinedPolygon> boundaryWays, 372 465 final List<PolygonLevel> result, JoinedPolygon outerWay) { 373 Pair<Boolean, List<JoinedPolygon>> p = findInnerWaysCandidates( outerWay, boundaryWays);466 Pair<Boolean, List<JoinedPolygon>> p = findInnerWaysCandidates(cache, outerWay, boundaryWays); 374 467 if (p == null) { 375 468 // ways intersect 376 469 return null; … … 382 475 383 476 //process inner ways 384 477 if (!p.b.isEmpty()) { 385 List<PolygonLevel> innerList = findOuterWaysRecursive(level + 1, p.b);478 List<PolygonLevel> innerList = findOuterWaysRecursive(level + 1, cache, p.b); 386 479 if (innerList == null) { 387 480 return null; //intersection found 388 481 } … … 408 501 } else { 409 502 final Collection<ForkJoinTask<List<PolygonLevel>>> tasks = new ArrayList<>(); 410 503 for (int fromIndex = from; fromIndex < to; fromIndex += directExecutionTaskSize) { 411 tasks.add(new Worker( input, fromIndex, Math.min(fromIndex + directExecutionTaskSize, to),504 tasks.add(new Worker(cache, input, fromIndex, Math.min(fromIndex + directExecutionTaskSize, to), 412 505 new ArrayList<PolygonLevel>(), directExecutionTaskSize)); 413 506 } 414 507 for (ForkJoinTask<List<PolygonLevel>> task : ForkJoinTask.invokeAll(tasks)) { … … 424 517 425 518 List<PolygonLevel> computeDirectly() { 426 519 for (int i = from; i < to; i++) { 427 if (processOuterWay(0, input, output, input.get(i)) == null) {520 if (processOuterWay(0, cache, input, output, input.get(i)) == null) { 428 521 return null; 429 522 } 430 523 }