/* 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 (props, 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/>. */ package org.opentripplanner.api.thrift.impl; import static org.junit.Assert.*; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Random; import java.util.Set; import org.apache.thrift.TException; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; import org.opentripplanner.api.thrift.definition.BulkPathsRequest; import org.opentripplanner.api.thrift.definition.BulkPathsResponse; import org.opentripplanner.api.thrift.definition.FindNearestVertexRequest; import org.opentripplanner.api.thrift.definition.FindNearestVertexResponse; import org.opentripplanner.api.thrift.definition.FindPathsRequest; import org.opentripplanner.api.thrift.definition.FindPathsResponse; import org.opentripplanner.api.thrift.definition.GraphEdge; import org.opentripplanner.api.thrift.definition.GraphEdgesRequest; import org.opentripplanner.api.thrift.definition.GraphEdgesResponse; import org.opentripplanner.api.thrift.definition.GraphVertex; import org.opentripplanner.api.thrift.definition.GraphVerticesRequest; import org.opentripplanner.api.thrift.definition.GraphVerticesResponse; import org.opentripplanner.api.thrift.definition.LatLng; import org.opentripplanner.api.thrift.definition.Location; import org.opentripplanner.api.thrift.definition.Path; import org.opentripplanner.api.thrift.definition.PathOptions; import org.opentripplanner.api.thrift.definition.TravelMode; import org.opentripplanner.api.thrift.definition.TravelState; import org.opentripplanner.api.thrift.definition.TripParameters; import org.opentripplanner.api.thrift.definition.TripPaths; import org.opentripplanner.api.thrift.definition.VertexQuery; import org.opentripplanner.common.model.P2; import org.opentripplanner.graph_builder.impl.osm.DefaultWayPropertySetSource; import org.opentripplanner.graph_builder.impl.osm.OpenStreetMapGraphBuilderImpl; import org.opentripplanner.openstreetmap.impl.FileBasedOpenStreetMapProviderImpl; import org.opentripplanner.routing.algorithm.GenericAStar; import org.opentripplanner.routing.core.RoutingRequest; import org.opentripplanner.routing.graph.Edge; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.graph.Vertex; import org.opentripplanner.routing.impl.DefaultStreetVertexIndexFactory; import org.opentripplanner.routing.impl.GraphServiceImpl; import org.opentripplanner.routing.impl.SimplifiedPathServiceImpl; import com.vividsolutions.jts.geom.Coordinate; public class OTPServiceImplTest { private static HashMap<Class<?>, Object> extra; private static Graph graph; private Random rand; private OTPServiceImpl serviceImpl; @BeforeClass public static void beforeClass() { extra = new HashMap<Class<?>, Object>(); graph = new Graph(); OpenStreetMapGraphBuilderImpl loader = new OpenStreetMapGraphBuilderImpl(); loader.setDefaultWayPropertySetSource(new DefaultWayPropertySetSource()); FileBasedOpenStreetMapProviderImpl provider = new FileBasedOpenStreetMapProviderImpl(); File file = new File(OTPServiceImplTest.class.getResource("NYC_small.osm.gz").getFile()); provider.setPath(file); loader.setProvider(provider); loader.buildGraph(graph, extra); // Need to set up the index because buildGraph doesn't do it. graph.rebuildVertexAndEdgeIndices(); graph.streetIndex = (new DefaultStreetVertexIndexFactory()).newIndex(graph); } @Before public void before() { rand = new Random(1234); GraphServiceImpl graphService = new GraphServiceImpl(); graphService.registerGraph("", graph); SimplifiedPathServiceImpl pathService = new SimplifiedPathServiceImpl(); pathService.setGraphService(graphService); pathService.setSptService(new GenericAStar()); RoutingRequest prototype = new RoutingRequest(); prototype.setTurnReluctance(1.0); prototype.setWalkReluctance(1.0); prototype.setStairsReluctance(1.0); serviceImpl = new OTPServiceImpl(); serviceImpl.setPrototypeRoutingRequest(prototype); serviceImpl.setGraphService(graphService); serviceImpl.setPathService(pathService); } @Test public void testGetGraphEdges() throws TException { GraphEdgesRequest req = new GraphEdgesRequest(); req.validate(); req.setStreet_edges_only(true); GraphEdgesResponse res = serviceImpl.GetEdges(req); Set<Integer> expectedEdgeIds = new HashSet<Integer>(); for (Edge e : graph.getStreetEdges()) { expectedEdgeIds.add(e.getId()); } Set<Integer> actualEdgeIds = new HashSet<Integer>(res.getEdgesSize()); for (GraphEdge ge : res.getEdges()) { actualEdgeIds.add(ge.getId()); } assertTrue(expectedEdgeIds.equals(actualEdgeIds)); } @Test public void testGetGraphVertices() throws TException { GraphVerticesRequest req = new GraphVerticesRequest(); req.validate(); GraphVerticesResponse res = serviceImpl.GetVertices(req); Set<Integer> expectedVertexIds = new HashSet<Integer>(); for (Vertex v : graph.getVertices()) { expectedVertexIds.add(v.getIndex()); } Set<Integer> actualVertexIds = new HashSet<Integer>(res.getVerticesSize()); for (GraphVertex gv : res.getVertices()) { actualVertexIds.add(gv.getId()); } assertTrue(expectedVertexIds.equals(actualVertexIds)); } private Location getLocationForVertex(Vertex v) { Location loc = new Location(); Coordinate c = v.getCoordinate(); loc.setLat_lng(new LatLng(c.y, c.x)); return loc; } private Location getLocationForTravelState(TravelState ts) { Location loc = new Location(); loc.setLat_lng(ts.getVertex().getLat_lng()); return loc; } private P2<Location> pickOriginAndDest() { List<Vertex> vList = new ArrayList<Vertex>(graph.getVertices()); Collections.shuffle(vList, rand); P2<Location> pair = new P2<Location>(getLocationForVertex(vList.get(0)), getLocationForVertex(vList.get(1))); return pair; } private void checkPath(Path p) { int duration = p.getDuration(); int nStates = p.getStatesSize(); long startTime = p.getStates().get(0).getArrival_time(); long endTime = p.getStates().get(nStates - 1).getArrival_time(); int computedDuration = (int) (endTime - startTime); assertEquals(duration, computedDuration); } @Test public void testFindPaths() throws TException { PathOptions opts = new PathOptions(); opts.setNum_paths(1); opts.setReturn_detailed_path(true); TripParameters trip = new TripParameters(); trip.addToAllowed_modes(TravelMode.CAR); P2<Location> pair = pickOriginAndDest(); trip.setOrigin(pair.getFirst()); trip.setDestination(pair.getSecond()); FindPathsRequest req = new FindPathsRequest(); req.setOptions(opts); req.setTrip(trip); req.validate(); FindPathsResponse res = serviceImpl.FindPaths(req); TripPaths paths = res.getPaths(); while (paths == null || paths.getPaths() == null || paths.getPaths().isEmpty()) { // Pick another if we got no result. pair = pickOriginAndDest(); trip.setOrigin(pair.getFirst()); trip.setDestination(pair.getSecond()); res = serviceImpl.FindPaths(req); paths = res.getPaths(); } assertEquals(1, paths.getPathsSize()); Path p = paths.getPaths().get(0); checkPath(p); // Check what happens when we decompose this path into subpaths. int expectedTotalDuration = p.getDuration(); int subPathDurations = 0; for (int i = 1; i < p.getStatesSize(); ++i) { TravelState firstState = p.getStates().get(i - 1); TravelState secondState = p.getStates().get(i); Location startLoc = getLocationForTravelState(firstState); Location endLoc = getLocationForTravelState(secondState); trip.setOrigin(startLoc); trip.setDestination(endLoc); req = new FindPathsRequest(); req.setOptions(opts); req.setTrip(trip); req.validate(); res = serviceImpl.FindPaths(req); paths = res.getPaths(); assertEquals(1, paths.getPathsSize()); Path subPath = paths.getPaths().get(0); checkPath(subPath); subPathDurations += subPath.getDuration(); } // Subpaths may take less time because they need not start on the // the same edges as the original path. assertTrue(subPathDurations <= expectedTotalDuration); } @Test public void testBulkFindPaths() throws TException { PathOptions opts = new PathOptions(); opts.setNum_paths(1); opts.setReturn_detailed_path(true); BulkPathsRequest req = new BulkPathsRequest(); req.setOptions(opts); for (int i = 0; i < 4; ++i) { TripParameters trip = new TripParameters(); trip.addToAllowed_modes(TravelMode.CAR); P2<Location> pair = pickOriginAndDest(); trip.setOrigin(pair.getFirst()); trip.setDestination(pair.getSecond()); req.addToTrips(trip); } req.validate(); BulkPathsResponse res = serviceImpl.BulkFindPaths(req); for (TripPaths paths : res.getPaths()) { int expectedPathsSize = paths.isNo_paths_found() ? 0 : 1; assertEquals(expectedPathsSize, paths.getPathsSize()); if (!paths.isSetNo_paths_found()) { Path p = paths.getPaths().get(0); checkPath(p); } } } @Test public void testFindNearestVertex() throws TException { for (Vertex v : graph.getVertices()) { FindNearestVertexRequest req = new FindNearestVertexRequest(); VertexQuery q = new VertexQuery(); q.setLocation(getLocationForVertex(v)); req.setQuery(q); FindNearestVertexResponse res = serviceImpl.FindNearestVertex(req); GraphVertex gv = res.getResult().getNearest_vertex(); int expectedId = v.getIndex(); int actualId = gv.getId(); if (expectedId != actualId) { // If not equal, then the distance should be approaching 0. LatLng ll = gv.getLat_lng(); Coordinate outCoord = new Coordinate(ll.getLng(), ll.getLat()); assertTrue(v.getCoordinate().distance(outCoord) < 0.00001); } } } }