package org.opentripplanner.graph_builder.linking; import com.google.common.collect.HashMultimap; import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.LineString; import org.opentripplanner.analyst.core.Sample; import org.opentripplanner.analyst.request.SampleFactory; import org.opentripplanner.common.geometry.GeometryUtils; import org.opentripplanner.common.geometry.SphericalDistanceLibrary; import org.opentripplanner.routing.edgetype.IntersectionTransitLink; import org.opentripplanner.routing.edgetype.SimpleTransfer; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.graph.Vertex; import org.opentripplanner.routing.vertextype.OsmVertex; import org.opentripplanner.routing.vertextype.TransitStop; import java.util.Collection; /** * Link transit stops to the street network in a non-destructive manner; i.e. don't * modify the street network. Being non-destructive is important in Analyst; new transit * models should not affect the length of existing streets, even slightly (due to float * roundoff from splits, etc.) An error of even a few seconds can be significant if it causes * the router to miss a transit vehicle or transfer, or causes the stop to move just beyond a * hard boundary. * * We do this by using the same linking code that is used * for Analyst (the SampleFactory code) which additionally means that this code * path is used and tested in multiple applications. * * This is not currently used. But it could be very useful for patching temporary transit lines in * interactively generated scenarios. */ public class SampleStopLinker { private Graph graph; /** keep track of stops that are linked to the same vertices */ private Multimap<VertexPair, TransitStop> links; public SampleStopLinker (Graph graph) { this.graph = graph; } /** * Link all transit stops. If makeTransfers is true, create direct transfer * edges between stops linked to the same pair of vertices. This is important * e.g. for transit centers where there are many stops on the same street segment; * we don't want to force the user to walk to the end of the street and back. * * If you're not generating transfers via the street network there is no need to make * transfers at this stage. But if you're not generating transfers via the street network, * why are you using this module at all? */ public void link (boolean makeTransfers) { if (makeTransfers) links = HashMultimap.create(); SampleFactory sf = graph.getSampleFactory(); for (TransitStop tstop : Iterables.filter(graph.getVertices(), TransitStop.class)) { Sample s = sf.getSample(tstop.getLon(), tstop.getLat()); // TODO: stop unlinked annotation if (s == null) continue; new IntersectionTransitLink(tstop, (OsmVertex) s.v0, s.d0); new IntersectionTransitLink((OsmVertex) s.v0, tstop, s.d0); new IntersectionTransitLink(tstop, (OsmVertex) s.v1, s.d1); new IntersectionTransitLink((OsmVertex) s.v1, tstop, s.d1); if (makeTransfers) { // save the sample so we can make direct transfers between stops VertexPair vp = new VertexPair(s.v0, s.v1); links.put(vp, tstop); } } if (makeTransfers) { // make direct transfers between stops for (Collection<TransitStop> tss : links.asMap().values()) { for (TransitStop ts0 : tss) { for (TransitStop ts1 : tss) { // make a geometry GeometryFactory gf = GeometryUtils.getGeometryFactory(); LineString geom = gf.createLineString(new Coordinate[] { ts0.getCoordinate(), ts1.getCoordinate() }); double dist = SphericalDistanceLibrary.distance(ts0.getLat(), ts0.getLon(), ts1.getLat(), ts1.getLon()); // building unidirectional edge, we'll hit this again in the opposite direction new SimpleTransfer(ts1, ts1, dist, geom); } } } } } /** represents an unordered pair of vertices from a sample */ private static class VertexPair { private final int v1, v2; public VertexPair(Vertex v1, Vertex v2) { this.v1 = v1.getIndex(); this.v2 = v2.getIndex(); } public int hashCode() { // bidirectional hash code return v1 + v2; } public boolean equals (Object other) { if (other instanceof VertexPair) { VertexPair vpo = (VertexPair) other; // bidirectional comparison return vpo.v1 == v1 && vpo.v2 == v2 || vpo.v2 == v1 && vpo.v1 == v2; } return false; } } }