public String toString() {
   String out = "";
   if (elevationProfile == null || elevationProfile.size() == 0) {
     return "(empty elevation profile)";
   }
   for (int i = 0; i < elevationProfile.size(); ++i) {
     Coordinate coord = elevationProfile.getCoordinate(i);
     out += "(" + coord.x + "," + coord.y + "), ";
   }
   return out.substring(0, out.length() - 2);
 }
  public boolean setElevationProfile(
      PackedCoordinateSequence elev, boolean computed, boolean slopeLimit) {
    if (elev == null || elev.size() < 2) {
      return false;
    }

    if (slopeOverride && !computed) {
      return false;
    }

    elevationProfile = elev;

    // compute the various costs of the elevation changes
    double lengthMultiplier = ElevationUtils.getLengthMultiplierFromElevation(elev);
    if (Double.isNaN(lengthMultiplier)) {
      LOG.error("lengthMultiplier from elevation profile is NaN, setting to 1");
      lengthMultiplier = 1;
    }

    length *= lengthMultiplier;
    bicycleSafetyEffectiveLength *= lengthMultiplier;

    SlopeCosts costs = ElevationUtils.getSlopeCosts(elev, slopeLimit);
    slopeSpeedEffectiveLength = costs.slopeSpeedEffectiveLength;
    maxSlope = costs.maxSlope;
    slopeWorkCost = costs.slopeWorkCost;
    bicycleSafetyEffectiveLength += costs.slopeSafetyCost;
    flattened = costs.flattened;

    return costs.flattened;
  }
  public static PackedCoordinateSequence getPartialElevationProfile(
      PackedCoordinateSequence elevationProfile, double start, double end) {
    if (elevationProfile == null) {
      return null;
    }
    List<Coordinate> coordList = new LinkedList<Coordinate>();

    if (start < 0) start = 0;

    Coordinate[] coordinateArray = elevationProfile.toCoordinateArray();
    double length = coordinateArray[coordinateArray.length - 1].x;
    if (end > length) end = length;

    boolean started = false;
    boolean finished = false;
    Coordinate lastCoord = null;
    for (Coordinate coord : coordinateArray) {
      if (coord.x >= start && coord.x <= end) {
        coordList.add(new Coordinate(coord.x - start, coord.y));
        if (!started) {
          started = true;
          if (lastCoord == null) {
            // no need to interpolate as this is the first coordinate
            continue;
          }
          // interpolate start coordinate
          double run = coord.x - lastCoord.x;
          if (run < 1) {
            // tiny runs are likely to lead to errors, so we'll skip them
            continue;
          }
          double p = (coord.x - start) / run;
          double rise = coord.y - lastCoord.y;
          Coordinate interpolatedStartCoordinate = new Coordinate(0, lastCoord.y + p * rise);
          coordList.add(0, interpolatedStartCoordinate);
        }
      } else if (coord.x > end && !finished && started && lastCoord != null) {
        finished = true;
        // interpolate end coordinate
        double run = coord.x - lastCoord.x;
        if (run < 1) {
          // tiny runs are likely to lead to errors, so we'll skip them
          continue;
        }
        double p = (end - lastCoord.x) / run;
        double rise = coord.y - lastCoord.y;
        Coordinate interpolatedEndCoordinate = new Coordinate(end, lastCoord.y + p * rise);
        coordList.add(interpolatedEndCoordinate);
      }
      lastCoord = coord;
    }

    Coordinate coordArr[] = new Coordinate[coordList.size()];
    return new PackedCoordinateSequence.Float(coordList.toArray(coordArr), 2);
  }
 /**
  * Adjusts an Itinerary's elevation fields from an elevation profile
  *
  * @return the elevation at the end of the profile
  */
 private double applyElevation(
     PackedCoordinateSequence profile, Itinerary itinerary, double previousElevation) {
   if (profile != null) {
     for (Coordinate coordinate : profile.toCoordinateArray()) {
       if (previousElevation == Double.MAX_VALUE) {
         previousElevation = coordinate.y;
         continue;
       }
       double elevationChange = previousElevation - coordinate.y;
       if (elevationChange > 0) {
         itinerary.elevationGained += elevationChange;
       } else {
         itinerary.elevationLost -= elevationChange;
       }
       previousElevation = coordinate.y;
     }
   }
   return previousElevation;
 }
  public static SlopeCosts getSlopeCosts(PackedCoordinateSequence elev, String name) {
    Coordinate[] coordinates = elev.toCoordinateArray();

    double maxSlope = 0;
    double slopeSpeedEffectiveLength = 0;
    double slopeWorkCost = 0;
    double slopeSafetyCost = 0;
    for (int i = 0; i < coordinates.length - 1; ++i) {
      double run = coordinates[i + 1].x - coordinates[i].x;
      double rise = coordinates[i + 1].y - coordinates[i].y;
      if (run == 0) {
        continue;
      }
      double slope = rise / run;
      if (slope > 0.35 || slope < -0.35) {
        slope = 0; // Baldwin St in Dunedin, NZ, is the steepest street
        // on earth, and has a
        // grade of 35%. So, this must be a data error.
        log.warn(
            "Warning: street "
                + name
                + " steeper than Baldwin Street.  This is an error in the algorithm or the data");
      }
      if (maxSlope < Math.abs(slope)) {
        maxSlope = Math.abs(slope);
      }

      double slope_or_zero = Math.max(slope, 0);
      double hypotenuse = Math.sqrt(rise * rise + run * run);
      double energy =
          hypotenuse
              * (ENERGY_PER_METER_ON_FLAT
                  + ENERGY_SLOPE_FACTOR * slope_or_zero * slope_or_zero * slope_or_zero);
      slopeWorkCost += energy;
      slopeSpeedEffectiveLength += hypotenuse / slopeSpeedCoefficient(slope, coordinates[i].y);
      // assume that speed and safety are inverses
      double safetyCost = hypotenuse * (slopeSpeedCoefficient(slope, coordinates[i].y) - 1) * 0.25;
      if (safetyCost > 0) {
        slopeSafetyCost += safetyCost;
      }
    }
    return new SlopeCosts(slopeSpeedEffectiveLength, slopeWorkCost, slopeSafetyCost, maxSlope);
  }