package org.opentripplanner.routing.core;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.opentripplanner.common.geometry.GeometryUtils;
import org.opentripplanner.routing.edgetype.PlainStreetEdge;
import org.opentripplanner.routing.edgetype.StreetTraversalPermission;
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;
/**
* Tests for SimpleIntersectionTraversalCostModel.
*
* TODO(flamholz): add tests for traversal with traffic lights and without a car.
*
* @author avi
*/
public class SimpleTraversalCostModelTest {
private Graph _graph;
private RoutingRequest options;
public SimpleIntersectionTraversalCostModel costModel;
@Before
public void before() {
_graph = new Graph();
costModel = new SimpleIntersectionTraversalCostModel();
// Initialize the routing request.
options = new RoutingRequest();
options.setCarSpeed(1.0);
options.setWalkSpeed(1.0);
options.setCarDecelerationSpeed(2.0);
options.setCarAccelerationSpeed(2.0);
options.setModes(TraverseModeSet.allModes());
}
@Test
public void testCalculateTurnAngle() {
// Graph for a fictional grid city with turn restrictions
IntersectionVertex v1 = vertex("maple_1st", new Coordinate(2.0, 2.0), false);
IntersectionVertex v2 = vertex("maple_2nd", new Coordinate(2.0, 1.0), false);
PlainStreetEdge e1 = edge(v1, v2, 1.0, false);
// Edge has same first and last angle.
assertEquals(91, e1.getInAngle());
assertEquals(91, e1.getOutAngle());
// 2 new ones
IntersectionVertex v3 = vertex("test2", new Coordinate(1.0, 1.0), false);
// Second edge
PlainStreetEdge e2 = edge(v2, v3, 1.0, false);
assertEquals(0, e2.getInAngle());
assertEquals(0, e2.getOutAngle());
// Difference should be about 90.
int diff = (e1.getOutAngle() - e2.getInAngle());
assertEquals(91, diff);
int turnAngle = costModel.calculateTurnAngle(e1, e2, options);
assertEquals(269, turnAngle);
}
@Test
public void testTurnDirectionChecking() {
// 3 points on a roughly on line
Coordinate a = new Coordinate(-73.990989, 40.750167);
Coordinate b = new Coordinate(-73.988049, 40.749094);
Coordinate c = new Coordinate(-73.984981, 40.747761);
// A vertex for each. No light.
IntersectionVertex u = vertex("from_v", a, false);
IntersectionVertex v = vertex("intersection", b, false);
IntersectionVertex w = vertex("to_v", c, false);
// Two edges.
PlainStreetEdge fromEdge = edge(u, v, 1.0, false);
PlainStreetEdge toEdge = edge(v, w, 1.0, false);
int turnAngle = costModel.calculateTurnAngle(fromEdge, toEdge, options);
assertFalse(costModel.isRightTurn(turnAngle));
assertFalse(costModel.isLeftTurn(turnAngle));
// AKA is a straight ahead.
}
@Test
public void testFreeFlowing() {
// 3 points on a roughly on line
Coordinate a = new Coordinate(-73.990989, 40.750167);
Coordinate b = new Coordinate(-73.988049, 40.749094);
Coordinate c = new Coordinate(-73.984981, 40.747761);
// A vertex for each. No light.
IntersectionVertex u = vertex("from_v", a, false);
IntersectionVertex v = vertex("intersection", b, false);
IntersectionVertex w = vertex("to_v", c, false);
v.setFreeFlowing(true);
// Two edges.
PlainStreetEdge fromEdge = edge(u, v, 1.0, false);
PlainStreetEdge toEdge = edge(v, w, 1.0, false);
float fromSpeed = 1.0f;
float toSpeed = 1.0f;
TraverseMode mode = TraverseMode.CAR;
double traversalCost = costModel.computeTraversalCost(v, fromEdge, toEdge, mode, options, fromSpeed, toSpeed);
// Vertex is free-flowing so cost should be 0.0.
assertEquals(0.0, traversalCost, 0.0);
}
@Test
public void testInferredFreeFlowing() {
// 3 points on a roughly on line
Coordinate a = new Coordinate(-73.990989, 40.750167);
Coordinate b = new Coordinate(-73.988049, 40.749094);
Coordinate c = new Coordinate(-73.984981, 40.747761);
// A vertex for each. No light.
IntersectionVertex u = vertex("from_v", a, false);
IntersectionVertex v = vertex("intersection", b, false);
IntersectionVertex w = vertex("to_v", c, false);
// Two edges - will infer that the vertex is free-flowing since there is no light.
PlainStreetEdge fromEdge = edge(u, v, 1.0, false);
PlainStreetEdge toEdge = edge(v, w, 1.0, false);
float fromSpeed = 1.0f;
float toSpeed = 1.0f;
TraverseMode mode = TraverseMode.CAR;
double traversalCost = costModel.computeTraversalCost(v, fromEdge, toEdge, mode, options, fromSpeed, toSpeed);
// Vertex is free-flowing so cost should be 0.0.
assertEquals(0.0, traversalCost, 0.0);
}
@Test
public void testStraightNoLightInCar() {
// 3 points on a roughly on line
Coordinate a = new Coordinate(-73.990989, 40.750167);
Coordinate b = new Coordinate(-73.988049, 40.749094);
Coordinate c = new Coordinate(-73.984981, 40.747761);
// A vertex for each. No light.
IntersectionVertex u = vertex("from_v", a, false);
IntersectionVertex v = vertex("intersection", b, false);
IntersectionVertex w = vertex("to_v", c, false);
// Two edges.
PlainStreetEdge fromEdge = edge(u, v, 1.0, false);
PlainStreetEdge toEdge = edge(v, w, 1.0, false);
// 3rd edge prevents inferral of free-flowingness
PlainStreetEdge extraEdge = edge(v, u, 1.0, false);
float fromSpeed = 1.0f;
float toSpeed = 1.0f;
TraverseMode mode = TraverseMode.CAR;
double traversalCost = costModel.computeTraversalCost(v, fromEdge, toEdge, mode, options, fromSpeed, toSpeed);
// Cost with default values = 12.375
assertEquals(12.375, traversalCost, 0.0);
}
@Test
public void testRightNoLightInCar() {
// 3 points that form a right turn on the map
Coordinate a = new Coordinate(40.750167, -73.990989);
Coordinate b = new Coordinate(40.749094, -73.988049);
Coordinate c = new Coordinate(40.748509, -73.988693);
// A vertex for each. No light.
IntersectionVertex u = vertex("from_v", a, false);
IntersectionVertex v = vertex("intersection", b, false);
IntersectionVertex w = vertex("to_v", c, false);
// Two edges.
PlainStreetEdge fromEdge = edge(u, v, 1.0, false);
PlainStreetEdge toEdge = edge(v, w, 1.0, false);
// 3rd edge prevents inferral of free-flowingness
PlainStreetEdge extraEdge = edge(v, u, 1.0, false);
int turnAngle = costModel.calculateTurnAngle(fromEdge, toEdge, options);
assertTrue(costModel.isRightTurn(turnAngle));
assertFalse(costModel.isLeftTurn(turnAngle));
float fromSpeed = 1.0f;
float toSpeed = 1.0f;
TraverseMode mode = TraverseMode.CAR;
double traversalCost = costModel.computeTraversalCost(v, fromEdge, toEdge, mode, options, fromSpeed, toSpeed);
// Cost with default values = 10.5
assertEquals(10.5, traversalCost, 0.0);
}
@Test
public void testLeftNoLightInCar() {
// 3 points that form a right turn on the map
Coordinate a = new Coordinate(40.750167, -73.990989);
Coordinate b = new Coordinate(40.749094, -73.988049);
Coordinate c = new Coordinate(40.749760 , -73.987749);
// A vertex for each. No light.
IntersectionVertex u = vertex("from_v", a, false);
IntersectionVertex v = vertex("intersection", b, false);
IntersectionVertex w = vertex("to_v", c, false);
// Two edges.
PlainStreetEdge fromEdge = edge(u, v, 1.0, false);
PlainStreetEdge toEdge = edge(v, w, 1.0, false);
// 3rd edge prevents inferral of free-flowingness
PlainStreetEdge extraEdge = edge(v, u, 1.0, false);
int turnAngle = costModel.calculateTurnAngle(fromEdge, toEdge, options);
assertFalse(costModel.isRightTurn(turnAngle));
assertTrue(costModel.isLeftTurn(turnAngle));
float fromSpeed = 1.0f;
float toSpeed = 1.0f;
TraverseMode mode = TraverseMode.CAR;
double traversalCost = costModel.computeTraversalCost(v, fromEdge, toEdge, mode, options, fromSpeed, toSpeed);
// Cost with default values = 15.5
assertEquals(15.5, traversalCost, 0.0);
}
/****
* Private Methods
****/
private IntersectionVertex vertex(String label, Coordinate coord, boolean hasLight) {
IntersectionVertex v = new IntersectionVertex(_graph, label, coord.y, coord.x);
v.setTrafficLight(hasLight);
return v;
}
/**
* Create an edge. If twoWay, create two edges (back and forth).
*
* @param vA
* @param vB
* @param length
* @param back true if this is a reverse edge
*/
private PlainStreetEdge edge(StreetVertex vA, StreetVertex vB, double length, boolean back) {
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);
StreetTraversalPermission perm = StreetTraversalPermission.ALL;
return new PlainStreetEdge(vA, vB, geom, name, length, perm, back);
}
}