Skip to content

Commit

Permalink
Merge pull request opentripplanner#6017 from entur/make_timetable_imm…
Browse files Browse the repository at this point in the history
…utable

Make Timetable immutable
  • Loading branch information
vpaturet authored Sep 26, 2024
2 parents c93fc7c + e15c3d0 commit cd5277e
Show file tree
Hide file tree
Showing 29 changed files with 808 additions and 356 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,12 @@ void testPopulateLegsWithRealtimeKeepStaySeated() {
private static TripPattern delay(TripPattern pattern1, int seconds) {
var originalTimeTable = pattern1.getScheduledTimetable();

var delayedTimetable = new Timetable(pattern1);
var delayedTripTimes = delay(originalTimeTable.getTripTimes(0), seconds);
delayedTimetable.addTripTimes(delayedTripTimes);
var delayedTimetable = Timetable
.of()
.withTripPattern(pattern1)
.addTripTimes(delayedTripTimes)
.build();

return pattern1.copy().withScheduledTimeTable(delayedTimetable).build();
}
Expand Down
18 changes: 10 additions & 8 deletions src/ext/java/org/opentripplanner/ext/siri/AddedTripBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -210,13 +210,6 @@ Result<TripUpdate, UpdateError> build() {

// TODO: We always create a new TripPattern to be able to modify its scheduled timetable
StopPattern stopPattern = new StopPattern(aimedStopTimes);
TripPattern pattern = TripPattern
.of(getTripPatternId.apply(trip))
.withRoute(trip.getRoute())
.withMode(trip.getMode())
.withNetexSubmode(trip.getNetexSubMode())
.withStopPattern(stopPattern)
.build();

RealTimeTripTimes tripTimes = TripTimesFactory.tripTimes(
trip,
Expand All @@ -229,7 +222,16 @@ Result<TripUpdate, UpdateError> build() {
// therefore they must be valid
tripTimes.validateNonIncreasingTimes();
tripTimes.setServiceCode(transitService.getServiceCodeForId(trip.getServiceId()));
pattern.add(tripTimes);

TripPattern pattern = TripPattern
.of(getTripPatternId.apply(trip))
.withRoute(trip.getRoute())
.withMode(trip.getMode())
.withNetexSubmode(trip.getNetexSubMode())
.withStopPattern(stopPattern)
.withScheduledTimeTableBuilder(builder -> builder.addTripTimes(tripTimes))
.build();

RealTimeTripTimes updatedTripTimes = tripTimes.copyScheduledTimes();

// Loop through calls again and apply updates
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.HashMap;
import java.util.Map;
import org.opentripplanner.graph_builder.model.GraphBuilderModule;
import org.opentripplanner.transit.model.network.TripPattern;
import org.opentripplanner.transit.service.TransitModel;

/**
Expand Down Expand Up @@ -45,9 +46,15 @@ public void buildGraph() {
return;
}

pattern
.getScheduledTimetable()
.updateAllTripTimes(it -> it.adjustTimesToGraphTimeZone(timeShift));
TripPattern updatedPattern = pattern
.copy()
.withScheduledTimeTableBuilder(builder ->
builder.updateAllTripTimes(tt -> tt.adjustTimesToGraphTimeZone(timeShift))
)
.build();
// replace the original pattern with the updated pattern in the transit model
transitModel.addTripPattern(updatedPattern.getId(), updatedPattern);
});
transitModel.index();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.collect.MultimapBuilder;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
Expand All @@ -23,6 +24,7 @@
import org.opentripplanner.transit.model.network.Route;
import org.opentripplanner.transit.model.network.StopPattern;
import org.opentripplanner.transit.model.network.TripPattern;
import org.opentripplanner.transit.model.network.TripPatternBuilder;
import org.opentripplanner.transit.model.timetable.Direction;
import org.opentripplanner.transit.model.timetable.FrequencyEntry;
import org.opentripplanner.transit.model.timetable.Trip;
Expand All @@ -40,13 +42,18 @@ public class GenerateTripPatternsOperation {

private final Map<String, Integer> tripPatternIdCounters = new HashMap<>();

private final OtpTransitServiceBuilder transitDaoBuilder;
private final OtpTransitServiceBuilder transitServiceBuilder;
private final DataImportIssueStore issueStore;
private final Deduplicator deduplicator;
private final Set<FeedScopedId> calendarServiceIds;
private final GeometryProcessor geometryProcessor;

private final Multimap<StopPattern, TripPattern> tripPatterns;
// TODO the linked hashset configuration ensures that TripPatterns are created in the same order
// as Trips are imported, as a workaround for issue #6067
private final Multimap<StopPattern, TripPatternBuilder> tripPatternBuilders = MultimapBuilder
.linkedHashKeys()
.linkedHashSetValues()
.build();
private final ListMultimap<Trip, Frequency> frequenciesForTrip = ArrayListMultimap.create();

private int freqCount = 0;
Expand All @@ -59,18 +66,17 @@ public GenerateTripPatternsOperation(
Set<FeedScopedId> calendarServiceIds,
GeometryProcessor geometryProcessor
) {
this.transitDaoBuilder = builder;
this.transitServiceBuilder = builder;
this.issueStore = issueStore;
this.deduplicator = deduplicator;
this.calendarServiceIds = calendarServiceIds;
this.geometryProcessor = geometryProcessor;
this.tripPatterns = transitDaoBuilder.getTripPatterns();
}

public void run() {
collectFrequencyByTrip();

final Collection<Trip> trips = transitDaoBuilder.getTripsById().values();
final Collection<Trip> trips = transitServiceBuilder.getTripsById().values();
var progressLogger = ProgressTracker.track("build trip patterns", 50_000, trips.size());
LOG.info(progressLogger.startMessage());

Expand All @@ -85,6 +91,14 @@ public void run() {
}
}

tripPatternBuilders
.values()
.stream()
.map(TripPatternBuilder::build)
.forEach(tripPattern ->
transitServiceBuilder.getTripPatterns().put(tripPattern.getStopPattern(), tripPattern)
);

LOG.info(progressLogger.completeMessage());
LOG.info(
"Added {} frequency-based and {} single-trip timetable entries.",
Expand All @@ -107,7 +121,7 @@ public boolean hasScheduledTrips() {
* the same trip can be added at once to the same Timetable/TripPattern.
*/
private void collectFrequencyByTrip() {
for (Frequency freq : transitDaoBuilder.getFrequencies()) {
for (Frequency freq : transitServiceBuilder.getFrequencies()) {
frequenciesForTrip.put(freq.getTrip(), freq);
}
}
Expand All @@ -119,7 +133,7 @@ private void buildTripPatternForTrip(Trip trip) {
return; // Invalid trip, skip it, it will break later
}

List<StopTime> stopTimes = transitDaoBuilder.getStopTimesSortedByTrip().get(trip);
List<StopTime> stopTimes = transitServiceBuilder.getStopTimesSortedByTrip().get(trip);

// If after filtering this trip does not contain at least 2 stoptimes, it does not serve any purpose.
var staticTripWithFewerThan2Stops =
Expand All @@ -134,8 +148,7 @@ private void buildTripPatternForTrip(Trip trip) {
// Get the existing TripPattern for this filtered StopPattern, or create one.
StopPattern stopPattern = new StopPattern(stopTimes);

Direction direction = trip.getDirection();
TripPattern tripPattern = findOrCreateTripPattern(stopPattern, trip, direction);
TripPatternBuilder tripPatternBuilder = findOrCreateTripPattern(stopPattern, trip);

// Create a TripTimes object for this list of stoptimes, which form one trip.
TripTimes tripTimes = TripTimesFactory.tripTimes(trip, stopTimes, deduplicator);
Expand All @@ -144,44 +157,42 @@ private void buildTripPatternForTrip(Trip trip) {
List<Frequency> frequencies = frequenciesForTrip.get(trip);
if (!frequencies.isEmpty()) {
for (Frequency freq : frequencies) {
tripPattern.add(new FrequencyEntry(freq, tripTimes));
tripPatternBuilder.withScheduledTimeTableBuilder(builder ->
builder.addFrequencyEntry(new FrequencyEntry(freq, tripTimes))
);
freqCount++;
}
}
// This trip was not frequency-based. Add the TripTimes directly to the TripPattern's scheduled timetable.
else {
tripPattern.add(tripTimes);
tripPatternBuilder.withScheduledTimeTableBuilder(builder -> builder.addTripTimes(tripTimes));
scheduledCount++;
}
}

private TripPattern findOrCreateTripPattern(
StopPattern stopPattern,
Trip trip,
Direction direction
) {
private TripPatternBuilder findOrCreateTripPattern(StopPattern stopPattern, Trip trip) {
Route route = trip.getRoute();
for (TripPattern tripPattern : tripPatterns.get(stopPattern)) {
Direction direction = trip.getDirection();
for (TripPatternBuilder tripPatternBuilder : tripPatternBuilders.get(stopPattern)) {
if (
tripPattern.getRoute().equals(route) &&
tripPattern.getDirection().equals(direction) &&
tripPattern.getMode().equals(trip.getMode()) &&
tripPattern.getNetexSubmode().equals(trip.getNetexSubMode())
tripPatternBuilder.getRoute().equals(route) &&
tripPatternBuilder.getDirection().equals(direction) &&
tripPatternBuilder.getMode().equals(trip.getMode()) &&
tripPatternBuilder.getNetexSubmode().equals(trip.getNetexSubMode())
) {
return tripPattern;
return tripPatternBuilder;
}
}
FeedScopedId patternId = generateUniqueIdForTripPattern(route, direction);
TripPattern tripPattern = TripPattern
TripPatternBuilder tripPatternBuilder = TripPattern
.of(patternId)
.withRoute(route)
.withStopPattern(stopPattern)
.withMode(trip.getMode())
.withNetexSubmode(trip.getNetexSubMode())
.withHopGeometries(geometryProcessor.createHopGeometries(trip))
.build();
tripPatterns.put(stopPattern, tripPattern);
return tripPattern;
.withHopGeometries(geometryProcessor.createHopGeometries(trip));
tripPatternBuilders.put(stopPattern, tripPatternBuilder);
return tripPatternBuilder;
}

/**
Expand Down
Loading

0 comments on commit cd5277e

Please sign in to comment.