/**
  * Makes a new empty leg from a starting edge
  *
  * @param itinerary
  */
 private Leg makeLeg(Itinerary itinerary, State s) {
   Leg leg = new Leg();
   itinerary.addLeg(leg);
   leg.startTime = makeCalendar(s.getBackState());
   EdgeNarrative en = s.getBackEdgeNarrative();
   leg.distance = 0.0;
   leg.from = makePlace(s.getBackState(), false);
   leg.mode = en.getMode().toString();
   return leg;
 }
 private void finalizeLeg(
     Leg leg,
     State state,
     List<State> states,
     int start,
     int end,
     CoordinateArrayListSequence coordinates) {
   if (start != -1) {
     leg.walkSteps = getWalkSteps(states.subList(start, end + 1));
   }
   leg.endTime = makeCalendar(state.getBackState());
   Geometry geometry = GeometryUtils.getGeometryFactory().createLineString(coordinates);
   leg.legGeometry = PolylineEncoder.createEncodings(geometry);
   leg.to = makePlace(state, true);
   coordinates.clear();
 }
 private Set<Alert> addNotesToLeg(Leg leg, Set<Alert> notes) {
   if (notes != null) {
     for (Alert note : notes) {
       leg.addAlert(note);
     }
   }
   return notes;
 }
 private Set<Alert> addNotesToLeg(Leg leg, EdgeNarrative edgeNarrative) {
   Set<Alert> notes = edgeNarrative.getNotes();
   if (notes != null) {
     for (Alert note : notes) {
       leg.addAlert(note);
     }
   }
   return notes;
 }
  private void finalizeLeg(
      Leg leg,
      State state,
      List<State> states,
      int start,
      int end,
      CoordinateArrayListSequence coordinates,
      Itinerary itinerary) {

    // this leg has already been added to the itinerary, so we actually want the penultimate leg, if
    // any
    if (states != null) {
      int extra = 0;
      WalkStep continuation = null;
      if (itinerary.legs.size() >= 2) {
        Leg previousLeg = itinerary.legs.get(itinerary.legs.size() - 2);
        if (previousLeg.walkSteps != null) {
          continuation = previousLeg.walkSteps.get(previousLeg.walkSteps.size() - 1);
          extra = 1;
        }
      }
      if (end == states.size() - 1) {
        extra = 1;
      }

      leg.walkSteps = getWalkSteps(states.subList(start, end + extra), continuation);
    }
    leg.endTime = makeCalendar(state.getBackState());
    Geometry geometry = GeometryUtils.getGeometryFactory().createLineString(coordinates);
    leg.legGeometry = PolylineEncoder.createEncodings(geometry);
    Edge backEdge = state.getBackEdge();
    String name;
    if (backEdge instanceof StreetEdge) {
      name = backEdge.getName();
    } else {
      name = state.getVertex().getName();
    }
    leg.to = makePlace(state, name, true);
    coordinates.clear();
  }
 /**
  * Makes a new empty leg from a starting edge
  *
  * @param itinerary
  */
 private Leg makeLeg(Itinerary itinerary, State s) {
   Leg leg = new Leg();
   itinerary.addLeg(leg);
   leg.startTime = makeCalendar(s.getBackState());
   leg.distance = 0.0;
   String name;
   Edge backEdge = s.getBackEdge();
   if (backEdge instanceof StreetEdge) {
     name = backEdge.getName();
   } else {
     name = s.getVertex().getName();
   }
   leg.from = makePlace(s.getBackState(), name, false);
   leg.mode = s.getBackMode().toString();
   if (s.isBikeRenting()) {
     leg.rentedBike = true;
   }
   return leg;
 }
  private Direction decryptNonTransit(Leg leg) {
    Direction direction = new Direction();

    //
    //	http://opentripplanner.usf.edu/opentripplanner-api-webapp/ws/plan?optimize=QUICK&time=09:24pm&arriveBy=false&wheelchair=false&maxWalkDistance=7600.0&fromPlace=28.033389%2C+-82.521034&toPlace=28.064709%2C+-82.471618&date=03/07/12&mode=WALK,TRAM,SUBWAY,RAIL,BUS,FERRY,CABLE_CAR,GONDOLA,FUNICULAR,TRANSIT,TRAINISH,BUSISH

    // Get appropriate action and icon
    String action = "Walk";
    int icon = R.drawable.mode_walk;
    TraverseMode mode = TraverseMode.valueOf((String) leg.mode);
    if (mode.compareTo(TraverseMode.BICYCLE) == 0) {
      action = "Bike";
      icon = R.drawable.mode_bicycle;
    } else if (mode.compareTo(TraverseMode.CAR) == 0) {
      action = "Drive";
      icon = R.drawable.icon;
    }

    direction.setIcon(icon);

    //		Main direction
    Place fromPlace = leg.from;
    Place toPlace = leg.to;
    String mainDirectionText = action;
    mainDirectionText += fromPlace.name == null ? "" : " from " + fromPlace.name;
    mainDirectionText += toPlace.name == null ? "" : " to " + toPlace.name;
    mainDirectionText +=
        toPlace.stopId == null
            ? ""
            : " (" + toPlace.stopId.getAgencyId() + " " + toPlace.stopId.getId() + ")";
    //		double duration = DateTimeConversion.getDuration(leg.startTime, leg.endTime);
    double totalDistance = leg.distance;
    DecimalFormat twoDForm = new DecimalFormat("#.##");
    totalDistance = Double.valueOf(twoDForm.format(totalDistance));
    mainDirectionText += "\n[" + totalDistance + "meters ]"; // Double.toString(duration);

    direction.setDirectionText(mainDirectionText);

    //		Sub-direction
    List<WalkStep> walkSteps = leg.getSteps();

    if (walkSteps == null) return direction;

    ArrayList<Direction> subDirections = new ArrayList<Direction>(walkSteps.size());

    for (WalkStep step : walkSteps) {
      Direction dir = new Direction();
      String subDirectionText = "";

      double distance = step.distance;
      // Distance traveled [distance]
      distance = Double.valueOf(twoDForm.format(distance));
      dir.setDistanceTraveled(distance);

      RelativeDirection relativeDir = step.relativeDirection;
      String streetName = step.streetName;
      AbsoluteDirection absoluteDir = step.absoluteDirection;
      String exit = step.exit;
      boolean isStayOn = (step.stayOn == null ? false : step.stayOn);
      boolean isBogusName = (step.bogusName == null ? false : step.bogusName);
      double lon = step.lon;
      double lat = step.lat;
      String elevation = step.elevation;
      List<Alerts> alert = step.alerts;

      // Walk East
      if (relativeDir == null) {
        subDirectionText += action + " ";
        subDirectionText += absoluteDir.name() + " ";
      }
      // (Turn left)/(Continue)
      else {
        if (!isStayOn) {
          RelativeDirection rDir = RelativeDirection.valueOf(relativeDir.name());

          // Do not need TURN Continue
          if (rDir.compareTo(RelativeDirection.CONTINUE) != 0
              && rDir.compareTo(RelativeDirection.CIRCLE_CLOCKWISE) != 0
              && rDir.compareTo(RelativeDirection.CIRCLE_COUNTERCLOCKWISE) != 0) {
            subDirectionText += "Turn ";
          }

          subDirectionText += relativeDir.name() + " ";
        } else {
          subDirectionText += relativeDir.name() + " ";
        }
      }

      // (on ABC)
      //			if(!isBogusName) {
      //				subDirectionText += "on "+ streetName + " ";
      //			}

      subDirectionText += "on " + streetName + " ";

      subDirectionText += "\n[" + Double.toString(distance) + "meters ]";

      dir.setDirectionText(subDirectionText);

      dir.setIcon(icon);

      // Add new sub-direction
      subDirections.add(dir);
    }

    direction.setSubDirections(subDirections);

    return direction;
  }
  private ArrayList<Direction> decryptTransit(Leg leg) {
    ArrayList<Direction> directions = new ArrayList<Direction>(2);
    Direction onDirection = new Direction();
    Direction offDirection = new Direction();

    //		set icon
    TraverseMode mode = TraverseMode.valueOf((String) leg.mode);
    int icon = R.drawable.mode_bus;
    if (mode.compareTo(TraverseMode.RAIL) == 0) {
      icon = R.drawable.mode_rail;
    } else if (mode.compareTo(TraverseMode.FERRY) == 0) {
      icon = R.drawable.mode_ferry;
    } else if (mode.compareTo(TraverseMode.GONDOLA) == 0) {
      icon = R.drawable.mode_gondola;
    } else if (mode.compareTo(TraverseMode.SUBWAY) == 0) {
      icon = R.drawable.mode_subway;
    } else if (mode.compareTo(TraverseMode.TRAM) == 0) {
      icon = R.drawable.mode_tram;
    }

    onDirection.setIcon(icon);

    //		set direction text
    String onDirectionText = "";
    String offDirectionText = "";

    String route = leg.route;
    String agencyName = leg.agencyName;
    String agencyUrl = leg.agencyId;
    String routeColor = leg.routeColor;
    String routeTextColor = leg.routeTextColor;
    boolean isInterlineWithPreviousLeg =
        (leg.interlineWithPreviousLeg == null ? false : leg.interlineWithPreviousLeg);
    String tripShortName = leg.tripShortName;
    String headsign = leg.headsign;
    String agencyId = leg.agencyId;
    String routeShortName = leg.routeShortName;
    String routeLongName = leg.routeLongName;
    String boardRule = leg.boardRule;
    String alignRule = leg.alightRule;

    ArrayList<Place> stopsInBetween = new ArrayList<Place>();
    if (leg.getIntermediateStops() != null) stopsInBetween.addAll(leg.getIntermediateStops());

    double distance = leg.distance;
    Place from = leg.from;
    AgencyAndId agencyAndIdFrom = from.stopId;
    Place to = leg.to;
    AgencyAndId agencyAndIdTo = to.stopId;
    long duration = leg.duration;

    // Get on HART BUS 6
    String serviceName = agencyName;
    if (serviceName == null) serviceName = agencyId;

    offDirectionText += "Get off " + serviceName + " " + mode + " " + route + "\n";
    offDirectionText +=
        "At " + to.name + " (" + agencyAndIdTo.getAgencyId() + " " + agencyAndIdTo.getId() + ")";
    offDirection.setDirectionText(offDirectionText);
    offDirection.setIcon(icon);

    // Only onDirection has subdirection (list of stops in between)
    onDirectionText += "Get on " + serviceName + " " + mode + " " + route + "\n";
    onDirectionText +=
        "At "
            + from.name
            + " ("
            + agencyAndIdFrom.getAgencyId()
            + " "
            + agencyAndIdFrom.getId()
            + ")\n";
    onDirectionText += stopsInBetween.size() + " stops in between";
    onDirection.setDirectionText(onDirectionText);
    onDirection.setIcon(icon);

    // sub-direction
    ArrayList<Direction> subDirections = new ArrayList<Direction>();
    for (int i = 0; i < stopsInBetween.size(); i++) {
      Direction subDirection = new Direction();

      Place stop = stopsInBetween.get(i);
      AgencyAndId agencyAndIdStop = stop.stopId;
      String subDirectionText =
          Integer.toString(i)
              + ". "
              + stop.name
              + " ("
              + agencyAndIdStop.getAgencyId()
              + " "
              + agencyAndIdStop.getId()
              + ")";

      subDirection.setDirectionText(subDirectionText);
      subDirection.setIcon(icon);

      subDirections.add(subDirection);
    }
    onDirection.setSubDirections(subDirections);

    // Distance traveled [distance]
    DecimalFormat twoDForm = new DecimalFormat("#.##");
    distance = Double.valueOf(twoDForm.format(distance));
    onDirection.setDistanceTraveled(distance);

    directions.add(onDirection);
    directions.add(offDirection);

    return directions;
  }
 private void fixupTransitLeg(Leg leg, State state, TransitIndexService transitIndex) {
   EdgeNarrative en = state.getBackEdgeNarrative();
   leg.route = en.getName();
   Trip trip = en.getTrip();
   if (trip != null) {
     leg.headsign = trip.getTripHeadsign();
     leg.tripId = trip.getId().getId();
     leg.agencyId = trip.getId().getAgencyId();
     leg.tripShortName = trip.getTripShortName();
     leg.routeShortName = trip.getRoute().getShortName();
     leg.routeLongName = trip.getRoute().getLongName();
     leg.routeColor = trip.getRoute().getColor();
     leg.routeTextColor = trip.getRoute().getTextColor();
     if (transitIndex != null) {
       Agency agency = transitIndex.getAgency(leg.agencyId);
       leg.agencyName = agency.getName();
       leg.agencyUrl = agency.getUrl();
     }
   }
   leg.mode = en.getMode().toString();
   leg.startTime = makeCalendar(state.getBackState());
 }
  /**
   * Generate an itinerary from a @{link GraphPath}. The algorithm here is to walk over each state
   * in the graph path, accumulating geometry, time, and length data from the incoming edge. When
   * the incoming edge and outgoing edge have different modes (or when a vehicle changes names due
   * to interlining) a new leg is generated. Street legs undergo an additional processing step to
   * generate turn-by-turn directions.
   *
   * @param path
   * @param showIntermediateStops whether intermediate stops are included in the generated itinerary
   * @return itinerary
   */
  private Itinerary generateItinerary(GraphPath path, boolean showIntermediateStops) {
    Graph graph = path.getRoutingContext().graph;
    TransitIndexService transitIndex = graph.getService(TransitIndexService.class);

    Itinerary itinerary = makeEmptyItinerary(path);
    EdgeNarrative postponedAlerts = null;
    Leg leg = null;
    CoordinateArrayListSequence coordinates = new CoordinateArrayListSequence();
    double previousElevation = Double.MAX_VALUE;
    int startWalk = -1;
    int i = -1;
    PlanGenState pgstate = PlanGenState.START;
    String nextName = null;
    for (State state : path.states) {
      i += 1;
      Edge backEdge = state.getBackEdge();
      EdgeNarrative backEdgeNarrative = state.getBackEdgeNarrative();
      if (backEdge == null) {
        continue;
      }

      TraverseMode mode = backEdgeNarrative.getMode();
      if (mode != null) {
        long dt = state.getAbsTimeDeltaSec();
        if (mode == TraverseMode.BOARDING
            || mode == TraverseMode.ALIGHTING
            || mode == TraverseMode.STL) {
          itinerary.waitingTime += dt;
        } else if (mode.isOnStreetNonTransit()) {
          itinerary.walkDistance += backEdgeNarrative.getDistance();
          itinerary.walkTime += dt;
        } else if (mode.isTransit()) {
          itinerary.transitTime += dt;
        }
      }

      if (backEdge instanceof FreeEdge) {
        if (backEdge instanceof PreBoardEdge) {
          // Add boarding alerts to the next leg
          postponedAlerts = backEdgeNarrative;
        } else if (backEdge instanceof PreAlightEdge) {
          // Add alighting alerts to the previous leg
          addNotesToLeg(itinerary.legs.get(itinerary.legs.size() - 1), backEdgeNarrative);
        }
        continue;
      }

      if (backEdge instanceof EdgeWithElevation) {
        PackedCoordinateSequence profile = ((EdgeWithElevation) backEdge).getElevationProfile();
        previousElevation = applyElevation(profile, itinerary, previousElevation);
      }

      switch (pgstate) {
        case START:
          if (mode == TraverseMode.WALK) {
            pgstate = PlanGenState.WALK;
            leg = makeLeg(itinerary, state);
            leg.from.orig = nextName;
            startWalk = i;
          } else if (mode == TraverseMode.BICYCLE) {
            pgstate = PlanGenState.BICYCLE;
            leg = makeLeg(itinerary, state);
            leg.from.orig = nextName;
            startWalk = i;
          } else if (mode == TraverseMode.CAR) {
            pgstate = PlanGenState.CAR;
            leg = makeLeg(itinerary, state);
            leg.from.orig = nextName;
            startWalk = i;
          } else if (mode == TraverseMode.BOARDING) {
            // this itinerary starts with transit
            pgstate = PlanGenState.PRETRANSIT;
            leg = makeLeg(itinerary, state);
            leg.from.orig = nextName;
            startWalk = -1;
          } else if (mode == TraverseMode.STL) {
            // this comes after an alight; do nothing
          } else if (mode == TraverseMode.TRANSFER) {
            // handle the whole thing in one step
            leg = makeLeg(itinerary, state);
            coordinates = new CoordinateArrayListSequence();
            coordinates.add(state.getBackState().getVertex().getCoordinate());
            coordinates.add(state.getVertex().getCoordinate());
            finalizeLeg(leg, state, path.states, i, i, coordinates);
            coordinates.clear();
          } else {
            LOG.error("Unexpected state (in START): " + mode);
          }
          break;
        case WALK:
          if (leg == null) {
            leg = makeLeg(itinerary, state);
          }
          if (mode == TraverseMode.WALK) {
            // do nothing
          } else if (mode == TraverseMode.BICYCLE) {
            finalizeLeg(leg, state, path.states, startWalk, i, coordinates);
            startWalk = i;
            leg = makeLeg(itinerary, state);
            if (backEdge instanceof RentABikeOnEdge) {
              leg.rentedBike = true;
            }
            pgstate = PlanGenState.BICYCLE;
          } else if (mode == TraverseMode.STL) {
            finalizeLeg(leg, state, path.states, startWalk, i, coordinates);
            leg = null;
            pgstate = PlanGenState.PRETRANSIT;
          } else if (mode == TraverseMode.BOARDING) {
            // this only happens in case of a timed transfer.
            pgstate = PlanGenState.PRETRANSIT;
            finalizeLeg(leg, state, path.states, startWalk, i, coordinates);
            leg = makeLeg(itinerary, state);
            itinerary.transfers++;
          } else if (backEdgeNarrative instanceof LegSwitchingEdge) {
            nextName = state.getBackState().getBackState().getBackState().getVertex().getName();
            finalizeLeg(leg, state, path.states, startWalk, i - 1, coordinates);
            leg = null;
            pgstate = PlanGenState.START;
          } else {
            LOG.error("Unexpected state (in WALK): " + mode);
          }
          break;
        case BICYCLE:
          if (leg == null) {
            leg = makeLeg(itinerary, state);
          }
          if (mode == TraverseMode.BICYCLE) {
            // do nothing
          } else if (mode == TraverseMode.WALK) {
            finalizeLeg(leg, state, path.states, startWalk, i, coordinates);
            leg = makeLeg(itinerary, state);
            startWalk = i;
            pgstate = PlanGenState.WALK;
          } else if (mode == TraverseMode.STL) {
            finalizeLeg(leg, state, path.states, startWalk, i, coordinates);
            leg = null;
            pgstate = PlanGenState.PRETRANSIT;
          } else if (backEdgeNarrative instanceof LegSwitchingEdge) {
            finalizeLeg(leg, state, path.states, startWalk, i - 1, coordinates);
            leg = null;
            pgstate = PlanGenState.START;
          } else {
            LOG.error("Unexpected state (in BICYCLE): " + mode);
          }
          break;
        case CAR:
          if (leg == null) {
            leg = makeLeg(itinerary, state);
          }
          if (mode == TraverseMode.CAR) {
            // do nothing
          } else if (mode == TraverseMode.STL) {
            finalizeLeg(leg, state, path.states, startWalk, i, coordinates);
            leg = null;
            pgstate = PlanGenState.PRETRANSIT;
          } else if (backEdgeNarrative instanceof LegSwitchingEdge) {
            finalizeLeg(leg, state, path.states, startWalk, i - 1, coordinates);
            leg = null;
            pgstate = PlanGenState.START;
          } else {
            LOG.error("Unexpected state (in CAR): " + mode);
          }
          break;
        case PRETRANSIT:
          if (mode == TraverseMode.BOARDING) {
            if (leg != null) {
              LOG.error("leg unexpectedly not null (boarding loop)");
            } else {
              leg = makeLeg(itinerary, state);
              leg.stop = new ArrayList<Place>();
              itinerary.transfers++;
              leg.boardRule = (String) state.getExtension("boardAlightRule");
            }
          } else if (backEdge instanceof HopEdge) {
            pgstate = PlanGenState.TRANSIT;
            fixupTransitLeg(leg, state, transitIndex);
            leg.stop = new ArrayList<Place>();
          } else {
            LOG.error("Unexpected state (in PRETRANSIT): " + mode);
          }
          break;
        case TRANSIT:
          String route = backEdgeNarrative.getName();
          if (mode == TraverseMode.ALIGHTING) {
            if (showIntermediateStops && leg.stop != null && leg.stop.size() > 0) {
              if (leg.stop.isEmpty()) {
                leg.stop = null;
              }
            }
            leg.alightRule = (String) state.getExtension("boardAlightRule");
            finalizeLeg(leg, state, null, -1, -1, coordinates);
            leg = null;
            pgstate = PlanGenState.START;
          } else if (mode.toString().equals(leg.mode)) {
            // no mode change, handle intermediate stops
            if (showIntermediateStops) {
              /*
               * any further transit edge, add "from" vertex to intermediate stops
               */
              if (!(backEdge instanceof DwellEdge)) {
                Place stop = makePlace(state.getBackState(), true);
                leg.stop.add(stop);
              } else if (leg.stop.size() > 0) {
                leg.stop.get(leg.stop.size() - 1).departure = makeCalendar(state);
              }
            }
            if (!route.equals(leg.route)) {
              // interline dwell
              finalizeLeg(leg, state, null, -1, -1, coordinates);
              leg = makeLeg(itinerary, state);
              leg.stop = new ArrayList<Place>();
              fixupTransitLeg(leg, state, transitIndex);
              leg.startTime = makeCalendar(state);
              leg.interlineWithPreviousLeg = true;
            }
          } else {
            LOG.error("Unexpected state (in TRANSIT): " + mode);
          }
          break;
      }
      if (leg != null) {
        leg.distance += backEdgeNarrative.getDistance();
        Geometry edgeGeometry = backEdgeNarrative.getGeometry();
        if (edgeGeometry != null) {
          Coordinate[] edgeCoordinates = edgeGeometry.getCoordinates();
          if (coordinates.size() > 0
              && coordinates.getCoordinate(coordinates.size() - 1).equals(edgeCoordinates[0])) {
            coordinates.extend(edgeCoordinates, 1);
          } else {
            coordinates.extend(edgeCoordinates);
          }
        }

        if (postponedAlerts != null) {
          addNotesToLeg(leg, postponedAlerts);
          postponedAlerts = null;
        }

        addNotesToLeg(leg, backEdgeNarrative);
      }
    } /* end loop over graphPath edge list */

    if (leg != null) {
      finalizeLeg(leg, path.states.getLast(), path.states, startWalk, i, coordinates);
    }
    itinerary.removeBogusLegs();
    itinerary.fixupDates(graph.getService(CalendarServiceData.class));
    if (itinerary.legs.size() == 0) throw new TrivialPathException();
    return itinerary;
  }
  private void fixupTransitLeg(Leg leg, State state, TransitIndexService transitIndex) {
    Edge en = state.getBackEdge();
    leg.route = en.getName();
    Trip trip = state.getBackTrip();
    leg.headsign = state.getBackDirection();
    if (trip != null) {
      // this is the stop headsign
      // leg.headsign = "This is the headsign";
      // handle no stop headsign
      if (leg.headsign == null) leg.headsign = trip.getTripHeadsign();

      leg.tripId = trip.getId().getId();
      leg.agencyId = trip.getId().getAgencyId();
      leg.tripShortName = trip.getTripShortName();
      leg.routeShortName = trip.getRoute().getShortName();
      leg.routeLongName = trip.getRoute().getLongName();
      leg.routeColor = trip.getRoute().getColor();
      leg.routeTextColor = trip.getRoute().getTextColor();
      leg.routeId = trip.getRoute().getId().getId();
      if (transitIndex != null) {
        Agency agency = transitIndex.getAgency(leg.agencyId);
        leg.agencyName = agency.getName();
        leg.agencyUrl = agency.getUrl();
      }
    }
    leg.mode = state.getBackMode().toString();
    leg.startTime = makeCalendar(state.getBackState());
  }
  /**
   * Generate an itinerary from a @{link GraphPath}. The algorithm here is to walk over each state
   * in the graph path, accumulating geometry, time, and length data from the incoming edge. When
   * the incoming edge and outgoing edge have different modes (or when a vehicle changes names due
   * to interlining) a new leg is generated. Street legs undergo an additional processing step to
   * generate turn-by-turn directions.
   *
   * @param path
   * @param showIntermediateStops whether intermediate stops are included in the generated itinerary
   * @return itinerary
   */
  private Itinerary generateItinerary(GraphPath path, boolean showIntermediateStops) {
    Graph graph = path.getRoutingContext().graph;
    TransitIndexService transitIndex = graph.getService(TransitIndexService.class);

    Itinerary itinerary = makeEmptyItinerary(path);
    Set<Alert> postponedAlerts = null;
    Leg leg = null;
    CoordinateArrayListSequence coordinates = new CoordinateArrayListSequence();
    double previousElevation = Double.MAX_VALUE;
    int startWalk = -1;
    int i = -1;
    boolean foldingElevatorLegIntoCycleLeg = false;
    PlanGenState pgstate = PlanGenState.START;
    String nextName = null;
    for (State state : path.states) {
      i += 1;
      Edge backEdge = state.getBackEdge();
      if (backEdge == null) {
        continue;
      }

      // debug: push vehicle late status out to UI
      //            if (backEdge instanceof PatternHop) {
      //                TripTimes tt = state.getTripTimes();
      //                int hop = ((PatternHop)backEdge).stopIndex;
      //                LOG.info("{} {}", tt.getTrip().toString(), hop);
      //                if ( ! tt.isScheduled()) {
      //                    int delay = tt.getDepartureDelay(hop);
      //                    String d = "on time";
      //                    if (Math.abs(delay) > 10) {
      //                        d = String.format("%2.1f min %s", delay / 60.0,
      //                                (delay < 0) ? "early" : "late");
      //                    }
      //                    d = "Using real-time delay information: ".concat(d);
      //                    leg.addAlert(Alert.createSimpleAlerts(d));
      //                    LOG.info(d);
      //                }
      //                else {
      //                    leg.addAlert(Alert.createSimpleAlerts("Using published timetables."));
      //                    LOG.info("sched");
      //                }
      //            }

      TraverseMode mode = state.getBackMode();
      if (mode != null) {
        long dt = state.getAbsTimeDeltaSec();
        if (mode == TraverseMode.BOARDING
            || mode == TraverseMode.ALIGHTING
            || mode == TraverseMode.STL) {
          itinerary.waitingTime += dt;
        } else if (mode.isOnStreetNonTransit()) {
          itinerary.walkDistance += backEdge.getDistance();
          itinerary.walkTime += dt;
        } else if (mode.isTransit()) {
          itinerary.transitTime += dt;
        }
      }

      if (backEdge instanceof FreeEdge) {
        if (backEdge instanceof PreBoardEdge) {
          // Add boarding alerts to the next leg
          postponedAlerts = state.getBackAlerts();
        } else if (backEdge instanceof PreAlightEdge) {
          // Add alighting alerts to the previous leg
          addNotesToLeg(itinerary.legs.get(itinerary.legs.size() - 1), state.getBackAlerts());
        }
        continue;
      }

      if (backEdge instanceof EdgeWithElevation) {
        PackedCoordinateSequence profile = ((EdgeWithElevation) backEdge).getElevationProfile();
        previousElevation = applyElevation(profile, itinerary, previousElevation);
      }

      switch (pgstate) {
        case START:
          if (mode == TraverseMode.WALK) {
            pgstate = PlanGenState.WALK;
            leg = makeLeg(itinerary, state);
            leg.from.orig = nextName;
            startWalk = i;
          } else if (mode == TraverseMode.BICYCLE) {
            pgstate = PlanGenState.BICYCLE;
            leg = makeLeg(itinerary, state);
            leg.from.orig = nextName;
            startWalk = i;
          } else if (mode == TraverseMode.CAR) {
            pgstate = PlanGenState.CAR;
            leg = makeLeg(itinerary, state);
            leg.from.orig = nextName;
            startWalk = i;
          } else if (mode == TraverseMode.BOARDING) {
            // this itinerary starts with transit
            pgstate = PlanGenState.PRETRANSIT;
            leg = makeLeg(itinerary, state);
            leg.from.orig = nextName;
            itinerary.transfers++;
            startWalk = -1;
          } else if (mode == TraverseMode.STL) {
            // this comes after an alight; do nothing
          } else if (mode == TraverseMode.TRANSFER) {
            // handle the whole thing in one step
            leg = makeLeg(itinerary, state);
            coordinates = new CoordinateArrayListSequence();
            coordinates.add(state.getBackState().getVertex().getCoordinate());
            coordinates.add(state.getVertex().getCoordinate());
            finalizeLeg(leg, state, path.states, i, i, coordinates, itinerary);
            coordinates.clear();
          } else {
            LOG.error("Unexpected state (in START): " + mode);
          }
          break;
        case WALK:
          if (leg == null) {
            leg = makeLeg(itinerary, state);
          }
          if (mode == TraverseMode.WALK) {
            // do nothing
          } else if (mode == TraverseMode.BICYCLE) {
            finalizeLeg(leg, state, path.states, startWalk, i, coordinates, itinerary);
            startWalk = i;
            leg = makeLeg(itinerary, state);
            pgstate = PlanGenState.BICYCLE;
          } else if (mode == TraverseMode.STL) {
            finalizeLeg(leg, state, path.states, startWalk, i, coordinates, itinerary);
            leg = null;
            pgstate = PlanGenState.PRETRANSIT;
          } else if (mode == TraverseMode.BOARDING) {
            // this only happens in case of a timed transfer.
            pgstate = PlanGenState.PRETRANSIT;
            finalizeLeg(leg, state, path.states, startWalk, i, coordinates, itinerary);
            leg = makeLeg(itinerary, state);
            itinerary.transfers++;
          } else if (backEdge instanceof LegSwitchingEdge) {
            nextName = state.getBackState().getBackState().getBackState().getVertex().getName();
            finalizeLeg(leg, state, path.states, startWalk, i - 1, coordinates, itinerary);
            leg = null;
            pgstate = PlanGenState.START;
          } else {
            LOG.error("Unexpected state (in WALK): " + mode);
          }
          break;
        case BICYCLE:
          if (leg == null) {
            leg = makeLeg(itinerary, state);
          }

          // If there are elevator edges that have mode == BICYCLE on both sides, they should
          // be folded into the bicycle leg. But ones with walk on one side or the other should
          // not
          if (state.getBackEdge() instanceof ElevatorBoardEdge) {
            int j = i + 1;
            // proceed forward from the current state until we find one that isn't on an
            // elevator, and check the traverse mode
            while (path.states.get(j).getBackEdge() instanceof ElevatorEdge) j++;

            // path.states[j] is not an elevator edge
            if (path.states.get(j).getBackMode() == TraverseMode.BICYCLE)
              foldingElevatorLegIntoCycleLeg = true;
          }

          if (foldingElevatorLegIntoCycleLeg) {
            if (state.getBackEdge() instanceof ElevatorEdge) {
              break; // from the case
            } else {
              foldingElevatorLegIntoCycleLeg = false;
              // do not break but allow it to be processed below (which will do nothing)
            }
          }

          if (mode == TraverseMode.BICYCLE) {
            // do nothing
          } else if (mode == TraverseMode.WALK) {
            finalizeLeg(leg, state, path.states, startWalk, i, coordinates, itinerary);
            leg = makeLeg(itinerary, state);
            startWalk = i;
            pgstate = PlanGenState.WALK;
          } else if (mode == TraverseMode.STL) {
            finalizeLeg(leg, state, path.states, startWalk, i, coordinates, itinerary);
            startWalk = i;
            leg = null;
            pgstate = PlanGenState.PRETRANSIT;
          } else if (backEdge instanceof LegSwitchingEdge) {
            finalizeLeg(leg, state, path.states, startWalk, i - 1, coordinates, itinerary);
            leg = null;
            pgstate = PlanGenState.START;
          } else {
            LOG.error("Unexpected state (in BICYCLE): " + mode);
          }
          break;
        case CAR:
          if (leg == null) {
            leg = makeLeg(itinerary, state);
          }
          if (mode == TraverseMode.CAR) {
            // do nothing
          } else if (mode == TraverseMode.STL) {
            finalizeLeg(leg, state, path.states, startWalk, i, coordinates, itinerary);
            leg = null;
            pgstate = PlanGenState.PRETRANSIT;
          } else if (backEdge instanceof LegSwitchingEdge) {
            finalizeLeg(leg, state, path.states, startWalk, i - 1, coordinates, itinerary);
            leg = null;
            pgstate = PlanGenState.START;
          } else {
            LOG.error("Unexpected state (in CAR): " + mode);
          }
          break;
        case PRETRANSIT:
          if (mode == TraverseMode.BOARDING) {
            if (leg != null) {
              LOG.error("leg unexpectedly not null (boarding loop)");
            } else {
              leg = makeLeg(itinerary, state);
              leg.from.stopIndex = ((OnBoardForwardEdge) backEdge).getStopIndex();
              leg.stop = new ArrayList<Place>();
              itinerary.transfers++;
              leg.boardRule = (String) state.getExtension("boardAlightRule");
            }
          } else if (backEdge instanceof HopEdge) {
            pgstate = PlanGenState.TRANSIT;
            fixupTransitLeg(leg, state, transitIndex);
            leg.stop = new ArrayList<Place>();
          } else {
            LOG.error("Unexpected state (in PRETRANSIT): " + mode);
          }
          break;
        case TRANSIT:
          String route = backEdge.getName();
          if (mode == TraverseMode.ALIGHTING) {
            if (showIntermediateStops && leg.stop != null && leg.stop.size() > 0) {
              if (leg.stop.isEmpty()) {
                leg.stop = null;
              }
            }
            leg.alightRule = (String) state.getExtension("boardAlightRule");
            finalizeLeg(leg, state, null, -1, -1, coordinates, itinerary);
            leg = null;
            pgstate = PlanGenState.START;
          } else if (mode.toString().equals(leg.mode)) {
            // no mode change, handle intermediate stops
            if (showIntermediateStops) {
              /*
               * any further transit edge, add "from" vertex to intermediate stops
               */
              if (!(backEdge instanceof DwellEdge)) {
                Place stop =
                    makePlace(
                        state.getBackState(), state.getBackState().getVertex().getName(), true);
                leg.stop.add(stop);
              } else if (leg.stop.size() > 0) {
                leg.stop.get(leg.stop.size() - 1).departure = makeCalendar(state);
              }
            }
            if (!route.equals(leg.route)) {
              // interline dwell
              finalizeLeg(leg, state, null, -1, -1, coordinates, itinerary);
              leg = makeLeg(itinerary, state);
              leg.stop = new ArrayList<Place>();
              fixupTransitLeg(leg, state, transitIndex);
              leg.startTime = makeCalendar(state);
              leg.interlineWithPreviousLeg = true;
            }
          } else {
            LOG.error("Unexpected state (in TRANSIT): " + mode);
          }
          break;
      }
      if (leg != null) {
        leg.distance += backEdge.getDistance();
        Geometry edgeGeometry = backEdge.getGeometry();
        if (edgeGeometry != null) {
          Coordinate[] edgeCoordinates = edgeGeometry.getCoordinates();
          if (coordinates.size() > 0
              && coordinates.getCoordinate(coordinates.size() - 1).equals(edgeCoordinates[0])) {
            coordinates.extend(edgeCoordinates, 1);
          } else {
            coordinates.extend(edgeCoordinates);
          }
        }

        if (postponedAlerts != null) {
          addNotesToLeg(leg, postponedAlerts);
          postponedAlerts = null;
        }

        addNotesToLeg(leg, state.getBackAlerts());
      }
    } /* end loop over graphPath edge list */

    if (leg != null) {
      finalizeLeg(leg, path.states.getLast(), path.states, startWalk, i, coordinates, itinerary);
    }
    itinerary.removeBogusLegs();
    itinerary.fixupDates(graph.getService(CalendarServiceData.class));
    if (itinerary.legs.size() == 0) throw new TrivialPathException();
    return itinerary;
  }
  public Direction generateTransitSubdirection(Leg leg, boolean isOnDirection) {
    Direction direction = new Direction();
    direction.setRealTimeInfo(leg.realTime);

    // Set icon
    String mode =
        getLocalizedMode(TraverseMode.valueOf(leg.mode), applicationContext.getResources());
    int modeIcon;
    String route;
    String agencyName = leg.agencyName;
    Place from = leg.from;
    Place to = leg.to;
    Calendar newTime = Calendar.getInstance();
    Calendar oldTime = Calendar.getInstance();

    String shortName;
    // As a work-around for #662, we always use routeShortName and not tripShortName
    shortName = leg.routeShortName;

    route = ConversionUtils.getRouteLongNameSafe(leg.routeLongName, shortName, true);

    direction.setTransit(true);

    String action, placeAndHeadsign, extra = "";

    if (isOnDirection) {
      action = applicationContext.getResources().getString(R.string.step_by_step_transit_get_on);
      placeAndHeadsign = from.name;
      TraverseModeSet modeSet = new TraverseModeSet(leg.mode);
      modeIcon = getModeIcon(modeSet);
      newTime.setTime(new Date(Long.parseLong(leg.startTime)));
      oldTime.setTime(new Date(newTime.getTimeInMillis()));
      oldTime.add(Calendar.SECOND, -leg.departureDelay);

      // Only onDirection has subdirection (list of stops in between)
      ArrayList<Place> stopsInBetween = new ArrayList<Place>();
      if ((leg.getIntermediateStops() != null) && !leg.getIntermediateStops().isEmpty()) {
        stopsInBetween.addAll(leg.getIntermediateStops());
      } else if ((leg.stop != null) && !leg.stop.isEmpty()) {
        stopsInBetween.addAll(leg.stop);
      }
      // sub-direction
      ArrayList<Direction> subDirections = new ArrayList<Direction>();
      for (int i = 0; i < stopsInBetween.size(); i++) {
        Direction subDirection = new Direction();

        Place stop = stopsInBetween.get(i);
        String extraStopInformation = stop.stopCode;
        String subDirectionText = Integer.toString(i + 1) + ". " + stop.name;
        if (!TextUtils.isEmpty(extraStopInformation)) {
          subDirectionText += " (" + extraStopInformation + ")";
        }
        subDirection.setDirectionText(subDirectionText);
        subDirection.setIcon(DirectionsGenerator.getStopIcon(modeSet));

        subDirections.add(subDirection);
      }
      direction.setSubDirections(subDirections);

      if (stopsInBetween.size() > 0) {
        String connector =
            applicationContext
                .getResources()
                .getString(R.string.step_by_step_transit_stops_in_between);
        if (stopsInBetween.size() == 1) {
          connector =
              applicationContext
                  .getResources()
                  .getString(R.string.step_by_step_transit_stops_in_between_singular);
        }
        extra = stopsInBetween.size() + " " + connector;
      }

      if (!TextUtils.isEmpty(leg.headsign)) {
        placeAndHeadsign +=
            " "
                + applicationContext
                    .getResources()
                    .getString(R.string.step_by_step_transit_connector_headsign)
                + " "
                + leg.headsign;
      }
    } else {
      action = applicationContext.getResources().getString(R.string.step_by_step_transit_get_off);
      placeAndHeadsign = to.name;
      modeIcon = -1;
      newTime.setTime(new Date(Long.parseLong(leg.endTime)));
      oldTime.setTime(new Date(newTime.getTimeInMillis()));
      oldTime.add(Calendar.SECOND, -leg.arrivalDelay);
    }

    direction.setIcon(modeIcon);
    direction.setPlaceAndHeadsign(
        applicationContext
                .getResources()
                .getString(R.string.step_by_step_transit_connector_stop_name)
            + " "
            + placeAndHeadsign);
    direction.setService(action + " " + mode + " " + route);
    direction.setAgency(agencyName);
    direction.setExtra(extra);

    SpannableString oldTimeString;

    if (leg.realTime) {
      CharSequence newTimeString;
      newTimeString =
          ConversionUtils.getTimeUpdated(
              applicationContext,
              leg.agencyTimeZoneOffset,
              oldTime.getTimeInMillis(),
              newTime.getTimeInMillis());
      direction.setNewTime(newTimeString);
    }

    oldTimeString =
        new SpannableString(
            ConversionUtils.getTimeWithContext(
                applicationContext, leg.agencyTimeZoneOffset, oldTime.getTimeInMillis(), true));
    direction.setOldTime(oldTimeString);

    return direction;
  }
  private Direction generateNonTransitDirections(Leg leg) {
    Direction direction = new Direction();

    // http://opentripplanner.usf.edu/opentripplanner-api-webapp/ws/plan?optimize=QUICK&time=09:24pm&arriveBy=false&wheelchair=false&maxWalkDistance=7600.0&fromPlace=28.033389%2C+-82.521034&toPlace=28.064709%2C+-82.471618&date=03/07/12&mode=WALK,TRAM,SUBWAY,RAIL,BUS,FERRY,CABLE_CAR,GONDOLA,FUNICULAR,TRANSIT,TRAINISH,BUSISH

    // Get appropriate action and icon
    String action =
        applicationContext
            .getResources()
            .getString(R.string.step_by_step_non_transit_mode_walk_action);
    TraverseMode mode = TraverseMode.valueOf((String) leg.mode);
    int icon = getModeIcon(new TraverseModeSet(mode));
    if (mode.compareTo(TraverseMode.BICYCLE) == 0) {
      action =
          applicationContext
              .getResources()
              .getString(R.string.step_by_step_non_transit_mode_bicycle_action);
    } else if (mode.compareTo(TraverseMode.CAR) == 0) {
      action =
          applicationContext
              .getResources()
              .getString(R.string.step_by_step_non_transit_mode_car_action);
    }

    direction.setIcon(icon);

    // Main direction
    Place fromPlace = leg.from;
    Place toPlace = leg.to;
    String mainDirectionText = action;
    mainDirectionText +=
        fromPlace.name == null
            ? ""
            : " "
                + applicationContext
                    .getResources()
                    .getString(R.string.step_by_step_non_transit_from)
                + " "
                + getLocalizedStreetName(fromPlace.name, applicationContext.getResources());
    mainDirectionText +=
        toPlace.name == null
            ? ""
            : " "
                + applicationContext.getResources().getString(R.string.step_by_step_non_transit_to)
                + " "
                + getLocalizedStreetName(toPlace.name, applicationContext.getResources());
    String extraStopInformation = toPlace.stopCode;
    long legDuration;
    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(applicationContext);
    if (prefs.getInt(OTPConstants.PREFERENCE_KEY_API_VERSION, OTPConstants.API_VERSION_V1)
        == OTPConstants.API_VERSION_V1) {
      legDuration = leg.duration;
    } else {
      legDuration = leg.duration / 1000;
    }
    if (!TextUtils.isEmpty(extraStopInformation)) {
      mainDirectionText += " (" + extraStopInformation + ")";
    }
    mainDirectionText +=
        "\n["
            + ConversionUtils.getFormattedDistance(leg.distance, applicationContext)
            + " - "
            + ConversionUtils.getFormattedDurationTextNoSeconds(
                legDuration, false, applicationContext)
            + " ]";
    direction.setDirectionText(mainDirectionText);

    // Sub-direction
    List<WalkStep> walkSteps = leg.getSteps();

    if (walkSteps == null) {
      return direction;
    }

    ArrayList<Direction> subDirections = new ArrayList<Direction>(walkSteps.size());

    for (WalkStep step : walkSteps) {
      int subdirection_icon = -1;
      Direction dir = new Direction();
      String subDirectionText = "";

      double distance = step.distance;

      RelativeDirection relativeDir = step.relativeDirection;
      String relativeDirString =
          getLocalizedRelativeDir(relativeDir, applicationContext.getResources());
      String streetName = step.streetName;
      AbsoluteDirection absoluteDir = step.absoluteDirection;
      String absoluteDirString =
          getLocalizedAbsoluteDir(absoluteDir, applicationContext.getResources());
      String exit = step.exit;
      boolean isStayOn = (step.stayOn == null ? false : step.stayOn);
      boolean isBogusName = (step.bogusName == null ? false : step.bogusName);
      double lon = step.lon;
      double lat = step.lat;
      String streetConnector =
          applicationContext
              .getResources()
              .getString(R.string.step_by_step_non_transit_connector_street_name);
      // Elevation[] elevation = step.getElevation();  //Removed elevation for now, since we're not
      // doing anything with it and it causes version issues between OTP server APIs v0.9.1-SNAPSHOT
      // and v0.9.2-SNAPSHOT
      List<Alerts> alert = step.alerts;

      // Walk East
      if (relativeDir == null) {
        subDirectionText +=
            action
                + " "
                + applicationContext
                    .getResources()
                    .getString(R.string.step_by_step_non_transit_heading)
                + " ";
        subDirectionText += absoluteDirString + " ";
      }
      // (Turn left)/(Continue)
      else {
        RelativeDirection rDir = RelativeDirection.valueOf(relativeDir.name());

        subdirection_icon = getRelativeDirectionIcon(rDir, applicationContext.getResources());

        // Do not need TURN Continue
        if (rDir.compareTo(RelativeDirection.RIGHT) == 0
            || rDir.compareTo(RelativeDirection.LEFT) == 0) {
          subDirectionText +=
              applicationContext.getResources().getString(R.string.step_by_step_non_transit_turn)
                  + " ";
        }

        subDirectionText += relativeDirString + " ";

        if (rDir.compareTo(RelativeDirection.CIRCLE_CLOCKWISE) == 0
            || rDir.compareTo(RelativeDirection.CIRCLE_COUNTERCLOCKWISE) == 0) {
          if (step.exit != null) {
            try {
              String ordinal =
                  getOrdinal(Integer.parseInt(step.exit), applicationContext.getResources());
              if (ordinal != null) {
                subDirectionText += ordinal + " ";
              } else {
                subDirectionText +=
                    applicationContext
                            .getResources()
                            .getString(R.string.step_by_step_non_transit_roundabout_number)
                        + " "
                        + ordinal
                        + " ";
              }
            } catch (NumberFormatException e) {
              // If is not a step_by_step_non_transit_roundabout_number and is not null is better to
              // try to display it
              subDirectionText += step.exit + " ";
            }
            subDirectionText +=
                applicationContext
                        .getResources()
                        .getString(R.string.step_by_step_non_transit_roundabout_exit)
                    + " ";
            streetConnector =
                applicationContext
                    .getResources()
                    .getString(R.string.step_by_step_non_transit_connector_street_name_roundabout);
          }
        }
      }

      subDirectionText +=
          streetConnector
              + " "
              + getLocalizedStreetName(streetName, applicationContext.getResources())
              + " ";

      subDirectionText +=
          "\n[" + ConversionUtils.getFormattedDistance(distance, applicationContext) + " ]";

      dir.setDirectionText(subDirectionText);

      dir.setIcon(subdirection_icon);

      // Add new sub-direction
      subDirections.add(dir);
    }

    direction.setSubDirections(subDirections);

    return direction;
  }