Skip to content

Commit 3da04c0

Browse files
Merge pull request opentripplanner#5783 from Skanetrafiken/fix-egress-transfer-cost-bug
Fix bug in heuristics cost calculation for egress legs
2 parents 329266c + a5d91fb commit 3da04c0

File tree

9 files changed

+128
-24
lines changed

9 files changed

+128
-24
lines changed

src/main/java/org/opentripplanner/raptor/rangeraptor/standard/heuristics/HeuristicsAdapter.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,13 @@ private int bestNumOfTransfers(int stop) {
140140
}
141141

142142
private int bestGeneralizedCost(int stop) {
143-
return costCalculator.calculateMinCost(bestTravelDuration(stop), bestNumOfTransfers(stop));
143+
return (
144+
costCalculator.calculateRemainingMinCost(
145+
bestTravelDuration(stop),
146+
bestNumOfTransfers(stop),
147+
stop
148+
)
149+
);
144150
}
145151

146152
/**

src/main/java/org/opentripplanner/raptor/spi/RaptorCostCalculator.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -52,12 +52,12 @@ int boardingCost(
5252

5353
/**
5454
* Used for estimating the remaining value for a criteria at a given stop arrival. The calculated
55-
* value should be a an optimistic estimate for the heuristics to work properly. So, to calculate
56-
* the generalized cost for given the {@code minTravelTime} and {@code minNumTransfers} retuning
55+
* value should be an optimistic estimate for the heuristics to work properly. So, to calculate
56+
* the generalized cost for given the {@code minTravelTime} and {@code minNumTransfers} returning
5757
* the greatest value, which is guaranteed to be less than the
58-
* <em>real value</em> would be correct and a good choose.
58+
* <em>real value</em> would be correct and a good choice.
5959
*/
60-
int calculateMinCost(int minTravelTime, int minNumTransfers);
60+
int calculateRemainingMinCost(int minTravelTime, int minNumTransfers, int fromStop);
6161

