From 04ae4997a70d97eb3771647f5807b7c9745b4a38 Mon Sep 17 00:00:00 2001 From: Leonard Ehrenfried Date: Wed, 15 Nov 2023 15:45:05 +0100 Subject: [PATCH] Add sanity limit for the cost of a transfer --- .../raptoradapter/transit/Transfer.java | 54 +++++++++++++------ .../street/search/state/State.java | 16 ++++++ 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java index 169a44cb730..86202687dbd 100644 --- a/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java +++ b/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/transit/Transfer.java @@ -1,5 +1,6 @@ package org.opentripplanner.routing.algorithm.raptoradapter.transit; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -16,6 +17,19 @@ public class Transfer { + /** + * Since transfers costs are not computed through a full A* they can incur an absurdly high + * cost that overflows the integer cost inside raptor. + *

+ * An example would be a transfer using lots of stairs being used on a wheelchair when no + * wheelchair-specific one has been generated. + * (see https://docs.opentripplanner.org/en/dev-2.x/Accessibility/). + *

+ * For this reason there is this sanit limit that make sure that the transfer cost stays inside a + * bound that is still very high but far away from the integer overflow. + */ + private final double MAX_TRANSFER_COST = Duration.ofDays(3).toSeconds(); + private final int toStop; private final int distanceMeters; @@ -63,14 +77,19 @@ public Optional asRaptorTransfer(StreetSearchRequest request) { WalkPreferences walkPreferences = request.preferences().walk(); if (edges == null || edges.isEmpty()) { double durationSeconds = distanceMeters / walkPreferences.speed(); - return Optional.of( - new DefaultRaptorTransfer( - this.toStop, - (int) Math.ceil(durationSeconds), - RaptorCostConverter.toRaptorCost(durationSeconds * walkPreferences.reluctance()), - this - ) - ); + final double domainCost = durationSeconds * walkPreferences.reluctance(); + if (domainCost > MAX_TRANSFER_COST) { + return Optional.empty(); + } else { + return Optional.of( + new DefaultRaptorTransfer( + this.toStop, + (int) Math.ceil(durationSeconds), + RaptorCostConverter.toRaptorCost(domainCost), + this + ) + ); + } } StateEditor se = new StateEditor(edges.get(0).getFromVertex(), request); @@ -78,14 +97,17 @@ public Optional asRaptorTransfer(StreetSearchRequest request) { var state = EdgeTraverser.traverseEdges(se.makeState(), edges); - return state.map(s -> - new DefaultRaptorTransfer( - this.toStop, - (int) s.getElapsedTimeSeconds(), - RaptorCostConverter.toRaptorCost(s.getWeight()), - this - ) - ); + return state + .filter(s -> s.weight < MAX_TRANSFER_COST) + .map(s -> { + final int raptorCost = RaptorCostConverter.toRaptorCost(s.getWeight()); + return new DefaultRaptorTransfer( + this.toStop, + (int) s.getElapsedTimeSeconds(), + raptorCost, + this + ); + }); } @Override diff --git a/src/main/java/org/opentripplanner/street/search/state/State.java b/src/main/java/org/opentripplanner/street/search/state/State.java index 37c3e81c097..b3f6c7e7ec2 100644 --- a/src/main/java/org/opentripplanner/street/search/state/State.java +++ b/src/main/java/org/opentripplanner/street/search/state/State.java @@ -5,6 +5,7 @@ import java.time.Instant; import java.util.ArrayList; import java.util.Collection; +import java.util.List; import java.util.Objects; import java.util.Set; import java.util.stream.Stream; @@ -481,6 +482,21 @@ public String toString() { .toString(); } + /** + * Iterates over all back states of this instance and returns them as a list. + *

+ * Note: Use this only for debugging not for performance-critical paths! + */ + public List chain() { + List states = new ArrayList<>(); + State current = this; + while (current != null) { + states.add(current); + current = current.backState; + } + return states; + } + void checkNegativeWeight() { double dw = this.weight - backState.weight; if (dw < 0) {