voice hints update

This commit is contained in:
Arndt 2016-05-05 12:46:27 +02:00
parent 47cfb4b83c
commit 23968b2a5b
7 changed files with 103 additions and 32 deletions

View file

@ -17,9 +17,8 @@ final class MessageData implements Cloneable
float costfactor; float costfactor;
int priorityclassifier; int priorityclassifier;
int classifiermask;
float turnangle; float turnangle;
int onwaydirection;
int roundaboutdirection;
String wayKeyValues; String wayKeyValues;
String nodeKeyValues; String nodeKeyValues;
@ -77,11 +76,32 @@ final class MessageData implements Cloneable
public int getPrio() public int getPrio()
{ {
return Math.abs( priorityclassifier ); return priorityclassifier;
}
public boolean isBadOneway()
{
return ( classifiermask & 1 ) != 0;
}
public boolean isGoodOneway()
{
return ( classifiermask & 2 ) != 0;
}
public boolean isRoundabout()
{
return ( classifiermask & 4 ) != 0;
}
public boolean isLinktType()
{
return ( classifiermask & 8 ) != 0;
} }
public boolean isGoodForCars() public boolean isGoodForCars()
{ {
return priorityclassifier > 0; return ( classifiermask & 16 ) != 0;
} }
} }

View file

@ -338,8 +338,7 @@ final class OsmPath implements OsmLinkHolder
{ {
msgData.costfactor = costfactor; msgData.costfactor = costfactor;
msgData.priorityclassifier = (int)rc.expctxWay.getPriorityClassifier(); msgData.priorityclassifier = (int)rc.expctxWay.getPriorityClassifier();
msgData.onwaydirection = (int)rc.expctxWay.getOnewayDirection(); msgData.classifiermask = (int)rc.expctxWay.getClassifierMask();
msgData.roundaboutdirection = (int)rc.expctxWay.getRoundaboutDirection();
msgData.lon = lon2; msgData.lon = lon2;
msgData.lat = lat2; msgData.lat = lat2;
msgData.ele = ele2; msgData.ele = ele2;

View file

@ -657,7 +657,7 @@ public final class OsmTrack
input.ilon = node.origin.getILon(); input.ilon = node.origin.getILon();
input.indexInTrack = --nodeNr; input.indexInTrack = --nodeNr;
input.goodWay = node.message; input.goodWay = node.message;
input.oldWay = node.origin.message; input.oldWay = node.origin.message == null ? node.message : node.origin.message;
OsmPathElementHolder detours = detourMap.get( node.origin.getIdFromPos() ); OsmPathElementHolder detours = detourMap.get( node.origin.getIdFromPos() );
if ( detours != null ) if ( detours != null )
@ -674,7 +674,8 @@ public final class OsmTrack
node = node.origin; node = node.origin;
} }
List<VoiceHint> results = VoiceHintProcessor.process( inputs ); VoiceHintProcessor vproc = new VoiceHintProcessor( rc.turnInstructionCatchingRange, rc.turnInstructionRoundabouts );
List<VoiceHint> results = vproc.process( inputs );
for( VoiceHint hint : results ) for( VoiceHint hint : results )
{ {
voiceHints.list.add( hint ); voiceHints.list.add( hint );

View file

@ -101,6 +101,8 @@ public final class RoutingContext implements DistanceChecker
{ {
turnInstructionMode = tiMode; turnInstructionMode = tiMode;
} }
turnInstructionCatchingRange = expctxGlobal.getVariableValue( "trafficSourceMinDist", 40.f );
turnInstructionRoundabouts = expctxGlobal.getVariableValue( "turnInstructionRoundabouts", 1.f ) != 0.f;
} }
public RoutingMessageHandler messageHandler = new RoutingMessageHandler(); public RoutingMessageHandler messageHandler = new RoutingMessageHandler();
@ -130,6 +132,8 @@ public final class RoutingContext implements DistanceChecker
public double trafficSourceMinDist; public double trafficSourceMinDist;
public int turnInstructionMode; // 0=none, 1=auto, 2=locus, 3=osmand, 4=comment-style, 5=gpsies-style public int turnInstructionMode; // 0=none, 1=auto, 2=locus, 3=osmand, 4=comment-style, 5=gpsies-style
public double turnInstructionCatchingRange;
public boolean turnInstructionRoundabouts;
public static void prepareNogoPoints( List<OsmNodeNamed> nogos ) public static void prepareNogoPoints( List<OsmNodeNamed> nogos )
{ {

View file

@ -10,11 +10,20 @@ import java.util.List;
public final class VoiceHintProcessor public final class VoiceHintProcessor
{ {
private static float sumNonConsumedWithin40( List<VoiceHint> inputs, int offset ) private double catchingRange; // range to catch angles and merge turns
private boolean explicitRoundabouts;
public VoiceHintProcessor( double catchingRange, boolean explicitRoundabouts )
{
this.catchingRange = catchingRange;
this.explicitRoundabouts = explicitRoundabouts;
}
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 < 40. ) while( offset >= 0 && distance < catchingRange )
{ {
VoiceHint input = inputs.get( offset-- ); VoiceHint input = inputs.get( offset-- );
if ( input.turnAngleConsumed ) if ( input.turnAngleConsumed )
@ -29,7 +38,24 @@ public final class VoiceHintProcessor
} }
public static List<VoiceHint> process( List<VoiceHint> inputs ) /**
* process voice hints. Uses VoiceHint objects
* for both input and output. Input is in reverse
* order (from target to start), but output is
* returned in travel-direction and only for
* those nodes that trigger a voice hint.
*
* Input objects are expected for every segment
* of the track, also for those without a junction
*
* VoiceHint objects in the output list are enriched
* by the voice-command, the total angle and the distance
* to the next hint
*
* @param inputs tracknodes, un reverse order
* @return voice hints, in forward order
*/
public List<VoiceHint> process( List<VoiceHint> inputs )
{ {
List<VoiceHint> results = new ArrayList<VoiceHint>(); List<VoiceHint> results = new ArrayList<VoiceHint>();
double distance = 0.; double distance = 0.;
@ -44,22 +70,20 @@ public final class VoiceHintProcessor
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();
int oldPrio = input.oldWay == null ? currentPrio : input.oldWay.getPrio(); int oldPrio = input.oldWay.getPrio();
int minPrio = Math.min( oldPrio, currentPrio ); int minPrio = Math.min( oldPrio, currentPrio );
// odd priorities are link-types boolean isLink2Highway = input.oldWay.isLinktType() && !input.goodWay.isLinktType();
boolean isLink2Highway = ( ( oldPrio & 1 ) == 1 ) && ( ( currentPrio & 1 ) == 0 );
boolean inRoundabout = input.oldWay != null && input.oldWay.roundaboutdirection != 0; if ( input.oldWay.isRoundabout() )
if ( inRoundabout )
{ {
roundAboutTurnAngle += sumNonConsumedWithin40( inputs, hintIdx ); roundAboutTurnAngle += sumNonConsumedWithinCatchingRange( inputs, hintIdx );
boolean isExit = roundaboutExit == 0; // exit point is always exit boolean isExit = roundaboutExit == 0; // exit point is always exit
if ( input.badWays != null ) if ( input.badWays != null )
{ {
for ( MessageData badWay : input.badWays ) for ( MessageData badWay : input.badWays )
{ {
if ( badWay.onwaydirection >= 0 && badWay.isGoodForCars() && Math.abs( badWay.turnangle ) < 120. ) if ( !badWay.isBadOneway() && badWay.isGoodForCars() && Math.abs( badWay.turnangle ) < 120. )
{ {
isExit = true; isExit = true;
} }
@ -73,7 +97,7 @@ public final class VoiceHintProcessor
} }
if ( roundaboutExit > 0 ) if ( roundaboutExit > 0 )
{ {
roundAboutTurnAngle += sumNonConsumedWithin40( inputs, hintIdx ); roundAboutTurnAngle += sumNonConsumedWithinCatchingRange( inputs, hintIdx );
input.angle = roundAboutTurnAngle; input.angle = roundAboutTurnAngle;
input.distanceToNext = distance; input.distanceToNext = distance;
input.roundaboutExit = turnAngle < 0 ? -roundaboutExit : roundaboutExit; input.roundaboutExit = turnAngle < 0 ? -roundaboutExit : roundaboutExit;
@ -88,31 +112,37 @@ public final class VoiceHintProcessor
float maxAngle = -180.f; float maxAngle = -180.f;
float minAngle = 180.f; float minAngle = 180.f;
float minAbsAngeRaw = 180.f;
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();
boolean badOneway = badWay.onwaydirection < 0; float badTurn = badWay.turnangle;
boolean isHighway2Link = ( ( badPrio & 1 ) == 1 ) && ( ( currentPrio & 1 ) == 0 ); boolean isHighway2Link = !input.oldWay.isLinktType() && badWay.isLinktType();
if ( badPrio > maxPrioAll && !isHighway2Link ) if ( badPrio > maxPrioAll && !isHighway2Link )
{ {
maxPrioAll = badPrio; maxPrioAll = badPrio;
} }
if ( badWay.costfactor < 20.f && Math.abs( badTurn ) < minAbsAngeRaw )
{
minAbsAngeRaw = Math.abs( badTurn );
}
if ( badPrio < minPrio ) if ( badPrio < minPrio )
{ {
continue; // ignore low prio ways continue; // ignore low prio ways
} }
if ( badOneway ) if ( badWay.isBadOneway() )
{ {
continue; // ignore wrong oneways continue; // ignore wrong oneways
} }
float badTurn = badWay.turnangle;
if ( Math.abs( badTurn ) - Math.abs( turnAngle ) > 80.f ) if ( Math.abs( badTurn ) - Math.abs( turnAngle ) > 80.f )
{ {
continue; // ways from the back should not trigger a slight turn continue; // ways from the back should not trigger a slight turn
@ -133,10 +163,12 @@ public final class VoiceHintProcessor
} }
} }
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 = ( maxPrioAll > minPrio && !isLink2Highway ) || ( maxPrioCandidates > currentPrio ); boolean unconditionalTrigger = hasSomethingMoreStraight || ( maxPrioAll > minPrio && !isLink2Highway ) || ( maxPrioCandidates > currentPrio );
// 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
@ -159,14 +191,14 @@ public final class VoiceHintProcessor
input.cmd = VoiceHint.KL; input.cmd = VoiceHint.KL;
} }
input.angle = sumNonConsumedWithin40( inputs, hintIdx ); input.angle = sumNonConsumedWithinCatchingRange( inputs, hintIdx );
input.distanceToNext = distance; input.distanceToNext = distance;
distance = 0.; distance = 0.;
results.add( input ); results.add( input );
} }
if ( results.size() > 0 && distance < 40. ) if ( results.size() > 0 && distance < catchingRange )
{ {
results.get( results.size()-1 ).angle += sumNonConsumedWithin40( inputs, hintIdx ); results.get( results.size()-1 ).angle += sumNonConsumedWithinCatchingRange( inputs, hintIdx );
} }
} }
@ -185,8 +217,8 @@ public final class VoiceHintProcessor
if ( ! ( hint.needsRealTurn && hint.cmd == VoiceHint.C ) ) if ( ! ( hint.needsRealTurn && hint.cmd == VoiceHint.C ) )
{ {
double dist = hint.distanceToNext; double dist = hint.distanceToNext;
// sum up other hints within 40m // sum up other hints within the catching range (e.g. 40m)
while( dist < 40. && i > 0 ) while( dist < catchingRange && i > 0 )
{ {
VoiceHint h2 = results.get(i-1); VoiceHint h2 = results.get(i-1);
dist = h2.distanceToNext; dist = h2.distanceToNext;
@ -200,6 +232,11 @@ public final class VoiceHintProcessor
break; break;
} }
} }
if ( !explicitRoundabouts )
{
hint.roundaboutExit = 0; // use an angular hint instead
}
hint.calcCommand(); hint.calcCommand();
results2.add( hint ); results2.add( hint );
} }

View file

@ -13,6 +13,7 @@ final class BExpression
private static final int MAX_EXP = 22; private static final int MAX_EXP = 22;
private static final int EQUAL_EXP = 23; private static final int EQUAL_EXP = 23;
private static final int GREATER_EXP = 24; private static final int GREATER_EXP = 24;
private static final int MIN_EXP = 25;
private static final int SWITCH_EXP = 30; private static final int SWITCH_EXP = 30;
private static final int ASSIGN_EXP = 31; private static final int ASSIGN_EXP = 31;
@ -102,6 +103,10 @@ final class BExpression
{ {
exp.typ = MAX_EXP; exp.typ = MAX_EXP;
} }
else if ( "min".equals( operator ) )
{
exp.typ = MIN_EXP;
}
else if ( "equal".equals( operator ) ) else if ( "equal".equals( operator ) )
{ {
exp.typ = EQUAL_EXP; exp.typ = EQUAL_EXP;
@ -230,6 +235,7 @@ final class BExpression
case ADD_EXP: return op1.evaluate(ctx) + op2.evaluate(ctx); case ADD_EXP: return op1.evaluate(ctx) + op2.evaluate(ctx);
case MULTIPLY_EXP: return op1.evaluate(ctx) * op2.evaluate(ctx); case MULTIPLY_EXP: return op1.evaluate(ctx) * op2.evaluate(ctx);
case MAX_EXP: return max( op1.evaluate(ctx), op2.evaluate(ctx) ); case MAX_EXP: return max( op1.evaluate(ctx), op2.evaluate(ctx) );
case MIN_EXP: return min( op1.evaluate(ctx), op2.evaluate(ctx) );
case EQUAL_EXP: return op1.evaluate(ctx) == op2.evaluate(ctx) ? 1.f : 0.f; case EQUAL_EXP: return op1.evaluate(ctx) == op2.evaluate(ctx) ? 1.f : 0.f;
case GREATER_EXP: return op1.evaluate(ctx) > op2.evaluate(ctx) ? 1.f : 0.f; case GREATER_EXP: return op1.evaluate(ctx) > op2.evaluate(ctx) ? 1.f : 0.f;
case SWITCH_EXP: return op1.evaluate(ctx) != 0.f ? op2.evaluate(ctx) : op3.evaluate(ctx); case SWITCH_EXP: return op1.evaluate(ctx) != 0.f ? op2.evaluate(ctx) : op3.evaluate(ctx);
@ -247,4 +253,9 @@ final class BExpression
{ {
return v1 > v2 ? v1 : v2; return v1 > v2 ? v1 : v2;
} }
private float min( float v1, float v2 )
{
return v1 < v2 ? v1 : v2;
}
} }

View file

@ -13,7 +13,7 @@ import btools.codec.TagValueValidator;
public final class BExpressionContextWay extends BExpressionContext implements TagValueValidator public final class BExpressionContextWay extends BExpressionContext implements TagValueValidator
{ {
private static String[] buildInVariables = private static String[] buildInVariables =
{ "costfactor", "turncost", "uphillcostfactor", "downhillcostfactor", "initialcost", "nodeaccessgranted", "initialclassifier", "trafficsourcedensity", "istrafficbackbone", "priorityclassifier", "onewaydirection", "roundaboutdirection"}; { "costfactor", "turncost", "uphillcostfactor", "downhillcostfactor", "initialcost", "nodeaccessgranted", "initialclassifier", "trafficsourcedensity", "istrafficbackbone", "priorityclassifier", "classifiermask" };
protected String[] getBuildInVariableNames() protected String[] getBuildInVariableNames()
{ {
@ -30,8 +30,7 @@ public final class BExpressionContextWay extends BExpressionContext implements T
public float getTrafficSourceDensity() { return getBuildInVariable(7); } public float getTrafficSourceDensity() { return getBuildInVariable(7); }
public float getIsTrafficBackbone() { return getBuildInVariable(8); } public float getIsTrafficBackbone() { return getBuildInVariable(8); }
public float getPriorityClassifier() { return getBuildInVariable(9); } public float getPriorityClassifier() { return getBuildInVariable(9); }
public float getOnewayDirection() { return getBuildInVariable(10); } public float getClassifierMask() { return getBuildInVariable(10); }
public float getRoundaboutDirection() { return getBuildInVariable(11); }
public BExpressionContextWay( BExpressionMetaData meta ) public BExpressionContextWay( BExpressionMetaData meta )
{ {