public RaptorStateSet getStateSet(RoutingRequest options) { final Graph graph; if (options.rctx == null) { graph = graphService.getGraph(options.getRouterId()); options.setRoutingContext(graph); options.rctx.pathParsers = new PathParser[] {new BasicPathParser(), new NoThruTrafficPathParser()}; } else { graph = options.rctx.graph; } RaptorData data = graph.getService(RaptorDataService.class).getData(); // we multiply the initial walk distance to account for epsilon dominance. options.setMaxWalkDistance(options.getMaxWalkDistance() * WALK_EPSILON); 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); for (int i = 0; i < options.getMaxTransfers() + 2; ++i) { if (!round(data, options, walkOptions, search, i)) break; } RaptorStateSet result = new RaptorStateSet(); result.statesByStop = search.statesByStop; return result; }
/** * 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; }
/** * Makes a new empty Itinerary for a given path. * * @return */ private Itinerary makeEmptyItinerary(GraphPath path) { Itinerary itinerary = new Itinerary(); State startState = path.states.getFirst(); State endState = path.states.getLast(); itinerary.startTime = makeCalendar(startState); itinerary.endTime = makeCalendar(endState); itinerary.duration = endState.getTimeInMillis() - startState.getTimeInMillis(); Graph graph = path.getRoutingContext().graph; FareService fareService = graph.getService(FareService.class); if (fareService != null) { itinerary.fare = fareService.getCost(path); } itinerary.transfers = -1; return itinerary; }
/** Returns the first trip of the service day. */ public TripPlan generateFirstTrip(RoutingRequest request) { Graph graph = graphService.getGraph(request.getRouterId()); TransitIndexService transitIndex = graph.getService(TransitIndexService.class); transitIndexWithBreakRequired(transitIndex); request.setArriveBy(false); TimeZone tz = graph.getTimeZone(); GregorianCalendar calendar = new GregorianCalendar(tz); calendar.setTimeInMillis(request.dateTime * 1000); calendar.set(Calendar.HOUR, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.AM_PM, 0); calendar.set(Calendar.SECOND, transitIndex.getOvernightBreak()); request.dateTime = calendar.getTimeInMillis() / 1000; return generate(request); }
public RoutingContext( RoutingRequest traverseOptions, Graph graph, Vertex from, Vertex to, boolean findPlaces) { this.opt = traverseOptions; this.graph = graph; if (findPlaces) { // normal mode, search for vertices based on fromPlace and toPlace fromVertex = graph.streetIndex.getVertexForPlace(opt.getFromPlace(), opt); toVertex = graph.streetIndex.getVertexForPlace(opt.getToPlace(), opt, fromVertex); if (opt.intermediatePlaces != null) { for (NamedPlace intermediate : opt.intermediatePlaces) { Vertex vertex = graph.streetIndex.getVertexForPlace(intermediate, opt); intermediateVertices.add(vertex); } } } else { // debug mode, force endpoint vertices to those specified rather than searching fromVertex = from; toVertex = to; } if (opt.getStartingTransitStopId() != null) { TransitIndexService tis = graph.getService(TransitIndexService.class); if (tis == null) { throw new RuntimeException( "Next/Previous/First/Last trip " + "functionality depends on the transit index. Rebuild " + "the graph with TransitIndexBuilder"); } AgencyAndId stopId = opt.getStartingTransitStopId(); startingStop = tis.getPreBoardEdge(stopId).getToVertex(); } origin = opt.arriveBy ? toVertex : fromVertex; target = opt.arriveBy ? fromVertex : toVertex; calendarService = graph.getCalendarService(); transferTable = graph.getTransferTable(); timetableSnapshot = null; setServiceDays(); if (opt.batch) remainingWeightHeuristic = new TrivialRemainingWeightHeuristic(); else remainingWeightHeuristic = heuristicFactory.getInstanceForSearch(opt); }
@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; }
/** * Generate an itinerary from a @{link GraphPath}. The algorithm here is to walk over each state * in the graph path, accumulating geometry, time, and length data from the incoming edge. When * the incoming edge and outgoing edge have different modes (or when a vehicle changes names due * to interlining) a new leg is generated. Street legs undergo an additional processing step to * generate turn-by-turn directions. * * @param path * @param showIntermediateStops whether intermediate stops are included in the generated itinerary * @return itinerary */ private Itinerary generateItinerary(GraphPath path, boolean showIntermediateStops) { Graph graph = path.getRoutingContext().graph; TransitIndexService transitIndex = graph.getService(TransitIndexService.class); Itinerary itinerary = makeEmptyItinerary(path); EdgeNarrative postponedAlerts = null; Leg leg = null; CoordinateArrayListSequence coordinates = new CoordinateArrayListSequence(); double previousElevation = Double.MAX_VALUE; int startWalk = -1; int i = -1; PlanGenState pgstate = PlanGenState.START; String nextName = null; for (State state : path.states) { i += 1; Edge backEdge = state.getBackEdge(); EdgeNarrative backEdgeNarrative = state.getBackEdgeNarrative(); if (backEdge == null) { continue; } TraverseMode mode = backEdgeNarrative.getMode(); if (mode != null) { long dt = state.getAbsTimeDeltaSec(); if (mode == TraverseMode.BOARDING || mode == TraverseMode.ALIGHTING || mode == TraverseMode.STL) { itinerary.waitingTime += dt; } else if (mode.isOnStreetNonTransit()) { itinerary.walkDistance += backEdgeNarrative.getDistance(); itinerary.walkTime += dt; } else if (mode.isTransit()) { itinerary.transitTime += dt; } } if (backEdge instanceof FreeEdge) { if (backEdge instanceof PreBoardEdge) { // Add boarding alerts to the next leg postponedAlerts = backEdgeNarrative; } else if (backEdge instanceof PreAlightEdge) { // Add alighting alerts to the previous leg addNotesToLeg(itinerary.legs.get(itinerary.legs.size() - 1), backEdgeNarrative); } continue; } if (backEdge instanceof EdgeWithElevation) { PackedCoordinateSequence profile = ((EdgeWithElevation) backEdge).getElevationProfile(); previousElevation = applyElevation(profile, itinerary, previousElevation); } switch (pgstate) { case START: if (mode == TraverseMode.WALK) { pgstate = PlanGenState.WALK; leg = makeLeg(itinerary, state); leg.from.orig = nextName; startWalk = i; } else if (mode == TraverseMode.BICYCLE) { pgstate = PlanGenState.BICYCLE; leg = makeLeg(itinerary, state); leg.from.orig = nextName; startWalk = i; } else if (mode == TraverseMode.CAR) { pgstate = PlanGenState.CAR; leg = makeLeg(itinerary, state); leg.from.orig = nextName; startWalk = i; } else if (mode == TraverseMode.BOARDING) { // this itinerary starts with transit pgstate = PlanGenState.PRETRANSIT; leg = makeLeg(itinerary, state); leg.from.orig = nextName; startWalk = -1; } else if (mode == TraverseMode.STL) { // this comes after an alight; do nothing } else if (mode == TraverseMode.TRANSFER) { // handle the whole thing in one step leg = makeLeg(itinerary, state); coordinates = new CoordinateArrayListSequence(); coordinates.add(state.getBackState().getVertex().getCoordinate()); coordinates.add(state.getVertex().getCoordinate()); finalizeLeg(leg, state, path.states, i, i, coordinates); coordinates.clear(); } else { LOG.error("Unexpected state (in START): " + mode); } break; case WALK: if (leg == null) { leg = makeLeg(itinerary, state); } if (mode == TraverseMode.WALK) { // do nothing } else if (mode == TraverseMode.BICYCLE) { finalizeLeg(leg, state, path.states, startWalk, i, coordinates); startWalk = i; leg = makeLeg(itinerary, state); if (backEdge instanceof RentABikeOnEdge) { leg.rentedBike = true; } pgstate = PlanGenState.BICYCLE; } else if (mode == TraverseMode.STL) { finalizeLeg(leg, state, path.states, startWalk, i, coordinates); leg = null; pgstate = PlanGenState.PRETRANSIT; } else if (mode == TraverseMode.BOARDING) { // this only happens in case of a timed transfer. pgstate = PlanGenState.PRETRANSIT; finalizeLeg(leg, state, path.states, startWalk, i, coordinates); leg = makeLeg(itinerary, state); itinerary.transfers++; } else if (backEdgeNarrative instanceof LegSwitchingEdge) { nextName = state.getBackState().getBackState().getBackState().getVertex().getName(); finalizeLeg(leg, state, path.states, startWalk, i - 1, coordinates); leg = null; pgstate = PlanGenState.START; } else { LOG.error("Unexpected state (in WALK): " + mode); } break; case BICYCLE: if (leg == null) { leg = makeLeg(itinerary, state); } if (mode == TraverseMode.BICYCLE) { // do nothing } else if (mode == TraverseMode.WALK) { finalizeLeg(leg, state, path.states, startWalk, i, coordinates); leg = makeLeg(itinerary, state); startWalk = i; pgstate = PlanGenState.WALK; } else if (mode == TraverseMode.STL) { finalizeLeg(leg, state, path.states, startWalk, i, coordinates); leg = null; pgstate = PlanGenState.PRETRANSIT; } else if (backEdgeNarrative instanceof LegSwitchingEdge) { finalizeLeg(leg, state, path.states, startWalk, i - 1, coordinates); leg = null; pgstate = PlanGenState.START; } else { LOG.error("Unexpected state (in BICYCLE): " + mode); } break; case CAR: if (leg == null) { leg = makeLeg(itinerary, state); } if (mode == TraverseMode.CAR) { // do nothing } else if (mode == TraverseMode.STL) { finalizeLeg(leg, state, path.states, startWalk, i, coordinates); leg = null; pgstate = PlanGenState.PRETRANSIT; } else if (backEdgeNarrative instanceof LegSwitchingEdge) { finalizeLeg(leg, state, path.states, startWalk, i - 1, coordinates); leg = null; pgstate = PlanGenState.START; } else { LOG.error("Unexpected state (in CAR): " + mode); } break; case PRETRANSIT: if (mode == TraverseMode.BOARDING) { if (leg != null) { LOG.error("leg unexpectedly not null (boarding loop)"); } else { leg = makeLeg(itinerary, state); leg.stop = new ArrayList<Place>(); itinerary.transfers++; leg.boardRule = (String) state.getExtension("boardAlightRule"); } } else if (backEdge instanceof HopEdge) { pgstate = PlanGenState.TRANSIT; fixupTransitLeg(leg, state, transitIndex); leg.stop = new ArrayList<Place>(); } else { LOG.error("Unexpected state (in PRETRANSIT): " + mode); } break; case TRANSIT: String route = backEdgeNarrative.getName(); if (mode == TraverseMode.ALIGHTING) { if (showIntermediateStops && leg.stop != null && leg.stop.size() > 0) { if (leg.stop.isEmpty()) { leg.stop = null; } } leg.alightRule = (String) state.getExtension("boardAlightRule"); finalizeLeg(leg, state, null, -1, -1, coordinates); leg = null; pgstate = PlanGenState.START; } else if (mode.toString().equals(leg.mode)) { // no mode change, handle intermediate stops if (showIntermediateStops) { /* * any further transit edge, add "from" vertex to intermediate stops */ if (!(backEdge instanceof DwellEdge)) { Place stop = makePlace(state.getBackState(), true); leg.stop.add(stop); } else if (leg.stop.size() > 0) { leg.stop.get(leg.stop.size() - 1).departure = makeCalendar(state); } } if (!route.equals(leg.route)) { // interline dwell finalizeLeg(leg, state, null, -1, -1, coordinates); leg = makeLeg(itinerary, state); leg.stop = new ArrayList<Place>(); fixupTransitLeg(leg, state, transitIndex); leg.startTime = makeCalendar(state); leg.interlineWithPreviousLeg = true; } } else { LOG.error("Unexpected state (in TRANSIT): " + mode); } break; } if (leg != null) { leg.distance += backEdgeNarrative.getDistance(); Geometry edgeGeometry = backEdgeNarrative.getGeometry(); if (edgeGeometry != null) { Coordinate[] edgeCoordinates = edgeGeometry.getCoordinates(); if (coordinates.size() > 0 && coordinates.getCoordinate(coordinates.size() - 1).equals(edgeCoordinates[0])) { coordinates.extend(edgeCoordinates, 1); } else { coordinates.extend(edgeCoordinates); } } if (postponedAlerts != null) { addNotesToLeg(leg, postponedAlerts); postponedAlerts = null; } addNotesToLeg(leg, backEdgeNarrative); } } /* end loop over graphPath edge list */ if (leg != null) { finalizeLeg(leg, path.states.getLast(), path.states, startWalk, i, coordinates); } itinerary.removeBogusLegs(); itinerary.fixupDates(graph.getService(CalendarServiceData.class)); if (itinerary.legs.size() == 0) throw new TrivialPathException(); return itinerary; }
/** * Generate an itinerary from a @{link GraphPath}. The algorithm here is to walk over each state * in the graph path, accumulating geometry, time, and length data from the incoming edge. When * the incoming edge and outgoing edge have different modes (or when a vehicle changes names due * to interlining) a new leg is generated. Street legs undergo an additional processing step to * generate turn-by-turn directions. * * @param path * @param showIntermediateStops whether intermediate stops are included in the generated itinerary * @return itinerary */ private Itinerary generateItinerary(GraphPath path, boolean showIntermediateStops) { Graph graph = path.getRoutingContext().graph; TransitIndexService transitIndex = graph.getService(TransitIndexService.class); Itinerary itinerary = makeEmptyItinerary(path); Set<Alert> postponedAlerts = null; Leg leg = null; CoordinateArrayListSequence coordinates = new CoordinateArrayListSequence(); double previousElevation = Double.MAX_VALUE; int startWalk = -1; int i = -1; boolean foldingElevatorLegIntoCycleLeg = false; PlanGenState pgstate = PlanGenState.START; String nextName = null; for (State state : path.states) { i += 1; Edge backEdge = state.getBackEdge(); if (backEdge == null) { continue; } // debug: push vehicle late status out to UI // if (backEdge instanceof PatternHop) { // TripTimes tt = state.getTripTimes(); // int hop = ((PatternHop)backEdge).stopIndex; // LOG.info("{} {}", tt.getTrip().toString(), hop); // if ( ! tt.isScheduled()) { // int delay = tt.getDepartureDelay(hop); // String d = "on time"; // if (Math.abs(delay) > 10) { // d = String.format("%2.1f min %s", delay / 60.0, // (delay < 0) ? "early" : "late"); // } // d = "Using real-time delay information: ".concat(d); // leg.addAlert(Alert.createSimpleAlerts(d)); // LOG.info(d); // } // else { // leg.addAlert(Alert.createSimpleAlerts("Using published timetables.")); // LOG.info("sched"); // } // } TraverseMode mode = state.getBackMode(); if (mode != null) { long dt = state.getAbsTimeDeltaSec(); if (mode == TraverseMode.BOARDING || mode == TraverseMode.ALIGHTING || mode == TraverseMode.STL) { itinerary.waitingTime += dt; } else if (mode.isOnStreetNonTransit()) { itinerary.walkDistance += backEdge.getDistance(); itinerary.walkTime += dt; } else if (mode.isTransit()) { itinerary.transitTime += dt; } } if (backEdge instanceof FreeEdge) { if (backEdge instanceof PreBoardEdge) { // Add boarding alerts to the next leg postponedAlerts = state.getBackAlerts(); } else if (backEdge instanceof PreAlightEdge) { // Add alighting alerts to the previous leg addNotesToLeg(itinerary.legs.get(itinerary.legs.size() - 1), state.getBackAlerts()); } continue; } if (backEdge instanceof EdgeWithElevation) { PackedCoordinateSequence profile = ((EdgeWithElevation) backEdge).getElevationProfile(); previousElevation = applyElevation(profile, itinerary, previousElevation); } switch (pgstate) { case START: if (mode == TraverseMode.WALK) { pgstate = PlanGenState.WALK; leg = makeLeg(itinerary, state); leg.from.orig = nextName; startWalk = i; } else if (mode == TraverseMode.BICYCLE) { pgstate = PlanGenState.BICYCLE; leg = makeLeg(itinerary, state); leg.from.orig = nextName; startWalk = i; } else if (mode == TraverseMode.CAR) { pgstate = PlanGenState.CAR; leg = makeLeg(itinerary, state); leg.from.orig = nextName; startWalk = i; } else if (mode == TraverseMode.BOARDING) { // this itinerary starts with transit pgstate = PlanGenState.PRETRANSIT; leg = makeLeg(itinerary, state); leg.from.orig = nextName; itinerary.transfers++; startWalk = -1; } else if (mode == TraverseMode.STL) { // this comes after an alight; do nothing } else if (mode == TraverseMode.TRANSFER) { // handle the whole thing in one step leg = makeLeg(itinerary, state); coordinates = new CoordinateArrayListSequence(); coordinates.add(state.getBackState().getVertex().getCoordinate()); coordinates.add(state.getVertex().getCoordinate()); finalizeLeg(leg, state, path.states, i, i, coordinates, itinerary); coordinates.clear(); } else { LOG.error("Unexpected state (in START): " + mode); } break; case WALK: if (leg == null) { leg = makeLeg(itinerary, state); } if (mode == TraverseMode.WALK) { // do nothing } else if (mode == TraverseMode.BICYCLE) { finalizeLeg(leg, state, path.states, startWalk, i, coordinates, itinerary); startWalk = i; leg = makeLeg(itinerary, state); pgstate = PlanGenState.BICYCLE; } else if (mode == TraverseMode.STL) { finalizeLeg(leg, state, path.states, startWalk, i, coordinates, itinerary); leg = null; pgstate = PlanGenState.PRETRANSIT; } else if (mode == TraverseMode.BOARDING) { // this only happens in case of a timed transfer. pgstate = PlanGenState.PRETRANSIT; finalizeLeg(leg, state, path.states, startWalk, i, coordinates, itinerary); leg = makeLeg(itinerary, state); itinerary.transfers++; } else if (backEdge instanceof LegSwitchingEdge) { nextName = state.getBackState().getBackState().getBackState().getVertex().getName(); finalizeLeg(leg, state, path.states, startWalk, i - 1, coordinates, itinerary); leg = null; pgstate = PlanGenState.START; } else { LOG.error("Unexpected state (in WALK): " + mode); } break; case BICYCLE: if (leg == null) { leg = makeLeg(itinerary, state); } // If there are elevator edges that have mode == BICYCLE on both sides, they should // be folded into the bicycle leg. But ones with walk on one side or the other should // not if (state.getBackEdge() instanceof ElevatorBoardEdge) { int j = i + 1; // proceed forward from the current state until we find one that isn't on an // elevator, and check the traverse mode while (path.states.get(j).getBackEdge() instanceof ElevatorEdge) j++; // path.states[j] is not an elevator edge if (path.states.get(j).getBackMode() == TraverseMode.BICYCLE) foldingElevatorLegIntoCycleLeg = true; } if (foldingElevatorLegIntoCycleLeg) { if (state.getBackEdge() instanceof ElevatorEdge) { break; // from the case } else { foldingElevatorLegIntoCycleLeg = false; // do not break but allow it to be processed below (which will do nothing) } } if (mode == TraverseMode.BICYCLE) { // do nothing } else if (mode == TraverseMode.WALK) { finalizeLeg(leg, state, path.states, startWalk, i, coordinates, itinerary); leg = makeLeg(itinerary, state); startWalk = i; pgstate = PlanGenState.WALK; } else if (mode == TraverseMode.STL) { finalizeLeg(leg, state, path.states, startWalk, i, coordinates, itinerary); startWalk = i; leg = null; pgstate = PlanGenState.PRETRANSIT; } else if (backEdge instanceof LegSwitchingEdge) { finalizeLeg(leg, state, path.states, startWalk, i - 1, coordinates, itinerary); leg = null; pgstate = PlanGenState.START; } else { LOG.error("Unexpected state (in BICYCLE): " + mode); } break; case CAR: if (leg == null) { leg = makeLeg(itinerary, state); } if (mode == TraverseMode.CAR) { // do nothing } else if (mode == TraverseMode.STL) { finalizeLeg(leg, state, path.states, startWalk, i, coordinates, itinerary); leg = null; pgstate = PlanGenState.PRETRANSIT; } else if (backEdge instanceof LegSwitchingEdge) { finalizeLeg(leg, state, path.states, startWalk, i - 1, coordinates, itinerary); leg = null; pgstate = PlanGenState.START; } else { LOG.error("Unexpected state (in CAR): " + mode); } break; case PRETRANSIT: if (mode == TraverseMode.BOARDING) { if (leg != null) { LOG.error("leg unexpectedly not null (boarding loop)"); } else { leg = makeLeg(itinerary, state); leg.from.stopIndex = ((OnBoardForwardEdge) backEdge).getStopIndex(); leg.stop = new ArrayList<Place>(); itinerary.transfers++; leg.boardRule = (String) state.getExtension("boardAlightRule"); } } else if (backEdge instanceof HopEdge) { pgstate = PlanGenState.TRANSIT; fixupTransitLeg(leg, state, transitIndex); leg.stop = new ArrayList<Place>(); } else { LOG.error("Unexpected state (in PRETRANSIT): " + mode); } break; case TRANSIT: String route = backEdge.getName(); if (mode == TraverseMode.ALIGHTING) { if (showIntermediateStops && leg.stop != null && leg.stop.size() > 0) { if (leg.stop.isEmpty()) { leg.stop = null; } } leg.alightRule = (String) state.getExtension("boardAlightRule"); finalizeLeg(leg, state, null, -1, -1, coordinates, itinerary); leg = null; pgstate = PlanGenState.START; } else if (mode.toString().equals(leg.mode)) { // no mode change, handle intermediate stops if (showIntermediateStops) { /* * any further transit edge, add "from" vertex to intermediate stops */ if (!(backEdge instanceof DwellEdge)) { Place stop = makePlace( state.getBackState(), state.getBackState().getVertex().getName(), true); leg.stop.add(stop); } else if (leg.stop.size() > 0) { leg.stop.get(leg.stop.size() - 1).departure = makeCalendar(state); } } if (!route.equals(leg.route)) { // interline dwell finalizeLeg(leg, state, null, -1, -1, coordinates, itinerary); leg = makeLeg(itinerary, state); leg.stop = new ArrayList<Place>(); fixupTransitLeg(leg, state, transitIndex); leg.startTime = makeCalendar(state); leg.interlineWithPreviousLeg = true; } } else { LOG.error("Unexpected state (in TRANSIT): " + mode); } break; } if (leg != null) { leg.distance += backEdge.getDistance(); Geometry edgeGeometry = backEdge.getGeometry(); if (edgeGeometry != null) { Coordinate[] edgeCoordinates = edgeGeometry.getCoordinates(); if (coordinates.size() > 0 && coordinates.getCoordinate(coordinates.size() - 1).equals(edgeCoordinates[0])) { coordinates.extend(edgeCoordinates, 1); } else { coordinates.extend(edgeCoordinates); } } if (postponedAlerts != null) { addNotesToLeg(leg, postponedAlerts); postponedAlerts = null; } addNotesToLeg(leg, state.getBackAlerts()); } } /* end loop over graphPath edge list */ if (leg != null) { finalizeLeg(leg, path.states.getLast(), path.states, startWalk, i, coordinates, itinerary); } itinerary.removeBogusLegs(); itinerary.fixupDates(graph.getService(CalendarServiceData.class)); if (itinerary.legs.size() == 0) throw new TrivialPathException(); return itinerary; }