/* 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/>. */ package org.opentripplanner.routing.edgetype; import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; import org.opentripplanner.common.TurnRestriction; import org.opentripplanner.common.geometry.GeometryUtils; import org.opentripplanner.routing.core.RoutingRequest; import org.opentripplanner.routing.core.State; import org.opentripplanner.routing.core.TraverseMode; import org.opentripplanner.routing.core.TraverseModeSet; import org.opentripplanner.routing.graph.Graph; import org.opentripplanner.routing.vertextype.IntersectionVertex; import org.opentripplanner.routing.vertextype.StreetVertex; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.LineString; public class PlainStreetEdgeTest { private Graph _graph; private IntersectionVertex v0, v1, v2; private RoutingRequest proto; @Before public void before() { _graph = new Graph(); v0 = vertex("maple_0th", 0.0, 0.0); // label, X, Y v1 = vertex("maple_1st", 2.0, 2.0); v2 = vertex("maple_2nd", 1.0, 2.0); proto = new RoutingRequest(); proto.setDummyRoutingContext(_graph); proto.carSpeed = 15.0f; proto.walkSpeed = 1.0; proto.bikeSpeed = 5.0f; proto.setWalkReluctance(1.0); proto.stairsReluctance = (1.0); proto.turnReluctance = (1.0); proto.setModes(TraverseModeSet.allModes()); } @Test public void testInAndOutAngles() { // An edge heading straight West StreetEdge e1 = edge(v1, v2, 1.0, StreetTraversalPermission.ALL); // Edge has same first and last angle. assertEquals(90, e1.getInAngle()); assertEquals(90, e1.getOutAngle()); // 2 new ones StreetVertex u = vertex("test1", 2.0, 1.0); StreetVertex v = vertex("test2", 2.0, 2.0); // Second edge, heading straight North StreetEdge e2 = edge(u, v, 1.0, StreetTraversalPermission.ALL); // 180 degrees could be expressed as 180 or -180. Our implementation happens to use -180. assertEquals(180, Math.abs(e2.getInAngle())); assertEquals(180, Math.abs(e2.getOutAngle())); } @Test public void testTraverseAsPedestrian() { StreetEdge e1 = edge(v1, v2, 100.0, StreetTraversalPermission.ALL); e1.setCarSpeed(10.0f); RoutingRequest options = proto.clone(); options.setMode(TraverseMode.WALK); options.setRoutingContext(_graph, v1, v2); State s0 = new State(options); State s1 = e1.traverse(s0); // Should use the speed on the edge. double expectedWeight = e1.getDistance() / options.walkSpeed; long expectedDuration = (long) Math.ceil(expectedWeight); assertEquals(expectedDuration, s1.getElapsedTimeSeconds(), 0.0); assertEquals(expectedWeight, s1.getWeight(), 0.0); } @Test public void testTraverseAsCar() { StreetEdge e1 = edge(v1, v2, 100.0, StreetTraversalPermission.ALL); e1.setCarSpeed(10.0f); RoutingRequest options = proto.clone(); options.setMode(TraverseMode.CAR); options.setRoutingContext(_graph, v1, v2); State s0 = new State(options); State s1 = e1.traverse(s0); // Should use the speed on the edge. double expectedWeight = e1.getDistance() / e1.getCarSpeed(); long expectedDuration = (long) Math.ceil(expectedWeight); assertEquals(expectedDuration, s1.getElapsedTimeSeconds(), 0.0); assertEquals(expectedWeight, s1.getWeight(), 0.0); } @Test public void testModeSetCanTraverse() { StreetEdge e = edge(v1, v2, 1.0, StreetTraversalPermission.ALL); TraverseModeSet modes = TraverseModeSet.allModes(); assertTrue(e.canTraverse(modes)); modes = new TraverseModeSet(TraverseMode.BICYCLE, TraverseMode.WALK); assertTrue(e.canTraverse(modes)); e = edge(v1, v2, 1.0, StreetTraversalPermission.CAR); assertFalse(e.canTraverse(modes)); modes = new TraverseModeSet(TraverseMode.CAR, TraverseMode.WALK); assertTrue(e.canTraverse(modes)); } /** * Test the traversal of two edges with different traverse modes, with a focus on cycling. * This test will fail unless the following three conditions are met: * 1. Turn costs are computed based on the back edge's traverse mode during reverse traversal. * 2. Turn costs are computed such that bike walking is taken into account correctly. * 3. User-specified bike speeds are applied correctly during turn cost computation. */ @Test public void testTraverseModeSwitchBike() { StreetEdge e0 = edge(v0, v1, 50.0, StreetTraversalPermission.PEDESTRIAN); StreetEdge e1 = edge(v1, v2, 18.4, StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE); v1.trafficLight = (true); RoutingRequest forward = proto.clone(); forward.setMode(TraverseMode.BICYCLE); forward.bikeSpeed = 3.0f; forward.setRoutingContext(_graph, v0, v2); State s0 = new State(forward); State s1 = e0.traverse(s0); State s2 = e1.traverse(s1); RoutingRequest reverse = proto.clone(); reverse.setMode(TraverseMode.BICYCLE); reverse.setArriveBy(true); reverse.bikeSpeed = 3.0f; reverse.setRoutingContext(_graph, v0, v2); State s3 = new State(reverse); State s4 = e1.traverse(s3); State s5 = e0.traverse(s4); assertEquals(73, s2.getElapsedTimeSeconds()); assertEquals(73, s5.getElapsedTimeSeconds()); } /** * Test the traversal of two edges with different traverse modes, with a focus on walking. * This test will fail unless the following three conditions are met: * 1. Turn costs are computed based on the back edge's traverse mode during reverse traversal. * 2. Turn costs are computed such that bike walking is taken into account correctly. * 3. Enabling bike mode on a routing request bases the bike walking speed on the walking speed. */ @Test public void testTraverseModeSwitchWalk() { StreetEdge e0 = edge(v0, v1, 50.0, StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE); StreetEdge e1 = edge(v1, v2, 18.4, StreetTraversalPermission.PEDESTRIAN); v1.trafficLight = (true); RoutingRequest forward = proto.clone(); forward.setMode(TraverseMode.BICYCLE); forward.setRoutingContext(_graph, v0, v2); State s0 = new State(forward); State s1 = e0.traverse(s0); State s2 = e1.traverse(s1); RoutingRequest reverse = proto.clone(); reverse.setMode(TraverseMode.BICYCLE); reverse.setArriveBy(true); reverse.setRoutingContext(_graph, v0, v2); State s3 = new State(reverse); State s4 = e1.traverse(s3); State s5 = e0.traverse(s4); assertEquals(42, s2.getElapsedTimeSeconds()); assertEquals(42, s5.getElapsedTimeSeconds()); } /** * Test the bike switching penalty feature, both its cost penalty and its separate time penalty. */ @Test public void testBikeSwitch() { StreetEdge e0 = edge(v0, v1, 0.0, StreetTraversalPermission.PEDESTRIAN); StreetEdge e1 = edge(v1, v2, 0.0, StreetTraversalPermission.BICYCLE); StreetEdge e2 = edge(v2, v0, 0.0, StreetTraversalPermission.PEDESTRIAN_AND_BICYCLE); RoutingRequest noPenalty = proto.clone(); noPenalty.setMode(TraverseMode.BICYCLE); noPenalty.setRoutingContext(_graph, v0, v0); State s0 = new State(noPenalty); State s1 = e0.traverse(s0); State s2 = e1.traverse(s1); State s3 = e2.traverse(s2); RoutingRequest withPenalty = proto.clone(); withPenalty.bikeSwitchTime = (42); withPenalty.bikeSwitchCost = (23); withPenalty.setMode(TraverseMode.BICYCLE); withPenalty.setRoutingContext(_graph, v0, v0); State s4 = new State(withPenalty); State s5 = e0.traverse(s4); State s6 = e1.traverse(s5); State s7 = e2.traverse(s6); assertEquals(0, s0.getElapsedTimeSeconds()); assertEquals(0, s1.getElapsedTimeSeconds()); assertEquals(0, s2.getElapsedTimeSeconds()); assertEquals(0, s3.getElapsedTimeSeconds()); assertEquals(0.0, s0.getWeight(), 0.0); assertEquals(0.0, s1.getWeight(), 0.0); assertEquals(0.0, s2.getWeight(), 0.0); assertEquals(0.0, s3.getWeight(), 0.0); assertEquals(0.0, s4.getWeight(), 0.0); assertEquals(23.0, s5.getWeight(), 0.0); assertEquals(23.0, s6.getWeight(), 0.0); assertEquals(23.0, s7.getWeight(), 0.0); assertEquals(0, s4.getElapsedTimeSeconds()); assertEquals(42, s5.getElapsedTimeSeconds()); assertEquals(42, s6.getElapsedTimeSeconds()); assertEquals(42, s7.getElapsedTimeSeconds()); } @Test public void testTurnRestriction() { StreetEdge e0 = edge(v0, v1, 50.0, StreetTraversalPermission.ALL); StreetEdge e1 = edge(v1, v2, 18.4, StreetTraversalPermission.ALL); State state = new State(v2, 0, proto.clone()); state.getOptions().setArriveBy(true); _graph.addTurnRestriction(e1, new TurnRestriction(e1, e0, null, TraverseModeSet.allModes())); assertNotNull(e0.traverse(e1.traverse(state))); } /**** * Private Methods ****/ private IntersectionVertex vertex(String label, double x, double y) { IntersectionVertex v = new IntersectionVertex(_graph, label, x, y); return v; } /** * Create an edge. If twoWay, create two edges (back and forth). * * @param vA * @param vB * @param length */ private StreetEdge edge(StreetVertex vA, StreetVertex vB, double length, StreetTraversalPermission perm) { String labelA = vA.getLabel(); String labelB = vB.getLabel(); String name = String.format("%s_%s", labelA, labelB); Coordinate[] coords = new Coordinate[2]; coords[0] = vA.getCoordinate(); coords[1] = vB.getCoordinate(); LineString geom = GeometryUtils.getGeometryFactory().createLineString(coords); return new StreetEdge(vA, vB, geom, name, length, perm, false); } }