Пример #1
0
 /**
  * Whether this leg is a transit leg or not.
  *
  * @return Boolean true if the leg is a transit leg
  */
 public Boolean isTransitLeg() {
   if (mode == null) return null;
   else if (mode.equals(TraverseMode.WALK.toString())) return false;
   else if (mode.equals(TraverseMode.CAR.toString())) return false;
   else if (mode.equals(TraverseMode.BICYCLE.toString())) return false;
   else if (mode.equals(TraverseMode.CUSTOM_MOTOR_VEHICLE.toString())) return false;
   else return true;
 }
Пример #2
0
/**
 * One leg of a trip -- that is, a temporally continuous piece of the journey that takes place on a
 * particular vehicle (or on foot).
 */
public class Leg {

  /** The date and time this leg begins. */
  public Calendar startTime = null;

  /** The date and time this leg ends. */
  public Calendar endTime = null;

  /**
   * For transit leg, the offset from the scheduled departure-time of the boarding stop in this leg.
   * "scheduled time of departure at boarding stop" = startTime - departureDelay
   */
  public int departureDelay = 0;
  /**
   * For transit leg, the offset from the scheduled arrival-time of the alighting stop in this leg.
   * "scheduled time of arrival at alighting stop" = endTime - arrivalDelay
   */
  public int arrivalDelay = 0;
  /** Whether there is real-time data about this Leg */
  public Boolean realTime = false;

  /** Is this a frequency-based trip with non-strict departure times? */
  public Boolean isNonExactFrequency = null;

  /**
   * The best estimate of the time between two arriving vehicles. This is particularly important for
   * non-strict frequency trips, but could become important for real-time trips, strict frequency
   * trips, and scheduled trips with empirical headways.
   */
  public Integer headway = null;

  /** The distance traveled while traversing the leg in meters. */
  public Double distance = null;

  /** The mode (e.g., <code>Walk</code>) used when traversing this leg. */
  @XmlAttribute @JsonSerialize public String mode = TraverseMode.WALK.toString();

  /**
   * For transit legs, the route of the bus or train being used. For non-transit legs, the name of
   * the street being traversed.
   */
  @XmlAttribute @JsonSerialize public String route = "";

  @XmlAttribute @JsonSerialize public String agencyName;

  @XmlAttribute @JsonSerialize public String agencyUrl;

  @XmlAttribute @JsonSerialize public int agencyTimeZoneOffset;

  /**
   * For transit leg, the route's (background) color (if one exists). For non-transit legs, null.
   */
  @XmlAttribute @JsonSerialize public String routeColor = null;

  /**
   * For transit legs, the type of the route. Non transit -1 When 0-7: 0 Tram, 1 Subway, 2 Train, 3
   * Bus, 4 Ferry, 5 Cable Car, 6 Gondola, 7 Funicular When equal or highter than 100, it is coded
   * using the Hierarchical Vehicle Type (HVT) codes from the European TPEG standard Also see
   * http://groups.google.com/group/gtfs-changes/msg/ed917a69cf8c5bef
   */
  @XmlAttribute @JsonSerialize public Integer routeType = null;

  /** For transit legs, the ID of the route. For non-transit legs, null. */
  @XmlAttribute @JsonSerialize public String routeId = null;

  /** For transit leg, the route's text color (if one exists). For non-transit legs, null. */
  @XmlAttribute @JsonSerialize public String routeTextColor = null;

  /** For transit legs, if the rider should stay on the vehicle as it changes route names. */
  @XmlAttribute @JsonSerialize public Boolean interlineWithPreviousLeg;

  /** For transit leg, the trip's short name (if one exists). For non-transit legs, null. */
  @XmlAttribute @JsonSerialize public String tripShortName = null;

  /** For transit leg, the trip's block ID (if one exists). For non-transit legs, null. */
  @XmlAttribute @JsonSerialize public String tripBlockId = null;

  /** For transit legs, the headsign of the bus or train being used. For non-transit legs, null. */
  @XmlAttribute @JsonSerialize public String headsign = null;

  /**
   * For transit legs, the ID of the transit agency that operates the service used for this leg. For
   * non-transit legs, null.
   */
  @XmlAttribute @JsonSerialize public String agencyId = null;

  /** For transit legs, the ID of the trip. For non-transit legs, null. */
  @XmlAttribute @JsonSerialize public String tripId = null;

