Ticket #13289: improve_MultipolygonBuilder_v2.patch
File improve_MultipolygonBuilder_v2.patch, 8.8 KB (added by , 8 years ago) |
---|
-
src/org/openstreetmap/josm/data/osm/MultipolygonBuilder.java
15 15 import java.util.List; 16 16 import java.util.Map; 17 17 import java.util.Set; 18 import java.util.concurrent.ConcurrentHashMap; 18 19 import java.util.concurrent.ForkJoinPool; 19 20 import java.util.concurrent.ForkJoinTask; 20 21 import java.util.concurrent.RecursiveTask; … … 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 long countCheck; 51 private long countMiss; 52 private final Map<JoinedPolygon, Map<JoinedPolygon, PolygonIntersection>> results; 53 54 IntersectionMatrix(Collection<JoinedPolygon> polygons) { 55 results = new ConcurrentHashMap<>(polygons.size()); 56 } 57 58 private PolygonIntersection getReverseIntersectionResult(PolygonIntersection intersection) { 59 if (intersection == PolygonIntersection.FIRST_INSIDE_SECOND) 60 return PolygonIntersection.SECOND_INSIDE_FIRST; 61 else if (intersection == PolygonIntersection.SECOND_INSIDE_FIRST) 62 return PolygonIntersection.FIRST_INSIDE_SECOND; 63 return intersection; 64 } 65 66 private void updateMap(JoinedPolygon pa, JoinedPolygon pb, PolygonIntersection intersection) { 67 Map<JoinedPolygon, PolygonIntersection> subMap = results.get(pa); 68 if (subMap == null) { 69 subMap = new ConcurrentHashMap<>(); 70 results.put(pa, subMap); 71 } 72 subMap.put(pb, intersection); 73 } 74 75 /** 76 * Store the result of intersection between two polygons 77 * @param pa first polygon 78 * @param pb second polygon 79 * @param intersection result of {@code Geometry.polygonIntersection(pa,pb)} 80 */ 81 public void update(JoinedPolygon pa, JoinedPolygon pb, PolygonIntersection intersection) { 82 updateMap(pa, pb, intersection); 83 updateMap(pb, pa, getReverseIntersectionResult(intersection)); 84 } 85 86 public PolygonIntersection getIntersection(JoinedPolygon pa, JoinedPolygon pb) { 87 countCheck++; 88 Map<JoinedPolygon, PolygonIntersection> subMap = results.get(pa); 89 if (subMap == null) { 90 countMiss++; 91 return null; 92 } 93 return subMap.get(pb); 94 } 95 96 @Override 97 public String toString() { 98 return "Tests: " + countCheck + " hit/miss " + (countCheck - countMiss) + "/" + countMiss; 99 } 100 } 101 102 /** 43 103 * Represents one polygon that consists of multiple ways. 44 104 */ 45 105 public static class JoinedPolygon { … … 291 351 return null; 292 352 } 293 353 294 private static Pair<Boolean, List<JoinedPolygon>> findInnerWaysCandidates(JoinedPolygon outerWay, Collection<JoinedPolygon> boundaryWays) { 354 private static Pair<Boolean, List<JoinedPolygon>> findInnerWaysCandidates(IntersectionMatrix cache, 355 JoinedPolygon outerWay, Collection<JoinedPolygon> boundaryWays) { 295 356 boolean outerGood = true; 296 357 List<JoinedPolygon> innerCandidates = new ArrayList<>(); 297 358 … … 303 364 // Preliminary computation on bounds. If bounds do not intersect, no need to do a costly area intersection 304 365 if (outerWay.bounds.intersects(innerWay.bounds)) { 305 366 // Bounds intersection, let's see in detail 306 PolygonIntersection intersection = Geometry.polygonIntersection(outerWay.area, innerWay.area); 367 PolygonIntersection intersection = cache.getIntersection(outerWay, innerWay); 368 if (intersection == null) { 369 intersection = Geometry.polygonIntersection(outerWay.area, innerWay.area); 370 cache.update(outerWay, innerWay, intersection); 371 } 307 372 308 373 if (intersection == PolygonIntersection.FIRST_INSIDE_SECOND) { 309 374 outerGood = false; // outer is inside another polygon … … 326 391 * @return the outermostWay, or {@code null} if intersection found. 327 392 */ 328 393 private static List<PolygonLevel> findOuterWaysMultiThread(List<JoinedPolygon> boundaryWays) { 329 return THREAD_POOL.invoke(new Worker(boundaryWays, 0, boundaryWays.size(), new ArrayList<PolygonLevel>(), 394 IntersectionMatrix im = new IntersectionMatrix(boundaryWays); 395 return THREAD_POOL.invoke(new Worker(im, boundaryWays, 0, boundaryWays.size(), new ArrayList<PolygonLevel>(), 330 396 Math.max(32, boundaryWays.size() / THREAD_POOL.getParallelism() / 3))); 331 397 } 332 398 … … 340 406 private final int to; 341 407 private final transient List<PolygonLevel> output; 342 408 private final int directExecutionTaskSize; 409 private final IntersectionMatrix cache; 343 410 344 Worker(List<JoinedPolygon> input, int from, int to, List<PolygonLevel> output, int directExecutionTaskSize) { 411 Worker(IntersectionMatrix cache, List<JoinedPolygon> input, int from, int to, List<PolygonLevel> output, int directExecutionTaskSize) { 412 this.cache = cache; 345 413 this.input = input; 346 414 this.from = from; 347 415 this.to = to; … … 352 420 /** 353 421 * Collects outer way and corresponding inner ways from all boundaries. 354 422 * @param level nesting level 423 * @param cache cache that tracks previously calculated results 355 424 * @param boundaryWays boundary ways 356 425 * @return the outermostWay, or {@code null} if intersection found. 357 426 */ 358 private static List<PolygonLevel> findOuterWaysRecursive(int level, List<JoinedPolygon> boundaryWays) {427 private static List<PolygonLevel> findOuterWaysRecursive(int level, IntersectionMatrix cache, List<JoinedPolygon> boundaryWays) { 359 428 360 429 final List<PolygonLevel> result = new ArrayList<>(); 361 430 362 431 for (JoinedPolygon outerWay : boundaryWays) { 363 if (processOuterWay(level, boundaryWays, result, outerWay) == null) {432 if (processOuterWay(level, cache, boundaryWays, result, outerWay) == null) { 364 433 return null; 365 434 } 366 435 } … … 368 437 return result; 369 438 } 370 439 371 private static List<PolygonLevel> processOuterWay(int level, List<JoinedPolygon> boundaryWays,440 private static List<PolygonLevel> processOuterWay(int level, IntersectionMatrix cache, List<JoinedPolygon> boundaryWays, 372 441 final List<PolygonLevel> result, JoinedPolygon outerWay) { 373 Pair<Boolean, List<JoinedPolygon>> p = findInnerWaysCandidates( outerWay, boundaryWays);442 Pair<Boolean, List<JoinedPolygon>> p = findInnerWaysCandidates(cache, outerWay, boundaryWays); 374 443 if (p == null) { 375 444 // ways intersect 376 445 return null; … … 382 451 383 452 //process inner ways 384 453 if (!p.b.isEmpty()) { 385 List<PolygonLevel> innerList = findOuterWaysRecursive(level + 1, p.b);454 List<PolygonLevel> innerList = findOuterWaysRecursive(level + 1, cache, p.b); 386 455 if (innerList == null) { 387 456 return null; //intersection found 388 457 } … … 408 477 } else { 409 478 final Collection<ForkJoinTask<List<PolygonLevel>>> tasks = new ArrayList<>(); 410 479 for (int fromIndex = from; fromIndex < to; fromIndex += directExecutionTaskSize) { 411 tasks.add(new Worker( input, fromIndex, Math.min(fromIndex + directExecutionTaskSize, to),480 tasks.add(new Worker(cache, input, fromIndex, Math.min(fromIndex + directExecutionTaskSize, to), 412 481 new ArrayList<PolygonLevel>(), directExecutionTaskSize)); 413 482 } 414 483 for (ForkJoinTask<List<PolygonLevel>> task : ForkJoinTask.invokeAll(tasks)) { … … 424 493 425 494 List<PolygonLevel> computeDirectly() { 426 495 for (int i = from; i < to; i++) { 427 if (processOuterWay(0, input, output, input.get(i)) == null) {496 if (processOuterWay(0, cache, input, output, input.get(i)) == null) { 428 497 return null; 429 498 } 430 499 }