diff --git a/src/main/java/com/conveyal/r5/analyst/scenario/SelectLink.java b/src/main/java/com/conveyal/r5/analyst/scenario/SelectLink.java index c00b1b696..502d12c4d 100644 --- a/src/main/java/com/conveyal/r5/analyst/scenario/SelectLink.java +++ b/src/main/java/com/conveyal/r5/analyst/scenario/SelectLink.java @@ -48,6 +48,8 @@ public class SelectLink extends Modification { private Map feedForUnscopedId; + private int nPatternsWithoutShapes = 0; + @Override public boolean resolve(TransportNetwork network) { // Convert the incoming description of the selected link area to a Geometry for computing intersections. @@ -87,6 +89,9 @@ public boolean apply(TransportNetwork network) { String feedId = feedIdForTripPattern(tripPattern); GTFSFeed feed = feedForUnscopedId.get(feedId); TransitLayer.addShapeToTripPattern(feed, tripPattern); + if (tripPattern.shape == null) { + nPatternsWithoutShapes += 1; + } // TransitLayer parameter enables fetching straight lines between stops in case shapes are not present. List hopGeometries = tripPattern.getHopGeometries(network.transitLayer); TIntArrayList intersectedHops = new TIntArrayList(); @@ -100,7 +105,11 @@ public boolean apply(TransportNetwork network) { hopsInTripPattern.put(patternIndex, intersectedHops.toArray()); } } - + if (nPatternsWithoutShapes > 0) { + addInfo( "Out of %d patterns, %d had no shapes and used straight lines.".formatted( + network.transitLayer.tripPatterns.size(), nPatternsWithoutShapes + )); + } // After finding all links (TripPattern hops) in the SelectedLink area, release the GTFSFeeds which don't really // belong in a Modification. This avoids memory leaks, and protects us from inadvertently relying on or // modifying those feed objects later. diff --git a/src/main/java/com/conveyal/r5/transit/TransitLayer.java b/src/main/java/com/conveyal/r5/transit/TransitLayer.java index 47bda5945..5a6aa490e 100644 --- a/src/main/java/com/conveyal/r5/transit/TransitLayer.java +++ b/src/main/java/com/conveyal/r5/transit/TransitLayer.java @@ -439,45 +439,50 @@ public static void addShapeToTripPattern ( TripPattern tripPattern ) { // First, find an exemplar trip that is representative of the TripPattern. - boolean foundExemplarTrip = false; Trip trip = null; Iterable stopTimes = null; for (TripSchedule tripSchedule : tripPattern.tripSchedules) { - // In constructor, TripSchedule.tripId = String.join(":", trip.feed_id, trip.trip_id); + // In constructor, TripSchedule.tripId is set to String.join(":", trip.feed_id, trip.trip_id). String[] tripIdParts = tripSchedule.tripId.split(":"); if (!tripIdParts[0].equals(gtfsFeed.feedId)) { LOG.warn("Feed ID scope of trip ID for TripSchedule in TripPattern does not match supplied GTFS feed."); continue; } String unscopedTripId = tripIdParts[1]; - trip = gtfsFeed.trips.get(unscopedTripId); - if (trip == null) { + Trip candidateTrip = gtfsFeed.trips.get(unscopedTripId); + if (candidateTrip == null) { LOG.warn("Could not find trip for unscoped ID " + unscopedTripId); continue; } - if (trip.shape_id == null) { - continue; + if (trip == null || candidateTrip.shape_id != null) { + try { + // FIXME here and below: why did we need interpolated stop times? We are only the positions right? + Iterable candidateStopTimes = gtfsFeed.getInterpolatedStopTimesForTrip(unscopedTripId); + // Iterable candidateStopTimes = gtfsFeed.getOrderedStopTimesForTrip(unscopedTripId); + // All checks succeeded, retain the information from this trip as the exemplar for the pattern. + trip = candidateTrip; + stopTimes = candidateStopTimes; + } catch (GTFSFeed.FirstAndLastStopsDoNotHaveTimes e) { + continue; + } } - try { - stopTimes = gtfsFeed.getInterpolatedStopTimesForTrip(unscopedTripId); - } catch (GTFSFeed.FirstAndLastStopsDoNotHaveTimes e) { - continue; + if (candidateTrip.shape_id != null) { + // Found exemplar trip with explicit shape, no need to search further. + // Otherwise, we found a suitable trip but its shape will be composed of straight lines. + // Retain it, but continue iterative search for trips in pattern having explicit shapes. + break; } - // All checks succeeded, record the information from this trip as the exemplar for the pattern. - foundExemplarTrip = true; - break; } // Could add a possibly slow check: get each trip, check if exemplarTrip.shapeId equals shape in each trip. // LOG.warn(String.format("Multiple trips in the same TripPattern have different shapes (e.g. %s and %s)") - if (!foundExemplarTrip) { - LOG.warn("Did not find any exemplar trip with usable Shape and StopTimes for pattern " + tripPattern); + if (trip == null) { + LOG.warn("Did not find any exemplar trip with usable StopTimes for pattern " + tripPattern); return; } - // This is assigned outside the loop only to make it final, as required by lambdas below. final Shape shape = gtfsFeed.getShape(trip.shape_id); if (shape == null) { - LOG.error("Shape {} for trip {} was missing", trip.shape_id, trip.trip_id); + LOG.debug("Trip {} has no explicit shape, straight lines will be used.", trip.trip_id, trip.shape_id); return; } tripPattern.shape = shape.geometry;