version 1.4.9

This commit is contained in:
Arndt Brenschede 2017-09-24 17:11:04 +02:00
parent 0171ba39a0
commit c517ccc2df
43 changed files with 2002 additions and 508 deletions

View file

@ -5,7 +5,7 @@
<parent>
<groupId>org.btools</groupId>
<artifactId>brouter</artifactId>
<version>1.4.8</version>
<version>1.4.9</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>brouter-core</artifactId>

View file

@ -0,0 +1,98 @@
/**
* Container for link between two Osm nodes
*
* @author ab
*/
package btools.router;
import btools.expressions.BExpressionContext;
import btools.expressions.BExpressionContextNode;
import btools.expressions.BExpressionContextWay;
class KinematicModel extends OsmPathModel
{
public OsmPrePath createPrePath()
{
return new KinematicPrePath();
}
public OsmPath createPath()
{
return new KinematicPath();
}
public double turnAngleDecayLength;
public double f_roll;
public double f_air;
public double f_recup;
public double p_standby;
public double recup_efficiency;
public double totalweight;
public double vmax;
public double leftWaySpeed;
public double rightWaySpeed;
// derived values
public double xweight; // the weight-factor between time and energy for cost calculation
public double timecost0; // minimum possible "energy-adjusted-time" per meter
private int wayIdxMaxspeed;
private int wayIdxMinspeed;
private int nodeIdxMaxspeed;
protected BExpressionContextWay ctxWay;
protected BExpressionContextNode ctxNode;
@Override
public void init( BExpressionContextWay expctxWay, BExpressionContextNode expctxNode )
{
ctxWay = expctxWay;
ctxNode = expctxNode;
BExpressionContext expctxGlobal = expctxWay; // just one of them...
turnAngleDecayLength = expctxGlobal.getVariableValue( "turnAngleDecayLength", 50.f );
f_roll = expctxGlobal.getVariableValue( "f_roll", 232.f );
f_air = expctxGlobal.getVariableValue( "f_air", 0.4f );
f_recup = expctxGlobal.getVariableValue( "f_recup", 400.f );
p_standby = expctxGlobal.getVariableValue( "p_standby", 250.f );
recup_efficiency = expctxGlobal.getVariableValue( "recup_efficiency", 0.7f );
totalweight = expctxGlobal.getVariableValue( "totalweight", 1640.f );
vmax = expctxGlobal.getVariableValue( "vmax", 80.f ) / 3.6;
leftWaySpeed = expctxGlobal.getVariableValue( "leftWaySpeed", 12.f ) / 3.6;
rightWaySpeed = expctxGlobal.getVariableValue( "rightWaySpeed", 12.f ) / 3.6;
xweight = 1./( 2. * f_air * vmax * vmax * vmax - p_standby );
timecost0 = 1./vmax + xweight*(f_roll + f_air*vmax*vmax + p_standby/vmax );
wayIdxMaxspeed = ctxWay.getOutputVariableIndex( "maxspeed", false );
wayIdxMinspeed = ctxWay.getOutputVariableIndex( "minspeed", false );
nodeIdxMaxspeed = ctxNode.getOutputVariableIndex( "maxspeed", false );
}
public float getWayMaxspeed()
{
return ctxWay.getBuildInVariable( wayIdxMaxspeed ) / 3.6f;
}
public float getWayMinspeed()
{
return ctxWay.getBuildInVariable( wayIdxMinspeed ) / 3.6f;
}
public float getNodeMaxspeed()
{
return ctxNode.getBuildInVariable( nodeIdxMaxspeed ) / 3.6f;
}
public double getMaxKineticEnergy()
{
// determine maximum possible speed and kinetic energy
double mspeed = Math.min( getWayMaxspeed(), Math.max( getWayMinspeed(), vmax ) );
return 0.5*totalweight*mspeed*mspeed;
}
}

View file

@ -0,0 +1,62 @@
/**
* Container for link between two Osm nodes
*
* @author ab
*/
package btools.router;
import btools.expressions.BExpressionContextNode;
import btools.expressions.BExpressionContextWay;
final class KinematicModelDummy extends KinematicModel
{
public OsmPrePath createPrePath()
{
return null;
}
public OsmPath createPath()
{
return null;
}
public KinematicModelDummy()
{
turnAngleDecayLength = 50.;
f_roll = 232.;
f_air = 0.4;
f_recup = 600.;
p_standby = 250.;
recup_efficiency = 0.7;
totalweight = 1640.;
vmax = 60./ 3.6;
leftWaySpeed = 12.f / 3.6;
rightWaySpeed = 12.f / 3.6;
}
public boolean useNewtonApprox;
// derived values
public double xweight = 1./( 2. * f_air * vmax * vmax * vmax - p_standby );
public double timecost0 = 1./vmax + xweight*(f_roll + f_air*vmax*vmax + p_standby/vmax );
@Override
public void init( BExpressionContextWay expctxWay, BExpressionContextNode expctxNode )
{
}
public float getWayMaxspeed()
{
return 100.f;
}
public float getWayMinspeed()
{
return 0.f;
}
public float getNodeMaxspeed()
{
return 999.f;
}
}

View file

@ -0,0 +1,271 @@
/**
* The path-instance of the kinematic model
*
* @author ab
*/
package btools.router;
final class KinematicPath extends OsmPath
{
private double ekin; // kinetic energy (Joule)
private double totalTime; // travel time (seconds)
private double totalEnergy; // total route energy (Joule)
private float floatingAngleLeft; // sliding average left bend (degree)
private float floatingAngleRight; // sliding average right bend (degree)
@Override
protected void init( OsmPath orig )
{
KinematicPath origin = (KinematicPath)orig;
ekin = origin.ekin;
totalTime = origin.totalTime;
totalEnergy = origin.totalEnergy;
floatingAngleLeft = origin.floatingAngleLeft;
floatingAngleRight = origin.floatingAngleRight;
priorityclassifier = origin.priorityclassifier;
}
@Override
protected void resetState()
{
ekin = 0.;
totalTime = 0.;
totalEnergy = 0.;
floatingAngleLeft = 0.f;
floatingAngleRight = 0.f;
}
@Override
protected double processWaySection( RoutingContext rc, double dist, double delta_h, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier )
{
KinematicModel km = (KinematicModel)rc.pm;
double cost = 0.;
if ( isStartpoint )
{
// for forward direction, we start with target speed
if ( !rc.inverseDirection )
{
cost = 0.5 * (1. - cosangle ) * 40. / km.timecost0; // 40 seconds turn penalty
}
}
else
{
double turnspeed = 999.; // just high
if ( km.turnAngleDecayLength != 0. ) // process turn-angle slowdown
{
double decayFactor = exp( - dist / km.turnAngleDecayLength );
floatingAngleLeft = (float)( floatingAngleLeft * decayFactor );
floatingAngleRight = (float)( floatingAngleRight * decayFactor );
if ( angle < 0 ) floatingAngleLeft -= (float)angle;
else floatingAngleRight += (float)angle;
float aa = Math.max( floatingAngleLeft, floatingAngleRight );
if ( aa > 130. ) turnspeed = 0.;
else if ( aa > 100. ) turnspeed = 1.;
else if ( aa > 70. ) turnspeed = 2.;
else if ( aa > 50. ) turnspeed = 4.;
else if ( aa > 30. ) turnspeed = 8.;
else if ( aa > 20. ) turnspeed = 14.;
else if ( aa > 10. ) turnspeed = 20.;
}
if ( nsection == 0 ) // process slowdown by crossing geometry
{
int classifiermask = (int)rc.expctxWay.getClassifierMask();
// penalty for equal priority crossing
boolean hasLeftWay = false;
boolean hasRightWay = false;
boolean hasResidential = false;
for( OsmPrePath prePath = rc.firstPrePath; prePath != null; prePath = prePath.next )
{
KinematicPrePath pp = (KinematicPrePath)prePath;
if ( ( (pp.classifiermask ^ classifiermask) & 8 ) != 0 ) // exactly one is linktype
{
continue;
}
if ( ( pp.classifiermask & 32 ) != 0 ) // touching a residential?
{
hasResidential = true;
}
if ( pp.priorityclassifier > priorityclassifier || pp.priorityclassifier == priorityclassifier && priorityclassifier < 20 )
{
double diff = pp.angle - angle;
if ( diff < -40. && diff > -140.) hasLeftWay = true;
if ( diff > 40. && diff < 140. ) hasRightWay = true;
}
}
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 ( (lastpriorityclassifier < 20) ^ (priorityclassifier < 20) ) turnspeed = 0; // full stop for entering or leaving road network
}
cutEkin( km.totalweight, turnspeed ); // apply turnspeed
}
double distanceCost = evolveDistance( km, dist, delta_h );
if ( message != null )
{
message.costfactor = (float)(distanceCost/dist);
}
return cost + distanceCost;
}
protected double evolveDistance( KinematicModel km, double dist, double delta_h )
{
// elevation force
double fh = delta_h * km.totalweight * 9.81 / dist;
double emax = km.getMaxKineticEnergy();
if ( emax <= 0. )
{
return -1.;
}
double elow = 0.5*emax; // recup phase below half energy (=70% vmax)
double elapsedTime = 0.;
double dissipatedEnergy = 0.;
double v = Math.sqrt( 2. * ekin / km.totalweight );
double d = dist;
while( d > 0. )
{
boolean slow = ekin < elow;
boolean fast = ekin >= emax;
double etarget = slow ? elow : emax;
double f = km.f_roll + km.f_air*v*v + fh;
double f_recup = Math.max( 0., fast ? -f : (slow ? km.f_recup :0 ) -fh ); // additional recup for slow part
f += f_recup;
double delta_ekin;
double x;
if ( fast )
{
x = d;
delta_ekin = x*f;
elapsedTime += x/v;
ekin = etarget;
}
else
{
delta_ekin = etarget-ekin;
double b = 2.*km.f_air / km.totalweight;
double x0 = delta_ekin/f;
double x0b = x0*b;
x = x0*(1. - x0b*(0.5 + x0b*(0.333333333-x0b*0.25 ) ) ); // = ln( delta_ekin*b/f + 1.) / b;
double maxstep = Math.min( 50., d );
if ( x >= maxstep )
{
x = maxstep;
double xb = x*b;
delta_ekin = x*f*(1.+xb*(0.5+xb*(0.166666667+xb*0.0416666667 ) ) ); // = f/b* exp(xb-1)
ekin += delta_ekin;
}
else
{
ekin = etarget;
}
double v2 = Math.sqrt( 2. * ekin / km.totalweight );
double a = f / km.totalweight; // TODO: average force?
elapsedTime += (v2-v)/a;
v = v2;
}
d -= x;
// dissipated energy does not contain elevation and efficient recup
dissipatedEnergy += delta_ekin - x*(fh + f_recup*km.recup_efficiency);
}
dissipatedEnergy += elapsedTime * km.p_standby;
totalTime += elapsedTime;
totalEnergy += dissipatedEnergy + dist*fh;
return (elapsedTime + km.xweight * dissipatedEnergy)/km.timecost0; // =cost
}
@Override
protected double processTargetNode( RoutingContext rc )
{
KinematicModel km = (KinematicModel)rc.pm;
// finally add node-costs for target node
if ( targetNode.nodeDescription != null )
{
rc.expctxNode.evaluate( false , targetNode.nodeDescription );
float initialcost = rc.expctxNode.getInitialcost();
if ( initialcost >= 1000000. )
{
return -1.;
}
cutEkin( km.totalweight, km.getNodeMaxspeed() ); // apply node maxspeed
if ( message != null )
{
message.linknodecost += (int)initialcost;
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription( false, targetNode.nodeDescription );
}
return initialcost;
}
return 0.;
}
private void cutEkin( double weight, double speed )
{
double e = 0.5*weight*speed*speed;
if ( ekin > e ) ekin = e;
}
private static double exp( double e )
{
double x = e;
double f = 1.;
while( e < -1. )
{
e += 1.;
f *= 0.367879;
}
return f*( 1. + x*( 1. + x * ( 0.5 + x * ( 0.166667 + 0.0416667 * x) ) ) );
}
@Override
public int elevationCorrection( RoutingContext rc )
{
return 0;
}
@Override
public boolean definitlyWorseThan( OsmPath path, RoutingContext rc )
{
KinematicPath p = (KinematicPath)path;
int c = p.cost;
return cost > c + 100;
}
public double getTotalTime()
{
return totalTime;
}
public double getTotalEnergy()
{
return totalEnergy;
}
}

