Skip to content

Commit f68d4f1

Browse files
committed
exaggerate SAFEST_STREET routing
1 parent d467f91 commit f68d4f1

File tree

5 files changed

+100
-40
lines changed

5 files changed

+100
-40
lines changed

application/src/main/java/org/opentripplanner/street/model/edge/StreetEdge.java

Lines changed: 8 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
package org.opentripplanner.street.model.edge;
22

3+
import static org.opentripplanner.street.model.edge.StreetEdgeReluctanceCalculator.computeReluctance;
4+
import static org.opentripplanner.street.model.edge.StreetEdgeReluctanceCalculator.getSafetyForSafestStreet;
5+
36
import java.io.IOException;
47
import java.io.ObjectOutputStream;
58
import java.util.ArrayList;
69
import java.util.Arrays;
7-
import java.util.Collections;
8-
import java.util.HashSet;
9-
import java.util.List;
1010
import java.util.Objects;
1111
import java.util.Optional;
1212
import org.locationtech.jts.geom.LineString;
@@ -22,8 +22,6 @@
2222
import org.opentripplanner.routing.util.ElevationUtils;
2323
import org.opentripplanner.street.model.RentalRestrictionExtension;
2424
import org.opentripplanner.street.model.StreetTraversalPermission;
25-
import org.opentripplanner.street.model.TurnRestriction;
26-
import org.opentripplanner.street.model.TurnRestrictionType;
2725
import org.opentripplanner.street.model.vertex.BarrierVertex;
2826
import org.opentripplanner.street.model.vertex.IntersectionVertex;
2927
import org.opentripplanner.street.model.vertex.SplitterVertex;
@@ -49,8 +47,6 @@ public class StreetEdge
4947

5048
private static final Logger LOG = LoggerFactory.getLogger(StreetEdge.class);
5149

52-
private static final double SAFEST_STREETS_SAFETY_FACTOR = 0.1;
53-
5450
/** If you have more than 16 flags, increase flags to short or int */
5551
static final int BACK_FLAG_INDEX = 0;
5652
static final int ROUNDABOUT_FLAG_INDEX = 1;
@@ -1067,14 +1063,7 @@ private TraversalCosts otherTraversalCosts(
10671063
double speed
10681064
) {
10691065
var time = getDistanceMeters() / speed;
1070-
var weight =
1071-
time *
1072-
StreetEdgeReluctanceCalculator.computeReluctance(
1073-
preferences,
1074-
traverseMode,
1075-
walkingBike,
1076-
isStairs()
1077-
);
1066+
var weight = time * computeReluctance(preferences, traverseMode, walkingBike, isStairs());
10781067
return new TraversalCosts(time, weight);
10791068
}
10801069

@@ -1089,13 +1078,8 @@ private TraversalCosts bicycleOrScooterTraversalCost(
10891078
? pref.bike().optimizeType()
10901079
: pref.scooter().optimizeType();
10911080
switch (optimizeType) {
1092-
case SAFEST_STREETS -> {
1093-
weight = (bicycleSafetyFactor * getDistanceMeters()) / speed;
1094-
if (bicycleSafetyFactor <= SAFEST_STREETS_SAFETY_FACTOR) {
1095-
// safest streets are treated as even safer than they really are
1096-
weight *= 0.66;
1097-
}
1098-
}
1081+
case SAFEST_STREETS -> weight = // we exaggerate the safety for this preference
1082+
(getSafetyForSafestStreet(bicycleSafetyFactor) * getDistanceMeters()) / speed;
10991083
case SAFE_STREETS -> weight = getEffectiveBicycleSafetyDistance() / speed;
11001084
case FLAT_STREETS -> /* see notes in StreetVertex on speed overhead */weight =
11011085
getEffectiveBikeDistanceForWorkCost() / speed;
@@ -1112,12 +1096,7 @@ private TraversalCosts bicycleOrScooterTraversalCost(
11121096
}
11131097
default -> weight = getDistanceMeters() / speed;
11141098
}
1115-
var reluctance = StreetEdgeReluctanceCalculator.computeReluctance(
1116-
pref,
1117-
mode,
1118-
false,
1119-
isStairs()
1120-
);
1099+
var reluctance = computeReluctance(pref, mode, false, isStairs());
11211100
weight *= reluctance;
11221101
return new TraversalCosts(time, weight);
11231102
}
@@ -1157,12 +1136,7 @@ private TraversalCosts walkingTraversalCosts(
11571136
weight /= speed;
11581137
}
11591138

