/** * Get a list of all trips that pass through a stop during a single ServiceDate. Useful when * creating complete stop timetables for a single day. * * @param stop Stop object to perform the search for * @param serviceDate Return all departures for the specified date * @return */ public List<StopTimesInPattern> getStopTimesForStop(Stop stop, ServiceDate serviceDate) { List<StopTimesInPattern> ret = new ArrayList<>(); TimetableSnapshot snapshot = null; if (graph.timetableSnapshotSource != null) { snapshot = graph.timetableSnapshotSource.getTimetableSnapshot(); } Collection<TripPattern> patterns = patternsForStop.get(stop); for (TripPattern pattern : patterns) { StopTimesInPattern stopTimes = new StopTimesInPattern(pattern); Timetable tt; if (snapshot != null) { tt = snapshot.resolve(pattern, serviceDate); } else { tt = pattern.scheduledTimetable; } ServiceDay sd = new ServiceDay(graph, serviceDate, calendarService, pattern.route.getAgency().getId()); int sidx = 0; for (Stop currStop : pattern.stopPattern.stops) { if (currStop == stop) { for (TripTimes t : tt.tripTimes) { if (!sd.serviceRunning(t.serviceCode)) continue; stopTimes.times.add(new TripTimeShort(t, sidx, stop, sd)); } } sidx++; } ret.add(stopTimes); } return ret; }
private boolean checkForInterliningArriveBy( RoutingRequest options, int nBoardings, RaptorRoute route, List<RaptorState> boardStates) { int firstStop = route.getNStops() - 1; boolean started = false; final List<RaptorState> states = statesByStop[route.stops[firstStop].index]; if (states == null) return false; INTERLINE: for (RaptorState oldState : states) { if (oldState.nBoardings != nBoardings - 1) { continue; } if (oldState.route == null) { continue; } if (oldState.route.stops[0] != oldState.stop) { continue; } RaptorInterlineData interline = oldState.route.interlinesIn.get(oldState.tripId); if (interline == null || interline.fromRoute != route) { continue; } RaptorState stayOn = oldState.clone(); stayOn.arrivalTime += options.getBoardSlack(); // go backwards in time stayOn.interlining = true; // generate a board state for this interline RaptorState boardState = new RaptorState(stayOn); // we need to subtract out the boardSlack that we are about to mistakenly pay boardState.weight = -options.getBoardSlack() - options.getAlightSlack(); boardState.nBoardings = boardState.nBoardings = nBoardings - 1; boardState.boardStop = route.stops[firstStop]; boardState.boardStopSequence = firstStop; TransitBoardAlight alight = route.alights[firstStop - 1][interline.fromPatternIndex]; TableTripPattern pattern = alight.getPattern(); boardState.tripTimes = pattern.getTripTimes(interline.fromTripIndex); final ServiceDay serviceDay = oldState.serviceDay; boardState.arrivalTime = (int) serviceDay.time(boardState.tripTimes.getArrivalTime(firstStop - 1)); boardState.patternIndex = interline.fromPatternIndex; boardState.tripId = interline.fromTripId; boardState.serviceDay = serviceDay; boardState.route = route; boardState.walkDistance = oldState.walkDistance; for (RaptorState state : boardStates) { if (state.eDominates(boardState)) { continue INTERLINE; } } boardStates.add(boardState); started = true; } return started; }
/** * Prune raptor data to include only routes and boardings which have trips today. Doesn't actually * improve speed */ @SuppressWarnings("unchecked") private RaptorData pruneDataForServiceDays(Graph graph, ArrayList<ServiceDay> serviceDays) { if (serviceDays.equals(cachedServiceDays)) return cachedRaptorData; RaptorData data = graph.getService(RaptorDataService.class).getData(); RaptorData pruned = new RaptorData(); pruned.raptorStopsForStopId = data.raptorStopsForStopId; pruned.stops = data.stops; pruned.routes = new ArrayList<RaptorRoute>(); pruned.routesForStop = new List[pruned.stops.length]; for (RaptorRoute route : data.routes) { ArrayList<Integer> keep = new ArrayList<Integer>(); for (int i = 0; i < route.boards[0].length; ++i) { Edge board = route.boards[0][i]; int serviceId; if (board instanceof TransitBoardAlight) { serviceId = ((TransitBoardAlight) board).getPattern().getServiceId(); } else { log.debug("Unexpected nonboard among boards"); continue; } for (ServiceDay day : serviceDays) { if (day.serviceIdRunning(serviceId)) { keep.add(i); break; } } } if (keep.isEmpty()) continue; int nPatterns = keep.size(); RaptorRoute prunedRoute = new RaptorRoute(route.getNStops(), nPatterns); for (int stop = 0; stop < route.getNStops() - 1; ++stop) { for (int pattern = 0; pattern < nPatterns; ++pattern) { prunedRoute.boards[stop][pattern] = route.boards[stop][keep.get(pattern)]; } } pruned.routes.add(route); for (RaptorStop stop : route.stops) { List<RaptorRoute> routes = pruned.routesForStop[stop.index]; if (routes == null) { routes = new ArrayList<RaptorRoute>(); pruned.routesForStop[stop.index] = routes; } routes.add(route); } } for (RaptorStop stop : data.stops) { if (pruned.routesForStop[stop.index] == null) { pruned.routesForStop[stop.index] = Collections.emptyList(); } } cachedServiceDays = serviceDays; cachedRaptorData = pruned; return pruned; }
/* See weightLowerBound comment. */ public double timeLowerBound(RoutingContext rctx) { if (rctx.opt.isArriveBy()) { if (!rctx.opt.getModes().get(modeMask)) { return Double.POSITIVE_INFINITY; } for (ServiceDay sd : rctx.serviceDays) if (sd.serviceIdRunning(serviceId)) return 0; return Double.POSITIVE_INFINITY; } else { return 0; } }
/** * Fetch upcoming vehicle departures from a stop. It goes though all patterns passing the stop for * the previous, current and next service date. It uses a priority queue to keep track of the next * departures. The queue is shared between all dates, as services from the previous service date * can visit the stop later than the current service date's services. This happens eg. with * sleeper trains. * * <p>TODO: Add frequency based trips * * @param stop Stop object to perform the search for * @param startTime Start time for the search. Seconds from UNIX epoch * @param timeRange Searches forward for timeRange seconds from startTime * @param numberOfDepartures Number of departures to fetch per pattern * @return */ public List<StopTimesInPattern> stopTimesForStop( Stop stop, long startTime, int timeRange, int numberOfDepartures) { if (startTime == 0) { startTime = System.currentTimeMillis() / 1000; } List<StopTimesInPattern> ret = new ArrayList<>(); TimetableSnapshot snapshot = null; if (graph.timetableSnapshotSource != null) { snapshot = graph.timetableSnapshotSource.getTimetableSnapshot(); } ServiceDate[] serviceDates = { new ServiceDate().previous(), new ServiceDate(), new ServiceDate().next() }; for (TripPattern pattern : patternsForStop.get(stop)) { // Use the Lucene PriorityQueue, which has a fixed size PriorityQueue<TripTimeShort> pq = new PriorityQueue<TripTimeShort>(numberOfDepartures) { @Override protected boolean lessThan(TripTimeShort tripTimeShort, TripTimeShort t1) { // Calculate exact timestamp return (tripTimeShort.serviceDay + tripTimeShort.realtimeDeparture) > (t1.serviceDay + t1.realtimeDeparture); } }; // Loop through all possible days for (ServiceDate serviceDate : serviceDates) { ServiceDay sd = new ServiceDay(graph, serviceDate, calendarService, pattern.route.getAgency().getId()); Timetable tt; if (snapshot != null) { tt = snapshot.resolve(pattern, serviceDate); } else { tt = pattern.scheduledTimetable; } if (!tt.temporallyViable(sd, startTime, timeRange, true)) continue; int secondsSinceMidnight = sd.secondsSinceMidnight(startTime); int sidx = 0; for (Stop currStop : pattern.stopPattern.stops) { if (currStop == stop) { for (TripTimes t : tt.tripTimes) { if (!sd.serviceRunning(t.serviceCode)) continue; if (t.getDepartureTime(sidx) != -1 && t.getDepartureTime(sidx) >= secondsSinceMidnight) { pq.insertWithOverflow(new TripTimeShort(t, sidx, stop, sd)); } } // TODO: This needs to be adapted after #1647 is merged for (FrequencyEntry freq : tt.frequencyEntries) { if (!sd.serviceRunning(freq.tripTimes.serviceCode)) continue; int departureTime = freq.nextDepartureTime(sidx, secondsSinceMidnight); if (departureTime == -1) continue; int lastDeparture = freq.endTime + freq.tripTimes.getArrivalTime(sidx) - freq.tripTimes.getDepartureTime(0); int i = 0; while (departureTime <= lastDeparture && i < numberOfDepartures) { pq.insertWithOverflow( new TripTimeShort(freq.materialize(sidx, departureTime, true), sidx, stop, sd)); departureTime += freq.headway; i++; } } } sidx++; } } if (pq.size() != 0) { StopTimesInPattern stopTimes = new StopTimesInPattern(pattern); while (pq.size() != 0) { stopTimes.times.add(0, pq.pop()); } ret.add(stopTimes); } } return ret; }
@Test public final void testOnBoardDepartureTime() { Coordinate[] coordinates = new Coordinate[5]; coordinates[0] = new Coordinate(0.0, 0.0); coordinates[1] = new Coordinate(0.0, 1.0); coordinates[2] = new Coordinate(2.0, 1.0); coordinates[3] = new Coordinate(5.0, 1.0); coordinates[4] = new Coordinate(5.0, 5.0); 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); when(graph.getTimeZone()).thenReturn(TimeZone.getTimeZone("GMT")); GeometryFactory geometryFactory = GeometryUtils.getGeometryFactory(); CoordinateSequenceFactory coordinateSequenceFactory = geometryFactory.getCoordinateSequenceFactory(); CoordinateSequence coordinateSequence = coordinateSequenceFactory.create(coordinates); LineString geometry = new LineString(coordinateSequence, geometryFactory); ArrayList<Edge> hops = new ArrayList<Edge>(2); RoutingContext routingContext = new RoutingContext(routingRequest, graph, null, arrive); AgencyAndId agencyAndId = new AgencyAndId("Agency", "ID"); Route route = new Route(); ArrayList<StopTime> stopTimes = new ArrayList<StopTime>(3); 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)); route.setId(agencyAndId); stopDepart.setId(agencyAndId); stopDwell.setId(agencyAndId); stopArrive.setId(agencyAndId); 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.setTripHeadsign("The right"); TripTimes tripTimes = new TripTimes(trip, stopTimes, new Deduplicator()); StopPattern stopPattern = new StopPattern(stopTimes); TripPattern tripPattern = new TripPattern(route, stopPattern); 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)); when(routingRequest.getFrom()).thenReturn(new GenericLocation()); when(routingRequest.getStartingTransitTripId()).thenReturn(agencyAndId); when(serviceDay.secondsSinceMidnight(anyInt())).thenReturn(9); patternHop0.setGeometry(geometry); tripPattern.add(tripTimes); graph.index = new GraphIndex(graph); coordinates = new Coordinate[3]; coordinates[0] = new Coordinate(3.5, 1.0); coordinates[1] = new Coordinate(5.0, 1.0); coordinates[2] = new Coordinate(5.0, 5.0); coordinateSequence = coordinateSequenceFactory.create(coordinates); geometry = new LineString(coordinateSequence, geometryFactory); Vertex vertex = onBoardDepartServiceImpl.setupDepartOnBoard(routingContext); Edge edge = vertex.getOutgoing().toArray(new Edge[1])[0]; assertEquals(vertex, edge.getFromVertex()); assertEquals(dwell, edge.getToVertex()); assertEquals("The right", edge.getDirection()); assertEquals(geometry, edge.getGeometry()); assertEquals(coordinates[0].x, vertex.getX(), 0.0); assertEquals(coordinates[0].y, vertex.getY(), 0.0); }
@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); when(graph.getTimeZone()).thenReturn(TimeZone.getTimeZone("GMT")); ArrayList<Edge> hops = new ArrayList<Edge>(2); RoutingContext routingContext = new RoutingContext(routingRequest, graph, null, arrive); 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)); route.setId(agencyAndId); 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); TripTimes tripTimes = new TripTimes(trip, stopTimes, new Deduplicator()); StopPattern stopPattern = new StopPattern(stopTimes); TripPattern tripPattern = new TripPattern(route, stopPattern); 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)); when(routingRequest.getFrom()).thenReturn(new GenericLocation()); when(routingRequest.getStartingTransitTripId()).thenReturn(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)); }
@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); 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"); 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)); route.setId(agencyAndId); 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); TripTimes tripTimes = new TripTimes(trip, stopTimes, new Deduplicator()); StopPattern stopPattern = new StopPattern(stopTimes); TripPattern tripPattern = new TripPattern(route, stopPattern); 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)); when(routingRequest.getFrom()).thenReturn(new GenericLocation()); when(routingRequest.getStartingTransitTripId()).thenReturn(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); }
public State traverse(State state0) { RoutingContext rctx = state0.getContext(); RoutingRequest options = state0.getOptions(); Trip trip = pattern.getTrip(); if (options.isArriveBy()) { /* reverse traversal, not so much to do */ // do not alight immediately when arrive-depart dwell has been eliminated // this affects multi-itinerary searches if (state0.getBackEdge() instanceof TransitBoardAlight && !((TransitBoardAlight) state0.getBackEdge()).isBoarding()) { return null; } StateEditor s1 = state0.edit(this); int type = pattern.getBoardType(stopIndex); if (TransitUtils.handleBoardAlightType(s1, type)) { return null; } s1.setTripId(null); s1.setLastAlightedTime(state0.getTime()); s1.setBackMode(TraverseMode.BOARDING); s1.setPreviousStop(fromv); return s1.makeState(); } else { /* forward traversal: look for a transit trip on this pattern */ if (!options.getModes().get(modeMask)) { return null; } /* find next boarding time */ /* * check lists of transit serviceIds running yesterday, today, and tomorrow (relative to * initial state) if this pattern's serviceId is running look for the next boarding time * choose the soonest boarding time among trips starting yesterday, today, or tomorrow */ long currentTime = state0.getTime(); int bestWait = -1; TraverseMode mode = state0.getNonTransitMode(); if (options.bannedTrips.containsKey(trip.getId())) { // see comment in FrequencyAlight for details return null; } for (ServiceDay sd : rctx.serviceDays) { int secondsSinceMidnight = sd.secondsSinceMidnight(currentTime); // only check for service on days that are not in the future // this avoids unnecessarily examining tomorrow's services if (secondsSinceMidnight < 0) continue; if (sd.serviceIdRunning(serviceId)) { int startTime = pattern.getNextDepartureTime( stopIndex, secondsSinceMidnight, options.wheelchairAccessible, mode == TraverseMode.BICYCLE, true); if (startTime >= 0) { // a trip was found, wait will be non-negative int wait = (int) (sd.time(startTime) - currentTime); if (wait < 0) _log.error("negative wait time on board"); if (bestWait < 0 || wait < bestWait) { // track the soonest departure over all relevant schedules bestWait = wait; } } } } if (bestWait < 0) { return null; } /* check if trip is banned for this plan */ if (options.tripIsBanned(trip)) return null; /* check if route is preferred for this plan */ long preferences_penalty = options.preferencesPenaltyForTrip(trip); StateEditor s1 = state0.edit(this); int type = pattern.getBoardType(stopIndex); if (TransitUtils.handleBoardAlightType(s1, type)) { return null; } s1.incrementTimeInSeconds(bestWait); s1.incrementNumBoardings(); s1.setTripId(trip.getId()); s1.setZone(pattern.getZone(stopIndex)); s1.setRoute(trip.getRoute().getId()); s1.setBackMode(TraverseMode.BOARDING); long wait_cost = bestWait; if (state0.getNumBoardings() == 0) { wait_cost *= options.waitAtBeginningFactor; } else { wait_cost *= options.waitReluctance; } s1.incrementWeight(preferences_penalty); s1.incrementWeight(wait_cost + options.getBoardCost(mode)); return s1.makeState(); } }
public List<RaptorState> transitPhase(RoutingRequest options, int nBoardings) { Collection<RaptorRoute> routesToVisit = new HashSet<RaptorRoute>(); if (data.routesForStop == null) { Collection<RaptorRoute> routes = data.routes; for (RaptorStop stop : visitedLastRound) { for (RaptorRoute route : data.routesForStop[stop.index]) { if (routes.contains(route)) { routesToVisit.add(route); } } } } else { for (RaptorStop stop : visitedLastRound) { for (RaptorRoute route : data.routesForStop[stop.index]) { routesToVisit.add(route); } } } HashSet<RaptorStop> visitedThisRound = new HashSet<RaptorStop>(); List<RaptorState> createdStates = new ArrayList<RaptorState>(); int boardSlack; if (options.isArriveBy()) { boardSlack = nBoardings == 1 ? options.getAlightSlack() : (options.getTransferSlack() - options.getBoardSlack()); } else { boardSlack = nBoardings == 1 ? options.getBoardSlack() : (options.getTransferSlack() - options.getAlightSlack()); } for (RaptorRoute route : routesToVisit) { List<RaptorState> boardStates = new ArrayList<RaptorState>(); // not really states boolean started; int firstStop, lastStop, direction, lastBoardStop; if (options.isArriveBy()) { firstStop = route.getNStops() - 1; lastStop = -1; direction = -1; lastBoardStop = 0; // check for interlining on the first stop started = checkForInterliningArriveBy(options, nBoardings, route, boardStates); } else { firstStop = 0; lastStop = route.getNStops(); direction = 1; lastBoardStop = lastStop - 1; started = checkForInterliningDepartAt(options, nBoardings, route, boardStates); } for (int stopNo = firstStop; stopNo != lastStop; stopNo += direction) { // find the current time at this stop RaptorStop stop = route.stops[stopNo]; if (!started && !visitedLastRound.contains(stop)) continue; started = true; // skip stops which aren't in this set of data; // this is used for the rush ahead search if (!data.raptorStopsForStopId.containsKey(stop.stopVertex.getStopId())) { continue; } // Skip banned stops if (options.getBannedStops().matches(stop.stopVertex.getStop())) { continue; } if (options.getBannedStopsHard().matches(stop.stopVertex.getStop())) { continue; } List<RaptorState> states = statesByStop[stop.index]; List<RaptorState> newStates = new ArrayList<RaptorState>(); if (states == null) { states = new ArrayList<RaptorState>(); statesByStop[stop.index] = states; } // this checks the case of continuing on the current trips. CONTINUE: for (RaptorState boardState : boardStates) { if (boardState.boardStop == stop) { // this only happens due to interlines where // the last stop of the first route is equal to the first stop of the // subsequent route. continue; } RaptorState newState = new RaptorState(boardState.getParent()); ServiceDay sd = boardState.serviceDay; int travelTime; if (options.isArriveBy()) { if (!route.alights[0][boardState.patternIndex].getPattern().canBoard(stopNo)) continue; int boardTime = route.getBoardTime(boardState.tripTimes, stopNo); newState.arrivalTime = (int) sd.time(boardTime); // add in slack newState.arrivalTime -= options.getBoardSlack(); travelTime = newState.getParent().arrivalTime - newState.arrivalTime; } else { if (!route.boards[0][boardState.patternIndex].getPattern().canAlight(stopNo)) continue; int alightTime = route.getAlightTime(boardState.tripTimes, stopNo); newState.arrivalTime = (int) sd.time(alightTime); // add in slack newState.arrivalTime += options.getAlightSlack(); travelTime = newState.arrivalTime - newState.getParent().arrivalTime; } newState.weight += travelTime; // TODO: consider transfer penalties newState.weight += boardState.weight; newState.boardStop = boardState.boardStop; newState.boardStopSequence = boardState.boardStopSequence; newState.route = route; newState.patternIndex = boardState.patternIndex; newState.tripTimes = boardState.tripTimes; newState.nBoardings = boardState.nBoardings; newState.walkDistance = boardState.walkDistance; newState.tripId = boardState.tripId; newState.stop = stop; newState.serviceDay = boardState.serviceDay; for (RaptorState oldState : states) { if (oldState.eDominates(newState)) { continue CONTINUE; } } for (RaptorState oldState : newStates) { if (oldState.eDominates(newState)) { continue CONTINUE; } } Iterator<RaptorState> it = states.iterator(); while (it.hasNext()) { RaptorState oldState = it.next(); if (newState.eDominates(oldState)) { it.remove(); } } it = newStates.iterator(); while (it.hasNext()) { RaptorState oldState = it.next(); if (newState.eDominates(oldState)) { it.remove(); } } visitedThisRound.add(stop); visitedEver.add(stop); newStates.add(newState); } if (stopNo != lastBoardStop) { if (stop.stopVertex.isLocal() && nBoardings > 1) { // cannot transfer at a local stop createdStates.addAll(newStates); states.addAll(newStates); continue; } // try boarding here TRYBOARD: for (RaptorState oldState : states) { if (oldState.nBoardings != nBoardings - 1) continue; if (oldState.getRoute() == route) continue; // we got here via this route, so no reason to transfer RaptorBoardSpec boardSpec; int waitTime; if (options.isArriveBy()) { int arrivalTime = oldState.arrivalTime - boardSlack; boardSpec = route.getTripIndexReverse(options, arrivalTime, stopNo); if (boardSpec == null) continue; waitTime = oldState.arrivalTime - boardSpec.departureTime; } else { int arrivalTime = oldState.arrivalTime + boardSlack; boardSpec = route.getTripIndex(options, arrivalTime, stopNo); if (boardSpec == null) continue; waitTime = boardSpec.departureTime - oldState.arrivalTime; } RaptorState boardState = new RaptorState(oldState); if (nBoardings == 1) { // do not count initial wait time, since it will be optimized away later boardState.initialWaitTime = waitTime; waitTime = 0; } boardState.weight = options.getBoardCost(route.mode) + waitTime; boardState.nBoardings = nBoardings; boardState.boardStop = stop; boardState.boardStopSequence = stopNo; boardState.arrivalTime = boardSpec.departureTime; boardState.patternIndex = boardSpec.patternIndex; boardState.tripTimes = boardSpec.tripTimes; boardState.serviceDay = boardSpec.serviceDay; boardState.route = route; boardState.walkDistance = oldState.walkDistance; boardState.tripId = boardSpec.tripId; for (RaptorState state : boardStates) { if (state.eDominates(boardState)) { continue TRYBOARD; } } for (RaptorState state : newStates) { if (state.eDominates(boardState)) { continue TRYBOARD; } } boardStates.add(boardState); } } createdStates.addAll(newStates); states.addAll(newStates); } } visitedLastRound = visitedThisRound; return createdStates; }