diff --git a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/TransitRouter.java b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/TransitRouter.java index d332013d5ac..23293e6d200 100644 --- a/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/TransitRouter.java +++ b/application/src/main/java/org/opentripplanner/routing/algorithm/raptoradapter/router/TransitRouter.java @@ -36,6 +36,7 @@ import org.opentripplanner.routing.algorithm.transferoptimization.configure.TransferOptimizationServiceConfigurator; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.api.request.StreetMode; +import org.opentripplanner.routing.api.request.preference.AccessEgressPreferences; import org.opentripplanner.routing.api.request.request.StreetRequest; import org.opentripplanner.routing.api.response.InputField; import org.opentripplanner.routing.api.response.RoutingError; @@ -239,6 +240,7 @@ private Collection fetchEgress() { private Collection fetchAccessEgresses(AccessEgressType type) { var streetRequest = type.isAccess() ? request.journey().access() : request.journey().egress(); + StreetMode mode = streetRequest.mode(); // Prepare access/egress lists RouteRequest accessRequest = request.clone(); @@ -252,13 +254,15 @@ private Collection fetchAccessEgresses(AccessEgre }); } - Duration durationLimit = accessRequest + AccessEgressPreferences accessEgressPreferences = accessRequest .preferences() .street() - .accessEgress() - .maxDuration() - .valueOf(streetRequest.mode()); - int stopCountLimit = accessRequest.preferences().street().accessEgress().maxStopCount(); + .accessEgress(); + + Duration durationLimit = accessEgressPreferences.maxDuration().valueOf(mode); + int stopCountLimit = accessEgressPreferences + .maxStopCountForMode() + .getOrDefault(mode, accessEgressPreferences.defaultMaxStopCount()); var nearbyStops = AccessEgressRouter.findAccessEgresses( accessRequest, @@ -275,7 +279,7 @@ private Collection fetchAccessEgresses(AccessEgre var results = new ArrayList<>(accessEgresses); // Special handling of flex accesses - if (OTPFeature.FlexRouting.isOn() && streetRequest.mode() == StreetMode.FLEXIBLE) { + if (OTPFeature.FlexRouting.isOn() && mode == StreetMode.FLEXIBLE) { var flexAccessList = FlexAccessEgressRouter.routeAccessEgress( accessRequest, temporaryVerticesContainer, diff --git a/application/src/main/java/org/opentripplanner/routing/api/request/preference/AccessEgressPreferences.java b/application/src/main/java/org/opentripplanner/routing/api/request/preference/AccessEgressPreferences.java index 289e06e6e02..4a8c342275f 100644 --- a/application/src/main/java/org/opentripplanner/routing/api/request/preference/AccessEgressPreferences.java +++ b/application/src/main/java/org/opentripplanner/routing/api/request/preference/AccessEgressPreferences.java @@ -4,6 +4,7 @@ import java.io.Serializable; import java.time.Duration; +import java.util.Collections; import java.util.Map; import java.util.Objects; import java.util.function.Consumer; @@ -27,18 +28,21 @@ public final class AccessEgressPreferences implements Serializable { private final TimeAndCostPenaltyForEnum penalty; private final DurationForEnum maxDuration; - private final int maxStopCount; + private final int defaultMaxStopCount; + private final Map maxStopCountForMode; private AccessEgressPreferences() { this.maxDuration = durationForStreetModeOf(ofMinutes(45)); this.penalty = DEFAULT_TIME_AND_COST; - this.maxStopCount = 500; + this.defaultMaxStopCount = 500; + this.maxStopCountForMode = Map.of(); } private AccessEgressPreferences(Builder builder) { this.maxDuration = builder.maxDuration; this.penalty = builder.penalty; - this.maxStopCount = builder.maxStopCount; + this.defaultMaxStopCount = builder.defaultMaxStopCount; + this.maxStopCountForMode = Collections.unmodifiableMap(builder.maxStopCountForMode); } public static Builder of() { @@ -57,8 +61,12 @@ public DurationForEnum maxDuration() { return maxDuration; } - public int maxStopCount() { - return maxStopCount; + public int defaultMaxStopCount() { + return defaultMaxStopCount; + } + + public Map maxStopCountForMode() { + return maxStopCountForMode; } @Override @@ -69,13 +77,14 @@ public boolean equals(Object o) { return ( penalty.equals(that.penalty) && maxDuration.equals(that.maxDuration) && - maxStopCount == that.maxStopCount + defaultMaxStopCount == that.defaultMaxStopCount && + maxStopCountForMode.equals(that.maxStopCountForMode) ); } @Override public int hashCode() { - return Objects.hash(penalty, maxDuration, maxStopCount); + return Objects.hash(penalty, maxDuration, defaultMaxStopCount, maxStopCountForMode); } @Override @@ -84,7 +93,8 @@ public String toString() { .of(AccessEgressPreferences.class) .addObj("penalty", penalty, DEFAULT.penalty) .addObj("maxDuration", maxDuration, DEFAULT.maxDuration) - .addObj("maxStopCount", maxStopCount, DEFAULT.maxStopCount) + .addObj("defaultMaxStopCount", defaultMaxStopCount, DEFAULT.defaultMaxStopCount) + .addObj("maxStopCountForMode", maxStopCountForMode, DEFAULT.maxStopCountForMode) .toString(); } @@ -93,13 +103,15 @@ public static class Builder { private final AccessEgressPreferences original; private TimeAndCostPenaltyForEnum penalty; private DurationForEnum maxDuration; - private int maxStopCount; + private Map maxStopCountForMode; + private int defaultMaxStopCount; public Builder(AccessEgressPreferences original) { this.original = original; this.maxDuration = original.maxDuration; this.penalty = original.penalty; - this.maxStopCount = original.maxStopCount; + this.defaultMaxStopCount = original.defaultMaxStopCount; + this.maxStopCountForMode = original.maxStopCountForMode; } public Builder withMaxDuration(Consumer> body) { @@ -112,8 +124,12 @@ public Builder withMaxDuration(Duration defaultValue, Map return withMaxDuration(b -> b.withDefault(defaultValue).withValues(values)); } - public Builder withMaxStopCount(int maxCount) { - this.maxStopCount = maxCount; + public Builder withMaxStopCount( + int defaultMaxStopCount, + Map maxStopCountForMode + ) { + this.defaultMaxStopCount = defaultMaxStopCount; + this.maxStopCountForMode = maxStopCountForMode; return this; } diff --git a/application/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java index 454ab29a68c..3ea1e83148c 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/routerequest/RouteRequestConfig.java @@ -542,7 +542,20 @@ duration can be set per mode(`maxDurationForMode`), because some street modes se Safety limit to prevent access to and egress from too many stops. """ ) - .asInt(dftAccessEgress.maxStopCount()) + .asInt(dftAccessEgress.defaultMaxStopCount()), + cae + .of("maxStopCountForMode") + .since(V2_7) + .summary( + "Maximal number of stops collected in access/egress routing for the given mode" + ) + .description( + """ + Safety limit to prevent access to and egress from too many stops. + Mode-specific version of `maxStopCount`. + """ + ) + .asEnumMap(StreetMode.class, Integer.class) ); }) .withMaxDirectDuration( diff --git a/doc/user/RouteRequest.md b/doc/user/RouteRequest.md index 332058b2a42..f223c644604 100644 --- a/doc/user/RouteRequest.md +++ b/doc/user/RouteRequest.md @@ -46,6 +46,7 @@ and in the [transferRequests in build-config.json](BuildConfiguration.md#transfe |    [maxDuration](#rd_accessEgress_maxDuration) | `duration` | This is the maximum duration for access/egress for street searches. | *Optional* | `"PT45M"` | 2.1 | |    [maxStopCount](#rd_accessEgress_maxStopCount) | `integer` | Maximal number of stops collected in access/egress routing | *Optional* | `500` | 2.4 | |    [maxDurationForMode](#rd_accessEgress_maxDurationForMode) | `enum map of duration` | Limit access/egress per street mode. | *Optional* | | 2.1 | +|    [maxStopCountForMode](#rd_accessEgress_maxStopCountForMode) | `enum map of integer` | Maximal number of stops collected in access/egress routing for the given mode | *Optional* | | 2.7 | |    [penalty](#rd_accessEgress_penalty) | `enum map of object` | Penalty for access/egress by street mode. | *Optional* | | 2.4 | |       FLEXIBLE | `object` | NA | *Optional* | | 2.4 | |          costFactor | `double` | A factor multiplied with the time-penalty to get the cost-penalty. | *Optional* | `0.0` | 2.4 | @@ -431,6 +432,18 @@ Override the settings in `maxDuration` for specific street modes. This is done because some street modes searches are much more resource intensive than others. +

maxStopCountForMode

+ +**Since version:** `2.7` ∙ **Type:** `enum map of integer` ∙ **Cardinality:** `Optional` +**Path:** /routingDefaults/accessEgress +**Enum keys:** `not-set` | `walk` | `bike` | `bike-to-park` | `bike-rental` | `scooter-rental` | `car` | `car-to-park` | `car-pickup` | `car-rental` | `car-hailing` | `flexible` + +Maximal number of stops collected in access/egress routing for the given mode + +Safety limit to prevent access to and egress from too many stops. +Mode-specific version of `maxStopCount`. + +

penalty

**Since version:** `2.4` ∙ **Type:** `enum map of object` ∙ **Cardinality:** `Optional`