// one less geometry than stoptime as these are hops not stops (fencepost problem)
LineString[] geoms = new LineString[stopTimes.size() - 1];
/* Detect presence or absence of shape_dist_traveled on a per-trip basis */
StopTime st0 = stopTimes.get(0);
boolean hasShapeDist = st0.isShapeDistTraveledSet();
if (hasShapeDist) {
// this trip has shape_dist in stop_times
for (int i = 0; i < stopTimes.size() - 1; ++i) {
st0 = stopTimes.get(i);
StopTime st1 = stopTimes.get(i + 1);
geoms[i] = getHopGeometryViaShapeDistTraveled(graph, shapeId, st0, st1);
}
return geoms;
}
LineString shape = getLineStringForShapeId(shapeId);
if (shape == null) {
// this trip has a shape_id, but no such shape exists, and no shape_dist in stop_times
// create straight line segments between stops for each hop
for (int i = 0; i < stopTimes.size() - 1; ++i) {
st0 = stopTimes.get(i);
StopTime st1 = stopTimes.get(i + 1);
LineString geometry = createSimpleGeometry(st0.getStop(), st1.getStop());
geoms[i] = geometry;
}
return geoms;
}
// This trip does not have shape_dist in stop_times, but does have an associated shape.
ArrayList<IndexedLineSegment> segments = new ArrayList<IndexedLineSegment>();
for (int i = 0 ; i < shape.getNumPoints() - 1; ++i) {
segments.add(new IndexedLineSegment(i, shape.getCoordinateN(i), shape.getCoordinateN(i + 1)));
}
// Find possible segment matches for each stop.
List<List<IndexedLineSegment>> possibleSegmentsForStop = new ArrayList<List<IndexedLineSegment>>();
int minSegmentIndex = 0;
for (int i = 0; i < stopTimes.size() ; ++i) {
Stop stop = stopTimes.get(i).getStop();
Coordinate coord = new Coordinate(stop.getLon(), stop.getLat());
List<IndexedLineSegment> stopSegments = new ArrayList<IndexedLineSegment>();
double bestDistance = Double.MAX_VALUE;
IndexedLineSegment bestSegment = null;
int maxSegmentIndex = -1;
int index = -1;
int minSegmentIndexForThisStop = -1;
for (IndexedLineSegment segment : segments) {
index ++;
if (segment.index < minSegmentIndex) {
continue;
}
double distance = segment.distance(coord);
if (distance < maxStopToShapeSnapDistance) {
stopSegments.add(segment);
maxSegmentIndex = index;
if (minSegmentIndexForThisStop == -1)
minSegmentIndexForThisStop = index;
} else if (distance < bestDistance) {
bestDistance = distance;
bestSegment = segment;
if (maxSegmentIndex != -1) {
maxSegmentIndex = index;
}
}
}
if (stopSegments.size() == 0) {
//no segments within 150m
//fall back to nearest segment
stopSegments.add(bestSegment);
minSegmentIndex = bestSegment.index;
} else {
minSegmentIndex = minSegmentIndexForThisStop;
Collections.sort(stopSegments, new IndexedLineSegmentComparator(coord));
}
for (int j = i - 1; j >= 0; j --) {
for (Iterator<IndexedLineSegment> it = possibleSegmentsForStop.get(j).iterator(); it.hasNext(); ) {
IndexedLineSegment segment = it.next();
if (segment.index > maxSegmentIndex) {
it.remove();
}
}
}
possibleSegmentsForStop.add(stopSegments);
}
List<LinearLocation> locations = getStopLocations(possibleSegmentsForStop, stopTimes, 0, -1);
if (locations == null) {
// this only happens on shape which have points very far from
// their stop sequence. So we'll fall back to trivial stop-to-stop
// linking, even though theoretically we could do better.
for (int i = 0; i < stopTimes.size() - 1; ++i) {
st0 = stopTimes.get(i);
StopTime st1 = stopTimes.get(i + 1);
LineString geometry = createSimpleGeometry(st0.getStop(), st1.getStop());
geoms[i] = geometry;
//this warning is not strictly correct, but will do
LOG.warn(graph.addBuilderAnnotation(new BogusShapeGeometryCaught(shapeId, st0, st1)));
}
return geoms;