Ticket #18368: 18368-v2.patch
| File 18368-v2.patch, 5.8 KB (added by , 6 years ago) |
|---|
-
src/org/openstreetmap/josm/data/osm/NodeGraph.java
4 4 import java.util.ArrayList; 5 5 import java.util.Collection; 6 6 import java.util.Collections; 7 import java.util.Comparator; 8 import java.util.HashMap; 9 import java.util.HashSet; 7 10 import java.util.LinkedHashMap; 8 11 import java.util.LinkedHashSet; 9 12 import java.util.LinkedList; 10 13 import java.util.List; 11 14 import java.util.Map; 15 import java.util.Map.Entry; 12 16 import java.util.Optional; 13 17 import java.util.Set; 14 18 import java.util.Stack; 19 import java.util.TreeMap; 15 20 16 21 import org.openstreetmap.josm.tools.Pair; 17 22 … … 135 140 private final Map<Node, List<NodePair>> predecessors = new LinkedHashMap<>(); 136 141 137 142 protected void rememberSuccessor(NodePair pair) { 138 if (successors.containsKey(pair.getA())) { 139 if (!successors.get(pair.getA()).contains(pair)) { 140 successors.get(pair.getA()).add(pair); 141 } 142 } else { 143 List<NodePair> l = new ArrayList<>(); 143 List<NodePair> l = successors.computeIfAbsent(pair.getA(), k -> new ArrayList<>()); 144 if (!l.contains(pair)) { 144 145 l.add(pair); 145 successors.put(pair.getA(), l);146 146 } 147 147 } 148 148 149 149 protected void rememberPredecessors(NodePair pair) { 150 if (predecessors.containsKey(pair.getB())) { 151 if (!predecessors.get(pair.getB()).contains(pair)) { 152 predecessors.get(pair.getB()).add(pair); 153 } 154 } else { 155 List<NodePair> l = new ArrayList<>(); 150 List<NodePair> l = predecessors.computeIfAbsent(pair.getB(), k -> new ArrayList<>()); 151 if (!l.contains(pair)) { 156 152 l.add(pair); 157 predecessors.put(pair.getB(), l);158 153 } 159 154 } 160 155 … … 239 234 return nodes; 240 235 } 241 236 242 protected boolean isSpanningWay( Stack<NodePair> way) {237 protected boolean isSpanningWay(Collection<NodePair> way) { 243 238 return numUndirectedEges == way.size(); 244 239 } 245 240 … … 262 257 */ 263 258 protected List<Node> buildSpanningPath(Node startNode) { 264 259 if (startNode != null) { 260 // do not simply replace `Stack` by `ArrayDeque` because of different iteration behaviour, see #11957 265 261 Stack<NodePair> path = new Stack<>(); 262 Set<NodePair> dupCheck = new HashSet<>(); 266 263 Stack<NodePair> nextPairs = new Stack<>(); 267 264 nextPairs.addAll(getOutboundPairs(startNode)); 268 265 while (!nextPairs.isEmpty()) { 269 266 NodePair cur = nextPairs.pop(); 270 if (! path.contains(cur) && !path.contains(cur.swap())) {267 if (!dupCheck.contains(cur) && !dupCheck.contains(cur.swap())) { 271 268 while (!path.isEmpty() && !path.peek().isPredecessorOf(cur)) { 272 path.pop();269 dupCheck.remove(path.pop()); 273 270 } 274 271 path.push(cur); 272 dupCheck.add(cur); 275 273 if (isSpanningWay(path)) return buildPathFromNodePairs(path); 276 274 nextPairs.addAll(getOutboundPairs(path.peek())); 277 275 } … … 283 281 /** 284 282 * Tries to find a path through the graph which visits each edge (i.e. 285 283 * the segment of a way) exactly once. 284 * <p><b>Note that duplicated edges are removed first!</b> 286 285 * 287 286 * @return the path; null, if no path was found 288 287 */ … … 292 291 // node which is connected by exactly one undirected edges (or 293 292 // two directed edges in opposite direction) to the graph. A 294 293 // graph built up from way segments is likely to include such 295 // nodes, unless all ways are closed. 296 // In the worst case this loops over all nodes which is very slow for large ways. 297 // 294 // nodes, unless the edges build one or more closed rings. 295 // We order the nodes to start with the best candidates, but 296 // it might take very long if there is no valid path as we iterate over all nodes 297 // to find out. This will happen with a disconnected graph. 298 298 Set<Node> nodes = getTerminalNodes(); 299 nodes = nodes.isEmpty() ? get Nodes() : nodes;300 for (Node n : nodes) {299 nodes = nodes.isEmpty() ? getMostFrequentVisitedNodesFirst() : nodes; 300 for (Node n : nodes) { 301 301 List<Node> path = buildSpanningPath(n); 302 302 if (!path.isEmpty()) 303 303 return path; … … 304 304 } 305 305 return null; 306 306 } 307 308 /** 309 * Sort the nodes by number of appearances in the edges. 310 * @return a linked set of all nodes ordered so that the nodes which appear in more edges appear first. 311 */ 312 private Set<Node> getMostFrequentVisitedNodesFirst() { 313 if (edges.isEmpty()) 314 return Collections.emptySet(); 315 // count appearance of nodes in edges 316 Map<Node, Integer> counters = new HashMap<>(); 317 for (NodePair pair : edges) { 318 Integer c = counters.get(pair.getA()); 319 counters.put(pair.getA(), c == null ? 1 : c + 1); 320 c = counters.get(pair.getB()); 321 counters.put(pair.getB(), c == null ? 1 : c + 1); 322 } 323 // group by counters 324 TreeMap<Integer, Set<Node>> sortedMap = new TreeMap<>(Comparator.reverseOrder()); 325 for (Entry<Node, Integer> e : counters.entrySet()) { 326 sortedMap.computeIfAbsent(e.getValue(), LinkedHashSet::new).add(e.getKey()); 327 } 328 LinkedHashSet<Node> result = new LinkedHashSet<>(); 329 for (Entry<Integer, Set<Node>> e : sortedMap.entrySet()) { 330 result.addAll(e.getValue()); 331 } 332 return result; 333 } 334 307 335 }
