// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.turnrestrictions;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.openstreetmap.josm.plugins.turnrestrictions.TurnRestrictionBuilder.intersectionAngle;
import static org.openstreetmap.josm.plugins.turnrestrictions.TurnRestrictionBuilder.selectToWayAfterSplit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import org.junit.Rule;
import org.junit.Test;
import org.openstreetmap.josm.data.coor.LatLon;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.Relation;
import org.openstreetmap.josm.data.osm.RelationMember;
import org.openstreetmap.josm.data.osm.Way;
import org.openstreetmap.josm.plugins.turnrestrictions.TurnRestrictionBuilder.RelativeWayJoinOrientation;
import org.openstreetmap.josm.plugins.turnrestrictions.editor.TurnRestrictionType;
import org.openstreetmap.josm.testutils.JOSMTestRules;
public class TurnRestrictionBuilderTest {
@Rule
public JOSMTestRules rules = new JOSMTestRules().preferences();
TurnRestrictionBuilder builder = new TurnRestrictionBuilder();
boolean hasExactlyOneMemberWithRole(Relation r, final String role) {
return r.getMembers().stream().filter(rm -> role.equals(rm.getRole())).findFirst().isPresent();
}
OsmPrimitive memberWithRole(Relation r, final String role) {
Optional<RelationMember> opt = r.getMembers().stream().filter(rm -> role.equals(rm.getRole())).findFirst();
if (!opt.isPresent())
return null;
return opt.get().getMember();
}
static void assertEmptyTurnRestriction(Relation r) {
assertNotNull(r);
assertEquals("restriction", r.get("type"));
assertEquals(0, r.getMembersCount());
}
/**
* Selection consist of one way and the start node of the way ->
* propose a No-U-Turn restriction
*
*/
@Test
public void noUTurn_1() {
Way w = new Way(1);
Node n1 = new Node(1);
Node n2 = new Node(2);
w.setNodes(Arrays.asList(n1, n2));
List<OsmPrimitive> sel = Arrays.asList(w, n1);
TurnRestrictionBuilder builder = new TurnRestrictionBuilder();
Relation r = builder.build(sel);
assertNotNull(r);
assertEquals(3, r.getMembersCount());
assertTrue(hasExactlyOneMemberWithRole(r, "from"));
assertTrue(hasExactlyOneMemberWithRole(r, "to"));
assertTrue(hasExactlyOneMemberWithRole(r, "via"));
assertEquals(w, memberWithRole(r, "from"));
assertEquals(w, memberWithRole(r, "to"));
assertEquals(n1, memberWithRole(r, "via"));
assertEquals("no_u_turn", r.get("restriction"));
}
/**
* Selection consist of one way and the end node of the way ->
* propose a No-U-Turn restriction
*
*/
@Test
public void noUTurn_2() {
Way w = new Way(1);
Node n1 = new Node(1);
Node n2 = new Node(2);
w.setNodes(Arrays.asList(n1, n2));
List<OsmPrimitive> sel = Arrays.asList(w, n2);
TurnRestrictionBuilder builder = new TurnRestrictionBuilder();
Relation r = builder.build(sel);
assertNotNull(r);
assertEquals(3, r.getMembersCount());
assertTrue(hasExactlyOneMemberWithRole(r, "from"));
assertTrue(hasExactlyOneMemberWithRole(r, "to"));
assertTrue(hasExactlyOneMemberWithRole(r, "via"));
assertEquals(w, memberWithRole(r, "from"));
assertEquals(w, memberWithRole(r, "to"));
assertEquals(n2, memberWithRole(r, "via"));
assertEquals("no_u_turn", r.get("restriction"));
}
@Test
public void nullSelection() {
assertEmptyTurnRestriction(builder.build(null));
}
@Test
public void emptySelection() {
assertEmptyTurnRestriction(builder.build(new ArrayList<>()));
}
/**
* One selected way -> build a turn restriction with a "from" leg
* only
*/
@Test
public void oneSelectedWay() {
Way w = new Way(1);
Relation tr = builder.build(Arrays.asList(w));
assertNotNull(tr);
assertEquals("restriction", tr.get("type"));
assertEquals(1, tr.getMembersCount());
assertEquals(w, memberWithRole(tr, "from"));
}
/**
* Two unconnected ways in the selection. The first one becomes the from leg,
* the second one the two leg.
*/
@Test
public void twoUnconnectedWays() {
Way w1 = new Way(1);
w1.setNodes(Arrays.asList(new Node(11), new Node(12)));
Way w2 = new Way(2);
w2.setNodes(Arrays.asList(new Node(21), new Node(22)));
Relation tr = builder.build(Arrays.asList(w1, w2));
assertNotNull(tr);
assertEquals("restriction", tr.get("type"));
assertFalse(tr.hasKey("restriction"));
assertEquals(2, tr.getMembersCount());
assertEquals(w1, memberWithRole(tr, "from"));
assertEquals(w2, memberWithRole(tr, "to"));
}
/**
* Two connected ways. end node of the first way connects to start node of
* the second way.
* w2
* -------->
* ^
* | w1
* |
*/
@Test
public void twoConnectedWays_1() {
Node n1 = new Node(1);
n1.setCoor(new LatLon(1, 1));
Node n2 = new Node(2);
n2.setCoor(new LatLon(2, 1));
Node n3 = new Node(3);
n3.setCoor(new LatLon(2, 2));
Way w1 = new Way(1);
w1.setNodes(Arrays.asList(n1, n2));
Way w2 = new Way(2);
w2.setNodes(Arrays.asList(n2, n3));
assertEquals(Math.toRadians(90), TurnRestrictionBuilder.phi(w1), 1e-7);
assertEquals(Math.toRadians(0), TurnRestrictionBuilder.phi(w2), 1e-7);
Relation tr = builder.build(Arrays.asList(w1, w2, n2));
assertNotNull(tr);
assertEquals("restriction", tr.get("type"));
assertEquals(3, tr.getMembersCount());
assertEquals(w1, memberWithRole(tr, "from"));
assertEquals(w2, memberWithRole(tr, "to"));
assertEquals(n2, memberWithRole(tr, "via"));
assertEquals("no_right_turn", tr.get("restriction"));
/*
* opposite order, from w2 to w1. In this case we have left turn.
*/
tr = builder.build(Arrays.asList(w2, w1, n2));
double a = intersectionAngle(w2, w1);
System.out.println("a=" + Math.toDegrees(a));
assertNotNull(tr);
assertEquals("restriction", tr.get("type"));
assertEquals(3, tr.getMembersCount());
assertEquals(w2, memberWithRole(tr, "from"));
assertEquals(w1, memberWithRole(tr, "to"));
assertEquals(n2, memberWithRole(tr, "via"));
assertEquals("no_left_turn", tr.get("restriction"));
}
/**
* Two connected ways. end node of the first way connects to end node of
* the second way. left turn.
*
* w2
* (7,2) -------> (7,5)
* ^
* | w1
* |
* (5,5)
*/
@Test
public void twoConnectedWays_2() {
Node n1 = new Node(1);
n1.setCoor(new LatLon(5, 5));
Node n2 = new Node(2);
n2.setCoor(new LatLon(7, 5));
Node n3 = new Node(3);
n3.setCoor(new LatLon(7, 2));
Way w1 = new Way(1);
w1.setNodes(Arrays.asList(n1, n2));
Way w2 = new Way(2);
w2.setNodes(Arrays.asList(n3, n2));
assertEquals(Math.toRadians(90), TurnRestrictionBuilder.phi(w1), 1e-7);
assertEquals(Math.toRadians(0), TurnRestrictionBuilder.phi(w2), 1e-7);
assertEquals(Math.toRadians(180), TurnRestrictionBuilder.phi(w2, true), 1e-7);
Relation tr = builder.build(Arrays.asList(w1, w2, n2));
assertNotNull(tr);
assertEquals("restriction", tr.get("type"));
assertEquals(3, tr.getMembersCount());
assertEquals(w1, memberWithRole(tr, "from"));
assertEquals(w2, memberWithRole(tr, "to"));
assertEquals(n2, memberWithRole(tr, "via"));
assertEquals("no_left_turn", tr.get("restriction"));
/*
* opposite order, from w2 to w1. In this case we have right turn.
*/
tr = builder.build(Arrays.asList(w2, w1, n2));
assertNotNull(tr);
assertEquals("restriction", tr.get("type"));
assertEquals(3, tr.getMembersCount());
assertEquals(w2, memberWithRole(tr, "from"));
assertEquals(w1, memberWithRole(tr, "to"));
assertEquals(n2, memberWithRole(tr, "via"));
assertEquals("no_right_turn", tr.get("restriction"));
}
/**
* Two connected ways. end node of the first way connects to end node of
* the second way. left turn.
*
*
* (7,5) -
* ^ - w2
* | w1 ------> (6,7)
* |
* (5,5)
*/
@Test
public void twoConnectedWays_3() {
Node n1 = new Node(1);
n1.setCoor(new LatLon(5, 5));
Node n2 = new Node(2);
n2.setCoor(new LatLon(7, 5));
Node n3 = new Node(3);
n3.setCoor(new LatLon(6, 7));
Way w1 = new Way(1);
w1.setNodes(Arrays.asList(n1, n2));
Way w2 = new Way(2);
w2.setNodes(Arrays.asList(n2, n3));
Relation tr = builder.build(Arrays.asList(w1, w2, n2));
assertNotNull(tr);
assertEquals("restriction", tr.get("type"));
assertEquals(3, tr.getMembersCount());
assertEquals(w1, memberWithRole(tr, "from"));
assertEquals(w2, memberWithRole(tr, "to"));
assertEquals(n2, memberWithRole(tr, "via"));
assertEquals("no_right_turn", tr.get("restriction"));
}
/**
* Two connected ways. end node of the first way connects to end node of
* the second way. left turn.
*
*
* (10,10)
* \
* \
* \
* v
* (8,15)
* /
* /
* /
* v
* (5,11)
*/
@Test
public void twoConnectedWays_4() {
Node n1 = new Node(1);
n1.setCoor(new LatLon(10, 10));
Node n2 = new Node(2);
n2.setCoor(new LatLon(8, 15));
Node n3 = new Node(3);
n3.setCoor(new LatLon(5, 11));
Way w1 = new Way(1);
w1.setNodes(Arrays.asList(n1, n2));
Way w2 = new Way(2);
w2.setNodes(Arrays.asList(n2, n3));
Relation tr = builder.build(Arrays.asList(w1, w2, n2));
assertNotNull(tr);
assertEquals("restriction", tr.get("type"));
assertEquals(3, tr.getMembersCount());
assertEquals(w1, memberWithRole(tr, "from"));
assertEquals(w2, memberWithRole(tr, "to"));
assertEquals(n2, memberWithRole(tr, "via"));
assertEquals("no_right_turn", tr.get("restriction"));
/*
* opposite order, from w2 to w1. In this case we have left turn.
*/
tr = builder.build(Arrays.asList(w2, w1, n2));
assertNotNull(tr);
assertEquals("restriction", tr.get("type"));
assertEquals(3, tr.getMembersCount());
assertEquals(w2, memberWithRole(tr, "from"));
assertEquals(w1, memberWithRole(tr, "to"));
assertEquals(n2, memberWithRole(tr, "via"));
assertEquals("no_left_turn", tr.get("restriction"));
}
static Node nn(long id, double lat, double lon) {
Node n = new Node(id);
n.setCoor(new LatLon(lat, lon));
return n;
}
static Way nw(long id, Node... nodes) {
Way w = new Way(id);
w.setNodes(Arrays.asList(nodes));
return w;
}
/**
* n3
* (10,10)
* ^
* | to
* n1 from |
* (5,5) --------------> (5,10) n2
*/
@Test
public void intersectionAngle_1() {
Node n1 = nn(1, 5, 5);
Node n2 = nn(2, 5, 10);
Node n3 = nn(3, 10, 10);
Way from = nw(1, n1, n2);
Way to = nw(2, n2, n3);
double a = TurnRestrictionBuilder.intersectionAngle(from, to);
RelativeWayJoinOrientation o = TurnRestrictionBuilder.determineWayJoinOrientation(from, to);
assertEquals(-90, Math.toDegrees(a), 1e-7);
assertEquals(RelativeWayJoinOrientation.LEFT, o);
/*
* if reversed from, the intersection angle is still -90
*/
from = nw(1, n2, n1);
to = nw(2, n2, n3);
a = TurnRestrictionBuilder.intersectionAngle(from, to);
o = TurnRestrictionBuilder.determineWayJoinOrientation(from, to);
assertEquals(-90, Math.toDegrees(a), 1e-7);
assertEquals(RelativeWayJoinOrientation.LEFT, o);
/*
* if reversed to, the intersection angle is still -90
*/
from = nw(1, n1, n2);
to = nw(2, n3, n2);
a = TurnRestrictionBuilder.intersectionAngle(from, to);
o = TurnRestrictionBuilder.determineWayJoinOrientation(from, to);
assertEquals(-90, Math.toDegrees(a), 1e-7);
assertEquals(RelativeWayJoinOrientation.LEFT, o);
/*
* if reversed both, the intersection angle is still -90
*/
from = nw(1, n2, n1);
to = nw(2, n3, n2);
a = TurnRestrictionBuilder.intersectionAngle(from, to);
o = TurnRestrictionBuilder.determineWayJoinOrientation(from, to);
assertEquals(-90, Math.toDegrees(a), 1e-7);
assertEquals(RelativeWayJoinOrientation.LEFT, o);
}
/**
* n1 from
* (5,5) --------------> (5,10) n2
* |
* | to
* |
* v
* (2,10)
* n3
*
*/
@Test
public void intersectionAngle_2() {
Node n1 = nn(1, 5, 5);
Node n2 = nn(2, 5, 10);
Node n3 = nn(3, 2, 10);
Way from = nw(1, n1, n2);
Way to = nw(2, n2, n3);
double a = TurnRestrictionBuilder.intersectionAngle(from, to);
assertEquals(90, Math.toDegrees(a), 1e-7);
/*
* if reversed from, the intersection angle is still 90
*/
from = nw(1, n2, n1);
to = nw(2, n2, n3);
a = TurnRestrictionBuilder.intersectionAngle(from, to);
assertEquals(90, Math.toDegrees(a), 1e-7);
/*
* if reversed to, the intersection angle is still 90
*/
from = nw(1, n1, n2);
to = nw(2, n3, n2);
a = TurnRestrictionBuilder.intersectionAngle(from, to);
assertEquals(90, Math.toDegrees(a), 1e-7);
/*
* if reversed both, the intersection angle is still 90
*/
from = nw(1, n2, n1);
to = nw(2, n3, n2);
a = TurnRestrictionBuilder.intersectionAngle(from, to);
assertEquals(90, Math.toDegrees(a), 1e-7);
}
/**
*
*
* (-1,-6) (n3)
* ^
* /
* / to
* /
* (-5, -10) n2
* ^
* |
* | from
* |
* (-10,-10) n1
*/
@Test
public void intersectionAngle_3() {
Node n1 = nn(1, -10, -10);
Node n2 = nn(2, -5, -10);
Node n3 = nn(3, -1, -6);
Way from = nw(1, n1, n2);
Way to = nw(2, n2, n3);
double a = TurnRestrictionBuilder.intersectionAngle(from, to);
assertEquals(45, Math.toDegrees(a), 1e-7);
/*
* if reversed from, the intersection angle is still 45
*/
from = nw(1, n2, n1);
to = nw(2, n2, n3);
a = TurnRestrictionBuilder.intersectionAngle(from, to);
assertEquals(45, Math.toDegrees(a), 1e-7);
/*
* if reversed to, the intersection angle is still 45
*/
from = nw(1, n1, n2);
to = nw(2, n3, n2);
a = TurnRestrictionBuilder.intersectionAngle(from, to);
assertEquals(45, Math.toDegrees(a), 1e-7);
/*
* if reversed both, the intersection angle is still 45
*/
from = nw(1, n2, n1);
to = nw(2, n3, n2);
a = TurnRestrictionBuilder.intersectionAngle(from, to);
assertEquals(45, Math.toDegrees(a), 1e-7);
}
/**
*
*
* (-1,-14) (n3)
* ^
* \
* \ to
* \
* (-5, -10) n2
* ^
* |
* | from
* |
* (-10,-10) n1
*/
@Test
public void intersectionAngle_4() {
Node n1 = nn(1, -10, -10);
Node n2 = nn(2, -5, -10);
Node n3 = nn(3, -1, -14);
Way from = nw(1, n1, n2);
Way to = nw(2, n2, n3);
double a = TurnRestrictionBuilder.intersectionAngle(from, to);
assertEquals(-45, Math.toDegrees(a), 1e-7);
/*
* if reversed from, the intersection angle is still -45
*/
from = nw(1, n2, n1);
to = nw(2, n2, n3);
a = TurnRestrictionBuilder.intersectionAngle(from, to);
assertEquals(-45, Math.toDegrees(a), 1e-7);
/*
* if reversed to, the intersection angle is still -45
*/
from = nw(1, n1, n2);
to = nw(2, n3, n2);
a = TurnRestrictionBuilder.intersectionAngle(from, to);
assertEquals(-45, Math.toDegrees(a), 1e-7);
/*
* if reversed both, the intersection angle is still 45
*/
from = nw(1, n2, n1);
to = nw(2, n3, n2);
a = TurnRestrictionBuilder.intersectionAngle(from, to);
assertEquals(-45, Math.toDegrees(a), 1e-7);
}
/*
*
* n21 w21 n22 w22 n23
* (10,10)-------------> (10,15) -------------- > (10,20)
* ^
* |
* | w1
* |
* (5,15)
* n11
*/
@Test
public void splitToWay() {
Node n11 = new Node(11);
n11.setCoor(new LatLon(5, 15));
Node n21 = new Node(21);
n21.setCoor(new LatLon(10, 10));
Node n22 = new Node(22);
n22.setCoor(new LatLon(10, 15));
Node n23 = new Node(23);
n23.setCoor(new LatLon(10, 20));
Way w1 = new Way(1);
w1.setNodes(Arrays.asList(n11, n22));
Way w21 = new Way(21);
w21.setNodes(Arrays.asList(n21, n22));
Way w22 = new Way(22);
w22.setNodes(Arrays.asList(n22, n23));
Way adjustedTo = selectToWayAfterSplit(
w1,
w21,
w22,
TurnRestrictionType.NO_LEFT_TURN
);
assertNotNull(adjustedTo);
assertEquals(w21, adjustedTo);
adjustedTo = selectToWayAfterSplit(
w1,
w21,
w22,
TurnRestrictionType.NO_RIGHT_TURN
);
assertNotNull(adjustedTo);
assertEquals(w22, adjustedTo);
adjustedTo = selectToWayAfterSplit(
w1,
w21,
w22,
TurnRestrictionType.ONLY_LEFT_TURN
);
assertNotNull(adjustedTo);
assertEquals(w21, adjustedTo);
adjustedTo = selectToWayAfterSplit(
w1,
w21,
w22,
TurnRestrictionType.ONLY_RIGHT_TURN
);
assertNotNull(adjustedTo);
assertEquals(w22, adjustedTo);
adjustedTo = selectToWayAfterSplit(
w1,
w21,
w22,
TurnRestrictionType.NO_STRAIGHT_ON
);
assertNull(adjustedTo);
}
}