Skip to content

Commit 7d1bdd1

Browse files
committed
RSSP-1975: Use max_speed if speed too high - draft
1 parent 5ff2ef3 commit 7d1bdd1

File tree

3 files changed

+200
-54
lines changed

3 files changed

+200
-54
lines changed

core/files/monaco2024.osm.gz

1010 KB
Binary file not shown.

core/src/main/java/com/graphhopper/GraphHopperCustomSpeeds.java

Lines changed: 35 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -71,21 +71,25 @@ private void setSpeedsFor(List<DecimalEncodedValue> speedEncoders) {
7171

7272
if (maybeCustomSpeed.isPresent()) {
7373
double customSpeed = maybeCustomSpeed.get();
74-
if (customSpeed < maxSpeed) {
75-
speedEncoders.forEach(encoder -> {
76-
double maxEncoderValue = encoder.getMaxStorableDecimal();
77-
double minEncoderValue = encoder.getMinStorableDecimal();
78-
if(customSpeed <= maxEncoderValue && customSpeed >= minEncoderValue){
79-
double speed = iter.get(encoder);
80-
logger.debug("Replace " + speed + " with " + customSpeed + " for edge " + edge);
81-
iter.set(encoder, customSpeed);
82-
}else{
83-
logger.warn("Invalid Custom Speed (" + customSpeed + ") for encoder " + encoder.getName() + " ( " + minEncoderValue + " - " + maxEncoderValue+ ") for edge " + edge + ",so it will be ignored");
84-
}
85-
});
74+
double speedToSet;
75+
if (customSpeed > maxSpeed) {
76+
logger.warn("OsmId " + osmWayId + ": Custom Speed (" + customSpeed + ") > MaxSpeed ( " + maxSpeed + ") for edge reverse " + edge + ", so max_speed will be used");
77+
speedToSet = maxSpeed;
8678
} else {
87-
logger.warn("Custom Speed (" + customSpeed + ") > MaxSpeed ( " + maxSpeed + ") for edge " + edge + ",so it will be ignored");
79+
speedToSet = customSpeed;
8880
}
81+
speedEncoders.forEach(encoder -> {
82+
double maxEncoderValue = encoder.getMaxStorableDecimal();
83+
double minEncoderValue = encoder.getMinStorableDecimal();
84+
if(speedToSet <= maxEncoderValue && speedToSet >= minEncoderValue){
85+
double speed = iter.get(encoder);
86+
logger.debug("Replace " + speed + " with " + speedToSet + " for edge " + edge);
87+
iter.set(encoder, speedToSet);
88+
}else{
89+
logger.warn("OsmId " + osmWayId + ": Invalid Custom Speed (" + speedToSet + ") for encoder " + encoder.getName() + " ( " + minEncoderValue + " - " + maxEncoderValue+ ") for edge " + edge + ",so it will be ignored");
90+
}
91+
});
92+
8993
}
9094

9195
//Reverse Direction
@@ -96,24 +100,26 @@ private void setSpeedsFor(List<DecimalEncodedValue> speedEncoders) {
96100

97101
if (maybeCustomSpeedReverse.isPresent()) {
98102
double customSpeedReverse = maybeCustomSpeedReverse.get();
99-
if (customSpeedReverse < maxSpeedReverse) {
100-
speedEncoders.forEach(encoder -> {
101-
if (encoder.isStoreTwoDirections()) {
102-
double maxEncoderValue = encoder.getMaxStorableDecimal();
103-
double minEncoderValue = encoder.getMinStorableDecimal();
104-
if(customSpeedReverse <= maxEncoderValue && customSpeedReverse >= minEncoderValue){
105-
double speed = iter.getReverse(encoder);
106-
logger.debug("Replace " + speed + " with " + customSpeedReverse + " for edge reverse " + edge);
107-
iter.setReverse(encoder, customSpeedReverse);
108-
}else{
109-
logger.warn("Invalid Custom Speed (" + customSpeedReverse + ") for encoder " + encoder.getName() + " ( " + minEncoderValue + " - " + maxEncoderValue+ ") for edge reverse " + edge + ",so it will be ignored");
110-
}
111-
112-
}
113-
});
103+
double speedToSet;
104+
if (customSpeedReverse > maxSpeedReverse) {
105+
logger.warn("OsmId " + osmWayId + ": Custom Speed (" + customSpeedReverse + ") > MaxSpeed ( " + maxSpeedReverse + ") for edge reverse " + edge + ", so max_speed will be used");
106+
speedToSet = maxSpeedReverse;
114107
} else {
115-
logger.warn("Custom Speed (" + customSpeedReverse + ") > MaxSpeed ( " + maxSpeedReverse + ") for edge reverse " + edge + ",so it will be ignored");
108+
speedToSet = customSpeedReverse;
116109
}
110+
speedEncoders.forEach(encoder -> {
111+
if (encoder.isStoreTwoDirections()) {
112+
double maxEncoderValue = encoder.getMaxStorableDecimal();
113+
double minEncoderValue = encoder.getMinStorableDecimal();
114+
if(speedToSet <= maxEncoderValue && speedToSet >= minEncoderValue){
115+
double speed = iter.getReverse(encoder);
116+
logger.debug("Replace " + speed + " with " + speedToSet + " for edge reverse " + edge);
117+
iter.setReverse(encoder, speedToSet);
118+
}else{
119+
//logger.warn("OsmId " + osmWayId + ": Invalid Custom Speed (" + speedToSet + ") for encoder " + encoder.getName() + " ( " + minEncoderValue + " - " + maxEncoderValue+ ") for edge reverse " + edge + ",so it will be ignored");
120+
}
121+
}
122+
});
117123
}
118124

119125
}

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

Lines changed: 165 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import com.graphhopper.config.CHProfile;
2121
import com.graphhopper.config.Profile;
22+
import com.graphhopper.json.Statement;
2223
import com.graphhopper.routing.ev.RoadClass;
2324
import com.graphhopper.speeds.SpeedKmByHour;
2425
import com.graphhopper.speeds.WaySpeedsProvider;
@@ -41,7 +42,7 @@ public class GraphHopperCustomSpeedsTest {
4142
public static final String DIR = "../core/files";
4243

4344
// map locations
44-
private static final String MONACO = DIR + "/monaco.osm.gz";
45+
private static final String MONACO = DIR + "/monaco2024.osm.gz";
4546

4647
// when creating GH instances make sure to use this as the GH location such that it will be cleaned between tests
4748
private static final String GH_LOCATION = "target/graphhopper-test-gh";
@@ -55,21 +56,22 @@ public void setup() {
5556
Helper.removeDir(new File(GH_LOCATION_CUSTOM_SPEEDS));
5657
}
5758

58-
class CustomWaySpeedProvider implements WaySpeedsProvider {
59+
@Test
60+
public void testDynamicSpeedProvider() {
5961

60-
@Override
61-
public Optional<SpeedKmByHour> speedForWay(long osmWayId) {
62-
return Optional.of(new SpeedKmByHour(10.10));
63-
}
62+
class CustomWaySpeedProvider implements WaySpeedsProvider {
6463

65-
@Override
66-
public Optional<SpeedKmByHour> speedForRoadClass(RoadClass roadClass) {
67-
return Optional.of(new SpeedKmByHour(10.1));
64+
@Override
65+
public Optional<SpeedKmByHour> speedForWay(long osmWayId) {
66+
return Optional.of(new SpeedKmByHour(10.10));
67+
}
68+
69+
@Override
70+
public Optional<SpeedKmByHour> speedForRoadClass(RoadClass roadClass) {
71+
return Optional.of(new SpeedKmByHour(10.1));
72+
}
6873
}
69-
}
7074

71-
@Test
72-
public void testDynamicSpeedProvider() {
7375
final String bikeProfile = "bike_profile";
7476
final String carProfile = "car_profile";
7577
List<Profile> profiles = asList(
@@ -101,36 +103,174 @@ public void testDynamicSpeedProvider() {
101103
hopper.importOrLoad();
102104
hopperCustomSpeeds.importOrLoad();
103105

104-
GHResponse rsp = hopper.route(new GHRequest(43.73005, 7.415707, 43.741522, 7.42826)
105-
.setProfile(carProfile));
106+
GHRequest request = new GHRequest(43.73005, 7.415707, 43.741522, 7.42826);
107+
108+
GHResponse rsp = hopper.route(request.setProfile(carProfile));
106109
ResponsePath res = rsp.getBest();
107110
assertFalse(rsp.hasErrors(), rsp.getErrors().toString());
108-
assertEquals(205, res.getTime() / 1000f, 1);
109-
assertEquals(2837, res.getDistance(), 1);
111+
assertEquals(208, res.getTime() / 1000f, 1); // 50kmh = 13.8 m/s
112+
assertEquals(2750, res.getDistance(), 1);
110113

111-
GHResponse rspSpeeds = hopperCustomSpeeds.route(new GHRequest(43.73005, 7.415707, 43.741522, 7.42826)
114+
GHResponse rspSpeeds = hopperCustomSpeeds.route(request
112115
.setProfile(carProfile));
113116
ResponsePath resSpeeds = rspSpeeds.getBest();
114117
assertFalse(rspSpeeds.hasErrors(), rspSpeeds.getErrors().toString());
115-
assertEquals(893, resSpeeds.getTime() / 1000f, 1);
116-
assertEquals(2481, resSpeeds.getDistance(), 1);
118+
assertEquals(893, resSpeeds.getTime() / 1000f, 1); // 10kmh = 2.7m/s
119+
assertEquals(2478, resSpeeds.getDistance(), 1);
117120

118121

119-
GHResponse rspBike = hopper.route(new GHRequest(43.73005, 7.415707, 43.741522, 7.42826)
122+
GHResponse rspBike = hopper.route(request
120123
.setProfile(bikeProfile));
121124
ResponsePath resBike = rspBike.getBest();
122125
assertFalse(rspBike.hasErrors(), rspBike.getErrors().toString());
123-
assertEquals(536, resBike.getTime() / 1000f, 1);
124-
assertEquals(2521, resBike.getDistance(), 1);
126+
assertEquals(544, resBike.getTime() / 1000f, 1); // 17kmh = 4.7 m/s
127+
assertEquals(2764, resBike.getDistance(), 1);
125128

126-
GHResponse rspBikeSpeeds = hopperCustomSpeeds.route(new GHRequest(43.73005, 7.415707, 43.741522, 7.42826)
129+
GHResponse rspBikeSpeeds = hopperCustomSpeeds.route(request
127130
.setProfile(bikeProfile));
128131
ResponsePath resBikeSpeeds = rspBikeSpeeds.getBest();
129132
assertFalse(rspBikeSpeeds.hasErrors(), rspBikeSpeeds.getErrors().toString());
130-
assertEquals(834, resBikeSpeeds.getTime() / 1000f, 1);
131-
assertEquals(2318, resBikeSpeeds.getDistance(), 1);
133+
assertEquals(784, resBikeSpeeds.getTime() / 1000f, 1); // 10kmh = 2.7m/s
134+
assertEquals(2178, resBikeSpeeds.getDistance(), 1);
135+
136+
}
137+
138+
@Test
139+
public void testDynamicSpeedProviderUsesMaxSpeedWhenInputSpeedIsTooHigh() {
140+
141+
class SpeedsTooHighSpeedProvider implements WaySpeedsProvider {
142+
143+
@Override
144+
public Optional<SpeedKmByHour> speedForWay(long osmWayId) {
145+
return Optional.of(new SpeedKmByHour(100));
146+
}
147+
148+
@Override
149+
public Optional<SpeedKmByHour> speedForRoadClass(RoadClass roadClass) {
150+
return Optional.of(new SpeedKmByHour(80.1));
151+
}
152+
}
153+
154+
final String carProfile = "car_profile";
155+
156+
List<Profile> profiles = asList(
157+
new Profile(carProfile).setVehicle("car")
158+
.setCustomModel(
159+
new CustomModel()
160+
.addToSpeed(
161+
Statement.If("road_class == SECONDARY", Statement.Op.LIMIT, "20")))
162+
);
163+
164+
GraphHopper hopper = new GraphHopper().
165+
setGraphHopperLocation(GH_LOCATION).
166+
setOSMFile(MONACO).
167+
setProfiles(profiles).
168+
setStoreOnFlush(true);
169+
hopper.getCHPreparationHandler().setCHProfiles(
170+
new CHProfile(carProfile)
171+
);
172+
173+
GraphHopper hopperCustomSpeeds = new GraphHopperCustomSpeeds(new SpeedsTooHighSpeedProvider()).
174+
setGraphHopperLocation(GH_LOCATION_CUSTOM_SPEEDS).
175+
setOSMFile(MONACO).
176+
setProfiles(profiles).
177+
setStoreOnFlush(true)
178+
.setEncodedValuesString("roundabout, road_class, road_class_link, road_environment, max_speed, road_access, ferry_speed, bike_network, get_off_bike, smoothness, osm_way_id");
179+
hopperCustomSpeeds.getCHPreparationHandler().setCHProfiles(
180+
new CHProfile(carProfile)
181+
);
182+
183+
hopper.importOrLoad();
184+
hopperCustomSpeeds.importOrLoad();
185+
186+
// requests uses osm way 166009792 (secondary road, that has max speed = 50km)
187+
// in the request we set a custom model with max speed of 20kmh for secondary roads
188+
GHRequest request = new GHRequest(43.73887, 7.42074, 43.73992, 7.42386);
189+
190+
GHResponse rsp = hopper.route(request.setProfile(carProfile));
191+
ResponsePath res = rsp.getBest();
192+
assertFalse(rsp.hasErrors(), rsp.getErrors().toString());
193+
assertEquals(49, res.getTime() / 1000f, 1); // 20kmh = 5.7m/s
194+
assertEquals(277, res.getDistance(), 1);
195+
196+
GHResponse rspSpeeds = hopperCustomSpeeds.route(request
197+
.setProfile(carProfile));
198+
ResponsePath resSpeeds = rspSpeeds.getBest();
199+
assertFalse(rspSpeeds.hasErrors(), rspSpeeds.getErrors().toString());
200+
assertEquals(21, resSpeeds.getTime() / 1000f, 1); // 50kmh = 13.8 m/s
201+
assertEquals(277, resSpeeds.getDistance(), 1);
132202

133203
}
134204

205+
@Test
206+
public void testDynamicSpeedProviderDiscardCustomSpeedIfHigherThanMaxEncoderSpeed() {
207+
208+
// For bike, the max encoder speed is 30kmh
209+
// For car, the max encoder speed is 254 kmh
210+
// We are trying to set a custom speed for bike > 30kmh, that is the limit for the bike
211+
// encoder, so we expect it to be discarded, and we expect that the default speed
212+
// for those roads will be used
213+
214+
class SpeedsTooHighSpeedProvider implements WaySpeedsProvider {
215+
216+
@Override
217+
public Optional<SpeedKmByHour> speedForWay(long osmWayId) {
218+
return Optional.of(new SpeedKmByHour(100));
219+
}
220+
221+
@Override
222+
public Optional<SpeedKmByHour> speedForRoadClass(RoadClass roadClass) {
223+
return Optional.of(new SpeedKmByHour(80.1));
224+
}
225+
}
226+
227+
final String bikeProfile = "bike_profile";
228+
229+
List<Profile> profiles = asList(
230+
new Profile(bikeProfile).setVehicle("bike")
231+
);
232+
233+
GraphHopper hopper = new GraphHopper().
234+
setGraphHopperLocation(GH_LOCATION).
235+
setOSMFile(MONACO).
236+
setProfiles(profiles).
237+
setStoreOnFlush(true);
238+
hopper.getCHPreparationHandler().setCHProfiles(
239+
new CHProfile(bikeProfile)
240+
);
241+
242+
GraphHopper hopperCustomSpeeds = new GraphHopperCustomSpeeds(new SpeedsTooHighSpeedProvider()).
243+
setGraphHopperLocation(GH_LOCATION_CUSTOM_SPEEDS).
244+
setOSMFile(MONACO).
245+
setProfiles(profiles).
246+
setStoreOnFlush(true)
247+
.setEncodedValuesString("roundabout, road_class, road_class_link, road_environment, max_speed, road_access, ferry_speed, bike_network, get_off_bike, smoothness, osm_way_id");
248+
hopperCustomSpeeds.getCHPreparationHandler().setCHProfiles(
249+
new CHProfile(bikeProfile)
250+
);
251+
252+
hopper.importOrLoad();
253+
hopperCustomSpeeds.importOrLoad();
254+
255+
GHRequest request = new GHRequest(43.73887, 7.42074, 43.73992, 7.42386);
256+
257+
GHResponse rspBike = hopper.route(request
258+
.setProfile(bikeProfile));
259+
ResponsePath resBike = rspBike.getBest();
260+
assertFalse(rspBike.hasErrors(), rspBike.getErrors().toString());
261+
assertEquals(55, resBike.getTime() / 1000f, 1); // 18kmh = 5 m/s
262+
assertEquals(277, resBike.getDistance(), 1);
263+
264+
GHResponse rspBikeSpeeds = hopperCustomSpeeds.route(request
265+
.setProfile(bikeProfile));
266+
ResponsePath resBikeSpeeds = rspBikeSpeeds.getBest();
267+
assertFalse(rspBikeSpeeds.hasErrors(), rspBikeSpeeds.getErrors().toString());
268+
assertEquals(55, resBikeSpeeds.getTime() / 1000f, 1); // 17kmh = 4.7 m/s
269+
assertEquals(277, resBikeSpeeds.getDistance(), 1);
270+
271+
// the result with and without custom speeds should be the same when the custom speed is > than the encoder limit
272+
assertEquals(resBike.getTime() / 1000f, resBikeSpeeds.getTime() / 1000f, 1);
273+
274+
}
135275
}
136276

0 commit comments

Comments
 (0)