/* 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.routing.edgetype; import org.onebusaway.gtfs.model.Trip; import org.opentripplanner.common.geometry.GeometryUtils; import org.opentripplanner.routing.core.State; import org.opentripplanner.routing.core.StateEditor; import org.opentripplanner.routing.core.TraverseMode; import org.opentripplanner.routing.core.RoutingRequest; import org.opentripplanner.routing.graph.Edge; import org.opentripplanner.routing.graph.Vertex; import org.opentripplanner.routing.vertextype.StreetVertex; import org.opentripplanner.routing.vertextype.TransitStop; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.LineString; import java.util.Locale; /** * This represents the connection between a street vertex and a transit vertex * where going from the street to the vehicle is immediate -- such as at a * curbside bus stop. */ public class StreetTransitLink extends Edge { private static final long serialVersionUID = -3311099256178798981L; static final int STL_TRAVERSE_COST = 1; private boolean wheelchairAccessible; private TransitStop transitStop; public StreetTransitLink(StreetVertex fromv, TransitStop tov, boolean wheelchairAccessible) { super(fromv, tov); transitStop = tov; this.wheelchairAccessible = wheelchairAccessible; } public StreetTransitLink(TransitStop fromv, StreetVertex tov, boolean wheelchairAccessible) { super(fromv, tov); transitStop = fromv; this.wheelchairAccessible = wheelchairAccessible; } public String getDirection() { return null; } public double getDistance() { return 0; } public LineString getGeometry() { Coordinate[] coordinates = new Coordinate[] { fromv.getCoordinate(), tov.getCoordinate()}; return GeometryUtils.getGeometryFactory().createLineString(coordinates); } public TraverseMode getMode() { return TraverseMode.LEG_SWITCH; } public String getName() { return "street transit link"; } @Override public String getName(Locale locale) { //TODO: localize return this.getName(); } public State traverse(State s0) { // Forbid taking shortcuts composed of two street-transit links in a row. Also avoids spurious leg transitions. if (s0.backEdge instanceof StreetTransitLink) { return null; } // Do not re-enter the street network following a transfer. if (s0.backEdge instanceof SimpleTransfer) { return null; } RoutingRequest req = s0.getOptions(); if (s0.getOptions().wheelchairAccessible && !wheelchairAccessible) { return null; } if (s0.getOptions().bikeParkAndRide && !s0.isBikeParked()) { // Forbid taking your own bike in the station if bike P+R activated. return null; } if (s0.isBikeRenting()) { // Forbid taking a rented bike on any transit. // TODO Check this condition, does this always make sense? return null; } // Do not check here whether any transit modes are selected. A check for the presence of // transit modes will instead be done in the following PreBoard edge. // This allows searching for nearby transit stops using walk-only options. StateEditor s1 = s0.edit(this); /* Only enter stations in CAR mode if parking is not required (kiss and ride) */ /* Note that in arriveBy searches this is double-traversing link edges to fork the state into both WALK and CAR mode. This is an insane hack. */ if (s0.getNonTransitMode() == TraverseMode.CAR) { if (req.kissAndRide && !s0.isCarParked()) { s1.setCarParked(true); } else { return null; } } s1.incrementTimeInSeconds(transitStop.getStreetToStopTime() + STL_TRAVERSE_COST); s1.incrementWeight(STL_TRAVERSE_COST + transitStop.getStreetToStopTime()); s1.setBackMode(TraverseMode.LEG_SWITCH); return s1.makeState(); } public State optimisticTraverse(State s0) { StateEditor s1 = s0.edit(this); s1.incrementWeight(STL_TRAVERSE_COST); s1.setBackMode(TraverseMode.LEG_SWITCH); return s1.makeState(); } // anecdotally, the lower bound search is about 2x faster when you don't reach stops // and therefore don't even consider boarding @Override public double weightLowerBound(RoutingRequest options) { return options.transitAllowed() ? 0 : Double.POSITIVE_INFINITY; } public Vertex getFromVertex() { return fromv; } public Vertex getToVertex() { return tov; } public Trip getTrip() { return null; } public boolean isRoundabout() { return false; } public String toString() { return "StreetTransitLink(" + fromv + " -> " + tov + ")"; } }