package org.opentripplanner.graph_builder.module.osm; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import org.opentripplanner.api.model.Itinerary; import org.opentripplanner.api.model.Leg; import org.opentripplanner.api.model.Place; import org.opentripplanner.api.model.TripPlan; import org.opentripplanner.api.resource.GraphPathToTripPlanConverter; import org.opentripplanner.common.model.GenericLocation; import org.opentripplanner.graph_builder.module.FakeGraph; import org.opentripplanner.routing.core.RoutingRequest; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.impl.DefaultStreetVertexIndexFactory; import org.opentripplanner.routing.impl.GraphPathFinder; import org.opentripplanner.routing.impl.MemoryGraphSource; import org.opentripplanner.routing.services.GraphService; import org.opentripplanner.routing.spt.GraphPath; import org.opentripplanner.standalone.CommandLineParameters; import org.opentripplanner.standalone.OTPServer; import org.opentripplanner.standalone.Router; import java.io.UnsupportedEncodingException; import java.util.Calendar; import java.util.List; import java.util.TimeZone; import static org.junit.Assert.*; /** * Tests for planning with intermediate places */ public class TestIntermediatePlaces { /** * The spacial deviation that we allow in degrees */ public static final double DELTA = 0.005; private static TimeZone timeZone; private static GraphPathFinder graphPathFinder; @BeforeClass public static void setUp() { try { Graph graph = FakeGraph.buildGraphNoTransit(); FakeGraph.addPerpendicularRoutes(graph); FakeGraph.link(graph); graph.index(new DefaultStreetVertexIndexFactory()); OTPServer otpServer = new OTPServer(new CommandLineParameters(), new GraphService()); otpServer.getGraphService().registerGraph("A", new MemoryGraphSource("A", graph)); Router router = otpServer.getGraphService().getRouter("A"); TestIntermediatePlaces.graphPathFinder = new GraphPathFinder(router); timeZone = graph.getTimeZone(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); assert false : "Could not build graph: " + e.getMessage(); } catch (Exception e) { e.printStackTrace(); assert false : "Could not add transit data: " + e.getMessage(); } } @Test public void testWithoutIntermediatePlaces() { GenericLocation fromLocation = new GenericLocation(39.93080, -82.98522); GenericLocation toLocation = new GenericLocation(39.96383, -82.96291); GenericLocation[] intermediateLocations = {}; handleRequest(fromLocation, toLocation, intermediateLocations, "WALK", false); handleRequest(fromLocation, toLocation, intermediateLocations, "WALK", true); } @Test @Ignore public void testOneIntermediatePlace() { GenericLocation fromLocation = new GenericLocation(39.93080, -82.98522); GenericLocation toLocation = new GenericLocation(39.96383, -82.96291); GenericLocation[] intermediateLocations = { new GenericLocation(39.92099, -82.95570) }; handleRequest(fromLocation, toLocation, intermediateLocations, "WALK", false); handleRequest(fromLocation, toLocation, intermediateLocations, "WALK", true); } @Test @Ignore public void testTwoIntermediatePlaces() { GenericLocation fromLocation = new GenericLocation(39.93080, -82.98522); GenericLocation toLocation = new GenericLocation(39.96383, -82.96291); GenericLocation[] intermediateLocations = new GenericLocation[2]; intermediateLocations[0] = new GenericLocation(39.92099, -82.95570); intermediateLocations[1] = new GenericLocation(39.96146, -82.99552); handleRequest(fromLocation, toLocation, intermediateLocations, "CAR", false); handleRequest(fromLocation, toLocation, intermediateLocations, "CAR", true); } @Test public void testTransitWithoutIntermediatePlaces() { GenericLocation fromLocation = new GenericLocation(39.9308, -83.0118); GenericLocation toLocation = new GenericLocation(39.9998, -83.0198); GenericLocation[] intermediateLocations = {}; handleRequest(fromLocation, toLocation, intermediateLocations, "TRANSIT,WALK", false); handleRequest(fromLocation, toLocation, intermediateLocations, "TRANSIT,WALK", true); } @Test public void testThreeBusStopPlaces() { GenericLocation fromLocation = new GenericLocation(39.9058, -83.1341); GenericLocation toLocation = new GenericLocation(39.9058, -82.8841); GenericLocation[] intermediateLocations = { new GenericLocation(39.9058, -82.9841) }; handleRequest(fromLocation, toLocation, intermediateLocations, "TRANSIT", false); handleRequest(fromLocation, toLocation, intermediateLocations, "TRANSIT", true); } @Test public void testTransitOneIntermediatePlace() { GenericLocation fromLocation = new GenericLocation(39.9108, -83.0118); GenericLocation toLocation = new GenericLocation(39.9698, -83.0198); GenericLocation[] intermediateLocations = { new GenericLocation(39.9948, -83.0148) }; handleRequest(fromLocation, toLocation, intermediateLocations, "TRANSIT,WALK", false); handleRequest(fromLocation, toLocation, intermediateLocations, "TRANSIT,WALK", true); } @Test public void testTransitTwoIntermediatePlaces() { GenericLocation fromLocation = new GenericLocation(39.9908, -83.0118); GenericLocation toLocation = new GenericLocation(39.9998, -83.0198); GenericLocation[] intermediateLocations = new GenericLocation[2]; intermediateLocations[0] = new GenericLocation(40.0000, -82.900); intermediateLocations[1] = new GenericLocation(39.9100, -83.100); handleRequest(fromLocation, toLocation, intermediateLocations, "TRANSIT,WALK", false); handleRequest(fromLocation, toLocation, intermediateLocations, "TRANSIT,WALK", true); } private void handleRequest(GenericLocation from, GenericLocation to, GenericLocation[] via, String modes, boolean arriveBy) { RoutingRequest request = new RoutingRequest(modes); request.setDateTime("2016-04-20", "13:00", timeZone); request.setArriveBy(arriveBy); request.from = from; request.to = to; for (GenericLocation intermediateLocation : via) { request.addIntermediatePlace(intermediateLocation); } List<GraphPath> pathList = graphPathFinder.graphPathFinderEntryPoint(request); assertNotNull(pathList); assertFalse(pathList.isEmpty()); TripPlan plan = GraphPathToTripPlanConverter.generatePlan(pathList, request); assertLocationIsVeryCloseToPlace(from, plan.from); assertLocationIsVeryCloseToPlace(to, plan.to); assertTrue(1 <= plan.itinerary.size()); for (Itinerary itinerary : plan.itinerary) { validateIntermediatePlacesVisited(itinerary, via); assertTrue(via.length < itinerary.legs.size()); validateLegsTemporally(request, itinerary); validateLegsSpatially(plan, itinerary); if (modes.contains("TRANSIT")) { assert itinerary.transitTime > 0; } } } // Check that every via location is visited in the right order private void validateIntermediatePlacesVisited(Itinerary itinerary, GenericLocation[] via) { int legIndex = 0; for (GenericLocation location : via) { Leg leg; do { assertTrue("Intermediate location was not an endpoint of any leg", legIndex < itinerary.legs.size()); leg = itinerary.legs.get(legIndex); legIndex++; } while (Math.abs(leg.to.lat - location.lat) > DELTA || Math.abs(leg.to.lon - location.lng) > DELTA); } } // Check that the end point of a leg is also the start point of the next leg private void validateLegsSpatially(TripPlan plan, Itinerary itinerary) { Place place = plan.from; for (Leg leg : itinerary.legs) { assertPlacesAreVeryClose(place, leg.from); place = leg.to; } assertPlacesAreVeryClose(place, plan.to); } // Check that the start time and end time of each leg are consistent private void validateLegsTemporally(RoutingRequest request, Itinerary itinerary) { Calendar departTime = Calendar.getInstance(timeZone); Calendar arriveTime = Calendar.getInstance(timeZone); if (request.arriveBy) { departTime = itinerary.legs.get(0).from.departure; arriveTime.setTimeInMillis(request.dateTime * 1000); } else { departTime.setTimeInMillis(request.dateTime * 1000); arriveTime = itinerary.legs.get(itinerary.legs.size() - 1).to.arrival; } long sumOfDuration = 0; for (Leg leg : itinerary.legs) { assertFalse(departTime.after(leg.startTime)); assertEquals(leg.startTime, leg.from.departure); assertEquals(leg.endTime, leg.to.arrival); assertFalse(leg.startTime.after(leg.endTime)); departTime = leg.to.arrival; sumOfDuration += leg.getDuration(); } sumOfDuration += itinerary.waitingTime; assertFalse(departTime.after(arriveTime)); // Check the total duration of the legs, int accuracy = itinerary.legs.size(); // allow 1 second per leg for rounding errors assertEquals(sumOfDuration, itinerary.duration.doubleValue(), accuracy); } private void assertLocationIsVeryCloseToPlace(GenericLocation location, Place place) { assertEquals(location.lat, place.lat, DELTA); assertEquals(location.lng, place.lon, DELTA); } private void assertPlacesAreVeryClose(Place a, Place b) { assertEquals(a.lat, b.lat, DELTA); assertEquals(a.lon, b.lon, DELTA); } }