Skip to content

Commit 3e2c71f

Browse files
authored
fix: correct surface information for routes involving sidewalks (#2075)
2 parents 427a5ea + d543ed3 commit 3e2c71f

File tree

12 files changed

+235
-42
lines changed

12 files changed

+235
-42
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ Releasing is documented in RELEASE.md
3636
### Removed
3737

3838
### Fixed
39+
- correct way type and surface extra info metadata for pedestrian routes that involve sidewalks attached to streets ([#2075](https://github.com/GIScience/openrouteservice/pull/2075))
3940
- country borders storage initialization ([#2095](https://github.com/GIScience/openrouteservice/pull/2095))
4041

4142
### Security
@@ -49,7 +50,7 @@ Releasing is documented in RELEASE.md
4950
- include OSM data version (`osm_date`) in the API response ([#1192](https://github.com/GIScience/openrouteservice/issues/1192))
5051
- added documentation for isochrone attributes ([#846](https://github.com/GIScience/openrouteservice/issues/846))
5152
- include `graph_date` and `osm_date` in the GPX response ([#2055](https://github.com/GIScience/openrouteservice/issues/2055))
52-
- use country information from an enriched OSM file containing nodes tagged with country IDs ([1753](https://github.com/GIScience/openrouteservice/pull/1753))
53+
- use country information from an enriched OSM file containing nodes tagged with country IDs ([#1753](https://github.com/GIScience/openrouteservice/pull/1753))
5354

5455
### Changed
5556
- refactor: core node ID map using DataAccess ([#2074](https://github.com/GIScience/openrouteservice/pull/2074))

ors-api/src/test/java/org/heigit/ors/apitests/routing/ResultTest.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4617,6 +4617,33 @@ void testCustomersAccess() {
46174617
.statusCode(200);
46184618
}
46194619

4620+
@Test
4621+
void testSidewalkSurface() {
4622+
JSONObject body = new JSONObject()
4623+
.put("coordinates",HelperFunctions.constructCoords("8.69840,49.408406|8.69816,49.408937"))
4624+
.put("preference", "shortest")
4625+
.put("extra_info", constructExtras("waytype|surface"));
4626+
4627+
given()
4628+
.config(JSON_CONFIG_DOUBLE_NUMBERS)
4629+
.headers(CommonHeaders.jsonContent)
4630+
.pathParam("profile", getParameter("footProfile"))
4631+
.body(body.toString())
4632+
.when()
4633+
.post(getEndPointPath() + "/{profile}")
4634+
.then()
4635+
.assertThat()
4636+
.body("any { it.key == 'routes' }", is(true))
4637+
.body("routes[0].extras.containsKey('waytype')", is(true))
4638+
.body("routes[0].extras.waytype.summary[0].value", is(7.0))
4639+
.body("routes[0].extras.waytype.summary[0].amount", is(100.0))
4640+
.body("routes[0].extras.containsKey('surface')", is(true))
4641+
.body("routes[0].extras.surface.summary[0].value", is(14.0))
4642+
.body("routes[0].extras.surface.summary[0].amount", is(100.0))
4643+
4644+
.statusCode(200);
4645+
}
4646+
46204647
private JSONArray constructBearings(String coordString) {
46214648
JSONArray coordinates = new JSONArray();
46224649
String[] coordPairs = coordString.split("\\|");

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import com.graphhopper.util.shapes.GHPoint;
2424
import org.apache.log4j.Logger;
2525
import org.heigit.ors.routing.graphhopper.extensions.reader.osmfeatureprocessors.OSMFeatureFilter;
26-
import org.heigit.ors.routing.graphhopper.extensions.reader.osmfeatureprocessors.WheelchairWayFilter;
26+
import org.heigit.ors.routing.graphhopper.extensions.reader.osmfeatureprocessors.PedestrianWayFilter;
2727
import org.heigit.ors.routing.graphhopper.extensions.storages.builders.*;
2828
import org.locationtech.jts.geom.Coordinate;
2929

@@ -75,7 +75,6 @@ public ORSOSMReader(GraphHopperStorage storage, GraphProcessContext procCntx) {
7575
}
7676

7777
if (b instanceof WheelchairGraphStorageBuilder) {
78-
filtersToApply.add(new WheelchairWayFilter());
7978
this.processNodeTags = true;
8079
this.detachSidewalksFromRoad = true;
8180
this.processSimpleGeom = true;
@@ -99,6 +98,14 @@ public ORSOSMReader(GraphHopperStorage storage, GraphProcessContext procCntx) {
9998
extraTagKeys.add("motorcar");
10099
extraTagKeys.add("motorcycle");
101100
}
101+
102+
if (b instanceof WaySurfaceTypeGraphStorageBuilder bb) {
103+
this.detachSidewalksFromRoad = bb.isUseSidewalks();
104+
}
105+
}
106+
107+
if (detachSidewalksFromRoad) {
108+
filtersToApply.add(new PedestrianWayFilter());
102109
}
103110
}
104111

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public enum SurfaceType {
2929
DIRT(11),
3030
GROUND(12),
3131
ICE(13),
32-
PAVING_STONE(14),
32+
PAVING_STONES(14),
3333
SAND(15),
3434
GRASS(17),
3535
GRASS_PAVER(18);
@@ -62,7 +62,7 @@ public static SurfaceType getFromString(String surface) {
6262
case "unpaved", "woodchips", "rock", "rocks", "stone", "shells", "salt" -> SurfaceType.UNPAVED;
6363
case "asphalt", "chipseal", "bitmac", "tarmac" -> SurfaceType.ASPHALT;
6464
case "concrete", "cement" -> SurfaceType.CONCRETE;
65-
case "paving_stones", "paved_stones", "sett", "cobblestone", "unhewn_cobblestone", "bricks", "brick" -> SurfaceType.PAVING_STONE;
65+
case "paving_stones", "paved_stones", "sett", "cobblestone", "unhewn_cobblestone", "bricks", "brick" -> SurfaceType.PAVING_STONES;
6666
case "metal" -> SurfaceType.METAL;
6767
case "wood" -> SurfaceType.WOOD;
6868
case "compacted", "pebblestone" -> SurfaceType.COMPACTED_GRAVEL;
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
import com.graphhopper.reader.ReaderWay;
44

5-
public class WheelchairSeparateWay extends PedestrianWay {
5+
public class PedestrianSeparateWay extends PedestrianWay {
66
boolean hasBeenProcessed = false;
77

8-
public WheelchairSeparateWay(ReaderWay way) {
8+
public PedestrianSeparateWay(ReaderWay way) {
99
super(way);
1010
}
1111

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
import com.graphhopper.reader.ReaderWay;
44

55

6-
public class WheelchairSidewalkWay extends PedestrianWay {
6+
public class PedestrianSidewalkWay extends PedestrianWay {
77
private final OSMAttachedSidewalkProcessor sidewalkProcessor;
88
private final OSMAttachedSidewalkProcessor.Side side;
99
private OSMAttachedSidewalkProcessor.Side lastPrepared = OSMAttachedSidewalkProcessor.Side.NONE;
1010

11-
public WheelchairSidewalkWay(ReaderWay way) {
11+
public PedestrianSidewalkWay(ReaderWay way) {
1212
super(way);
1313

1414
sidewalkProcessor = new OSMAttachedSidewalkProcessor();
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@
55

66
import java.io.InvalidObjectException;
77

8-
public class WheelchairWayFilter implements OSMFeatureFilter {
8+
public class PedestrianWayFilter implements OSMFeatureFilter {
99
private final OSMAttachedSidewalkProcessor osmAttachedSidewalkProcessor;
1010

1111
private Way osmWay;
1212

13-
public WheelchairWayFilter() {
13+
public PedestrianWayFilter() {
1414
super();
1515
osmAttachedSidewalkProcessor = new OSMAttachedSidewalkProcessor();
1616
}
@@ -20,12 +20,12 @@ public void assignFeatureForFiltering(ReaderElement element) throws InvalidObjec
2020
if (element instanceof ReaderWay way) {
2121

2222
if (osmAttachedSidewalkProcessor.hasSidewalkInfo(way)) {
23-
this.osmWay = new WheelchairSidewalkWay(way);
23+
this.osmWay = new PedestrianSidewalkWay(way);
2424
} else {
25-
this.osmWay = new WheelchairSeparateWay(way);
25+
this.osmWay = new PedestrianSeparateWay(way);
2626
}
2727
} else {
28-
throw new InvalidObjectException("Wheelchair Filtering can only be applied to ways");
28+
throw new InvalidObjectException("Pedestrian filtering can only be applied to ways");
2929
}
3030
}
3131

ors-engine/src/main/java/org/heigit/ors/routing/graphhopper/extensions/storages/builders/WaySurfaceTypeGraphStorageBuilder.java

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,34 @@
1717
import com.graphhopper.reader.ReaderWay;
1818
import com.graphhopper.storage.GraphExtension;
1919
import com.graphhopper.util.EdgeIteratorState;
20+
import lombok.Getter;
21+
import lombok.Setter;
22+
import org.heigit.ors.common.EncoderNameEnum;
2023
import org.heigit.ors.routing.graphhopper.extensions.SurfaceType;
2124
import org.heigit.ors.routing.graphhopper.extensions.WayType;
2225
import org.heigit.ors.routing.graphhopper.extensions.storages.WaySurfaceTypeGraphStorage;
2326
import org.heigit.ors.routing.util.WaySurfaceDescription;
2427

2528
import java.util.HashSet;
2629

30+
import static org.heigit.ors.routing.graphhopper.extensions.reader.osmfeatureprocessors.OSMAttachedSidewalkProcessor.*;
31+
2732
public class WaySurfaceTypeGraphStorageBuilder extends AbstractGraphStorageBuilder {
2833
public static final String TAG_HIGHWAY = "highway";
2934
public static final String TAG_SURFACE = "surface";
3035
public static final String TAG_ROUTE = "route";
36+
3137
private WaySurfaceTypeGraphStorage storage;
32-
private final WaySurfaceDescription waySurfaceDesc = new WaySurfaceDescription();
3338
protected final HashSet<String> ferries;
3439

40+
private final WaySurfaceDescription waySurfaceDescription = new WaySurfaceDescription();
41+
private SurfaceType sidewalkLeftSurface = SurfaceType.UNKNOWN;
42+
private SurfaceType sidewalkRightSurface = SurfaceType.UNKNOWN;
43+
44+
@Getter
45+
@Setter
46+
private boolean useSidewalks = false;
47+
3548
public WaySurfaceTypeGraphStorageBuilder() {
3649
ferries = new HashSet<>(5);
3750
ferries.add("shuttle_train");
@@ -42,12 +55,18 @@ public GraphExtension init(GraphHopper graphhopper) throws Exception {
4255
if (storage != null)
4356
throw new Exception("GraphStorageBuilder has been already initialized.");
4457

58+
var flagEncoders = graphhopper.getEncodingManager().fetchEdgeEncoders();
59+
var flagEncoder = EncoderNameEnum.getFromEncoderName(flagEncoders.get(0).toString());
60+
useSidewalks = flagEncoders.size() == 1 && EncoderNameEnum.isPedestrian(flagEncoder);
61+
4562
storage = new WaySurfaceTypeGraphStorage();
4663
return storage;
4764
}
4865

4966
public void processWay(ReaderWay way) {
50-
waySurfaceDesc.reset();
67+
waySurfaceDescription.reset();
68+
sidewalkLeftSurface = SurfaceType.UNKNOWN;
69+
sidewalkRightSurface = SurfaceType.UNKNOWN;
5170

5271
int wayType;
5372
if (way.hasTag(TAG_ROUTE, ferries)) {
@@ -57,15 +76,56 @@ public void processWay(ReaderWay way) {
5776
} else {
5877
return;
5978
}
60-
waySurfaceDesc.setWayType(wayType);
79+
waySurfaceDescription.setWayType(wayType);
80+
81+
SurfaceType waySurface = way.hasTag(TAG_SURFACE) ? SurfaceType.getFromString(way.getTag(TAG_SURFACE)) : SurfaceType.UNKNOWN;
82+
waySurfaceDescription.setSurfaceType(waySurface);
83+
84+
if (useSidewalks) {
85+
// obtain default surface type for sidewalks
86+
SurfaceType sidewalkSurface = way.hasTag("sidewalk:surface") ?
87+
SurfaceType.getFromString(way.getTag("sidewalk:surface")) : SurfaceType.UNKNOWN;
88+
89+
sidewalkSurface = way.hasTag("sidewalk:both:surface") ?
90+
SurfaceType.getFromString(way.getTag("sidewalk:both:surface")) : sidewalkSurface;
6191

62-
SurfaceType surfaceType = way.hasTag(TAG_SURFACE) ? SurfaceType.getFromString(way.getTag(TAG_SURFACE)) : SurfaceType.UNKNOWN;
63-
waySurfaceDesc.setSurfaceType(surfaceType);
92+
sidewalkLeftSurface = way.hasTag("sidewalk:left:surface") ?
93+
SurfaceType.getFromString(way.getTag("sidewalk:left:surface")) : sidewalkSurface;
6494

95+
sidewalkRightSurface = way.hasTag("sidewalk:right:surface") ?
96+
SurfaceType.getFromString(way.getTag("sidewalk:right:surface")) : sidewalkSurface;
97+
}
6598
}
6699

100+
/**
101+
* Process an individual edge which has been derived from the way and then store it in the storage.
102+
*
103+
* @param way The parent way feature
104+
* @param edge The specific edge to be processed
105+
*/
106+
@Override
67107
public void processEdge(ReaderWay way, EdgeIteratorState edge) {
68-
storage.setEdgeValue(edge.getEdge(), waySurfaceDesc);
108+
storage.setEdgeValue(edge.getEdge(), getStoredValue(way));
109+
}
110+
111+
public WaySurfaceDescription getStoredValue(ReaderWay way) {
112+
var value = new WaySurfaceDescription();
113+
114+
value.setWayType(waySurfaceDescription.getWayType());
115+
value.setSurfaceType(waySurfaceDescription.getSurfaceType());
116+
117+
if (useSidewalks && way.hasTag(KEY_ORS_SIDEWALK_SIDE)) {
118+
value.setWayType(WayType.FOOTWAY);
119+
String side = way.getTag(KEY_ORS_SIDEWALK_SIDE);
120+
if (side.equals(VAL_LEFT)) {
121+
value.setSurfaceType(sidewalkLeftSurface);
122+
}
123+
else if (side.equals(VAL_RIGHT)) {
124+
value.setSurfaceType(sidewalkRightSurface);
125+
}
126+
}
127+
128+
return value;
69129
}
70130

71131
@Override
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@
77
import static org.junit.jupiter.api.Assertions.assertFalse;
88
import static org.junit.jupiter.api.Assertions.assertTrue;
99

10-
class WheelchairSeparateWayTest {
11-
WheelchairSeparateWay way;
10+
class PedestrianSeparateWayTest {
11+
PedestrianSeparateWay way;
1212

1313
@BeforeEach
1414
void reset() {
1515
ReaderWay readerWay = new ReaderWay(1);
16-
way = new WheelchairSeparateWay(readerWay);
16+
way = new PedestrianSeparateWay(readerWay);
1717
}
1818

1919
@Test
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,32 +5,32 @@
55

66
import static org.junit.jupiter.api.Assertions.*;
77

8-
class WheelchairSidewalkWayTest {
8+
class PedestrianSidewalkWayTest {
99
@Test
1010
void TestShowAsPedestrian() {
11-
WheelchairSidewalkWay way = new WheelchairSidewalkWay(new ReaderWay(1));
11+
PedestrianSidewalkWay way = new PedestrianSidewalkWay(new ReaderWay(1));
1212
assertTrue(way.isPedestrianised());
1313
}
1414

1515
@Test
1616
void TestInitiallyProcessedIfNoSidewalk() {
17-
WheelchairSidewalkWay way = new WheelchairSidewalkWay(new ReaderWay(1));
17+
PedestrianSidewalkWay way = new PedestrianSidewalkWay(new ReaderWay(1));
1818
assertTrue(way.hasWayBeenFullyProcessed());
1919
}
2020

2121
@Test
2222
void TestInitiallyNotProcessedIfSidewalk() {
2323
ReaderWay readerWay = new ReaderWay(1);
2424
readerWay.setTag("sidewalk", "left");
25-
WheelchairSidewalkWay way = new WheelchairSidewalkWay(readerWay);
25+
PedestrianSidewalkWay way = new PedestrianSidewalkWay(readerWay);
2626
assertFalse(way.hasWayBeenFullyProcessed());
2727
}
2828

2929
@Test
3030
void TestThatBothSidesGetProcessed() {
3131
ReaderWay readerWay = new ReaderWay(1);
3232
readerWay.setTag("sidewalk", "both");
33-
WheelchairSidewalkWay way = new WheelchairSidewalkWay(readerWay);
33+
PedestrianSidewalkWay way = new PedestrianSidewalkWay(readerWay);
3434

3535
int count = 0;
3636

0 commit comments

Comments
 (0)