/** * 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 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); }
@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)); }
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(); } }