Changeset 30782 in osm for applications/editors/josm
- Timestamp:
- 2014-10-29T21:56:35+01:00 (10 years ago)
- Location:
- applications/editors/josm/plugins/merge-overlap/src/mergeoverlap
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
applications/editors/josm/plugins/merge-overlap/src/mergeoverlap/MergeOverlapAction.java
r30766 r30782 50 50 * Merge overlapping part of ways. 51 51 */ 52 @SuppressWarnings("serial")53 52 public class MergeOverlapAction extends JosmAction { 54 53 55 public MergeOverlapAction() { 56 super(tr("Merge overlap"), "merge_overlap", 57 tr("Merge overlap of ways."), 58 Shortcut.registerShortcut("tools:mergeoverlap",tr("Tool: {0}", tr("Merge overlap")), KeyEvent.VK_O, 59 Shortcut.ALT_CTRL) 60 , true); 61 } 62 63 Map<Way, List<Relation>> relations = new HashMap<>(); 64 Map<Way, Way> oldWays = new HashMap<>(); 65 Map<Relation, Relation> newRelations = new HashMap<>(); 66 Set<Way> deletes = new HashSet<>(); 67 68 /** 69 * The action button has been clicked 70 * 71 * @param e 72 * Action Event 73 */ 54 /** 55 * Constructs a new {@code MergeOverlapAction}. 56 */ 57 public MergeOverlapAction() { 58 super(tr("Merge overlap"), "merge_overlap", 59 tr("Merge overlap of ways."), 60 Shortcut.registerShortcut("tools:mergeoverlap",tr("Tool: {0}", tr("Merge overlap")), KeyEvent.VK_O, 61 Shortcut.ALT_CTRL), true); 62 } 63 64 Map<Way, List<Relation>> relations = new HashMap<>(); 65 Map<Way, Way> oldWays = new HashMap<>(); 66 Map<Relation, Relation> newRelations = new HashMap<>(); 67 Set<Way> deletes = new HashSet<>(); 68 69 /** 70 * The action button has been clicked 71 * 72 * @param e 73 * Action Event 74 */ 75 @Override 76 public void actionPerformed(ActionEvent e) { 77 78 // List of selected ways 79 List<Way> ways = new ArrayList<>(); 80 relations.clear(); 81 newRelations.clear(); 82 83 // For every selected way 84 for (OsmPrimitive osm : Main.main.getCurrentDataSet().getSelected()) { 85 if (osm instanceof Way && !osm.isDeleted()) { 86 Way way = (Way) osm; 87 ways.add(way); 88 List<Relation> rels = new ArrayList<>(); 89 for (Relation r : OsmPrimitive.getFilteredList(way.getReferrers(), Relation.class)) { 90 rels.add(r); 91 } 92 relations.put(way, rels); 93 } 94 } 95 96 List<Way> sel = new ArrayList<>(ways); 97 Collection<Command> cmds = new LinkedList<>(); 98 99 // ***** 100 // split 101 // ***** 102 for (Way way : ways) { 103 Set<Node> nodes = new HashSet<>(); 104 for (Way opositWay : ways) { 105 if (way != opositWay) { 106 List<NodePos> nodesPos = new LinkedList<>(); 107 108 int pos = 0; 109 for (Node node : way.getNodes()) { 110 int opositPos = 0; 111 for (Node opositNode : opositWay.getNodes()) { 112 if (node == opositNode) { 113 if (opositWay.isClosed()) { 114 opositPos %= opositWay.getNodesCount() - 1; 115 } 116 nodesPos.add(new NodePos(node, pos, opositPos)); 117 break; 118 } 119 opositPos++; 120 } 121 pos++; 122 } 123 124 NodePos start = null; 125 NodePos end = null; 126 int increment = 0; 127 128 boolean hasFirst = false; 129 for (NodePos node : nodesPos) { 130 if (start == null) { 131 start = node; 132 } else { 133 if (end == null) { 134 if (follows(way, opositWay, start, node, 1)) { 135 end = node; 136 increment = +1; 137 } else if (follows(way, opositWay, start, node, -1)) { 138 end = node; 139 increment = -1; 140 } else { 141 start = node; 142 end = null; 143 } 144 } else { 145 if (follows(way, opositWay, end, node, increment)) { 146 end = node; 147 } else { 148 hasFirst = addNodes(start, end, way, nodes, hasFirst); 149 start = node; 150 end = null; 151 } 152 } 153 } 154 } 155 156 if (start != null && end != null) { 157 hasFirst = addNodes(start, end, way, nodes, hasFirst); 158 start = null; 159 end = null; 160 } 161 } 162 } 163 if (!nodes.isEmpty() && !way.isClosed() || nodes.size() >= 2) { 164 List<List<Node>> wayChunks = SplitWayAction.buildSplitChunks(way, new ArrayList<>(nodes)); 165 SplitWayResult result = splitWay(getEditLayer(), way, wayChunks); 166 167 cmds.add(result.getCommand()); 168 sel.remove(way); 169 sel.add(result.getOriginalWay()); 170 sel.addAll(result.getNewWays()); 171 List<Relation> rels = relations.remove(way); 172 relations.put(result.getOriginalWay(), rels); 173 for (Way w : result.getNewWays()) { 174 relations.put(w, rels); 175 } 176 } 177 } 178 179 // ***** 180 // merge 181 // ***** 182 ways = new ArrayList<>(sel); 183 while (!ways.isEmpty()) { 184 Way way = ways.get(0); 185 List<Way> combine = new ArrayList<>(); 186 combine.add(way); 187 for (Way opositWay : ways) { 188 if (way != opositWay 189 && way.getNodesCount() == opositWay.getNodesCount()) { 190 boolean equals1 = true; 191 for (int i = 0; i < way.getNodesCount(); i++) { 192 if (way.getNode(i) != opositWay.getNode(i)) { 193 equals1 = false; 194 break; 195 } 196 } 197 boolean equals2 = true; 198 for (int i = 0; i < way.getNodesCount(); i++) { 199 if (way.getNode(i) != opositWay.getNode(way 200 .getNodesCount() 201 - i - 1)) { 202 equals2 = false; 203 break; 204 } 205 } 206 if (equals1 || equals2) { 207 combine.add(opositWay); 208 } 209 } 210 } 211 ways.removeAll(combine); 212 if (combine.size() > 1) { 213 sel.removeAll(combine); 214 // combine 215 Pair<Way, List<Command>> combineResult; 216 try { 217 combineResult = combineWaysWorker(combine); 218 } catch (UserCancelException ex) { 219 return; 220 } 221 sel.add(combineResult.a); 222 cmds.addAll(combineResult.b); 223 } 224 } 225 226 for (Relation old : newRelations.keySet()) { 227 cmds.add(new ChangeCommand(old, newRelations.get(old))); 228 } 229 230 List<Way> del = new LinkedList<>(); 231 for (Way w : deletes) { 232 if (!w.isDeleted()) { 233 del.add(w); 234 } 235 } 236 if (!del.isEmpty()) { 237 cmds.add(new DeleteCommand(del)); 238 } 239 240 // Commit 241 Main.main.undoRedo.add(new SequenceCommand(tr("Merge Overlap (combine)"), cmds)); 242 getCurrentDataSet().setSelected(sel); 243 Main.map.repaint(); 244 245 relations.clear(); 246 newRelations.clear(); 247 oldWays.clear(); 248 } 249 250 private static class NodePos { 251 Node node; 252 int pos; 253 int opositPos; 254 255 NodePos(Node n, int p, int op) { 256 node = n; 257 pos = p; 258 opositPos = op; 259 } 260 74 261 @Override 75 public void actionPerformed(ActionEvent e) { 76 77 // List of selected ways 78 List<Way> ways = new ArrayList<>(); 79 relations.clear(); 80 newRelations.clear(); 81 82 // For every selected way 83 for (OsmPrimitive osm : Main.main.getCurrentDataSet().getSelected()) { 84 if (osm instanceof Way && !osm.isDeleted()) { 85 Way way = (Way) osm; 86 ways.add(way); 87 List<Relation> rels = new ArrayList<>(); 88 for (Relation r : OsmPrimitive.getFilteredList(way 89 .getReferrers(), Relation.class)) { 90 rels.add(r); 91 } 92 relations.put(way, rels); 93 } 94 } 95 96 List<Way> sel = new ArrayList<>(ways); 97 Collection<Command> cmds = new LinkedList<>(); 98 99 // ***** 100 // split 101 // ***** 102 for (Way way : ways) { 103 Set<Node> nodes = new HashSet<>(); 104 for (Way opositWay : ways) { 105 if (way != opositWay) { 106 List<NodePos> nodesPos = new LinkedList<>(); 107 108 int pos = 0; 109 for (Node node : way.getNodes()) { 110 int opositPos = 0; 111 for (Node opositNode : opositWay.getNodes()) { 112 if (node == opositNode) { 113 if (opositWay.isClosed()) { 114 opositPos %= opositWay.getNodesCount() - 1; 115 } 116 nodesPos.add(new NodePos(node, pos, opositPos)); 117 break; 118 } 119 opositPos++; 120 } 121 pos++; 122 } 123 124 NodePos start = null; 125 NodePos end = null; 126 int increment = 0; 127 128 boolean hasFirst = false; 129 for (NodePos node : nodesPos) { 130 if (start == null) { 131 start = node; 132 } else { 133 if (end == null) { 134 if (follows(way, opositWay, start, node, 1)) { 135 end = node; 136 increment = +1; 137 } else if (follows(way, opositWay, start, node, 138 -1)) { 139 end = node; 140 increment = -1; 141 } else { 142 start = node; 143 end = null; 144 } 145 } else { 146 if (follows(way, opositWay, end, node, 147 increment)) { 148 end = node; 149 } else { 150 hasFirst = addNodes(start, end, way, nodes, 151 hasFirst); 152 start = node; 153 end = null; 154 } 155 } 156 } 157 } 158 159 if (start != null && end != null) { 160 hasFirst = addNodes(start, end, way, nodes, hasFirst); 161 start = null; 162 end = null; 163 } 164 } 165 } 166 if (!nodes.isEmpty() && !way.isClosed() || nodes.size() >= 2) { 167 List<List<Node>> wayChunks = SplitWayAction.buildSplitChunks( 168 way, new ArrayList<>(nodes)); 169 SplitWayResult result = splitWay(getEditLayer(), way, wayChunks); 170 171 cmds.add(result.getCommand()); 172 sel.remove(way); 173 sel.add(result.getOriginalWay()); 174 sel.addAll(result.getNewWays()); 175 List<Relation> rels = relations.remove(way); 176 relations.put(result.getOriginalWay(), rels); 177 for (Way w : result.getNewWays()) { 178 relations.put(w, rels); 179 } 180 } 181 } 182 183 // ***** 184 // merge 185 // ***** 186 ways = new ArrayList<>(sel); 187 while (!ways.isEmpty()) { 188 Way way = ways.get(0); 189 List<Way> combine = new ArrayList<>(); 190 combine.add(way); 191 for (Way opositWay : ways) { 192 if (way != opositWay 193 && way.getNodesCount() == opositWay.getNodesCount()) { 194 boolean equals1 = true; 195 for (int i = 0; i < way.getNodesCount(); i++) { 196 if (way.getNode(i) != opositWay.getNode(i)) { 197 equals1 = false; 198 break; 199 } 200 } 201 boolean equals2 = true; 202 for (int i = 0; i < way.getNodesCount(); i++) { 203 if (way.getNode(i) != opositWay.getNode(way 204 .getNodesCount() 205 - i - 1)) { 206 equals2 = false; 207 break; 208 } 209 } 210 if (equals1 || equals2) { 211 combine.add(opositWay); 212 } 213 } 214 } 215 ways.removeAll(combine); 216 if (combine.size() > 1) { 217 sel.removeAll(combine); 218 // combine 219 Pair<Way, List<Command>> combineResult; 220 try { 221 combineResult = combineWaysWorker(combine); 222 } catch (UserCancelException e1) { 223 return; 224 } 225 sel.add(combineResult.a); 226 cmds.addAll(combineResult.b); 227 } 228 } 229 230 for (Relation old : newRelations.keySet()) { 231 cmds.add(new ChangeCommand(old, newRelations.get(old))); 232 } 233 234 List<Way> del = new LinkedList<>(); 235 for (Way w : deletes) { 236 if (!w.isDeleted()) { 237 del.add(w); 238 } 239 } 240 if (!del.isEmpty()) { 241 cmds.add(new DeleteCommand(del)); 242 } 243 244 // Commit 245 Main.main.undoRedo.add(new SequenceCommand( 246 tr("Merge Overlap (combine)"), cmds)); 247 getCurrentDataSet().setSelected(sel); 248 Main.map.repaint(); 249 250 relations.clear(); 251 newRelations.clear(); 252 oldWays.clear(); 253 } 254 255 private class NodePos { 256 Node node; 257 int pos; 258 int opositPos; 259 260 NodePos(Node n, int p, int op) { 261 node = n; 262 pos = p; 263 opositPos = op; 264 } 265 266 @Override 267 public String toString() { 268 return "NodePos: " + pos + ", " + opositPos + ", " + node; 269 } 270 } 271 272 private boolean addNodes(NodePos start, NodePos end, Way way, 273 Set<Node> nodes, boolean hasFirst) { 274 if (way.isClosed() 275 || (start.node != way.getNode(0) && start.node != way 276 .getNode(way.getNodesCount() - 1))) { 277 hasFirst = hasFirst || start.node == way.getNode(0); 278 nodes.add(start.node); 279 } 280 if (way.isClosed() 281 || (end.node != way.getNode(0) && end.node != way.getNode(way 282 .getNodesCount() - 1))) { 283 if (hasFirst && (end.node == way.getNode(way.getNodesCount() - 1))) { 284 nodes.remove(way.getNode(0)); 285 } else { 286 nodes.add(end.node); 287 } 288 } 289 return hasFirst; 290 } 291 292 private boolean follows(Way way1, Way way2, NodePos np1, NodePos np2, 293 int incr) { 294 if (way2.isClosed() && incr == 1 295 && np1.opositPos == way2.getNodesCount() - 2) { 296 return np2.pos == np1.pos + 1 && np2.opositPos == 0; 297 } else if (way2.isClosed() && incr == 1 && np1.opositPos == 0) { 298 return np2.pos == np1.pos && np2.opositPos == 0 299 || np2.pos == np1.pos + 1 && np2.opositPos == 1; 300 } else if (way2.isClosed() && incr == -1 && np1.opositPos == 0) { 301 return np2.pos == np1.pos && np2.opositPos == 0 302 || np2.pos == np1.pos + 1 303 && np2.opositPos == way2.getNodesCount() - 2; 304 } else { 305 return np2.pos == np1.pos + 1 306 && np2.opositPos == np1.opositPos + incr; 307 } 308 } 309 310 /** 311 * Splits a way 312 * 313 * @param layer 314 * @param way 315 * @param wayChunks 316 * @return 317 */ 318 private SplitWayResult splitWay(OsmDataLayer layer, Way way, 319 List<List<Node>> wayChunks) { 320 // build a list of commands, and also a new selection list 321 Collection<Command> commandList = new ArrayList<>(wayChunks 322 .size()); 323 324 Iterator<List<Node>> chunkIt = wayChunks.iterator(); 325 Collection<String> nowarnroles = Main.pref.getCollection( 326 "way.split.roles.nowarn", Arrays.asList(new String[] { "outer", 327 "inner", "forward", "backward" })); 328 329 // First, change the original way 330 Way changedWay = new Way(way); 331 oldWays.put(changedWay, way); 332 changedWay.setNodes(chunkIt.next()); 333 commandList.add(new ChangeCommand(way, changedWay)); 334 335 List<Way> newWays = new ArrayList<>(); 336 // Second, create new ways 337 while (chunkIt.hasNext()) { 338 Way wayToAdd = new Way(); 339 wayToAdd.setKeys(way.getKeys()); 340 newWays.add(wayToAdd); 341 wayToAdd.setNodes(chunkIt.next()); 342 commandList.add(new AddCommand(layer, wayToAdd)); 343 } 344 boolean warnmerole = false; 345 boolean warnme = false; 346 // now copy all relations to new way also 347 348 for (Relation r : getParentRelations(way)) { 349 if (!r.isUsable()) { 350 continue; 351 } 352 Relation c = null; 353 String type = r.get("type"); 354 if (type == null) { 355 type = ""; 356 } 357 358 int i_c = 0, i_r = 0; 359 List<RelationMember> relationMembers = r.getMembers(); 360 for (RelationMember rm : relationMembers) { 361 if (rm.isWay() && rm.getMember() == way) { 362 boolean insert = true; 363 if ("restriction".equals(type)) { 364 /* 365 * this code assumes the restriction is correct. No real 366 * error checking done 367 */ 368 String role = rm.getRole(); 369 if ("from".equals(role) || "to".equals(role)) { 370 OsmPrimitive via = null; 371 for (RelationMember rmv : r.getMembers()) { 372 if ("via".equals(rmv.getRole())) { 373 via = rmv.getMember(); 374 } 375 } 376 List<Node> nodes = new ArrayList<>(); 377 if (via != null) { 378 if (via instanceof Node) { 379 nodes.add((Node) via); 380 } else if (via instanceof Way) { 381 nodes.add(((Way) via).lastNode()); 382 nodes.add(((Way) via).firstNode()); 383 } 384 } 385 Way res = null; 386 for (Node n : nodes) { 387 if (changedWay.isFirstLastNode(n)) { 388 res = way; 389 } 390 } 391 if (res == null) { 392 for (Way wayToAdd : newWays) { 393 for (Node n : nodes) { 394 if (wayToAdd.isFirstLastNode(n)) { 395 res = wayToAdd; 396 } 397 } 398 } 399 if (res != null) { 400 if (c == null) { 401 c = getNew(r); 402 } 403 c.addMember(new RelationMember(role, res)); 404 c.removeMembersFor(way); 405 insert = false; 406 } 407 } else { 408 insert = false; 409 } 410 } else if (!"via".equals(role)) { 411 warnme = true; 412 } 413 } else if (!("route".equals(type)) 414 && !("multipolygon".equals(type))) { 415 warnme = true; 416 } 417 if (c == null) { 418 c = getNew(r); 419 } 420 421 if (insert) { 422 if (rm.hasRole() && !nowarnroles.contains(rm.getRole())) { 423 warnmerole = true; 424 } 425 426 Boolean backwards = null; 427 int k = 1; 428 while (i_r - k >= 0 || i_r + k < relationMembers.size()) { 429 if ((i_r - k >= 0) 430 && relationMembers.get(i_r - k).isWay()) { 431 Way w = relationMembers.get(i_r - k).getWay(); 432 if ((w.lastNode() == way.firstNode()) 433 || w.firstNode() == way.firstNode()) { 434 backwards = false; 435 } else if ((w.firstNode() == way.lastNode()) 436 || w.lastNode() == way.lastNode()) { 437 backwards = true; 438 } 439 break; 440 } 441 if ((i_r + k < relationMembers.size()) 442 && relationMembers.get(i_r + k).isWay()) { 443 Way w = relationMembers.get(i_r + k).getWay(); 444 if ((w.lastNode() == way.firstNode()) 445 || w.firstNode() == way.firstNode()) { 446 backwards = true; 447 } else if ((w.firstNode() == way.lastNode()) 448 || w.lastNode() == way.lastNode()) { 449 backwards = false; 450 } 451 break; 452 } 453 k++; 454 } 455 456 int j = i_c; 457 for (Way wayToAdd : newWays) { 458 RelationMember em = new RelationMember( 459 rm.getRole(), wayToAdd); 460 j++; 461 if ((backwards != null) && backwards) { 462 c.addMember(i_c, em); 463 } else { 464 c.addMember(j, em); 465 } 466 } 467 i_c = j; 468 } 469 } 470 i_c++; 471 i_r++; 472 } 473 474 if (c != null) { 475 // commandList.add(new ChangeCommand(layer, r, c)); 476 newRelations.put(r, c); 477 } 478 } 479 if (warnmerole) { 480 JOptionPane 481 .showMessageDialog( 482 Main.parent, 483 tr("<html>A role based relation membership was copied to all new ways.<br>You should verify this and correct it when necessary.</html>"), 484 tr("Warning"), JOptionPane.WARNING_MESSAGE); 485 } else if (warnme) { 486 JOptionPane 487 .showMessageDialog( 488 Main.parent, 489 tr("<html>A relation membership was copied to all new ways.<br>You should verify this and correct it when necessary.</html>"), 490 tr("Warning"), JOptionPane.WARNING_MESSAGE); 491 } 492 493 return new SplitWayResult( 494 new SequenceCommand(tr("Split way"), commandList), null, 495 changedWay, newWays); 496 } 497 498 /** 499 * @param ways 500 * @return null if ways cannot be combined. Otherwise returns the combined 501 * ways and the commands to combine 502 * @throws UserCancelException 503 */ 504 private Pair<Way, List<Command>> combineWaysWorker(Collection<Way> ways) 505 throws UserCancelException { 506 507 // prepare and clean the list of ways to combine 508 if (ways == null || ways.isEmpty()) 509 return null; 510 ways.remove(null); // just in case - remove all null ways from the 511 // collection 512 513 // remove duplicates, preserving order 514 ways = new LinkedHashSet<>(ways); 515 516 // try to build a new way which includes all the combined ways 517 NodeGraph graph = NodeGraph.createUndirectedGraphFromNodeWays(ways); 518 List<Node> path = graph.buildSpanningPath(); 519 520 // check whether any ways have been reversed in the process 521 // and build the collection of tags used by the ways to combine 522 TagCollection wayTags = TagCollection.unionOfAllPrimitives(ways); 523 524 List<Way> reversedWays = new LinkedList<>(); 525 List<Way> unreversedWays = new LinkedList<>(); 526 for (Way w : ways) { 527 if ((path.indexOf(w.getNode(0)) + 1) == path.lastIndexOf(w 528 .getNode(1))) { 529 unreversedWays.add(w); 530 } else { 531 reversedWays.add(w); 532 } 533 } 534 // reverse path if all ways have been reversed 535 if (unreversedWays.isEmpty()) { 536 Collections.reverse(path); 537 unreversedWays = reversedWays; 538 reversedWays = null; 539 } 540 if ((reversedWays != null) && !reversedWays.isEmpty()) { 541 // filter out ways that have no direction-dependent tags 542 unreversedWays = ReverseWayTagCorrector 543 .irreversibleWays(unreversedWays); 544 reversedWays = ReverseWayTagCorrector 545 .irreversibleWays(reversedWays); 546 // reverse path if there are more reversed than unreversed ways with 547 // direction-dependent tags 548 if (reversedWays.size() > unreversedWays.size()) { 549 Collections.reverse(path); 550 List<Way> tempWays = unreversedWays; 551 unreversedWays = reversedWays; 552 reversedWays = tempWays; 553 } 554 // if there are still reversed ways with direction-dependent tags, 555 // reverse their tags 556 if (!reversedWays.isEmpty()) { 557 List<Way> unreversedTagWays = new ArrayList<>(ways); 558 unreversedTagWays.removeAll(reversedWays); 559 ReverseWayTagCorrector reverseWayTagCorrector = new ReverseWayTagCorrector(); 560 List<Way> reversedTagWays = new ArrayList<>(); 561 Collection<Command> changePropertyCommands = null; 562 for (Way w : reversedWays) { 563 Way wnew = new Way(w); 564 reversedTagWays.add(wnew); 565 changePropertyCommands = reverseWayTagCorrector.execute(w, 566 wnew); 567 } 568 if ((changePropertyCommands != null) 569 && !changePropertyCommands.isEmpty()) { 570 for (Command c : changePropertyCommands) { 571 c.executeCommand(); 572 } 573 } 574 wayTags = TagCollection.unionOfAllPrimitives(reversedTagWays); 575 wayTags.add(TagCollection 576 .unionOfAllPrimitives(unreversedTagWays)); 577 } 578 } 579 580 // create the new way and apply the new node list 581 Way targetWay = getTargetWay(ways); 582 Way modifiedTargetWay = new Way(targetWay); 583 modifiedTargetWay.setNodes(path); 584 585 TagCollection completeWayTags = new TagCollection(wayTags); 586 combineTigerTags(completeWayTags); 587 normalizeTagCollectionBeforeEditing(completeWayTags, ways); 588 TagCollection tagsToEdit = new TagCollection(completeWayTags); 589 completeTagCollectionForEditing(tagsToEdit); 590 591 MyCombinePrimitiveResolverDialog dialog = MyCombinePrimitiveResolverDialog 592 .getInstance(); 593 dialog.getTagConflictResolverModel().populate(tagsToEdit, 594 completeWayTags.getKeysWithMultipleValues()); 595 dialog.setTargetPrimitive(targetWay); 596 Set<Relation> parentRelations = getParentRelations(ways); 597 dialog.getRelationMemberConflictResolverModel().populate( 598 parentRelations, ways, oldWays); 599 dialog.prepareDefaultDecisions(); 600 601 // resolve tag conflicts if necessary 602 if (askForMergeTag(ways) || duplicateParentRelations(ways)) { 603 dialog.setVisible(true); 604 if (dialog.isCanceled()) 605 throw new UserCancelException(); 606 } 607 608 LinkedList<Command> cmds = new LinkedList<>(); 609 deletes.addAll(ways); 610 deletes.remove(targetWay); 611 612 cmds.add(new ChangeCommand(targetWay, modifiedTargetWay)); 613 cmds.addAll(dialog.buildWayResolutionCommands()); 614 dialog.buildRelationCorrespondance(newRelations, oldWays); 615 616 return new Pair<Way, List<Command>>(targetWay, cmds); 617 } 618 619 private static Way getTargetWay(Collection<Way> combinedWays) { 620 // init with an arbitrary way 621 Way targetWay = combinedWays.iterator().next(); 622 623 // look for the first way already existing on 624 // the server 625 for (Way w : combinedWays) { 626 targetWay = w; 627 if (!w.isNew()) { 628 break; 629 } 630 } 631 return targetWay; 632 } 633 634 /** 635 * @return has tag to be merged (=> ask) 636 */ 637 private static boolean askForMergeTag(Collection<Way> ways) { 638 for (Way way : ways) { 639 for (Way oposite : ways) { 640 for (String key : way.getKeys().keySet()) { 641 if (!"source".equals(key) && oposite.hasKey(key) 642 && !way.get(key).equals(oposite.get(key))) { 643 return true; 644 } 645 } 646 } 647 } 648 return false; 649 } 650 651 /** 652 * @return has duplicate parent relation 653 */ 654 private boolean duplicateParentRelations(Collection<Way> ways) { 655 Set<Relation> relations = new HashSet<>(); 656 for (Way w : ways) { 657 List<Relation> rs = getParentRelations(w); 658 for (Relation r : rs) { 659 if (relations.contains(r)) { 660 return true; 661 } 662 } 663 relations.addAll(rs); 664 } 665 return false; 666 } 667 668 /** 669 * Replies the set of referring relations 670 * 671 * @return the set of referring relations 672 */ 673 private List<Relation> getParentRelations(Way way) { 674 List<Relation> rels = new ArrayList<>(); 675 for (Relation r : relations.get(way)) { 676 if (newRelations.containsKey(r)) { 677 rels.add(newRelations.get(r)); 678 } else { 679 rels.add(r); 680 } 681 } 682 return rels; 683 } 684 685 private Relation getNew(Relation r) { 686 return getNew(r, newRelations); 687 } 688 689 public static Relation getNew(Relation r, 690 Map<Relation, Relation> newRelations) { 691 if (newRelations.containsValue(r)) { 692 return r; 693 } else { 694 Relation c = new Relation(r); 695 newRelations.put(r, c); 696 return c; 697 } 698 } 262 public String toString() { 263 return "NodePos: " + pos + ", " + opositPos + ", " + node; 264 } 265 } 266 267 private boolean addNodes(NodePos start, NodePos end, Way way, 268 Set<Node> nodes, boolean hasFirst) { 269 if (way.isClosed() || (start.node != way.getNode(0) && start.node != way.getNode(way.getNodesCount() - 1))) { 270 hasFirst = hasFirst || start.node == way.getNode(0); 271 nodes.add(start.node); 272 } 273 if (way.isClosed() || (end.node != way.getNode(0) && end.node != way.getNode(way.getNodesCount() - 1))) { 274 if (hasFirst && (end.node == way.getNode(way.getNodesCount() - 1))) { 275 nodes.remove(way.getNode(0)); 276 } else { 277 nodes.add(end.node); 278 } 279 } 280 return hasFirst; 281 } 282 283 private boolean follows(Way way1, Way way2, NodePos np1, NodePos np2, 284 int incr) { 285 if (way2.isClosed() && incr == 1 286 && np1.opositPos == way2.getNodesCount() - 2) { 287 return np2.pos == np1.pos + 1 && np2.opositPos == 0; 288 } else if (way2.isClosed() && incr == 1 && np1.opositPos == 0) { 289 return np2.pos == np1.pos && np2.opositPos == 0 290 || np2.pos == np1.pos + 1 && np2.opositPos == 1; 291 } else if (way2.isClosed() && incr == -1 && np1.opositPos == 0) { 292 return np2.pos == np1.pos && np2.opositPos == 0 293 || np2.pos == np1.pos + 1 294 && np2.opositPos == way2.getNodesCount() - 2; 295 } else { 296 return np2.pos == np1.pos + 1 297 && np2.opositPos == np1.opositPos + incr; 298 } 299 } 300 301 /** 302 * Splits a way 303 * 304 * @param layer 305 * @param way 306 * @param wayChunks 307 * @return 308 */ 309 private SplitWayResult splitWay(OsmDataLayer layer, Way way, 310 List<List<Node>> wayChunks) { 311 // build a list of commands, and also a new selection list 312 Collection<Command> commandList = new ArrayList<>(wayChunks 313 .size()); 314 315 Iterator<List<Node>> chunkIt = wayChunks.iterator(); 316 Collection<String> nowarnroles = Main.pref.getCollection( 317 "way.split.roles.nowarn", Arrays.asList(new String[] { "outer", 318 "inner", "forward", "backward" })); 319 320 // First, change the original way 321 Way changedWay = new Way(way); 322 oldWays.put(changedWay, way); 323 changedWay.setNodes(chunkIt.next()); 324 commandList.add(new ChangeCommand(way, changedWay)); 325 326 List<Way> newWays = new ArrayList<>(); 327 // Second, create new ways 328 while (chunkIt.hasNext()) { 329 Way wayToAdd = new Way(); 330 wayToAdd.setKeys(way.getKeys()); 331 newWays.add(wayToAdd); 332 wayToAdd.setNodes(chunkIt.next()); 333 commandList.add(new AddCommand(layer, wayToAdd)); 334 } 335 boolean warnmerole = false; 336 boolean warnme = false; 337 // now copy all relations to new way also 338 339 for (Relation r : getParentRelations(way)) { 340 if (!r.isUsable()) { 341 continue; 342 } 343 Relation c = null; 344 String type = r.get("type"); 345 if (type == null) { 346 type = ""; 347 } 348 349 int ic = 0, ir = 0; 350 List<RelationMember> relationMembers = r.getMembers(); 351 for (RelationMember rm : relationMembers) { 352 if (rm.isWay() && rm.getMember() == way) { 353 boolean insert = true; 354 if ("restriction".equals(type)) { 355 /* 356 * this code assumes the restriction is correct. No real 357 * error checking done 358 */ 359 String role = rm.getRole(); 360 if ("from".equals(role) || "to".equals(role)) { 361 OsmPrimitive via = null; 362 for (RelationMember rmv : r.getMembers()) { 363 if ("via".equals(rmv.getRole())) { 364 via = rmv.getMember(); 365 } 366 } 367 List<Node> nodes = new ArrayList<>(); 368 if (via != null) { 369 if (via instanceof Node) { 370 nodes.add((Node) via); 371 } else if (via instanceof Way) { 372 nodes.add(((Way) via).lastNode()); 373 nodes.add(((Way) via).firstNode()); 374 } 375 } 376 Way res = null; 377 for (Node n : nodes) { 378 if (changedWay.isFirstLastNode(n)) { 379 res = way; 380 } 381 } 382 if (res == null) { 383 for (Way wayToAdd : newWays) { 384 for (Node n : nodes) { 385 if (wayToAdd.isFirstLastNode(n)) { 386 res = wayToAdd; 387 } 388 } 389 } 390 if (res != null) { 391 if (c == null) { 392 c = getNew(r); 393 } 394 c.addMember(new RelationMember(role, res)); 395 c.removeMembersFor(way); 396 insert = false; 397 } 398 } else { 399 insert = false; 400 } 401 } else if (!"via".equals(role)) { 402 warnme = true; 403 } 404 } else if (!("route".equals(type)) 405 && !("multipolygon".equals(type))) { 406 warnme = true; 407 } 408 if (c == null) { 409 c = getNew(r); 410 } 411 412 if (insert) { 413 if (rm.hasRole() && !nowarnroles.contains(rm.getRole())) { 414 warnmerole = true; 415 } 416 417 Boolean backwards = null; 418 int k = 1; 419 while (ir - k >= 0 || ir + k < relationMembers.size()) { 420 if ((ir - k >= 0) 421 && relationMembers.get(ir - k).isWay()) { 422 Way w = relationMembers.get(ir - k).getWay(); 423 if ((w.lastNode() == way.firstNode()) 424 || w.firstNode() == way.firstNode()) { 425 backwards = false; 426 } else if ((w.firstNode() == way.lastNode()) 427 || w.lastNode() == way.lastNode()) { 428 backwards = true; 429 } 430 break; 431 } 432 if ((ir + k < relationMembers.size()) 433 && relationMembers.get(ir + k).isWay()) { 434 Way w = relationMembers.get(ir + k).getWay(); 435 if ((w.lastNode() == way.firstNode()) 436 || w.firstNode() == way.firstNode()) { 437 backwards = true; 438 } else if ((w.firstNode() == way.lastNode()) 439 || w.lastNode() == way.lastNode()) { 440 backwards = false; 441 } 442 break; 443 } 444 k++; 445 } 446 447 int j = ic; 448 for (Way wayToAdd : newWays) { 449 RelationMember em = new RelationMember( 450 rm.getRole(), wayToAdd); 451 j++; 452 if ((backwards != null) && backwards) { 453 c.addMember(ic, em); 454 } else { 455 c.addMember(j, em); 456 } 457 } 458 ic = j; 459 } 460 } 461 ic++; 462 ir++; 463 } 464 465 if (c != null) { 466 // commandList.add(new ChangeCommand(layer, r, c)); 467 newRelations.put(r, c); 468 } 469 } 470 if (warnmerole) { 471 JOptionPane.showMessageDialog( 472 Main.parent, 473 tr("<html>A role based relation membership was copied to all new ways.<br>You should verify this and correct it when necessary.</html>"), 474 tr("Warning"), JOptionPane.WARNING_MESSAGE); 475 } else if (warnme) { 476 JOptionPane.showMessageDialog( 477 Main.parent, 478 tr("<html>A relation membership was copied to all new ways.<br>You should verify this and correct it when necessary.</html>"), 479 tr("Warning"), JOptionPane.WARNING_MESSAGE); 480 } 481 482 return new SplitWayResult( 483 new SequenceCommand(tr("Split way"), commandList), null, 484 changedWay, newWays); 485 } 486 487 /** 488 * @param ways 489 * @return null if ways cannot be combined. Otherwise returns the combined 490 * ways and the commands to combine 491 * @throws UserCancelException 492 */ 493 private Pair<Way, List<Command>> combineWaysWorker(Collection<Way> ways) 494 throws UserCancelException { 495 496 // prepare and clean the list of ways to combine 497 if (ways == null || ways.isEmpty()) 498 return null; 499 ways.remove(null); // just in case - remove all null ways from the 500 // collection 501 502 // remove duplicates, preserving order 503 ways = new LinkedHashSet<>(ways); 504 505 // try to build a new way which includes all the combined ways 506 NodeGraph graph = NodeGraph.createUndirectedGraphFromNodeWays(ways); 507 List<Node> path = graph.buildSpanningPath(); 508 509 // check whether any ways have been reversed in the process 510 // and build the collection of tags used by the ways to combine 511 TagCollection wayTags = TagCollection.unionOfAllPrimitives(ways); 512 513 List<Way> reversedWays = new LinkedList<>(); 514 List<Way> unreversedWays = new LinkedList<>(); 515 for (Way w : ways) { 516 if ((path.indexOf(w.getNode(0)) + 1) == path.lastIndexOf(w 517 .getNode(1))) { 518 unreversedWays.add(w); 519 } else { 520 reversedWays.add(w); 521 } 522 } 523 // reverse path if all ways have been reversed 524 if (unreversedWays.isEmpty()) { 525 Collections.reverse(path); 526 unreversedWays = reversedWays; 527 reversedWays = null; 528 } 529 if ((reversedWays != null) && !reversedWays.isEmpty()) { 530 // filter out ways that have no direction-dependent tags 531 unreversedWays = ReverseWayTagCorrector 532 .irreversibleWays(unreversedWays); 533 reversedWays = ReverseWayTagCorrector 534 .irreversibleWays(reversedWays); 535 // reverse path if there are more reversed than unreversed ways with 536 // direction-dependent tags 537 if (reversedWays.size() > unreversedWays.size()) { 538 Collections.reverse(path); 539 List<Way> tempWays = unreversedWays; 540 unreversedWays = reversedWays; 541 reversedWays = tempWays; 542 } 543 // if there are still reversed ways with direction-dependent tags, 544 // reverse their tags 545 if (!reversedWays.isEmpty()) { 546 List<Way> unreversedTagWays = new ArrayList<>(ways); 547 unreversedTagWays.removeAll(reversedWays); 548 ReverseWayTagCorrector reverseWayTagCorrector = new ReverseWayTagCorrector(); 549 List<Way> reversedTagWays = new ArrayList<>(); 550 Collection<Command> changePropertyCommands = null; 551 for (Way w : reversedWays) { 552 Way wnew = new Way(w); 553 reversedTagWays.add(wnew); 554 changePropertyCommands = reverseWayTagCorrector.execute(w, 555 wnew); 556 } 557 if ((changePropertyCommands != null) 558 && !changePropertyCommands.isEmpty()) { 559 for (Command c : changePropertyCommands) { 560 c.executeCommand(); 561 } 562 } 563 wayTags = TagCollection.unionOfAllPrimitives(reversedTagWays); 564 wayTags.add(TagCollection.unionOfAllPrimitives(unreversedTagWays)); 565 } 566 } 567 568 // create the new way and apply the new node list 569 Way targetWay = getTargetWay(ways); 570 Way modifiedTargetWay = new Way(targetWay); 571 modifiedTargetWay.setNodes(path); 572 573 TagCollection completeWayTags = new TagCollection(wayTags); 574 combineTigerTags(completeWayTags); 575 normalizeTagCollectionBeforeEditing(completeWayTags, ways); 576 TagCollection tagsToEdit = new TagCollection(completeWayTags); 577 completeTagCollectionForEditing(tagsToEdit); 578 579 MyCombinePrimitiveResolverDialog dialog = MyCombinePrimitiveResolverDialog.getInstance(); 580 dialog.getTagConflictResolverModel().populate(tagsToEdit, completeWayTags.getKeysWithMultipleValues()); 581 dialog.setTargetPrimitive(targetWay); 582 Set<Relation> parentRelations = getParentRelations(ways); 583 dialog.getRelationMemberConflictResolverModel().populate(parentRelations, ways, oldWays); 584 dialog.prepareDefaultDecisions(); 585 586 // resolve tag conflicts if necessary 587 if (askForMergeTag(ways) || duplicateParentRelations(ways)) { 588 dialog.setVisible(true); 589 if (dialog.isCanceled()) 590 throw new UserCancelException(); 591 } 592 593 List<Command> cmds = new LinkedList<>(); 594 deletes.addAll(ways); 595 deletes.remove(targetWay); 596 597 cmds.add(new ChangeCommand(targetWay, modifiedTargetWay)); 598 cmds.addAll(dialog.buildWayResolutionCommands()); 599 dialog.buildRelationCorrespondance(newRelations, oldWays); 600 601 return new Pair<>(targetWay, cmds); 602 } 603 604 private static Way getTargetWay(Collection<Way> combinedWays) { 605 // init with an arbitrary way 606 Way targetWay = combinedWays.iterator().next(); 607 608 // look for the first way already existing on the server 609 for (Way w : combinedWays) { 610 targetWay = w; 611 if (!w.isNew()) { 612 break; 613 } 614 } 615 return targetWay; 616 } 617 618 /** 619 * @return has tag to be merged (=> ask) 620 */ 621 private static boolean askForMergeTag(Collection<Way> ways) { 622 for (Way way : ways) { 623 for (Way oposite : ways) { 624 for (String key : way.getKeys().keySet()) { 625 if (!"source".equals(key) && oposite.hasKey(key) 626 && !way.get(key).equals(oposite.get(key))) { 627 return true; 628 } 629 } 630 } 631 } 632 return false; 633 } 634 635 /** 636 * @return has duplicate parent relation 637 */ 638 private boolean duplicateParentRelations(Collection<Way> ways) { 639 Set<Relation> relations = new HashSet<>(); 640 for (Way w : ways) { 641 List<Relation> rs = getParentRelations(w); 642 for (Relation r : rs) { 643 if (relations.contains(r)) { 644 return true; 645 } 646 } 647 relations.addAll(rs); 648 } 649 return false; 650 } 651 652 /** 653 * Replies the set of referring relations 654 * 655 * @return the set of referring relations 656 */ 657 private List<Relation> getParentRelations(Way way) { 658 List<Relation> rels = new ArrayList<>(); 659 for (Relation r : relations.get(way)) { 660 if (newRelations.containsKey(r)) { 661 rels.add(newRelations.get(r)); 662 } else { 663 rels.add(r); 664 } 665 } 666 return rels; 667 } 668 669 private Relation getNew(Relation r) { 670 return getNew(r, newRelations); 671 } 672 673 public static Relation getNew(Relation r, Map<Relation, Relation> newRelations) { 674 if (newRelations.containsValue(r)) { 675 return r; 676 } else { 677 Relation c = new Relation(r); 678 newRelations.put(r, c); 679 return c; 680 } 681 } 699 682 /* 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 HashSet<Relation> ret = new HashSet<>();719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 683 private Way getOld(Way r) { 684 return getOld(r, oldWays); 685 }*/ 686 687 public static Way getOld(Way w, Map<Way, Way> oldWays) { 688 if (oldWays.containsKey(w)) { 689 return oldWays.get(w); 690 } else { 691 return w; 692 } 693 } 694 695 /** 696 * Replies the set of referring relations 697 * 698 * @return the set of referring relations 699 */ 700 private Set<Relation> getParentRelations(Collection<Way> ways) { 701 Set<Relation> ret = new HashSet<>(); 702 for (Way w : ways) { 703 ret.addAll(getParentRelations(w)); 704 } 705 return ret; 706 } 707 708 /** Enable this action only if something is selected */ 709 @Override 710 protected void updateEnabledState() { 711 if (getCurrentDataSet() == null) { 712 setEnabled(false); 713 } else { 714 updateEnabledState(getCurrentDataSet().getSelected()); 715 } 716 } 717 718 /** Enable this action only if something is selected */ 719 @Override 720 protected void updateEnabledState( 721 Collection<? extends OsmPrimitive> selection) { 722 if (selection == null) { 723 setEnabled(false); 724 return; 725 } 726 for (OsmPrimitive primitive : selection) { 727 if (!(primitive instanceof Way) || primitive.isDeleted()) { 728 setEnabled(false); 729 return; 730 } 731 } 732 setEnabled(selection.size() >= 2); 733 } 751 734 } -
applications/editors/josm/plugins/merge-overlap/src/mergeoverlap/MergeOverlapPlugin.java
r29778 r30782 1 1 package mergeoverlap; 2 3 import static org.openstreetmap.josm.tools.I18n.tr;4 2 5 3 import org.openstreetmap.josm.Main; … … 13 11 public class MergeOverlapPlugin extends Plugin { 14 12 15 protected String name; 16 13 /** 14 * Constructs a new {@code MergeOverlapPlugin}. 15 * @param info plugin information 16 */ 17 17 public MergeOverlapPlugin(PluginInformation info) { 18 18 super(info); 19 name = tr("Merge overlap");20 19 MainMenu.add(Main.main.menu.moreToolsMenu, new MergeOverlapAction()); 21 20 } -
applications/editors/josm/plugins/merge-overlap/src/mergeoverlap/hack/MyCombinePrimitiveResolverDialog.java
r30778 r30782 129 129 130 130 public void buildRelationCorrespondance(Map<Relation, Relation> newRelations, Map<Way, Way> oldWays) { 131 131 getRelationMemberConflictResolverModel().buildRelationCorrespondance(targetPrimitive, newRelations, oldWays); 132 132 } 133 133 } -
applications/editors/josm/plugins/merge-overlap/src/mergeoverlap/hack/MyRelationMemberConflictResolverModel.java
r30766 r30782 53 53 @Override 54 54 public void populate(Collection<Relation> relations, Collection<? extends OsmPrimitive> memberPrimitives) { 55 throw new UnsupportedOperationException("Use populate(Collection<Relation>, Collection<? extends OsmPrimitive>, Map<Way, Way>) instead"); 55 throw new UnsupportedOperationException( 56 "Use populate(Collection<Relation>, Collection<? extends OsmPrimitive>, Map<Way, Way>) instead"); 56 57 } 57 58 … … 78 79 @Override 79 80 protected Command buildResolveCommand(Relation relation, OsmPrimitive newPrimitive) { 80 throw new UnsupportedOperationException("Use buildResolveCorrespondance(Relation, OsmPrimitive, Map<Relation, Relation>, Map<Way, Way>) instead"); 81 throw new UnsupportedOperationException( 82 "Use buildResolveCorrespondance(Relation, OsmPrimitive, Map<Relation, Relation>, Map<Way, Way>) instead"); 81 83 } 82 84 83 protected void buildResolveCorrespondance(Relation relation, OsmPrimitive newPrimitive, Map<Relation, Relation> newRelations, Map<Way, Way> oldWays) { 85 protected void buildResolveCorrespondance( 86 Relation relation, OsmPrimitive newPrimitive, Map<Relation, Relation> newRelations, Map<Way, Way> oldWays) { 84 87 85 86 88 List<RelationMember> relationsMembers = relation.getMembers(); 89 Relation modifiedRelation = MergeOverlapAction.getNew(relation, newRelations); 87 90 modifiedRelation.setMembers(null); 88 91 for (int i=0; i < relationsMembers.size(); i++) { 89 92 RelationMember rm = relationsMembers.get(i); 90 93 RelationMemberConflictDecision decision = getDecision(relation, i); 91 94 if (decision == null) { … … 94 97 switch(decision.getDecision()) { 95 98 case KEEP: 96 97 modifiedRelation.addMember(new RelationMember(decision.getRole(), MergeOverlapAction.getOld((Way)newPrimitive, oldWays)));98 }99 100 101 99 if (newPrimitive instanceof Way) { 100 modifiedRelation.addMember(new RelationMember(decision.getRole(), 101 MergeOverlapAction.getOld((Way)newPrimitive, oldWays))); 102 } else { 103 modifiedRelation.addMember(new RelationMember(decision.getRole(), newPrimitive)); 104 } 102 105 break; 103 106 case REMOVE: … … 114 117 @Override 115 118 public List<Command> buildResolutionCommands(OsmPrimitive newPrimitive) { 116 throw new UnsupportedOperationException("Use buildRelationCorrespondance(OsmPrimitive, Map<Relation, Relation>, Map<Way, Way>) instead"); 119 throw new UnsupportedOperationException( 120 "Use buildRelationCorrespondance(OsmPrimitive, Map<Relation, Relation>, Map<Way, Way>) instead"); 117 121 } 118 122 … … 125 129 public void buildRelationCorrespondance(OsmPrimitive newPrimitive, Map<Relation, Relation> newRelations, Map<Way, Way> oldWays) { 126 130 for (Relation relation : relations) { 127 131 buildResolveCorrespondance(relation, newPrimitive, newRelations, oldWays); 128 132 } 129 133 }
Note:
See TracChangeset
for help on using the changeset viewer.