Skip to content

Commit 9dc4e01

Browse files
authored
introduce required encoded values (graphhopper#1805)
* initial version of required encoded values * exception no longer possible * minor changes
1 parent 96f8798 commit 9dc4e01

19 files changed

+292
-222
lines changed

core/files/changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
1.0
2+
add required EncodedValues like road_class to EncodingManager if not added from user
23
removed PathNative,PathBidirRef,Path4CH,EdgeBasedPathCH and moved path extraction code out of Path class, added PathExtractor,BidirPathExtractor(+subclasses for CH) instead, #1730
34
conditional turn restrictions now supported, #1683
45
added new `curbside` feature, #1697

core/src/main/java/com/graphhopper/routing/util/DataFlagEncoder.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -91,13 +91,6 @@ public void createEncodedValues(List<EncodedValue> registerNewEncodedValue, Stri
9191
// TODO support different vehicle types, currently just roundabout and fwd&bwd for one vehicle type
9292
super.createEncodedValues(registerNewEncodedValue, prefix, index);
9393

94-
for (String key : Arrays.asList(RoadClass.KEY, RoadEnvironment.KEY, RoadAccess.KEY, MaxSpeed.KEY)) {
95-
if (!encodedValueLookup.hasEncodedValue(key))
96-
throw new IllegalStateException("To use DataFlagEncoder and the GenericWeighting you need to add " +
97-
"the encoded value " + key + " before this '" + toString() + "' flag encoder. Order is important! " +
98-
"E.g. use the config: graph.encoded_values: " + key);
99-
}
100-
10194
// workaround to init AbstractWeighting.avSpeedEnc variable that GenericWeighting does not need
10295
avgSpeedEnc = new UnsignedDecimalEncodedValue("fake", 1, 1, false);
10396
roadEnvironmentEnc = getEnumEncodedValue(RoadEnvironment.KEY, RoadEnvironment.class);

core/src/main/java/com/graphhopper/routing/util/EncodingManager.java

Lines changed: 120 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public static EncodingManager create(EncodedValueFactory evFactory, FlagEncoderF
108108
throw new IllegalStateException("Cannot load properties to fetch EncodingManager configuration at: "
109109
+ dir.getLocation());
110110

111-
EncodingManager.Builder builder = new EncodingManager.Builder(false);
111+
EncodingManager.Builder builder = new EncodingManager.Builder();
112112
String encodedValuesStr = properties.get("graph.encoded_values");
113113
if (!Helper.isEmpty(encodedValuesStr))
114114
builder.addAll(evFactory, encodedValuesStr);
@@ -144,15 +144,14 @@ public void releaseParsers() {
144144

145145
public static class Builder {
146146
private EncodingManager em;
147+
List<AbstractFlagEncoder> flagEncoderList = new ArrayList<>();
148+
List<EncodedValue> encodedValueList = new ArrayList<>();
149+
List<TagParser> tagParsers = new ArrayList<>();
150+
List<TurnCostParser> turnCostParsers = new ArrayList<>();
151+
List<RelationTagParser> relationTagParsers = new ArrayList<>();
147152

148153
public Builder() {
149-
this(true);
150-
}
151-
152-
private Builder(boolean addRoundabout) {
153154
em = new EncodingManager();
154-
if (addRoundabout)
155-
add(new OSMRoundaboutParser());
156155
}
157156

158157
/**
@@ -186,67 +185,46 @@ public Builder setEnableInstructions(boolean enable) {
186185
* For backward compatibility provide a way to add multiple FlagEncoders
187186
*/
188187
public Builder addAll(FlagEncoderFactory factory, String flagEncodersStr) {
188+
check();
189189
for (FlagEncoder fe : parseEncoderString(factory, flagEncodersStr)) {
190-
add(fe);
190+
flagEncoderList.add((AbstractFlagEncoder) fe);
191191
}
192192
return this;
193193
}
194194

195195
public Builder addAll(EncodedValueFactory factory, String encodedValueString) {
196-
em.add(this, factory, encodedValueString);
196+
check();
197+
em.add(encodedValueList, factory, encodedValueString);
197198
return this;
198199
}
199200

200201
public Builder addAll(TagParserFactory factory, String tagParserString) {
201-
em.add(this, factory, tagParserString);
202+
check();
203+
em.add(tagParsers, factory, tagParserString);
202204
return this;
203205
}
204206

205207
public Builder addTurnCostParser(TurnCostParser parser) {
206-
List<EncodedValue> list = new ArrayList<>();
207-
parser.createTurnCostEncodedValues(em, list);
208-
for (EncodedValue ev : list) {
209-
ev.init(em.turnCostConfig);
210-
if (em.encodedValueMap.containsKey(ev.getName()))
211-
throw new IllegalArgumentException("Already defined: " + ev.getName() + ". Please note that " +
212-
"EncodedValues for edges and turn cost are in the same namespace.");
213-
em.encodedValueMap.put(ev.getName(), ev);
214-
}
215-
em.turnCostParsers.put(parser.getName(), parser);
208+
check();
209+
turnCostParsers.add(parser);
216210
return this;
217211
}
218212

219213
public Builder addRelationTagParser(RelationTagParser tagParser) {
220-
List<EncodedValue> list = new ArrayList<>();
221-
tagParser.createRelationEncodedValues(em, list);
222-
for (EncodedValue ev : list) {
223-
ev.init(em.relationConfig);
224-
}
225-
em.relationTagParsers.add(tagParser);
226-
227-
// add as "edge" TagParser
228-
add(tagParser, true);
214+
check();
215+
relationTagParsers.add(tagParser);
229216
return this;
230217
}
231218

232219
public Builder add(FlagEncoder encoder) {
233220
check();
234-
if (encoder instanceof BikeCommonFlagEncoder && !em.hasEncodedValue(getKey("bike", RouteNetwork.EV_SUFFIX))) {
235-
addRelationTagParser(new OSMBikeNetworkTagParser());
236-
} else if (encoder instanceof FootFlagEncoder && !em.hasEncodedValue(getKey("foot", RouteNetwork.EV_SUFFIX))) {
237-
addRelationTagParser(new OSMFootNetworkTagParser());
238-
}
239-
240-
em.addEncoder((AbstractFlagEncoder) encoder);
221+
flagEncoderList.add((AbstractFlagEncoder) encoder);
241222
return this;
242223
}
243224

244225
public Builder add(EncodedValue encodedValue) {
245226
check();
246-
if (!em.edgeEncoders.isEmpty())
247-
throw new IllegalArgumentException("Always add shared EncodedValues before FlagEncoders to ensure they can be loaded first");
248-
249-
em.addEncodedValue(encodedValue, false);
227+
encodedValueList.add(encodedValue);
250228
return this;
251229
}
252230

@@ -255,34 +233,115 @@ public Builder add(EncodedValue encodedValue) {
255233
* createEncodedValues.
256234
*/
257235
public Builder add(TagParser tagParser) {
258-
return add(tagParser, false);
236+
check();
237+
tagParsers.add(tagParser);
238+
return this;
259239
}
260240

261-
private Builder add(TagParser tagParser, boolean encValBoundToFlagEncoder) {
241+
private void check() {
242+
if (em == null)
243+
throw new IllegalStateException("Cannot call method after Builder.build() was called");
244+
}
245+
246+
private void _addEdgeTagParser(TagParser tagParser, boolean withNamespace, boolean insert) {
262247
List<EncodedValue> list = new ArrayList<>();
263248
tagParser.createEncodedValues(em, list);
264249
for (EncodedValue ev : list) {
265-
em.addEncodedValue(ev, encValBoundToFlagEncoder);
250+
em.addEncodedValue(ev, withNamespace);
266251
}
267-
em.edgeTagParsers.add(tagParser);
268-
return this;
252+
if (insert)
253+
em.edgeTagParsers.add(0, tagParser);
254+
else
255+
em.edgeTagParsers.add(tagParser);
269256
}
270257

271-
private void check() {
272-
if (em == null)
273-
throw new IllegalStateException("Cannot call method after Builder.build() was called");
258+
private void _addRelationTagParser(RelationTagParser tagParser) {
259+
List<EncodedValue> list = new ArrayList<>();
260+
tagParser.createRelationEncodedValues(em, list);
261+
for (EncodedValue ev : list) {
262+
ev.init(em.relationConfig);
263+
}
264+
em.relationTagParsers.add(tagParser);
265+
266+
// for simplicity add into edge-EncodedValue
267+
_addEdgeTagParser(tagParser, true, false);
268+
}
269+
270+
private void _addTurnCostParser(TurnCostParser parser) {
271+
List<EncodedValue> list = new ArrayList<>();
272+
parser.createTurnCostEncodedValues(em, list);
273+
for (EncodedValue ev : list) {
274+
ev.init(em.turnCostConfig);
275+
if (em.encodedValueMap.containsKey(ev.getName()))
276+
throw new IllegalArgumentException("Already defined: " + ev.getName() + ". Please note that " +
277+
"EncodedValues for edges and turn cost are in the same namespace.");
278+
em.encodedValueMap.put(ev.getName(), ev);
279+
}
280+
em.turnCostParsers.put(parser.getName(), parser);
274281
}
275282

276283
public EncodingManager build() {
277284
check();
278-
if (em.encodedValueMap.isEmpty())
279-
throw new IllegalStateException("No EncodedValues found");
280285

281-
for (AbstractFlagEncoder encoder : em.edgeEncoders) {
286+
for (RelationTagParser tagParser : relationTagParsers) {
287+
_addRelationTagParser(tagParser);
288+
}
289+
290+
List<SpatialRuleParser> insertLater = new ArrayList<>();
291+
for (TagParser tagParser : tagParsers) {
292+
if (SpatialRuleParser.class.isAssignableFrom(tagParser.getClass())) {
293+
insertLater.add((SpatialRuleParser) tagParser);
294+
} else {
295+
_addEdgeTagParser(tagParser, false, false);
296+
}
297+
}
298+
299+
for (EncodedValue ev : encodedValueList) {
300+
em.addEncodedValue(ev, false);
301+
}
302+
303+
if (!em.encodedValueMap.containsKey(RoadAccess.KEY))
304+
_addEdgeTagParser(new OSMRoadAccessParser(), false, true);
305+
if (!em.encodedValueMap.containsKey(MaxSpeed.KEY))
306+
_addEdgeTagParser(new OSMMaxSpeedParser(), false, true);
307+
if (!em.encodedValueMap.containsKey(RoadEnvironment.KEY))
308+
_addEdgeTagParser(new OSMRoadEnvironmentParser(), false, true);
309+
if (!em.encodedValueMap.containsKey(RoadClassLink.KEY))
310+
_addEdgeTagParser(new OSMRoadClassLinkParser(), false, true);
311+
if (!em.encodedValueMap.containsKey(RoadClass.KEY))
312+
_addEdgeTagParser(new OSMRoadClassParser(), false, true);
313+
if (!em.encodedValueMap.containsKey(Roundabout.KEY))
314+
_addEdgeTagParser(new OSMRoundaboutParser(), false, true);
315+
316+
// TODO can we avoid this hack without complex dependency management?
317+
// ensure that SpatialRuleParser come after required EncodedValues like max_speed or road_access
318+
for (SpatialRuleParser srp : insertLater) {
319+
_addEdgeTagParser(srp, false, true);
320+
}
321+
322+
for (AbstractFlagEncoder encoder : flagEncoderList) {
323+
if (encoder instanceof BikeCommonFlagEncoder && !em.hasEncodedValue(getKey("bike", RouteNetwork.EV_SUFFIX))) {
324+
_addRelationTagParser(new OSMBikeNetworkTagParser());
325+
} else if (encoder instanceof FootFlagEncoder && !em.hasEncodedValue(getKey("foot", RouteNetwork.EV_SUFFIX))) {
326+
_addRelationTagParser(new OSMFootNetworkTagParser());
327+
}
328+
329+
em.addEncoder(encoder);
330+
}
331+
332+
for (TurnCostParser parser : turnCostParsers) {
333+
_addTurnCostParser(parser);
334+
}
335+
336+
// FlagEncoder can demand TurnCostParsers => add them after the explicitly added ones
337+
for (AbstractFlagEncoder encoder : flagEncoderList) {
282338
if (encoder.supports(TurnWeighting.class) && !em.turnCostParsers.containsKey(encoder.toString()))
283-
addTurnCostParser(new OSMTurnRelationParser(encoder.toString(), encoder.getMaxTurnCosts()));
339+
_addTurnCostParser(new OSMTurnRelationParser(encoder.toString(), encoder.getMaxTurnCosts()));
284340
}
285341

342+
if (em.encodedValueMap.isEmpty())
343+
throw new IllegalStateException("No EncodedValues found");
344+
286345
EncodingManager tmp = em;
287346
em = null;
288347
return tmp;
@@ -315,7 +374,7 @@ static List<FlagEncoder> parseEncoderString(FlagEncoderFactory factory, String e
315374
return resultEncoders;
316375
}
317376

318-
private void add(Builder builder, EncodedValueFactory factory, String evList) {
377+
private void add(List<EncodedValue> returnList, EncodedValueFactory factory, String evList) {
319378
if (!evList.equals(toLowerCase(evList)))
320379
throw new IllegalArgumentException("Use lower case for EncodedValues: " + evList);
321380

@@ -325,14 +384,14 @@ private void add(Builder builder, EncodedValueFactory factory, String evList) {
325384
continue;
326385

327386
EncodedValue evObject = factory.create(entry);
328-
builder.add(evObject);
329387
PMap map = new PMap(entry);
330388
if (!map.has("version"))
331389
throw new IllegalArgumentException("encoded value must have a version specified but it was " + entry);
390+
returnList.add(evObject);
332391
}
333392
}
334393

335-
private void add(Builder builder, TagParserFactory factory, String tpList) {
394+
private void add(List<TagParser> returnList, TagParserFactory factory, String tpList) {
336395
if (!tpList.equals(toLowerCase(tpList)))
337396
throw new IllegalArgumentException("Use lower case for TagParser: " + tpList);
338397

@@ -343,7 +402,7 @@ private void add(Builder builder, TagParserFactory factory, String tpList) {
343402

344403
PMap map = new PMap(entry);
345404
TagParser tp = factory.create(entry, map);
346-
builder.add(tp);
405+
returnList.add(tp);
347406
}
348407
}
349408

@@ -398,13 +457,13 @@ private void addEncoder(AbstractFlagEncoder encoder) {
398457
edgeEncoders.add(encoder);
399458
}
400459

401-
private void addEncodedValue(EncodedValue ev, boolean encValBoundToFlagEncoder) {
460+
private void addEncodedValue(EncodedValue ev, boolean withNamespace) {
402461
if (encodedValueMap.containsKey(ev.getName()))
403462
throw new IllegalStateException("EncodedValue " + ev.getName() + " already exists " + encodedValueMap.get(ev.getName()) + " vs " + ev);
404-
if (!encValBoundToFlagEncoder && ev.getName().contains(SPECIAL_SEPARATOR))
405-
throw new IllegalArgumentException("EncodedValue " + ev.getName() + " must not contain '" + SPECIAL_SEPARATOR + "'");
406-
if (encValBoundToFlagEncoder && !ev.getName().contains(SPECIAL_SEPARATOR))
407-
throw new IllegalArgumentException("EncodedValue " + ev.getName() + " must contain '" + SPECIAL_SEPARATOR + "' as reserved for a single profile");
463+
if (!withNamespace && ev.getName().contains(SPECIAL_SEPARATOR))
464+
throw new IllegalArgumentException("EncodedValue " + ev.getName() + " must not contain namespace character '" + SPECIAL_SEPARATOR + "'");
465+
if (withNamespace && !ev.getName().contains(SPECIAL_SEPARATOR))
466+
throw new IllegalArgumentException("EncodedValue " + ev.getName() + " must contain namespace character '" + SPECIAL_SEPARATOR + "'");
408467
ev.init(edgeConfig);
409468
encodedValueMap.put(ev.getName(), ev);
410469
}

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,6 @@ DecimalEncodedValue getTurnCostEnc() {
8282
@Override
8383
public void createTurnCostEncodedValues(EncodedValueLookup lookup, List<EncodedValue> registerNewEncodedValue) {
8484
String accessKey = getKey(name, "access");
85-
if (!lookup.hasEncodedValue(accessKey))
86-
throw new IllegalArgumentException("Add TurnCostParsers to EncodingManager after everything else");
8785
accessEnc = lookup.getEncodedValue(accessKey, BooleanEncodedValue.class);
8886
registerNewEncodedValue.add(turnCostEnc = TurnCost.create(name, maxTurnCosts));
8987
}
@@ -100,7 +98,7 @@ private EdgeExplorer getInExplorer(Graph graph) {
10098
return cachedInExplorer == null ? cachedInExplorer = graph.createEdgeExplorer(DefaultEdgeFilter.inEdges(accessEnc)) : cachedInExplorer;
10199
}
102100

103-
EdgeExplorer getOutExplorer(Graph graph) {
101+
private EdgeExplorer getOutExplorer(Graph graph) {
104102
return cachedOutExplorer == null ? cachedOutExplorer = graph.createEdgeExplorer(DefaultEdgeFilter.outEdges(accessEnc)) : cachedOutExplorer;
105103
}
106104

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727

2828
import java.util.List;
2929

30+
/**
31+
* This parser stores the spatialId in the edgeFlags based on previously defined areas.
32+
*/
3033
public class SpatialRuleParser implements TagParser {
3134

3235
private final IntEncodedValue spatialRuleEnc;

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -631,12 +631,6 @@ public static EdgeIteratorState setProperties(EdgeIteratorState edge, FlagEncode
631631
return edge;
632632
}
633633

634-
public static EncodingManager.Builder addDefaultEncodedValues(EncodingManager.Builder builder) {
635-
return builder.add(new OSMRoadClassParser()).add(new OSMRoadClassLinkParser()).
636-
add(new OSMRoadEnvironmentParser()).add(new OSMMaxSpeedParser()).add(new OSMRoadAccessParser()).
637-
add(new OSMSurfaceParser());
638-
}
639-
640634
public static void updateDistancesFor(Graph g, int node, double lat, double lon) {
641635
NodeAccess na = g.getNodeAccess();
642636
na.setNode(node, lat, lon);

core/src/test/java/com/graphhopper/GraphHopperAPITest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ public void testDisconnected179() {
115115
public void testDoNotInterpolateTwice1645() {
116116
String loc = "./target/issue1645";
117117
Helper.removeDir(new File(loc));
118-
EncodingManager em = new EncodingManager.Builder().add(new OSMRoadEnvironmentParser()).add(new CarFlagEncoder()).build();
118+
EncodingManager em = new EncodingManager.Builder().add(new CarFlagEncoder()).build();
119119
GraphHopperStorage graph = GraphBuilder.start(em).setRAM(loc, true).set3D(true).create();
120120

121121
// we need elevation

core/src/test/java/com/graphhopper/reader/dem/EdgeElevationInterpolatorTest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,7 @@ public abstract class EdgeElevationInterpolatorTest {
5555
@Before
5656
public void setUp() {
5757
graph = new GraphHopperStorage(new RAMDirectory(),
58-
encodingManager = new EncodingManager.Builder().add(new CarFlagEncoder()).add(new FootFlagEncoder()).
59-
add(new OSMRoadEnvironmentParser()).build(),
58+
encodingManager = new EncodingManager.Builder().add(new CarFlagEncoder()).add(new FootFlagEncoder()).build(),
6059
true).create(100);
6160
roadEnvEnc = encodingManager.getEnumEncodedValue(RoadEnvironment.KEY, RoadEnvironment.class);
6261
edgeElevationInterpolator = createEdgeElevationInterpolator();

core/src/test/java/com/graphhopper/routing/PathTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public class PathTest {
4949
private final BooleanEncodedValue carManagerRoundabout = carManager.getBooleanEncodedValue(Roundabout.KEY);
5050
private final BooleanEncodedValue carAccessEnc = encoder.getAccessEnc();
5151
private final DecimalEncodedValue carAvSpeedEnv = encoder.getAverageSpeedEnc();
52-
private final EncodingManager dataFlagManager = GHUtility.addDefaultEncodedValues(new EncodingManager.Builder()).add(dataFlagEncoder).build();
52+
private final EncodingManager dataFlagManager = new EncodingManager.Builder().add(dataFlagEncoder).build();
5353
private final IntsRef edgeFlags = dataFlagManager.createEdgeFlags();
5454
private final EncodingManager mixedEncoders = EncodingManager.create(new CarFlagEncoder(), new FootFlagEncoder());
5555
private final BooleanEncodedValue mixedManagerRoundabout = mixedEncoders.getBooleanEncodedValue(Roundabout.KEY);

core/src/test/java/com/graphhopper/routing/util/CarFlagEncoderTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
public class CarFlagEncoderTest {
4545
final CarFlagEncoder encoder = createEncoder();
4646
private final EncodingManager em = new EncodingManager.Builder().
47-
add(new OSMRoadAccessParser()).
4847
add(encoder).
4948
add(new BikeFlagEncoder()).add(new FootFlagEncoder()).build();
5049

0 commit comments

Comments
 (0)