Skip to content
Draft
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 @@ -11,6 +11,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import org.opentripplanner.core.model.id.FeedScopedId;
import org.opentripplanner.framework.application.OTPFeature;
import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore;
import org.opentripplanner.graph_builder.issues.StopNotLinkedForTransfers;
Expand Down Expand Up @@ -51,6 +52,7 @@ public class DirectTransferGenerator implements GraphBuilderModule {

private final Duration defaultMaxTransferDuration;

private final Set<FeedScopedId> includeStopsWithRegularTransfers;
private final List<RouteRequest> transferRequests;
private final Map<StreetMode, TransferParametersForMode> transferParametersForMode;
private final Graph graph;
Expand All @@ -59,7 +61,8 @@ public class DirectTransferGenerator implements GraphBuilderModule {
private final DataImportIssueStore issueStore;

/**
* Constructor used in tests. This initializes transferParametersForMode as an empty map.
* Constructor used in tests. This initializes transferParametersForMode as an empty map and
* stopsWithRegularTransfers as an empty set.
*/
public DirectTransferGenerator(
Graph graph,
Expand All @@ -73,6 +76,7 @@ public DirectTransferGenerator(
this.timetableRepository = timetableRepository;
this.issueStore = issueStore;
this.defaultMaxTransferDuration = defaultMaxTransferDuration;
this.includeStopsWithRegularTransfers = Set.of();
this.transferRequests = transferRequests;
this.transferRepository = transferRepository;
this.transferParametersForMode = Map.of();
Expand All @@ -89,6 +93,7 @@ public DirectTransferGenerator(
this.timetableRepository = timetableRepository;
this.issueStore = issueStore;
this.defaultMaxTransferDuration = parameters.maxDuration();
this.includeStopsWithRegularTransfers = Set.copyOf(parameters.includeStops());
this.transferRequests = parameters.requests();
this.transferParametersForMode = parameters.parametersForMode();
this.transferRepository = transferRepository;
Expand Down Expand Up @@ -247,7 +252,11 @@ private NearbyStopFinder createNearbyStopFinder(Duration radiusAsDuration) {
}

if (OTPFeature.ConsiderPatternsForDirectTransfers.isOn()) {
return new PatternConsideringNearbyStopFinder(transitService, finder);
return new PatternConsideringNearbyStopFinder(
transitService,
finder,
includeStopsWithRegularTransfers
);
} else {
return finder;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,34 @@
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.opentripplanner.core.model.id.FeedScopedId;
import org.opentripplanner.routing.api.request.RouteRequest;
import org.opentripplanner.street.model.StreetMode;

public final class RegularTransferParameters {

private final Duration maxDuration;
private final List<FeedScopedId> includeStops;
private final Map<StreetMode, TransferParametersForMode> parametersForMode;
private final List<RouteRequest> requests;

public static RegularTransferParameters DEFAULT = new RegularTransferParameters();

private RegularTransferParameters() {
this.maxDuration = Duration.ofMinutes(30);
this.includeStops = List.of();
this.parametersForMode = Map.of();
this.requests = List.of();
}

public RegularTransferParameters(
Duration maxDuration,
List<FeedScopedId> includeStops,
Map<StreetMode, TransferParametersForMode> parametersForMode,
List<RouteRequest> requests
) {
this.maxDuration = Objects.requireNonNull(maxDuration);
this.includeStops = List.copyOf(includeStops);
this.parametersForMode = Map.copyOf(parametersForMode);
this.requests = List.copyOf(requests);
}
Expand All @@ -40,6 +45,10 @@ public Duration maxDuration() {
return maxDuration;
}

public List<FeedScopedId> includeStops() {
return includeStops;
}

public Map<StreetMode, TransferParametersForMode> parametersForMode() {
return parametersForMode;
}
Expand All @@ -51,11 +60,13 @@ public List<RouteRequest> requests() {
public static class Builder {

private Duration maxDuration;
private List<FeedScopedId> includeStops;
private Map<StreetMode, TransferParametersForMode> parametersForMode = new HashMap<>();
private List<RouteRequest> requests;

private Builder(RegularTransferParameters original) {
this.maxDuration = original.maxDuration;
this.includeStops = original.includeStops;
this.parametersForMode.putAll(original.parametersForMode);
this.requests = original.requests;
}
Expand All @@ -65,6 +76,11 @@ public Builder withMaxDuration(Duration maxDuration) {
return this;
}

public Builder withIncludeStops(List<FeedScopedId> includeStops) {
this.includeStops = includeStops;
return this;
}

/// Note! This method replaces/overwrites any existing content
public Builder withParametersForMode(Map<StreetMode, TransferParametersForMode> map) {
this.parametersForMode.clear();
Expand All @@ -84,7 +100,7 @@ public Builder withRequests(List<RouteRequest> requests) {
}

public RegularTransferParameters build() {
return new RegularTransferParameters(maxDuration, parametersForMode, requests);
return new RegularTransferParameters(maxDuration, includeStops, parametersForMode, requests);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import java.util.Collection;
import java.util.List;
import java.util.Set;
import org.opentripplanner.core.model.id.FeedScopedId;
import org.opentripplanner.framework.application.OTPFeature;
import org.opentripplanner.graph_builder.module.nearbystops.NearbyStopFinder;
import org.opentripplanner.routing.api.request.RouteRequest;
Expand Down Expand Up @@ -36,10 +38,13 @@ public class PatternConsideringNearbyStopFinder implements NearbyStopFinder {

public PatternConsideringNearbyStopFinder(
TransitService transitService,
NearbyStopFinder delegateNearbyStopFinder
NearbyStopFinder delegateNearbyStopFinder,
Set<FeedScopedId> stopsWithRegularTransfers
) {
this.transitService = transitService;
var builder = CompositeNearbyStopFilter.of().add(new PatternNearbyStopFilter(transitService));
var builder = CompositeNearbyStopFilter.of().add(
new PatternNearbyStopFilter(transitService, stopsWithRegularTransfers)
);

if (OTPFeature.FlexRouting.isOn()) {
builder.add(new FlexTripNearbyStopFilter(transitService));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,20 @@
* or alighting is possible (depending on direction).
* <p>
* Stops without patterns may still be included if they are marked as sometimes-used by real-time
* updates (when the IncludeStopsUsedRealTimeInTransfers feature is enabled).
* updates (when the IncludeStopsUsedRealTimeInTransfers feature is enabled), or if they are
* explicitly listed in the {@code stopsWithRegularTransfers} build configuration.
*/
class PatternNearbyStopFilter implements NearbyStopFilter {

private final TransitService transitService;
private final Set<FeedScopedId> stopsWithRegularTransfers;

PatternNearbyStopFilter(TransitService transitService) {
PatternNearbyStopFilter(
TransitService transitService,
Set<FeedScopedId> stopsWithRegularTransfers
) {
this.transitService = transitService;
this.stopsWithRegularTransfers = stopsWithRegularTransfers;
}

@Override
Expand Down Expand Up @@ -82,6 +88,15 @@ private List<FeedScopedId> findPatternsForStop(RegularStop stop, boolean reverse
}

private boolean includeStopUsedRealtime(RegularStop stop) {
return OTPFeature.IncludeStopsUsedRealTimeInTransfers.isOn() && stop.isSometimesUsedRealtime();
// Some stops, like railway and bus-replacement stops, are tagged during graph building/data
// import. These should allways be included.
if (OTPFeature.IncludeStopsUsedRealTimeInTransfers.isOn() && stop.isSometimesUsedRealtime()) {
return true;
}
// Some stops is listed in the build-config, these are regular stops to include.
if (stopsWithRegularTransfers.contains(stop.getId())) {
return true;
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,32 @@ public static RegularTransferParameters map(NodeAdapter root) {
.asDuration(dft.maxDuration())
);

builder.withIncludeStops(
root
.of("stopsWithRegularTransfers")
.since(V2_9)
.summary(
"Stops that should always have regular transfers computed, even without scheduled trips."
)
.description(
"""
List of stop IDs for which regular transfers are always pre-computed during graph build,
even if the stop has no scheduled trips. Remember to include _feedId_ like this
`"RB:NSR:Quay:102541"`.

This is useful for stops that are unused in static transit data, but may be visited by
real-time updates (e.g. a platform that a train can be re-routed to at runtime). Without
this configuration, stops with no scheduled trips are excluded from transfer pre-computation
and become unreachable islands when a real-time update routes a trip to them.

Note! This parameter should be replaced with an automatic update to regular transfers
based on real-time updates.
"""
)
.experimentalFeature()
.asFeedScopedIds(List.of())
);

builder.withParametersForMode(
root
.of("transferParametersForMode")
Expand Down
23 changes: 23 additions & 0 deletions doc/user/BuildConfiguration.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ Sections follow that describe particular settings in more depth.
|    includeOsmStationEntrances | `boolean` | Whether to include station entrances from the OSM data. | *Optional* | `false` | 2.10 |
|    [osmTagMapping](#od_osmTagMapping) | `enum` | The named set of mapping rules applied when parsing OSM tags. | *Optional* | `"default"` | 2.2 |
|    timeZone | `time-zone` | The timezone used to resolve opening hours in OSM data. | *Optional* | | 2.2 |
| [stopsWithRegularTransfers](#stopsWithRegularTransfers) | `feed-scoped-id[]` | Stops that should always have regular transfers computed, even without scheduled trips. | *Optional* | | 2.9 |
| [transferParametersForMode](#transferParametersForMode) | `enum map of object` | Configures mode-specific properties for transfer calculations. | *Optional* | | 2.7 |
|    BIKE | `object` | NA | *Optional* | | 2.7 |
|       [bikesAllowedStopMaxTransferDuration](#tpfm_BIKE_bikesAllowedStopMaxTransferDuration) | `duration` | This is used for specifying a `maxTransferDuration` value to use with transfers between stops which are visited by trips that allow bikes. | *Optional* | | 2.9 |
Expand Down Expand Up @@ -950,6 +951,28 @@ The named set of mapping rules applied when parsing OSM tags. Overrides the valu

The named set of mapping rules applied when parsing OSM tags.

<h3 id="stopsWithRegularTransfers">stopsWithRegularTransfers</h3>

**Since version:** `2.9` ∙ **Type:** `feed-scoped-id[]` ∙ **Cardinality:** `Optional`
**Path:** /

Stops that should always have regular transfers computed, even without scheduled trips.

List of stop IDs for which regular transfers are always pre-computed during graph build,
even if the stop has no scheduled trips. Remember to include _feedId_ like this
`"RB:NSR:Quay:102541"`.

This is useful for stops that are unused in static transit data, but may be visited by
real-time updates (e.g. a platform that a train can be re-routed to at runtime). Without
this configuration, stops with no scheduled trips are excluded from transfer pre-computation
and become unreachable islands when a real-time update routes a trip to them.

Note! This parameter should be replaced with an automatic update to regular transfers
based on real-time updates.


**THIS IS STILL AN EXPERIMENTAL FEATURE - IT MAY CHANGE WITHOUT ANY NOTICE!**

<h3 id="transferParametersForMode">transferParametersForMode</h3>

**Since version:** `2.7` ∙ **Type:** `enum map of object` ∙ **Cardinality:** `Optional`
Expand Down
Loading