1160-
weight *= StreetEdgeReluctanceCalculator.computeReluctance(
1161-
preferences,
1162-
traverseMode,
1163-
walkingBike,
1164-
isStairs()
1165-
);
1139+
weight *= computeReluctance(preferences, traverseMode, walkingBike, isStairs());
11661140
}
11671141

11681142
return new TraversalCosts(time, weight);

application/src/main/java/org/opentripplanner/street/model/edge/StreetEdgeReluctanceCalculator.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import org.opentripplanner.routing.api.request.preference.RoutingPreferences;
44
import org.opentripplanner.street.search.TraverseMode;
55

6-
class StreetEdgeReluctanceCalculator {
6+
public class StreetEdgeReluctanceCalculator {
77

88
/** Utility class, private constructor to prevent instantiation */
99
private StreetEdgeReluctanceCalculator() {}
@@ -60,4 +60,13 @@ static double computeWheelchairReluctance(
6060
}
6161
return reluctance;
6262
}
63+
64+
/**
65+
* Exaggerate the safety for SAFEST_STREET routing.
66+
* The effect is to make normal streets not safe, "reasonably safe" streets appear neutral, and
67+
* "very safe" streets appear safe.
68+
*/
69+
public static double getSafetyForSafestStreet(double originalSafety) {
70+
return originalSafety * originalSafety * 2.0;
71+
}
6372
}

application/src/main/java/org/opentripplanner/street/search/strategy/EuclideanRemainingWeightHeuristic.java

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package org.opentripplanner.street.search.strategy;
22

3+
import static org.opentripplanner.street.model.edge.StreetEdgeReluctanceCalculator.getSafetyForSafestStreet;
4+
35
import java.util.Comparator;
46
import java.util.Set;
57
import java.util.stream.Stream;
68
import org.opentripplanner.astar.spi.RemainingWeightHeuristic;
79
import org.opentripplanner.framework.geometry.SphericalDistanceLibrary;
810
import org.opentripplanner.routing.api.request.StreetMode;
911
import org.opentripplanner.routing.api.request.preference.RoutingPreferences;
12+
import org.opentripplanner.routing.api.request.preference.TimeSlopeSafetyTriangle;
13+
import org.opentripplanner.routing.core.VehicleRoutingOptimizeType;
1014
import org.opentripplanner.street.model.edge.Edge;
1115
import org.opentripplanner.street.model.edge.FreeEdge;
1216
import org.opentripplanner.street.model.vertex.Vertex;
@@ -61,22 +65,49 @@ public void initialize(
6165
lon = target.getLon();
6266
}
6367

