Changeset 23193 in osm for applications/editors/josm/plugins/simplifyarea
- Timestamp:
- 2010-09-15T19:01:04+02:00 (15 years ago)
- Location:
- applications/editors/josm/plugins/simplifyarea/src/sk/zdila/josm/plugin/simplify
- Files:
-
- 2 edited
-
SimplifyAreaAction.java (modified) (1 diff)
-
SimplifyAreaPlugin.java (modified) (1 diff)
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 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 icon76 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 icon91 options, options[0], null);92 return ret == 0;93 }94 95 96 @Override97 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 download105 // bounding box. Otherwise nodes may get deleted that are necessary by106 // 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 way148 * the way to be simplified149 * @param node150 * the node to check151 * @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 way162 *163 * @param w164 * the way to simplify165 */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 nodes190 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 coord1236 }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; // m290 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 @Override310 protected void updateEnabledState() {311 if (getCurrentDataSet() == null) {312 setEnabled(false);313 } else {314 updateEnabledState(getCurrentDataSet().getSelected());315 }316 }317 318 319 @Override320 protected void updateEnabledState(final Collection<? extends OsmPrimitive> selection) {321 setEnabled(selection != null && !selection.isEmpty());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 public SimplifyAreaPlugin(final PluginInformation info) {11 super(info);12 MainMenu.add(Main.main.menu.toolsMenu, new SimplifyAreaAction());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.
