Ticket #13289: improve_MultipolygonBuilder.patch

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

     
    3636    private static final ForkJoinPool THREAD_POOL =
    3737            Utils.newForkJoinPool("multipolygon_creation.numberOfThreads", "multipolygon-builder-%d", Thread.NORM_PRIORITY);
    3838
     39    private static class IntersectionMatrix {
     40        Geometry.PolygonIntersection[] results;
     41        private final int dim;
     42        private long countCheck;
     43        private long countMiss;
     44
     45        public IntersectionMatrix(Collection<JoinedPolygon> polygons) {
     46            int id = 0;
     47            for (JoinedPolygon p : polygons)
     48                p.setCacheId(id++);
     49
     50            this.dim = id;
     51            results = new Geometry.PolygonIntersection[dim*dim];
     52        }
     53
     54        private PolygonIntersection getReverseIntersectionResult(PolygonIntersection intersection) {
     55            if (intersection == PolygonIntersection.FIRST_INSIDE_SECOND){
     56                return PolygonIntersection.SECOND_INSIDE_FIRST;
     57            } else if (intersection == PolygonIntersection.SECOND_INSIDE_FIRST)
     58                return PolygonIntersection.FIRST_INSIDE_SECOND;
     59            return intersection;
     60        }
     61
     62        private void checkId (JoinedPolygon poly){
     63            if (poly.id < 0 || poly.id >= dim ){
     64                throw new JoinedPolygonCreationException(tr("Internal error: unexpected id in polygon", poly.id));
     65            }
     66
     67        }
     68        private synchronized void setResult(JoinedPolygon pa, JoinedPolygon pb, PolygonIntersection intersection){
     69            checkId(pa);
     70            checkId(pb);
     71            int posAB = pa.id * dim + pb.id;
     72            int posBA = pb.id * dim + pa.id;
     73            results[posAB] = intersection;
     74            results[posBA] = getReverseIntersectionResult(intersection);
     75        }
     76
     77        private synchronized PolygonIntersection getResult(JoinedPolygon pa, JoinedPolygon pb){
     78            checkId(pa);
     79            checkId(pb);
     80            countCheck++;
     81            int posAB = pa.id * dim + pb.id;
     82            if (results[posAB] == null)
     83                countMiss++;
     84            return results[posAB];
     85        }
     86
     87        @Override
     88        public String toString() {
     89            return "Tests: " + countCheck + " hit/miss " + (countCheck - countMiss) + "/" + countMiss;
     90        }
     91    }
     92
    3993    /**
    4094     * Represents one polygon that consists of multiple ways.
    4195     */
     
    4599        public final List<Node> nodes;
    46100        public final Area area;
    47101        public final Rectangle bounds;
     102        private int id;
    48103
     104
    49105        /**
    50106         * Constructs a new {@code JoinedPolygon} from given list of ways.
    51107         * @param ways The ways used to build joined polygon
     
    57113            this.nodes = this.getNodes();
    58114            this.area = Geometry.getArea(nodes);
    59115            this.bounds = area.getBounds();
     116            this.id = -1;
    60117        }
    61118
    62119        /**
     
    91148
    92149            return nodes;
    93150        }
     151
     152        /**
     153         * Set id that is used in ResultCache
     154         * @param id
     155         */
     156        public void setCacheId(int id) {
     157            this.id = id;
     158        }
    94159    }
    95160
    96161    /**
     
    240305            usedWays.addAll(collectedWays);
    241306            joinedWays.add(new JoinedPolygon(collectedWays, collectedWaysReverse));
    242307        }
    243 
    244308        return joinedWays;
    245309    }
    246310
     
    271335        return null;
    272336    }
    273337
    274     private static Pair<Boolean, List<JoinedPolygon>> findInnerWaysCandidates(JoinedPolygon outerWay, Collection<JoinedPolygon> boundaryWays) {
     338    private static Pair<Boolean, List<JoinedPolygon>> findInnerWaysCandidates(IntersectionMatrix cache, JoinedPolygon outerWay, Collection<JoinedPolygon> boundaryWays) {
    275339        boolean outerGood = true;
    276340        List<JoinedPolygon> innerCandidates = new ArrayList<>();
    277341
     
    283347            // Preliminary computation on bounds. If bounds do not intersect, no need to do a costly area intersection
    284348            if (outerWay.bounds.intersects(innerWay.bounds)) {
    285349                // Bounds intersection, let's see in detail
    286                 PolygonIntersection intersection = Geometry.polygonIntersection(outerWay.area, innerWay.area);
     350                PolygonIntersection intersection = cache.getResult(outerWay, innerWay);
     351                if (intersection == null){
     352                    intersection = Geometry.polygonIntersection(outerWay.area, innerWay.area);
     353                    cache.setResult(outerWay, innerWay, intersection);
     354                }
    287355
    288356                if (intersection == PolygonIntersection.FIRST_INSIDE_SECOND) {
    289357                    outerGood = false;  // outer is inside another polygon
     
    305373     * @param boundaryWays boundary ways
    306374     * @return the outermostWay, or {@code null} if intersection found.
    307375     */
    308     private static List<PolygonLevel> findOuterWaysMultiThread(List<JoinedPolygon> boundaryWays) {
    309         return THREAD_POOL.invoke(new Worker(boundaryWays, 0, boundaryWays.size(), new ArrayList<PolygonLevel>(),
     376    private List<PolygonLevel> findOuterWaysMultiThread(List<JoinedPolygon> boundaryWays) {
     377        IntersectionMatrix im = new IntersectionMatrix(boundaryWays);
     378        List<PolygonLevel> res = THREAD_POOL.invoke(new Worker(im, boundaryWays, 0, boundaryWays.size(), new ArrayList<PolygonLevel>(),
    310379                Math.max(32, boundaryWays.size() / THREAD_POOL.getParallelism() / 3)));
     380//        if (!boundaryWays.isEmpty())
     381//            Main.debug("mp cache: " + im.toString());
     382        return res;
    311383    }
    312384
    313385    private static class Worker extends RecursiveTask<List<PolygonLevel>> {
     
    321393        private final transient List<PolygonLevel> output;
    322394        private final int directExecutionTaskSize;
    323395
    324         Worker(List<JoinedPolygon> input, int from, int to, List<PolygonLevel> output, int directExecutionTaskSize) {
     396        private IntersectionMatrix cache;
     397
     398        Worker(IntersectionMatrix cache, List<JoinedPolygon> input, int from, int to, List<PolygonLevel> output, int directExecutionTaskSize) {
     399            this.cache = cache;
    325400            this.input = input;
    326401            this.from = from;
    327402            this.to = to;
     
    332407        /**
    333408         * Collects outer way and corresponding inner ways from all boundaries.
    334409         * @param level nesting level
     410         * @param cache cache that tracks previously calculated results
    335411         * @param boundaryWays boundary ways
    336412         * @return the outermostWay, or {@code null} if intersection found.
    337413         */
    338         private static List<PolygonLevel> findOuterWaysRecursive(int level, List<JoinedPolygon> boundaryWays) {
     414        private static List<PolygonLevel> findOuterWaysRecursive(int level, IntersectionMatrix cache, List<JoinedPolygon> boundaryWays) {
    339415
    340416            final List<PolygonLevel> result = new ArrayList<>();
    341417
    342418            for (JoinedPolygon outerWay : boundaryWays) {
    343                 if (processOuterWay(level, boundaryWays, result, outerWay) == null) {
     419                if (processOuterWay(level, cache, boundaryWays, result, outerWay) == null) {
    344420                    return null;
    345421                }
    346422            }
     
    348424            return result;
    349425        }
    350426
    351         private static List<PolygonLevel> processOuterWay(int level, List<JoinedPolygon> boundaryWays,
     427        private static List<PolygonLevel> processOuterWay(int level, IntersectionMatrix cache, List<JoinedPolygon> boundaryWays,
    352428                final List<PolygonLevel> result, JoinedPolygon outerWay) {
    353             Pair<Boolean, List<JoinedPolygon>> p = findInnerWaysCandidates(outerWay, boundaryWays);
     429            Pair<Boolean, List<JoinedPolygon>> p = findInnerWaysCandidates(cache, outerWay, boundaryWays);
    354430            if (p == null) {
    355431                // ways intersect
    356432                return null;
     
    362438
    363439                //process inner ways
    364440                if (!p.b.isEmpty()) {
    365                     List<PolygonLevel> innerList = findOuterWaysRecursive(level + 1, p.b);
     441                    List<PolygonLevel> innerList = findOuterWaysRecursive(level + 1, cache, p.b);
    366442                    if (innerList == null) {
    367443                        return null; //intersection found
    368444                    }
     
    388464            } else {
    389465                final Collection<ForkJoinTask<List<PolygonLevel>>> tasks = new ArrayList<>();
    390466                for (int fromIndex = from; fromIndex < to; fromIndex += directExecutionTaskSize) {
    391                     tasks.add(new Worker(input, fromIndex, Math.min(fromIndex + directExecutionTaskSize, to),
     467                    tasks.add(new Worker(cache, input, fromIndex, Math.min(fromIndex + directExecutionTaskSize, to),
    392468                            new ArrayList<PolygonLevel>(), directExecutionTaskSize));
    393469                }
    394470                for (ForkJoinTask<List<PolygonLevel>> task : ForkJoinTask.invokeAll(tasks)) {
     
    404480
    405481        List<PolygonLevel> computeDirectly() {
    406482            for (int i = from; i < to; i++) {
    407                 if (processOuterWay(0, input, output, input.get(i)) == null) {
     483                if (processOuterWay(0, cache, input, output, input.get(i)) == null) {
    408484                    return null;
    409485                }
    410486            }