  /** For transit legs, the service date of the trip. For non-transit legs, null. */
  @XmlAttribute @JsonSerialize public String serviceDate = null;

  /** The Place where the leg originates. */
  public Place from = null;

  /** The Place where the leg begins. */
  public Place to = null;

  /**
   * For transit legs, intermediate stops between the Place where the leg originates and the Place
   * where the leg ends. For non-transit legs, null. This field is optional i.e. it is always null
   * unless "showIntermediateStops" parameter is set to "true" in the planner request.
   */
  @XmlElementWrapper(name = "intermediateStops")
  @JsonProperty(value = "intermediateStops")
  public List<Place> stop;

  /** The leg's geometry. */
  public EncodedPolylineBean legGeometry;

  /** A series of turn by turn instructions used for walking, biking and driving. */
  @XmlElementWrapper(name = "steps")
  @JsonProperty(value = "steps")
  public List<WalkStep> walkSteps;

  /** Deprecated field formerly used for notes -- will be removed. See alerts */
  @XmlElement @JsonSerialize public List<Note> notes;

  @XmlElement @JsonSerialize public List<Alert> alerts;

  @XmlAttribute @JsonSerialize public String routeShortName;

  @XmlAttribute @JsonSerialize public String routeLongName;

  @XmlAttribute @JsonSerialize public String boardRule;

  @XmlAttribute @JsonSerialize public String alightRule;

  @XmlAttribute @JsonSerialize public Boolean rentedBike;

  /**
   * Whether this leg is a transit leg or not.
   *
   * @return Boolean true if the leg is a transit leg
   */
  public Boolean isTransitLeg() {
    if (mode == null) return null;
    else if (mode.equals(TraverseMode.WALK.toString())) return false;
    else if (mode.equals(TraverseMode.CAR.toString())) return false;
    else if (mode.equals(TraverseMode.BICYCLE.toString())) return false;
    else if (mode.equals(TraverseMode.CUSTOM_MOTOR_VEHICLE.toString())) return false;
    else return true;
  }

  /** The leg's duration in seconds */
  @XmlElement
  @JsonSerialize
  public double getDuration() {
    return endTime.getTimeInMillis() / 1000.0 - startTime.getTimeInMillis() / 1000.0;
  }

  public void addAlert(Alert alert) {
    if (notes == null) {
      notes = new ArrayList<Note>();
    }
    if (alerts == null) {
      alerts = new ArrayList<Alert>();
    }
    String text = alert.alertHeaderText.getSomeTranslation();
    if (text == null) {
      text = alert.alertDescriptionText.getSomeTranslation();
    }
    if (text == null) {
      text = alert.alertUrl.getSomeTranslation();
    }
    Note note = new Note(text);
    if (!notes.contains(note)) {
      notes.add(note);
    }
    if (!alerts.contains(alert)) {
      alerts.add(alert);
    }
  }

