diff --git a/OsmAnd-java/src/net/osmand/router/RouteResultPreparation.java b/OsmAnd-java/src/net/osmand/router/RouteResultPreparation.java index c426c9378a..9ecd54e101 100644 --- a/OsmAnd-java/src/net/osmand/router/RouteResultPreparation.java +++ b/OsmAnd-java/src/net/osmand/router/RouteResultPreparation.java @@ -10,6 +10,8 @@ import java.util.Arrays; import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Set; +import java.util.TreeSet; import net.osmand.PlatformUtil; import net.osmand.binary.BinaryInspector; @@ -485,42 +487,46 @@ public class RouteResultPreparation { } if (!changed) { TurnType tt = currentSegment.getTurnType(); - boolean goForward = false; - if (tt.getValue() == TurnType.C && tt.getLanes() != null) { - for (int it = 0; it < tt.getLanes().length; it++) { - int turn = tt.getLanes()[it]; - if (TurnType.getPrimaryTurn(turn) == TurnType.C || - TurnType.getSecondaryTurn(turn) == TurnType.C || - TurnType.getTertiaryTurn(turn) == TurnType.C) { - goForward = true; - break; - } - } - } - if(goForward) { - for (int it = 0; it < tt.getLanes().length; it++) { - int turn = tt.getLanes()[it]; - if (TurnType.getPrimaryTurn(turn) != TurnType.C) { - if(TurnType.getSecondaryTurn(turn) == TurnType.C) { - int st = TurnType.getSecondaryTurn(turn); - TurnType.setSecondaryTurn(tt.getLanes(), it, TurnType.getPrimaryTurn(turn)); - TurnType.setPrimaryTurn(tt.getLanes(), it, st); - } else if(TurnType.getTertiaryTurn(turn) == TurnType.C) { - int st = TurnType.getTertiaryTurn(turn); - TurnType.setTertiaryTurn(tt.getLanes(), it, TurnType.getPrimaryTurn(turn)); - TurnType.setPrimaryTurn(tt.getLanes(), it, st); - } else { - tt.getLanes()[it] = turn & (~1); - } - } - } - } + inferActiveTurnLanesFromTurn(tt, TurnType.C); } nextSegment = currentSegment; dist = 0; } } } + + private void inferActiveTurnLanesFromTurn(TurnType tt, int type) { + boolean found = false; + if (tt.getValue() == type && tt.getLanes() != null) { + for (int it = 0; it < tt.getLanes().length; it++) { + int turn = tt.getLanes()[it]; + if (TurnType.getPrimaryTurn(turn) == type || + TurnType.getSecondaryTurn(turn) == type || + TurnType.getTertiaryTurn(turn) == type) { + found = true; + break; + } + } + } + if(found) { + for (int it = 0; it < tt.getLanes().length; it++) { + int turn = tt.getLanes()[it]; + if (TurnType.getPrimaryTurn(turn) != type) { + if(TurnType.getSecondaryTurn(turn) == type) { + int st = TurnType.getSecondaryTurn(turn); + TurnType.setSecondaryTurn(tt.getLanes(), it, TurnType.getPrimaryTurn(turn)); + TurnType.setPrimaryTurn(tt.getLanes(), it, st); + } else if(TurnType.getTertiaryTurn(turn) == type) { + int st = TurnType.getTertiaryTurn(turn); + TurnType.setTertiaryTurn(tt.getLanes(), it, TurnType.getPrimaryTurn(turn)); + TurnType.setPrimaryTurn(tt.getLanes(), it, st); + } else { + tt.getLanes()[it] = turn & (~1); + } + } + } + } + } private class MergeTurnLaneTurn { TurnType turn; @@ -610,17 +616,36 @@ public class RouteResultPreparation { } TurnType currentTurn = currentSegment.getTurnType(); currentTurn.setLanes(active.disabledLanes); - // There is a test which fails that assumption and there is no contr-test how it could help here -// int turn = inferTurnFromLanes(active.disabledLanes); -// if (turn != 0 && turn != currentTurn.getValue()) { -// TurnType newTurnType = TurnType.valueOf(turn, leftSide); -// newTurnType.setLanes(active.disabledLanes); -// newTurnType.setSkipToSpeak(currentTurn.isSkipToSpeak()); -// currentSegment.setTurnType(newTurnType); -// } + inferCommonActiveLane(currentTurn); return true; } + private void inferCommonActiveLane(TurnType currentTurn) { + int[] lanes = currentTurn.getLanes(); + int singleTurn = 0; + for(int i = 0; i < lanes.length; i++) { + if(lanes[i] % 2 == 1 && TurnType.getSecondaryTurn(lanes[i]) == 0) { + singleTurn = TurnType.getPrimaryTurn(lanes[i]); + break; + } + } + if(singleTurn == 0) { + singleTurn = currentTurn.getValue(); + } + for(int i = 0; i < lanes.length; i++) { + if(lanes[i] % 2 == 1 && TurnType.getPrimaryTurn(lanes[i]) != singleTurn) { + if(TurnType.getSecondaryTurn(lanes[i]) == singleTurn) { + TurnType.setSecondaryTurn(lanes, i, TurnType.getPrimaryTurn(lanes[i])); + TurnType.setPrimaryTurn(lanes, i, singleTurn); + } else if(TurnType.getTertiaryTurn(lanes[i]) == singleTurn) { + TurnType.setTertiaryTurn(lanes, i, TurnType.getPrimaryTurn(lanes[i])); + TurnType.setPrimaryTurn(lanes, i, singleTurn); + } + } + } + + } + private static final int MAX_SPEAK_PRIORITY = 5; private int highwaySpeakPriority(String highway) { if(highway == null || highway.endsWith("track") || highway.endsWith("services") || highway.endsWith("service") @@ -795,7 +820,9 @@ public class RouteResultPreparation { boolean keepLeft = false; boolean keepRight = false; boolean speak = false; + List leftLanesInfo = new ArrayList(); int leftLanes = 0; + List rightLanesInfo = new ArrayList(); int rightLanes = 0; int roadsOnLeft = 0; int addRoadsOnLeft = 0; @@ -837,9 +864,9 @@ public class RouteResultPreparation { if (rs.keepLeft || rs.keepRight) { String[] splitLaneOptions = turnLanes.split("\\|", -1); int activeBeginIndex = findActiveIndex(rawLanes, splitLaneOptions, rs.leftLanes, true, - rs.roadsOnLeft, rs.addRoadsOnLeft); + rs.leftLanesInfo, rs.roadsOnLeft, rs.addRoadsOnLeft); int activeEndIndex = findActiveIndex(rawLanes, splitLaneOptions, rs.rightLanes, false, - rs.roadsOnRight, rs.addRoadsOnRight); + rs.rightLanesInfo, rs.roadsOnRight, rs.addRoadsOnRight); if (activeBeginIndex == -1 || activeEndIndex == -1 || activeBeginIndex > activeEndIndex) { // something went wrong return createSimpleKeepLeftRightTurn(leftSide, prevSegm, currentSegm, rs); @@ -859,12 +886,12 @@ public class RouteResultPreparation { int sturn = TurnType.getSecondaryTurn(rawLanes[k]); boolean active = false; if (turn == TurnType.C) { - active = true; if ((TurnType.isRightTurn(sturn) && rs.roadsOnRight == 0) || (TurnType.isLeftTurn(sturn) && rs.roadsOnLeft == 0)) { TurnType.setPrimaryTurn(rawLanes, k, sturn); TurnType.setSecondaryTurn(rawLanes, k, turn); } + 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 @@ -887,26 +914,48 @@ public class RouteResultPreparation { } protected int findActiveIndex(int[] rawLanes, String[] splitLaneOptions, int lanes, boolean left, - int roads, int addRoads) { + List lanesInfo, int roads, int addRoads) { int activeStartIndex = -1; boolean lookupSlightTurn = addRoads > 0; + Set addedTurns = new TreeSet(); + // if we have information increase number of roads per each turn direction + int diffTurnRoads = roads; + for(int[] li : lanesInfo) { + TIntHashSet set = new TIntHashSet(); + if(li != null) { + for(int k = 0; k < li.length; k++) { + TurnType.collectTurnTypes(li[k], set); + } + } + diffTurnRoads += Math.max(set.size() - 1, 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 --; + String[] laneTurns = splitLaneOptions[ind].split(";"); + int cnt = 0; + for(String lTurn : laneTurns) { + boolean added = addedTurns.add(lTurn); + if(added) { + cnt++; + diffTurnRoads --; + } } +// 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 --; +// } lanes -= cnt; // we already found slight turn others are turn in different direction lookupSlightTurn = false; } - if (lanes < 0 || roads < 0) { + if (lanes < 0 || diffTurnRoads < 0) { activeStartIndex = ind; break; } @@ -923,6 +972,7 @@ public class RouteResultPreparation { double mpi = Math.abs(MapUtils.degreesDiff(prevSegm.getBearingEnd(), attached.getBearingBegin())); int rsSpeakPriority = highwaySpeakPriority(attached.getObject().getHighway()); int lanes = countLanesMinOne(attached); + int[] turnLanes = parseTurnLanes(attached.getObject(), attached.getBearingBegin()); boolean smallStraightVariation = mpi < TURN_DEGREE_MIN; boolean smallTargetVariation = Math.abs(ex) < TURN_DEGREE_MIN; boolean attachedOnTheRight = ex >= 0; @@ -936,9 +986,15 @@ public class RouteResultPreparation { if (attachedOnTheRight) { rs.keepLeft = true; rs.rightLanes += lanes; + if(turnLanes != null) { + rs.rightLanesInfo.add(turnLanes); + } } else { rs.keepRight = true; rs.leftLanes += lanes; + if(turnLanes != null) { + rs.leftLanesInfo.add(turnLanes); + } } rs.speak = rs.speak || rsSpeakPriority <= speakPriority; } else { diff --git a/OsmAnd-java/src/net/osmand/router/TurnType.java b/OsmAnd-java/src/net/osmand/router/TurnType.java index 085a514b6a..4cad6226a2 100644 --- a/OsmAnd-java/src/net/osmand/router/TurnType.java +++ b/OsmAnd-java/src/net/osmand/router/TurnType.java @@ -1,5 +1,7 @@ package net.osmand.router; +import gnu.trove.set.hash.TIntHashSet; + public class TurnType { public static final int C = 1;//"C"; // continue (go straight) //$NON-NLS-1$ public static final int TL = 2; // turn left //$NON-NLS-1$ @@ -215,7 +217,33 @@ public class TurnType { return (laneValue >> 10); } - + public static String toString(int[] lns) { + 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(); + } + int tt = TurnType.getTertiaryTurn(lns[h]); + if (tt != 0) { + s += "," + TurnType.valueOf(tt, false).toXmlString(); + } + + } + s += ""; + return s; + } public int[] getLanes() { return lanes; } @@ -291,4 +319,21 @@ public class TurnType { public static boolean isSlightTurn(int type) { return type == TSLL || type == TSLR || type == C || type == KL || type == KR; } + + public static void collectTurnTypes(int lane, TIntHashSet set) { + int pt = TurnType.getPrimaryTurn(lane); + if(pt != 0) { + set.add(pt); + } + pt = TurnType.getSecondaryTurn(lane); + if(pt != 0) { + set.add(pt); + } + pt = TurnType.getTertiaryTurn(lane); + if(pt != 0) { + set.add(pt); + } + } + + } diff --git a/OsmAnd-java/test/java/net/osmand/router/RouteResultPreparationTest.java b/OsmAnd-java/test/java/net/osmand/router/RouteResultPreparationTest.java index 5bd0037ce4..f2ba068770 100644 --- a/OsmAnd-java/test/java/net/osmand/router/RouteResultPreparationTest.java +++ b/OsmAnd-java/test/java/net/osmand/router/RouteResultPreparationTest.java @@ -132,31 +132,7 @@ public class RouteResultPreparationTest { private String getLanesString(RouteSegmentResult segment) { final int[] lns = segment.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(); - } - int tt = TurnType.getTertiaryTurn(lns[h]); - if (tt != 0) { - s += "," + TurnType.valueOf(tt, false).toXmlString(); - } - - } - s += ""; - return s; + return TurnType.toString(lns); } return null; } diff --git a/OsmAnd-java/test/resources/test_turn_lanes.json b/OsmAnd-java/test/resources/test_turn_lanes.json index e26d02d95a..f6dbe578fd 100644 --- a/OsmAnd-java/test/resources/test_turn_lanes.json +++ b/OsmAnd-java/test/resources/test_turn_lanes.json @@ -44,7 +44,7 @@ } }, { - "testName": "2.Valley View Lane TU Highway 161 Service Road", + "testName": "2. Valley View Lane TU Highway 161 Service Road", "startPoint": { "latitude": 45.694859388262195, "longitude": 35.467755138874054 @@ -58,7 +58,39 @@ } }, { - "testName": "2.1.Highway 161 Service Road to Lyndon B Johnson Freeway (I 635) link", + "testName": "2.1 Valley View Lane C Highway 161 Service Road", + "startPoint": { + "latitude": 45.694859388262195, + "longitude": 35.467755138874054 + }, + "endPoint": { + "latitude": 45.692210341270474, + "longitude": 35.462753385305405 + }, + "expectedResults": { + "26705": "TU|TL|+C,TL|C|C,TR", + "26897": "TL|+C,TL|C|C,TR", + "26739": "TL|+C,TL|C|C" + } + }, + { + "testName": "2.2 Valley View Lane TL Highway 161 Service Road", + "startPoint": { + "latitude": 45.694859388262195, + "longitude": 35.467755138874054 + }, + "endPoint": { + "latitude": 45.69354592206, + "longitude": 35.46670215889 + }, + "expectedResults": { + "26705": "TU|+TL|+TL,C|C|C,TR", + "26897": "+TL|+TL,C|C|C,TR", + "26605": "TL:+TL|+TL,C|C|C" + } + }, + { + "testName": "2.3. Highway 161 Service Road to Lyndon B Johnson Freeway (I 635) link", "startPoint": { "latitude": 45.69384114913531, "longitude": 35.46754793822765 @@ -74,7 +106,7 @@ } }, { - "testName": "3.Motorway link from Ringweg Zuid TL Amstelveenseweg", + "testName": "3. Motorway link from Ringweg Zuid TL Amstelveenseweg", "startPoint": { "latitude": 45.6971206184178, "longitude": 35.51630312204361