Ticket #14528: josm-joinAreasAction-minimal-set-of-boundaries.patch
File josm-joinAreasAction-minimal-set-of-boundaries.patch, 7.7 KB (added by , 6 years ago) |
---|
-
src/org/openstreetmap/josm/actions/JoinAreasAction.java
7 7 8 8 import java.awt.event.ActionEvent; 9 9 import java.awt.event.KeyEvent; 10 import java.util.AbstractMap.SimpleEntry; 10 11 import java.util.ArrayList; 12 import java.util.Arrays; 11 13 import java.util.Collection; 12 14 import java.util.Collections; 13 15 import java.util.HashMap; … … 102 104 public static class Multipolygon { 103 105 private final Way outerWay; 104 106 private final List<Way> innerWays; 107 private transient List<WayInPolygon> splitWayCache; 105 108 106 109 /** 107 110 * Constructs a new {@code Multipolygon}. … … 552 555 allWays.addAll(pol.innerWays); 553 556 } 554 557 if (ds != null) { 558 555 559 ds.setSelected(allWays); 556 560 } 557 561 } else { … … 601 605 // Dataset retrieving allows to call this code without relying on Main.getCurrentDataSet(), thus, on a mapview instance 602 606 if (!areas.isEmpty()) { 603 607 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(); 604 615 } 605 616 606 617 boolean hasChanges = false; … … 644 655 // Don't warn now, because it will really look corrupted 645 656 boolean warnAboutRelations = !relations.isEmpty() && allStartingWays.size() > 1; 646 657 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)); 648 687 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); 653 699 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(); 658 708 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(); 662 711 663 712 //find polygons 664 713 List<AssembledMultipolygon> preparedPolygons = findPolygons(boundaries); … … 1150 1199 */ 1151 1200 public static List<AssembledPolygon> findBoundaryPolygons(Collection<WayInPolygon> multigonWays, 1152 1201 List<Way> discardedResult) { 1202 List<AssembledPolygon> result = new ArrayList<>(); 1203 1153 1204 // In multigonWays collection, some way are just a point (i.e. way like nodeA-nodeA) 1154 1205 // 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.1156 1206 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 }); 1161 1221 1162 1222 WayTraverser traverser = new WayTraverser(cleanMultigonWays); 1163 List<AssembledPolygon> result = new ArrayList<>();1164 1165 1223 WayInPolygon startWay; 1166 1224 while ((startWay = traverser.startNewWay()) != null) { 1167 1225 List<WayInPolygon> path = new ArrayList<>(); … … 1202 1260 // Inner loop -> remove 1203 1261 int index = path.indexOf(nextWay); 1204 1262 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++; 1209 1269 } 1210 1270 traverser.setStartWay(path.get(index-1)); 1211 1271 } else {