Changeset 23193 in osm for applications/editors/josm/plugins/simplifyarea/src
- Timestamp:
- 2010-09-15T19:01:04+02:00 (14 years ago)
- Location:
- applications/editors/josm/plugins/simplifyarea/src/sk/zdila/josm/plugin/simplify
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
applications/editors/josm/plugins/simplifyarea/src/sk/zdila/josm/plugin/simplify/SimplifyAreaAction.java
r21870 r23193 36 36 public class SimplifyAreaAction extends JosmAction { 37 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 38 private static final long serialVersionUID = 6854238214548011750L; 39 40 public SimplifyAreaAction() { 41 super(tr("Simplify Area"), "simplify", tr("Delete unnecessary nodes from an area."), 42 Shortcut.registerShortcut("tools:simplifyArea", tr("Tool: {0}", tr("Simplify Area")), KeyEvent.VK_A, Shortcut.GROUP_EDIT, Shortcut.SHIFT_DEFAULT), true); 43 } 44 45 46 private List<Bounds> getCurrentEditBounds() { 47 final LinkedList<Bounds> bounds = new LinkedList<Bounds>(); 48 final OsmDataLayer dataLayer = Main.map.mapView.getEditLayer(); 49 for (final DataSource ds : dataLayer.data.dataSources) { 50 if (ds.bounds != null) { 51 bounds.add(ds.bounds); 52 } 53 } 54 return bounds; 55 } 56 57 58 private boolean isInBounds(final Node node, final List<Bounds> bounds) { 59 for (final Bounds b : bounds) { 60 if (b.contains(node.getCoor())) { 61 return true; 62 } 63 } 64 return false; 65 } 66 67 68 private boolean confirmWayWithNodesOutsideBoundingBox() { 69 final ButtonSpec[] options = new ButtonSpec[] { new ButtonSpec(tr("Yes, delete nodes"), ImageProvider.get("ok"), tr("Delete nodes outside of downloaded data regions"), null), 70 new ButtonSpec(tr("No, abort"), ImageProvider.get("cancel"), tr("Cancel operation"), null) }; 71 final int ret = HelpAwareOptionPane.showOptionDialog( 72 Main.parent, 73 "<html>" + trn("The selected way has nodes outside of the downloaded data region.", "The selected ways have nodes outside of the downloaded data region.", getCurrentDataSet().getSelectedWays().size()) 74 + "<br>" + tr("This can lead to nodes being deleted accidentally.") + "<br>" + tr("Do you want to delete them anyway?") + "</html>", 75 tr("Delete nodes outside of data regions?"), JOptionPane.WARNING_MESSAGE, null, // no special icon 76 options, options[0], null); 77 return ret == 0; 78 } 79 80 81 private void alertSelectAtLeastOneWay() { 82 HelpAwareOptionPane.showOptionDialog(Main.parent, tr("Please select at least one way to simplify."), tr("Warning"), JOptionPane.WARNING_MESSAGE, null); 83 } 84 85 86 private boolean confirmSimplifyManyWays(final int numWays) { 87 final ButtonSpec[] options = new ButtonSpec[] { new ButtonSpec(tr("Yes"), ImageProvider.get("ok"), tr("Simplify all selected ways"), null), 88 new ButtonSpec(tr("Cancel"), ImageProvider.get("cancel"), tr("Cancel operation"), null) }; 89 final int ret = HelpAwareOptionPane.showOptionDialog(Main.parent, tr("The selection contains {0} ways. Are you sure you want to simplify them all?", numWays), tr("Simplify ways?"), 90 JOptionPane.WARNING_MESSAGE, null, // no special icon 91 options, options[0], null); 92 return ret == 0; 93 } 94 95 96 @Override 97 public void actionPerformed(final ActionEvent e) { 98 final Collection<OsmPrimitive> selection = getCurrentDataSet().getSelected(); 99 100 final List<Bounds> bounds = getCurrentEditBounds(); 101 for (final OsmPrimitive prim : selection) { 102 if (prim instanceof Way && bounds.size() > 0) { 103 final Way way = (Way) prim; 104 // We check if each node of each way is at least in one download 105 // bounding box. Otherwise nodes may get deleted that are necessary by 106 // unloaded ways (see Ticket #1594) 107 for (final Node node : way.getNodes()) { 108 if (!isInBounds(node, bounds)) { 109 if (!confirmWayWithNodesOutsideBoundingBox()) { 110 return; 111 } 112 break; 113 } 114 } 115 } 116 } 117 final List<Way> ways = OsmPrimitive.getFilteredList(selection, Way.class); 118 if (ways.isEmpty()) { 119 alertSelectAtLeastOneWay(); 120 return; 121 } else if (ways.size() > 10) { 122 if (!confirmSimplifyManyWays(ways.size())) { 123 return; 124 } 125 } 126 127 final Collection<Command> allCommands = new LinkedList<Command>(); 128 for (final Way way : ways) { 129 final SequenceCommand simplifyCommand = simplifyWay(way); 130 if (simplifyCommand == null) { 131 continue; 132 } 133 allCommands.add(simplifyCommand); 134 } 135 136 if (!allCommands.isEmpty()) { 137 final SequenceCommand rootCommand = new SequenceCommand(trn("Simplify {0} way", "Simplify {0} ways", allCommands.size(), allCommands.size()), allCommands); 138 Main.main.undoRedo.add(rootCommand); 139 Main.map.repaint(); 140 } 141 } 142 143 144 /** 145 * Replies true if <code>node</code> is a required node which can't be removed in order to simplify the way. 146 * 147 * @param way 148 * the way to be simplified 149 * @param node 150 * the node to check 151 * @return true if <code>node</code> is a required node which can't be removed in order to simplify the way. 152 */ 153 private boolean isRequiredNode(final Way way, final Node node) { 154 final List<OsmPrimitive> parents = new LinkedList<OsmPrimitive>(node.getReferrers()); 155 parents.remove(way); 156 return !parents.isEmpty() || node.isTagged(); 157 } 158 159 160 /** 161 * Simplifies a way 162 * 163 * @param w 164 * the way to simplify 165 */ 166 private SequenceCommand simplifyWay(final Way w) { 167 final double angleThreshold = Double.parseDouble(Main.pref.get("simplify-area.angle", "10.0")); 168 final double distanceTreshold = Double.parseDouble(Main.pref.get("simplify-area.distance", "0.2")); 169 final double areaTreshold = Double.parseDouble(Main.pref.get("simplify-area.area", "5.0")); 170 171 final List<Node> nodes = w.getNodes(); 172 final int size = nodes.size(); 173 174 if (size == 0) { 175 return null; 176 } 177 178 final List<MoveCommand> moveCommandList = new ArrayList<MoveCommand>(); 179 180 final boolean closed = nodes.get(0).equals(nodes.get(size - 1)); 181 182 final List<Node> newNodes = new ArrayList<Node>(size); 183 184 if (closed) { 185 nodes.remove(size - 1); // remove end node ( = start node) 186 } 187 188 { 189 // remove near nodes 190 for (int i = 0; i < size; i++) { 191 final boolean closing = closed && i == size - 1; 192 final Node n1 = closing ? nodes.get(0) : nodes.get(i); 193 194 if (newNodes.isEmpty()) { 195 newNodes.add(n1); 196 continue; 197 } 198 199 final Node n2 = newNodes.get(newNodes.size() - 1); 200 201 final LatLon coord1 = n1.getCoor(); 202 final LatLon coord2 = n2.getCoor(); 203 204 if (isRequiredNode(w, n1) || isRequiredNode(w, n2) || computeDistance(coord1, coord2) > distanceTreshold) { 205 if (!closing) { 206 newNodes.add(n1); 207 } 208 } else { 209 moveCommandList.add(new MoveCommand(n2, new LatLon((coord1.lat() + coord2.lat()) / 2.0, (coord1.lon() + coord2.lon()) / 2.0))); 210 if (closing) { 211 newNodes.remove(0); 212 } 213 } 214 } 215 } 216 217 final int size2 = newNodes.size(); 218 219 final List<Node> newNodes2 = new ArrayList<Node>(size2); 220 221 Node prevNode = null; 222 LatLon coord1 = null; 223 LatLon coord2 = null; 224 225 for (int i = 0, len = size2 + 1 + (closed ? 1 : 0); i < len; i++) { 226 final Node n = newNodes.get(i % size2); 227 final LatLon coord3 = n.getCoor(); 228 229 if (coord1 != null) { 230 if (isRequiredNode(w, prevNode) || 231 Math.abs(computeBearing(coord2, coord3) - computeBearing(coord1, coord2)) > angleThreshold || 232 computeArea(coord1, coord2, coord3) > areaTreshold) { 233 newNodes2.add(prevNode); 234 } else { 235 coord2 = coord1; // at the end of the iteration preserve coord1 236 } 237 } else if (!closed && prevNode != null) { 238 newNodes2.add(prevNode); 239 } 240 241 coord1 = coord2; 242 coord2 = coord3; 243 prevNode = n; 244 } 245 246 if (closed) { 247 newNodes2.add(newNodes2.get(0)); // set end node ( = start node) 248 } 249 250 final HashSet<Node> delNodes = new HashSet<Node>(); 251 delNodes.addAll(nodes); 252 delNodes.removeAll(newNodes2); 253 254 if (delNodes.isEmpty()) { 255 return null; 256 } 257 258 final Collection<Command> cmds = new LinkedList<Command>(); 259 final Way newWay = new Way(w); 260 newWay.setNodes(newNodes2); 261 262 cmds.addAll(moveCommandList); 263 cmds.add(new ChangeCommand(w, newWay)); 264 cmds.add(new DeleteCommand(delNodes)); 265 return new SequenceCommand(trn("Simplify Way (remove {0} node)", "Simplify Way (remove {0} nodes)", delNodes.size(), delNodes.size()), cmds); 266 } 267 268 269 private double computeBearing(final LatLon coord1, final LatLon coord2) { 270 final double lon1 = Math.toRadians(coord1.getX()); 271 final double lat1 = Math.toRadians(coord1.getY()); 272 273 final double lon2 = Math.toRadians(coord2.getX()); 274 final double lat2 = Math.toRadians(coord2.getY()); 275 276 final double dLon = lon2 - lon1; 277 final double y = Math.sin(dLon) * Math.cos(lat2); 278 final double x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon); 279 return Math.toDegrees(Math.atan2(y, x)); 280 } 281 282 283 private double computeDistance(final LatLon coord1, final LatLon coord2) { 284 final double lon1 = Math.toRadians(coord1.getX()); 285 final double lon2 = Math.toRadians(coord2.getX()); 286 final double lat1 = Math.toRadians(coord1.getY()); 287 final double lat2 = Math.toRadians(coord2.getY()); 288 289 final double R = 6378137d; // m 290 final double dLon = lon2 - lon1; 291 final double dLat = lat2 - lat1; 292 final double a = Math.sin(dLat / 2d) * Math.sin(dLat / 2d) + Math.cos(lat1) * Math.cos(lat2) * Math.sin(dLon / 2d) * Math.sin(dLon / 2d); 293 final double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); 294 return R * c; 295 } 296 297 298 private double computeArea(final LatLon coord1, final LatLon coord2, final LatLon coord3) { 299 final double a = computeDistance(coord1, coord2); 300 final double b = computeDistance(coord2, coord3); 301 final double c = computeDistance(coord3, coord1); 302 303 final double p = (a + b + c) / 2.0; 304 305 return Math.sqrt(p * (p - a) * (p - b) * (p - c)); 306 } 307 308 309 @Override 310 protected void updateEnabledState() { 311 if (getCurrentDataSet() == null) { 312 setEnabled(false); 313 } else { 314 updateEnabledState(getCurrentDataSet().getSelected()); 315 } 316 } 317 318 319 @Override 320 protected void updateEnabledState(final Collection<? extends OsmPrimitive> selection) { 321 setEnabled(selection != null && !selection.isEmpty()); 322 } 323 323 324 324 } -
applications/editors/josm/plugins/simplifyarea/src/sk/zdila/josm/plugin/simplify/SimplifyAreaPlugin.java
r21870 r23193 8 8 public class SimplifyAreaPlugin extends Plugin { 9 9 10 11 12 13 10 public SimplifyAreaPlugin(final PluginInformation info) { 11 super(info); 12 MainMenu.add(Main.main.menu.toolsMenu, new SimplifyAreaAction()); 13 } 14 14 15 15 }
Note:
See TracChangeset
for help on using the changeset viewer.