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 }