@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; }
public void testHalfEdges() { // the shortest half-edge from the start vertex takes you down, but the shortest total path // is up and over TraverseOptions options = new TraverseOptions(); HashSet<Edge> turns = new HashSet<Edge>(graph.getOutgoing(left)); turns.addAll(graph.getOutgoing(leftBack)); StreetLocation start = StreetLocation.createStreetLocation( "start", "start", cast(turns, StreetEdge.class), new LinearLocation(0, 0.4).getCoordinate(left.getGeometry())); HashSet<Edge> endTurns = new HashSet<Edge>(graph.getOutgoing(right)); endTurns.addAll(graph.getOutgoing(rightBack)); StreetLocation end = StreetLocation.createStreetLocation( "end", "end", cast(endTurns, StreetEdge.class), new LinearLocation(0, 0.8).getCoordinate(right.getGeometry())); assertTrue(start.getX() < end.getX()); assertTrue(start.getY() < end.getY()); List<DirectEdge> extra = end.getExtra(); assertEquals(12, extra.size()); GregorianCalendar startTime = new GregorianCalendar(2009, 11, 1, 12, 34, 25); ShortestPathTree spt1 = AStar.getShortestPathTree(graph, brOut, end, TestUtils.toSeconds(startTime), options); GraphPath pathBr = spt1.getPath(end, false); assertNotNull("There must be a path from br to end", pathBr); ShortestPathTree spt2 = AStar.getShortestPathTree(graph, trOut, end, TestUtils.toSeconds(startTime), options); GraphPath pathTr = spt2.getPath(end, false); assertNotNull("There must be a path from tr to end", pathTr); assertTrue( "path from bottom to end must be longer than path from top to end", pathBr.getWeight() > pathTr.getWeight()); ShortestPathTree spt = AStar.getShortestPathTree(graph, start, end, TestUtils.toSeconds(startTime), options); GraphPath path = spt.getPath(end, false); assertNotNull("There must be a path from start to end", path); // the bottom is not part of the shortest path for (State s : path.states) { assertNotSame(s.getVertex(), graph.getVertex("bottom")); assertNotSame(s.getVertex(), graph.getVertex("bottomBack")); } startTime = new GregorianCalendar(2009, 11, 1, 12, 34, 25); options.setArriveBy(true); spt = AStar.getShortestPathTree(graph, start, end, TestUtils.toSeconds(startTime), options); path = spt.getPath(start, false); assertNotNull("There must be a path from start to end (looking back)", path); // the bottom edge is not part of the shortest path for (State s : path.states) { assertNotSame(s.getVertex(), graph.getVertex("bottom")); assertNotSame(s.getVertex(), graph.getVertex("bottomBack")); } /* Now, the right edge is not bikeable. But the user can walk their bike. So here are some tests * that prove (a) that walking bikes works, but that (b) it is not preferred to riding a tiny bit longer. */ options = new TraverseOptions(new TraverseModeSet(TraverseMode.BICYCLE)); start = StreetLocation.createStreetLocation( "start1", "start1", cast(turns, StreetEdge.class), new LinearLocation(0, 0.95).getCoordinate(top.getGeometry())); end = StreetLocation.createStreetLocation( "end1", "end1", cast(turns, StreetEdge.class), new LinearLocation(0, 0.95).getCoordinate(bottom.getGeometry())); spt = AStar.getShortestPathTree(graph, start, end, TestUtils.toSeconds(startTime), options); path = spt.getPath(start, false); assertNotNull("There must be a path from top to bottom along the right", path); // the left edge is not part of the shortest path (even though the bike must be walked along the // right) for (State s : path.states) { assertNotSame(s.getVertex(), graph.getVertex("left")); assertNotSame(s.getVertex(), graph.getVertex("leftBack")); } start = StreetLocation.createStreetLocation( "start2", "start2", cast(turns, StreetEdge.class), new LinearLocation(0, 0.55).getCoordinate(top.getGeometry())); end = StreetLocation.createStreetLocation( "end2", "end2", cast(turns, StreetEdge.class), new LinearLocation(0, 0.55).getCoordinate(bottom.getGeometry())); spt = AStar.getShortestPathTree(graph, start, end, TestUtils.toSeconds(startTime), options); path = spt.getPath(start, false); assertNotNull("There must be a path from top to bottom", path); // the right edge is not part of the shortest path, e for (State s : path.states) { assertNotSame(s.getVertex(), graph.getVertex("right")); assertNotSame(s.getVertex(), graph.getVertex("rightBack")); } }