/* 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.map; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.LineString; import org.onebusaway.gtfs.model.Route; import org.opentripplanner.common.geometry.GeometryUtils; import org.opentripplanner.extra_graph.EdgesForRoute; import org.opentripplanner.graph_builder.services.GraphBuilderModule; import org.opentripplanner.routing.core.TraverseMode; import org.opentripplanner.routing.edgetype.TripPattern; import org.opentripplanner.routing.graph.Edge; import org.opentripplanner.routing.graph.Graph; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import org.opentripplanner.routing.impl.DefaultStreetVertexIndexFactory; /** * Uses the shapes from GTFS to determine which streets buses drive on. This is used to improve the quality of * the stop-to-street linkage. It encourages the linker to link to streets where transit actually travels. * * GTFS provides a mapping from trips->shapes. This module provides a mapping from stops->trips and shapes->edges. * Then transitively we get a mapping from stop->edges. * The edges that "belong" to a stop are favored when linking that stop to the street network. */ public class BusRouteStreetMatcher implements GraphBuilderModule { private static final Logger log = LoggerFactory.getLogger(BusRouteStreetMatcher.class); public List<String> provides() { return Arrays.asList("edge matching"); } public List<String> getPrerequisites() { return Arrays.asList("streets", "transit"); } /* The "extra" parameter is a mechanism for passing arbitrary things between graph builder modules. Whether or not this is a good idea is open to debate, but that's what it is. An EdgesForRoute instance is generated by MapBuilder and StreetMatcher, then retrieved later by the NetworkLinkerLibrary later (actually in LinkRequests). */ public void buildGraph(Graph graph, HashMap<Class<?>, Object> extra) { //Mapbuilder needs transit index graph.index(new DefaultStreetVertexIndexFactory()); StreetMatcher matcher = new StreetMatcher(graph); EdgesForRoute edgesForRoute = new EdgesForRoute(); extra.put(EdgesForRoute.class, edgesForRoute); log.info("Finding corresponding street edges for trip patterns..."); // Why do we need to iterate over the routes? Why not just patterns? for (Route route : graph.index.routeForId.values()) { for (TripPattern pattern : graph.index.patternsForRoute.get(route)) { if (pattern.mode == TraverseMode.BUS) { /* we can only match geometry to streets on bus routes */ log.debug("Matching {}", pattern); //If there are no shapes in GTFS pattern geometry is generated //generated geometry is useless for street matching //that is why pattern.geometry is null in that case if (pattern.geometry == null) { continue; } List<Edge> edges = matcher.match(pattern.geometry); if (edges == null || edges.isEmpty()) { log.warn("Could not match to street network: {}", pattern); continue; } List<Coordinate> coordinates = new ArrayList<Coordinate>(); for (Edge e : edges) { coordinates.addAll(Arrays.asList(e.getGeometry().getCoordinates())); edgesForRoute.edgesForRoute.put(route, e); } Coordinate[] coordinateArray = new Coordinate[coordinates.size()]; LineString ls = GeometryUtils.getGeometryFactory().createLineString(coordinates.toArray(coordinateArray)); // Replace the pattern's geometry from GTFS with that of the equivalent OSM edges. pattern.geometry = ls; } } } } @Override public void checkInputs() { //no file inputs } }