/** Two paths are equal if they use the same ordered list of trips */ public boolean equals(Object o) { if (o instanceof GraphPath) { GraphPath go = (GraphPath) o; return go.getTrips().equals(getTrips()); } return false; }
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 void testTransfers() throws Exception { TransferTable transferTable = graph.getTransferTable(); // create dummy routes and trips Route fromRoute = new Route(); fromRoute.setId(new AgencyAndId("agency", "1")); Trip fromTrip = new Trip(); fromTrip.setId(new AgencyAndId("agency", "1.1")); fromTrip.setRoute(fromRoute); Route toRoute = new Route(); toRoute.setId(new AgencyAndId("agency", "2")); Trip toTrip = new Trip(); toTrip.setId(new AgencyAndId("agency", "2.1")); toTrip.setRoute(toRoute); Trip toTrip2 = new Trip(); toTrip2.setId(new AgencyAndId("agency", "2.2")); toTrip2.setRoute(toRoute); // find stops Stop stopK = ((TransitStopArrive) graph.getVertex("agency_K_arrive")).getStop(); Stop stopN = ((TransitStopDepart) graph.getVertex("agency_N_depart")).getStop(); Stop stopM = ((TransitStopDepart) graph.getVertex("agency_M_depart")).getStop(); assertTrue(transferTable.hasPreferredTransfers()); assertEquals( StopTransfer.UNKNOWN_TRANSFER, transferTable.getTransferTime(stopN, stopM, fromTrip, toTrip)); assertEquals( StopTransfer.FORBIDDEN_TRANSFER, transferTable.getTransferTime(stopK, stopM, fromTrip, toTrip)); assertEquals( StopTransfer.PREFERRED_TRANSFER, transferTable.getTransferTime(stopN, stopK, toTrip, toTrip2)); assertEquals( StopTransfer.TIMED_TRANSFER, transferTable.getTransferTime(stopN, stopK, fromTrip, toTrip)); assertEquals(15, transferTable.getTransferTime(stopN, stopK, fromTrip, toTrip2)); Vertex e_arrive = graph.getVertex("agency_E_arrive"); Vertex f_depart = graph.getVertex("agency_F_depart"); Edge edge = new TransferEdge(e_arrive, f_depart, 10000, 10000); long startTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 18, 0, 50, 0); Vertex stop_b = graph.getVertex("agency_B_depart"); Vertex stop_g = graph.getVertex("agency_G_arrive"); RoutingRequest options = new RoutingRequest(); options.dateTime = startTime; options.setRoutingContext(graph, stop_b, stop_g); ShortestPathTree spt = aStar.getShortestPathTree(options); GraphPath path = spt.getPath(stop_g, false); assertNotNull(path); assertTrue( "expected to use much later trip due to min transfer time", path.getEndTime() - startTime > 4.5 * 60 * 60); /* cleanup */ e_arrive.removeOutgoing(edge); f_depart.removeIncoming(edge); }
public void testFewestTransfers() { Vertex stop_c = graph.getVertex("agency:C"); Vertex stop_d = graph.getVertex("agency:D"); RoutingRequest options = new RoutingRequest(); options.optimize = OptimizeType.QUICK; options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 1, 16, 0, 0); options.setRoutingContext(graph, stop_c, stop_d); ShortestPathTree spt = aStar.getShortestPathTree(options); // when optimizing for speed, take the fast two-bus path GraphPath path = spt.getPath(stop_d, false); assertNotNull(path); assertEquals( TestUtils.dateInSeconds("America/New_York", 2009, 8, 1, 16, 20, 0), path.getEndTime()); // when optimizing for fewest transfers, take the slow one-bus path options.transferPenalty = 1800; spt = aStar.getShortestPathTree(options); path = spt.getPath(stop_d, false); assertNotNull(path); assertEquals( TestUtils.dateInSeconds("America/New_York", 2009, 8, 1, 16, 50, 0), path.getEndTime()); }
public void testRoutingOverMidnight() throws Exception { // this route only runs on weekdays Vertex stop_g = graph.getVertex("agency:G_depart"); Vertex stop_h = graph.getVertex("agency:H_arrive"); ShortestPathTree spt; GraphPath path; RoutingRequest options = new RoutingRequest(); // Friday evening options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 18, 23, 20, 0); options.setRoutingContext(graph, stop_g, stop_h); spt = aStar.getShortestPathTree(options); path = spt.getPath(stop_h, false); assertNotNull(path); assertEquals(4, path.states.size()); // Saturday morning long startTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 19, 0, 5, 0); options.dateTime = startTime; options.setRoutingContext(graph, stop_g.getLabel(), stop_h.getLabel()); spt = aStar.getShortestPathTree(options); path = spt.getPath(stop_h, false); assertNotNull(path); assertEquals(4, path.states.size()); long endTime = path.getEndTime(); assertTrue(endTime < startTime + 60 * 60); }
/** Generates a TripPlan from a set of paths */ public TripPlan generatePlan(List<GraphPath> paths, RoutingRequest request) { GraphPath exemplar = paths.get(0); Vertex tripStartVertex = exemplar.getStartVertex(); Vertex tripEndVertex = exemplar.getEndVertex(); String startName = tripStartVertex.getName(); String endName = tripEndVertex.getName(); // Use vertex labels if they don't have names if (startName == null) { startName = tripStartVertex.getLabel(); } if (endName == null) { endName = tripEndVertex.getLabel(); } Place from = new Place(tripStartVertex.getX(), tripStartVertex.getY(), startName); Place to = new Place(tripEndVertex.getX(), tripEndVertex.getY(), endName); TripPlan plan = new TripPlan(from, to, request.getDateTime()); for (GraphPath path : paths) { Itinerary itinerary = generateItinerary(path, request.getShowIntermediateStops()); plan.addItinerary(itinerary); } return plan; }
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); }
public void testRouting() throws Exception { Vertex stop_a = graph.getVertex("agency:A"); Vertex stop_b = graph.getVertex("agency:B"); Vertex stop_c = graph.getVertex("agency:C"); Vertex stop_d = graph.getVertex("agency:D"); Vertex stop_e = graph.getVertex("agency:E"); RoutingRequest options = new RoutingRequest(); // test feed is designed for instantaneous transfers options.transferSlack = (0); long startTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 0, 0, 0); options.dateTime = startTime; ShortestPathTree spt; GraphPath path; // A to B options.setRoutingContext(graph, stop_a, stop_b); spt = aStar.getShortestPathTree(options); path = spt.getPath(stop_b, false); assertNotNull(path); assertEquals(6, path.states.size()); // A to C options.setRoutingContext(graph, stop_a, stop_c); spt = aStar.getShortestPathTree(options); path = spt.getPath(stop_c, false); assertNotNull(path); assertEquals(8, path.states.size()); // A to D (change at C) options.setRoutingContext(graph, stop_a, stop_d); spt = aStar.getShortestPathTree(options); path = spt.getPath(stop_d, false); assertNotNull(path); // there are two paths of different lengths // both arrive at 40 minutes after midnight List<TransitStop> stops = extractStopVertices(path); assertEquals(stops.size(), 3); assertEquals(stops.get(1), stop_c); long endTime = startTime + 40 * 60; assertEquals(endTime, path.getEndTime()); // A to E (change at C) options.setRoutingContext(graph, stop_a, stop_e); spt = aStar.getShortestPathTree(options); path = spt.getPath(stop_e, false); assertNotNull(path); stops = extractStopVertices(path); assertEquals(stops.size(), 3); assertEquals(stops.get(1), stop_c); endTime = startTime + 70 * 60; assertEquals(endTime, path.getEndTime()); }
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()); }
public void testTimelessStops() throws Exception { Vertex stop_d = graph.getVertex("agency:D"); Vertex stop_c = graph.getVertex("agency:C"); RoutingRequest options = new RoutingRequest(); options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 1, 10, 0, 0); options.setRoutingContext(graph, stop_d, stop_c); ShortestPathTree spt = aStar.getShortestPathTree(options); GraphPath path = spt.getPath(stop_c, false); assertNotNull(path); assertEquals( TestUtils.dateInSeconds("America/New_York", 2009, 8, 1, 11, 0, 0), path.getEndTime()); }
public void testRouting() throws Exception { Vertex stop_a = graph.getVertex("agency_A"); Vertex stop_b = graph.getVertex("agency_B"); Vertex stop_c = graph.getVertex("agency_C"); Vertex stop_d = graph.getVertex("agency_D"); Vertex stop_e = graph.getVertex("agency_E"); RoutingRequest options = new RoutingRequest(); options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 0, 0, 0); ShortestPathTree spt; GraphPath path; // A to B options.setRoutingContext(graph, stop_a, stop_b); spt = aStar.getShortestPathTree(options); path = spt.getPath(stop_b, false); assertNotNull(path); assertEquals(extractStopVertices(path), Lists.newArrayList(stop_a, stop_b)); // A to C options.setRoutingContext(graph, stop_a, stop_c); spt = aStar.getShortestPathTree(options); path = spt.getPath(stop_c, false); assertNotNull(path); assertEquals(extractStopVertices(path), Lists.newArrayList(stop_a, stop_c)); // A to D options.setRoutingContext(graph, stop_a, stop_d); spt = aStar.getShortestPathTree(options); path = spt.getPath(stop_d, false); assertNotNull(path); assertEquals(extractStopVertices(path), Lists.newArrayList(stop_a, stop_c, stop_d)); long endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 0, 0, 0) + 40 * 60; assertEquals(endTime, path.getEndTime()); // A to E options.setRoutingContext(graph, stop_a, stop_e); spt = aStar.getShortestPathTree(options); path = spt.getPath(stop_e, false); assertNotNull(path); assertEquals(extractStopVertices(path), Lists.newArrayList(stop_a, stop_c, stop_e)); endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 0, 0, 0) + 70 * 60; assertEquals(endTime, path.getEndTime()); }
public void testDwell() throws Exception { Vertex stop_a = graph.getVertex("agency_A_depart"); Vertex stop_c = graph.getVertex("agency_C_arrive"); RoutingRequest options = new RoutingRequest(); options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 8, 0, 0); options.setRoutingContext(graph, stop_a, stop_c); ShortestPathTree spt = aStar.getShortestPathTree(options); GraphPath path = spt.getPath(stop_c, false); assertNotNull(path); assertEquals(6, path.states.size()); long endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 8, 30, 0); assertEquals(endTime, path.getEndTime()); }
public void testPathways() throws Exception { Vertex entrance = graph.getVertex("agency:entrance_a"); assertNotNull(entrance); Vertex stop = graph.getVertex("agency:A"); assertNotNull(stop); RoutingRequest options = new RoutingRequest(); options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 1, 16, 0, 0); options.setRoutingContext(graph, entrance, stop); ShortestPathTree spt = aStar.getShortestPathTree(options); GraphPath path = spt.getPath(stop, false); assertNotNull(path); assertEquals( TestUtils.dateInSeconds("America/New_York", 2009, 8, 1, 16, 0, 34), path.getEndTime()); }
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()); // TODO more detailed testing of frequencies }
public void testRunForTrain() { /** * This is the notorious Byrd bug: we're going from Q to T at 8:30. There's a trip from S to T * at 8:50 and a second one at 9:50. To get to S by 8:50, we need to take trip 12.1 from Q to R, * and 13.1 from R to S. If we take the direct-but-slower 11.1, we'll miss the 8:50 and have to * catch the 9:50. */ Vertex destination = graph.getVertex("agency:T"); RoutingRequest options = new RoutingRequest(); // test is designed such that transfers must be instantaneous options.transferSlack = (0); GregorianCalendar startTime = new GregorianCalendar(2009, 11, 2, 8, 30, 0); startTime.setTimeZone(TimeZone.getTimeZone("America/New_York")); options.dateTime = TestUtils.toSeconds(startTime); options.setRoutingContext(graph, "agency:Q", destination.getLabel()); ShortestPathTree spt = aStar.getShortestPathTree(options); GraphPath path = spt.getPath(destination, false); long endTime = path.getEndTime(); Calendar c = new GregorianCalendar(); c.setTimeInMillis(endTime * 1000L); assertTrue(endTime - TestUtils.toSeconds(startTime) < 7200); }
/** * 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; }
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")); } }
/** * 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; }
@Test public void testUpdate() { TripUpdate tripUpdate; TripUpdate.Builder tripUpdateBuilder; TripDescriptor.Builder tripDescriptorBuilder; StopTimeUpdate.Builder stopTimeUpdateBuilder; StopTimeEvent.Builder stopTimeEventBuilder; int trip_1_1_index = timetable.getTripIndex(new AgencyAndId("agency", "1.1")); Vertex stop_a = graph.getVertex("agency_A"); Vertex stop_c = graph.getVertex("agency_C"); RoutingRequest options = new RoutingRequest(); ShortestPathTree spt; GraphPath path; // non-existing trip tripDescriptorBuilder = TripDescriptor.newBuilder(); tripDescriptorBuilder.setTripId("b"); tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.CANCELED); tripUpdateBuilder = TripUpdate.newBuilder(); tripUpdateBuilder.setTrip(tripDescriptorBuilder); tripUpdate = tripUpdateBuilder.build(); assertFalse(timetable.update(tripUpdate, "a", timeZone, serviceDate)); // update trip with bad data tripDescriptorBuilder = TripDescriptor.newBuilder(); tripDescriptorBuilder.setTripId("1.1"); tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED); tripUpdateBuilder = TripUpdate.newBuilder(); tripUpdateBuilder.setTrip(tripDescriptorBuilder); stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0); stopTimeUpdateBuilder.setStopSequence(0); stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SKIPPED); tripUpdate = tripUpdateBuilder.build(); assertFalse(timetable.update(tripUpdate, "agency", timeZone, serviceDate)); // update trip with non-increasing data tripDescriptorBuilder = TripDescriptor.newBuilder(); tripDescriptorBuilder.setTripId("1.1"); tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED); tripUpdateBuilder = TripUpdate.newBuilder(); tripUpdateBuilder.setTrip(tripDescriptorBuilder); stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0); stopTimeUpdateBuilder.setStopSequence(2); stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED); stopTimeEventBuilder = stopTimeUpdateBuilder.getArrivalBuilder(); stopTimeEventBuilder.setTime( TestUtils.dateInSeconds("America/New_York", 2009, AUGUST, 7, 0, 10, 1)); stopTimeEventBuilder = stopTimeUpdateBuilder.getDepartureBuilder(); stopTimeEventBuilder.setTime( TestUtils.dateInSeconds("America/New_York", 2009, AUGUST, 7, 0, 10, 0)); tripUpdate = tripUpdateBuilder.build(); assertFalse(timetable.update(tripUpdate, "agency", timeZone, serviceDate)); // --- long startTime = TestUtils.dateInSeconds("America/New_York", 2009, AUGUST, 7, 0, 0, 0); long endTime; options.dateTime = startTime; // --- options.setRoutingContext(graph, stop_a, stop_c); spt = aStar.getShortestPathTree(options); path = spt.getPath(stop_c, false); assertNotNull(path); endTime = startTime + 20 * 60; assertEquals(endTime, path.getEndTime()); // update trip tripDescriptorBuilder = TripDescriptor.newBuilder(); tripDescriptorBuilder.setTripId("1.1"); tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED); tripUpdateBuilder = TripUpdate.newBuilder(); tripUpdateBuilder.setTrip(tripDescriptorBuilder); stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0); stopTimeUpdateBuilder.setStopSequence(1); stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED); stopTimeEventBuilder = stopTimeUpdateBuilder.getArrivalBuilder(); stopTimeEventBuilder.setTime( TestUtils.dateInSeconds("America/New_York", 2009, AUGUST, 7, 0, 2, 0)); stopTimeEventBuilder = stopTimeUpdateBuilder.getDepartureBuilder(); stopTimeEventBuilder.setTime( TestUtils.dateInSeconds("America/New_York", 2009, AUGUST, 7, 0, 2, 0)); tripUpdate = tripUpdateBuilder.build(); assertEquals(20 * 60, timetable.getTripTimes(trip_1_1_index).getArrivalTime(2)); assertTrue(timetable.update(tripUpdate, "agency", timeZone, serviceDate)); assertEquals(20 * 60 + 120, timetable.getTripTimes(trip_1_1_index).getArrivalTime(2)); // --- options.setRoutingContext(graph, stop_a, stop_c); spt = aStar.getShortestPathTree(options); path = spt.getPath(stop_c, false); assertNotNull(path); endTime = startTime + 20 * 60 + 120; assertEquals(endTime, path.getEndTime()); // cancel trip tripDescriptorBuilder = TripDescriptor.newBuilder(); tripDescriptorBuilder.setTripId("1.1"); tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.CANCELED); tripUpdateBuilder = TripUpdate.newBuilder(); tripUpdateBuilder.setTrip(tripDescriptorBuilder); tripUpdate = tripUpdateBuilder.build(); assertTrue(timetable.update(tripUpdate, "agency", timeZone, serviceDate)); TripTimes tripTimes = timetable.getTripTimes(trip_1_1_index); for (int i = 0; i < tripTimes.getNumStops(); i++) { assertEquals(TripTimes.UNAVAILABLE, tripTimes.getDepartureTime(i)); assertEquals(TripTimes.UNAVAILABLE, tripTimes.getArrivalTime(i)); } // --- options.setRoutingContext(graph, stop_a, stop_c); spt = aStar.getShortestPathTree(options); path = spt.getPath(stop_c, false); assertNotNull(path); endTime = startTime + 40 * 60; assertEquals(endTime, path.getEndTime()); // update trip arrival time incorrectly tripDescriptorBuilder = TripDescriptor.newBuilder(); tripDescriptorBuilder.setTripId("1.1"); tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED); tripUpdateBuilder = TripUpdate.newBuilder(); tripUpdateBuilder.setTrip(tripDescriptorBuilder); stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0); stopTimeUpdateBuilder.setStopSequence(1); stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED); stopTimeEventBuilder = stopTimeUpdateBuilder.getArrivalBuilder(); stopTimeEventBuilder.setDelay(0); tripUpdate = tripUpdateBuilder.build(); assertTrue(timetable.update(tripUpdate, "agency", timeZone, serviceDate)); // update trip arrival time only tripDescriptorBuilder = TripDescriptor.newBuilder(); tripDescriptorBuilder.setTripId("1.1"); tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED); tripUpdateBuilder = TripUpdate.newBuilder(); tripUpdateBuilder.setTrip(tripDescriptorBuilder); stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0); stopTimeUpdateBuilder.setStopSequence(2); stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED); stopTimeEventBuilder = stopTimeUpdateBuilder.getArrivalBuilder(); stopTimeEventBuilder.setDelay(1); tripUpdate = tripUpdateBuilder.build(); assertTrue(timetable.update(tripUpdate, "agency", timeZone, serviceDate)); // update trip departure time only tripDescriptorBuilder = TripDescriptor.newBuilder(); tripDescriptorBuilder.setTripId("1.1"); tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED); tripUpdateBuilder = TripUpdate.newBuilder(); tripUpdateBuilder.setTrip(tripDescriptorBuilder); stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0); stopTimeUpdateBuilder.setStopSequence(2); stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED); stopTimeEventBuilder = stopTimeUpdateBuilder.getDepartureBuilder(); stopTimeEventBuilder.setDelay(-1); tripUpdate = tripUpdateBuilder.build(); assertTrue(timetable.update(tripUpdate, "agency", timeZone, serviceDate)); // update trip using stop id tripDescriptorBuilder = TripDescriptor.newBuilder(); tripDescriptorBuilder.setTripId("1.1"); tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED); tripUpdateBuilder = TripUpdate.newBuilder(); tripUpdateBuilder.setTrip(tripDescriptorBuilder); stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0); stopTimeUpdateBuilder.setStopId("B"); stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED); stopTimeEventBuilder = stopTimeUpdateBuilder.getDepartureBuilder(); stopTimeEventBuilder.setDelay(-1); tripUpdate = tripUpdateBuilder.build(); assertTrue(timetable.update(tripUpdate, "agency", timeZone, serviceDate)); // update trip arrival time at first stop and make departure time incoherent at second stop tripDescriptorBuilder = TripDescriptor.newBuilder(); tripDescriptorBuilder.setTripId("1.1"); tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED); tripUpdateBuilder = TripUpdate.newBuilder(); tripUpdateBuilder.setTrip(tripDescriptorBuilder); stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0); stopTimeUpdateBuilder.setStopSequence(1); stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED); stopTimeEventBuilder = stopTimeUpdateBuilder.getArrivalBuilder(); stopTimeEventBuilder.setDelay(0); stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(1); stopTimeUpdateBuilder.setStopSequence(2); stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED); stopTimeEventBuilder = stopTimeUpdateBuilder.getDepartureBuilder(); stopTimeEventBuilder.setDelay(-1); tripUpdate = tripUpdateBuilder.build(); assertFalse(timetable.update(tripUpdate, "agency", timeZone, serviceDate)); }
@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; }