Ticket #8431: bug8431_2.patch
File bug8431_2.patch, 11.1 KB (added by , 11 years ago) |
---|
-
src/org/openstreetmap/josm/actions/AlignInCircleAction.java
9 9 import java.util.ArrayList; 10 10 import java.util.Collection; 11 11 import java.util.Collections; 12 import java.util.HashSet; 12 13 import java.util.LinkedList; 13 14 import java.util.List; 14 15 … … 99 100 } 100 101 } 101 102 103 104 /** 105 * Perform AlignInCircle action. 106 * 107 * A fixed node is a node for which it is forbidden to change the angle relative to center of the circle. 108 * All other nodes are uniformly distributed. 109 * 110 * Case 1: One unclosed way. 111 * --> allow action, and align selected way nodes 112 * If nodes contained by this way are selected, there are fix. 113 * If nodes outside from the way are selected there are ignored. 114 * 115 * Case 2: One or more ways are selected and can be joined into a polygon 116 * --> allow action, and align selected ways nodes 117 * If 1 node outside of way is selected, it became center 118 * If 1 node outside and 1 node inside are selected there define center and radius 119 * If no outside node and 2 inside nodes are selected those 2 nodes define diameter 120 * In all other cases outside nodes are ignored 121 * In all cases, selected nodes are fix, nodes with more than one referrers are fix 122 * (first referrer is the selected way) 123 * 124 * Case 3: Only nodes are selected 125 * --> Align these nodes, all are fix 126 */ 102 127 @Override 103 128 public void actionPerformed(ActionEvent e) { 104 129 if (!isEnabled()) … … 106 131 107 132 Collection<OsmPrimitive> sel = getCurrentDataSet().getSelected(); 108 133 List<Node> nodes = new LinkedList<Node>(); 134 // fixNodes: All nodes for which the angle relative to center should not be modified 135 HashSet<Node> fixNodes = new HashSet<Node>(); 109 136 List<Way> ways = new LinkedList<Way>(); 110 137 EastNorth center = null; 111 138 double radius = 0; 112 boolean isPolygon = false;113 139 114 140 for (OsmPrimitive osm : sel) { 115 141 if (osm instanceof Node) { … … 119 145 } 120 146 } 121 147 122 // special case if no single nodes are selected and exactly one way is: 123 // then use the way's nodes 124 if ((nodes.size() <= 2) && checkWaysArePolygon(ways)) { 125 // some more special combinations: 126 // When is selected node that is part of the way, then make a regular polygon, selected 127 // node doesn't move. 128 // I haven't got better idea, how to activate that function. 129 // 130 // When one way and one node is selected, set center to position of that node. 131 // When one more node, part of the way, is selected, set the radius equal to the 132 // distance between two nodes. 133 if (nodes.size() >= 1) { 134 boolean[] isContained = new boolean[nodes.size()]; 135 for(int i = 0; i < nodes.size(); i++) { 136 Node n = nodes.get(i); 137 isContained[i] = false; 138 for(Way way: ways) 139 if(way.containsNode(n)) { 140 isContained[i] = true; 141 break; 142 } 143 } 144 if(nodes.size() == 1) { 145 if(!isContained[0]) 146 center = nodes.get(0).getEastNorth(); 147 } else { 148 if(!isContained[0] && !isContained[1]) { 149 // 2 nodes outside of way, can't choose one as center 150 new Notification( 151 tr("Please select only one node as center.")) 152 .setIcon(JOptionPane.INFORMATION_MESSAGE) 153 .setDuration(Notification.TIME_SHORT) 154 .show(); 155 return; 156 } else if (!isContained[0] || !isContained[1]) { 157 // 1 node inside and 1 outside, outside is center, inside node define radius 158 center = nodes.get(isContained[0] ? 1 : 0).getEastNorth(); 159 radius = distance(nodes.get(0).getEastNorth(), nodes.get(1).getEastNorth()); 160 } else { 161 // 2 nodes inside, define diameter 162 EastNorth en0 = nodes.get(0).getEastNorth(); 163 EastNorth en1 = nodes.get(1).getEastNorth(); 164 center = new EastNorth((en0.east() + en1.east()) / 2, (en0.north() + en1.north()) / 2); 165 radius = distance(en0, en1) / 2; 148 if (ways.size() == 1 && ways.get(0).firstNode() != ways.get(0).lastNode()) { 149 // Case 1 150 Way w = ways.get(0); 151 fixNodes.add(w.firstNode()); 152 fixNodes.add(w.lastNode()); 153 fixNodes.addAll(nodes); 154 fixNodes.addAll(collectNodesWithExternReferers(ways)); 155 // Temporary closed way used to reorder nodes 156 Way closedWay = new Way(w); 157 closedWay.addNode(w.firstNode()); 158 ArrayList<Way> usedWays = new ArrayList<Way>(1); 159 usedWays.add(closedWay); 160 nodes = collectNodesAnticlockwise(usedWays); 161 } else if (!ways.isEmpty() && checkWaysArePolygon(ways)) { 162 // Case 2 163 ArrayList<Node> inside = new ArrayList<Node>(); 164 ArrayList<Node> outside = new ArrayList<Node>(); 165 166 for(Node n: nodes) { 167 boolean isInside = false; 168 for(Way w: ways) { 169 if(w.getNodes().contains(n)) { 170 isInside = true; 171 break; 166 172 } 167 173 } 174 if(isInside) 175 inside.add(n); 176 else 177 outside.add(n); 168 178 } 179 180 if(outside.size() == 1 && inside.isEmpty()) { 181 center = outside.get(0).getEastNorth(); 182 } else if(outside.size() == 1 && inside.size() == 1) { 183 center = outside.get(0).getEastNorth(); 184 radius = distance(center, inside.get(0).getEastNorth()); 185 } else if(inside.size() == 2 && outside.isEmpty()) { 186 // 2 nodes inside, define diameter 187 EastNorth en0 = inside.get(0).getEastNorth(); 188 EastNorth en1 = inside.get(1).getEastNorth(); 189 center = new EastNorth((en0.east() + en1.east()) / 2, (en0.north() + en1.north()) / 2); 190 radius = distance(en0, en1) / 2; 191 } 192 193 fixNodes.addAll(inside); 194 fixNodes.addAll(collectNodesWithExternReferers(ways)); 169 195 nodes = collectNodesAnticlockwise(ways); 170 isPolygon = true; 171 } 172 173 if (nodes.size() < 4) { 196 if(nodes.size() < 4) { 197 new Notification( 198 tr("Not enouth nodes in selected ways.")) 199 .setIcon(JOptionPane.INFORMATION_MESSAGE) 200 .setDuration(Notification.TIME_SHORT) 201 .show(); 202 return; 203 } 204 } else if (ways.isEmpty() && nodes.size() > 3) { 205 // Case 3 206 fixNodes.addAll(nodes); 207 // No need to reorder nodes since all are fix 208 } else { 209 // Invalid action 174 210 new Notification( 175 211 tr("Please select at least four nodes.")) 176 212 .setIcon(JOptionPane.INFORMATION_MESSAGE) … … 184 220 center = Geometry.getCentroid(nodes); 185 221 } 186 222 // Node "center" now is central to all selected nodes. 187 188 if (!isPolygon) {189 // Then reorder them based on heading from the center point190 List<Node> newNodes = new LinkedList<Node>();191 while (!nodes.isEmpty()) {192 double maxHeading = -1.0;193 Node maxNode = null;194 for (Node n : nodes) {195 double heading = center.heading(n.getEastNorth());196 if (heading > maxHeading) {197 maxHeading = heading;198 maxNode = n;199 }200 }201 newNodes.add(maxNode);202 nodes.remove(maxNode);203 }204 nodes = newNodes;205 }206 223 207 224 // Now calculate the average distance to each node from the 208 // cent re. This method is ok as long as distances are short225 // center. This method is ok as long as distances are short 209 226 // relative to the distance from the N or S poles. 210 227 if (radius == 0) { 211 228 for (Node n : nodes) { … … 224 241 // Search first fixed node 225 242 int startPosition = 0; 226 243 for(startPosition = 0; startPosition < nodeCount; startPosition++) 227 if( isFixNode(nodes.get(startPosition % nodeCount), sel)) break;244 if(fixNodes.contains(nodes.get(startPosition % nodeCount))) break; 228 245 int i = startPosition; // Start position for current arc 229 246 int j; // End position for current arc 230 247 while(i < startPosition + nodeCount) { 231 248 for(j = i + 1; j < startPosition + nodeCount; j++) 232 if( isFixNode(nodes.get(j % nodeCount), sel)) break;249 if(fixNodes.contains(nodes.get(j % nodeCount))) break; 233 250 Node first = nodes.get(i % nodeCount); 234 251 PolarCoor pcFirst = new PolarCoor(first.getEastNorth(), center, 0); 235 252 pcFirst.radius = radius; … … 258 275 } 259 276 260 277 /** 278 * Collect all nodes with more than one referrer. 279 * @param ways Ways from witch nodes are selected 280 * @return List of nodes with more than one referrer 281 */ 282 private List<Node> collectNodesWithExternReferers(List<Way> ways) { 283 ArrayList<Node> withReferrers = new ArrayList<Node>(); 284 for(Way w: ways) 285 for(Node n: w.getNodes()) 286 if(n.getReferrers().size() > 1) 287 withReferrers.add(n); 288 return withReferrers; 289 } 290 291 /** 261 292 * Assuming all ways can be joined into polygon, create an ordered list of node. 262 293 * @param ways List of ways to be joined 263 294 * @return Nodes anticlockwise ordered … … 323 354 return true; 324 355 } 325 356 326 /**327 * Test if angle of a node can be change.328 * @param n Node329 * @param sel Selection which action is apply330 * @return true is this node does't have a fix angle331 */332 private boolean isFixNode(Node n, Collection<OsmPrimitive> sel) {333 List<OsmPrimitive> referrers = n.getReferrers();334 if(referrers.isEmpty()) return false;335 if(sel.contains(n) || referrers.size() > 1 || !sel.contains(referrers.get(0))) return true;336 return false;337 }338 339 357 @Override 340 358 protected void updateEnabledState() { 341 359 setEnabled(getCurrentDataSet() != null && !getCurrentDataSet().getSelected().isEmpty());