6262
/**
6363
* This method allows the cost calculator to add cost in addition to the generalized-cost of the

src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculator.java

+15-8
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,21 @@ public int waitCost(int waitTimeInSeconds) {
121121
}
122122

123123
@Override
124-
public int calculateMinCost(int minTravelTime, int minNumTransfers) {
125-
return (
126-
boardCostOnly +
127-
boardAndTransferCost *
128-
minNumTransfers +
129-
transitFactors.minFactor() *
130-
minTravelTime
131-
);
124+
public int calculateRemainingMinCost(int minTravelTime, int minNumTransfers, int fromStop) {
125+
if (minNumTransfers > -1) {
126+
return (
127+
boardCostOnly +
128+
boardAndTransferCost *
129+
minNumTransfers +
130+
transitFactors.minFactor() *
131+
minTravelTime
132+
);
133+
} else {
134+
// Remove cost that was added during alighting similar as we do in the costEgress() method
135+
int fixedCost = transitFactors.minFactor() * minTravelTime;
136+
137+
return stopTransferCost == null ? fixedCost : fixedCost - stopTransferCost[fromStop];
138+
}
132139
}
133140

134141
@Override

src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/PatternCostCalculator.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,8 @@ public int waitCost(int waitTimeInSeconds) {
7777
}
7878

7979
@Override
80-
public int calculateMinCost(int minTravelTime, int minNumTransfers) {
81-
return delegate.calculateMinCost(minTravelTime, minNumTransfers);
80+
public int calculateRemainingMinCost(int minTravelTime, int minNumTransfers, int fromStop) {
81+
return delegate.calculateRemainingMinCost(minTravelTime, minNumTransfers, fromStop);
8282
}
8383

8484
@Override

src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/WheelchairCostCalculator.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -66,8 +66,8 @@ public int waitCost(int waitTimeInSeconds) {
6666
}
6767

6868
@Override
69-
public int calculateMinCost(int minTravelTime, int minNumTransfers) {
70-
return delegate.calculateMinCost(minTravelTime, minNumTransfers);
69+
public int calculateRemainingMinCost(int minTravelTime, int minNumTransfers, int fromStop) {
70+
return delegate.calculateRemainingMinCost(minTravelTime, minNumTransfers, fromStop);
7171
}
7272

7373
@Override

src/test/java/org/opentripplanner/raptor/_data/RaptorTestConstants.java

+2
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ public interface RaptorTestConstants {
6060
int STOP_L = 12;
6161
int STOP_M = 13;
6262

63+
int NUM_STOPS = 14;
64+
6365
// Stop position in pattern
6466
int STOP_POS_0 = 0;
6567
int STOP_POS_1 = 1;

src/test/java/org/opentripplanner/raptor/_data/transit/TestTransitData.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ public class TestTransitData
6666
private final List<ConstrainedTransfer> constrainedTransfers = new ArrayList<>();
6767
private final GeneralizedCostParametersBuilder costParamsBuilder = GeneralizedCostParameters.of();
6868

69+
private final int[] stopBoardAlightCosts = new int[NUM_STOPS];
70+
6971
private RaptorSlackProvider slackProvider = SLACK_PROVIDER;
7072

7173
@Override
@@ -300,6 +302,11 @@ public TestTransitData withConstrainedTransfer(
300302
return this;
301303
}
302304

305+
public TestTransitData withStopBoardAlightCost(int stop, int boardAlightCost) {
306+
stopBoardAlightCosts[stop] = boardAlightCost;
307+
return this;
308+
}
309+
303310
public GeneralizedCostParametersBuilder mcCostParamsBuilder() {
304311
return costParamsBuilder;
305312
}
@@ -352,7 +359,7 @@ public List<DefaultTripPattern> getPatterns() {
352359

353360
private int[] stopBoardAlightCost() {
354361
// Not implemented, no test for this yet.
355-
return null;
362+
return stopBoardAlightCosts;
356363
}
357364

358365
private void expandNumOfStops(int stopIndex) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package org.opentripplanner.raptor.moduletests;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.opentripplanner.raptor._data.transit.TestRoute.route;
5+
import static org.opentripplanner.raptor._data.transit.TestTripSchedule.schedule;
6+
import static org.opentripplanner.raptor.moduletests.support.RaptorModuleTestConfig.multiCriteria;
7+
8+
import java.util.List;
9+
import org.junit.jupiter.api.BeforeEach;
10+
import org.junit.jupiter.params.ParameterizedTest;
11+
import org.junit.jupiter.params.provider.MethodSource;
12+
import org.opentripplanner.raptor.RaptorService;
13+
import org.opentripplanner.raptor._data.RaptorTestConstants;
14+
import org.opentripplanner.raptor._data.transit.TestAccessEgress;
15+
import org.opentripplanner.raptor._data.transit.TestTransitData;
16+
import org.opentripplanner.raptor._data.transit.TestTripSchedule;
17+
import org.opentripplanner.raptor.api.request.RaptorRequestBuilder;
18+
import org.opentripplanner.raptor.configure.RaptorConfig;
19+
import org.opentripplanner.raptor.moduletests.support.ModuleTestDebugLogging;
20+
import org.opentripplanner.raptor.moduletests.support.RaptorModuleTestCase;
21+
22+
/**
23+
* FEATURE UNDER TEST
24+
* <p>
25+
* This verifies that the stopTransferCost is not applied for egress legs. If this is not correctly
26+
* handled by the heuristics optimization, the cheapest journey could be discarded.
27+
*/
28+
public class B05_EgressStopTransferCostTest implements RaptorTestConstants {
29+
30+
private final TestTransitData data = new TestTransitData();
31+
private final RaptorRequestBuilder<TestTripSchedule> requestBuilder = new RaptorRequestBuilder<>();
32+
private final RaptorService<TestTripSchedule> raptorService = new RaptorService<>(
33+
RaptorConfig.defaultConfigForTest()
34+
);
35+
36+
@BeforeEach
37+
void setup() {
38+
data
39+
.withRoute(route("R1", STOP_B, STOP_C).withTimetable(schedule("0:10, 0:14")))
40+
.withRoute(route("R2", STOP_C, STOP_D).withTimetable(schedule("0:18, 0:20")));
41+
42+
data.mcCostParamsBuilder().transferCost(0).boardCost(0);
43+
data.withStopBoardAlightCost(STOP_D, 60000);
44+
45+
requestBuilder
46+
.searchParams()
47+
.addAccessPaths(TestAccessEgress.free(STOP_B))
48+
.addEgressPaths(
49+
TestAccessEgress.walk(STOP_C, D5m), // This will be the fastest
50+
TestAccessEgress.walk(STOP_D, D20s) // This will be the cheapest
51+
)
52+
.earliestDepartureTime(T00_00)
53+
.latestArrivalTime(T00_30);
54+
55+
ModuleTestDebugLogging.setupDebugLogging(data, requestBuilder);
56+
}
57+
58+
static List<RaptorModuleTestCase> testCases() {
59+
return RaptorModuleTestCase
60+
.of()
61+
.add(
62+
multiCriteria(),
63+
// We should get both the fastest and the c1-cheapest results
64+
// The stopTransferCost should not be applied to the egress leg from STOP_D
65+
"B ~ BUS R1 0:10 0:14 ~ C ~ Walk 5m [0:10 0:19 9m Tₓ0 C₁840]",
66+
"B ~ BUS R1 0:10 0:14 ~ C ~ BUS R2 0:18 0:20 ~ D ~ Walk 20s [0:10 0:20:20 10m20s Tₓ1 C₁640]"
67+
)
68+
.build();
69+
}
70+
71+
@ParameterizedTest
72+
@MethodSource("testCases")
73+
void testRaptor(RaptorModuleTestCase testCase) {
74+
assertEquals(testCase.expected(), testCase.run(raptorService, data, requestBuilder));
75+
}
76+
}

