Changeset 8247 in josm for trunk/src/org
- Timestamp:
- 2015-04-22T22:21:50+02:00 (10 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/actions/DistributeAction.java
r7828 r8247 11 11 import java.util.Iterator; 12 12 import java.util.LinkedList; 13 import java.util.List; 13 14 import java.util.Set; 14 15 … … 32 33 public final class DistributeAction extends JosmAction { 33 34 35 private static final String ACTION_SHORT_NAME = "Distribute Nodes"; 36 34 37 /** 35 38 * Constructs a new {@code DistributeAction}. 36 39 */ 37 40 public DistributeAction() { 38 super(tr("Distribute Nodes"), "distribute", tr("Distribute the selected nodes to equal distances along a line."), 39 Shortcut.registerShortcut("tools:distribute", tr("Tool: {0}", tr("Distribute Nodes")), KeyEvent.VK_B, 40 Shortcut.SHIFT), true); 41 super(tr(ACTION_SHORT_NAME), "distribute", 42 tr("Distribute the selected nodes to equal distances along a line."), 43 Shortcut.registerShortcut("tools:distribute", 44 tr("Tool: {0}", tr(ACTION_SHORT_NAME)), 45 KeyEvent.VK_B, Shortcut.SHIFT), 46 true); 41 47 putValue("help", ht("/Action/DistributeNodes")); 42 48 } 43 49 44 50 /** 45 * The general algorithm here is to find the two selected nodes 46 * that are furthest apart, and then to distribute all other selected 47 * nodes along the straight line between these nodes. 51 * Perform action. 52 * Select method according to user selection. 53 * Case 1: One Way (no self-crossing) and at most 2 nodes contains by this way: 54 * Distribute nodes keeping order along the way 55 * Case 2: Other 56 * Distribute nodes 48 57 */ 49 58 @Override … … 51 60 if (!isEnabled()) 52 61 return; 53 Collection<OsmPrimitive> sel = getCurrentDataSet().getSelected(); 54 Collection<Node> nodes = new LinkedList<>(); 55 Collection<Node> itnodes = new LinkedList<>(); 56 for (OsmPrimitive osm : sel) 57 if (osm instanceof Node) { 58 nodes.add((Node)osm); 59 itnodes.add((Node)osm); 60 } 61 // special case if no single nodes are selected and exactly one way is: 62 // then use the way's nodes 63 if (nodes.isEmpty() && (sel.size() == 1)) { 64 for (OsmPrimitive osm : sel) 65 if (osm instanceof Way) { 66 nodes.addAll(((Way)osm).getNodes()); 67 itnodes.addAll(((Way)osm).getNodes()); 68 } 62 63 // Collect user selected objects 64 Collection<OsmPrimitive> selected = getCurrentDataSet().getSelected(); 65 Collection<Way> ways = new LinkedList<>(); 66 Collection<Node> nodes = new HashSet<>(); 67 for(OsmPrimitive osm : selected) { 68 if(osm instanceof Node) { 69 nodes.add((Node) osm); 70 } else if(osm instanceof Way) { 71 ways.add((Way) osm); 72 } 69 73 } 70 74 71 75 Set<Node> ignoredNodes = removeNodesWithoutCoordinates(nodes); 72 ignoredNodes.addAll(removeNodesWithoutCoordinates(itnodes));73 76 if (!ignoredNodes.isEmpty()) { 74 77 Main.warn(tr("Ignoring {0} nodes with null coordinates", ignoredNodes.size())); … … 76 79 } 77 80 78 if (nodes.size() < 3) { 81 // Switch between algorithms 82 Collection<Command> cmds; 83 if(checkDistributeWay(ways, nodes)) { 84 cmds = distributeWay(ways, nodes); 85 } else if(checkDistributeNodes(ways, nodes)) { 86 cmds = distributeNodes(nodes); 87 } else { 79 88 new Notification( 80 tr("Please select at least three nodes.")) 81 .setIcon(JOptionPane.INFORMATION_MESSAGE) 82 .setDuration(Notification.TIME_SHORT) 83 .show(); 89 tr("Please select :\n" + 90 "* One no self-crossing way with at most two of its nodes;\n" + 91 "* Three nodes.")) 92 .setIcon(JOptionPane.INFORMATION_MESSAGE) 93 .setDuration(Notification.TIME_SHORT) 94 .show(); 84 95 return; 85 96 } 86 97 98 if(cmds.isEmpty()) { 99 return; 100 } 101 102 // Do it! 103 Main.main.undoRedo.add(new SequenceCommand(tr(ACTION_SHORT_NAME), cmds)); 104 Main.map.repaint(); 105 } 106 107 /** 108 * Test if one way, no self-crossing, is selected with at most two of its nodes. 109 * @param ways Selected ways 110 * @param nodes Selected nodes 111 * @return true in this case 112 */ 113 private Boolean checkDistributeWay(Collection<Way> ways, Collection<Node> nodes) { 114 if(ways.size() == 1 && nodes.size() <= 2) { 115 Way w = ways.iterator().next(); 116 Set<Node> unduplicated = new HashSet<>(w.getNodes()); 117 if(unduplicated.size() != w.getNodesCount()) { 118 // No self crossing way 119 return false; 120 } 121 for(Node node: nodes) { 122 if(!w.containsNode(node)) { 123 return false; 124 } 125 } 126 return true; 127 } 128 return false; 129 } 130 131 /** 132 * Distribute nodes contained by a way, keeping nodes order. 133 * If one or two nodes are selected, keep these nodes in place. 134 * @param ways Selected ways, must be collection of size 1. 135 * @param nodes Selected nodes, at most two nodes. 136 * @return Collection of command to be executed. 137 */ 138 private Collection<Command> distributeWay(Collection<Way> ways, 139 Collection<Node> nodes) { 140 Way w = ways.iterator().next(); 141 Collection<Command> cmds = new LinkedList<>(); 142 143 if(w.getNodesCount() == nodes.size() || w.getNodesCount() <= 2) { 144 // Nothing to do 145 return cmds; 146 } 147 148 double xa, ya; // Start point 149 double dx, dy; // Segment increment 150 if(nodes.isEmpty()) { 151 Node na = w.firstNode(); 152 nodes.add(na); 153 Node nb = w.lastNode(); 154 nodes.add(nb); 155 xa = na.getEastNorth().east(); 156 ya = na.getEastNorth().north(); 157 dx = (nb.getEastNorth().east() - xa) / (w.getNodesCount() - 1); 158 dy = (nb.getEastNorth().north() - ya) / (w.getNodesCount() - 1); 159 } else if(nodes.size() == 1) { 160 Node n = nodes.iterator().next(); 161 int nIdx = w.getNodes().indexOf(n); 162 Node na = w.firstNode(); 163 Node nb = w.lastNode(); 164 dx = (nb.getEastNorth().east() - na.getEastNorth().east()) / 165 (w.getNodesCount() - 1); 166 dy = (nb.getEastNorth().north() - na.getEastNorth().north()) / 167 (w.getNodesCount() - 1); 168 xa = n.getEastNorth().east() - dx * nIdx; 169 ya = n.getEastNorth().north() - dy * nIdx; 170 } else { 171 Iterator<Node> it = nodes.iterator(); 172 Node na = it.next(); 173 Node nb = it.next(); 174 List<Node> wayNodes = w.getNodes(); 175 int naIdx = wayNodes.indexOf(na); 176 int nbIdx = wayNodes.indexOf(nb); 177 dx = (nb.getEastNorth().east() - na.getEastNorth().east()) / (nbIdx - naIdx); 178 dy = (nb.getEastNorth().north() - na.getEastNorth().north()) / (nbIdx - naIdx); 179 xa = na.getEastNorth().east() - dx * naIdx; 180 ya = na.getEastNorth().north() - dy * naIdx; 181 } 182 183 for(int i = 0; i < w.getNodesCount(); i++) { 184 Node n = w.getNode(i); 185 if (!n.isLatLonKnown() || nodes.contains(n)) { 186 continue; 187 } 188 double x = xa + i * dx; 189 double y = ya + i * dy; 190 cmds.add(new MoveCommand(n, x - n.getEastNorth().east(), 191 y - n.getEastNorth().north())); 192 } 193 return cmds; 194 } 195 196 /** 197 * Test if nodes oriented algorithm applies to the selection. 198 * @param ways Selected ways 199 * @param nodes Selected nodes 200 * @return true in this case 201 */ 202 private Boolean checkDistributeNodes(Collection<Way> ways, Collection<Node> nodes) { 203 return ways.isEmpty() && nodes.size() >= 3; 204 } 205 206 /** 207 * Distribute nodes when only nodes are selected. 208 * The general algorithm here is to find the two selected nodes 209 * that are furthest apart, and then to distribute all other selected 210 * nodes along the straight line between these nodes. 211 * @return Commands to execute to perform action 212 */ 213 private Collection<Command> distributeNodes(Collection<Node> nodes) { 87 214 // Find from the selected nodes two that are the furthest apart. 88 215 // Let's call them A and B. … … 92 219 Node nodeb = null; 93 220 221 Collection<Node> itnodes = new LinkedList<>(nodes); 94 222 for (Node n : nodes) { 95 223 itnodes.remove(n); … … 146 274 } 147 275 148 // Do it! 149 Main.main.undoRedo.add(new SequenceCommand(tr("Distribute Nodes"), cmds)); 150 Main.map.repaint(); 151 } 152 276 return cmds; 277 } 278 279 /** 280 * Remove nodes without knowned coordinates from a collection. 281 * @param col Collection of nodes to check 282 * @return Set of nodes without coordinates 283 */ 153 284 private Set<Node> removeNodesWithoutCoordinates(Collection<Node> col) { 154 285 Set<Node> result = new HashSet<>();
Note:
See TracChangeset
for help on using the changeset viewer.