Fix issue with turn type

This commit is contained in:
Victor Shcherb 2014-11-23 19:45:31 +01:00
parent ddf6d63704
commit a9b1031ce9
2 changed files with 165 additions and 184 deletions

View file

@ -1,16 +1,16 @@
package net.osmand.router;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.set.hash.TIntHashSet;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
import net.osmand.PlatformUtil;
import net.osmand.binary.BinaryMapIndexReader;
import net.osmand.binary.BinaryMapRouteReaderAdapter.RouteTypeRule;
@ -21,6 +21,8 @@ import net.osmand.router.BinaryRoutePlanner.RouteSegment;
import net.osmand.router.RoutePlannerFrontEnd.RouteCalculationMode;
import net.osmand.util.MapUtils;
import org.apache.commons.logging.Log;
public class RouteResultPreparation {
public static boolean PRINT_TO_CONSOLE_ROUTE_INFORMATION_TO_TEST = false;
@ -440,7 +442,10 @@ public class RouteResultPreparation {
} else {
t = TurnType.valueOf(TurnType.TU, leftSide);
}
assignLanesInfo(prev, t, leftSide);
int[] lanes = getTurnLanesInfo(prev, t.getValue(), leftSide, true);
if(lanes != null) {
t.setLanes(lanes);
}
} else if (mpi < -TURN_DEGREE_MIN) {
if (mpi > -60) {
t = TurnType.valueOf(TurnType.TSLR, leftSide);
@ -451,7 +456,10 @@ public class RouteResultPreparation {
} else {
t = TurnType.valueOf(TurnType.TU, leftSide);
}
assignLanesInfo(prev, t, leftSide);
int[] lanes = getTurnLanesInfo(prev, t.getValue(), leftSide, false);
if(lanes != null) {
t.setLanes(lanes);
}
} else {
t = attachKeepLeftInfoAndLanes(leftSide, prev, rr, t);
}
@ -462,62 +470,52 @@ public class RouteResultPreparation {
return t;
}
private void assignLanesInfo(RouteSegmentResult prevSegm, TurnType t, boolean leftSide) {
int lanes = prevSegm.getObject().getLanes();
if (prevSegm.getObject().getOneway() == 0) {
lanes = countLanes(prevSegm, lanes);
}
if (lanes <= 0) {
return;
}
private int[] getTurnLanesInfo(RouteSegmentResult prevSegm, int mainTurnType, boolean leftSide,
boolean leftTurn) {
String turnLanes = getTurnLanesString(prevSegm);
if (turnLanes == null) {
return;
return null;
}
String[] splitLaneOptions = turnLanes.split("\\|", -1);
if (splitLaneOptions.length != lanes) {
if (splitLaneOptions.length != countLanesMinOne(prevSegm)) {
// Error in data or missing data
return;
return null;
}
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 indicated as per turn:lanes, but is allowed and being used here. This section adds in that indicator. The same applies for where leftSide is true.
if (leftSide) {
if (t.getValue() == TurnType.TL
&& TurnType.getPrimaryTurn(lanesArray[0]) != TurnType.TL
&& TurnType.getPrimaryTurn(lanesArray[0]) != TurnType.TSLL
&& TurnType.getPrimaryTurn(lanesArray[0]) != TurnType.TSHL) {
if (TurnType.getPrimaryTurn(lanesArray[0]) != 0) {
int[] lanesArray = calculateRawTurnLanes(splitLaneOptions, mainTurnType);
// Manually set the allowed lanes.
boolean isSet = setAllowedLanes(mainTurnType, lanesArray);
if(!isSet && lanesArray.length > 0) {
// In some cases (at least in the US), the rightmost lane might not have a right turn indicated as per turn:lanes,
// but is allowed and being used here. This section adds in that indicator. The same applies for where leftSide is true.
int ind = leftTurn? 0 : lanesArray.length - 1;
final int tt = TurnType.getPrimaryTurn(lanesArray[ind]);
if (leftTurn) {
if (!TurnType.isLeftTurn(tt)) {
// This was just to make sure that there's no bad data.
t.setSecondaryTurn(0, TurnType.getPrimaryTurn(lanesArray[0]));
t.setPrimaryTurn(0, TurnType.TL);
}
TurnType.setSecondaryTurn(lanesArray, ind, tt);
TurnType.setPrimaryTurn(lanesArray, ind, TurnType.TL);
}
} else {
int lastIndex = lanesArray.length - 1;
if (t.getValue() == TurnType.TR
&& TurnType.getPrimaryTurn(lanesArray[lastIndex]) != TurnType.TR
&& TurnType.getPrimaryTurn(lanesArray[lastIndex]) != TurnType.TSLR
&& TurnType.getPrimaryTurn(lanesArray[lastIndex]) != TurnType.TSHR) {
if (TurnType.getPrimaryTurn(lanesArray[lastIndex]) != 0) {
if (!TurnType.isRightTurn(tt)) {
// This was just to make sure that there's no bad data.
t.setSecondaryTurn(lastIndex, TurnType.getPrimaryTurn(lanesArray[lastIndex]));
t.setPrimaryTurn(lastIndex, TurnType.TR);
TurnType.setSecondaryTurn(lanesArray, ind, tt);
TurnType.setPrimaryTurn(lanesArray, ind, TurnType.TR);
}
}
setAllowedLanes(lanesArray[ind], lanesArray);
}
return lanesArray;
}
// Manually set the allowed lanes.
protected boolean setAllowedLanes(int mainTurnType, int[] lanesArray) {
boolean turnSet = false;
for (int i = 0; i < lanesArray.length; i++) {
if (TurnType.getPrimaryTurn(lanesArray[i]) == t.getValue()) {
if (TurnType.getPrimaryTurn(lanesArray[i]) == mainTurnType) {
lanesArray[i] |= 1;
turnSet = true;
}
}
return turnSet;
}
private TurnType processRoundaboutTurn(List<RouteSegmentResult> result, int i, boolean leftSide, RouteSegmentResult prev,
@ -554,7 +552,6 @@ public class RouteResultPreparation {
private TurnType attachKeepLeftInfoAndLanes(boolean leftSide, RouteSegmentResult prevSegm, RouteSegmentResult currentSegm, TurnType t) {
// keep left/right
int[] lanes = null;
boolean kl = false;
boolean kr = false;
List<RouteSegmentResult> attachedRoutes = currentSegm.getAttachedRoutes(currentSegm.getStartPointIndex());
@ -574,27 +571,11 @@ public class RouteResultPreparation {
if (rsSpeakPriority != MAX_SPEAK_PRIORITY || speakPriority == MAX_SPEAK_PRIORITY) {
if ((ex < TURN_DEGREE_MIN || mpi < TURN_DEGREE_MIN) && ex >= 0) {
kl = true;
int lns = attached.getObject().getLanes();
if(attached.getObject().getOneway() == 0) {
lns = countLanes(attached, lns);
}
if (lns <= 0) {
right += 1;
} else {
right += lns;
}
right += countLanesMinOne(attached);
speak = speak || rsSpeakPriority <= speakPriority;
} else if ((ex > -TURN_DEGREE_MIN || mpi < TURN_DEGREE_MIN) && ex <= 0) {
kr = true;
int lns = attached.getObject().getLanes();
if(attached.getObject().getOneway() == 0) {
lns = countLanes(attached, lns);
}
if (lns <= 0) {
left += 1;
} else {
left += lns;
}
left += countLanesMinOne(attached);
speak = speak || rsSpeakPriority <= speakPriority;
}
}
@ -605,16 +586,8 @@ public class RouteResultPreparation {
} else if(kl && right == 0) {
right = 1;
}
int current = currentSegm.getObject().getLanes();
// attachedRoutes covers all allowed outbound routes at that point except currentSegm.
if (currentSegm.getObject().getOneway() == 0) {
current = countLanes(currentSegm, current);
}
if (current <= 0) {
current = 1;
}
// if(ls >= 0 /*&& current + left + right >= ls*/){
lanes = new int[current + left + right];
int current = countLanesMinOne(currentSegm);
int[] lanes = new int[current + left + right];
ls = current + left + right;
for (int it = 0; it < ls; it++) {
if (it < left || it >= left + current) {
@ -627,7 +600,6 @@ public class RouteResultPreparation {
if ((current <= left + right) && (left > 1 || right > 1)) {
speak = true;
}
// }
double devation = Math.abs(MapUtils.degreesDiff(prevSegm.getBearingEnd(), currentSegm.getBearingBegin()));
boolean makeSlightTurn = devation > 5 && (!isMotorway(prevSegm) || !isMotorway(currentSegm));
@ -640,14 +612,42 @@ public class RouteResultPreparation {
t.setSkipToSpeak(!speak);
}
if (t != null && lanes != null) {
t.setLanes(lanes);
int[] calcLanes = attachTurnLanesData(prevSegm, lanes);
if(calcLanes != lanes) {
int tp = inferTurnFromLanes(calcLanes);
if (tp != 0 && tp != t.getValue()) {
TurnType derivedTurnType = TurnType.valueOf(tp, leftSide);
derivedTurnType.setLanes(calcLanes);
derivedTurnType.setSkipToSpeak(t.isSkipToSpeak());
// Because only the primary turn is displayed, if the turn to be taken is currently set as the
// secondary turn, then that needs to be switched around.
for (int i = 0; i < calcLanes.length; i++) {
if (TurnType.getSecondaryTurn(calcLanes[i]) == tp) {
derivedTurnType.setSecondaryTurn(i, TurnType.getPrimaryTurn(calcLanes[i]));
derivedTurnType.setPrimaryTurn(i, tp);
}
}
}
}
t.setLanes(calcLanes);
t = attachTurnLanesData(leftSide, prevSegm, t);
}
return t;
}
protected int countLanes(RouteSegmentResult attached, int lns) {
protected int countLanesMinOne(RouteSegmentResult attached) {
final boolean oneway = attached.getObject().getOneway() != 0;
int lns = attached.getObject().getLanes();
if(lns == 0) {
String tls = getTurnLanesString(attached);
if(tls != null) {
lns = countOccurrences(tls, '|');
}
}
if (oneway) {
return Math.max(1, lns);
}
try {
if (attached.isForwardDirection() && attached.getObject().getValue("lanes:forward") != null) {
return Integer.parseInt(attached.getObject().getValue("lanes:forward"));
@ -657,7 +657,7 @@ public class RouteResultPreparation {
} catch (NumberFormatException e) {
e.printStackTrace();
}
return (lns + 1) / 2;
return Math.max(1, (lns + 1) / 2);
}
protected String getTurnLanesString(RouteSegmentResult segment) {
@ -672,64 +672,44 @@ public class RouteResultPreparation {
}
}
private TurnType attachTurnLanesData(boolean leftSide, RouteSegmentResult prevSegm, TurnType t) {
int lanes = prevSegm.getObject().getLanes();
private int[] attachTurnLanesData(RouteSegmentResult prevSegm, int[] outgoingCalcLanes) {
String turnLanes = getTurnLanesString(prevSegm);
if (turnLanes == null) {
return t;
return outgoingCalcLanes;
}
String[] splitLaneOptions = turnLanes.split("\\|", -1);
if (splitLaneOptions.length != lanes) {
if (splitLaneOptions.length != countLanesMinOne(prevSegm)) {
// Error in data or missing data
return t;
return outgoingCalcLanes;
}
int[] usableLanes = mergeOutgoingLanesUsingTurnLanes(outgoingCalcLanes, splitLaneOptions);
int[] rawLanes = calculateRawTurnLanes(splitLaneOptions, 0);
for(int k = 0 ; k < splitLaneOptions.length; k++) {
if((usableLanes[k] & 1) != 0) {
rawLanes[k] |= 1;
}
}
return rawLanes;
}
if (t.getLanes().length != lanes) {
protected int[] mergeOutgoingLanesUsingTurnLanes(int[] outgoingCalcLanes, String[] splitLaneOptions) {
int[] usableLanes = outgoingCalcLanes;
if (outgoingCalcLanes.length != splitLaneOptions.length) {
usableLanes = new int[splitLaneOptions.length];
// 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(";")) {
// Two or more allowed turns for this lane
while (outgoingLanesIndex < outgoingCalcLanes.length &&
sourceLanesIndex < splitLaneOptions.length) {
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 {
// Only one allowed turn; behave normally
sourceLanes.add(t.getLanes()[outgoingLanesIndex]);
for (int k = 0; k <= options && outgoingLanesIndex < outgoingCalcLanes.length; k++) {
usableLanes[sourceLanesIndex] |= outgoingCalcLanes[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;
return usableLanes;
}
private int countOccurrences(String haystack, char needle) {
@ -742,7 +722,8 @@ public class RouteResultPreparation {
return count;
}
private void assignTurns(String[] splitLaneOptions, TurnType t) {
private int[] calculateRawTurnLanes(String[] splitLaneOptions, int calcTurnType) {
int[] lanes = new int[splitLaneOptions.length];
for (int i = 0; i < splitLaneOptions.length; i++) {
String[] laneOptions = splitLaneOptions[i].split(";");
@ -769,80 +750,64 @@ public class RouteResultPreparation {
continue;
}
if (TurnType.getPrimaryTurn(t.getLanes()[i]) == 0) {
t.setPrimaryTurn(i, turn);
if (TurnType.getPrimaryTurn(lanes[i]) == 0) {
TurnType.setPrimaryTurn(lanes, i, turn);
} else {
if (turn == t.getValue()) {
t.setSecondaryTurn(i, TurnType.getPrimaryTurn(t.getLanes()[i]));
t.setPrimaryTurn(i, turn);
if (turn == calcTurnType) {
TurnType.setSecondaryTurn(lanes, i, TurnType.getPrimaryTurn(lanes[i]));
TurnType.setPrimaryTurn(lanes, i, turn);
} else {
t.setSecondaryTurn(i, turn);
TurnType.setSecondaryTurn(lanes, i, turn);
}
break; // Move on to the next lane
}
}
}
return lanes;
}
private TurnType inferTurnFromLanes(TurnType t, boolean leftSide) {
List<Integer> possibleTurns = new ArrayList<Integer>();
for (int i = 0; i < t.getLanes().length; i++) {
if ((t.getLanes()[i] & 1) == 0) {
private int inferTurnFromLanes(int[] oLanes) {
TIntHashSet possibleTurns = new TIntHashSet();
for (int i = 0; i < oLanes.length; i++) {
if ((oLanes[i] & 1) == 0) {
continue;
}
if (possibleTurns.isEmpty()) {
// Nothing is in the list to compare to, so add the first elements
possibleTurns.add(TurnType.getPrimaryTurn(t.getLanes()[i]));
if (TurnType.getSecondaryTurn(t.getLanes()[i]) != 0) {
possibleTurns.add(TurnType.getSecondaryTurn(t.getLanes()[i]));
possibleTurns.add(TurnType.getPrimaryTurn(oLanes[i]));
if (TurnType.getSecondaryTurn(oLanes[i]) != 0) {
possibleTurns.add(TurnType.getSecondaryTurn(oLanes[i]));
}
} else {
List<Integer> laneTurns = new ArrayList<Integer>();
laneTurns.add(TurnType.getPrimaryTurn(t.getLanes()[i]));
if (TurnType.getSecondaryTurn(t.getLanes()[i]) != 0) {
laneTurns.add(TurnType.getSecondaryTurn(t.getLanes()[i]));
TIntArrayList laneTurns = new TIntArrayList();
laneTurns.add(TurnType.getPrimaryTurn(oLanes[i]));
if (TurnType.getSecondaryTurn(oLanes[i]) != 0) {
laneTurns.add(TurnType.getSecondaryTurn(oLanes[i]));
}
possibleTurns.retainAll(laneTurns);
if (possibleTurns.isEmpty()) {
// No common turns, so can't determine anything.
return t;
return 0;
}
}
}
// Remove all turns from lanes not selected...because those aren't it
for (int i = 0; i < t.getLanes().length; i++) {
if ((t.getLanes()[i] & 1) == 0 && !possibleTurns.isEmpty()) {
List<Integer> notLaneTurns = new ArrayList<Integer>();
notLaneTurns.add(TurnType.getPrimaryTurn(t.getLanes()[i]));
if (TurnType.getSecondaryTurn(t.getLanes()[i]) != 0) {
notLaneTurns.add(TurnType.getSecondaryTurn(t.getLanes()[i]));
for (int i = 0; i < oLanes.length; i++) {
if ((oLanes[i] & 1) == 0 && !possibleTurns.isEmpty()) {
possibleTurns.remove((Integer) TurnType.getPrimaryTurn(oLanes[i]));
if (TurnType.getSecondaryTurn(oLanes[i]) != 0) {
possibleTurns.remove((Integer) TurnType.getSecondaryTurn(oLanes[i]));
}
possibleTurns.removeAll(notLaneTurns);
}
}
// Checking to see that there is only one unique turn
if (new HashSet<Integer>(possibleTurns).size() == 1) {
TurnType derivedTurnType = TurnType.valueOf(possibleTurns.get(0), leftSide);
derivedTurnType.setLanes(t.getLanes());
derivedTurnType.setSkipToSpeak(t.isSkipToSpeak());
t = derivedTurnType;
// Because only the primary turn is displayed, if the turn to be taken is currently set as the secondary turn, then that needs to be switched around.
for (int i = 0; i < t.getLanes().length; i++) {
if (TurnType.getSecondaryTurn(t.getLanes()[i]) == t.getValue()) {
int temp = TurnType.getSecondaryTurn(t.getLanes()[i]);
t.setSecondaryTurn(i, TurnType.getPrimaryTurn(t.getLanes()[i]));
t.setPrimaryTurn(i, temp);
}
}
if (possibleTurns.size() == 1) {
return possibleTurns.iterator().next();
}
return t;
return 0;
}
private boolean isMotorway(RouteSegmentResult s){

View file

@ -163,6 +163,10 @@ public class TurnType {
}
// Note that there is no "weight" or ordering between the primary and secondary turns.
public static void setPrimaryTurn(int[] lanes, int lane, int turnType) {
lanes[lane] |= (turnType << 1);
}
public void setPrimaryTurn(int lane, int turnType) {
lanes[lane] |= (turnType << 1);
}
@ -172,6 +176,10 @@ public class TurnType {
return (laneValue >> 1) & ((1 << 4) - 1);
}
public static void setSecondaryTurn(int[] lanes, int lane, int turnType) {
lanes[lane] |= (turnType << 5);
}
public void setSecondaryTurn(int lane, int turnType) {
lanes[lane] |= (turnType << 5);
}
@ -237,4 +245,12 @@ public class TurnType {
}
return super.toString();
}
public static boolean isLeftTurn(int type) {
return type == TL || type == TSHL || type == TSLL;
}
public static boolean isRightTurn(int type) {
return type == TR || type == TSHR || type == TSLR;
}
}