/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* this is in api.common so it can set package-private fields */ package org.opentripplanner.api.ws; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import static org.mockito.Mockito.reset; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; import junit.framework.TestCase; import org.codehaus.jettison.json.JSONException; import org.onebusaway.gtfs.model.Agency; import org.onebusaway.gtfs.model.AgencyAndId; import org.onebusaway.gtfs.model.Route; import org.onebusaway.gtfs.model.Stop; import org.onebusaway.gtfs.model.Trip; import org.opentripplanner.api.common.ParameterException; import org.opentripplanner.api.model.AbsoluteDirection; import org.opentripplanner.api.model.Itinerary; import org.opentripplanner.api.model.Leg; import org.opentripplanner.api.model.RelativeDirection; import org.opentripplanner.api.model.RouterInfo; import org.opentripplanner.api.model.RouterList; import org.opentripplanner.api.model.WalkStep; import org.opentripplanner.api.model.internals.EdgeSet; import org.opentripplanner.api.model.internals.FeatureCount; import org.opentripplanner.api.model.internals.VertexSet; import org.opentripplanner.api.model.patch.PatchResponse; import org.opentripplanner.api.model.transit.AgencyList; import org.opentripplanner.api.model.transit.ModeList; import org.opentripplanner.api.model.transit.RouteData; import org.opentripplanner.api.model.transit.RouteDataList; import org.opentripplanner.api.model.transit.RouteList; import org.opentripplanner.api.model.transit.StopList; import org.opentripplanner.api.model.transit.StopTimeList; import org.opentripplanner.api.ws.internals.Components; import org.opentripplanner.api.ws.internals.GraphInternals; import org.opentripplanner.api.ws.services.MetadataService; import org.opentripplanner.common.geometry.GeometryUtils; import org.opentripplanner.common.geometry.SphericalDistanceLibrary; import org.opentripplanner.graph_builder.impl.GtfsGraphBuilderImpl; import org.opentripplanner.graph_builder.impl.TransitToStreetNetworkGraphBuilderImpl; import org.opentripplanner.graph_builder.impl.shapefile.AttributeFeatureConverter; import org.opentripplanner.graph_builder.impl.shapefile.CaseBasedTraversalPermissionConverter; import org.opentripplanner.graph_builder.impl.shapefile.ShapefileFeatureSourceFactoryImpl; import org.opentripplanner.graph_builder.impl.shapefile.ShapefileStreetGraphBuilderImpl; import org.opentripplanner.graph_builder.impl.shapefile.ShapefileStreetSchema; import org.opentripplanner.graph_builder.impl.transit_index.TransitIndexBuilder; import org.opentripplanner.graph_builder.model.GtfsBundle; import org.opentripplanner.graph_builder.model.GtfsBundles; import org.opentripplanner.graph_builder.services.GraphBuilderWithGtfsDao; import org.opentripplanner.graph_builder.services.shapefile.FeatureSourceFactory; import org.opentripplanner.model.json_serialization.WithGraph; import org.opentripplanner.routing.algorithm.GenericAStar; import org.opentripplanner.routing.bike_rental.BikeRentalStation; import org.opentripplanner.routing.bike_rental.BikeRentalStationService; import org.opentripplanner.routing.core.OptimizeType; import org.opentripplanner.routing.core.RoutingRequest; import org.opentripplanner.routing.core.State; import org.opentripplanner.routing.core.StopMatcher; import org.opentripplanner.routing.core.StopTransfer; import org.opentripplanner.routing.core.TransferTable; import org.opentripplanner.routing.core.TraverseMode; import org.opentripplanner.routing.core.TraverseModeSet; import org.opentripplanner.routing.edgetype.PlainStreetEdge; import org.opentripplanner.routing.edgetype.StreetTraversalPermission; import org.opentripplanner.routing.edgetype.TableTripPattern; import org.opentripplanner.routing.edgetype.TimedTransferEdge; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.graph.Graph.LoadLevel; import org.opentripplanner.routing.graph.Vertex; import org.opentripplanner.routing.impl.RetryingPathServiceImpl; import org.opentripplanner.routing.impl.StreetVertexIndexServiceImpl; import org.opentripplanner.routing.impl.TravelingSalesmanPathService; import org.opentripplanner.routing.patch.Patch; import org.opentripplanner.routing.services.GraphService; import org.opentripplanner.routing.services.PatchService; import org.opentripplanner.routing.spt.GraphPath; import org.opentripplanner.routing.trippattern.Update; import org.opentripplanner.routing.trippattern.Update.Status; import org.opentripplanner.routing.trippattern.UpdateBlock; import org.opentripplanner.routing.vertextype.IntersectionVertex; import org.opentripplanner.routing.vertextype.PatternStopVertex; import org.opentripplanner.routing.vertextype.StreetVertex; import org.opentripplanner.routing.vertextype.TransitStop; import org.opentripplanner.util.TestUtils; import com.vividsolutions.jts.geom.LineString; class SimpleGraphServiceImpl implements GraphService { private HashMap<String, Graph> graphs = new HashMap<String, Graph>(); @Override public void setLoadLevel(LoadLevel level) { } @Override public Graph getGraph() { return graphs.get(null); } @Override public Graph getGraph(String routerId) { return graphs.get(routerId); } @Override public Collection<String> getRouterIds() { return graphs.keySet(); } public void putGraph(String graphId, Graph graph) { graphs.put(graphId, graph); } @Override public boolean registerGraph(String graphId, boolean preEvict) { return false; } @Override public boolean registerGraph(String graphId, Graph graph) { return false; } @Override public boolean evictGraph(String graphId) { return false; } @Override public int evictAll() { return 0; } @Override public boolean reloadGraphs(boolean preEvict) { throw new UnsupportedOperationException(); } } /* This is a hack to hold context and graph data between test runs, since loading it is slow. */ class Context { /** * Save a temporary graph object when this is true */ private static final boolean DEBUG_OUTPUT = false; public Graph graph = spy(new Graph()); public SimpleGraphServiceImpl graphService = new SimpleGraphServiceImpl(); public PlanGenerator planGenerator = new PlanGenerator(); public RetryingPathServiceImpl pathService = new RetryingPathServiceImpl(); private static Context instance = null; public static Context getInstance() { if (instance == null) { instance = new Context(); } return instance; } public Context() { graphService.putGraph(null, makeSimpleGraph()); // default graph is tiny test graph graphService.putGraph("portland", graph); ShapefileStreetGraphBuilderImpl builder = new ShapefileStreetGraphBuilderImpl(); FeatureSourceFactory factory = new ShapefileFeatureSourceFactoryImpl(new File( "src/test/resources/portland/Streets_pdx.shp")); builder.setFeatureSourceFactory(factory); ShapefileStreetSchema schema = new ShapefileStreetSchema(); schema.setIdAttribute("LOCALID"); schema.setNameAttribute("FULL_NAME"); CaseBasedTraversalPermissionConverter perms = new CaseBasedTraversalPermissionConverter( "DIRECTION", StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE); perms.addPermission("2", StreetTraversalPermission.ALL, StreetTraversalPermission.PEDESTRIAN); perms.addPermission("3", StreetTraversalPermission.PEDESTRIAN, StreetTraversalPermission.ALL); perms.addPermission("1", StreetTraversalPermission.ALL, StreetTraversalPermission.ALL); schema.setPermissionConverter(perms); // as a test, use prefixes ("NE", SE", etc) as an alert schema.setNoteConverter(new AttributeFeatureConverter<String>("PREFIX")); builder.setSchema(schema); builder.buildGraph(graph, new HashMap<Class<?>, Object>()); initTransit(); initBikeRental(); graph.streetIndex = new StreetVertexIndexServiceImpl(graph); if (DEBUG_OUTPUT) { try { graph.save(File.createTempFile("graph", ".obj")); } catch (IOException e) { throw new RuntimeException(e); } } pathService.setSptService(new GenericAStar()); pathService.setGraphService(graphService); planGenerator.pathService = pathService; } private void initTransit() { GtfsGraphBuilderImpl gtfsBuilder = new GtfsGraphBuilderImpl(); GtfsBundle bundle = new GtfsBundle(); bundle.setPath(new File("../opentripplanner-routing/src/test/resources/google_transit.zip")); ArrayList<GtfsBundle> bundleList = new ArrayList<GtfsBundle>(); bundleList.add(bundle); GtfsBundles bundles = new GtfsBundles(); bundles.setBundles(bundleList); gtfsBuilder.setGtfsBundles(bundles); gtfsBuilder.setGtfsGraphBuilders(Arrays .asList((GraphBuilderWithGtfsDao) new TransitIndexBuilder())); HashMap<Class<?>, Object> extra = new HashMap<Class<?>, Object>(); gtfsBuilder.buildGraph(graph, extra); TransitToStreetNetworkGraphBuilderImpl linker = new TransitToStreetNetworkGraphBuilderImpl(); linker.buildGraph(graph, extra); } private void initBikeRental() { BikeRentalStationService service = new BikeRentalStationService(); BikeRentalStation station = new BikeRentalStation(); station.x = -122.637634; station.y = 45.513084; station.bikesAvailable = 5; station.spacesAvailable = 4; station.id = "1"; station.name = "bike rental station"; service.addStation(station); graph.putService(BikeRentalStationService.class, service); } private Graph makeSimpleGraph() { Graph graph = new Graph(); StreetVertex tl = new IntersectionVertex(graph, "tl", -80.01, 40.01, "top and left"); StreetVertex tr = new IntersectionVertex(graph, "tr", -80.0, 40.01, "top and right"); StreetVertex bl = new IntersectionVertex(graph, "bl", -80.01, 40.0, "bottom and left"); StreetVertex br = new IntersectionVertex(graph, "br", -80.0, 40.0, "bottom and right"); makeEdges(tl, tr, "top"); makeEdges(tl, bl, "left"); makeEdges(br, tr, "right"); makeEdges(bl, br, "bottom"); return graph; } private void makeEdges(StreetVertex v1, StreetVertex v2, String name) { LineString geometry = GeometryUtils.makeLineString(v1.getCoordinate().x, v1.getCoordinate().y, v2.getCoordinate().x, v2.getCoordinate().y); double length = SphericalDistanceLibrary.getInstance().distance(v1.getCoordinate(), v2.getCoordinate()); new PlainStreetEdge(v1, v2, geometry, name, length, StreetTraversalPermission.ALL, false); geometry = GeometryUtils.makeLineString(v2.getCoordinate().x, v2.getCoordinate().y, v1.getCoordinate().x, v1.getCoordinate().y); new PlainStreetEdge(v2, v1, geometry, name, length, StreetTraversalPermission.ALL, true); } } public class TestRequest extends TestCase { public void testRequest() { RoutingRequest request = new RoutingRequest(); request.addMode(TraverseMode.CAR); assertTrue(request.getModes().getCar()); request.removeMode(TraverseMode.CAR); assertFalse(request.getModes().getCar()); request.addMode(TraverseMode.CUSTOM_MOTOR_VEHICLE); assertFalse(request.getModes().getCar()); assertTrue(request.getModes().getDriving()); request.removeMode(TraverseMode.CUSTOM_MOTOR_VEHICLE); assertFalse(request.getModes().getCar()); assertFalse(request.getModes().getDriving()); request.setModes(new TraverseModeSet("BICYCLE,WALK")); assertFalse(request.getModes().getCar()); assertTrue(request.getModes().getBicycle()); assertTrue(request.getModes().getWalk()); } public void testBuildRequest() throws Exception { TestPlanner planner = new TestPlanner("portland", "45.58,-122.68", "45.48,-122.6"); RoutingRequest options = planner.buildRequest(); assertEquals(new Date(1254420671000L), options.getDateTime()); assertEquals(1600.0, options.getMaxWalkDistance()); assertEquals(8.0, options.getWalkReluctance()); assertEquals(1, options.getNumItineraries()); } public void testPlanner() throws Exception { Planner planner = new TestPlanner("portland", "NE 43RD AVE at NE GLISAN ST", "NE 43RD AVE at NE ROYAL CT"); Response response = planner.getItineraries(); Itinerary itinerary = response.getPlan().itinerary.get(0); Leg leg = itinerary.legs.get(0); List<WalkStep> steps = leg.walkSteps; assertEquals(3, steps.size()); WalkStep step0 = steps.get(0); WalkStep step2 = steps.get(2); assertEquals(AbsoluteDirection.NORTH, step0.absoluteDirection); assertEquals("NE 43RD AVE", step0.streetName); assertEquals("NE 43RD AVE", step2.streetName); assertEquals(RelativeDirection.LEFT, step2.relativeDirection); assertTrue(step2.stayOn); } public void testFirstTrip() throws Exception { Planner planner = new TestPlanner("portland", "45.58,-122.68", "45.48,-122.6"); Response response = planner.getFirstTrip(); Itinerary itinerary = response.getPlan().itinerary.get(0); Leg leg = itinerary.legs.get(1); assertTrue(leg.startTime.get(Calendar.HOUR) >= 4); assertTrue(leg.startTime.get(Calendar.HOUR) <= 7); } public void testAlerts() throws Exception { // SE 47th and Ash, NE 47th and Davis (note that we cross Burnside, this goes from SE to NE) Planner planner = new TestPlanner("portland", "SE 47TH AVE at SE ASH ST", "NE 47TH AVE at NE COUCH ST"); Response response = planner.getItineraries(); Itinerary itinerary = response.getPlan().itinerary.get(0); Leg leg = itinerary.legs.get(0); List<WalkStep> steps = leg.walkSteps; assertEquals(2, steps.size()); WalkStep step0 = steps.get(0); WalkStep step1 = steps.get(1); assertNotNull(step0.alerts); assertEquals(1, step0.alerts.size()); assertEquals("SE", step0.alerts.get(0).alertHeaderText.getSomeTranslation()); assertEquals(1, step1.alerts.size()); assertEquals("NE", step1.alerts.get(0).alertHeaderText.getSomeTranslation()); } public void testIntermediate() throws Exception { Vertex v1 = getVertexByCrossStreets("NW 10TH AVE", "W BURNSIDE ST"); Vertex v2 = getVertexByCrossStreets("SE 82ND AVE", "SE ASH ST").getOutgoing().iterator() .next().getToVertex(); Vertex v3 = getVertexByCrossStreets("NE 21ST AVE", "NE MASON ST").getOutgoing().iterator() .next().getToVertex(); Vertex v4 = getVertexByCrossStreets("SE 92ND AVE", "SE FLAVEL ST"); Vertex[] vertices = { v1, v3, v2, v4 }; assertNotNull(v1); assertNotNull(v2); assertNotNull(v3); assertNotNull(v4); TestPlanner planner = new TestPlanner("portland", v1.getLabel(), v4.getLabel(), Arrays.asList(v2.getLabel(), v3.getLabel())); List<GraphPath> paths = planner.getPaths(); assertTrue(paths.size() > 0); GraphPath path = paths.get(0); int curVertex = 0; for (State s : path.states) { if (s.getVertex().equals(vertices[curVertex])) { curVertex += 1; } } assertEquals(4, curVertex); // found all four, in the correct order (1, 3, 2, 4) } private Vertex getVertexByCrossStreets(String s1, String s2) { for (Vertex v : Context.getInstance().graph.getVertices()) { if (v.getName().contains(s1) && v.getName().contains(s2)) { return v; } } return null; } public void testBikeRental() { BikeRental bikeRental = new BikeRental(); bikeRental.setGraphService(Context.getInstance().graphService); // no stations in graph BikeRentalStationList stations = bikeRental.getBikeRentalStations(null, null, null); assertEquals(0, stations.stations.size()); // no stations in range stations = bikeRental.getBikeRentalStations("55.5,-122.7", "65.6,-122.6", "portland"); assertEquals(0, stations.stations.size()); // finally, a station stations = bikeRental.getBikeRentalStations("45.5,-122.7", "45.6,-122.6", "portland"); assertEquals(1, stations.stations.size()); } public void testMetadata() throws JSONException { Metadata metadata = new Metadata(); MetadataService metadataService = new MetadataService(); metadata.setMetadataService(metadataService); metadataService.setGraphService(Context.getInstance().graphService); GraphMetadata data1 = metadata.getMetadata(null); assertTrue("centerLatitude is not 40.005; got " + data1.getCenterLatitude(), Math.abs(40.005 - data1.getCenterLatitude()) < 0.000001); GraphMetadata data2 = metadata.getMetadata("portland"); assertTrue(Math.abs(-122 - data2.getCenterLongitude()) < 1); assertTrue(Math.abs(-122 - data2.getLowerLeftLongitude()) < 2); assertTrue(Math.abs(-122 - data2.getUpperRightLongitude()) < 2); } /** Smoke test for patcher */ public void testPatcher() throws JSONException { Patcher p = new Patcher(); PatchService service = mock(PatchService.class); when(service.getStopPatches(any(AgencyAndId.class))).thenReturn(new ArrayList<Patch>()); when(service.getRoutePatches(any(AgencyAndId.class))).thenReturn(new ArrayList<Patch>()); p.setPatchService(service); PatchResponse stopPatches = p.getStopPatches("TriMet", "5678"); assertNull(stopPatches.patches); PatchResponse routePatches = p.getRoutePatches("TriMet", "100"); assertNull(routePatches.patches); } public void testRouters() throws JSONException { Routers routerApi = new Routers(); routerApi.graphService = Context.getInstance().graphService; RouterList routers = routerApi.getRouterIds(); assertEquals(2, routers.routerInfo.size()); RouterInfo router0 = routers.routerInfo.get(0); RouterInfo router1 = routers.routerInfo.get(1); RouterInfo otherRouter; RouterInfo defaultRouter; if (router0.routerId == null) { defaultRouter = router0; otherRouter = router1; } else { defaultRouter = router1; otherRouter = router0; } assertNull(defaultRouter.routerId); assertNotNull(otherRouter.routerId); assertTrue(otherRouter.polygon.getArea() > 0); } public void testTransitIndex() throws JSONException { TransitIndex index = new TransitIndex(); index.setGraphService(Context.getInstance().graphService); String routerId = "portland"; AgencyList agencyIds = index.getAgencyIds(routerId); assertEquals(agencyIds.agencies.toArray(new Agency[0])[0].getId(), ("TriMet")); assertEquals(1, agencyIds.agencies.size()); RouteDataList routeDataList = (RouteDataList) index.getRouteData("TriMet", "100", false, false, routerId); assertEquals(new AgencyAndId("TriMet", "100"), routeDataList.routeData.toArray(new RouteData[0])[0].id); assertTrue(routeDataList.routeData.toArray(new RouteData[0])[0].variants.size() >= 2); RouteList routes = (RouteList) index.getRoutes("TriMet", false, routerId); assertTrue(routes.routes.size() > 50); //without agencyId routes = (RouteList) index.getRoutes(null, true, routerId); assertTrue(routes.routes.size() > 50); //without agencyId routes = (RouteList) index.getRoutes(null, true, routerId); assertTrue(routes.routes.size() > 50); ModeList modes = (ModeList) index.getModes(routerId); assertTrue(modes.modes.contains(TraverseMode.TRAM)); assertFalse(modes.modes.contains(TraverseMode.FUNICULAR)); RouteList routesForStop = (RouteList) index.getRoutesForStop("TriMet", "10579", false, routerId); assertEquals(1, routesForStop.routes.size()); routesForStop = (RouteList) index.getRoutesForStop(null, "10579", false, routerId); assertEquals(1, routesForStop.routes.size()); // assertEquals("MAX Red Line", routesForStop.routes.get(0).routeLongName); StopList stopsNearPoint = (StopList) index.getStopsNearPoint("TriMet", 45.464783, -122.578918, false, routerId, null); assertTrue(stopsNearPoint.stops.size() > 0); long startTime = TestUtils.dateInSeconds("America/Los_Angeles", 2009, 9, 1, 7, 50, 0) * 1000; long endTime = startTime + 60 * 60 * 1000; StopTimeList stopTimesForStop = (StopTimeList) index.getStopTimesForStop("TriMet", "10579", startTime, endTime, false, false, null, routerId); assertTrue(stopTimesForStop.stopTimes.size() > 0); stopTimesForStop = (StopTimeList) index.getStopTimesForStop(null, "10579", startTime, endTime, false, false, null, routerId); assertTrue(stopTimesForStop.stopTimes.size() > 0); stopTimesForStop = (StopTimeList) index.getStopTimesForStop(null, "10579", startTime, endTime, false, false, null, routerId); assertTrue(stopTimesForStop.stopTimes.size() > 0); stopTimesForStop = (StopTimeList) index.getStopTimesForStop(null, "10579", startTime, endTime, false, false, null, routerId); assertTrue(stopTimesForStop.stopTimes.size() > 0); // StopTimeList stopTimesForTrip = (StopTimeList) index.getStopTimesForTrip("TriMet", "1254", // "TriMet", "10W1040", startTime, routerId); // assertTrue(stopTimesForTrip.stopTimes.size() > 0); } public void testComponents() { Components components = new Components(); components.setGraphService(Context.getInstance().graphService); // GraphComponentPolygons componentPolygons = components.getComponentPolygons( // new TraverseModeSet(TraverseMode.WALK), "2009/10/1", "12:00:00", "", "portland"); // assertTrue(componentPolygons.components.size() >= 1); } public void testGraphInternals() { GraphInternals internals = new GraphInternals(); internals.setGraphService(Context.getInstance().graphService); FeatureCount counts = internals.countVertices("45.5,-122.6", "45.6,-122.5", "portland"); assertTrue(counts.vertices > 0); assertTrue(counts.edges > 0); WithGraph obj = (WithGraph) internals.getEdges("45.5,-122.6", "45.55,-122.55", "", false, false, true, "portland"); EdgeSet edges = (EdgeSet) obj.getObject(); assertTrue(edges.edges.size() > 0); obj = (WithGraph) internals.getVertices("45.5,-122.6", "45.55,-122.55", false, "", false, false, "portland"); VertexSet vertices = (VertexSet) obj.getObject(); assertTrue(vertices.vertices.size() > 0); } public void testBannedTrips() throws JSONException { // Plan short trip along NE GLISAN ST TestPlanner planner = new TestPlanner("portland", "NE 57TH AVE at NE GLISAN ST #2", "NE 30TH AVE at NE GLISAN ST"); // Ban trips with ids 190W1280 and 190W1260 from agency with id TriMet planner.setBannedTrips(Arrays.asList("TriMet_190W1280,TriMet_190W1260")); // Do the planning Response response = planner.getItineraries(); Itinerary itinerary = response.getPlan().itinerary.get(0); Leg leg = itinerary.legs.get(1); // Without bannedTrips this leg would contain a trip with id 190W1280 assertFalse(leg.tripId.equals("190W1280")); // Instead a trip is now expected with id 190W1290 assertTrue(leg.tripId.equals("190W1290")); } public void testBannedStops() throws JSONException, ParameterException { // Plan short trip along NE GLISAN ST TestPlanner planner = new TestPlanner("portland", "NE 57TH AVE at NE GLISAN ST #2", "NE 30TH AVE at NE GLISAN ST"); // Ban stops with ids 2106 and 2107 from agency with id TriMet // These are the two stops near NE 30TH AVE at NE GLISAN ST planner.setBannedStops(Arrays.asList("TriMet_2106,TriMet_2107")); // Do the planning Response response = planner.getItineraries(); // First check the request Itinerary itinerary = response.getPlan().itinerary.get(0); Leg leg = itinerary.legs.get(1); // Without bannedStops this leg would stop at the stop with id 2107 assertFalse(leg.to.stopId.getId().equals("2107")); // Instead a stop is now expected with id 2109 assertTrue(leg.to.stopId.getId().equals("2109")); } @SuppressWarnings("deprecation") public void testBannedStopGroup() throws JSONException, ParameterException { // Create StopMatcher instance StopMatcher stopMatcher = StopMatcher.parse("TriMet_2106,TriMet_65-tc"); // Find stops in graph Graph graph = Context.getInstance().graph; Stop stop65_tc = ((TransitStop) graph.getVertex("TriMet_65-tc")).getStop(); assertNotNull(stop65_tc); Stop stop12921 = ((TransitStop) graph.getVertex("TriMet_12921")).getStop(); assertNotNull(stop12921); Stop stop13132 = ((TransitStop) graph.getVertex("TriMet_13132")).getStop(); assertNotNull(stop13132); Stop stop2106 = ((TransitStop) graph.getVertex("TriMet_2106")).getStop(); assertNotNull(stop2106); Stop stop2107 = ((TransitStop) graph.getVertex("TriMet_2107")).getStop(); assertNotNull(stop2107); // Match stop with id 65-tc assertTrue(stopMatcher.matches(stop65_tc)); // Match stop with id 12921 that has TriMet_65-tc as a parent assertTrue(stopMatcher.matches(stop12921)); // Match stop with id 13132 that has TriMet_65-tc as a parent assertTrue(stopMatcher.matches(stop13132)); // Match stop with id 2106 assertTrue(stopMatcher.matches(stop2106)); // Match stop with id 2107 assertFalse(stopMatcher.matches(stop2107)); } public void testWalkLimitExceeded() throws JSONException, ParameterException { // Plan short trip TestPlanner planner = new TestPlanner("portland", "45.501115,-122.738214", "45.469487,-122.500343"); // Do the planning Response response = planner.getItineraries(); // Check itinerary for walkLimitExceeded Itinerary itinerary = response.getPlan().itinerary.get(0); assertTrue(itinerary.walkDistance > planner.getMaxWalkDistance().get(0)); assertTrue(itinerary.walkLimitExceeded); planner = new TestPlanner("portland", "45.445631,-122.845388", "45.459961,-122.752347"); // Do the planning with high walk reluctance response = planner.getItineraries(); // Check itinerary for walkLimitExceeded itinerary = response.getPlan().itinerary.get(0); assertTrue(itinerary.walkDistance <= planner.getMaxWalkDistance().get(0)); assertFalse(itinerary.walkLimitExceeded); } public void testTransferPenalty() throws JSONException { // Plan short trip TestPlanner planner = new TestPlanner("portland", "45.5264892578125,-122.60479259490967", "45.511622,-122.645564"); // Don't use non-preferred transfer penalty planner.setNonpreferredTransferPenalty(Arrays.asList(0)); // Check number of legs when using different transfer penalties checkLegsWithTransferPenalty(planner, 0, 7, false); checkLegsWithTransferPenalty(planner, 1800, 7, true); } public void testTransferPenalty2() throws JSONException { // Plan short trip TestPlanner planner = new TestPlanner("portland", "45.514861,-122.612035", "45.483096,-122.540624"); // Don't use non-preferred transfer penalty planner.setNonpreferredTransferPenalty(Arrays.asList(0)); // Check number of legs when using different transfer penalties checkLegsWithTransferPenalty(planner, 0, 5, false); checkLegsWithTransferPenalty(planner, 1800, 5, true); } /** * Checks the number of legs when using a specific transfer penalty while planning. * @param planner is the test planner to use * @param transferPenalty is the value for the transfer penalty * @param expectedLegs is the number of expected legs * @param smaller if true, number of legs should be smaller; if false, number of legs should be exact * @throws JSONException */ private void checkLegsWithTransferPenalty(TestPlanner planner, int transferPenalty, int expectedLegs, boolean smaller) throws JSONException { // Set transfer penalty planner.setTransferPenalty(Arrays.asList(transferPenalty)); // Do the planning Response response = planner.getItineraries(); Itinerary itinerary = response.getPlan().itinerary.get(0); // Check the number of legs if (smaller) { assertTrue(itinerary.legs.size() < expectedLegs); } else { assertEquals(expectedLegs, itinerary.legs.size()); } } public void testTripToTripTransfer() throws JSONException { // Plan short trip TestPlanner planner = new TestPlanner("portland", "45.5264892578125,-122.60479259490967", "45.511622,-122.645564"); // Replace the transfer table with an empty table TransferTable table = new TransferTable(); Graph graph = Context.getInstance().graph; when(graph.getTransferTable()).thenReturn(table); // Do the planning Response response = planner.getItineraries(); Itinerary itinerary = response.getPlan().itinerary.get(0); // Check the ids of the first two busses assertEquals("190W1280", itinerary.legs.get(1).tripId); assertEquals("751W1330", itinerary.legs.get(3).tripId); // Now add a transfer between the two busses of minimal 126 seconds (transfer was 125 seconds) addTripToTripTransferTimeToTable(table, "2111", "7452", "19", "75", "190W1280", "751W1330", 126); // Do the planning again response = planner.getItineraries(); itinerary = response.getPlan().itinerary.get(0); // The ids of the first two busses should be different assertFalse("190W1280".equals(itinerary.legs.get(1).tripId) && "751W1330".equals(itinerary.legs.get(3).tripId)); // Now apply a real-time update: let the to-trip have a delay of 3 seconds @SuppressWarnings("deprecation") TableTripPattern pattern = ((PatternStopVertex) graph.getVertex("TriMet_7452_TriMet_751W1090_79_A")).getTripPattern(); applyUpdateToTripPattern(pattern, "751W1330", "7452", 78, 41228, 41228, Update.Status.PREDICTION, 0); // Do the planning again response = planner.getItineraries(); itinerary = response.getPlan().itinerary.get(0); // Check the ids of the first two busses, they should be the original again assertEquals("190W1280", itinerary.legs.get(1).tripId); assertEquals("751W1330", itinerary.legs.get(3).tripId); // "Revert" the real-time update applyUpdateToTripPattern(pattern, "751W1330", "7452", 78, 41225, 41225, Update.Status.PREDICTION, 0); // Revert the graph, thus using the original transfer table again reset(graph); } public void testForbiddenTripToTripTransfer() throws JSONException { // Plan short trip TestPlanner planner = new TestPlanner("portland", "45.5264892578125,-122.60479259490967", "45.511622,-122.645564"); // Replace the transfer table with an empty table TransferTable table = new TransferTable(); Graph graph = Context.getInstance().graph; when(graph.getTransferTable()).thenReturn(table); // Do the planning Response response = planner.getItineraries(); Itinerary itinerary = response.getPlan().itinerary.get(0); // Check the ids of the first two busses assertEquals("190W1280", itinerary.legs.get(1).tripId); assertEquals("751W1330", itinerary.legs.get(3).tripId); // Now add a forbidden transfer between the two busses addTripToTripTransferTimeToTable(table, "2111", "7452", "19", "75", "190W1280", "751W1330" , StopTransfer.FORBIDDEN_TRANSFER); // Do the planning again response = planner.getItineraries(); itinerary = response.getPlan().itinerary.get(0); // The ids of the first two busses should be different assertFalse("190W1280".equals(itinerary.legs.get(1).tripId) && "751W1330".equals(itinerary.legs.get(3).tripId)); // Revert the graph, thus using the original transfer table again reset(graph); } public void testPreferredTripToTripTransfer() throws JSONException { // Plan short trip TestPlanner planner = new TestPlanner("portland", "45.506077,-122.621139", "45.464637,-122.706061"); // Replace the transfer table with an empty table TransferTable table = new TransferTable(); Graph graph = Context.getInstance().graph; when(graph.getTransferTable()).thenReturn(table); // Do the planning Response response = planner.getItineraries(); Itinerary itinerary = response.getPlan().itinerary.get(0); // Check the ids of the first two busses assertEquals("751W1320", itinerary.legs.get(1).tripId); assertEquals("91W1350", itinerary.legs.get(3).tripId); // Now add a preferred transfer between two other busses addTripToTripTransferTimeToTable(table, "7528", "9756", "75", "12", "750W1300", "120W1320" , StopTransfer.PREFERRED_TRANSFER); // Do the planning again response = planner.getItineraries(); itinerary = response.getPlan().itinerary.get(0); // Check the ids of the first two busses, the preferred transfer should be used assertEquals("750W1300", itinerary.legs.get(1).tripId); assertEquals("120W1320", itinerary.legs.get(3).tripId); // Revert the graph, thus using the original transfer table again reset(graph); } public void testTimedTripToTripTransfer() throws JSONException { // Plan short trip TestPlanner planner = new TestPlanner("portland", "45.506077,-122.621139", "45.464637,-122.706061"); // Replace the transfer table with an empty table TransferTable table = new TransferTable(); Graph graph = Context.getInstance().graph; when(graph.getTransferTable()).thenReturn(table); // Do the planning Response response = planner.getItineraries(); Itinerary itinerary = response.getPlan().itinerary.get(0); // Check the ids of the first two busses assertEquals("751W1320", itinerary.legs.get(1).tripId); assertEquals("91W1350", itinerary.legs.get(3).tripId); // Now add a timed transfer between two other busses addTripToTripTransferTimeToTable(table, "7528", "9756", "75", "12", "750W1300", "120W1320" , StopTransfer.TIMED_TRANSFER); // Don't forget to also add a TimedTransferEdge @SuppressWarnings("deprecation") Vertex fromVertex = graph.getVertex("TriMet_7528_arrive"); @SuppressWarnings("deprecation") Vertex toVertex = graph.getVertex("TriMet_9756_depart"); TimedTransferEdge timedTransferEdge = new TimedTransferEdge(fromVertex, toVertex); // Do the planning again response = planner.getItineraries(); itinerary = response.getPlan().itinerary.get(0); // Check the ids of the first two busses, the timed transfer should be used assertEquals("750W1300", itinerary.legs.get(1).tripId); assertEquals("120W1320", itinerary.legs.get(3).tripId); // Now apply a real-time update: let the to-trip be early by 240 seconds, resulting in a transfer time of 0 seconds @SuppressWarnings("deprecation") TableTripPattern pattern = ((PatternStopVertex) graph.getVertex("TriMet_9756_TriMet_120W1320_22_A")).getTripPattern(); applyUpdateToTripPattern(pattern, "120W1320", "9756", 21, 41580, 41580, Update.Status.PREDICTION, 0); // Do the planning again response = planner.getItineraries(); itinerary = response.getPlan().itinerary.get(0); // Check the ids of the first two busses, the timed transfer should still be used assertEquals("750W1300", itinerary.legs.get(1).tripId); assertEquals("120W1320", itinerary.legs.get(3).tripId); // "Revert" the real-time update applyUpdateToTripPattern(pattern, "120W1320", "9756", 21, 41820, 41820, Update.Status.PREDICTION, 0); // Remove the timed transfer from the graph timedTransferEdge.detach(); // Revert the graph, thus using the original transfer table again reset(graph); } public void testTimedStopToStopTransfer() throws JSONException { // Plan short trip TestPlanner planner = new TestPlanner("portland", "45.506077,-122.621139", "45.464637,-122.706061"); // Replace the transfer table with an empty table TransferTable table = new TransferTable(); Graph graph = Context.getInstance().graph; when(graph.getTransferTable()).thenReturn(table); // Do the planning Response response = planner.getItineraries(); Itinerary itinerary = response.getPlan().itinerary.get(0); // Check the ids of the first two busses assertEquals("751W1320", itinerary.legs.get(1).tripId); assertEquals("91W1350", itinerary.legs.get(3).tripId); // Now add a timed transfer between two other busses addStopToStopTransferTimeToTable(table, "7528", "9756", StopTransfer.TIMED_TRANSFER); // Don't forget to also add a TimedTransferEdge @SuppressWarnings("deprecation") Vertex fromVertex = graph.getVertex("TriMet_7528_arrive"); @SuppressWarnings("deprecation") Vertex toVertex = graph.getVertex("TriMet_9756_depart"); TimedTransferEdge timedTransferEdge = new TimedTransferEdge(fromVertex, toVertex); // Do the planning again response = planner.getItineraries(); itinerary = response.getPlan().itinerary.get(0); // Check the ids of the first two busses, the timed transfer should be used assertEquals("750W1300", itinerary.legs.get(1).tripId); assertEquals("120W1320", itinerary.legs.get(3).tripId); // Now apply a real-time update: let the to-trip be early by 240 seconds, resulting in a transfer time of 0 seconds @SuppressWarnings("deprecation") TableTripPattern pattern = ((PatternStopVertex) graph.getVertex("TriMet_9756_TriMet_120W1320_22_A")).getTripPattern(); applyUpdateToTripPattern(pattern, "120W1320", "9756", 21, 41580, 41580, Update.Status.PREDICTION, 0); // Do the planning again response = planner.getItineraries(); itinerary = response.getPlan().itinerary.get(0); // Check the ids of the first two busses, the timed transfer should still be used assertEquals("750W1300", itinerary.legs.get(1).tripId); assertEquals("120W1320", itinerary.legs.get(3).tripId); // Now apply a real-time update: let the to-trip be early by 241 seconds, resulting in a transfer time of -1 seconds applyUpdateToTripPattern(pattern, "120W1320", "9756", 21, 41579, 41579, Update.Status.PREDICTION, 0); // Do the planning again response = planner.getItineraries(); itinerary = response.getPlan().itinerary.get(0); // The ids of the first two busses should be different assertFalse("190W1280".equals(itinerary.legs.get(1).tripId) && "751W1330".equals(itinerary.legs.get(3).tripId)); // "Revert" the real-time update applyUpdateToTripPattern(pattern, "120W1320", "9756", 21, 41820, 41820, Update.Status.PREDICTION, 0); // Remove the timed transfer from the graph timedTransferEdge.detach(); // Revert the graph, thus using the original transfer table again reset(graph); } /** * Add a trip-to-trip transfer time to a transfer table and check the result */ private void addTripToTripTransferTimeToTable(TransferTable table, String fromStopId, String toStopId, String fromRouteId, String toRouteId, String fromTripId, String toTripId, int transferTime) { Stop fromStop = new Stop(); fromStop.setId(new AgencyAndId("TriMet", fromStopId)); Stop toStop = new Stop(); toStop.setId(new AgencyAndId("TriMet", toStopId)); Route fromRoute = new Route(); fromRoute.setId(new AgencyAndId("TriMet", fromRouteId)); Route toRoute = new Route(); toRoute.setId(new AgencyAndId("TriMet", toRouteId)); Trip fromTrip = new Trip(); fromTrip.setId(new AgencyAndId("TriMet", fromTripId)); fromTrip.setRoute(fromRoute); Trip toTrip = new Trip(); toTrip.setId(new AgencyAndId("TriMet", toTripId)); toTrip.setRoute(toRoute); table.addTransferTime(fromStop, toStop, null, null, fromTrip, toTrip, transferTime); assertEquals(transferTime, table.getTransferTime(fromStop, toStop, fromTrip, toTrip, true)); } /** * Add a stop-to-stop transfer time to a transfer table and check the result */ private void addStopToStopTransferTimeToTable(TransferTable table, String fromStopId, String toStopId, int transferTime) { Stop fromStop = new Stop(); fromStop.setId(new AgencyAndId("TriMet", fromStopId)); Stop toStop = new Stop(); toStop.setId(new AgencyAndId("TriMet", toStopId)); Route fromRoute = new Route(); fromRoute.setId(new AgencyAndId("TriMet", "dummy")); Route toRoute = new Route(); toRoute.setId(new AgencyAndId("TriMet", "dummy")); Trip fromTrip = new Trip(); fromTrip.setId(new AgencyAndId("TriMet", "dummy")); fromTrip.setRoute(fromRoute); Trip toTrip = new Trip(); toTrip.setId(new AgencyAndId("TriMet", "dummy")); toTrip.setRoute(toRoute); table.addTransferTime(fromStop, toStop, null, null, null, null, transferTime); assertEquals(transferTime, table.getTransferTime(fromStop, toStop, fromTrip, toTrip, true)); } /** * Apply an update to a table trip pattern and check whether the update was applied correctly */ private void applyUpdateToTripPattern(TableTripPattern pattern, String tripId, String stopId, int stopSeq, int arrive, int depart, Status prediction, int timestamp) { Update update = new Update(new AgencyAndId("TriMet",tripId), stopId, stopSeq, arrive, depart, prediction, timestamp); ArrayList<Update> updates = new ArrayList<Update>(Arrays.asList(update)); UpdateBlock block = UpdateBlock.splitByTrip(updates).get(0); boolean success = pattern.update(block); assertTrue(success); } /** * Subclass of Planner for testing. Constructor sets fields that would usually be set by Jersey from HTTP Query string. */ private static class TestPlanner extends Planner { public TestPlanner(String routerId, String v1, String v2) { super(); this.fromPlace = Arrays.asList(v1); this.toPlace = Arrays.asList(v2); this.date = Arrays.asList("2009-10-01"); this.time = Arrays.asList("11:11:11"); this.maxWalkDistance = Arrays.asList(1600.0); this.walkReluctance = Arrays.asList(8.0); this.walkSpeed = Arrays.asList(1.33); this.optimize = Arrays.asList(OptimizeType.QUICK); this.modes = Arrays.asList(new TraverseModeSet("WALK,TRANSIT")); this.numItineraries = Arrays.asList(1); this.transferPenalty = Arrays.asList(0); this.nonpreferredTransferPenalty = Arrays.asList(180); this.maxTransfers = Arrays.asList(2); this.routerId = Arrays.asList(routerId); this.planGenerator = Context.getInstance().planGenerator; this.graphService = Context.getInstance().graphService; this.planGenerator.graphService = Context.getInstance().graphService; this.prototypeRoutingRequest = new RoutingRequest(); } public TestPlanner(String routerId, String v1, String v2, List<String> intermediates) { this(routerId, v1, v2); this.modes = Arrays.asList(new TraverseModeSet("WALK")); this.intermediatePlaces = intermediates; TravelingSalesmanPathService tsp = new TravelingSalesmanPathService(); tsp.setChainedPathService(Context.getInstance().pathService); tsp.graphService = Context.getInstance().graphService; this.planGenerator.pathService = tsp; } public void setBannedTrips(List<String> bannedTrips) { this.bannedTrips = bannedTrips; } public void setBannedStops(List<String> bannedStops) { this.bannedStops = bannedStops; } public void setTransferPenalty(List<Integer> transferPenalty) { this.transferPenalty = transferPenalty; } public void setNonpreferredTransferPenalty(List<Integer> nonpreferredTransferPenalty) { this.nonpreferredTransferPenalty = nonpreferredTransferPenalty; } public List<Double> getMaxWalkDistance() { return this.maxWalkDistance; } public void setMaxWalkDistance(List<Double> maxWalkDistance) { this.maxWalkDistance = maxWalkDistance; } public RoutingRequest buildRequest() throws ParameterException { return super.buildRequest(); } public List<GraphPath> getPaths() { try { RoutingRequest options = this.buildRequest(); options.intermediatePlacesOrdered = false; return this.planGenerator.pathService.getPaths(options); } catch (ParameterException e) { e.printStackTrace(); return null; } } } }