Changeset 15554 in josm for trunk/src/org
- Timestamp:
- 2019-12-02T19:47:42+01:00 (5 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/osm/NodeGraph.java
r12478 r15554 2 2 package org.openstreetmap.josm.data.osm; 3 3 4 import java.util.ArrayDeque; 4 5 import java.util.ArrayList; 5 6 import java.util.Collection; 6 7 import java.util.Collections; 8 import java.util.Comparator; 9 import java.util.Deque; 10 import java.util.HashMap; 11 import java.util.HashSet; 7 12 import java.util.LinkedHashMap; 8 13 import java.util.LinkedHashSet; … … 10 15 import java.util.List; 11 16 import java.util.Map; 17 import java.util.Map.Entry; 12 18 import java.util.Optional; 13 19 import java.util.Set; 14 20 import java.util.Stack; 21 import java.util.TreeMap; 15 22 16 23 import org.openstreetmap.josm.tools.Pair; … … 136 143 137 144 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<>(); 145 List<NodePair> l = successors.computeIfAbsent(pair.getA(), k -> new ArrayList<>()); 146 if (!l.contains(pair)) { 144 147 l.add(pair); 145 successors.put(pair.getA(), l);146 148 } 147 149 } 148 150 149 151 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<>(); 152 List<NodePair> l = predecessors.computeIfAbsent(pair.getB(), k -> new ArrayList<>()); 153 if (!l.contains(pair)) { 156 154 l.add(pair); 157 predecessors.put(pair.getB(), l);158 155 } 159 156 } … … 240 237 } 241 238 242 protected boolean isSpanningWay( Stack<NodePair> way) {239 protected boolean isSpanningWay(Collection<NodePair> way) { 243 240 return numUndirectedEges == way.size(); 244 241 } … … 263 260 protected List<Node> buildSpanningPath(Node startNode) { 264 261 if (startNode != null) { 262 // do not simply replace `Stack` by `ArrayDeque` because of different iteration behaviour, see #11957 265 263 Stack<NodePair> path = new Stack<>(); 264 Set<NodePair> dupCheck = new HashSet<>(); 266 265 Stack<NodePair> nextPairs = new Stack<>(); 267 266 nextPairs.addAll(getOutboundPairs(startNode)); 268 267 while (!nextPairs.isEmpty()) { 269 268 NodePair cur = nextPairs.pop(); 270 if (! path.contains(cur) && !path.contains(cur.swap())) {269 if (!dupCheck.contains(cur) && !dupCheck.contains(cur.swap())) { 271 270 while (!path.isEmpty() && !path.peek().isPredecessorOf(cur)) { 272 path.pop();271 dupCheck.remove(path.pop()); 273 272 } 274 273 path.push(cur); 274 dupCheck.add(cur); 275 275 if (isSpanningWay(path)) return buildPathFromNodePairs(path); 276 276 nextPairs.addAll(getOutboundPairs(path.peek())); … … 284 284 * Tries to find a path through the graph which visits each edge (i.e. 285 285 * the segment of a way) exactly once. 286 * <p><b>Note that duplicated edges are removed first!</b> 286 287 * 287 288 * @return the path; null, if no path was found … … 289 290 public List<Node> buildSpanningPath() { 290 291 prepare(); 291 // try to find a path from each "terminal node", i.e. from a 292 // node which is connected by exactly one undirected edges (or 293 // two directed edges in opposite direction) to the graph. A 294 // 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 // 298 Set<Node> nodes = getTerminalNodes(); 299 nodes = nodes.isEmpty() ? getNodes() : nodes; 300 for (Node n: nodes) { 301 List<Node> path = buildSpanningPath(n); 302 if (!path.isEmpty()) 303 return path; 292 if(numUndirectedEges > 0 && isConnected()) { 293 // try to find a path from each "terminal node", i.e. from a 294 // node which is connected by exactly one undirected edges (or 295 // two directed edges in opposite direction) to the graph. A 296 // graph built up from way segments is likely to include such 297 // nodes, unless the edges build one or more closed rings. 298 // We order the nodes to start with the best candidates, but 299 // it might take very long if there is no valid path as we iterate over all nodes 300 // to find out. 301 Set<Node> nodes = getTerminalNodes(); 302 nodes = nodes.isEmpty() ? getMostFrequentVisitedNodesFirst() : nodes; 303 for (Node n : nodes) { 304 List<Node> path = buildSpanningPath(n); 305 if (!path.isEmpty()) 306 return path; 307 } 304 308 } 305 309 return null; 306 310 } 311 312 /** 313 * Find out if the graph is connected. 314 * @return true if it is connected. 315 */ 316 private boolean isConnected() { 317 Set<Node> nodes = getNodes(); 318 if (nodes.isEmpty()) 319 return false; 320 Deque<Node> toVisit = new ArrayDeque<>(); 321 HashSet<Node> visited = new HashSet<>(); 322 toVisit.add(nodes.iterator().next()); 323 while(!toVisit.isEmpty()) { 324 Node n = toVisit.pop(); 325 if (!visited.contains(n)) { 326 List<NodePair> neighbours = getOutboundPairs(n); 327 for (NodePair pair : neighbours) { 328 toVisit.addLast(pair.getA()); 329 toVisit.addLast(pair.getB()); 330 } 331 visited.add(n); 332 } 333 } 334 return nodes.size() == visited.size(); 335 } 336 337 /** 338 * Sort the nodes by number of appearances in the edges. 339 * @return set of nodes which can be start nodes in a spanning way. 340 */ 341 private Set<Node> getMostFrequentVisitedNodesFirst() { 342 if (edges.isEmpty()) 343 return Collections.emptySet(); 344 // count appearance of nodes in edges 345 Map<Node, Integer> counters = new HashMap<>(); 346 for (NodePair pair : edges) { 347 Integer c = counters.get(pair.getA()); 348 counters.put(pair.getA(), c == null ? 1 : c + 1); 349 c = counters.get(pair.getB()); 350 counters.put(pair.getB(), c == null ? 1 : c + 1); 351 } 352 // group by counters 353 TreeMap<Integer, Set<Node>> sortedMap = new TreeMap<>(Comparator.reverseOrder()); 354 for (Entry<Node, Integer> e : counters.entrySet()) { 355 sortedMap.computeIfAbsent(e.getValue(), LinkedHashSet::new).add(e.getKey()); 356 } 357 LinkedHashSet<Node> result = new LinkedHashSet<>(); 358 for (Entry<Integer, Set<Node>> e : sortedMap.entrySet()) { 359 if (e.getKey() > 4 || result.isEmpty()) { 360 result.addAll(e.getValue()); 361 } 362 } 363 return result; 364 } 365 307 366 }
Note:
See TracChangeset
for help on using the changeset viewer.