Ignore:
Timestamp:
2010-08-18T17:29:56+02:00 (14 years ago)
Author:
bastiK
Message:

see #5179 (patch by viesturs) - Join overlapping areas produces bad results

Location:
trunk/src/org/openstreetmap/josm/actions
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/actions/CombineWayAction.java

    r3409 r3445  
    1515import java.util.HashMap;
    1616import java.util.HashSet;
     17import java.util.LinkedHashMap;
     18import java.util.LinkedHashSet;
    1719import java.util.LinkedList;
    1820import java.util.List;
     
    113115            return null;
    114116        ways.remove(null); // just in case -  remove all null ways from the collection
    115         ways = new HashSet<Way>(ways); // remove duplicates
     117
     118        // remove duplicates, preserving order
     119        ways = new LinkedHashSet<Way>(ways);
    116120
    117121        // try to build a new way which includes all the combined
     
    474478
    475479        protected void prepare() {
    476             Set<NodePair> undirectedEdges = new HashSet<NodePair>();
    477             successors = new HashMap<Node, List<NodePair>>();
    478             predecessors = new HashMap<Node, List<NodePair>>();
     480            Set<NodePair> undirectedEdges = new LinkedHashSet<NodePair>();
     481            successors = new LinkedHashMap<Node, List<NodePair>>();
     482            predecessors = new LinkedHashMap<Node, List<NodePair>>();
    479483
    480484            for (NodePair pair: edges) {
     
    489493
    490494        public NodeGraph() {
    491             edges = new HashSet<NodePair>();
     495            edges = new LinkedHashSet<NodePair>();
    492496        }
    493497
     
    514518
    515519        protected Set<Node> getTerminalNodes() {
    516             Set<Node> ret = new HashSet<Node>();
     520            Set<Node> ret = new LinkedHashSet<Node>();
    517521            for (Node n: getNodes()) {
    518522                if (isTerminalNode(n)) {
     
    524528
    525529        protected Set<Node> getNodes(Stack<NodePair> pairs) {
    526             HashSet<Node> nodes = new HashSet<Node>();
     530            HashSet<Node> nodes = new LinkedHashSet<Node>();
    527531            for (NodePair pair: pairs) {
    528532                nodes.add(pair.getA());
     
    544548
    545549        protected Set<Node> getNodes() {
    546             Set<Node> nodes = new HashSet<Node>();
     550            Set<Node> nodes = new LinkedHashSet<Node>();
    547551            for (NodePair pair: edges) {
    548552                nodes.add(pair.getA());
  • trunk/src/org/openstreetmap/josm/actions/JoinAreasAction.java

    r3383 r3445  
    77
    88import java.awt.GridBagLayout;
    9 import java.awt.Polygon;
    109import java.awt.event.ActionEvent;
    1110import java.awt.event.KeyEvent;
     
    1615import java.util.Collections;
    1716import java.util.HashMap;
     17import java.util.HashSet;
    1818import java.util.LinkedList;
    1919import java.util.List;
     
    3131
    3232import org.openstreetmap.josm.Main;
     33import org.openstreetmap.josm.actions.SplitWayAction.SplitWayResult;
    3334import org.openstreetmap.josm.command.AddCommand;
    3435import org.openstreetmap.josm.command.ChangeCommand;
     
    5556    private int cmdsCount = 0;
    5657
     58    /**
     59     * This helper class describes join ares action result.
     60     * @author viesturs
     61     *
     62     */
     63    public static class JoinAreasResult {
     64
     65        public Way outerWay;
     66        public List<Way> innerWays;
     67
     68        public boolean mergeSuccessful;
     69        public boolean hasChanges;
     70        public boolean hasRelationProblems;
     71    }
     72
    5773    // HelperClass
    5874    // Saves a node and two positions where to insert the node into the ways
     
    107123            RelationRole otherMember = (RelationRole) other;
    108124            return otherMember.role.equals(role) && otherMember.rel.equals(rel);
     125        }
     126    }
     127
     128    /**
     129     * HelperClass
     130     * saves a way and the "inside" side
     131     * insideToTheLeft: if true left side is "in", false -right side is "in".
     132     * Left and right are determined along the orientation of way.
     133     */
     134    private static class WayInPath {
     135        public final Way way;
     136        public boolean insideToTheLeft;
     137
     138        public WayInPath(Way way, boolean insideLeft) {
     139            this.way = way;
     140            this.insideToTheLeft = insideLeft;
     141        }
     142
     143        @Override
     144        public int hashCode() {
     145            return way.hashCode();
     146        }
     147
     148        @Override
     149        public boolean equals(Object other) {
     150            if (!(other instanceof WayInPath))
     151                return false;
     152            WayInPath otherMember = (WayInPath) other;
     153            return otherMember.way.equals(this.way) && otherMember.insideToTheLeft == this.insideToTheLeft;
    109154        }
    110155    }
     
    164209        }
    165210
    166         if(joinAreas(ways.getFirst(), ways.getLast())) {
     211        if (checkForTagConflicts(ways.getFirst(), ways.getLast())) {
     212            //do nothing. //FIXME: abort?
     213        }
     214
     215        JoinAreasResult result = joinAreas(ways.getFirst(), ways.getLast());
     216
     217        if (result.hasChanges) {
    167218            Main.map.mapView.repaint();
    168219            DataSet ds = Main.main.getCurrentDataSet();
     
    177228     * @param Way First way/area
    178229     * @param Way Second way/area
    179      * @return boolean Whether to display the "no operation" message
    180      */
    181     private boolean joinAreas(Way a, Way b) {
     230     */
     231    private JoinAreasResult joinAreas(Way a, Way b) {
     232
     233        JoinAreasResult result = new JoinAreasResult();
     234        result.hasChanges = false;
     235
    182236        // Fix self-overlapping first or other errors
    183237        boolean same = a.equals(b);
    184         boolean hadChanges = false;
    185238        if(!same) {
    186239            int i = 0;
    187             if(checkForTagConflicts(a, b)) return true; // User aborted, so don't warn again
    188             if(joinAreas(a, a)) {
     240
     241            //join each area with itself, fixing self-crossings.
     242            JoinAreasResult resultA = joinAreas(a, a);
     243            JoinAreasResult resultB = joinAreas(b, b);
     244
     245            if (resultA.mergeSuccessful) {
     246                a = resultA.outerWay;
    189247                ++i;
    190248            }
    191             if(joinAreas(b, b)) {
     249            if(resultB.mergeSuccessful) {
     250                b = resultB.outerWay;
    192251                ++i;
    193252            }
    194             hadChanges = i > 0;
     253
     254            result.hasChanges = i > 0;
    195255            cmdsCount = i;
    196256        }
    197257
    198         ArrayList<OsmPrimitive> nodes = addIntersections(a, b);
    199         if(nodes.size() == 0) return hadChanges;
     258        ArrayList<Node> nodes = addIntersections(a, b);
     259
     260        //no intersections, return.
     261        if(nodes.size() == 0) return result;
    200262        commitCommands(marktr("Added node on all intersections"));
    201263
     
    209271        boolean warnAboutRelations = relations.size() > 0;
    210272
    211         Collection<Way> allWays = splitWaysOnNodes(a, b, nodes);
    212 
    213         // Find all nodes and inner ways save them to a list
    214         Collection<Node> allNodes = getNodesFromWays(allWays);
    215         Collection<Way> innerWays = findInnerWays(allWays, allNodes);
     273        ArrayList<Way> allWays = splitWaysOnNodes(a, b, nodes);
     274
     275        // Find inner ways save them to a list
     276        ArrayList<WayInPath> outerWays = findOuterWays(allWays);
     277        ArrayList<Way> innerWays = findInnerWays(allWays, outerWays);
    216278
    217279        // Join outer ways
    218         Way outerWay = joinOuterWays(allWays, innerWays);
    219         if (outerWay == null)
    220             return true;
     280        Way outerWay = joinOuterWays(outerWays);
    221281
    222282        // Fix Multipolygons if there are any
    223         Collection<Way> newInnerWays = fixMultipolygons(innerWays, outerWay, same);
     283        List<Way> newInnerWays = fixMultipolygons(innerWays, outerWay, same);
    224284
    225285        // Delete the remaining inner ways
     
    235295
    236296        stripTags(newInnerWays);
     297
    237298        makeCommitsOneAction(
    238299                same
     
    245306        }
    246307
    247         return true;
     308        result.mergeSuccessful = true;
     309        result.outerWay = outerWay;
     310        result.innerWays = newInnerWays;
     311
     312        return result;
    248313    }
    249314
     
    331396     * @return ArrayList<OsmPrimitive> List of new nodes
    332397     */
    333     private ArrayList<OsmPrimitive> addIntersections(Way a, Way b) {
     398    private ArrayList<Node> addIntersections(Way a, Way b) {
    334399        boolean same = a.equals(b);
    335400        int nodesSizeA = a.getNodesCount();
    336401        int nodesSizeB = b.getNodesCount();
    337402
    338         // We use OsmPrimitive here instead of Node because we later need to split a way at these nodes.
    339         // With OsmPrimitve we can simply add the way and don't have to loop over the nodes
    340         ArrayList<OsmPrimitive> nodes = new ArrayList<OsmPrimitive>();
     403        ArrayList<Node> nodes = new ArrayList<Node>();
    341404        ArrayList<NodeToSegs> nodesA = new ArrayList<NodeToSegs>();
    342405        ArrayList<NodeToSegs> nodesB = new ArrayList<NodeToSegs>();
     
    489552
    490553    /**
    491      * This is a hacky implementation to make use of the splitWayAction code and
    492      * should be improved. SplitWayAction needs to expose its splitWay function though.
    493      */
    494     private Collection<Way> splitWaysOnNodes(Way a, Way b, Collection<OsmPrimitive> nodes) {
    495         ArrayList<Way> ways = new ArrayList<Way>();
     554     * This method splits ways into smaller parts, using the prepared nodes list as split points.
     555     * Uses SplitWayAction.splitWay for the heavy lifting.
     556     * @return list of split ways (or original ways if no splitting is done).
     557     */
     558    private ArrayList<Way> splitWaysOnNodes(Way a, Way b, Collection<Node> nodes) {
     559
     560        ArrayList<Way> result = new ArrayList<Way>();
     561        List<Way> ways = new ArrayList<Way>();
    496562        ways.add(a);
    497         if(!a.equals(b)) {
    498             ways.add(b);
    499         }
    500 
    501         List<OsmPrimitive> affected = new ArrayList<OsmPrimitive>();
    502         for (Way way : ways) {
    503             nodes.add(way);
    504             Main.main.getCurrentDataSet().setSelected(nodes);
    505             nodes.remove(way);
    506             new SplitWayAction().actionPerformed(null);
    507             cmdsCount++;
    508             affected.addAll(Main.main.getCurrentDataSet().getSelectedWays());
    509         }
    510         return osmprim2way(affected);
    511     }
    512 
    513     /**
    514      * Converts a list of OsmPrimitives to a list of Ways
    515      * @param Collection<OsmPrimitive> The OsmPrimitives list that's needed as a list of Ways
    516      * @return Collection<Way> The list as list of Ways
    517      */
    518     static private Collection<Way> osmprim2way(Collection<OsmPrimitive> ways) {
    519         Collection<Way> result = new ArrayList<Way>();
    520         for(OsmPrimitive w: ways) {
    521             if(w instanceof Way) {
    522                 result.add((Way) w);
    523             }
    524         }
     563        ways.add(b);
     564
     565        for (Way way: ways) {
     566            List<List<Node>> chunks = buildNodeChunks(way, nodes);
     567            SplitWayResult split = SplitWayAction.splitWay(Main.map.mapView.getEditLayer(), way, chunks, Collections.<OsmPrimitive>emptyList());
     568
     569            //execute the command, we need the results
     570            Main.main.undoRedo.add(split.getCommand());
     571            cmdsCount ++;
     572
     573            result.add(split.getOriginalWay());
     574            result.addAll(split.getNewWays());
     575        }
     576
     577        return result;
     578    }
     579
     580    /**
     581     * Simple chunking version. Does not care about circular ways and result being proper, we will glue it all back together later on.
     582     * @param way the way to chunk
     583     * @param splitNodes the places where to cut.
     584     * @return list of node segments to produce.
     585     */
     586    private List<List<Node>> buildNodeChunks(Way way, Collection<Node> splitNodes)
     587    {
     588        List<List<Node>> result = new ArrayList<List<Node>>();
     589        List<Node> curList = new ArrayList<Node>();
     590
     591        for(Node node: way.getNodes()){
     592            curList.add(node);
     593            if (curList.size() > 1 && splitNodes.contains(node)){
     594                result.add(curList);
     595                curList = new ArrayList<Node>();
     596                curList.add(node);
     597            }
     598        }
     599
     600        if (curList.size() > 1)
     601        {
     602            result.add(curList);
     603        }
     604
    525605        return result;
    526606    }
     
    540620
    541621    /**
    542      * Finds all inner ways for a given list of Ways and Nodes from a multigon by constructing a polygon
    543      * for each way, looking for inner nodes that are not part of this way. If a node is found, all ways
    544      * containing this node are added to the list
     622     * Gets all inner ways given all ways and outer ways.
     623     * @param multigonWays
     624     * @param outerWays
     625     * @return list of inner ways.
     626     */
     627    private ArrayList<Way> findInnerWays(Collection<Way> multigonWays, Collection<WayInPath> outerWays) {
     628        ArrayList<Way> innerWays = new ArrayList<Way>();
     629        Set<Way> outerSet = new HashSet<Way>();
     630
     631        for(WayInPath w: outerWays) {
     632            outerSet.add(w.way);
     633        }
     634
     635        for(Way way: multigonWays) {
     636            if (!outerSet.contains(way)) {
     637                innerWays.add(way);
     638            }
     639        }
     640
     641        return innerWays;
     642    }
     643
     644
     645    /**
     646     * Finds all ways for a given list of Ways that form the outer hull.
     647     * This works by starting with one node and traversing the multigon clockwise, always picking the leftmost path.
     648     * Prerequisites - the ways must not intersect and have common end nodes where they meet.
    545649     * @param Collection<Way> A list of (splitted) ways that form a multigon
    546      * @param Collection<Node> A list of nodes that belong to the multigon
    547      * @return Collection<Way> A list of ways that are positioned inside the outer borders of the multigon
    548      */
    549     private Collection<Way> findInnerWays(Collection<Way> multigonWays, Collection<Node> multigonNodes) {
    550         Collection<Way> innerWays = new ArrayList<Way>();
    551         for(Way w: multigonWays) {
    552             Polygon poly = new Polygon();
    553             for(Node n: (w).getNodes()) {
    554                 poly.addPoint(latlonToXY(n.getCoor().lat()), latlonToXY(n.getCoor().lon()));
    555             }
    556 
    557             for(Node n: multigonNodes) {
    558                 if(!(w).containsNode(n) && poly.contains(latlonToXY(n.getCoor().lat()), latlonToXY(n.getCoor().lon()))) {
    559                     getWaysByNode(innerWays, multigonWays, n);
    560                 }
    561             }
    562         }
    563 
    564         return innerWays;
    565     }
    566 
    567     // Polygon only supports int coordinates, so convert them
    568     private int latlonToXY(double val) {
    569         return (int)Math.round(val*1000000);
    570     }
    571 
    572     /**
    573      * Finds all ways that contain the given node.
    574      * @param Collection<Way> A list to which matching ways will be added
    575      * @param Collection<Way> A list of ways to check
    576      * @param Node The node the ways should be checked against
    577      */
    578     private void getWaysByNode(Collection<Way> innerWays, Collection<Way> w, Node n) {
    579         for(Way way : w) {
    580             if(!(way).containsNode(n)) {
     650     * @return Collection<Way> A list of ways that form the outer boundary of the multigon.
     651     */
     652    private static ArrayList<WayInPath> findOuterWays(Collection<Way> multigonWays) {
     653
     654        //find the node with minimum lat - it's guaranteed to be outer. (What about the south pole?)
     655        Way bestWay = null;
     656        Node topNode = null;
     657        int topIndex = 0;
     658        double minLat = Double.POSITIVE_INFINITY;
     659
     660        for(Way way: multigonWays) {
     661            for (int pos = 0; pos < way.getNodesCount(); pos ++) {
     662                Node node = way.getNode(pos);
     663
     664                if (node.getCoor().lat() < minLat) {
     665                    minLat = node.getCoor().lat();
     666                    bestWay = way;
     667                    topNode = node;
     668                    topIndex = pos;
     669                }
     670            }
     671        }
     672
     673        //get two final nodes from best way to mark as starting point and orientation.
     674        Node headNode = null;
     675        Node prevNode = null;
     676
     677        if (topNode.equals(bestWay.firstNode()) || topNode.equals(bestWay.lastNode())) {
     678            //node is in split point
     679            headNode = topNode;
     680            //make a fake node that is downwards from head node (smaller latitude). It will be a division point between paths.
     681            prevNode = new Node(new LatLon(headNode.getCoor().lat() - 1000, headNode.getCoor().lon()));
     682        } else {
     683            //node is inside way - pick the clockwise going end.
     684            Node prev = bestWay.getNode(topIndex - 1);
     685            Node next = bestWay.getNode(topIndex + 1);
     686
     687            if (angleIsClockwise(prev, topNode, next)) {
     688                headNode = bestWay.lastNode();
     689                prevNode = bestWay.getNode(bestWay.getNodesCount() - 2);
     690            }
     691            else {
     692                headNode = bestWay.firstNode();
     693                prevNode = bestWay.getNode(1);
     694            }
     695        }
     696
     697        Set<Way> outerWays = new HashSet<Way>();
     698        ArrayList<WayInPath> result = new ArrayList<WayInPath>();
     699
     700        //iterate till full circle is reached
     701        while (true) {
     702
     703            bestWay = null;
     704            Node bestWayNextNode = null;
     705            boolean bestWayReverse = false;
     706
     707            for (Way way: multigonWays) {
     708                boolean wayReverse;
     709                Node nextNode;
     710
     711                if (way.firstNode().equals(headNode)) {
     712                    //start adjacent to headNode
     713                    nextNode = way.getNode(1);
     714                    wayReverse = false;
     715
     716                    if (nextNode.equals(prevNode)) {
     717                        //this is the path we came from - ignore it.
     718                    } else if (bestWay == null || !isToTheRightSideOfLine(prevNode, headNode, bestWayNextNode, nextNode)) {
     719                        //the new way is better
     720                        bestWay = way;
     721                        bestWayReverse = wayReverse;
     722                        bestWayNextNode = nextNode;
     723                    }
     724                }
     725
     726                if (way.lastNode().equals(headNode)) {
     727                    //end adjacent to headNode
     728                    nextNode = way.getNode(way.getNodesCount() - 2);
     729                    wayReverse = true;
     730
     731                    if (nextNode.equals(prevNode)) {
     732                        //this is the path we came from - ignore it.
     733                    } else if (bestWay == null || !isToTheRightSideOfLine(prevNode, headNode, bestWayNextNode, nextNode)) {
     734                        //the new way is better
     735                        bestWay = way;
     736                        bestWayReverse = wayReverse;
     737                        bestWayNextNode = nextNode;
     738                    }
     739                }
     740            }
     741
     742            if (bestWay == null)
     743                throw new RuntimeException();
     744            else if (outerWays.contains(bestWay))
     745                break; //full circle reached, terminate.
     746            else {
     747                //add to outer ways, repeat.
     748                outerWays.add(bestWay);
     749                result.add(new WayInPath(bestWay, bestWayReverse));
     750                headNode = bestWayReverse ? bestWay.firstNode() : bestWay.lastNode();
     751                prevNode = bestWayReverse ? bestWay.getNode(1) : bestWay.getNode(bestWay.getNodesCount() - 2);
     752            }
     753        }
     754
     755        return result;
     756    }
     757
     758    /**
     759     * Tests if given point is to the right side of path consisting of 3 points.
     760     * @param lineP1 first point in path
     761     * @param lineP2 second point in path
     762     * @param lineP3 third point in path
     763     * @param testPoint
     764     * @return true if to the right side, false otherwise
     765     */
     766    public static boolean isToTheRightSideOfLine(Node lineP1, Node lineP2, Node lineP3, Node testPoint)
     767    {
     768        boolean pathBendToRight = angleIsClockwise(lineP1, lineP2, lineP3);
     769        boolean rightOfSeg1 = angleIsClockwise(lineP1, lineP2, testPoint);
     770        boolean rightOfSeg2 = angleIsClockwise(lineP2, lineP3, testPoint);
     771
     772        if (pathBendToRight)
     773            return rightOfSeg1 && rightOfSeg2;
     774        else
     775            return !(!rightOfSeg1 && !rightOfSeg2);
     776    }
     777
     778    /**
     779     * This method tests if secondNode is clockwise to first node.
     780     * @param commonNode starting point for both vectors
     781     * @param firstNode first vector end node
     782     * @param secondNode second vector end node
     783     * @return true if first vector is clockwise before second vector.
     784     */
     785    public static boolean angleIsClockwise(Node commonNode, Node firstNode, Node secondNode)
     786    {
     787        double dla1 = (firstNode.getCoor().lat() - commonNode.getCoor().lat());
     788        double dla2 = (secondNode.getCoor().lat() - commonNode.getCoor().lat());
     789        double dlo1 = (firstNode.getCoor().lon() - commonNode.getCoor().lon());
     790        double dlo2 = (secondNode.getCoor().lon() - commonNode.getCoor().lon());
     791
     792        return dla1 * dlo2 - dlo1 * dla2 > 0;
     793    }
     794
     795    /**
     796     * Tests if point is inside a polygon. The polygon can be self-intersecting. In such case the contains function works in xor-like manner.
     797     * @param polygonNodes list of nodes from polygon path.
     798     * @param point the point to test
     799     * @return true if the point is inside polygon.
     800     * FIXME: this should probably be moved to tools..
     801     */
     802    public static boolean nodeInsidePolygon(ArrayList<Node> polygonNodes, Node point)
     803    {
     804        if (polygonNodes.size() < 3)
     805            return false;
     806
     807        boolean inside = false;
     808        Node p1, p2;
     809
     810        //iterate each side of the polygon, start with the last segment
     811        Node oldPoint = polygonNodes.get(polygonNodes.size() - 1);
     812
     813        for(Node newPoint: polygonNodes)
     814        {
     815            //skip duplicate points
     816            if (newPoint.equals(oldPoint)) {
    581817                continue;
    582818            }
    583             if(!innerWays.contains(way)) {
    584                 innerWays.add(way); // Will need this later for multigons
    585             }
    586         }
    587     }
    588 
    589     /**
    590      * Joins the two outer ways and deletes all short ways that can't be part of a multipolygon anyway
    591      * @param Collection<OsmPrimitive> The list of all ways that belong to that multigon
    592      * @param Collection<Way> The list of inner ways that belong to that multigon
     819
     820            //order points so p1.lat <= p2.lat;
     821            if (newPoint.getCoor().lat() > oldPoint.getCoor().lat())
     822            {
     823                p1 = oldPoint;
     824                p2 = newPoint;
     825            }
     826            else
     827            {
     828                p1 = newPoint;
     829                p2 = oldPoint;
     830            }
     831
     832            //test if the line is crossed and if so invert the inside flag.
     833            if ((newPoint.getCoor().lat() < point.getCoor().lat()) == (point.getCoor().lat() <= oldPoint.getCoor().lat())
     834                    && (point.getCoor().lon() - p1.getCoor().lon()) * (p2.getCoor().lat() - p1.getCoor().lat())
     835                    < (p2.getCoor().lon() - p1.getCoor().lon()) * (point.getCoor().lat() - p1.getCoor().lat()))
     836            {
     837                inside = !inside;
     838            }
     839
     840            oldPoint = newPoint;
     841        }
     842
     843        return inside;
     844    }
     845
     846    /**
     847     * Joins the outer ways and deletes all short ways that can't be part of a multipolygon anyway.
     848     * @param Collection<Way> The list of outer ways that belong to that multigon.
    593849     * @return Way The newly created outer way
    594850     */
    595     private Way joinOuterWays(Collection<Way> multigonWays, Collection<Way> innerWays) {
    596         ArrayList<Way> join = new ArrayList<Way>();
    597         for(Way w: multigonWays) {
    598             // Skip inner ways
    599             if(innerWays.contains(w)) {
    600                 continue;
    601             }
    602 
    603             if(w.getNodesCount() <= 2) {
    604                 cmds.add(new DeleteCommand(w));
    605             } else {
    606                 join.add(w);
     851    private Way joinOuterWays(ArrayList<WayInPath> outerWays) {
     852
     853        //leave original orientation, if all paths are reverse.
     854        boolean allReverse = true;
     855        for(WayInPath way: outerWays) {
     856            allReverse &= way.insideToTheLeft;
     857        }
     858
     859        if (allReverse) {
     860            for(WayInPath way: outerWays){
     861                way.insideToTheLeft = !way.insideToTheLeft;
    607862            }
    608863        }
    609864
    610865        commitCommands(marktr("Join Areas: Remove Short Ways"));
    611         Way joinedWay = joinWays(join);
     866        Way joinedWay = joinOrientedWays(outerWays);
    612867        if (joinedWay != null)
    613868            return closeWay(joinedWay);
     
    633888
    634889    /**
     890     * Joins a list of ways (using CombineWayAction and ReverseWayAction as specified in WayInPath)
     891     * @param ArrayList<Way> The list of ways to join and reverse
     892     * @return Way The newly created way
     893     */
     894    private Way joinOrientedWays(ArrayList<WayInPath> ways) {
     895        if(ways.size() < 2)
     896            return ways.get(0).way;
     897
     898        // This will turn ways so all of them point in the same direction and CombineAction won't bug
     899        // the user about this.
     900
     901        List<Way> actionWays = new ArrayList<Way>(ways.size());
     902
     903        for(WayInPath way : ways) {
     904            actionWays.add(way.way);
     905
     906            if (way.insideToTheLeft) {
     907                Main.main.getCurrentDataSet().setSelected(way.way);
     908                new ReverseWayAction().actionPerformed(null);
     909                cmdsCount++;
     910            }
     911        }
     912
     913        Way result = new CombineWayAction().combineWays(actionWays);
     914
     915        if(result != null) {
     916            cmdsCount++;
     917        }
     918        return result;
     919    }
     920
     921    /**
    635922     * Joins a list of ways (using CombineWayAction and ReverseWayAction if necessary to quiet the former)
    636923     * @param ArrayList<Way> The list of ways to join
     
    638925     */
    639926    private Way joinWays(ArrayList<Way> ways) {
    640         if(ways.size() < 2) return ways.get(0);
     927        if(ways.size() < 2)
     928            return ways.get(0);
    641929
    642930        // This will turn ways so all of them point in the same direction and CombineAction won't bug
    643931        // the user about this.
    644932        Way a = null;
    645         for(Way b : ways) {
     933        for (Way b : ways) {
    646934            if(a == null) {
    647935                a = b;
     
    656944            a = b;
    657945        }
    658         if((a = new CombineWayAction().combineWays(ways)) != null) {
     946        if ((a = new CombineWayAction().combineWays(ways)) != null) {
    659947            cmdsCount++;
    660948        }
Note: See TracChangeset for help on using the changeset viewer.