Skip to content

Commit a0abdd7

Browse files
committed
fix: inject node tags for kerb height parser
1 parent 2c8cf2d commit a0abdd7

6 files changed

Lines changed: 307 additions & 67 deletions

File tree

ors-engine/src/main/java/org/heigit/ors/export/ExportRequest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ public ExportResult computeExport() {
9090
osmWayIdEnc = null;
9191
}
9292

93-
wheelchairAttributesEnc = new WheelchairAttributesEncodedValues(gh.getEncodingManager());
93+
if(gh.getEncodingManager().hasEncoder("wheelchair"))
94+
wheelchairAttributesEnc = new WheelchairAttributesEncodedValues(gh.getEncodingManager());
9495

9596
// filter graph for nodes in Bounding Box
9697
Set<Integer> nodesInBBox = nodesInBBox(gh.getLocationIndex(), nodeAccess, graph);

ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/ORSOSMReader.java

Lines changed: 74 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.graphhopper.reader.ReaderWay;
2121
import com.graphhopper.reader.osm.OSMReader;
2222
import com.graphhopper.routing.OSMReaderConfig;
23+
import com.graphhopper.routing.ev.WheelchairKerb;
2324
import com.graphhopper.routing.ev.HillIndex;
2425
import com.graphhopper.routing.util.AbstractFlagEncoder;
2526
import com.graphhopper.routing.util.EncodingManager;
@@ -36,6 +37,7 @@
3637
import org.heigit.ors.routing.graphhopper.extensions.reader.osmfeatureprocessors.OSMFeatureFilter;
3738
import org.heigit.ors.routing.graphhopper.extensions.reader.osmfeatureprocessors.PedestrianWayFilter;
3839
import org.heigit.ors.routing.graphhopper.extensions.storages.builders.*;
40+
import org.heigit.ors.routing.graphhopper.extensions.util.parsers.wheelchair.WheelchairKerbHeightParser;
3941
import org.heigit.ors.routing.util.HillIndexCalculator;
4042
import org.locationtech.jts.geom.Coordinate;
4143

