All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opentripplanner.graph_builder.module.DirectTransferGenerator Maven / Gradle / Ivy

The newest version!
package org.opentripplanner.graph_builder.module;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimaps;
import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.opentripplanner.framework.application.OTPFeature;
import org.opentripplanner.framework.logging.ProgressTracker;
import org.opentripplanner.graph_builder.issue.api.DataImportIssueStore;
import org.opentripplanner.graph_builder.issues.StopNotLinkedForTransfers;
import org.opentripplanner.graph_builder.model.GraphBuilderModule;
import org.opentripplanner.graph_builder.module.nearbystops.NearbyStopFinder;
import org.opentripplanner.graph_builder.module.nearbystops.PatternConsideringNearbyStopFinder;
import org.opentripplanner.graph_builder.module.nearbystops.StraightLineNearbyStopFinder;
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.request.StreetRequest;
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;
import org.opentripplanner.transit.service.TransitModel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * {@link GraphBuilderModule} module that links up the
 * stops of a transit network among themselves. This is necessary for routing in long-distance
 * mode.
 * 

* It will use the street network if OSM data has already been loaded into the graph. Otherwise it * will use straight-line distance between stops. */ public class DirectTransferGenerator implements GraphBuilderModule { private static final Logger LOG = LoggerFactory.getLogger(DirectTransferGenerator.class); private final Duration radiusByDuration; private final List transferRequests; private final Graph graph; private final TransitModel transitModel; private final DataImportIssueStore issueStore; public DirectTransferGenerator( Graph graph, TransitModel transitModel, DataImportIssueStore issueStore, Duration radiusByDuration, List transferRequests ) { this.graph = graph; this.transitModel = transitModel; this.issueStore = issueStore; this.radiusByDuration = radiusByDuration; this.transferRequests = transferRequests; } @Override public void buildGraph() { /* Initialize transit model index which is needed by the nearby stop finder. */ transitModel.index(); /* The linker will use streets if they are available, or straight-line distance otherwise. */ NearbyStopFinder nearbyStopFinder = createNearbyStopFinder(); List stops = graph.getVerticesOfType(TransitStopVertex.class); ProgressTracker progress = ProgressTracker.track( "Create transfer edges for stops", 1000, stops.size() ); AtomicInteger nTransfersTotal = new AtomicInteger(); AtomicInteger nLinkedStops = new AtomicInteger(); // This is a synchronizedMultimap so that a parallel stream may be used to insert elements. var transfersByStop = Multimaps.synchronizedMultimap( HashMultimap.create() ); stops .stream() .parallel() .forEach(ts0 -> { /* Make transfers to each nearby stop that has lowest weight on some trip pattern. * Use map based on the list of edges, so that only distinct transfers are stored. */ Map distinctTransfers = new HashMap<>(); RegularStop stop = ts0.getStop(); if (stop.transfersNotAllowed()) { return; } LOG.debug("Linking stop '{}' {}", stop, ts0); for (RouteRequest transferProfile : transferRequests) { for (NearbyStop sd : findNearbyStops( nearbyStopFinder, 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) ); } 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. for (NearbyStop sd : findNearbyStops( nearbyStopFinder, 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; } distinctTransfers.put( new TransferKey(sd.stop, stop, sd.edges), new PathTransfer(sd.stop, stop, sd.distance, sd.edges) ); } } } LOG.debug( "Linked stop {} with {} transfers to stops with different patterns.", stop, distinctTransfers.size() ); if (distinctTransfers.isEmpty()) { issueStore.add(new StopNotLinkedForTransfers(ts0)); } else { distinctTransfers .values() .forEach(transfer -> transfersByStop.put(transfer.from, transfer)); nLinkedStops.incrementAndGet(); nTransfersTotal.addAndGet(distinctTransfers.size()); } //Keep lambda! A method-ref would causes incorrect class and line number to be logged //noinspection Convert2MethodRef progress.step(m -> LOG.info(m)); }); transitModel.addAllTransfersByStops(transfersByStop); LOG.info(progress.completeMessage()); LOG.info( "Done connecting stops to one another. Created a total of {} transfers from {} stops.", nTransfersTotal, nLinkedStops ); } /** * Factory method for creating a NearbyStopFinder. Will create different finders depending on * whether the graph has a street network and if ConsiderPatternsForDirectTransfers feature is * enabled. */ private NearbyStopFinder createNearbyStopFinder() { var transitService = new DefaultTransitService(transitModel); NearbyStopFinder finder; if (!graph.hasStreets) { LOG.info( "Creating direct transfer edges between stops using straight line distance (not streets)..." ); 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); } if (OTPFeature.ConsiderPatternsForDirectTransfers.isOn()) { return new PatternConsideringNearbyStopFinder(transitService, finder); } else { return finder; } } private static Iterable findNearbyStops( NearbyStopFinder nearbyStopFinder, Vertex vertex, RouteRequest request, StreetRequest streetRequest, boolean reverseDirection ) { return nearbyStopFinder.findNearbyStops(vertex, request, streetRequest, reverseDirection); } private record TransferKey(StopLocation source, StopLocation target, List edges) {} }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy