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

Last change on this file since 13691 was 12778, checked in by bastiK, 7 years ago

see #15229 - deprecate Projections#project and Projections#inverseProject

replacement is a bit more verbose, but the fact that Main.proj is
involved need not be hidden

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