@@ -78,6 +80,19 @@ public ORSOSMReader(GraphHopperStorage storage, OSMReaderConfig osmReaderConfig,
7880
nodeTagsToStore = new HashSet<>(Arrays.asList("maxheight", "maxweight", "maxweight:hgv", "maxwidth", "maxlength", "maxlength:hgv", "maxaxleload"));
7981
osmNodeTagValues = new GHLongObjectHashMap<>(200, .5f);
8082

83+
if(super.encodingManager.hasEncoder("wheelchair")) {
84+
this.processNodeTags = true;
85+
this.processSimpleGeom = true;
86+
extraTagKeys.add("kerb");
87+
extraTagKeys.add("kerb:both");
88+
extraTagKeys.add("kerb:left");
89+
extraTagKeys.add("kerb:right");
90+
extraTagKeys.add("kerb:height");
91+
extraTagKeys.add("kerb:both:height");
92+
extraTagKeys.add("kerb:left:height");
93+
extraTagKeys.add("kerb:right:height");
94+
}
95+
8196
// Look if we should do border processing - if so then we have to process the geometry
8297
for (GraphStorageBuilder b : this.procCntx.getStorageBuilders()) {
8398
if (b instanceof BordersGraphStorageBuilder) {
@@ -90,19 +105,6 @@ public ORSOSMReader(GraphHopperStorage storage, OSMReaderConfig osmReaderConfig,
90105
this.processGeom = true;
91106
this.processWholeGeom = true;
92107
}
93-
94-
if (b instanceof WheelchairGraphStorageBuilder) {
95-
this.processNodeTags = true;
96-
this.processSimpleGeom = true;
97-
extraTagKeys.add("kerb");
98-
extraTagKeys.add("kerb:both");
99-
extraTagKeys.add("kerb:left");
100-
extraTagKeys.add("kerb:right");
101-
extraTagKeys.add("kerb:height");
102-
extraTagKeys.add("kerb:both:height");
103-
extraTagKeys.add("kerb:left:height");
104-
extraTagKeys.add("kerb:right:height");
105-
}
106108
}
107109

108110
if (procCntx.isUseSidewalks()) {
@@ -190,6 +192,7 @@ protected void preprocessWay(GHPoint first, GHPoint last, ReaderWay way) {
190192
}
191193

192194
applyNodeTagsToWay(way);
195+
attachNodeTagsToWay(way);
193196
onProcessWay(way);
194197
recordExactWayDistance(way);
195198
recordEstimatedWayDistance(way);// Required for backward compatibility of the acceleration heuristic
@@ -359,7 +362,64 @@ private void applyNodeTagsToWay(ReaderWay way) {
359362
if (osmNodeTagValues.containsKey(nodeId)) {
360363
osmNodeTagValues.get(nodeId).forEach((key, value) -> way.setTag(key, value.toString()));
361364
}
365+
applyExtraNodeTagsToWay(way, nodeId);
366+
}
367+
}
368+
}
369+
370+
private void applyExtraNodeTagsToWay(ReaderWay way, long nodeId) {
371+
Map<String, String> tagsForNode = nodeTags.get(nodeId);
372+
if (tagsForNode == null) {
373+
return;
374+
}
375+
376+
for (Entry<String, String> tag : tagsForNode.entrySet()) {
377+
if (!extraTagKeys.contains(tag.getKey())) {
378+
continue;
379+
}
380+
381+
String newValue = tag.getValue();
382+
383+
if (isKerbTag(tag.getKey())) {
384+
applyTagValue(way, "ors_node:" + tag.getKey(), newValue);
385+
} else {
386+
applyTagValue(way, tag.getKey(), newValue);
387+
}
388+
}
389+
}
390+
391+
private void applyTagValue(ReaderWay way, String key, String value) {
392+
String newValue = value;
393+
if (way.hasTag(key)) {
394+
try {
395+
double newValInt = Double.parseDouble(newValue);
396+
double oldValInt = Double.parseDouble(way.getTag(key));
397+
newValue = Math.max(newValInt, oldValInt) + "";
398+
} catch (Exception e) {
399+
// If the value is not a number, we cannot use it anyway, so it does not matter which one we use.
400+
}
401+
}
402+
403+
way.setTag(key, newValue);
404+
}
405+
406+
private boolean isKerbTag(String key) {
407+
return key.contains("kerb") || key.contains("curb");
408+
}
409+
410+
private void attachNodeTagsToWay(ReaderWay way) {
411+
LongArrayList osmNodeIds = way.getNodes();
412+
int size = osmNodeIds.size();
413+
if (size > 2) {
414+
// If it is a crossing then we need to apply any kerb tags to the way, but we need to make sure we keep the "worse" one
415+
GHLongObjectHashMap<Map<String, String>> wayNodeTagValues = new GHLongObjectHashMap<>();
416+
for (int i = 1; i < size - 1; i++) {
417+
long nodeId = osmNodeIds.get(i);
418+
if (nodeTags.containsKey(nodeId)) {
419+
wayNodeTagValues.put(nodeId, nodeTags.get(nodeId));
420+
}
362421
}
422+
way.setTag("ors:node_tags", wayNodeTagValues);
363423
}
364424
}
365425

@@ -385,6 +445,7 @@ protected void onProcessEdge(ReaderWay way, EdgeIteratorState edge, IntsRef edge
385445
}
386446
}
387447