View file

@ -0,0 +1,58 @@
/**
* Simple version of OsmPath just to get angle and priority of first segment
*
* @author ab
*/
package btools.router;
import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmTransferNode;
final class KinematicPrePath extends OsmPrePath
{
public double angle;
public int priorityclassifier;
public int classifiermask;
protected void initPrePath(OsmPath origin, RoutingContext rc )
{
byte[] description = link.descriptionBitmap;
if ( description == null ) throw new IllegalArgumentException( "null description for: " + link );
// extract the 3 positions of the first section
int lon0 = origin.originLon;
int lat0 = origin.originLat;
OsmNode p1 = sourceNode;
int lon1 = p1.getILon();
int lat1 = p1.getILat();
boolean isReverse = link.isReverse( sourceNode );
// evaluate the way tags
rc.expctxWay.evaluate( rc.inverseDirection ^ isReverse, description );
OsmTransferNode transferNode = link.geometry == null ? null
: rc.geometryDecoder.decodeGeometry( link.geometry, p1, targetNode, isReverse );
int lon2;
int lat2;
if ( transferNode == null )
{
lon2 = targetNode.ilon;
lat2 = targetNode.ilat;
}
else
{
lon2 = transferNode.ilon;
lat2 = transferNode.ilat;
}
int dist = rc.calcDistance( lon1, lat1, lon2, lat2 );
angle = rc.calcAngle( lon0, lat0, lon1, lat1, lon2, lat2 );
priorityclassifier = (int)rc.expctxWay.getPriorityClassifier();
classifiermask = (int)rc.expctxWay.getClassifierMask();
}
}

View file

