source: josm/trunk/src/org/openstreetmap/josm/command/MoveCommand.java@ 12767

Last change on this file since 12767 was 12759, checked in by Don-vip, 7 years ago

fix #15252 - see #13036 - MoveCommand created without data set

  • Property svn:eol-style set to native
File size: 11.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.command;
3
4import static org.openstreetmap.josm.tools.I18n.trn;
5
6import java.util.Collection;
7import java.util.Collections;
8import java.util.Iterator;
9import java.util.LinkedList;
10import java.util.List;
11import java.util.NoSuchElementException;
12import java.util.Objects;
13
14import javax.swing.Icon;
15
16import org.openstreetmap.josm.data.coor.EastNorth;
17import org.openstreetmap.josm.data.coor.LatLon;
18import org.openstreetmap.josm.data.osm.DataSet;
19import org.openstreetmap.josm.data.osm.Node;
20import org.openstreetmap.josm.data.osm.OsmPrimitive;
21import org.openstreetmap.josm.data.osm.visitor.AllNodesVisitor;
22import org.openstreetmap.josm.data.projection.Projections;
23import org.openstreetmap.josm.tools.ImageProvider;
24
25/**
26 * MoveCommand moves a set of OsmPrimitives along the map. It can be moved again
27 * to collect several MoveCommands into one command.
28 *
29 * @author imi
30 */
31public class MoveCommand extends Command {
32 /**
33 * The objects that should be moved.
34 */
35 private Collection<Node> nodes = new LinkedList<>();
36 /**
37 * Starting position, base command point, current (mouse-drag) position = startEN + (x,y) =
38 */
39 private EastNorth startEN;
40
41 /**
42 * x difference movement. Coordinates are in northern/eastern
43 */
44 private double x;
45 /**
46 * y difference movement. Coordinates are in northern/eastern
47 */
48 private double y;
49
50 private double backupX;
51 private double backupY;
52
53 /**
54 * List of all old states of the objects.
55 */
56 private final List<OldNodeState> oldState = new LinkedList<>();
57
58 /**
59 * Constructs a new {@code MoveCommand} to move a primitive.
60 * @param osm The primitive to move
61 * @param x X difference movement. Coordinates are in northern/eastern
62 * @param y Y difference movement. Coordinates are in northern/eastern
63 */
64 public MoveCommand(OsmPrimitive osm, double x, double y) {
65 this(Collections.singleton(osm), x, y);
66 }
67
68 /**
69 * Constructs a new {@code MoveCommand} to move a node.
70 * @param node The node to move
71 * @param position The new location (lat/lon)
72 */
73 public MoveCommand(Node node, LatLon position) {
74 this(Collections.singleton((OsmPrimitive) node), Projections.project(position).subtract(node.getEastNorth()));
75 }
76
77 /**
78 * Constructs a new {@code MoveCommand} to move a collection of primitives.
79 * @param objects The primitives to move
80 * @param offset The movement vector
81 */
82 public MoveCommand(Collection<OsmPrimitive> objects, EastNorth offset) {
83 this(objects, offset.getX(), offset.getY());
84 }
85
86 /**
87 * Constructs a new {@code MoveCommand} and assign the initial object set and movement vector.
88 * @param objects The primitives to move. Must neither be null nor empty. Objects must belong to a data set
89 * @param x X difference movement. Coordinates are in northern/eastern
90 * @param y Y difference movement. Coordinates are in northern/eastern
91 * @throws NullPointerException if objects is null or contain null item
92 * @throws NoSuchElementException if objects is empty
93 */
94 public MoveCommand(Collection<OsmPrimitive> objects, double x, double y) {
95 this(objects.iterator().next().getDataSet(), objects, x, y);
96 }
97
98 /**
99 * Constructs a new {@code MoveCommand} and assign the initial object set and movement vector.
100 * @param ds the dataset context for moving these primitives. Must not be null.
101 * @param objects The primitives to move. Must neither be null.
102 * @param x X difference movement. Coordinates are in northern/eastern
103 * @param y Y difference movement. Coordinates are in northern/eastern
104 * @throws NullPointerException if objects is null or contain null item
105 * @throws NoSuchElementException if objects is empty
106 * @since 12759
107 */
108 public MoveCommand(DataSet ds, Collection<OsmPrimitive> objects, double x, double y) {
109 super(ds);
110 startEN = null;
111 saveCheckpoint(); // (0,0) displacement will be saved
112 this.x = x;
113 this.y = y;
114 Objects.requireNonNull(objects, "objects");
115 this.nodes = AllNodesVisitor.getAllNodes(objects);
116 for (Node n : this.nodes) {
117 oldState.add(new OldNodeState(n));
118 }
119 }
120
121 /**
122 * Constructs a new {@code MoveCommand} to move a collection of primitives.
123 * @param ds the dataset context for moving these primitives. Must not be null.
124 * @param objects The primitives to move
125 * @param start The starting position (northern/eastern)
126 * @param end The ending position (northern/eastern)
127 * @since 12759
128 */
129 public MoveCommand(DataSet ds, Collection<OsmPrimitive> objects, EastNorth start, EastNorth end) {
130 this(Objects.requireNonNull(ds, "ds"),
131 Objects.requireNonNull(objects, "objects"),
132 Objects.requireNonNull(end, "end").getX() - Objects.requireNonNull(start, "start").getX(),
133 Objects.requireNonNull(end, "end").getY() - Objects.requireNonNull(start, "start").getY());
134 startEN = start;
135 }
136
137 /**
138 * Constructs a new {@code MoveCommand} to move a collection of primitives.
139 * @param objects The primitives to move
140 * @param start The starting position (northern/eastern)
141 * @param end The ending position (northern/eastern)
142 */
143 public MoveCommand(Collection<OsmPrimitive> objects, EastNorth start, EastNorth end) {
144 this(Objects.requireNonNull(objects, "objects").iterator().next().getDataSet(), objects, start, end);
145 }
146
147 /**
148 * Constructs a new {@code MoveCommand} to move a primitive.
149 * @param ds the dataset context for moving these primitives. Must not be null.
150 * @param p The primitive to move
151 * @param start The starting position (northern/eastern)
152 * @param end The ending position (northern/eastern)
153 * @since 12759
154 */
155 public MoveCommand(DataSet ds, OsmPrimitive p, EastNorth start, EastNorth end) {
156 this(ds, Collections.singleton(Objects.requireNonNull(p, "p")), start, end);
157 }
158
159 /**
160 * Constructs a new {@code MoveCommand} to move a primitive.
161 * @param p The primitive to move
162 * @param start The starting position (northern/eastern)
163 * @param end The ending position (northern/eastern)
164 */
165 public MoveCommand(OsmPrimitive p, EastNorth start, EastNorth end) {
166 this(Collections.singleton(Objects.requireNonNull(p, "p")), start, end);
167 }
168
169 /**
170 * Move the same set of objects again by the specified vector. The vectors
171 * are added together and so the resulting will be moved to the previous
172 * vector plus this one.
173 *
174 * The move is immediately executed and any undo will undo both vectors to
175 * the original position the objects had before first moving.
176 *
177 * @param x X difference movement. Coordinates are in northern/eastern
178 * @param y Y difference movement. Coordinates are in northern/eastern
179 */
180 public void moveAgain(double x, double y) {
181 for (Node n : nodes) {
182 n.setEastNorth(n.getEastNorth().add(x, y));
183 }
184 this.x += x;
185 this.y += y;
186 }
187
188 /**
189 * Move again to the specified coordinates.
190 * @param x X coordinate
191 * @param y Y coordinate
192 * @see #moveAgain
193 */
194 public void moveAgainTo(double x, double y) {
195 moveAgain(x - this.x, y - this.y);
196 }
197
198 /**
199 * Change the displacement vector to have endpoint {@code currentEN}.
200 * starting point is startEN
201 * @param currentEN the new endpoint
202 */
203 public void applyVectorTo(EastNorth currentEN) {
204 if (startEN == null)
205 return;
206 x = currentEN.getX() - startEN.getX();
207 y = currentEN.getY() - startEN.getY();
208 updateCoordinates();
209 }
210
211 /**
212 * Changes base point of movement
213 * @param newDraggedStartPoint - new starting point after movement (where user clicks to start new drag)
214 */
215 public void changeStartPoint(EastNorth newDraggedStartPoint) {
216 startEN = new EastNorth(newDraggedStartPoint.getX()-x, newDraggedStartPoint.getY()-y);
217 }
218
219 /**
220 * Save curent displacement to restore in case of some problems
221 */
222 public final void saveCheckpoint() {
223 backupX = x;
224 backupY = y;
225 }
226
227 /**
228 * Restore old displacement in case of some problems
229 */
230 public void resetToCheckpoint() {
231 x = backupX;
232 y = backupY;
233 updateCoordinates();
234 }
235
236 private void updateCoordinates() {
237 Iterator<OldNodeState> it = oldState.iterator();
238 for (Node n : nodes) {
239 OldNodeState os = it.next();
240 if (os.getEastNorth() != null) {
241 n.setEastNorth(os.getEastNorth().add(x, y));
242 }
243 }
244 }
245
246 @Override
247 public boolean executeCommand() {
248 ensurePrimitivesAreInDataset();
249
250 for (Node n : nodes) {
251 // in case #3892 happens again
252 if (n == null)
253 throw new AssertionError("null detected in node list");
254 EastNorth en = n.getEastNorth();
255 if (en != null) {
256 n.setEastNorth(en.add(x, y));
257 n.setModified(true);
258 }
259 }
260 return true;
261 }
262
263 @Override
264 public void undoCommand() {
265 ensurePrimitivesAreInDataset();
266
267 Iterator<OldNodeState> it = oldState.iterator();
268 for (Node n : nodes) {
269 OldNodeState os = it.next();
270 n.setCoor(os.getLatLon());
271 n.setModified(os.isModified());
272 }
273 }
274
275 @Override
276 public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
277 for (OsmPrimitive osm : nodes) {
278 modified.add(osm);
279 }
280 }
281
282 @Override
283 public String getDescriptionText() {
284 return trn("Move {0} node", "Move {0} nodes", nodes.size(), nodes.size());
285 }
286
287 @Override
288 public Icon getDescriptionIcon() {
289 return ImageProvider.get("data", "node");
290 }
291
292 @Override
293 public Collection<Node> getParticipatingPrimitives() {
294 return nodes;
295 }
296
297 /**
298 * Gets the offset.
299 * @return The current offset.
300 */
301 protected EastNorth getOffset() {
302 return new EastNorth(x, y);
303 }
304
305 @Override
306 public int hashCode() {
307 return Objects.hash(super.hashCode(), nodes, startEN, x, y, backupX, backupY, oldState);
308 }
309
310 @Override
311 public boolean equals(Object obj) {
312 if (this == obj) return true;
313 if (obj == null || getClass() != obj.getClass()) return false;
314 if (!super.equals(obj)) return false;
315 MoveCommand that = (MoveCommand) obj;
316 return Double.compare(that.x, x) == 0 &&
317 Double.compare(that.y, y) == 0 &&
318 Double.compare(that.backupX, backupX) == 0 &&
319 Double.compare(that.backupY, backupY) == 0 &&
320 Objects.equals(nodes, that.nodes) &&
321 Objects.equals(startEN, that.startEN) &&
322 Objects.equals(oldState, that.oldState);
323 }
324}
Note: See TracBrowser for help on using the repository browser.