[6380] | 1 | // License: GPL. For details, see LICENSE file.
|
---|
[626] | 2 | package org.openstreetmap.josm.command;
|
---|
| 3 |
|
---|
| 4 | import static org.openstreetmap.josm.tools.I18n.trn;
|
---|
| 5 |
|
---|
| 6 | import java.util.Collection;
|
---|
[307] | 7 | import java.util.Collections;
|
---|
[626] | 8 | import java.util.Iterator;
|
---|
| 9 | import java.util.LinkedList;
|
---|
| 10 | import java.util.List;
|
---|
[9371] | 11 | import java.util.Objects;
|
---|
[6173] | 12 |
|
---|
[4918] | 13 | import javax.swing.Icon;
|
---|
[626] | 14 |
|
---|
[3266] | 15 | import org.openstreetmap.josm.data.coor.EastNorth;
|
---|
[626] | 16 | import org.openstreetmap.josm.data.coor.LatLon;
|
---|
| 17 | import org.openstreetmap.josm.data.osm.Node;
|
---|
| 18 | import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
---|
| 19 | import org.openstreetmap.josm.data.osm.visitor.AllNodesVisitor;
|
---|
[4126] | 20 | import org.openstreetmap.josm.data.projection.Projections;
|
---|
[626] | 21 | import 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 | */
|
---|
| 29 | public 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 | }
|
---|