/* 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.graph_builder.impl.osm; import static org.junit.Assert.*; import java.io.File; import java.util.HashMap; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; 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.ConstantIntersectionTraversalCostModel; import org.opentripplanner.routing.core.RoutingRequest; import org.opentripplanner.routing.core.TraverseMode; import org.opentripplanner.routing.core.TraverseModeSet; import org.opentripplanner.routing.graph.Edge; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.graph.Vertex; import org.opentripplanner.routing.services.SPTService; import org.opentripplanner.routing.spt.BasicShortestPathTree; import org.opentripplanner.routing.spt.GraphPath; import org.opentripplanner.routing.spt.MultiShortestPathTree; import org.opentripplanner.routing.spt.ShortestPathTree; import org.opentripplanner.routing.spt.ShortestPathTreeFactory; public class TriangleInequalityTest { private static HashMap<Class<?>, Object> extra; private static Graph _graph; private Vertex start; private Vertex end; @BeforeClass public static void onlyOnce() { extra = new HashMap<Class<?>, Object>(); _graph = new Graph(); OpenStreetMapGraphBuilderImpl loader = new OpenStreetMapGraphBuilderImpl(); loader.setDefaultWayPropertySetSource(new DefaultWayPropertySetSource()); FileBasedOpenStreetMapProviderImpl provider = new FileBasedOpenStreetMapProviderImpl(); File file = new File(TriangleInequalityTest.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(); } @Before public void before() { start = _graph.getVertex("osm:node:1919595913"); end = _graph.getVertex("osm:node:42448554"); } private GraphPath getPath(SPTService sptService, RoutingRequest proto, Edge startBackEdge, Vertex u, Vertex v) { RoutingRequest options = proto.clone(); options.setRoutingContext(_graph, startBackEdge, u, v); ShortestPathTree tree = sptService.getShortestPathTree(options); GraphPath path = tree.getPath(v, false); options.cleanup(); return path; } private void checkTriangleInequality() { checkTriangleInequality(null, null); } private void checkTriangleInequality(TraverseModeSet traverseModes) { checkTriangleInequality(traverseModes, null); } private void checkTriangleInequality(TraverseModeSet traverseModes, ShortestPathTreeFactory sptFactory) { assertNotNull(start); assertNotNull(end); RoutingRequest prototypeOptions = new RoutingRequest(); // All reluctance terms are 1.0 so that duration is monotonically increasing in weight. prototypeOptions.setStairsReluctance(1.0); prototypeOptions.setWalkReluctance(1.0); prototypeOptions.setTurnReluctance(1.0); prototypeOptions.setCarSpeed(1.0); prototypeOptions.setWalkSpeed(1.0); prototypeOptions.setBikeSpeed(1.0); prototypeOptions.setTraversalCostModel(new ConstantIntersectionTraversalCostModel(10.0)); if (traverseModes != null) { prototypeOptions.setModes(traverseModes); } RoutingRequest options = prototypeOptions.clone(); options.setRoutingContext(_graph, start, end); GenericAStar aStar = new GenericAStar(); if (sptFactory != null) { aStar.setShortestPathTreeFactory(sptFactory); } ShortestPathTree tree = aStar.getShortestPathTree(options); GraphPath path = tree.getPath(end, false); options.cleanup(); assertNotNull(path); double startEndWeight = path.getWeight(); int startEndDuration = path.getDuration(); assertTrue(startEndWeight > 0); assertEquals(startEndWeight, (double) startEndDuration, 1.0 * path.edges.size()); // Try every vertex in the graph as an intermediate. boolean violated = false; for (Vertex intermediate : _graph.getVertices()) { if (intermediate == start || intermediate == end) { continue; } GraphPath startIntermediatePath = getPath(aStar, prototypeOptions, null, start, intermediate); if (startIntermediatePath == null) { continue; } Edge back = startIntermediatePath.states.getLast().getBackEdge(); GraphPath intermediateEndPath = getPath(aStar, prototypeOptions, back, intermediate, end); if (intermediateEndPath == null) { continue; } double startIntermediateWeight = startIntermediatePath.getWeight(); int startIntermediateDuration = startIntermediatePath.getDuration(); double intermediateEndWeight = intermediateEndPath.getWeight(); int intermediateEndDuration = intermediateEndPath.getDuration(); // TODO(flamholz): fix traversal so that there's no rounding at the second resolution. assertEquals(startIntermediateWeight, (double) startIntermediateDuration, 1.0 * startIntermediatePath.edges.size()); assertEquals(intermediateEndWeight, (double) intermediateEndDuration, 1.0 * intermediateEndPath.edges.size()); double diff = startIntermediateWeight + intermediateEndWeight - startEndWeight; if (diff < -0.01) { System.out.println("Triangle inequality violated - diff = " + diff); violated = true; } //assertTrue(startIntermediateDuration + intermediateEndDuration >= // startEndDuration); } assertFalse(violated); } @Test public void testTriangleInequalityDefaultModes() { checkTriangleInequality(); } @Test public void testTriangleInequalityWalkingOnly() { TraverseModeSet modes = new TraverseModeSet(TraverseMode.WALK); checkTriangleInequality(modes); } @Test public void testTriangleInequalityDrivingOnly() { TraverseModeSet modes = new TraverseModeSet(TraverseMode.CAR); checkTriangleInequality(modes); } @Test public void testTriangleInequalityWalkTransit() { TraverseModeSet modes = new TraverseModeSet(TraverseMode.WALK, TraverseMode.TRANSIT); checkTriangleInequality(modes); } @Test public void testTriangleInequalityWalkBike() { TraverseModeSet modes = new TraverseModeSet(TraverseMode.WALK, TraverseMode.BICYCLE); checkTriangleInequality(modes); } @Test public void testTriangleInequalityDefaultModesBasicSPT() { checkTriangleInequality(null, new BasicShortestPathTree.FactoryImpl()); } @Test public void testTriangleInequalityWalkingOnlyBasicSPT() { TraverseModeSet modes = new TraverseModeSet(TraverseMode.WALK); checkTriangleInequality(modes, new BasicShortestPathTree.FactoryImpl()); } @Test public void testTriangleInequalityDrivingOnlyBasicSPT() { TraverseModeSet modes = new TraverseModeSet(TraverseMode.CAR); checkTriangleInequality(modes, new BasicShortestPathTree.FactoryImpl()); } @Test public void testTriangleInequalityWalkTransitBasicSPT() { TraverseModeSet modes = new TraverseModeSet(TraverseMode.WALK, TraverseMode.TRANSIT); checkTriangleInequality(modes, new BasicShortestPathTree.FactoryImpl()); } @Test public void testTriangleInequalityWalkBikeBasicSPT() { TraverseModeSet modes = new TraverseModeSet(TraverseMode.WALK, TraverseMode.BICYCLE); checkTriangleInequality(modes, new BasicShortestPathTree.FactoryImpl()); } @Test public void testTriangleInequalityDefaultModesMultiSPT() { checkTriangleInequality(null, MultiShortestPathTree.FACTORY); } @Test public void testTriangleInequalityWalkingOnlyMultiSPT() { TraverseModeSet modes = new TraverseModeSet(TraverseMode.WALK); checkTriangleInequality(modes, MultiShortestPathTree.FACTORY); } @Test public void testTriangleInequalityDrivingOnlyMultiSPT() { TraverseModeSet modes = new TraverseModeSet(TraverseMode.CAR); checkTriangleInequality(modes, MultiShortestPathTree.FACTORY); } @Test public void testTriangleInequalityWalkTransitMultiSPT() { TraverseModeSet modes = new TraverseModeSet(TraverseMode.WALK, TraverseMode.TRANSIT); checkTriangleInequality(modes, MultiShortestPathTree.FACTORY); } @Test public void testTriangleInequalityWalkBikeMultiSPT() { TraverseModeSet modes = new TraverseModeSet(TraverseMode.WALK, TraverseMode.BICYCLE); checkTriangleInequality(modes, MultiShortestPathTree.FACTORY); } }