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

Last change on this file since 12574 was 12348, checked in by michael2402, 7 years ago

See #13036: Add more consistency checks to move / delete command.

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