  public void setTimeZone(TimeZone timeZone) {
    Calendar calendar = Calendar.getInstance(timeZone);
    calendar.setTime(startTime.getTime());
    startTime = calendar;
    calendar = Calendar.getInstance(timeZone);
    calendar.setTime(endTime.getTime());
    endTime = calendar;
    agencyTimeZoneOffset = timeZone.getOffset(startTime.getTimeInMillis());
  }
}
  /**
   * return a StateEditor rather than a State so that we can make parking/mode switch modifications
   * for kiss-and-ride.
   */
  private StateEditor doTraverse(State s0, RoutingRequest options, TraverseMode traverseMode) {
    boolean walkingBike = options.walkingBike;
    boolean backWalkingBike = s0.isBackWalkingBike();
    TraverseMode backMode = s0.getBackMode();
    Edge backEdge = s0.getBackEdge();
    if (backEdge != null) {
      // No illegal U-turns.
      // NOTE(flamholz): we check both directions because both edges get a chance to decide
      // if they are the reverse of the other. Also, because it doesn't matter which direction
      // we are searching in - these traversals are always disallowed (they are U-turns in one
      // direction
      // or the other).
      // TODO profiling indicates that this is a hot spot.
      if (this.isReverseOf(backEdge) || backEdge.isReverseOf(this)) {
        return null;
      }
    }

    // Ensure we are actually walking, when walking a bike
    backWalkingBike &= TraverseMode.WALK.equals(backMode);
    walkingBike &= TraverseMode.WALK.equals(traverseMode);

    /* Check whether this street allows the current mode. If not and we are biking, attempt to walk the bike. */
    if (!canTraverse(options, traverseMode)) {
      if (traverseMode == TraverseMode.BICYCLE) {
        return doTraverse(s0, options.bikeWalkingOptions, TraverseMode.WALK);
      }
      return null;
    }

    // Automobiles have variable speeds depending on the edge type
    double speed = calculateSpeed(options, traverseMode, s0.getTimeInMillis());

    double time = getDistance() / speed;
    double weight;
    // TODO(flamholz): factor out this bike, wheelchair and walking specific logic to somewhere
    // central.
    if (options.wheelchairAccessible) {
      weight = getSlopeSpeedEffectiveLength() / speed;
    } else if (traverseMode.equals(TraverseMode.BICYCLE)) {
      time = getSlopeSpeedEffectiveLength() / speed;
      switch (options.optimize) {
        case SAFE:
          weight = bicycleSafetyFactor * getDistance() / speed;
          break;
        case GREENWAYS:
          weight = bicycleSafetyFactor * getDistance() / speed;
          if (bicycleSafetyFactor <= GREENWAY_SAFETY_FACTOR) {
            // greenways are treated as even safer than they really are
            weight *= 0.66;
          }
          break;
        case FLAT:
          /* see notes in StreetVertex on speed overhead */
          weight = getDistance() / speed + getSlopeWorkCostEffectiveLength();
          break;
        case QUICK:
          weight = getSlopeSpeedEffectiveLength() / speed;
          break;
        case TRIANGLE:
          double quick = getSlopeSpeedEffectiveLength();
          double safety = bicycleSafetyFactor * getDistance();
          // TODO This computation is not coherent with the one for FLAT
          double slope = getSlopeWorkCostEffectiveLength();
          weight =
              quick * options.triangleTimeFactor
                  + slope * options.triangleSlopeFactor
                  + safety * options.triangleSafetyFactor;
          weight /= speed;
          break;
        default:
          weight = getDistance() / speed;
      }
    } else {
      if (walkingBike) {
        // take slopes into account when walking bikes
        time = getSlopeSpeedEffectiveLength() / speed;
      }
      weight = time;
      if (traverseMode.equals(TraverseMode.WALK)) {
        // take slopes into account when walking
        // FIXME: this causes steep stairs to be avoided. see #1297.
        double costs = ElevationUtils.getWalkCostsForSlope(getDistance(), getMaxSlope());
        // as the cost walkspeed is assumed to be for 4.8km/h (= 1.333 m/sec) we need to adjust
        // for the walkspeed set by the user
        double elevationUtilsSpeed = 4.0 / 3.0;
        weight = costs * (elevationUtilsSpeed / speed);
        time =
            weight; // treat cost as time, as in the current model it actually is the same (this can
                    // be checked for maxSlope == 0)
        /*
        // debug code
        if(weight > 100){
            double timeflat = length / speed;
            System.out.format("line length: %.1f m, slope: %.3f ---> slope costs: %.1f , weight: %.1f , time (flat):  %.1f %n", length, elevationProfile.getMaxSlope(), costs, weight, timeflat);
        }
        */
      }
    }

    if (isStairs()) {
      weight *= options.stairsReluctance;
    } else {
      // TODO: this is being applied even when biking or driving.
      weight *= options.walkReluctance;
    }

    StateEditor s1 = s0.edit(this);
    s1.setBackMode(traverseMode);
    s1.setBackWalkingBike(walkingBike);

    /* Handle no through traffic areas. */
    if (this.isNoThruTraffic()) {
      // Record transition into no-through-traffic area.
      if (backEdge instanceof StreetEdge && !((StreetEdge) backEdge).isNoThruTraffic()) {
        s1.setEnteredNoThroughTrafficArea();
      }
      // If we transitioned into a no-through-traffic area at some point, check if we are exiting
      // it.
      if (s1.hasEnteredNoThroughTrafficArea()) {
        // Only Edges are marked as no-thru, but really we need to avoid creating dominant, pruned
        // states
        // on thru _Vertices_. This could certainly be improved somehow.
        for (StreetEdge se : Iterables.filter(s1.getVertex().getOutgoing(), StreetEdge.class)) {
          if (!se.isNoThruTraffic()) {
            // This vertex has at least one through-traffic edge. We can't dominate it with a
            // no-thru state.
            return null;
          }
        }
      }
    }

    /* Compute turn cost. */
    StreetEdge backPSE;
    if (backEdge != null && backEdge instanceof StreetEdge) {
      backPSE = (StreetEdge) backEdge;
      RoutingRequest backOptions =
          backWalkingBike ? s0.getOptions().bikeWalkingOptions : s0.getOptions();
      double backSpeed = backPSE.calculateSpeed(backOptions, backMode, s0.getTimeInMillis());
      final double realTurnCost; // Units are seconds.

      // Apply turn restrictions
      if (options.arriveBy && !canTurnOnto(backPSE, s0, backMode)) {
        return null;
      } else if (!options.arriveBy && !backPSE.canTurnOnto(this, s0, traverseMode)) {
        return null;
      }

      /*
       * This is a subtle piece of code. Turn costs are evaluated differently during
       * forward and reverse traversal. During forward traversal of an edge, the turn
       * *into* that edge is used, while during reverse traversal, the turn *out of*
       * the edge is used.
       *
       * However, over a set of edges, the turn costs must add up the same (for
       * general correctness and specifically for reverse optimization). This means
       * that during reverse traversal, we must also use the speed for the mode of
       * the backEdge, rather than of the current edge.
       */
      if (options.arriveBy && tov instanceof IntersectionVertex) { // arrive-by search
        IntersectionVertex traversedVertex = ((IntersectionVertex) tov);

        realTurnCost =
            backOptions
                .getIntersectionTraversalCostModel()
                .computeTraversalCost(
                    traversedVertex,
                    this,
                    backPSE,
                    backMode,
                    backOptions,
                    (float) speed,
                    (float) backSpeed);
      } else if (!options.arriveBy && fromv instanceof IntersectionVertex) { // depart-after search
        IntersectionVertex traversedVertex = ((IntersectionVertex) fromv);

        realTurnCost =
            options
                .getIntersectionTraversalCostModel()
                .computeTraversalCost(
                    traversedVertex,
                    backPSE,
                    this,
                    traverseMode,
                    options,
                    (float) backSpeed,
                    (float) speed);
      } else {
        // In case this is a temporary edge not connected to an IntersectionVertex
        LOG.debug("Not computing turn cost for edge {}", this);
        realTurnCost = 0;
      }

      if (!traverseMode.isDriving()) {
        s1.incrementWalkDistance(realTurnCost / 100); // just a tie-breaker
      }

      long turnTime = (long) Math.ceil(realTurnCost);
      time += turnTime;
      weight += options.turnReluctance * realTurnCost;
    }

    if (walkingBike || TraverseMode.BICYCLE.equals(traverseMode)) {
      if (!(backWalkingBike || TraverseMode.BICYCLE.equals(backMode))) {
        s1.incrementTimeInSeconds(options.bikeSwitchTime);
        s1.incrementWeight(options.bikeSwitchCost);
      }
    }

    if (!traverseMode.isDriving()) {
      s1.incrementWalkDistance(getDistance());
    }

    /* On the pre-kiss/pre-park leg, limit both walking and driving, either soft or hard. */
    int roundedTime = (int) Math.ceil(time);
    if (options.kissAndRide || options.parkAndRide) {
      if (options.arriveBy) {
        if (!s0.isCarParked()) s1.incrementPreTransitTime(roundedTime);
      } else {
        if (!s0.isEverBoarded()) s1.incrementPreTransitTime(roundedTime);
      }
      if (s1.isMaxPreTransitTimeExceeded(options)) {
        if (options.softPreTransitLimiting) {
          weight +=
              calculateOverageWeight(
                  s0.getPreTransitTime(),
                  s1.getPreTransitTime(),
                  options.maxPreTransitTime,
                  options.preTransitPenalty,
                  options.preTransitOverageRate);
        } else return null;
      }
    }

    /* Apply a strategy for avoiding walking too far, either soft (weight increases) or hard limiting (pruning). */
    if (s1.weHaveWalkedTooFar(options)) {

      // if we're using a soft walk-limit
      if (options.softWalkLimiting) {
        // just slap a penalty for the overage onto s1
        weight +=
            calculateOverageWeight(
                s0.getWalkDistance(),
                s1.getWalkDistance(),
                options.getMaxWalkDistance(),
                options.softWalkPenalty,
                options.softWalkOverageRate);
      } else {
        // else, it's a hard limit; bail
        LOG.debug("Too much walking. Bailing.");
        return null;
      }
    }

    s1.incrementTimeInSeconds(roundedTime);

    s1.incrementWeight(weight);

    return s1;
  }