448+
388449
private void storeConditionalAccess(AcceptWay acceptWay, EdgeIteratorState edge) {
389450
for (FlagEncoder encoder : encodingManager.fetchEdgeEncoders()) {
390451
String encoderName = encoder.toString();

ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/edgefilters/WheelchairEdgeFilter.java

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -44,24 +44,15 @@ public WheelchairEdgeFilter(WheelchairParameters params, GraphHopperStorage grap
4444
public boolean accept(EdgeIteratorState iter) {
4545
attributes = encodedValues.getAttributes(iter.getFlags());
4646
LOGGER.debug("edge: " + iter + (attributes.hasValues() ? " suitable: " + attributes.isSuitable() + " surfaceQualityKnown: " + attributes.isSurfaceQualityKnown() : " no wheelchair attributes"));
47-
boolean checkSurfaceType = checkSurfaceType();
48-
boolean checkSmoothnessType = checkSmoothnessType();
49-
boolean checkTrackType = checkTrackType();
50-
boolean checkMaximumIncline = checkMaximumIncline();
51-
boolean checkMaximumSlopedKerb = checkMaximumSlopedKerb();
52-
boolean checkMinimumWidth = checkMinimumWidth();
53-
boolean checkSurfaceQualityKnown = checkSurfaceQualityKnown();
54-
boolean checkUnsuitable = checkUnsuitable();
55-
5647
return !attributes.hasValues() || !(
57-
checkSurfaceType
58-
|| checkSmoothnessType
59-
|| checkTrackType
60-
|| checkMaximumIncline
61-
|| checkMaximumSlopedKerb
62-
|| checkMinimumWidth
63-
|| checkSurfaceQualityKnown
64-
|| checkUnsuitable
48+
checkSurfaceType()
49+
|| checkSmoothnessType()
50+
|| checkTrackType()
51+
|| checkMaximumIncline()
52+
|| checkMaximumSlopedKerb()
53+
|| checkMinimumWidth()
54+
|| checkSurfaceQualityKnown()
55+
|| checkUnsuitable()
6556
);
6657
}
6758

Lines changed: 131 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,155 @@
11
package org.heigit.ors.routing.graphhopper.extensions.util.parsers.wheelchair;
22

3+
import com.graphhopper.coll.GHLongObjectHashMap;
34
import com.graphhopper.reader.ReaderWay;
45
import com.graphhopper.routing.ev.IntEncodedValue;
56
import com.graphhopper.routing.ev.WheelchairKerb;
67
import com.graphhopper.storage.IntsRef;
78

9+
import java.util.ArrayList;
10+
import java.util.Collections;
11+
import java.util.List;
12+
import java.util.Map;
13+
14+
import static org.heigit.ors.routing.graphhopper.extensions.storages.builders.WheelchairGraphStorageBuilder.*;
15+
816
public class WheelchairKerbHeightParser extends WheelchairBaseParser {
917
public static final String TAG_NAME = "kerb_height";
18+
public static boolean kerbHeightOnlyOnCrossing = true; // TODO: expose this as a configuration option
19+
1020

11-
public WheelchairKerbHeightParser(){
21+
public WheelchairKerbHeightParser() {
1222
this.encoder = WheelchairKerb.create();
1323
}
1424

15-
@Override
25+
@Override
1626
public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, boolean ferry, IntsRef relationFlags) {
1727
beforeHandleWayTags(way);
1828

19-
int height = calcSingleKerbHeightFromTagList(assumedKerbTags, -1);
20-
height = calcSingleKerbHeightFromTagList(explicitKerbTags, height);
29+
GHLongObjectHashMap<Map<String, String>> nodeTags = way.getTag("ors:node_tags", new GHLongObjectHashMap<>());
30+
31+
String[] assumedKerbTags = new String[]{
32+
"curb",
33+
"kerb",
34+
KEY_SLOPED_CURB,
35+
KEY_SLOPED_KERB
36+
};
37+
String[] explicitKerbTags = new String[]{
38+
KEY_KERB_HEIGHT,
39+
KEY_CURB_HEIGHT
40+
};
41+
42+
int heightC = -1;
43+
int heightL = -1;
44+
int heightR = -1;
45+
46+
int height = calcSingleKerbHeightFromTagList(assumedKerbTags, -1);
47+
// Explicit heights overwrite assumed
48+
height = calcSingleKerbHeightFromTagList(explicitKerbTags, height);
49+
50+
if (height > -1) {
51+
heightC = height;
52+
}
53+
54+
// Now for if the values are attached to sides of the way
55+
int[] heights = calcSingleKerbHeightFromSidedTagList(assumedKerbTags, new int[]{-1, -1});
56+
heights = calcSingleKerbHeightFromSidedTagList(explicitKerbTags, heights);
57+
58+
if (heights[0] > -1) {
59+
hasLeftSidewalk = true;
60+
heightL = heights[0];
61+
}
62+
63+
if (heights[1] > -1) {
64+
hasLeftSidewalk = true;
65+
heightR = heights[1];
66+
}
2167

22-
int left = -1;
23-
int right = -1;
2468

25-
// Now for if the values are attached to sides of the way
26-
int[] heights = calcSingleKerbHeightFromSidedTagList(assumedKerbTags, new int[]{-1, -1});
27-
heights = calcSingleKerbHeightFromSidedTagList(explicitKerbTags, heights);
69+
int kerbHeight = getKerbHeightForEdge(way, nodeTags);
70+
if (kerbHeight > -1) {
71+
heightC = kerbHeight;
72+
}
2873

29-
if (heights[0] > -1) {
30-
left = heights[0];
31-
}
74+
int finalHeight = heightC;
3275

33-
if (heights[1] > -1) {
34-
right = heights[1];
35-
}
76+
// Check for if we have specified which side the processing is for
77+
if (way.hasTag("ors-sidewalk-side")) {
78+
String side = way.getTag("ors-sidewalk-side");
79+
if (side.equals(SW_VAL_LEFT)) {
80+
// Only get the attributes for the left side
81+
finalHeight = heightL;
82+
}
83+
if (side.equals(SW_VAL_RIGHT)) {
84+
finalHeight = heightR;
85+
}
86+
} else {
87+
// if we have sidewalks attached, then we should also look at those. We should only hit this point if
88+
// the preprocessing hasn't detected that there are sidewalks even though there are...
89+
if (hasRightSidewalk || hasLeftSidewalk) {
90+
finalHeight = Math.max(Math.max(heightL, heightR), heightC);
91+
}
92+
}
3693

37-
height = selectIntValueForSidewalkSide(way, height, left, right);
94+
if (finalHeight > -1)
95+
((IntEncodedValue) encoder).setInt(false, edgeFlags, finalHeight);
3896

39-
if(height > -1)
40-
((IntEncodedValue) encoder).setInt(false, edgeFlags, height);
97+
return edgeFlags;
98+
}
99+
100+
101+
/**
102+
* Get an overriding kerb height if needed from the nodes that are on the way rather than the data stored on the way itself.
103+
* This should be the case if we are specifying to only store kerb heights on crossings as these features do not normally
104+
* have kerb heights attached to them
105+
*
106+
* @param way The way that is being investigated
107+
* @return A kerb height from the tags of the nodes on the way, or -1 if no kerb heights are found/required
108+
*/
109+
int getKerbHeightForEdge(ReaderWay way, GHLongObjectHashMap<Map<String, String>> nodeTags) {
110+
int kerbHeight = -1;
111+
112+
if (!kerbHeightOnlyOnCrossing || (way.hasTag(KEY_FOOTWAY) && way.getTag(KEY_FOOTWAY).equals("crossing"))) {
113+
// Look for kerb information
114+
kerbHeight = getKerbHeightFromNodeTags(nodeTags);
115+
}
116+
117+
return kerbHeight;
118+
}
119+
120+
/**
121+
* Look at the information stored against the nodes of the way and extract the kerb height to use for the whole way
122+
* from those data.
123+
*
124+
* @return The derived kerb height in centimetres from teh nodes that are on the way
125+
*/
126+
int getKerbHeightFromNodeTags(GHLongObjectHashMap<Map<String, String>> nodeTags) {
127+
// Assumed kerb heights are those obtained from a tag without the explicit :height attribute
128+
List<Integer> assumedKerbHeights = new ArrayList<>();
129+
// Explicit heights are those provided by the :height tag - these should take precidence
130+
List<Integer> explicitKerbHeights = new ArrayList<>();
41131

42-
return edgeFlags;
43-
}
132+
for (var entry : nodeTags.values()) {
133+
Map<String, String> tags = entry.value;
134+
for (Map.Entry<String, String> tag : tags.entrySet()) {
135+
switch (tag.getKey()) {
136+
case KEY_SLOPED_CURB, "curb", "kerb", KEY_SLOPED_KERB ->
137+
assumedKerbHeights.add(convertKerbTagValueToCentimetres(tag.getValue()));
138+
case KEY_KERB_HEIGHT -> explicitKerbHeights.add(convertKerbTagValueToCentimetres(tag.getValue()));
139+
default -> {
140+
}
141+
}
142+
}
143+
}
144+
if (!explicitKerbHeights.isEmpty()) {
145+
return Collections.max(explicitKerbHeights);
146+
} else if (!assumedKerbHeights.isEmpty()) {
147+
// If we have multiple kerb heights, we need to apply the largest to the edge as this is the worst
148+
return Collections.max(assumedKerbHeights);
149+
} else {
150+
return -1;
151+
}
152+
}
44153
}
154+
155+

ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/util/parsers/wheelchair/WheelchairSurfaceQualityKnownParser.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,24 @@ public IntsRef handleWayTags(IntsRef edgeFlags, ReaderWay way, boolean ferry, In
4747
right = true;
4848
}
4949

50+
center = getSidedBoolean(way, center, left, right);
51+
52+
((SimpleBooleanEncodedValue) encoder).setBool(false, edgeFlags, center);
53+
return edgeFlags;
54+
}
55+
56+
boolean getSidedBoolean(ReaderWay way, boolean center, boolean left, boolean right){
5057
if (way.hasTag(KEY_ORS_SIDEWALK_SIDE)) {
5158
String side = way.getTag(KEY_ORS_SIDEWALK_SIDE);
5259
if (side.equals(SW_VAL_LEFT)) {
53-
center = left;
60+
return left;
5461
} else if (side.equals(SW_VAL_RIGHT)) {
55-
center = right;
62+
return right;
5663
}
64+
} else if(hasRightSidewalk || hasLeftSidewalk) {
65+
return left && right && center;
5766
}
58-
((SimpleBooleanEncodedValue) encoder).setBool(false, edgeFlags, center);
59-
return edgeFlags;
67+
68+
return center;
6069
}
6170
}

0 commit comments

Comments
 (0)