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

Last change on this file since 12759 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
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
[3266]16import org.openstreetmap.josm.data.coor.EastNorth;
[626]17import org.openstreetmap.josm.data.coor.LatLon;
[12759]18import org.openstreetmap.josm.data.osm.DataSet;
[626]19import org.openstreetmap.josm.data.osm.Node;
20import org.openstreetmap.josm.data.osm.OsmPrimitive;
21import org.openstreetmap.josm.data.osm.visitor.AllNodesVisitor;
[4126]22import org.openstreetmap.josm.data.projection.Projections;
[626]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) {
[8549]74 this(Collections.singleton((OsmPrimitive) node), Projections.project(position).subtract(node.getEastNorth()));
[3266]75 }
76
[6881]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 */
[3266]82 public MoveCommand(Collection<OsmPrimitive> objects, EastNorth offset) {
83 this(objects, offset.getX(), offset.getY());
84 }
[6069]85
[1169]86 /**
[6881]87 * Constructs a new {@code MoveCommand} and assign the initial object set and movement vector.
[12726]88 * @param objects The primitives to move. Must neither be null nor empty. Objects must belong to a data set
[6881]89 * @param x X difference movement. Coordinates are in northern/eastern
90 * @param y Y difference movement. Coordinates are in northern/eastern
[12726]91 * @throws NullPointerException if objects is null or contain null item
92 * @throws NoSuchElementException if objects is empty
[1169]93 */
94 public MoveCommand(Collection<OsmPrimitive> objects, double x, double y) {
[12759]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);
[5418]110 startEN = null;
111 saveCheckpoint(); // (0,0) displacement will be saved
[1169]112 this.x = x;
113 this.y = y;
[11070]114 Objects.requireNonNull(objects, "objects");
[1750]115 this.nodes = AllNodesVisitor.getAllNodes(objects);
116 for (Node n : this.nodes) {
[6173]117 oldState.add(new OldNodeState(n));
[1169]118 }
119 }
[626]120
[8447]121 /**
122 * Constructs a new {@code MoveCommand} to move a collection of primitives.
[12759]123 * @param ds the dataset context for moving these primitives. Must not be null.
[8447]124 * @param objects The primitives to move
125 * @param start The starting position (northern/eastern)
126 * @param end The ending position (northern/eastern)
[12759]127 * @since 12759
[8447]128 */
[12759]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());
[10378]134 startEN = start;
[6173]135 }
[5418]136
[8447]137 /**
[12759]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 /**
[8447]148 * Constructs a new {@code MoveCommand} to move a primitive.
[12759]149 * @param ds the dataset context for moving these primitives. Must not be null.
[8447]150 * @param p The primitive to move
151 * @param start The starting position (northern/eastern)
152 * @param end The ending position (northern/eastern)
[12759]153 * @since 12759
[8447]154 */
[12759]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 */
[6173]165 public MoveCommand(OsmPrimitive p, EastNorth start, EastNorth end) {
[12759]166 this(Collections.singleton(Objects.requireNonNull(p, "p")), start, end);
[6173]167 }
[6069]168
[1169]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.
[6881]176 *
177 * @param x X difference movement. Coordinates are in northern/eastern
178 * @param y Y difference movement. Coordinates are in northern/eastern
[1169]179 */
180 public void moveAgain(double x, double y) {
[1750]181 for (Node n : nodes) {
[1640]182 n.setEastNorth(n.getEastNorth().add(x, y));
[1169]183 }
184 this.x += x;
185 this.y += y;
186 }
[626]187
[9231]188 /**
189 * Move again to the specified coordinates.
190 * @param x X coordinate
191 * @param y Y coordinate
192 * @see #moveAgain
193 */
[3682]194 public void moveAgainTo(double x, double y) {
195 moveAgain(x - this.x, y - this.y);
196 }
[6069]197
[5418]198 /**
[9231]199 * Change the displacement vector to have endpoint {@code currentEN}.
200 * starting point is startEN
201 * @param currentEN the new endpoint
[5418]202 */
203 public void applyVectorTo(EastNorth currentEN) {
[6069]204 if (startEN == null)
[5418]205 return;
206 x = currentEN.getX() - startEN.getX();
207 y = currentEN.getY() - startEN.getY();
208 updateCoordinates();
209 }
[3682]210
[6173]211 /**
[5418]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);
[6173]217 }
[5418]218
219 /**
220 * Save curent displacement to restore in case of some problems
221 */
[6890]222 public final void saveCheckpoint() {
[5418]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() {
[6173]237 Iterator<OldNodeState> it = oldState.iterator();
[5418]238 for (Node n : nodes) {
[6173]239 OldNodeState os = it.next();
[8285]240 if (os.getEastNorth() != null) {
241 n.setEastNorth(os.getEastNorth().add(x, y));
[6215]242 }
[5418]243 }
244 }
245
[6881]246 @Override
247 public boolean executeCommand() {
[12348]248 ensurePrimitivesAreInDataset();
249
[1750]250 for (Node n : nodes) {
[2435]251 // in case #3892 happens again
[3173]252 if (n == null)
[3172]253 throw new AssertionError("null detected in node list");
[6215]254 EastNorth en = n.getEastNorth();
255 if (en != null) {
256 n.setEastNorth(en.add(x, y));
257 n.setModified(true);
258 }
[1169]259 }
260 return true;
[626]261 }
[1169]262
[6881]263 @Override
264 public void undoCommand() {
[12348]265 ensurePrimitivesAreInDataset();
266
[6173]267 Iterator<OldNodeState> it = oldState.iterator();
[1750]268 for (Node n : nodes) {
[6173]269 OldNodeState os = it.next();
[10248]270 n.setCoor(os.getLatLon());
[8285]271 n.setModified(os.isModified());
[1169]272 }
273 }
274
[6881]275 @Override
276 public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
[1750]277 for (OsmPrimitive osm : nodes) {
[1169]278 modified.add(osm);
[1750]279 }
[1169]280 }
281
[4918]282 @Override
283 public String getDescriptionText() {
284 return trn("Move {0} node", "Move {0} nodes", nodes.size(), nodes.size());
[1169]285 }
[1750]286
[3262]287 @Override
[4918]288 public Icon getDescriptionIcon() {
289 return ImageProvider.get("data", "node");
290 }
291
292 @Override
[3262]293 public Collection<Node> getParticipatingPrimitives() {
294 return nodes;
295 }
[8447]296
[10663]297 /**
298 * Gets the offset.
299 * @return The current offset.
300 */
301 protected EastNorth getOffset() {
302 return new EastNorth(x, y);
303 }
304
[8447]305 @Override
306 public int hashCode() {
[9371]307 return Objects.hash(super.hashCode(), nodes, startEN, x, y, backupX, backupY, oldState);
[8447]308 }
309
310 @Override
311 public boolean equals(Object obj) {
[9371]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);
[8447]323 }
[626]324}
Note: See TracBrowser for help on using the repository browser.