68+
private double scaleSafety(double safety, double safetyFactor) {
69+
return 1 + (safety - 1) * safetyFactor;
70+
}
71+
72+
private double getEffectiveSafetyForOptimization(
73+
VehicleRoutingOptimizeType optimizeType,
74+
TimeSlopeSafetyTriangle triangle,
75+
double safety
76+
) {
77+
return switch (optimizeType) {
78+
case SHORTEST_DURATION -> 1.0;
79+
case SAFE_STREETS -> safety;
80+
case FLAT_STREETS -> 1.0;
81+
case SAFEST_STREETS -> getSafetyForSafestStreet(safety);
82+
case TRIANGLE -> scaleSafety(safety, triangle.safety());
83+
};
84+
}
85+
6486
/**
6587
* @return the minimum of (pace × reluctance × safety) for the applicable modes
6688
*/
6789
private double getMinimumCostPerDistance(RoutingPreferences preferences, StreetMode streetMode) {
6890
double drivingPace = streetMode.includesDriving()
6991
? 1.0 / streetLimitationParametersService.getMaxCarSpeed()
7092
: Double.MAX_VALUE;
93+
double bestBikeSafety = streetLimitationParametersService.getBestBikeSafety();
7194
double cyclingPace = streetMode.includesBiking()
7295
? (1.0 / preferences.bike().speed()) *
7396
preferences.bike().reluctance() *
74-
streetLimitationParametersService.getBestBikeSafety()
97+
getEffectiveSafetyForOptimization(
98+
preferences.bike().optimizeType(),
99+
preferences.bike().optimizeTriangle(),
100+
bestBikeSafety
101+
)
75102
: Double.MAX_VALUE;
76103
double scooterPace = streetMode.includesScooter()
77104
? (1.0 / preferences.scooter().speed()) *
78105
preferences.scooter().reluctance() *
79-
streetLimitationParametersService.getBestBikeSafety()
106+
getEffectiveSafetyForOptimization(
107+
preferences.scooter().optimizeType(),
108+
preferences.scooter().optimizeTriangle(),
109+
bestBikeSafety
110+
)
80111
: Double.MAX_VALUE;
81112
double walkingPace = streetMode.includesWalking()
82113
? getWalkingCostPerDistance(preferences)
@@ -90,7 +121,10 @@ private double getWalkingCostPerDistance(RoutingPreferences preferences) {
90121
return (
91122
(1.0 / preferences.walk().speed()) *
92123
preferences.walk().reluctance() *
93-
streetLimitationParametersService.getBestWalkSafety()
124+
scaleSafety(
125+
streetLimitationParametersService.getBestWalkSafety(),
126+
preferences.walk().safetyFactor()
127+
)
94128
);
95129
}
96130

application/src/test/java/org/opentripplanner/street/model/edge/StreetEdgeCostTest.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import org.junit.jupiter.params.provider.Arguments;
1111
import org.junit.jupiter.params.provider.MethodSource;
1212
import org.opentripplanner.routing.api.request.StreetMode;
13+
import org.opentripplanner.routing.core.VehicleRoutingOptimizeType;
1314
import org.opentripplanner.street.model.StreetTraversalPermission;
1415
import org.opentripplanner.street.search.request.StreetSearchRequest;
1516
import org.opentripplanner.street.search.state.State;
@@ -79,6 +80,38 @@ public void bikeReluctance(double bikeReluctance, long expectedCost) {
7980
assertEquals(20, result.getElapsedTimeSeconds());
8081
}
8182

83+
static Stream<Arguments> bikeSafetyCases() {
84+
return Stream.of(
85+
Arguments.of(VehicleRoutingOptimizeType.SHORTEST_DURATION, 20),
86+
Arguments.of(VehicleRoutingOptimizeType.SAFE_STREETS, 40),
87+
Arguments.of(VehicleRoutingOptimizeType.SAFEST_STREETS, 160)
88+
);
89+
}
90+
91+
@ParameterizedTest(name = "bikeOptimizeType of {0} should lead to traversal costs of {1}")
92+
@MethodSource("bikeSafetyCases")
93+
public void bikeSafety(VehicleRoutingOptimizeType type, long expectedCost) {
94+
double length = 100;
95+
var edge = new StreetEdgeBuilder<>()
96+
.withFromVertex(V1)
97+
.withToVertex(V2)
98+
.withName("edge")
99+
.withMeterLength(length)
100+
.withPermission(StreetTraversalPermission.ALL)
101+
.withBicycleSafetyFactor(2)
102+
.withBack(false)
103+
.buildAndConnect();
104+
105+
var req = StreetSearchRequest.of();
106+
req.withPreferences(p -> p.withBike(b -> b.withReluctance(1.0).withOptimizeType(type)));
107+
108+
State result = traverse(edge, req.withMode(StreetMode.BIKE).build());
109+
assertNotNull(result);
110+
assertEquals(expectedCost, (long) result.weight);
111+
112+
assertEquals(20, result.getElapsedTimeSeconds());
113+
}
114+
82115
static Stream<Arguments> carReluctanceCases() {
83116
return Stream.of(
84117
Arguments.of(0.5, 4),

application/src/test/java/org/opentripplanner/street/search/strategy/EuclideanRemainingWeightHeuristicTest.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,12 +101,22 @@ public static Stream<Arguments> testCases() {
101101
RoutingPreferences.of()
102102
.withBike(b -> b.withOptimizeType(VehicleRoutingOptimizeType.SAFEST_STREETS))
103103
.build(),
104-
24
104+
28.8
105105
),
106106
// a slow car
107107
Arguments.argumentSet("slow car", slowCar, StreetMode.CAR, RoutingPreferences.DEFAULT, 100),
108108
// slow car speed should not affect cycling
109-
Arguments.argumentSet("slow car", slowCar, StreetMode.BIKE, RoutingPreferences.DEFAULT, 40)
109+
Arguments.argumentSet("slow car", slowCar, StreetMode.BIKE, RoutingPreferences.DEFAULT, 40),
110+
// minimum safety 0.8 with safety factor 0.6 = effectively safety factor 0.88
111+
Arguments.argumentSet(
112+
"intermediate walk safety",
113+
safeStreets,
114+
StreetMode.WALK,
115+
RoutingPreferences.of()
116+
.withWalk(w -> w.withSpeed(1).withReluctance(1).withSafetyFactor(0.6))
117+
.build(),
118+
88
119+
)
110120
);
111121
}
112122

0 commit comments

Comments
 (0)