Changeset 2976 in josm
- Timestamp:
- 2010-02-13T23:50:41+01:00 (15 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/actions/SimplifyWayAction.java
r2842 r2976 2 2 package org.openstreetmap.josm.actions; 3 3 4 import static org.openstreetmap.josm.gui.help.HelpUtil.ht; 4 5 import static org.openstreetmap.josm.tools.I18n.tr; 5 6 import static org.openstreetmap.josm.tools.I18n.trn; … … 7 8 import java.awt.event.ActionEvent; 8 9 import java.awt.event.KeyEvent; 9 import java.util.ArrayList;10 10 import java.util.Collection; 11 11 import java.util.Collections; … … 17 17 18 18 import org.openstreetmap.josm.Main; 19 import org.openstreetmap.josm.actions.JosmAction;20 19 import org.openstreetmap.josm.command.ChangeCommand; 21 20 import org.openstreetmap.josm.command.Command; … … 27 26 import org.openstreetmap.josm.data.osm.OsmPrimitive; 28 27 import org.openstreetmap.josm.data.osm.Way; 28 import org.openstreetmap.josm.gui.HelpAwareOptionPane; 29 import org.openstreetmap.josm.gui.HelpAwareOptionPane.ButtonSpec; 30 import org.openstreetmap.josm.gui.help.HelpUtil; 29 31 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 32 import org.openstreetmap.josm.tools.ImageProvider; 30 33 import org.openstreetmap.josm.tools.Shortcut; 31 34 … … 33 36 public SimplifyWayAction() { 34 37 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() { 42 43 LinkedList<Bounds> bounds = new LinkedList<Bounds>(); 43 44 OsmDataLayer dataLayer = Main.map.mapView.getEditLayer(); 44 45 for (DataSource ds : dataLayer.data.dataSources) { 45 if (ds.bounds != null) 46 if (ds.bounds != null) { 46 47 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(); 48 142 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; 77 156 } 78 157 } 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(); 86 163 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())) 94 166 return; 95 167 } 96 168 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) { 105 215 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 } 130 238 131 239 HashSet<Node> delNodes = new HashSet<Node>(); 132 240 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 160 261 */ 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); 163 266 164 267 int imax = -1; … … 176 279 } 177 280 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 } 182 292 } 183 293 }
Note:
See TracChangeset
for help on using the changeset viewer.