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    }