@ -26,6 +26,9 @@ final class MessageData implements Cloneable
int lat;
short ele;
float time;
float energy;
String toMessage()
{
if ( wayKeyValues == null )

View file

@ -13,33 +13,27 @@ import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmTransferNode;
import btools.mapaccess.TurnRestriction;
final class OsmPath implements OsmLinkHolder
abstract class OsmPath implements OsmLinkHolder
{
/**
* The cost of that path (a modified distance)
*/
public int cost = 0;
/**
* The elevation-hysteresis-buffer (0-10 m)
*/
private int ehbd; // in micrometer
private int ehbu; // in micrometer
// the elevation assumed for that path can have a value
// if the corresponding node has not
public short selev;
public int airdistance = 0; // distance to endpos
private OsmNode sourceNode;
private OsmNode targetNode;
protected OsmNode sourceNode;
protected OsmNode targetNode;
private OsmLink link;
protected OsmLink link;
public OsmPathElement originElement;
public OsmPathElement myElement;
private float traffic;
protected float traffic;
private OsmLinkHolder nextForLink = null;
@ -51,7 +45,9 @@ final class OsmPath implements OsmLinkHolder
public int originLat;
// the classifier of the segment just before this paths position
public float lastClassifier;
protected float lastClassifier;
protected int priorityclassifier;
public MessageData message;
@ -81,13 +77,8 @@ final class OsmPath implements OsmLinkHolder
}
}
OsmPath()
public void init( OsmLink link )
{
}
OsmPath( OsmLink link )
{
this();
this.link = link;
targetNode = link.getTarget( null );
selev = targetNode.getSElev();
@ -96,9 +87,8 @@ final class OsmPath implements OsmLinkHolder
originLat = -1;
}
OsmPath( OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode, RoutingContext rc )
public void init( OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode, RoutingContext rc )
{
this();
if ( origin.myElement == null )
{
origin.myElement = OsmPathElement.create( origin, rc.countTraffic );
@ -108,19 +98,22 @@ final class OsmPath implements OsmLinkHolder
this.sourceNode = origin.targetNode;
this.targetNode = link.getTarget( sourceNode );
this.cost = origin.cost;
this.ehbd = origin.ehbd;
this.ehbu = origin.ehbu;
this.lastClassifier = origin.lastClassifier;
init( origin );
addAddionalPenalty(refTrack, detailMode, origin, link, rc );
}
protected abstract void init( OsmPath orig );
private void addAddionalPenalty(OsmTrack refTrack, boolean detailMode, OsmPath origin, OsmLink link, RoutingContext rc )
protected abstract void resetState();
protected void addAddionalPenalty(OsmTrack refTrack, boolean detailMode, OsmPath origin, OsmLink link, RoutingContext rc )
{
byte[] description = link.descriptionBitmap;
if ( description == null ) throw new IllegalArgumentException( "null description for: " + link );
if ( description == null ) throw new IllegalArgumentException( "null description for: " + link );
boolean recordTransferNodes = detailMode || rc.countTraffic;
boolean recordMessageData = detailMode;
rc.nogomatch = false;
@ -128,55 +121,48 @@ final class OsmPath implements OsmLinkHolder
int lon0 = origin.originLon;
int lat0 = origin.originLat;
OsmNode p1 = sourceNode;
int lon1 = p1.getILon();
int lat1 = p1.getILat();
int lon1 = sourceNode.getILon();
int lat1 = sourceNode.getILat();
short ele1 = origin.selev;
int linkdisttotal = 0;
MessageData msgData = recordMessageData ? new MessageData() : null;
message = detailMode ? new MessageData() : null;
boolean isReverse = link.isReverse( sourceNode );
// evaluate the way tags
rc.expctxWay.evaluate( rc.inverseDirection ^ isReverse, description );
// calculate the costfactor inputs
float costfactor = rc.expctxWay.getCostfactor();
boolean isTrafficBackbone = cost == 0 && rc.expctxWay.getIsTrafficBackbone() > 0.f;
float turncostbase = rc.expctxWay.getTurncost();
float cfup = rc.expctxWay.getUphillCostfactor();
float cfdown = rc.expctxWay.getDownhillCostfactor();
float cf = rc.expctxWay.getCostfactor();
cfup = cfup == 0.f ? cf : cfup;
cfdown = cfdown == 0.f ? cf : cfdown;
int lastpriorityclassifier = priorityclassifier;
priorityclassifier = (int)rc.expctxWay.getPriorityClassifier();
int classifiermask = (int)rc.expctxWay.getClassifierMask();
// *** add initial cost if the classifier changed
float newClassifier = rc.expctxWay.getInitialClassifier();
if ( newClassifier == 0. )
{
newClassifier = (cfup + cfdown + cf)/3;
}
float classifierDiff = newClassifier - lastClassifier;
if ( classifierDiff > 0.0005 || classifierDiff < -0.0005 )
{
lastClassifier = newClassifier;
float initialcost = rc.expctxWay.getInitialcost();
int iicost = (int)initialcost;
if ( recordMessageData )
if ( message != null )
{
msgData.linkinitcost += iicost;
message.linkinitcost += iicost;
}
cost += iicost;
}
OsmTransferNode transferNode = link.geometry == null ? null
: rc.geometryDecoder.decodeGeometry( link.geometry, p1, targetNode, isReverse );
: rc.geometryDecoder.decodeGeometry( link.geometry, sourceNode, targetNode, isReverse );
boolean isFirstSection = true;
for(;;)
for(int nsection=0; ;nsection++)
{
originLon = lon1;
originLat = lat1;
@ -197,19 +183,8 @@ final class OsmPath implements OsmLinkHolder
ele2 = transferNode.selev;
}
// check turn restrictions: do we have one with that origin?
boolean checkTRs = false;
if ( isFirstSection )
{
isFirstSection = false;
// TODO: TRs for inverse routing would need inverse TR logic,
// inverse routing for now just for target island check, so don't care (?)
// in detail mode (=final pass) no TR to not mess up voice hints
checkTRs = rc.considerTurnRestrictions && !rc.inverseDirection && !detailMode;
}
if ( checkTRs )
// check turn restrictions (n detail mode (=final pass) no TR to not mess up voice hints)
if ( nsection == 0 && rc.considerTurnRestrictions && !detailMode )
{
boolean hasAnyPositive = false;
boolean hasPositive = false;
@ -217,14 +192,22 @@ final class OsmPath implements OsmLinkHolder
TurnRestriction tr = sourceNode.firstRestriction;
while( tr != null )
{
boolean trValid = ! (tr.exceptBikes() && rc.bikeMode);
if ( trValid && tr.fromLon == lon0 && tr.fromLat == lat0 )
if ( tr.exceptBikes() && rc.bikeMode )
{
tr = tr.next;
continue;
}
int fromLon = rc.inverseDirection ? lon2 : lon0;
int fromLat = rc.inverseDirection ? lat2 : lat0;
int toLon = rc.inverseDirection ? lon0 : lon2;
int toLat = rc.inverseDirection ? lat0 : lat2;
if ( tr.fromLon == fromLon && tr.fromLat == fromLat )
{
if ( tr.isPositive )
{
hasAnyPositive = true;
}
if ( tr.toLon == lon2 && tr.toLat == lat2 )
if ( tr.toLon == toLon && tr.toLat == toLat )
{
if ( tr.isPositive )
{
@ -246,13 +229,14 @@ final class OsmPath implements OsmLinkHolder
}
// if recording, new MessageData for each section (needed for turn-instructions)
if ( recordMessageData && msgData.wayKeyValues != null )
if ( message != null && message.wayKeyValues != null )
{
originElement.message = msgData;
msgData = new MessageData();
originElement.message = message;
message = new MessageData();
}
int dist = rc.calcDistance( lon1, lat1, lon2, lat2 );
boolean stopAtEndpoint = false;
if ( rc.shortestmatch )
{
@ -263,10 +247,9 @@ final class OsmPath implements OsmLinkHolder
}
else
{
// we just start here, reset cost
// we just start here, reset everything
cost = 0;
ehbd = 0;
ehbu = 0;
resetState();
lon0 = -1; // reset turncost-pipe
lat0 = -1;
@ -285,131 +268,57 @@ final class OsmPath implements OsmLinkHolder
}
}
if ( recordMessageData )
if ( message != null )
{
msgData.linkdist += dist;
message.linkdist += dist;
}
linkdisttotal += dist;
// apply a start-direction if appropriate (by faking the origin position)
if ( lon0 == -1 && lat0 == -1 )
boolean isStartpoint = lon0 == -1 && lat0 == -1;
if ( isStartpoint )
{
double coslat = Math.cos( ( lat1 - 90000000 ) * 0.00000001234134 );
if ( rc.startDirectionValid && coslat > 0. )
if ( rc.startDirectionValid )
{
double dir = rc.startDirection.intValue() / 57.29578;
lon0 = lon1 - (int) ( 1000. * Math.sin( dir ) / coslat );
lon0 = lon1 - (int) ( 1000. * Math.sin( dir ) / rc.getCosLat() );
lat0 = lat1 - (int) ( 1000. * Math.cos( dir ) );
}
}
// *** penalty for turning angles
if ( !isTrafficBackbone && lon0 != -1 && lat0 != -1 )
{
// penalty proportional to direction change
double cos = rc.calcCosAngle( lon0, lat0, lon1, lat1, lon2, lat2 );
int actualturncost = (int)(cos * turncostbase + 0.2 ); // e.g. turncost=90 -> 90 degree = 90m penalty
cost += actualturncost;
if ( recordMessageData )
else
{
msgData.linkturncost += actualturncost;
msgData.turnangle = (float)rc.calcAngle( lon0, lat0, lon1, lat1, lon2, lat2 );
lon0 = lon1 - (lon2-lon1);
lat0 = lat1 - (lat2-lat1);
}
}
double angle = rc.calcAngle( lon0, lat0, lon1, lat1, lon2, lat2 );
double cosangle = rc.getCosAngle();
// *** penalty for elevation (penalty is for descend! in a way that slow descends give no penalty)
// only the part of the descend that does not fit into the elevation-hysteresis-buffer
// leads to an immediate penalty
int elefactor = 250000;
// *** elevation stuff
double delta_h = 0.;
if ( ele2 == Short.MIN_VALUE ) ele2 = ele1;
if ( ele1 != Short.MIN_VALUE )
{
ehbd += (ele1 - ele2)*elefactor - dist * rc.downhillcutoff;
ehbu += (ele2 - ele1)*elefactor - dist * rc.uphillcutoff;
}
float downweight = 0.f;
if ( ehbd > rc.elevationpenaltybuffer )
{
downweight = 1.f;
int excess = ehbd - rc.elevationpenaltybuffer;
int reduce = dist * rc.elevationbufferreduce;
if ( reduce > excess )
{
downweight = ((float)excess)/reduce;
reduce = excess;
}
excess = ehbd - rc.elevationmaxbuffer;
if ( reduce < excess )
{
reduce = excess;
}
ehbd -= reduce;
if ( rc.downhillcostdiv > 0 )
{
int elevationCost = reduce/rc.downhillcostdiv;
cost += elevationCost;
if ( recordMessageData )
{
msgData.linkelevationcost += elevationCost;
}
}
}
else if ( ehbd < 0 )
{
ehbd = 0;
}
float upweight = 0.f;
if ( ehbu > rc.elevationpenaltybuffer )
{
upweight = 1.f;
int excess = ehbu - rc.elevationpenaltybuffer;
int reduce = dist * rc.elevationbufferreduce;
if ( reduce > excess )
delta_h = (ele2 - ele1)/4.;
if ( rc.inverseDirection )
{
upweight = ((float)excess)/reduce;
reduce = excess;
}
excess = ehbu - rc.elevationmaxbuffer;
if ( reduce < excess )
{
reduce = excess;
}
ehbu -= reduce;
if ( rc.uphillcostdiv > 0 )
{
int elevationCost = reduce/rc.uphillcostdiv;
cost += elevationCost;
if ( recordMessageData )
{
msgData.linkelevationcost += elevationCost;
}
delta_h = -delta_h;
}
}
else if ( ehbu < 0 )
{
ehbu = 0;
}
// get the effective costfactor (slope dependent)
float costfactor = cfup*upweight + cf*(1.f - upweight - downweight) + cfdown*downweight;
if ( isTrafficBackbone )
{
costfactor = 0.f;
}
float fcost = dist * costfactor + 0.5f;
if ( ( costfactor > 9998. && !detailMode ) || fcost + cost >= 2000000000. )
double sectionCost = processWaySection( rc, dist, delta_h, angle, cosangle, isStartpoint, nsection, lastpriorityclassifier );
if ( ( sectionCost < 0. || costfactor > 9998. && !detailMode ) || sectionCost + cost >= 2000000000. )
{
cost = -1;
return;
}
int waycost = (int)(fcost);
cost += waycost;
if ( isTrafficBackbone )
{
sectionCost = 0.;
}
cost += (int)sectionCost;
// calculate traffic
if ( rc.countTraffic )
@ -419,15 +328,17 @@ final class OsmPath implements OsmLinkHolder
traffic += dist*rc.expctxWay.getTrafficSourceDensity()*Math.pow(cost2/10000.f,rc.trafficSourceExponent);
}
if ( recordMessageData )
if ( message != null )
{
msgData.costfactor = costfactor;
msgData.priorityclassifier = (int)rc.expctxWay.getPriorityClassifier();
msgData.classifiermask = (int)rc.expctxWay.getClassifierMask();
msgData.lon = lon2;
msgData.lat = lat2;
msgData.ele = ele2;
msgData.wayKeyValues = rc.expctxWay.getKeyValueDescription( isReverse, description );
message.turnangle = (float)angle;
message.time = (float)getTotalTime();
message.energy = (float)getTotalEnergy();
message.priorityclassifier = priorityclassifier;
message.classifiermask = classifiermask;
message.lon = lon2;
message.lat = lat2;
message.ele = ele2;
message.wayKeyValues = rc.expctxWay.getKeyValueDescription( isReverse, description );
}
if ( stopAtEndpoint )
@ -436,9 +347,9 @@ final class OsmPath implements OsmLinkHolder
{
originElement = OsmPathElement.create( rc.ilonshortest, rc.ilatshortest, ele2, originElement, rc.countTraffic );
originElement.cost = cost;
if ( recordMessageData )
if ( message != null )
{
originElement.message = msgData;
originElement.message = message;
}
}
if ( rc.nogomatch )
@ -473,44 +384,26 @@ final class OsmPath implements OsmLinkHolder
lon1 = lon2;
lat1 = lat2;
ele1 = ele2;
}
// check for nogo-matches (after the *actual* start of segment)
if ( rc.nogomatch )
{
cost = -1;
return;
cost = -1;
return;
}
// finally add node-costs for target node
if ( targetNode.nodeDescription != null )
// add target-node costs
double targetCost = processTargetNode( rc );
if ( targetCost < 0. || targetCost + cost >= 2000000000. )
{
boolean nodeAccessGranted = rc.expctxWay.getNodeAccessGranted() != 0.;
rc.expctxNode.evaluate( nodeAccessGranted , targetNode.nodeDescription );
float initialcost = rc.expctxNode.getInitialcost();
if ( initialcost >= 1000000. )
{
cost = -1;
return;
}
int iicost = (int)initialcost;
cost += iicost;
if ( recordMessageData )
{
msgData.linknodecost += iicost;
msgData.nodeKeyValues = rc.expctxNode.getKeyValueDescription( nodeAccessGranted, targetNode.nodeDescription );
}
cost = -1;
return;
}
if ( recordMessageData )
{
message = msgData;
}
cost += (int)targetCost;
}
public short interpolateEle( short e1, short e2, double fraction )
{
if ( e1 == Short.MIN_VALUE || e2 == Short.MIN_VALUE )
@ -520,29 +413,13 @@ final class OsmPath implements OsmLinkHolder
return (short)( e1*(1.-fraction) + e2*fraction );
}
public int elevationCorrection( RoutingContext rc )
{
return ( rc.downhillcostdiv > 0 ? ehbd/rc.downhillcostdiv : 0 )
+ ( rc.uphillcostdiv > 0 ? ehbu/rc.uphillcostdiv : 0 );
}
protected abstract double processWaySection( RoutingContext rc, double dist, double delta_h, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier );
public boolean definitlyWorseThan( OsmPath p, RoutingContext rc )
{
int c = p.cost;
if ( rc.downhillcostdiv > 0 )
{
int delta = p.ehbd - ehbd;
if ( delta > 0 ) c += delta/rc.downhillcostdiv;
}
if ( rc.uphillcostdiv > 0 )
{
int delta = p.ehbu - ehbu;
if ( delta > 0 ) c += delta/rc.uphillcostdiv;
}
return cost > c;
}
protected abstract double processTargetNode( RoutingContext rc );
public abstract int elevationCorrection( RoutingContext rc );
public abstract boolean definitlyWorseThan( OsmPath p, RoutingContext rc );
public OsmNode getSourceNode()
{
@ -569,4 +446,14 @@ final class OsmPath implements OsmLinkHolder
{
return nextForLink;
}
public double getTotalTime()
{
return 0.;
}
public double getTotalEnergy()
{
return 0.;
}
}

View file

@ -18,7 +18,7 @@ public class OsmPathElement implements OsmPos
private int ilat; // latitude
private int ilon; // longitude
private short selev; // longitude
public MessageData message = null; // description
public int cost;
@ -44,6 +44,19 @@ public class OsmPathElement implements OsmPos
return selev / 4.;
}
public final float getTime()
{
return message == null ? 0.f : message.time;
}
public final void setTime( float t )
{
if ( message != null )
{
message.time = t;
}
}
public final long getIdFromPos()
{
return ((long)ilon)<<32 | ilat;
@ -58,7 +71,7 @@ public class OsmPathElement implements OsmPos
double dlat = (ilat - p.getILat() )/1000000.;
double dlon = (ilon - p.getILon() )/1000000. * coslat;
double d = Math.sqrt( dlat*dlat + dlon*dlon ) * (6378000. / 57.);
double d = Math.sqrt( dlat*dlat + dlon*dlon ) * 110984.; // 6378000. / 57.3;
return (int)(d + 1.0 );
}

View file

@ -0,0 +1,20 @@
/**
* Container for link between two Osm nodes
*
* @author ab
*/
package btools.router;
import btools.expressions.BExpressionContext;
import btools.expressions.BExpressionContextNode;
import btools.expressions.BExpressionContextWay;
abstract class OsmPathModel
{
public abstract OsmPrePath createPrePath();
public abstract OsmPath createPath();
public abstract void init( BExpressionContextWay expctxWay, BExpressionContextNode expctxNode );
}

View file

@ -0,0 +1,29 @@
/**
* Simple version of OsmPath just to get angle and priority of first segment
*
* @author ab
*/
package btools.router;
import btools.mapaccess.OsmLink;
import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmTransferNode;
public abstract class OsmPrePath
{
protected OsmNode sourceNode;
protected OsmNode targetNode;
protected OsmLink link;
public OsmPrePath next;
public void init( OsmPath origin, OsmLink link, RoutingContext rc )
{
this.link = link;
this.sourceNode = origin.getTargetNode();
this.targetNode = link.getTarget( sourceNode );
initPrePath(origin, rc );
}
protected abstract void initPrePath(OsmPath origin, RoutingContext rc );
}

View file

@ -37,6 +37,8 @@ public final class OsmTrack
public long profileTimestamp;
public boolean isDirty;
public boolean showspeed;
private static class OsmPathElementHolder
{
public OsmPathElement node;
@ -120,7 +122,7 @@ public final class OsmTrack
MessageData current = null;
for ( OsmPathElement n : nodes )
{
if ( n.message != null )
if ( n.message != null && n.message.wayKeyValues != null )
{
MessageData md = n.message.copy();
if ( current != null )
@ -289,11 +291,15 @@ public final class OsmTrack
public void appendTrack( OsmTrack t )
{
int ourSize = nodes.size();
float t0 = ourSize > 0 ? nodes.get(ourSize - 1 ).getTime() : 0;
for ( int i = 0; i < t.nodes.size(); i++ )
{
if ( i > 0 || nodes.size() == 0 )
if ( i > 0 || ourSize == 0 )
{
nodes.add( t.nodes.get( i ) );
OsmPathElement e = t.nodes.get( i );
e.setTime( e.getTime() + t0 );
nodes.add( e );
}
}
@ -313,12 +319,16 @@ public final class OsmTrack
ascend += t.ascend;
plainAscend += t.plainAscend;
cost += t.cost;
energy += t.energy;
showspeed |= t.showspeed;
}
public int distance;
public int ascend;
public int plainAscend;
public int cost;
public int energy;
/**
* writes the track in gpx-format to a file
@ -390,7 +400,7 @@ public final class OsmTrack
}
else
{
sb.append( " creator=\"BRouter-1.4.8\" version=\"1.1\">\n" );
sb.append( " creator=\"BRouter-1.4.9\" version=\"1.1\">\n" );
}
if ( turnInstructionMode == 3) // osmand style
@ -537,6 +547,8 @@ public final class OsmTrack
sb.append( " \"track-length\": \"" ).append( distance ).append( "\",\n" );
sb.append( " \"filtered ascend\": \"" ).append( ascend ).append( "\",\n" );
sb.append( " \"plain-ascend\": \"" ).append( plainAscend ).append( "\",\n" );
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( " \"messages\": [\n" );
sb.append( " [\"" ).append( MESSAGES_HEADER.replaceAll( "\t", "\", \"" ) ).append( "\"],\n" );
@ -563,11 +575,27 @@ public final class OsmTrack
sb.append( " \"type\": \"LineString\",\n" );
sb.append( " \"coordinates\": [\n" );
OsmPathElement nn = null;
for ( OsmPathElement n : nodes )
{
String sele = n.getSElev() == Short.MIN_VALUE ? "" : ", " + n.getElev();
if ( showspeed ) // hack: show speed instead of elevation
{
int speed = 0;
if ( nn != null )
{
int dist = n.calcDistance( nn );
float dt = n.getTime()-nn.getTime();
if ( dt != 0.f )
{
speed = (int)((3.6f*dist)/dt + 0.5);
}
}
sele = ", " + speed;
}
sb.append( " [" ).append( formatILon( n.getILon() ) ).append( ", " ).append( formatILat( n.getILat() ) )
.append( sele ).append( "],\n" );
nn = n;
}
sb.deleteCharAt( sb.lastIndexOf( "," ) );
@ -580,6 +608,22 @@ public final class OsmTrack
return sb.toString();
}
private int getTotalSeconds()
{
float s = nodes.size() < 2 ? 0 : nodes.get( nodes.size()-1 ).getTime() - nodes.get( 0 ).getTime();
return (int)(s + 0.5);
}
public String getFormattedTime()
{
return format1( getTotalSeconds()/60. ) + "m";
}
public String getFormattedEnergy()
{
return format1( energy/3600000. ) + "kwh";
}
private static String formatILon( int ilon )
{
return formatPos( ilon - 180000000 );
@ -608,6 +652,13 @@ public final class OsmTrack
ac[i--] = '-';
return new String( ac, i + 1, 11 - i );
}
private String format1( double n )
{
String s = "" + (long)(n*10 + 0.5);
int len = s.length();
return s.substring( 0, len-1 ) + "." + s.charAt( len-1 );
}
public void dumpMessages( String filename, RoutingContext rc ) throws Exception
{

View file

@ -7,7 +7,6 @@ package btools.router;
import java.io.File;
import btools.expressions.BExpressionContextGlobal;
import btools.expressions.BExpressionContextNode;
import btools.expressions.BExpressionContextWay;
import btools.expressions.BExpressionMetaData;
@ -54,7 +53,7 @@ public final class ProfileCache
rc.expctxWay = expctxWay;
rc.expctxNode = expctxNode;
profilesBusy = true;
rc.readGlobalConfig(expctxWay);
rc.readGlobalConfig();
return true;
}
}
@ -62,19 +61,16 @@ public final class ProfileCache
BExpressionMetaData meta = new BExpressionMetaData();
BExpressionContextGlobal expctxGlobal = new BExpressionContextGlobal( meta );
rc.expctxWay = new BExpressionContextWay( rc.memoryclass * 512, meta );
rc.expctxNode = new BExpressionContextNode( 0, meta );
rc.expctxNode.setForeignContext( rc.expctxWay );
meta.readMetaData( new File( profileDir, "lookups.dat" ) );
expctxGlobal.parseFile( profileFile, null );
expctxGlobal.evaluate( new int[0] );
rc.readGlobalConfig(expctxGlobal);
rc.expctxWay.parseFile( profileFile, "global" );
rc.expctxNode.parseFile( profileFile, "global" );
rc.readGlobalConfig();
if ( rc.processUnusedTags )
{

View file

@ -14,6 +14,7 @@ import btools.expressions.BExpressionContext;
import btools.expressions.BExpressionContextNode;
import btools.expressions.BExpressionContextWay;
import btools.mapaccess.GeometryDecoder;
import btools.mapaccess.OsmLink;
public final class RoutingContext
{
@ -71,8 +72,34 @@ public final class RoutingContext
public double starttimeoffset;
public boolean transitonly;
public void readGlobalConfig( BExpressionContext expctxGlobal )
private void setModel( String className )
{
if ( className == null )
{
pm = new StdModel();
}
else
{
try
{
Class clazz = Class.forName( className );
pm = (OsmPathModel) clazz.newInstance();
}
catch( Exception e )
{
throw new RuntimeException( "Cannot create path-model: " + e );
}
}
pm.init( expctxWay, expctxNode );
}
public void readGlobalConfig()
{
BExpressionContext expctxGlobal = expctxWay; // just one of them...
setModel( expctxGlobal._modelClass );
downhillcostdiv = (int)expctxGlobal.getVariableValue( "downhillcost", 0.f );
downhillcutoff = (int)(expctxGlobal.getVariableValue( "downhillcutoff", 0.f )*10000);
uphillcostdiv = (int)expctxGlobal.getVariableValue( "uphillcost", 0.f );
@ -112,6 +139,9 @@ public final class RoutingContext
trafficSourceExponent = expctxGlobal.getVariableValue( "trafficSourceExponent", -0.7f );
trafficSourceMinDist = expctxGlobal.getVariableValue( "trafficSourceMinDist", 3000.f );
showspeed = 0.f != expctxGlobal.getVariableValue( "showspeed", 0.f );
inverseRouting = 0.f != expctxGlobal.getVariableValue( "inverseRouting", 0.f );
int tiMode = (int)expctxGlobal.getVariableValue( "turnInstructionMode", 0.f );
if ( tiMode != 1 ) // automatic selection from coordinate source
{
@ -128,6 +158,7 @@ public final class RoutingContext
public boolean startDirectionValid;
private double coslat;
private double cosangle;
public boolean nogomatch = false;
public boolean isEndpoint = false;
@ -148,6 +179,11 @@ public final class RoutingContext
public double trafficSourceExponent;
public double trafficSourceMinDist;
public boolean showspeed;
public boolean inverseRouting;
public OsmPrePath firstPrePath;
public int turnInstructionMode; // 0=none, 1=auto, 2=locus, 3=osmand, 4=comment-style, 5=gpsies-style
public double turnInstructionCatchingRange;
public boolean turnInstructionRoundabouts;
@ -165,7 +201,7 @@ public final class RoutingContext
try { ir = Integer.parseInt( s.substring( 4 ) ); }
catch( Exception e ) { /* ignore */ }
}
nogo.radius = ir / 111894.; // 6378000. / 57.;
nogo.radius = ir / 110984.; // 6378000. / 57.3;
}
}
@ -308,22 +344,19 @@ public final class RoutingContext
}
}
}
double dd = d * 111894.7368; // 6378000. / 57.;
double dd = d * 110984.; // 6378000. / 57.3;
return (int)(dd + 1.0 );
}
// assumes that calcDistance/calcCosAngle called in sequence, so coslat valid
public double calcCosAngle( int lon0, int lat0, int lon1, int lat1, int lon2, int lat2 )
public double getCosAngle()
{
double dlat1 = (lat1 - lat0);
double dlon1 = (lon1 - lon0) * coslat;
double dlat2 = (lat2 - lat1);
double dlon2 = (lon2 - lon1) * coslat;
return cosangle;
}
double dd = Math.sqrt( (dlat1*dlat1 + dlon1*dlon1)*(dlat2*dlat2 + dlon2*dlon2) );
if ( dd == 0. ) return 0.;
double cosp = (dlat1*dlat2 + dlon1*dlon2)/dd;
return 1.-cosp; // don't care to really do acos..
public double getCosLat()
{
return coslat;
}
public double calcAngle( int lon0, int lat0, int lon1, int lat1, int lon2, int lat2 )
@ -334,14 +367,15 @@ public final class RoutingContext
double dlon2 = (lon2 - lon1) * coslat;
double dd = Math.sqrt( (dlat1*dlat1 + dlon1*dlon1)*(dlat2*dlat2 + dlon2*dlon2) );
if ( dd == 0. ) return 0.;
if ( dd == 0. ) { cosangle = 1.; return 0.; }
double sinp = (dlat1*dlon2 - dlon1*dlat2)/dd;
double cosp = (dlat1*dlat2 + dlon1*dlon2)/dd;
cosangle = cosp;
double p;
if ( sinp > -0.7 && sinp < 0.7 )
if ( sinp > -0.7071 && sinp < 0.7071 )
{
p = Math.asin( sinp )*57.3;
p = asin( sinp );
if ( cosp < 0. )
{
p = 180. - p;
@ -349,7 +383,7 @@ public final class RoutingContext
}
else
{
p = Math.acos( cosp )*57.3;
p = 90. - asin( cosp );
if ( sinp < 0. )
{
p = - p;
@ -362,4 +396,37 @@ public final class RoutingContext
return p;
}
private static double asin( double x )
{
double x2 = x*x;
double x4 = x2*x2;
return x * ( 57.4539 + 9.57565 * x2 + 4.30904 * x4 + 2.56491 * x2*x4 );
}
public OsmPathModel pm;
public OsmPrePath createPrePath( OsmPath origin, OsmLink link )
{
OsmPrePath p = pm.createPrePath();
if ( p != null )
{
p.init( origin, link, this );
}
return p;
}
public OsmPath createPath( OsmLink link )
{
OsmPath p = pm.createPath();
p.init( link );
return p;
}
public OsmPath createPath( OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode )
{
OsmPath p = pm.createPath();
p.init( origin, link, refTrack, detailMode, this );
return p;
}
}

View file

@ -163,6 +163,10 @@ public class RoutingEngine extends Thread
track = findTrack( refTracks, lastTracks );
track.message = "track-length = " + track.distance + " filtered ascend = " + track.ascend
+ " plain-ascend = " + track.plainAscend + " cost=" + track.cost;
if ( track.energy != 0 )
{
track.message += " energy=" + track.getFormattedEnergy() + " time=" + track.getFormattedTime();
}
track.name = "brouter_" + routingContext.getProfileName() + "_" + i;
messageList.add( track.message );
@ -369,15 +373,26 @@ public class RoutingEngine extends Thread
matchWaypointsToNodes( matchedWaypoints );
// detect target islands: restricted search in inverse direction
routingContext.inverseDirection = true;
routingContext.inverseDirection = !routingContext.inverseRouting;
airDistanceCostFactor = 0.;
for( int i=0; i<matchedWaypoints.size() -1; i++ )
{
nodeLimit = 200;
OsmTrack seg = findTrack( "target-island-check", matchedWaypoints.get(i+1), matchedWaypoints.get(i), null, null, false );
if ( seg == null && nodeLimit > 0 )
if ( routingContext.inverseRouting )
{
throw new IllegalArgumentException( "target island detected for section " + i );
OsmTrack seg = findTrack( "start-island-check", matchedWaypoints.get(i), matchedWaypoints.get(i+1), null, null, false );
if ( seg == null && nodeLimit > 0 )
{
throw new IllegalArgumentException( "start island detected for section " + i );
}
}
else
{
OsmTrack seg = findTrack( "target-island-check", matchedWaypoints.get(i+1), matchedWaypoints.get(i), null, null, false );
if ( seg == null && nodeLimit > 0 )
{
throw new IllegalArgumentException( "target island detected for section " + i );
}
}
}
routingContext.inverseDirection = false;
@ -397,7 +412,18 @@ public class RoutingEngine extends Thread
refTracks[i].addNodes( lastTracks[i] );
}
OsmTrack seg = searchTrack( matchedWaypoints.get(i), matchedWaypoints.get(i+1), i == matchedWaypoints.size()-2 ? nearbyTrack : null, refTracks[i] );
OsmTrack seg;
if ( routingContext.inverseRouting )
{
routingContext.inverseDirection = true;
seg = searchTrack( matchedWaypoints.get(i+1), matchedWaypoints.get(i), null, refTracks[i] );
routingContext.inverseDirection = false;
}
else
{
seg = searchTrack( matchedWaypoints.get(i), matchedWaypoints.get(i+1), i == matchedWaypoints.size()-2 ? nearbyTrack : null, refTracks[i] );
}
if ( seg == null ) return null;
totaltrack.appendTrack( seg );
lastTracks[i] = seg;
@ -651,7 +677,7 @@ public class RoutingEngine extends Thread
OsmPath bestPath = null;
OsmLink bestLink = null;
OsmLink startLink = new OsmLink( null, n1 );
OsmPath startPath = new OsmPath( startLink );
OsmPath startPath = routingContext.createPath( startLink );
startLink.addLinkHolder( startPath, null );
double minradius = 1e10;
for( OsmLink link = n1.firstlink; link != null; link = link.getNext( n1 ) )
@ -663,7 +689,7 @@ public class RoutingEngine extends Thread
if ( nextNode != n2 ) continue; // just that link
wp.radius = 1e9;
OsmPath testPath = new OsmPath( startPath, link, null, guideTrack != null, routingContext );
OsmPath testPath = routingContext.createPath( startPath, link, null, guideTrack != null );
testPath.airdistance = endPos == null ? 0 : nextNode.calcDistance( endPos );
if ( wp.radius < minradius )
{
@ -692,12 +718,12 @@ public class RoutingEngine extends Thread
{
if ( wp != null ) routingContext.setWaypoint( wp, true );
OsmLink startLink = new OsmLink( null, n1 );
OsmPath startPath = new OsmPath( startLink );
OsmPath startPath = routingContext.createPath( startLink );
startLink.addLinkHolder( startPath, null );
if ( wp != null ) wp.radius = 1e-5;
return new OsmPath( startPath, link, null, guideTrack != null, routingContext );
return routingContext.createPath( startPath, link, null, guideTrack != null );
}
finally
{
@ -761,7 +787,7 @@ public class RoutingEngine extends Thread
if ( start1 == null || start2 == null ) return null;
if ( routingContext.startDirectionValid = ( fastPartialRecalc && routingContext.startDirection != null ) )
if ( routingContext.startDirectionValid = ( fastPartialRecalc && routingContext.startDirection != null && !routingContext.inverseDirection ) )
{
logInfo( "using start direction " + routingContext.startDirection );
}
@ -865,7 +891,9 @@ public class RoutingEngine extends Thread
{
// track found, compile
logInfo( "found track at cost " + path.cost + " nodesVisited = " + nodesVisited );
return compileTrack( path, verbose );
OsmTrack t = compileTrack( path, verbose );
t.showspeed = routingContext.showspeed;
return t;
}
// check for a match with the cost-cutting-track
@ -916,11 +944,38 @@ public class RoutingEngine extends Thread
}
// recheck cutoff before doing expensive stuff
if ( path.cost + path.airdistance > maxTotalCost + 10 )
if ( path.cost + path.airdistance > maxTotalCost + 100 )
{
path.unregisterUpTree( routingContext );
continue;
}
routingContext.firstPrePath = null;
for( OsmLink link = currentNode.firstlink; link != null; link = link.getNext( currentNode) )
{
OsmNode nextNode = link.getTarget( currentNode );
if ( ! nodesCache.obtainNonHollowNode( nextNode ) )
{
continue; // border node?
}
if ( nextNode.firstlink == null )
{
continue; // don't care about dead ends
}
if ( nextNode == sourceNode )
{
continue; // border node?
}
OsmPrePath prePath = routingContext.createPrePath( path, link );
if ( prePath != null )
{
prePath.next = routingContext.firstPrePath;
routingContext.firstPrePath = prePath;
}
}
for( OsmLink link = currentNode.firstlink; link != null; link = link.getNext( currentNode) )
{
@ -946,14 +1001,14 @@ public class RoutingEngine extends Thread
{
continue;
}
OsmPathElement guideNode = guideTrack.nodes.get( gidx );
OsmPathElement guideNode = guideTrack.nodes.get( routingContext.inverseRouting ? guideTrack.nodes.size() - 1 - gidx : gidx );
long nextId = nextNode.getIdFromPos();
if ( nextId != guideNode.getIdFromPos() )
{
// not along the guide-track, discard, but register for voice-hint processing
if ( routingContext.turnInstructionMode > 0 )
{
OsmPath detour = new OsmPath( path, link, refTrack, true, routingContext );
OsmPath detour = routingContext.createPath( path, link, refTrack, true );
if ( detour.cost >= 0. && nextId != startNodeId1 && nextId != startNodeId2 )
{
guideTrack.registerDetourForId( currentNode.getIdFromPos(), OsmPathElement.create( detour, false ) );
@ -985,7 +1040,7 @@ public class RoutingEngine extends Thread
endPos.radius = 1e-5;
routingContext.setWaypoint( endPos, true );
}
OsmPath testPath = new OsmPath( otherPath, link, refTrack, guideTrack != null, routingContext );
OsmPath testPath = routingContext.createPath( otherPath, link, refTrack, guideTrack != null );
if ( testPath.cost >= 0 && ( bestPath == null || testPath.cost < bestPath.cost ) )
{
bestPath = testPath;
@ -1004,7 +1059,7 @@ public class RoutingEngine extends Thread
boolean inRadius = boundary == null || boundary.isInBoundary( nextNode, bestPath.cost );
if ( inRadius && ( isFinalLink || bestPath.cost + bestPath.airdistance <= maxTotalCost + 10 ) )
if ( inRadius && ( isFinalLink || bestPath.cost + bestPath.airdistance <= maxTotalCost + 100 ) )
{
// add only if this may beat an existing path for that link
OsmLinkHolder dominator = link.getFirstLinkHolder( currentNode );
@ -1052,12 +1107,18 @@ public class RoutingEngine extends Thread
private OsmTrack compileTrack( OsmPath path, boolean verbose )
{
OsmPathElement element = OsmPathElement.create( path, false );
// for final track, cut endnode
if ( guideTrack != null ) element = element.origin;
if ( guideTrack != null )
{
element = element.origin;
}
float totalTime = element.getTime();
OsmTrack track = new OsmTrack();
track.cost = path.cost;
track.energy = (int)path.getTotalEnergy();
int distance = 0;
double ascend = 0;
@ -1066,9 +1127,24 @@ public class RoutingEngine extends Thread
short ele_start = Short.MIN_VALUE;
short ele_end = Short.MIN_VALUE;
double eleFactor = routingContext.inverseRouting ? -0.25 : 0.25;
while ( element != null )
{
track.addNode( element );
if ( guideTrack != null && element.message == null )
{
element.message = new MessageData();
}
if ( routingContext.inverseRouting )
{
element.setTime( totalTime - element.getTime() );
track.nodes.add( element );
}
else
{
track.nodes.add( 0, element );
}
OsmPathElement nextElement = element.origin;
short ele = element.getSElev();
@ -1081,7 +1157,7 @@ public class RoutingEngine extends Thread
short ele_next = nextElement.getSElev();
if ( ele_next != Short.MIN_VALUE )
{
ehb = ehb + (ele - ele_next)/4.;
ehb = ehb + (ele - ele_next)*eleFactor;
}
if ( ehb > 10. )
{
@ -1098,7 +1174,7 @@ public class RoutingEngine extends Thread
ascend += ehb;
track.distance = distance;
track.ascend = (int)ascend;
track.plainAscend = ( ele_end - ele_start ) / 4;
track.plainAscend = (int)(( ele_end - ele_start )*eleFactor+0.5);
logInfo( "track-length = " + track.distance );
logInfo( "filtered ascend = " + track.ascend );
track.buildMap();

View file

@ -0,0 +1,38 @@
/**
* Container for link between two Osm nodes
*
* @author ab
*/
package btools.router;
import btools.expressions.BExpressionContext;
import btools.expressions.BExpressionContextNode;
import btools.expressions.BExpressionContextWay;
final class StdModel extends OsmPathModel
{
public OsmPrePath createPrePath()
{
return null;
}
public OsmPath createPath()
{
return new StdPath();
}
protected BExpressionContextWay ctxWay;
protected BExpressionContextNode ctxNode;
@Override
public void init( BExpressionContextWay expctxWay, BExpressionContextNode expctxNode )
{
ctxWay = expctxWay;
ctxNode = expctxNode;
BExpressionContext expctxGlobal = expctxWay; // just one of them...
}
}

View file

@ -0,0 +1,596 @@
/**
* Container for link between two Osm nodes
*
* @author ab
*/
package btools.router;
import btools.mapaccess.OsmLink;
import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmTransferNode;
import btools.mapaccess.TurnRestriction;
final class StdPath extends OsmPath
{
/**
* The elevation-hysteresis-buffer (0-10 m)
*/
private int ehbd; // in micrometer
private int ehbu; // in micrometer
@Override
public void init( OsmPath orig )
{
StdPath origin = (StdPath)orig;
this.ehbd = origin.ehbd;
this.ehbu = origin.ehbu;
}
@Override
protected void resetState()
{
ehbd = 0;
ehbu = 0;
}
@Override
protected double processWaySection( RoutingContext rc, double distance, double delta_h, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier )
{
// calculate the costfactor inputs
float turncostbase = rc.expctxWay.getTurncost();
float cfup = rc.expctxWay.getUphillCostfactor();
float cfdown = rc.expctxWay.getDownhillCostfactor();
float cf = rc.expctxWay.getCostfactor();
cfup = cfup == 0.f ? cf : cfup;
cfdown = cfdown == 0.f ? cf : cfdown;
int dist = (int)distance; // legacy arithmetics needs int
// penalty for turning angle
int turncost = (int)((1.-cosangle) * turncostbase + 0.2 ); // e.g. turncost=90 -> 90 degree = 90m penalty
if ( message != null )
{
message.linkturncost += turncost;
message.turnangle = (float)angle;
}
double sectionCost = turncost;
// *** penalty for elevation
// only the part of the descend that does not fit into the elevation-hysteresis-buffers
// leads to an immediate penalty
int delta_h_micros = (int)(1000000. * delta_h);
ehbd += -delta_h_micros - dist * rc.downhillcutoff;
ehbu += delta_h_micros - dist * rc.uphillcutoff;
float downweight = 0.f;
if ( ehbd > rc.elevationpenaltybuffer )
{
downweight = 1.f;
int excess = ehbd - rc.elevationpenaltybuffer;
int reduce = dist * rc.elevationbufferreduce;
if ( reduce > excess )
{
downweight = ((float)excess)/reduce;
reduce = excess;
}
excess = ehbd - rc.elevationmaxbuffer;
if ( reduce < excess )
{
reduce = excess;
}
ehbd -= reduce;
if ( rc.downhillcostdiv > 0 )
{
int elevationCost = reduce/rc.downhillcostdiv;
sectionCost += elevationCost;
if ( message != null )
{
message.linkelevationcost += elevationCost;
}
}
}
else if ( ehbd < 0 )
{
ehbd = 0;
}
float upweight = 0.f;
if ( ehbu > rc.elevationpenaltybuffer )
{
upweight = 1.f;
int excess = ehbu - rc.elevationpenaltybuffer;
int reduce = dist * rc.elevationbufferreduce;
if ( reduce > excess )
{
upweight = ((float)excess)/reduce;
reduce = excess;
}
excess = ehbu - rc.elevationmaxbuffer;
if ( reduce < excess )
{
reduce = excess;
}
ehbu -= reduce;
if ( rc.uphillcostdiv > 0 )
{
int elevationCost = reduce/rc.uphillcostdiv;
sectionCost += elevationCost;
if ( message != null )
{
message.linkelevationcost += elevationCost;
}
}
}
else if ( ehbu < 0 )
{
ehbu = 0;
}
// get the effective costfactor (slope dependent)
float costfactor = cfup*upweight + cf*(1.f - upweight - downweight) + cfdown*downweight;
if ( message != null )
{
message.costfactor = costfactor;
}
sectionCost += dist * costfactor + 0.5f;
return sectionCost;
}
@Override
protected double processTargetNode( RoutingContext rc )
{
// finally add node-costs for target node
if ( targetNode.nodeDescription != null )
{
boolean nodeAccessGranted = rc.expctxWay.getNodeAccessGranted() != 0.;
rc.expctxNode.evaluate( nodeAccessGranted , targetNode.nodeDescription );
float initialcost = rc.expctxNode.getInitialcost();
if ( initialcost >= 1000000. )
{
return -1.;
}
if ( message != null )
{
message.linknodecost += (int)initialcost;
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription( nodeAccessGranted, targetNode.nodeDescription );
}
return initialcost;
}
return 0.;
}
// @Override
protected void xxxaddAddionalPenalty(OsmTrack refTrack, boolean detailMode, OsmPath origin, OsmLink link, RoutingContext rc )
{
byte[] description = link.descriptionBitmap;
if ( description == null ) throw new IllegalArgumentException( "null description for: " + link );
boolean recordTransferNodes = detailMode || rc.countTraffic;
boolean recordMessageData = detailMode;
rc.nogomatch = false;
// extract the 3 positions of the first section
int lon0 = origin.originLon;
int lat0 = origin.originLat;
OsmNode p1 = sourceNode;
int lon1 = p1.getILon();
int lat1 = p1.getILat();
short ele1 = origin.selev;
int linkdisttotal = 0;
MessageData msgData = recordMessageData ? new MessageData() : null;
boolean isReverse = link.isReverse( sourceNode );
// evaluate the way tags
rc.expctxWay.evaluate( rc.inverseDirection ^ isReverse, description );
// calculate the costfactor inputs
boolean isTrafficBackbone = cost == 0 && rc.expctxWay.getIsTrafficBackbone() > 0.f;
float turncostbase = rc.expctxWay.getTurncost();
float cfup = rc.expctxWay.getUphillCostfactor();
float cfdown = rc.expctxWay.getDownhillCostfactor();
float cf = rc.expctxWay.getCostfactor();
cfup = cfup == 0.f ? cf : cfup;
cfdown = cfdown == 0.f ? cf : cfdown;
// *** add initial cost if the classifier changed
float newClassifier = rc.expctxWay.getInitialClassifier();
if ( newClassifier == 0. )
{
newClassifier = (cfup + cfdown + cf)/3;
}
float classifierDiff = newClassifier - lastClassifier;
if ( classifierDiff > 0.0005 || classifierDiff < -0.0005 )
{
lastClassifier = newClassifier;
float initialcost = rc.expctxWay.getInitialcost();
int iicost = (int)initialcost;
if ( recordMessageData )
{
msgData.linkinitcost += iicost;
}
cost += iicost;
}
OsmTransferNode transferNode = link.geometry == null ? null
: rc.geometryDecoder.decodeGeometry( link.geometry, p1, targetNode, isReverse );
boolean isFirstSection = true;
for(;;)
{
originLon = lon1;
originLat = lat1;
int lon2;
int lat2;
short ele2;
if ( transferNode == null )
{
lon2 = targetNode.ilon;
lat2 = targetNode.ilat;
ele2 = targetNode.selev;
}
else
{
lon2 = transferNode.ilon;
lat2 = transferNode.ilat;
ele2 = transferNode.selev;
}
// check turn restrictions: do we have one with that origin?
boolean checkTRs = false;
if ( isFirstSection )
{
isFirstSection = false;
// TODO: TRs for inverse routing would need inverse TR logic,
// inverse routing for now just for target island check, so don't care (?)
// in detail mode (=final pass) no TR to not mess up voice hints
checkTRs = rc.considerTurnRestrictions && !rc.inverseDirection && !detailMode;
}
if ( checkTRs )
{
boolean hasAnyPositive = false;
boolean hasPositive = false;
boolean hasNegative = false;
TurnRestriction tr = sourceNode.firstRestriction;
while( tr != null )
{
boolean trValid = ! (tr.exceptBikes() && rc.bikeMode);
if ( trValid && tr.fromLon == lon0 && tr.fromLat == lat0 )
{
if ( tr.isPositive )
{
hasAnyPositive = true;
}
if ( tr.toLon == lon2 && tr.toLat == lat2 )
{
if ( tr.isPositive )
{
hasPositive = true;
}
else
{
hasNegative = true;
}
}
}
tr = tr.next;
}
if ( !hasPositive && ( hasAnyPositive || hasNegative ) )
{
cost = -1;
return;
}
}
// if recording, new MessageData for each section (needed for turn-instructions)
if ( recordMessageData && msgData.wayKeyValues != null )
{
originElement.message = msgData;
msgData = new MessageData();
}
int dist = rc.calcDistance( lon1, lat1, lon2, lat2 );
boolean stopAtEndpoint = false;
if ( rc.shortestmatch )
{
if ( rc.isEndpoint )
{
stopAtEndpoint = true;
ele2 = interpolateEle( ele1, ele2, rc.wayfraction );
}
else
{
// we just start here, reset cost
cost = 0;
ehbd = 0;
ehbu = 0;
lon0 = -1; // reset turncost-pipe
lat0 = -1;
if ( recordTransferNodes )
{
if ( rc.wayfraction > 0. )
{
ele1 = interpolateEle( ele1, ele2, 1. - rc.wayfraction );
originElement = OsmPathElement.create( rc.ilonshortest, rc.ilatshortest, ele1, null, rc.countTraffic );
}
else
{
originElement = null; // prevent duplicate point
}
}
}
}
if ( recordMessageData )
{
msgData.linkdist += dist;
}
linkdisttotal += dist;
// apply a start-direction if appropriate (by faking the origin position)
if ( lon0 == -1 && lat0 == -1 )
{
double coslat = Math.cos( ( lat1 - 90000000 ) * 0.00000001234134 );
if ( rc.startDirectionValid && coslat > 0. )
{
double dir = rc.startDirection.intValue() / 57.29578;
lon0 = lon1 - (int) ( 1000. * Math.sin( dir ) / coslat );
lat0 = lat1 - (int) ( 1000. * Math.cos( dir ) );
}
}
// *** penalty for turning angles
if ( !isTrafficBackbone && lon0 != -1 && lat0 != -1 )
{
// penalty proportional to direction change
double angle = rc.calcAngle( lon0, lat0, lon1, lat1, lon2, lat2 );
double cos = 1. - rc.getCosAngle();
int actualturncost = (int)(cos * turncostbase + 0.2 ); // e.g. turncost=90 -> 90 degree = 90m penalty
cost += actualturncost;
if ( recordMessageData )
{
msgData.linkturncost += actualturncost;
msgData.turnangle = (float)rc.calcAngle( lon0, lat0, lon1, lat1, lon2, lat2 );
}
}
// *** penalty for elevation (penalty is for descend! in a way that slow descends give no penalty)
// only the part of the descend that does not fit into the elevation-hysteresis-buffer
// leads to an immediate penalty
int elefactor = 250000;
if ( ele2 == Short.MIN_VALUE ) ele2 = ele1;
if ( ele1 != Short.MIN_VALUE )
{
ehbd += (ele1 - ele2)*elefactor - dist * rc.downhillcutoff;
ehbu += (ele2 - ele1)*elefactor - dist * rc.uphillcutoff;
}
float downweight = 0.f;
if ( ehbd > rc.elevationpenaltybuffer )
{
downweight = 1.f;
int excess = ehbd - rc.elevationpenaltybuffer;
int reduce = dist * rc.elevationbufferreduce;
if ( reduce > excess )
{
downweight = ((float)excess)/reduce;
reduce = excess;
}
excess = ehbd - rc.elevationmaxbuffer;
if ( reduce < excess )
{
reduce = excess;
}
ehbd -= reduce;
if ( rc.downhillcostdiv > 0 )
{
int elevationCost = reduce/rc.downhillcostdiv;
cost += elevationCost;
if ( recordMessageData )
{
msgData.linkelevationcost += elevationCost;
}
}
}
else if ( ehbd < 0 )
{
ehbd = 0;
}
float upweight = 0.f;
if ( ehbu > rc.elevationpenaltybuffer )
{
upweight = 1.f;
int excess = ehbu - rc.elevationpenaltybuffer;
int reduce = dist * rc.elevationbufferreduce;
if ( reduce > excess )
{
upweight = ((float)excess)/reduce;
reduce = excess;
}
excess = ehbu - rc.elevationmaxbuffer;
if ( reduce < excess )
{
reduce = excess;
}
ehbu -= reduce;
if ( rc.uphillcostdiv > 0 )
{
int elevationCost = reduce/rc.uphillcostdiv;
cost += elevationCost;
if ( recordMessageData )
{
msgData.linkelevationcost += elevationCost;
}
}
}
else if ( ehbu < 0 )
{
ehbu = 0;
}
// get the effective costfactor (slope dependent)
float costfactor = cfup*upweight + cf*(1.f - upweight - downweight) + cfdown*downweight;
if ( isTrafficBackbone )
{
costfactor = 0.f;
}
float fcost = dist * costfactor + 0.5f;
if ( ( costfactor > 9998. && !detailMode ) || fcost + cost >= 2000000000. )
{
cost = -1;
return;
}
int waycost = (int)(fcost);
cost += waycost;
// calculate traffic
if ( rc.countTraffic )
{
int minDist = (int)rc.trafficSourceMinDist;
int cost2 = cost < minDist ? minDist : cost;
traffic += dist*rc.expctxWay.getTrafficSourceDensity()*Math.pow(cost2/10000.f,rc.trafficSourceExponent);
}
if ( recordMessageData )
{
msgData.costfactor = costfactor;
msgData.priorityclassifier = (int)rc.expctxWay.getPriorityClassifier();
msgData.classifiermask = (int)rc.expctxWay.getClassifierMask();
msgData.lon = lon2;
msgData.lat = lat2;
msgData.ele = ele2;
msgData.wayKeyValues = rc.expctxWay.getKeyValueDescription( isReverse, description );
}
if ( stopAtEndpoint )
{
if ( recordTransferNodes )
{
originElement = OsmPathElement.create( rc.ilonshortest, rc.ilatshortest, ele2, originElement, rc.countTraffic );
originElement.cost = cost;
if ( recordMessageData )
{
originElement.message = msgData;
}
}
if ( rc.nogomatch )
{
cost = -1;
}
return;
}
if ( transferNode == null )
{
// *** penalty for being part of the reference track
if ( refTrack != null && refTrack.containsNode( targetNode ) && refTrack.containsNode( sourceNode ) )
{
int reftrackcost = linkdisttotal;
cost += reftrackcost;
}
selev = ele2;
break;
}
transferNode = transferNode.next;
if ( recordTransferNodes )
{
originElement = OsmPathElement.create( lon2, lat2, ele2, originElement, rc.countTraffic );
originElement.cost = cost;
originElement.addTraffic( traffic );
traffic = 0;
}
lon0 = lon1;
lat0 = lat1;
lon1 = lon2;
lat1 = lat2;
ele1 = ele2;
}
// check for nogo-matches (after the *actual* start of segment)
if ( rc.nogomatch )
{
cost = -1;
return;
}
// finally add node-costs for target node
if ( targetNode.nodeDescription != null )
{
boolean nodeAccessGranted = rc.expctxWay.getNodeAccessGranted() != 0.;
rc.expctxNode.evaluate( nodeAccessGranted , targetNode.nodeDescription );
float initialcost = rc.expctxNode.getInitialcost();
if ( initialcost >= 1000000. )
{
cost = -1;
return;
}
int iicost = (int)initialcost;
cost += iicost;
if ( recordMessageData )
{
msgData.linknodecost += iicost;
msgData.nodeKeyValues = rc.expctxNode.getKeyValueDescription( nodeAccessGranted, targetNode.nodeDescription );
}
}
if ( recordMessageData )
{
message = msgData;
}
}
@Override
public int elevationCorrection( RoutingContext rc )
{
return ( rc.downhillcostdiv > 0 ? ehbd/rc.downhillcostdiv : 0 )
+ ( rc.uphillcostdiv > 0 ? ehbu/rc.uphillcostdiv : 0 );
}
@Override
public boolean definitlyWorseThan( OsmPath path, RoutingContext rc )
{
StdPath p = (StdPath)path;
int c = p.cost;
if ( rc.downhillcostdiv > 0 )
{
int delta = p.ehbd - ehbd;
if ( delta > 0 ) c += delta/rc.downhillcostdiv;
}
if ( rc.uphillcostdiv > 0 )
{
int delta = p.ehbu - ehbu;
if ( delta > 0 ) c += delta/rc.uphillcostdiv;
}
return cost > c;
}
}

View file

@ -27,7 +27,7 @@ public final class WaypointMatcherImpl implements WaypointMatcher
this.waypoints = waypoints;
for ( MatchedWaypoint mwp : waypoints )
{
mwp.radius = maxDistance / 111894.; // 6378000. / 57.;
mwp.radius = maxDistance * 110984.; // 6378000. / 57.3;
}
}