Update routes
Conflicts: OsmAnd-java/src/net/osmand/binary/BinaryInspector.java OsmAnd-java/src/net/osmand/router/RouteResultPreparation.java OsmAnd-java/src/net/osmand/router/TurnType.java
This commit is contained in:
parent
c5ecb5e63f
commit
5a19801695
2 changed files with 395 additions and 284 deletions
|
@ -40,15 +40,35 @@ public class RouteResultPreparation {
|
|||
List<RouteSegmentResult> prepareResult(RoutingContext ctx, List<RouteSegmentResult> result) throws IOException {
|
||||
validateAllPointsConnected(result);
|
||||
splitRoadsAndAttachRoadSegments(ctx, result);
|
||||
// calculate time
|
||||
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);
|
||||
justifyUTurns(ctx.leftSideNavigation, result);
|
||||
addTurnInfoDescriptions(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 {
|
||||
for (int i = 0; i < result.size(); 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) {
|
||||
int prevSegment = -1;
|
||||
float dist = 0;
|
||||
|
@ -359,8 +359,29 @@ public class RouteResultPreparation {
|
|||
if (i == result.size() || result.get(i).getTurnType() != null) {
|
||||
if (prevSegment >= 0) {
|
||||
String turn = result.get(prevSegment).getTurnType().toString();
|
||||
if (result.get(prevSegment).getTurnType().getLanes() != null) {
|
||||
turn += Arrays.toString(result.get(prevSegment).getTurnType().getLanes());
|
||||
final int[] lns = 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(
|
||||
turn + MessageFormat.format(" and go {0,number,#.##} meters", dist));
|
||||
|
@ -381,7 +402,7 @@ public class RouteResultPreparation {
|
|||
boolean tl = TurnType.TL == t.getValue();
|
||||
boolean tr = TurnType.TR == t.getValue();
|
||||
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) { //
|
||||
boolean ut = true;
|
||||
if (i > 0) {
|
||||
|
@ -397,6 +418,7 @@ public class RouteResultPreparation {
|
|||
ut = false;
|
||||
}
|
||||
if (ut) {
|
||||
tnext.setSkipToSpeak(true);
|
||||
if (tl && TurnType.TL == tnext.getValue()) {
|
||||
return TurnType.valueOf(TurnType.TU, false);
|
||||
} else if (tr && TurnType.TR == tnext.getValue()) {
|
||||
|
@ -409,108 +431,97 @@ public class RouteResultPreparation {
|
|||
}
|
||||
|
||||
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;
|
||||
|
||||
// We need to get the next segment that has a turn and lanes attached.
|
||||
for (int j = i + 1; j < result.size(); j++) {
|
||||
RouteSegmentResult possibleSegment = result.get(j);
|
||||
if (possibleSegment.getTurnType() != null && possibleSegment.getTurnType().getLanes() != null) {
|
||||
nextSegment = possibleSegment;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nextSegment == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double dist = 0;
|
||||
for (int i = result.size() - 1; i >= 0; i--) {
|
||||
RouteSegmentResult currentSegment = result.get(i);
|
||||
TurnType currentTurn = currentSegment.getTurnType();
|
||||
TurnType nextTurn = nextSegment.getTurnType();
|
||||
|
||||
if (currentTurn == null || currentTurn.getLanes() == null || nextTurn == null || nextTurn.getLanes() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 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
|
||||
dist += currentSegment.getDistance();
|
||||
if (currentTurn == null || currentTurn.getLanes() == null) {
|
||||
// skip
|
||||
} else {
|
||||
if (nextSegment != null) {
|
||||
String hw = currentSegment.getObject().getHighway();
|
||||
double mergeDistance = 200;
|
||||
if (hw != null && (hw.startsWith("trunk") || hw.startsWith("motorway"))) {
|
||||
mergeDistance = 400;
|
||||
}
|
||||
if (currentSegment.getDistance() < mergeDistance/*
|
||||
&& TurnType.isSlightTurn(currentTurn.getValue())*/) {
|
||||
if (dist < mergeDistance) {
|
||||
mergeTurnLanes(leftside, currentSegment, nextSegment);
|
||||
}
|
||||
}
|
||||
nextSegment = currentSegment;
|
||||
dist = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void mergeTurnLanes(boolean leftSide, RouteSegmentResult currentSegment, RouteSegmentResult nextSegment) {
|
||||
TurnType currentTurn = currentSegment.getTurnType();
|
||||
TurnType nextTurn = nextSegment.getTurnType();
|
||||
boolean isUsingTurnLanes = TurnType.getPrimaryTurn(currentTurn.getLanes()[0]) != 0
|
||||
&& TurnType.getPrimaryTurn(nextTurn.getLanes()[0]) != 0;
|
||||
if (isUsingTurnLanes) {
|
||||
int[] lanes = new int[currentTurn.getLanes().length];
|
||||
int activeIndex = -1;
|
||||
private class MergeTurnLaneTurn {
|
||||
TurnType turn;
|
||||
int[] originalLanes;
|
||||
int[] disabledLanes;
|
||||
int activeStartIndex = -1;
|
||||
int activeEndIndex = -1;
|
||||
int activeLen = 0;
|
||||
// define enabled lanes
|
||||
for(int i = 0; i < lanes.length; i++) {
|
||||
int ln = currentTurn.getLanes()[i];
|
||||
lanes[i] = ln & ~1;
|
||||
|
||||
public MergeTurnLaneTurn(RouteSegmentResult segment) {
|
||||
this.turn = segment.getTurnType();
|
||||
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(activeIndex == -1) {
|
||||
activeIndex = i;
|
||||
activeLen++;
|
||||
} else {
|
||||
if (activeStartIndex == -1) {
|
||||
activeStartIndex = i;
|
||||
}
|
||||
activeEndIndex = i;
|
||||
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;
|
||||
}
|
||||
int targetActiveIndex = -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) {
|
||||
if (target.activeStartIndex == -1) {
|
||||
return;
|
||||
}
|
||||
boolean changed = false;
|
||||
// next turn is left
|
||||
if(targetActiveIndex == 0) {
|
||||
if (target.isActiveTurnMostLeft()) {
|
||||
// let only the most left lanes be enabled
|
||||
if(targetActiveLen <= activeLen) {
|
||||
activeLen = targetActiveLen;
|
||||
if (target.activeLen <= active.activeLen) {
|
||||
active.activeEndIndex -= (active.activeLen - target.activeLen);
|
||||
changed = true;
|
||||
}
|
||||
} else if(targetActiveIndex + targetActiveLen == nextLanes.length) {
|
||||
} else if (target.isActiveTurnMostRight()) {
|
||||
// next turn is right
|
||||
// let only the most right lanes be enabled
|
||||
if(targetActiveLen <= activeLen) {
|
||||
activeIndex += (activeLen - targetActiveLen);
|
||||
if (target.activeLen < active.activeLen) {
|
||||
active.activeStartIndex += (active.activeLen - target.activeLen);
|
||||
changed = true;
|
||||
}
|
||||
} else {
|
||||
// next turn is get through (take out the left and the right turn)
|
||||
if(nextLanes.length >= activeLen) {
|
||||
float ratio = (nextLanes.length / (float)activeLen);
|
||||
activeLen = (int) Math.ceil(targetActiveLen * ratio);
|
||||
activeIndex = (int) Math.floor(targetActiveIndex / ratio);
|
||||
if (target.originalLanes.length >= active.activeLen) {
|
||||
float ratio = (target.originalLanes.length / (float) active.activeLen);
|
||||
active.activeEndIndex = (int) Math.ceil(target.activeEndIndex * ratio);
|
||||
active.activeStartIndex = (int) Math.floor(target.activeStartIndex / ratio);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
@ -518,23 +529,22 @@ public class RouteResultPreparation {
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
// set the allowed lane bit
|
||||
for (int i = 0; i < lanes.length; i++) {
|
||||
if(i >= activeIndex && i < activeIndex + activeLen) {
|
||||
lanes[i] |= 1;
|
||||
for (int i = 0; i < active.disabledLanes.length; i++) {
|
||||
if (i >= active.activeStartIndex && i <= active.activeEndIndex) {
|
||||
active.disabledLanes[i] |= 1;
|
||||
}
|
||||
}
|
||||
currentTurn.setLanes(lanes);
|
||||
int turn = inferTurnFromLanes(lanes);
|
||||
TurnType currentTurn = currentSegment.getTurnType();
|
||||
currentTurn.setLanes(active.disabledLanes);
|
||||
int turn = inferTurnFromLanes(active.disabledLanes);
|
||||
if (turn != 0 && turn != currentTurn.getValue()) {
|
||||
TurnType newTurnType = TurnType.valueOf(turn, leftSide);
|
||||
newTurnType.setLanes(lanes);
|
||||
newTurnType.setLanes(active.disabledLanes);
|
||||
newTurnType.setSkipToSpeak(currentTurn.isSkipToSpeak());
|
||||
currentSegment.setTurnType(newTurnType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final int MAX_SPEAK_PRIORITY = 5;
|
||||
private int highwaySpeakPriority(String highway) {
|
||||
|
@ -585,10 +595,8 @@ public class RouteResultPreparation {
|
|||
} else {
|
||||
t = TurnType.valueOf(TurnType.TU, leftSide);
|
||||
}
|
||||
int[] lanes = getTurnLanesInfo(prev, t.getValue(), leftSide, true);
|
||||
if(lanes != null) {
|
||||
int[] lanes = getTurnLanesInfo(prev, t.getValue());
|
||||
t.setLanes(lanes);
|
||||
}
|
||||
} else if (mpi < -TURN_DEGREE_MIN) {
|
||||
if (mpi > -60) {
|
||||
t = TurnType.valueOf(TurnType.TSLR, leftSide);
|
||||
|
@ -597,14 +605,12 @@ public class RouteResultPreparation {
|
|||
} else if (mpi > -135 || !leftSide) {
|
||||
t = TurnType.valueOf(TurnType.TSHR, leftSide);
|
||||
} else {
|
||||
t = TurnType.valueOf(TurnType.TU, leftSide);
|
||||
t = TurnType.valueOf(TurnType.TRU, leftSide);
|
||||
}
|
||||
int[] lanes = getTurnLanesInfo(prev, t.getValue(), leftSide, false);
|
||||
if(lanes != null) {
|
||||
int[] lanes = getTurnLanesInfo(prev, t.getValue());
|
||||
t.setLanes(lanes);
|
||||
}
|
||||
} else {
|
||||
t = attachKeepLeftInfoAndLanes(leftSide, prev, rr, t);
|
||||
t = attachKeepLeftInfoAndLanes(leftSide, prev, rr);
|
||||
}
|
||||
if (t != null) {
|
||||
t.setTurnAngle((float) -mpi);
|
||||
|
@ -613,39 +619,34 @@ public class RouteResultPreparation {
|
|||
return t;
|
||||
}
|
||||
|
||||
private int[] getTurnLanesInfo(RouteSegmentResult prevSegm, int mainTurnType, boolean leftSide,
|
||||
boolean leftTurn) {
|
||||
private int[] getTurnLanesInfo(RouteSegmentResult prevSegm, int mainTurnType) {
|
||||
String turnLanes = getTurnLanesString(prevSegm);
|
||||
if (turnLanes == null) {
|
||||
return null;
|
||||
}
|
||||
String[] splitLaneOptions = turnLanes.split("\\|", -1);
|
||||
if (splitLaneOptions.length != countLanesMinOne(prevSegm)) {
|
||||
// Error in data or missing data
|
||||
return null;
|
||||
}
|
||||
int[] lanesArray = calculateRawTurnLanes(splitLaneOptions, mainTurnType);
|
||||
int[] lanesArray = calculateRawTurnLanes(turnLanes, 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.
|
||||
boolean leftTurn = TurnType.isLeftTurn(mainTurnType);
|
||||
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.
|
||||
TurnType.setPrimaryTurnAndReset(lanesArray, ind, TurnType.TL);
|
||||
TurnType.setSecondaryTurn(lanesArray, ind, tt);
|
||||
TurnType.setPrimaryTurn(lanesArray, ind, TurnType.TL);
|
||||
}
|
||||
} else {
|
||||
if (!TurnType.isRightTurn(tt)) {
|
||||
// This was just to make sure that there's no bad data.
|
||||
TurnType.setPrimaryTurnAndReset(lanesArray, ind, TurnType.TR);
|
||||
TurnType.setSecondaryTurn(lanesArray, ind, tt);
|
||||
TurnType.setPrimaryTurn(lanesArray, ind, TurnType.TR);
|
||||
}
|
||||
}
|
||||
setAllowedLanes(lanesArray[ind], lanesArray);
|
||||
setAllowedLanes(tt, lanesArray);
|
||||
}
|
||||
return lanesArray;
|
||||
}
|
||||
|
@ -692,89 +693,189 @@ public class RouteResultPreparation {
|
|||
return t;
|
||||
}
|
||||
|
||||
|
||||
private TurnType attachKeepLeftInfoAndLanes(boolean leftSide, RouteSegmentResult prevSegm, RouteSegmentResult currentSegm, TurnType t) {
|
||||
// keep left/right
|
||||
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;
|
||||
private class RoadSplitStructure {
|
||||
boolean keepLeft = false;
|
||||
boolean keepRight = 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()));
|
||||
if (attachedRoutes != null) {
|
||||
for (RouteSegmentResult attached : attachedRoutes) {
|
||||
double ex = MapUtils.degreesDiff(attached.getBearingBegin(), currentSegm.getBearingBegin());
|
||||
double mpi = Math.abs(MapUtils.degreesDiff(prevSegm.getBearingEnd(), attached.getBearingBegin()));
|
||||
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) {
|
||||
kl = true;
|
||||
right += countLanesMinOne(attached);
|
||||
speak = speak || rsSpeakPriority <= speakPriority;
|
||||
} else if ((ex > -TURN_DEGREE_MIN || mpi < TURN_DEGREE_MIN) && ex <= 0) {
|
||||
kr = true;
|
||||
left += countLanesMinOne(attached);
|
||||
speak = speak || rsSpeakPriority <= speakPriority;
|
||||
int lanes = countLanesMinOne(attached);
|
||||
boolean smallStraightVariation = mpi < TURN_DEGREE_MIN;
|
||||
boolean smallTargetVariation = Math.abs(ex) < TURN_DEGREE_MIN;
|
||||
boolean attachedOnTheRight = ex >= 0;
|
||||
if (attachedOnTheRight) {
|
||||
rs.roadsOnRight++;
|
||||
} else {
|
||||
rs.roadsOnLeft++;
|
||||
}
|
||||
if (smallTargetVariation || smallStraightVariation) {
|
||||
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) {
|
||||
left = 1;
|
||||
} else if(kl && right == 0) {
|
||||
right = 1;
|
||||
return rs;
|
||||
}
|
||||
|
||||
protected TurnType createSimpleKeepLeftRightTurn(boolean leftSide, RouteSegmentResult prevSegm,
|
||||
RouteSegmentResult currentSegm, RoadSplitStructure rs) {
|
||||
int current = countLanesMinOne(currentSegm);
|
||||
int[] lanes = new int[current + left + right];
|
||||
ls = current + left + right;
|
||||
int ls = current + rs.leftLanes + rs.rightLanes;
|
||||
int[] lanes = new int[ls];
|
||||
for (int it = 0; it < ls; it++) {
|
||||
if (it < left || it >= left + current) {
|
||||
if (it < rs.leftLanes || it >= rs.leftLanes + current) {
|
||||
lanes[it] = 0;
|
||||
} else {
|
||||
lanes[it] = 1;
|
||||
}
|
||||
}
|
||||
// sometimes links are
|
||||
if ((current <= left + right) && (left > 1 || right > 1)) {
|
||||
speak = true;
|
||||
if ((current <= rs.leftLanes + rs.rightLanes) && (rs.leftLanes > 1 || rs.rightLanes > 1)) {
|
||||
rs.speak = true;
|
||||
}
|
||||
|
||||
double devation = Math.abs(MapUtils.degreesDiff(prevSegm.getBearingEnd(), currentSegm.getBearingBegin()));
|
||||
boolean makeSlightTurn = devation > 5 && (!isMotorway(prevSegm) || !isMotorway(currentSegm));
|
||||
if (kl) {
|
||||
TurnType t = null;
|
||||
if (rs.keepLeft && rs.keepRight) {
|
||||
t = TurnType.valueOf(TurnType.C, leftSide);
|
||||
} else if (rs.keepLeft) {
|
||||
t = TurnType.valueOf(makeSlightTurn ? TurnType.TSLL : TurnType.KL, leftSide);
|
||||
t.setSkipToSpeak(!speak);
|
||||
}
|
||||
if (kr) {
|
||||
} else if (rs.keepRight) {
|
||||
t = TurnType.valueOf(makeSlightTurn ? TurnType.TSLR : TurnType.KR, leftSide);
|
||||
t.setSkipToSpeak(!speak);
|
||||
}
|
||||
if (t != null && lanes != null) {
|
||||
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);
|
||||
|
||||
} else {
|
||||
return t;
|
||||
}
|
||||
t.setSkipToSpeak(!rs.speak);
|
||||
t.setLanes(lanes);
|
||||
return t;
|
||||
}
|
||||
|
||||
|
@ -815,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) {
|
||||
int count = 0;
|
||||
|
@ -865,7 +928,57 @@ public class RouteResultPreparation {
|
|||
return count;
|
||||
}
|
||||
|
||||
private int[] calculateRawTurnLanes(String[] splitLaneOptions, int calcTurnType) {
|
||||
public static int[] parseTurnLanes(RouteDataObject ro, double dirToNorthEastPi) {
|
||||
String turnLanes = null;
|
||||
if (ro.getOneway() == 0) {
|
||||
// we should get direction to detect forward or backward
|
||||
double cmp = ro.directionRoute(0, true);
|
||||
if(Math.abs(MapUtils.alignAngleDifference(dirToNorthEastPi -cmp)) < Math.PI / 2) {
|
||||
turnLanes = ro.getValue("turn:lanes:forward");
|
||||
} else {
|
||||
turnLanes = ro.getValue("turn:lanes:backward");
|
||||
}
|
||||
} else {
|
||||
turnLanes = ro.getValue("turn:lanes");
|
||||
}
|
||||
if(turnLanes == null) {
|
||||
return null;
|
||||
}
|
||||
return calculateRawTurnLanes(turnLanes, 0);
|
||||
}
|
||||
|
||||
public static int[] parseLanes(RouteDataObject ro, double dirToNorthEastPi) {
|
||||
int lns = 0;
|
||||
try {
|
||||
if (ro.getOneway() == 0) {
|
||||
// we should get direction to detect forward or backward
|
||||
double cmp = ro.directionRoute(0, true);
|
||||
|
||||
if(Math.abs(MapUtils.alignAngleDifference(dirToNorthEastPi -cmp)) < Math.PI / 2) {
|
||||
if(ro.getValue("lanes:forward") != null) {
|
||||
lns = Integer.parseInt(ro.getValue("lanes:forward"));
|
||||
}
|
||||
} else {
|
||||
if(ro.getValue("lanes:backward") != null) {
|
||||
lns = Integer.parseInt(ro.getValue("lanes:backward"));
|
||||
}
|
||||
}
|
||||
if (lns == 0 && ro.getValue("lanes") != null) {
|
||||
lns = Integer.parseInt(ro.getValue("lanes")) / 2;
|
||||
}
|
||||
} else {
|
||||
lns = Integer.parseInt(ro.getValue("lanes"));
|
||||
}
|
||||
if(lns > 0 ) {
|
||||
return new int[lns];
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static int[] calculateRawTurnLanes(String turnLanes, int calcTurnType) {
|
||||
String[] splitLaneOptions = turnLanes.split("\\|", -1);
|
||||
int[] lanes = new int[splitLaneOptions.length];
|
||||
for (int i = 0; i < splitLaneOptions.length; i++) {
|
||||
String[] laneOptions = splitLaneOptions[i].split(";");
|
||||
|
@ -893,12 +1006,16 @@ public class RouteResultPreparation {
|
|||
continue;
|
||||
}
|
||||
|
||||
if (TurnType.getPrimaryTurn(lanes[i]) == 0) {
|
||||
TurnType.setPrimaryTurn(lanes, i, turn);
|
||||
final int primary = TurnType.getPrimaryTurn(lanes[i]);
|
||||
if (primary == 0) {
|
||||
TurnType.setPrimaryTurnAndReset(lanes, i, turn);
|
||||
} else {
|
||||
if (turn == calcTurnType) {
|
||||
TurnType.setSecondaryTurn(lanes, i, TurnType.getPrimaryTurn(lanes[i]));
|
||||
TurnType.setPrimaryTurn(lanes, i, turn);
|
||||
if (turn == calcTurnType ||
|
||||
(TurnType.isRightTurn(calcTurnType) && TurnType.isRightTurn(turn)) ||
|
||||
(TurnType.isLeftTurn(calcTurnType) && TurnType.isLeftTurn(turn))
|
||||
) {
|
||||
TurnType.setPrimaryTurnAndReset(lanes, i, turn);
|
||||
TurnType.setSecondaryTurn(lanes, i, primary);
|
||||
} else {
|
||||
TurnType.setSecondaryTurn(lanes, i, turn);
|
||||
}
|
||||
|
|
|
@ -162,14 +162,12 @@ public class TurnType {
|
|||
this.lanes = lanes;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// Note that the primary turn will be the one displayed on the map.
|
||||
public static void setPrimaryTurnAndReset(int[] lanes, int lane, int turnType) {
|
||||
lanes[lane] = (turnType << 1);
|
||||
}
|
||||
|
||||
public void setPrimaryTurn(int lane, int turnType) {
|
||||
lanes[lane] |= (turnType << 1);
|
||||
}
|
||||
|
||||
public static int getPrimaryTurn(int laneValue) {
|
||||
// Get the primary turn modifier for the lane
|
||||
|
@ -180,10 +178,6 @@ public class TurnType {
|
|||
lanes[lane] |= (turnType << 5);
|
||||
}
|
||||
|
||||
public void setSecondaryTurn(int lane, int turnType) {
|
||||
lanes[lane] |= (turnType << 5);
|
||||
}
|
||||
|
||||
public static int getSecondaryTurn(int laneValue) {
|
||||
// Get the primary turn modifier for the lane
|
||||
return (laneValue >> 5);
|
||||
|
@ -247,10 +241,10 @@ public class TurnType {
|
|||
}
|
||||
|
||||
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) {
|
||||
return type == TR || type == TSHR || type == TSLR;
|
||||
return type == TR || type == TSHR || type == TSLR || type == TU;
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue