Changeset 2095 in josm for trunk/src/org/openstreetmap/josm/actions
- Timestamp:
- 2009-09-12T06:21:30+02:00 (16 years ago)
- Location:
- trunk/src/org/openstreetmap/josm/actions
- Files:
-
- 3 edited
-
CombineWayAction.java (modified) (3 diffs)
-
MergeNodesAction.java (modified) (6 diffs)
-
mapmode/SelectAction.java (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/actions/CombineWayAction.java
r2079 r2095 33 33 import org.openstreetmap.josm.data.osm.Way; 34 34 import org.openstreetmap.josm.gui.ExtendedDialog; 35 import org.openstreetmap.josm.gui.conflict.tags.Combine WaysConflictResolverDialog;35 import org.openstreetmap.josm.gui.conflict.tags.CombinePrimitiveResolverDialog; 36 36 import org.openstreetmap.josm.tools.Pair; 37 37 import org.openstreetmap.josm.tools.Shortcut; … … 166 166 completeTagCollectionWithMissingTags(completeWayTags, ways); 167 167 168 Combine WaysConflictResolverDialog dialog = CombineWaysConflictResolverDialog.getInstance();168 CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance(); 169 169 dialog.getTagConflictResolverModel().populate(completeWayTags); 170 dialog.setTarget Way(targetWay);170 dialog.setTargetPrimitive(targetWay); 171 171 dialog.getRelationMemberConflictResolverModel().populate( 172 172 referringRelations.getRelations(), … … 191 191 cmds.add(new DeleteCommand(deletedWays)); 192 192 cmds.add(new ChangeCommand(targetWay, modifiedTargetWay)); 193 cmds.addAll(dialog.buildResolutionCommands( targetWay));193 cmds.addAll(dialog.buildResolutionCommands()); 194 194 final SequenceCommand sequenceCommand = new SequenceCommand(tr("Combine {0} ways", ways.size()), cmds); 195 195 -
trunk/src/org/openstreetmap/josm/actions/MergeNodesAction.java
r2070 r2095 4 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 5 6 import java.awt.GridBagLayout;7 6 import java.awt.event.ActionEvent; 8 7 import java.awt.event.KeyEvent; 9 8 import java.util.ArrayList; 10 9 import java.util.Collection; 11 import java.util.HashMap;12 10 import java.util.HashSet; 13 11 import java.util.LinkedList; 14 import java.util.List;15 import java.util.Map;16 12 import java.util.Set; 17 import java.util.TreeMap; 18 import java.util.TreeSet; 19 import java.util.Map.Entry; 20 21 import javax.swing.Box; 22 import javax.swing.JComboBox; 23 import javax.swing.JLabel; 13 24 14 import javax.swing.JOptionPane; 25 import javax.swing.JPanel;26 15 27 16 import org.openstreetmap.josm.Main; … … 30 19 import org.openstreetmap.josm.command.DeleteCommand; 31 20 import org.openstreetmap.josm.command.SequenceCommand; 21 import org.openstreetmap.josm.data.osm.BackreferencedDataSet; 32 22 import org.openstreetmap.josm.data.osm.Node; 33 23 import org.openstreetmap.josm.data.osm.OsmPrimitive; 34 import org.openstreetmap.josm.data.osm.Relation; 35 import org.openstreetmap.josm.data.osm.RelationMember; 36 import org.openstreetmap.josm.data.osm.TigerUtils; 24 import org.openstreetmap.josm.data.osm.Tag; 25 import org.openstreetmap.josm.data.osm.TagCollection; 37 26 import org.openstreetmap.josm.data.osm.Way; 38 import org.openstreetmap.josm.data.osm.visitor.CollectBackReferencesVisitor; 39 import org.openstreetmap.josm.gui.ExtendedDialog; 40 import org.openstreetmap.josm.tools.GBC; 41 import org.openstreetmap.josm.tools.Pair; 27 import org.openstreetmap.josm.data.osm.BackreferencedDataSet.RelationToChildReference; 28 import org.openstreetmap.josm.gui.conflict.tags.CombinePrimitiveResolverDialog; 29 import org.openstreetmap.josm.gui.layer.OsmDataLayer; 42 30 import org.openstreetmap.josm.tools.Shortcut; 43 31 44 32 45 33 /** 46 * Merge two or more nodes into one node. 47 * (based on Combine ways) 48 * 49 * @author Matthew Newton 50 * 34 * Merges a collection of nodes into one node. 35 * 51 36 */ 52 37 public class MergeNodesAction extends JosmAction { … … 60 45 if (!isEnabled()) 61 46 return; 62 63 47 Collection<OsmPrimitive> selection = getCurrentDataSet().getSelected(); 64 LinkedList<Node> selectedNodes = new LinkedList<Node>(); 65 66 // the selection check should stop this procedure starting if 67 // nothing but node are selected - otherwise we don't care 68 // anyway as long as we have at least two nodes 69 for (OsmPrimitive osm : selection) 70 if (osm instanceof Node) { 71 selectedNodes.add((Node)osm); 72 } 73 48 Set<Node> selectedNodes = OsmPrimitive.getFilteredSet(selection, Node.class); 74 49 if (selectedNodes.size() < 2) { 75 50 JOptionPane.showMessageDialog( … … 82 57 } 83 58 59 60 Node targetNode = selectTargetNode(selectedNodes); 61 Command cmd = mergeNodes(Main.main.getEditLayer(), selectedNodes, targetNode); 62 if (cmd != null) { 63 Main.main.undoRedo.add(cmd); 64 Main.main.getEditLayer().data.setSelected(targetNode); 65 } 66 } 67 68 protected void completeTagCollectionWithMissingTags(TagCollection tc, Collection<Node> mergedNodes) { 69 for (String key: tc.getKeys()) { 70 // make sure the empty value is in the tag set if a tag is not present 71 // on all merged nodes 72 // 73 for (Node n: mergedNodes) { 74 if (n.get(key) == null) { 75 tc.add(new Tag(key)); // add a tag with key and empty value 76 } 77 } 78 } 79 // remove irrelevant tags 80 // 81 tc.removeByKey("created_by"); 82 } 83 84 protected void completeTagCollectionForEditing(TagCollection tc) { 85 for (String key: tc.getKeys()) { 86 // make sure the empty value is in the tag set such that we can delete the tag 87 // in the conflict dialog if necessary 88 // 89 tc.add(new Tag(key,"")); 90 } 91 } 92 93 /** 94 * Selects a node out of a collection of candidate nodes. The selected 95 * node will become the target node the remaining nodes are merged to. 96 * 97 * @param candidates the collection of candidate nodes 98 * @return the selected target node 99 */ 100 public Node selectTargetNode(Collection<Node> candidates) { 84 101 // Find which node to merge into (i.e. which one will be left) 85 102 // - this should be combined from two things: … … 93 110 // that the user doesn't know which node will be chosen (so 94 111 // (2) is not implemented yet.) :-( 95 Node useNode = null;96 for (Node n: selectedNodes) {112 Node targetNode = null; 113 for (Node n: candidates) { 97 114 if (n.getId() > 0) { 98 useNode = n;115 targetNode = n; 99 116 break; 100 117 } 101 118 } 102 if (useNode == null) { 103 useNode = selectedNodes.iterator().next(); 104 } 105 106 mergeNodes(selectedNodes, useNode); 107 } 108 109 /** 110 * really do the merging - returns the node that is left 111 */ 112 public Node mergeNodes(LinkedList<Node> allNodes, Node dest) { 113 Node newNode = new Node(dest); 114 115 // Check whether all ways have identical relationship membership. More 116 // specifically: If one of the selected ways is a member of relation X 117 // in role Y, then all selected ways must be members of X in role Y. 118 119 // FIXME: In a later revision, we should display some sort of conflict 120 // dialog like we do for tags, to let the user choose which relations 121 // should be kept. 122 123 // Step 1, iterate over all relations and figure out which of our 124 // selected ways are members of a relation. 125 HashMap<Pair<Relation,String>, HashSet<Node>> backlinks = 126 new HashMap<Pair<Relation,String>, HashSet<Node>>(); 127 HashSet<Relation> relationsUsingNodes = new HashSet<Relation>(); 128 for (Relation r : getCurrentDataSet().relations) { 129 if (r.isDeleted() || r.incomplete) { 130 continue; 131 } 132 for (RelationMember rm : r.getMembers()) { 133 if (rm.isNode()) { 134 for (Node n : allNodes) { 135 if (rm.getMember() == n) { 136 Pair<Relation,String> pair = new Pair<Relation,String>(r, rm.getRole()); 137 HashSet<Node> nodelinks = new HashSet<Node>(); 138 if (backlinks.containsKey(pair)) { 139 nodelinks = backlinks.get(pair); 140 } else { 141 nodelinks = new HashSet<Node>(); 142 backlinks.put(pair, nodelinks); 143 } 144 nodelinks.add(n); 145 146 // this is just a cache for later use 147 relationsUsingNodes.add(r); 148 } 149 } 119 if (targetNode == null) { 120 // an arbitrary node 121 targetNode = candidates.iterator().next(); 122 } 123 return targetNode; 124 } 125 126 127 /** 128 * Merges the nodes in <code>node</code> onto one of the nodes. Uses the dataset 129 * managed by <code>layer</code> as reference. 130 * 131 * @param layer the reference data layer. Must not be null. 132 * @param nodes the collection of nodes. Ignored if null. 133 * @param targetNode the target node the collection of nodes is merged to. Must not be null. 134 * @throws IllegalArgumentException thrown if layer is null 135 * @throws IllegalArgumentException thrown if targetNode is null 136 * 137 */ 138 public Command mergeNodes(OsmDataLayer layer, Collection<Node> nodes, Node targetNode) throws IllegalArgumentException{ 139 if (layer == null) 140 throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "nodes")); 141 if (targetNode == null) 142 throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "targetNode")); 143 144 if (nodes == null) 145 return null; 146 nodes.remove(null); // just in case 147 BackreferencedDataSet backreferences = new BackreferencedDataSet(layer.data); 148 backreferences.build(); 149 return mergeNodes(layer,backreferences, nodes, targetNode); 150 } 151 152 /** 153 * Merges the nodes in <code>node</code> onto one of the nodes. Uses the dataset 154 * managed by <code>layer</code> as reference. <code>backreferences</code> is precomputed 155 * collection of all parent/child references in the dataset. 156 * 157 * @param layer layer the reference data layer. Must not be null. 158 * @param backreferences if null, backreferneces are first computed from layer.data; otherwise 159 * backreferences.getSource() == layer.data must hold 160 * @param nodes the collection of nodes. Ignored if null. 161 * @param targetNode the target node the collection of nodes is merged to. Must not be null. 162 * @throw IllegalArgumentException thrown if layer is null 163 * @throw IllegalArgumentException thrown if backreferences.getSource() != layer.data 164 */ 165 public Command mergeNodes(OsmDataLayer layer, BackreferencedDataSet backreferences, Collection<Node> nodes, Node targetNode) { 166 if (layer == null) 167 throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "nodes")); 168 if (targetNode == null) 169 throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "targetNode")); 170 if (nodes == null) 171 return null; 172 if (backreferences == null) { 173 backreferences = new BackreferencedDataSet(layer.data); 174 backreferences.build(); 175 } 176 177 Set<RelationToChildReference> relationToNodeReferences = backreferences.getRelationToChildReferences(nodes); 178 179 // build the tag collection 180 // 181 TagCollection nodeTags = TagCollection.unionOfAllPrimitives(nodes); 182 completeTagCollectionWithMissingTags(nodeTags, nodes); 183 TagCollection nodeTagsToEdit = new TagCollection(nodeTags); 184 completeTagCollectionForEditing(nodeTagsToEdit); 185 186 // launch a conflict resolution dialog, if necessary 187 // 188 CombinePrimitiveResolverDialog dialog = CombinePrimitiveResolverDialog.getInstance(); 189 dialog.getTagConflictResolverModel().populate(nodeTagsToEdit); 190 dialog.getRelationMemberConflictResolverModel().populate(relationToNodeReferences); 191 dialog.setTargetPrimitive(targetNode); 192 dialog.prepareDefaultDecisions(); 193 if (! nodeTags.isApplicableToPrimitive() || relationToNodeReferences.size() > 1) { 194 dialog.setVisible(true); 195 if (dialog.isCancelled()) 196 return null; 197 } 198 LinkedList<Command> cmds = new LinkedList<Command>(); 199 200 // the nodes we will have to delete 201 // 202 Collection<OsmPrimitive> nodesToDelete = new HashSet<OsmPrimitive>(nodes); 203 nodesToDelete.remove(targetNode); 204 205 // change the ways referring to at least one of the merge nodes 206 // 207 Collection<Way> waysToDelete= new HashSet<Way>(); 208 for (Way w : OsmPrimitive.getFilteredList(backreferences.getParents(nodesToDelete), Way.class)) { 209 // OK - this way contains one or more nodes to change 210 ArrayList<Node> newNodes = new ArrayList<Node>(w.getNodesCount()); 211 for (Node n: w.getNodes()) { 212 if (! nodesToDelete.contains(n)) { 213 newNodes.add(n); 150 214 } 151 215 } 152 } 153 154 // Complain to the user if the ways don't have equal memberships. 155 for (HashSet<Node> nodelinks : backlinks.values()) { 156 if (!nodelinks.containsAll(allNodes)) { 157 ExtendedDialog ed = new ExtendedDialog(Main.parent, 158 tr("Merge nodes with different memberships?"), 159 new String[] {tr("Merge Anyway"), tr("Cancel")}); 160 ed.setButtonIcons(new String[] {"mergenodes.png", "cancel.png"}); 161 ed.setContent(tr("The selected nodes have differing relation memberships. " 162 + "Do you still want to merge them?")); 163 ed.showDialog(); 164 165 if (ed.getValue() == 1) { 166 break; 167 } 168 return null; 169 } 170 } 171 172 // collect properties for later conflict resolving 173 Map<String, Set<String>> props = new TreeMap<String, Set<String>>(); 174 for (Node n : allNodes) { 175 for (Entry<String,String> e : n.entrySet()) { 176 if (!props.containsKey(e.getKey())) { 177 props.put(e.getKey(), new TreeSet<String>()); 178 } 179 props.get(e.getKey()).add(e.getValue()); 180 } 181 } 182 183 // display conflict dialog 184 Map<String, JComboBox> components = new HashMap<String, JComboBox>(); 185 JPanel p = new JPanel(new GridBagLayout()); 186 for (Entry<String, Set<String>> e : props.entrySet()) { 187 if (TigerUtils.isTigerTag(e.getKey())) { 188 String combined = TigerUtils.combineTags(e.getKey(), e.getValue()); 189 newNode.put(e.getKey(), combined); 190 } else if (e.getValue().size() > 1) { 191 JComboBox c = new JComboBox(e.getValue().toArray()); 192 c.setEditable(true); 193 p.add(new JLabel(e.getKey()), GBC.std()); 194 p.add(Box.createHorizontalStrut(10), GBC.std()); 195 p.add(c, GBC.eol()); 196 components.put(e.getKey(), c); 197 } else { 198 newNode.put(e.getKey(), e.getValue().iterator().next()); 199 } 200 } 201 202 if (!components.isEmpty()) { 203 ExtendedDialog dialog = new ExtendedDialog( 204 Main.parent, 205 tr("Enter values for all conflicts."), 206 new String[] {tr("Solve Conflicts"), tr("Cancel")} 207 ); 208 dialog.setButtonIcons(new String[] {"dialogs/conflict.png", "cancel.png"}); 209 dialog.setContent(p); 210 dialog.showDialog(); 211 int answer = dialog.getValue(); 212 213 if (answer != 1) 214 return null; 215 for (Entry<String, JComboBox> e : components.entrySet()) { 216 newNode.put(e.getKey(), e.getValue().getEditor().getItem().toString()); 217 } 218 } 219 220 LinkedList<Command> cmds = new LinkedList<Command>(); 221 222 if (!newNode.getKeys().equals(dest.getKeys())) { 223 cmds.add(new ChangeCommand(dest, newNode)); 224 } 225 226 Collection<OsmPrimitive> del = new HashSet<OsmPrimitive>(); 227 228 for (Way w : getCurrentDataSet().ways) { 229 if (w.isDeleted() || w.incomplete || w.getNodesCount() < 1) { 230 continue; 231 } 232 boolean modify = false; 233 for (Node sn : allNodes) { 234 if (sn == dest) { 235 continue; 236 } 237 if (w.containsNode(sn)) { 238 modify = true; 239 } 240 } 241 if (!modify) { 242 continue; 243 } 244 // OK - this way contains one or more nodes to change 245 ArrayList<Node> nn = new ArrayList<Node>(); 246 Node lastNode = null; 247 for (Node pushNode: w.getNodes()) { 248 if (allNodes.contains(pushNode)) { 249 pushNode = dest; 250 } 251 if (pushNode != lastNode) { 252 nn.add(pushNode); 253 } 254 lastNode = pushNode; 255 } 256 if (nn.size() < 2) { 257 CollectBackReferencesVisitor backRefs = 258 new CollectBackReferencesVisitor(getCurrentDataSet(), false); 259 w.visit(backRefs); 260 if (!backRefs.data.isEmpty()) { 216 if (newNodes.size() < 2) { 217 if (backreferences.getParents(w).isEmpty()) { 218 waysToDelete.add(w); 219 } else { 261 220 JOptionPane.showMessageDialog( 262 221 Main.parent, … … 268 227 return null; 269 228 } 270 del.add(w); 229 } else if(newNodes.size() < 2 && backreferences.getParents(w).isEmpty()) { 230 waysToDelete.add(w); 271 231 } else { 272 232 Way newWay = new Way(w); 273 newWay.setNodes(n n);233 newWay.setNodes(newNodes); 274 234 cmds.add(new ChangeCommand(w, newWay)); 275 235 } 276 236 } 277 237 278 // delete any merged nodes 279 del.addAll(allNodes); 280 del.remove(dest); 281 if (!del.isEmpty()) { 282 cmds.add(new DeleteCommand(del)); 283 } 284 285 // modify all relations containing the now-deleted nodes 286 for (Relation r : relationsUsingNodes) { 287 List<RelationMember> newMembers = new ArrayList<RelationMember>(); 288 HashSet<String> rolesToReAdd = new HashSet<String>(); 289 for (RelationMember rm : r.getMembers()) { 290 // Don't copy the member if it points to one of our nodes, 291 // just keep a note to re-add it later on. 292 if (allNodes.contains(rm.getMember())) { 293 rolesToReAdd.add(rm.getRole()); 294 } else { 295 newMembers.add(rm); 296 } 297 } 298 for (String role : rolesToReAdd) { 299 newMembers.add(new RelationMember(role, dest)); 300 } 301 Relation newRel = new Relation(r); 302 newRel.setMembers(newMembers); 303 cmds.add(new ChangeCommand(r, newRel)); 304 } 305 306 Main.main.undoRedo.add(new SequenceCommand(tr("Merge {0} nodes", allNodes.size()), cmds)); 307 getCurrentDataSet().setSelected(dest); 308 309 return dest; 238 // build the commands 239 // 240 if (!nodesToDelete.isEmpty()) { 241 cmds.add(new DeleteCommand(nodesToDelete)); 242 } 243 if (!waysToDelete.isEmpty()) { 244 cmds.add(new DeleteCommand(waysToDelete)); 245 } 246 cmds.addAll(dialog.buildResolutionCommands()); 247 Command cmd = new SequenceCommand(tr("Merge {0} nodes", nodes.size()), cmds); 248 return cmd; 310 249 } 311 250 -
trunk/src/org/openstreetmap/josm/actions/mapmode/SelectAction.java
r2031 r2095 17 17 import java.util.LinkedList; 18 18 import java.util.List; 19 import java.util.Set; 19 20 import java.util.TreeSet; 20 21 import java.util.logging.Logger; … … 43 44 import org.openstreetmap.josm.gui.SelectionManager; 44 45 import org.openstreetmap.josm.gui.SelectionManager.SelectionEnded; 46 import org.openstreetmap.josm.gui.dialogs.LayerListDialog.MergeAction; 45 47 import org.openstreetmap.josm.gui.layer.Layer; 46 48 import org.openstreetmap.josm.gui.layer.OsmDataLayer; … … 489 491 if (nn != null) { 490 492 Node n = nn.iterator().next(); 491 LinkedList<Node> selNodes = new LinkedList<Node>(); 492 for (OsmPrimitive osm : selection) 493 if (osm instanceof Node) { 494 selNodes.add((Node)osm); 495 } 496 if (selNodes.size() > 0) { 497 selNodes.add(n); 498 new MergeNodesAction().mergeNodes(selNodes, n); 493 Set<Node> selectedNodes = OsmPrimitive.getFilteredSet(selection, Node.class); 494 if (!selectedNodes.isEmpty()) { 495 selectedNodes.add(n); 496 MergeNodesAction mergeAction = new MergeNodesAction(); 497 Node targetNode = mergeAction.selectTargetNode(selectedNodes); 498 mergeAction.mergeNodes(Main.main.getEditLayer(),selectedNodes, targetNode); 499 499 } 500 500 }
Note:
See TracChangeset
for help on using the changeset viewer.
