diff --git a/brouter-core/src/main/java/btools/router/MessageData.java b/brouter-core/src/main/java/btools/router/MessageData.java index 66534e0..ce6265d 100644 --- a/brouter-core/src/main/java/btools/router/MessageData.java +++ b/brouter-core/src/main/java/btools/router/MessageData.java @@ -16,8 +16,10 @@ final class MessageData implements Cloneable int linkinitcost = 0; float costfactor; - float priorityclassifier; + int priorityclassifier; float turnangle; + int onwaydirection; + int roundaboutdirection; String wayKeyValues; String nodeKeyValues; @@ -72,4 +74,14 @@ final class MessageData implements Cloneable { return "dist=" + linkdist + " prio=" + priorityclassifier + " turn=" + turnangle; } + + public int getPrio() + { + return Math.abs( priorityclassifier ); + } + + public boolean isGoodForCars() + { + return priorityclassifier > 0; + } } diff --git a/brouter-core/src/main/java/btools/router/OsmPath.java b/brouter-core/src/main/java/btools/router/OsmPath.java index c3ce12b..133423a 100644 --- a/brouter-core/src/main/java/btools/router/OsmPath.java +++ b/brouter-core/src/main/java/btools/router/OsmPath.java @@ -304,7 +304,7 @@ final class OsmPath implements OsmLinkHolder } float fcost = dist * costfactor + 0.5f; - if ( costfactor > 9999. || fcost + cost >= 2000000000. ) + if ( ( costfactor > 9999. && !detailMode ) || fcost + cost >= 2000000000. ) { cost = -1; return; @@ -337,7 +337,9 @@ final class OsmPath implements OsmLinkHolder if ( recordMessageData ) { 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.lat = lat2; msgData.ele = ele2; diff --git a/brouter-core/src/main/java/btools/router/OsmTrack.java b/brouter-core/src/main/java/btools/router/OsmTrack.java index 354802a..91015ae 100644 --- a/brouter-core/src/main/java/btools/router/OsmTrack.java +++ b/brouter-core/src/main/java/btools/router/OsmTrack.java @@ -45,7 +45,7 @@ public final class OsmTrack private CompactLongMap detourMap; - private List voiceHints; + private VoiceHintList voiceHints; public String message = null; public ArrayList messageList = null; @@ -261,9 +261,13 @@ public final class OsmTrack 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() { StringBuilder sb = new StringBuilder( 8192 ); + int turnInstructionMode = voiceHints != null ? voiceHints.turnInstructionMode : 0; sb.append( "\n" ); for ( int i = messageList.size() - 1; i >= 0; i-- ) @@ -305,46 +310,107 @@ public final class OsmTrack if ( message != null ) sb.append( "\n" ); } + + if ( turnInstructionMode == 4 ) // comment style + { + sb.append( "\n" ); + sb.append( "\n" ); + sb.append( "\n" ); + } sb.append( "\n" ); - - if ( voiceHints != null ) + if ( turnInstructionMode == 2 ) // locus style { - 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(" \n"); + for( VoiceHint hint: voiceHints.list ) + { + sb.append(" \n" ) + .append ( " " ).append( hint.getMessageString() ).append( "\n \n " ) + .append( hint.getCommandString() ).append("\n ").append( hint.angle ) + .append("\n ").append( hint.indexInTrack ).append("\n \n \n"); + } + sb.append("\n"); + } + + if ( turnInstructionMode == 2 ) // locus style + { + for( VoiceHint hint: voiceHints.list ) { sb.append( " " ) - .append( "" ).append( hint.message ).append( "" ); - if ( hint.turnInstructionMode == 2 ) - { - sb.append( "" ).append( hint.distanceToNext ).append( "" ) - .append( "" ).append( hint.locusAction ).append( "" ); - } - else - { - sb.append( "" ).append( hint.symbol.toLowerCase() ).append( "" ) - .append( "" ).append( hint.symbol ).append( "" ); - } - sb.append( "\n" ); + .append( "" ).append( hint.getMessageString() ).append( "" ) + .append( "" ).append( hint.distanceToNext ).append( "" ) + .append( "" ).append( hint.getLocusAction() ).append( "" ) + .append( "\n" ); + } + } + if ( turnInstructionMode == 5 ) // gpsies style + { + for( VoiceHint hint: voiceHints.list ) + { + sb.append( " " ) + .append( "" ).append( hint.getMessageString() ).append( "" ) + .append( "" ).append( hint.getSymbolString().toLowerCase() ).append( "" ) + .append( "" ).append( hint.getSymbolString() ).append( "" ) + .append( "\n" ); } } - sb.append( " \n" ); sb.append( " " ).append( name ).append( "\n" ); - - if ( voiceHints != null && voiceHints.size() > 0 && voiceHints.get(0).turnInstructionMode == 2 ) + if ( turnInstructionMode == 1 ) // trkpt/sym style { - sb.append( " " ).append( voiceHints.get(0).locusRouteType ).append( "\n" ); + sb.append( " " ).append( voiceHints.getTransportMode() ).append( "\n" ); + } + + if ( turnInstructionMode == 2 ) + { + int routeType = voiceHints.getLocusRouteType(); + if ( routeType != 4 ) // 4 = car = default seems to work better as default + { + sb.append( " " ).append( voiceHints.getLocusRouteType() ).append( "\n" ); + } } sb.append( " \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 ? "" : "" + n.getElev() + ""; + if ( turnInstructionMode == 1 ) // trkpt/sym style + { + for ( VoiceHint hint : voiceHints.list ) + { + if ( hint.indexInTrack == idx ) + { + sele += "" + hint.getCommandString() + ""; + } + } + } sb.append( " " ).append( sele ).append( "\n" ); } @@ -568,22 +634,18 @@ public final class OsmTrack return true; } - private void addVoiceHint( VoiceHint hint ) - { - if ( voiceHints == null ) - { - voiceHints = new ArrayList(); - } - voiceHints.add( hint ); - } - public void processVoiceHints( RoutingContext rc ) { + voiceHints = new VoiceHintList(); + voiceHints.setTransportMode( rc.carMode ); + voiceHints.turnInstructionMode = rc.turnInstructionMode; + if ( detourMap == null ) { return; } - OsmPathElement node = nodes.get( nodes.size() - 1 ); + int nodeNr = nodes.size() - 1; + OsmPathElement node = nodes.get( nodeNr ); List inputs = new ArrayList(); while (node != null) { @@ -593,9 +655,9 @@ public final class OsmTrack inputs.add( input ); input.ilat = node.origin.getILat(); input.ilon = node.origin.getILon(); - input.locusRouteType = rc.carMode ? 4 : 5; - input.turnInstructionMode = rc.turnInstructionMode; + input.indexInTrack = --nodeNr; input.goodWay = node.message; + input.oldWay = node.origin.message; OsmPathElementHolder detours = detourMap.get( node.origin.getIdFromPos() ); if ( detours != null ) @@ -613,9 +675,9 @@ public final class OsmTrack } List 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 ); } } diff --git a/brouter-core/src/main/java/btools/router/RoutingContext.java b/brouter-core/src/main/java/btools/router/RoutingContext.java index c3626e3..7b3c3e2 100644 --- a/brouter-core/src/main/java/btools/router/RoutingContext.java +++ b/brouter-core/src/main/java/btools/router/RoutingContext.java @@ -96,7 +96,11 @@ public final class RoutingContext implements DistanceChecker trafficSourceExponent = expctxGlobal.getVariableValue( "trafficSourceExponent", -0.7f ); 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(); @@ -125,7 +129,7 @@ public final class RoutingContext implements DistanceChecker public double trafficSourceExponent; 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 nogos ) { diff --git a/brouter-core/src/main/java/btools/router/VoiceHint.java b/brouter-core/src/main/java/btools/router/VoiceHint.java index 2636a05..6b2b282 100644 --- a/brouter-core/src/main/java/btools/router/VoiceHint.java +++ b/brouter-core/src/main/java/btools/router/VoiceHint.java @@ -11,16 +11,40 @@ import java.util.List; 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 ilat; - String message; - String symbol; - int locusAction; + int cmd; + MessageData oldWay; MessageData goodWay; List badWays; double distanceToNext; - int locusRouteType; - int turnInstructionMode; + int indexInTrack; + + float angle; + boolean turnAngleConsumed; + boolean needsRealTurn; + + int roundaboutExit; + + boolean isRoundabout() + { + return roundaboutExit != 0; + } public void addBadWay( MessageData badWay ) { @@ -34,58 +58,168 @@ public class VoiceHint } badWays.add( badWay ); } - - public boolean setTurnAngle( float angle ) + + public String getCommandString() { - if ( angle < -165. || angle > 165. ) + switch ( cmd ) { - symbol = "TU"; - message = "u-turn"; - locusAction = 12; + 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 : throw new IllegalArgumentException( "unknown command: " + cmd ); } - else if ( angle < -115. ) + } + + public String getSymbolString() + { + switch ( cmd ) { - symbol = "TSHL"; - message = "sharp left"; - locusAction = 5; + case TU : return "TU"; + case TSHL : return "TSHL"; + 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"; - message = "left"; - locusAction = 4; + 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"; + 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"; - message = "slight left"; - locusAction = 3; + case TU : return 12; + case TSHL : return 5; + 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"; - message = "straight"; - locusAction = 1; - return false; + cmd = RNDB; } - else if ( angle < 65. ) + else if (roundaboutExit < 0) { - symbol = "TSLR"; - message = "slight right"; - locusAction = 6; + cmd = RNLB; } - else if ( angle < 115. ) + else if ( angle < -159. ) { - symbol = "Right"; - message = "right"; - locusAction = 7; + cmd = TU; + } + 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 { - symbol = "TSHR"; - message = "sharp right"; - locusAction = 8; + cmd = TRU; } - 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) ); + } + } diff --git a/brouter-core/src/main/java/btools/router/VoiceHintList.java b/brouter-core/src/main/java/btools/router/VoiceHintList.java new file mode 100644 index 0000000..a32e50e --- /dev/null +++ b/brouter-core/src/main/java/btools/router/VoiceHintList.java @@ -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 list = new ArrayList(); + + 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; // ?? + } +} diff --git a/brouter-core/src/main/java/btools/router/VoiceHintProcessor.java b/brouter-core/src/main/java/btools/router/VoiceHintProcessor.java index 5d111b8..e937e15 100644 --- a/brouter-core/src/main/java/btools/router/VoiceHintProcessor.java +++ b/brouter-core/src/main/java/btools/router/VoiceHintProcessor.java @@ -10,39 +10,201 @@ import java.util.List; public final class VoiceHintProcessor { + private static float sumNonConsumedWithin40( List 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 process( List inputs ) { List results = new ArrayList(); 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; - 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; - for ( MessageData badWay : input.badWays ) + roundAboutTurnAngle += sumNonConsumedWithin40( inputs, hintIdx ); + boolean isExit = roundaboutExit == 0; // exit point is always exit + if ( input.badWays != null ) { -// System.out.println( " --> badWay: " + badWay ); - if ( badWay.priorityclassifier > maxprio ) + for ( MessageData badWay : input.badWays ) { - 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 ); - if ( isTurn || input.goodWay.priorityclassifier < maxprio ) + roundaboutExit++; + } + 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; - distance = 0.; - results.add( input ); + maxPrioAll = badPrio; + } + + 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 results2 = new ArrayList(); + 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; } } diff --git a/brouter-expressions/src/main/java/btools/expressions/BExpressionContextWay.java b/brouter-expressions/src/main/java/btools/expressions/BExpressionContextWay.java index 910c63b..9110ff5 100644 --- a/brouter-expressions/src/main/java/btools/expressions/BExpressionContextWay.java +++ b/brouter-expressions/src/main/java/btools/expressions/BExpressionContextWay.java @@ -13,7 +13,7 @@ import btools.codec.TagValueValidator; public final class BExpressionContextWay extends BExpressionContext implements TagValueValidator { 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() { @@ -30,7 +30,8 @@ public final class BExpressionContextWay extends BExpressionContext implements T public float getTrafficSourceDensity() { return getBuildInVariable(7); } public float getIsTrafficBackbone() { return getBuildInVariable(8); } public float getPriorityClassifier() { return getBuildInVariable(9); } - + public float getOnewayDirection() { return getBuildInVariable(10); } + public float getRoundaboutDirection() { return getBuildInVariable(11); } public BExpressionContextWay( BExpressionMetaData meta ) { diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java b/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java index fa8341d..9b362cf 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/BRouterView.java @@ -347,7 +347,11 @@ public class BRouterView extends View { 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; } @@ -405,6 +409,7 @@ public class BRouterView extends View RoutingContext rc = new RoutingContext(); rc.localFunction = profilePath; + rc.turnInstructionMode = cor.getTurnInstructionMode(); int plain_distance = 0; int maxlon = Integer.MIN_VALUE; diff --git a/brouter-routing-app/src/main/java/btools/routingapp/BRouterWorker.java b/brouter-routing-app/src/main/java/btools/routingapp/BRouterWorker.java index 5021d1e..1877cad 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/BRouterWorker.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/BRouterWorker.java @@ -41,6 +41,19 @@ public class BRouterWorker RoutingContext rc = new RoutingContext(); rc.rawTrackPath = rawTrackPath; 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 ) { rc.prepareNogoPoints( nogoList ); diff --git a/brouter-routing-app/src/main/java/btools/routingapp/CoordinateReader.java b/brouter-routing-app/src/main/java/btools/routingapp/CoordinateReader.java index 523b7b9..8800127 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/CoordinateReader.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/CoordinateReader.java @@ -40,6 +40,8 @@ public abstract class CoordinateReader public abstract long getTimeStamp() throws Exception; + public abstract int getTurnInstructionMode(); + public void readAllPoints() throws Exception { allpointsMap = new TreeMap>(); diff --git a/brouter-routing-app/src/main/java/btools/routingapp/CoordinateReaderLocus.java b/brouter-routing-app/src/main/java/btools/routingapp/CoordinateReaderLocus.java index 492aa2f..907197f 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/CoordinateReaderLocus.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/CoordinateReaderLocus.java @@ -25,6 +25,12 @@ public class CoordinateReaderLocus extends CoordinateReader return t1; } + @Override + public int getTurnInstructionMode() + { + return 2; // locus style + } + /* * read the from and to position from a ggx-file * (with hardcoded name for now) diff --git a/brouter-routing-app/src/main/java/btools/routingapp/CoordinateReaderNone.java b/brouter-routing-app/src/main/java/btools/routingapp/CoordinateReaderNone.java index a92f7e6..1b8d8be 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/CoordinateReaderNone.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/CoordinateReaderNone.java @@ -18,6 +18,12 @@ public class CoordinateReaderNone extends CoordinateReader return 0L; } + @Override + public int getTurnInstructionMode() + { + return 0; // none + } + @Override public void readPointmap() throws Exception { diff --git a/brouter-routing-app/src/main/java/btools/routingapp/CoordinateReaderOrux.java b/brouter-routing-app/src/main/java/btools/routingapp/CoordinateReaderOrux.java index b275b25..f92f2ff 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/CoordinateReaderOrux.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/CoordinateReaderOrux.java @@ -25,6 +25,12 @@ public class CoordinateReaderOrux extends CoordinateReader return t1; } + @Override + public int getTurnInstructionMode() + { + return 0; // none + } + /* * read the from and to position from a ggx-file * (with hardcoded name for now) diff --git a/brouter-routing-app/src/main/java/btools/routingapp/CoordinateReaderOsmAnd.java b/brouter-routing-app/src/main/java/btools/routingapp/CoordinateReaderOsmAnd.java index 8d910fa..917c45a 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/CoordinateReaderOsmAnd.java +++ b/brouter-routing-app/src/main/java/btools/routingapp/CoordinateReaderOsmAnd.java @@ -44,6 +44,12 @@ public class CoordinateReaderOsmAnd extends CoordinateReader return t1 > t2 ? t1 : t2; } + @Override + public int getTurnInstructionMode() + { + return 3; // osmand style + } + /* * read the from and to position from a gpx-file * (with hardcoded name for now) diff --git a/brouter-routing-app/src/main/java/btools/routingapp/IBRouterService.aidl b/brouter-routing-app/src/main/java/btools/routingapp/IBRouterService.aidl index 55b225f..0f092b1 100644 --- a/brouter-routing-app/src/main/java/btools/routingapp/IBRouterService.aidl +++ b/brouter-routing-app/src/main/java/btools/routingapp/IBRouterService.aidl @@ -8,6 +8,7 @@ interface IBRouterService { // "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 // "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 // "lats"-->double[] array of latitudes; 2 values at least. // "lons"-->double[] array of longitudes; 2 values at least.