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

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

see #13036 - deprecate Command() default constructor, fix unit tests and java warnings

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