Ticket #20041: 20041.2.patch
File 20041.2.patch, 9.9 KB (added by , 3 years ago) |
---|
-
src/org/openstreetmap/josm/actions/AlignInCircleAction.java
27 27 import org.openstreetmap.josm.data.osm.Node; 28 28 import org.openstreetmap.josm.data.osm.OsmPrimitive; 29 29 import org.openstreetmap.josm.data.osm.Way; 30 import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon; 31 import org.openstreetmap.josm.data.validation.tests.SelfIntersectingWay; 30 32 import org.openstreetmap.josm.gui.Notification; 31 33 import org.openstreetmap.josm.tools.Geometry; 32 34 import org.openstreetmap.josm.tools.Shortcut; … … 54 56 } 55 57 56 58 /** 57 * Create a {@link MoveCommand} to move a node to a PolarCoor.59 * Add a {@link MoveCommand} to move a node to a PolarCoor if there is a significant move. 58 60 * @param n Node to move 59 61 * @param coor polar coordinate where to move the node 60 * @ return new MoveCommand61 * @since 1310762 * @param cmds list of commands 63 * @since xxx 62 64 */ 63 public static MoveCommand createMoveCommand(Node n, PolarCoor coor) {65 public static void addMoveCommandIfNeeded(Node n, PolarCoor coor, List<Command> cmds) { 64 66 EastNorth en = coor.toEastNorth(); 65 return new MoveCommand(n, en.east() - n.getEastNorth().east(), en.north() - n.getEastNorth().north()); 67 double deltaEast = en.east() - n.getEastNorth().east(); 68 double deltaNorth = en.north() - n.getEastNorth().north(); 69 if (Math.abs(deltaEast) > 5e-6 || Math.abs(deltaNorth) > 5e-6) { 70 cmds.add(new MoveCommand(n, deltaEast, deltaNorth)); 71 } 66 72 } 67 73 68 74 /** … … 112 118 if (ways.size() == 1 && !ways.get(0).isClosed()) { 113 119 // Case 1 114 120 Way w = ways.get(0); 121 SelfIntersectingWay test = new SelfIntersectingWay(); 122 test.startTest(null); 123 test.visit(w); 124 test.endTest(); 125 if (!test.getErrors().isEmpty()) { 126 showInvalidSelection(tr("Self-intersecting way")); 127 return; 128 } 129 115 130 fixNodes.add(w.firstNode()); 116 131 fixNodes.add(w.lastNode()); 117 132 fixNodes.addAll(nodes); … … 151 166 fixNodes.addAll(collectNodesWithExternReferrers(ways)); 152 167 nodes = collectNodesAnticlockwise(ways); 153 168 if (nodes.size() < 4) { 154 new Notification( 155 tr("Not enough nodes in selected ways.")) 156 .setIcon(JOptionPane.INFORMATION_MESSAGE) 157 .setDuration(Notification.TIME_SHORT) 158 .show(); 169 showInvalidSelection(tr("Not enough nodes in selected ways.")); 159 170 return; 160 171 } 161 172 } else if (ways.isEmpty() && nodes.size() > 3) { … … 163 174 fixNodes.addAll(nodes); 164 175 // No need to reorder nodes since all are fix 165 176 } else { 166 // Invalid action 167 new Notification( 168 tr("Please select at least four nodes.")) 169 .setIcon(JOptionPane.INFORMATION_MESSAGE) 170 .setDuration(Notification.TIME_SHORT) 171 .show(); 177 showInvalidSelection(null); 172 178 return; 173 179 } 174 180 181 if (!actionAllowed(nodes)) return; 182 175 183 if (center == null) { 176 184 // Compute the center of nodes 177 185 center = Geometry.getCenter(nodes); 178 186 if (center == null) { 179 new Notification(tr("Cannot determine center of selected nodes.")) 180 .setIcon(JOptionPane.INFORMATION_MESSAGE) 181 .setDuration(Notification.TIME_SHORT) 182 .show(); 187 showInvalidSelection(tr("Cannot determine center of selected nodes.")); 183 188 return; 184 189 } 185 190 } … … 194 199 radius = radius / nodes.size(); 195 200 } 196 201 197 if (!actionAllowed(nodes)) return;202 List<Command> cmds = new LinkedList<>(); 198 203 199 Collection<Command> cmds = new LinkedList<>();200 201 204 // Move each node to that distance from the center. 202 205 // Nodes that are not "fix" will be adjust making regular arcs. 203 206 int nodeCount = nodes.size(); … … 216 219 } 217 220 Node first = nodes.get(i % nodeCount); 218 221 PolarCoor pcFirst = new PolarCoor(radius, PolarCoor.computeAngle(first.getEastNorth(), center), center); 219 cmds.add(createMoveCommand(first, pcFirst));222 addMoveCommandIfNeeded(first, pcFirst, cmds); 220 223 if (j > i + 1) { 221 224 double delta; 222 225 if (j == i + nodeCount) { … … 230 233 } 231 234 for (int k = i+1; k < j; k++) { 232 235 PolarCoor p = new PolarCoor(radius, pcFirst.angle + (k-i)*delta, center); 233 cmds.add(createMoveCommand(nodes.get(k % nodeCount), p));236 addMoveCommandIfNeeded(nodes.get(k % nodeCount), p, cmds); 234 237 } 235 238 } 236 239 i = j; // Update start point for next iteration 237 240 } 241 if (cmds.isEmpty()) { 242 showInvalidSelection(null); 243 } else { 244 UndoRedoHandler.getInstance().add(new SequenceCommand(tr("Align Nodes in Circle"), cmds)); 245 } 246 } 238 247 239 UndoRedoHandler.getInstance().add(new SequenceCommand(tr("Align Nodes in Circle"), cmds)); 248 private static void showInvalidSelection(String message) { 249 if (message == null) 250 message = tr("Selection could not be used to align in circle."); 251 // Invalid action 252 new Notification(message) 253 .setIcon(JOptionPane.INFORMATION_MESSAGE) 254 .setDuration(Notification.TIME_SHORT) 255 .show(); 240 256 } 241 257 242 258 /** … … 254 270 * @return Nodes anticlockwise ordered 255 271 */ 256 272 private static List<Node> collectNodesAnticlockwise(List<Way> ways) { 257 List<Node> nodes = new ArrayList<>(); 258 Node firstNode = ways.get(0).firstNode(); 259 Node lastNode = null; 260 Way lastWay = null; 261 while (firstNode != lastNode) { 262 if (lastNode == null) lastNode = firstNode; 263 for (Way way: ways) { 264 if (way == lastWay) continue; 265 if (way.firstNode() == lastNode) { 266 List<Node> wayNodes = way.getNodes(); 267 for (int i = 0; i < wayNodes.size() - 1; i++) { 268 nodes.add(wayNodes.get(i)); 269 } 270 lastNode = way.lastNode(); 271 lastWay = way; 272 break; 273 } 274 if (way.lastNode() == lastNode) { 275 List<Node> wayNodes = way.getNodes(); 276 for (int i = wayNodes.size() - 1; i > 0; i--) { 277 nodes.add(wayNodes.get(i)); 278 } 279 lastNode = way.firstNode(); 280 lastWay = way; 281 break; 282 } 283 } 284 } 285 // Check if nodes are in anticlockwise order 286 int nc = nodes.size(); 287 double area = 0; 288 for (int i = 0; i < nc; i++) { 289 EastNorth p1 = nodes.get(i).getEastNorth(); 290 EastNorth p2 = nodes.get((i+1) % nc).getEastNorth(); 291 area += p1.east()*p2.north() - p2.east()*p1.north(); 292 } 293 if (area < 0) 273 List<Node> nodes = new ArrayList<>(Multipolygon.joinWays(ways).iterator().next().getNodes()); 274 if (Geometry.isClockwise(nodes)) 294 275 Collections.reverse(nodes); 276 nodes.remove(nodes.size() - 1); 295 277 return nodes; 296 278 } 297 279 … … 308 290 .setIcon(JOptionPane.WARNING_MESSAGE) 309 291 .setDuration(Notification.TIME_SHORT) 310 292 .show(); 311 return true;293 return !outside; 312 294 } 313 295 314 296 @Override … … 323 305 } 324 306 325 307 /** 326 * Determines if ways can be joined into a polygon.308 * Determines if ways can be joined into a single polygon. 327 309 * @param ways The ways collection to check 328 * @return true if all ways can be joined into a polygon310 * @return true if all ways can be joined into a single polygon 329 311 */ 330 312 private static boolean checkWaysArePolygon(Collection<Way> ways) { 313 if (Multipolygon.joinWays(ways).size() != 1) 314 return false; 331 315 // For each way, nodes strictly between first and last should't be reference by an other way 332 316 for (Way way: ways) { 333 317 for (Node node: way.getNodes()) { … … 337 321 } 338 322 } 339 323 } 340 // Test if ways can be joined 341 Way currentWay = null; 342 Node startNode = null, endNode = null; 343 int used = 0; 344 while (true) { 345 Way nextWay = null; 346 for (Way w: ways) { 347 if (w.isClosed()) return ways.size() == 1; 348 if (w == currentWay) continue; 349 if (currentWay == null) { 350 nextWay = w; 351 startNode = w.firstNode(); 352 endNode = w.lastNode(); 353 break; 354 } 355 if (w.firstNode() == endNode) { 356 nextWay = w; 357 endNode = w.lastNode(); 358 break; 359 } 360 if (w.lastNode() == endNode) { 361 nextWay = w; 362 endNode = w.firstNode(); 363 break; 364 } 365 } 366 if (nextWay == null) return false; 367 used += 1; 368 currentWay = nextWay; 369 if (endNode == startNode) return used == ways.size(); 370 } 324 return true; 371 325 } 326 372 327 }