@Override public List<GraphPath> getPaths(RoutingRequest options) { final Graph graph = graphService.getGraph(options.getRouterId()); if (options.rctx == null) { options.setRoutingContext(graph); options.rctx.pathParsers = new PathParser[] {new BasicPathParser(), new NoThruTrafficPathParser()}; } if (!options.getModes().isTransit()) { return sptService.getShortestPathTree(options).getPaths(); } // also fall back to A* for short trips double distance = distanceLibrary.distance( options.rctx.origin.getCoordinate(), options.rctx.target.getCoordinate()); if (distance < shortPathCutoff) { log.debug("Falling back to A* for very short path"); return shortPathService.getPaths(options); } RaptorDataService service = graph.getService(RaptorDataService.class); if (service == null) { log.warn("No raptor data. Rebuild with RaptorDataBuilder"); return Collections.emptyList(); } RaptorData data = service.getData(); // we multiply the initial walk distance to account for epsilon dominance. double initialWalk = options.getMaxWalkDistance() * WALK_EPSILON; options.setMaxWalkDistance(initialWalk); // do not even bother with obviously impossible walks double minWalk = options.rctx.origin.getDistanceToNearestTransitStop() + options.rctx.target.getDistanceToNearestTransitStop(); if (options.getMaxWalkDistance() < minWalk) { options.setMaxWalkDistance(minWalk); } RoutingRequest walkOptions = options.clone(); walkOptions.rctx.pathParsers = new PathParser[0]; TraverseModeSet modes = options.getModes().clone(); modes.setTransit(false); walkOptions.setModes(modes); RaptorSearch search = new RaptorSearch(data, options); if (data.maxTransitRegions != null) { Calendar tripDate = Calendar.getInstance(graph.getTimeZone()); tripDate.setTime(new Date(1000L * options.dateTime)); Calendar maxTransitStart = Calendar.getInstance(graph.getTimeZone()); maxTransitStart.set(Calendar.YEAR, data.maxTransitRegions.startYear); maxTransitStart.set(Calendar.MONTH, data.maxTransitRegions.startMonth); maxTransitStart.set(Calendar.DAY_OF_MONTH, data.maxTransitRegions.startDay); int day = 0; while (tripDate.after(maxTransitStart)) { day++; tripDate.add(Calendar.DAY_OF_MONTH, -1); } if (day > data.maxTransitRegions.maxTransit.length || options.isWheelchairAccessible()) { day = -1; } search.maxTimeDayIndex = day; } int rushAheadRound = preliminaryRaptorSearch(data, options, walkOptions, search); long searchBeginTime = System.currentTimeMillis(); double expectedWorstTime = 1.5 * distanceLibrary.distance( options.rctx.origin.getCoordinate(), options.rctx.target.getCoordinate()) / options.getWalkSpeed(); int foundSoFar = 0; double firstWalkDistance = 0; List<RaptorState> targetStates = new ArrayList<RaptorState>(); do { int bestElapsedTime = Integer.MAX_VALUE; RETRY: do { for (int round = 0; round < options.getMaxTransfers() + 2; ++round) { if (!round(data, options, walkOptions, search, round)) break; long elapsed = System.currentTimeMillis() - searchBeginTime; if (elapsed > multiPathTimeout * 1000 && multiPathTimeout > 0 && targetStates.size() > 0) break RETRY; ArrayList<RaptorState> toRemove = new ArrayList<RaptorState>(); for (RaptorState state : search.getTargetStates()) { if (state.nBoardings == 0 && options.getMaxWalkDistance() > initialWalk) { toRemove.add(state); } } if (search.getTargetStates().size() > 0) { if (firstWalkDistance == 0) { firstWalkDistance = options.getMaxWalkDistance(); } for (RaptorState state : toRemove) { search.removeTargetState(state.walkPath); } } if (targetStates.size() >= options.getNumItineraries() && round >= rushAheadRound) { int oldBest = bestElapsedTime; for (RaptorState state : search.getTargetStates()) { final int elapsedTime = (int) Math.abs(state.arrivalTime - options.dateTime); if (elapsedTime < bestElapsedTime) { bestElapsedTime = elapsedTime; } } int improvement = oldBest - bestElapsedTime; if (improvement < 600 && bestElapsedTime < expectedWorstTime) break RETRY; } } if (foundSoFar < search.getTargetStates().size()) { foundSoFar = search.getTargetStates().size(); } else if (foundSoFar > 0) { // we didn't find anything new in this round, and we already have // some paths, so bail out break; } options = options.clone(); walkOptions = walkOptions.clone(); if (search.getTargetStates().size() > 0 && bestElapsedTime < expectedWorstTime) { // we have found some paths so we no longer want to expand the max walk distance break RETRY; } else { options.setMaxWalkDistance(options.getMaxWalkDistance() * 2); walkOptions.setMaxWalkDistance(options.getMaxWalkDistance()); options.setWalkReluctance(options.getWalkReluctance() * 2); walkOptions.setWalkReluctance(options.getWalkReluctance()); } search.reset(options); } while (options.getMaxWalkDistance() < initialWalk * MAX_WALK_MULTIPLE && initialWalk < Double.MAX_VALUE); options = options.clone(); walkOptions = walkOptions.clone(); for (RaptorState state : search.getTargetStates()) { for (AgencyAndId trip : state.getTrips()) { options.bannedTrips.add(trip); } } if (search.getTargetStates().size() == 0) break; // no paths found; searching more won't help options.setMaxWalkDistance(firstWalkDistance); walkOptions.setMaxWalkDistance(firstWalkDistance); targetStates.addAll(search.getTargetStates()); search = new RaptorSearch(data, options); } while (targetStates.size() < options.getNumItineraries()); collectRoutesUsed(data, options, targetStates); if (targetStates.isEmpty()) { log.info("RAPTOR found no paths"); } Collections.sort(targetStates); if (targetStates.size() > options.getNumItineraries()) targetStates = targetStates.subList(0, options.getNumItineraries()); List<GraphPath> paths = new ArrayList<GraphPath>(); for (RaptorState targetState : targetStates) { // reconstruct path ArrayList<RaptorState> states = new ArrayList<RaptorState>(); RaptorState cur = targetState; while (cur != null) { states.add(cur); cur = cur.getParent(); } // states is in reverse order of time State state = getState(targetState.getRequest(), data, states); paths.add(new GraphPath(state, true)); } return paths; }
/** * @param options * @param walkOptions * @param nBoardings * @param createdStates * @return whether search should continue */ public boolean walkPhase( RoutingRequest options, RoutingRequest walkOptions, int nBoardings, List<RaptorState> createdStates) { double distanceToNearestTransitStop = 0; if (options.rctx.target != null) { distanceToNearestTransitStop = options.rctx.target.getDistanceToNearestTransitStop(); } final int boardSlack = nBoardings == 1 ? options.getBoardSlack() : (options.getTransferSlack() - options.getAlightSlack()); ShortestPathTree spt; GenericDijkstra dijkstra = new GenericDijkstra(walkOptions); dijkstra.setShortestPathTreeFactory(bounder); List<State> transitStopStates = new ArrayList<State>(); if (nBoardings == 0) { // TODO: retry min-time bounding with this and with maxtime if (options.rctx.target != null && bounder.getTargetDistance(options.rctx.origin) < options.getMaxWalkDistance()) dijkstra.setHeuristic(bounder); MaxWalkState start = new MaxWalkState(options.rctx.origin, walkOptions); spt = dijkstra.getShortestPathTree(start); for (State state : spt.getAllStates()) { if (state.getVertex() instanceof TransitStop || state.getVertex() instanceof TransitStopArrive || state.getVertex() instanceof TransitStopDepart) transitStopStates.add(state); } // also, compute an initial spt from the target so that we can find out what transit // stops are nearby and what // the time is to them, so that we can start target bounding earlier if (maxTimeDayIndex > 0) { RoutingRequest reversedWalkOptions = walkOptions.clone(); reversedWalkOptions.setArriveBy(!walkOptions.isArriveBy()); GenericDijkstra destDijkstra = new GenericDijkstra(reversedWalkOptions); start = new MaxWalkState(options.rctx.target, reversedWalkOptions); ShortestPathTree targetSpt = destDijkstra.getShortestPathTree(start); for (State state : targetSpt.getAllStates()) { final Vertex vertex = state.getVertex(); if (!(vertex instanceof TransitStop)) continue; RaptorStop stop = data.raptorStopsForStopId.get(((TransitStop) vertex).getStopId()); if (stop == null) { // we have found a stop is totally unused, so skip it continue; } // Skip banned stops if (options.getBannedStops().matches(stop.stopVertex.getStop())) { continue; } if (options.getBannedStopsHard().matches(stop.stopVertex.getStop())) { continue; } addStopNearTarget(stop, state.getWalkDistance(), (int) state.getElapsedTimeSeconds()); } } } else { final List<MaxWalkState> startPoints = new ArrayList<MaxWalkState>(); for (RaptorState state : createdStates) { // bounding states // this reduces the number of initial vertices // and the state space size Vertex stopVertex = options.isArriveBy() ? state.stop.departVertex : state.stop.arriveVertex; if (stopVertex == null) { stopVertex = state.stop.stopVertex; } if (options.rctx.target != null) { double minWalk = distanceToNearestTransitStop; double targetDistance = bounder.getTargetDistance(stopVertex); if (targetDistance + state.walkDistance > options.getMaxWalkDistance()) { // can't walk to destination, so we can't alight at a local vertex if (state.stop.stopVertex.isLocal()) continue; } if (minWalk + state.walkDistance > options.getMaxWalkDistance()) { continue; } } StateEditor dijkstraState = new MaxWalkState.MaxWalkStateEditor(walkOptions, stopVertex); dijkstraState.setInitialWaitTimeSeconds(state.initialWaitTime); dijkstraState.setStartTimeSeconds(options.dateTime); dijkstraState.setNumBoardings(state.nBoardings); dijkstraState.setWalkDistance(state.walkDistance); dijkstraState.setTimeSeconds(state.arrivalTime); dijkstraState.setExtension("raptorParent", state); dijkstraState.setOptions(walkOptions); dijkstraState.incrementWeight(state.weight); MaxWalkState newState = (MaxWalkState) dijkstraState.makeState(); startPoints.add(newState); } if (startPoints.size() == 0) { return false; } System.out.println("walk starts: " + startPoints.size() + " / " + visitedEver.size()); dijkstra.setPriorityQueueFactory( new PrefilledPriorityQueueFactory(startPoints.subList(1, startPoints.size()))); bounder.addSptStates(startPoints.subList(1, startPoints.size())); bounder.prepareForSearch(); dijkstra.setSearchTerminationStrategy(bounder); if (options.rctx.target != null) { dijkstra.setSkipTraverseResultStrategy(bounder); dijkstra.setHeuristic(bounder); } // Do local search spt = dijkstra.getShortestPathTree(startPoints.get(0)); transitStopStates = bounder.getTransitStopsVisited(); } List<? extends State> targetStates = null; if (walkOptions.rctx.target != null) targetStates = spt.getStates(walkOptions.rctx.target); if (targetStates != null) { TARGET: for (State targetState : targetStates) { RaptorState parent = (RaptorState) targetState.getExtension("raptorParent"); RaptorState state; if (parent != null) { state = new RaptorState(parent); state.nBoardings = parent.nBoardings; state.rentingBike = targetState.isBikeRenting(); } else { state = new RaptorState(options); } state.weight = targetState.getWeight(); state.walkDistance = targetState.getWalkDistance(); state.arrivalTime = (int) targetState.getTimeSeconds(); state.walkPath = targetState; for (Iterator<RaptorState> it = getTargetStates().iterator(); it.hasNext(); ) { RaptorState oldState = it.next(); if (oldState.eDominates(state)) { continue TARGET; } else if (state.eDominates(oldState)) { it.remove(); } } addTargetState(state); log.debug("Found target at: " + state + " on " + state.getTrips()); } } for (State state : bounder.removedBoundingStates) { removeTargetState(state); } SPTSTATE: for (State state : transitStopStates) { final Vertex vertex = state.getVertex(); RaptorStop stop = data.raptorStopsForStopId.get(((OffboardVertex) vertex).getStopId()); if (stop == null) { // we have found a stop is totally unused, so skip it continue; } // Skip banned stops if (options.getBannedStops().matches(stop.stopVertex.getStop())) { continue; } if (options.getBannedStopsHard().matches(stop.stopVertex.getStop())) { continue; } if (options.rctx.target != null) { double minWalk = distanceToNearestTransitStop; double targetDistance = bounder.getTargetDistance(vertex); final double remainingWalk = options.maxWalkDistance - state.getWalkDistance(); if (maxTimeDayIndex > 0 && remainingWalk < 3218) { double minTime = (targetDistance - minWalk) / Raptor.MAX_TRANSIT_SPEED + minWalk / options.getStreetSpeedUpperBound(); if (targetDistance > remainingWalk) minTime += boardSlack; int maxTimeForVertex = 0; int region = vertex.getGroupIndex(); final int elapsedTime = (int) state.getElapsedTimeSeconds(); for (StopNearTarget stopNearTarget : stopsNearTarget.values()) { int destinationRegion = stopNearTarget.stop.stopVertex.getGroupIndex(); final int maxTimeFromThisRegion = data.maxTransitRegions.maxTransit[maxTimeDayIndex][destinationRegion][region]; int maxTime = elapsedTime + maxTimeFromThisRegion + stopNearTarget.time; if (maxTime > maxTimeForVertex) { maxTimeForVertex = maxTime; } } if (maxTimeForVertex < maxTime) { maxTime = maxTimeForVertex; } else { if (elapsedTime + minTime > maxTime * 1.5) { continue; } } } } List<RaptorState> states = statesByStop[stop.index]; if (states == null) { states = new ArrayList<RaptorState>(); statesByStop[stop.index] = states; } RaptorState parent = (RaptorState) state.getExtension("raptorParent"); RaptorState newState; if (parent != null) { newState = new RaptorState(parent); } else { // this only happens in round 0 newState = new RaptorState(options); } newState.weight = state.getWeight(); newState.nBoardings = nBoardings; newState.walkDistance = state.getWalkDistance(); newState.arrivalTime = (int) state.getTimeSeconds(); newState.walkPath = state; newState.stop = stop; newState.rentingBike = state.isBikeRenting(); for (RaptorState oldState : states) { if (oldState.eDominates(newState)) { continue SPTSTATE; } } visitedLastRound.add(stop); visitedEver.add(stop); states.add(newState); } return true; }