Skip to content

Commit 218c847

Browse files
committed
Replace shortcut handlers in NodeBasedNodeContractor
1 parent 819a9dd commit 218c847

File tree

1 file changed

+57
-97
lines changed

1 file changed

+57
-97
lines changed

core/src/main/java/com/graphhopper/routing/ch/NodeBasedNodeContractor.java

Lines changed: 57 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,6 @@
3131

3232
class NodeBasedNodeContractor extends AbstractNodeContractor {
3333
private final Map<Shortcut, Shortcut> shortcuts = new HashMap<>();
34-
private final AddShortcutHandler addScHandler = new AddShortcutHandler();
35-
private final CalcShortcutHandler calcScHandler = new CalcShortcutHandler();
3634
private final Params params = new Params();
3735
private PrepareCHEdgeExplorer allEdgeExplorer;
3836
private NodeBasedWitnessPathSearcher prepareAlgo;
@@ -42,6 +40,9 @@ class NodeBasedNodeContractor extends AbstractNodeContractor {
4240
// meanDegree is the number of edges / number of nodes ratio of the graph, not really the average degree, because
4341
// each edge can exist in both directions
4442
private double meanDegree;
43+
// temporary counters used for priority calculation
44+
private int originalEdgesCount;
45+
private int shortcutsCount;
4546

4647
NodeBasedNodeContractor(PrepareCHGraph prepareGraph, PMap pMap) {
4748
super(prepareGraph);
@@ -78,24 +79,24 @@ public void close() {
7879
}
7980

8081
/**
81-
* Warning: the calculated priority must NOT depend on priority(v) and therefore findShortcuts should also not
82+
* Warning: the calculated priority must NOT depend on priority(v) and therefore handleShortcuts should also not
8283
* depend on the priority(v). Otherwise updating the priority before contracting in contractNodes() could lead to
8384
* a slowish or even endless loop.
8485
*/
8586
@Override
8687
public float calculatePriority(int node) {
87-
if (prepareGraph.getLevel(node) != maxLevel) {
88+
if (prepareGraph.getLevel(node) != maxLevel)
8889
throw new IllegalArgumentException("Priority should only be calculated for not yet contracted nodes");
89-
}
90-
CalcShortcutsResult calcShortcutsResult = calcShortcutCount(node);
9190

9291
// # huge influence: the bigger the less shortcuts gets created and the faster is the preparation
9392
//
9493
// every adjNode has an 'original edge' number associated. initially it is r=1
9594
// when a new shortcut is introduced then r of the associated edges is summed up:
9695
// r(u,w)=r(u,v)+r(v,w) now we can define
9796
// originalEdgesCount = σ(v) := sum_{ (u,w) ∈ shortcuts(v) } of r(u, w)
98-
int originalEdgesCount = calcShortcutsResult.originalEdgesCount;
97+
shortcutsCount = 0;
98+
originalEdgesCount = 0;
99+
handleShortcuts(node, this::countShortcut);
99100

100101
// # lowest influence on preparation speed or shortcut creation count
101102
// (but according to paper should speed up queries)
@@ -124,7 +125,7 @@ public float calculatePriority(int node) {
124125
// |shortcuts(v)| − |{(u, v) | v uncontracted}| − |{(v, w) | v uncontracted}|
125126
// meanDegree is used instead of outDegree+inDegree as if one adjNode is in both directions
126127
// only one bucket memory is used. Additionally one shortcut could also stand for two directions.
127-
int edgeDifference = calcShortcutsResult.shortcutsCount - degree;
128+
int edgeDifference = shortcutsCount - degree;
128129

129130
// according to the paper do a simple linear combination of the properties to get the priority.
130131
return params.edgeDifferenceWeight * edgeDifference +
@@ -135,8 +136,8 @@ public float calculatePriority(int node) {
135136
@Override
136137
public void contractNode(int node) {
137138
shortcuts.clear();
138-
long degree = findShortcuts(addScHandler.setNode(node));
139-
addedShortcutsCount += addShortcuts(shortcuts.keySet());
139+
long degree = handleShortcuts(node, this::addOrUpdateShortcut);
140+
addedShortcutsCount += writeShortcuts(shortcuts.keySet());
140141
// put weight factor on meanDegree instead of taking the average => meanDegree is more stable
141142
meanDegree = (meanDegree * 2 + degree) / 3;
142143
}
@@ -153,15 +154,15 @@ public String getStatisticsString() {
153154
* Returns the 'degree' of the handler's node (disregarding edges from/to already contracted nodes). Note that
154155
* here the degree is not the total number of adjacent edges, but only the number of incoming edges
155156
*/
156-
private long findShortcuts(ShortcutHandler sch) {
157+
private long handleShortcuts(int node, ShortcutHandler handler) {
157158
int maxVisitedNodes = getMaxVisitedNodesEstimate();
158159
long degree = 0;
159-
PrepareCHEdgeIterator incomingEdges = inEdgeExplorer.setBaseNode(sch.getNode());
160+
PrepareCHEdgeIterator incomingEdges = inEdgeExplorer.setBaseNode(node);
160161
// collect outgoing nodes (goal-nodes) only once
161162
while (incomingEdges.next()) {
162163
int fromNode = incomingEdges.getAdjNode();
163164
// accept only not-contracted nodes, do not consider loops at the node that is being contracted
164-
if (fromNode == sch.getNode() || isContracted(fromNode))
165+
if (fromNode == node || isContracted(fromNode))
165166
continue;
166167

167168
final double incomingEdgeWeight = incomingEdges.getWeight(true);
@@ -172,14 +173,14 @@ private long findShortcuts(ShortcutHandler sch) {
172173
int incomingEdge = incomingEdges.getEdge();
173174
int inOrigEdgeCount = getOrigEdgeCount(incomingEdge);
174175
// collect outgoing nodes (goal-nodes) only once
175-
PrepareCHEdgeIterator outgoingEdges = outEdgeExplorer.setBaseNode(sch.getNode());
176+
PrepareCHEdgeIterator outgoingEdges = outEdgeExplorer.setBaseNode(node);
176177
// force fresh maps etc as this cannot be determined by from node alone (e.g. same from node but different avoidNode)
177178
prepareAlgo.clear();
178179
degree++;
179180
while (outgoingEdges.next()) {
180181
int toNode = outgoingEdges.getAdjNode();
181182
// add only not-contracted nodes, do not consider loops at the node that is being contracted
182-
if (toNode == sch.getNode() || isContracted(toNode) || fromNode == toNode)
183+
if (toNode == node || isContracted(toNode) || fromNode == toNode)
183184
continue;
184185

185186
// Limit weight as ferries or forbidden edges can increase local search too much.
@@ -195,7 +196,7 @@ private long findShortcuts(ShortcutHandler sch) {
195196

196197
prepareAlgo.setWeightLimit(existingDirectWeight);
197198
prepareAlgo.setMaxVisitedNodes(maxVisitedNodes);
198-
prepareAlgo.ignoreNode(sch.getNode());
199+
prepareAlgo.ignoreNode(node);
199200

200201
dijkstraSW.start();
201202
dijkstraCount++;
@@ -207,7 +208,7 @@ private long findShortcuts(ShortcutHandler sch) {
207208
// FOUND witness path, so do not add shortcut
208209
continue;
209210

210-
sch.foundShortcut(fromNode, toNode, existingDirectWeight,
211+
handler.handleShortcut(fromNode, toNode, existingDirectWeight,
211212
outgoingEdges.getEdge(), getOrigEdgeCount(outgoingEdges.getEdge()),
212213
incomingEdge, inOrigEdgeCount);
213214
}
@@ -216,11 +217,11 @@ private long findShortcuts(ShortcutHandler sch) {
216217
}
217218

218219
/**
219-
* Adds the given shortcuts to the graph.
220+
* Actually writes the given shortcuts to the graph.
220221
*
221222
* @return the actual number of shortcuts that were added to the graph
222223
*/
223-
private int addShortcuts(Collection<Shortcut> shortcuts) {
224+
private int writeShortcuts(Collection<Shortcut> shortcuts) {
224225
int tmpNewShortcuts = 0;
225226
NEXT_SC:
226227
for (Shortcut sc : shortcuts) {
@@ -267,11 +268,6 @@ private int addShortcuts(Collection<Shortcut> shortcuts) {
267268
return tmpNewShortcuts;
268269
}
269270

270-
private CalcShortcutsResult calcShortcutCount(int node) {
271-
findShortcuts(calcScHandler.setNode(node));
272-
return calcScHandler.calcShortcutsResult;
273-
}
274-
275271
private String getCoords(PrepareCHEdgeIterator edge, NodeAccess na) {
276272
int base = edge.getBaseNode();
277273
int adj = edge.getAdjNode();
@@ -347,87 +343,51 @@ public String toString() {
347343
}
348344
}
349345

346+
@FunctionalInterface
350347
private interface ShortcutHandler {
351-
void foundShortcut(int fromNode, int toNode, double existingDirectWeight,
352-
int outgoingEdge, int outOrigEdgeCount,
353-
int incomingEdge, int inOrigEdgeCount);
354-
355-
int getNode();
348+
void handleShortcut(int fromNode, int toNode, double existingDirectWeight,
349+
int outgoingEdge, int outOrigEdgeCount,
350+
int incomingEdge, int inOrigEdgeCount);
356351
}
357352

358-
private class CalcShortcutHandler implements ShortcutHandler {
359-
int node;
360-
CalcShortcutsResult calcShortcutsResult = new CalcShortcutsResult();
361-
362-
@Override
363-
public int getNode() {
364-
return node;
365-
}
366-
367-
public CalcShortcutHandler setNode(int node) {
368-
this.node = node;
369-
calcShortcutsResult.originalEdgesCount = 0;
370-
calcShortcutsResult.shortcutsCount = 0;
371-
return this;
372-
}
373-
374-
@Override
375-
public void foundShortcut(int fromNode, int toNode, double existingDirectWeight,
376-
int outgoingEdge, int outOrigEdgeCount,
377-
int incomingEdge, int inOrigEdgeCount) {
378-
calcShortcutsResult.shortcutsCount++;
379-
calcShortcutsResult.originalEdgesCount += inOrigEdgeCount + outOrigEdgeCount;
380-
}
353+
private void countShortcut(int fromNode, int toNode, double existingDirectWeight,
354+
int outgoingEdge, int outOrigEdgeCount,
355+
int incomingEdge, int inOrigEdgeCount) {
356+
shortcutsCount++;
357+
originalEdgesCount += inOrigEdgeCount + outOrigEdgeCount;
381358
}
382359

383-
private class AddShortcutHandler implements ShortcutHandler {
384-
int node;
385-
386-
@Override
387-
public int getNode() {
388-
return node;
389-
}
390-
391-
public AddShortcutHandler setNode(int node) {
392-
shortcuts.clear();
393-
this.node = node;
394-
return this;
360+
/**
361+
* Adds a new shortcut to the temporary set of shortcuts or updates/merges it with an
362+
* existing one.
363+
*/
364+
private void addOrUpdateShortcut(int fromNode, int toNode, double existingDirectWeight,
365+
int outgoingEdge, int outOrigEdgeCount,
366+
int incomingEdge, int inOrigEdgeCount) {
367+
// FOUND shortcut
368+
// but be sure that it is the only shortcut in the collection
369+
// and also in the graph for u->w. If existing AND identical weight => update setProperties.
370+
// Hint: shortcuts are always one-way due to distinct level of every node but we don't
371+
// know yet the levels so we need to determine the correct direction or if both directions
372+
Shortcut sc = new Shortcut(fromNode, toNode, existingDirectWeight);
373+
if (shortcuts.containsKey(sc))
374+
return;
375+
376+
Shortcut tmpSc = new Shortcut(toNode, fromNode, existingDirectWeight);
377+
Shortcut tmpRetSc = shortcuts.get(tmpSc);
378+
// overwrite flags only if skipped edges are identical
379+
if (tmpRetSc != null && tmpRetSc.skippedEdge2 == incomingEdge && tmpRetSc.skippedEdge1 == outgoingEdge) {
380+
tmpRetSc.flags = PrepareEncoder.getScDirMask();
381+
return;
395382
}
396383

397-
@Override
398-
public void foundShortcut(int fromNode, int toNode, double existingDirectWeight,
399-
int outgoingEdge, int outOrigEdgeCount,
400-
int incomingEdge, int inOrigEdgeCount) {
401-
// FOUND shortcut
402-
// but be sure that it is the only shortcut in the collection
403-
// and also in the graph for u->w. If existing AND identical weight => update setProperties.
404-
// Hint: shortcuts are always one-way due to distinct level of every node but we don't
405-
// know yet the levels so we need to determine the correct direction or if both directions
406-
Shortcut sc = new Shortcut(fromNode, toNode, existingDirectWeight);
407-
if (shortcuts.containsKey(sc))
408-
return;
409-
410-
Shortcut tmpSc = new Shortcut(toNode, fromNode, existingDirectWeight);
411-
Shortcut tmpRetSc = shortcuts.get(tmpSc);
412-
// overwrite flags only if skipped edges are identical
413-
if (tmpRetSc != null && tmpRetSc.skippedEdge2 == incomingEdge && tmpRetSc.skippedEdge1 == outgoingEdge) {
414-
tmpRetSc.flags = PrepareEncoder.getScDirMask();
415-
return;
416-
}
417-
418-
Shortcut old = shortcuts.put(sc, sc);
419-
if (old != null)
420-
throw new IllegalStateException("Shortcut did not exist (" + sc + ") but was overwriting another one? " + old);
421-
422-
sc.skippedEdge1 = incomingEdge;
423-
sc.skippedEdge2 = outgoingEdge;
424-
sc.originalEdges = inOrigEdgeCount + outOrigEdgeCount;
425-
}
426-
}
384+
Shortcut old = shortcuts.put(sc, sc);
385+
if (old != null)
386+
throw new IllegalStateException("Shortcut did not exist (" + sc + ") but was overwriting another one? " + old);
427387

428-
private static class CalcShortcutsResult {
429-
int originalEdgesCount;
430-
int shortcutsCount;
388+
sc.skippedEdge1 = incomingEdge;
389+
sc.skippedEdge2 = outgoingEdge;
390+
sc.originalEdges = inOrigEdgeCount + outOrigEdgeCount;
431391
}
432392

433393
public static class Params {

0 commit comments

Comments
 (0)