001 // License: GPL. For details, see LICENSE file.
002 package org.openstreetmap.josm.command;
003
004 import static org.openstreetmap.josm.tools.I18n.trn;
005
006 import java.util.Collection;
007 import java.util.HashMap;
008 import java.util.LinkedList;
009 import java.util.Map;
010
011 import javax.swing.Icon;
012
013 import org.openstreetmap.josm.data.coor.EastNorth;
014 import org.openstreetmap.josm.data.coor.LatLon;
015 import org.openstreetmap.josm.data.osm.Node;
016 import org.openstreetmap.josm.data.osm.OsmPrimitive;
017 import org.openstreetmap.josm.data.osm.visitor.AllNodesVisitor;
018 import org.openstreetmap.josm.tools.ImageProvider;
019
020 /**
021 * Abstract class with common services for nodes rotation and scaling commands.
022 *
023 * @author Olivier Croquette <ocroquette@free.fr>
024 */
025 public abstract class TransformNodesCommand extends Command {
026
027 /**
028 * The nodes to transform.
029 */
030 protected Collection<Node> nodes = new LinkedList<Node>();
031
032 /**
033 * Small helper for holding the interesting part of the old data state of the
034 * nodes.
035 */
036 public static class OldState {
037 LatLon latlon;
038 EastNorth eastNorth;
039 boolean modified;
040 }
041
042 /**
043 * List of all old states of the nodes.
044 */
045 protected Map<Node, OldState> oldStates = new HashMap<Node, OldState>();
046
047 /**
048 * Stores the state of the nodes before the command.
049 */
050 protected void storeOldState() {
051 for (Node n : this.nodes) {
052 OldState os = new OldState();
053 os.latlon = new LatLon(n.getCoor());
054 os.eastNorth = n.getEastNorth();
055 os.modified = n.isModified();
056 oldStates.put(n, os);
057 }
058 }
059
060 /**
061 * Creates a TransformNodesObject.
062 * Find out the impacted nodes and store their initial state.
063 */
064 public TransformNodesCommand(Collection<OsmPrimitive> objects) {
065 this.nodes = AllNodesVisitor.getAllNodes(objects);
066 storeOldState();
067 }
068
069 /**
070 * Handling of a mouse event (e.g. dragging event).
071 * @param currentEN the current world position of the mouse
072 */
073 public abstract void handleEvent(EastNorth currentEN);
074
075 /**
076 * Implementation for the nodes transformation.
077 * No parameters are given here, you should handle the user input in handleEvent()
078 * and store it internally.
079 */
080 protected abstract void transformNodes();
081
082 /**
083 * Finally apply the transformation of the nodes.
084 * This is called when the user is happy with the current state of the command
085 * and its effects.
086 */
087 @Override
088 public boolean executeCommand() {
089 transformNodes();
090 flagNodesAsModified();
091 return true;
092 }
093
094 /**
095 * Flag all nodes as modified.
096 */
097 public void flagNodesAsModified() {
098 for (Node n : nodes) {
099 n.setModified(true);
100 }
101 }
102
103 /**
104 * Restore the state of the nodes from the backup.
105 */
106 @Override
107 public void undoCommand() {
108 for (Node n : nodes) {
109 OldState os = oldStates.get(n);
110 n.setCoor(os.latlon);
111 n.setModified(os.modified);
112 }
113 }
114
115 @Override
116 public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) {
117 }
118
119 @Override
120 public Collection<? extends OsmPrimitive> getParticipatingPrimitives() {
121 return nodes;
122 }
123
124 @Override
125 public String getDescriptionText() {
126 return trn("Transform {0} node", "Transform {0} nodes", nodes.size(), nodes.size());
127 }
128
129 @Override
130 public Icon getDescriptionIcon() {
131 return ImageProvider.get("data", "node");
132 }
133
134 /**
135 * Get the nodes with the current transformation applied.
136 */
137 public Collection<Node> getTransformedNodes() {
138 return nodes;
139 }
140
141 /**
142 * Get the center of the nodes under modification.
143 * It's just the barycenter.
144 */
145 public EastNorth getNodesCenter() {
146 EastNorth sum = new EastNorth(0,0);
147
148 for (Node n : nodes ) {
149 EastNorth en = n.getEastNorth();
150 sum = sum.add(en.east(), en.north());
151 }
152 return new EastNorth(sum.east()/this.nodes.size(), sum.north()/this.nodes.size());
153
154 }
155 }