Update routes

This commit is contained in:
Victor Shcherb 2015-01-15 02:42:33 +01:00
parent e0a8cc36f9
commit 1756e39616
3 changed files with 344 additions and 309 deletions

View file

@ -60,8 +60,8 @@ public class BinaryInspector {
// test cases show info // test cases show info
if(args.length == 1 && "test".equals(args[0])) { if(args.length == 1 && "test".equals(args[0])) {
in.inspector(new String[]{ in.inspector(new String[]{
// "-vpoi", "-vpoi",
"-vmap", "-vmapobjects", // "-vmap", "-vmapobjects",
// "-vrouting", // "-vrouting",
// "-vaddress", "-vcities", "-vstreets", "-vstreetgroups","-vbuildings", // "-vaddress", "-vcities", "-vstreets", "-vstreetgroups","-vbuildings",
// "-zoom=16", // "-zoom=16",

View file

@ -40,15 +40,35 @@ public class RouteResultPreparation {
List<RouteSegmentResult> prepareResult(RoutingContext ctx, List<RouteSegmentResult> result) throws IOException { List<RouteSegmentResult> prepareResult(RoutingContext ctx, List<RouteSegmentResult> result) throws IOException {
validateAllPointsConnected(result); validateAllPointsConnected(result);
splitRoadsAndAttachRoadSegments(ctx, result); splitRoadsAndAttachRoadSegments(ctx, result);
// calculate time
calculateTimeSpeed(ctx, result); calculateTimeSpeed(ctx, result);
addTurnInfo(ctx.leftSideNavigation, result); for (int i = 0; i < result.size(); i ++) {
TurnType turnType = getTurnInfo(result, i, ctx.leftSideNavigation);
result.get(i).setTurnType(turnType);
}
determineTurnsToMerge(ctx.leftSideNavigation, result); determineTurnsToMerge(ctx.leftSideNavigation, result);
justifyUTurns(ctx.leftSideNavigation, result);
addTurnInfoDescriptions(result); addTurnInfoDescriptions(result);
return result; return result;
} }
private void justifyUTurns(boolean leftSide, List<RouteSegmentResult> result) {
int next;
for (int i = 0; i < result.size() - 1; i = next) {
next = i + 1;
TurnType t = result.get(i).getTurnType();
// justify turn
if (t != null) {
TurnType jt = justifyUTurn(leftSide, result, i, t);
if (jt != null) {
result.get(i).setTurnType(jt);
next = i + 2;
}
}
}
}
private void calculateTimeSpeed(RoutingContext ctx, List<RouteSegmentResult> result) throws IOException { private void calculateTimeSpeed(RoutingContext ctx, List<RouteSegmentResult> result) throws IOException {
for (int i = 0; i < result.size(); i++) { for (int i = 0; i < result.size(); i++) {
RouteSegmentResult rr = result.get(i); RouteSegmentResult rr = result.get(i);
@ -332,26 +352,6 @@ public class RouteResultPreparation {
} }
private void addTurnInfo(boolean leftside, List<RouteSegmentResult> result) {
int next = 1;
for (int i = 0; i <= result.size(); i = next) {
TurnType t = null;
next = i + 1;
if (i < result.size()) {
t = getTurnInfo(result, i, leftside);
// justify turn
if(t != null && i < result.size() - 1) {
TurnType jt = justifyUTurn(leftside, result, i, t);
if(jt != null) {
t = jt;
next = i + 2;
}
}
result.get(i).setTurnType(t);
}
}
}
protected void addTurnInfoDescriptions(List<RouteSegmentResult> result) { protected void addTurnInfoDescriptions(List<RouteSegmentResult> result) {
int prevSegment = -1; int prevSegment = -1;
float dist = 0; float dist = 0;
@ -359,8 +359,29 @@ public class RouteResultPreparation {
if (i == result.size() || result.get(i).getTurnType() != null) { if (i == result.size() || result.get(i).getTurnType() != null) {
if (prevSegment >= 0) { if (prevSegment >= 0) {
String turn = result.get(prevSegment).getTurnType().toString(); String turn = result.get(prevSegment).getTurnType().toString();
if (result.get(prevSegment).getTurnType().getLanes() != null) { final int[] lns = result.get(prevSegment).getTurnType().getLanes();
turn += Arrays.toString(result.get(prevSegment).getTurnType().getLanes()); if (lns != null) {
String s = "[ ";
for (int h = 0; h < lns.length; h++) {
if (h > 0) {
s += ", ";
}
if (lns[h] % 2 == 1) {
s += "+";
}
int pt = TurnType.getPrimaryTurn(lns[h]);
if (pt == 0) {
pt = 1;
}
s += TurnType.valueOf(pt, false).toXmlString();
int st = TurnType.getSecondaryTurn(lns[h]);
if (st != 0) {
s += ";" + TurnType.valueOf(st, false).toXmlString();
}
}
s += "]";
turn += s;
} }
result.get(prevSegment).setDescription( result.get(prevSegment).setDescription(
turn + MessageFormat.format(" and go {0,number,#.##} meters", dist)); turn + MessageFormat.format(" and go {0,number,#.##} meters", dist));
@ -381,7 +402,7 @@ public class RouteResultPreparation {
boolean tl = TurnType.TL == t.getValue(); boolean tl = TurnType.TL == t.getValue();
boolean tr = TurnType.TR == t.getValue(); boolean tr = TurnType.TR == t.getValue();
if(tl || tr) { if(tl || tr) {
TurnType tnext = getTurnInfo(result, i + 1, leftside); TurnType tnext = result.get(i + 1).getTurnType();
if (tnext != null && result.get(i).getDistance() < 35) { // if (tnext != null && result.get(i).getDistance() < 35) { //
boolean ut = true; boolean ut = true;
if (i > 0) { if (i > 0) {
@ -397,6 +418,7 @@ public class RouteResultPreparation {
ut = false; ut = false;
} }
if (ut) { if (ut) {
tnext.setSkipToSpeak(true);
if (tl && TurnType.TL == tnext.getValue()) { if (tl && TurnType.TL == tnext.getValue()) {
return TurnType.valueOf(TurnType.TU, false); return TurnType.valueOf(TurnType.TU, false);
} else if (tr && TurnType.TR == tnext.getValue()) { } else if (tr && TurnType.TR == tnext.getValue()) {
@ -409,108 +431,97 @@ public class RouteResultPreparation {
} }
private void determineTurnsToMerge(boolean leftside, List<RouteSegmentResult> result) { private void determineTurnsToMerge(boolean leftside, List<RouteSegmentResult> result) {
for (int i = result.size() - 2; i >= 0; i--) {
RouteSegmentResult currentSegment = result.get(i);
RouteSegmentResult nextSegment = null; RouteSegmentResult nextSegment = null;
double dist = 0;
// We need to get the next segment that has a turn and lanes attached. for (int i = result.size() - 1; i >= 0; i--) {
for (int j = i + 1; j < result.size(); j++) { RouteSegmentResult currentSegment = result.get(i);
RouteSegmentResult possibleSegment = result.get(j);
if (possibleSegment.getTurnType() != null && possibleSegment.getTurnType().getLanes() != null) {
nextSegment = possibleSegment;
break;
}
}
if (nextSegment == null) {
continue;
}
TurnType currentTurn = currentSegment.getTurnType(); TurnType currentTurn = currentSegment.getTurnType();
TurnType nextTurn = nextSegment.getTurnType(); dist += currentSegment.getDistance();
if (currentTurn == null || currentTurn.getLanes() == null) {
if (currentTurn == null || currentTurn.getLanes() == null || nextTurn == null || nextTurn.getLanes() == null) { // skip
continue; } else {
} if (nextSegment != null) {
// Only allow slight turns that are nearby to be merged.
// [disabled cause it is valuable for two consequent sharp turns as well]
// the distance could be longer on highways and shorter in city
String hw = currentSegment.getObject().getHighway(); String hw = currentSegment.getObject().getHighway();
double mergeDistance = 200; double mergeDistance = 200;
if (hw != null && (hw.startsWith("trunk") || hw.startsWith("motorway"))) { if (hw != null && (hw.startsWith("trunk") || hw.startsWith("motorway"))) {
mergeDistance = 400; mergeDistance = 400;
} }
if (currentSegment.getDistance() < mergeDistance/* if (dist < mergeDistance) {
&& TurnType.isSlightTurn(currentTurn.getValue())*/) {
mergeTurnLanes(leftside, currentSegment, nextSegment); mergeTurnLanes(leftside, currentSegment, nextSegment);
} }
} }
nextSegment = currentSegment;
dist = 0;
}
}
} }
private void mergeTurnLanes(boolean leftSide, RouteSegmentResult currentSegment, RouteSegmentResult nextSegment) { private class MergeTurnLaneTurn {
TurnType currentTurn = currentSegment.getTurnType(); TurnType turn;
TurnType nextTurn = nextSegment.getTurnType(); int[] originalLanes;
boolean isUsingTurnLanes = TurnType.getPrimaryTurn(currentTurn.getLanes()[0]) != 0 int[] disabledLanes;
&& TurnType.getPrimaryTurn(nextTurn.getLanes()[0]) != 0; int activeStartIndex = -1;
if (isUsingTurnLanes) { int activeEndIndex = -1;
int[] lanes = new int[currentTurn.getLanes().length];
int activeIndex = -1;
int activeLen = 0; int activeLen = 0;
// define enabled lanes
for(int i = 0; i < lanes.length; i++) { public MergeTurnLaneTurn(RouteSegmentResult segment) {
int ln = currentTurn.getLanes()[i]; this.turn = segment.getTurnType();
lanes[i] = ln & ~1; if(turn != null) {
originalLanes = turn.getLanes();
}
if(originalLanes != null) {
disabledLanes = new int[originalLanes.length];
for (int i = 0; i < originalLanes.length; i++) {
int ln = originalLanes[i];
disabledLanes[i] = ln & ~1;
if ((ln & 1) > 0) { if ((ln & 1) > 0) {
if(activeIndex == -1) { if (activeStartIndex == -1) {
activeIndex = i; activeStartIndex = i;
activeLen++; }
} else { activeEndIndex = i;
activeLen++; activeLen++;
} }
} }
} }
if(activeLen < 2) { }
public boolean isActiveTurnMostLeft() {
return activeStartIndex == 0;
}
public boolean isActiveTurnMostRight() {
return activeEndIndex == originalLanes.length - 1;
}
}
private void mergeTurnLanes(boolean leftSide, RouteSegmentResult currentSegment, RouteSegmentResult nextSegment) {
MergeTurnLaneTurn active = new MergeTurnLaneTurn(currentSegment);
MergeTurnLaneTurn target = new MergeTurnLaneTurn(nextSegment);
if (active.activeLen < 2) {
return; return;
} }
int targetActiveIndex = -1; if (target.activeStartIndex == -1) {
int targetActiveLen = 0;
int[] nextLanes = nextTurn.getLanes();
for(int i = 0; i < nextLanes.length; i++) {
int ln = nextLanes[i];
if((ln & 1) > 0) {
if(targetActiveIndex == -1) {
targetActiveIndex = i;
targetActiveLen++;
} else {
targetActiveLen++;
}
}
}
if(targetActiveIndex == -1) {
return; return;
} }
boolean changed = false; boolean changed = false;
// next turn is left if (target.isActiveTurnMostLeft()) {
if(targetActiveIndex == 0) {
// let only the most left lanes be enabled // let only the most left lanes be enabled
if(targetActiveLen <= activeLen) { if (target.activeLen <= active.activeLen) {
activeLen = targetActiveLen; active.activeEndIndex -= (active.activeLen - target.activeLen);
changed = true; changed = true;
} }
} else if(targetActiveIndex + targetActiveLen == nextLanes.length) { } else if (target.isActiveTurnMostRight()) {
// next turn is right // next turn is right
// let only the most right lanes be enabled // let only the most right lanes be enabled
if(targetActiveLen <= activeLen) { if (target.activeLen < active.activeLen) {
activeIndex += (activeLen - targetActiveLen); active.activeStartIndex += (active.activeLen - target.activeLen);
changed = true; changed = true;
} }
} else { } else {
// next turn is get through (take out the left and the right turn) // next turn is get through (take out the left and the right turn)
if(nextLanes.length >= activeLen) { if (target.originalLanes.length >= active.activeLen) {
float ratio = (nextLanes.length / (float)activeLen); float ratio = (target.originalLanes.length / (float) active.activeLen);
activeLen = (int) Math.ceil(targetActiveLen * ratio); active.activeEndIndex = (int) Math.ceil(target.activeEndIndex * ratio);
activeIndex = (int) Math.floor(targetActiveIndex / ratio); active.activeStartIndex = (int) Math.floor(target.activeStartIndex / ratio);
changed = true; changed = true;
} }
} }
@ -518,23 +529,22 @@ public class RouteResultPreparation {
return; return;
} }
// set the allowed lane bit // set the allowed lane bit
for (int i = 0; i < lanes.length; i++) { for (int i = 0; i < active.disabledLanes.length; i++) {
if(i >= activeIndex && i < activeIndex + activeLen) { if (i >= active.activeStartIndex && i <= active.activeEndIndex) {
lanes[i] |= 1; active.disabledLanes[i] |= 1;
} }
} }
currentTurn.setLanes(lanes); TurnType currentTurn = currentSegment.getTurnType();
int turn = inferTurnFromLanes(lanes); currentTurn.setLanes(active.disabledLanes);
int turn = inferTurnFromLanes(active.disabledLanes);
if (turn != 0 && turn != currentTurn.getValue()) { if (turn != 0 && turn != currentTurn.getValue()) {
TurnType newTurnType = TurnType.valueOf(turn, leftSide); TurnType newTurnType = TurnType.valueOf(turn, leftSide);
newTurnType.setLanes(lanes); newTurnType.setLanes(active.disabledLanes);
newTurnType.setSkipToSpeak(currentTurn.isSkipToSpeak()); newTurnType.setSkipToSpeak(currentTurn.isSkipToSpeak());
currentSegment.setTurnType(newTurnType); currentSegment.setTurnType(newTurnType);
} }
} }
}
private static final int MAX_SPEAK_PRIORITY = 5; private static final int MAX_SPEAK_PRIORITY = 5;
private int highwaySpeakPriority(String highway) { private int highwaySpeakPriority(String highway) {
@ -585,10 +595,8 @@ public class RouteResultPreparation {
} else { } else {
t = TurnType.valueOf(TurnType.TU, leftSide); t = TurnType.valueOf(TurnType.TU, leftSide);
} }
int[] lanes = getTurnLanesInfo(prev, t.getValue(), leftSide, true); int[] lanes = getTurnLanesInfo(prev, t.getValue());
if(lanes != null) {
t.setLanes(lanes); t.setLanes(lanes);
}
} else if (mpi < -TURN_DEGREE_MIN) { } else if (mpi < -TURN_DEGREE_MIN) {
if (mpi > -60) { if (mpi > -60) {
t = TurnType.valueOf(TurnType.TSLR, leftSide); t = TurnType.valueOf(TurnType.TSLR, leftSide);
@ -597,14 +605,12 @@ public class RouteResultPreparation {
} else if (mpi > -135 || !leftSide) { } else if (mpi > -135 || !leftSide) {
t = TurnType.valueOf(TurnType.TSHR, leftSide); t = TurnType.valueOf(TurnType.TSHR, leftSide);
} else { } else {
t = TurnType.valueOf(TurnType.TU, leftSide); t = TurnType.valueOf(TurnType.TRU, leftSide);
} }
int[] lanes = getTurnLanesInfo(prev, t.getValue(), leftSide, false); int[] lanes = getTurnLanesInfo(prev, t.getValue());
if(lanes != null) {
t.setLanes(lanes); t.setLanes(lanes);
}
} else { } else {
t = attachKeepLeftInfoAndLanes(leftSide, prev, rr, t); t = attachKeepLeftInfoAndLanes(leftSide, prev, rr);
} }
if (t != null) { if (t != null) {
t.setTurnAngle((float) -mpi); t.setTurnAngle((float) -mpi);
@ -613,39 +619,34 @@ public class RouteResultPreparation {
return t; return t;
} }
private int[] getTurnLanesInfo(RouteSegmentResult prevSegm, int mainTurnType, boolean leftSide, private int[] getTurnLanesInfo(RouteSegmentResult prevSegm, int mainTurnType) {
boolean leftTurn) {
String turnLanes = getTurnLanesString(prevSegm); String turnLanes = getTurnLanesString(prevSegm);
if (turnLanes == null) { if (turnLanes == null) {
return null; return null;
} }
String[] splitLaneOptions = turnLanes.split("\\|", -1); int[] lanesArray = calculateRawTurnLanes(turnLanes, mainTurnType);
if (splitLaneOptions.length != countLanesMinOne(prevSegm)) {
// Error in data or missing data
return null;
}
int[] lanesArray = calculateRawTurnLanes(splitLaneOptions, mainTurnType);
// Manually set the allowed lanes. // Manually set the allowed lanes.
boolean isSet = setAllowedLanes(mainTurnType, lanesArray); boolean isSet = setAllowedLanes(mainTurnType, lanesArray);
if(!isSet && lanesArray.length > 0) { 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, // 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. // but is allowed and being used here. This section adds in that indicator. The same applies for where leftSide is true.
boolean leftTurn = TurnType.isLeftTurn(mainTurnType);
int ind = leftTurn? 0 : lanesArray.length - 1; int ind = leftTurn? 0 : lanesArray.length - 1;
final int tt = TurnType.getPrimaryTurn(lanesArray[ind]); final int tt = TurnType.getPrimaryTurn(lanesArray[ind]);
if (leftTurn) { if (leftTurn) {
if (!TurnType.isLeftTurn(tt)) { if (!TurnType.isLeftTurn(tt)) {
// This was just to make sure that there's no bad data. // This was just to make sure that there's no bad data.
TurnType.setPrimaryTurnAndReset(lanesArray, ind, TurnType.TL);
TurnType.setSecondaryTurn(lanesArray, ind, tt); TurnType.setSecondaryTurn(lanesArray, ind, tt);
TurnType.setPrimaryTurn(lanesArray, ind, TurnType.TL);
} }
} else { } else {
if (!TurnType.isRightTurn(tt)) { if (!TurnType.isRightTurn(tt)) {
// This was just to make sure that there's no bad data. // This was just to make sure that there's no bad data.
TurnType.setPrimaryTurnAndReset(lanesArray, ind, TurnType.TR);
TurnType.setSecondaryTurn(lanesArray, ind, tt); TurnType.setSecondaryTurn(lanesArray, ind, tt);
TurnType.setPrimaryTurn(lanesArray, ind, TurnType.TR);
} }
} }
setAllowedLanes(lanesArray[ind], lanesArray); setAllowedLanes(tt, lanesArray);
} }
return lanesArray; return lanesArray;
} }
@ -692,113 +693,189 @@ public class RouteResultPreparation {
return t; return t;
} }
private class RoadSplitStructure {
private TurnType attachKeepLeftInfoAndLanes(boolean leftSide, RouteSegmentResult prevSegm, RouteSegmentResult currentSegm, TurnType t) { boolean keepLeft = false;
// keep left/right boolean keepRight = false;
boolean kl = false;
boolean kr = false;
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; boolean speak = false;
int leftLanes = 0;
int rightLanes = 0;
int roadsOnLeft = 0;
int addRoadsOnLeft = 0;
int roadsOnRight = 0;
int addRoadsOnRight = 0;
}
private TurnType attachKeepLeftInfoAndLanes(boolean leftSide, RouteSegmentResult prevSegm, RouteSegmentResult currentSegm) {
List<RouteSegmentResult> attachedRoutes = currentSegm.getAttachedRoutes(currentSegm.getStartPointIndex());
if(attachedRoutes == null || attachedRoutes.size() == 0) {
return null;
}
// keep left/right
RoadSplitStructure rs = calculateRoadSplitStructure(prevSegm, currentSegm, attachedRoutes);
if(rs.roadsOnLeft + rs.roadsOnRight == 0) {
return null;
}
// turn lanes exist
String turnLanes = getTurnLanesString(prevSegm);
if (turnLanes != null) {
return createKeepLeftRightTurnBasedOnTurnTypes(rs, prevSegm, currentSegm, turnLanes, leftSide);
}
// turn lanes don't exist
if (rs.keepLeft || rs.keepRight) {
return createSimpleKeepLeftRightTurn(leftSide, prevSegm, currentSegm, rs);
}
return null;
}
protected TurnType createKeepLeftRightTurnBasedOnTurnTypes(RoadSplitStructure rs, RouteSegmentResult prevSegm,
RouteSegmentResult currentSegm, String turnLanes,boolean leftSide) {
// Maybe going straight at a 90-degree intersection
TurnType t = TurnType.valueOf(TurnType.C, leftSide);
int[] rawLanes = calculateRawTurnLanes(turnLanes, TurnType.C);
if (rs.keepLeft || rs.keepRight) {
String[] splitLaneOptions = turnLanes.split("\\|", -1);
int activeBeginIndex = findActiveIndex(rawLanes, splitLaneOptions, rs.leftLanes, true,
rs.roadsOnLeft, rs.addRoadsOnLeft);
int activeEndIndex = findActiveIndex(rawLanes, splitLaneOptions, rs.rightLanes, false,
rs.roadsOnRight, rs.addRoadsOnRight);
if (activeBeginIndex == -1 || activeEndIndex == -1 || activeBeginIndex > activeEndIndex) {
// something went wrong
return createSimpleKeepLeftRightTurn(leftSide, prevSegm, currentSegm, rs);
}
for (int k = 0; k < rawLanes.length; k++) {
if (k >= activeBeginIndex && k <= activeEndIndex) {
rawLanes[k] |= 1;
}
}
int tp = inferTurnFromLanes(rawLanes);
if (tp != t.getValue() && tp != 0) {
t = TurnType.valueOf(tp, leftSide);
}
} else {
for (int k = 0; k < rawLanes.length; k++) {
int turn = rawLanes[k];
boolean active = false;
if (TurnType.getPrimaryTurn(turn) == TurnType.C) {
active = true;
} else if (TurnType.isRightTurn(turn) && rs.roadsOnRight == 0) {
// some turns go through many segments (to turn right or left)
// so on one first segment the lane could be available and may be only 1 possible
// all undesired lanes will be disabled through the 2nd pass
active = true;
} else if (TurnType.isLeftTurn(turn) && rs.roadsOnLeft == 0) {
active = true;
}
if (active) {
rawLanes[k] |= 1;
}
}
}
t.setSkipToSpeak(!rs.speak);
t.setLanes(rawLanes);
return t;
}
protected int findActiveIndex(int[] rawLanes, String[] splitLaneOptions, int lanes, boolean left,
int roads, int addRoads) {
int activeStartIndex = -1;
boolean lookupSlightTurn = addRoads > 0;
for (int i = 0; i < rawLanes.length; i++) {
int ind = left ? i : (rawLanes.length - i - 1);
if (!lookupSlightTurn ||
TurnType.isSlightTurn(TurnType.getPrimaryTurn(rawLanes[ind]))
|| TurnType.isSlightTurn(TurnType.getSecondaryTurn(rawLanes[ind]))) {
int cnt = countOccurrences(splitLaneOptions[ind], ';') + 1;
if(cnt > 1) {
// sometimes slight right turn goes to the road with 2 lanes
// the better situation to group all the lanes and
// when ';' we know for sure the lane combines 2 group
roads -= 2;
}
lanes -= cnt;
// we already found slight turn others are turn in different direction
lookupSlightTurn = false;
}
if (lanes < 0 || roads < 0) {
activeStartIndex = ind;
break;
}
}
return activeStartIndex;
}
protected RoadSplitStructure calculateRoadSplitStructure(RouteSegmentResult prevSegm, RouteSegmentResult currentSegm,
List<RouteSegmentResult> attachedRoutes) {
RoadSplitStructure rs = new RoadSplitStructure();
int speakPriority = Math.max(highwaySpeakPriority(prevSegm.getObject().getHighway()), highwaySpeakPriority(currentSegm.getObject().getHighway())); int speakPriority = Math.max(highwaySpeakPriority(prevSegm.getObject().getHighway()), highwaySpeakPriority(currentSegm.getObject().getHighway()));
boolean otherRoutesExist = false;
if (attachedRoutes != null) {
for (RouteSegmentResult attached : attachedRoutes) { for (RouteSegmentResult attached : attachedRoutes) {
double ex = MapUtils.degreesDiff(attached.getBearingBegin(), currentSegm.getBearingBegin()); double ex = MapUtils.degreesDiff(attached.getBearingBegin(), currentSegm.getBearingBegin());
double mpi = Math.abs(MapUtils.degreesDiff(prevSegm.getBearingEnd(), attached.getBearingBegin())); double mpi = Math.abs(MapUtils.degreesDiff(prevSegm.getBearingEnd(), attached.getBearingBegin()));
int rsSpeakPriority = highwaySpeakPriority(attached.getObject().getHighway()); int rsSpeakPriority = highwaySpeakPriority(attached.getObject().getHighway());
if (rsSpeakPriority != MAX_SPEAK_PRIORITY || speakPriority == MAX_SPEAK_PRIORITY) { if (rsSpeakPriority != MAX_SPEAK_PRIORITY || speakPriority == MAX_SPEAK_PRIORITY) {
if ((ex < TURN_DEGREE_MIN || mpi < TURN_DEGREE_MIN) && ex >= 0) { int lanes = countLanesMinOne(attached);
kl = true; boolean smallStraightVariation = mpi < TURN_DEGREE_MIN;
right += countLanesMinOne(attached); boolean smallTargetVariation = Math.abs(ex) < TURN_DEGREE_MIN;
speak = speak || rsSpeakPriority <= speakPriority; boolean attachedOnTheRight = ex >= 0;
} else if ((ex > -TURN_DEGREE_MIN || mpi < TURN_DEGREE_MIN) && ex <= 0) { if (attachedOnTheRight) {
kr = true; rs.roadsOnRight++;
left += countLanesMinOne(attached); } else {
speak = speak || rsSpeakPriority <= speakPriority; rs.roadsOnLeft++;
} else if (mpi >= TURN_DEGREE_MIN) { }
// Indicate that there are other turns at this intersection, and displaying the lanes may be helpful here. if (smallTargetVariation || smallStraightVariation) {
otherRoutesExist = true; if (attachedOnTheRight) {
rs.keepLeft = true;
rs.rightLanes += lanes;
} else {
rs.keepRight = true;
rs.leftLanes += lanes;
}
rs.speak = rs.speak || rsSpeakPriority <= speakPriority;
} else {
if (attachedOnTheRight) {
rs.addRoadsOnRight++;
} else {
rs.addRoadsOnLeft++;
} }
} }
} }
} }
if(kr && left == 0) { return rs;
left = 1;
} else if(kl && right == 0) {
right = 1;
} }
protected TurnType createSimpleKeepLeftRightTurn(boolean leftSide, RouteSegmentResult prevSegm,
RouteSegmentResult currentSegm, RoadSplitStructure rs) {
int current = countLanesMinOne(currentSegm); int current = countLanesMinOne(currentSegm);
int[] lanes = new int[current + left + right]; int ls = current + rs.leftLanes + rs.rightLanes;
ls = current + left + right; int[] lanes = new int[ls];
for (int it = 0; it < ls; it++) { for (int it = 0; it < ls; it++) {
if (it < left || it >= left + current) { if (it < rs.leftLanes || it >= rs.leftLanes + current) {
lanes[it] = 0; lanes[it] = 0;
} else { } else {
lanes[it] = 1; lanes[it] = 1;
} }
} }
// sometimes links are // sometimes links are
if ((current <= left + right) && (left > 1 || right > 1)) { if ((current <= rs.leftLanes + rs.rightLanes) && (rs.leftLanes > 1 || rs.rightLanes > 1)) {
speak = true; rs.speak = true;
} }
double devation = Math.abs(MapUtils.degreesDiff(prevSegm.getBearingEnd(), currentSegm.getBearingBegin())); double devation = Math.abs(MapUtils.degreesDiff(prevSegm.getBearingEnd(), currentSegm.getBearingBegin()));
boolean makeSlightTurn = devation > 5 && (!isMotorway(prevSegm) || !isMotorway(currentSegm)); boolean makeSlightTurn = devation > 5 && (!isMotorway(prevSegm) || !isMotorway(currentSegm));
if (kl && kr) { TurnType t = null;
if (rs.keepLeft && rs.keepRight) {
t = TurnType.valueOf(TurnType.C, leftSide); t = TurnType.valueOf(TurnType.C, leftSide);
t.setSkipToSpeak(!speak); } else if (rs.keepLeft) {
} else if (kl) {
t = TurnType.valueOf(makeSlightTurn ? TurnType.TSLL : TurnType.KL, leftSide); t = TurnType.valueOf(makeSlightTurn ? TurnType.TSLL : TurnType.KL, leftSide);
t.setSkipToSpeak(!speak); } else if (rs.keepRight) {
} else if (kr) {
t = TurnType.valueOf(makeSlightTurn ? TurnType.TSLR : TurnType.KR, leftSide); t = TurnType.valueOf(makeSlightTurn ? TurnType.TSLR : TurnType.KR, leftSide);
t.setSkipToSpeak(!speak); } else {
} else if (otherRoutesExist && getTurnLanesString(prevSegm) != null) {
// Maybe going straight at a 90-degree intersection
t = TurnType.valueOf(TurnType.C, leftSide);
t.setSkipToSpeak(true);
// When going straight, the lanes have to be calculated from the previous segment, not the current/next segment.
int prevLanes = countLanesMinOne(prevSegm);
t.setLanes(attachTurnLanesData(prevSegm, new int[prevLanes]));
// Manually set the allowed lanes based on the turn type
for (int i = 0; i < t.getLanes().length; i++) {
if (TurnType.getPrimaryTurn(t.getLanes()[i]) == TurnType.C) {
t.getLanes()[i] |= 1;
}
}
return t; return t;
} }
if (t != null && lanes != null) { t.setSkipToSpeak(!rs.speak);
int[] calcLanes = attachTurnLanesData(prevSegm, lanes); t.setLanes(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);
}
return t; return t;
} }
@ -839,45 +916,7 @@ public class RouteResultPreparation {
} }
} }
private int[] attachTurnLanesData(RouteSegmentResult prevSegm, int[] outgoingCalcLanes) {
String turnLanes = getTurnLanesString(prevSegm);
if (turnLanes == null) {
return outgoingCalcLanes;
}
String[] splitLaneOptions = turnLanes.split("\\|", -1);
if (splitLaneOptions.length != countLanesMinOne(prevSegm)) {
// Error in data or missing data
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;
}
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.
int outgoingLanesIndex = 0;
int sourceLanesIndex = 0;
while (outgoingLanesIndex < outgoingCalcLanes.length &&
sourceLanesIndex < splitLaneOptions.length) {
int options = countOccurrences(splitLaneOptions[sourceLanesIndex], ';');
for (int k = 0; k <= options && outgoingLanesIndex < outgoingCalcLanes.length; k++) {
usableLanes[sourceLanesIndex] |= outgoingCalcLanes[outgoingLanesIndex];
outgoingLanesIndex++;
}
sourceLanesIndex++;
}
}
return usableLanes;
}
private int countOccurrences(String haystack, char needle) { private int countOccurrences(String haystack, char needle) {
int count = 0; int count = 0;
@ -905,8 +944,7 @@ public class RouteResultPreparation {
if(turnLanes == null) { if(turnLanes == null) {
return null; return null;
} }
String[] splitLaneOptions = turnLanes.split("\\|", -1); return calculateRawTurnLanes(turnLanes, 0);
return calculateRawTurnLanes(splitLaneOptions, 0);
} }
public static int[] parseLanes(RouteDataObject ro, double dirToNorthEastPi) { public static int[] parseLanes(RouteDataObject ro, double dirToNorthEastPi) {
@ -939,7 +977,8 @@ public class RouteResultPreparation {
return null; return null;
} }
private static int[] calculateRawTurnLanes(String[] splitLaneOptions, int calcTurnType) { private static int[] calculateRawTurnLanes(String turnLanes, int calcTurnType) {
String[] splitLaneOptions = turnLanes.split("\\|", -1);
int[] lanes = new int[splitLaneOptions.length]; int[] lanes = new int[splitLaneOptions.length];
for (int i = 0; i < splitLaneOptions.length; i++) { for (int i = 0; i < splitLaneOptions.length; i++) {
String[] laneOptions = splitLaneOptions[i].split(";"); String[] laneOptions = splitLaneOptions[i].split(";");
@ -967,12 +1006,16 @@ public class RouteResultPreparation {
continue; continue;
} }
if (TurnType.getPrimaryTurn(lanes[i]) == 0) { final int primary = TurnType.getPrimaryTurn(lanes[i]);
TurnType.setPrimaryTurn(lanes, i, turn); if (primary == 0) {
TurnType.setPrimaryTurnAndReset(lanes, i, turn);
} else { } else {
if (turn == calcTurnType) { if (turn == calcTurnType ||
TurnType.setSecondaryTurn(lanes, i, TurnType.getPrimaryTurn(lanes[i])); (TurnType.isRightTurn(calcTurnType) && TurnType.isRightTurn(turn)) ||
TurnType.setPrimaryTurn(lanes, i, turn); (TurnType.isLeftTurn(calcTurnType) && TurnType.isLeftTurn(turn))
) {
TurnType.setPrimaryTurnAndReset(lanes, i, turn);
TurnType.setSecondaryTurn(lanes, i, primary);
} else { } else {
TurnType.setSecondaryTurn(lanes, i, turn); TurnType.setSecondaryTurn(lanes, i, turn);
} }

View file

@ -163,14 +163,10 @@ public class TurnType {
} }
// Note that the primary turn will be the one displayed on the map. // Note that the primary turn will be the one displayed on the map.
public static void setPrimaryTurn(int[] lanes, int lane, int turnType) { public static void setPrimaryTurnAndReset(int[] lanes, int lane, int turnType) {
lanes[lane] |= (turnType << 1); lanes[lane] = (turnType << 1);
} }
public void setPrimaryTurn(int lane, int turnType) {
lanes[lane] &= ~(15 << 1);
lanes[lane] |= (turnType << 1);
}
public static int getPrimaryTurn(int laneValue) { public static int getPrimaryTurn(int laneValue) {
// Get the primary turn modifier for the lane // Get the primary turn modifier for the lane
@ -182,10 +178,6 @@ public class TurnType {
lanes[lane] |= (turnType << 5); lanes[lane] |= (turnType << 5);
} }
public void setSecondaryTurn(int lane, int turnType) {
lanes[lane] |= (turnType << 5);
}
public static int getSecondaryTurn(int laneValue) { public static int getSecondaryTurn(int laneValue) {
// Get the primary turn modifier for the lane // Get the primary turn modifier for the lane
return (laneValue >> 5); return (laneValue >> 5);
@ -249,11 +241,11 @@ public class TurnType {
} }
public static boolean isLeftTurn(int type) { public static boolean isLeftTurn(int type) {
return type == TL || type == TSHL || type == TSLL; return type == TL || type == TSHL || type == TSLL || type == TRU;
} }
public static boolean isRightTurn(int type) { public static boolean isRightTurn(int type) {
return type == TR || type == TSHR || type == TSLR; return type == TR || type == TSHR || type == TSLR || type == TU;
} }
public static boolean isSlightTurn(int type) { public static boolean isSlightTurn(int type) {