private int doPreliminarySearch( RoutingRequest options, RoutingRequest walkOptions, RaptorSearch search, RaptorData trimmedData) { RaptorSearch rushSearch = new RaptorSearch(trimmedData, options); int bestElapsedTime = Integer.MAX_VALUE; int round; for (round = 0; round < options.getMaxTransfers() + 2; round++) { if (!round(trimmedData, options, walkOptions, rushSearch, round)) break; if (rushSearch.getTargetStates().size() > 0) { int oldBest = bestElapsedTime; for (RaptorState state : rushSearch.getTargetStates()) { final int elapsedTime = (int) Math.abs(state.arrivalTime - options.dateTime); if (elapsedTime < bestElapsedTime) { bestElapsedTime = elapsedTime; } } int improvement = oldBest - bestElapsedTime; if (improvement < 600) break; } } for (RaptorState state : rushSearch.getTargetStates()) { search.bounder.addBounder(state.walkPath); search.addTargetState(state); } return round; }
private boolean round( RaptorData data, RoutingRequest options, RoutingRequest walkOptions, final RaptorSearch search, int nBoardings) { log.debug("Round " + nBoardings); /* Phase 2: handle transit */ List<RaptorState> createdStates = search.transitPhase(options, nBoardings); /* Phase 3: handle walking paths */ return search.walkPhase(options, walkOptions, nBoardings, createdStates); }
/** * This does preliminary search over just routes and stops that have been used in the past between * these regions. */ private int preliminaryRaptorSearch( RaptorData data, RoutingRequest options, RoutingRequest walkOptions, RaptorSearch search) { // find start/end regions List<Integer> startRegions = getRegionsForVertex(data.regionData, options.rctx.fromVertex); int startRegion; // for trips that span regions, we can safely pick either region startRegion = startRegions.get(0); List<Integer> endRegions = getRegionsForVertex(data.regionData, options.rctx.toVertex); int endRegion; endRegion = endRegions.get(0); // create a reduced set of RaptorData with only the stops/routes previously seen on trips // from the start region to the end region RaptorData trimmedData = new RaptorData(); trimmedData.raptorStopsForStopId = new HashMap<AgencyAndId, RaptorStop>(); HashSet<RaptorStop> stops = data.regionData.stops[startRegion][endRegion]; for (RaptorStop stop : stops) { trimmedData.raptorStopsForStopId.put(stop.stopVertex.getStopId(), stop); } trimmedData.regionData = data.regionData; trimmedData.routes = data.regionData.routes[startRegion][endRegion]; trimmedData.stops = data.stops; // trimmedData.allowedStops = stops; trimmedData.routesForStop = data.routesForStop; double walkDistance = options.getMaxWalkDistance(); options = options.clone(); walkOptions = walkOptions.clone(); if (walkDistance > 4000) { // this is a really long walk. We'll almost never actually need this. So let's do our // preliminary search over just 4km first. options.setMaxWalkDistance(4000); walkOptions.setMaxWalkDistance(4000); } int round; if (trimmedData.routes.size() > 0) { log.debug( "Doing preliminary search on limited route set (" + trimmedData.routes.size() + ", " + stops.size() + ")"); round = doPreliminarySearch(options, walkOptions, search, trimmedData); } else { round = 0; } if (search.getTargetStates().size() == 0 && walkDistance > 5000) { // nothing found in preliminary search // so we'll do a search with full set of routes & stops, but still limited distance log.debug("Doing preliminary search at limited distance"); round = doPreliminarySearch(options, walkOptions, search, data); } return round; }
@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; }