From 36336bf11dcf98e3c242eae2328043aa8d6cf80f Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Fri, 1 Nov 2024 11:58:27 +0200 Subject: [PATCH 01/23] Add ability to create special transfer requests for cars and bikes with the 'carsAllowedStopMaxTransferDurationsForMode' build config parameter. --- .../api/parameter/QualifiedModeSet.java | 6 +- .../module/DirectTransferGenerator.java | 135 +++++++++-- .../module/configure/GraphBuilderModules.java | 3 +- .../nearbystops/StreetNearbyStopFinder.java | 30 ++- .../request/framework/DurationForEnum.java | 4 + .../standalone/config/BuildConfig.java | 10 + ...llowedStopMaxTransferDurationsForMode.java | 55 +++++ doc/user/BuildConfiguration.md | 222 ++++++++++-------- 8 files changed, 337 insertions(+), 128 deletions(-) create mode 100644 application/src/main/java/org/opentripplanner/standalone/config/buildconfig/CarsAllowedStopMaxTransferDurationsForMode.java diff --git a/application/src/main/java/org/opentripplanner/api/parameter/QualifiedModeSet.java b/application/src/main/java/org/opentripplanner/api/parameter/QualifiedModeSet.java index c6f1a3d74ec..c13fb576ad5 100644 --- a/application/src/main/java/org/opentripplanner/api/parameter/QualifiedModeSet.java +++ b/application/src/main/java/org/opentripplanner/api/parameter/QualifiedModeSet.java @@ -126,10 +126,8 @@ public RequestModes getRequestModes() { mBuilder.withEgressMode(StreetMode.CAR_HAILING); mBuilder.withDirectMode(StreetMode.WALK); } else { - mBuilder.withAccessMode(StreetMode.WALK); - mBuilder.withTransferMode(StreetMode.WALK); - mBuilder.withEgressMode(StreetMode.WALK); - mBuilder.withDirectMode(StreetMode.CAR); + // This is used in transfer cache request calculations. + mBuilder.withAllStreetModes(StreetMode.CAR); } } } diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index a1a0796c66a..fcf5ea80cfc 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -3,10 +3,14 @@ import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimaps; import java.time.Duration; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import java.util.stream.Collectors; import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.framework.logging.ProgressTracker; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; @@ -18,10 +22,13 @@ import org.opentripplanner.graph_builder.module.nearbystops.StreetNearbyStopFinder; import org.opentripplanner.model.PathTransfer; import org.opentripplanner.routing.api.request.RouteRequest; +import org.opentripplanner.routing.api.request.StreetMode; +import org.opentripplanner.routing.api.request.framework.DurationForEnum; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.graphfinder.NearbyStop; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.vertex.TransitStopVertex; +import org.opentripplanner.street.model.vertex.Vertex; import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.service.DefaultTransitService; @@ -44,6 +51,7 @@ public class DirectTransferGenerator implements GraphBuilderModule { private final Duration radiusByDuration; private final List transferRequests; + private final DurationForEnum carsAllowedStopMaxTransferDurationsForMode; private final Graph graph; private final TimetableRepository timetableRepository; private final DataImportIssueStore issueStore; @@ -60,6 +68,23 @@ public DirectTransferGenerator( this.issueStore = issueStore; this.radiusByDuration = radiusByDuration; this.transferRequests = transferRequests; + this.carsAllowedStopMaxTransferDurationsForMode = DurationForEnum.of(StreetMode.class).build(); + } + + public DirectTransferGenerator( + Graph graph, + TimetableRepository timetableRepository, + DataImportIssueStore issueStore, + Duration radiusByDuration, + List transferRequests, + DurationForEnum carsAllowedStopMaxTransferDurationsForMode + ) { + this.graph = graph; + this.timetableRepository = timetableRepository; + this.issueStore = issueStore; + this.radiusByDuration = radiusByDuration; + this.transferRequests = transferRequests; + this.carsAllowedStopMaxTransferDurationsForMode = carsAllowedStopMaxTransferDurationsForMode; } @Override @@ -68,9 +93,16 @@ public void buildGraph() { timetableRepository.index(); /* The linker will use streets if they are available, or straight-line distance otherwise. */ - NearbyStopFinder nearbyStopFinder = createNearbyStopFinder(); + NearbyStopFinder nearbyStopFinder = createNearbyStopFinder(radiusByDuration, Set.of()); List stops = graph.getVerticesOfType(TransitStopVertex.class); + Set carsAllowedStops = timetableRepository + .getStopLocationsUsedForCarsAllowedTrips() + .stream() + .map(StopLocation::getId) + .map(graph::getStopVertexForStopId) + .filter(TransitStopVertex.class::isInstance) // filter out null values if no TransitStopVertex is found for ID + .collect(Collectors.toSet()); ProgressTracker progress = ProgressTracker.track( "Create transfer edges for stops", @@ -86,6 +118,37 @@ public void buildGraph() { HashMultimap.create() ); + List filteredTransferRequests = new ArrayList(); + List carsAllowedStopTransferRequests = new ArrayList(); + HashMap carsAllowedStopNearbyStopFinders = new HashMap<>(); + + // Split transfer requests into normal and carsAllowedStop requests. + for (RouteRequest transferProfile : transferRequests) { + StreetMode mode = transferProfile.journey().transfer().mode(); + if (carsAllowedStopMaxTransferDurationsForMode.containsKey(mode)) { + carsAllowedStopNearbyStopFinders.put( + mode, + createNearbyStopFinder( + carsAllowedStopMaxTransferDurationsForMode.valueOf(mode), + Collections.unmodifiableSet(carsAllowedStops) + ) + ); + + carsAllowedStopTransferRequests.add(transferProfile); + // For bikes, also normal transfer requests are wanted. + if (mode == StreetMode.BIKE) { + filteredTransferRequests.add(transferProfile); + } + } else if (mode == StreetMode.CAR) { + // Special transfers are always created for cars. + // If a duration is not specified for cars, the default is used. + carsAllowedStopNearbyStopFinders.put(mode, nearbyStopFinder); + carsAllowedStopTransferRequests.add(transferProfile); + } else { + filteredTransferRequests.add(transferProfile); + } + } + stops .stream() .parallel() @@ -101,25 +164,8 @@ public void buildGraph() { LOG.debug("Linking stop '{}' {}", stop, ts0); - for (RouteRequest transferProfile : transferRequests) { - for (NearbyStop sd : nearbyStopFinder.findNearbyStops( - ts0, - transferProfile, - transferProfile.journey().transfer(), - false - )) { - // Skip the origin stop, loop transfers are not needed. - if (sd.stop == stop) { - continue; - } - if (sd.stop.transfersNotAllowed()) { - continue; - } - distinctTransfers.put( - new TransferKey(stop, sd.stop, sd.edges), - new PathTransfer(stop, sd.stop, sd.distance, sd.edges) - ); - } + for (RouteRequest transferProfile : filteredTransferRequests) { + findNearbyStops(nearbyStopFinder, ts0, transferProfile, stop, distinctTransfers); if (OTPFeature.FlexRouting.isOn()) { // This code is for finding transfers from AreaStops to Stops, transfers // from Stops to AreaStops and between Stops are already covered above. @@ -143,6 +189,21 @@ public void buildGraph() { } } } + // This calculates transfers between stops that can use trips with cars. + for (RouteRequest transferProfile : carsAllowedStopTransferRequests) { + StreetMode mode = transferProfile.journey().transfer().mode(); + if ( + carsAllowedStops.contains(ts0) && carsAllowedStopNearbyStopFinders.containsKey(mode) + ) { + findNearbyStops( + carsAllowedStopNearbyStopFinders.get(mode), + ts0, + transferProfile, + stop, + distinctTransfers + ); + } + } LOG.debug( "Linked stop {} with {} transfers to stops with different patterns.", @@ -179,7 +240,10 @@ public void buildGraph() { * whether the graph has a street network and if ConsiderPatternsForDirectTransfers feature is * enabled. */ - private NearbyStopFinder createNearbyStopFinder() { + private NearbyStopFinder createNearbyStopFinder( + Duration radiusByDuration, + Set findOnlyVertices + ) { var transitService = new DefaultTransitService(timetableRepository); NearbyStopFinder finder; if (!graph.hasStreets) { @@ -189,7 +253,7 @@ private NearbyStopFinder createNearbyStopFinder() { finder = new StraightLineNearbyStopFinder(transitService, radiusByDuration); } else { LOG.info("Creating direct transfer edges between stops using the street network from OSM..."); - finder = new StreetNearbyStopFinder(radiusByDuration, 0, null); + finder = new StreetNearbyStopFinder(radiusByDuration, 0, null, Set.of(), findOnlyVertices); } if (OTPFeature.ConsiderPatternsForDirectTransfers.isOn()) { @@ -199,5 +263,32 @@ private NearbyStopFinder createNearbyStopFinder() { } } + private void findNearbyStops( + NearbyStopFinder nearbyStopFinder, + TransitStopVertex ts0, + RouteRequest transferProfile, + RegularStop stop, + Map distinctTransfers + ) { + for (NearbyStop sd : nearbyStopFinder.findNearbyStops( + ts0, + transferProfile, + transferProfile.journey().transfer(), + false + )) { + // Skip the origin stop, loop transfers are not needed. + if (sd.stop == stop) { + continue; + } + if (sd.stop.transfersNotAllowed()) { + continue; + } + distinctTransfers.put( + new TransferKey(stop, sd.stop, sd.edges), + new PathTransfer(stop, sd.stop, sd.distance, sd.edges) + ); + } + } + private record TransferKey(StopLocation source, StopLocation target, List edges) {} } diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java b/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java index 080d69c571e..03ea13e9f11 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java @@ -237,7 +237,8 @@ static DirectTransferGenerator provideDirectTransferGenerator( timetableRepository, issueStore, config.maxTransferDuration, - config.transferRequests + config.transferRequests, + config.carsAllowedStopMaxTransferDurationsForMode ); } diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinder.java b/application/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinder.java index e54c27249e1..34d61e5c99d 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinder.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinder.java @@ -30,8 +30,6 @@ import org.opentripplanner.street.model.vertex.Vertex; import org.opentripplanner.street.search.StreetSearchBuilder; import org.opentripplanner.street.search.TraverseMode; -import org.opentripplanner.street.search.request.StreetSearchRequest; -import org.opentripplanner.street.search.request.StreetSearchRequestMapper; import org.opentripplanner.street.search.state.State; import org.opentripplanner.street.search.strategy.DominanceFunctions; import org.opentripplanner.transit.model.site.AreaStop; @@ -42,6 +40,7 @@ public class StreetNearbyStopFinder implements NearbyStopFinder { private final int maxStopCount; private final DataOverlayContext dataOverlayContext; private final Set ignoreVertices; + private final Set findOnlyVertices; /** * Construct a NearbyStopFinder for the given graph and search radius. @@ -54,7 +53,7 @@ public StreetNearbyStopFinder( int maxStopCount, DataOverlayContext dataOverlayContext ) { - this(durationLimit, maxStopCount, dataOverlayContext, Set.of()); + this(durationLimit, maxStopCount, dataOverlayContext, Set.of(), Set.of()); } /** @@ -69,11 +68,31 @@ public StreetNearbyStopFinder( int maxStopCount, DataOverlayContext dataOverlayContext, Set ignoreVertices + ) { + this(durationLimit, maxStopCount, dataOverlayContext, ignoreVertices, Set.of()); + } + + /** + * Construct a NearbyStopFinder for the given graph and search radius. + * + * @param maxStopCount The maximum stops to return. 0 means no limit. Regardless of the maxStopCount + * we will always return all the directly connected stops. + * @param ignoreVertices A set of stop vertices to ignore and not return NearbyStops for. + * + * @param findOnlyVertices Only return NearbyStops that are in this set. If this is empty, no filtering is performed. + */ + public StreetNearbyStopFinder( + Duration durationLimit, + int maxStopCount, + DataOverlayContext dataOverlayContext, + Set ignoreVertices, + Set findOnlyVertices ) { this.dataOverlayContext = dataOverlayContext; this.durationLimit = durationLimit; this.maxStopCount = maxStopCount; this.ignoreVertices = ignoreVertices; + this.findOnlyVertices = findOnlyVertices; } /** @@ -146,7 +165,10 @@ public Collection findNearbyStops( continue; } if (targetVertex instanceof TransitStopVertex tsv && state.isFinal()) { - stopsFound.add(NearbyStop.nearbyStopForState(state, tsv.getStop())); + // If a set of findOnlyVertices is provided, only add stops that are in the set. + if (findOnlyVertices.isEmpty() || findOnlyVertices.contains(targetVertex)) { + stopsFound.add(NearbyStop.nearbyStopForState(state, tsv.getStop())); + } } if ( OTPFeature.FlexRouting.isOn() && diff --git a/application/src/main/java/org/opentripplanner/routing/api/request/framework/DurationForEnum.java b/application/src/main/java/org/opentripplanner/routing/api/request/framework/DurationForEnum.java index e6dacaac19d..69b15ef4624 100644 --- a/application/src/main/java/org/opentripplanner/routing/api/request/framework/DurationForEnum.java +++ b/application/src/main/java/org/opentripplanner/routing/api/request/framework/DurationForEnum.java @@ -49,6 +49,10 @@ public Duration defaultValue() { return defaultValue; } + public boolean containsKey(E key) { + return valueForEnum.containsKey(key); + } + /** * Utility method to get {@link #defaultValue} as an number in unit seconds. Equivalent to * {@code (int) defaultValue.toSeconds()}. The downcast is safe since we only allow days, hours, diff --git a/application/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java index ef2931b987e..6482868d578 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java @@ -33,7 +33,10 @@ import org.opentripplanner.model.calendar.ServiceDateInterval; import org.opentripplanner.netex.config.NetexFeedParameters; import org.opentripplanner.routing.api.request.RouteRequest; +import org.opentripplanner.routing.api.request.StreetMode; +import org.opentripplanner.routing.api.request.framework.DurationForEnum; import org.opentripplanner.routing.fares.FareServiceFactory; +import org.opentripplanner.standalone.config.buildconfig.CarsAllowedStopMaxTransferDurationsForMode; import org.opentripplanner.standalone.config.buildconfig.DemConfig; import org.opentripplanner.standalone.config.buildconfig.GtfsConfig; import org.opentripplanner.standalone.config.buildconfig.IslandPruningConfig; @@ -151,6 +154,7 @@ public class BuildConfig implements OtpDataStoreConfig { public final IslandPruningConfig islandPruning; public final Duration maxTransferDuration; + public final DurationForEnum carsAllowedStopMaxTransferDurationsForMode; public final NetexFeedParameters netexDefaults; public final GtfsFeedParameters gtfsDefaults; @@ -287,6 +291,12 @@ When set to true (it is false by default), the elevation module will include the "Transfers up to this duration with the default walk speed value will be pre-calculated and included in the Graph." ) .asDuration(Duration.ofMinutes(30)); + carsAllowedStopMaxTransferDurationsForMode = + CarsAllowedStopMaxTransferDurationsForMode.map( + root, + "carsAllowedStopMaxTransferDurationsForMode", + maxTransferDuration + ); maxStopToShapeSnapDistance = root .of("maxStopToShapeSnapDistance") diff --git a/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/CarsAllowedStopMaxTransferDurationsForMode.java b/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/CarsAllowedStopMaxTransferDurationsForMode.java new file mode 100644 index 00000000000..322ed32d682 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/CarsAllowedStopMaxTransferDurationsForMode.java @@ -0,0 +1,55 @@ +package org.opentripplanner.standalone.config.buildconfig; + +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_7; + +import java.time.Duration; +import java.util.Map; +import org.opentripplanner.routing.api.request.StreetMode; +import org.opentripplanner.routing.api.request.framework.DurationForEnum; +import org.opentripplanner.standalone.config.framework.json.NodeAdapter; + +public class CarsAllowedStopMaxTransferDurationsForMode { + + public static DurationForEnum map( + NodeAdapter root, + String parameterName, + Duration maxTransferDuration + ) { + Map values = root + .of(parameterName) + .since(V2_7) + .summary( + "This is used for specifying a `maxTransferDuration` value for bikes and cars to use with transfers between stops that have trips with cars." + ) + .description( + """ +This is a special parameter that only works on transfers between stops that have trips that allow cars. +The duration can be set for either 'BIKE' or 'CAR'. +For cars, transfers are only calculated between stops that have trips that allow cars. +For cars, this overrides the default `maxTransferDuration`. +For bicycles, this indicates that additional transfers should be calculated with the specified duration between stops that have trips that allow cars. + +**Example** + +```JSON +// build-config.json +{ + "carsAllowedStopMaxTransferDurationsForMode": { + "CAR": "2h", + "BIKE": "3h" + } +} +``` +""" + ) + .asEnumMap(StreetMode.class, Duration.class); + for (StreetMode mode : values.keySet()) { + if (mode != StreetMode.BIKE && mode != StreetMode.CAR) { + throw new IllegalArgumentException( + "Only the CAR and BIKE modes are allowed in the carsAllowedStopMaxTransferDurationsForMode parameter." + ); + } + } + return DurationForEnum.of(StreetMode.class).withValues(values).build(); + } +} diff --git a/doc/user/BuildConfiguration.md b/doc/user/BuildConfiguration.md index 99e98066e73..e2d46e98d73 100644 --- a/doc/user/BuildConfiguration.md +++ b/doc/user/BuildConfiguration.md @@ -17,103 +17,104 @@ Sections follow that describe particular settings in more depth. -| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | -|--------------------------------------------------------------------------|:------------------:|----------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------:|-----------------------------------|:-----:| -| [areaVisibility](#areaVisibility) | `boolean` | Perform visibility calculations. | *Optional* | `false` | 1.5 | -| [buildReportDir](#buildReportDir) | `uri` | URI to the directory where the graph build report should be written to. | *Optional* | | 2.0 | -| [configVersion](#configVersion) | `string` | Deployment version of the *build-config.json*. | *Optional* | | 2.1 | -| [dataImportReport](#dataImportReport) | `boolean` | Generate nice HTML report of Graph errors/warnings | *Optional* | `false` | 2.0 | -| [distanceBetweenElevationSamples](#distanceBetweenElevationSamples) | `double` | The distance between elevation samples in meters. | *Optional* | `10.0` | 2.0 | -| embedRouterConfig | `boolean` | Embed the Router config in the graph, which allows it to be sent to a server fully configured over the wire. | *Optional* | `true` | 2.0 | -| [graph](#graph) | `uri` | URI to the graph object file for reading and writing. | *Optional* | | 2.0 | -| [gsCredentials](#gsCredentials) | `string` | Local file system path to Google Cloud Platform service accounts credentials file. | *Optional* | | 2.0 | -| [includeEllipsoidToGeoidDifference](#includeEllipsoidToGeoidDifference) | `boolean` | Include the Ellipsoid to Geoid difference in the calculations of every point along every StreetWithElevationEdge. | *Optional* | `false` | 2.0 | -| maxAreaNodes | `integer` | Visibility calculations for an area will not be done if there are more nodes than this limit. | *Optional* | `150` | 2.1 | -| [maxDataImportIssuesPerFile](#maxDataImportIssuesPerFile) | `integer` | When to split the import report. | *Optional* | `1000` | 2.0 | -| maxElevationPropagationMeters | `integer` | The maximum distance to propagate elevation to vertices which have no elevation. | *Optional* | `2000` | 1.5 | -| [maxStopToShapeSnapDistance](#maxStopToShapeSnapDistance) | `double` | Maximum distance between route shapes and their stops. | *Optional* | `150.0` | 2.1 | -| maxTransferDuration | `duration` | Transfers up to this duration with the default walk speed value will be pre-calculated and included in the Graph. | *Optional* | `"PT30M"` | 2.1 | -| [multiThreadElevationCalculations](#multiThreadElevationCalculations) | `boolean` | Configuring multi-threading during elevation calculations. | *Optional* | `false` | 2.0 | -| [osmCacheDataInMem](#osmCacheDataInMem) | `boolean` | If OSM data should be cached in memory during processing. | *Optional* | `false` | 2.0 | -| [osmNaming](#osmNaming) | `enum` | A custom OSM namer to use. | *Optional* | `"default"` | 1.5 | -| platformEntriesLinking | `boolean` | Link unconnected entries to public transport platforms. | *Optional* | `false` | 2.0 | -| [readCachedElevations](#readCachedElevations) | `boolean` | Whether to read cached elevation data. | *Optional* | `true` | 2.0 | -| staticBikeParkAndRide | `boolean` | Whether we should create bike P+R stations from OSM data. | *Optional* | `false` | 1.5 | -| staticParkAndRide | `boolean` | Whether we should create car P+R stations from OSM data. | *Optional* | `true` | 1.5 | -| stopConsolidationFile | `uri` | Name of the CSV-formatted file in the build directory which contains the configuration for stop consolidation. | *Optional* | | 2.5 | -| [streetGraph](#streetGraph) | `uri` | URI to the street graph object file for reading and writing. | *Optional* | | 2.0 | -| [subwayAccessTime](#subwayAccessTime) | `double` | Minutes necessary to reach stops served by trips on routes of route_type=1 (subway) from the street. | *Optional* | `2.0` | 1.5 | -| [transitModelTimeZone](#transitModelTimeZone) | `time-zone` | Time zone for the graph. | *Optional* | | 2.2 | -| [transitServiceEnd](#transitServiceEnd) | `duration` | Limit the import of transit services to the given end date. | *Optional* | `"P3Y"` | 2.0 | -| [transitServiceStart](#transitServiceStart) | `duration` | Limit the import of transit services to the given START date. | *Optional* | `"-P1Y"` | 2.0 | -| [writeCachedElevations](#writeCachedElevations) | `boolean` | Reusing elevation data from previous builds | *Optional* | `false` | 2.0 | -| [boardingLocationTags](#boardingLocationTags) | `string[]` | What OSM tags should be looked on for the source of matching stops to platforms and stops. | *Optional* | | 2.2 | -| [dataOverlay](sandbox/DataOverlay.md) | `object` | Config for the DataOverlay Sandbox module | *Optional* | | 2.2 | -| [dem](#dem) | `object[]` | Specify parameters for DEM extracts. | *Optional* | | 2.2 | -|       [elevationUnitMultiplier](#dem_0_elevationUnitMultiplier) | `double` | Specify a multiplier to convert elevation units from source to meters. Overrides the value specified in `demDefaults`. | *Optional* | `1.0` | 2.3 | -|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | -| demDefaults | `object` | Default properties for DEM extracts. | *Optional* | | 2.3 | -|    [elevationUnitMultiplier](#demDefaults_elevationUnitMultiplier) | `double` | Specify a multiplier to convert elevation units from source to meters. | *Optional* | `1.0` | 2.3 | -| [elevationBucket](#elevationBucket) | `object` | Used to download NED elevation tiles from the given AWS S3 bucket. | *Optional* | | na | -| [emissions](sandbox/Emissions.md) | `object` | Emissions configuration. | *Optional* | | 2.5 | -| [fares](sandbox/Fares.md) | `object` | Fare configuration. | *Optional* | | 2.0 | -| gtfsDefaults | `object` | The gtfsDefaults section allows you to specify default properties for GTFS files. | *Optional* | | 2.3 | -|    blockBasedInterlining | `boolean` | Whether to create stay-seated transfers in between two trips with the same block id. | *Optional* | `true` | 2.3 | -|    [discardMinTransferTimes](#gd_discardMinTransferTimes) | `boolean` | Should minimum transfer times in GTFS files be discarded. | *Optional* | `false` | 2.3 | -|    maxInterlineDistance | `integer` | Maximal distance between stops in meters that will connect consecutive trips that are made with same vehicle. | *Optional* | `200` | 2.3 | -|    removeRepeatedStops | `boolean` | Should consecutive identical stops be merged into one stop time entry. | *Optional* | `true` | 2.3 | -|    [stationTransferPreference](#gd_stationTransferPreference) | `enum` | Should there be some preference or aversion for transfers at stops that are part of a station. | *Optional* | `"allowed"` | 2.3 | -| islandPruning | `object` | Settings for fixing street graph connectivity errors | *Optional* | | 2.3 | -|    [adaptivePruningDistance](#islandPruning_adaptivePruningDistance) | `integer` | Search distance for analyzing islands in pruning. | *Optional* | `250` | 2.3 | -|    [adaptivePruningFactor](#islandPruning_adaptivePruningFactor) | `double` | Defines how much pruning thresholds grow maximally by distance. | *Optional* | `50.0` | 2.3 | -|    [islandWithStopsMaxSize](#islandPruning_islandWithStopsMaxSize) | `integer` | When a graph island with stops in it should be pruned. | *Optional* | `2` | 2.3 | -|    [islandWithoutStopsMaxSize](#islandPruning_islandWithoutStopsMaxSize) | `integer` | When a graph island without stops should be pruned. | *Optional* | `10` | 2.3 | -| [localFileNamePatterns](#localFileNamePatterns) | `object` | Patterns for matching OTP file types in the base directory | *Optional* | | 2.0 | -|    [dem](#lfp_dem) | `regexp` | Pattern for matching elevation DEM files. | *Optional* | `"(?i)\.tiff?$"` | 2.0 | -|    [gtfs](#lfp_gtfs) | `regexp` | Patterns for matching GTFS zip-files or directories. | *Optional* | `"(?i)gtfs"` | 2.0 | -|    [netex](#lfp_netex) | `regexp` | Patterns for matching NeTEx zip files or directories. | *Optional* | `"(?i)netex"` | 2.0 | -|    [osm](#lfp_osm) | `regexp` | Pattern for matching Open Street Map input files. | *Optional* | `"(?i)(\.pbf¦\.osm¦\.osm\.xml)$"` | 2.0 | -| netexDefaults | `object` | The netexDefaults section allows you to specify default properties for NeTEx files. | *Optional* | | 2.2 | -|    feedId | `string` | This field is used to identify the specific NeTEx feed. It is used instead of the feed_id field in GTFS file feed_info.txt. | *Optional* | `"NETEX"` | 2.2 | -|    [groupFilePattern](#nd_groupFilePattern) | `regexp` | Pattern for matching group NeTEx files. | *Optional* | `"(\w{3})-.*\.xml"` | 2.0 | -|    ignoreFareFrame | `boolean` | Ignore contents of the FareFrame | *Optional* | `false` | 2.3 | -|    [ignoreFilePattern](#nd_ignoreFilePattern) | `regexp` | Pattern for matching ignored files in a NeTEx bundle. | *Optional* | `"$^"` | 2.0 | -|    ignoreParking | `boolean` | Ignore Parking elements. | *Optional* | `true` | 2.6 | -|    noTransfersOnIsolatedStops | `boolean` | Whether we should allow transfers to and from StopPlaces marked with LimitedUse.ISOLATED | *Optional* | `false` | 2.2 | -|    [sharedFilePattern](#nd_sharedFilePattern) | `regexp` | Pattern for matching shared NeTEx files in a NeTEx bundle. | *Optional* | `"shared-data\.xml"` | 2.0 | -|    [sharedGroupFilePattern](#nd_sharedGroupFilePattern) | `regexp` | Pattern for matching shared group NeTEx files in a NeTEx bundle. | *Optional* | `"(\w{3})-.*-shared\.xml"` | 2.0 | -|    [ferryIdsNotAllowedForBicycle](#nd_ferryIdsNotAllowedForBicycle) | `string[]` | List ferries which do not allow bikes. | *Optional* | | 2.0 | -| [osm](#osm) | `object[]` | Configure properties for a given OpenStreetMap feed. | *Optional* | | 2.2 | -|       [osmTagMapping](#osm_0_osmTagMapping) | `enum` | The named set of mapping rules applied when parsing OSM tags. Overrides the value specified in `osmDefaults`. | *Optional* | `"default"` | 2.2 | -|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | -|       timeZone | `time-zone` | The timezone used to resolve opening hours in OSM data. Overrides the value specified in `osmDefaults`. | *Optional* | | 2.2 | -| osmDefaults | `object` | Default properties for OpenStreetMap feeds. | *Optional* | | 2.2 | -|    [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 | -| [transferRequests](RouteRequest.md) | `object[]` | Routing requests to use for pre-calculating stop-to-stop transfers. | *Optional* | | 2.1 | -| [transitFeeds](#transitFeeds) | `object[]` | Scan for transit data files | *Optional* | | 2.2 | -|    { object } | `object` | Nested object in array. The object type is determined by the parameters. | *Optional* | | 2.2 | -|       type = "gtfs" | `enum` | The feed input format. | *Required* | | 2.2 | -|       blockBasedInterlining | `boolean` | Whether to create stay-seated transfers in between two trips with the same block id. Overrides the value specified in `gtfsDefaults`. | *Optional* | `true` | 2.3 | -|       [discardMinTransferTimes](#tf_0_discardMinTransferTimes) | `boolean` | Should minimum transfer times in GTFS files be discarded. Overrides the value specified in `gtfsDefaults`. | *Optional* | `false` | 2.3 | -|       feedId | `string` | The unique ID for this feed. This overrides any feed ID defined within the feed itself. | *Optional* | | 2.2 | -|       maxInterlineDistance | `integer` | Maximal distance between stops in meters that will connect consecutive trips that are made with same vehicle. Overrides the value specified in `gtfsDefaults`. | *Optional* | `200` | 2.3 | -|       removeRepeatedStops | `boolean` | Should consecutive identical stops be merged into one stop time entry. Overrides the value specified in `gtfsDefaults`. | *Optional* | `true` | 2.3 | -|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | -|       [stationTransferPreference](#tf_0_stationTransferPreference) | `enum` | Should there be some preference or aversion for transfers at stops that are part of a station. Overrides the value specified in `gtfsDefaults`. | *Optional* | `"allowed"` | 2.3 | -|    { object } | `object` | Nested object in array. The object type is determined by the parameters. | *Optional* | | 2.2 | -|       type = "netex" | `enum` | The feed input format. | *Required* | | 2.2 | -|       feedId | `string` | This field is used to identify the specific NeTEx feed. It is used instead of the feed_id field in GTFS file feed_info.txt. | *Required* | | 2.2 | -|       [groupFilePattern](#tf_1_groupFilePattern) | `regexp` | Pattern for matching group NeTEx files. | *Optional* | `"(\w{3})-.*\.xml"` | 2.0 | -|       ignoreFareFrame | `boolean` | Ignore contents of the FareFrame | *Optional* | `false` | 2.3 | -|       [ignoreFilePattern](#tf_1_ignoreFilePattern) | `regexp` | Pattern for matching ignored files in a NeTEx bundle. | *Optional* | `"$^"` | 2.0 | -|       ignoreParking | `boolean` | Ignore Parking elements. | *Optional* | `true` | 2.6 | -|       noTransfersOnIsolatedStops | `boolean` | Whether we should allow transfers to and from StopPlaces marked with LimitedUse.ISOLATED | *Optional* | `false` | 2.2 | -|       [sharedFilePattern](#tf_1_sharedFilePattern) | `regexp` | Pattern for matching shared NeTEx files in a NeTEx bundle. | *Optional* | `"shared-data\.xml"` | 2.0 | -|       [sharedGroupFilePattern](#tf_1_sharedGroupFilePattern) | `regexp` | Pattern for matching shared group NeTEx files in a NeTEx bundle. | *Optional* | `"(\w{3})-.*-shared\.xml"` | 2.0 | -|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | -|       [ferryIdsNotAllowedForBicycle](#tf_1_ferryIdsNotAllowedForBicycle) | `string[]` | List ferries which do not allow bikes. | *Optional* | | 2.0 | -| [transitRouteToStationCentroid](#transitRouteToStationCentroid) | `feed-scoped-id[]` | List stations that should route to centroid. | *Optional* | | 2.7 | +| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | +|-------------------------------------------------------------------------------------------|:----------------------:|----------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------:|-----------------------------------|:-----:| +| [areaVisibility](#areaVisibility) | `boolean` | Perform visibility calculations. | *Optional* | `false` | 1.5 | +| [buildReportDir](#buildReportDir) | `uri` | URI to the directory where the graph build report should be written to. | *Optional* | | 2.0 | +| [configVersion](#configVersion) | `string` | Deployment version of the *build-config.json*. | *Optional* | | 2.1 | +| [dataImportReport](#dataImportReport) | `boolean` | Generate nice HTML report of Graph errors/warnings | *Optional* | `false` | 2.0 | +| [distanceBetweenElevationSamples](#distanceBetweenElevationSamples) | `double` | The distance between elevation samples in meters. | *Optional* | `10.0` | 2.0 | +| embedRouterConfig | `boolean` | Embed the Router config in the graph, which allows it to be sent to a server fully configured over the wire. | *Optional* | `true` | 2.0 | +| [graph](#graph) | `uri` | URI to the graph object file for reading and writing. | *Optional* | | 2.0 | +| [gsCredentials](#gsCredentials) | `string` | Local file system path to Google Cloud Platform service accounts credentials file. | *Optional* | | 2.0 | +| [includeEllipsoidToGeoidDifference](#includeEllipsoidToGeoidDifference) | `boolean` | Include the Ellipsoid to Geoid difference in the calculations of every point along every StreetWithElevationEdge. | *Optional* | `false` | 2.0 | +| maxAreaNodes | `integer` | Visibility calculations for an area will not be done if there are more nodes than this limit. | *Optional* | `150` | 2.1 | +| [maxDataImportIssuesPerFile](#maxDataImportIssuesPerFile) | `integer` | When to split the import report. | *Optional* | `1000` | 2.0 | +| maxElevationPropagationMeters | `integer` | The maximum distance to propagate elevation to vertices which have no elevation. | *Optional* | `2000` | 1.5 | +| [maxStopToShapeSnapDistance](#maxStopToShapeSnapDistance) | `double` | Maximum distance between route shapes and their stops. | *Optional* | `150.0` | 2.1 | +| maxTransferDuration | `duration` | Transfers up to this duration with the default walk speed value will be pre-calculated and included in the Graph. | *Optional* | `"PT30M"` | 2.1 | +| [multiThreadElevationCalculations](#multiThreadElevationCalculations) | `boolean` | Configuring multi-threading during elevation calculations. | *Optional* | `false` | 2.0 | +| [osmCacheDataInMem](#osmCacheDataInMem) | `boolean` | If OSM data should be cached in memory during processing. | *Optional* | `false` | 2.0 | +| [osmNaming](#osmNaming) | `enum` | A custom OSM namer to use. | *Optional* | `"default"` | 1.5 | +| platformEntriesLinking | `boolean` | Link unconnected entries to public transport platforms. | *Optional* | `false` | 2.0 | +| [readCachedElevations](#readCachedElevations) | `boolean` | Whether to read cached elevation data. | *Optional* | `true` | 2.0 | +| staticBikeParkAndRide | `boolean` | Whether we should create bike P+R stations from OSM data. | *Optional* | `false` | 1.5 | +| staticParkAndRide | `boolean` | Whether we should create car P+R stations from OSM data. | *Optional* | `true` | 1.5 | +| stopConsolidationFile | `uri` | Name of the CSV-formatted file in the build directory which contains the configuration for stop consolidation. | *Optional* | | 2.5 | +| [streetGraph](#streetGraph) | `uri` | URI to the street graph object file for reading and writing. | *Optional* | | 2.0 | +| [subwayAccessTime](#subwayAccessTime) | `double` | Minutes necessary to reach stops served by trips on routes of route_type=1 (subway) from the street. | *Optional* | `2.0` | 1.5 | +| [transitModelTimeZone](#transitModelTimeZone) | `time-zone` | Time zone for the graph. | *Optional* | | 2.2 | +| [transitServiceEnd](#transitServiceEnd) | `duration` | Limit the import of transit services to the given end date. | *Optional* | `"P3Y"` | 2.0 | +| [transitServiceStart](#transitServiceStart) | `duration` | Limit the import of transit services to the given START date. | *Optional* | `"-P1Y"` | 2.0 | +| [writeCachedElevations](#writeCachedElevations) | `boolean` | Reusing elevation data from previous builds | *Optional* | `false` | 2.0 | +| [boardingLocationTags](#boardingLocationTags) | `string[]` | What OSM tags should be looked on for the source of matching stops to platforms and stops. | *Optional* | | 2.2 | +| [carsAllowedStopMaxTransferDurationsForMode](#carsAllowedStopMaxTransferDurationsForMode) | `enum map of duration` | This is used for specifying a `maxTransferDuration` value for bikes and cars to use with transfers between stops that have trips with cars. | *Optional* | | 2.7 | +| [dataOverlay](sandbox/DataOverlay.md) | `object` | Config for the DataOverlay Sandbox module | *Optional* | | 2.2 | +| [dem](#dem) | `object[]` | Specify parameters for DEM extracts. | *Optional* | | 2.2 | +|       [elevationUnitMultiplier](#dem_0_elevationUnitMultiplier) | `double` | Specify a multiplier to convert elevation units from source to meters. Overrides the value specified in `demDefaults`. | *Optional* | `1.0` | 2.3 | +|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | +| demDefaults | `object` | Default properties for DEM extracts. | *Optional* | | 2.3 | +|    [elevationUnitMultiplier](#demDefaults_elevationUnitMultiplier) | `double` | Specify a multiplier to convert elevation units from source to meters. | *Optional* | `1.0` | 2.3 | +| [elevationBucket](#elevationBucket) | `object` | Used to download NED elevation tiles from the given AWS S3 bucket. | *Optional* | | na | +| [emissions](sandbox/Emissions.md) | `object` | Emissions configuration. | *Optional* | | 2.5 | +| [fares](sandbox/Fares.md) | `object` | Fare configuration. | *Optional* | | 2.0 | +| gtfsDefaults | `object` | The gtfsDefaults section allows you to specify default properties for GTFS files. | *Optional* | | 2.3 | +|    blockBasedInterlining | `boolean` | Whether to create stay-seated transfers in between two trips with the same block id. | *Optional* | `true` | 2.3 | +|    [discardMinTransferTimes](#gd_discardMinTransferTimes) | `boolean` | Should minimum transfer times in GTFS files be discarded. | *Optional* | `false` | 2.3 | +|    maxInterlineDistance | `integer` | Maximal distance between stops in meters that will connect consecutive trips that are made with same vehicle. | *Optional* | `200` | 2.3 | +|    removeRepeatedStops | `boolean` | Should consecutive identical stops be merged into one stop time entry. | *Optional* | `true` | 2.3 | +|    [stationTransferPreference](#gd_stationTransferPreference) | `enum` | Should there be some preference or aversion for transfers at stops that are part of a station. | *Optional* | `"allowed"` | 2.3 | +| islandPruning | `object` | Settings for fixing street graph connectivity errors | *Optional* | | 2.3 | +|    [adaptivePruningDistance](#islandPruning_adaptivePruningDistance) | `integer` | Search distance for analyzing islands in pruning. | *Optional* | `250` | 2.3 | +|    [adaptivePruningFactor](#islandPruning_adaptivePruningFactor) | `double` | Defines how much pruning thresholds grow maximally by distance. | *Optional* | `50.0` | 2.3 | +|    [islandWithStopsMaxSize](#islandPruning_islandWithStopsMaxSize) | `integer` | When a graph island with stops in it should be pruned. | *Optional* | `2` | 2.3 | +|    [islandWithoutStopsMaxSize](#islandPruning_islandWithoutStopsMaxSize) | `integer` | When a graph island without stops should be pruned. | *Optional* | `10` | 2.3 | +| [localFileNamePatterns](#localFileNamePatterns) | `object` | Patterns for matching OTP file types in the base directory | *Optional* | | 2.0 | +|    [dem](#lfp_dem) | `regexp` | Pattern for matching elevation DEM files. | *Optional* | `"(?i)\.tiff?$"` | 2.0 | +|    [gtfs](#lfp_gtfs) | `regexp` | Patterns for matching GTFS zip-files or directories. | *Optional* | `"(?i)gtfs"` | 2.0 | +|    [netex](#lfp_netex) | `regexp` | Patterns for matching NeTEx zip files or directories. | *Optional* | `"(?i)netex"` | 2.0 | +|    [osm](#lfp_osm) | `regexp` | Pattern for matching Open Street Map input files. | *Optional* | `"(?i)(\.pbf¦\.osm¦\.osm\.xml)$"` | 2.0 | +| netexDefaults | `object` | The netexDefaults section allows you to specify default properties for NeTEx files. | *Optional* | | 2.2 | +|    feedId | `string` | This field is used to identify the specific NeTEx feed. It is used instead of the feed_id field in GTFS file feed_info.txt. | *Optional* | `"NETEX"` | 2.2 | +|    [groupFilePattern](#nd_groupFilePattern) | `regexp` | Pattern for matching group NeTEx files. | *Optional* | `"(\w{3})-.*\.xml"` | 2.0 | +|    ignoreFareFrame | `boolean` | Ignore contents of the FareFrame | *Optional* | `false` | 2.3 | +|    [ignoreFilePattern](#nd_ignoreFilePattern) | `regexp` | Pattern for matching ignored files in a NeTEx bundle. | *Optional* | `"$^"` | 2.0 | +|    ignoreParking | `boolean` | Ignore Parking elements. | *Optional* | `true` | 2.6 | +|    noTransfersOnIsolatedStops | `boolean` | Whether we should allow transfers to and from StopPlaces marked with LimitedUse.ISOLATED | *Optional* | `false` | 2.2 | +|    [sharedFilePattern](#nd_sharedFilePattern) | `regexp` | Pattern for matching shared NeTEx files in a NeTEx bundle. | *Optional* | `"shared-data\.xml"` | 2.0 | +|    [sharedGroupFilePattern](#nd_sharedGroupFilePattern) | `regexp` | Pattern for matching shared group NeTEx files in a NeTEx bundle. | *Optional* | `"(\w{3})-.*-shared\.xml"` | 2.0 | +|    [ferryIdsNotAllowedForBicycle](#nd_ferryIdsNotAllowedForBicycle) | `string[]` | List ferries which do not allow bikes. | *Optional* | | 2.0 | +| [osm](#osm) | `object[]` | Configure properties for a given OpenStreetMap feed. | *Optional* | | 2.2 | +|       [osmTagMapping](#osm_0_osmTagMapping) | `enum` | The named set of mapping rules applied when parsing OSM tags. Overrides the value specified in `osmDefaults`. | *Optional* | `"default"` | 2.2 | +|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | +|       timeZone | `time-zone` | The timezone used to resolve opening hours in OSM data. Overrides the value specified in `osmDefaults`. | *Optional* | | 2.2 | +| osmDefaults | `object` | Default properties for OpenStreetMap feeds. | *Optional* | | 2.2 | +|    [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 | +| [transferRequests](RouteRequest.md) | `object[]` | Routing requests to use for pre-calculating stop-to-stop transfers. | *Optional* | | 2.1 | +| [transitFeeds](#transitFeeds) | `object[]` | Scan for transit data files | *Optional* | | 2.2 | +|    { object } | `object` | Nested object in array. The object type is determined by the parameters. | *Optional* | | 2.2 | +|       type = "gtfs" | `enum` | The feed input format. | *Required* | | 2.2 | +|       blockBasedInterlining | `boolean` | Whether to create stay-seated transfers in between two trips with the same block id. Overrides the value specified in `gtfsDefaults`. | *Optional* | `true` | 2.3 | +|       [discardMinTransferTimes](#tf_0_discardMinTransferTimes) | `boolean` | Should minimum transfer times in GTFS files be discarded. Overrides the value specified in `gtfsDefaults`. | *Optional* | `false` | 2.3 | +|       feedId | `string` | The unique ID for this feed. This overrides any feed ID defined within the feed itself. | *Optional* | | 2.2 | +|       maxInterlineDistance | `integer` | Maximal distance between stops in meters that will connect consecutive trips that are made with same vehicle. Overrides the value specified in `gtfsDefaults`. | *Optional* | `200` | 2.3 | +|       removeRepeatedStops | `boolean` | Should consecutive identical stops be merged into one stop time entry. Overrides the value specified in `gtfsDefaults`. | *Optional* | `true` | 2.3 | +|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | +|       [stationTransferPreference](#tf_0_stationTransferPreference) | `enum` | Should there be some preference or aversion for transfers at stops that are part of a station. Overrides the value specified in `gtfsDefaults`. | *Optional* | `"allowed"` | 2.3 | +|    { object } | `object` | Nested object in array. The object type is determined by the parameters. | *Optional* | | 2.2 | +|       type = "netex" | `enum` | The feed input format. | *Required* | | 2.2 | +|       feedId | `string` | This field is used to identify the specific NeTEx feed. It is used instead of the feed_id field in GTFS file feed_info.txt. | *Required* | | 2.2 | +|       [groupFilePattern](#tf_1_groupFilePattern) | `regexp` | Pattern for matching group NeTEx files. | *Optional* | `"(\w{3})-.*\.xml"` | 2.0 | +|       ignoreFareFrame | `boolean` | Ignore contents of the FareFrame | *Optional* | `false` | 2.3 | +|       [ignoreFilePattern](#tf_1_ignoreFilePattern) | `regexp` | Pattern for matching ignored files in a NeTEx bundle. | *Optional* | `"$^"` | 2.0 | +|       ignoreParking | `boolean` | Ignore Parking elements. | *Optional* | `true` | 2.6 | +|       noTransfersOnIsolatedStops | `boolean` | Whether we should allow transfers to and from StopPlaces marked with LimitedUse.ISOLATED | *Optional* | `false` | 2.2 | +|       [sharedFilePattern](#tf_1_sharedFilePattern) | `regexp` | Pattern for matching shared NeTEx files in a NeTEx bundle. | *Optional* | `"shared-data\.xml"` | 2.0 | +|       [sharedGroupFilePattern](#tf_1_sharedGroupFilePattern) | `regexp` | Pattern for matching shared group NeTEx files in a NeTEx bundle. | *Optional* | `"(\w{3})-.*-shared\.xml"` | 2.0 | +|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | +|       [ferryIdsNotAllowedForBicycle](#tf_1_ferryIdsNotAllowedForBicycle) | `string[]` | List ferries which do not allow bikes. | *Optional* | | 2.0 | +| [transitRouteToStationCentroid](#transitRouteToStationCentroid) | `feed-scoped-id[]` | List stations that should route to centroid. | *Optional* | | 2.7 | @@ -653,6 +654,33 @@ What OSM tags should be looked on for the source of matching stops to platforms [Detailed documentation](BoardingLocations.md) +

carsAllowedStopMaxTransferDurationsForMode

+ +**Since version:** `2.7` ∙ **Type:** `enum map of duration` ∙ **Cardinality:** `Optional` +**Path:** / +**Enum keys:** `not-set` | `walk` | `bike` | `bike-to-park` | `bike-rental` | `scooter-rental` | `car` | `car-to-park` | `car-pickup` | `car-rental` | `car-hailing` | `flexible` + +This is used for specifying a `maxTransferDuration` value for bikes and cars to use with transfers between stops that have trips with cars. + +This is a special parameter that only works on transfers between stops that have trips that allow cars. +The duration can be set for either 'BIKE' or 'CAR'. +For cars, transfers are only calculated between stops that have trips that allow cars. +For cars, this overrides the default `maxTransferDuration`. +For bicycles, this indicates that additional transfers should be calculated with the specified duration between stops that have trips that allow cars. + +**Example** + +```JSON +// build-config.json +{ + "carsAllowedStopMaxTransferDurationsForMode": { + "CAR": "2h", + "BIKE": "3h" + } +} +``` + +

dem

**Since version:** `2.2` ∙ **Type:** `object[]` ∙ **Cardinality:** `Optional` From f1380e31bfcbe3eaaadd290f3517015f285455e1 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Fri, 1 Nov 2024 11:59:05 +0200 Subject: [PATCH 02/23] Add tests for 'carsAllowedStopMaxTransferDurationsForMode' build config parameter. --- .../module/DirectTransferGeneratorTest.java | 239 +++++++++++++++++- .../StreetNearbyStopFinderTest.java | 72 ++++++ 2 files changed, 306 insertions(+), 5 deletions(-) diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java index bb3144aa904..30bc5f78f0f 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java @@ -22,15 +22,18 @@ import org.opentripplanner.routing.algorithm.GraphRoutingTest; import org.opentripplanner.routing.api.request.RouteRequest; import org.opentripplanner.routing.api.request.StreetMode; +import org.opentripplanner.routing.api.request.framework.DurationForEnum; import org.opentripplanner.street.model.StreetTraversalPermission; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.vertex.StreetVertex; import org.opentripplanner.street.model.vertex.TransitStopVertex; import org.opentripplanner.transit.model._data.TimetableRepositoryForTest; import org.opentripplanner.transit.model.basic.TransitMode; +import org.opentripplanner.transit.model.network.CarAccess; import org.opentripplanner.transit.model.network.StopPattern; import org.opentripplanner.transit.model.network.TripPattern; import org.opentripplanner.transit.model.site.StopLocation; +import org.opentripplanner.transit.model.timetable.ScheduledTripTimes; /** * This creates a graph with trip patterns @@ -196,7 +199,7 @@ public void testMultipleRequestsWithoutPatterns() { reqWalk.journey().transfer().setMode(StreetMode.WALK); var reqBike = new RouteRequest(); - reqWalk.journey().transfer().setMode(StreetMode.BIKE); + reqBike.journey().transfer().setMode(StreetMode.BIKE); var transferRequests = List.of(reqWalk, reqBike); @@ -223,7 +226,7 @@ public void testMultipleRequestsWithPatterns() { reqWalk.journey().transfer().setMode(StreetMode.WALK); var reqBike = new RouteRequest(); - reqWalk.journey().transfer().setMode(StreetMode.BIKE); + reqBike.journey().transfer().setMode(StreetMode.BIKE); var transferRequests = List.of(reqWalk, reqBike); @@ -252,7 +255,7 @@ public void testMultipleRequestsWithPatterns() { @Test public void testTransferOnIsolatedStations() { - var otpModel = model(true, false, true); + var otpModel = model(true, false, true, false); var graph = otpModel.graph(); graph.hasStreets = false; @@ -273,18 +276,174 @@ public void testTransferOnIsolatedStations() { assertTrue(timetableRepository.getAllPathTransfers().isEmpty()); } + @Test + public void testRequestWithCarsAllowedPatterns() { + var reqCar = new RouteRequest(); + reqCar.journey().transfer().setMode(StreetMode.CAR); + + var transferRequests = List.of(reqCar); + + var otpModel = model(false, false, false, true); + var graph = otpModel.graph(); + graph.hasStreets = true; + var timetableRepository = otpModel.timetableRepository(); + + new DirectTransferGenerator( + graph, + timetableRepository, + DataImportIssueStore.NOOP, + MAX_TRANSFER_DURATION, + transferRequests, + DurationForEnum.of(StreetMode.class).with(StreetMode.CAR, Duration.ofMinutes(60)).build() + ) + .buildGraph(); + + assertTransfers( + timetableRepository.getAllPathTransfers(), + tr(S0, 100, List.of(V0, V11), S11), + tr(S0, 200, List.of(V0, V12), S12) + ); + } + + @Test + public void testRequestWithCarsAllowedPatternsWithDurationLimit() { + var reqCar = new RouteRequest(); + reqCar.journey().transfer().setMode(StreetMode.CAR); + + var transferRequests = List.of(reqCar); + + var otpModel = model(false, false, false, true); + var graph = otpModel.graph(); + graph.hasStreets = true; + var timetableRepository = otpModel.timetableRepository(); + + new DirectTransferGenerator( + graph, + timetableRepository, + DataImportIssueStore.NOOP, + MAX_TRANSFER_DURATION, + transferRequests, + DurationForEnum.of(StreetMode.class).with(StreetMode.CAR, Duration.ofSeconds(10)).build() + ) + .buildGraph(); + + assertTransfers(timetableRepository.getAllPathTransfers(), tr(S0, 100, List.of(V0, V11), S11)); + } + + @Test + public void testMultipleRequestsWithPatternsAndWithCarsAllowedPatterns() { + var reqWalk = new RouteRequest(); + reqWalk.journey().transfer().setMode(StreetMode.WALK); + + var reqBike = new RouteRequest(); + reqBike.journey().transfer().setMode(StreetMode.BIKE); + + var reqCar = new RouteRequest(); + reqCar.journey().transfer().setMode(StreetMode.CAR); + + var transferRequests = List.of(reqWalk, reqBike, reqCar); + + var otpModel = model(true, false, false, true); + var graph = otpModel.graph(); + graph.hasStreets = true; + var timetableRepository = otpModel.timetableRepository(); + + new DirectTransferGenerator( + graph, + timetableRepository, + DataImportIssueStore.NOOP, + MAX_TRANSFER_DURATION, + transferRequests, + DurationForEnum.of(StreetMode.class).with(StreetMode.CAR, Duration.ofMinutes(60)).build() + ) + .buildGraph(); + + assertTransfers( + timetableRepository.getAllPathTransfers(), + tr(S0, 100, List.of(V0, V11), S11), + tr(S0, 100, List.of(V0, V21), S21), + tr(S0, 200, List.of(V0, V12), S12), + tr(S11, 100, List.of(V11, V21), S21), + tr(S11, 110, List.of(V11, V22), S22), + tr(S11, 100, List.of(V11, V12), S12) + ); + } + + @Test + public void testBikeRequestWithPatternsAndWithCarsAllowedPatterns() { + var reqBike = new RouteRequest(); + reqBike.journey().transfer().setMode(StreetMode.BIKE); + + var transferRequests = List.of(reqBike); + + var otpModel = model(true, false, false, true); + var graph = otpModel.graph(); + graph.hasStreets = true; + var timetableRepository = otpModel.timetableRepository(); + + new DirectTransferGenerator( + graph, + timetableRepository, + DataImportIssueStore.NOOP, + Duration.ofSeconds(30), + transferRequests, + DurationForEnum.of(StreetMode.class).with(StreetMode.BIKE, Duration.ofSeconds(120)).build() + ) + .buildGraph(); + + assertTransfers( + timetableRepository.getAllPathTransfers(), + tr(S0, 100, List.of(V0, V11), S11), + tr(S0, 100, List.of(V0, V21), S21), + tr(S0, 200, List.of(V0, V12), S12), + tr(S11, 110, List.of(V11, V22), S22), + tr(S11, 100, List.of(V11, V12), S12) + ); + } + + @Test + public void testBikeRequestWithPatternsAndWithCarsAllowedPatternsWithoutCarInTransferRequests() { + var reqBike = new RouteRequest(); + reqBike.journey().transfer().setMode(StreetMode.BIKE); + + var transferRequests = List.of(reqBike); + + var otpModel = model(true, false, false, true); + var graph = otpModel.graph(); + graph.hasStreets = true; + var timetableRepository = otpModel.timetableRepository(); + + new DirectTransferGenerator( + graph, + timetableRepository, + DataImportIssueStore.NOOP, + Duration.ofSeconds(30), + transferRequests, + DurationForEnum.of(StreetMode.class).with(StreetMode.CAR, Duration.ofSeconds(120)).build() + ) + .buildGraph(); + + assertTransfers( + timetableRepository.getAllPathTransfers(), + tr(S0, 100, List.of(V0, V11), S11), + tr(S0, 100, List.of(V0, V21), S21), + tr(S11, 110, List.of(V11, V22), S22) + ); + } + private TestOtpModel model(boolean addPatterns) { return model(addPatterns, false); } private TestOtpModel model(boolean addPatterns, boolean withBoardingConstraint) { - return model(addPatterns, withBoardingConstraint, false); + return model(addPatterns, withBoardingConstraint, false, false); } private TestOtpModel model( boolean addPatterns, boolean withBoardingConstraint, - boolean withNoTransfersOnStations + boolean withNoTransfersOnStations, + boolean addCarsAllowedPatterns ) { return modelOf( new Builder() { @@ -352,6 +511,76 @@ public void build() { .build() ); } + + if (addCarsAllowedPatterns) { + var agency = TimetableRepositoryForTest.agency("FerryAgency"); + + tripPattern( + TripPattern + .of(TimetableRepositoryForTest.id("TP3")) + .withRoute(route("R3", TransitMode.FERRY, agency)) + .withStopPattern(new StopPattern(List.of(st(S11), st(S21)))) + .withScheduledTimeTableBuilder(builder -> + builder.addTripTimes( + ScheduledTripTimes + .of() + .withTrip( + TimetableRepositoryForTest + .trip("carsAllowedTrip") + .withCarsAllowed(CarAccess.ALLOWED) + .build() + ) + .withDepartureTimes("00:00 01:00") + .build() + ) + ) + .build() + ); + + tripPattern( + TripPattern + .of(TimetableRepositoryForTest.id("TP4")) + .withRoute(route("R4", TransitMode.FERRY, agency)) + .withStopPattern(new StopPattern(List.of(st(S0), st(S13)))) + .withScheduledTimeTableBuilder(builder -> + builder.addTripTimes( + ScheduledTripTimes + .of() + .withTrip( + TimetableRepositoryForTest + .trip("carsAllowedTrip") + .withCarsAllowed(CarAccess.ALLOWED) + .build() + ) + .withDepartureTimes("00:00 01:00") + .build() + ) + ) + .build() + ); + + tripPattern( + TripPattern + .of(TimetableRepositoryForTest.id("TP5")) + .withRoute(route("R5", TransitMode.FERRY, agency)) + .withStopPattern(new StopPattern(List.of(st(S12), st(S22)))) + .withScheduledTimeTableBuilder(builder -> + builder.addTripTimes( + ScheduledTripTimes + .of() + .withTrip( + TimetableRepositoryForTest + .trip("carsAllowedTrip") + .withCarsAllowed(CarAccess.ALLOWED) + .build() + ) + .withDepartureTimes("00:00 01:00") + .build() + ) + ) + .build() + ); + } } } ); diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinderTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinderTest.java index 261a40454f0..f6197e9b56c 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinderTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinderTest.java @@ -165,6 +165,78 @@ void testIgnoreStopsWithMaxStops() { assertStopAtDistance(stopC, 200, sortedNearbyStops.get(0)); } + @Test + void testFindOnlyVerticesStops() { + var durationLimit = Duration.ofMinutes(10); + var maxStopCount = 0; + Set findOnlyStops = Set.of(stopB, stopC); + var finder = new StreetNearbyStopFinder( + durationLimit, + maxStopCount, + null, + Set.of(), + findOnlyStops + ); + + var sortedNearbyStops = sort( + finder.findNearbyStops(stopA, new RouteRequest(), new StreetRequest(), false) + ); + + assertThat(sortedNearbyStops).hasSize(3); + assertZeroDistanceStop(stopA, sortedNearbyStops.get(0)); + assertStopAtDistance(stopB, 100, sortedNearbyStops.get(1)); + assertStopAtDistance(stopC, 200, sortedNearbyStops.get(2)); + } + + @Test + void testFindOnlyVerticesStopsWithIgnore() { + var durationLimit = Duration.ofMinutes(10); + var maxStopCount = 0; + Set findOnlyStops = Set.of(stopB, stopC); + Set ignore = Set.of(stopB); + var finder = new StreetNearbyStopFinder( + durationLimit, + maxStopCount, + null, + ignore, + findOnlyStops + ); + + var sortedNearbyStops = sort( + finder.findNearbyStops(stopA, new RouteRequest(), new StreetRequest(), false) + ); + + assertThat(sortedNearbyStops).hasSize(2); + assertZeroDistanceStop(stopA, sortedNearbyStops.get(0)); + assertStopAtDistance(stopC, 200, sortedNearbyStops.get(1)); + } + + @Test + void testFindOnlyVerticesStopsWithDurationLimit() { + // If we only allow walk for 101 seconds and speed is 1 m/s we should only be able to reach + // one extra stop. + var durationLimit = Duration.ofSeconds(101); + var maxStopCount = 0; + Set findOnlyStops = Set.of(stopB, stopC); + var routeRequest = new RouteRequest() + .withPreferences(b -> b.withWalk(walkPreferences -> walkPreferences.withSpeed(1.0))); + + var finder = new StreetNearbyStopFinder( + durationLimit, + maxStopCount, + null, + Set.of(), + findOnlyStops + ); + var sortedNearbyStops = sort( + finder.findNearbyStops(stopA, routeRequest, new StreetRequest(), false) + ); + + assertThat(sortedNearbyStops).hasSize(2); + assertZeroDistanceStop(stopA, sortedNearbyStops.get(0)); + assertStopAtDistance(stopB, 100, sortedNearbyStops.get(1)); + } + private List sort(Collection stops) { return stops.stream().sorted(Comparator.comparing(x -> x.distance)).toList(); } From 2175e64b58f0cb36b398a45ee27e0306f3f80668 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Fri, 1 Nov 2024 13:13:50 +0200 Subject: [PATCH 03/23] Move if statement outside of for loop. --- .../graph_builder/module/DirectTransferGenerator.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index fcf5ea80cfc..a7b9e269c63 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -166,7 +166,9 @@ public void buildGraph() { for (RouteRequest transferProfile : filteredTransferRequests) { findNearbyStops(nearbyStopFinder, ts0, transferProfile, stop, distinctTransfers); - if (OTPFeature.FlexRouting.isOn()) { + } + if (OTPFeature.FlexRouting.isOn()) { + for (RouteRequest transferProfile : filteredTransferRequests) { // This code is for finding transfers from AreaStops to Stops, transfers // from Stops to AreaStops and between Stops are already covered above. for (NearbyStop sd : nearbyStopFinder.findNearbyStops( @@ -189,6 +191,7 @@ public void buildGraph() { } } } + // This calculates transfers between stops that can use trips with cars. for (RouteRequest transferProfile : carsAllowedStopTransferRequests) { StreetMode mode = transferProfile.journey().transfer().mode(); From e1e9bbbafe36b2548ef1a4f718913e3393834612 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 3 Dec 2024 08:43:11 +0200 Subject: [PATCH 04/23] Fixes based on review comments. --- .../opentripplanner/api/parameter/QualifiedModeSet.java | 7 +++++-- .../graph_builder/module/DirectTransferGenerator.java | 3 ++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/api/parameter/QualifiedModeSet.java b/application/src/main/java/org/opentripplanner/api/parameter/QualifiedModeSet.java index c13fb576ad5..98ce58c6d21 100644 --- a/application/src/main/java/org/opentripplanner/api/parameter/QualifiedModeSet.java +++ b/application/src/main/java/org/opentripplanner/api/parameter/QualifiedModeSet.java @@ -126,8 +126,11 @@ public RequestModes getRequestModes() { mBuilder.withEgressMode(StreetMode.CAR_HAILING); mBuilder.withDirectMode(StreetMode.WALK); } else { - // This is used in transfer cache request calculations. - mBuilder.withAllStreetModes(StreetMode.CAR); + // This is necessary for transfer calculations. + mBuilder.withAccessMode(StreetMode.CAR); + mBuilder.withTransferMode(StreetMode.CAR); + mBuilder.withEgressMode(StreetMode.CAR); + mBuilder.withDirectMode(StreetMode.CAR); } } } diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index fc00a135f27..da71d2afb1b 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -101,7 +101,8 @@ public void buildGraph() { .stream() .map(StopLocation::getId) .map(graph::getStopVertexForStopId) - .filter(TransitStopVertex.class::isInstance) // filter out null values if no TransitStopVertex is found for ID + // filter out null values if no TransitStopVertex is found for ID + .filter(TransitStopVertex.class::isInstance) .collect(Collectors.toSet()); ProgressTracker progress = ProgressTracker.track( From 89e617f26a6e7e8d82d7888bd435cd0b146cd462 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 3 Dec 2024 13:26:10 +0200 Subject: [PATCH 05/23] Add TransferParameters to build config. --- .../module/DirectTransferGenerator.java | 73 +++++++++++-------- .../module/TransferParameters.java | 55 ++++++++++++++ .../module/configure/GraphBuilderModules.java | 3 +- .../standalone/config/BuildConfig.java | 8 +- ...llowedStopMaxTransferDurationsForMode.java | 6 +- .../config/buildconfig/TransferConfig.java | 20 +++++ .../buildconfig/TransferParametersMapper.java | 35 +++++++++ .../module/DirectTransferGeneratorTest.java | 16 ++-- 8 files changed, 172 insertions(+), 44 deletions(-) create mode 100644 application/src/main/java/org/opentripplanner/graph_builder/module/TransferParameters.java create mode 100644 application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferConfig.java create mode 100644 application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferParametersMapper.java diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index da71d2afb1b..51e6294f3b3 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -51,6 +51,7 @@ public class DirectTransferGenerator implements GraphBuilderModule { private final Duration radiusByDuration; private final List transferRequests; + private final Map transferParametersForMode; private final DurationForEnum carsAllowedStopMaxTransferDurationsForMode; private final Graph graph; private final TimetableRepository timetableRepository; @@ -69,6 +70,7 @@ public DirectTransferGenerator( this.radiusByDuration = radiusByDuration; this.transferRequests = transferRequests; this.carsAllowedStopMaxTransferDurationsForMode = DurationForEnum.of(StreetMode.class).build(); + this.transferParametersForMode = Collections.emptyMap(); } public DirectTransferGenerator( @@ -77,7 +79,8 @@ public DirectTransferGenerator( DataImportIssueStore issueStore, Duration radiusByDuration, List transferRequests, - DurationForEnum carsAllowedStopMaxTransferDurationsForMode + DurationForEnum carsAllowedStopMaxTransferDurationsForMode, + Map transferParametersForMode ) { this.graph = graph; this.timetableRepository = timetableRepository; @@ -85,6 +88,7 @@ public DirectTransferGenerator( this.radiusByDuration = radiusByDuration; this.transferRequests = transferRequests; this.carsAllowedStopMaxTransferDurationsForMode = carsAllowedStopMaxTransferDurationsForMode; + this.transferParametersForMode = transferParametersForMode; } @Override @@ -92,9 +96,6 @@ public void buildGraph() { /* Initialize transit model index which is needed by the nearby stop finder. */ timetableRepository.index(); - /* The linker will use streets if they are available, or straight-line distance otherwise. */ - NearbyStopFinder nearbyStopFinder = createNearbyStopFinder(radiusByDuration, Set.of()); - List stops = graph.getVerticesOfType(TransitStopVertex.class); Set carsAllowedStops = timetableRepository .getStopLocationsUsedForCarsAllowedTrips() @@ -121,32 +122,37 @@ public void buildGraph() { List filteredTransferRequests = new ArrayList(); List carsAllowedStopTransferRequests = new ArrayList(); + /* The linker will use streets if they are available, or straight-line distance otherwise. */ + HashMap nearbyStopFinders = new HashMap<>(); + /* These are used for calculating transfers only between carsAllowedStops. */ HashMap carsAllowedStopNearbyStopFinders = new HashMap<>(); - // Split transfer requests into normal and carsAllowedStop requests. + // Parse transfer parameters. for (RouteRequest transferProfile : transferRequests) { StreetMode mode = transferProfile.journey().transfer().mode(); - if (carsAllowedStopMaxTransferDurationsForMode.containsKey(mode)) { - carsAllowedStopNearbyStopFinders.put( - mode, - createNearbyStopFinder( - carsAllowedStopMaxTransferDurationsForMode.valueOf(mode), - Collections.unmodifiableSet(carsAllowedStops) - ) - ); - - carsAllowedStopTransferRequests.add(transferProfile); - // For bikes, also normal transfer requests are wanted. - if (mode == StreetMode.BIKE) { + TransferParameters transferParameters = transferParametersForMode.get(mode); + if (transferParameters != null) { + // Disable normal transfer calculations if disableDefaultTransfers is set in the build config. + if (!transferParameters.disableDefaultTransfers()) { filteredTransferRequests.add(transferProfile); + // Set mode-specific maxTransferDuration, if it is set in the build config. + Duration maxTransferDuration = radiusByDuration; + if (transferParameters.maxTransferDuration() != Duration.ZERO) { + maxTransferDuration = transferParameters.maxTransferDuration(); + } + nearbyStopFinders.put(mode, createNearbyStopFinder(maxTransferDuration, Set.of())); + } + // Create transfers between carsAllowedStops for the specific mode if carsAllowedStopMaxTransferDuration is set in the build config. + if (transferParameters.carsAllowedStopMaxTransferDuration() != Duration.ZERO) { + carsAllowedStopTransferRequests.add(transferProfile); + carsAllowedStopNearbyStopFinders.put( + mode, + createNearbyStopFinder( + transferParameters.carsAllowedStopMaxTransferDuration(), + Collections.unmodifiableSet(carsAllowedStops) + ) + ); } - } else if (mode == StreetMode.CAR) { - // Special transfers are always created for cars. - // If a duration is not specified for cars, the default is used. - carsAllowedStopNearbyStopFinders.put(mode, nearbyStopFinder); - carsAllowedStopTransferRequests.add(transferProfile); - } else { - filteredTransferRequests.add(transferProfile); } } @@ -166,18 +172,23 @@ public void buildGraph() { LOG.debug("Linking stop '{}' {}", stop, ts0); for (RouteRequest transferProfile : filteredTransferRequests) { - findNearbyStops(nearbyStopFinder, ts0, transferProfile, stop, distinctTransfers); + StreetMode mode = transferProfile.journey().transfer().mode(); + findNearbyStops( + nearbyStopFinders.get(mode), + ts0, + transferProfile, + stop, + distinctTransfers + ); } if (OTPFeature.FlexRouting.isOn()) { for (RouteRequest transferProfile : filteredTransferRequests) { + StreetMode mode = transferProfile.journey().transfer().mode(); // This code is for finding transfers from AreaStops to Stops, transfers // from Stops to AreaStops and between Stops are already covered above. - for (NearbyStop sd : nearbyStopFinder.findNearbyStops( - ts0, - transferProfile, - transferProfile.journey().transfer(), - true - )) { + for (NearbyStop sd : nearbyStopFinders + .get(mode) + .findNearbyStops(ts0, transferProfile, transferProfile.journey().transfer(), true)) { // Skip the origin stop, loop transfers are not needed. if (sd.stop == stop) { continue; diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/TransferParameters.java b/application/src/main/java/org/opentripplanner/graph_builder/module/TransferParameters.java new file mode 100644 index 00000000000..962d8579ddd --- /dev/null +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/TransferParameters.java @@ -0,0 +1,55 @@ +package org.opentripplanner.graph_builder.module; + +import java.time.Duration; + +public record TransferParameters( + Duration maxTransferDuration, + Duration carsAllowedStopMaxTransferDuration, + boolean disableDefaultTransfers +) { + public static final Duration DEFAULT_MAX_TRANSFER_DURATION = Duration.ZERO; + public static final Duration DEFAULT_CARS_ALLOWED_STOP_MAX_TRANSFER_DURATION = Duration.ZERO; + public static final boolean DEFAULT_DISABLE_DEFAULT_TRANSFERS = false; + + TransferParameters(Builder builder) { + this( + builder.maxTransferDuration, + builder.carsAllowedStopMaxTransferDuration, + builder.disableDefaultTransfers + ); + } + + public static class Builder { + + private Duration maxTransferDuration; + private Duration carsAllowedStopMaxTransferDuration; + private boolean disableDefaultTransfers; + + public Builder() { + this.maxTransferDuration = DEFAULT_MAX_TRANSFER_DURATION; + this.carsAllowedStopMaxTransferDuration = DEFAULT_CARS_ALLOWED_STOP_MAX_TRANSFER_DURATION; + this.disableDefaultTransfers = DEFAULT_DISABLE_DEFAULT_TRANSFERS; + } + + public Builder withMaxTransferDuration(Duration maxTransferDuration) { + this.maxTransferDuration = maxTransferDuration; + return this; + } + + public Builder withCarsAllowedStopMaxTransferDuration( + Duration carsAllowedStopMaxTransferDuration + ) { + this.carsAllowedStopMaxTransferDuration = carsAllowedStopMaxTransferDuration; + return this; + } + + public Builder withDisableDefaultTransfers(boolean disableDefaultTransfers) { + this.disableDefaultTransfers = disableDefaultTransfers; + return this; + } + + public TransferParameters build() { + return new TransferParameters(this); + } + } +} diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java b/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java index 03ea13e9f11..b6c8eb484ee 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java @@ -238,7 +238,8 @@ static DirectTransferGenerator provideDirectTransferGenerator( issueStore, config.maxTransferDuration, config.transferRequests, - config.carsAllowedStopMaxTransferDurationsForMode + config.carsAllowedStopMaxTransferDurationsForMode, + config.transferParametersForMode ); } diff --git a/application/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java index 6f726c806fe..323b7635708 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java @@ -15,6 +15,7 @@ import java.time.LocalDate; import java.time.ZoneId; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import javax.annotation.Nullable; @@ -23,6 +24,7 @@ import org.opentripplanner.ext.emissions.EmissionsConfig; import org.opentripplanner.ext.fares.FaresConfiguration; import org.opentripplanner.framework.geometry.CompactElevationProfile; +import org.opentripplanner.graph_builder.module.TransferParameters; import org.opentripplanner.graph_builder.module.ned.parameter.DemExtractParameters; import org.opentripplanner.graph_builder.module.ned.parameter.DemExtractParametersList; import org.opentripplanner.graph_builder.module.osm.parameters.OsmExtractParameters; @@ -42,6 +44,7 @@ import org.opentripplanner.standalone.config.buildconfig.NetexConfig; import org.opentripplanner.standalone.config.buildconfig.OsmConfig; import org.opentripplanner.standalone.config.buildconfig.S3BucketConfig; +import org.opentripplanner.standalone.config.buildconfig.TransferConfig; import org.opentripplanner.standalone.config.buildconfig.TransferRequestConfig; import org.opentripplanner.standalone.config.buildconfig.TransitFeedConfig; import org.opentripplanner.standalone.config.buildconfig.TransitFeeds; @@ -154,6 +157,7 @@ public class BuildConfig implements OtpDataStoreConfig { public final IslandPruningConfig islandPruning; public final Duration maxTransferDuration; + public final Map transferParametersForMode; public final DurationForEnum carsAllowedStopMaxTransferDurationsForMode; public final NetexFeedParameters netexDefaults; public final GtfsFeedParameters gtfsDefaults; @@ -291,11 +295,11 @@ When set to true (it is false by default), the elevation module will include the "Transfers up to this duration with the default walk speed value will be pre-calculated and included in the Graph." ) .asDuration(Duration.ofMinutes(30)); + transferParametersForMode = TransferConfig.map(root, "transfers"); carsAllowedStopMaxTransferDurationsForMode = CarsAllowedStopMaxTransferDurationsForMode.map( root, - "carsAllowedStopMaxTransferDurationsForMode", - maxTransferDuration + "carsAllowedStopMaxTransferDurationsForMode" ); maxStopToShapeSnapDistance = root diff --git a/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/CarsAllowedStopMaxTransferDurationsForMode.java b/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/CarsAllowedStopMaxTransferDurationsForMode.java index 322ed32d682..531d5d9af7f 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/CarsAllowedStopMaxTransferDurationsForMode.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/CarsAllowedStopMaxTransferDurationsForMode.java @@ -10,11 +10,7 @@ public class CarsAllowedStopMaxTransferDurationsForMode { - public static DurationForEnum map( - NodeAdapter root, - String parameterName, - Duration maxTransferDuration - ) { + public static DurationForEnum map(NodeAdapter root, String parameterName) { Map values = root .of(parameterName) .since(V2_7) diff --git a/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferConfig.java new file mode 100644 index 00000000000..3f171238c05 --- /dev/null +++ b/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferConfig.java @@ -0,0 +1,20 @@ +package org.opentripplanner.standalone.config.buildconfig; + +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_7; + +import java.util.EnumMap; +import java.util.Map; +import org.opentripplanner.graph_builder.module.TransferParameters; +import org.opentripplanner.routing.api.request.StreetMode; +import org.opentripplanner.standalone.config.framework.json.NodeAdapter; + +public class TransferConfig { + + public static Map map(NodeAdapter root, String parameterName) { + return root + .of(parameterName) + .since(V2_7) + .summary("Configures properties for transfer calculations.") + .asEnumMap(StreetMode.class, TransferParametersMapper::map, new EnumMap<>(StreetMode.class)); + } +} diff --git a/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferParametersMapper.java b/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferParametersMapper.java new file mode 100644 index 00000000000..caf35f0155c --- /dev/null +++ b/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferParametersMapper.java @@ -0,0 +1,35 @@ +package org.opentripplanner.standalone.config.buildconfig; + +import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_7; + +import org.opentripplanner.graph_builder.module.TransferParameters; +import org.opentripplanner.standalone.config.framework.json.NodeAdapter; + +public class TransferParametersMapper { + + public static TransferParameters map(NodeAdapter c) { + TransferParameters.Builder builder = new TransferParameters.Builder(); + builder.withMaxTransferDuration( + c + .of("maxTransferDuration") + .summary("") + .since(V2_7) + .asDuration(TransferParameters.DEFAULT_MAX_TRANSFER_DURATION) + ); + builder.withCarsAllowedStopMaxTransferDuration( + c + .of("carsAllowedStopMaxTransferDuration") + .summary("") + .since(V2_7) + .asDuration(TransferParameters.DEFAULT_CARS_ALLOWED_STOP_MAX_TRANSFER_DURATION) + ); + builder.withDisableDefaultTransfers( + c + .of("disableDefaultTransfers") + .summary("") + .since(V2_7) + .asBoolean(TransferParameters.DEFAULT_DISABLE_DEFAULT_TRANSFERS) + ); + return builder.build(); + } +} diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java index fc4aed8833f..d66d0b29930 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java @@ -8,6 +8,7 @@ import java.time.Duration; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Objects; @@ -294,7 +295,8 @@ public void testRequestWithCarsAllowedPatterns() { DataImportIssueStore.NOOP, MAX_TRANSFER_DURATION, transferRequests, - DurationForEnum.of(StreetMode.class).with(StreetMode.CAR, Duration.ofMinutes(60)).build() + DurationForEnum.of(StreetMode.class).with(StreetMode.CAR, Duration.ofMinutes(60)).build(), + Collections.emptyMap() ) .buildGraph(); @@ -323,7 +325,8 @@ public void testRequestWithCarsAllowedPatternsWithDurationLimit() { DataImportIssueStore.NOOP, MAX_TRANSFER_DURATION, transferRequests, - DurationForEnum.of(StreetMode.class).with(StreetMode.CAR, Duration.ofSeconds(10)).build() + DurationForEnum.of(StreetMode.class).with(StreetMode.CAR, Duration.ofSeconds(10)).build(), + Collections.emptyMap() ) .buildGraph(); @@ -354,7 +357,8 @@ public void testMultipleRequestsWithPatternsAndWithCarsAllowedPatterns() { DataImportIssueStore.NOOP, MAX_TRANSFER_DURATION, transferRequests, - DurationForEnum.of(StreetMode.class).with(StreetMode.CAR, Duration.ofMinutes(60)).build() + DurationForEnum.of(StreetMode.class).with(StreetMode.CAR, Duration.ofMinutes(60)).build(), + Collections.emptyMap() ) .buildGraph(); @@ -387,7 +391,8 @@ public void testBikeRequestWithPatternsAndWithCarsAllowedPatterns() { DataImportIssueStore.NOOP, Duration.ofSeconds(30), transferRequests, - DurationForEnum.of(StreetMode.class).with(StreetMode.BIKE, Duration.ofSeconds(120)).build() + DurationForEnum.of(StreetMode.class).with(StreetMode.BIKE, Duration.ofSeconds(120)).build(), + Collections.emptyMap() ) .buildGraph(); @@ -419,7 +424,8 @@ public void testBikeRequestWithPatternsAndWithCarsAllowedPatternsWithoutCarInTra DataImportIssueStore.NOOP, Duration.ofSeconds(30), transferRequests, - DurationForEnum.of(StreetMode.class).with(StreetMode.CAR, Duration.ofSeconds(120)).build() + DurationForEnum.of(StreetMode.class).with(StreetMode.CAR, Duration.ofSeconds(120)).build(), + Collections.emptyMap() ) .buildGraph(); From a1e080791c8bdc304a40c33e7f625afc39fb21b0 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 3 Dec 2024 14:17:01 +0200 Subject: [PATCH 06/23] Remove mentions of carsAllowedStopMaxTransferDurationsForMode and fix tests. --- .../module/DirectTransferGenerator.java | 7 +- .../module/configure/GraphBuilderModules.java | 1 - .../request/framework/DurationForEnum.java | 4 - .../standalone/config/BuildConfig.java | 7 - ...llowedStopMaxTransferDurationsForMode.java | 51 ---- .../module/DirectTransferGeneratorTest.java | 47 +++- doc/user/BuildConfiguration.md | 231 ++++++++---------- 7 files changed, 146 insertions(+), 202 deletions(-) delete mode 100644 application/src/main/java/org/opentripplanner/standalone/config/buildconfig/CarsAllowedStopMaxTransferDurationsForMode.java diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index 51e6294f3b3..62221750f68 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -52,7 +52,6 @@ public class DirectTransferGenerator implements GraphBuilderModule { private final List transferRequests; private final Map transferParametersForMode; - private final DurationForEnum carsAllowedStopMaxTransferDurationsForMode; private final Graph graph; private final TimetableRepository timetableRepository; private final DataImportIssueStore issueStore; @@ -69,7 +68,6 @@ public DirectTransferGenerator( this.issueStore = issueStore; this.radiusByDuration = radiusByDuration; this.transferRequests = transferRequests; - this.carsAllowedStopMaxTransferDurationsForMode = DurationForEnum.of(StreetMode.class).build(); this.transferParametersForMode = Collections.emptyMap(); } @@ -79,7 +77,6 @@ public DirectTransferGenerator( DataImportIssueStore issueStore, Duration radiusByDuration, List transferRequests, - DurationForEnum carsAllowedStopMaxTransferDurationsForMode, Map transferParametersForMode ) { this.graph = graph; @@ -87,7 +84,6 @@ public DirectTransferGenerator( this.issueStore = issueStore; this.radiusByDuration = radiusByDuration; this.transferRequests = transferRequests; - this.carsAllowedStopMaxTransferDurationsForMode = carsAllowedStopMaxTransferDurationsForMode; this.transferParametersForMode = transferParametersForMode; } @@ -153,6 +149,9 @@ public void buildGraph() { ) ); } + } else { + filteredTransferRequests.add(transferProfile); + nearbyStopFinders.put(mode, createNearbyStopFinder(radiusByDuration, Set.of())); } } diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java b/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java index b6c8eb484ee..4cf0d3816bf 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/configure/GraphBuilderModules.java @@ -238,7 +238,6 @@ static DirectTransferGenerator provideDirectTransferGenerator( issueStore, config.maxTransferDuration, config.transferRequests, - config.carsAllowedStopMaxTransferDurationsForMode, config.transferParametersForMode ); } diff --git a/application/src/main/java/org/opentripplanner/routing/api/request/framework/DurationForEnum.java b/application/src/main/java/org/opentripplanner/routing/api/request/framework/DurationForEnum.java index 7547a58ecc7..c6586aa8b0f 100644 --- a/application/src/main/java/org/opentripplanner/routing/api/request/framework/DurationForEnum.java +++ b/application/src/main/java/org/opentripplanner/routing/api/request/framework/DurationForEnum.java @@ -49,10 +49,6 @@ public Duration defaultValue() { return defaultValue; } - public boolean containsKey(E key) { - return valueForEnum.containsKey(key); - } - /** * Utility method to get {@link #defaultValue} as an number in unit seconds. Equivalent to * {@code (int) defaultValue.toSeconds()}. The downcast is safe since we only allow days, hours, diff --git a/application/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java index 323b7635708..d7ed602aef5 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java @@ -37,7 +37,6 @@ import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.routing.api.request.framework.DurationForEnum; import org.opentripplanner.routing.fares.FareServiceFactory; -import org.opentripplanner.standalone.config.buildconfig.CarsAllowedStopMaxTransferDurationsForMode; import org.opentripplanner.standalone.config.buildconfig.DemConfig; import org.opentripplanner.standalone.config.buildconfig.GtfsConfig; import org.opentripplanner.standalone.config.buildconfig.IslandPruningConfig; @@ -158,7 +157,6 @@ public class BuildConfig implements OtpDataStoreConfig { public final Duration maxTransferDuration; public final Map transferParametersForMode; - public final DurationForEnum carsAllowedStopMaxTransferDurationsForMode; public final NetexFeedParameters netexDefaults; public final GtfsFeedParameters gtfsDefaults; @@ -296,11 +294,6 @@ When set to true (it is false by default), the elevation module will include the ) .asDuration(Duration.ofMinutes(30)); transferParametersForMode = TransferConfig.map(root, "transfers"); - carsAllowedStopMaxTransferDurationsForMode = - CarsAllowedStopMaxTransferDurationsForMode.map( - root, - "carsAllowedStopMaxTransferDurationsForMode" - ); maxStopToShapeSnapDistance = root .of("maxStopToShapeSnapDistance") diff --git a/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/CarsAllowedStopMaxTransferDurationsForMode.java b/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/CarsAllowedStopMaxTransferDurationsForMode.java deleted file mode 100644 index 531d5d9af7f..00000000000 --- a/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/CarsAllowedStopMaxTransferDurationsForMode.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.opentripplanner.standalone.config.buildconfig; - -import static org.opentripplanner.standalone.config.framework.json.OtpVersion.V2_7; - -import java.time.Duration; -import java.util.Map; -import org.opentripplanner.routing.api.request.StreetMode; -import org.opentripplanner.routing.api.request.framework.DurationForEnum; -import org.opentripplanner.standalone.config.framework.json.NodeAdapter; - -public class CarsAllowedStopMaxTransferDurationsForMode { - - public static DurationForEnum map(NodeAdapter root, String parameterName) { - Map values = root - .of(parameterName) - .since(V2_7) - .summary( - "This is used for specifying a `maxTransferDuration` value for bikes and cars to use with transfers between stops that have trips with cars." - ) - .description( - """ -This is a special parameter that only works on transfers between stops that have trips that allow cars. -The duration can be set for either 'BIKE' or 'CAR'. -For cars, transfers are only calculated between stops that have trips that allow cars. -For cars, this overrides the default `maxTransferDuration`. -For bicycles, this indicates that additional transfers should be calculated with the specified duration between stops that have trips that allow cars. - -**Example** - -```JSON -// build-config.json -{ - "carsAllowedStopMaxTransferDurationsForMode": { - "CAR": "2h", - "BIKE": "3h" - } -} -``` -""" - ) - .asEnumMap(StreetMode.class, Duration.class); - for (StreetMode mode : values.keySet()) { - if (mode != StreetMode.BIKE && mode != StreetMode.CAR) { - throw new IllegalArgumentException( - "Only the CAR and BIKE modes are allowed in the carsAllowedStopMaxTransferDurationsForMode parameter." - ); - } - } - return DurationForEnum.of(StreetMode.class).withValues(values).build(); - } -} diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java index d66d0b29930..8cd8dae3b6f 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java @@ -9,8 +9,10 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Stream; @@ -289,14 +291,19 @@ public void testRequestWithCarsAllowedPatterns() { graph.hasStreets = true; var timetableRepository = otpModel.timetableRepository(); + TransferParameters.Builder transferParametersBuilder = new TransferParameters.Builder(); + transferParametersBuilder.withCarsAllowedStopMaxTransferDuration(Duration.ofMinutes(60)); + transferParametersBuilder.withDisableDefaultTransfers(true); + Map transferParametersForMode = new HashMap<>(); + transferParametersForMode.put(StreetMode.CAR, transferParametersBuilder.build()); + new DirectTransferGenerator( graph, timetableRepository, DataImportIssueStore.NOOP, MAX_TRANSFER_DURATION, transferRequests, - DurationForEnum.of(StreetMode.class).with(StreetMode.CAR, Duration.ofMinutes(60)).build(), - Collections.emptyMap() + transferParametersForMode ) .buildGraph(); @@ -319,14 +326,19 @@ public void testRequestWithCarsAllowedPatternsWithDurationLimit() { graph.hasStreets = true; var timetableRepository = otpModel.timetableRepository(); + TransferParameters.Builder transferParametersBuilder = new TransferParameters.Builder(); + transferParametersBuilder.withCarsAllowedStopMaxTransferDuration(Duration.ofSeconds(10)); + transferParametersBuilder.withDisableDefaultTransfers(true); + Map transferParametersForMode = new HashMap<>(); + transferParametersForMode.put(StreetMode.CAR, transferParametersBuilder.build()); + new DirectTransferGenerator( graph, timetableRepository, DataImportIssueStore.NOOP, MAX_TRANSFER_DURATION, transferRequests, - DurationForEnum.of(StreetMode.class).with(StreetMode.CAR, Duration.ofSeconds(10)).build(), - Collections.emptyMap() + transferParametersForMode ) .buildGraph(); @@ -351,14 +363,19 @@ public void testMultipleRequestsWithPatternsAndWithCarsAllowedPatterns() { graph.hasStreets = true; var timetableRepository = otpModel.timetableRepository(); + TransferParameters.Builder transferParametersBuilder = new TransferParameters.Builder(); + transferParametersBuilder.withCarsAllowedStopMaxTransferDuration(Duration.ofMinutes(60)); + transferParametersBuilder.withDisableDefaultTransfers(true); + Map transferParametersForMode = new HashMap<>(); + transferParametersForMode.put(StreetMode.CAR, transferParametersBuilder.build()); + new DirectTransferGenerator( graph, timetableRepository, DataImportIssueStore.NOOP, MAX_TRANSFER_DURATION, transferRequests, - DurationForEnum.of(StreetMode.class).with(StreetMode.CAR, Duration.ofMinutes(60)).build(), - Collections.emptyMap() + transferParametersForMode ) .buildGraph(); @@ -385,14 +402,19 @@ public void testBikeRequestWithPatternsAndWithCarsAllowedPatterns() { graph.hasStreets = true; var timetableRepository = otpModel.timetableRepository(); + TransferParameters.Builder transferParametersBuilder = new TransferParameters.Builder(); + transferParametersBuilder.withCarsAllowedStopMaxTransferDuration(Duration.ofMinutes(120)); + transferParametersBuilder.withDisableDefaultTransfers(true); + Map transferParametersForMode = new HashMap<>(); + transferParametersForMode.put(StreetMode.BIKE, transferParametersBuilder.build()); + new DirectTransferGenerator( graph, timetableRepository, DataImportIssueStore.NOOP, Duration.ofSeconds(30), transferRequests, - DurationForEnum.of(StreetMode.class).with(StreetMode.BIKE, Duration.ofSeconds(120)).build(), - Collections.emptyMap() + transferParametersForMode ) .buildGraph(); @@ -418,14 +440,19 @@ public void testBikeRequestWithPatternsAndWithCarsAllowedPatternsWithoutCarInTra graph.hasStreets = true; var timetableRepository = otpModel.timetableRepository(); + TransferParameters.Builder transferParametersBuilder = new TransferParameters.Builder(); + transferParametersBuilder.withCarsAllowedStopMaxTransferDuration(Duration.ofSeconds(120)); + transferParametersBuilder.withDisableDefaultTransfers(true); + Map transferParametersForMode = new HashMap<>(); + transferParametersForMode.put(StreetMode.CAR, transferParametersBuilder.build()); + new DirectTransferGenerator( graph, timetableRepository, DataImportIssueStore.NOOP, Duration.ofSeconds(30), transferRequests, - DurationForEnum.of(StreetMode.class).with(StreetMode.CAR, Duration.ofSeconds(120)).build(), - Collections.emptyMap() + transferParametersForMode ) .buildGraph(); diff --git a/doc/user/BuildConfiguration.md b/doc/user/BuildConfiguration.md index e2d46e98d73..413d8a8c7a1 100644 --- a/doc/user/BuildConfiguration.md +++ b/doc/user/BuildConfiguration.md @@ -17,104 +17,104 @@ Sections follow that describe particular settings in more depth. -| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | -|-------------------------------------------------------------------------------------------|:----------------------:|----------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------:|-----------------------------------|:-----:| -| [areaVisibility](#areaVisibility) | `boolean` | Perform visibility calculations. | *Optional* | `false` | 1.5 | -| [buildReportDir](#buildReportDir) | `uri` | URI to the directory where the graph build report should be written to. | *Optional* | | 2.0 | -| [configVersion](#configVersion) | `string` | Deployment version of the *build-config.json*. | *Optional* | | 2.1 | -| [dataImportReport](#dataImportReport) | `boolean` | Generate nice HTML report of Graph errors/warnings | *Optional* | `false` | 2.0 | -| [distanceBetweenElevationSamples](#distanceBetweenElevationSamples) | `double` | The distance between elevation samples in meters. | *Optional* | `10.0` | 2.0 | -| embedRouterConfig | `boolean` | Embed the Router config in the graph, which allows it to be sent to a server fully configured over the wire. | *Optional* | `true` | 2.0 | -| [graph](#graph) | `uri` | URI to the graph object file for reading and writing. | *Optional* | | 2.0 | -| [gsCredentials](#gsCredentials) | `string` | Local file system path to Google Cloud Platform service accounts credentials file. | *Optional* | | 2.0 | -| [includeEllipsoidToGeoidDifference](#includeEllipsoidToGeoidDifference) | `boolean` | Include the Ellipsoid to Geoid difference in the calculations of every point along every StreetWithElevationEdge. | *Optional* | `false` | 2.0 | -| maxAreaNodes | `integer` | Visibility calculations for an area will not be done if there are more nodes than this limit. | *Optional* | `150` | 2.1 | -| [maxDataImportIssuesPerFile](#maxDataImportIssuesPerFile) | `integer` | When to split the import report. | *Optional* | `1000` | 2.0 | -| maxElevationPropagationMeters | `integer` | The maximum distance to propagate elevation to vertices which have no elevation. | *Optional* | `2000` | 1.5 | -| [maxStopToShapeSnapDistance](#maxStopToShapeSnapDistance) | `double` | Maximum distance between route shapes and their stops. | *Optional* | `150.0` | 2.1 | -| maxTransferDuration | `duration` | Transfers up to this duration with the default walk speed value will be pre-calculated and included in the Graph. | *Optional* | `"PT30M"` | 2.1 | -| [multiThreadElevationCalculations](#multiThreadElevationCalculations) | `boolean` | Configuring multi-threading during elevation calculations. | *Optional* | `false` | 2.0 | -| [osmCacheDataInMem](#osmCacheDataInMem) | `boolean` | If OSM data should be cached in memory during processing. | *Optional* | `false` | 2.0 | -| [osmNaming](#osmNaming) | `enum` | A custom OSM namer to use. | *Optional* | `"default"` | 1.5 | -| platformEntriesLinking | `boolean` | Link unconnected entries to public transport platforms. | *Optional* | `false` | 2.0 | -| [readCachedElevations](#readCachedElevations) | `boolean` | Whether to read cached elevation data. | *Optional* | `true` | 2.0 | -| staticBikeParkAndRide | `boolean` | Whether we should create bike P+R stations from OSM data. | *Optional* | `false` | 1.5 | -| staticParkAndRide | `boolean` | Whether we should create car P+R stations from OSM data. | *Optional* | `true` | 1.5 | -| stopConsolidationFile | `uri` | Name of the CSV-formatted file in the build directory which contains the configuration for stop consolidation. | *Optional* | | 2.5 | -| [streetGraph](#streetGraph) | `uri` | URI to the street graph object file for reading and writing. | *Optional* | | 2.0 | -| [subwayAccessTime](#subwayAccessTime) | `double` | Minutes necessary to reach stops served by trips on routes of route_type=1 (subway) from the street. | *Optional* | `2.0` | 1.5 | -| [transitModelTimeZone](#transitModelTimeZone) | `time-zone` | Time zone for the graph. | *Optional* | | 2.2 | -| [transitServiceEnd](#transitServiceEnd) | `duration` | Limit the import of transit services to the given end date. | *Optional* | `"P3Y"` | 2.0 | -| [transitServiceStart](#transitServiceStart) | `duration` | Limit the import of transit services to the given START date. | *Optional* | `"-P1Y"` | 2.0 | -| [writeCachedElevations](#writeCachedElevations) | `boolean` | Reusing elevation data from previous builds | *Optional* | `false` | 2.0 | -| [boardingLocationTags](#boardingLocationTags) | `string[]` | What OSM tags should be looked on for the source of matching stops to platforms and stops. | *Optional* | | 2.2 | -| [carsAllowedStopMaxTransferDurationsForMode](#carsAllowedStopMaxTransferDurationsForMode) | `enum map of duration` | This is used for specifying a `maxTransferDuration` value for bikes and cars to use with transfers between stops that have trips with cars. | *Optional* | | 2.7 | -| [dataOverlay](sandbox/DataOverlay.md) | `object` | Config for the DataOverlay Sandbox module | *Optional* | | 2.2 | -| [dem](#dem) | `object[]` | Specify parameters for DEM extracts. | *Optional* | | 2.2 | -|       [elevationUnitMultiplier](#dem_0_elevationUnitMultiplier) | `double` | Specify a multiplier to convert elevation units from source to meters. Overrides the value specified in `demDefaults`. | *Optional* | `1.0` | 2.3 | -|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | -| demDefaults | `object` | Default properties for DEM extracts. | *Optional* | | 2.3 | -|    [elevationUnitMultiplier](#demDefaults_elevationUnitMultiplier) | `double` | Specify a multiplier to convert elevation units from source to meters. | *Optional* | `1.0` | 2.3 | -| [elevationBucket](#elevationBucket) | `object` | Used to download NED elevation tiles from the given AWS S3 bucket. | *Optional* | | na | -| [emissions](sandbox/Emissions.md) | `object` | Emissions configuration. | *Optional* | | 2.5 | -| [fares](sandbox/Fares.md) | `object` | Fare configuration. | *Optional* | | 2.0 | -| gtfsDefaults | `object` | The gtfsDefaults section allows you to specify default properties for GTFS files. | *Optional* | | 2.3 | -|    blockBasedInterlining | `boolean` | Whether to create stay-seated transfers in between two trips with the same block id. | *Optional* | `true` | 2.3 | -|    [discardMinTransferTimes](#gd_discardMinTransferTimes) | `boolean` | Should minimum transfer times in GTFS files be discarded. | *Optional* | `false` | 2.3 | -|    maxInterlineDistance | `integer` | Maximal distance between stops in meters that will connect consecutive trips that are made with same vehicle. | *Optional* | `200` | 2.3 | -|    removeRepeatedStops | `boolean` | Should consecutive identical stops be merged into one stop time entry. | *Optional* | `true` | 2.3 | -|    [stationTransferPreference](#gd_stationTransferPreference) | `enum` | Should there be some preference or aversion for transfers at stops that are part of a station. | *Optional* | `"allowed"` | 2.3 | -| islandPruning | `object` | Settings for fixing street graph connectivity errors | *Optional* | | 2.3 | -|    [adaptivePruningDistance](#islandPruning_adaptivePruningDistance) | `integer` | Search distance for analyzing islands in pruning. | *Optional* | `250` | 2.3 | -|    [adaptivePruningFactor](#islandPruning_adaptivePruningFactor) | `double` | Defines how much pruning thresholds grow maximally by distance. | *Optional* | `50.0` | 2.3 | -|    [islandWithStopsMaxSize](#islandPruning_islandWithStopsMaxSize) | `integer` | When a graph island with stops in it should be pruned. | *Optional* | `2` | 2.3 | -|    [islandWithoutStopsMaxSize](#islandPruning_islandWithoutStopsMaxSize) | `integer` | When a graph island without stops should be pruned. | *Optional* | `10` | 2.3 | -| [localFileNamePatterns](#localFileNamePatterns) | `object` | Patterns for matching OTP file types in the base directory | *Optional* | | 2.0 | -|    [dem](#lfp_dem) | `regexp` | Pattern for matching elevation DEM files. | *Optional* | `"(?i)\.tiff?$"` | 2.0 | -|    [gtfs](#lfp_gtfs) | `regexp` | Patterns for matching GTFS zip-files or directories. | *Optional* | `"(?i)gtfs"` | 2.0 | -|    [netex](#lfp_netex) | `regexp` | Patterns for matching NeTEx zip files or directories. | *Optional* | `"(?i)netex"` | 2.0 | -|    [osm](#lfp_osm) | `regexp` | Pattern for matching Open Street Map input files. | *Optional* | `"(?i)(\.pbf¦\.osm¦\.osm\.xml)$"` | 2.0 | -| netexDefaults | `object` | The netexDefaults section allows you to specify default properties for NeTEx files. | *Optional* | | 2.2 | -|    feedId | `string` | This field is used to identify the specific NeTEx feed. It is used instead of the feed_id field in GTFS file feed_info.txt. | *Optional* | `"NETEX"` | 2.2 | -|    [groupFilePattern](#nd_groupFilePattern) | `regexp` | Pattern for matching group NeTEx files. | *Optional* | `"(\w{3})-.*\.xml"` | 2.0 | -|    ignoreFareFrame | `boolean` | Ignore contents of the FareFrame | *Optional* | `false` | 2.3 | -|    [ignoreFilePattern](#nd_ignoreFilePattern) | `regexp` | Pattern for matching ignored files in a NeTEx bundle. | *Optional* | `"$^"` | 2.0 | -|    ignoreParking | `boolean` | Ignore Parking elements. | *Optional* | `true` | 2.6 | -|    noTransfersOnIsolatedStops | `boolean` | Whether we should allow transfers to and from StopPlaces marked with LimitedUse.ISOLATED | *Optional* | `false` | 2.2 | -|    [sharedFilePattern](#nd_sharedFilePattern) | `regexp` | Pattern for matching shared NeTEx files in a NeTEx bundle. | *Optional* | `"shared-data\.xml"` | 2.0 | -|    [sharedGroupFilePattern](#nd_sharedGroupFilePattern) | `regexp` | Pattern for matching shared group NeTEx files in a NeTEx bundle. | *Optional* | `"(\w{3})-.*-shared\.xml"` | 2.0 | -|    [ferryIdsNotAllowedForBicycle](#nd_ferryIdsNotAllowedForBicycle) | `string[]` | List ferries which do not allow bikes. | *Optional* | | 2.0 | -| [osm](#osm) | `object[]` | Configure properties for a given OpenStreetMap feed. | *Optional* | | 2.2 | -|       [osmTagMapping](#osm_0_osmTagMapping) | `enum` | The named set of mapping rules applied when parsing OSM tags. Overrides the value specified in `osmDefaults`. | *Optional* | `"default"` | 2.2 | -|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | -|       timeZone | `time-zone` | The timezone used to resolve opening hours in OSM data. Overrides the value specified in `osmDefaults`. | *Optional* | | 2.2 | -| osmDefaults | `object` | Default properties for OpenStreetMap feeds. | *Optional* | | 2.2 | -|    [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 | -| [transferRequests](RouteRequest.md) | `object[]` | Routing requests to use for pre-calculating stop-to-stop transfers. | *Optional* | | 2.1 | -| [transitFeeds](#transitFeeds) | `object[]` | Scan for transit data files | *Optional* | | 2.2 | -|    { object } | `object` | Nested object in array. The object type is determined by the parameters. | *Optional* | | 2.2 | -|       type = "gtfs" | `enum` | The feed input format. | *Required* | | 2.2 | -|       blockBasedInterlining | `boolean` | Whether to create stay-seated transfers in between two trips with the same block id. Overrides the value specified in `gtfsDefaults`. | *Optional* | `true` | 2.3 | -|       [discardMinTransferTimes](#tf_0_discardMinTransferTimes) | `boolean` | Should minimum transfer times in GTFS files be discarded. Overrides the value specified in `gtfsDefaults`. | *Optional* | `false` | 2.3 | -|       feedId | `string` | The unique ID for this feed. This overrides any feed ID defined within the feed itself. | *Optional* | | 2.2 | -|       maxInterlineDistance | `integer` | Maximal distance between stops in meters that will connect consecutive trips that are made with same vehicle. Overrides the value specified in `gtfsDefaults`. | *Optional* | `200` | 2.3 | -|       removeRepeatedStops | `boolean` | Should consecutive identical stops be merged into one stop time entry. Overrides the value specified in `gtfsDefaults`. | *Optional* | `true` | 2.3 | -|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | -|       [stationTransferPreference](#tf_0_stationTransferPreference) | `enum` | Should there be some preference or aversion for transfers at stops that are part of a station. Overrides the value specified in `gtfsDefaults`. | *Optional* | `"allowed"` | 2.3 | -|    { object } | `object` | Nested object in array. The object type is determined by the parameters. | *Optional* | | 2.2 | -|       type = "netex" | `enum` | The feed input format. | *Required* | | 2.2 | -|       feedId | `string` | This field is used to identify the specific NeTEx feed. It is used instead of the feed_id field in GTFS file feed_info.txt. | *Required* | | 2.2 | -|       [groupFilePattern](#tf_1_groupFilePattern) | `regexp` | Pattern for matching group NeTEx files. | *Optional* | `"(\w{3})-.*\.xml"` | 2.0 | -|       ignoreFareFrame | `boolean` | Ignore contents of the FareFrame | *Optional* | `false` | 2.3 | -|       [ignoreFilePattern](#tf_1_ignoreFilePattern) | `regexp` | Pattern for matching ignored files in a NeTEx bundle. | *Optional* | `"$^"` | 2.0 | -|       ignoreParking | `boolean` | Ignore Parking elements. | *Optional* | `true` | 2.6 | -|       noTransfersOnIsolatedStops | `boolean` | Whether we should allow transfers to and from StopPlaces marked with LimitedUse.ISOLATED | *Optional* | `false` | 2.2 | -|       [sharedFilePattern](#tf_1_sharedFilePattern) | `regexp` | Pattern for matching shared NeTEx files in a NeTEx bundle. | *Optional* | `"shared-data\.xml"` | 2.0 | -|       [sharedGroupFilePattern](#tf_1_sharedGroupFilePattern) | `regexp` | Pattern for matching shared group NeTEx files in a NeTEx bundle. | *Optional* | `"(\w{3})-.*-shared\.xml"` | 2.0 | -|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | -|       [ferryIdsNotAllowedForBicycle](#tf_1_ferryIdsNotAllowedForBicycle) | `string[]` | List ferries which do not allow bikes. | *Optional* | | 2.0 | -| [transitRouteToStationCentroid](#transitRouteToStationCentroid) | `feed-scoped-id[]` | List stations that should route to centroid. | *Optional* | | 2.7 | +| Config Parameter | Type | Summary | Req./Opt. | Default Value | Since | +|--------------------------------------------------------------------------|:--------------------:|----------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------:|-----------------------------------|:-----:| +| [areaVisibility](#areaVisibility) | `boolean` | Perform visibility calculations. | *Optional* | `false` | 1.5 | +| [buildReportDir](#buildReportDir) | `uri` | URI to the directory where the graph build report should be written to. | *Optional* | | 2.0 | +| [configVersion](#configVersion) | `string` | Deployment version of the *build-config.json*. | *Optional* | | 2.1 | +| [dataImportReport](#dataImportReport) | `boolean` | Generate nice HTML report of Graph errors/warnings | *Optional* | `false` | 2.0 | +| [distanceBetweenElevationSamples](#distanceBetweenElevationSamples) | `double` | The distance between elevation samples in meters. | *Optional* | `10.0` | 2.0 | +| embedRouterConfig | `boolean` | Embed the Router config in the graph, which allows it to be sent to a server fully configured over the wire. | *Optional* | `true` | 2.0 | +| [graph](#graph) | `uri` | URI to the graph object file for reading and writing. | *Optional* | | 2.0 | +| [gsCredentials](#gsCredentials) | `string` | Local file system path to Google Cloud Platform service accounts credentials file. | *Optional* | | 2.0 | +| [includeEllipsoidToGeoidDifference](#includeEllipsoidToGeoidDifference) | `boolean` | Include the Ellipsoid to Geoid difference in the calculations of every point along every StreetWithElevationEdge. | *Optional* | `false` | 2.0 | +| maxAreaNodes | `integer` | Visibility calculations for an area will not be done if there are more nodes than this limit. | *Optional* | `150` | 2.1 | +| [maxDataImportIssuesPerFile](#maxDataImportIssuesPerFile) | `integer` | When to split the import report. | *Optional* | `1000` | 2.0 | +| maxElevationPropagationMeters | `integer` | The maximum distance to propagate elevation to vertices which have no elevation. | *Optional* | `2000` | 1.5 | +| [maxStopToShapeSnapDistance](#maxStopToShapeSnapDistance) | `double` | Maximum distance between route shapes and their stops. | *Optional* | `150.0` | 2.1 | +| maxTransferDuration | `duration` | Transfers up to this duration with the default walk speed value will be pre-calculated and included in the Graph. | *Optional* | `"PT30M"` | 2.1 | +| [multiThreadElevationCalculations](#multiThreadElevationCalculations) | `boolean` | Configuring multi-threading during elevation calculations. | *Optional* | `false` | 2.0 | +| [osmCacheDataInMem](#osmCacheDataInMem) | `boolean` | If OSM data should be cached in memory during processing. | *Optional* | `false` | 2.0 | +| [osmNaming](#osmNaming) | `enum` | A custom OSM namer to use. | *Optional* | `"default"` | 1.5 | +| platformEntriesLinking | `boolean` | Link unconnected entries to public transport platforms. | *Optional* | `false` | 2.0 | +| [readCachedElevations](#readCachedElevations) | `boolean` | Whether to read cached elevation data. | *Optional* | `true` | 2.0 | +| staticBikeParkAndRide | `boolean` | Whether we should create bike P+R stations from OSM data. | *Optional* | `false` | 1.5 | +| staticParkAndRide | `boolean` | Whether we should create car P+R stations from OSM data. | *Optional* | `true` | 1.5 | +| stopConsolidationFile | `uri` | Name of the CSV-formatted file in the build directory which contains the configuration for stop consolidation. | *Optional* | | 2.5 | +| [streetGraph](#streetGraph) | `uri` | URI to the street graph object file for reading and writing. | *Optional* | | 2.0 | +| [subwayAccessTime](#subwayAccessTime) | `double` | Minutes necessary to reach stops served by trips on routes of route_type=1 (subway) from the street. | *Optional* | `2.0` | 1.5 | +| [transitModelTimeZone](#transitModelTimeZone) | `time-zone` | Time zone for the graph. | *Optional* | | 2.2 | +| [transitServiceEnd](#transitServiceEnd) | `duration` | Limit the import of transit services to the given end date. | *Optional* | `"P3Y"` | 2.0 | +| [transitServiceStart](#transitServiceStart) | `duration` | Limit the import of transit services to the given START date. | *Optional* | `"-P1Y"` | 2.0 | +| [writeCachedElevations](#writeCachedElevations) | `boolean` | Reusing elevation data from previous builds | *Optional* | `false` | 2.0 | +| [boardingLocationTags](#boardingLocationTags) | `string[]` | What OSM tags should be looked on for the source of matching stops to platforms and stops. | *Optional* | | 2.2 | +| [dataOverlay](sandbox/DataOverlay.md) | `object` | Config for the DataOverlay Sandbox module | *Optional* | | 2.2 | +| [dem](#dem) | `object[]` | Specify parameters for DEM extracts. | *Optional* | | 2.2 | +|       [elevationUnitMultiplier](#dem_0_elevationUnitMultiplier) | `double` | Specify a multiplier to convert elevation units from source to meters. Overrides the value specified in `demDefaults`. | *Optional* | `1.0` | 2.3 | +|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | +| demDefaults | `object` | Default properties for DEM extracts. | *Optional* | | 2.3 | +|    [elevationUnitMultiplier](#demDefaults_elevationUnitMultiplier) | `double` | Specify a multiplier to convert elevation units from source to meters. | *Optional* | `1.0` | 2.3 | +| [elevationBucket](#elevationBucket) | `object` | Used to download NED elevation tiles from the given AWS S3 bucket. | *Optional* | | na | +| [emissions](sandbox/Emissions.md) | `object` | Emissions configuration. | *Optional* | | 2.5 | +| [fares](sandbox/Fares.md) | `object` | Fare configuration. | *Optional* | | 2.0 | +| gtfsDefaults | `object` | The gtfsDefaults section allows you to specify default properties for GTFS files. | *Optional* | | 2.3 | +|    blockBasedInterlining | `boolean` | Whether to create stay-seated transfers in between two trips with the same block id. | *Optional* | `true` | 2.3 | +|    [discardMinTransferTimes](#gd_discardMinTransferTimes) | `boolean` | Should minimum transfer times in GTFS files be discarded. | *Optional* | `false` | 2.3 | +|    maxInterlineDistance | `integer` | Maximal distance between stops in meters that will connect consecutive trips that are made with same vehicle. | *Optional* | `200` | 2.3 | +|    removeRepeatedStops | `boolean` | Should consecutive identical stops be merged into one stop time entry. | *Optional* | `true` | 2.3 | +|    [stationTransferPreference](#gd_stationTransferPreference) | `enum` | Should there be some preference or aversion for transfers at stops that are part of a station. | *Optional* | `"allowed"` | 2.3 | +| islandPruning | `object` | Settings for fixing street graph connectivity errors | *Optional* | | 2.3 | +|    [adaptivePruningDistance](#islandPruning_adaptivePruningDistance) | `integer` | Search distance for analyzing islands in pruning. | *Optional* | `250` | 2.3 | +|    [adaptivePruningFactor](#islandPruning_adaptivePruningFactor) | `double` | Defines how much pruning thresholds grow maximally by distance. | *Optional* | `50.0` | 2.3 | +|    [islandWithStopsMaxSize](#islandPruning_islandWithStopsMaxSize) | `integer` | When a graph island with stops in it should be pruned. | *Optional* | `2` | 2.3 | +|    [islandWithoutStopsMaxSize](#islandPruning_islandWithoutStopsMaxSize) | `integer` | When a graph island without stops should be pruned. | *Optional* | `10` | 2.3 | +| [localFileNamePatterns](#localFileNamePatterns) | `object` | Patterns for matching OTP file types in the base directory | *Optional* | | 2.0 | +|    [dem](#lfp_dem) | `regexp` | Pattern for matching elevation DEM files. | *Optional* | `"(?i)\.tiff?$"` | 2.0 | +|    [gtfs](#lfp_gtfs) | `regexp` | Patterns for matching GTFS zip-files or directories. | *Optional* | `"(?i)gtfs"` | 2.0 | +|    [netex](#lfp_netex) | `regexp` | Patterns for matching NeTEx zip files or directories. | *Optional* | `"(?i)netex"` | 2.0 | +|    [osm](#lfp_osm) | `regexp` | Pattern for matching Open Street Map input files. | *Optional* | `"(?i)(\.pbf¦\.osm¦\.osm\.xml)$"` | 2.0 | +| netexDefaults | `object` | The netexDefaults section allows you to specify default properties for NeTEx files. | *Optional* | | 2.2 | +|    feedId | `string` | This field is used to identify the specific NeTEx feed. It is used instead of the feed_id field in GTFS file feed_info.txt. | *Optional* | `"NETEX"` | 2.2 | +|    [groupFilePattern](#nd_groupFilePattern) | `regexp` | Pattern for matching group NeTEx files. | *Optional* | `"(\w{3})-.*\.xml"` | 2.0 | +|    ignoreFareFrame | `boolean` | Ignore contents of the FareFrame | *Optional* | `false` | 2.3 | +|    [ignoreFilePattern](#nd_ignoreFilePattern) | `regexp` | Pattern for matching ignored files in a NeTEx bundle. | *Optional* | `"$^"` | 2.0 | +|    ignoreParking | `boolean` | Ignore Parking elements. | *Optional* | `true` | 2.6 | +|    noTransfersOnIsolatedStops | `boolean` | Whether we should allow transfers to and from StopPlaces marked with LimitedUse.ISOLATED | *Optional* | `false` | 2.2 | +|    [sharedFilePattern](#nd_sharedFilePattern) | `regexp` | Pattern for matching shared NeTEx files in a NeTEx bundle. | *Optional* | `"shared-data\.xml"` | 2.0 | +|    [sharedGroupFilePattern](#nd_sharedGroupFilePattern) | `regexp` | Pattern for matching shared group NeTEx files in a NeTEx bundle. | *Optional* | `"(\w{3})-.*-shared\.xml"` | 2.0 | +|    [ferryIdsNotAllowedForBicycle](#nd_ferryIdsNotAllowedForBicycle) | `string[]` | List ferries which do not allow bikes. | *Optional* | | 2.0 | +| [osm](#osm) | `object[]` | Configure properties for a given OpenStreetMap feed. | *Optional* | | 2.2 | +|       [osmTagMapping](#osm_0_osmTagMapping) | `enum` | The named set of mapping rules applied when parsing OSM tags. Overrides the value specified in `osmDefaults`. | *Optional* | `"default"` | 2.2 | +|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | +|       timeZone | `time-zone` | The timezone used to resolve opening hours in OSM data. Overrides the value specified in `osmDefaults`. | *Optional* | | 2.2 | +| osmDefaults | `object` | Default properties for OpenStreetMap feeds. | *Optional* | | 2.2 | +|    [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 | +| [transferRequests](RouteRequest.md) | `object[]` | Routing requests to use for pre-calculating stop-to-stop transfers. | *Optional* | | 2.1 | +| [transfers](#transfers) | `enum map of object` | Configures properties for transfer calculations. | *Optional* | | 2.7 | +| [transitFeeds](#transitFeeds) | `object[]` | Scan for transit data files | *Optional* | | 2.2 | +|    { object } | `object` | Nested object in array. The object type is determined by the parameters. | *Optional* | | 2.2 | +|       type = "gtfs" | `enum` | The feed input format. | *Required* | | 2.2 | +|       blockBasedInterlining | `boolean` | Whether to create stay-seated transfers in between two trips with the same block id. Overrides the value specified in `gtfsDefaults`. | *Optional* | `true` | 2.3 | +|       [discardMinTransferTimes](#tf_0_discardMinTransferTimes) | `boolean` | Should minimum transfer times in GTFS files be discarded. Overrides the value specified in `gtfsDefaults`. | *Optional* | `false` | 2.3 | +|       feedId | `string` | The unique ID for this feed. This overrides any feed ID defined within the feed itself. | *Optional* | | 2.2 | +|       maxInterlineDistance | `integer` | Maximal distance between stops in meters that will connect consecutive trips that are made with same vehicle. Overrides the value specified in `gtfsDefaults`. | *Optional* | `200` | 2.3 | +|       removeRepeatedStops | `boolean` | Should consecutive identical stops be merged into one stop time entry. Overrides the value specified in `gtfsDefaults`. | *Optional* | `true` | 2.3 | +|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | +|       [stationTransferPreference](#tf_0_stationTransferPreference) | `enum` | Should there be some preference or aversion for transfers at stops that are part of a station. Overrides the value specified in `gtfsDefaults`. | *Optional* | `"allowed"` | 2.3 | +|    { object } | `object` | Nested object in array. The object type is determined by the parameters. | *Optional* | | 2.2 | +|       type = "netex" | `enum` | The feed input format. | *Required* | | 2.2 | +|       feedId | `string` | This field is used to identify the specific NeTEx feed. It is used instead of the feed_id field in GTFS file feed_info.txt. | *Required* | | 2.2 | +|       [groupFilePattern](#tf_1_groupFilePattern) | `regexp` | Pattern for matching group NeTEx files. | *Optional* | `"(\w{3})-.*\.xml"` | 2.0 | +|       ignoreFareFrame | `boolean` | Ignore contents of the FareFrame | *Optional* | `false` | 2.3 | +|       [ignoreFilePattern](#tf_1_ignoreFilePattern) | `regexp` | Pattern for matching ignored files in a NeTEx bundle. | *Optional* | `"$^"` | 2.0 | +|       ignoreParking | `boolean` | Ignore Parking elements. | *Optional* | `true` | 2.6 | +|       noTransfersOnIsolatedStops | `boolean` | Whether we should allow transfers to and from StopPlaces marked with LimitedUse.ISOLATED | *Optional* | `false` | 2.2 | +|       [sharedFilePattern](#tf_1_sharedFilePattern) | `regexp` | Pattern for matching shared NeTEx files in a NeTEx bundle. | *Optional* | `"shared-data\.xml"` | 2.0 | +|       [sharedGroupFilePattern](#tf_1_sharedGroupFilePattern) | `regexp` | Pattern for matching shared group NeTEx files in a NeTEx bundle. | *Optional* | `"(\w{3})-.*-shared\.xml"` | 2.0 | +|       source | `uri` | The unique URI pointing to the data file. | *Required* | | 2.2 | +|       [ferryIdsNotAllowedForBicycle](#tf_1_ferryIdsNotAllowedForBicycle) | `string[]` | List ferries which do not allow bikes. | *Optional* | | 2.0 | +| [transitRouteToStationCentroid](#transitRouteToStationCentroid) | `feed-scoped-id[]` | List stations that should route to centroid. | *Optional* | | 2.7 | @@ -654,33 +654,6 @@ What OSM tags should be looked on for the source of matching stops to platforms [Detailed documentation](BoardingLocations.md) -

carsAllowedStopMaxTransferDurationsForMode

- -**Since version:** `2.7` ∙ **Type:** `enum map of duration` ∙ **Cardinality:** `Optional` -**Path:** / -**Enum keys:** `not-set` | `walk` | `bike` | `bike-to-park` | `bike-rental` | `scooter-rental` | `car` | `car-to-park` | `car-pickup` | `car-rental` | `car-hailing` | `flexible` - -This is used for specifying a `maxTransferDuration` value for bikes and cars to use with transfers between stops that have trips with cars. - -This is a special parameter that only works on transfers between stops that have trips that allow cars. -The duration can be set for either 'BIKE' or 'CAR'. -For cars, transfers are only calculated between stops that have trips that allow cars. -For cars, this overrides the default `maxTransferDuration`. -For bicycles, this indicates that additional transfers should be calculated with the specified duration between stops that have trips that allow cars. - -**Example** - -```JSON -// build-config.json -{ - "carsAllowedStopMaxTransferDurationsForMode": { - "CAR": "2h", - "BIKE": "3h" - } -} -``` - -

dem

**Since version:** `2.2` ∙ **Type:** `object[]` ∙ **Cardinality:** `Optional` @@ -980,6 +953,14 @@ 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. +

transfers

+ +**Since version:** `2.7` ∙ **Type:** `enum map of object` ∙ **Cardinality:** `Optional` +**Path:** / +**Enum keys:** `not-set` | `walk` | `bike` | `bike-to-park` | `bike-rental` | `scooter-rental` | `car` | `car-to-park` | `car-pickup` | `car-rental` | `car-hailing` | `flexible` + +Configures properties for transfer calculations. +

transitFeeds

**Since version:** `2.2` ∙ **Type:** `object[]` ∙ **Cardinality:** `Optional` From 21bb3078592482af160b9fb2bc57bc72dcf83c13 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Thu, 5 Dec 2024 10:00:21 +0200 Subject: [PATCH 07/23] Rename variables and small improvements. --- .../module/DirectTransferGenerator.java | 52 ++++++++++--------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index 62221750f68..57681d4893a 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -48,7 +48,7 @@ public class DirectTransferGenerator implements GraphBuilderModule { private static final Logger LOG = LoggerFactory.getLogger(DirectTransferGenerator.class); - private final Duration radiusByDuration; + private final Duration defaultMaxTransferDuration; private final List transferRequests; private final Map transferParametersForMode; @@ -60,13 +60,13 @@ public DirectTransferGenerator( Graph graph, TimetableRepository timetableRepository, DataImportIssueStore issueStore, - Duration radiusByDuration, + Duration defaultMaxTransferDuration, List transferRequests ) { this.graph = graph; this.timetableRepository = timetableRepository; this.issueStore = issueStore; - this.radiusByDuration = radiusByDuration; + this.defaultMaxTransferDuration = defaultMaxTransferDuration; this.transferRequests = transferRequests; this.transferParametersForMode = Collections.emptyMap(); } @@ -75,14 +75,14 @@ public DirectTransferGenerator( Graph graph, TimetableRepository timetableRepository, DataImportIssueStore issueStore, - Duration radiusByDuration, + Duration defaultMaxTransferDuration, List transferRequests, Map transferParametersForMode ) { this.graph = graph; this.timetableRepository = timetableRepository; this.issueStore = issueStore; - this.radiusByDuration = radiusByDuration; + this.defaultMaxTransferDuration = defaultMaxTransferDuration; this.transferRequests = transferRequests; this.transferParametersForMode = transferParametersForMode; } @@ -116,10 +116,10 @@ public void buildGraph() { HashMultimap.create() ); - List filteredTransferRequests = new ArrayList(); + List defaultTransferRequests = new ArrayList(); List carsAllowedStopTransferRequests = new ArrayList(); /* The linker will use streets if they are available, or straight-line distance otherwise. */ - HashMap nearbyStopFinders = new HashMap<>(); + HashMap defaultNearbyStopFinders = new HashMap<>(); /* These are used for calculating transfers only between carsAllowedStops. */ HashMap carsAllowedStopNearbyStopFinders = new HashMap<>(); @@ -130,28 +130,32 @@ public void buildGraph() { if (transferParameters != null) { // Disable normal transfer calculations if disableDefaultTransfers is set in the build config. if (!transferParameters.disableDefaultTransfers()) { - filteredTransferRequests.add(transferProfile); + defaultTransferRequests.add(transferProfile); // Set mode-specific maxTransferDuration, if it is set in the build config. - Duration maxTransferDuration = radiusByDuration; - if (transferParameters.maxTransferDuration() != Duration.ZERO) { - maxTransferDuration = transferParameters.maxTransferDuration(); + Duration maxTransferDuration = transferParameters.maxTransferDuration(); + if (maxTransferDuration == Duration.ZERO) { + maxTransferDuration = defaultMaxTransferDuration; } - nearbyStopFinders.put(mode, createNearbyStopFinder(maxTransferDuration, Set.of())); + defaultNearbyStopFinders.put(mode, createNearbyStopFinder(maxTransferDuration, Set.of())); } // Create transfers between carsAllowedStops for the specific mode if carsAllowedStopMaxTransferDuration is set in the build config. - if (transferParameters.carsAllowedStopMaxTransferDuration() != Duration.ZERO) { + Duration carsAllowedStopMaxTransferDuration = transferParameters.carsAllowedStopMaxTransferDuration(); + if (carsAllowedStopMaxTransferDuration != Duration.ZERO) { carsAllowedStopTransferRequests.add(transferProfile); carsAllowedStopNearbyStopFinders.put( mode, createNearbyStopFinder( - transferParameters.carsAllowedStopMaxTransferDuration(), + carsAllowedStopMaxTransferDuration, Collections.unmodifiableSet(carsAllowedStops) ) ); } } else { - filteredTransferRequests.add(transferProfile); - nearbyStopFinders.put(mode, createNearbyStopFinder(radiusByDuration, Set.of())); + defaultTransferRequests.add(transferProfile); + defaultNearbyStopFinders.put( + mode, + createNearbyStopFinder(defaultMaxTransferDuration, Set.of()) + ); } } @@ -170,10 +174,10 @@ public void buildGraph() { LOG.debug("Linking stop '{}' {}", stop, ts0); - for (RouteRequest transferProfile : filteredTransferRequests) { + for (RouteRequest transferProfile : defaultTransferRequests) { StreetMode mode = transferProfile.journey().transfer().mode(); findNearbyStops( - nearbyStopFinders.get(mode), + defaultNearbyStopFinders.get(mode), ts0, transferProfile, stop, @@ -181,11 +185,11 @@ public void buildGraph() { ); } if (OTPFeature.FlexRouting.isOn()) { - for (RouteRequest transferProfile : filteredTransferRequests) { + for (RouteRequest transferProfile : defaultTransferRequests) { StreetMode mode = transferProfile.journey().transfer().mode(); // This code is for finding transfers from AreaStops to Stops, transfers // from Stops to AreaStops and between Stops are already covered above. - for (NearbyStop sd : nearbyStopFinders + for (NearbyStop sd : defaultNearbyStopFinders .get(mode) .findNearbyStops(ts0, transferProfile, transferProfile.journey().transfer(), true)) { // Skip the origin stop, loop transfers are not needed. @@ -204,11 +208,9 @@ public void buildGraph() { } // This calculates transfers between stops that can use trips with cars. - for (RouteRequest transferProfile : carsAllowedStopTransferRequests) { - StreetMode mode = transferProfile.journey().transfer().mode(); - if ( - carsAllowedStops.contains(ts0) && carsAllowedStopNearbyStopFinders.containsKey(mode) - ) { + if (carsAllowedStops.contains(ts0)) { + for (RouteRequest transferProfile : carsAllowedStopTransferRequests) { + StreetMode mode = transferProfile.journey().transfer().mode(); findNearbyStops( carsAllowedStopNearbyStopFinders.get(mode), ts0, From 573078a462bf9dfd3aafbacc0f89336d43f79338 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Thu, 5 Dec 2024 10:24:05 +0200 Subject: [PATCH 08/23] Change test. --- .../graph_builder/module/DirectTransferGeneratorTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java index 8cd8dae3b6f..2d92f83ea6c 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java @@ -404,7 +404,6 @@ public void testBikeRequestWithPatternsAndWithCarsAllowedPatterns() { TransferParameters.Builder transferParametersBuilder = new TransferParameters.Builder(); transferParametersBuilder.withCarsAllowedStopMaxTransferDuration(Duration.ofMinutes(120)); - transferParametersBuilder.withDisableDefaultTransfers(true); Map transferParametersForMode = new HashMap<>(); transferParametersForMode.put(StreetMode.BIKE, transferParametersBuilder.build()); From 3fd5bf55577a88efc6d9fe56e176dd5ba0bb44cc Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Thu, 5 Dec 2024 10:58:51 +0200 Subject: [PATCH 09/23] Add documentation for build config fields. --- .../config/buildconfig/TransferConfig.java | 24 ++++++++++++++++++- .../buildconfig/TransferParametersMapper.java | 15 +++++++++--- doc/user/BuildConfiguration.md | 24 +++++++++++++++++-- 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferConfig.java index 3f171238c05..aa70a2788c4 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferConfig.java @@ -14,7 +14,29 @@ public static Map map(NodeAdapter root, String p return root .of(parameterName) .since(V2_7) - .summary("Configures properties for transfer calculations.") + .summary("Configures mode-specific properties for transfer calculations.") + .description( + """ +This field enables configuring mode-specific parameters for transfer calculations. +To configure mode-specific parameters, the modes should also be used in the `transferRequests` field in the build config. + +**Example** + +// build-config.json +{ + "transfers": { + "CAR": { + "disableDefaultTransfers": true, + "carsAllowedStopMaxTransferDuration": "3h" + }, + "BIKE": { + "maxTransferDuration": "30m", + "carsAllowedStopMaxTransferDuration": "3h" + } + } +} +""" + ) .asEnumMap(StreetMode.class, TransferParametersMapper::map, new EnumMap<>(StreetMode.class)); } } diff --git a/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferParametersMapper.java b/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferParametersMapper.java index caf35f0155c..d14858d79c4 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferParametersMapper.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferParametersMapper.java @@ -12,21 +12,30 @@ public static TransferParameters map(NodeAdapter c) { builder.withMaxTransferDuration( c .of("maxTransferDuration") - .summary("") + .summary("This overwrites the `maxTransferDuration` for the given mode.") .since(V2_7) .asDuration(TransferParameters.DEFAULT_MAX_TRANSFER_DURATION) ); builder.withCarsAllowedStopMaxTransferDuration( c .of("carsAllowedStopMaxTransferDuration") - .summary("") + .summary( + "This is used for specifying a `maxTransferDuration` value to use with transfers between stops that have trips with cars." + ) + .description( + """ +This parameter configures additional transfers to be calculated for the specified mode only between stops that have trips with cars. +The transfers are calculated for the mode in a range based on the given duration. +By default, these transfers are not calculated for the specified mode. +""" + ) .since(V2_7) .asDuration(TransferParameters.DEFAULT_CARS_ALLOWED_STOP_MAX_TRANSFER_DURATION) ); builder.withDisableDefaultTransfers( c .of("disableDefaultTransfers") - .summary("") + .summary("This disables default transfer calculations.") .since(V2_7) .asBoolean(TransferParameters.DEFAULT_DISABLE_DEFAULT_TRANSFERS) ); diff --git a/doc/user/BuildConfiguration.md b/doc/user/BuildConfiguration.md index 413d8a8c7a1..dee3a266f8a 100644 --- a/doc/user/BuildConfiguration.md +++ b/doc/user/BuildConfiguration.md @@ -91,7 +91,7 @@ Sections follow that describe particular settings in more depth. |    [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 | | [transferRequests](RouteRequest.md) | `object[]` | Routing requests to use for pre-calculating stop-to-stop transfers. | *Optional* | | 2.1 | -| [transfers](#transfers) | `enum map of object` | Configures properties for transfer calculations. | *Optional* | | 2.7 | +| [transfers](#transfers) | `enum map of object` | Configures mode-specific properties for transfer calculations. | *Optional* | | 2.7 | | [transitFeeds](#transitFeeds) | `object[]` | Scan for transit data files | *Optional* | | 2.2 | |    { object } | `object` | Nested object in array. The object type is determined by the parameters. | *Optional* | | 2.2 | |       type = "gtfs" | `enum` | The feed input format. | *Required* | | 2.2 | @@ -959,7 +959,27 @@ The named set of mapping rules applied when parsing OSM tags. **Path:** / **Enum keys:** `not-set` | `walk` | `bike` | `bike-to-park` | `bike-rental` | `scooter-rental` | `car` | `car-to-park` | `car-pickup` | `car-rental` | `car-hailing` | `flexible` -Configures properties for transfer calculations. +Configures mode-specific properties for transfer calculations. + +This field enables configuring mode-specific parameters for transfer calculations. +To configure mode-specific parameters, the modes should also be used in the `transferRequests` field in the build config. + +**Example** + +// build-config.json +{ + "transfers": { + "CAR": { + "disableDefaultTransfers": true, + "carsAllowedStopMaxTransferDuration": "3h" + }, + "BIKE": { + "maxTransferDuration": "30m", + "carsAllowedStopMaxTransferDuration": "3h" + } + } +} +

transitFeeds

From 35c98bc04ee093488ba4303e2f8144165ad5572c Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Thu, 5 Dec 2024 13:11:41 +0200 Subject: [PATCH 10/23] Add logging for transfers. --- .../graph_builder/module/DirectTransferGenerator.java | 11 +++++++++++ .../graph_builder/module/TransferParameters.java | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index 57681d4893a..7dc69946d3a 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -102,6 +102,17 @@ public void buildGraph() { .filter(TransitStopVertex.class::isInstance) .collect(Collectors.toSet()); + LOG.info("Creating transfers based on requests:"); + transferRequests.forEach(transferProfile -> LOG.info(transferProfile.toString())); + if (transferParametersForMode.isEmpty()) { + LOG.info("No mode-specific transfer configurations provided."); + } else { + LOG.info("Using transfer configurations for modes:"); + transferParametersForMode.forEach((mode, transferParameters) -> + LOG.info(mode + ": " + transferParameters) + ); + } + ProgressTracker progress = ProgressTracker.track( "Create transfer edges for stops", 1000, diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/TransferParameters.java b/application/src/main/java/org/opentripplanner/graph_builder/module/TransferParameters.java index 962d8579ddd..89553eb1e39 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/TransferParameters.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/TransferParameters.java @@ -1,6 +1,7 @@ package org.opentripplanner.graph_builder.module; import java.time.Duration; +import org.opentripplanner.utils.tostring.ToStringBuilder; public record TransferParameters( Duration maxTransferDuration, @@ -19,6 +20,15 @@ public record TransferParameters( ); } + public String toString() { + return ToStringBuilder + .of(getClass()) + .addDuration("maxTransferDuration", maxTransferDuration) + .addDuration("carsAllowedStopMaxTransferDuration", carsAllowedStopMaxTransferDuration) + .addBool("disableDefaultTransfers", disableDefaultTransfers) + .toString(); + } + public static class Builder { private Duration maxTransferDuration; From 97238a3446d2c44f979a4ec36bffd333166eb996 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Thu, 5 Dec 2024 15:35:35 +0200 Subject: [PATCH 11/23] Fix merge issues. --- .../module/DirectTransferGenerator.java | 91 +++++++------------ 1 file changed, 35 insertions(+), 56 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index 6349ab122fa..f5978513080 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -92,6 +92,15 @@ public void buildGraph() { /* Initialize transit model index which is needed by the nearby stop finder. */ timetableRepository.index(); + /* The linker will use streets if they are available, or straight-line distance otherwise. */ + NearbyStopFinder nearbyStopFinder = createNearbyStopFinder( + defaultMaxTransferDuration, + Set.of() + ); + HashMap defaultNearbyStopFinders = new HashMap<>(); + /* These are used for calculating transfers only between carsAllowedStops. */ + HashMap carsAllowedStopNearbyStopFinders = new HashMap<>(); + List stops = graph.getVerticesOfType(TransitStopVertex.class); Set carsAllowedStops = timetableRepository .getStopLocationsUsedForCarsAllowedTrips() @@ -130,20 +139,6 @@ public void buildGraph() { List defaultTransferRequests = new ArrayList<>(); List carsAllowedStopTransferRequests = new ArrayList<>(); List flexTransferRequests = new ArrayList<>(); - // Flex transfer requests only use the WALK mode. - if (OTPFeature.FlexRouting.isOn()) { - flexTransferRequests.addAll( - transferRequests - .stream() - .filter(transferProfile -> transferProfile.journey().transfer().mode() == StreetMode.WALK) - .toList() - ); - } - - /* The linker will use streets if they are available, or straight-line distance otherwise. */ - HashMap defaultNearbyStopFinders = new HashMap<>(); - /* These are used for calculating transfers only between carsAllowedStops. */ - HashMap carsAllowedStopNearbyStopFinders = new HashMap<>(); // Parse transfer parameters. for (RouteRequest transferProfile : transferRequests) { @@ -155,10 +150,14 @@ public void buildGraph() { defaultTransferRequests.add(transferProfile); // Set mode-specific maxTransferDuration, if it is set in the build config. Duration maxTransferDuration = transferParameters.maxTransferDuration(); - if (maxTransferDuration == Duration.ZERO) { - maxTransferDuration = defaultMaxTransferDuration; + if (maxTransferDuration != Duration.ZERO) { + defaultNearbyStopFinders.put( + mode, + createNearbyStopFinder(maxTransferDuration, Set.of()) + ); + } else { + defaultNearbyStopFinders.put(mode, nearbyStopFinder); } - defaultNearbyStopFinders.put(mode, createNearbyStopFinder(maxTransferDuration, Set.of())); } // Create transfers between carsAllowedStops for the specific mode if carsAllowedStopMaxTransferDuration is set in the build config. Duration carsAllowedStopMaxTransferDuration = transferParameters.carsAllowedStopMaxTransferDuration(); @@ -174,10 +173,20 @@ public void buildGraph() { } } else { defaultTransferRequests.add(transferProfile); - defaultNearbyStopFinders.put( - mode, - createNearbyStopFinder(defaultMaxTransferDuration, Set.of()) - ); + defaultNearbyStopFinders.put(mode, nearbyStopFinder); + } + } + + // Flex transfer requests only use the WALK mode. + if (OTPFeature.FlexRouting.isOn()) { + flexTransferRequests.addAll( + transferRequests + .stream() + .filter(transferProfile -> transferProfile.journey().transfer().mode() == StreetMode.WALK) + .toList() + ); + if (!defaultNearbyStopFinders.containsKey(StreetMode.WALK)) { + defaultNearbyStopFinders.put(StreetMode.WALK, nearbyStopFinder); } } @@ -196,34 +205,8 @@ public void buildGraph() { LOG.debug("Linking stop '{}' {}", stop, ts0); - /* for (RouteRequest transferProfile : defaultTransferRequests) { - StreetMode mode = transferProfile.journey().transfer().mode(); - findNearbyStops( - defaultNearbyStopFinders.get(mode), - ts0, - transferProfile, - stop, - distinctTransfers - ); - } - if (OTPFeature.FlexRouting.isOn()) { - for (RouteRequest transferProfile : defaultTransferRequests) { - StreetMode mode = transferProfile.journey().transfer().mode(); - // This code is for finding transfers from AreaStops to Stops, transfers - // from Stops to AreaStops and between Stops are already covered above. - for (NearbyStop sd : defaultNearbyStopFinders - .get(mode) - .findNearbyStops(ts0, transferProfile, transferProfile.journey().transfer(), true)) { - // Skip the origin stop, loop transfers are not needed. - if (sd.stop == stop) { - continue; - } - if (sd.stop instanceof RegularStop) { - continue; - } */ - // Calculate default transfers. - for (RouteRequest transferProfile : transferRequests) { + for (RouteRequest transferProfile : defaultTransferRequests) { StreetMode mode = transferProfile.journey().transfer().mode(); findNearbyStops( defaultNearbyStopFinders.get(mode), @@ -239,12 +222,9 @@ public void buildGraph() { StreetMode mode = transferProfile.journey().transfer().mode(); // This code is for finding transfers from AreaStops to Stops, transfers // from Stops to AreaStops and between Stops are already covered above. - for (NearbyStop sd : defaultNearbyStopFinders.get(mode).findNearbyStops( - ts0, - transferProfile, - transferProfile.journey().transfer(), - true - )) { + for (NearbyStop sd : defaultNearbyStopFinders + .get(mode) + .findNearbyStops(ts0, transferProfile, transferProfile.journey().transfer(), true)) { // Skip the origin stop, loop transfers are not needed. if (sd.stop == stop) { continue; @@ -266,8 +246,7 @@ public void buildGraph() { } } } - - // This calculates transfers between stops that can use trips with cars. + // Calculate transfers between stops that can use trips with cars if configured. if (carsAllowedStops.contains(ts0)) { for (RouteRequest transferProfile : carsAllowedStopTransferRequests) { StreetMode mode = transferProfile.journey().transfer().mode(); From 41a0674970e734bed4b4152002e94dbef72e0a4b Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 10 Dec 2024 12:02:37 +0200 Subject: [PATCH 12/23] Add tests and small improvements. --- .../module/DirectTransferGenerator.java | 2 +- .../transit/service/TimetableRepository.java | 9 ++ .../module/DirectTransferGeneratorTest.java | 143 +++++++++++++++--- 3 files changed, 136 insertions(+), 18 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index f5978513080..e679bb52ca5 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -145,7 +145,7 @@ public void buildGraph() { StreetMode mode = transferProfile.journey().transfer().mode(); TransferParameters transferParameters = transferParametersForMode.get(mode); if (transferParameters != null) { - // Disable normal transfer calculations if disableDefaultTransfers is set in the build config. + // Disable normal transfer calculations for the specific mode, if disableDefaultTransfers is set in the build config. if (!transferParameters.disableDefaultTransfers()) { defaultTransferRequests.add(transferProfile); // Set mode-specific maxTransferDuration, if it is set in the build config. diff --git a/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java b/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java index b81dd7d4f14..d83c3f9b996 100644 --- a/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java +++ b/application/src/main/java/org/opentripplanner/transit/service/TimetableRepository.java @@ -35,6 +35,7 @@ import org.opentripplanner.model.transfer.DefaultTransferService; import org.opentripplanner.routing.algorithm.raptoradapter.transit.TransitLayer; import org.opentripplanner.routing.algorithm.raptoradapter.transit.mappers.TransitLayerUpdater; +import org.opentripplanner.routing.api.request.StreetMode; import org.opentripplanner.routing.impl.DelegatingTransitAlertServiceImpl; import org.opentripplanner.routing.services.TransitAlertService; import org.opentripplanner.routing.util.ConcurrentPublished; @@ -433,6 +434,14 @@ public Collection getTransfersByStop(StopLocation stop) { return transfersByStop.get(stop); } + public Collection getTransfersByMode(StreetMode mode) { + return transfersByStop + .values() + .stream() + .filter(pathTransfer -> pathTransfer.getModes().contains(mode)) + .toList(); + } + public SiteRepository getSiteRepository() { return siteRepository; } diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java index 5d974c200d7..8446a87f21a 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java @@ -280,21 +280,9 @@ public void testPathTransfersWithModesForMultipleRequestsWithPatterns() { ) .buildGraph(); - var walkTransfers = timetableRepository - .getAllPathTransfers() - .stream() - .filter(pathTransfer -> pathTransfer.getModes().contains(StreetMode.WALK)) - .toList(); - var bikeTransfers = timetableRepository - .getAllPathTransfers() - .stream() - .filter(pathTransfer -> pathTransfer.getModes().contains(StreetMode.BIKE)) - .toList(); - var carTransfers = timetableRepository - .getAllPathTransfers() - .stream() - .filter(pathTransfer -> pathTransfer.getModes().contains(StreetMode.CAR)) - .toList(); + var walkTransfers = timetableRepository.getTransfersByMode(StreetMode.WALK); + var bikeTransfers = timetableRepository.getTransfersByMode(StreetMode.BIKE); + var carTransfers = timetableRepository.getTransfersByMode(StreetMode.CAR); assertTransfers( walkTransfers, @@ -434,15 +422,32 @@ public void testMultipleRequestsWithPatternsAndWithCarsAllowedPatterns() { ) .buildGraph(); + var walkTransfers = timetableRepository.getTransfersByMode(StreetMode.WALK); + var bikeTransfers = timetableRepository.getTransfersByMode(StreetMode.BIKE); + var carTransfers = timetableRepository.getTransfersByMode(StreetMode.CAR); + assertTransfers( - timetableRepository.getAllPathTransfers(), + walkTransfers, tr(S0, 100, List.of(V0, V11), S11), tr(S0, 100, List.of(V0, V21), S21), - tr(S0, 200, List.of(V0, V12), S12), tr(S11, 100, List.of(V11, V21), S21), + tr(S0, 200, List.of(V0, V12), S12), + tr(S11, 100, List.of(V11, V12), S12) + ); + assertTransfers( + bikeTransfers, + tr(S0, 100, List.of(V0, V11), S11), + tr(S0, 100, List.of(V0, V21), S21), tr(S11, 110, List.of(V11, V22), S22), + tr(S0, 200, List.of(V0, V12), S12), tr(S11, 100, List.of(V11, V12), S12) ); + assertTransfers( + carTransfers, + tr(S0, 100, List.of(V0, V11), S11), + tr(S0, 200, List.of(V0, V12), S12), + tr(S0, 100, List.of(V0, V21), S21) + ); } @Test @@ -518,6 +523,110 @@ public void testBikeRequestWithPatternsAndWithCarsAllowedPatternsWithoutCarInTra ); } + @Test + public void testDisableDefaultTransfersForMode() { + var reqWalk = new RouteRequest(); + reqWalk.journey().transfer().setMode(StreetMode.WALK); + + var reqBike = new RouteRequest(); + reqBike.journey().transfer().setMode(StreetMode.BIKE); + + var reqCar = new RouteRequest(); + reqCar.journey().transfer().setMode(StreetMode.CAR); + + var transferRequests = List.of(reqWalk, reqBike, reqCar); + + var otpModel = model(true, false, false, true); + var graph = otpModel.graph(); + graph.hasStreets = true; + var timetableRepository = otpModel.timetableRepository(); + + TransferParameters.Builder transferParametersBuilderBike = new TransferParameters.Builder(); + transferParametersBuilderBike.withDisableDefaultTransfers(true); + TransferParameters.Builder transferParametersBuilderCar = new TransferParameters.Builder(); + transferParametersBuilderCar.withDisableDefaultTransfers(true); + Map transferParametersForMode = new HashMap<>(); + transferParametersForMode.put(StreetMode.BIKE, transferParametersBuilderBike.build()); + transferParametersForMode.put(StreetMode.CAR, transferParametersBuilderCar.build()); + + new DirectTransferGenerator( + graph, + timetableRepository, + DataImportIssueStore.NOOP, + MAX_TRANSFER_DURATION, + transferRequests, + transferParametersForMode + ) + .buildGraph(); + + var walkTransfers = timetableRepository.getTransfersByMode(StreetMode.WALK); + var bikeTransfers = timetableRepository.getTransfersByMode(StreetMode.BIKE); + var carTransfers = timetableRepository.getTransfersByMode(StreetMode.CAR); + + assertTransfers( + walkTransfers, + tr(S0, 100, List.of(V0, V11), S11), + tr(S0, 100, List.of(V0, V21), S21), + tr(S11, 100, List.of(V11, V21), S21), + tr(S0, 200, List.of(V0, V12), S12), + tr(S11, 100, List.of(V11, V12), S12) + ); + assertTransfers(bikeTransfers); + assertTransfers(carTransfers); + } + + @Test + public void testMaxTransferDurationForMode() { + var reqWalk = new RouteRequest(); + reqWalk.journey().transfer().setMode(StreetMode.WALK); + + var reqBike = new RouteRequest(); + reqBike.journey().transfer().setMode(StreetMode.BIKE); + + var transferRequests = List.of(reqWalk, reqBike); + + var otpModel = model(true, false, false, true); + var graph = otpModel.graph(); + graph.hasStreets = true; + var timetableRepository = otpModel.timetableRepository(); + + TransferParameters.Builder transferParametersBuilderWalk = new TransferParameters.Builder(); + transferParametersBuilderWalk.withMaxTransferDuration(Duration.ofSeconds(100)); + TransferParameters.Builder transferParametersBuilderBike = new TransferParameters.Builder(); + transferParametersBuilderBike.withMaxTransferDuration(Duration.ofSeconds(21)); + Map transferParametersForMode = new HashMap<>(); + transferParametersForMode.put(StreetMode.WALK, transferParametersBuilderWalk.build()); + transferParametersForMode.put(StreetMode.BIKE, transferParametersBuilderBike.build()); + + new DirectTransferGenerator( + graph, + timetableRepository, + DataImportIssueStore.NOOP, + MAX_TRANSFER_DURATION, + transferRequests, + transferParametersForMode + ) + .buildGraph(); + + var walkTransfers = timetableRepository.getTransfersByMode(StreetMode.WALK); + var bikeTransfers = timetableRepository.getTransfersByMode(StreetMode.BIKE); + var carTransfers = timetableRepository.getTransfersByMode(StreetMode.CAR); + + assertTransfers( + walkTransfers, + tr(S0, 100, List.of(V0, V11), S11), + tr(S0, 100, List.of(V0, V21), S21), + tr(S11, 100, List.of(V11, V21), S21), + tr(S11, 100, List.of(V11, V12), S12) + ); + assertTransfers( + bikeTransfers, + tr(S0, 100, List.of(V0, V11), S11), + tr(S0, 100, List.of(V0, V21), S21) + ); + assertTransfers(carTransfers); + } + private TestOtpModel model(boolean addPatterns) { return model(addPatterns, false); } From 2e048f9e8a3032f4acfe396b97820a487fe657f2 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Mon, 16 Dec 2024 15:47:14 +0200 Subject: [PATCH 13/23] Refactor transfer parameter parsing. --- .../module/DirectTransferGenerator.java | 116 ++++++++++-------- 1 file changed, 67 insertions(+), 49 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index 88b9cc5b19b..90cab4019e3 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -140,55 +140,16 @@ public void buildGraph() { List carsAllowedStopTransferRequests = new ArrayList<>(); List flexTransferRequests = new ArrayList<>(); - // Parse transfer parameters. - for (RouteRequest transferProfile : transferRequests) { - StreetMode mode = transferProfile.journey().transfer().mode(); - TransferParameters transferParameters = transferParametersForMode.get(mode); - if (transferParameters != null) { - // Disable normal transfer calculations for the specific mode, if disableDefaultTransfers is set in the build config. - if (!transferParameters.disableDefaultTransfers()) { - defaultTransferRequests.add(transferProfile); - // Set mode-specific maxTransferDuration, if it is set in the build config. - Duration maxTransferDuration = transferParameters.maxTransferDuration(); - if (maxTransferDuration != Duration.ZERO) { - defaultNearbyStopFinders.put( - mode, - createNearbyStopFinder(maxTransferDuration, Set.of()) - ); - } else { - defaultNearbyStopFinders.put(mode, nearbyStopFinder); - } - } - // Create transfers between carsAllowedStops for the specific mode if carsAllowedStopMaxTransferDuration is set in the build config. - Duration carsAllowedStopMaxTransferDuration = transferParameters.carsAllowedStopMaxTransferDuration(); - if (carsAllowedStopMaxTransferDuration != Duration.ZERO) { - carsAllowedStopTransferRequests.add(transferProfile); - carsAllowedStopNearbyStopFinders.put( - mode, - createNearbyStopFinder( - carsAllowedStopMaxTransferDuration, - Collections.unmodifiableSet(carsAllowedStops) - ) - ); - } - } else { - defaultTransferRequests.add(transferProfile); - defaultNearbyStopFinders.put(mode, nearbyStopFinder); - } - } - - // Flex transfer requests only use the WALK mode. - if (OTPFeature.FlexRouting.isOn()) { - flexTransferRequests.addAll( - transferRequests - .stream() - .filter(transferProfile -> transferProfile.journey().transfer().mode() == StreetMode.WALK) - .toList() - ); - if (!defaultNearbyStopFinders.containsKey(StreetMode.WALK)) { - defaultNearbyStopFinders.put(StreetMode.WALK, nearbyStopFinder); - } - } + // Parse the transfer configuration from the parameters given in the build config. + parseTransferParameters( + defaultTransferRequests, + carsAllowedStopTransferRequests, + flexTransferRequests, + defaultNearbyStopFinders, + carsAllowedStopNearbyStopFinders, + nearbyStopFinder, + carsAllowedStops + ); stops .stream() @@ -332,6 +293,63 @@ private NearbyStopFinder createNearbyStopFinder( } } + private void parseTransferParameters( + List defaultTransferRequests, + List carsAllowedStopTransferRequests, + List flexTransferRequests, + HashMap defaultNearbyStopFinders, + HashMap carsAllowedStopNearbyStopFinders, + NearbyStopFinder nearbyStopFinder, + Set carsAllowedStops + ) { + for (RouteRequest transferProfile : transferRequests) { + StreetMode mode = transferProfile.journey().transfer().mode(); + TransferParameters transferParameters = transferParametersForMode.get(mode); + if (transferParameters != null) { + // Disable normal transfer calculations for the specific mode, if disableDefaultTransfers is set in the build config. + // WALK mode transfers can not be disabled. For example, flex transfers need them. + if (!transferParameters.disableDefaultTransfers() || mode == StreetMode.WALK) { + defaultTransferRequests.add(transferProfile); + // Set mode-specific maxTransferDuration, if it is set in the build config. + Duration maxTransferDuration = transferParameters.maxTransferDuration(); + if (maxTransferDuration != Duration.ZERO) { + defaultNearbyStopFinders.put( + mode, + createNearbyStopFinder(maxTransferDuration, Set.of()) + ); + } else { + defaultNearbyStopFinders.put(mode, nearbyStopFinder); + } + } + // Create transfers between carsAllowedStops for the specific mode if carsAllowedStopMaxTransferDuration is set in the build config. + Duration carsAllowedStopMaxTransferDuration = transferParameters.carsAllowedStopMaxTransferDuration(); + if (carsAllowedStopMaxTransferDuration != Duration.ZERO) { + carsAllowedStopTransferRequests.add(transferProfile); + carsAllowedStopNearbyStopFinders.put( + mode, + createNearbyStopFinder( + carsAllowedStopMaxTransferDuration, + Collections.unmodifiableSet(carsAllowedStops) + ) + ); + } + } else { + defaultTransferRequests.add(transferProfile); + defaultNearbyStopFinders.put(mode, nearbyStopFinder); + } + } + + // Flex transfer requests only use the WALK mode. + if (OTPFeature.FlexRouting.isOn()) { + flexTransferRequests.addAll( + transferRequests + .stream() + .filter(transferProfile -> transferProfile.journey().transfer().mode() == StreetMode.WALK) + .toList() + ); + } + } + private void findNearbyStops( NearbyStopFinder nearbyStopFinder, TransitStopVertex ts0, From c90c3ecb8acdaa76792571fdd38f1c26ee6126f4 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Mon, 16 Dec 2024 15:52:07 +0200 Subject: [PATCH 14/23] Remove duplicate test. --- .../module/DirectTransferGeneratorTest.java | 33 ------------------- 1 file changed, 33 deletions(-) diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java index fae47da7b0a..7d75aecf89c 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java @@ -238,39 +238,6 @@ public void testMultipleRequestsWithPatterns() { graph.hasStreets = true; var timetableRepository = model.timetableRepository(); - new DirectTransferGenerator( - graph, - timetableRepository, - DataImportIssueStore.NOOP, - MAX_TRANSFER_DURATION, - transferRequests - ) - .buildGraph(); - - assertTransfers( - timetableRepository.getAllPathTransfers(), - tr(S0, 100, List.of(V0, V11), S11), - tr(S0, 100, List.of(V0, V21), S21), - tr(S11, 100, List.of(V11, V21), S21), - tr(S11, 110, List.of(V11, V22), S22) - ); - } - - @Test - public void testPathTransfersWithModesForMultipleRequestsWithPatterns() { - var reqWalk = new RouteRequest(); - reqWalk.journey().transfer().setMode(StreetMode.WALK); - - var reqBike = new RouteRequest(); - reqBike.journey().transfer().setMode(StreetMode.BIKE); - - var transferRequests = List.of(reqWalk, reqBike); - - TestOtpModel model = model(true); - var graph = model.graph(); - graph.hasStreets = true; - var timetableRepository = model.timetableRepository(); - new DirectTransferGenerator( graph, timetableRepository, From 30cc9251a31bb1a7ee88e15a67b9f244c0d70b47 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 17 Dec 2024 09:47:50 +0200 Subject: [PATCH 15/23] Simplify implementation by removing changes to StreetNearbyStopFinder. --- .../module/DirectTransferGenerator.java | 141 ++++++++---------- .../nearbystops/StreetNearbyStopFinder.java | 28 +--- .../StreetNearbyStopFinderTest.java | 72 --------- 3 files changed, 61 insertions(+), 180 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index 90cab4019e3..6b7c53efc15 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -93,23 +93,13 @@ public void buildGraph() { timetableRepository.index(); /* The linker will use streets if they are available, or straight-line distance otherwise. */ - NearbyStopFinder nearbyStopFinder = createNearbyStopFinder( - defaultMaxTransferDuration, - Set.of() - ); + NearbyStopFinder nearbyStopFinder = createNearbyStopFinder(defaultMaxTransferDuration); HashMap defaultNearbyStopFinders = new HashMap<>(); /* These are used for calculating transfers only between carsAllowedStops. */ HashMap carsAllowedStopNearbyStopFinders = new HashMap<>(); List stops = graph.getVerticesOfType(TransitStopVertex.class); - Set carsAllowedStops = timetableRepository - .getStopLocationsUsedForCarsAllowedTrips() - .stream() - .map(StopLocation::getId) - .map(graph::getStopVertexForStopId) - // filter out null values if no TransitStopVertex is found for ID - .filter(TransitStopVertex.class::isInstance) - .collect(Collectors.toSet()); + Set carsAllowedStops = timetableRepository.getStopLocationsUsedForCarsAllowedTrips(); LOG.info("Creating transfers based on requests:"); transferRequests.forEach(transferProfile -> LOG.info(transferProfile.toString())); @@ -147,8 +137,7 @@ public void buildGraph() { flexTransferRequests, defaultNearbyStopFinders, carsAllowedStopNearbyStopFinders, - nearbyStopFinder, - carsAllowedStops + nearbyStopFinder ); stops @@ -169,14 +158,29 @@ public void buildGraph() { // Calculate default transfers. for (RouteRequest transferProfile : defaultTransferRequests) { StreetMode mode = transferProfile.journey().transfer().mode(); - findNearbyStops( - defaultNearbyStopFinders.get(mode), - ts0, - transferProfile, - stop, - distinctTransfers, - mode - ); + for (NearbyStop sd : defaultNearbyStopFinders + .get(mode) + .findNearbyStops(ts0, transferProfile, transferProfile.journey().transfer(), false)) { + // Skip the origin stop, loop transfers are not needed. + if (sd.stop == stop) { + continue; + } + if (sd.stop.transfersNotAllowed()) { + continue; + } + TransferKey transferKey = new TransferKey(stop, sd.stop, sd.edges); + PathTransfer pathTransfer = distinctTransfers.get(transferKey); + if (pathTransfer == null) { + // If the PathTransfer can't be found, it is created. + distinctTransfers.put( + transferKey, + new PathTransfer(stop, sd.stop, sd.distance, sd.edges, EnumSet.of(mode)) + ); + } else { + // If the PathTransfer is found, a new PathTransfer with the added mode is created. + distinctTransfers.put(transferKey, pathTransfer.withAddedMode(mode)); + } + } } // Calculate flex transfers if flex routing is enabled. for (RouteRequest transferProfile : flexTransferRequests) { @@ -210,17 +214,36 @@ public void buildGraph() { } } // Calculate transfers between stops that can use trips with cars if configured. - if (carsAllowedStops.contains(ts0)) { + if (carsAllowedStops.contains(stop)) { for (RouteRequest transferProfile : carsAllowedStopTransferRequests) { StreetMode mode = transferProfile.journey().transfer().mode(); - findNearbyStops( - carsAllowedStopNearbyStopFinders.get(mode), - ts0, - transferProfile, - stop, - distinctTransfers, - mode - ); + for (NearbyStop sd : carsAllowedStopNearbyStopFinders + .get(mode) + .findNearbyStops(ts0, transferProfile, transferProfile.journey().transfer(), false)) { + // Skip the origin stop, loop transfers are not needed. + if (sd.stop == stop) { + continue; + } + if (sd.stop.transfersNotAllowed()) { + continue; + } + // Only calculate transfers between carsAllowedStops. + if (!carsAllowedStops.contains(sd.stop)) { + continue; + } + TransferKey transferKey = new TransferKey(stop, sd.stop, sd.edges); + PathTransfer pathTransfer = distinctTransfers.get(transferKey); + if (pathTransfer == null) { + // If the PathTransfer can't be found, it is created. + distinctTransfers.put( + transferKey, + new PathTransfer(stop, sd.stop, sd.distance, sd.edges, EnumSet.of(mode)) + ); + } else { + // If the PathTransfer is found, a new PathTransfer with the added mode is created. + distinctTransfers.put(transferKey, pathTransfer.withAddedMode(mode)); + } + } } } @@ -270,10 +293,7 @@ public void buildGraph() { * whether the graph has a street network and if ConsiderPatternsForDirectTransfers feature is * enabled. */ - private NearbyStopFinder createNearbyStopFinder( - Duration radiusByDuration, - Set findOnlyVertices - ) { + private NearbyStopFinder createNearbyStopFinder(Duration radiusByDuration) { var transitService = new DefaultTransitService(timetableRepository); NearbyStopFinder finder; if (!graph.hasStreets) { @@ -283,7 +303,7 @@ private NearbyStopFinder createNearbyStopFinder( finder = new StraightLineNearbyStopFinder(transitService, radiusByDuration); } else { LOG.info("Creating direct transfer edges between stops using the street network from OSM..."); - finder = new StreetNearbyStopFinder(radiusByDuration, 0, null, Set.of(), findOnlyVertices); + finder = new StreetNearbyStopFinder(radiusByDuration, 0, null, Set.of()); } if (OTPFeature.ConsiderPatternsForDirectTransfers.isOn()) { @@ -299,8 +319,7 @@ private void parseTransferParameters( List flexTransferRequests, HashMap defaultNearbyStopFinders, HashMap carsAllowedStopNearbyStopFinders, - NearbyStopFinder nearbyStopFinder, - Set carsAllowedStops + NearbyStopFinder nearbyStopFinder ) { for (RouteRequest transferProfile : transferRequests) { StreetMode mode = transferProfile.journey().transfer().mode(); @@ -313,10 +332,7 @@ private void parseTransferParameters( // Set mode-specific maxTransferDuration, if it is set in the build config. Duration maxTransferDuration = transferParameters.maxTransferDuration(); if (maxTransferDuration != Duration.ZERO) { - defaultNearbyStopFinders.put( - mode, - createNearbyStopFinder(maxTransferDuration, Set.of()) - ); + defaultNearbyStopFinders.put(mode, createNearbyStopFinder(maxTransferDuration)); } else { defaultNearbyStopFinders.put(mode, nearbyStopFinder); } @@ -327,10 +343,7 @@ private void parseTransferParameters( carsAllowedStopTransferRequests.add(transferProfile); carsAllowedStopNearbyStopFinders.put( mode, - createNearbyStopFinder( - carsAllowedStopMaxTransferDuration, - Collections.unmodifiableSet(carsAllowedStops) - ) + createNearbyStopFinder(carsAllowedStopMaxTransferDuration) ); } } else { @@ -350,41 +363,5 @@ private void parseTransferParameters( } } - private void findNearbyStops( - NearbyStopFinder nearbyStopFinder, - TransitStopVertex ts0, - RouteRequest transferProfile, - RegularStop stop, - Map distinctTransfers, - StreetMode mode - ) { - for (NearbyStop sd : nearbyStopFinder.findNearbyStops( - ts0, - transferProfile, - transferProfile.journey().transfer(), - false - )) { - // Skip the origin stop, loop transfers are not needed. - if (sd.stop == stop) { - continue; - } - if (sd.stop.transfersNotAllowed()) { - continue; - } - TransferKey transferKey = new TransferKey(stop, sd.stop, sd.edges); - PathTransfer pathTransfer = distinctTransfers.get(transferKey); - if (pathTransfer == null) { - // If the PathTransfer can't be found, it is created. - distinctTransfers.put( - transferKey, - new PathTransfer(stop, sd.stop, sd.distance, sd.edges, EnumSet.of(mode)) - ); - } else { - // If the PathTransfer is found, a new PathTransfer with the added mode is created. - distinctTransfers.put(transferKey, pathTransfer.withAddedMode(mode)); - } - } - } - private record TransferKey(StopLocation source, StopLocation target, List edges) {} } diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinder.java b/application/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinder.java index 327315a0be9..8277bd47e4c 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinder.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinder.java @@ -38,7 +38,6 @@ public class StreetNearbyStopFinder implements NearbyStopFinder { private final int maxStopCount; private final DataOverlayContext dataOverlayContext; private final Set ignoreVertices; - private final Set findOnlyVertices; /** * Construct a NearbyStopFinder for the given graph and search radius. @@ -51,7 +50,7 @@ public StreetNearbyStopFinder( int maxStopCount, DataOverlayContext dataOverlayContext ) { - this(durationLimit, maxStopCount, dataOverlayContext, Set.of(), Set.of()); + this(durationLimit, maxStopCount, dataOverlayContext, Set.of()); } /** @@ -66,31 +65,11 @@ public StreetNearbyStopFinder( int maxStopCount, DataOverlayContext dataOverlayContext, Set ignoreVertices - ) { - this(durationLimit, maxStopCount, dataOverlayContext, ignoreVertices, Set.of()); - } - - /** - * Construct a NearbyStopFinder for the given graph and search radius. - * - * @param maxStopCount The maximum stops to return. 0 means no limit. Regardless of the maxStopCount - * we will always return all the directly connected stops. - * @param ignoreVertices A set of stop vertices to ignore and not return NearbyStops for. - * - * @param findOnlyVertices Only return NearbyStops that are in this set. If this is empty, no filtering is performed. - */ - public StreetNearbyStopFinder( - Duration durationLimit, - int maxStopCount, - DataOverlayContext dataOverlayContext, - Set ignoreVertices, - Set findOnlyVertices ) { this.dataOverlayContext = dataOverlayContext; this.durationLimit = durationLimit; this.maxStopCount = maxStopCount; this.ignoreVertices = ignoreVertices; - this.findOnlyVertices = findOnlyVertices; } /** @@ -170,10 +149,7 @@ public Collection findNearbyStops( continue; } if (targetVertex instanceof TransitStopVertex tsv && state.isFinal()) { - // If a set of findOnlyVertices is provided, only add stops that are in the set. - if (findOnlyVertices.isEmpty() || findOnlyVertices.contains(targetVertex)) { - stopsFound.add(NearbyStop.nearbyStopForState(state, tsv.getStop())); - } + stopsFound.add(NearbyStop.nearbyStopForState(state, tsv.getStop())); } if ( OTPFeature.FlexRouting.isOn() && diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinderTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinderTest.java index d970569d091..aae02451b33 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinderTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/nearbystops/StreetNearbyStopFinderTest.java @@ -163,78 +163,6 @@ void testIgnoreStopsWithMaxStops() { assertStopAtDistance(stopC, 200, sortedNearbyStops.get(0)); } - @Test - void testFindOnlyVerticesStops() { - var durationLimit = Duration.ofMinutes(10); - var maxStopCount = 0; - Set findOnlyStops = Set.of(stopB, stopC); - var finder = new StreetNearbyStopFinder( - durationLimit, - maxStopCount, - null, - Set.of(), - findOnlyStops - ); - - var sortedNearbyStops = sort( - finder.findNearbyStops(stopA, new RouteRequest(), new StreetRequest(), false) - ); - - assertThat(sortedNearbyStops).hasSize(3); - assertZeroDistanceStop(stopA, sortedNearbyStops.get(0)); - assertStopAtDistance(stopB, 100, sortedNearbyStops.get(1)); - assertStopAtDistance(stopC, 200, sortedNearbyStops.get(2)); - } - - @Test - void testFindOnlyVerticesStopsWithIgnore() { - var durationLimit = Duration.ofMinutes(10); - var maxStopCount = 0; - Set findOnlyStops = Set.of(stopB, stopC); - Set ignore = Set.of(stopB); - var finder = new StreetNearbyStopFinder( - durationLimit, - maxStopCount, - null, - ignore, - findOnlyStops - ); - - var sortedNearbyStops = sort( - finder.findNearbyStops(stopA, new RouteRequest(), new StreetRequest(), false) - ); - - assertThat(sortedNearbyStops).hasSize(2); - assertZeroDistanceStop(stopA, sortedNearbyStops.get(0)); - assertStopAtDistance(stopC, 200, sortedNearbyStops.get(1)); - } - - @Test - void testFindOnlyVerticesStopsWithDurationLimit() { - // If we only allow walk for 101 seconds and speed is 1 m/s we should only be able to reach - // one extra stop. - var durationLimit = Duration.ofSeconds(101); - var maxStopCount = 0; - Set findOnlyStops = Set.of(stopB, stopC); - var routeRequest = new RouteRequest() - .withPreferences(b -> b.withWalk(walkPreferences -> walkPreferences.withSpeed(1.0))); - - var finder = new StreetNearbyStopFinder( - durationLimit, - maxStopCount, - null, - Set.of(), - findOnlyStops - ); - var sortedNearbyStops = sort( - finder.findNearbyStops(stopA, routeRequest, new StreetRequest(), false) - ); - - assertThat(sortedNearbyStops).hasSize(2); - assertZeroDistanceStop(stopA, sortedNearbyStops.get(0)); - assertStopAtDistance(stopB, 100, sortedNearbyStops.get(1)); - } - static List sort(Collection stops) { return stops.stream().sorted(Comparator.comparing(x -> x.distance)).toList(); } From 2e57c2f72b8c69af02af67d7637eaa92e11c333e Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 17 Dec 2024 09:59:00 +0200 Subject: [PATCH 16/23] Remove unnecessary parameter from function call. --- .../graph_builder/module/DirectTransferGenerator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index 6b7c53efc15..d80144636a4 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -303,7 +303,7 @@ private NearbyStopFinder createNearbyStopFinder(Duration radiusByDuration) { finder = new StraightLineNearbyStopFinder(transitService, radiusByDuration); } else { LOG.info("Creating direct transfer edges between stops using the street network from OSM..."); - finder = new StreetNearbyStopFinder(radiusByDuration, 0, null, Set.of()); + finder = new StreetNearbyStopFinder(radiusByDuration, 0, null); } if (OTPFeature.ConsiderPatternsForDirectTransfers.isOn()) { From f2296cd62497c2804e6d23c62644cfd361508b28 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 7 Jan 2025 08:29:59 +0200 Subject: [PATCH 17/23] Add formatting for JSON in documentation. --- .../standalone/config/buildconfig/TransferConfig.java | 2 ++ doc/user/BuildConfiguration.md | 2 ++ 2 files changed, 4 insertions(+) diff --git a/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferConfig.java index aa70a2788c4..ce6a87f21e7 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferConfig.java @@ -22,6 +22,7 @@ public static Map map(NodeAdapter root, String p **Example** +```JSON // build-config.json { "transfers": { @@ -35,6 +36,7 @@ public static Map map(NodeAdapter root, String p } } } +``` """ ) .asEnumMap(StreetMode.class, TransferParametersMapper::map, new EnumMap<>(StreetMode.class)); diff --git a/doc/user/BuildConfiguration.md b/doc/user/BuildConfiguration.md index dee3a266f8a..80bd08e8e0c 100644 --- a/doc/user/BuildConfiguration.md +++ b/doc/user/BuildConfiguration.md @@ -966,6 +966,7 @@ To configure mode-specific parameters, the modes should also be used in the `tra **Example** +```JSON // build-config.json { "transfers": { @@ -979,6 +980,7 @@ To configure mode-specific parameters, the modes should also be used in the `tra } } } +```

transitFeeds

From 0ac172ec2955eab5446365c91f1a905306fce6e0 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 7 Jan 2025 08:42:37 +0200 Subject: [PATCH 18/23] Rename variables. --- .../module/DirectTransferGenerator.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index d80144636a4..ffdfc1f6793 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -94,9 +94,9 @@ public void buildGraph() { /* The linker will use streets if they are available, or straight-line distance otherwise. */ NearbyStopFinder nearbyStopFinder = createNearbyStopFinder(defaultMaxTransferDuration); - HashMap defaultNearbyStopFinders = new HashMap<>(); + HashMap defaultNearbyStopFinderForMode = new HashMap<>(); /* These are used for calculating transfers only between carsAllowedStops. */ - HashMap carsAllowedStopNearbyStopFinders = new HashMap<>(); + HashMap carsAllowedStopNearbyStopFinderForMode = new HashMap<>(); List stops = graph.getVerticesOfType(TransitStopVertex.class); Set carsAllowedStops = timetableRepository.getStopLocationsUsedForCarsAllowedTrips(); @@ -135,8 +135,8 @@ public void buildGraph() { defaultTransferRequests, carsAllowedStopTransferRequests, flexTransferRequests, - defaultNearbyStopFinders, - carsAllowedStopNearbyStopFinders, + defaultNearbyStopFinderForMode, + carsAllowedStopNearbyStopFinderForMode, nearbyStopFinder ); @@ -158,7 +158,7 @@ public void buildGraph() { // Calculate default transfers. for (RouteRequest transferProfile : defaultTransferRequests) { StreetMode mode = transferProfile.journey().transfer().mode(); - for (NearbyStop sd : defaultNearbyStopFinders + for (NearbyStop sd : defaultNearbyStopFinderForMode .get(mode) .findNearbyStops(ts0, transferProfile, transferProfile.journey().transfer(), false)) { // Skip the origin stop, loop transfers are not needed. @@ -188,7 +188,7 @@ public void buildGraph() { StreetMode mode = StreetMode.WALK; // This code is for finding transfers from AreaStops to Stops, transfers // from Stops to AreaStops and between Stops are already covered above. - for (NearbyStop sd : defaultNearbyStopFinders + for (NearbyStop sd : defaultNearbyStopFinderForMode .get(mode) .findNearbyStops(ts0, transferProfile, transferProfile.journey().transfer(), true)) { // Skip the origin stop, loop transfers are not needed. @@ -217,7 +217,7 @@ public void buildGraph() { if (carsAllowedStops.contains(stop)) { for (RouteRequest transferProfile : carsAllowedStopTransferRequests) { StreetMode mode = transferProfile.journey().transfer().mode(); - for (NearbyStop sd : carsAllowedStopNearbyStopFinders + for (NearbyStop sd : carsAllowedStopNearbyStopFinderForMode .get(mode) .findNearbyStops(ts0, transferProfile, transferProfile.journey().transfer(), false)) { // Skip the origin stop, loop transfers are not needed. @@ -317,8 +317,8 @@ private void parseTransferParameters( List defaultTransferRequests, List carsAllowedStopTransferRequests, List flexTransferRequests, - HashMap defaultNearbyStopFinders, - HashMap carsAllowedStopNearbyStopFinders, + HashMap defaultNearbyStopFinderForMode, + HashMap carsAllowedStopNearbyStopFinderForMode, NearbyStopFinder nearbyStopFinder ) { for (RouteRequest transferProfile : transferRequests) { @@ -332,23 +332,23 @@ private void parseTransferParameters( // Set mode-specific maxTransferDuration, if it is set in the build config. Duration maxTransferDuration = transferParameters.maxTransferDuration(); if (maxTransferDuration != Duration.ZERO) { - defaultNearbyStopFinders.put(mode, createNearbyStopFinder(maxTransferDuration)); + defaultNearbyStopFinderForMode.put(mode, createNearbyStopFinder(maxTransferDuration)); } else { - defaultNearbyStopFinders.put(mode, nearbyStopFinder); + defaultNearbyStopFinderForMode.put(mode, nearbyStopFinder); } } // Create transfers between carsAllowedStops for the specific mode if carsAllowedStopMaxTransferDuration is set in the build config. Duration carsAllowedStopMaxTransferDuration = transferParameters.carsAllowedStopMaxTransferDuration(); if (carsAllowedStopMaxTransferDuration != Duration.ZERO) { carsAllowedStopTransferRequests.add(transferProfile); - carsAllowedStopNearbyStopFinders.put( + carsAllowedStopNearbyStopFinderForMode.put( mode, createNearbyStopFinder(carsAllowedStopMaxTransferDuration) ); } } else { defaultTransferRequests.add(transferProfile); - defaultNearbyStopFinders.put(mode, nearbyStopFinder); + defaultNearbyStopFinderForMode.put(mode, nearbyStopFinder); } } From bff77a5c91937518c96a246cab080d14d986f0cb Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 7 Jan 2025 09:18:14 +0200 Subject: [PATCH 19/23] Refactor transfer generation. --- .../module/DirectTransferGenerator.java | 73 ++++++++++--------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index ffdfc1f6793..ec776ae2b77 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -94,9 +94,6 @@ public void buildGraph() { /* The linker will use streets if they are available, or straight-line distance otherwise. */ NearbyStopFinder nearbyStopFinder = createNearbyStopFinder(defaultMaxTransferDuration); - HashMap defaultNearbyStopFinderForMode = new HashMap<>(); - /* These are used for calculating transfers only between carsAllowedStops. */ - HashMap carsAllowedStopNearbyStopFinderForMode = new HashMap<>(); List stops = graph.getVerticesOfType(TransitStopVertex.class); Set carsAllowedStops = timetableRepository.getStopLocationsUsedForCarsAllowedTrips(); @@ -126,19 +123,8 @@ public void buildGraph() { HashMultimap.create() ); - List defaultTransferRequests = new ArrayList<>(); - List carsAllowedStopTransferRequests = new ArrayList<>(); - List flexTransferRequests = new ArrayList<>(); - // Parse the transfer configuration from the parameters given in the build config. - parseTransferParameters( - defaultTransferRequests, - carsAllowedStopTransferRequests, - flexTransferRequests, - defaultNearbyStopFinderForMode, - carsAllowedStopNearbyStopFinderForMode, - nearbyStopFinder - ); + TransferConfiguration transferConfiguration = parseTransferParameters(nearbyStopFinder); stops .stream() @@ -156,11 +142,12 @@ public void buildGraph() { LOG.debug("Linking stop '{}' {}", stop, ts0); // Calculate default transfers. - for (RouteRequest transferProfile : defaultTransferRequests) { + for (RouteRequest transferProfile : transferConfiguration.defaultTransferRequests) { StreetMode mode = transferProfile.journey().transfer().mode(); - for (NearbyStop sd : defaultNearbyStopFinderForMode + var nearbyStops = transferConfiguration.defaultNearbyStopFinderForMode .get(mode) - .findNearbyStops(ts0, transferProfile, transferProfile.journey().transfer(), false)) { + .findNearbyStops(ts0, transferProfile, transferProfile.journey().transfer(), false); + for (NearbyStop sd : nearbyStops) { // Skip the origin stop, loop transfers are not needed. if (sd.stop == stop) { continue; @@ -183,14 +170,15 @@ public void buildGraph() { } } // Calculate flex transfers if flex routing is enabled. - for (RouteRequest transferProfile : flexTransferRequests) { + for (RouteRequest transferProfile : transferConfiguration.flexTransferRequests) { // Flex transfer requests only use the WALK mode. StreetMode mode = StreetMode.WALK; + var nearbyStops = transferConfiguration.defaultNearbyStopFinderForMode + .get(mode) + .findNearbyStops(ts0, transferProfile, transferProfile.journey().transfer(), true); // This code is for finding transfers from AreaStops to Stops, transfers // from Stops to AreaStops and between Stops are already covered above. - for (NearbyStop sd : defaultNearbyStopFinderForMode - .get(mode) - .findNearbyStops(ts0, transferProfile, transferProfile.journey().transfer(), true)) { + for (NearbyStop sd : nearbyStops) { // Skip the origin stop, loop transfers are not needed. if (sd.stop == stop) { continue; @@ -213,13 +201,14 @@ public void buildGraph() { } } } - // Calculate transfers between stops that can use trips with cars if configured. + // Calculate transfers between stops that are visited by trips that allow cars, if configured. if (carsAllowedStops.contains(stop)) { - for (RouteRequest transferProfile : carsAllowedStopTransferRequests) { + for (RouteRequest transferProfile : transferConfiguration.carsAllowedStopTransferRequests) { StreetMode mode = transferProfile.journey().transfer().mode(); - for (NearbyStop sd : carsAllowedStopNearbyStopFinderForMode + var nearbyStops = transferConfiguration.carsAllowedStopNearbyStopFinderForMode .get(mode) - .findNearbyStops(ts0, transferProfile, transferProfile.journey().transfer(), false)) { + .findNearbyStops(ts0, transferProfile, transferProfile.journey().transfer(), false); + for (NearbyStop sd : nearbyStops) { // Skip the origin stop, loop transfers are not needed. if (sd.stop == stop) { continue; @@ -313,14 +302,14 @@ private NearbyStopFinder createNearbyStopFinder(Duration radiusByDuration) { } } - private void parseTransferParameters( - List defaultTransferRequests, - List carsAllowedStopTransferRequests, - List flexTransferRequests, - HashMap defaultNearbyStopFinderForMode, - HashMap carsAllowedStopNearbyStopFinderForMode, - NearbyStopFinder nearbyStopFinder - ) { + private TransferConfiguration parseTransferParameters(NearbyStopFinder nearbyStopFinder) { + List defaultTransferRequests = new ArrayList<>(); + List carsAllowedStopTransferRequests = new ArrayList<>(); + List flexTransferRequests = new ArrayList<>(); + HashMap defaultNearbyStopFinderForMode = new HashMap<>(); + /* These are used for calculating transfers only between carsAllowedStops. */ + HashMap carsAllowedStopNearbyStopFinderForMode = new HashMap<>(); + for (RouteRequest transferProfile : transferRequests) { StreetMode mode = transferProfile.journey().transfer().mode(); TransferParameters transferParameters = transferParametersForMode.get(mode); @@ -361,7 +350,23 @@ private void parseTransferParameters( .toList() ); } + + return new TransferConfiguration( + defaultTransferRequests, + carsAllowedStopTransferRequests, + flexTransferRequests, + defaultNearbyStopFinderForMode, + carsAllowedStopNearbyStopFinderForMode + ); } + private record TransferConfiguration( + List defaultTransferRequests, + List carsAllowedStopTransferRequests, + List flexTransferRequests, + HashMap defaultNearbyStopFinderForMode, + HashMap carsAllowedStopNearbyStopFinderForMode + ) {} + private record TransferKey(StopLocation source, StopLocation target, List edges) {} } From 9f5d63c441e19e64af9357a25195f685549bbdcb Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 7 Jan 2025 11:42:29 +0200 Subject: [PATCH 20/23] Rename transfers to transferParameters in the build config and throw errors when invalid transferParameters are given. --- .../module/DirectTransferGenerator.java | 20 +++++++++++++++++-- .../standalone/config/BuildConfig.java | 2 +- .../module/DirectTransferGeneratorTest.java | 9 +-------- doc/user/BuildConfiguration.md | 4 ++-- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index ec776ae2b77..f173c52d6a0 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -310,13 +310,29 @@ private TransferConfiguration parseTransferParameters(NearbyStopFinder nearbySto /* These are used for calculating transfers only between carsAllowedStops. */ HashMap carsAllowedStopNearbyStopFinderForMode = new HashMap<>(); + // Check that the mode specified in transferParameters can also be found in transferRequests. + for (StreetMode mode : transferParametersForMode.keySet()) { + if ( + !transferRequests + .stream() + .anyMatch(transferProfile -> transferProfile.journey().transfer().mode() == mode) + ) { + throw new IllegalArgumentException( + String.format("Mode %s is used in transferParameters but not in transferRequests", mode) + ); + } + } + for (RouteRequest transferProfile : transferRequests) { StreetMode mode = transferProfile.journey().transfer().mode(); TransferParameters transferParameters = transferParametersForMode.get(mode); if (transferParameters != null) { - // Disable normal transfer calculations for the specific mode, if disableDefaultTransfers is set in the build config. // WALK mode transfers can not be disabled. For example, flex transfers need them. - if (!transferParameters.disableDefaultTransfers() || mode == StreetMode.WALK) { + if (transferParameters.disableDefaultTransfers() && mode == StreetMode.WALK) { + throw new IllegalArgumentException("WALK mode transfers can not be disabled"); + } + // Disable normal transfer calculations for the specific mode, if disableDefaultTransfers is set in the build config. + if (!transferParameters.disableDefaultTransfers()) { defaultTransferRequests.add(transferProfile); // Set mode-specific maxTransferDuration, if it is set in the build config. Duration maxTransferDuration = transferParameters.maxTransferDuration(); diff --git a/application/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java index d7ed602aef5..f9efbb8faa4 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/BuildConfig.java @@ -293,7 +293,7 @@ When set to true (it is false by default), the elevation module will include the "Transfers up to this duration with the default walk speed value will be pre-calculated and included in the Graph." ) .asDuration(Duration.ofMinutes(30)); - transferParametersForMode = TransferConfig.map(root, "transfers"); + transferParametersForMode = TransferConfig.map(root, "transferParameters"); maxStopToShapeSnapDistance = root .of("maxStopToShapeSnapDistance") diff --git a/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java b/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java index 7d75aecf89c..c18200793df 100644 --- a/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java +++ b/application/src/test/java/org/opentripplanner/graph_builder/module/DirectTransferGeneratorTest.java @@ -466,19 +466,12 @@ public void testBikeRequestWithPatternsAndWithCarsAllowedPatternsWithoutCarInTra graph.hasStreets = true; var timetableRepository = otpModel.timetableRepository(); - TransferParameters.Builder transferParametersBuilder = new TransferParameters.Builder(); - transferParametersBuilder.withCarsAllowedStopMaxTransferDuration(Duration.ofSeconds(120)); - transferParametersBuilder.withDisableDefaultTransfers(true); - Map transferParametersForMode = new HashMap<>(); - transferParametersForMode.put(StreetMode.CAR, transferParametersBuilder.build()); - new DirectTransferGenerator( graph, timetableRepository, DataImportIssueStore.NOOP, Duration.ofSeconds(30), - transferRequests, - transferParametersForMode + transferRequests ) .buildGraph(); diff --git a/doc/user/BuildConfiguration.md b/doc/user/BuildConfiguration.md index 80bd08e8e0c..ea0ef0c5855 100644 --- a/doc/user/BuildConfiguration.md +++ b/doc/user/BuildConfiguration.md @@ -90,8 +90,8 @@ Sections follow that describe particular settings in more depth. | osmDefaults | `object` | Default properties for OpenStreetMap feeds. | *Optional* | | 2.2 | |    [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 | +| [transferParameters](#transferParameters) | `enum map of object` | Configures mode-specific properties for transfer calculations. | *Optional* | | 2.7 | | [transferRequests](RouteRequest.md) | `object[]` | Routing requests to use for pre-calculating stop-to-stop transfers. | *Optional* | | 2.1 | -| [transfers](#transfers) | `enum map of object` | Configures mode-specific properties for transfer calculations. | *Optional* | | 2.7 | | [transitFeeds](#transitFeeds) | `object[]` | Scan for transit data files | *Optional* | | 2.2 | |    { object } | `object` | Nested object in array. The object type is determined by the parameters. | *Optional* | | 2.2 | |       type = "gtfs" | `enum` | The feed input format. | *Required* | | 2.2 | @@ -953,7 +953,7 @@ 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. -

transfers

+

transferParameters

**Since version:** `2.7` ∙ **Type:** `enum map of object` ∙ **Cardinality:** `Optional` **Path:** / From 4f84804a493b8f267a69d8ab7dfcd7a804dc073e Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 7 Jan 2025 11:57:26 +0200 Subject: [PATCH 21/23] Change wording of documentation. --- .../config/buildconfig/TransferParametersMapper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferParametersMapper.java b/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferParametersMapper.java index d14858d79c4..8a65ccdf712 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferParametersMapper.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferParametersMapper.java @@ -12,7 +12,7 @@ public static TransferParameters map(NodeAdapter c) { builder.withMaxTransferDuration( c .of("maxTransferDuration") - .summary("This overwrites the `maxTransferDuration` for the given mode.") + .summary("This overwrites the default `maxTransferDuration` for the given mode.") .since(V2_7) .asDuration(TransferParameters.DEFAULT_MAX_TRANSFER_DURATION) ); @@ -20,7 +20,7 @@ public static TransferParameters map(NodeAdapter c) { c .of("carsAllowedStopMaxTransferDuration") .summary( - "This is used for specifying a `maxTransferDuration` value to use with transfers between stops that have trips with cars." + "This is used for specifying a `maxTransferDuration` value to use with transfers between stops which are visited by trips that allow cars." ) .description( """ From 4d599a2603513521763f76d572e013492d75d223 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 7 Jan 2025 13:11:40 +0200 Subject: [PATCH 22/23] Add comments and refactor transfer generation. --- .../module/DirectTransferGenerator.java | 71 ++++++++----------- 1 file changed, 31 insertions(+), 40 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java index f173c52d6a0..c9b6c04bfcc 100644 --- a/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java +++ b/application/src/main/java/org/opentripplanner/graph_builder/module/DirectTransferGenerator.java @@ -4,14 +4,12 @@ import com.google.common.collect.Multimaps; import java.time.Duration; import java.util.ArrayList; -import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; import org.opentripplanner.framework.application.OTPFeature; import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore; import org.opentripplanner.graph_builder.issues.StopNotLinkedForTransfers; @@ -27,7 +25,6 @@ import org.opentripplanner.routing.graphfinder.NearbyStop; import org.opentripplanner.street.model.edge.Edge; import org.opentripplanner.street.model.vertex.TransitStopVertex; -import org.opentripplanner.street.model.vertex.Vertex; import org.opentripplanner.transit.model.site.RegularStop; import org.opentripplanner.transit.model.site.StopLocation; import org.opentripplanner.transit.service.DefaultTransitService; @@ -56,6 +53,9 @@ public class DirectTransferGenerator implements GraphBuilderModule { private final TimetableRepository timetableRepository; private final DataImportIssueStore issueStore; + /** + * Constructor used in tests. This initializes transferParametersForMode as an empty map. + */ public DirectTransferGenerator( Graph graph, TimetableRepository timetableRepository, @@ -68,7 +68,7 @@ public DirectTransferGenerator( this.issueStore = issueStore; this.defaultMaxTransferDuration = defaultMaxTransferDuration; this.transferRequests = transferRequests; - this.transferParametersForMode = Collections.emptyMap(); + this.transferParametersForMode = Map.of(); } public DirectTransferGenerator( @@ -155,18 +155,7 @@ public void buildGraph() { if (sd.stop.transfersNotAllowed()) { continue; } - TransferKey transferKey = new TransferKey(stop, sd.stop, sd.edges); - PathTransfer pathTransfer = distinctTransfers.get(transferKey); - if (pathTransfer == null) { - // If the PathTransfer can't be found, it is created. - distinctTransfers.put( - transferKey, - new PathTransfer(stop, sd.stop, sd.distance, sd.edges, EnumSet.of(mode)) - ); - } else { - // If the PathTransfer is found, a new PathTransfer with the added mode is created. - distinctTransfers.put(transferKey, pathTransfer.withAddedMode(mode)); - } + createPathTransfer(stop, sd.stop, sd, distinctTransfers, mode); } } // Calculate flex transfers if flex routing is enabled. @@ -187,18 +176,7 @@ public void buildGraph() { continue; } // The TransferKey and PathTransfer are created differently for flex routing. - TransferKey transferKey = new TransferKey(sd.stop, stop, sd.edges); - PathTransfer pathTransfer = distinctTransfers.get(transferKey); - if (pathTransfer == null) { - // If the PathTransfer can't be found, it is created. - distinctTransfers.put( - transferKey, - new PathTransfer(sd.stop, stop, sd.distance, sd.edges, EnumSet.of(mode)) - ); - } else { - // If the PathTransfer is found, a new PathTransfer with the added mode is created. - distinctTransfers.put(transferKey, pathTransfer.withAddedMode(mode)); - } + createPathTransfer(sd.stop, stop, sd, distinctTransfers, mode); } } // Calculate transfers between stops that are visited by trips that allow cars, if configured. @@ -220,18 +198,7 @@ public void buildGraph() { if (!carsAllowedStops.contains(sd.stop)) { continue; } - TransferKey transferKey = new TransferKey(stop, sd.stop, sd.edges); - PathTransfer pathTransfer = distinctTransfers.get(transferKey); - if (pathTransfer == null) { - // If the PathTransfer can't be found, it is created. - distinctTransfers.put( - transferKey, - new PathTransfer(stop, sd.stop, sd.distance, sd.edges, EnumSet.of(mode)) - ); - } else { - // If the PathTransfer is found, a new PathTransfer with the added mode is created. - distinctTransfers.put(transferKey, pathTransfer.withAddedMode(mode)); - } + createPathTransfer(stop, sd.stop, sd, distinctTransfers, mode); } } } @@ -302,6 +269,30 @@ private NearbyStopFinder createNearbyStopFinder(Duration radiusByDuration) { } } + private void createPathTransfer( + StopLocation from, + StopLocation to, + NearbyStop sd, + Map distinctTransfers, + StreetMode mode + ) { + TransferKey transferKey = new TransferKey(from, to, sd.edges); + PathTransfer pathTransfer = distinctTransfers.get(transferKey); + if (pathTransfer == null) { + // If the PathTransfer can't be found, it is created. + distinctTransfers.put( + transferKey, + new PathTransfer(from, to, sd.distance, sd.edges, EnumSet.of(mode)) + ); + } else { + // If the PathTransfer is found, a new PathTransfer with the added mode is created. + distinctTransfers.put(transferKey, pathTransfer.withAddedMode(mode)); + } + } + + /** + * This method parses the given transfer parameters into a transfer configuration and checks for invalid input. + */ private TransferConfiguration parseTransferParameters(NearbyStopFinder nearbyStopFinder) { List defaultTransferRequests = new ArrayList<>(); List carsAllowedStopTransferRequests = new ArrayList<>(); From 2eb0912beadf2fdb07afd0db2b6248e56d9a6625 Mon Sep 17 00:00:00 2001 From: Ville Pihlava Date: Tue, 7 Jan 2025 13:25:39 +0200 Subject: [PATCH 23/23] Rename transfers to transferParameters in documentation. --- .../standalone/config/buildconfig/TransferConfig.java | 2 +- doc/user/BuildConfiguration.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferConfig.java b/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferConfig.java index ce6a87f21e7..39683e16c18 100644 --- a/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferConfig.java +++ b/application/src/main/java/org/opentripplanner/standalone/config/buildconfig/TransferConfig.java @@ -25,7 +25,7 @@ public static Map map(NodeAdapter root, String p ```JSON // build-config.json { - "transfers": { + "transferParameters": { "CAR": { "disableDefaultTransfers": true, "carsAllowedStopMaxTransferDuration": "3h" diff --git a/doc/user/BuildConfiguration.md b/doc/user/BuildConfiguration.md index ea0ef0c5855..50c1dbab21a 100644 --- a/doc/user/BuildConfiguration.md +++ b/doc/user/BuildConfiguration.md @@ -969,7 +969,7 @@ To configure mode-specific parameters, the modes should also be used in the `tra ```JSON // build-config.json { - "transfers": { + "transferParameters": { "CAR": { "disableDefaultTransfers": true, "carsAllowedStopMaxTransferDuration": "3h"