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; 010import java.util.Objects; 011 012import javax.swing.Icon; 013 014import org.openstreetmap.josm.data.coor.EastNorth; 015import org.openstreetmap.josm.data.osm.Node; 016import org.openstreetmap.josm.data.osm.OsmPrimitive; 017import org.openstreetmap.josm.data.osm.visitor.AllNodesVisitor; 018import 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 */ 025public abstract class TransformNodesCommand extends Command { 026 027 /** 028 * The nodes to transform. 029 */ 030 protected Collection<Node> nodes = new LinkedList<>(); 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 * @param objects objects to fetch nodes from 050 */ 051 public TransformNodesCommand(Collection<OsmPrimitive> objects) { 052 this.nodes = AllNodesVisitor.getAllNodes(objects); 053 storeOldState(); 054 } 055 056 /** 057 * Handling of a mouse event (e.g. dragging event). 058 * @param currentEN the current world position of the mouse 059 */ 060 public abstract void handleEvent(EastNorth currentEN); 061 062 /** 063 * Implementation for the nodes transformation. 064 * No parameters are given here, you should handle the user input in handleEvent() 065 * and store it internally. 066 */ 067 protected abstract void transformNodes(); 068 069 /** 070 * Finally apply the transformation of the nodes. 071 * This is called when the user is happy with the current state of the command 072 * and its effects. 073 */ 074 @Override 075 public boolean executeCommand() { 076 transformNodes(); 077 flagNodesAsModified(); 078 return true; 079 } 080 081 /** 082 * Flag all nodes as modified. 083 */ 084 public void flagNodesAsModified() { 085 for (Node n : nodes) { 086 n.setModified(true); 087 } 088 } 089 090 /** 091 * Restore the state of the nodes from the backup. 092 */ 093 @Override 094 public void undoCommand() { 095 for (Node n : nodes) { 096 OldNodeState os = oldStates.get(n); 097 n.setCoor(os.getLatlon()); 098 n.setModified(os.isModified()); 099 } 100 } 101 102 @Override 103 public void fillModifiedData(Collection<OsmPrimitive> modified, Collection<OsmPrimitive> deleted, Collection<OsmPrimitive> added) { 104 } 105 106 @Override 107 public Collection<? extends OsmPrimitive> getParticipatingPrimitives() { 108 return nodes; 109 } 110 111 @Override 112 public String getDescriptionText() { 113 return trn("Transform {0} node", "Transform {0} nodes", nodes.size(), nodes.size()); 114 } 115 116 @Override 117 public Icon getDescriptionIcon() { 118 return ImageProvider.get("data", "node"); 119 } 120 121 /** 122 * Get the nodes with the current transformation applied. 123 * @return nodes with the current transformation applied 124 */ 125 public Collection<Node> getTransformedNodes() { 126 return nodes; 127 } 128 129 /** 130 * Get the center of the nodes under modification. 131 * It's just the barycenter. 132 * @return center east/north of the nodes under modification 133 * @see org.openstreetmap.josm.tools.Geometry#getCentroid(java.util.List) 134 */ 135 public EastNorth getNodesCenter() { 136 EastNorth sum = new EastNorth(0, 0); 137 138 for (Node n : nodes) { 139 EastNorth en = n.getEastNorth(); 140 sum = sum.add(en.east(), en.north()); 141 } 142 return new EastNorth(sum.east()/this.nodes.size(), sum.north()/this.nodes.size()); 143 144 } 145 146 @Override 147 public int hashCode() { 148 return Objects.hash(super.hashCode(), nodes, oldStates); 149 } 150 151 @Override 152 public boolean equals(Object obj) { 153 if (this == obj) return true; 154 if (obj == null || getClass() != obj.getClass()) return false; 155 if (!super.equals(obj)) return false; 156 TransformNodesCommand that = (TransformNodesCommand) obj; 157 return Objects.equals(nodes, that.nodes) && 158 Objects.equals(oldStates, that.oldStates); 159 } 160}