
org.opentripplanner.street.model.edge.StreetTransitEntityLink Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of otp Show documentation
Show all versions of otp Show documentation
The OpenTripPlanner multimodal journey planning system
The newest version!
package org.opentripplanner.street.model.edge;
import javax.annotation.Nonnull;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.LineString;
import org.opentripplanner.framework.geometry.GeometryUtils;
import org.opentripplanner.framework.i18n.I18NString;
import org.opentripplanner.framework.tostring.ToStringBuilder;
import org.opentripplanner.routing.api.request.preference.RoutingPreferences;
import org.opentripplanner.street.model.vertex.StreetVertex;
import org.opentripplanner.street.model.vertex.Vertex;
import org.opentripplanner.street.search.state.State;
import org.opentripplanner.street.search.state.StateEditor;
import org.opentripplanner.transit.model.basic.Accessibility;
/**
* This represents the connection between a street vertex and a transit vertex.
*/
public abstract class StreetTransitEntityLink
extends Edge
implements CarPickupableEdge {
static final int STEL_TRAVERSE_COST = 1;
private final T transitEntityVertex;
private final Accessibility wheelchairAccessibility;
protected StreetTransitEntityLink(
StreetVertex fromv,
T tov,
Accessibility wheelchairAccessibility
) {
super(fromv, tov);
this.transitEntityVertex = tov;
this.wheelchairAccessibility = wheelchairAccessibility;
}
protected StreetTransitEntityLink(
T fromv,
StreetVertex tov,
Accessibility wheelchairAccessibility
) {
super(fromv, tov);
this.transitEntityVertex = fromv;
this.wheelchairAccessibility = wheelchairAccessibility;
}
public String toString() {
return ToStringBuilder.of(this.getClass()).addObj("from", fromv).addObj("to", tov).toString();
}
@Override
@Nonnull
public State[] traverse(State s0) {
// Forbid taking shortcuts composed of two street-transit links associated with the same stop in a row. Also
// avoids spurious leg transitions. As noted in https://github.com/opentripplanner/OpenTripPlanner/issues/2815,
// it is possible that two stops can have the same GPS coordinate thus creating a possibility for a
// legitimate StreetTransitLink > StreetTransitLink sequence, so only forbid two StreetTransitLinks to be taken
// if they are for the same stop.
if (
s0.backEdge instanceof StreetTransitEntityLink> link &&
link.transitEntityVertex == this.transitEntityVertex
) {
return State.empty();
}
RoutingPreferences pref = s0.getPreferences();
// 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);
if (s0.getRequest().wheelchair()) {
var accessibility = pref.wheelchair();
if (
accessibility.stop().onlyConsiderAccessible() &&
wheelchairAccessibility != Accessibility.POSSIBLE
) {
return State.empty();
} else if (wheelchairAccessibility == Accessibility.NO_INFORMATION) {
s1.incrementWeight(accessibility.stop().unknownCost());
} else if (wheelchairAccessibility == Accessibility.NOT_POSSIBLE) {
s1.incrementWeight(accessibility.stop().inaccessibleCost());
}
}
return switch (s0.currentMode()) {
case BICYCLE, SCOOTER -> {
// Forbid taking your own bike in the station if bike P+R activated.
if (s0.getRequest().mode().includesParking() && !s0.isVehicleParked()) {
yield State.empty();
}
// Forbid taking a (station) rental vehicle in the station. This allows taking along
// floating rental vehicles.
else if (
s0.isRentingVehicleFromStation() &&
!(
s0.mayKeepRentedVehicleAtDestination() &&
s0
.getRequest()
.preferences()
.rental(s0.getRequest().mode())
.allowArrivingInRentedVehicleAtDestination()
)
) {
yield State.empty();
}
yield buildState(s0, s1, pref);
}
// Allow taking an owned bike in the station
case CAR -> {
// Forbid taking your own car in the station if bike P+R activated.
if (s0.getRequest().mode().includesParking() && !s0.isVehicleParked()) {
yield State.empty();
}
// For Kiss & Ride allow dropping of the passenger before entering the station
if (s0.getCarPickupState() != null) {
if (canDropOffAfterDriving(s0) && isLeavingStreetNetwork(s0.getRequest().arriveBy())) {
dropOffAfterDriving(s0, s1);
} else {
yield State.empty();
}
}
if (s0.isRentingVehicleFromStation()) {
yield State.empty();
}
yield buildState(s0, s1, pref);
}
// If Kiss & Ride (Taxi) mode is not enabled allow car traversal so that the Stop
// may be reached by car
case WALK -> buildState(s0, s1, pref);
case FLEX -> State.empty();
};
}
@Nonnull
private State[] buildState(State s0, StateEditor s1, RoutingPreferences pref) {
if (s0.isRentingVehicleFromStation() && s0.mayKeepRentedVehicleAtDestination()) {
var rentalPreferences = s0.getRequest().preferences().rental(s0.getRequest().mode());
if (rentalPreferences.allowArrivingInRentedVehicleAtDestination()) {
s1.incrementWeight(
rentalPreferences.arrivingInRentalVehicleAtDestinationCost().toSeconds()
);
}
}
s1.setBackMode(null);
// streetToStopTime may be zero so that searching from the stop coordinates instead of
// the stop id catch transit departing at that exact search time.
int streetToStopTime = getStreetToStopTime();
s1.incrementTimeInSeconds(streetToStopTime);
s1.incrementWeight(STEL_TRAVERSE_COST + streetToStopTime);
return s1.makeStateArray();
}
public I18NString getName() {
return this.transitEntityVertex.getName();
}
public LineString getGeometry() {
Coordinate[] coordinates = new Coordinate[] { fromv.getCoordinate(), tov.getCoordinate() };
return GeometryUtils.getGeometryFactory().createLineString(coordinates);
}
protected abstract int getStreetToStopTime();
protected T getTransitEntityVertex() {
return transitEntityVertex;
}
boolean isLeavingStreetNetwork(boolean arriveBy) {
return (arriveBy ? fromv : tov) == getTransitEntityVertex();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy