All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opentripplanner.street.model.edge.StreetTransitEntityLink Maven / Gradle / Ivy

The newest version!
package org.opentripplanner.street.model.edge;

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.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;
  }

  @Override
  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();
    };
  }

  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