Ticket #13289: improve_MultipolygonBuilder_v3.patch

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

     
    1111import java.util.ArrayList;
    1212import java.util.Collection;
    1313import java.util.Collections;
     14import java.util.HashMap;
    1415import java.util.HashSet;
    1516import java.util.List;
    1617import java.util.Map;
     
    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 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    /**
    43124     * Represents one polygon that consists of multiple ways.
    44125     */
    45126    public static class JoinedPolygon {
     
    291372        return null;
    292373    }
    293374
    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) {
    295377        boolean outerGood = true;
    296378        List<JoinedPolygon> innerCandidates = new ArrayList<>();
    297379
     
    303385            // Preliminary computation on bounds. If bounds do not intersect, no need to do a costly area intersection
    304386            if (outerWay.bounds.intersects(innerWay.bounds)) {
    305387                // 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                }
    307393
    308394                if (intersection == PolygonIntersection.FIRST_INSIDE_SECOND) {
    309395                    outerGood = false;  // outer is inside another polygon
     
    326412     * @return the outermostWay, or {@code null} if intersection found.
    327413     */
    328414    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>(),
    330417                Math.max(32, boundaryWays.size() / THREAD_POOL.getParallelism() / 3)));
     418        if (!boundaryWays.isEmpty())
     419            Main.trace(im.toString());
     420        return res;
    331421    }
    332422
    333423    private static class Worker extends RecursiveTask<List<PolygonLevel>> {
     
    340430        private final int to;
    341431        private final transient List<PolygonLevel> output;
    342432        private final int directExecutionTaskSize;
     433        private final IntersectionMatrix cache;
    343434
    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;
    345437            this.input = input;
    346438            this.from = from;
    347439            this.to = to;
     
    352444        /**
    353445         * Collects outer way and corresponding inner ways from all boundaries.
    354446         * @param level nesting level
     447         * @param cache cache that tracks previously calculated results
    355448         * @param boundaryWays boundary ways
    356449         * @return the outermostWay, or {@code null} if intersection found.
    357450         */
    358         private static List<PolygonLevel> findOuterWaysRecursive(int level, List<JoinedPolygon> boundaryWays) {
     451        private static List<PolygonLevel> findOuterWaysRecursive(int level, IntersectionMatrix cache, List<JoinedPolygon> boundaryWays) {
    359452
    360453            final List<PolygonLevel> result = new ArrayList<>();
    361454
    362455            for (JoinedPolygon outerWay : boundaryWays) {
    363                 if (processOuterWay(level, boundaryWays, result, outerWay) == null) {
     456                if (processOuterWay(level, cache, boundaryWays, result, outerWay) == null) {
    364457                    return null;
    365458                }
    366459            }
     
    368461            return result;
    369462        }
    370463
    371         private static List<PolygonLevel> processOuterWay(int level, List<JoinedPolygon> boundaryWays,
     464        private static List<PolygonLevel> processOuterWay(int level, IntersectionMatrix cache, List<JoinedPolygon> boundaryWays,
    372465                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);
    374467            if (p == null) {
    375468                // ways intersect
    376469                return null;
     
    382475
    383476                //process inner ways
    384477                if (!p.b.isEmpty()) {
    385                     List<PolygonLevel> innerList = findOuterWaysRecursive(level + 1, p.b);
     478                    List<PolygonLevel> innerList = findOuterWaysRecursive(level + 1, cache, p.b);
    386479                    if (innerList == null) {
    387480                        return null; //intersection found
    388481                    }
     
    408501            } else {
    409502                final Collection<ForkJoinTask<List<PolygonLevel>>> tasks = new ArrayList<>();
    410503                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),
    412505                            new ArrayList<PolygonLevel>(), directExecutionTaskSize));
    413506                }
    414507                for (ForkJoinTask<List<PolygonLevel>> task : ForkJoinTask.invokeAll(tasks)) {
     
    424517
    425518        List<PolygonLevel> computeDirectly() {
    426519            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) {
    428521                    return null;
    429522                }
    430523            }