Changeset 27997 in osm
- Timestamp:
- 2012-03-06T03:29:53+01:00 (13 years ago)
- Location:
- applications/editors/josm/plugins
- Files:
-
- 1 added
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
applications/editors/josm/plugins/conflation/src/org/openstreetmap/josm/plugins/conflation/ConflationToggleDialog.java
r27971 r27997 32 32 import static org.openstreetmap.josm.tools.I18n.tr; 33 33 import org.openstreetmap.josm.tools.Shortcut; 34 import utilsplugin2.dumbutils.ReplaceGeometry Action;34 import utilsplugin2.dumbutils.ReplaceGeometryUtils; 35 35 36 36 public class ConflationToggleDialog extends ToggleDialog … … 206 206 @Override 207 207 public void actionPerformed(ActionEvent e) { 208 ReplaceGeometryAction rg = new ReplaceGeometryAction();209 208 ConflationCandidate c = conflationLayer.getSelectedCandidate(); 210 if ( rg.replace(c.getReferenceObject(), c.getSubjectObject())) {209 if (ReplaceGeometryUtils.replace(c.getReferenceObject(), c.getSubjectObject())) { 211 210 candidates.remove(c); 212 211 } -
applications/editors/josm/plugins/utilsplugin2/src/utilsplugin2/dumbutils/ReplaceGeometryAction.java
r27973 r27997 3 3 import java.awt.event.ActionEvent; 4 4 import java.awt.event.KeyEvent; 5 import java. awt.geom.Area;6 import java. awt.geom.Point2D;7 import java.util. *;5 import java.util.ArrayList; 6 import java.util.Collection; 7 import java.util.List; 8 8 import javax.swing.JOptionPane; 9 9 import org.openstreetmap.josm.Main; 10 10 import org.openstreetmap.josm.actions.JosmAction; 11 import org.openstreetmap.josm.actions.MergeNodesAction;12 import org.openstreetmap.josm.command.*;13 import org.openstreetmap.josm.data.osm.Node;14 11 import org.openstreetmap.josm.data.osm.OsmPrimitive; 15 import org.openstreetmap.josm.data.osm.Relation;16 import org.openstreetmap.josm.data.osm.RelationMember;17 import org.openstreetmap.josm.data.osm.RelationToChildReference;18 import org.openstreetmap.josm.data.osm.TagCollection;19 import org.openstreetmap.josm.data.osm.Way;20 import org.openstreetmap.josm.gui.DefaultNameFormatter;21 import org.openstreetmap.josm.gui.conflict.tags.CombinePrimitiveResolverDialog;22 import org.openstreetmap.josm.gui.conflict.tags.TagConflictResolutionUtil;23 12 import static org.openstreetmap.josm.tools.I18n.tr; 24 13 import org.openstreetmap.josm.tools.Shortcut; … … 57 46 58 47 try { 59 replaceWithNew(firstObject, secondObject);48 ReplaceGeometryUtils.replaceWithNew(firstObject, secondObject); 60 49 } catch (IllegalArgumentException ex) { 61 50 JOptionPane.showMessageDialog(Main.parent, … … 63 52 } 64 53 65 }66 67 /**68 * Replace new or uploaded object with new object69 *70 * @param firstObject71 * @param secondObject72 * @return73 */74 public boolean replaceWithNew(OsmPrimitive firstObject, OsmPrimitive secondObject) {75 if (firstObject instanceof Node && secondObject instanceof Node) {76 return replaceNodeWithNew((Node) firstObject, (Node) secondObject);77 } else if (firstObject instanceof Way && secondObject instanceof Way) {78 return replaceWayWithNew(Arrays.asList((Way) firstObject, (Way) secondObject));79 } else if (firstObject instanceof Node) {80 return upgradeNode((Node) firstObject, secondObject);81 } else if (secondObject instanceof Node) {82 return upgradeNode((Node) secondObject, firstObject);83 } else {84 throw new IllegalArgumentException(tr("This tool can only replace a node, upgrade a node to a way or a multipolygon, or replace a way with a way."));85 }86 }87 88 /**89 * Replace subjectObject geometry with referenceObject geometry and merge tags90 * and relation memberships.91 *92 * @param subjectObject93 * @param referenceSubject94 * @return95 */96 public boolean replace(OsmPrimitive subjectObject, OsmPrimitive referenceSubject) {97 if (subjectObject instanceof Node && referenceSubject instanceof Node) {98 return replaceNode((Node) subjectObject, (Node) referenceSubject);99 } else if (subjectObject instanceof Way && referenceSubject instanceof Way) {100 return replaceWay((Way) subjectObject, (Way) referenceSubject);101 } else if (subjectObject instanceof Node) {102 return upgradeNode((Node) subjectObject, referenceSubject);103 } else if (referenceSubject instanceof Node) {104 // TODO: fix this illogical reversal?105 return upgradeNode((Node) referenceSubject, subjectObject);106 } else {107 throw new IllegalArgumentException(tr("This tool can only replace a node, upgrade a node to a way or a multipolygon, or replace a way with a way."));108 }109 }110 111 /**112 * Replace a new or uploaded node with a new node113 * @param firstNode114 * @param secondNode115 * @return116 */117 public boolean replaceNodeWithNew(Node firstNode, Node secondNode) {118 if (firstNode.isNew() && !secondNode.isNew())119 return replaceNode(secondNode, firstNode);120 else if (!firstNode.isNew() && secondNode.isNew())121 return replaceNode(firstNode, secondNode);122 else123 // both nodes are new OR uploaded, act like MergeNodes, moving first124 // node to second125 return replaceNode(firstNode, secondNode);126 }127 128 /**129 * Replace a node with another node (similar to MergeNodesAction)130 *131 * @param subjectNode132 * @param referenceNode133 * @return134 */135 public boolean replaceNode(Node subjectNode, Node referenceNode) {136 if (!OsmPrimitive.getFilteredList(subjectNode.getReferrers(), Way.class).isEmpty()) {137 JOptionPane.showMessageDialog(Main.parent, tr("Node belongs to way(s), cannot replace."),138 TITLE, JOptionPane.INFORMATION_MESSAGE);139 return false;140 }141 // FIXME: handle different layers142 List<Command> commands = new ArrayList<Command>();143 commands.add(MergeNodesAction.mergeNodes(Main.main.getEditLayer(), Arrays.asList(subjectNode, referenceNode), referenceNode));144 145 Main.main.undoRedo.add(new SequenceCommand(146 tr("Replace geometry for node {0}", subjectNode.getDisplayName(DefaultNameFormatter.getInstance())),147 commands));148 return true;149 }150 151 /**152 * Upgrade a node to a way or multipolygon153 *154 * @param subjectNode node to be replaced155 * @param referenceObject object with greater spatial quality156 */157 public boolean upgradeNode(Node subjectNode, OsmPrimitive referenceObject) {158 if (!OsmPrimitive.getFilteredList(subjectNode.getReferrers(), Way.class).isEmpty()) {159 JOptionPane.showMessageDialog(Main.parent, tr("Node belongs to way(s), cannot replace."),160 TITLE, JOptionPane.INFORMATION_MESSAGE);161 return false;162 }163 164 if (referenceObject instanceof Relation && !((Relation) referenceObject).isMultipolygon()) {165 JOptionPane.showMessageDialog(Main.parent, tr("Relation is not a multipolygon, cannot be used as a replacement."),166 TITLE, JOptionPane.INFORMATION_MESSAGE);167 return false;168 }169 170 Node nodeToReplace = null;171 // see if we need to replace a node in the replacement way to preserve connection in history172 if (!subjectNode.isNew()) {173 // Prepare a list of nodes that are not important174 Collection<Node> nodePool = new HashSet<Node>();175 if (referenceObject instanceof Way) {176 nodePool.addAll(getUnimportantNodes((Way) referenceObject));177 } else if (referenceObject instanceof Relation) {178 for (RelationMember member : ((Relation) referenceObject).getMembers()) {179 if ((member.getRole().equals("outer") || member.getRole().equals("inner"))180 && member.isWay()) {181 // TODO: could consider more nodes, such as nodes that are members of other ways,182 // just need to replace occurences in all referrers183 nodePool.addAll(getUnimportantNodes(member.getWay()));184 }185 }186 } else {187 assert false;188 }189 nodeToReplace = findNearestNode(subjectNode, nodePool);190 }191 192 List<Command> commands = new ArrayList<Command>();193 AbstractMap<String, String> nodeTags = (AbstractMap<String, String>) subjectNode.getKeys();194 195 // merge tags196 Collection<Command> tagResolutionCommands = getTagConflictResolutionCommands(subjectNode, referenceObject);197 if (tagResolutionCommands == null) {198 // user canceled tag merge dialog199 return false;200 }201 commands.addAll(tagResolutionCommands);202 203 // replace sacrificial node in way with node that is being upgraded204 if (nodeToReplace != null) {205 // node should only have one parent, a way206 Way parentWay = (Way) nodeToReplace.getReferrers().get(0);207 List<Node> wayNodes = parentWay.getNodes();208 int idx = wayNodes.indexOf(nodeToReplace);209 wayNodes.set(idx, subjectNode);210 if (idx == 0 && parentWay.isClosed()) {211 // node is at start/end of way212 wayNodes.set(wayNodes.size() - 1, subjectNode);213 }214 commands.add(new ChangeNodesCommand(parentWay, wayNodes));215 commands.add(new MoveCommand(subjectNode, nodeToReplace.getCoor()));216 commands.add(new DeleteCommand(nodeToReplace));217 218 // delete tags from node219 if (!nodeTags.isEmpty()) {220 for (String key : nodeTags.keySet()) {221 commands.add(new ChangePropertyCommand(subjectNode, key, null));222 }223 224 }225 } else {226 // no node to replace, so just delete the original node227 commands.add(new DeleteCommand(subjectNode));228 }229 230 getCurrentDataSet().setSelected(referenceObject);231 232 Main.main.undoRedo.add(new SequenceCommand(233 tr("Replace geometry for node {0}", subjectNode.getDisplayName(DefaultNameFormatter.getInstance())),234 commands));235 return true;236 }237 238 public boolean replaceWayWithNew(List<Way> selection) {239 // determine which way will be replaced and which will provide the geometry240 boolean overrideNewCheck = false;241 int idxNew = selection.get(0).isNew() ? 0 : 1;242 if( selection.get(1-idxNew).isNew() ) {243 // if both are new, select the one with all the DB nodes244 boolean areNewNodes = false;245 for (Node n : selection.get(0).getNodes()) {246 if (n.isNew()) {247 areNewNodes = true;248 }249 }250 idxNew = areNewNodes ? 0 : 1;251 overrideNewCheck = true;252 for (Node n : selection.get(1 - idxNew).getNodes()) {253 if (n.isNew()) {254 overrideNewCheck = false;255 }256 }257 }258 Way referenceWay = selection.get(idxNew);259 Way subjectWay = selection.get(1 - idxNew);260 261 if( !overrideNewCheck && (subjectWay.isNew() || !referenceWay.isNew()) ) {262 JOptionPane.showMessageDialog(Main.parent,263 tr("Please select one way that exists in the database and one new way with correct geometry."),264 TITLE, JOptionPane.WARNING_MESSAGE);265 return false;266 }267 return replaceWay(subjectWay, referenceWay);268 }269 270 public static boolean replaceWay(Way subjectWay, Way referenceWay) {271 272 Area a = getCurrentDataSet().getDataSourceArea();273 if (!isInArea(subjectWay, a) || !isInArea(referenceWay, a)) {274 JOptionPane.showMessageDialog(Main.parent,275 tr("The ways must be entirely within the downloaded area."),276 TITLE, JOptionPane.WARNING_MESSAGE);277 return false;278 }279 280 if (hasImportantNode(referenceWay, subjectWay)) {281 JOptionPane.showMessageDialog(Main.parent,282 tr("The way to be replaced cannot have any nodes with properties or relation memberships unless they belong to both ways."),283 TITLE, JOptionPane.WARNING_MESSAGE);284 return false;285 }286 287 List<Command> commands = new ArrayList<Command>();288 289 // merge tags290 Collection<Command> tagResolutionCommands = getTagConflictResolutionCommands(referenceWay, subjectWay);291 if (tagResolutionCommands == null) {292 // user canceled tag merge dialog293 return false;294 }295 commands.addAll(tagResolutionCommands);296 297 // Prepare a list of nodes that are not used anywhere except in the way298 Collection<Node> nodePool = getUnimportantNodes(subjectWay);299 300 // And the same for geometry, list nodes that can be freely deleted301 Set<Node> geometryPool = new HashSet<Node>();302 for( Node node : referenceWay.getNodes() ) {303 List<OsmPrimitive> referrers = node.getReferrers();304 if( node.isNew() && !node.isDeleted() && referrers.size() == 1305 && referrers.get(0).equals(referenceWay) && !subjectWay.containsNode(node)306 && !hasInterestingKey(node))307 geometryPool.add(node);308 }309 310 // Find new nodes that are closest to the old ones, remove matching old ones from the pool311 Map<Node, Node> nodeAssoc = new HashMap<Node, Node>();312 for( Node n : geometryPool ) {313 Node nearest = findNearestNode(n, nodePool);314 if( nearest != null ) {315 nodeAssoc.put(n, nearest);316 nodePool.remove(nearest);317 }318 }319 320 // Now that we have replacement list, move all unused new nodes to nodePool (and delete them afterwards)321 for( Node n : geometryPool )322 if( nodeAssoc.containsKey(n) )323 nodePool.add(n);324 325 // And prepare a list of nodes with all the replacements326 List<Node> geometryNodes = referenceWay.getNodes();327 for( int i = 0; i < geometryNodes.size(); i++ )328 if( nodeAssoc.containsKey(geometryNodes.get(i)) )329 geometryNodes.set(i, nodeAssoc.get(geometryNodes.get(i)));330 331 // Now do the replacement332 commands.add(new ChangeNodesCommand(subjectWay, geometryNodes));333 334 // Move old nodes to new positions335 for( Node node : nodeAssoc.keySet() )336 commands.add(new MoveCommand(nodeAssoc.get(node), node.getCoor()));337 338 // Remove geometry way from selection339 getCurrentDataSet().clearSelection(referenceWay);340 341 // And delete old geometry way342 commands.add(new DeleteCommand(referenceWay));343 344 // Delete nodes that are not used anymore345 if( !nodePool.isEmpty() )346 commands.add(new DeleteCommand(nodePool));347 348 // Two items in undo stack: change original way and delete geometry way349 Main.main.undoRedo.add(new SequenceCommand(350 tr("Replace geometry for way {0}", subjectWay.getDisplayName(DefaultNameFormatter.getInstance())),351 commands));352 return true;353 }354 355 /**356 * Create a list of nodes that are not used anywhere except in the way.357 *358 * @param way359 * @return360 */361 protected static Collection<Node> getUnimportantNodes(Way way) {362 Set<Node> nodePool = new HashSet<Node>();363 for (Node n : way.getNodes()) {364 List<OsmPrimitive> referrers = n.getReferrers();365 if (!n.isDeleted() && referrers.size() == 1 && referrers.get(0).equals(way)366 && !hasInterestingKey(n)) {367 nodePool.add(n);368 }369 }370 return nodePool;371 }372 373 /**374 * Checks if a way has at least one important node (e.g. interesting tag,375 * role membership), and thus cannot be safely modified.376 *377 * @param way378 * @return379 */380 protected static boolean hasImportantNode(Way geometry, Way way) {381 for (Node n : way.getNodes()) {382 // if original and replacement way share a node, it's safe to replace383 if (geometry.containsNode(n)) {384 continue;385 }386 //TODO: if way is connected to other ways, warn or disallow?387 for (OsmPrimitive o : n.getReferrers()) {388 if (o instanceof Relation) {389 return true;390 }391 }392 if (hasInterestingKey(n)) {393 return true;394 }395 }396 return false;397 }398 399 protected static boolean hasInterestingKey(OsmPrimitive object) {400 for (String key : object.getKeys().keySet()) {401 if (!OsmPrimitive.isUninterestingKey(key)) {402 return true;403 }404 }405 return false;406 }407 408 protected static boolean isInArea(Node node, Area area) {409 if (node.isNewOrUndeleted() || area == null || area.contains(node.getCoor())) {410 return true;411 }412 return false;413 }414 415 protected static boolean isInArea(Way way, Area area) {416 if (area == null) {417 return true;418 }419 420 for (Node n : way.getNodes()) {421 if (!isInArea(n, area)) {422 return false;423 }424 }425 426 return true;427 }428 429 /**430 * Merge tags from source to target object, showing resolution dialog if431 * needed.432 *433 * @param source object tags are merged from434 * @param target object tags are merged to435 * @return436 */437 protected static List<Command> getTagConflictResolutionCommands(OsmPrimitive source, OsmPrimitive target) {438 Collection<OsmPrimitive> primitives = Arrays.asList(source, target);439 440 Set<RelationToChildReference> relationToNodeReferences = RelationToChildReference.getRelationToChildReferences(primitives);441 442 // build the tag collection443 TagCollection tags = TagCollection.unionOfAllPrimitives(primitives);444 TagConflictResolutionUtil.combineTigerTags(tags);445 TagConflictResolutionUtil.normalizeTagCollectionBeforeEditing(tags, primitives);446 TagCollection tagsToEdit = new TagCollection(tags);447 TagConflictResolutionUtil.completeTagCollectionForEditing(tagsToEdit);448 449 // launch a conflict resolution dialog, if necessary450 CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance();451 dialog.getTagConflictResolverModel().populate(tagsToEdit, tags.getKeysWithMultipleValues());452 dialog.getRelationMemberConflictResolverModel().populate(relationToNodeReferences);453 dialog.setTargetPrimitive(target);454 dialog.prepareDefaultDecisions();455 456 // conflict resolution is necessary if there are conflicts in the merged tags457 // or if at least one of the merged nodes is referred to by a relation458 if (!tags.isApplicableToPrimitive() || relationToNodeReferences.size() > 1) {459 dialog.setVisible(true);460 if (dialog.isCanceled()) {461 return null;462 }463 }464 return dialog.buildResolutionCommands();465 }466 467 468 /**469 * Find node from the collection which is nearest to <tt>node</tt>. Max distance is taken in consideration.470 * @return null if there is no such node.471 */472 protected static Node findNearestNode( Node node, Collection<Node> nodes ) {473 if( nodes.contains(node) )474 return node;475 476 Node nearest = null;477 // TODO: use meters instead of degrees, but do it fast478 double distance = Double.parseDouble(Main.pref.get("utilsplugin2.replace-geometry.max-distance", "1"));479 Point2D coor = node.getCoor();480 481 for( Node n : nodes ) {482 double d = n.getCoor().distance(coor);483 if( d < distance ) {484 distance = d;485 nearest = n;486 }487 }488 return nearest;489 54 } 490 55
Note:
See TracChangeset
for help on using the changeset viewer.