use of org.opentripplanner.routing.algorithm.raptor.transit.TripPatternForDate in project OpenTripPlanner by opentripplanner.
the class TripPatternForDateMapper method map.
/**
* This method is THREAD SAFE.
*
* @param timetable The timetable to be mapped to TripPatternForDate - READ ONLY
* @param serviceDate The date to map the TripPatternForDate for - READ ONLY
* @return TripPatternForDate for this timetable and serviceDate
*/
public TripPatternForDate map(Timetable timetable, ServiceDate serviceDate) {
TIntSet serviceCodesRunning = serviceCodesRunningForDate.get(serviceDate);
TripPattern oldTripPattern = timetable.pattern;
List<TripTimes> times = new ArrayList<>();
// The TripTimes are not sorted by departure time in the source timetable because
// OTP1 performs a simple/ linear search. Raptor results depend on trips being
// sorted. We reuse the same timetables many times on different days, so cache the
// sorted versions to avoid repeated compute-intensive sorting. Anecdotally this
// reduces mapping time by more than half, but it is still rather slow. NL Mapping
// takes 32 seconds sorting every timetable, 9 seconds with cached sorting, and 6
// seconds with no timetable sorting at all.
List<TripTimes> sortedTripTimes = sortedTripTimesForTimetable.computeIfAbsent(timetable, TransitLayerMapper::getSortedTripTimes);
for (TripTimes tripTimes : sortedTripTimes) {
if (!serviceCodesRunning.contains(tripTimes.serviceCode)) {
continue;
}
if (tripTimes.getRealTimeState() == RealTimeState.CANCELED) {
continue;
}
times.add(tripTimes);
}
if (times.isEmpty()) {
if (timetable.serviceDate == serviceDate) {
LOG.debug("Tried to update TripPattern {}, but no service codes are valid for date {}", timetable.pattern.getId(), serviceDate);
}
return null;
}
return new TripPatternForDate(newTripPatternForOld.get(oldTripPattern), times.toArray(TripTimes[]::new), ServiceCalendarMapper.localDateFromServiceDate(serviceDate));
}
use of org.opentripplanner.routing.algorithm.raptor.transit.TripPatternForDate in project OpenTripPlanner by opentripplanner.
the class TransitLayerUpdater method update.
public void update(Set<Timetable> updatedTimetables) {
if (!graph.hasRealtimeTransitLayer()) {
return;
}
// Make a shallow copy of the realtime transit layer. Only the objects that are copied will be
// changed during this update process.
TransitLayer realtimeTransitLayer = new TransitLayer(graph.getRealtimeTransitLayer());
double startTime = System.currentTimeMillis();
// Map TripPatterns for this update to Raptor TripPatterns
final Map<org.opentripplanner.model.TripPattern, TripPatternWithRaptorStopIndexes> newTripPatternForOld = mapOldTripPatternToRaptorTripPattern(realtimeTransitLayer.getStopIndex(), updatedTimetables.stream().map(t -> t.pattern).collect(Collectors.toSet()));
// Instantiate a TripPatternForDateMapper with the new TripPattern mappings
TripPatternForDateMapper tripPatternForDateMapper = new TripPatternForDateMapper(serviceCodesRunningForDate, newTripPatternForOld);
// Index updated timetables by date
@SuppressWarnings("ConstantConditions") Multimap<LocalDate, Timetable> timetablesByDate = Multimaps.index(updatedTimetables, t -> ServiceCalendarMapper.localDateFromServiceDate(t.serviceDate));
for (LocalDate date : timetablesByDate.keySet()) {
Collection<Timetable> timetablesForDate = timetablesByDate.get(date);
List<TripPatternForDate> patternsForDate = realtimeTransitLayer.getTripPatternsForDateCopy(date);
if (patternsForDate == null) {
continue;
}
Map<org.opentripplanner.model.TripPattern, TripPatternForDate> patternsForDateMap = tripPatternForDateMapCache.computeIfAbsent(date, p -> patternsForDate.stream().collect(Collectors.toMap(t -> t.getTripPattern().getPattern(), t -> t)));
for (Timetable timetable : timetablesForDate) {
TripPatternForDate tripPatternForDate = tripPatternForDateMapper.map(timetable, timetable.serviceDate);
if (tripPatternForDate != null) {
patternsForDateMap.put(timetable.pattern, tripPatternForDate);
}
}
realtimeTransitLayer.replaceTripPatternsForDate(date, new ArrayList<>(patternsForDateMap.values()));
// Switch out the reference with the updated realtimeTransitLayer. This is synchronized to
// guarantee that the reference is set after all the fields have been updated.
graph.setRealtimeTransitLayer(realtimeTransitLayer);
LOG.debug("UPDATING {} tripPatterns took {} ms", updatedTimetables.size(), System.currentTimeMillis() - startTime);
}
}
use of org.opentripplanner.routing.algorithm.raptor.transit.TripPatternForDate in project OpenTripPlanner by opentripplanner.
the class RaptorRoutingRequestTransitDataCreatorTest method testMergeTripPatterns.
@Test
public void testMergeTripPatterns() {
TripTimes[] times = new TripTimes[] { null };
LocalDate first = LocalDate.of(2019, 3, 30);
LocalDate second = LocalDate.of(2019, 3, 31);
LocalDate third = LocalDate.of(2019, 4, 1);
ZonedDateTime startOfTime = DateMapper.asStartOfService(second, ZoneId.of("Europe/London"));
// Total available trip patterns
TripPatternWithRaptorStopIndexes tripPattern1 = new TripPatternWithId(new FeedScopedId("", "1"), null, null);
TripPatternWithRaptorStopIndexes tripPattern2 = new TripPatternWithId(new FeedScopedId("", "2"), null, null);
TripPatternWithRaptorStopIndexes tripPattern3 = new TripPatternWithId(new FeedScopedId("", "3"), null, null);
List<Map<FeedScopedId, TripPatternForDate>> tripPatternsForDates = new ArrayList<>();
// TripPatterns valid for 1st day in search range
Map<FeedScopedId, TripPatternForDate> tripPatternForDatesById = new HashMap<>();
tripPatternForDatesById.put(tripPattern1.getId(), new TripPatternForDate(tripPattern1, times, first));
tripPatternForDatesById.put(tripPattern2.getId(), new TripPatternForDate(tripPattern2, times, first));
tripPatternForDatesById.put(tripPattern3.getId(), new TripPatternForDate(tripPattern1, times, first));
tripPatternsForDates.add(tripPatternForDatesById);
// TripPatterns valid for 2nd day in search range
Map<FeedScopedId, TripPatternForDate> tripPatternForDatesById2 = new HashMap<>();
tripPatternForDatesById2.put(tripPattern2.getId(), new TripPatternForDate(tripPattern2, times, second));
tripPatternForDatesById2.put(tripPattern3.getId(), new TripPatternForDate(tripPattern3, times, second));
tripPatternsForDates.add(tripPatternForDatesById2);
// TripPatterns valid for 3rd day in search range
Map<FeedScopedId, TripPatternForDate> tripPatternForDatesById3 = new HashMap<>();
tripPatternForDatesById3.put(tripPattern1.getId(), new TripPatternForDate(tripPattern1, times, third));
tripPatternForDatesById3.put(tripPattern3.getId(), new TripPatternForDate(tripPattern3, times, third));
tripPatternsForDates.add(tripPatternForDatesById3);
// Patterns containing trip schedules for all 3 days. Trip schedules for later days are offset in time when requested.
List<TripPatternForDates> combinedTripPatterns = RaptorRoutingRequestTransitDataCreator.merge(startOfTime, tripPatternsForDates);
// Check the number of trip schedules available for each pattern after combining dates in the search range
assertEquals(2, combinedTripPatterns.get(0).numberOfTripSchedules());
assertEquals(2, combinedTripPatterns.get(1).numberOfTripSchedules());
assertEquals(3, combinedTripPatterns.get(2).numberOfTripSchedules());
// Verify that the per-day offsets were calculated correctly
// DST - Clocks go forward on March 31st
assertEquals(-82800, ((TripScheduleWithOffset) combinedTripPatterns.get(2).getTripSchedule(0)).getSecondsOffset());
assertEquals(0, ((TripScheduleWithOffset) combinedTripPatterns.get(2).getTripSchedule(1)).getSecondsOffset());
assertEquals(86400, ((TripScheduleWithOffset) combinedTripPatterns.get(2).getTripSchedule(2)).getSecondsOffset());
}
use of org.opentripplanner.routing.algorithm.raptor.transit.TripPatternForDate in project OpenTripPlanner by opentripplanner.
the class TransitLayerMapper method mapTripPatterns.
/**
* Map pre-Raptor TripPatterns and Trips to the corresponding Raptor classes.
* <p>
* Part of this method runs IN PARALLEL.
* <p>
*/
private HashMap<LocalDate, List<TripPatternForDate>> mapTripPatterns(StopIndexForRaptor stopIndex) {
Collection<TripPattern> allTripPatterns = graph.tripPatternForId.values();
final Map<TripPattern, TripPatternWithRaptorStopIndexes> newTripPatternForOld = mapOldTripPatternToRaptorTripPattern(stopIndex, allTripPatterns);
TripPatternForDateMapper tripPatternForDateMapper = new TripPatternForDateMapper(graph.index.getServiceCodesRunningForDate(), newTripPatternForOld);
Set<ServiceDate> allServiceDates = graph.index.getServiceCodesRunningForDate().keySet();
// The return value of this entire process.
ConcurrentHashMap<LocalDate, List<TripPatternForDate>> result = new ConcurrentHashMap<>();
// THIS CODE RUNS IN PARALLEL
allServiceDates.parallelStream().forEach(serviceDate -> {
// Create LocalDate equivalent to the OTP/GTFS ServiceDate object, serving as the key of
// the return Map.
LocalDate localDate = ServiceCalendarMapper.localDateFromServiceDate(serviceDate);
// Create a List to hold the values for one entry in the return Map.
List<TripPatternForDate> values = new ArrayList<>();
// Maybe determine in advance which patterns are running on each service and day.
for (org.opentripplanner.model.TripPattern oldTripPattern : allTripPatterns) {
TripPatternForDate tripPatternForDate = tripPatternForDateMapper.map(oldTripPattern.scheduledTimetable, serviceDate);
if (tripPatternForDate != null) {
values.add(tripPatternForDate);
}
}
if (!values.isEmpty()) {
result.put(localDate, values);
}
});
return new HashMap<>(result);
}
use of org.opentripplanner.routing.algorithm.raptor.transit.TripPatternForDate in project OpenTripPlanner by opentripplanner.
the class RaptorRoutingRequestTransitDataCreator method merge.
/**
* This method merges several list of TripPatterns for several consecutive dates into a single
* list of TripPatternsForDates. The purpose of doing this is so that TripSchedules for several
* dates are combined by TripPattern instead of having their own TripPattern. This is to improve
* performance for searching, as each TripPattern is searched only once per round.
*/
static List<TripPatternForDates> merge(ZonedDateTime searchStartTime, List<Map<FeedScopedId, TripPatternForDate>> tripPatternForDateList) {
List<TripPatternForDates> combinedList = new ArrayList<>();
// Extract all distinct TripPatterns across the search days
Map<FeedScopedId, TripPatternWithRaptorStopIndexes> allTripPatternsById = tripPatternForDateList.stream().flatMap(t -> t.values().stream()).map(TripPatternForDate::getTripPattern).distinct().collect(Collectors.toMap(TripPatternWithRaptorStopIndexes::getId, t -> t));
// For each TripPattern, time expand each TripPatternForDate and merge into a single TripPatternForDates
for (Map.Entry<FeedScopedId, TripPatternWithRaptorStopIndexes> patternEntry : allTripPatternsById.entrySet()) {
List<TripPatternForDate> tripPatterns = new ArrayList<>();
List<Integer> offsets = new ArrayList<>();
for (Map<FeedScopedId, TripPatternForDate> tripPatternById : tripPatternForDateList) {
TripPatternForDate tripPatternForDate = tripPatternById.get(patternEntry.getKey());
if (tripPatternForDate != null) {
tripPatterns.add(tripPatternForDate);
offsets.add(secondsSinceStartOfTime(searchStartTime, tripPatternForDate.getLocalDate()));
}
}
combinedList.add(new TripPatternForDates(patternEntry.getValue(), tripPatterns, offsets));
}
return combinedList;
}
Aggregations