Package org.onebusaway.transit_data_federation.bundle.tasks.transit_graph

Source Code of org.onebusaway.transit_data_federation.bundle.tasks.transit_graph.DistanceAlongShapeLibrary$InvalidStopToShapeMappingException

/**
* Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org>
* Copyright (C) 2011 Google, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*         http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.onebusaway.transit_data_federation.bundle.tasks.transit_graph;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.onebusaway.collections.Min;
import org.onebusaway.container.ConfigurationParameter;
import org.onebusaway.geospatial.model.CoordinatePoint;
import org.onebusaway.geospatial.model.XYPoint;
import org.onebusaway.geospatial.services.UTMLibrary;
import org.onebusaway.geospatial.services.UTMProjection;
import org.onebusaway.gtfs.model.AgencyAndId;
import org.onebusaway.transit_data_federation.impl.shapes.PointAndIndex;
import org.onebusaway.transit_data_federation.impl.shapes.ShapePointsLibrary;
import org.onebusaway.transit_data_federation.impl.transit_graph.StopEntryImpl;
import org.onebusaway.transit_data_federation.impl.transit_graph.StopTimeEntryImpl;
import org.onebusaway.transit_data_federation.impl.transit_graph.TripEntryImpl;
import org.onebusaway.transit_data_federation.model.ShapePoints;
import org.onebusaway.transit_data_federation.services.transit_graph.StopTimeEntry;
import org.onebusaway.transit_data_federation.services.transit_graph.TripEntry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class DistanceAlongShapeLibrary {

  private static Logger _log = LoggerFactory.getLogger(DistanceAlongShapeLibrary.class);

  private static NumberFormat _errorFormatter = new DecimalFormat("0.00");

  private ShapePointsLibrary _shapePointsLibrary = new ShapePointsLibrary();

  private double _maxDistanceFromStopToShapePoint = 1000;

  private int _maximumNumberOfPotentialAssignments = 10000;

  private Set<AgencyAndId> _shapeIdsWeHavePrinted = new HashSet<AgencyAndId>();

  /**
   * When computing stop-to-shape matches, we will consider multiple potential
   * matches for a stop if the shape passes by the multiple times, as is the
   * case in a looped route, for example. A potential match from the stop to
   * shape will be considered each time the shape is within the
   * localMinimumThreshold distance of the stop, such that the shape is more
   * than the localMinimumThreshold distance threshold from the stop in between
   * the potential matches.
   *
   * @param localMinimumThreshold distance in meters
   */
  @ConfigurationParameter
  public void setLocalMinimumThreshold(double localMinimumThreshold) {
    _shapePointsLibrary.setLocalMinimumThreshold(localMinimumThreshold);
  }

  /**
   * If the closest distance from a stop to a shape is more than
   * maxDistanceFromStopToShapePoint in the
   * {@link #getDistancesAlongShape(ShapePoints, List)}, then a
   * {@link StopIsTooFarFromShapeException} will be thrown.
   *
   * @param maxDistanceFromStopToShapePoint distance in meters
   */
  @ConfigurationParameter
  public void setMaxDistanceFromStopToShapePoint(
      double maxDistanceFromStopToShapePoint) {
    _maxDistanceFromStopToShapePoint = maxDistanceFromStopToShapePoint;
  }

  /**
   * When computing stop-to-shape matches, the potential number of assignments
   * for possible matches can grow so large that checking for the best
   * assignment could take way too long. This is often the case when dealing
   * with malformed shapes.
   *
   * This parameter controls the maximum number of potential assignments we are
   * willing to consider. If the number of potential assignments is higher than
   * this number, we throw an exception.
   *
   * @param maximumNumberOfPotentialAssignments
   */
  @ConfigurationParameter
  public void setMaximumNumberOfPotentialAssignment(
      int maximumNumberOfPotentialAssignments) {
    _maximumNumberOfPotentialAssignments = maximumNumberOfPotentialAssignments;
  }

  public PointAndIndex[] getDistancesAlongShape(ShapePoints shapePoints,
      List<StopTimeEntryImpl> stopTimes)
      throws DistanceAlongShapeException {

    PointAndIndex[] stopTimePoints = new PointAndIndex[stopTimes.size()];

    UTMProjection projection = UTMLibrary.getProjectionForPoint(
        shapePoints.getLats()[0], shapePoints.getLons()[0]);

    List<XYPoint> projectedShapePoints = _shapePointsLibrary.getProjectedShapePoints(
        shapePoints, projection);

    double[] shapePointsDistTraveled = shapePoints.getDistTraveled();

    List<List<PointAndIndex>> possibleAssignments = computePotentialAssignments(
        projection, projectedShapePoints, shapePointsDistTraveled, stopTimes);

    pruneUnnecessaryAssignments(possibleAssignments);
    assignmentSanityCheck(shapePoints, stopTimes, possibleAssignments);

    double maxDistanceTraveled = shapePointsDistTraveled[shapePointsDistTraveled.length - 1];

    List<PointAndIndex> bestAssignment = computeBestAssignment(shapePoints,
        stopTimes, possibleAssignments, projection, projectedShapePoints);

    for (int i = 0; i < stopTimePoints.length; i++) {
      PointAndIndex pindex = bestAssignment.get(i);
      if (pindex.distanceAlongShape > maxDistanceTraveled) {
        int index = projectedShapePoints.size() - 1;
        XYPoint point = projectedShapePoints.get(index);
        StopEntryImpl stop = stopTimes.get(i).getStop();
        XYPoint stopPoint = projection.forward(stop.getStopLocation());
        double d = stopPoint.getDistance(point);
        pindex = new PointAndIndex(point, index, d, maxDistanceTraveled);
      }
      stopTimePoints[i] = pindex;
    }
    return stopTimePoints;
  }

  /**
   * The {@link ShapePointsLibrary} returns a number of potential assignments
   * for a stop's position along a shape. While some of those potential
   * assignments can help us correctly deal with cases where a shape loops back
   * on itself or other weird configurations, many of the potential assignments
   * are just local minimas that won't ultimately have much affect on the final
   * assignment if they don't overlap with the previous or next stop. To help
   * reduce the set of possible assignments that we'll have to explore, we just
   * pick the best assignment and toss out the rest when the set of assignments
   * for a stop don't overlap with the assignments of the previous or next stop.
   *
   * @param possibleAssignments
   */
  private void pruneUnnecessaryAssignments(
      List<List<PointAndIndex>> possibleAssignments) {

    double[] mins = new double[possibleAssignments.size()];
    double[] maxs = new double[possibleAssignments.size()];

    for (int i = 0; i < possibleAssignments.size(); ++i) {
      double minScore = Double.POSITIVE_INFINITY;
      double maxScore = Double.NEGATIVE_INFINITY;
      for (PointAndIndex pi : possibleAssignments.get(i)) {
        minScore = Math.min(minScore, pi.distanceAlongShape);
        maxScore = Math.max(maxScore, pi.distanceAlongShape);
      }
      mins[i] = minScore;
      maxs[i] = maxScore;
    }

    for (int i = 0; i < possibleAssignments.size(); i++) {
      List<PointAndIndex> points = possibleAssignments.get(i);
      // If there is only one possible assignment, there is nothing to prune
      if (points.size() == 1)
        continue;
      double currentMin = mins[i];
      double currentMax = maxs[i];
      if (i > 0) {
        double prevMax = maxs[i - 1];
        if (currentMin < prevMax)
          continue;
      }
      if (i + 1 < possibleAssignments.size()) {
        double nextMin = mins[i + 1];
        if (currentMax > nextMin)
          continue;
      }

      Collections.sort(points, PointAndIndex.DISTANCE_FROM_TARGET_COMPARATOR);
      while (points.size() > 1)
        points.remove(points.size() - 1);
    }
  }

  private void assignmentSanityCheck(ShapePoints shapePoints,
      List<StopTimeEntryImpl> stopTimes,
      List<List<PointAndIndex>> possibleAssignments)
      throws DistanceAlongShapeException {

    int stIndex = 0;
    for (List<PointAndIndex> assignments : possibleAssignments) {
      if (assignments.isEmpty()) {
        StopTimeEntry stopTime = stopTimes.get(stIndex);
        throw new InvalidStopToShapeMappingException(stopTime.getTrip());
      }
      Min<PointAndIndex> m = new Min<PointAndIndex>();
      for (PointAndIndex pindex : assignments)
        m.add(pindex.distanceFromTarget, pindex);
      if (m.getMinValue() > _maxDistanceFromStopToShapePoint) {
        StopTimeEntry stopTime = stopTimes.get(stIndex);
        PointAndIndex pindex = m.getMinElement();
        CoordinatePoint point = shapePoints.getPointForIndex(pindex.index);
        throw new StopIsTooFarFromShapeException(stopTime, pindex, point);
      }
      stIndex++;
    }
  }

  private List<List<PointAndIndex>> computePotentialAssignments(
      UTMProjection projection, List<XYPoint> projectedShapePoints,
      double[] shapePointDistance, List<StopTimeEntryImpl> stopTimes) {

    List<List<PointAndIndex>> possibleAssignments = new ArrayList<List<PointAndIndex>>();

    for (StopTimeEntryImpl stopTime : stopTimes) {

      StopEntryImpl stop = stopTime.getStop();
      XYPoint stopPoint = projection.forward(stop.getStopLocation());

      List<PointAndIndex> assignments = _shapePointsLibrary.computePotentialAssignments(
          projectedShapePoints, shapePointDistance, stopPoint, 0,
          projectedShapePoints.size());

      possibleAssignments.add(assignments);
    }
    return possibleAssignments;
  }

  private List<PointAndIndex> computeBestAssignment(ShapePoints shapePoints,
      List<StopTimeEntryImpl> stopTimes,
      List<List<PointAndIndex>> possibleAssignments, UTMProjection projection,
      List<XYPoint> projectedShapePoints)
      throws InvalidStopToShapeMappingException {

    checkFirstAndLastStop(stopTimes, possibleAssignments, shapePoints,
        projection, projectedShapePoints);

    int startIndex = 0;
    int assingmentCount = 1;

    /**
     * We iterate over each stop, examining its possible assignments. If we find
     * a region of stops where the first and last stop have a single assignment
     * but the stops in-between have multiple assignments, we compute the best
     * assignments for that section. We also handle the edge-cases where the
     * multiple potential assignments occur at the start or end of the route.
     */
    for (int index = 0; index < possibleAssignments.size(); index++) {
      List<PointAndIndex> possibleAssignment = possibleAssignments.get(index);
      int count = possibleAssignment.size();
      if (count == 0) {
        constructErrorForPotentialAssignmentCount(shapePoints, stopTimes, count);
      }

      boolean hasRegion = index > startIndex;
      boolean hasSingleAssignmentFollowingMultipleAssignments = count == 1
          && assingmentCount > 1;
      boolean hasMultipleAssignmentsAndLastPoint = count > 1
          && index == possibleAssignments.size() - 1;

      if (hasRegion
          && (hasSingleAssignmentFollowingMultipleAssignments || hasMultipleAssignmentsAndLastPoint)) {

        List<PointAndIndex> currentAssignment = new ArrayList<PointAndIndex>(
            index - startIndex + 1);
        Min<Assignment> bestAssignments = new Min<Assignment>();
        recursivelyConstructAssignments(possibleAssignments, currentAssignment,
            startIndex, startIndex, index + 1, bestAssignments);
        if (bestAssignments.isEmpty()) {
          constructError(shapePoints, stopTimes, possibleAssignments,
              projection);
        } else {
          List<PointAndIndex> bestAssignment = bestAssignments.getMinElement().assigment;
          for (int bestIndex = 0; bestIndex < bestAssignment.size(); bestIndex++) {
            possibleAssignments.set(startIndex + bestIndex,
                Arrays.asList(bestAssignment.get(bestIndex)));
          }
        }
      }
      if (count == 1) {
        startIndex = index;
        assingmentCount = 1;
      } else {
        assingmentCount *= count;
        if (assingmentCount > _maximumNumberOfPotentialAssignments) {
          constructErrorForPotentialAssignmentCount(shapePoints, stopTimes,
              assingmentCount);
        }
      }
    }

    List<PointAndIndex> bestAssignment = new ArrayList<PointAndIndex>();
    for (List<PointAndIndex> possibleAssignment : possibleAssignments) {
      if (possibleAssignment.size() != 1) {
        String msg = "expected just one assignment at this point, found "
            + possibleAssignment.size() + "; " + "shapePoint="
            + shapePoints.getShapeId() + ", " + "\npossibleAssignments=\n";
        for (PointAndIndex pa : possibleAssignment) {
          msg += "PointAndIndex(index=" + pa.index + ", point=" + pa.point
              + ", distanceAlongShape=" + pa.distanceAlongShape
              + ", distanceFromTarget=" + pa.distanceFromTarget + "), ";
        }
        msg += "\nstopTime=\n";
        for (StopTimeEntryImpl st : stopTimes) {
          msg += "StopTimeEntry(Stop(" + st.getStop().getId() + ":"
              + st.getStop().getStopLat() + ", " + st.getStop().getStopLon()
              + ")" + ", trip=" + st.getTrip().getId() + "), ";
        }
        _log.error(msg);
        throw new IllegalStateException(msg);       
      }
      bestAssignment.add(possibleAssignment.get(0));
    }

    return bestAssignment;
  }

  /**
   * Special check for an issue with start points where the first stop isn't all
   * that near the start of the shape (the first stop being more of a layover
   * point). If the shape is working against us, the closest point for the first
   * stop can be a point further along the shape, which causes problems.
   */
  private void checkFirstAndLastStop(List<StopTimeEntryImpl> stopTimes,
      List<List<PointAndIndex>> possibleAssignments, ShapePoints shapePoints,
      UTMProjection projection, List<XYPoint> projectedShapePoints) {

    if (possibleAssignments.size() >= 2) {

      PointAndIndex first = possibleAssignments.get(0).get(0);
      PointAndIndex second = possibleAssignments.get(1).get(0);
      if (first.distanceAlongShape > second.distanceAlongShape) {

        StopTimeEntryImpl firstStopTime = stopTimes.get(0);

        _log.warn("snapping first stop time id=" + firstStopTime.getId()
            + " to start of shape");

        XYPoint point = projectedShapePoints.get(0);

        StopEntryImpl stop = firstStopTime.getStop();
        XYPoint stopPoint = projection.forward(stop.getStopLocation());

        double d = stopPoint.getDistance(point);

        possibleAssignments.get(0).add(new PointAndIndex(point, 0, d, 0.0));
      }

      int n = possibleAssignments.size();
      PointAndIndex prev = possibleAssignments.get(n - 2).get(0);
      PointAndIndex last = possibleAssignments.get(n - 1).get(0);
      if (prev.distanceAlongShape > last.distanceAlongShape) {
      }

    }

    if (possibleAssignments.size() > 0) {

      /**
       * We snap the last stop to the end of the shape and add it to the set of
       * possible assignments. In the worst case, it will be a higher-scoring
       * assignment and ignored, but it can help in cases where the stop was
       * weirdly assigned.
       */
      PointAndIndex lastSnapped = getLastStopSnappedToEndOfShape(stopTimes,
          shapePoints, projection, projectedShapePoints);

      possibleAssignments.get(possibleAssignments.size() - 1).add(lastSnapped);
    }
  }

  private PointAndIndex getLastStopSnappedToEndOfShape(
      List<StopTimeEntryImpl> stopTimes, ShapePoints shapePoints,
      UTMProjection projection, List<XYPoint> projectedShapePoints) {

    int i = stopTimes.size() - 1;
    StopTimeEntryImpl lastStopTime = stopTimes.get(i);

    int lastShapePointIndex = projectedShapePoints.size() - 1;
    XYPoint lastShapePoint = projectedShapePoints.get(lastShapePointIndex);
    XYPoint stopLocation = projection.forward(lastStopTime.getStop().getStopLocation());

    double existingDistanceAlongShape = shapePoints.getDistTraveledForIndex(lastShapePointIndex);
    double extraDistanceAlongShape = lastShapePoint.getDistance(stopLocation);
    double distanceAlongShape = existingDistanceAlongShape
        + extraDistanceAlongShape;

    double d = lastShapePoint.getDistance(stopLocation);

    return new PointAndIndex(lastShapePoint, lastShapePointIndex, d,
        distanceAlongShape);
  }

  private void recursivelyConstructAssignments(
      List<List<PointAndIndex>> possibleAssignments,
      List<PointAndIndex> currentAssignment, int index, int indexFrom,
      int indexTo, Min<Assignment> best) {

    /**
     * If we've made it through ALL assignments, we have a valid assignment!
     */
    if (index == indexTo) {

      double score = 0;
      for (PointAndIndex p : currentAssignment)
        score += p.distanceFromTarget;
      currentAssignment = new ArrayList<PointAndIndex>(currentAssignment);
      Assignment result = new Assignment(currentAssignment, score);
      best.add(score, result);
      return;
    }

    List<PointAndIndex> possibleAssignmentsForIndex = possibleAssignments.get(index);

    List<PointAndIndex> validAssignments = new ArrayList<PointAndIndex>();

    double lastDistanceAlongShape = -1;

    if (index > indexFrom) {
      PointAndIndex prev = currentAssignment.get(index - 1 - indexFrom);
      lastDistanceAlongShape = prev.distanceAlongShape;
    }

    for (PointAndIndex possibleAssignmentForIndex : possibleAssignmentsForIndex) {
      if (possibleAssignmentForIndex.distanceAlongShape >= lastDistanceAlongShape)
        validAssignments.add(possibleAssignmentForIndex);
    }

    /**
     * There is no satisfying assignment for this search tree, so we return
     */
    if (validAssignments.isEmpty()) {
      return;
    }

    /**
     * For each valid assignment, pop it onto the current assignment and
     * recursively evaluate
     */
    for (PointAndIndex validAssignment : validAssignments) {

      currentAssignment.add(validAssignment);

      recursivelyConstructAssignments(possibleAssignments, currentAssignment,
          index + 1, indexFrom, indexTo, best);

      currentAssignment.remove(currentAssignment.size() - 1);
    }
  }

  private void constructErrorForPotentialAssignmentCount(
      ShapePoints shapePoints, List<StopTimeEntryImpl> stopTimes, long count)
      throws InvalidStopToShapeMappingException {

    if (count == 0) {
      _log.error("We were attempting to compute the distance along a particular trip for each stop time of that "
          + "trip by snapping them to the shape for that trip.  However, we could not find an assignment for each "
          + "stop time of the trip, which usually indicates that there is something wrong with the underlying "
          + "shape data.  For more information on errors of this kind, see:\n"
          + "  https://github.com/OneBusAway/onebusaway-application-modules/wiki/Stop-to-Shape-Matching");
    } else {
      _log.error("We were attempting to compute the distance along a particular trip for each stop time of that "
          + "trip by snapping them to the shape for that trip.  However, we found WAY TOO MANY potential "
          + "assignments, which usually indicates that there is something wrong with the underlying shape data.  "
          + "For more information on errors of this kind, see:\n"
          + "  https://github.com/OneBusAway/onebusaway-application-modules/wiki/Stop-to-Shape-Matching");
    }

    StopTimeEntryImpl first = stopTimes.get(0);
    TripEntryImpl trip = first.getTrip();
    StopTimeEntryImpl last = stopTimes.get(stopTimes.size() - 1);

    _log.error("error constructing stop-time distances along shape for trip="
        + trip.getId() + " shape=" + trip.getShapeId() + " firstStopTime="
        + first.getId() + " lastStopTime=" + last.getId());

    if (_shapeIdsWeHavePrinted.add(trip.getShapeId())) {
      StringBuilder b = new StringBuilder();
      for (int i = 0; i < shapePoints.getSize(); i++) {
        b.append(shapePoints.getLatForIndex(i));
        b.append(' ');
        b.append(shapePoints.getLonForIndex(i));
        b.append(' ');
        b.append(shapePoints.getDistTraveledForIndex(i));
        b.append('\n');
      }
      _log.error("shape points:\n" + b.toString());
    }
    throw new InvalidStopToShapeMappingException(first.getTrip());
  }

  private void constructError(ShapePoints shapePoints,
      List<StopTimeEntryImpl> stopTimes,
      List<List<PointAndIndex>> possibleAssignments, UTMProjection projection)
      throws InvalidStopToShapeMappingException {

    StopTimeEntryImpl first = stopTimes.get(0);
    StopTimeEntryImpl last = stopTimes.get(stopTimes.size() - 1);

    _log.error("We were attempting to compute the distance along a particular trip for each stop time of that trip by "
        + "snapping them to the shape for that trip.  However, we could not find an assignment for each stop time "
        + "where the distance traveled along the shape for each stop time was strictly increasing (aka a stop time "
        + "seemed to travel backwards).  For more information on errors of this kind, see:\n"
        + "  https://github.com/OneBusAway/onebusaway-application-modules/wiki/Stop-to-Shape-Matching");

    TripEntryImpl trip = first.getTrip();
    _log.error("error constructing stop-time distances along shape for trip="
        + trip.getId() + " shape=" + trip.getShapeId() + " firstStopTime="
        + first.getId() + " lastStopTime=" + last.getId());

    StringBuilder b = new StringBuilder();
    int index = 0;

    b.append("# potential assignments:\n");
    b.append("# stopLat stopLon stopId\n");
    b.append("#   locationOnShapeLat locationOnShapeLon distanceAlongShape distanceFromShape shapePointIndex\n");
    b.append("#   ...\n");

    double prevMaxDistanceAlongShape = Double.NEGATIVE_INFINITY;

    for (List<PointAndIndex> possible : possibleAssignments) {
      StopTimeEntryImpl stopTime = stopTimes.get(index);
      StopEntryImpl stop = stopTime.getStop();
      b.append(stop.getStopLat());
      b.append(' ');
      b.append(stop.getStopLon());
      b.append(' ');
      b.append(index);
      b.append(' ');
      b.append(stop.getId());
      b.append('\n');

      double maxDistanceAlongShape = Double.NEGATIVE_INFINITY;
      double minDistanceAlongShape = Double.POSITIVE_INFINITY;

      for (PointAndIndex pindex : possible) {
        b.append("  ");
        b.append(projection.reverse(pindex.point));
        b.append(' ');
        b.append(_errorFormatter.format(pindex.distanceAlongShape));
        b.append(' ');
        b.append(_errorFormatter.format(pindex.distanceFromTarget));
        b.append(' ');
        b.append(pindex.index);
        b.append("\n");
        maxDistanceAlongShape = Math.max(maxDistanceAlongShape,
            pindex.distanceAlongShape);
        minDistanceAlongShape = Math.min(minDistanceAlongShape,
            pindex.distanceAlongShape);
      }

      if (minDistanceAlongShape < prevMaxDistanceAlongShape) {
        b.append("    ^ potential problem here ^\n");
      }

      prevMaxDistanceAlongShape = maxDistanceAlongShape;

      index++;
    }
    _log.error(b.toString());

    if (_shapeIdsWeHavePrinted.add(trip.getShapeId())) {
      b = new StringBuilder();
      index = 0;
      for (int i = 0; i < shapePoints.getSize(); i++) {
        b.append(shapePoints.getLatForIndex(i));
        b.append(' ');
        b.append(shapePoints.getLonForIndex(i));
        b.append(' ');
        b.append(shapePoints.getDistTraveledForIndex(i));
        b.append('\n');
      }
      _log.error("shape points:\n" + b.toString());
    }

    throw new InvalidStopToShapeMappingException(first.getTrip());
  }

  private static class Assignment implements Comparable<Assignment> {
    private final List<PointAndIndex> assigment;
    private final double score;

    public Assignment(List<PointAndIndex> assignment, double score) {
      this.assigment = assignment;
      this.score = score;
    }

    @Override
    public int compareTo(Assignment o) {
      return Double.compare(score, o.score);
    }
  }

  public static class DistanceAlongShapeException extends Exception {

    private static final long serialVersionUID = 1L;

    public DistanceAlongShapeException(String message) {
      super(message);
    }
  }

  public static class StopIsTooFarFromShapeException extends
      DistanceAlongShapeException {

    private static final long serialVersionUID = 1L;
    private final StopTimeEntry _stopTime;
    private final PointAndIndex _pointAndIndex;
    private final CoordinatePoint _point;

    public StopIsTooFarFromShapeException(StopTimeEntry stopTime,
        PointAndIndex pointAndIndex, CoordinatePoint point) {
      super("stopTime=" + stopTime + " pointAndIndex=" + pointAndIndex
          + " point=" + point);
      _stopTime = stopTime;
      _pointAndIndex = pointAndIndex;
      _point = point;
    }

    public StopTimeEntry getStopTime() {
      return _stopTime;
    }

    public PointAndIndex getPointAndIndex() {
      return _pointAndIndex;
    }

    public CoordinatePoint getPoint() {
      return _point;
    }
  }

  public static class InvalidStopToShapeMappingException extends
      DistanceAlongShapeException {

    private static final long serialVersionUID = 1L;

    private final TripEntry _trip;

    public InvalidStopToShapeMappingException(TripEntry trip) {
      super("trip=" + trip.getId());
      _trip = trip;
    }

    public TripEntry getTrip() {
      return _trip;
    }
  }
}
TOP

Related Classes of org.onebusaway.transit_data_federation.bundle.tasks.transit_graph.DistanceAlongShapeLibrary$InvalidStopToShapeMappingException

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.