src/test/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/cost/DefaultCostCalculatorTest.java

+12-6
Original file line numberDiff line numberDiff line change
@@ -76,22 +76,28 @@ public void calculateMinCost() {
7676
// - Transit factor: 80 (min of 80 and 100)
7777

7878
// Board cost is 500:
79-
assertEquals(500, subject.calculateMinCost(0, 0));
79+
assertEquals(500, subject.calculateRemainingMinCost(0, 0, 0));
8080
// The transfer 1s * 80 = 80 + board cost 500
81-
assertEquals(580, subject.calculateMinCost(1, 0));
81+
assertEquals(580, subject.calculateRemainingMinCost(1, 0, 0));
8282
// Board 2 times and transfer 1: 2 * 500 + 200
83-
assertEquals(1200, subject.calculateMinCost(0, 1));
83+
assertEquals(1200, subject.calculateRemainingMinCost(0, 1, 0));
8484

8585
// Transit 200s * 80 + Board 4 * 500 + Transfer 3 * 200
86-
assertEquals(18_600, subject.calculateMinCost(200, 3));
86+
assertEquals(18_600, subject.calculateRemainingMinCost(200, 3, 0));
87+
88+
// Cost of egress should subtract the stop transfer cost 25
89+
assertEquals(-25, subject.calculateRemainingMinCost(0, -1, 1));
8790
}
8891

8992
@Test
9093
public void testConvertBetweenRaptorAndMainOtpDomainModel() {
91-
assertEquals(RaptorCostConverter.toRaptorCost(BOARD_COST_SEC), subject.calculateMinCost(0, 0));
94+
assertEquals(
95+
RaptorCostConverter.toRaptorCost(BOARD_COST_SEC),
96+
subject.calculateRemainingMinCost(0, 0, 0)
97+
);
9298
assertEquals(
9399
RaptorCostConverter.toRaptorCost(0.8 * 20 + BOARD_COST_SEC),
94-
subject.calculateMinCost(20, 0)
100+
subject.calculateRemainingMinCost(20, 0, 0)
95101
);
96102
}
97103

0 commit comments

Comments
 (0)