Skip to content

Commit f8ea4c5

Browse files
authored
Merge pull request graphhopper#977 from graphhopper/issue_976
Fix CH preparation bug graphhopper#976
2 parents 9bef6d5 + 3535805 commit f8ea4c5

File tree

9 files changed

+101
-47
lines changed

9 files changed

+101
-47
lines changed

core/src/main/java/com/graphhopper/routing/VirtualEdgeIterator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ public void setSkippedEdges(int edge1, int edge2) {
187187
}
188188

189189
@Override
190-
public boolean canBeOverwritten(long flags) {
190+
public int getMergeStatus(long flags) {
191191
throw new UnsupportedOperationException("Not supported.");
192192
}
193193
}

core/src/main/java/com/graphhopper/routing/VirtualEdgeIteratorState.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ public int getAdditionalField() {
177177
}
178178

179179
@Override
180-
public boolean canBeOverwritten(long flags) {
180+
public int getMergeStatus(long flags) {
181181
throw new UnsupportedOperationException("Not supported.");
182182
}
183183

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

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,7 @@
2727
import org.slf4j.Logger;
2828
import org.slf4j.LoggerFactory;
2929

30-
import java.util.HashMap;
31-
import java.util.Map;
32-
import java.util.Random;
33-
import java.util.Set;
30+
import java.util.*;
3431

3532
import static com.graphhopper.util.Parameters.Algorithms.ASTAR_BI;
3633
import static com.graphhopper.util.Parameters.Algorithms.DIJKSTRA_BI;
@@ -281,6 +278,7 @@ void contractNodes() {
281278

282279
counter++;
283280
int polledNode = sortedNodes.pollKey();
281+
284282
if (!sortedNodes.isEmpty() && sortedNodes.getSize() < lastNodesLazyUpdates) {
285283
lazySW.start();
286284
int priority = oldPriorities[polledNode] = calculatePriority(polledNode);
@@ -293,8 +291,10 @@ void contractNodes() {
293291
lazySW.stop();
294292
}
295293

296-
// contract!
297-
newShortcuts += addShortcuts(polledNode);
294+
// contract node v!
295+
shortcuts.clear();
296+
findShortcuts(addScHandler.setNode(polledNode));
297+
newShortcuts += addShortcuts(shortcuts.keySet());
298298
prepareGraph.setLevel(polledNode, level);
299299
level++;
300300

@@ -505,19 +505,27 @@ void findShortcuts(ShortcutHandler sch) {
505505
/**
506506
* Introduces the necessary shortcuts for adjNode v in the graph.
507507
*/
508-
int addShortcuts(int v) {
509-
shortcuts.clear();
510-
findShortcuts(addScHandler.setNode(v));
508+
int addShortcuts(Collection<Shortcut> tmpShortcuts) {
511509
int tmpNewShortcuts = 0;
512510
NEXT_SC:
513-
for (Shortcut sc : shortcuts.keySet()) {
511+
for (Shortcut sc : tmpShortcuts) {
514512
boolean updatedInGraph = false;
515513
// check if we need to update some existing shortcut in the graph
516514
CHEdgeIterator iter = vehicleOutExplorer.setBaseNode(sc.from);
517515
while (iter.next()) {
518-
if (iter.isShortcut() && iter.getAdjNode() == sc.to && iter.canBeOverwritten(sc.flags)) {
519-
if (sc.weight >= prepareWeighting.calcWeight(iter, false, EdgeIterator.NO_EDGE))
516+
if (iter.isShortcut() && iter.getAdjNode() == sc.to) {
517+
int status = iter.getMergeStatus(sc.flags);
518+
if (status == 0)
519+
continue;
520+
521+
if (sc.weight >= prepareWeighting.calcWeight(iter, false, EdgeIterator.NO_EDGE)) {
522+
// special case if a bidirectional shortcut has worse weight and still has to be added as otherwise the opposite direction would be missing
523+
// see testShortcutMergeBug
524+
if (status == 2)
525+
break;
526+
520527
continue NEXT_SC;
528+
}
521529

522530
if (iter.getEdge() == sc.skippedEdge1 || iter.getEdge() == sc.skippedEdge2) {
523531
throw new IllegalStateException("Shortcut cannot update itself! " + iter.getEdge()
@@ -871,7 +879,10 @@ public void foundShortcut(int u_fromNode, int w_toNode,
871879
}
872880
}
873881

874-
shortcuts.put(sc, sc);
882+
Shortcut old = shortcuts.put(sc, sc);
883+
if (old != null)
884+
throw new IllegalStateException("Shortcut did not exist (" + sc + ") but was overwriting another one? " + old);
885+
875886
sc.skippedEdge1 = skippedEdge1;
876887
sc.skippedEdge2 = outgoingEdges.getEdge();
877888
sc.originalEdges = incomingEdgeOrigCount + getOrigEdgeCount(outgoingEdges.getEdge());

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

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
package com.graphhopper.routing.ch;
1919

2020
/**
21-
* The flags are stored differently for shortcuts: just one weight and the direction flags.
22-
* <p>
21+
* The flags are stored differently for shortcuts: just one weight and the two direction bits which is handled by this
22+
* class for now as static methods.
2323
*
2424
* @author Peter Karich
2525
*/
@@ -29,33 +29,49 @@ public class PrepareEncoder {
2929
private static final long scBwdDir = 0x2;
3030
private static final long scDirMask = 0x3;
3131

32+
/**
33+
* A bitmask for two directions
34+
*/
3235
public static final long getScDirMask() {
3336
return scDirMask;
3437
}
3538

39+
/**
40+
* The bit for forward direction
41+
*/
3642
public static final long getScFwdDir() {
3743
return scFwdDir;
3844
}
3945

46+
/**
47+
* The bit for backward direction
48+
*/
4049
public static final long getScBwdDir() {
4150
return scBwdDir;
4251
}
4352

4453
/**
45-
* Returns true if flags1 can be overwritten in the edge by flags2 without restricting or
46-
* changing the directions of flags1.
47-
* <p>
54+
* Returns 1 if existingScFlags of an existing shortcut can be overwritten with a new shortcut by
55+
* newScFlags without limiting or changing the directions of the existing shortcut.
56+
* The method returns 2 for the same condition but only if the new shortcut has to be added
57+
* even if weight is higher than existing shortcut weight.
58+
* <pre>
59+
* | newScFlags:
60+
* existingScFlags | -> | <- | <->
61+
* -> | 1 | 0 | 2
62+
* <- | 0 | 1 | 2
63+
* <-> | 0 | 0 | 1
64+
* </pre>
4865
*
49-
* @return true if flags2 is enabled in both directions or if both flags are pointing into the
50-
* same direction.
66+
* @return 1 if newScFlags is identical to existingScFlags for the two direction bits and 0 otherwise.
67+
* There are two special cases when it returns 2.
5168
*/
52-
// \ flags2:
53-
// flags1 \ -> | <- | <->
54-
// -> t | f | t
55-
// <- f | t | t
56-
// <-> f | f | t
57-
public static final boolean canBeOverwritten(long flags1, long flags2) {
58-
return (flags2 & scDirMask) == scDirMask
59-
|| (flags1 & scDirMask) == (flags2 & scDirMask);
69+
public static final int getScMergeStatus(long existingScFlags, long newScFlags) {
70+
if ((existingScFlags & scDirMask) == (newScFlags & scDirMask))
71+
return 1;
72+
else if ((newScFlags & scDirMask) == scDirMask)
73+
return 2;
74+
75+
return 0;
6076
}
6177
}

core/src/main/java/com/graphhopper/storage/CHGraphImpl.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -523,8 +523,8 @@ public final EdgeIteratorState setWayGeometry(PointList list) {
523523
}
524524

525525
@Override
526-
public boolean canBeOverwritten(long flags) {
527-
return PrepareEncoder.canBeOverwritten(getDirectFlags(), flags);
526+
public int getMergeStatus(long flags) {
527+
return PrepareEncoder.getScMergeStatus(getDirectFlags(), flags);
528528
}
529529
}
530530

@@ -619,8 +619,8 @@ public final double getWeight() {
619619
}
620620

621621
@Override
622-
public boolean canBeOverwritten(long flags) {
623-
return PrepareEncoder.canBeOverwritten(getDirectFlags(), flags);
622+
public int getMergeStatus(long flags) {
623+
return PrepareEncoder.getScMergeStatus(getDirectFlags(), flags);
624624
}
625625
}
626626
}

core/src/main/java/com/graphhopper/util/CHEdgeIteratorState.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,10 @@ public interface CHEdgeIteratorState extends EdgeIteratorState {
4545

4646
/**
4747
* This method is only used on preparation.
48-
* <p>
4948
*
50-
* @see PrepareEncoder#canBeOverwritten(long, long)
49+
* @see PrepareEncoder#getScMergeStatus(long, long)
5150
*/
52-
boolean canBeOverwritten(long flags);
51+
int getMergeStatus(long flags);
5352

5453
/**
5554
* Returns the weight of this shortcut.

core/src/main/java/com/graphhopper/util/GHUtility.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ public CHEdgeIteratorState setWeight(double weight) {
491491
}
492492

493493
@Override
494-
public boolean canBeOverwritten(long flags) {
494+
public int getMergeStatus(long flags) {
495495
throw new UnsupportedOperationException("Not supported. Edge is empty.");
496496
}
497497
}

core/src/test/java/com/graphhopper/routing/ch/PrepareContractionHierarchiesTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,4 +645,31 @@ void checkPath(GraphHopperStorage ghStorage, Weighting w, int expShortcuts, doub
645645
assertEquals(w.toString(), expDistance, p.getDistance(), 1e-5);
646646
assertEquals(w.toString(), expNodes, p.calcNodes());
647647
}
648+
649+
@Test
650+
public void testShortcutMergeBug() {
651+
// We refer to this real world situation http://www.openstreetmap.org/#map=19/52.71205/-1.77326
652+
// assume the following graph:
653+
//
654+
// ---1---->----2-----3
655+
// \--------/
656+
//
657+
// where there are two roads from 1 to 2 and the directed road has a smaller weight
658+
// leading to two shortcuts sc1 (unidir) and sc2 (bidir) where the second should NOT be rejected due to the larger weight
659+
GraphHopperStorage g = createGHStorage();
660+
g.edge(1, 2, 1, true);
661+
g.edge(1, 2, 1, false);
662+
g.edge(2, 3, 1, true);
663+
664+
CHGraph lg = g.getGraph(CHGraph.class);
665+
PrepareContractionHierarchies prepare = new PrepareContractionHierarchies(dir, g, lg, weighting, tMode);
666+
prepare.initFromGraph();
667+
668+
// order is important here
669+
Shortcut sc1 = new Shortcut(1, 3, 6.81620625, 121.18);
670+
Shortcut sc2 = new Shortcut(1, 3, 6.82048125, 121.25);
671+
sc2.flags = PrepareEncoder.getScDirMask();
672+
List<Shortcut> list = Arrays.asList(sc1, sc2);
673+
assertEquals(2, prepare.addShortcuts(list));
674+
}
648675
}

core/src/test/java/com/graphhopper/routing/ch/PrepareEncoderTest.java

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import org.junit.Test;
2121

22+
import static org.junit.Assert.assertEquals;
2223
import static org.junit.Assert.assertFalse;
2324
import static org.junit.Assert.assertTrue;
2425

@@ -32,15 +33,15 @@ public void testOverwrite() {
3233
long forward = PrepareEncoder.getScFwdDir();
3334
long backward = PrepareEncoder.getScFwdDir() ^ PrepareEncoder.getScDirMask();
3435
long both = PrepareEncoder.getScDirMask();
35-
assertTrue(PrepareEncoder.canBeOverwritten(forward, forward));
36-
assertTrue(PrepareEncoder.canBeOverwritten(backward, backward));
37-
assertTrue(PrepareEncoder.canBeOverwritten(forward, both));
38-
assertTrue(PrepareEncoder.canBeOverwritten(backward, both));
36+
assertEquals(1, PrepareEncoder.getScMergeStatus(forward, forward));
37+
assertEquals(1, PrepareEncoder.getScMergeStatus(backward, backward));
38+
assertEquals(2, PrepareEncoder.getScMergeStatus(forward, both));
39+
assertEquals(2, PrepareEncoder.getScMergeStatus(backward, both));
3940

40-
assertTrue(PrepareEncoder.canBeOverwritten(both, both));
41-
assertFalse(PrepareEncoder.canBeOverwritten(both, forward));
42-
assertFalse(PrepareEncoder.canBeOverwritten(both, backward));
43-
assertFalse(PrepareEncoder.canBeOverwritten(forward, backward));
44-
assertFalse(PrepareEncoder.canBeOverwritten(backward, forward));
41+
assertEquals(1, PrepareEncoder.getScMergeStatus(both, both));
42+
assertEquals(0, PrepareEncoder.getScMergeStatus(both, forward));
43+
assertEquals(0, PrepareEncoder.getScMergeStatus(both, backward));
44+
assertEquals(0, PrepareEncoder.getScMergeStatus(forward, backward));
45+
assertEquals(0, PrepareEncoder.getScMergeStatus(backward, forward));
4546
}
4647
}

0 commit comments

Comments
 (0)