public static int getNextDepartTime( RoutingRequest request, int departureTime, Vertex stopVertex) { int bestArrivalTime = Integer.MAX_VALUE; request.arriveBy = false; // find the boards for (Edge preboard : stopVertex.getOutgoing()) { if (preboard instanceof PreBoardEdge) { Vertex departure = preboard.getToVertex(); // this is the departure vertex for (Edge board : departure.getOutgoing()) { if (board instanceof TransitBoardAlight) { State state = new State(board.getFromVertex(), departureTime, request); State result = board.traverse(state); if (result == null) continue; int time = (int) result.getTime(); if (time < bestArrivalTime) { bestArrivalTime = time; } } } } } request.arriveBy = true; return bestArrivalTime; }
public void testBoardAlight() throws Exception { Vertex stop_a_depart = graph.getVertex("agency:A_depart"); Vertex stop_b_depart = graph.getVertex("agency:B_depart"); assertEquals(1, stop_a_depart.getDegreeOut()); assertEquals(3, stop_b_depart.getDegreeOut()); for (Edge e : stop_a_depart.getOutgoing()) { assertEquals(TransitBoardAlight.class, e.getClass()); assertTrue(((TransitBoardAlight) e).boarding); } TransitBoardAlight pb = (TransitBoardAlight) stop_a_depart.getOutgoing().iterator().next(); Vertex journey_a_1 = pb.getToVertex(); assertEquals(1, journey_a_1.getDegreeIn()); for (Edge e : journey_a_1.getOutgoing()) { if (e.getToVertex() instanceof TransitStop) { assertEquals(TransitBoardAlight.class, e.getClass()); } else { assertEquals(PatternHop.class, e.getClass()); } } }
/** * The safest bike lane should have a safety weight no lower than the time weight of a flat * street. This method divides the safety lengths by the length ratio of the safest street, * ensuring this property. * * @param graph */ private void applyBikeSafetyFactor(Graph graph) { _log.info( GraphBuilderAnnotation.register( graph, Variety.GRAPHWIDE, "Multiplying all bike safety values by " + (1 / bestBikeSafety))); HashSet<Edge> seenEdges = new HashSet<Edge>(); for (Vertex vertex : graph.getVertices()) { for (Edge e : vertex.getOutgoing()) { if (!(e instanceof PlainStreetEdge)) { continue; } PlainStreetEdge pse = (PlainStreetEdge) e; if (!seenEdges.contains(e)) { seenEdges.add(e); pse.setBicycleSafetyEffectiveLength( pse.getBicycleSafetyEffectiveLength() / bestBikeSafety); } } for (Edge e : vertex.getIncoming()) { if (!(e instanceof PlainStreetEdge)) { continue; } PlainStreetEdge pse = (PlainStreetEdge) e; if (!seenEdges.contains(e)) { seenEdges.add(e); pse.setBicycleSafetyEffectiveLength( pse.getBicycleSafetyEffectiveLength() / bestBikeSafety); } } } }
public void testPickupDropoff() throws Exception { Vertex stop_o = graph.getVertex("agency_O_depart"); Vertex stop_p = graph.getVertex("agency_P"); int i = 0; for (@SuppressWarnings("unused") Edge e : stop_o.getOutgoing()) { ++i; } assertTrue(i == 3); long startTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 19, 12, 0, 0); RoutingRequest options = new RoutingRequest(); options.dateTime = startTime; options.setRoutingContext(graph, stop_o, stop_p); ShortestPathTree spt = aStar.getShortestPathTree(options); GraphPath path = spt.getPath(stop_p, false); assertNotNull(path); long endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 19, 12, 10, 0); assertEquals(endTime, path.getEndTime()); startTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 19, 12, 0, 1); options.dateTime = startTime; options.setRoutingContext(graph, stop_o, stop_p); spt = aStar.getShortestPathTree(options); path = spt.getPath(stop_p, false); assertNotNull(path); endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 19, 15, 10, 0); assertEquals(endTime, path.getEndTime()); }
public synchronized void initIndexes() { if (vertexIndex != null) { return; } graphService.setLoadLevel(LoadLevel.DEBUG); Graph graph = graphService.getGraph(); vertexIndex = new STRtree(); edgeIndex = new STRtree(); for (Vertex v : graph.getVertices()) { Envelope vertexEnvelope = new Envelope(v.getCoordinate()); vertexIndex.insert(vertexEnvelope, v); for (Edge e : v.getOutgoing()) { Envelope envelope; Geometry geometry = e.getGeometry(); if (geometry == null) { envelope = vertexEnvelope; } else { envelope = geometry.getEnvelopeInternal(); } edgeIndex.insert(envelope, e); } } vertexIndex.build(); edgeIndex.build(); }
public void testFrequencies() { Vertex stop_u = graph.getVertex("agency_U_depart"); Vertex stop_v = graph.getVertex("agency_V_arrive"); ShortestPathTree spt; GraphPath path; RoutingRequest options = new RoutingRequest(); options.setModes(new TraverseModeSet("TRANSIT")); options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 0, 0, 0); options.setRoutingContext(graph, stop_u, stop_v); // U to V - original stop times - shouldn't be used spt = aStar.getShortestPathTree(options); path = spt.getPath(stop_v, false); assertNotNull(path); assertEquals(4, path.states.size()); long endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 6, 40, 0); assertEquals(endTime, path.getEndTime()); // U to V - first frequency options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 7, 0, 0); options.setRoutingContext(graph, stop_u, stop_v); spt = aStar.getShortestPathTree(options); path = spt.getPath(stop_v, false); assertNotNull(path); assertEquals(4, path.states.size()); endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 7, 40, 0); assertEquals(endTime, path.getEndTime()); // U to V - second frequency options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 14, 0, 0); options.setRoutingContext(graph, stop_u, stop_v); spt = aStar.getShortestPathTree(options); path = spt.getPath(stop_v, false); assertNotNull(path); assertEquals(4, path.states.size()); endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 14, 40, 0); assertEquals(endTime, path.getEndTime()); boolean boarded = false; for (FrequencyBoard e : filter(stop_u.getOutgoing(), FrequencyBoard.class)) { boarded = true; FrequencyBoard board = (FrequencyBoard) e; FrequencyBasedTripPattern pattern = board.getPattern(); int previousArrivalTime = pattern.getPreviousArrivalTime(0, 0, false, false, false); assertTrue(previousArrivalTime < 0); previousArrivalTime = pattern.getPreviousArrivalTime(0, 60 * 60 * 7 - 1, false, false, false); assertEquals(60 * 60 * 6, previousArrivalTime); previousArrivalTime = pattern.getPreviousArrivalTime(0, 60 * 60 * 11, false, false, false); assertEquals(60 * 60 * 10, previousArrivalTime); previousArrivalTime = pattern.getPreviousArrivalTime(0, 60 * 60 * 18, false, false, false); assertEquals(60 * 60 * 16, previousArrivalTime); } assertTrue(boarded); }
protected List<Edge> getOutgoingMatchableEdges(Vertex vertex) { List<Edge> edges = new ArrayList<Edge>(); for (Edge e : vertex.getOutgoing()) { if (!(e instanceof StreetEdge)) continue; if (e.getGeometry() == null) continue; edges.add(e); } return edges; }
public PatternHop getHopOut(Vertex v) { for (TransitBoardAlight e : filter(v.getOutgoing(), TransitBoardAlight.class)) { if (!e.isBoarding()) continue; for (PatternHop f : filter(e.getToVertex().getOutgoing(), PatternHop.class)) { return f; } } return null; }
public static DisjointSet<Vertex> getConnectedComponents(Graph graph) { DisjointSet<Vertex> components = new DisjointSet<Vertex>(); for (Vertex v : graph.getVertices()) { for (Edge e : v.getOutgoing()) { components.union(e.getFromVertex(), e.getToVertex()); } } return components; }
public void testWheelchairAccessible() throws Exception { Vertex near_a = graph.getVertex("near_1_agency_entrance_a"); Vertex near_b = graph.getVertex("near_1_agency_entrance_b"); Vertex near_c = graph.getVertex("near_1_agency_C"); Vertex near_e = graph.getVertex("near_1_agency_E"); Vertex stop_d = graph.getVertex("agency:D"); Vertex split_d = null; for (StreetTransitLink e : Iterables.filter(stop_d.getOutgoing(), StreetTransitLink.class)) { split_d = e.getToVertex(); } RoutingRequest options = new RoutingRequest(); options.wheelchairAccessible = true; options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 18, 0, 0, 0); ShortestPathTree spt; GraphPath path; // stop B is accessible, so there should be a path. options.setRoutingContext(graph, near_a, near_b); spt = aStar.getShortestPathTree(options); path = spt.getPath(near_b, false); assertNotNull(path); // stop C is not accessible, so there should be no path. options.setRoutingContext(graph, near_a, near_c); spt = aStar.getShortestPathTree(options); path = spt.getPath(near_c, false); assertNull(path); // stop E has no accessibility information, but we should still be able to route to it. options.setRoutingContext(graph, near_a, near_e); spt = aStar.getShortestPathTree(options); path = spt.getPath(near_e, false); assertNotNull(path); // from stop A to stop D would normally be trip 1.1 to trip 2.1, arriving at 00:30. But trip // 2 is not accessible, so we'll do 1.1 to 3.1, arriving at 01:00 GregorianCalendar time = new GregorianCalendar(2009, 8, 18, 0, 0, 0); time.setTimeZone(TimeZone.getTimeZone("America/New_York")); options.dateTime = TestUtils.toSeconds(time); options.setRoutingContext(graph, near_a, split_d); spt = aStar.getShortestPathTree(options); time.add(Calendar.HOUR, 1); time.add(Calendar.SECOND, 1); // for the StreetTransitLink path = spt.getPath(split_d, false); assertNotNull(path); assertEquals(TestUtils.toSeconds(time), path.getEndTime()); }
/* Somewhat hackish convenience method to grab a hop edge on a particular route leaving a particular stop. */ private PatternHop getHopEdge(String stopId, String routeId) { Vertex stopDepartVertex = graph.getVertex("agency:" + stopId + "_depart"); for (Edge edge : stopDepartVertex.getOutgoing()) { if (edge instanceof TransitBoardAlight) { TransitBoardAlight tba = ((TransitBoardAlight) edge); if (tba.boarding && tba.getPattern().route.getId().getId().equals(routeId)) { for (Edge edge2 : tba.getToVertex().getOutgoing()) { if (edge2 instanceof PatternHop) { return (PatternHop) edge2; } } } } } return null; }
public void testBoardAlightStopIndex() { Vertex stop_b_arrive = graph.getVertex("agency:C_arrive"); Vertex stop_b_depart = graph.getVertex("agency:C_depart"); Map<TripPattern, Integer> stopIndex = new HashMap<TripPattern, Integer>(); for (Edge edge : stop_b_depart.getOutgoing()) { TransitBoardAlight tba = (TransitBoardAlight) edge; stopIndex.put(tba.getPattern(), tba.getStopIndex()); } for (Edge edge : stop_b_arrive.getIncoming()) { TransitBoardAlight tba = (TransitBoardAlight) edge; if (stopIndex.containsKey(tba.getPattern())) assertEquals((Integer) stopIndex.get(tba.getPattern()), new Integer(tba.getStopIndex())); } }
/* * Iterate through all vertices and their (outgoing) edges. If they are of 'interesting' types, add them to the corresponding spatial index. */ public synchronized void buildSpatialIndex() { vertexIndex = new STRtree(); edgeIndex = new STRtree(); Envelope env; // int xminx, xmax, ymin, ymax; for (Vertex v : graph.getVertices()) { Coordinate c = v.getCoordinate(); env = new Envelope(c); vertexIndex.insert(env, v); for (Edge e : v.getOutgoing()) { if (e.getGeometry() == null) continue; if (e instanceof PatternEdge || e instanceof StreetTransitLink || e instanceof StreetEdge) { env = e.getGeometry().getEnvelopeInternal(); edgeIndex.insert(env, e); } } } vertexIndex.build(); edgeIndex.build(); }
/** * Internals of getRegionsForVertex; keeps track of seen vertices to avoid loops. * * @param regionData * @param vertex * @param seen * @param depth * @return */ private static HashSet<Integer> getRegionsForVertex( RegionData regionData, Vertex vertex, HashSet<Vertex> seen, int depth) { seen.add(vertex); HashSet<Integer> regions = new HashSet<Integer>(); int region = vertex.getGroupIndex(); if (region >= 0) { regions.add(region); } else { for (Edge e : vertex.getOutgoing()) { final Vertex tov = e.getToVertex(); if (!seen.contains(tov)) regions.addAll(getRegionsForVertex(regionData, tov, seen, depth + 1)); } for (Edge e : vertex.getIncoming()) { final Vertex fromv = e.getFromVertex(); if (!seen.contains(fromv)) regions.addAll(getRegionsForVertex(regionData, fromv, seen, depth + 1)); } } return regions; }
/** * Get edges connected to an vertex * * @return */ @Secured({"ROLE_USER"}) @GET @Path("/edgesForVertex") @Produces({MediaType.APPLICATION_JSON}) public EdgesForVertex getEdgesForVertex(@QueryParam("vertex") String label) { Graph graph = graphService.getGraph(); Vertex vertex = graph.getVertex(label); if (vertex == null) { return null; } EdgeSet incoming = new EdgeSet(); incoming.addEdges(vertex.getIncoming(), graph); EdgeSet outgoing = new EdgeSet(); outgoing.addEdges(vertex.getOutgoing(), graph); EdgesForVertex e4v = new EdgesForVertex(); e4v.incoming = incoming.withGraph(graph); e4v.outgoing = outgoing.withGraph(graph); return e4v; }
@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; }
@Test public final void testOnBoardDepartureTime() { Coordinate[] coordinates = new Coordinate[5]; coordinates[0] = new Coordinate(0.0, 0.0); coordinates[1] = new Coordinate(0.0, 1.0); coordinates[2] = new Coordinate(2.0, 1.0); coordinates[3] = new Coordinate(5.0, 1.0); coordinates[4] = new Coordinate(5.0, 5.0); PatternDepartVertex depart = mock(PatternDepartVertex.class); PatternArriveVertex dwell = mock(PatternArriveVertex.class); PatternArriveVertex arrive = mock(PatternArriveVertex.class); Graph graph = mock(Graph.class); RoutingRequest routingRequest = mock(RoutingRequest.class); ServiceDay serviceDay = mock(ServiceDay.class); when(graph.getTimeZone()).thenReturn(TimeZone.getTimeZone("GMT")); GeometryFactory geometryFactory = GeometryUtils.getGeometryFactory(); CoordinateSequenceFactory coordinateSequenceFactory = geometryFactory.getCoordinateSequenceFactory(); CoordinateSequence coordinateSequence = coordinateSequenceFactory.create(coordinates); LineString geometry = new LineString(coordinateSequence, geometryFactory); ArrayList<Edge> hops = new ArrayList<Edge>(2); RoutingContext routingContext = new RoutingContext(routingRequest, graph, null, arrive); AgencyAndId agencyAndId = new AgencyAndId("Agency", "ID"); Route route = new Route(); ArrayList<StopTime> stopTimes = new ArrayList<StopTime>(3); StopTime stopDepartTime = new StopTime(); StopTime stopDwellTime = new StopTime(); StopTime stopArriveTime = new StopTime(); Stop stopDepart = new Stop(); Stop stopDwell = new Stop(); Stop stopArrive = new Stop(); Trip trip = new Trip(); routingContext.serviceDays = new ArrayList<ServiceDay>(Collections.singletonList(serviceDay)); route.setId(agencyAndId); stopDepart.setId(agencyAndId); stopDwell.setId(agencyAndId); stopArrive.setId(agencyAndId); stopDepartTime.setStop(stopDepart); stopDepartTime.setDepartureTime(0); stopDwellTime.setArrivalTime(20); stopDwellTime.setStop(stopDwell); stopDwellTime.setDepartureTime(40); stopArriveTime.setArrivalTime(60); stopArriveTime.setStop(stopArrive); stopTimes.add(stopDepartTime); stopTimes.add(stopDwellTime); stopTimes.add(stopArriveTime); trip.setId(agencyAndId); trip.setTripHeadsign("The right"); TripTimes tripTimes = new TripTimes(trip, stopTimes, new Deduplicator()); StopPattern stopPattern = new StopPattern(stopTimes); TripPattern tripPattern = new TripPattern(route, stopPattern); when(depart.getTripPattern()).thenReturn(tripPattern); when(dwell.getTripPattern()).thenReturn(tripPattern); PatternHop patternHop0 = new PatternHop(depart, dwell, stopDepart, stopDwell, 0); PatternHop patternHop1 = new PatternHop(dwell, arrive, stopDwell, stopArrive, 1); hops.add(patternHop0); hops.add(patternHop1); when(graph.getEdges()).thenReturn(hops); when(depart.getCoordinate()).thenReturn(new Coordinate(0, 0)); when(dwell.getCoordinate()).thenReturn(new Coordinate(0, 0)); when(arrive.getCoordinate()).thenReturn(new Coordinate(0, 0)); when(routingRequest.getFrom()).thenReturn(new GenericLocation()); when(routingRequest.getStartingTransitTripId()).thenReturn(agencyAndId); when(serviceDay.secondsSinceMidnight(anyInt())).thenReturn(9); patternHop0.setGeometry(geometry); tripPattern.add(tripTimes); graph.index = new GraphIndex(graph); coordinates = new Coordinate[3]; coordinates[0] = new Coordinate(3.5, 1.0); coordinates[1] = new Coordinate(5.0, 1.0); coordinates[2] = new Coordinate(5.0, 5.0); coordinateSequence = coordinateSequenceFactory.create(coordinates); geometry = new LineString(coordinateSequence, geometryFactory); Vertex vertex = onBoardDepartServiceImpl.setupDepartOnBoard(routingContext); Edge edge = vertex.getOutgoing().toArray(new Edge[1])[0]; assertEquals(vertex, edge.getFromVertex()); assertEquals(dwell, edge.getToVertex()); assertEquals("The right", edge.getDirection()); assertEquals(geometry, edge.getGeometry()); assertEquals(coordinates[0].x, vertex.getX(), 0.0); assertEquals(coordinates[0].y, vertex.getY(), 0.0); }
/** * Get polygons covering the components of the graph. The largest component (in terms of number of * nodes) will not overlap any other components (it will have holes); the others may overlap each * other. * * @param dateTime */ public static List<Geometry> getComponentPolygons( Graph graph, RoutingRequest options, long time) { DisjointSet<Vertex> components = getConnectedComponents(graph); LinkedListMultimap<Integer, Coordinate> componentCoordinates = LinkedListMultimap.create(); options.setDummyRoutingContext(graph); for (Vertex v : graph.getVertices()) { for (Edge e : v.getOutgoing()) { State s0 = new State(v, time, options); State s1 = e.traverse(s0); if (s1 != null) { Integer component = components.find(e.getFromVertex()); Geometry geometry = s1.getBackEdge().getGeometry(); if (geometry != null) { List<Coordinate> coordinates = new ArrayList<Coordinate>(Arrays.asList(geometry.getCoordinates())); for (int i = 0; i < coordinates.size(); ++i) { Coordinate coordinate = new Coordinate(coordinates.get(i)); coordinate.x = Math.round(coordinate.x * PRECISION) / PRECISION; coordinate.y = Math.round(coordinate.y * PRECISION) / PRECISION; coordinates.set(i, coordinate); } componentCoordinates.putAll(component, coordinates); } } } } // generate convex hull of each component List<Geometry> geoms = new ArrayList<Geometry>(); int mainComponentSize = 0; int mainComponentIndex = -1; int component = 0; for (Integer key : componentCoordinates.keySet()) { List<Coordinate> coords = componentCoordinates.get(key); Coordinate[] coordArray = new Coordinate[coords.size()]; ConvexHull hull = new ConvexHull(coords.toArray(coordArray), GeometryUtils.getGeometryFactory()); Geometry geom = hull.getConvexHull(); // buffer components which are mere lines so that they do not disappear. if (geom instanceof LineString) { geom = geom.buffer(0.01); // ~10 meters } else if (geom instanceof Point) { geom = geom.buffer(0.05); // ~50 meters, so that it shows up } geoms.add(geom); if (mainComponentSize < coordArray.length) { mainComponentIndex = component; mainComponentSize = coordArray.length; } ++component; } // subtract small components out of main component // (small components are permitted to overlap each other) Geometry mainComponent = geoms.get(mainComponentIndex); for (int i = 0; i < geoms.size(); ++i) { Geometry geom = geoms.get(i); if (i == mainComponentIndex) { continue; } mainComponent = mainComponent.difference(geom); } geoms.set(mainComponentIndex, mainComponent); return geoms; }
@Override public List<GraphPath> getPaths(RoutingRequest options) { if (options.rctx == null) { options.setRoutingContext(graphService.getGraph(options.getRouterId())); // move into setRoutingContext ? options.rctx.pathParsers = new PathParser[] {new BasicPathParser(), new NoThruTrafficPathParser()}; } RemainingWeightHeuristic heuristic; if (options.getModes().isTransit()) { LOG.debug("Transit itinerary requested."); // always use the bidirectional heuristic because the others are not precise enough heuristic = new BidirectionalRemainingWeightHeuristic(options.rctx.graph); } else { LOG.debug("Non-transit itinerary requested."); heuristic = new DefaultRemainingWeightHeuristic(); } // the states that will eventually be turned into paths and returned List<State> returnStates = new LinkedList<State>(); BinHeap<State> pq = new BinHeap<State>(); // List<State> boundingStates = new ArrayList<State>(); Vertex originVertex = options.rctx.origin; Vertex targetVertex = options.rctx.target; // increase maxWalk repeatedly in case hard limiting is in use WALK: for (double maxWalk = options.getMaxWalkDistance(); returnStates.isEmpty(); maxWalk *= 2) { if (maxWalk != Double.MAX_VALUE && maxWalk > MAX_WALK) { break; } LOG.debug("try search with max walk {}", maxWalk); // increase maxWalk if settings make trip impossible if (maxWalk < Math.min( distanceLibrary.distance(originVertex.getCoordinate(), targetVertex.getCoordinate()), originVertex.getDistanceToNearestTransitStop() + targetVertex.getDistanceToNearestTransitStop())) continue WALK; options.setMaxWalkDistance(maxWalk); // cap search / heuristic weight final double AVG_TRANSIT_SPEED = 25; // m/sec double cutoff = (distanceLibrary.distance(originVertex.getCoordinate(), targetVertex.getCoordinate()) * 1.5) / AVG_TRANSIT_SPEED; // wait time is irrelevant in the heuristic cutoff += options.getMaxWalkDistance() * options.walkReluctance; options.maxWeight = cutoff; State origin = new State(options); // (used to) initialize heuristic outside loop so table can be reused heuristic.computeInitialWeight(origin, targetVertex); options.maxWeight = cutoff + 30 * 60 * options.waitReluctance; // reinitialize states for each retry HashMap<Vertex, List<State>> states = new HashMap<Vertex, List<State>>(); pq.reset(); pq.insert(origin, 0); long startTime = System.currentTimeMillis(); long endTime = startTime + (int) (_timeouts[0] * 1000); LOG.debug("starttime {} endtime {}", startTime, endTime); QUEUE: while (!pq.empty()) { if (System.currentTimeMillis() > endTime) { LOG.debug("timeout at {} msec", System.currentTimeMillis() - startTime); if (returnStates.isEmpty()) break WALK; // disable walk distance increases else { storeMemory(); break WALK; } } // if (pq.peek_min_key() > options.maxWeight) { // LOG.debug("max weight {} exceeded", options.maxWeight); // break QUEUE; // } State su = pq.extract_min(); // for (State bs : boundingStates) { // if (eDominates(bs, su)) { // continue QUEUE; // } // } Vertex u = su.getVertex(); if (traverseVisitor != null) { traverseVisitor.visitVertex(su); } if (u.equals(targetVertex)) { // boundingStates.add(su); returnStates.add(su); if (!options.getModes().isTransit()) break QUEUE; // options should contain max itineraries if (returnStates.size() >= _maxPaths) break QUEUE; if (returnStates.size() < _timeouts.length) { endTime = startTime + (int) (_timeouts[returnStates.size()] * 1000); LOG.debug( "{} path, set timeout to {}", returnStates.size(), _timeouts[returnStates.size()] * 1000); } continue QUEUE; } for (Edge e : options.isArriveBy() ? u.getIncoming() : u.getOutgoing()) { STATE: for (State new_sv = e.traverse(su); new_sv != null; new_sv = new_sv.getNextResult()) { if (traverseVisitor != null) { traverseVisitor.visitEdge(e, new_sv); } double h = heuristic.computeForwardWeight(new_sv, targetVertex); if (h == Double.MAX_VALUE) continue; // for (State bs : boundingStates) { // if (eDominates(bs, new_sv)) { // continue STATE; // } // } Vertex v = new_sv.getVertex(); List<State> old_states = states.get(v); if (old_states == null) { old_states = new LinkedList<State>(); states.put(v, old_states); } else { for (State old_sv : old_states) { if (eDominates(old_sv, new_sv)) { continue STATE; } } Iterator<State> iter = old_states.iterator(); while (iter.hasNext()) { State old_sv = iter.next(); if (eDominates(new_sv, old_sv)) { iter.remove(); } } } if (traverseVisitor != null) traverseVisitor.visitEnqueue(new_sv); old_states.add(new_sv); pq.insert(new_sv, new_sv.getWeight() + h); } } } } storeMemory(); // Make the states into paths and return them List<GraphPath> paths = new LinkedList<GraphPath>(); for (State s : returnStates) { LOG.debug(s.toStringVerbose()); paths.add(new GraphPath(s, true)); } // sort by arrival time, though paths are already in order of increasing difficulty // Collections.sort(paths, new PathComparator(origin.getOptions().isArriveBy())); return paths; }