voice hints update

This commit is contained in:
Arndt 2016-04-30 17:23:02 +02:00
parent ccf2eb28d3
commit 23d1812371
16 changed files with 564 additions and 102 deletions

View file

@ -16,8 +16,10 @@ final class MessageData implements Cloneable
int linkinitcost = 0; int linkinitcost = 0;
float costfactor; float costfactor;
float priorityclassifier; int priorityclassifier;
float turnangle; float turnangle;
int onwaydirection;
int roundaboutdirection;
String wayKeyValues; String wayKeyValues;
String nodeKeyValues; String nodeKeyValues;
@ -72,4 +74,14 @@ final class MessageData implements Cloneable
{ {
return "dist=" + linkdist + " prio=" + priorityclassifier + " turn=" + turnangle; return "dist=" + linkdist + " prio=" + priorityclassifier + " turn=" + turnangle;
} }
public int getPrio()
{
return Math.abs( priorityclassifier );
}
public boolean isGoodForCars()
{
return priorityclassifier > 0;
}
} }

View file

@ -304,7 +304,7 @@ final class OsmPath implements OsmLinkHolder
} }
float fcost = dist * costfactor + 0.5f; float fcost = dist * costfactor + 0.5f;
if ( costfactor > 9999. || fcost + cost >= 2000000000. ) if ( ( costfactor > 9999. && !detailMode ) || fcost + cost >= 2000000000. )
{ {
cost = -1; cost = -1;
return; return;
@ -337,7 +337,9 @@ final class OsmPath implements OsmLinkHolder
if ( recordMessageData ) if ( recordMessageData )
{ {
msgData.costfactor = costfactor; msgData.costfactor = costfactor;
msgData.priorityclassifier = rc.expctxWay.getPriorityClassifier(); msgData.priorityclassifier = (int)rc.expctxWay.getPriorityClassifier();
msgData.onwaydirection = (int)rc.expctxWay.getOnewayDirection();
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

@ -45,7 +45,7 @@ public final class OsmTrack
private CompactLongMap<OsmPathElementHolder> detourMap; private CompactLongMap<OsmPathElementHolder> detourMap;
private List<VoiceHint> voiceHints; private VoiceHintList voiceHints;
public String message = null; public String message = null;
public ArrayList<String> messageList = null; public ArrayList<String> messageList = null;
@ -261,9 +261,13 @@ public final class OsmTrack
if ( t.voiceHints != null ) if ( t.voiceHints != null )
{ {
for( VoiceHint hint : t.voiceHints ) if ( voiceHints == null )
{ {
addVoiceHint( hint ); voiceHints = t.voiceHints;
}
else
{
voiceHints.list.addAll( t.voiceHints.list );
} }
} }
@ -295,6 +299,7 @@ public final class OsmTrack
public String formatAsGpx() public String formatAsGpx()
{ {
StringBuilder sb = new StringBuilder( 8192 ); StringBuilder sb = new StringBuilder( 8192 );
int turnInstructionMode = voiceHints != null ? voiceHints.turnInstructionMode : 0;
sb.append( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" ); sb.append( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
for ( int i = messageList.size() - 1; i >= 0; i-- ) for ( int i = messageList.size() - 1; i >= 0; i-- )
@ -305,46 +310,107 @@ public final class OsmTrack
if ( message != null ) if ( message != null )
sb.append( "<!-- " ).append( message ).append( " -->\n" ); sb.append( "<!-- " ).append( message ).append( " -->\n" );
} }
if ( turnInstructionMode == 4 ) // comment style
{
sb.append( "<!-- $transport-mode$").append( voiceHints.getTransportMode() ).append( "$ -->\n" );
sb.append( "<!-- cmd idx lon lat d2next geometry -->\n" );
sb.append( "<!-- $turn-instruction-start$\n" );
for( VoiceHint hint: voiceHints.list )
{
sb.append( String.format( " $turn$%6s;%6d;%10s;%10s;%6d;%s$\n", hint.getCommandString(), hint.indexInTrack,
formatILon( hint.ilon ), formatILat( hint.ilat ), (int)(hint.distanceToNext), hint.formatGeometry() ) );
}
sb.append( " $turn-instruction-end$ -->\n" );
}
sb.append( "<gpx \n" ); sb.append( "<gpx \n" );
sb.append( " xmlns=\"http://www.topografix.com/GPX/1/1\" \n" ); sb.append( " xmlns=\"http://www.topografix.com/GPX/1/1\" \n" );
sb.append( " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \n" ); sb.append( " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \n" );
sb.append( " xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\" \n" ); if ( turnInstructionMode == 2 ) // locus style
sb.append( " creator=\"BRouter-1.3.2\" version=\"1.1\">\n" );
if ( voiceHints != null )
{ {
for( VoiceHint hint: voiceHints ) sb.append( " xmlns:locus=\"http://www.locusmap.eu\" \n" );
}
sb.append( " xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\" \n" );
if ( turnInstructionMode == 3)
{
sb.append(" creator=\"OsmAndRouter\" version=\"1.1\">\n" );
}
else
{
sb.append( " creator=\"BRouter-1.3.2\" version=\"1.1\">\n" );
}
if ( turnInstructionMode == 3) // osmand style
{
sb.append(" <rte>\n");
for( VoiceHint hint: voiceHints.list )
{
sb.append(" <rtept lat=\"").append( formatILat( hint.ilat ) ).append( "\" lon=\"" )
.append( formatILon( hint.ilon ) ).append( "\">\n" )
.append ( " <desc>" ).append( hint.getMessageString() ).append( "</desc>\n <extensions>\n <turn>" )
.append( hint.getCommandString() ).append("</turn>\n <turn-angle>").append( hint.angle )
.append("</turn-angle>\n <offset>").append( hint.indexInTrack ).append("</offset>\n </extensions>\n </rtept>\n");
}
sb.append("</rte>\n");
}
if ( turnInstructionMode == 2 ) // locus style
{
for( VoiceHint hint: voiceHints.list )
{ {
sb.append( " <wpt lon=\"" ).append( formatILon( hint.ilon ) ).append( "\" lat=\"" ) sb.append( " <wpt lon=\"" ).append( formatILon( hint.ilon ) ).append( "\" lat=\"" )
.append( formatILat( hint.ilat ) ).append( "\">" ) .append( formatILat( hint.ilat ) ).append( "\">" )
.append( "<name>" ).append( hint.message ).append( "</name>" ); .append( "<name>" ).append( hint.getMessageString() ).append( "</name>" )
if ( hint.turnInstructionMode == 2 ) .append( "<extensions><locus:rteDistance>" ).append( hint.distanceToNext ).append( "</locus:rteDistance>" )
{ .append( "<locus:rtePointAction>" ).append( hint.getLocusAction() ).append( "</locus:rtePointAction></extensions>" )
sb.append( "<extensions><locus:rteDistance>" ).append( hint.distanceToNext ).append( "</locus:rteDistance>" ) .append( "</wpt>\n" );
.append( "<locus:rtePointAction>" ).append( hint.locusAction ).append( "</locus:rtePointAction></extensions>" ); }
} }
else if ( turnInstructionMode == 5 ) // gpsies style
{ {
sb.append( "<sym>" ).append( hint.symbol.toLowerCase() ).append( "</sym>" ) for( VoiceHint hint: voiceHints.list )
.append( "<type>" ).append( hint.symbol ).append( "</type>" ); {
} sb.append( " <wpt lon=\"" ).append( formatILon( hint.ilon ) ).append( "\" lat=\"" )
sb.append( "</wpt>\n" ); .append( formatILat( hint.ilat ) ).append( "\">" )
.append( "<name>" ).append( hint.getMessageString() ).append( "</name>" )
.append( "<sym>" ).append( hint.getSymbolString().toLowerCase() ).append( "</sym>" )
.append( "<type>" ).append( hint.getSymbolString() ).append( "</type>" )
.append( "</wpt>\n" );
} }
} }
sb.append( " <trk>\n" ); sb.append( " <trk>\n" );
sb.append( " <name>" ).append( name ).append( "</name>\n" ); sb.append( " <name>" ).append( name ).append( "</name>\n" );
if ( turnInstructionMode == 1 ) // trkpt/sym style
if ( voiceHints != null && voiceHints.size() > 0 && voiceHints.get(0).turnInstructionMode == 2 )
{ {
sb.append( " <extensions><locus:rteComputeType>" ).append( voiceHints.get(0).locusRouteType ).append( "</locus:rteComputeType></extensions>\n" ); sb.append( " <type>" ).append( voiceHints.getTransportMode() ).append( "</type>\n" );
}
if ( turnInstructionMode == 2 )
{
int routeType = voiceHints.getLocusRouteType();
if ( routeType != 4 ) // 4 = car = default seems to work better as default
{
sb.append( " <extensions><locus:rteComputeType>" ).append( voiceHints.getLocusRouteType() ).append( "</locus:rteComputeType></extensions>\n" );
}
} }
sb.append( " <trkseg>\n" ); sb.append( " <trkseg>\n" );
for ( OsmPathElement n : nodes ) for ( int idx = 0; idx < nodes.size(); idx++ )
{ {
OsmPathElement n = nodes.get(idx);
String sele = n.getSElev() == Short.MIN_VALUE ? "" : "<ele>" + n.getElev() + "</ele>"; String sele = n.getSElev() == Short.MIN_VALUE ? "" : "<ele>" + n.getElev() + "</ele>";
if ( turnInstructionMode == 1 ) // trkpt/sym style
{
for ( VoiceHint hint : voiceHints.list )
{
if ( hint.indexInTrack == idx )
{
sele += "<sym>" + hint.getCommandString() + "</sym>";
}
}
}
sb.append( " <trkpt lon=\"" ).append( formatILon( n.getILon() ) ).append( "\" lat=\"" ) sb.append( " <trkpt lon=\"" ).append( formatILon( n.getILon() ) ).append( "\" lat=\"" )
.append( formatILat( n.getILat() ) ).append( "\">" ).append( sele ).append( "</trkpt>\n" ); .append( formatILat( n.getILat() ) ).append( "\">" ).append( sele ).append( "</trkpt>\n" );
} }
@ -568,22 +634,18 @@ public final class OsmTrack
return true; return true;
} }
private void addVoiceHint( VoiceHint hint )
{
if ( voiceHints == null )
{
voiceHints = new ArrayList<VoiceHint>();
}
voiceHints.add( hint );
}
public void processVoiceHints( RoutingContext rc ) public void processVoiceHints( RoutingContext rc )
{ {
voiceHints = new VoiceHintList();
voiceHints.setTransportMode( rc.carMode );
voiceHints.turnInstructionMode = rc.turnInstructionMode;
if ( detourMap == null ) if ( detourMap == null )
{ {
return; return;
} }
OsmPathElement node = nodes.get( nodes.size() - 1 ); int nodeNr = nodes.size() - 1;
OsmPathElement node = nodes.get( nodeNr );
List<VoiceHint> inputs = new ArrayList<VoiceHint>(); List<VoiceHint> inputs = new ArrayList<VoiceHint>();
while (node != null) while (node != null)
{ {
@ -593,9 +655,9 @@ public final class OsmTrack
inputs.add( input ); inputs.add( input );
input.ilat = node.origin.getILat(); input.ilat = node.origin.getILat();
input.ilon = node.origin.getILon(); input.ilon = node.origin.getILon();
input.locusRouteType = rc.carMode ? 4 : 5; input.indexInTrack = --nodeNr;
input.turnInstructionMode = rc.turnInstructionMode;
input.goodWay = node.message; input.goodWay = node.message;
input.oldWay = node.origin.message;
OsmPathElementHolder detours = detourMap.get( node.origin.getIdFromPos() ); OsmPathElementHolder detours = detourMap.get( node.origin.getIdFromPos() );
if ( detours != null ) if ( detours != null )
@ -613,9 +675,9 @@ public final class OsmTrack
} }
List<VoiceHint> results = VoiceHintProcessor.process( inputs ); List<VoiceHint> results = VoiceHintProcessor.process( inputs );
for( int i=results.size()-1; i >= 0; i-- ) for( VoiceHint hint : results )
{ {
addVoiceHint( results.get(i) ); voiceHints.list.add( hint );
} }
} }

View file

@ -96,7 +96,11 @@ public final class RoutingContext implements DistanceChecker
trafficSourceExponent = expctxGlobal.getVariableValue( "trafficSourceExponent", -0.7f ); trafficSourceExponent = expctxGlobal.getVariableValue( "trafficSourceExponent", -0.7f );
trafficSourceMinDist = expctxGlobal.getVariableValue( "trafficSourceMinDist", 3000.f ); trafficSourceMinDist = expctxGlobal.getVariableValue( "trafficSourceMinDist", 3000.f );
turnInstructionMode = (int)expctxGlobal.getVariableValue( "turnInstructionMode", 0.f ); int tiMode = (int)expctxGlobal.getVariableValue( "turnInstructionMode", 0.f );
if ( tiMode != 1 ) // automatic selection from coordinate source
{
turnInstructionMode = tiMode;
}
} }
public RoutingMessageHandler messageHandler = new RoutingMessageHandler(); public RoutingMessageHandler messageHandler = new RoutingMessageHandler();
@ -125,7 +129,7 @@ public final class RoutingContext implements DistanceChecker
public double trafficSourceExponent; public double trafficSourceExponent;
public double trafficSourceMinDist; public double trafficSourceMinDist;
public int turnInstructionMode; // 0=none, 1=osmand, 2=locus public int turnInstructionMode; // 0=none, 1=auto, 2=locus, 3=osmand, 4=comment-style, 5=gpsies-style
public static void prepareNogoPoints( List<OsmNodeNamed> nogos ) public static void prepareNogoPoints( List<OsmNodeNamed> nogos )
{ {

View file

@ -11,16 +11,40 @@ import java.util.List;
public class VoiceHint public class VoiceHint
{ {
static final int C = 1; // continue (go straight)
static final int TL = 2; // turn left
static final int TSLL = 3; // turn slightly left
static final int TSHL = 4; // turn sharply left
static final int TR = 5; // turn right
static final int TSLR = 6; // turn slightly right
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
int ilon; int ilon;
int ilat; int ilat;
String message; int cmd;
String symbol; MessageData oldWay;
int locusAction;
MessageData goodWay; MessageData goodWay;
List<MessageData> badWays; List<MessageData> badWays;
double distanceToNext; double distanceToNext;
int locusRouteType; int indexInTrack;
int turnInstructionMode;
float angle;
boolean turnAngleConsumed;
boolean needsRealTurn;
int roundaboutExit;
boolean isRoundabout()
{
return roundaboutExit != 0;
}
public void addBadWay( MessageData badWay ) public void addBadWay( MessageData badWay )
{ {
@ -35,57 +59,167 @@ public class VoiceHint
badWays.add( badWay ); badWays.add( badWay );
} }
public boolean setTurnAngle( float angle ) public String getCommandString()
{ {
if ( angle < -165. || angle > 165. ) switch ( cmd )
{ {
symbol = "TU"; case TU : return "TU";
message = "u-turn"; case TSHL : return "TSHL";
locusAction = 12; 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 : throw new IllegalArgumentException( "unknown command: " + cmd );
} }
else if ( angle < -115. ) }
public String getSymbolString()
{
switch ( cmd )
{ {
symbol = "TSHL"; case TU : return "TU";
message = "sharp left"; case TSHL : return "TSHL";
locusAction = 5; case TL : return "Left";
case TSLL : return "TSLL";
case KL : return "TSLL"; // ?
case C : return "Straight";
case KR : return "TSLR"; // ?
case TSLR : return "TSLR";
case TR : return "Right";
case TSHR : return "TSHR";
case TRU : return "TU";
case RNDB : return "RNDB" + roundaboutExit;
case RNLB : return "RNLB" + (-roundaboutExit);
default : throw new IllegalArgumentException( "unknown command: " + cmd );
} }
else if ( angle < -65. ) }
public String getMessageString()
{
switch ( cmd )
{ {
symbol = "Left"; case TU : return "u-turn";
message = "left"; case TSHL : return "sharp left";
locusAction = 4; 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";
case RNDB : return "Take exit " + roundaboutExit;
case RNLB : return "Take exit " + (-roundaboutExit);
default : throw new IllegalArgumentException( "unknown command: " + cmd );
} }
else if ( angle < -15. ) }
public int getLocusAction()
{
switch ( cmd )
{ {
symbol = "TSLL"; case TU : return 12;
message = "slight left"; case TSHL : return 5;
locusAction = 3; case TL : return 4;
case TSLL : return 3;
case KL : return 9; // ?
case C : return 1;
case KR : return 10; // ?
case TSLR : return 6;
case TR : return 7;
case TSHR : return 8;
case TRU : return 12;
case RNDB : return 26 + roundaboutExit;
case RNLB : return 26 - roundaboutExit;
default : throw new IllegalArgumentException( "unknown command: " + cmd );
} }
else if ( angle < 15. ) }
public void calcCommand()
{
if (roundaboutExit > 0)
{ {
symbol = "Straight"; cmd = RNDB;
message = "straight";
locusAction = 1;
return false;
} }
else if ( angle < 65. ) else if (roundaboutExit < 0)
{ {
symbol = "TSLR"; cmd = RNLB;
message = "slight right";
locusAction = 6;
} }
else if ( angle < 115. ) else if ( angle < -159. )
{ {
symbol = "Right"; cmd = TU;
message = "right"; }
locusAction = 7; else if ( angle < -113. )
{
cmd = TSHL;
}
else if ( angle < -67. )
{
cmd = TL;
}
else if ( angle < -21. )
{
if ( cmd != KR ) // don't overwrite KR with TSLL
{
cmd = TSLL;
}
}
else if ( angle < 21. )
{
if ( cmd != KR && cmd != KL ) // don't overwrite KL/KR hints!
{
cmd = C;
}
}
else if ( angle < 67. )
{
if ( cmd != KL ) // don't overwrite KL with TSLR
{
cmd = TSLR;
}
}
else if ( angle < 113. )
{
cmd = TR;
}
else if ( angle < 159. )
{
cmd = TSHR;
} }
else else
{ {
symbol = "TSHR"; cmd = TRU;
message = "sharp right";
locusAction = 8;
} }
return true;
} }
public String formatGeometry()
{
float oldPrio = oldWay == null ? 0.f : oldWay.priorityclassifier;
StringBuilder sb = new StringBuilder(30);
sb.append( ' ' ).append( (int)oldPrio );
appendTurnGeometry(sb,goodWay);
if ( badWays != null )
{
for ( MessageData badWay : badWays )
{
sb.append( " " );
appendTurnGeometry( sb, badWay );
}
}
return sb.toString();
}
private void appendTurnGeometry( StringBuilder sb, MessageData msg )
{
sb.append( "(" ).append( (int)(msg.turnangle+0.5) ).append( ")" ).append( (int)(msg.priorityclassifier) );
}
} }

View file

@ -0,0 +1,40 @@
/**
* Container for a voice hint
* (both input- and result data for voice hint processing)
*
* @author ab
*/
package btools.router;
import java.util.ArrayList;
import java.util.List;
public class VoiceHintList
{
private String transportMode;
int turnInstructionMode;
ArrayList<VoiceHint> list = new ArrayList<VoiceHint>();
public void setTransportMode( boolean isCar )
{
transportMode = isCar ? "car" : "bike";
}
public String getTransportMode()
{
return transportMode;
}
public int getLocusRouteType()
{
if ( "car".equals( transportMode ) )
{
return 4;
}
if ( "bike".equals( transportMode ) )
{
return 5;
}
return 5; // ??
}
}

View file

@ -10,39 +10,201 @@ import java.util.List;
public final class VoiceHintProcessor public final class VoiceHintProcessor
{ {
private static float sumNonConsumedWithin40( List<VoiceHint> inputs, int offset )
{
double distance = 0.;
float angle = 0.f;
while( offset >= 0 && distance < 40. )
{
VoiceHint input = inputs.get( offset-- );
if ( input.turnAngleConsumed )
{
break;
}
angle += input.goodWay.turnangle;
distance += input.goodWay.linkdist;
input.turnAngleConsumed = true;
}
return angle;
}
public static List<VoiceHint> process( List<VoiceHint> inputs ) public static List<VoiceHint> process( List<VoiceHint> inputs )
{ {
List<VoiceHint> results = new ArrayList<VoiceHint>(); List<VoiceHint> results = new ArrayList<VoiceHint>();
double distance = 0.; double distance = 0.;
for ( VoiceHint input : inputs ) float roundAboutTurnAngle = 0.f; // sums up angles in roundabout
int roundaboutExit = 0;
for ( int hintIdx = 0; hintIdx < inputs.size(); hintIdx++ )
{ {
// System.out.println( "***** processing: " + input.ilat + " " + input.ilon + " goodWay=" + input.goodWay ); VoiceHint input = inputs.get( hintIdx );
float turnAngle = input.goodWay.turnangle;
distance += input.goodWay.linkdist; distance += input.goodWay.linkdist;
if ( input.badWays != null ) int currentPrio = input.goodWay.getPrio();
int oldPrio = input.oldWay == null ? currentPrio : input.oldWay.getPrio();
int minPrio = Math.min( oldPrio, currentPrio );
// odd priorities are link-types
boolean isLink2Highway = ( ( oldPrio & 1 ) == 1 ) && ( ( currentPrio & 1 ) == 0 );
boolean inRoundabout = input.oldWay != null && input.oldWay.roundaboutdirection != 0;
if ( inRoundabout )
{ {
float maxprio = 0.f; roundAboutTurnAngle += sumNonConsumedWithin40( inputs, hintIdx );
for ( MessageData badWay : input.badWays ) boolean isExit = roundaboutExit == 0; // exit point is always exit
if ( input.badWays != null )
{ {
// System.out.println( " --> badWay: " + badWay ); for ( MessageData badWay : input.badWays )
if ( badWay.priorityclassifier > maxprio )
{ {
maxprio = badWay.priorityclassifier; if ( badWay.onwaydirection >= 0 && badWay.isGoodForCars() && Math.abs( badWay.turnangle ) < 120. )
{
isExit = true;
}
} }
} }
if ( maxprio > 0. && maxprio >= input.goodWay.priorityclassifier ) if ( isExit )
{ {
boolean isTurn = input.setTurnAngle( input.goodWay.turnangle ); roundaboutExit++;
if ( isTurn || input.goodWay.priorityclassifier < maxprio ) }
continue;
}
if ( roundaboutExit > 0 )
{
roundAboutTurnAngle += sumNonConsumedWithin40( inputs, hintIdx );
input.angle = roundAboutTurnAngle;
input.distanceToNext = distance;
input.roundaboutExit = turnAngle < 0 ? -roundaboutExit : roundaboutExit;
distance = 0.;
results.add( input );
roundAboutTurnAngle = 0.f;
roundaboutExit = 0;
continue;
}
int maxPrioAll = -1; // max prio of all detours
int maxPrioCandidates = -1; // max prio of real candidates
float maxAngle = -180.f;
float minAngle = 180.f;
if ( input.badWays != null )
{
for ( MessageData badWay : input.badWays )
{
int badPrio = badWay.getPrio();
boolean badOneway = badWay.onwaydirection < 0;
boolean isHighway2Link = ( ( badPrio & 1 ) == 1 ) && ( ( currentPrio & 1 ) == 0 );
if ( badPrio > maxPrioAll && !isHighway2Link )
{ {
input.distanceToNext = distance; maxPrioAll = badPrio;
distance = 0.; }
results.add( input );
if ( badPrio < minPrio )
{
continue; // ignore low prio ways
}
if ( badOneway )
{
continue; // ignore wrong oneways
}
float badTurn = badWay.turnangle;
if ( Math.abs( badTurn ) - Math.abs( turnAngle ) > 80.f )
{
continue; // ways from the back should not trigger a slight turn
}
if ( badPrio > maxPrioCandidates )
{
maxPrioCandidates = badPrio;
}
if ( badTurn > maxAngle )
{
maxAngle = badTurn;
}
if ( badTurn < minAngle )
{
minAngle = badTurn;
} }
} }
} }
// 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 = ( maxPrioAll > minPrio && !isLink2Highway ) || ( maxPrioCandidates > currentPrio );
// conditional triggers (=real turning angle required) are junctions
// with candidate detours equal in priority than the route exit leg
boolean conditionalTrigger = maxPrioCandidates >= minPrio;
if ( unconditionalTrigger || conditionalTrigger )
{
input.angle = turnAngle;
input.calcCommand();
boolean isStraight = input.cmd == VoiceHint.C;
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;
}
input.angle = sumNonConsumedWithin40( inputs, hintIdx );
input.distanceToNext = distance;
distance = 0.;
results.add( input );
}
if ( results.size() > 0 && distance < 40. )
{
results.get( results.size()-1 ).angle += sumNonConsumedWithin40( inputs, hintIdx );
}
} }
return results;
// go through the hint list again in reverse order (=travel direction)
// and filter out non-signficant hints and hints too close to it's predecessor
List<VoiceHint> results2 = new ArrayList<VoiceHint>();
int i = results.size();
while( i > 0 )
{
VoiceHint hint = results.get(--i);
if ( hint.cmd == 0 )
{
hint.calcCommand();
}
if ( ! ( hint.needsRealTurn && hint.cmd == VoiceHint.C ) )
{
double dist = hint.distanceToNext;
// sum up other hints within 40m
while( dist < 40. && i > 0 )
{
VoiceHint h2 = results.get(i-1);
dist = h2.distanceToNext;
hint.distanceToNext+= dist;
hint.angle += h2.angle;
i--;
if ( h2.isRoundabout() ) // if we hit a roundabout, use that as the trigger
{
h2.angle = hint.angle;
hint = h2;
break;
}
}
hint.calcCommand();
results2.add( hint );
}
}
return results2;
} }
} }

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" }; { "costfactor", "turncost", "uphillcostfactor", "downhillcostfactor", "initialcost", "nodeaccessgranted", "initialclassifier", "trafficsourcedensity", "istrafficbackbone", "priorityclassifier", "onewaydirection", "roundaboutdirection"};
protected String[] getBuildInVariableNames() protected String[] getBuildInVariableNames()
{ {
@ -30,7 +30,8 @@ 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 getRoundaboutDirection() { return getBuildInVariable(11); }
public BExpressionContextWay( BExpressionMetaData meta ) public BExpressionContextWay( BExpressionMetaData meta )
{ {

View file

@ -347,7 +347,11 @@ public class BRouterView extends View
{ {
if ( wp.ilat != 0 || wp.ilat != 0 ) if ( wp.ilat != 0 || wp.ilat != 0 )
{ {
wpList.add( wp ); int nwp = wpList.size();
if ( nwp == 0 || wpList.get( nwp-1 ) != wp )
{
wpList.add( wp );
}
} }
return; return;
} }
@ -405,6 +409,7 @@ public class BRouterView extends View
RoutingContext rc = new RoutingContext(); RoutingContext rc = new RoutingContext();
rc.localFunction = profilePath; rc.localFunction = profilePath;
rc.turnInstructionMode = cor.getTurnInstructionMode();
int plain_distance = 0; int plain_distance = 0;
int maxlon = Integer.MIN_VALUE; int maxlon = Integer.MIN_VALUE;

View file

@ -41,6 +41,19 @@ public class BRouterWorker
RoutingContext rc = new RoutingContext(); RoutingContext rc = new RoutingContext();
rc.rawTrackPath = rawTrackPath; rc.rawTrackPath = rawTrackPath;
rc.localFunction = profilePath; rc.localFunction = profilePath;
String tiFormat = params.getString( "turnInstructionFormat" );
if ( tiFormat != null )
{
if ( "osmand".equalsIgnoreCase( tiFormat ) )
{
rc.turnInstructionMode = 3;
}
else if ( "locus".equalsIgnoreCase( tiFormat ) )
{
rc.turnInstructionMode = 2;
}
}
if ( nogoList != null ) if ( nogoList != null )
{ {
rc.prepareNogoPoints( nogoList ); rc.prepareNogoPoints( nogoList );

View file

@ -40,6 +40,8 @@ public abstract class CoordinateReader
public abstract long getTimeStamp() throws Exception; public abstract long getTimeStamp() throws Exception;
public abstract int getTurnInstructionMode();
public void readAllPoints() throws Exception public void readAllPoints() throws Exception
{ {
allpointsMap = new TreeMap<String, Map<String,OsmNodeNamed>>(); allpointsMap = new TreeMap<String, Map<String,OsmNodeNamed>>();

View file

@ -25,6 +25,12 @@ public class CoordinateReaderLocus extends CoordinateReader
return t1; return t1;
} }
@Override
public int getTurnInstructionMode()
{
return 2; // locus style
}
/* /*
* read the from and to position from a ggx-file * read the from and to position from a ggx-file
* (with hardcoded name for now) * (with hardcoded name for now)

View file

@ -18,6 +18,12 @@ public class CoordinateReaderNone extends CoordinateReader
return 0L; return 0L;
} }
@Override
public int getTurnInstructionMode()
{
return 0; // none
}
@Override @Override
public void readPointmap() throws Exception public void readPointmap() throws Exception
{ {

View file

@ -25,6 +25,12 @@ public class CoordinateReaderOrux extends CoordinateReader
return t1; return t1;
} }
@Override
public int getTurnInstructionMode()
{
return 0; // none
}
/* /*
* read the from and to position from a ggx-file * read the from and to position from a ggx-file
* (with hardcoded name for now) * (with hardcoded name for now)

View file

@ -44,6 +44,12 @@ public class CoordinateReaderOsmAnd extends CoordinateReader
return t1 > t2 ? t1 : t2; return t1 > t2 ? t1 : t2;
} }
@Override
public int getTurnInstructionMode()
{
return 3; // osmand style
}
/* /*
* read the from and to position from a gpx-file * read the from and to position from a gpx-file
* (with hardcoded name for now) * (with hardcoded name for now)

View file

@ -8,6 +8,7 @@ interface IBRouterService {
// "pathToFileResult"-->String with the path to where the result must be saved, including file name and extension // "pathToFileResult"-->String with the path to where the result must be saved, including file name and extension
// -->if null, the track is passed via the return argument // -->if null, the track is passed via the return argument
// "maxRunningTime"-->String with a number of seconds for the routing timeout, default = 60 // "maxRunningTime"-->String with a number of seconds for the routing timeout, default = 60
// "turnInstructionFormat"-->String selecting the format for turn-instructions values: osmand, locus
// "trackFormat"-->[kml|gpx] default = gpx // "trackFormat"-->[kml|gpx] default = gpx
// "lats"-->double[] array of latitudes; 2 values at least. // "lats"-->double[] array of latitudes; 2 values at least.
// "lons"-->double[] array of longitudes; 2 values at least. // "lons"-->double[] array of longitudes; 2 values at least.