Changeset 25488 in osm for applications/editors/josm
- Timestamp:
- 2011-03-02T22:34:51+01:00 (14 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
applications/editors/josm/plugins/simplifyarea/src/sk/zdila/josm/plugin/simplify/SimplifyAreaAction.java
r25030 r25488 10 10 import java.awt.event.ActionEvent; 11 11 import java.awt.event.KeyEvent; 12 import java.util.ArrayList; 12 13 import java.util.Collection; 13 14 import java.util.HashMap; … … 16 17 import java.util.List; 17 18 import java.util.Map; 19 import java.util.Map.Entry; 20 import java.util.Set; 18 21 19 22 import javax.swing.JOptionPane; … … 44 47 public SimplifyAreaAction() { 45 48 super(tr("Simplify Area"), "simplify", tr("Delete unnecessary nodes from an area."), 46 Shortcut.registerShortcut("tools:simplifyArea", tr("Tool: {0}", tr("Simplify Area")), KeyEvent.VK_A, Shortcut.GROUP_EDIT, Shortcut.SHIFT_DEFAULT), true); 47 } 49 Shortcut.registerShortcut("tools:simplifyArea", tr("Tool: {0}", tr("Simplify Area")), KeyEvent.VK_F, Shortcut.GROUP_EDIT, Shortcut.SHIFT_DEFAULT), true); 50 } 51 48 52 49 53 … … 119 123 } 120 124 } 121 final List<Way> ways = OsmPrimitive.getFilteredList(selection, Way.class);125 final Collection<Way> ways = OsmPrimitive.getFilteredSet(selection, Way.class); 122 126 if (ways.isEmpty()) { 123 127 alertSelectAtLeastOneWay(); … … 129 133 } 130 134 131 final Collection<Command> allCommands = new LinkedList<Command>(); 135 final List<Node> nodesToDelete = new ArrayList<Node>(); // can contain duplicate instances 136 132 137 for (final Way way : ways) { 133 final SequenceCommand simplifyCommand = simplifyWay(way); 134 if (simplifyCommand == null) { 135 continue; 136 } 137 allCommands.add(simplifyCommand); 138 addNodesToDelete(nodesToDelete, way); 139 } 140 141 class Count { 142 int count; 143 } 144 145 final Map<Node, Count> nodeCountMap = new HashMap<Node, Count>(); 146 for (final Node node : nodesToDelete) { 147 Count count = nodeCountMap.get(node); 148 if (count == null) { 149 count = new Count(); 150 nodeCountMap.put(node, count); 151 } 152 count.count++; 153 } 154 155 final Collection<Node> nodesReallyToRemove = new ArrayList<Node>(); 156 157 for (final Entry<Node, Count> entry : nodeCountMap.entrySet()) { 158 final Node node = entry.getKey(); 159 final int count = entry.getValue().count; 160 161 if (!node.isTagged() && node.getReferrers().size() == count) { 162 nodesReallyToRemove.add(node); 163 } 164 } 165 166 final Collection<Command> allCommands = new ArrayList<Command>(); 167 168 if (!nodesReallyToRemove.isEmpty()) { 169 for (final Way way : ways) { 170 final List<Node> nodes = way.getNodes(); 171 final boolean closed = nodes.get(0).equals(nodes.get(nodes.size() - 1)); 172 if (closed) { 173 nodes.remove(nodes.size() - 1); 174 } 175 176 if (nodes.removeAll(nodesReallyToRemove)) { 177 if (closed) { 178 nodes.add(nodes.get(0)); 179 } 180 181 final Way newWay = new Way(way); 182 newWay.setNodes(nodes); 183 allCommands.add(new ChangeCommand(way, newWay)); 184 } 185 } 186 187 allCommands.add(new DeleteCommand(nodesReallyToRemove)); 188 } 189 190 final Collection<Command> avgCommands = averageNearbyNodes(ways, nodesReallyToRemove); 191 if (avgCommands != null) { 192 allCommands.add(new SequenceCommand("average nearby nodes", avgCommands)); 138 193 } 139 194 … … 146 201 147 202 148 /** 149 * Replies true if <code>node</code> is a required node which can't be removed in order to simplify the way. 150 * 151 * @param way 152 * the way to be simplified 153 * @param node 154 * the node to check 155 * @return true if <code>node</code> is a required node which can't be removed in order to simplify the way. 156 */ 157 private boolean isRequiredNode(final Way way, final Node node) { 158 final List<OsmPrimitive> parents = new LinkedList<OsmPrimitive>(node.getReferrers()); 159 parents.remove(way); 160 return !parents.isEmpty() || node.isTagged(); 161 } 162 163 164 /** 165 * Simplifies a way 166 * 167 * @param w 168 * the way to simplify 169 */ 170 private SequenceCommand simplifyWay(final Way w) { 203 // average nearby nodes 204 private Collection<Command> averageNearbyNodes(final Collection<Way> ways, final Collection<Node> nodesAlreadyDeleted) { 205 final double mergeThreshold = Main.pref.getDouble("simplify-area.merge.threshold", 0.2); 206 207 final Map<Node, LatLon> coordMap = new HashMap<Node, LatLon>(); 208 for (final Way way : ways) { 209 for (final Node n : way.getNodes()) { 210 coordMap.put(n, n.getCoor()); 211 } 212 } 213 214 coordMap.keySet().removeAll(nodesAlreadyDeleted); 215 216 for (final Way w : ways) { 217 final List<Node> nodes = w.getNodes(); 218 219 final Node lastNode = nodes.get(nodes.size() - 1); 220 final boolean closed = nodes.get(0).equals(lastNode); 221 if (closed) { 222 nodes.remove(lastNode); 223 } 224 225 nodes.retainAll(coordMap.keySet()); // removes already deleted nodes 226 227 while (true) { 228 double minDist = Double.MAX_VALUE; 229 Node node1 = null; 230 Node node2 = null; 231 232 // find smallest distance 233 for (int i = 0, len = nodes.size(); i <= len; i++) { 234 final Node n1 = nodes.get(i % len); 235 final Node n2 = nodes.get((i + 1) % len); 236 237 if (n1.isTagged() || n2.isTagged()) { 238 continue; 239 } 240 241 // test if both nodes are on the same ways 242 final List<OsmPrimitive> referrers = n1.getReferrers(); 243 if (!ways.containsAll(referrers)) { 244 continue; 245 } 246 247 final List<OsmPrimitive> referrers2 = n2.getReferrers(); 248 if (!ways.containsAll(referrers2)) { 249 continue; 250 } 251 252 // test if both nodes have same parents 253 if (!referrers.containsAll(referrers2) || !referrers2.containsAll(referrers)) { 254 continue; 255 } 256 257 final double dist = coordMap.get(n1).greatCircleDistance(coordMap.get(n2)); 258 if (dist < minDist && dist < mergeThreshold) { 259 minDist = dist; 260 node1 = n1; 261 node2 = n2; 262 } 263 } 264 265 if (node1 == null || node2 == null) { 266 break; 267 } 268 269 final LatLon coord = coordMap.get(node1).getCenter(coordMap.get(node2)); 270 coordMap.put(node1, coord); 271 272 nodes.remove(node2); 273 coordMap.remove(node2); 274 } 275 } 276 277 278 final Collection<Command> commands = new ArrayList<Command>(); 279 final Set<Node> nodesToDelete2 = new HashSet<Node>(); 280 for (final Way way : ways) { 281 final List<Node> nodesToDelete = way.getNodes(); 282 nodesToDelete.removeAll(nodesAlreadyDeleted); 283 if (nodesToDelete.removeAll(coordMap.keySet())) { 284 nodesToDelete2.addAll(nodesToDelete); 285 final Way newWay = new Way(way); 286 final List<Node> nodes = way.getNodes(); 287 final boolean closed = nodes.get(0).equals(nodes.get(nodes.size() - 1)); 288 if (closed) { 289 nodes.remove(nodes.size() - 1); 290 } 291 nodes.retainAll(coordMap.keySet()); 292 if (closed) { 293 nodes.add(nodes.get(0)); 294 } 295 296 newWay.setNodes(nodes); 297 commands.add(new ChangeCommand(way, newWay)); 298 } 299 } 300 301 if (!nodesToDelete2.isEmpty()) { 302 commands.add(new DeleteCommand(nodesToDelete2)); 303 } 304 305 for (final Entry<Node, LatLon> entry : coordMap.entrySet()) { 306 final Node node = entry.getKey(); 307 final LatLon coord = entry.getValue(); 308 if (!node.getCoor().equals(coord)) { 309 commands.add(new MoveCommand(node, coord)); 310 } 311 } 312 313 return commands; 314 } 315 316 317 private void addNodesToDelete(final Collection<Node> nodesToDelete, final Way w) { 318 // System.out.println("---------------- " + w.getId()); 319 171 320 final double angleThreshold = Main.pref.getDouble("simplify-area.angle.threshold", 10); 172 321 final double angleFactor = Main.pref.getDouble("simplify-area.angle.factor", 1.0); 173 final double mergeThreshold = Main.pref.getDouble("simplify-area.merge.threshold", 0.2);174 322 final double areaThreshold = Main.pref.getDouble("simplify-area.area.threshold", 5.0); 175 323 final double areaFactor = Main.pref.getDouble("simplify-area.area.factor", 1.0); … … 181 329 182 330 if (size == 0) { 183 return null;331 return; 184 332 } 185 333 … … 192 340 // remove nodes within threshold 193 341 342 final List<Double> weightList = new ArrayList<Double>(nodes.size()); // weight cache 343 for (int i = 0; i < nodes.size(); i++) { 344 weightList.add(null); 345 } 346 194 347 while (true) { 348 // System.out.println(nodes.size()); 195 349 Node prevNode = null; 196 350 LatLon coord1 = null; … … 201 355 202 356 for (int i = 0, len = nodes.size() + (closed ? 2 : 1); i < len; i++) { 203 final Node n = nodes.get(i % nodes.size()); 357 final int index = i % nodes.size(); 358 359 final Node n = nodes.get(index); 204 360 final LatLon coord3 = n.getCoor(); 205 361 206 362 if (coord1 != null) { 207 final double angleWeight = computeConvectAngle(coord1, coord2, coord3) / angleThreshold; 208 final double areaWeight = computeArea(coord1, coord2, coord3) / areaThreshold; 209 final double distanceWeight = Math.abs(crossTrackError(coord1, coord2, coord3)) / distanceThreshold; 210 211 final double weight = isRequiredNode(w, prevNode) || 212 !closed && i == len - 1 || // don't remove last node of the not closed way 213 angleWeight > 1.0 || areaWeight > 1.0 || distanceWeight > 1.0 ? Double.MAX_VALUE : 214 angleWeight * angleFactor + areaWeight * areaFactor + distanceWeight * distanceFactor; 363 final double weight; 364 365 if (weightList.get(index) == null) { 366 final double angleWeight = computeConvectAngle(coord1, coord2, coord3) / angleThreshold; 367 final double areaWeight = computeArea(coord1, coord2, coord3) / areaThreshold; 368 final double distanceWeight = Math.abs(crossTrackError(coord1, coord2, coord3)) / distanceThreshold; 369 370 weight = /*isRequiredNode(w, prevNode, minUse) ||*/ 371 !closed && i == len - 1 || // don't remove last node of the not closed way 372 angleWeight > 1.0 || areaWeight > 1.0 || distanceWeight > 1.0 ? Double.MAX_VALUE : 373 angleWeight * angleFactor + areaWeight * areaFactor + distanceWeight * distanceFactor; 374 375 weightList.set(index, weight); 376 } else { 377 weight = weightList.get(index); 378 } 215 379 216 380 if (weight < minWeight) { … … 229 393 } 230 394 231 nodes.remove(bestMatch); 232 } 233 234 235 // average nearby nodes 236 237 final Map<Node, LatLon> coordMap = new HashMap<Node, LatLon>(); 238 for (final Node n : nodes) { 239 coordMap.put(n, n.getCoor()); 240 } 241 242 final Map<Node, MoveCommand> moveCommandList = new HashMap<Node, MoveCommand>(); 243 244 while (true) { 245 double minDist = Double.MAX_VALUE; 246 Node node1 = null; 247 Node node2 = null; 248 249 for (int i = 0, len = nodes.size() + (closed ? 2 : 1); i < len; i++) { 250 final Node n1 = nodes.get(i % nodes.size()); 251 final Node n2 = nodes.get((i + 1) % nodes.size()); 252 253 if (isRequiredNode(w, n1) || isRequiredNode(w, n2)) { 254 continue; 255 } 256 257 final double dist = coordMap.get(n1).greatCircleDistance(coordMap.get(n2)); 258 if (dist < minDist && dist < mergeThreshold) { 259 minDist = dist; 260 node1 = n1; 261 node2 = n2; 262 } 263 } 264 265 if (node1 == null || node2 == null) { 266 break; 267 } 268 269 270 final LatLon coord = coordMap.get(node1).getCenter(coordMap.get(node2)); 271 coordMap.put(node1, coord); 272 moveCommandList.put(node1, new MoveCommand(node1, coord)); 273 274 nodes.remove(node2); 275 coordMap.remove(node2); 276 moveCommandList.remove(node2); 277 } 278 279 280 if (closed) { 281 nodes.add(nodes.get(0)); // set end node ( = start node) 395 final int index = nodes.indexOf(bestMatch); 396 397 final int size2 = nodes.size(); 398 weightList.set((index - 1 + size2) % size2, null); 399 weightList.set((index + 1 + size2) % size2, null); 400 weightList.remove(index); 401 nodes.remove(index); 282 402 } 283 403 … … 285 405 delNodes.removeAll(nodes); 286 406 287 if (delNodes.isEmpty()) { 288 return null; 289 } 290 291 final Collection<Command> cmds = new LinkedList<Command>(); 292 final Way newWay = new Way(w); 293 newWay.setNodes(nodes); 294 295 cmds.addAll(moveCommandList.values()); 296 cmds.add(new ChangeCommand(w, newWay)); 297 cmds.add(new DeleteCommand(delNodes)); 298 return new SequenceCommand(trn("Simplify Area (remove {0} node)", "Simplify Area (remove {0} nodes)", delNodes.size(), delNodes.size()), cmds); 407 nodesToDelete.addAll(delNodes); 299 408 } 300 409
Note:
See TracChangeset
for help on using the changeset viewer.