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

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