Changeset 2976 in josm for trunk/src/org/openstreetmap/josm


Ignore:
Timestamp:
2010-02-13T23:50:41+01:00 (14 years ago)
Author:
Gubaer
Message:

fixed #4512: "Simplify way" should not deletes nodes used by other ways
Migrated message dialogs to HelpawareOptionPane and added help topics

File:
1 edited

Legend:

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

    r2842 r2976  
    22package org.openstreetmap.josm.actions;
    33
     4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
    45import static org.openstreetmap.josm.tools.I18n.tr;
    56import static org.openstreetmap.josm.tools.I18n.trn;
     
    78import java.awt.event.ActionEvent;
    89import java.awt.event.KeyEvent;
    9 import java.util.ArrayList;
    1010import java.util.Collection;
    1111import java.util.Collections;
     
    1717
    1818import org.openstreetmap.josm.Main;
    19 import org.openstreetmap.josm.actions.JosmAction;
    2019import org.openstreetmap.josm.command.ChangeCommand;
    2120import org.openstreetmap.josm.command.Command;
     
    2726import org.openstreetmap.josm.data.osm.OsmPrimitive;
    2827import org.openstreetmap.josm.data.osm.Way;
     28import org.openstreetmap.josm.gui.HelpAwareOptionPane;
     29import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec;
     30import org.openstreetmap.josm.gui.help.HelpUtil;
    2931import org.openstreetmap.josm.gui.layer.OsmDataLayer;
     32import org.openstreetmap.josm.tools.ImageProvider;
    3033import org.openstreetmap.josm.tools.Shortcut;
    3134
     
    3336    public SimplifyWayAction() {
    3437        super(tr("Simplify Way"), "simplify", tr("Delete unnecessary nodes from a way."), Shortcut.registerShortcut("tools:simplify", tr("Tool: {0}", tr("Simplify Way")),
    35         KeyEvent.VK_Y, Shortcut.GROUP_EDIT, Shortcut.SHIFT_DEFAULT), true);
    36     }
    37 
    38     public void actionPerformed(ActionEvent e) {
    39         Collection<OsmPrimitive> selection = Main.main.getCurrentDataSet().getSelected();
    40 
    41         int ways = 0;
     38                KeyEvent.VK_Y, Shortcut.GROUP_EDIT, Shortcut.SHIFT_DEFAULT), true);
     39        putValue("help", ht("/Action/SimplifyWay"));
     40    }
     41
     42    protected List<Bounds> getCurrentEditBounds() {
    4243        LinkedList<Bounds> bounds = new LinkedList<Bounds>();
    4344        OsmDataLayer dataLayer = Main.map.mapView.getEditLayer();
    4445        for (DataSource ds : dataLayer.data.dataSources) {
    45             if (ds.bounds != null)
     46            if (ds.bounds != null) {
    4647                bounds.add(ds.bounds);
    47         }
     48            }
     49        }
     50        return bounds;
     51    }
     52
     53    protected boolean isInBounds(Node node, List<Bounds> bounds) {
     54        for (Bounds b : bounds) {
     55            if (b.contains(node.getCoor()))
     56                return true;
     57        }
     58        return false;
     59    }
     60
     61    protected boolean confirmWayWithNodesOutsideBoundingBox() {
     62        ButtonSpec[] options = new ButtonSpec[] {
     63                new ButtonSpec(
     64                        tr("Yes, delete nodes"),
     65                        ImageProvider.get("ok"),
     66                        tr("Delete nodes outside of downloaded data regions"),
     67                        null
     68                ),
     69                new ButtonSpec(
     70                        tr("No, abort"),
     71                        ImageProvider.get("cancel"),
     72                        tr("Cancel operation"),
     73                        null
     74                )
     75        };
     76        int ret = HelpAwareOptionPane.showOptionDialog(
     77                Main.parent,
     78                "<html>"
     79                + trn("The selected way has nodes outside of the downloaded data region.",
     80                        "The selected ways have nodes outside of the downloaded data region.",
     81                        getCurrentDataSet().getSelectedWays().size())
     82
     83                        + "<br>"
     84                        + tr("This can lead to nodes being deleted accidentally.") + "<br>"
     85                        + tr("Do you want to delete them anyway?")
     86                        + "</html>",
     87                        tr("Delete nodes outside of data regions?"),
     88                        JOptionPane.WARNING_MESSAGE,
     89                        null, // no special icon
     90                        options,
     91                        options[0],
     92                        HelpUtil.ht("/Action/SimplifyAction#ConfirmDeleteNodesOutsideBoundingBoxes")
     93        );
     94        return ret == 0;
     95    }
     96
     97    protected void alertSelectAtLeastOneWay() {
     98        HelpAwareOptionPane.showOptionDialog(
     99                Main.parent,
     100                tr("Please select at least one way to simplify."),
     101                tr("Warning"),
     102                JOptionPane.WARNING_MESSAGE,
     103                HelpUtil.ht("/Action/SimplifyAction#SelectAWayToSimplify")
     104        );
     105    }
     106
     107    protected boolean confirmSimplifyManyWays(int numWays) {
     108        ButtonSpec[] options = new ButtonSpec[] {
     109                new ButtonSpec(
     110                        tr("Yes"),
     111                        ImageProvider.get("ok"),
     112                        tr("Simplify all selected ways"),
     113                        null
     114                ),
     115                new ButtonSpec(
     116                        tr("Cancel"),
     117                        ImageProvider.get("cancel"),
     118                        tr("Cancel operation"),
     119                        null
     120                )
     121        };
     122        int ret = HelpAwareOptionPane.showOptionDialog(
     123                Main.parent,
     124                tr(
     125                        "The selection contains {0} ways. Are you sure you want to simplify them all?",
     126                        numWays
     127                ),
     128                tr("Simplify ways?"),
     129                JOptionPane.WARNING_MESSAGE,
     130                null, // no special icon
     131                options,
     132                options[0],
     133                HelpUtil.ht("/Action/SimplifyAction#ConfirmSimplifyAll")
     134        );
     135        return ret == 0;
     136    }
     137
     138    public void actionPerformed(ActionEvent e) {
     139        Collection<OsmPrimitive> selection = getCurrentDataSet().getSelected();
     140
     141        List<Bounds> bounds = getCurrentEditBounds();
    48142        for (OsmPrimitive prim : selection) {
    49             if (prim instanceof Way) {
    50                 if (bounds.size() > 0) {
    51                     Way way = (Way) prim;
    52                     // We check if each node of each way is at least in one download
    53                     // bounding box. Otherwise nodes may get deleted that are necessary by
    54                     // unloaded ways (see Ticket #1594)
    55                     for (Node node : way.getNodes()) {
    56                         boolean isInsideOneBoundingBox = false;
    57                         for (Bounds b : bounds) {
    58                             if (b.contains(node.getCoor())) {
    59                                 isInsideOneBoundingBox = true;
    60                                 break;
    61                             }
    62                         }
    63                         if (!isInsideOneBoundingBox) {
    64                             int option = JOptionPane.showConfirmDialog(Main.parent,
    65                                     trn("The selected way has nodes outside of the downloaded data region.",
    66                                             "The selected ways have nodes outside of the downloaded data region.",
    67                                             Main.main.getCurrentDataSet().getSelectedWays().size()) + "\n"
    68                                             + tr("This can lead to nodes being deleted accidentally.") + "\n"
    69                                             + tr("Are you really sure to continue?"),
    70                                     tr("Please abort if you are not sure"), JOptionPane.YES_NO_CANCEL_OPTION,
    71                                     JOptionPane.WARNING_MESSAGE);
    72 
    73                             if (option != JOptionPane.YES_OPTION)
    74                                 return;
    75                             break;
    76                         }
     143            if (! (prim instanceof Way)) {
     144                continue;
     145            }
     146            if (bounds.size() > 0) {
     147                Way way = (Way) prim;
     148                // We check if each node of each way is at least in one download
     149                // bounding box. Otherwise nodes may get deleted that are necessary by
     150                // unloaded ways (see Ticket #1594)
     151                for (Node node : way.getNodes()) {
     152                    if (!isInBounds(node, bounds)) {
     153                        if (!confirmWayWithNodesOutsideBoundingBox())
     154                            return;
     155                        break;
    77156                    }
    78157                }
    79 
    80                 ways++;
    81             }
    82         }
    83 
    84         if (ways == 0) {
    85             JOptionPane.showMessageDialog(Main.parent, tr("Please select at least one way to simplify."));
     158            }
     159        }
     160        List<Way> ways = OsmPrimitive.getFilteredList(selection, Way.class);
     161        if (ways.isEmpty()) {
     162            alertSelectAtLeastOneWay();
    86163            return;
    87         } else if (ways > 10) {
    88             int option = JOptionPane.showConfirmDialog(Main.parent, trn(
    89                     "The selection contains {0} way. Are you sure you want to simplify it?",
    90                     "The selection contains {0} ways. Are you sure you want to simplify them all?",
    91                     ways,ways),
    92                     tr("Are you sure?"), JOptionPane.YES_NO_OPTION);
    93             if (option != JOptionPane.YES_OPTION)
     164        } else if (ways.size() > 10) {
     165            if (!confirmSimplifyManyWays(ways.size()))
    94166                return;
    95167        }
    96168
    97         for (OsmPrimitive prim : selection) {
    98             if (prim instanceof Way) {
    99                 simplifyWay((Way) prim);
    100             }
    101         }
    102     }
    103 
    104     public void simplifyWay(Way w) {
     169        Collection<Command> allCommands = new LinkedList<Command>();
     170        for (Way way: ways) {
     171            SequenceCommand simplifyCommand = simplifyWay(way);
     172            if (simplifyCommand == null) {
     173                continue;
     174            }
     175            allCommands.add(simplifyCommand);
     176        }
     177        if (allCommands.isEmpty()) return;
     178        SequenceCommand rootCommand = new SequenceCommand(
     179                trn("Simplify {0} way", "Simplify {0} ways", allCommands.size(), allCommands.size()),
     180                allCommands
     181        );
     182        Main.main.undoRedo.add(rootCommand);
     183        Main.map.repaint();
     184    }
     185
     186    /**
     187     * Replies true if <code>node</code> is a required node which can't be removed
     188     * in order to simplify the way.
     189     *
     190     * @param way the way to be simplified
     191     * @param node the node to check
     192     * @return true if <code>node</code> is a required node which can't be removed
     193     * in order to simplify the way.
     194     */
     195    protected boolean isRequiredNode(Way way, Node node) {
     196        boolean isRequired =  Collections.frequency(way.getNodes(), node) > 1;
     197        if (! isRequired) {
     198            List<OsmPrimitive> parents = new LinkedList<OsmPrimitive>();
     199            parents.addAll(node.getReferrers());
     200            parents.remove(way);
     201            isRequired = !parents.isEmpty();
     202        }
     203        if (!isRequired) {
     204            isRequired = node.isTagged();
     205        }
     206        return isRequired;
     207    }
     208
     209    /**
     210     * Simplifies a way
     211     *
     212     * @param w the way to simplify
     213     */
     214    public SequenceCommand simplifyWay(Way w) {
    105215        double threshold = Double.parseDouble(Main.pref.get("simplify-way.max-error", "3"));
    106 
    107         Way wnew = new Way(w);
    108 
    109         int toI = wnew.getNodesCount() - 1;
    110         List<OsmPrimitive> parents = new ArrayList<OsmPrimitive>();
    111         for (int i = wnew.getNodesCount() - 1; i >= 0; i--) {
    112             parents.addAll(w.getNode(i).getReferrers());
    113             boolean used = false;
    114             if (parents.size() == 2) {
    115                 used = Collections.frequency(w.getNodes(), wnew.getNode(i)) > 1;
    116             } else {
    117                 parents.remove(w);
    118                 parents.remove(wnew);
    119                 used = !parents.isEmpty();
    120             }
    121             if (!used)
    122                 used = wnew.getNode(i).isTagged();
    123 
    124             if (used) {
    125                 simplifyWayRange(wnew, i, toI, threshold);
    126                 toI = i;
    127             }
    128         }
    129         simplifyWayRange(wnew, 0, toI, threshold);
     216        int lower = 0;
     217        int i = 0;
     218        List<Node> newNodes = new LinkedList<Node>();
     219        while(i < w.getNodesCount()){
     220            if (isRequiredNode(w,w.getNode(i))) {
     221                // copy a required node to the list of new nodes. Simplify not
     222                // possible
     223                newNodes.add(w.getNode(i));
     224                i++;
     225                lower++;
     226                continue;
     227            }
     228            i++;
     229            // find the longest sequence of not required nodes ...
     230            while(i<w.getNodesCount() && !isRequiredNode(w,w.getNode(i))) {
     231                i++;
     232            }
     233            // ... and simplify them
     234            buildSimplifiedNodeList(w, lower, Math.min(w.getNodesCount()-1, i), threshold,newNodes);
     235            lower=i;
     236            i++;
     237        }
    130238
    131239        HashSet<Node> delNodes = new HashSet<Node>();
    132240        delNodes.addAll(w.getNodes());
    133         delNodes.removeAll(wnew.getNodes());
    134 
    135         if (wnew.getNodesCount() != w.getNodesCount()) {
    136             Collection<Command> cmds = new LinkedList<Command>();
    137             cmds.add(new ChangeCommand(w, wnew));
    138             cmds.add(new DeleteCommand(delNodes));
    139             Main.main.undoRedo.add(new SequenceCommand(trn("Simplify Way (remove {0} node)", "Simplify Way (remove {0} nodes)", delNodes.size(), delNodes.size()), cmds));
    140             Main.map.repaint();
    141         }
    142     }
    143 
    144     public void simplifyWayRange(Way wnew, int from, int to, double thr) {
    145         if (to - from >= 2) {
    146             ArrayList<Node> ns = new ArrayList<Node>();
    147             simplifyWayRange(wnew, from, to, ns, thr);
    148             List<Node> nodes = wnew.getNodes();
    149             for (int j = to - 1; j > from; j--) {
    150                 nodes.remove(j);
    151             }
    152             nodes.addAll(from + 1, ns);
    153             wnew.setNodes(nodes);
    154         }
    155     }
    156 
    157     /*
    158      * Takes an interval [from,to] and adds nodes from (from,to) to ns.
    159      * (from and to are indices of wnew.nodes.)
     241        delNodes.removeAll(newNodes);
     242
     243        if (delNodes.isEmpty()) return null;
     244
     245        Collection<Command> cmds = new LinkedList<Command>();
     246        Way newWay = new Way(w);
     247        newWay.setNodes(newNodes);
     248        cmds.add(new ChangeCommand(w, newWay));
     249        cmds.add(new DeleteCommand(delNodes));
     250        return new SequenceCommand(trn("Simplify Way (remove {0} node)", "Simplify Way (remove {0} nodes)", delNodes.size(), delNodes.size()), cmds);
     251    }
     252
     253    /**
     254     * Builds the simplified list of nodes for a way segment given by a lower index <code>from</code>
     255     * and an upper index <code>to</code>
     256     *
     257     * @param wnew the way to simplify
     258     * @param from the lower index
     259     * @param to the upper index
     260     * @param threshold
    160261     */
    161     public void simplifyWayRange(Way wnew, int from, int to, ArrayList<Node> ns, double thr) {
    162         Node fromN = wnew.getNode(from), toN = wnew.getNode(to);
     262    protected void buildSimplifiedNodeList(Way wnew, int from, int to, double threshold, List<Node> simplifiedNodes) {
     263
     264        Node fromN = wnew.getNode(from);
     265        Node toN = wnew.getNode(to);
    163266
    164267        int imax = -1;
     
    176279        }
    177280
    178         if (imax != -1 && xtemax >= thr) {
    179             simplifyWayRange(wnew, from, imax, ns, thr);
    180             ns.add(wnew.getNode(imax));
    181             simplifyWayRange(wnew, imax, to, ns, thr);
     281        if (imax != -1 && xtemax >= threshold) {
     282            buildSimplifiedNodeList(wnew, from, imax,threshold,simplifiedNodes);
     283            simplifiedNodes.add(wnew.getNode(imax));
     284            buildSimplifiedNodeList(wnew, imax, to, threshold,simplifiedNodes);
     285        } else {
     286            if (simplifiedNodes.isEmpty() || simplifiedNodes.get(simplifiedNodes.size()-1) != fromN) {
     287                simplifiedNodes.add(fromN);
     288            }
     289            if (simplifiedNodes.isEmpty() || simplifiedNodes.get(simplifiedNodes.size()-1) != toN) {
     290                simplifiedNodes.add(toN);
     291            }
    182292        }
    183293    }
Note: See TracChangeset for help on using the changeset viewer.