source: josm/trunk/src/org/openstreetmap/josm/actions/UnGlueAction.java@ 858

Last change on this file since 858 was 858, checked in by stoecker, 16 years ago

added unglue action to main JOSM. Thanks to Henry Loenwind and Robin Rattay.

File size: 5.7 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.actions;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.event.ActionEvent;
7import java.awt.event.KeyEvent;
8import java.util.ArrayList;
9import java.util.Collection;
10import java.util.HashSet;
11import java.util.LinkedList;
12import java.util.List;
13
14import javax.swing.JOptionPane;
15
16import org.openstreetmap.josm.Main;
17import org.openstreetmap.josm.actions.JosmAction;
18import org.openstreetmap.josm.command.AddCommand;
19import org.openstreetmap.josm.command.ChangeCommand;
20import org.openstreetmap.josm.command.Command;
21import org.openstreetmap.josm.command.SequenceCommand;
22import org.openstreetmap.josm.data.SelectionChangedListener;
23import org.openstreetmap.josm.data.osm.DataSet;
24import org.openstreetmap.josm.data.osm.Node;
25import org.openstreetmap.josm.data.osm.OsmPrimitive;
26import org.openstreetmap.josm.data.osm.Relation;
27import org.openstreetmap.josm.data.osm.RelationMember;
28import org.openstreetmap.josm.data.osm.Way;
29
30/**
31 * Dupe a node that is used my multiple ways, so each way has its own node.
32 *
33 * Resulting nodes are identical, up to their position.
34 *
35 * This is the opposite of the MergeNodesAction.
36 */
37
38public class UnGlueAction extends JosmAction implements SelectionChangedListener {
39
40 private Node selectedNode;
41 private Way selectedWay;
42
43 /**
44 * Create a new SplitWayAction.
45 */
46 public UnGlueAction() {
47 super(tr("UnGlue Ways"), "unglueways", tr("Duplicate the selected node so each way using ist has its own copy."), KeyEvent.VK_G, 0, true);
48 DataSet.selListeners.add(this);
49 }
50
51 /**
52 * Called when the action is executed.
53 *
54 * This method just collects the single node selected and calls the unGlueWay method.
55 */
56 public void actionPerformed(ActionEvent e) {
57
58 Collection<OsmPrimitive> selection = Main.ds.getSelected();
59
60 if (!checkSelection(selection)) {
61 JOptionPane.showMessageDialog(Main.parent, tr("The current selection cannot be used for unglueing."));
62 return;
63 }
64
65 int count = 0;
66 for (Way w : Main.ds.ways) {
67 if (w.deleted || w.incomplete || w.nodes.size() < 1) continue;
68 if (!w.nodes.contains(selectedNode)) continue;
69 count++;
70 }
71 if (count < 2) {
72 JOptionPane.showMessageDialog(Main.parent, tr("You must select a node that is used by at least 2 ways."));
73 return;
74 }
75
76 // and then do the work.
77 unglueWays();
78 }
79
80 /**
81 * Checks if the selection consists of something we can work with.
82 * Checks only if the number and type of items selected looks good;
83 * does not check whether the selected items are really a valid
84 * input for splitting (this would be too expensive to be carried
85 * out from the selectionChanged listener).
86 */
87 private boolean checkSelection(Collection<? extends OsmPrimitive> selection) {
88
89 int size = selection.size();
90 if (size < 1 || size > 2)
91 return false;
92
93 selectedNode = null;
94 selectedWay = null;
95
96 for (OsmPrimitive p : selection) {
97 if (p instanceof Node) {
98 selectedNode = (Node) p;
99 if (size == 1 || selectedWay != null)
100 return size == 1 || selectedWay.nodes.contains(selectedNode);
101 } else if (p instanceof Way) {
102 selectedWay = (Way) p;
103 if (size == 2 && selectedNode != null)
104 return selectedWay.nodes.contains(selectedNode);
105 }
106 }
107
108 return false;
109 }
110
111 private boolean modifyWay(boolean firstway, Way w, List<Command> cmds,
112 List<Node> newNodes) {
113 ArrayList<Node> nn = new ArrayList<Node>();
114 for (Node pushNode : w.nodes) {
115 if (selectedNode == pushNode) {
116 if (firstway) {
117 // reuse the old node for the first (==a random) way
118 firstway = false;
119 } else {
120 // clone the node for all other ways
121 pushNode = new Node(selectedNode);
122 pushNode.id = 0;
123 newNodes.add(pushNode);
124 cmds.add(new AddCommand(pushNode));
125 }
126 }
127 nn.add(pushNode);
128 }
129 Way newWay = new Way(w);
130 newWay.nodes.clear();
131 newWay.nodes.addAll(nn);
132 cmds.add(new ChangeCommand(w, newWay));
133
134 return firstway;
135 }
136
137 /**
138 * see above
139 */
140 private void unglueWays() {
141
142 LinkedList<Command> cmds = new LinkedList<Command>();
143 List<Node> newNodes = new LinkedList<Node>();
144
145 if (selectedWay == null) {
146
147 boolean firstway = true;
148 // modify all ways containing the nodes
149 for (Way w : Main.ds.ways) {
150 if (w.deleted || w.incomplete || w.nodes.size() < 1) continue;
151 if (!w.nodes.contains(selectedNode)) continue;
152
153 firstway = modifyWay(firstway, w, cmds, newNodes);
154 }
155 } else {
156 modifyWay(false, selectedWay, cmds, newNodes);
157 }
158
159 // modify all relations containing the node
160 Relation newRel = null;
161 HashSet<String> rolesToReAdd = null;
162 for (Relation r : Main.ds.relations) {
163 if (r.deleted || r.incomplete) continue;
164 newRel = null;
165 rolesToReAdd = null;
166 for (RelationMember rm : r.members) {
167 if (rm.member instanceof Node) {
168 if (rm.member == selectedNode) {
169 if (newRel == null) {
170 newRel = new Relation(r);
171 newRel.members.clear();
172 rolesToReAdd = new HashSet<String>();
173 }
174 rolesToReAdd.add(rm.role);
175 }
176 }
177 }
178 if (newRel != null) {
179 for (RelationMember rm : r.members) {
180 //if (rm.member != selectedNode) {
181 newRel.members.add(rm);
182 //}
183 }
184 for (Node n : newNodes) {
185 for (String role : rolesToReAdd) {
186 newRel.members.add(new RelationMember(role, n));
187 }
188 }
189 cmds.add(new ChangeCommand(r, newRel));
190 }
191 }
192
193 newNodes.add(selectedNode); // just for the next 2 lines
194 Main.main.undoRedo.add(new SequenceCommand(tr("Dupe into {0} nodes", newNodes.size()), cmds));
195 Main.ds.setSelected(newNodes);
196
197 }
198
199 /**
200 * Enable the "split way" menu option if the selection looks like we could use it.
201 */
202 public void selectionChanged(Collection<? extends OsmPrimitive> newSelection) {
203 setEnabled(checkSelection(newSelection));
204 }
205}
Note: See TracBrowser for help on using the repository browser.