Skip to content

Commit b3c4607

Browse files
authored
ModeAccessParser improvements (graphhopper#3026)
* include barriers properly; service=emergency_access; ferry access * minor * minor clean up * simplify with toSet
1 parent db87593 commit b3c4607

File tree

5 files changed

+92
-33
lines changed

5 files changed

+92
-33
lines changed

core/src/main/java/com/graphhopper/routing/ev/DefaultImportRegistry.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@
2020

2121
import com.graphhopper.routing.util.*;
2222
import com.graphhopper.routing.util.parsers.*;
23-
24-
import java.util.Arrays;
25-
import java.util.stream.Collectors;
23+
import com.graphhopper.util.PMap;
2624

2725
public class DefaultImportRegistry implements ImportRegistry {
2826
@Override
@@ -209,15 +207,17 @@ else if (BikeNetwork.KEY.equals(name) || MtbNetwork.KEY.equals(name) || FootNetw
209207

210208
else if (BusAccess.KEY.equals(name))
211209
return ImportUnit.create(name, props -> BusAccess.create(),
212-
(lookup, props) -> new ModeAccessParser(TransportationMode.BUS, lookup.getBooleanEncodedValue(BusAccess.KEY),
213-
lookup.getBooleanEncodedValue(Roundabout.KEY), Arrays.stream(props.getString("restrictions", "").split(";")).filter(s -> !s.isEmpty()).collect(Collectors.toList())),
210+
(lookup, props) -> new ModeAccessParser(TransportationMode.BUS,
211+
lookup.getBooleanEncodedValue(name), true, lookup.getBooleanEncodedValue(Roundabout.KEY),
212+
PMap.toSet(props.getString("restrictions", "")), PMap.toSet(props.getString("barriers", ""))),
214213
"roundabout"
215214
);
216215

217216
else if (HovAccess.KEY.equals(name))
218217
return ImportUnit.create(name, props -> HovAccess.create(),
219-
(lookup, props) -> new ModeAccessParser(TransportationMode.HOV, lookup.getBooleanEncodedValue(HovAccess.KEY),
220-
lookup.getBooleanEncodedValue(Roundabout.KEY), Arrays.stream(props.getString("restrictions", "").split(";")).filter(s -> !s.isEmpty()).collect(Collectors.toList())),
218+
(lookup, props) -> new ModeAccessParser(TransportationMode.HOV,
219+
lookup.getBooleanEncodedValue(name), true, lookup.getBooleanEncodedValue(Roundabout.KEY),
220+
PMap.toSet(props.getString("restrictions", "")), PMap.toSet(props.getString("barriers", ""))),
221221
"roundabout"
222222
);
223223
else if (FootTemporalAccess.KEY.equals(name))

core/src/main/java/com/graphhopper/routing/util/parsers/CarAccessParser.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ public WayAccess getAccess(ReaderWay way) {
106106
if (!highwayValues.contains(highwayValue))
107107
return WayAccess.CAN_SKIP;
108108

109+
// this is a very rare tagging which we should/could remove (the status key itself is described as "vague")
109110
if (way.hasTag("impassable", "yes") || way.hasTag("status", "impassable"))
110111
return WayAccess.CAN_SKIP;
111112

core/src/main/java/com/graphhopper/routing/util/parsers/ModeAccessParser.java

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,51 +7,77 @@
77
import com.graphhopper.routing.util.TransportationMode;
88
import com.graphhopper.storage.IntsRef;
99

10-
import java.util.*;
10+
import java.util.List;
11+
import java.util.Map;
12+
import java.util.Set;
1113

1214
import static com.graphhopper.routing.util.parsers.OSMTemporalAccessParser.hasTemporalRestriction;
1315

1416
public class ModeAccessParser implements TagParser {
1517

16-
private final static Set<String> onewaysForward = new HashSet<>(Arrays.asList("yes", "true", "1"));
18+
private static final Set<String> CAR_BARRIERS = Set.of("kissing_gate", "fence",
19+
"bollard", "stile", "turnstile", "cycle_barrier", "motorcycle_barrier", "block",
20+
"bus_trap", "sump_buster", "jersey_barrier");
21+
private static final Set<String> INTENDED = Set.of("yes", "designated", "official", "permissive", "private", "permit");
22+
private static final Set<String> ONEWAYS_FW = Set.of("yes", "true", "1");
1723
private final Set<String> restrictedValues;
1824
private final List<String> restrictionKeys;
1925
private final List<String> vehicleForward;
2026
private final List<String> vehicleBackward;
2127
private final List<String> ignoreOnewayKeys;
2228
private final BooleanEncodedValue accessEnc;
2329
private final BooleanEncodedValue roundaboutEnc;
30+
private final boolean skipEmergency;
31+
private final Set<String> barriers;
2432

25-
public ModeAccessParser(TransportationMode mode, BooleanEncodedValue accessEnc, BooleanEncodedValue roundaboutEnc, List<String> restrictions) {
33+
public ModeAccessParser(TransportationMode mode, BooleanEncodedValue accessEnc,
34+
boolean skipEmergency, BooleanEncodedValue roundaboutEnc,
35+
Set<String> restrictions, Set<String> barriers) {
2636
this.accessEnc = accessEnc;
2737
this.roundaboutEnc = roundaboutEnc;
2838
restrictionKeys = OSMRoadAccessParser.toOSMRestrictions(mode);
2939
vehicleForward = restrictionKeys.stream().map(r -> r + ":forward").toList();
3040
vehicleBackward = restrictionKeys.stream().map(r -> r + ":backward").toList();
3141
ignoreOnewayKeys = restrictionKeys.stream().map(r -> "oneway:" + r).toList();
32-
restrictedValues = new HashSet<>(restrictions.isEmpty() ? Arrays.asList("no", "restricted", "military", "emergency") : restrictions);
42+
restrictedValues = restrictions.isEmpty() ? Set.of("no", "restricted", "military", "emergency") : restrictions;
43+
this.barriers = barriers.isEmpty() ? CAR_BARRIERS : barriers;
3344
if (restrictedValues.contains(""))
3445
throw new IllegalArgumentException("restriction values cannot contain empty string");
46+
this.skipEmergency = skipEmergency;
3547
}
3648

3749
@Override
3850
public void handleWayTags(int edgeId, EdgeIntAccess edgeIntAccess, ReaderWay way, IntsRef relationFlags) {
51+
String highwayValue = way.getTag("highway");
52+
if (skipEmergency && "service".equals(highwayValue) && "emergency_access".equals(way.getTag("service")))
53+
return;
54+
3955
int firstIndex = way.getFirstIndex(restrictionKeys);
4056
String firstValue = firstIndex < 0 ? "" : way.getTag(restrictionKeys.get(firstIndex), "");
4157
if (restrictedValues.contains(firstValue) && !hasTemporalRestriction(way, firstIndex, restrictionKeys))
4258
return;
4359

4460
if (way.hasTag("gh:barrier_edge") && way.hasTag("node_tags")) {
4561
List<Map<String, Object>> nodeTags = way.getTag("node_tags", null);
62+
Map<String, Object> firstNodeTags = nodeTags.get(0);
4663
// a barrier edge has the restriction in both nodes and the tags are the same -> get(0)
47-
firstValue = getFirstPriorityNodeTag(nodeTags.get(0), restrictionKeys);
48-
if (restrictedValues.contains(firstValue))
64+
firstValue = getFirstPriorityNodeTag(firstNodeTags, restrictionKeys);
65+
String barrierValue = firstNodeTags.containsKey("barrier") ? (String) firstNodeTags.get("barrier") : "";
66+
if (restrictedValues.contains(firstValue) || barriers.contains(barrierValue)
67+
|| "yes".equals(firstNodeTags.get("locked")) && !INTENDED.contains(firstValue))
4968
return;
5069
}
5170

5271
if (FerrySpeedCalculator.isFerry(way)) {
53-
accessEnc.setBool(false, edgeId, edgeIntAccess, true);
54-
accessEnc.setBool(true, edgeId, edgeIntAccess, true);
72+
boolean isCar = restrictionKeys.contains("motorcar");
73+
if (INTENDED.contains(firstValue)
74+
// implied default is allowed only if foot and bicycle is not specified:
75+
|| isCar && firstValue.isEmpty() && !way.hasTag("foot") && !way.hasTag("bicycle")
76+
// if hgv is allowed then smaller trucks and cars are allowed too even if not specified
77+
|| isCar && way.hasTag("hgv", "yes")) {
78+
accessEnc.setBool(false, edgeId, edgeIntAccess, true);
79+
accessEnc.setBool(true, edgeId, edgeIntAccess, true);
80+
}
5581
} else {
5682
boolean isRoundabout = roundaboutEnc.getBool(false, edgeId, edgeIntAccess);
5783
boolean ignoreOneway = "no".equals(way.getFirstValue(ignoreOnewayKeys));
@@ -80,6 +106,6 @@ protected boolean isBackwardOneway(ReaderWay way) {
80106

81107
protected boolean isForwardOneway(ReaderWay way) {
82108
// vehicle:backward=no is like oneway=yes
83-
return way.hasTag("oneway", onewaysForward) || "no".equals(way.getFirstValue(vehicleBackward));
109+
return way.hasTag("oneway", ONEWAYS_FW) || "no".equals(way.getFirstValue(vehicleBackward));
84110
}
85111
}

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

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,17 @@
66
import com.graphhopper.routing.util.TransportationMode;
77
import org.junit.jupiter.api.Test;
88

9-
import java.util.Arrays;
10-
import java.util.HashMap;
11-
import java.util.List;
12-
import java.util.Map;
9+
import java.util.*;
1310

1411
import static org.junit.jupiter.api.Assertions.assertFalse;
1512
import static org.junit.jupiter.api.Assertions.assertTrue;
1613

1714
class ModeAccessParserTest {
1815

1916
private final EncodingManager em = new EncodingManager.Builder().add(Roundabout.create()).add(BusAccess.create()).build();
20-
private final ModeAccessParser parser = new ModeAccessParser(TransportationMode.BUS, em.getBooleanEncodedValue(BusAccess.KEY), em.getBooleanEncodedValue(Roundabout.KEY), List.of());
17+
private final ModeAccessParser parser = new ModeAccessParser(TransportationMode.BUS,
18+
em.getBooleanEncodedValue(BusAccess.KEY), true,
19+
em.getBooleanEncodedValue(Roundabout.KEY), Set.of(), Set.of());
2120
private final BooleanEncodedValue busAccessEnc = em.getBooleanEncodedValue(BusAccess.KEY);
2221

2322
@Test
@@ -140,33 +139,58 @@ public void testBusNodeAccess() {
140139
way.setTag("highway", "secondary");
141140
way.setTag("gh:barrier_edge", true);
142141

143-
Map<String, Object> nodeTags = new HashMap<>();
144-
nodeTags.put("access", "no");
145-
nodeTags.put("bus", "yes");
146-
way.setTag("node_tags", Arrays.asList(nodeTags, new HashMap<>()));
142+
way.setTag("node_tags", List.of(Map.of("access", "no", "bus", "yes"), Map.of()));
147143
EdgeIntAccess access = new ArrayEdgeIntAccess(1);
148144
int edgeId = 0;
149145
parser.handleWayTags(edgeId, access, way, null);
150146
assertTrue(busAccessEnc.getBool(false, edgeId, access));
151147

152-
nodeTags = new HashMap<>();
153-
nodeTags.put("access", "yes");
154-
nodeTags.put("bus", "no");
155-
way.setTag("node_tags", Arrays.asList(nodeTags));
148+
way.setTag("node_tags", List.of(Map.of("access", "yes", "bus", "no")));
156149
access = new ArrayEdgeIntAccess(1);
157150
parser.handleWayTags(edgeId, access, way, null);
158151
assertFalse(busAccessEnc.getBool(false, edgeId, access));
159152

160153
// ensure that allowing node tags (bus=yes) do not unblock the inaccessible way
161154
way.setTag("access", "no");
162-
nodeTags = new HashMap<>();
163-
nodeTags.put("bus", "yes");
164-
way.setTag("node_tags", Arrays.asList(nodeTags, new HashMap<>()));
155+
way.setTag("node_tags", List.of(Map.of("bus", "yes"), Map.of()));
165156
access = new ArrayEdgeIntAccess(1);
166157
parser.handleWayTags(edgeId, access, way, null);
167158
assertFalse(busAccessEnc.getBool(false, edgeId, access));
168159
}
169160

161+
@Test
162+
public void testBarrier() {
163+
ReaderWay way = new ReaderWay(1);
164+
way.setTag("highway", "secondary");
165+
way.setTag("gh:barrier_edge", true);
166+
167+
way.setTag("node_tags", Arrays.asList(Map.of("barrier", "bollard"), Map.of()));
168+
EdgeIntAccess access = new ArrayEdgeIntAccess(1);
169+
int edgeId = 0;
170+
parser.handleWayTags(edgeId, access, way, null);
171+
assertFalse(busAccessEnc.getBool(false, edgeId, access));
172+
173+
way.setTag("node_tags", Arrays.asList(Map.of("barrier", "gate"), Map.of()));
174+
access = new ArrayEdgeIntAccess(1);
175+
parser.handleWayTags(edgeId, access, way, null);
176+
assertTrue(busAccessEnc.getBool(false, edgeId, access));
177+
178+
// this special mode ignores all barriers except kissing_gate
179+
BooleanEncodedValue tmpAccessEnc = new SimpleBooleanEncodedValue("tmp_access", true);
180+
EncodingManager tmpEM = new EncodingManager.Builder().add(tmpAccessEnc).add(Roundabout.create()).build();
181+
ModeAccessParser tmpParser = new ModeAccessParser(TransportationMode.CAR, tmpAccessEnc, true,
182+
tmpEM.getBooleanEncodedValue(Roundabout.KEY), Set.of(), Set.of("kissing_gate"));
183+
184+
way = new ReaderWay(1);
185+
way.setTag("highway", "secondary");
186+
way.setTag("gh:barrier_edge", true);
187+
188+
way.setTag("node_tags", List.of(Map.of("barrier", "bollard"), Map.of()));
189+
access = new ArrayEdgeIntAccess(1);
190+
tmpParser.handleWayTags(edgeId, access, way, null);
191+
assertTrue(tmpAccessEnc.getBool(false, edgeId, access));
192+
}
193+
170194
@Test
171195
public void testPsvYes() {
172196
EdgeIntAccess access = new ArrayEdgeIntAccess(1);
@@ -192,7 +216,8 @@ public void testPsvYes() {
192216
public void testMotorcycleYes() {
193217
BooleanEncodedValue mcAccessEnc = new SimpleBooleanEncodedValue("motorcycle_access", true);
194218
EncodingManager mcEM = new EncodingManager.Builder().add(mcAccessEnc).add(Roundabout.create()).build();
195-
ModeAccessParser mcParser = new ModeAccessParser(TransportationMode.MOTORCYCLE, mcAccessEnc, mcEM.getBooleanEncodedValue(Roundabout.KEY), List.of());
219+
ModeAccessParser mcParser = new ModeAccessParser(TransportationMode.MOTORCYCLE, mcAccessEnc, true,
220+
mcEM.getBooleanEncodedValue(Roundabout.KEY), Set.of(), Set.of());
196221

197222
int edgeId = 0;
198223
EdgeIntAccess access = new ArrayEdgeIntAccess(1);

web-api/src/main/java/com/graphhopper/util/PMap.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717
*/
1818
package com.graphhopper.util;
1919

20+
import java.util.Arrays;
2021
import java.util.LinkedHashMap;
2122
import java.util.Map;
23+
import java.util.Set;
24+
import java.util.stream.Collectors;
2225

2326
/**
2427
* A properties map (String to Object) with convenient methods to access the content.
@@ -140,6 +143,10 @@ public PMap putObject(String key, Object object) {
140143
return this;
141144
}
142145

146+
public static Set<String> toSet(String value) {
147+
return Arrays.stream(value.split(";")).filter(s -> !s.isEmpty()).collect(Collectors.toSet());
148+
}
149+
143150
/**
144151
* This method copies the underlying structure into a new Map object
145152
*/

0 commit comments

Comments
 (0)