Ticket #14528: josm-joinAreasAction-minimal-set-of-boundaries.patch

File josm-joinAreasAction-minimal-set-of-boundaries.patch, 7.7 KB (added by cmuelle8, 4 years ago)

intermediary patch for the problem observed by klumbumbus, though the problem solving follows a very general approach, so might fix some other cases as well; may serve as a better bridge until the new style joinAreas code from Michael is ready and might also give some more time to thoroughly test the new code; fixes ticket:10511 test-cases

  • src/org/openstreetmap/josm/actions/JoinAreasAction.java

     
    77
    88import java.awt.event.ActionEvent;
    99import java.awt.event.KeyEvent;
     10import java.util.AbstractMap.SimpleEntry;
    1011import java.util.ArrayList;
     12import java.util.Arrays;
    1113import java.util.Collection;
    1214import java.util.Collections;
    1315import java.util.HashMap;
     
    102104    public static class Multipolygon {
    103105        private final Way outerWay;
    104106        private final List<Way> innerWays;
     107        private transient List<WayInPolygon> splitWayCache;
    105108
    106109        /**
    107110         * Constructs a new {@code Multipolygon}.
     
    552555                    allWays.addAll(pol.innerWays);
    553556                }
    554557                if (ds != null) {
     558
    555559                    ds.setSelected(allWays);
    556560                }
    557561            } else {
     
    601605        // Dataset retrieving allows to call this code without relying on Main.getCurrentDataSet(), thus, on a mapview instance
    602606        if (!areas.isEmpty()) {
    603607            ds = areas.get(0).getOuterWay().getDataSet();
     608
     609            /* there is an EDT race without clearSelection(), if the join operation
     610             * was started with more than one selected row (interval selection) in
     611             * an open SelectionListDialog (triggers in old revision of JOSM as well,
     612             * unrelated to further changes made below)
     613             * .. needs to happen before cmds are commited to the redoundo stack */
     614            ds.clearSelection();
    604615        }
    605616
    606617        boolean hasChanges = false;
     
    644655        // Don't warn now, because it will really look corrupted
    645656        boolean warnAboutRelations = !relations.isEmpty() && allStartingWays.size() > 1;
    646657
    647         List<WayInPolygon> preparedWays = new ArrayList<>();
     658        SimpleEntry<List<AssembledPolygon>, List<Way>> entry =
     659        areas.stream().collect(ArrayList<LinkedList<Multipolygon>>::new,
     660            (r, e) -> {
     661                if (r.size() == 0) {
     662                    r.add(new LinkedList<Multipolygon>());
     663                    r.get(0).add(e);
     664                } else {
     665                    int i, j, li, lj;
     666                    LinkedList<Multipolygon> tmp;
     667                    for (j=0, lj=r.size(); j<lj; j++) {
     668                        for (i=0, li=r.get(j).size(); i<li; i++) {
     669                            tmp = new LinkedList<>();
     670                            tmp.addAll(r.get(j));
     671                            tmp.add(i, e);
     672                            r.add(tmp);
     673                        }
     674                        r.get(j).add(i, e);
     675                    }
     676                }
     677            },
     678            (r1, r2) -> r1.addAll(r2))
     679        .stream().map(
     680            joinorder_variant -> {
     681                List<WayInPolygon> preparedWays = new ArrayList<>();
     682                final List<Way> discardedWays = new ArrayList<>();
     683                final List<AssembledPolygon> boundaries = new ArrayList<>();
     684                joinorder_variant.stream().forEachOrdered(
     685                    mp -> {
     686                        preparedWays.removeIf(t -> discardedWays.contains(t.way));
    648687
    649         for (Way way : outerStartingWays) {
    650             List<Way> splitWays = splitWayOnNodes(way, nodes);
    651             preparedWays.addAll(markWayInsideSide(splitWays, false));
    652         }
     688                        synchronized (mp) {
     689                            // splitWayOnNodes() changes geom without updating mp, but mp may be
     690                            // accessed multiple times, so cache and use/reuse first time results
     691                            if (mp.splitWayCache == null) {
     692                                List<WayInPolygon> split = markWayInsideSide(splitWayOnNodes(mp.outerWay, nodes), false);
     693                                for (Way way : mp.innerWays)
     694                                    split.addAll(markWayInsideSide(splitWayOnNodes(way, nodes), true));
     695                                mp.splitWayCache = split;
     696                            }
     697                        }
     698                        preparedWays.addAll(mp.splitWayCache);
    653699
    654         for (Way way : innerStartingWays) {
    655             List<Way> splitWays = splitWayOnNodes(way, nodes);
    656             preparedWays.addAll(markWayInsideSide(splitWays, true));
    657         }
     700                        // Find boundary ways
     701                        boundaries.clear();
     702                        boundaries.addAll(findBoundaryPolygons(preparedWays, discardedWays));
     703                    }
     704                );
     705                return new SimpleEntry<>(boundaries, discardedWays);
     706            })
     707        .min((r1, r2) -> r1.getKey().size() - r2.getKey().size()).get();
    658708
    659         // Find boundary ways
    660         List<Way> discardedWays = new ArrayList<>();
    661         List<AssembledPolygon> boundaries = findBoundaryPolygons(preparedWays, discardedWays);
     709        List<Way> discardedWays = entry.getValue();
     710        List<AssembledPolygon> boundaries = entry.getKey();
    662711
    663712        //find polygons
    664713        List<AssembledMultipolygon> preparedPolygons = findPolygons(boundaries);
     
    11501199     */
    11511200    public static List<AssembledPolygon> findBoundaryPolygons(Collection<WayInPolygon> multigonWays,
    11521201            List<Way> discardedResult) {
     1202        List<AssembledPolygon> result = new ArrayList<>();
     1203
    11531204        // In multigonWays collection, some way are just a point (i.e. way like nodeA-nodeA)
    11541205        // This seems to appear when is apply over invalid way like #9911 test-case
    1155         // Remove all of these way to make the next work.
    11561206        List<WayInPolygon> cleanMultigonWays = new ArrayList<>();
    1157         for (WayInPolygon way: multigonWays) {
    1158             if (way.way.getNodesCount() != 2 || !way.way.isClosed())
    1159                 cleanMultigonWays.add(way);
    1160         }
     1207        multigonWays.stream().forEach(w -> {
     1208            if (w.way.isArea())
     1209                result.add(new AssembledPolygon(Arrays.asList(w)));
     1210            else if (w.way.getNodesCount()>1 && w.way.firstNode()!=w.way.lastNode() &&
     1211                // check that a duplicate or reversed duplicate of w has not already been added, fixes #10511
     1212                !cleanMultigonWays.stream().anyMatch(t -> {
     1213                    List<Node> l = w.way.getNodes();
     1214                    Collections.reverse(l);
     1215                    return t.way.getNodes().equals(w.way.getNodes()) || t.way.getNodes().equals(l);
     1216                }))
     1217                cleanMultigonWays.add(w);
     1218            else
     1219                discardedResult.add(w.way);
     1220        });
    11611221
    11621222        WayTraverser traverser = new WayTraverser(cleanMultigonWays);
    1163         List<AssembledPolygon> result = new ArrayList<>();
    1164 
    11651223        WayInPolygon startWay;
    11661224        while ((startWay = traverser.startNewWay()) != null) {
    11671225            List<WayInPolygon> path = new ArrayList<>();
     
    12021260                    // Inner loop -> remove
    12031261                    int index = path.indexOf(nextWay);
    12041262                    while (path.size() > index) {
    1205                         WayInPolygon currentWay = path.get(index);
    1206                         discardedResult.add(currentWay.way);
    1207                         traverser.removeWay(currentWay);
    1208                         path.remove(index);
     1263                        traverser.removeWay(path.get(index));
     1264                        traverser.setStartWay(path.get(index-1));
     1265                        if (traverser.walk() != null)
     1266                            discardedResult.add(path.remove(index).way);
     1267                        else
     1268                            index++;
    12091269                    }
    12101270                    traverser.setStartWay(path.get(index-1));
    12111271                } else {