Merge pull request #508 from afischerdev/update-lib-five

Update lib part five - change voicehints
This commit is contained in:
afischerdev 2023-02-25 12:18:41 +01:00 committed by GitHub
commit b735cd3e4e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 459 additions and 58 deletions

View file

@ -121,11 +121,26 @@ abstract class OsmPath implements OsmLinkHolder {
protected abstract void resetState(); protected abstract void resetState();
static int seg = 1;
protected void addAddionalPenalty(OsmTrack refTrack, boolean detailMode, OsmPath origin, OsmLink link, RoutingContext rc) { protected void addAddionalPenalty(OsmTrack refTrack, boolean detailMode, OsmPath origin, OsmLink link, RoutingContext rc) {
byte[] description = link.descriptionBitmap; byte[] description = link.descriptionBitmap;
if (description == null) { if (description == null) { // could be a beeline path
return; // 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; boolean recordTransferNodes = detailMode || rc.countTraffic;
@ -208,16 +223,18 @@ abstract class OsmPath implements OsmLinkHolder {
int lon2; int lon2;
int lat2; int lat2;
short ele2; short ele2;
short originEle2;
if (transferNode == null) { if (transferNode == null) {
lon2 = targetNode.ilon; lon2 = targetNode.ilon;
lat2 = targetNode.ilat; lat2 = targetNode.ilat;
ele2 = targetNode.selev; originEle2 = targetNode.selev;
} else { } else {
lon2 = transferNode.ilon; lon2 = transferNode.ilon;
lat2 = transferNode.ilat; lat2 = transferNode.ilat;
ele2 = transferNode.selev; originEle2 = transferNode.selev;
} }
ele2 = originEle2;
boolean isStartpoint = lon0 == -1 && lat0 == -1; boolean isStartpoint = lon0 == -1 && lat0 == -1;
@ -334,13 +351,13 @@ abstract class OsmPath implements OsmLinkHolder {
message.classifiermask = classifiermask; message.classifiermask = classifiermask;
message.lon = lon2; message.lon = lon2;
message.lat = lat2; message.lat = lat2;
message.ele = ele2; message.ele = originEle2;
message.wayKeyValues = rc.expctxWay.getKeyValueDescription(isReverse, description); message.wayKeyValues = rc.expctxWay.getKeyValueDescription(isReverse, description);
} }
if (stopAtEndpoint) { if (stopAtEndpoint) {
if (recordTransferNodes) { 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; originElement.cost = cost;
if (message != null) { if (message != null) {
originElement.message = message; originElement.message = message;
@ -366,7 +383,7 @@ abstract class OsmPath implements OsmLinkHolder {
transferNode = transferNode.next; transferNode = transferNode.next;
if (recordTransferNodes) { if (recordTransferNodes) {
originElement = OsmPathElement.create(lon2, lat2, ele2, originElement, rc.countTraffic); originElement = OsmPathElement.create(lon2, lat2, originEle2, originElement, rc.countTraffic);
originElement.cost = cost; originElement.cost = cost;
originElement.addTraffic(traffic); originElement.addTraffic(traffic);
traffic = 0; traffic = 0;

View file

@ -1080,7 +1080,9 @@ public final class OsmTrack {
VoiceHintProcessor vproc = new VoiceHintProcessor(rc.turnInstructionCatchingRange, rc.turnInstructionRoundabouts); VoiceHintProcessor vproc = new VoiceHintProcessor(rc.turnInstructionCatchingRange, rc.turnInstructionRoundabouts);
List<VoiceHint> results = vproc.process(inputs); List<VoiceHint> 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); voiceHints.list.add(hint);
} }

View file

@ -297,7 +297,7 @@ public class RoutingEngine extends Thread {
int ind = s.indexOf("%"); int ind = s.indexOf("%");
if (ind != -1) if (ind != -1)
s = s.substring(0, ind); s = s.substring(0, ind);
ind = s.indexOf("<EFBFBD>"); ind = s.indexOf("°");
if (ind != -1) if (ind != -1)
s = s.substring(0, ind); s = s.substring(0, ind);
tmpincline = Double.parseDouble(s.trim()); tmpincline = Double.parseDouble(s.trim());
@ -313,7 +313,7 @@ public class RoutingEngine extends Thread {
if (startincline == 0) { if (startincline == 0) {
startincline = tmpincline; startincline = tmpincline;
} else if (startincline < 0 && tmpincline > 0) { } 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; double diff = endElev - selev;
tmpincline = diff / (distRest / 100.); tmpincline = diff / (distRest / 100.);
} }
@ -733,7 +733,7 @@ public class RoutingEngine extends Thread {
lon2 = t.nodes.get(i).getILon(); lon2 = t.nodes.get(i).getILon();
lat2 = t.nodes.get(i).getILat(); lat2 = t.nodes.get(i).getILat();
nLast = t.nodes.get(0); nLast = t.nodes.get(0);
dist = routingContext.calcDistance(lon0, lat0, lon1, lat1); dist = nLast.calcDistance(n);
} else { } else {
lon0 = t.nodes.get(i - 2).getILon(); lon0 = t.nodes.get(i - 2).getILon();
lat0 = t.nodes.get(i - 2).getILat(); lat0 = t.nodes.get(i - 2).getILat();
@ -742,7 +742,7 @@ public class RoutingEngine extends Thread {
lon2 = t.nodes.get(i).getILon(); lon2 = t.nodes.get(i).getILon();
lat2 = t.nodes.get(i).getILat(); lat2 = t.nodes.get(i).getILat();
nLast = t.nodes.get(i - 1); 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); angle = routingContext.anglemeter.calcAngle(lon0, lat0, lon1, lat1, lon2, lat2);
n.message.linkdist = dist; n.message.linkdist = dist;

View file

@ -19,11 +19,13 @@ public class VoiceHint {
static final int TSHR = 7; // turn sharply right static final int TSHR = 7; // turn sharply right
static final int KL = 8; // keep left static final int KL = 8; // keep left
static final int KR = 9; // keep right static final int KR = 9; // keep right
static final int TU = 10; // U-turn static final int TLU = 10; // U-turn
static final int TRU = 11; // Right U-turn static final int TU = 11; // 180 degree u-turn
static final int OFFR = 12; // Off route static final int TRU = 12; // Right U-turn
static final int RNDB = 13; // Roundabout static final int OFFR = 13; // Off route
static final int RNLB = 14; // Roundabout left static final int RNDB = 14; // Roundabout
static final int RNLB = 15; // Roundabout left
static final int BL = 16; // Beeline routing
int ilon; int ilon;
int ilat; int ilat;
@ -39,7 +41,7 @@ public class VoiceHint {
return oldWay == null ? 0.f : oldWay.time; return oldWay == null ? 0.f : oldWay.time;
} }
float angle; float angle = Float.MAX_VALUE;
boolean turnAngleConsumed; boolean turnAngleConsumed;
boolean needsRealTurn; boolean needsRealTurn;
@ -67,8 +69,13 @@ public class VoiceHint {
return roundaboutExit; return roundaboutExit;
} }
/*
* used by comment style, osmand style
*/
public String getCommandString() { public String getCommandString() {
switch (cmd) { switch (cmd) {
case TLU:
return "TU"; // should be changed to TLU when osmand uses new voice hint constants
case TU: case TU:
return "TU"; return "TU";
case TSHL: 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() { public String getSymbolString() {
switch (cmd) { switch (cmd) {
case TLU:
return "TU";
case TU: case TU:
return "TU"; return "TU";
case TSHL: 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() { public String getMessageString() {
switch (cmd) { switch (cmd) {
case TLU:
return "u-turn"; // should be changed to u-turn-left when osmand uses new voice hint constants
case TU: case TU:
return "u-turn"; return "u-turn";
case TSHL: case TSHL:
@ -156,7 +251,7 @@ public class VoiceHint {
case TSHR: case TSHR:
return "sharp right"; return "sharp right";
case TRU: case TRU:
return "u-turn"; return "u-turn"; // should be changed to u-turn-right when osmand uses new voice hint constants
case RNDB: case RNDB:
return "Take exit " + roundaboutExit; return "Take exit " + roundaboutExit;
case RNLB: case RNLB:
@ -166,10 +261,15 @@ public class VoiceHint {
} }
} }
/*
* used by old locus style
*/
public int getLocusAction() { public int getLocusAction() {
switch (cmd) { switch (cmd) {
case TU: case TLU:
return 13; return 13;
case TU:
return 12;
case TSHL: case TSHL:
return 5; return 5;
case TL: case TL:
@ -199,8 +299,13 @@ public class VoiceHint {
} }
} }
/*
* used by orux style
*/
public int getOruxAction() { public int getOruxAction() {
switch (cmd) { switch (cmd) {
case TLU:
return 1003;
case TU: case TU:
return 1003; return 1003;
case TSHL: 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() { public void calcCommand() {
float lowerBadWayAngle = -181; float lowerBadWayAngle = -181;
float higherBadWayAngle = 181; float higherBadWayAngle = 181;
@ -252,58 +437,98 @@ public class VoiceHint {
float cmdAngle = angle; float cmdAngle = angle;
// fall back to local angle if otherwise inconsistent // 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; cmdAngle = goodWay.turnangle;
} }
if (cmd == BL) return;
if (roundaboutExit > 0) { if (roundaboutExit > 0) {
cmd = RNDB; cmd = RNDB;
} else if (roundaboutExit < 0) { } else if (roundaboutExit < 0) {
cmd = RNLB; cmd = RNLB;
} else if (cmdAngle < -159.) { } else if (is180DegAngle(cmdAngle) && cmdAngle <= -179.f && higherBadWayAngle == 181.f && lowerBadWayAngle == -181.f) {
cmd = TU; cmd = TU;
} else if (cmdAngle < -135.) { } else if (cmdAngle < -159.f) {
cmd = TLU;
} else if (cmdAngle < -135.f) {
cmd = TSHL; cmd = TSHL;
} else if (cmdAngle < -45.) { } else if (cmdAngle < -45.f) {
// a TL can be pushed in either direction by a close-by alternative // 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; cmd = TSHL;
} else if (lowerBadWayAngle > -180. && lowerBadWayAngle < -90. && higherBadWayAngle > 0.) { } else if (cmdAngle > -85.f && lowerBadWayAngle > -180.f && higherBadWayAngle > -10.f) {
cmd = TSLL; cmd = TSLL;
} else { } else {
cmd = TL; if (cmdAngle < -110.f) {
cmd = TSHL;
} else if (cmdAngle > -60.f) {
cmd = TSLL;
} else {
cmd = TL;
}
} }
} else if (cmdAngle < -21.) { } else if (cmdAngle < -21.f) {
if (cmd != KR) // don't overwrite KR with TSLL if (cmd != KR) { // don't overwrite KR with TSLL
{
cmd = TSLL; cmd = TSLL;
} }
} else if (cmdAngle < 21.) { } else if (cmdAngle < -5.f) {
if (cmd != KR && cmd != KL) // don't overwrite KL/KR hints! if (lowerBadWayAngle < -100.f && higherBadWayAngle < 45.f) {
{ cmd = TSLL;
} else if (lowerBadWayAngle >= -100.f && higherBadWayAngle < 45.f) {
cmd = KL;
} else {
cmd = C; cmd = C;
} }
} else if (cmdAngle < 45.) { } else if (cmdAngle < 5.f) {
if (cmd != KL) // don't overwrite KL with TSLR if (lowerBadWayAngle > -30.f) {
{ cmd = KR;
cmd = TSLR; } 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 // 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; 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; cmd = TSHR;
} else { } 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; cmd = TSHR;
} else if (is180DegAngle(cmdAngle) && cmdAngle >= 179.f && higherBadWayAngle == 181.f && lowerBadWayAngle == -181.f) {
cmd = TU;
} else { } else {
cmd = TRU; cmd = TRU;
} }
} }
static boolean is180DegAngle(float angle) {
return (Math.abs(angle) <= 180.f && Math.abs(angle) >= 179.f);
}
public String formatGeometry() { public String formatGeometry() {
float oldPrio = oldWay == null ? 0.f : oldWay.priorityclassifier; float oldPrio = oldWay == null ? 0.f : oldWay.priorityclassifier;
StringBuilder sb = new StringBuilder(30); StringBuilder sb = new StringBuilder(30);

View file

@ -9,18 +9,22 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public final class VoiceHintProcessor { 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; private boolean explicitRoundabouts;
public VoiceHintProcessor(double catchingRange, boolean explicitRoundabouts) { public VoiceHintProcessor(double catchingRange, boolean explicitRoundabouts) {
this.catchingRange = catchingRange; // this.catchingRange = catchingRange;
this.explicitRoundabouts = explicitRoundabouts; this.explicitRoundabouts = explicitRoundabouts;
} }
private float sumNonConsumedWithinCatchingRange(List<VoiceHint> inputs, int offset) { private float sumNonConsumedWithinCatchingRange(List<VoiceHint> inputs, int offset) {
double distance = 0.; double distance = 0.;
float angle = 0.f; float angle = 0.f;
while (offset >= 0 && distance < catchingRange) { while (offset >= 0 && distance < INTERNAL_CATCHING_RANGE) {
VoiceHint input = inputs.get(offset--); VoiceHint input = inputs.get(offset--);
if (input.turnAngleConsumed) { if (input.turnAngleConsumed) {
break; break;
@ -60,6 +64,10 @@ public final class VoiceHintProcessor {
for (int hintIdx = 0; hintIdx < inputs.size(); hintIdx++) { for (int hintIdx = 0; hintIdx < inputs.size(); hintIdx++) {
VoiceHint input = inputs.get(hintIdx); VoiceHint input = inputs.get(hintIdx);
if (input.cmd == VoiceHint.BL) {
results.add(input);
continue;
}
float turnAngle = input.goodWay.turnangle; float turnAngle = input.goodWay.turnangle;
distance += input.goodWay.linkdist; distance += input.goodWay.linkdist;
int currentPrio = input.goodWay.getPrio(); int currentPrio = input.goodWay.getPrio();
@ -67,6 +75,7 @@ public final class VoiceHintProcessor {
int minPrio = Math.min(oldPrio, currentPrio); int minPrio = Math.min(oldPrio, currentPrio);
boolean isLink2Highway = input.oldWay.isLinktType() && !input.goodWay.isLinktType(); boolean isLink2Highway = input.oldWay.isLinktType() && !input.goodWay.isLinktType();
boolean isHighway2Link = !input.oldWay.isLinktType() && input.goodWay.isLinktType();
if (input.oldWay.isRoundabout()) { if (input.oldWay.isRoundabout()) {
roundAboutTurnAngle += sumNonConsumedWithinCatchingRange(inputs, hintIdx); roundAboutTurnAngle += sumNonConsumedWithinCatchingRange(inputs, hintIdx);
@ -101,14 +110,18 @@ public final class VoiceHintProcessor {
float minAngle = 180.f; float minAngle = 180.f;
float minAbsAngeRaw = 180.f; float minAbsAngeRaw = 180.f;
boolean isBadwayLink = false;
if (input.badWays != null) { if (input.badWays != null) {
for (MessageData badWay : input.badWays) { for (MessageData badWay : input.badWays) {
int badPrio = badWay.getPrio(); int badPrio = badWay.getPrio();
float badTurn = badWay.turnangle; 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 && !isBadHighway2Link) {
if (badPrio > maxPrioAll && !isHighway2Link) {
maxPrioAll = badPrio; 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 // unconditional triggers are all junctions with
// - higher detour prios than the minimum route prio (except link->highway junctions) // - higher detour prios than the minimum route prio (except link->highway junctions)
// - or candidate detours with higher prio then the route exit leg // - 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 // conditional triggers (=real turning angle required) are junctions
// with candidate detours equal in priority than the route exit leg // with candidate detours equal in priority than the route exit leg
@ -158,11 +176,13 @@ public final class VoiceHintProcessor {
input.needsRealTurn = (!unconditionalTrigger) && isStraight; input.needsRealTurn = (!unconditionalTrigger) && isStraight;
// check for KR/KL // check for KR/KL
if (maxAngle < turnAngle && maxAngle > turnAngle - 45.f - (turnAngle > 0.f ? turnAngle : 0.f)) { if (Math.abs(turnAngle) > 5.) { // don't use to small angles
input.cmd = VoiceHint.KR; if (maxAngle < turnAngle && maxAngle > turnAngle - 45.f - (Math.max(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 (minAngle > turnAngle && minAngle < turnAngle + 45.f - (Math.min(turnAngle, 0.f))) {
input.cmd = VoiceHint.KL;
}
} }
input.angle = sumNonConsumedWithinCatchingRange(inputs, hintIdx); input.angle = sumNonConsumedWithinCatchingRange(inputs, hintIdx);
@ -170,7 +190,7 @@ public final class VoiceHintProcessor {
distance = 0.; distance = 0.;
results.add(input); 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); results.get(results.size() - 1).angle += sumNonConsumedWithinCatchingRange(inputs, hintIdx);
} }
} }
@ -185,10 +205,10 @@ public final class VoiceHintProcessor {
if (hint.cmd == 0) { if (hint.cmd == 0) {
hint.calcCommand(); hint.calcCommand();
} }
if (!(hint.needsRealTurn && hint.cmd == VoiceHint.C)) { if (!(hint.needsRealTurn && (hint.cmd == VoiceHint.C || hint.cmd == VoiceHint.BL))) {
double dist = hint.distanceToNext; double dist = hint.distanceToNext;
// sum up other hints within the catching range (e.g. 40m) // 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); VoiceHint h2 = results.get(i - 1);
dist = h2.distanceToNext; dist = h2.distanceToNext;
hint.distanceToNext += dist; hint.distanceToNext += dist;
@ -207,9 +227,92 @@ public final class VoiceHintProcessor {
} }
hint.calcCommand(); hint.calcCommand();
results2.add(hint); 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; return results2;
} }
public List<VoiceHint> postProcess(List<VoiceHint> inputs, double catchingRange, double minRange) {
List<VoiceHint> results = new ArrayList<VoiceHint>();
double distance = 0;
VoiceHint inputLast = null;
ArrayList<VoiceHint> 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;
}
} }

View file

@ -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 |