Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ void flexDirect() {
// walk, flex
assertEquals(2, itin.legs().size());
assertEquals("2021-12-02T12:52:54-05:00[America/New_York]", itin.startTime().toString());
assertEquals(3203, itin.generalizedCost());
assertEquals(3253, itin.generalizedCost());

var walkToFlex = itin.streetLeg(0);
assertEquals(WALK, walkToFlex.getMode());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ private GraphQLObjectType createGraphQLType() {
.field(
GraphQLFieldDefinition.newFieldDefinition()
.name("stairsReluctance")
.description("Used instead of walkReluctance for stairs.")
.description(
"A multiplier to specify how bad walking on stairs is, compared to walking on flat ground."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should clarify that this is used in addition to the walk reluctance.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is still unchanged.

)
.type(Scalars.GraphQLFloat)
.dataFetcher(env -> preferences.walk().stairsReluctance())
.build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ private VehicleWalkingPreferences() {
this.reluctance = 5.0;
this.mountDismountTime = Duration.ZERO;
this.mountDismountCost = Cost.ZERO;
// very high reluctance to carry the bike up/down a flight of stairs
this.stairsReluctance = 10;
// multiplicative factor to carry the bike up/down a flight of stairs on top of the walk reluctance
this.stairsReluctance = 2;
}

/**
Expand Down Expand Up @@ -82,7 +82,7 @@ public Cost mountDismountCost() {
return mountDismountCost;
}

/** Reluctance of walking carrying a vehicle up a flight of stairs. */
/** Reluctance of carrying a vehicle up a flight of stairs on top of walking the vehicle. */
public double stairsReluctance() {
return stairsReluctance;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,10 @@ public int boardCost() {
return boardCost.toSeconds();
}

/** Used instead of walk reluctance for stairs */
/**
* Used on top of {@link #reluctance()} for stairs. If the value is set to 1, walking on stairs is
* treated the same as walking on flat ground.
*/
public double stairsReluctance() {
return stairsReluctance;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -841,7 +841,9 @@ private static void mapWalkPreferences(NodeAdapter root, WalkPreferences.Builder
c
.of("stairsReluctance")
.since(V2_0)
.summary("Used instead of walkReluctance for stairs.")
.summary(
"A multiplier to specify how bad walking on stairs is, on top of the reluctance parameter."
)
.asDouble(dft.stairsReluctance())
)
.withStairsTimeFactor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ private static void mapVehicleWalkingPreferences(
.of("stairsReluctance")
.since(V2_3)
.summary(
"How bad is it to walk the vehicle up/down a flight of stairs compared to taking a detour."
"How bad is it to walk the vehicle up/down a flight of stairs, on top of the reluctance parameter."
)
.asDouble(dft.stairsReluctance())
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1021,14 +1021,16 @@ private StateEditor doTraverse(State s0, TraverseMode traverseMode, boolean walk
* that during reverse traversal, we must also use the speed for the mode of
* the backEdge, rather than of the current edge.
*/
var intersectionMode = arriveBy ? backMode : traverseMode;
boolean walkingBikeThroughIntersection = arriveBy ? s0.isBackWalkingBike() : walkingBike;
if (arriveBy && tov instanceof IntersectionVertex traversedVertex) { // arrive-by search
turnDuration = s0
.intersectionTraversalCalculator()
.computeTraversalDuration(
traversedVertex,
this,
backPSE,
backMode,
intersectionMode,
(float) speed,
(float) backSpeed
);
Expand All @@ -1039,7 +1041,7 @@ private StateEditor doTraverse(State s0, TraverseMode traverseMode, boolean walk
traversedVertex,
backPSE,
this,
traverseMode,
intersectionMode,
(float) backSpeed,
(float) speed
);
Expand All @@ -1049,8 +1051,18 @@ private StateEditor doTraverse(State s0, TraverseMode traverseMode, boolean walk
turnDuration = 0;
}

var modeReluctance =
switch (intersectionMode) {
case WALK -> walkingBikeThroughIntersection
? preferences.bike().walking().reluctance()
: preferences.walk().reluctance();
case BICYCLE -> preferences.bike().reluctance();
case SCOOTER -> preferences.scooter().reluctance();
case CAR -> preferences.car().reluctance();
case FLEX -> 1;
};
time_ms += (long) Math.ceil(1000.0 * turnDuration);
weight += preferences.street().turnReluctance() * turnDuration;
weight += preferences.street().turnReluctance() * modeReluctance * turnDuration;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we do (turn reluctance + mode reluctance) * turn duration instead? We have had issues in the past the reluctance becomes supra linear. Same for the stairs reluctance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, reluctance is a factor. They can't be added together.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry I made a mistake so (turn reluctance + mode reluctance - 1) * turn duration would be more correct, but we can discuss this in another meeting.

Copy link
Contributor Author

@miklcct miklcct Sep 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't work mathematically as well. For example, put turn reluctance as 0.5 and mode reluctance as 0.2, it will result in a negative number.

}

if (!traverseMode.isInCar()) {
Expand Down Expand Up @@ -1152,10 +1164,6 @@ private TraversalCosts walkingTraversalCosts(
if (walkingBike) {
// take slopes into account when walking bikes
time = weight = (getEffectiveBikeDistance() / speed);
if (isStairs()) {
// we do allow walking the bike across a stairs but there is a very high default penalty
weight *= preferences.bike().walking().stairsReluctance();
}
} else {
// take slopes into account when walking
time = getEffectiveWalkDistance() / speed;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.opentripplanner.street.model.edge;

import org.opentripplanner.routing.api.request.preference.RoutingPreferences;
import org.opentripplanner.routing.api.request.preference.VehicleWalkingPreferences;
import org.opentripplanner.routing.api.request.preference.WalkPreferences;
import org.opentripplanner.street.search.TraverseMode;

class StreetEdgeReluctanceCalculator {
Expand All @@ -18,19 +20,28 @@ static double computeReluctance(
boolean walkingBike,
boolean edgeIsStairs
) {
if (edgeIsStairs) {
return pref.walk().stairsReluctance();
} else {
return switch (traverseMode) {
case WALK -> walkingBike ? pref.bike().walking().reluctance() : pref.walk().reluctance();
case BICYCLE -> pref.bike().reluctance();
case CAR -> pref.car().reluctance();
case SCOOTER -> pref.scooter().reluctance();
default -> throw new IllegalArgumentException(
"getReluctance(): Invalid mode " + traverseMode
);
};
}
return switch (traverseMode) {
case WALK -> walkingBike
? computeBikeWalkingReluctance(pref.bike().walking(), edgeIsStairs)
: computeWalkReluctance(pref.walk(), edgeIsStairs);
case BICYCLE -> pref.bike().reluctance();
case CAR -> pref.car().reluctance();
case SCOOTER -> pref.scooter().reluctance();
default -> throw new IllegalArgumentException(
"getReluctance(): Invalid mode " + traverseMode
);
};
}

private static double computeWalkReluctance(WalkPreferences pref, boolean edgeIsStairs) {
return pref.reluctance() * (edgeIsStairs ? pref.stairsReluctance() : 1);
}

private static double computeBikeWalkingReluctance(
VehicleWalkingPreferences pref,
boolean edgeIsStairs
) {
return pref.reluctance() * (edgeIsStairs ? pref.stairsReluctance() : 1);
}

static double computeWheelchairReluctance(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1109,7 +1109,7 @@ type RoutingParameters {
reverseOptimizeOnTheFly: Boolean @deprecated(reason : "NOT IN USE IN OTP2.")
"Whether the planner should return intermediate stops lists for transit legs."
showIntermediateStops: Boolean @deprecated(reason : "This parameter is always enabled")
"Used instead of walkReluctance for stairs."
"A multiplier to specify how bad walking on stairs is, compared to walking on flat ground."
stairsReluctance: Float
"An extra penalty added on transfers (i.e. all boardings except the first one)."
transferPenalty: Int
Expand Down
Loading
Loading