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