diff --git a/brouter-core/src/main/java/btools/router/OsmPath.java b/brouter-core/src/main/java/btools/router/OsmPath.java index e2abe00..7360bec 100644 --- a/brouter-core/src/main/java/btools/router/OsmPath.java +++ b/brouter-core/src/main/java/btools/router/OsmPath.java @@ -121,11 +121,26 @@ abstract class OsmPath implements OsmLinkHolder { protected abstract void resetState(); + static int seg = 1; protected void addAddionalPenalty(OsmTrack refTrack, boolean detailMode, OsmPath origin, OsmLink link, RoutingContext rc) { byte[] description = link.descriptionBitmap; - if (description == null) { - return; // could be a beeline path + if (description == null) { // could be a beeline path + message = new MessageData(); + if (message != null) { + message.turnangle = 0; + message.time = (float) 1; + message.energy = (float) 0; + message.priorityclassifier = 0; + message.classifiermask = 0; + message.lon = targetNode.getILon(); + message.lat = targetNode.getILat(); + message.ele = Short.MIN_VALUE; + message.linkdist = sourceNode.calcDistance(targetNode); + message.wayKeyValues = "direct_segment=" + seg; + } + seg++; + return; } boolean recordTransferNodes = detailMode || rc.countTraffic; @@ -208,16 +223,18 @@ abstract class OsmPath implements OsmLinkHolder { int lon2; int lat2; short ele2; + short originEle2; if (transferNode == null) { lon2 = targetNode.ilon; lat2 = targetNode.ilat; - ele2 = targetNode.selev; + originEle2 = targetNode.selev; } else { lon2 = transferNode.ilon; lat2 = transferNode.ilat; - ele2 = transferNode.selev; + originEle2 = transferNode.selev; } + ele2 = originEle2; boolean isStartpoint = lon0 == -1 && lat0 == -1; @@ -334,13 +351,13 @@ abstract class OsmPath implements OsmLinkHolder { message.classifiermask = classifiermask; message.lon = lon2; message.lat = lat2; - message.ele = ele2; + message.ele = originEle2; message.wayKeyValues = rc.expctxWay.getKeyValueDescription(isReverse, description); } if (stopAtEndpoint) { if (recordTransferNodes) { - originElement = OsmPathElement.create(rc.ilonshortest, rc.ilatshortest, ele2, originElement, rc.countTraffic); + originElement = OsmPathElement.create(rc.ilonshortest, rc.ilatshortest, originEle2, originElement, rc.countTraffic); originElement.cost = cost; if (message != null) { originElement.message = message; @@ -366,7 +383,7 @@ abstract class OsmPath implements OsmLinkHolder { transferNode = transferNode.next; if (recordTransferNodes) { - originElement = OsmPathElement.create(lon2, lat2, ele2, originElement, rc.countTraffic); + originElement = OsmPathElement.create(lon2, lat2, originEle2, originElement, rc.countTraffic); originElement.cost = cost; originElement.addTraffic(traffic); traffic = 0; diff --git a/brouter-core/src/main/java/btools/router/OsmTrack.java b/brouter-core/src/main/java/btools/router/OsmTrack.java index abd2b07..2896b9d 100644 --- a/brouter-core/src/main/java/btools/router/OsmTrack.java +++ b/brouter-core/src/main/java/btools/router/OsmTrack.java @@ -1080,7 +1080,9 @@ public final class OsmTrack { VoiceHintProcessor vproc = new VoiceHintProcessor(rc.turnInstructionCatchingRange, rc.turnInstructionRoundabouts); List results = vproc.process(inputs); - for (VoiceHint hint : results) { + double minDistance = getMinDistance(); + List < VoiceHint > resultsLast = vproc.postProcess(results, rc.turnInstructionCatchingRange, minDistance); + for (VoiceHint hint: resultsLast) { voiceHints.list.add(hint); } diff --git a/brouter-core/src/main/java/btools/router/RoutingEngine.java b/brouter-core/src/main/java/btools/router/RoutingEngine.java index ad8c141..b5a2d89 100644 --- a/brouter-core/src/main/java/btools/router/RoutingEngine.java +++ b/brouter-core/src/main/java/btools/router/RoutingEngine.java @@ -297,7 +297,7 @@ public class RoutingEngine extends Thread { int ind = s.indexOf("%"); if (ind != -1) s = s.substring(0, ind); - ind = s.indexOf("�"); + ind = s.indexOf("°"); if (ind != -1) s = s.substring(0, ind); tmpincline = Double.parseDouble(s.trim()); @@ -313,7 +313,7 @@ public class RoutingEngine extends Thread { if (startincline == 0) { startincline = tmpincline; } else if (startincline < 0 && tmpincline > 0) { - // for the way ?p find the exit point + // for the way up find the exit point double diff = endElev - selev; tmpincline = diff / (distRest / 100.); } @@ -733,7 +733,7 @@ public class RoutingEngine extends Thread { lon2 = t.nodes.get(i).getILon(); lat2 = t.nodes.get(i).getILat(); nLast = t.nodes.get(0); - dist = routingContext.calcDistance(lon0, lat0, lon1, lat1); + dist = nLast.calcDistance(n); } else { lon0 = t.nodes.get(i - 2).getILon(); lat0 = t.nodes.get(i - 2).getILat(); @@ -742,7 +742,7 @@ public class RoutingEngine extends Thread { lon2 = t.nodes.get(i).getILon(); lat2 = t.nodes.get(i).getILat(); nLast = t.nodes.get(i - 1); - dist = routingContext.calcDistance(lon1, lat1, lon2, lat2); + dist = nLast.calcDistance(n); } angle = routingContext.anglemeter.calcAngle(lon0, lat0, lon1, lat1, lon2, lat2); n.message.linkdist = dist; diff --git a/brouter-core/src/main/java/btools/router/VoiceHint.java b/brouter-core/src/main/java/btools/router/VoiceHint.java index 18b2d54..5c8386d 100644 --- a/brouter-core/src/main/java/btools/router/VoiceHint.java +++ b/brouter-core/src/main/java/btools/router/VoiceHint.java @@ -19,11 +19,13 @@ public class VoiceHint { static final int TSHR = 7; // turn sharply right static final int KL = 8; // keep left static final int KR = 9; // keep right - static final int TU = 10; // U-turn - static final int TRU = 11; // Right U-turn - static final int OFFR = 12; // Off route - static final int RNDB = 13; // Roundabout - static final int RNLB = 14; // Roundabout left + static final int TLU = 10; // U-turn + static final int TU = 11; // 180 degree u-turn + static final int TRU = 12; // Right U-turn + static final int OFFR = 13; // Off route + static final int RNDB = 14; // Roundabout + static final int RNLB = 15; // Roundabout left + static final int BL = 16; // Beeline routing int ilon; int ilat; @@ -39,7 +41,7 @@ public class VoiceHint { return oldWay == null ? 0.f : oldWay.time; } - float angle; + float angle = Float.MAX_VALUE; boolean turnAngleConsumed; boolean needsRealTurn; @@ -67,8 +69,13 @@ public class VoiceHint { return roundaboutExit; } + /* + * used by comment style, osmand style + */ public String getCommandString() { switch (cmd) { + case TLU: + return "TU"; // should be changed to TLU when osmand uses new voice hint constants case TU: return "TU"; case TSHL: @@ -100,8 +107,51 @@ public class VoiceHint { } } + /* + * used by trkpt/sym style + */ + public String getCommandString(int c) { + switch (c) { + case TLU: + return "TLU"; + case TU: + return "TU"; + case TSHL: + return "TSHL"; + case TL: + return "TL"; + case TSLL: + return "TSLL"; + case KL: + return "KL"; + case C: + return "C"; + case KR: + return "KR"; + case TSLR: + return "TSLR"; + case TR: + return "TR"; + case TSHR: + return "TSHR"; + case TRU: + return "TRU"; + case RNDB: + return "RNDB" + roundaboutExit; + case RNLB: + return "RNLB" + (-roundaboutExit); + default: + return "unknown command: " + c; + } + } + + /* + * used by gpsies style + */ public String getSymbolString() { switch (cmd) { + case TLU: + return "TU"; case TU: return "TU"; case TSHL: @@ -133,8 +183,53 @@ public class VoiceHint { } } + /* + * used by new locus trkpt style + */ + public String getLocusSymbolString() { + switch (cmd) { + case TLU: + return "u-turn_left"; + case TU: + return "u-turn"; + case TSHL: + return "left_sharp"; + case TL: + return "left"; + case TSLL: + return "left_slight"; + case KL: + return "stay_left"; // ? + case C: + return "straight"; + case KR: + return "stay_right"; // ? + case TSLR: + return "right_slight"; + case TR: + return "right"; + case TSHR: + return "right_sharp"; + case TRU: + return "u-turn_right"; + case RNDB: + return "roundabout_e" + roundaboutExit; + case RNLB: + return "roundabout_e" + (-roundaboutExit); + case BL: + return "beeline"; + default: + throw new IllegalArgumentException("unknown command: " + cmd); + } + } + + /* + * used by osmand style + */ public String getMessageString() { switch (cmd) { + case TLU: + return "u-turn"; // should be changed to u-turn-left when osmand uses new voice hint constants case TU: return "u-turn"; case TSHL: @@ -156,7 +251,7 @@ public class VoiceHint { case TSHR: return "sharp right"; case TRU: - return "u-turn"; + return "u-turn"; // should be changed to u-turn-right when osmand uses new voice hint constants case RNDB: return "Take exit " + roundaboutExit; case RNLB: @@ -166,10 +261,15 @@ public class VoiceHint { } } + /* + * used by old locus style + */ public int getLocusAction() { switch (cmd) { - case TU: + case TLU: return 13; + case TU: + return 12; case TSHL: return 5; case TL: @@ -199,8 +299,13 @@ public class VoiceHint { } } + /* + * used by orux style + */ public int getOruxAction() { switch (cmd) { + case TLU: + return 1003; case TU: return 1003; case TSHL: @@ -232,6 +337,86 @@ public class VoiceHint { } } + /* + * used by cruiser, equivalent to getCommandString() - osmand style - when osmand changes the voice hint constants + */ + public String getCruiserCommandString() { + switch (cmd) { + case TLU: + return "TLU"; + case TU: + return "TU"; + case TSHL: + return "TSHL"; + case TL: + return "TL"; + case TSLL: + return "TSLL"; + case KL: + return "KL"; + case C: + return "C"; + case KR: + return "KR"; + case TSLR: + return "TSLR"; + case TR: + return "TR"; + case TSHR: + return "TSHR"; + case TRU: + return "TRU"; + case RNDB: + return "RNDB" + roundaboutExit; + case RNLB: + return "RNLB" + (-roundaboutExit); + case BL: + return "BL"; + default: + throw new IllegalArgumentException("unknown command: " + cmd); + } + } + + /* + * used by cruiser, equivalent to getMessageString() - osmand style - when osmand changes the voice hint constants + */ + public String getCruiserMessageString() { + switch (cmd) { + case TLU: + return "u-turn left"; + case TU: + return "u-turn"; + case TSHL: + return "sharp left"; + case TL: + return "left"; + case TSLL: + return "slight left"; + case KL: + return "keep left"; + case C: + return "straight"; + case KR: + return "keep right"; + case TSLR: + return "slight right"; + case TR: + return "right"; + case TSHR: + return "sharp right"; + case TRU: + return "u-turn right"; + case RNDB: + return "Take exit " + roundaboutExit; + case RNLB: + return "Take exit " + (-roundaboutExit); + case BL: + return "Beeline"; + default: + throw new IllegalArgumentException("unknown command: " + cmd); + } + } + public void calcCommand() { float lowerBadWayAngle = -181; float higherBadWayAngle = 181; @@ -252,58 +437,98 @@ public class VoiceHint { float cmdAngle = angle; // fall back to local angle if otherwise inconsistent - if (lowerBadWayAngle > angle || higherBadWayAngle < angle) { + //if ( lowerBadWayAngle > angle || higherBadWayAngle < angle ) + //{ + //cmdAngle = goodWay.turnangle; + //} + if (angle == Float.MAX_VALUE) { cmdAngle = goodWay.turnangle; } + if (cmd == BL) return; if (roundaboutExit > 0) { cmd = RNDB; } else if (roundaboutExit < 0) { cmd = RNLB; - } else if (cmdAngle < -159.) { + } else if (is180DegAngle(cmdAngle) && cmdAngle <= -179.f && higherBadWayAngle == 181.f && lowerBadWayAngle == -181.f) { cmd = TU; - } else if (cmdAngle < -135.) { + } else if (cmdAngle < -159.f) { + cmd = TLU; + } else if (cmdAngle < -135.f) { cmd = TSHL; - } else if (cmdAngle < -45.) { + } else if (cmdAngle < -45.f) { // a TL can be pushed in either direction by a close-by alternative - if (higherBadWayAngle > -90. && higherBadWayAngle < -15. && lowerBadWayAngle < -180.) { + if (cmdAngle < -95.f && higherBadWayAngle < -30.f && lowerBadWayAngle < -180.f) { cmd = TSHL; - } else if (lowerBadWayAngle > -180. && lowerBadWayAngle < -90. && higherBadWayAngle > 0.) { + } else if (cmdAngle > -85.f && lowerBadWayAngle > -180.f && higherBadWayAngle > -10.f) { cmd = TSLL; } else { - cmd = TL; + if (cmdAngle < -110.f) { + cmd = TSHL; + } else if (cmdAngle > -60.f) { + cmd = TSLL; + } else { + cmd = TL; + } } - } else if (cmdAngle < -21.) { - if (cmd != KR) // don't overwrite KR with TSLL - { + } else if (cmdAngle < -21.f) { + if (cmd != KR) { // don't overwrite KR with TSLL cmd = TSLL; } - } else if (cmdAngle < 21.) { - if (cmd != KR && cmd != KL) // don't overwrite KL/KR hints! - { + } else if (cmdAngle < -5.f) { + if (lowerBadWayAngle < -100.f && higherBadWayAngle < 45.f) { + cmd = TSLL; + } else if (lowerBadWayAngle >= -100.f && higherBadWayAngle < 45.f) { + cmd = KL; + } else { cmd = C; } - } else if (cmdAngle < 45.) { - if (cmd != KL) // don't overwrite KL with TSLR - { - cmd = TSLR; + } else if (cmdAngle < 5.f) { + if (lowerBadWayAngle > -30.f) { + cmd = KR; + } else if (higherBadWayAngle < 30.f) { + cmd = KL; + } else { + cmd = C; } - } else if (cmdAngle < 135.) { + } else if (cmdAngle < 21.f) { // a TR can be pushed in either direction by a close-by alternative - if (higherBadWayAngle > 90. && higherBadWayAngle < 180. && lowerBadWayAngle < 0.) { + if (lowerBadWayAngle > -45.f && higherBadWayAngle > 100.f) { cmd = TSLR; - } else if (lowerBadWayAngle > 15. && lowerBadWayAngle < 90. && higherBadWayAngle > 180.) { + } else if (lowerBadWayAngle > -45.f && higherBadWayAngle <= 100.f) { + cmd = KR; + } else { + cmd = C; + } + } else if (cmdAngle < 45.f) { + cmd = TSLR; + } else if (cmdAngle < 135.f) { + if (cmdAngle < 85.f && higherBadWayAngle < 180.f && lowerBadWayAngle < 10.f) { + cmd = TSLR; + } else if (cmdAngle > 95.f && lowerBadWayAngle > 30.f && higherBadWayAngle > 180.f) { cmd = TSHR; } else { - cmd = TR; + if (cmdAngle > 110.) { + cmd = TSHR; + } else if (cmdAngle < 60.) { + cmd = TSLR; + } else { + cmd = TR; + } } - } else if (cmdAngle < 159.) { + } else if (cmdAngle < 159.f) { cmd = TSHR; + } else if (is180DegAngle(cmdAngle) && cmdAngle >= 179.f && higherBadWayAngle == 181.f && lowerBadWayAngle == -181.f) { + cmd = TU; } else { cmd = TRU; } } + static boolean is180DegAngle(float angle) { + return (Math.abs(angle) <= 180.f && Math.abs(angle) >= 179.f); + } + public String formatGeometry() { float oldPrio = oldWay == null ? 0.f : oldWay.priorityclassifier; StringBuilder sb = new StringBuilder(30); diff --git a/brouter-core/src/main/java/btools/router/VoiceHintProcessor.java b/brouter-core/src/main/java/btools/router/VoiceHintProcessor.java index 7ae23a7..d7ac010 100644 --- a/brouter-core/src/main/java/btools/router/VoiceHintProcessor.java +++ b/brouter-core/src/main/java/btools/router/VoiceHintProcessor.java @@ -9,18 +9,22 @@ import java.util.ArrayList; import java.util.List; public final class VoiceHintProcessor { - private double catchingRange; // range to catch angles and merge turns + + double SIGNIFICANT_ANGLE = 22.5; + double INTERNAL_CATCHING_RANGE = 2.; + + // private double catchingRange; // range to catch angles and merge turns private boolean explicitRoundabouts; public VoiceHintProcessor(double catchingRange, boolean explicitRoundabouts) { - this.catchingRange = catchingRange; + // this.catchingRange = catchingRange; this.explicitRoundabouts = explicitRoundabouts; } private float sumNonConsumedWithinCatchingRange(List inputs, int offset) { double distance = 0.; float angle = 0.f; - while (offset >= 0 && distance < catchingRange) { + while (offset >= 0 && distance < INTERNAL_CATCHING_RANGE) { VoiceHint input = inputs.get(offset--); if (input.turnAngleConsumed) { break; @@ -60,6 +64,10 @@ public final class VoiceHintProcessor { for (int hintIdx = 0; hintIdx < inputs.size(); hintIdx++) { VoiceHint input = inputs.get(hintIdx); + if (input.cmd == VoiceHint.BL) { + results.add(input); + continue; + } float turnAngle = input.goodWay.turnangle; distance += input.goodWay.linkdist; int currentPrio = input.goodWay.getPrio(); @@ -67,6 +75,7 @@ public final class VoiceHintProcessor { int minPrio = Math.min(oldPrio, currentPrio); boolean isLink2Highway = input.oldWay.isLinktType() && !input.goodWay.isLinktType(); + boolean isHighway2Link = !input.oldWay.isLinktType() && input.goodWay.isLinktType(); if (input.oldWay.isRoundabout()) { roundAboutTurnAngle += sumNonConsumedWithinCatchingRange(inputs, hintIdx); @@ -101,14 +110,18 @@ public final class VoiceHintProcessor { float minAngle = 180.f; float minAbsAngeRaw = 180.f; + boolean isBadwayLink = false; + if (input.badWays != null) { for (MessageData badWay : input.badWays) { int badPrio = badWay.getPrio(); float badTurn = badWay.turnangle; + if (badWay.isLinktType()) { + isBadwayLink = true; + } + boolean isBadHighway2Link = !input.oldWay.isLinktType() && badWay.isLinktType(); - boolean isHighway2Link = !input.oldWay.isLinktType() && badWay.isLinktType(); - - if (badPrio > maxPrioAll && !isHighway2Link) { + if (badPrio > maxPrioAll && !isBadHighway2Link) { maxPrioAll = badPrio; } @@ -140,12 +153,17 @@ public final class VoiceHintProcessor { } } - boolean hasSomethingMoreStraight = Math.abs(turnAngle) - minAbsAngeRaw > 20.; + boolean hasSomethingMoreStraight = (Math.abs(turnAngle) - minAbsAngeRaw) > 20.; // unconditional triggers are all junctions with // - higher detour prios than the minimum route prio (except link->highway junctions) // - or candidate detours with higher prio then the route exit leg - boolean unconditionalTrigger = hasSomethingMoreStraight || (maxPrioAll > minPrio && !isLink2Highway) || (maxPrioCandidates > currentPrio); + boolean unconditionalTrigger = hasSomethingMoreStraight || + (maxPrioAll > minPrio && !isLink2Highway) || + (maxPrioCandidates > currentPrio) || + VoiceHint.is180DegAngle(turnAngle) || + (!isHighway2Link && isBadwayLink && Math.abs(turnAngle) > 5.f) || + (isHighway2Link && !isBadwayLink && Math.abs(turnAngle) < 5.f); // conditional triggers (=real turning angle required) are junctions // with candidate detours equal in priority than the route exit leg @@ -158,11 +176,13 @@ public final class VoiceHintProcessor { input.needsRealTurn = (!unconditionalTrigger) && isStraight; // check for KR/KL - if (maxAngle < turnAngle && maxAngle > turnAngle - 45.f - (turnAngle > 0.f ? turnAngle : 0.f)) { - input.cmd = VoiceHint.KR; - } - if (minAngle > turnAngle && minAngle < turnAngle + 45.f - (turnAngle < 0.f ? turnAngle : 0.f)) { - input.cmd = VoiceHint.KL; + if (Math.abs(turnAngle) > 5.) { // don't use to small angles + if (maxAngle < turnAngle && maxAngle > turnAngle - 45.f - (Math.max(turnAngle, 0.f))) { + input.cmd = VoiceHint.KR; + } + if (minAngle > turnAngle && minAngle < turnAngle + 45.f - (Math.min(turnAngle, 0.f))) { + input.cmd = VoiceHint.KL; + } } input.angle = sumNonConsumedWithinCatchingRange(inputs, hintIdx); @@ -170,7 +190,7 @@ public final class VoiceHintProcessor { distance = 0.; results.add(input); } - if (results.size() > 0 && distance < catchingRange) { + if (results.size() > 0 && distance < INTERNAL_CATCHING_RANGE) { //catchingRange results.get(results.size() - 1).angle += sumNonConsumedWithinCatchingRange(inputs, hintIdx); } } @@ -185,10 +205,10 @@ public final class VoiceHintProcessor { if (hint.cmd == 0) { hint.calcCommand(); } - if (!(hint.needsRealTurn && hint.cmd == VoiceHint.C)) { + if (!(hint.needsRealTurn && (hint.cmd == VoiceHint.C || hint.cmd == VoiceHint.BL))) { double dist = hint.distanceToNext; // sum up other hints within the catching range (e.g. 40m) - while (dist < catchingRange && i > 0) { + while (dist < INTERNAL_CATCHING_RANGE && i > 0) { VoiceHint h2 = results.get(i - 1); dist = h2.distanceToNext; hint.distanceToNext += dist; @@ -207,9 +227,92 @@ public final class VoiceHintProcessor { } hint.calcCommand(); results2.add(hint); + } else if (hint.cmd == VoiceHint.BL) { + results2.add(hint); + } else { + if (results2.size() > 0) + results2.get(results2.size() - 1).distanceToNext += hint.distanceToNext; } } return results2; } + public List postProcess(List inputs, double catchingRange, double minRange) { + List results = new ArrayList(); + double distance = 0; + VoiceHint inputLast = null; + ArrayList tmpList = new ArrayList<>(); + for (int hintIdx = 0; hintIdx < inputs.size(); hintIdx++) { + VoiceHint input = inputs.get(hintIdx); + + if (input.cmd == VoiceHint.C && !input.goodWay.isLinktType()) { + int badWayPrio = 0; + for (MessageData md : input.badWays) { + badWayPrio = Math.max(badWayPrio, md.getPrio()); + } + if (input.goodWay.getPrio() < badWayPrio) { + results.add(input); + } else { + if (inputLast != null) { // when drop add distance to last + inputLast.distanceToNext += input.distanceToNext; + } + continue; + } + } else { + if (input.distanceToNext < catchingRange) { + double dist = input.distanceToNext; + float angles = input.angle; + int i = 1; + boolean save = true; + tmpList.clear(); + while (dist < catchingRange && hintIdx + i < inputs.size()) { + VoiceHint h2 = inputs.get(hintIdx + i); + dist += h2.distanceToNext; + angles += h2.angle; + if (VoiceHint.is180DegAngle(input.angle) || VoiceHint.is180DegAngle(h2.angle)) { // u-turn, 180 degree + save = true; + break; + } else if (Math.abs(angles) > 180 - SIGNIFICANT_ANGLE) { // u-turn, collects e.g. two left turns in range + input.angle = angles; + input.calcCommand(); + input.distanceToNext += h2.distanceToNext; + save = true; + hintIdx++; + break; + } else if (Math.abs(angles) < SIGNIFICANT_ANGLE && input.distanceToNext < minRange) { + input.angle = angles; + input.calcCommand(); + input.distanceToNext += h2.distanceToNext; + save = true; + hintIdx++; + break; + } else if (Math.abs(input.angle) > SIGNIFICANT_ANGLE) { + tmpList.add(h2); + hintIdx++; + } else if (dist > catchingRange) { // distance reached + break; + } else { + if (inputLast != null) { // when drop add distance to last + inputLast.distanceToNext += input.distanceToNext; + } + save = false; + } + i++; + } + if (save) { + results.add(input); // add when last + if (tmpList.size() > 0) { // add when something in stock + results.addAll(tmpList); + hintIdx += tmpList.size() - 1; + } + } + } else { + results.add(input); + } + inputLast = input; + } + } + return results; + } + } diff --git a/docs/features/voicehints.md b/docs/features/voicehints.md new file mode 100644 index 0000000..f4c3e78 --- /dev/null +++ b/docs/features/voicehints.md @@ -0,0 +1,54 @@ +--- +parent: Features +--- + +# Remarks on Voice Hints + +BRouter calculates voice hints but they are not present in all export formats. And within formats, +how they are presented will vary. + +There are gpx formats for +* OsmAnd +* Locus +* Comment-style +* Gpsies +* Orux + +The calculation starts with angles and comparing with the 'bad ways' (ways that are not +used on this junction). So e.g. an almost 90 degree hint can become "sharp right turn" if another +way is at 110 degrees. + +And there are other rules +* show 'continue' only if the way crosses a higher priority way +* roundabouts have an exit marker +* u-turn between -179 and +179 degree +* merge two hints when near to each other - e.g. left, left to u-turn left +* marker when highway exit and continue nearly same direction +* beeline goes direct from via to via point + +There are some variables in the profiles that affect on the voice hint generation: +* considerTurnRestrictions - +* turnInstructionCatchingRange - check distance to merge voice hints +* turnInstructionRoundabouts - use voice hints on roundabouts + +Voice hint variables + +| short | description | +| :----- | :----- | +| C | continue (go straight) | +| TL | turn left | +| TSLL | turn slightly left | +| TSHL | turn sharply left | +| TR | turn right | +| TSLR | turn slightly right | +| TSHR | turn sharply right | +| KL | keep left | +| KR | keep right | +| TLU | u-turn left | +| TU | 180 degree u-turn | +| TRU | u-turn right | +| OFFR | off route | +| RNDB | roundabout | +| RNLB | roundabout left | +| BL | beeline routing | +