@Override public State traverse(State s0) { EdgeNarrative en = new FixedModeEdge(this, s0.getNonTransitMode(s0.getOptions())); RoutingRequest options = s0.getOptions(); if (options.wheelchairAccessible && !wheelchairAccessible) { return null; } TraverseMode mode = s0.getNonTransitMode(options); if (mode == TraverseMode.WALK && !permission.allows(StreetTraversalPermission.PEDESTRIAN)) { return null; } if (mode == TraverseMode.BICYCLE && !permission.allows(StreetTraversalPermission.BICYCLE)) { return null; } // there are elevators which allow cars if (mode == TraverseMode.CAR && !permission.allows(StreetTraversalPermission.CAR)) { return null; } StateEditor s1 = s0.edit(this, en); s1.incrementWeight(options.elevatorHopCost); s1.incrementTimeInSeconds(options.elevatorHopTime); return s1.makeState(); }
public State optimisticTraverse(State s0) { // do not include minimum transfer time in heuristic weight // (it is path-dependent) StateEditor s1 = s0.edit(this); s1.setBackMode(getMode()); return s1.makeState(); }
@Override public State traverse(State s0) { EdgeNarrative en = new FixedModeEdge(this, s0.getNonTransitMode(s0.getOptions())); StateEditor s1 = s0.edit(this, en); s1.incrementWeight(1); return s1.makeState(); }
public State optimisticTraverse(State state0) { int runningTime = getPattern().getBestRunningTime(stopIndex); StateEditor s1 = state0.edit(this); s1.incrementTimeInSeconds(runningTime); s1.setBackMode(getMode()); s1.incrementWeight(runningTime); return s1.makeState(); }
@Override public State traverse(State s0) { final RoutingRequest options = s0.getOptions(); final TraverseMode currMode = s0.getNonTransitMode(); StateEditor editor = doTraverse(s0, options, s0.getNonTransitMode()); State state = (editor == null) ? null : editor.makeState(); /* Kiss and ride support. Mode transitions occur without the explicit loop edges used in park-and-ride. */ if (options.kissAndRide) { if (options.arriveBy) { // Branch search to "unparked" CAR mode ASAP after transit has been used. // Final WALK check prevents infinite recursion. if (s0.isCarParked() && s0.isEverBoarded() && currMode == TraverseMode.WALK) { editor = doTraverse(s0, options, TraverseMode.CAR); if (editor != null) { editor.setCarParked(false); // Also has the effect of switching to CAR State forkState = editor.makeState(); if (forkState != null) { forkState.addToExistingResultChain(state); return forkState; // return both parked and unparked states } } } } else { /* departAfter */ // Irrevocable transition from driving to walking. "Parking" means being dropped off in this // case. // Final CAR check needed to prevent infinite recursion. if (!s0.isCarParked() && !getPermission().allows(TraverseMode.CAR) && currMode == TraverseMode.CAR) { editor = doTraverse(s0, options, TraverseMode.WALK); if (editor != null) { editor.setCarParked( true); // has the effect of switching to WALK and preventing further car use return editor.makeState(); // return only the "parked" walking state } } } } return state; }
public State traverse(State s0) { TripTimes tripTimes = s0.getTripTimes(); int runningTime = tripTimes.getRunningTime(stopIndex); StateEditor s1 = s0.edit(this); s1.incrementTimeInSeconds(runningTime); if (s0.getOptions().isArriveBy()) s1.setZone(getStartStop().getZoneId()); else s1.setZone(getEndStop().getZoneId()); // s1.setRoute(pattern.getExemplar().getRoute().getId()); s1.incrementWeight(runningTime); s1.setBackMode(getMode()); return s1.makeState(); }
@SuppressWarnings("unchecked") public Void call() throws Exception { // LOG.debug("ORIGIN " + origin); int oi = stopIndices.get(origin); // origin index // first check for walking transfers // LOG.debug(" Walk"); BinHeap<State> heap; try { heap = (BinHeap<State>) heapPool.borrowObject(); } catch (Exception e) { throw new RuntimeException(e); } BasicShortestPathTree spt = new BasicShortestPathTree(500000); State s0 = new State(origin, options); spt.add(s0); heap.insert(s0, s0.getWeight()); while (!heap.empty()) { double w = heap.peek_min_key(); State u = heap.extract_min(); if (!spt.visit(u)) continue; Vertex uVertex = u.getVertex(); // LOG.debug("heap extract " + u + " weight " + w); if (w > MAX_WEIGHT) break; if (uVertex instanceof TransitStop) { int di = stopIndices.get(uVertex); // dest index table[oi][di] = (float) w; // LOG.debug(" Dest " + u + " w=" + w); } for (Edge e : uVertex.getOutgoing()) { if (!(e instanceof PreBoardEdge)) { State v = e.optimisticTraverse(u); if (v != null && spt.add(v)) heap.insert(v, v.getWeight()); } } } // then check what is accessible in one transit trip heap.reset(); // recycle heap spt = new BasicShortestPathTree(50000); // first handle preboard edges Queue<Vertex> q = new ArrayDeque<Vertex>(100); q.add(origin); while (!q.isEmpty()) { Vertex u = q.poll(); for (Edge e : u.getOutgoing()) { if (e instanceof PatternBoard) { Vertex v = ((PatternBoard) e).getToVertex(); // give onboard vertices same index as their // corresponding station stopIndices.put(v, oi); StateEditor se = (new State(u, options)).edit(e); se.incrementWeight(OPTIMISTIC_BOARD_COST); s0 = se.makeState(); spt.add(s0); heap.insert(s0, s0.getWeight()); // _log.debug(" board " + tov); } else if (e instanceof FreeEdge) { // handle preboard Vertex v = ((FreeEdge) e).getToVertex(); // give onboard vertices same index as their // corresponding station stopIndices.put(v, oi); q.add(v); } } } // all boarding edges for this stop have now been traversed // LOG.debug(" Transit"); while (!heap.empty()) { // check for transit stops when pulling off of heap // and continue when one is found // this is enough to prevent reboarding // need to mark closed vertices because otherwise cycles may // appear (interlining...) double w = heap.peek_min_key(); State u = heap.extract_min(); if (!spt.visit(u)) continue; // LOG.debug(" Extract " + u + " w=" + w); Vertex uVertex = u.getVertex(); if (uVertex instanceof TransitStop) { int di = stopIndices.get(uVertex); // dest index if (table[oi][di] > w) { table[oi][di] = (float) w; // LOG.debug(" Dest " + u + "w=" + w); } continue; } for (Edge e : uVertex.getOutgoing()) { // LOG.debug(" Edge " + e); State v = e.optimisticTraverse(u); if (v != null && spt.add(v)) heap.insert(v, v.getWeight()); // else LOG.debug(" (skip)"); } } heapPool.returnObject(heap); incrementCount(); return null; }
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 State optimisticTraverse(State state0) { StateEditor s1 = state0.edit(this); // no cost (see patternalight) s1.setBackMode(TraverseMode.BOARDING); return s1.makeState(); }
@Override public List<GraphPath> plan(State origin, Vertex target, int nItineraries) { Date targetTime = new Date(origin.getTime() * 1000); TraverseOptions options = origin.getOptions(); if (_graphService.getCalendarService() != null) options.setCalendarService(_graphService.getCalendarService()); options.setTransferTable(_graphService.getGraph().getTransferTable()); options.setServiceDays(targetTime.getTime() / 1000); if (options.getModes().getTransit() && !_graphService.getGraph().transitFeedCovers(targetTime)) { // user wants a path through the transit network, // but the date provided is outside those covered by the transit feed. throw new TransitTimesException(); } // decide which A* heuristic to use options.remainingWeightHeuristic = _remainingWeightHeuristicFactory.getInstanceForSearch(options, target); LOG.debug("Applied A* heuristic: {}", options.remainingWeightHeuristic); // If transit is not to be used, disable walk limit and only search for one itinerary. if (!options.getModes().getTransit()) { nItineraries = 1; options.setMaxWalkDistance(Double.MAX_VALUE); } ArrayList<GraphPath> paths = new ArrayList<GraphPath>(); // The list of options specifying various modes, banned routes, etc to try for multiple // itineraries Queue<TraverseOptions> optionQueue = new LinkedList<TraverseOptions>(); optionQueue.add(options); /* if the user wants to travel by transit, create a bus-only set of options */ if (options.getModes().getTrainish() && options.getModes().contains(TraverseMode.BUS)) { TraverseOptions busOnly = options.clone(); busOnly.setModes(options.getModes().clone()); busOnly.getModes().setTrainish(false); // Moved inside block to avoid double insertion in list ? (AMB) // optionQueue.add(busOnly); } double maxWeight = Double.MAX_VALUE; long maxTime = options.isArriveBy() ? 0 : Long.MAX_VALUE; while (paths.size() < nItineraries) { options = optionQueue.poll(); if (options == null) { break; } StateEditor editor = new StateEditor(origin, null); editor.setTraverseOptions(options); origin = editor.makeState(); // options.worstTime = maxTime; // options.maxWeight = maxWeight; long searchBeginTime = System.currentTimeMillis(); LOG.debug("BEGIN SEARCH"); List<GraphPath> somePaths = _routingService.route(origin, target); LOG.debug("END SEARCH {} msec", System.currentTimeMillis() - searchBeginTime); if (maxWeight == Double.MAX_VALUE) { /* * the worst trip we are willing to accept is at most twice as bad or twice as long. */ if (somePaths.isEmpty()) { // if there is no first path, there won't be any other paths return null; } GraphPath path = somePaths.get(0); long duration = path.getDuration(); LOG.debug("Setting max time and weight for subsequent searches."); LOG.debug("First path start time: {}", path.getStartTime()); maxTime = path.getStartTime() + MAX_TIME_FACTOR * (options.isArriveBy() ? -duration : duration); LOG.debug("First path duration: {}", duration); LOG.debug("Max time set to: {}", maxTime); maxWeight = path.getWeight() * MAX_WEIGHT_FACTOR; LOG.debug("Max weight set to: {}", maxWeight); } if (somePaths.isEmpty()) { LOG.debug("NO PATHS FOUND"); continue; } for (GraphPath path : somePaths) { if (!paths.contains(path)) { // DEBUG // path.dump(); paths.add(path); // now, create a list of options, one with each trip in this journey banned. LOG.debug("New trips: {}", path.getTrips()); TraverseOptions newOptions = options.clone(); for (AgencyAndId trip : path.getTrips()) { newOptions.bannedTrips.add(trip); } if (!optionQueue.contains(newOptions)) { optionQueue.add(newOptions); } /* * // now, create a list of options, one with each route in this trip banned. // * the HashSet banned is not strictly necessary as the optionsQueue will // * already remove duplicate options, but it might be slightly faster as // * hashing TraverseOptions is slow. LOG.debug("New routespecs: {}", * path.getRouteSpecs()); for (RouteSpec spec : path.getRouteSpecs()) { * TraverseOptions newOptions = options.clone(); * newOptions.bannedRoutes.add(spec); if (!optionQueue.contains(newOptions)) { * optionQueue.add(newOptions); } } */ } } LOG.debug("{} / {} itineraries", paths.size(), nItineraries); } if (paths.size() == 0) { return null; } // We order the list of returned paths by the time of arrival or departure (not path duration) Collections.sort(paths, new PathComparator(origin.getOptions().isArriveBy())); return paths; }
@Override public State traverse(State s0) { RoutingRequest options = s0.getOptions(); // Ignore this edge if its stop is banned if (!options.bannedStops.isEmpty()) { if (options.bannedStops.matches(((TransitStop) fromv).getStop())) { return null; } } if (!options.bannedStopsHard.isEmpty()) { if (options.bannedStopsHard.matches(((TransitStop) fromv).getStop())) { return null; } } if (options.arriveBy) { /* Traverse backward: not much to do */ StateEditor s1 = s0.edit(this); TransitStop fromVertex = (TransitStop) getFromVertex(); // apply board slack s1.incrementTimeInSeconds(options.boardSlack); s1.alightTransit(); s1.setBackMode(getMode()); return s1.makeState(); } else { /* Traverse forward: apply stop(pair)-specific costs */ // Do not pre-board if transit modes are not selected. // Return null here rather than in StreetTransitLink so that walk-only // options can be used to find transit stops without boarding vehicles. if (!options.modes.isTransit()) return null; // If we've hit our transfer limit, don't go any further if (s0.getNumBoardings() > options.maxTransfers) return null; /* apply transfer rules */ /* * look in the global transfer table for the rules from the previous stop to this stop. */ long t0 = s0.getTimeSeconds(); long slack; if (s0.isEverBoarded()) { slack = options.transferSlack - options.alightSlack; } else { slack = options.boardSlack; } long board_after = t0 + slack; long transfer_penalty = 0; // penalize transfers more heavily if requested by the user if (s0.isEverBoarded()) { // this is not the first boarding, therefore we must have "transferred" -- whether // via a formal transfer or by walking. transfer_penalty += options.transferPenalty; } StateEditor s1 = s0.edit(this); s1.setTimeSeconds(board_after); long wait_cost = board_after - t0; s1.incrementWeight(wait_cost + transfer_penalty); s1.setBackMode(getMode()); return s1.makeState(); } }
/** * @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; }