Skip to content

Commit 742ca09

Browse files
committed
Handle via-way restrictions with multiple from/to ways correctly (#3100)
(cherry picked from commit a9e7ced)
1 parent c8d90ed commit 742ca09

5 files changed

Lines changed: 153 additions & 18 deletions

File tree

core/src/main/java/com/graphhopper/reader/osm/OSMRestrictionConverter.java

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,9 +208,14 @@ public static List<RestrictionSetter.Restriction> buildRestrictionsForOSMRestric
208208
List<RestrictionSetter.Restriction> result = new ArrayList<>();
209209
if (type == NO) {
210210
if (topology.isViaWayRestriction()) {
211-
if (topology.getFromEdges().size() > 1 || topology.getToEdges().size() > 1)
212-
throw new IllegalArgumentException("Via-way restrictions with multiple from- or to- edges are not supported");
213-
result.add(RestrictionSetter.createViaEdgeRestriction(collectEdges(topology)));
211+
for (IntCursor fromEdge : topology.getFromEdges())
212+
for (IntCursor toEdge : topology.getToEdges()) {
213+
IntArrayList edges = new IntArrayList(topology.getViaEdges().size()+2);
214+
edges.add(fromEdge.value);
215+
edges.addAll(topology.getViaEdges());
216+
edges.add(toEdge.value);
217+
result.add(RestrictionSetter.createViaEdgeRestriction(edges));
218+
}
214219
} else {
215220
for (IntCursor fromEdge : topology.getFromEdges())
216221
for (IntCursor toEdge : topology.getToEdges())
@@ -232,7 +237,7 @@ public static List<RestrictionSetter.Restriction> buildRestrictionsForOSMRestric
232237
private static IntArrayList collectEdges(RestrictionTopology r) {
233238
IntArrayList result = new IntArrayList(r.getViaEdges().size() + 2);
234239
result.add(r.getFromEdges().get(0));
235-
r.getViaEdges().iterator().forEachRemaining(c -> result.add(c.value));
240+
result.addAll(r.getViaEdges());
236241
result.add(r.getToEdges().get(0));
237242
return result;
238243
}

core/src/main/java/com/graphhopper/reader/osm/WayToEdgeConverter.java

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.carrotsearch.hppc.cursors.IntCursor;
2424
import com.carrotsearch.hppc.cursors.LongCursor;
2525
import com.graphhopper.storage.BaseGraph;
26+
import com.graphhopper.util.ArrayUtil;
2627
import com.graphhopper.util.EdgeIteratorState;
2728

2829
import java.util.ArrayList;
@@ -116,26 +117,36 @@ public EdgeResult convertForViaWays(LongArrayList fromWays, LongArrayList viaWay
116117
throw new OSMRestrictionException("has disconnected member ways");
117118
else if (solutions.size() > fromWays.size() * toWays.size())
118119
throw new OSMRestrictionException("has member ways that do not form a unique path");
119-
return buildResult(solutions, new EdgeResult(fromWays.size(), viaWays.size(), toWays.size()));
120+
return buildResult(solutions, fromWays, viaWays, toWays);
120121
}
121122

122-
private static EdgeResult buildResult(List<IntArrayList> edgeChains, EdgeResult result) {
123-
for (IntArrayList edgeChain : edgeChains) {
124-
result.fromEdges.add(edgeChain.get(0));
125-
if (result.nodes.isEmpty()) {
126-
// the via-edges and nodes are the same for edge chain
127-
for (int i = 1; i < edgeChain.size() - 3; i += 2) {
128-
result.nodes.add(edgeChain.get(i));
129-
result.viaEdges.add(edgeChain.get(i + 1));
130-
}
131-
result.nodes.add(edgeChain.get(edgeChain.size() - 2));
132-
}
133-
result.toEdges.add(edgeChain.get(edgeChain.size() - 1));
123+
private static EdgeResult buildResult(List<IntArrayList> edgeChains, LongArrayList fromWays, LongArrayList viaWays, LongArrayList toWays) {
124+
EdgeResult result = new EdgeResult(fromWays.size(), viaWays.size(), toWays.size());
125+
// we get multiple edge chains, but they are expected to be identical except for their first or last members
126+
IntArrayList firstChain = edgeChains.get(0);
127+
result.fromEdges.add(firstChain.get(0));
128+
for (int i = 1; i < firstChain.size() - 3; i += 2) {
129+
result.nodes.add(firstChain.get(i));
130+
result.viaEdges.add(firstChain.get(i + 1));
134131
}
132+
result.nodes.add(firstChain.get(firstChain.size() - 2));
133+
result.toEdges.add(firstChain.get(firstChain.size() - 1));
134+
// We keep the first/last elements of all chains in case there are multiple from/to ways
135+
List<IntArrayList> otherChains = edgeChains.subList(1, edgeChains.size());
136+
if (fromWays.size() > 1) {
137+
if (otherChains.stream().anyMatch(chain -> chain.get(chain.size() - 1) != firstChain.get(firstChain.size() - 1)))
138+
throw new IllegalArgumentException("edge chains were supposed to be the same except for their first elements, but got: " + edgeChains + " - for: " + fromWays + ", " + viaWays + ", " + toWays);
139+
otherChains.forEach(chain -> result.fromEdges.add(chain.get(0)));
140+
} else if (toWays.size() > 1) {
141+
if (otherChains.stream().anyMatch(chain -> chain.get(0) != firstChain.get(0)))
142+
throw new IllegalArgumentException("edge chains were supposed to be the same except for their last elements, but got: " + edgeChains + " - for: " + fromWays + ", " + viaWays + ", " + toWays);
143+
otherChains.forEach(chain -> result.toEdges.add(chain.get(chain.size() - 1)));
144+
} else if (!otherChains.isEmpty())
145+
throw new IllegalStateException("If there are multiple chains there must be either multiple from- or to-ways.");
135146
return result;
136147
}
137148

138-
private void findEdgeChain(long fromWay, LongArrayList viaWays, long toWay, List<IntArrayList> solutions) throws OSMRestrictionException {
149+
private void findEdgeChain(long fromWay, LongArrayList viaWays, long toWay, List<IntArrayList> solutions) {
139150
// For each edge chain there must be one edge associated with the from-way, at least one for each via-way and one
140151
// associated with the to-way. We use DFS with backtracking to find all edge chains that connect an edge
141152
// associated with the from-way with one associated with the to-way.

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,13 @@ public static IntArrayList invert(IntArrayList list) {
224224
return result;
225225
}
226226

227+
public static IntArrayList subList(IntArrayList list, int fromIndex, int toIndex) {
228+
IntArrayList result = new IntArrayList(toIndex - fromIndex);
229+
for (int i = fromIndex; i < toIndex; i++)
230+
result.add(list.get(i));
231+
return result;
232+
}
233+
227234
/**
228235
* @param a sorted array
229236
* @param b sorted array

core/src/test/java/com/graphhopper/reader/osm/WayToEdgeConverterTest.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,64 @@ void convertForViaWays_loop() {
124124
assertTrue(e.getMessage().contains("has member ways that do not form a unique path"), e.getMessage());
125125
}
126126

127+
@Test
128+
void convertForViaNode_multipleFrom() throws OSMRestrictionException {
129+
BaseGraph graph = new BaseGraph.Builder(1).create();
130+
graph.edge(1, 0);
131+
graph.edge(2, 0);
132+
graph.edge(3, 0);
133+
graph.edge(0, 4);
134+
WayToEdgeConverter.NodeResult nodeResult = new WayToEdgeConverter(graph, way -> IntArrayList.from(Math.toIntExact(way)).iterator())
135+
.convertForViaNode(ways(0, 1, 2), 0, ways(3));
136+
assertEquals(IntArrayList.from(0, 1, 2), nodeResult.getFromEdges());
137+
assertEquals(IntArrayList.from(3), nodeResult.getToEdges());
138+
}
139+
140+
@Test
141+
void convertForViaNode_multipleTo() throws OSMRestrictionException {
142+
BaseGraph graph = new BaseGraph.Builder(1).create();
143+
graph.edge(1, 0);
144+
graph.edge(2, 0);
145+
graph.edge(3, 0);
146+
graph.edge(0, 4);
147+
WayToEdgeConverter.NodeResult nodeResult = new WayToEdgeConverter(graph, way -> IntArrayList.from(Math.toIntExact(way)).iterator())
148+
.convertForViaNode(ways(3), 0, ways(0, 1, 2));
149+
assertEquals(IntArrayList.from(3), nodeResult.getFromEdges());
150+
assertEquals(IntArrayList.from(0, 1, 2), nodeResult.getToEdges());
151+
}
152+
153+
@Test
154+
void convertForViaWay_multipleFrom() throws OSMRestrictionException {
155+
BaseGraph graph = new BaseGraph.Builder(1).create();
156+
graph.edge(1, 0);
157+
graph.edge(2, 0);
158+
graph.edge(3, 0);
159+
graph.edge(0, 4);
160+
graph.edge(4, 5);
161+
WayToEdgeConverter.EdgeResult edgeResult = new WayToEdgeConverter(graph, way -> IntArrayList.from(Math.toIntExact(way)).iterator())
162+
.convertForViaWays(ways(0, 1, 2), ways(3), ways(4));
163+
assertEquals(IntArrayList.from(0, 1, 2), edgeResult.getFromEdges());
164+
assertEquals(IntArrayList.from(3), edgeResult.getViaEdges());
165+
assertEquals(IntArrayList.from(4), edgeResult.getToEdges());
166+
assertEquals(IntArrayList.from(0, 4), edgeResult.getNodes());
167+
}
168+
169+
@Test
170+
void convertForViaWay_multipleTo() throws OSMRestrictionException {
171+
BaseGraph graph = new BaseGraph.Builder(1).create();
172+
graph.edge(1, 0);
173+
graph.edge(2, 0);
174+
graph.edge(3, 0);
175+
graph.edge(0, 4);
176+
graph.edge(4, 5);
177+
WayToEdgeConverter.EdgeResult edgeResult = new WayToEdgeConverter(graph, way -> IntArrayList.from(Math.toIntExact(way)).iterator())
178+
.convertForViaWays(ways(4), ways(3), ways(0, 1, 2));
179+
assertEquals(IntArrayList.from(0, 1, 2), edgeResult.getToEdges());
180+
assertEquals(IntArrayList.from(3), edgeResult.getViaEdges());
181+
assertEquals(IntArrayList.from(4), edgeResult.getFromEdges());
182+
assertEquals(IntArrayList.from(4, 0), edgeResult.getNodes());
183+
}
184+
127185
private LongArrayList ways(long... ways) {
128186
return LongArrayList.from(ways);
129187
}

core/src/test/java/com/graphhopper/routing/util/parsers/OSMRestrictionSetterTest.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,56 @@ void loop_only() {
415415
assertEquals(NO_PATH, calcPath(3, 4));
416416
}
417417

418+
@Test
419+
void multiFrom_viaWay() {
420+
// 1 \ / 5
421+
// 2 - 0 - 4 - 6
422+
// 3 /
423+
int e1_0 = edge(1, 0);
424+
int e2_0 = edge(2, 0);
425+
int e3_0 = edge(3, 0);
426+
int e0_4 = edge(0, 4);
427+
int e4_5 = edge(4, 5);
428+
int e4_6 = edge(4, 6);
429+
setRestrictions(List.of(
430+
// "no_entry" via-way restrictions have multiple from edges
431+
new Pair<>(RestrictionTopology.way(edges(e1_0, e2_0, e3_0), edges(e0_4), edges(e4_6), nodes(1, 4)), RestrictionType.NO)
432+
));
433+
for (int s = 1; s <= 3; s++) {
434+
assertEquals(nodes(s, 0, 4, 5), calcPath(s, 5));
435+
assertEquals(NO_PATH, calcPath(s, 6));
436+
assertEquals(nodes(5, 4, 0, s), calcPath(5, s));
437+
assertEquals(nodes(6, 4, 0, s), calcPath(6, s));
438+
}
439+
assertEquals(nodes(1, 0, 2), calcPath(1, 2));
440+
assertEquals(nodes(3, 0, 1), calcPath(3, 1));
441+
}
442+
443+
@Test
444+
void multiTo_viaWay() {
445+
// 1 \ / 4
446+
// 2 - 0 - 3 - 5
447+
// \ 6
448+
int e1_0 = edge(1, 0);
449+
int e2_0 = edge(2, 0);
450+
int e0_3 = edge(0, 3);
451+
int e3_4 = edge(3, 4);
452+
int e3_5 = edge(3, 5);
453+
int e3_6 = edge(3, 6);
454+
setRestrictions(List.of(
455+
// "no_exit" via-way restrictions have multiple to edges
456+
new Pair<>(RestrictionTopology.way(edges(e1_0), edges(e0_3), edges(e3_4, e3_5, e3_6), nodes(0, 3)), RestrictionType.NO)
457+
));
458+
for (int s = 4; s <= 6; s++) {
459+
assertEquals(NO_PATH, calcPath(1, s));
460+
assertEquals(nodes(2, 0, 3, s), calcPath(2, s));
461+
assertEquals(nodes(s, 3, 0, 1), calcPath(s, 1));
462+
assertEquals(nodes(s, 3, 0, 2), calcPath(s, 2));
463+
}
464+
assertEquals(nodes(4, 3, 5), calcPath(4, 5));
465+
assertEquals(nodes(5, 3, 6), calcPath(5, 6));
466+
}
467+
418468
/**
419469
* Shorthand version that only sets restriction for the first turn restriction encoded value
420470
*/
@@ -468,6 +518,10 @@ public long calcTurnMillis(int inEdge, int viaNode, int outEdge) {
468518
})), TraversalMode.EDGE_BASED).calcPath(from, to).calcNodes());
469519
}
470520

521+
private IntArrayList edges(int... edges) {
522+
return IntArrayList.from(edges);
523+
}
524+
471525
private IntArrayList nodes(int... nodes) {
472526
return IntArrayList.from(nodes);
473527
}

0 commit comments

Comments
 (0)