/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.opentripplanner.graph_builder.module; import com.google.common.collect.Iterables; import org.opentripplanner.graph_builder.annotation.StopNotLinkedForTransfers; import org.opentripplanner.graph_builder.services.GraphBuilderModule; import org.opentripplanner.routing.edgetype.PathwayEdge; import org.opentripplanner.routing.edgetype.SimpleTransfer; import org.opentripplanner.routing.graph.Edge; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.graph.GraphIndex; import org.opentripplanner.routing.vertextype.TransitStop; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; /** * {@link org.opentripplanner.graph_builder.services.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. * * TODO make tests for this that are sensitive to the presence of trip patterns */ public class DirectTransferGenerator implements GraphBuilderModule { private static Logger LOG = LoggerFactory.getLogger(DirectTransferGenerator.class); final double radiusMeters; public List<String> provides() { return Arrays.asList("linking"); } public List<String> getPrerequisites() { return Arrays.asList("street to transit"); } public DirectTransferGenerator (double radiusMeters) { this.radiusMeters = radiusMeters; } @Override public void buildGraph(Graph graph, HashMap<Class<?>, Object> extra) { /* Initialize graph index which is needed by the nearby stop finder. */ if (graph.index == null) { graph.index = new GraphIndex(graph); } /* The linker will use streets if they are available, or straight-line distance otherwise. */ NearbyStopFinder nearbyStopFinder = new NearbyStopFinder(graph, radiusMeters); if (nearbyStopFinder.useStreets) { LOG.info("Creating direct transfer edges between stops using the street network from OSM..."); } else { LOG.info("Creating direct transfer edges between stops using straight line distance (not streets)..."); } int nTransfersTotal = 0; int nLinkableStops = 0; for (TransitStop ts0 : Iterables.filter(graph.getVertices(), TransitStop.class)) { /* Skip stops that are entrances to stations or whose entrances are coded separately */ if (!ts0.isStreetLinkable()) continue; if (++nLinkableStops % 1000 == 0) { LOG.info("Linked {} stops", nLinkableStops); } LOG.debug("Linking stop '{}' {}", ts0.getStop(), ts0); /* Determine the set of stops that are already reachable via other pathways or transfers */ Set<TransitStop> pathwayDestinations = new HashSet<TransitStop>(); for (Edge e : ts0.getOutgoing()) { if (e instanceof PathwayEdge || e instanceof SimpleTransfer) { if (e.getToVertex() instanceof TransitStop) { TransitStop to = (TransitStop) e.getToVertex(); pathwayDestinations.add(to); } } } /* Make transfers to each nearby stop that is the closest stop on some trip pattern. */ int n = 0; for (NearbyStopFinder.StopAtDistance sd : nearbyStopFinder.findNearbyStopsConsideringPatterns(ts0)) { /* Skip the origin stop, loop transfers are not needed. */ if (sd.tstop == ts0 || pathwayDestinations.contains(sd.tstop)) continue; new SimpleTransfer(ts0, sd.tstop, sd.dist, sd.geom, sd.edges); n += 1; } LOG.debug("Linked stop {} to {} nearby stops on other patterns.", ts0.getStop(), n); if (n == 0) { LOG.debug(graph.addBuilderAnnotation(new StopNotLinkedForTransfers(ts0))); } nTransfersTotal += n; } LOG.info("Done connecting stops to one another. Created a total of {} transfers from {} stops.", nTransfersTotal, nLinkableStops); graph.hasDirectTransfers = true; } @Override public void checkInputs() { // No inputs } }