Ticket #13289: improve_MultipolygonBuilder_v2.patch

File improve_MultipolygonBuilder_v2.patch, 8.8 KB (added by GerdP, 5 years ago)
  • src/org/openstreetmap/josm/data/osm/MultipolygonBuilder.java

     
    1515import java.util.List;
    1616import java.util.Map;
    1717import java.util.Set;
     18import java.util.concurrent.ConcurrentHashMap;
    1819import java.util.concurrent.ForkJoinPool;
    1920import java.util.concurrent.ForkJoinTask;
    2021import java.util.concurrent.RecursiveTask;
     
    4041            Utils.newForkJoinPool("multipolygon_creation.numberOfThreads", "multipolygon-builder-%d", Thread.NORM_PRIORITY);
    4142
    4243    /**
     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    /**
    43103     * Represents one polygon that consists of multiple ways.
    44104     */
    45105    public static class JoinedPolygon {
     
    291351        return null;
    292352    }
    293353
    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) {
    295356        boolean outerGood = true;
    296357        List<JoinedPolygon> innerCandidates = new ArrayList<>();
    297358
     
    303364            // Preliminary computation on bounds. If bounds do not intersect, no need to do a costly area intersection
    304365            if (outerWay.bounds.intersects(innerWay.bounds)) {
    305366                // 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                }
    307372
    308373                if (intersection == PolygonIntersection.FIRST_INSIDE_SECOND) {
    309374                    outerGood = false;  // outer is inside another polygon
     
    326391     * @return the outermostWay, or {@code null} if intersection found.
    327392     */
    328393    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>(),
    330396                Math.max(32, boundaryWays.size() / THREAD_POOL.getParallelism() / 3)));
    331397    }
    332398
     
    340406        private final int to;
    341407        private final transient List<PolygonLevel> output;
    342408        private final int directExecutionTaskSize;
     409        private final IntersectionMatrix cache;
    343410
    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;
    345413            this.input = input;
    346414            this.from = from;
    347415            this.to = to;
     
    352420        /**
    353421         * Collects outer way and corresponding inner ways from all boundaries.
    354422         * @param level nesting level
     423         * @param cache cache that tracks previously calculated results
    355424         * @param boundaryWays boundary ways
    356425         * @return the outermostWay, or {@code null} if intersection found.
    357426         */
    358         private static List<PolygonLevel> findOuterWaysRecursive(int level, List<JoinedPolygon> boundaryWays) {
     427        private static List<PolygonLevel> findOuterWaysRecursive(int level, IntersectionMatrix cache, List<JoinedPolygon> boundaryWays) {
    359428
    360429            final List<PolygonLevel> result = new ArrayList<>();
    361430
    362431            for (JoinedPolygon outerWay : boundaryWays) {
    363                 if (processOuterWay(level, boundaryWays, result, outerWay) == null) {
     432                if (processOuterWay(level, cache, boundaryWays, result, outerWay) == null) {
    364433                    return null;
    365434                }
    366435            }
     
    368437            return result;
    369438        }
    370439
    371         private static List<PolygonLevel> processOuterWay(int level, List<JoinedPolygon> boundaryWays,
     440        private static List<PolygonLevel> processOuterWay(int level, IntersectionMatrix cache, List<JoinedPolygon> boundaryWays,
    372441                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);
    374443            if (p == null) {
    375444                // ways intersect
    376445                return null;
     
    382451
    383452                //process inner ways
    384453                if (!p.b.isEmpty()) {
    385                     List<PolygonLevel> innerList = findOuterWaysRecursive(level + 1, p.b);
     454                    List<PolygonLevel> innerList = findOuterWaysRecursive(level + 1, cache, p.b);
    386455                    if (innerList == null) {
    387456                        return null; //intersection found
    388457                    }
     
    408477            } else {
    409478                final Collection<ForkJoinTask<List<PolygonLevel>>> tasks = new ArrayList<>();
    410479                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),
    412481                            new ArrayList<PolygonLevel>(), directExecutionTaskSize));
    413482                }
    414483                for (ForkJoinTask<List<PolygonLevel>> task : ForkJoinTask.invokeAll(tasks)) {
     
    424493
    425494        List<PolygonLevel> computeDirectly() {
    426495            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) {
    428497                    return null;
    429498                }
    430499            }