OsmAnd/diff_turn_lanes
Victor Shcherb 29e20eaede Fix builds
2014-10-03 16:48:49 +02:00

693 lines
No EOL
24 KiB
Text

diff --git a/OsmAnd-java/src/net/osmand/router/RouteResultPreparation.java b/OsmAnd-java/src/net/osmand/router/RouteResultPreparation.java
index 1fa0b3a..0ba0981 100644
--- a/OsmAnd-java/src/net/osmand/router/RouteResultPreparation.java
+++ b/OsmAnd-java/src/net/osmand/router/RouteResultPreparation.java
@@ -7,6 +7,7 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
+import java.util.HashSet;
import org.apache.commons.logging.Log;
@@ -388,6 +389,77 @@ public class RouteResultPreparation {
dist += result.get(i).getDistance();
}
}
+
+ for (int i = result.size() - 2; i >= 0; i--) {
+ RouteSegmentResult currentSegment = result.get(i);
+ RouteSegmentResult nextSegment = result.get(i + 1);
+
+ if (currentSegment.getTurnType() == null || currentSegment.getTurnType().getLanes() == null || nextSegment.getTurnType() == null || nextSegment.getTurnType().getLanes() == null) {
+ continue;
+ }
+
+ // Only allow slight turns to be merged.
+ if (currentSegment.getDistance() < 60 && nextSegment.getTurnType().getLanes().length <= currentSegment.getTurnType().getLanes().length
+ && (currentSegment.getTurnType().getValue() == TurnType.C
+ || currentSegment.getTurnType().getValue() == TurnType.TSLL
+ || currentSegment.getTurnType().getValue() == TurnType.TSLR
+ || currentSegment.getTurnType().getValue() == TurnType.KL
+ || currentSegment.getTurnType().getValue() == TurnType.KR)) {
+ mergeTurnLanes(leftside, currentSegment, nextSegment);
+ }
+ }
+ }
+
private static final int MAX_SPEAK_PRIORITY = 5;
@@ -439,6 +511,7 @@ public class RouteResultPreparation {
} else {
t = TurnType.valueOf(TurnType.TU, leftSide);
}
+ getLanesInfo(prev, t, leftSide);
} else if (mpi < -TURN_DEGREE_MIN) {
if (mpi > -60) {
t = TurnType.valueOf(TurnType.TSLR, leftSide);
@@ -449,6 +522,7 @@ public class RouteResultPreparation {
} else {
t = TurnType.valueOf(TurnType.TU, leftSide);
}
+ getLanesInfo(prev, t, leftSide);
} else {
t = attachKeepLeftInfoAndLanes(leftSide, prev, rr, t);
}
@@ -459,6 +533,82 @@ public class RouteResultPreparation {
return t;
}
private TurnType processRoundaboutTurn(List<RouteSegmentResult> result, int i, boolean leftSide, RouteSegmentResult prev,
RouteSegmentResult rr) {
@@ -491,21 +641,52 @@ public class RouteResultPreparation {
return t;
}
private TurnType attachKeepLeftInfoAndLanes(boolean leftSide, RouteSegmentResult prevSegm, RouteSegmentResult currentSegm, TurnType t) {
// keep left/right
int[] lanes = null;
boolean kl = false;
boolean kr = false;
+ List<OutboundRoad> outboundRoads = new ArrayList<OutboundRoad>();
List<RouteSegmentResult> attachedRoutes = currentSegm.getAttachedRoutes(currentSegm.getStartPointIndex());
- int ls = prevSegm.getObject().getLanes();
- if(ls >= 0 && prevSegm.getObject().getOneway() == 0) {
- ls = (ls + 1) / 2;
- }
- int left = 0;
- int right = 0;
boolean speak = false;
int speakPriority = Math.max(highwaySpeakPriority(prevSegm.getObject().getHighway()), highwaySpeakPriority(currentSegm.getObject().getHighway()));
+ boolean otherRoutesExist = false;
+ boolean rightTurnPossible = false;
+ boolean leftTurnPossible = false;
if (attachedRoutes != null) {
for (RouteSegmentResult attached : attachedRoutes) {
double ex = MapUtils.degreesDiff(attached.getBearingBegin(), currentSegm.getBearingBegin());
@@ -513,73 +694,439 @@ public class RouteResultPreparation {
int rsSpeakPriority = highwaySpeakPriority(attached.getObject().getHighway());
if (rsSpeakPriority != MAX_SPEAK_PRIORITY || speakPriority == MAX_SPEAK_PRIORITY) {
if ((ex < TURN_DEGREE_MIN || mpi < TURN_DEGREE_MIN) && ex >= 0) {
+ // It's ex >= 0 so that turns remaining on the current road are counted as well
kl = true;
int lns = attached.getObject().getLanes();
}
- if (lns > 0) {
- right += lns;
+ if (lns <= 0) {
+ lns = 1;
}
- speak = speak || rsSpeakPriority <= speakPriority;
+ outboundRoads.add(new OutboundRoad(ex, lns, attached));
+ speak |= rsSpeakPriority <= speakPriority;
} else if ((ex > -TURN_DEGREE_MIN || mpi < TURN_DEGREE_MIN) && ex <= 0) {
}
- if (lns > 0) {
- left += lns;
+ if (lns <= 0) {
+ lns = 1;
}
- speak = speak || rsSpeakPriority <= speakPriority;
+ outboundRoads.add(new OutboundRoad(ex, lns, attached));
+ speak |= rsSpeakPriority <= speakPriority;
+ }
+ }
+ if (mpi > TURN_DEGREE_MIN) {
+ otherRoutesExist = true;
+ if (ex > TURN_DEGREE_MIN) {
+ // A right turn is allowed at this intersection.
+ rightTurnPossible = true;
+ } else if (ex < TURN_DEGREE_MIN) {
+ // A left turn is allowed at this intersection.
+ leftTurnPossible = true;
}
}
}
}
- if(kr && left == 0) {
- left = 1;
- } else if(kl && right == 0) {
- right = 1;
- }
+
+ // attachedRoutes covers all allowed outbound routes at that point except currentSegm.
if (current <= 0) {
current = 1;
}
-// if(ls >= 0 /*&& current + left + right >= ls*/){
- lanes = new int[current + left + right];
- ls = current + left + right;
- for(int it=0; it< ls; it++) {
- if(it < left || it >= left + current) {
- lanes[it] = 0;
- } else {
- lanes[it] = 1;
+ outboundRoads.add(new OutboundRoad(0, current, currentSegm));
+
+ Collections.sort(outboundRoads);
+
+ int totalOutboundLanes = 0;
+ for (OutboundRoad road : outboundRoads) {
+ totalOutboundLanes += road.getLanes();
+ }
+
+ lanes = new int[totalOutboundLanes];
+ int i = 0;
+ int left = 0;
+ int right = 0;
+ for (OutboundRoad road : outboundRoads) {
+ for (int j = 0; i < lanes.length && j < road.getLanes(); i++, j++) {
+ lanes[i] = road.getAngle() == 0 ? TurnType.BIT_LANE_ALLOWED : 0;
+ if (road.getAngle() < 0) {
+ left++;
+ } else if (road.getAngle() > 0) {
+ right++;
}
}
- // sometimes links are
- if ((current <= left + right) && (left > 1 || right > 1)) {
- speak = true;
- }
-// }
+ }
+ // sometimes links are
+ if ((current <= left + right) && (left > 1 || right > 1)) {
+ speak = true;
+ }
+
+ double deviation = Math.abs(MapUtils.degreesDiff(prevSegm.getBearingEnd(), currentSegm.getBearingBegin()));
+ boolean makeSlightTurn = deviation > 5 && (!isMotorway(prevSegm) || !isMotorway(currentSegm));
+ if (kl && kr) {
+ t = TurnType.valueOf(TurnType.C, leftSide);
- double devation = Math.abs(MapUtils.degreesDiff(prevSegm.getBearingEnd(), currentSegm.getBearingBegin()));
- boolean makeSlightTurn = devation > 5 && (!isMotorway(prevSegm) || !isMotorway(currentSegm));
- if (kl) {
+ t.setSkipToSpeak(!speak);
+ } else if (kl) {
t = TurnType.valueOf(makeSlightTurn ? TurnType.TSLL : TurnType.KL, leftSide);
t.setSkipToSpeak(!speak);
- }
- if (kr) {
+ } else if (kr) {
t = TurnType.valueOf(makeSlightTurn ? TurnType.TSLR : TurnType.KR, leftSide);
t.setSkipToSpeak(!speak);
+ } else {
+ if (!otherRoutesExist) {
+ return null;
+ }
+ // Most likely, we're going straight at a 90-degree intersection with another road
+ t = TurnType.valueOf(TurnType.C, leftSide);
+ t.setSkipToSpeak(true); // Always skip the talking when going straight
+
+ int prevLanes = prevSegm.getObject().getLanes();
+ if(prevSegm.getObject().getOneway() == 0) {
+ if (prevSegm.isForwardDirection()
+ && prevSegm.getObject().getValue("lanes:forward") != null) {
+ prevLanes = Integer.parseInt(prevSegm.getObject().getValue("lanes:forward"));
+ } else if (!prevSegm.isForwardDirection()
+ && prevSegm.getObject().getValue("lanes:backward") != null) {
+ prevLanes = Integer.parseInt(prevSegm.getObject().getValue("lanes:backward"));
+ } else {
+ prevLanes = (prevLanes + 1) / 2;
+ }
+ }
+ if (prevLanes <= 0) {
+ return null;
+ }
+ lanes = new int[prevLanes];
+
+ String turnLanes;
+ if (prevSegm.getObject().getOneway() == 0) {
+ if (prevSegm.isForwardDirection()) {
+ turnLanes = prevSegm.getObject().getValue("turn:lanes:forward");
+ } else {
+ turnLanes = prevSegm.getObject().getValue("turn:lanes:backward");
+ }
+ } else {
+ turnLanes = prevSegm.getObject().getValue("turn:lanes");
+ }
+
+ if (turnLanes == null) {
+ return null;
+ }
+
+ String[] splitLaneOptions = turnLanes.split("\\|", -1);
+ if (splitLaneOptions.length != lanes.length) {
+ // Error in data or missing data
+ return null;
+ }
+
+ t.setLanes(lanes);
+
+ assignTurns(splitLaneOptions, t);
+
+ // In some cases (at least in the US), the rightmost lane might not have a right turn indicator, but is allowed. This section adds in that indicator. The same applies for where leftSide is true.
+ if (leftSide) {
+ if (leftTurnPossible
+ && !t.isTurnAllowed(0, TurnType.Turn.LEFT)
+ && !t.isTurnAllowed(0, TurnType.Turn.SLIGHT_LEFT)
+ && !t.isTurnAllowed(0, TurnType.Turn.SHARP_LEFT)) {
+ if (t.getPrimaryTurn(0) != TurnType.Turn.UNKNOWN && t.getSecondaryTurn(0) == TurnType.Turn.UNKNOWN) {
+ // This was just to make sure that there's no bad data and that there's an empty slot.
+ t.setSecondaryTurn(0, TurnType.Turn.LEFT);
+ }
+ }
+ } else {
+ if (rightTurnPossible
+ && !t.isTurnAllowed(lanes.length - 1, TurnType.Turn.RIGHT)
+ && !t.isTurnAllowed(lanes.length - 1, TurnType.Turn.SLIGHT_RIGHT)
+ && !t.isTurnAllowed(lanes.length - 1, TurnType.Turn.SHARP_RIGHT)) {
+ if (t.getPrimaryTurn(lanes.length - 1) != TurnType.Turn.UNKNOWN && t.getSecondaryTurn(lanes.length - 1) == TurnType.Turn.UNKNOWN) {
+ // This was just to make sure that there's no bad data and that there's an empty slot.
+ t.setSecondaryTurn(lanes.length - 1, TurnType.Turn.RIGHT);
+ }
+ }
+ }
+
+ // Manually set the allowed lanes, in case the turn gets merged.
+ for (int j = 0; j < lanes.length; j++) {
+ if (t.isTurnAllowed(j, TurnType.Turn.STRAIGHT)) {
+ t.getLanes()[j] |= TurnType.BIT_LANE_ALLOWED;
+ }
+ }
+
+ return t;
}
+
if (t != null && lanes != null) {
t.setLanes(lanes);
+
+ // This code block, in the cases where turn:lanes is not available, replaces the straight arrows with slight-left or slight-right arrows. It compares the number of lanes going straight to the number of lanes turning to determine which symbol to display where.
+ boolean crossedLeftSide = false;
+ for (int j = 0; j < t.getLanes().length; j++) {
+ if (!crossedLeftSide && (t.getLanes()[j] & TurnType.BIT_LANE_ALLOWED) == 0) {
+ if (current > left + right || right != 0) {
+ t.setPrimaryTurn(j, TurnType.Turn.SLIGHT_LEFT);
+ } else {
+ t.setPrimaryTurn(j, TurnType.Turn.STRAIGHT);
+ }
+ } else {
+ crossedLeftSide = true;
+ if ((t.getLanes()[j] & TurnType.BIT_LANE_ALLOWED) == TurnType.BIT_LANE_ALLOWED) {
+ if (current > left + right || (left != 0 && right != 0)) {
+ t.setPrimaryTurn(j, TurnType.Turn.STRAIGHT);
+ } else if (left != 0) {
+ t.setPrimaryTurn(j, TurnType.Turn.SLIGHT_RIGHT);
+ } else if (right != 0) {
+ t.setPrimaryTurn(j, TurnType.Turn.SLIGHT_LEFT);
+ }
+ } else {
+ if (current > left + right || left != 0) {
+ t.setPrimaryTurn(j, TurnType.Turn.SLIGHT_RIGHT);
+ } else {
+ t.setPrimaryTurn(j, TurnType.Turn.STRAIGHT);
+ }
+ }
+ }
+ }
+
+ t = attachTurnLanesData(leftSide, prevSegm, t);
}
+
return t;
}
-
===============================================================================
+ private void mergeTurnLanes(boolean leftSide, RouteSegmentResult currentSegment, RouteSegmentResult nextSegment) {
+ if (currentSegment.getTurnType().isTurnLanesRendering() && nextSegment.getTurnType().isTurnLanesRendering()) {
+ int[] lanes = new int[currentSegment.getTurnType().getLanes().length];
+ for (int i = 0; i < lanes.length; i++) {
+ lanes[i] = currentSegment.getTurnType().getLanes()[i] & ~TurnType.BIT_LANE_ALLOWED;
+ }
+ int matchingIndex = 0;
+ int maxMatchedLanes = 0;
+ for (int i = 0; i < lanes.length; i++) {
+ int matchedLanes = 0;
+ for (int j = 0; j < nextSegment.getTurnType().getLanes().length - i; j++) {
+ if (nextSegment.getTurnType().getPrimaryTurn(j) == currentSegment.getTurnType().getPrimaryTurn(i + j)) {
+ matchedLanes++;
+ } else {
+ break;
+ }
+ }
+ if (matchedLanes > maxMatchedLanes) {
+ matchingIndex = i;
+ maxMatchedLanes = matchedLanes;
+ }
+ }
+ if (maxMatchedLanes <= 1) {
+ return;
+ }
+ for (int i = matchingIndex; i - matchingIndex < nextSegment.getTurnType().getLanes().length; i++) {
+ lanes[i] |= nextSegment.getTurnType().getLanes()[i - matchingIndex] & TurnType.BIT_LANE_ALLOWED;
+ }
+ currentSegment.getTurnType().setLanes(lanes);
+ currentSegment.setTurnType(inferTurnFromLanes(currentSegment.getTurnType(), leftSide));
+ } else if (!currentSegment.getTurnType().isTurnLanesRendering() && !nextSegment.getTurnType().isTurnLanesRendering()) {
+ int[] lanes = new int[currentSegment.getTurnType().getLanes().length];
+ int matchingIndex = 0;
+ int expectedLanes = 0;
+ for (int i = 0; i < lanes.length; i++) {
+ lanes[i] = currentSegment.getTurnType().getLanes()[i] & ~TurnType.BIT_LANE_ALLOWED;
+ if (currentSegment.getTurnType().getPrimaryTurn(i) == TurnType.Turn.SLIGHT_LEFT) {
+ matchingIndex++;
+ } else if (currentSegment.getTurnType().getPrimaryTurn(i) == TurnType.Turn.STRAIGHT) {
+ expectedLanes++;
+ }
+ }
+ if (nextSegment.getTurnType().getLanes().length != expectedLanes) {
+ return;
+ }
+ for (int i = matchingIndex; i - matchingIndex < nextSegment.getTurnType().getLanes().length; i++) {
+ lanes[i] = nextSegment.getTurnType().getLanes()[i - matchingIndex];
+ }
+ currentSegment.getTurnType().setLanes(lanes);
+ }
}
+ private class OutboundRoad implements Comparable<OutboundRoad>{
+ private final double angle;
+ private final int lanes;
+ private final RouteSegmentResult road;
+
+ public OutboundRoad(double angle, int lanes, RouteSegmentResult road) {
+ this.angle = angle;
+ this.lanes = lanes;
+ this.road = road;
+ }
+
+ public double getAngle() {
+ return angle;
+ }
+
+ public int getLanes() {
+ return lanes;
+ }
+
+ public RouteSegmentResult getRoad() {
+ return road;
+ }
+
+ public int compareTo(OutboundRoad other) {
+ if (other.angle - this.angle < 0) {
+ return 1;
+ } else if (other.angle - this.angle > 0) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ }
+ private void getLanesInfo(RouteSegmentResult prevSegm, TurnType t, boolean leftSide) {
+ int lanes = prevSegm.getObject().getLanes();
+ if(prevSegm.getObject().getOneway() == 0) {
+ if (prevSegm.isForwardDirection()
+ && prevSegm.getObject().getValue("lanes:forward") != null) {
+ lanes = Integer.parseInt(prevSegm.getObject().getValue("lanes:forward"));
+ } else if (!prevSegm.isForwardDirection()
+ && prevSegm.getObject().getValue("lanes:backward") != null) {
+ lanes = Integer.parseInt(prevSegm.getObject().getValue("lanes:backward"));
+ } else {
+ lanes = (lanes + 1) / 2;
+ }
+ }
+ if (lanes <= 0) {
+ return;
+ }
+
+ String turnLanes;
+ if (prevSegm.getObject().getOneway() == 0) {
+ if (prevSegm.isForwardDirection()) {
+ turnLanes = prevSegm.getObject().getValue("turn:lanes:forward");
+ } else {
+ turnLanes = prevSegm.getObject().getValue("turn:lanes:backward");
+ }
+ } else {
+ turnLanes = prevSegm.getObject().getValue("turn:lanes");
+ }
+
+ if (turnLanes == null) {
+ return;
+ }
+
+ String[] splitLaneOptions = turnLanes.split("\\|", -1);
+ if (splitLaneOptions.length != lanes) {
+ // Error in data or missing data, go to old behavior
+ return;
+ }
+
+ int[] lanesArray = new int[lanes];
+
+ t.setLanes(lanesArray);
+
+ assignTurns(splitLaneOptions, t);
+
+ // In some cases (at least in the US), the rightmost lane might not have a right turn indicator, but is allowed. This section adds in that indicator. The same applies for where leftSide is true.
+ if (leftSide) {
+ if (t.getValue() == TurnType.TL
+ && !t.isTurnAllowed(0, TurnType.Turn.LEFT)
+ && !t.isTurnAllowed(0, TurnType.Turn.SLIGHT_LEFT)
+ && !t.isTurnAllowed(0, TurnType.Turn.SHARP_LEFT)) {
+ if (t.getPrimaryTurn(0) != TurnType.Turn.UNKNOWN && t.getSecondaryTurn(0) == TurnType.Turn.UNKNOWN) {
+ // This was just to make sure that there's no bad data and that there's an empty slot.
+ t.setSecondaryTurn(0, TurnType.Turn.LEFT);
+ lanesArray[0] |= TurnType.BIT_LANE_ALLOWED;
+ }
+ }
+ } else {
+ if (t.getValue() == TurnType.TR
+ && !t.isTurnAllowed(lanesArray.length - 1, TurnType.Turn.RIGHT)
+ && !t.isTurnAllowed(lanesArray.length - 1, TurnType.Turn.SLIGHT_RIGHT)
+ && !t.isTurnAllowed(lanesArray.length - 1, TurnType.Turn.SHARP_RIGHT)) {
+ if (t.getPrimaryTurn(lanesArray.length - 1) != TurnType.Turn.UNKNOWN && t.getSecondaryTurn(lanesArray.length - 1) == TurnType.Turn.UNKNOWN) {
+ // This was just to make sure that there's no bad data and that there's an empty slot.
+ t.setSecondaryTurn(lanesArray.length - 1, TurnType.Turn.RIGHT);
+ lanesArray[lanesArray.length - 1] |= TurnType.BIT_LANE_ALLOWED;
+ }
+ }
+ }
+
+ // Manually set the allowed lanes.
+ for (int i = 0; i < lanesArray.length; i++) {
+ if (t.getPrimaryTurn(i).getValue() == t.getValue() || t.getSecondaryTurn(i).getValue() == t.getValue()) {
+ lanesArray[i] |= TurnType.BIT_LANE_ALLOWED;
+ }
+ }
+ }
+
+ private TurnType attachTurnLanesData(boolean leftSide, RouteSegmentResult prevSegm, TurnType t) {
+ int lanes = prevSegm.getObject().getLanes();
+ String turnLanes;
+ if (prevSegm.getObject().getOneway() == 0) {
+ if (prevSegm.isForwardDirection()) {
+ turnLanes = prevSegm.getObject().getValue("turn:lanes:forward");
+ } else {
+ turnLanes = prevSegm.getObject().getValue("turn:lanes:backward");
+ }
+ } else {
+ turnLanes = prevSegm.getObject().getValue("turn:lanes");
+ }
+
+ if (turnLanes == null) {
+ return t;
+ }
+
+ String[] splitLaneOptions = turnLanes.split("\\|", -1);
+ if (splitLaneOptions.length != lanes) {
+ // Error in data or missing data, go to old behavior
+ return t;
+ }
+
+ if (t.getLanes().length != lanes) {
+ // The turn:lanes don't easily match up to the target road.
+
+ List<Integer> sourceLanes = new ArrayList<Integer>();
+
+ int outgoingLanesIndex = 0;
+ int sourceLanesIndex = 0;
+
+ while (outgoingLanesIndex < t.getLanes().length && sourceLanesIndex < lanes) {
+ if (splitLaneOptions[sourceLanesIndex].contains(";")) {
+ int options = countOccurrences(splitLaneOptions[sourceLanesIndex], ';');
+ if (options == 1) {
+ if (outgoingLanesIndex + 1 >= t.getLanes().length) {
+ // Likely an error in data
+ return t;
+ }
+ int usability = t.getLanes()[outgoingLanesIndex] | t.getLanes()[outgoingLanesIndex + 1];
+ sourceLanes.add(usability);
+ outgoingLanesIndex += 2;
+ sourceLanesIndex++;
+ } else {
+ // Not supported
+ return t;
+ }
+ } else {
+ sourceLanes.add(t.getLanes()[outgoingLanesIndex]);
+ outgoingLanesIndex++;
+ sourceLanesIndex++;
+ }
+ }
+
+ int[] newLanes = new int[sourceLanes.size()];
+
+ for (int i = 0; i < sourceLanes.size(); i++) {
+ newLanes[i] = sourceLanes.get(i);
+ }
+
+ t.setLanes(newLanes);
+ }
+
+ assignTurns(splitLaneOptions, t);
+
+ t = inferTurnFromLanes(t, leftSide);
+
+ return t;
+ }
+
+ private void assignTurns(String[] splitLaneOptions, TurnType t) {
+ for (int i = 0; i < splitLaneOptions.length; i++) {
+ String[] laneOptions = splitLaneOptions[i].split(";");
+
+ boolean primaryTurnSet = false;
+
+ for (int j = 0; j < laneOptions.length; j++) {
+ if (laneOptions[j].equals("none") || laneOptions[j].equals("through")) {
+ TurnType.Turn turn = TurnType.Turn.STRAIGHT;
+ if (!primaryTurnSet) {
+ t.setPrimaryTurn(i, turn);
+ primaryTurnSet = true;
+ } else {
+ t.setSecondaryTurn(i, turn);
+ continue;
+ }
+ } else if (laneOptions[j].equals("slight_right")) {
+ TurnType.Turn turn = TurnType.Turn.SLIGHT_RIGHT;
+ if (!primaryTurnSet) {
+ t.setPrimaryTurn(i, turn);
+ primaryTurnSet = true;
+ } else {
+ t.setSecondaryTurn(i, turn);
+ continue;
+ }
+ } else if (laneOptions[j].equals("slight_left")) {
+ TurnType.Turn turn = TurnType.Turn.SLIGHT_LEFT;
+ if (!primaryTurnSet) {
+ t.setPrimaryTurn(i, turn);
+ primaryTurnSet = true;
+ } else {
+ t.setSecondaryTurn(i, turn);
+ continue;
+ }
+ } else if (laneOptions[j].equals("right")) {
+ TurnType.Turn turn = TurnType.Turn.RIGHT;
+ if (!primaryTurnSet) {
+ t.setPrimaryTurn(i, turn);
+ primaryTurnSet = true;
+ } else {
+ t.setSecondaryTurn(i, turn);
+ continue;
+ }
+ } else if (laneOptions[j].equals("left")) {
+ TurnType.Turn turn = TurnType.Turn.LEFT;
+ if (!primaryTurnSet) {
+ t.setPrimaryTurn(i, turn);
+ primaryTurnSet = true;
+ } else {
+ t.setSecondaryTurn(i, turn);
+ continue;
+ }
+ } else if (laneOptions[j].equals("sharp_right")) {
+ TurnType.Turn turn = TurnType.Turn.SHARP_RIGHT;
+ if (!primaryTurnSet) {
+ t.setPrimaryTurn(i, turn);
+ primaryTurnSet = true;
+ } else {
+ t.setSecondaryTurn(i, turn);
+ continue;
+ }
+ } else if (laneOptions[j].equals("sharp_left")) {
+ TurnType.Turn turn = TurnType.Turn.SHARP_LEFT;
+ if (!primaryTurnSet) {
+ t.setPrimaryTurn(i, turn);
+ primaryTurnSet = true;
+ } else {
+ t.setSecondaryTurn(i, turn);
+ continue;
+ }
+ } else if (laneOptions[j].equals("reverse")) {
+ TurnType.Turn turn = TurnType.Turn.UTURN;
+ if (!primaryTurnSet) {
+ t.setPrimaryTurn(i, turn);
+ primaryTurnSet = true;
+ } else {
+ t.setSecondaryTurn(i, turn);
+ continue;
+ }
+ }
+ }
+ }
+
+ t.setTurnLanesRendering(true);
+ }
+
+ private TurnType inferTurnFromLanes(TurnType t, boolean leftSide) {
+ List<TurnType.Turn> possibleTurns = new ArrayList<TurnType.Turn>();
+ boolean filled = false;
+ for (int i = 0; i < t.getLanes().length; i++) {
+ if ((t.getLanes()[i] & TurnType.BIT_LANE_ALLOWED) == TurnType.BIT_LANE_ALLOWED) {
+ if (!filled) {
+ filled = true;
+ possibleTurns.add(t.getPrimaryTurn(i));
+ if (t.getSecondaryTurn(i) != TurnType.Turn.UNKNOWN) {
+ possibleTurns.add(t.getSecondaryTurn(i));
+ }
+ } else {
+ List<TurnType.Turn> laneTurns = new ArrayList<TurnType.Turn>();
+ laneTurns.add(t.getPrimaryTurn(i));
+ laneTurns.add(t.getSecondaryTurn(i));
+ possibleTurns.retainAll(laneTurns);
+ }
+ }
+ }
+
+ if (possibleTurns.size() > 1) {
+ for (int i = 0; i < t.getLanes().length; i++) {
+ if ((t.getLanes()[i] & TurnType.BIT_LANE_ALLOWED) == 0 && !possibleTurns.isEmpty()) {
+ List<TurnType.Turn> notLaneTurns = new ArrayList<TurnType.Turn>();
+ notLaneTurns.add(t.getPrimaryTurn(i));
+ notLaneTurns.add(t.getSecondaryTurn(i));
+ possibleTurns.removeAll(notLaneTurns);
+ }
+ }
+ }
+
+ // removing duplicates
+ if (new HashSet<TurnType.Turn>(possibleTurns).size() == 1) {
+ TurnType derivedTurnType = TurnType.valueOf(possibleTurns.get(0).getValue(), false);
+ derivedTurnType.setLanes(t.getLanes());
+ derivedTurnType.setSkipToSpeak(t.isSkipToSpeak());
+ derivedTurnType.setTurnLanesRendering(t.isTurnLanesRendering());
+ t = derivedTurnType;
+ }
+
+ return t;
+ }
+
+ private int countOccurrences(String haystack, char needle) {
+ int count = 0;
+ for (int i = 0; i < haystack.length(); i++) {
+ if (haystack.charAt(i) == needle) {
+ count++;
+ }
+ }
+ return count;
+ }
+