use of org.opentripplanner.routing.edgetype.TripPattern in project OpenTripPlanner by opentripplanner.
the class TimetableSnapshotSourceTest method testHandleDelayedTrip.
@Test
public void testHandleDelayedTrip() {
final AgencyAndId tripId = new AgencyAndId(feedId, "1.1");
final AgencyAndId tripId2 = new AgencyAndId(feedId, "1.2");
final Trip trip = graph.index.tripForId.get(tripId);
final TripPattern pattern = graph.index.patternForTrip.get(trip);
final int tripIndex = pattern.scheduledTimetable.getTripIndex(tripId);
final int tripIndex2 = pattern.scheduledTimetable.getTripIndex(tripId2);
final TripDescriptor.Builder tripDescriptorBuilder = TripDescriptor.newBuilder();
tripDescriptorBuilder.setTripId("1.1");
tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED);
final TripUpdate.Builder tripUpdateBuilder = TripUpdate.newBuilder();
tripUpdateBuilder.setTrip(tripDescriptorBuilder);
final StopTimeUpdate.Builder stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder();
stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED);
stopTimeUpdateBuilder.setStopSequence(2);
final StopTimeEvent.Builder arrivalBuilder = stopTimeUpdateBuilder.getArrivalBuilder();
final StopTimeEvent.Builder departureBuilder = stopTimeUpdateBuilder.getDepartureBuilder();
arrivalBuilder.setDelay(1);
departureBuilder.setDelay(1);
final TripUpdate tripUpdate = tripUpdateBuilder.build();
updater.applyTripUpdates(graph, fullDataset, Arrays.asList(tripUpdate), feedId);
final TimetableSnapshot snapshot = updater.getTimetableSnapshot();
final Timetable forToday = snapshot.resolve(pattern, serviceDate);
final Timetable schedule = snapshot.resolve(pattern, null);
assertNotSame(forToday, schedule);
assertNotSame(forToday.getTripTimes(tripIndex), schedule.getTripTimes(tripIndex));
assertSame(forToday.getTripTimes(tripIndex2), schedule.getTripTimes(tripIndex2));
assertEquals(1, forToday.getTripTimes(tripIndex).getArrivalDelay(1));
assertEquals(1, forToday.getTripTimes(tripIndex).getDepartureDelay(1));
assertEquals(RealTimeState.SCHEDULED, schedule.getTripTimes(tripIndex).getRealTimeState());
assertEquals(RealTimeState.UPDATED, forToday.getTripTimes(tripIndex).getRealTimeState());
assertEquals(RealTimeState.SCHEDULED, schedule.getTripTimes(tripIndex2).getRealTimeState());
assertEquals(RealTimeState.SCHEDULED, forToday.getTripTimes(tripIndex2).getRealTimeState());
}
use of org.opentripplanner.routing.edgetype.TripPattern in project OpenTripPlanner by opentripplanner.
the class TimetableSnapshotSourceTest method testPurgeExpiredData.
@Test
public void testPurgeExpiredData() throws InvalidProtocolBufferException {
final AgencyAndId tripId = new AgencyAndId(feedId, "1.1");
// Just to be safe...
final ServiceDate previously = serviceDate.previous().previous();
final Trip trip = graph.index.tripForId.get(tripId);
final TripPattern pattern = graph.index.patternForTrip.get(trip);
updater.maxSnapshotFrequency = (0);
updater.purgeExpiredData = (false);
updater.applyTripUpdates(graph, fullDataset, Arrays.asList(TripUpdate.parseFrom(cancellation)), feedId);
final TimetableSnapshot snapshotA = updater.getTimetableSnapshot();
updater.purgeExpiredData = (true);
final TripDescriptor.Builder tripDescriptorBuilder = TripDescriptor.newBuilder();
tripDescriptorBuilder.setTripId("1.1");
tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.CANCELED);
tripDescriptorBuilder.setStartDate(previously.getAsString());
final TripUpdate.Builder tripUpdateBuilder = TripUpdate.newBuilder();
tripUpdateBuilder.setTrip(tripDescriptorBuilder);
final TripUpdate tripUpdate = tripUpdateBuilder.build();
updater.applyTripUpdates(graph, fullDataset, Arrays.asList(tripUpdate), feedId);
final TimetableSnapshot snapshotB = updater.getTimetableSnapshot();
assertNotSame(snapshotA, snapshotB);
assertSame(snapshotA.resolve(pattern, null), snapshotB.resolve(pattern, null));
assertSame(snapshotA.resolve(pattern, serviceDate), snapshotB.resolve(pattern, serviceDate));
assertNotSame(snapshotA.resolve(pattern, null), snapshotA.resolve(pattern, serviceDate));
assertSame(snapshotB.resolve(pattern, null), snapshotB.resolve(pattern, previously));
}
use of org.opentripplanner.routing.edgetype.TripPattern in project OpenTripPlanner by opentripplanner.
the class OnBoardDepartServiceImplTest method testOnBoardAtStation.
@Test
public final void testOnBoardAtStation() {
TransitStop station0 = mock(TransitStop.class);
TransitStop station1 = mock(TransitStop.class);
TransitStop station2 = mock(TransitStop.class);
PatternDepartVertex depart = mock(PatternDepartVertex.class);
PatternArriveVertex dwell = mock(PatternArriveVertex.class);
PatternArriveVertex arrive = mock(PatternArriveVertex.class);
Graph graph = mock(Graph.class);
RoutingRequest routingRequest = mock(RoutingRequest.class);
ServiceDay serviceDay = mock(ServiceDay.class);
// You're probably not supposed to do this to mocks (access their fields directly)
// But I know of no other way to do this since the mock object has only action-free stub methods.
routingRequest.modes = new TraverseModeSet("WALK,TRANSIT");
when(graph.getTimeZone()).thenReturn(TimeZone.getTimeZone("GMT"));
ArrayList<Edge> hops = new ArrayList<Edge>(2);
RoutingContext routingContext = new RoutingContext(routingRequest, graph, null, arrive);
Agency agency = new Agency();
AgencyAndId agencyAndId = new AgencyAndId("Agency", "ID");
Route route = new Route();
ArrayList<StopTime> stopTimes = new ArrayList<StopTime>(2);
StopTime stopDepartTime = new StopTime();
StopTime stopDwellTime = new StopTime();
StopTime stopArriveTime = new StopTime();
Stop stopDepart = new Stop();
Stop stopDwell = new Stop();
Stop stopArrive = new Stop();
Trip trip = new Trip();
routingContext.serviceDays = new ArrayList<ServiceDay>(Collections.singletonList(serviceDay));
agency.setId(agencyAndId.getAgencyId());
route.setId(agencyAndId);
route.setAgency(agency);
stopDepart.setId(new AgencyAndId("Station", "0"));
stopDwell.setId(new AgencyAndId("Station", "1"));
stopArrive.setId(new AgencyAndId("Station", "2"));
stopDepartTime.setStop(stopDepart);
stopDepartTime.setDepartureTime(0);
stopDwellTime.setArrivalTime(20);
stopDwellTime.setStop(stopDwell);
stopDwellTime.setDepartureTime(40);
stopArriveTime.setArrivalTime(60);
stopArriveTime.setStop(stopArrive);
stopTimes.add(stopDepartTime);
stopTimes.add(stopDwellTime);
stopTimes.add(stopArriveTime);
trip.setId(agencyAndId);
trip.setRoute(route);
TripTimes tripTimes = new TripTimes(trip, stopTimes, new Deduplicator());
StopPattern stopPattern = new StopPattern(stopTimes);
TripPattern tripPattern = new TripPattern(route, stopPattern);
TripPattern.generateUniqueIds(Arrays.asList(tripPattern));
when(depart.getTripPattern()).thenReturn(tripPattern);
when(dwell.getTripPattern()).thenReturn(tripPattern);
PatternHop patternHop0 = new PatternHop(depart, dwell, stopDepart, stopDwell, 0);
PatternHop patternHop1 = new PatternHop(dwell, arrive, stopDwell, stopArrive, 1);
hops.add(patternHop0);
hops.add(patternHop1);
when(graph.getEdges()).thenReturn(hops);
when(depart.getCoordinate()).thenReturn(new Coordinate(0, 0));
when(dwell.getCoordinate()).thenReturn(new Coordinate(0, 0));
when(arrive.getCoordinate()).thenReturn(new Coordinate(0, 0));
routingRequest.from = new GenericLocation();
routingRequest.startingTransitTripId = agencyAndId;
when(graph.getVertex("Station_0")).thenReturn(station0);
when(graph.getVertex("Station_1")).thenReturn(station1);
when(graph.getVertex("Station_2")).thenReturn(station2);
tripPattern.add(tripTimes);
graph.index = new GraphIndex(graph);
when(serviceDay.secondsSinceMidnight(anyInt())).thenReturn(0);
assertEquals(station0, onBoardDepartServiceImpl.setupDepartOnBoard(routingContext));
when(serviceDay.secondsSinceMidnight(anyInt())).thenReturn(20);
assertEquals(station1, onBoardDepartServiceImpl.setupDepartOnBoard(routingContext));
when(serviceDay.secondsSinceMidnight(anyInt())).thenReturn(30);
assertEquals(station1, onBoardDepartServiceImpl.setupDepartOnBoard(routingContext));
when(serviceDay.secondsSinceMidnight(anyInt())).thenReturn(40);
assertEquals(station1, onBoardDepartServiceImpl.setupDepartOnBoard(routingContext));
when(serviceDay.secondsSinceMidnight(anyInt())).thenReturn(60);
assertEquals(station2, onBoardDepartServiceImpl.setupDepartOnBoard(routingContext));
}
use of org.opentripplanner.routing.edgetype.TripPattern in project OpenTripPlanner by opentripplanner.
the class OnBoardDepartServiceImplTest method testOnBoardDepartureAtArrivalTime.
@Test
public final void testOnBoardDepartureAtArrivalTime() {
Coordinate[] coordinates = new Coordinate[2];
coordinates[0] = new Coordinate(0.0, 0.0);
coordinates[1] = new Coordinate(0.0, 1.0);
TransitStop station0 = mock(TransitStop.class);
TransitStop station1 = mock(TransitStop.class);
PatternDepartVertex depart = mock(PatternDepartVertex.class);
PatternArriveVertex arrive = mock(PatternArriveVertex.class);
Graph graph = mock(Graph.class);
RoutingRequest routingRequest = mock(RoutingRequest.class);
ServiceDay serviceDay = mock(ServiceDay.class);
// You're probably not supposed to do this to mocks (access their fields directly)
// But I know of no other way to do this since the mock object has only action-free stub methods.
routingRequest.modes = new TraverseModeSet("WALK,TRANSIT");
when(graph.getTimeZone()).thenReturn(TimeZone.getTimeZone("GMT"));
when(station0.getX()).thenReturn(coordinates[0].x);
when(station0.getY()).thenReturn(coordinates[0].y);
when(station1.getX()).thenReturn(coordinates[1].x);
when(station1.getY()).thenReturn(coordinates[1].y);
RoutingContext routingContext = new RoutingContext(routingRequest, graph, null, arrive);
AgencyAndId agencyAndId = new AgencyAndId("Agency", "ID");
Agency agency = new Agency();
Route route = new Route();
ArrayList<StopTime> stopTimes = new ArrayList<StopTime>(2);
StopTime stopDepartTime = new StopTime();
StopTime stopArriveTime = new StopTime();
Stop stopDepart = new Stop();
Stop stopArrive = new Stop();
Trip trip = new Trip();
routingContext.serviceDays = new ArrayList<ServiceDay>(Collections.singletonList(serviceDay));
agency.setId(agencyAndId.getAgencyId());
route.setId(agencyAndId);
route.setAgency(agency);
stopDepart.setId(new AgencyAndId("Station", "0"));
stopArrive.setId(new AgencyAndId("Station", "1"));
stopDepartTime.setStop(stopDepart);
stopDepartTime.setDepartureTime(0);
stopArriveTime.setArrivalTime(10);
stopArriveTime.setStop(stopArrive);
stopTimes.add(stopDepartTime);
stopTimes.add(stopArriveTime);
trip.setId(agencyAndId);
trip.setRoute(route);
TripTimes tripTimes = new TripTimes(trip, stopTimes, new Deduplicator());
StopPattern stopPattern = new StopPattern(stopTimes);
TripPattern tripPattern = new TripPattern(route, stopPattern);
TripPattern.generateUniqueIds(Arrays.asList(tripPattern));
when(depart.getTripPattern()).thenReturn(tripPattern);
PatternHop patternHop = new PatternHop(depart, arrive, stopDepart, stopArrive, 0);
when(graph.getEdges()).thenReturn(Collections.<Edge>singletonList(patternHop));
when(depart.getCoordinate()).thenReturn(new Coordinate(0, 0));
when(arrive.getCoordinate()).thenReturn(new Coordinate(0, 0));
routingRequest.from = new GenericLocation();
routingRequest.startingTransitTripId = agencyAndId;
when(serviceDay.secondsSinceMidnight(anyInt())).thenReturn(10);
when(graph.getVertex("Station_0")).thenReturn(station0);
when(graph.getVertex("Station_1")).thenReturn(station1);
tripPattern.add(tripTimes);
graph.index = new GraphIndex(graph);
Vertex vertex = onBoardDepartServiceImpl.setupDepartOnBoard(routingContext);
assertEquals(coordinates[1].x, vertex.getX(), 0.0);
assertEquals(coordinates[1].y, vertex.getY(), 0.0);
}
use of org.opentripplanner.routing.edgetype.TripPattern in project OpenTripPlanner by opentripplanner.
the class ProfileRouter method route.
/* Maybe don't even try to accumulate stats and weights on the fly, just enumerate options. */
/* TODO Or actually use a priority queue based on the min time. */
public ProfileResponse route() {
// Lazy-initialize stop clusters (threadsafe method)
graph.index.clusterStopsAsNeeded();
// Lazy-initialize profile transfers (before setting timeouts, since this is slow)
if (graph.index.transfersFromStopCluster == null) {
synchronized (graph.index) {
// we don't initialize it again.
if (graph.index.transfersFromStopCluster == null) {
graph.index.initializeProfileTransfers();
}
}
}
LOG.info("access modes: {}", request.accessModes);
LOG.info("egress modes: {}", request.egressModes);
LOG.info("direct modes: {}", request.directModes);
// Establish search timeouts
long searchBeginTime = System.currentTimeMillis();
long abortTime = searchBeginTime + TIMEOUT * 1000;
// TimeWindow could constructed in the caller, which does have access to the graph index.
this.window = new TimeWindow(request.fromTime, request.toTime, graph.index.servicesRunning(request.date));
LOG.info("Finding access/egress paths.");
// Look for stops that are within a given time threshold of the origin and destination
// Find the closest stop on each pattern near the origin and destination
// TODO consider that some stops may be closer by one mode than another
// and that some stops may be accessible by one mode but not another
fromStopPaths = findClosestStops(false);
fromStops = findClosestPatterns(fromStopPaths);
if (!request.analyst) {
toStopPaths = findClosestStops(true);
toStops = findClosestPatterns(toStopPaths);
// Also look for options connecting origin to destination with no transit.
for (QualifiedMode qmode : request.directModes.qModes) {
LOG.info("Finding non-transit path for mode {}", qmode);
findDirectOption(qmode);
}
}
LOG.info("Done finding access/egress paths.");
// printStopsForPatterns("from", fromStops);
// printStopsForPatterns("to", toStops);
/* Enqueue an unfinished PatternRide for each pattern near the origin, grouped by Stop into unfinished Rides. */
// One ride per stop cluster
Map<StopCluster, Ride> initialRides = Maps.newHashMap();
/* This Multimap will contain multiple entries for the same pattern if it can be reached by multiple modes. */
for (TripPattern pattern : fromStops.keySet()) {
if (!request.transitModes.contains(pattern.mode)) {
// FIXME do not even store these patterns when performing access/egress searches
continue;
}
Collection<StopAtDistance> sds = fromStops.get(pattern);
for (StopAtDistance sd : sds) {
/* Fetch or construct a new Ride beginning at this stop cluster. */
Ride ride = initialRides.get(sd.stopCluster);
if (ride == null) {
// null previous ride because this is the first ride
ride = new Ride(sd.stopCluster, null);
// empty stats, times for each access mode will be merged in
ride.accessStats = new Stats();
// higher than any value that will be merged in
ride.accessStats.min = Integer.MAX_VALUE;
// TODO verify correctness and uses
ride.accessDist = (int) sd.state.getWalkDistance();
initialRides.put(sd.stopCluster, ride);
}
/* Record the access time for this mode in the stats. */
ride.accessStats.merge(sd.etime);
/* Loop over stop clusters in case stop cluster appears more than once in the pattern. */
STOP_INDEX: for (int i = 0; i < pattern.getStops().size(); ++i) {
if (sd.stopCluster == graph.index.stopClusterForStop.get(pattern.getStops().get(i))) {
PatternRide newPatternRide = new PatternRide(pattern, i);
// TODO this would be a !contains() call if PatternRides had semantic equality
for (PatternRide existingPatternRide : ride.patternRides) {
if (existingPatternRide.pattern == newPatternRide.pattern && existingPatternRide.fromIndex == newPatternRide.fromIndex) {
continue STOP_INDEX;
}
}
ride.patternRides.add(newPatternRide);
}
}
}
}
for (Ride ride : initialRides.values()) {
// logRide(ride);
queue.insert(ride, 0);
}
/* Explore incomplete rides as long as there are any in the queue. */
while (!queue.empty()) {
/* Get the minimum-time unfinished ride off the queue. */
Ride ride = queue.extract_min();
// TODO should we check whether ride.previous != null (it is an initial ride)?
if (dominated(ride, ride.from))
continue;
// Maybe when ride is complete, then find transfers here, but that makes for more queue operations.
if (ride.to != null)
throw new AssertionError("Ride should be unfinished.");
/* Track finished Rides by their destination StopCluster, so we can add PatternRides to them. */
Map<StopCluster, Ride> rides = Maps.newHashMap();
/* Complete partial PatternRides (with only a pattern and a beginning stop) which were enqueued in this
* partial ride. This is done by scanning through the Pattern, creating rides to all downstream stops. */
PR: for (PatternRide pr : ride.patternRides) {
// LOG.info(" {}", pr);
List<Stop> stops = pr.pattern.getStops();
for (int s = pr.fromIndex + 1; s < stops.size(); ++s) {
StopCluster cluster = graph.index.stopClusterForStop.get(stops.get(s));
/* Originally we only extended rides to destination stops considered useful in the search, i.e.
* those that had transfers leading out of them or were known to be near the destination.
* However, analyst needs to know the times we can reach every stop, and pruning is more effective
* if we know when rides pass through all stops.*/
PatternRide pr2 = pr.extendToIndex(s, window);
// PatternRide may be empty because there are no trips in time window.
if (pr2 == null)
continue PR;
// LOG.info(" {}", pr2);
// Get or create the completed Ride to this destination stop.
Ride ride2 = rides.get(cluster);
if (ride2 == null) {
ride2 = ride.extendTo(cluster);
rides.put(cluster, ride2);
}
// Add the completed PatternRide to the completed Ride.
ride2.patternRides.add(pr2);
}
}
/* Build new downstream Rides by transferring from patterns in current Rides. */
// Create a map of incomplete rides (start but no end point) after transfers, one for each stop.
Map<StopCluster, Ride> xferRides = Maps.newHashMap();
for (Ride r1 : rides.values()) {
r1.calcStats(window, request.walkSpeed);
if (r1.waitStats == null) {
// This is a sign of a questionable algorithm, since we eliminate the ride rather late.
continue;
} else {
r1.recomputeBounds();
}
/* Retain this ride if it is not dominated by some existing ride at the same location. */
if (dominated(r1, r1.to))
continue;
retainedRides.put(r1.to, r1);
/* We have a new, non-dominated, completed ride. Find transfers out of this new ride, respecting the transfer limit. */
int nRides = r1.pathLength;
if (nRides >= MAX_RIDES)
continue;
boolean penultimateRide = (nRides == MAX_RIDES - 1);
// TODO benchmark, this is so not efficient
for (ProfileTransfer tr : graph.index.transfersFromStopCluster.get(r1.to)) {
if (!request.transitModes.contains(tr.tp2.mode))
continue;
if (r1.containsPattern(tr.tp1)) {
// Prune loopy or repetitive paths.
if (r1.pathContainsRoute(tr.tp2.route))
continue;
if (tr.sc1 != tr.sc2 && r1.pathContainsStop(tr.sc2))
continue;
// only transfer to patterns that pass near the destination.
if (!request.analyst && penultimateRide && !toStops.containsKey(tr.tp2))
continue;
// Scan through stops looking for transfer target: stop might appear more than once in a pattern.
TARGET_STOP: for (int i = 0; i < tr.tp2.getStops().size(); ++i) {
StopCluster cluster = graph.index.stopClusterForStop.get(tr.tp2.getStops().get(i));
if (cluster == tr.sc2) {
// Save transfer result in an unfinished ride for later exploration.
Ride r2 = xferRides.get(tr.sc2);
if (r2 == null) {
r2 = new Ride(tr.sc2, r1);
r2.accessDist = tr.distance;
r2.accessStats = new Stats((int) (tr.distance / request.walkSpeed));
r2.recomputeBounds();
xferRides.put(tr.sc2, r2);
}
for (PatternRide pr : r2.patternRides) {
// TODO refactor with equals function and contains().
if (pr.pattern == tr.tp2 && pr.fromIndex == i)
continue TARGET_STOP;
}
r2.patternRides.add(new PatternRide(tr.tp2, i));
}
}
}
}
}
/* Enqueue new incomplete Rides resulting from transfers if they are not dominated at their from-cluster. */
for (Ride r : xferRides.values()) {
if (!dominated(r, r.from)) {
// This ride is unfinished, use the previous ride's travel time lower bound as the p-queue key.
// Note that we are not adding these transfer results to the retained rides, just enqueuing them.
queue.insert(r, r.dlb);
}
}
if (System.currentTimeMillis() > abortTime)
throw new RuntimeException("TIMEOUT");
}
LOG.info("Profile routing request finished in {} sec.", (System.currentTimeMillis() - searchBeginTime) / 1000.0);
if (request.analyst) {
makeSurfaces();
return null;
}
/* In non-analyst (point-to-point) mode, determine which rides are good ways to reach the destination. */
/* A fake stop cluster to allow applying generic domination logic at the final destination. */
final StopCluster DESTINATION = new StopCluster("The Destination", "The Destination");
for (StopCluster cluster : toStopPaths.keySet()) {
// TODO shared logic for making access/egress stats
Stats egressStats = new Stats();
egressStats.min = Integer.MAX_VALUE;
for (StopAtDistance sd : toStopPaths.get(cluster)) {
egressStats.merge(sd.etime);
}
for (Ride ride : retainedRides.get(cluster)) {
// Construct a new unfinished ride, representing "transferring" from the final stop to the destination
Ride rideAtDestination = new Ride(DESTINATION, ride);
rideAtDestination.accessStats = egressStats;
rideAtDestination.recomputeBounds();
if (!dominated(rideAtDestination, DESTINATION)) {
retainedRides.put(DESTINATION, rideAtDestination);
}
}
}
LOG.info("{} nondominated rides reach the destination.", retainedRides.get(DESTINATION).size());
/* Non-analyst: Build the list of Options by following the back-pointers in Rides. */
List<Option> options = Lists.newArrayList();
for (Ride ride : retainedRides.get(DESTINATION)) {
// slice off the final unfinished ride that only contains the egress stats
// TODO actually use this ride in preparing the response
ride = ride.previous;
// All PatternRides in a Ride end at the same stop.
Collection<StopAtDistance> accessPaths = fromStopPaths.get(ride.getAccessStopCluster());
Collection<StopAtDistance> egressPaths = toStopPaths.get(ride.getEgressStopCluster());
Option option = new Option(ride, accessPaths, egressPaths);
if (!option.hasEmptyRides())
options.add(option);
}
/* Include the direct (no-transit) biking, driving, and walking options. */
options.add(new Option(null, directPaths, null));
return new ProfileResponse(options, request.orderBy, request.limit);
}
Aggregations