diff --git a/brouter-core/src/main/java/btools/router/KinematicModel.java b/brouter-core/src/main/java/btools/router/KinematicModel.java index 7c89c59..82a12a6 100644 --- a/brouter-core/src/main/java/btools/router/KinematicModel.java +++ b/brouter-core/src/main/java/btools/router/KinematicModel.java @@ -40,6 +40,7 @@ final class KinematicModel extends OsmPathModel public double cost0; // minimum possible cost per meter private int wayIdxMaxspeed; + private int wayIdxMaxspeedExplicit; private int wayIdxMinspeed; private int nodeIdxMaxspeed; @@ -61,6 +62,7 @@ final class KinematicModel extends OsmPathModel ctxWay = expctxWay; ctxNode = expctxNode; wayIdxMaxspeed = ctxWay.getOutputVariableIndex( "maxspeed", false ); + wayIdxMaxspeedExplicit = ctxWay.getOutputVariableIndex( "maxspeed_explicit", false ); wayIdxMinspeed = ctxWay.getOutputVariableIndex( "minspeed", false ); nodeIdxMaxspeed = ctxNode.getOutputVariableIndex( "maxspeed", false ); initDone = true; @@ -104,6 +106,11 @@ final class KinematicModel extends OsmPathModel return ctxWay.getBuildInVariable( wayIdxMaxspeed ) / 3.6f; } + public float getWayMaxspeedExplicit() + { + return ctxWay.getBuildInVariable( wayIdxMaxspeedExplicit ) / 3.6f; + } + public float getWayMinspeed() { return ctxWay.getBuildInVariable( wayIdxMinspeed ) / 3.6f; diff --git a/brouter-core/src/main/java/btools/router/KinematicPath.java b/brouter-core/src/main/java/btools/router/KinematicPath.java index 5a23a8e..cf4e25b 100644 --- a/brouter-core/src/main/java/btools/router/KinematicPath.java +++ b/brouter-core/src/main/java/btools/router/KinematicPath.java @@ -76,6 +76,8 @@ final class KinematicPath extends OsmPath if ( nsection == 0 ) // process slowdown by crossing geometry { + double junctionspeed = 999.; // just high + int classifiermask = (int)rc.expctxWay.getClassifierMask(); // penalty for equal priority crossing @@ -105,16 +107,27 @@ final class KinematicPath extends OsmPath } double residentialSpeed = 13.; - if ( hasLeftWay && turnspeed > km.leftWaySpeed ) turnspeed = km.leftWaySpeed; - if ( hasRightWay && turnspeed > km.rightWaySpeed ) turnspeed = km.rightWaySpeed; - if ( hasResidential && turnspeed > residentialSpeed ) turnspeed = residentialSpeed; + if ( hasLeftWay && junctionspeed > km.leftWaySpeed ) junctionspeed = km.leftWaySpeed; + if ( hasRightWay && junctionspeed > km.rightWaySpeed ) junctionspeed = km.rightWaySpeed; + if ( hasResidential && junctionspeed > residentialSpeed ) junctionspeed = residentialSpeed; + if ( (lastpriorityclassifier < 20) ^ (priorityclassifier < 20) ) { extraTime += 10.; - turnspeed = 0; // full stop for entering or leaving road network + junctionspeed = 0; // full stop for entering or leaving road network + } + + if ( lastpriorityclassifier != priorityclassifier && (classifiermask & 8) != 0 ) + { + extraTime += 2.; // two seconds for entering a link-type + } + turnspeed = turnspeed > junctionspeed ? junctionspeed : turnspeed; + + if ( message != null ) + { + message.vnode0 = (int) ( junctionspeed * 3.6 + 0.5 ); } } - cutEkin( km.totalweight, turnspeed ); // apply turnspeed } @@ -131,6 +144,10 @@ final class KinematicPath extends OsmPath if ( message != null ) { message.costfactor = (float)(distanceCost/dist); + message.vmax = (int) ( km.getWayMaxspeed() * 3.6 + 0.5 ); + message.vmaxExplicit = (int) ( km.getWayMaxspeedExplicit() * 3.6 + 0.5 ); + message.vmin = (int) ( km.getWayMinspeed() * 3.6 + 0.5 ); + message.extraTime = (int)(extraTime*1000); } cost += extraTime * km.pw / km.cost0; @@ -242,6 +259,8 @@ final class KinematicPath extends OsmPath { message.linknodecost += (int)initialcost; message.nodeKeyValues = rc.expctxNode.getKeyValueDescription( false, targetNode.nodeDescription ); + + message.vnode1 = (int) ( km.getNodeMaxspeed() * 3.6 + 0.5 ); } return initialcost; } diff --git a/brouter-core/src/main/java/btools/router/MessageData.java b/brouter-core/src/main/java/btools/router/MessageData.java index 873b96d..10c3c8f 100644 --- a/brouter-core/src/main/java/btools/router/MessageData.java +++ b/brouter-core/src/main/java/btools/router/MessageData.java @@ -29,6 +29,14 @@ final class MessageData implements Cloneable float time; float energy; + // speed profile + int vmaxExplicit = -1; + int vmax = -1; + int vmin = -1; + int vnode0 = 999; + int vnode1 = 999; + int extraTime = 0; + String toMessage() { if ( wayKeyValues == null ) diff --git a/brouter-core/src/main/java/btools/router/OsmPath.java b/brouter-core/src/main/java/btools/router/OsmPath.java index 75dda0a..2b9c2f6 100644 --- a/brouter-core/src/main/java/btools/router/OsmPath.java +++ b/brouter-core/src/main/java/btools/router/OsmPath.java @@ -345,7 +345,7 @@ abstract class OsmPath implements OsmLinkHolder { if ( rc.startDirectionValid ) { - double dir = rc.startDirection.intValue() / CheapRulerSingleton.DEG_TO_RAD; + double dir = rc.startDirection.intValue() * CheapRulerSingleton.DEG_TO_RAD; double[] lonlat2m = CheapRulerSingleton.getLonLatToMeterScales( (lon0 + lat1) >> 1 ); lon0 = lon1 - (int) ( 1000. * Math.sin( dir ) / lonlat2m[0] ); lat0 = lat1 - (int) ( 1000. * Math.cos( dir ) / lonlat2m[1] ); diff --git a/brouter-core/src/main/java/btools/router/OsmTrack.java b/brouter-core/src/main/java/btools/router/OsmTrack.java index 4b8ac23..3a286fc 100644 --- a/brouter-core/src/main/java/btools/router/OsmTrack.java +++ b/brouter-core/src/main/java/btools/router/OsmTrack.java @@ -53,6 +53,8 @@ public final class OsmTrack private VoiceHintList voiceHints; + private boolean sendSpeedProfile; + public String message = null; public ArrayList messageList = null; @@ -146,6 +148,31 @@ public final class OsmTrack return res; } + private ArrayList aggregateSpeedProfile() + { + ArrayList res = new ArrayList(); + int vmax = -1; + int vmaxe = -1; + int vmin = -1; + int extraTime = 0; + for( int i = nodes.size()-1; i > 0; i-- ) + { + OsmPathElement n = nodes.get(i); + MessageData m = n.message; + int vnode = getVNode( i ); + if ( m != null && ( vmax != m.vmax || vmin != m.vmin || vmaxe != m.vmaxExplicit || vnode < m.vmax || extraTime != m.extraTime ) ) + { + vmax = m.vmax; + vmin = m.vmin; + vmaxe = m.vmaxExplicit; + extraTime = m.extraTime; + res.add( i + "," + vmaxe + "," + vmax + "," + vmin + "," + vnode + "," + extraTime ); + } + } + return res; + } + + /** * writes the track in binary-format to a file * @@ -324,6 +351,7 @@ public final class OsmTrack energy += t.energy; showspeed |= t.showspeed; + sendSpeedProfile |= t.sendSpeedProfile; } public int distance; @@ -563,24 +591,41 @@ public final class OsmTrack sb.append( " \"total-time\": \"" ).append( getTotalSeconds() ).append( "\",\n" ); sb.append( " \"total-energy\": \"" ).append( energy ).append( "\",\n" ); sb.append( " \"cost\": \"" ).append( cost ).append( "\",\n" ); - sb.append( " \"voicehints\": [\n" ); - for( VoiceHint hint: voiceHints.list ) + if ( voiceHints != null && !voiceHints.list.isEmpty() ) { - sb.append( " [" ).append( hint.indexInTrack ).append( ',' ).append( hint.getCommand() ).append( ',' ).append( hint.getExitNumber() ).append( "],\n" ); + sb.append( " \"voicehints\": [\n" ); + for( VoiceHint hint: voiceHints.list ) + { + sb.append( " [" ).append( hint.indexInTrack ).append( ',' ).append( hint.getCommand() ).append( ',' ).append( hint.getExitNumber() ).append( "],\n" ); + } + sb.deleteCharAt( sb.lastIndexOf( "," ) ); + sb.append( " ],\n" ); } - sb.deleteCharAt( sb.lastIndexOf( "," ) ); - sb.append( " ],\n" ); - sb.append( " \"messages\": [\n" ); - sb.append( " [\"" ).append( MESSAGES_HEADER.replaceAll( "\t", "\", \"" ) ).append( "\"],\n" ); - for ( String m : aggregateMessages() ) + if ( sendSpeedProfile ) // true if vmax was send { - sb.append( " [\"" ).append( m.replaceAll( "\t", "\", \"" ) ).append( "\"],\n" ); + ArrayList sp = aggregateSpeedProfile(); + if ( sp.size() > 0 ) + { + sb.append( " \"speedprofile\": [\n" ); + for( int i=sp.size()-1; i>=0; i-- ) + { + sb.append( " [" ).append( sp.get(i) ).append( i> 0 ? "],\n" : "]\n" ); + } + sb.append( " ]\n" ); + } + } + else // ... otherwise traditional message list + { + sb.append( " \"messages\": [\n" ); + sb.append( " [\"" ).append( MESSAGES_HEADER.replaceAll( "\t", "\", \"" ) ).append( "\"],\n" ); + for ( String m : aggregateMessages() ) + { + sb.append( " [\"" ).append( m.replaceAll( "\t", "\", \"" ) ).append( "\"],\n" ); + } + sb.deleteCharAt( sb.lastIndexOf( "," ) ); + sb.append( " ]\n" ); } - sb.deleteCharAt( sb.lastIndexOf( "," ) ); - sb.append( " ]\n" ); - sb.append( " },\n" ); - if ( iternity != null ) { sb.append( " \"iternity\": [\n" ); @@ -628,6 +673,15 @@ public final class OsmTrack return sb.toString(); } + private int getVNode( int i ) + { + MessageData m1 = i+1 < nodes.size() ? nodes.get(i+1).message : null; + MessageData m0 = i < nodes.size() ? nodes.get(i ).message : null; + int vnode0 = m1 == null ? 999 : m1.vnode0; + int vnode1 = m0 == null ? 999 : m0.vnode1; + return vnode0 < vnode1 ? vnode0 : vnode1; + } + private int getTotalSeconds() { float s = nodes.size() < 2 ? 0 : nodes.get( nodes.size()-1 ).getTime() - nodes.get( 0 ).getTime(); @@ -755,6 +809,11 @@ public final class OsmTrack return true; } + public void prepareSpeedProfile( RoutingContext rc ) + { + sendSpeedProfile = rc.keyValues != null && rc.keyValues.containsKey( "vmax" ); + } + public void processVoiceHints( RoutingContext rc ) { voiceHints = new VoiceHintList(); diff --git a/brouter-core/src/main/java/btools/router/RoutingContext.java b/brouter-core/src/main/java/btools/router/RoutingContext.java index db3302c..0592496 100644 --- a/brouter-core/src/main/java/btools/router/RoutingContext.java +++ b/brouter-core/src/main/java/btools/router/RoutingContext.java @@ -192,6 +192,7 @@ public final class RoutingContext public Integer startDirection; public boolean startDirectionValid; + public boolean forceUseStartDirection; private double cosangle; public double nogoCost = 0.; diff --git a/brouter-core/src/main/java/btools/router/RoutingEngine.java b/brouter-core/src/main/java/btools/router/RoutingEngine.java index bcb84b1..0441801 100644 --- a/brouter-core/src/main/java/btools/router/RoutingEngine.java +++ b/brouter-core/src/main/java/btools/router/RoutingEngine.java @@ -808,7 +808,9 @@ public class RoutingEngine extends Thread if ( start1 == null || start2 == null ) return null; - if ( routingContext.startDirectionValid = ( fastPartialRecalc && routingContext.startDirection != null && !routingContext.inverseDirection ) ) + routingContext.startDirectionValid = routingContext.forceUseStartDirection || fastPartialRecalc; + routingContext.startDirectionValid &= routingContext.startDirection != null && !routingContext.inverseDirection; + if ( routingContext.startDirectionValid ) { logInfo( "using start direction " + routingContext.startDirection ); } @@ -1221,6 +1223,7 @@ public class RoutingEngine extends Thread { track.copyDetours( guideTrack ); track.processVoiceHints( routingContext ); + track.prepareSpeedProfile( routingContext ); } return track; } diff --git a/brouter-server/src/main/java/btools/server/RouteServer.java b/brouter-server/src/main/java/btools/server/RouteServer.java index c907eef..bba52b3 100644 --- a/brouter-server/src/main/java/btools/server/RouteServer.java +++ b/brouter-server/src/main/java/btools/server/RouteServer.java @@ -188,7 +188,26 @@ public class RouteServer extends Thread { NearRecentWps.add( wplist ); } - + for( Map.Entry e : params.entrySet() ) + { + if ( "timode".equals( e.getKey() ) ) + { + rc.turnInstructionMode = Integer.parseInt( e.getValue() ); + } + else if ( "heading".equals( e.getKey() ) ) + { + rc.startDirection = Integer.valueOf( Integer.parseInt( e.getValue() ) ); + rc.forceUseStartDirection = true; + } + else if ( e.getKey().startsWith( "profile:" ) ) + { + if ( rc.keyValues == null ) + { + rc.keyValues = new HashMap(); + } + rc.keyValues.put( e.getKey().substring( 8 ), e.getValue() ); + } + } cr = new RoutingEngine( null, null, serviceContext.segmentDir, wplist, rc ); cr.quite = true; cr.doRun( maxRunningTime );