Ticket #2343: Fix JoinAreas.patch

File Fix JoinAreas.patch, 7.4 KB (added by xeen, 17 years ago)
  • utilsplugin/src/UtilsPlugin/JoinAreasAction.java

     
    246246
    247247        stripTags(newInnerWays);
    248248        makeCommitsOneAction(
    249             a.equals(b)
    250                 ? "Joined self-overlapping area " + a.getName()
    251                 : "Joined overlapping areas " + a.getName() + " and " + b.getName()
     249            same
     250                ? "Joined self-overlapping area"
     251                : "Joined overlapping areas"
    252252        );
    253253
    254254        if(warnAboutRelations)
     
    541541
    542542            for(Node n: multigonNodes) {
    543543                if(!((Way)w).nodes.contains(n) && poly.contains(latlonToXY(n.coor.lat()), latlonToXY(n.coor.lon()))) {
    544                     innerWays.addAll(getWaysByNode(multigonWays, n));
     544                    getWaysByNode(innerWays, multigonWays, n);
    545545                }
    546546            }
    547547        }
     
    556556
    557557    /**
    558558     * Finds all ways that contain the given node.
    559      * @param Collection<OsmPrimitive> A collection of OsmPrimitives, but only ways will be honored
     559     * @param Collection<Way> A list to which matching ways will be added
     560     * @param Collection<Way> A list of ways to check
    560561     * @param Node The node the ways should be checked against
    561      * @return Collection<Way> A list of ways that contain the given node
    562562     */
    563     private Collection<Way> getWaysByNode(Collection<Way> w, Node n) {
    564         Collection<Way> deletedWays = new ArrayList<Way>();
     563    private void getWaysByNode(Collection<Way> innerWays, Collection<Way> w, Node n) {
    565564        for(Way way : w) {
    566565            if(!((Way)way).nodes.contains(n)) continue;
    567             if(!deletedWays.contains(way)) deletedWays.add(way); // Will need this later for multigons
     566            if(!innerWays.contains(way)) innerWays.add(way); // Will need this later for multigons
    568567        }
    569         return deletedWays;
    570568    }
    571569
    572570    /**
     
    588586        }
    589587
    590588        commitCommands("Join Areas: Remove Short Ways");
    591         return joinWays(join);
     589        return closeWay(joinWays(join));
    592590    }
    593591
    594592    /**
     593     * Ensures a way is closed. If it isn't, last and first node are connected.
     594     * @param Way the way to ensure it's closed
     595     * @return Way The joined way.
     596     */
     597    private Way closeWay(Way w) {
     598        if(w.isClosed())
     599            return w;
     600        Main.ds.setSelected(w);
     601        Way wnew = new Way(w);
     602        wnew.addNode(wnew.firstNode());
     603        cmds.add(new ChangeCommand(w, wnew));
     604        commitCommands("Closed Way");
     605        return (Way)(Main.ds.getSelectedWays().toArray())[0];
     606    }
     607
     608    /**
    595609     * Joins a list of ways (using CombineWayAction and ReverseWayAction if necessary to quiet the former)
    596610     * @param ArrayList<Way> The list of ways to join
    597611     * @return Way The newly created way
    598612     */
    599613    private Way joinWays(ArrayList<Way> ways) {
    600614        if(ways.size() < 2) return ways.get(0);
    601         //Main.ds.setSelected(ways);
    602615
    603616        // This will turn ways so all of them point in the same direction and CombineAction won't bug
    604617        // the user about this.
     
    617630            a = b;
    618631        }
    619632        Main.ds.setSelected(ways);
     633        // TODO: It might be possible that a confirmation dialog is presented even after reversing (for
     634        // "strange" ways). If the user cancels this, makeCommitsOneAction will wrongly consume a previous
     635        // action. Make CombineWayAction either silent or expose its combining capabilities.
    620636        new CombineWayAction().actionPerformed(null);
    621637        cmdsCount++;
    622638        return (Way)(Main.ds.getSelectedWays().toArray())[0];
     
    640656        // Remaining nodes are those that contain to more than one way. All nodes that belong to an
    641657        // inner multigon part will have at least two ways, so we can use this to find which ways do
    642658        // belong to the multigon.
    643         Collection<Way> possibleWays = new ArrayList<Way>();
     659        ArrayList<Way> possibleWays = new ArrayList<Way>();
    644660        wayIterator: for(Way w : uninterestingWays) {
    645661            boolean hasInnerNodes = false;
    646662            for(Node n : w.nodes) {
    647663                if(outerNodes.contains(n)) continue wayIterator;
    648664                if(!hasInnerNodes && innerNodes.contains(n)) hasInnerNodes = true;
    649665            }
    650             if(!hasInnerNodes && w.nodes.size() >= 2) continue;
     666            if(!hasInnerNodes || w.nodes.size() < 2) continue;
    651667            possibleWays.add(w);
    652668        }
    653669
     670        // This removes unnecessary ways that might have been added.
     671        removeAlmostAlikeWays(possibleWays);
     672        removePartlyUnconnectedWays(possibleWays);
     673
    654674        // Join all ways that have one start/ending node in common
    655675        Way joined = null;
    656676        outerIterator: do {
     
    687707    }
    688708
    689709    /**
     710     * Removes almost alike ways (= ways that are on top of each other for all nodes)
     711     * @param ArrayList<Way> the ways to remove almost-duplicates from
     712     */
     713    private void removeAlmostAlikeWays(ArrayList<Way> ways) {
     714        Collection<Way> removables = new ArrayList<Way>();
     715        outer: for(int i=0; i < ways.size(); i++) {
     716            Way a = ways.get(i);
     717            for(int j=i+1; j < ways.size(); j++) {
     718                Way b = ways.get(j);
     719                List<Node> revNodes = new ArrayList<Node>(b.nodes);
     720                Collections.reverse(revNodes);
     721                if(a.nodes.equals(b.nodes) || a.nodes.equals(revNodes)) {
     722                    removables.add(a);
     723                    continue outer;
     724                }
     725            }
     726        }
     727        ways.removeAll(removables);
     728    }
     729
     730    /**
     731     * Removes ways from the given list whose starting or ending node doesn't
     732     * connect to other ways from the same list (it's like removing spikes).
     733     * @param ArrayList<Way> The list of ways to remove "spikes" from
     734     */
     735    private void removePartlyUnconnectedWays(ArrayList<Way> ways) {
     736        List<Way> removables = new ArrayList<Way>();
     737        for(Way a : ways) {
     738            if(a.isClosed()) continue;
     739            boolean connectedStart = false;
     740            boolean connectedEnd = false;
     741            for(Way b : ways) {
     742                if(a.equals(b))
     743                    continue;
     744                if(b.isFirstLastNode(a.firstNode()))
     745                    connectedStart = true;
     746                if(b.isFirstLastNode(a.lastNode()))
     747                    connectedEnd = true;
     748            }
     749            if(!connectedStart || !connectedEnd)
     750                removables.add(a);
     751        }
     752        ways.removeAll(removables);
     753    }
     754
     755    /**
    690756     * Checks if a way is collapsed (i.e. looks like <---->)
    691757     * @param Way A *closed* way to check if it is collapsed
    692758     * @return boolean If the closed way is collapsed or not
     
    824890    private void makeCommitsOneAction(String message) {
    825891        UndoRedoHandler ur = Main.main.undoRedo;
    826892        cmds.clear();
    827         for(int i = ur.commands.size() - cmdsCount; i < ur.commands.size(); i++)
     893        int i = Math.max(ur.commands.size() - cmdsCount, 0);
     894        for(; i < ur.commands.size(); i++)
    828895            cmds.add(ur.commands.get(i));
    829896
    830         for(int i = 0; i < cmdsCount; i++)
     897        for(i = 0; i < cmds.size(); i++)
    831898            ur.undo();
    832899
    833900        commitCommands(message == null ? "Join Areas Function" : message);