Reformat whole codebase using Android Studio

This commit is contained in:
Manuel Fuhr 2022-07-11 06:30:17 +02:00
parent d5322667d5
commit c15913c1ab
161 changed files with 15124 additions and 18537 deletions

View file

@ -11,15 +11,12 @@ import btools.expressions.BExpressionContextNode;
import btools.expressions.BExpressionContextWay;
final class KinematicModel extends OsmPathModel
{
public OsmPrePath createPrePath()
{
final class KinematicModel extends OsmPathModel {
public OsmPrePath createPrePath() {
return new KinematicPrePath();
}
public OsmPath createPath()
{
public OsmPath createPath() {
return new KinematicPath();
}
@ -38,7 +35,7 @@ final class KinematicModel extends OsmPathModel
// derived values
public double pw; // balance power
public double cost0; // minimum possible cost per meter
private int wayIdxMaxspeed;
private int wayIdxMaxspeedExplicit;
private int wayIdxMinspeed;
@ -47,7 +44,7 @@ final class KinematicModel extends OsmPathModel
protected BExpressionContextWay ctxWay;
protected BExpressionContextNode ctxNode;
protected Map<String,String> params;
protected Map<String, String> params;
private boolean initDone = false;
@ -55,77 +52,67 @@ final class KinematicModel extends OsmPathModel
private double lastBreakingSpeed;
@Override
public void init( BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String,String> extraParams )
{
if ( !initDone )
{
public void init(BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String, String> extraParams) {
if (!initDone) {
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 );
wayIdxMaxspeed = ctxWay.getOutputVariableIndex("maxspeed", false);
wayIdxMaxspeedExplicit = ctxWay.getOutputVariableIndex("maxspeed_explicit", false);
wayIdxMinspeed = ctxWay.getOutputVariableIndex("minspeed", false);
nodeIdxMaxspeed = ctxNode.getOutputVariableIndex("maxspeed", false);
initDone = true;
}
params = extraParams;
turnAngleDecayTime = getParam( "turnAngleDecayTime", 5.f );
f_roll = getParam( "f_roll", 232.f );
f_air = getParam( "f_air", 0.4f );
f_recup = getParam( "f_recup", 400.f );
p_standby = getParam( "p_standby", 250.f );
outside_temp = getParam( "outside_temp", 20.f );
recup_efficiency = getParam( "recup_efficiency", 0.7f );
totalweight = getParam( "totalweight", 1640.f );
vmax = getParam( "vmax", 80.f ) / 3.6;
leftWaySpeed = getParam( "leftWaySpeed", 12.f ) / 3.6;
rightWaySpeed = getParam( "rightWaySpeed", 12.f ) / 3.6;
turnAngleDecayTime = getParam("turnAngleDecayTime", 5.f);
f_roll = getParam("f_roll", 232.f);
f_air = getParam("f_air", 0.4f);
f_recup = getParam("f_recup", 400.f);
p_standby = getParam("p_standby", 250.f);
outside_temp = getParam("outside_temp", 20.f);
recup_efficiency = getParam("recup_efficiency", 0.7f);
totalweight = getParam("totalweight", 1640.f);
vmax = getParam("vmax", 80.f) / 3.6;
leftWaySpeed = getParam("leftWaySpeed", 12.f) / 3.6;
rightWaySpeed = getParam("rightWaySpeed", 12.f) / 3.6;
pw = 2. * f_air * vmax * vmax * vmax - p_standby;
cost0 = (pw+p_standby)/vmax + f_roll + f_air*vmax*vmax;
cost0 = (pw + p_standby) / vmax + f_roll + f_air * vmax * vmax;
}
protected float getParam( String name, float defaultValue )
{
String sval = params == null ? null : params.get( name );
if ( sval != null )
{
return Float.parseFloat( sval );
protected float getParam(String name, float defaultValue) {
String sval = params == null ? null : params.get(name);
if (sval != null) {
return Float.parseFloat(sval);
}
float v = ctxWay.getVariableValue( name, defaultValue );
if ( params != null )
{
params.put( name, "" + v );
float v = ctxWay.getVariableValue(name, defaultValue);
if (params != null) {
params.put(name, "" + v);
}
return v;
}
public float getWayMaxspeed()
{
return ctxWay.getBuildInVariable( wayIdxMaxspeed ) / 3.6f;
public float getWayMaxspeed() {
return ctxWay.getBuildInVariable(wayIdxMaxspeed) / 3.6f;
}
public float getWayMaxspeedExplicit()
{
return ctxWay.getBuildInVariable( wayIdxMaxspeedExplicit ) / 3.6f;
public float getWayMaxspeedExplicit() {
return ctxWay.getBuildInVariable(wayIdxMaxspeedExplicit) / 3.6f;
}
public float getWayMinspeed()
{
return ctxWay.getBuildInVariable( wayIdxMinspeed ) / 3.6f;
public float getWayMinspeed() {
return ctxWay.getBuildInVariable(wayIdxMinspeed) / 3.6f;
}
public float getNodeMaxspeed()
{
return ctxNode.getBuildInVariable( nodeIdxMaxspeed ) / 3.6f;
public float getNodeMaxspeed() {
return ctxNode.getBuildInVariable(nodeIdxMaxspeed) / 3.6f;
}
/**
* get the effective speed limit from the way-limit and vmax/vmin
*/
public double getEffectiveSpeedLimit( )
{
/**
* get the effective speed limit from the way-limit and vmax/vmin
*/
public double getEffectiveSpeedLimit() {
// performance related inline coding
double minspeed = getWayMinspeed();
double espeed = minspeed > vmax ? minspeed : vmax;
@ -133,30 +120,27 @@ final class KinematicModel extends OsmPathModel
return maxspeed < espeed ? maxspeed : espeed;
}
/**
* get the breaking speed for current balance-power (pw) and effective speed limit (vl)
*/
public double getBreakingSpeed( double vl )
{
if ( vl == lastEffectiveLimit )
{
/**
* get the breaking speed for current balance-power (pw) and effective speed limit (vl)
*/
public double getBreakingSpeed(double vl) {
if (vl == lastEffectiveLimit) {
return lastBreakingSpeed;
}
double v = vl*0.8;
double pw2 = pw+p_standby;
double v = vl * 0.8;
double pw2 = pw + p_standby;
double e = recup_efficiency;
double x0 = pw2/vl+f_air*e*vl*vl+(1.-e)*f_roll;
for(int i=0;i<5;i++)
{
double v2 = v*v;
double x = pw2/v+f_air*e*v2 - x0;
double dx = 2.*e*f_air*v - pw2/v2;
v -= x/dx;
double x0 = pw2 / vl + f_air * e * vl * vl + (1. - e) * f_roll;
for (int i = 0; i < 5; i++) {
double v2 = v * v;
double x = pw2 / v + f_air * e * v2 - x0;
double dx = 2. * e * f_air * v - pw2 / v2;
v -= x / dx;
}
lastEffectiveLimit = vl;
lastBreakingSpeed = v;
return v;
}

View file

@ -8,8 +8,7 @@ package btools.router;
import btools.util.FastMath;
final class KinematicPath extends OsmPath
{
final class KinematicPath extends OsmPath {
private double ekin; // kinetic energy (Joule)
private double totalTime; // travel time (seconds)
private double totalEnergy; // total route energy (Joule)
@ -17,9 +16,8 @@ final class KinematicPath extends OsmPath
private float floatingAngleRight; // sliding average right bend (degree)
@Override
protected void init( OsmPath orig )
{
KinematicPath origin = (KinematicPath)orig;
protected void init(OsmPath orig) {
KinematicPath origin = (KinematicPath) orig;
ekin = origin.ekin;
totalTime = origin.totalTime;
totalEnergy = origin.totalEnergy;
@ -29,8 +27,7 @@ final class KinematicPath extends OsmPath
}
@Override
protected void resetState()
{
protected void resetState() {
ekin = 0.;
totalTime = 0.;
totalEnergy = 0.;
@ -39,267 +36,237 @@ final class KinematicPath extends OsmPath
}
@Override
protected double processWaySection( RoutingContext rc, double dist, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier )
{
KinematicModel km = (KinematicModel)rc.pm;
protected double processWaySection(RoutingContext rc, double dist, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier) {
KinematicModel km = (KinematicModel) rc.pm;
double cost = 0.;
double extraTime = 0.;
if ( isStartpoint )
{
if (isStartpoint) {
// for forward direction, we start with target speed
if ( !rc.inverseDirection )
{
extraTime = 0.5 * (1. - cosangle ) * 40.; // 40 seconds turn penalty
if (!rc.inverseDirection) {
extraTime = 0.5 * (1. - cosangle) * 40.; // 40 seconds turn penalty
}
}
else
{
} else {
double turnspeed = 999.; // just high
if ( km.turnAngleDecayTime != 0. ) // process turn-angle slowdown
if (km.turnAngleDecayTime != 0.) // process turn-angle slowdown
{
if ( angle < 0 ) floatingAngleLeft -= (float)angle;
else floatingAngleRight += (float)angle;
float aa = Math.max( floatingAngleLeft, floatingAngleRight );
if (angle < 0) floatingAngleLeft -= (float) angle;
else floatingAngleRight += (float) angle;
float aa = Math.max(floatingAngleLeft, floatingAngleRight);
double curveSpeed = aa > 10. ? 200. / aa : 20.;
double curveSpeed = aa > 10. ? 200. / aa : 20.;
double distanceTime = dist / curveSpeed;
double decayFactor = FastMath.exp( - distanceTime / km.turnAngleDecayTime );
floatingAngleLeft = (float)( floatingAngleLeft * decayFactor );
floatingAngleRight = (float)( floatingAngleRight * decayFactor );
double decayFactor = FastMath.exp(-distanceTime / km.turnAngleDecayTime);
floatingAngleLeft = (float) (floatingAngleLeft * decayFactor);
floatingAngleRight = (float) (floatingAngleRight * decayFactor);
if ( curveSpeed < 20. )
{
if (curveSpeed < 20.) {
turnspeed = curveSpeed;
}
}
if ( nsection == 0 ) // process slowdown by crossing geometry
if (nsection == 0) // process slowdown by crossing geometry
{
double junctionspeed = 999.; // just high
int classifiermask = (int)rc.expctxWay.getClassifierMask();
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;
for (OsmPrePath prePath = rc.firstPrePath; prePath != null; prePath = prePath.next) {
KinematicPrePath pp = (KinematicPrePath) prePath;
if ( ( (pp.classifiermask ^ classifiermask) & 8 ) != 0 ) // exactly one is linktype
if (((pp.classifiermask ^ classifiermask) & 8) != 0) // exactly one is linktype
{
continue;
}
if ( ( pp.classifiermask & 32 ) != 0 ) // touching a residential?
if ((pp.classifiermask & 32) != 0) // touching a residential?
{
hasResidential = true;
}
if ( pp.priorityclassifier > priorityclassifier || pp.priorityclassifier == priorityclassifier && priorityclassifier < 20 )
{
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;
if (diff < -40. && diff > -140.) hasLeftWay = true;
if (diff > 40. && diff < 140.) hasRightWay = true;
}
}
double residentialSpeed = 13.;
if ( hasLeftWay && junctionspeed > km.leftWaySpeed ) junctionspeed = km.leftWaySpeed;
if ( hasRightWay && junctionspeed > km.rightWaySpeed ) junctionspeed = km.rightWaySpeed;
if ( hasResidential && junctionspeed > residentialSpeed ) junctionspeed = 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) )
{
if ((lastpriorityclassifier < 20) ^ (priorityclassifier < 20)) {
extraTime += 10.;
junctionspeed = 0; // full stop for entering or leaving road network
}
if ( lastpriorityclassifier != priorityclassifier && (classifiermask & 8) != 0 )
{
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 );
if (message != null) {
message.vnode0 = (int) (junctionspeed * 3.6 + 0.5);
}
}
cutEkin( km.totalweight, turnspeed ); // apply turnspeed
cutEkin(km.totalweight, turnspeed); // apply turnspeed
}
// linear temperature correction
double tcorr = (20.-km.outside_temp)*0.0035;
double tcorr = (20. - km.outside_temp) * 0.0035;
// air_pressure down 1mb/8m
double ecorr = 0.0001375 * (elevation - 100.);
double f_air = km.f_air * ( 1. + tcorr - ecorr );
double f_air = km.f_air * (1. + tcorr - ecorr);
double distanceCost = evolveDistance( km, dist, delta_h, f_air );
double distanceCost = evolveDistance(km, dist, delta_h, f_air);
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);
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;
cost += extraTime * km.pw / km.cost0;
totalTime += extraTime;
return cost + distanceCost;
}
protected double evolveDistance( KinematicModel km, double dist, double delta_h, double f_air )
{
protected double evolveDistance(KinematicModel km, double dist, double delta_h, double f_air) {
// elevation force
double fh = delta_h * km.totalweight * 9.81 / dist;
double effectiveSpeedLimit = km.getEffectiveSpeedLimit();
double emax = 0.5*km.totalweight*effectiveSpeedLimit*effectiveSpeedLimit;
if ( emax <= 0. )
{
double emax = 0.5 * km.totalweight * effectiveSpeedLimit * effectiveSpeedLimit;
if (emax <= 0.) {
return -1.;
}
double vb = km.getBreakingSpeed( effectiveSpeedLimit );
double elow = 0.5*km.totalweight*vb*vb;
double vb = km.getBreakingSpeed(effectiveSpeedLimit);
double elow = 0.5 * km.totalweight * vb * vb;
double elapsedTime = 0.;
double dissipatedEnergy = 0.;
double v = Math.sqrt( 2. * ekin / km.totalweight );
double v = Math.sqrt(2. * ekin / km.totalweight);
double d = dist;
while( d > 0. )
{
while (d > 0.) {
boolean slow = ekin < elow;
boolean fast = ekin >= emax;
double etarget = slow ? elow : emax;
double f = km.f_roll + f_air*v*v + fh;
double f_recup = Math.max( 0., fast ? -f : (slow ? km.f_recup :0 ) -fh ); // additional recup for slow part
double f = km.f_roll + 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 timeStep;
double x;
if ( fast )
{
if (fast) {
x = d;
delta_ekin = x*f;
timeStep = x/v;
delta_ekin = x * f;
timeStep = x / v;
ekin = etarget;
}
else
{
delta_ekin = etarget-ekin;
double b = 2.*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 )
{
} else {
delta_ekin = etarget - ekin;
double b = 2. * 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)
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
{
} else {
ekin = etarget;
}
double v2 = Math.sqrt( 2. * ekin / km.totalweight );
double v2 = Math.sqrt(2. * ekin / km.totalweight);
double a = f / km.totalweight; // TODO: average force?
timeStep = (v2-v)/a;
timeStep = (v2 - v) / a;
v = v2;
}
d -= x;
elapsedTime += timeStep;
// dissipated energy does not contain elevation and efficient recup
dissipatedEnergy += delta_ekin - x*(fh + f_recup*km.recup_efficiency);
dissipatedEnergy += delta_ekin - x * (fh + f_recup * km.recup_efficiency);
// correction: inefficient recup going into heating is half efficient
double ieRecup = x*f_recup*(1.-km.recup_efficiency);
double eaux = timeStep*km.p_standby;
dissipatedEnergy -= Math.max( ieRecup, eaux ) * 0.5;
double ieRecup = x * f_recup * (1. - km.recup_efficiency);
double eaux = timeStep * km.p_standby;
dissipatedEnergy -= Math.max(ieRecup, eaux) * 0.5;
}
dissipatedEnergy += elapsedTime * km.p_standby;
totalTime += elapsedTime;
totalEnergy += dissipatedEnergy + dist*fh;
totalEnergy += dissipatedEnergy + dist * fh;
return (km.pw * elapsedTime + dissipatedEnergy)/km.cost0; // =cost
return (km.pw * elapsedTime + dissipatedEnergy) / km.cost0; // =cost
}
@Override
protected double processTargetNode( RoutingContext rc )
{
KinematicModel km = (KinematicModel)rc.pm;
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 );
if (targetNode.nodeDescription != null) {
rc.expctxNode.evaluate(false, targetNode.nodeDescription);
float initialcost = rc.expctxNode.getInitialcost();
if ( initialcost >= 1000000. )
{
if (initialcost >= 1000000.) {
return -1.;
}
cutEkin( km.totalweight, km.getNodeMaxspeed() ); // apply node maxspeed
cutEkin(km.totalweight, km.getNodeMaxspeed()); // apply node maxspeed
if ( message != null )
{
message.linknodecost += (int)initialcost;
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription( false, targetNode.nodeDescription );
if (message != null) {
message.linknodecost += (int) initialcost;
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription(false, targetNode.nodeDescription);
message.vnode1 = (int) ( km.getNodeMaxspeed() * 3.6 + 0.5 );
message.vnode1 = (int) (km.getNodeMaxspeed() * 3.6 + 0.5);
}
return initialcost;
}
return 0.;
}
private void cutEkin( double weight, double speed )
{
double e = 0.5*weight*speed*speed;
if ( ekin > e ) ekin = e;
private void cutEkin(double weight, double speed) {
double e = 0.5 * weight * speed * speed;
if (ekin > e) ekin = e;
}
@Override
public int elevationCorrection( RoutingContext rc )
{
public int elevationCorrection(RoutingContext rc) {
return 0;
}
@Override
public boolean definitlyWorseThan( OsmPath path, RoutingContext rc )
{
KinematicPath p = (KinematicPath)path;
public boolean definitlyWorseThan(OsmPath path, RoutingContext rc) {
KinematicPath p = (KinematicPath) path;
int c = p.cost;
return cost > c + 100;
int c = p.cost;
return cost > c + 100;
}
@Override
public double getTotalTime()
{
public double getTotalTime() {
return totalTime;
}
@Override
public double getTotalEnergy()
{
public double getTotalEnergy() {
return totalEnergy;
}
}

View file

@ -8,16 +8,14 @@ package btools.router;
import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmTransferNode;
final class KinematicPrePath extends OsmPrePath
{
final class KinematicPrePath extends OsmPrePath {
public double angle;
public int priorityclassifier;
public int classifiermask;
protected void initPrePath(OsmPath origin, RoutingContext rc )
{
protected void initPrePath(OsmPath origin, 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);
// extract the 3 positions of the first section
int lon0 = origin.originLon;
@ -27,32 +25,29 @@ final class KinematicPrePath extends OsmPrePath
int lon1 = p1.getILon();
int lat1 = p1.getILat();
boolean isReverse = link.isReverse( sourceNode );
boolean isReverse = link.isReverse(sourceNode);
// evaluate the way tags
rc.expctxWay.evaluate( rc.inverseDirection ^ isReverse, description );
rc.expctxWay.evaluate(rc.inverseDirection ^ isReverse, description);
OsmTransferNode transferNode = link.geometry == null ? null
: rc.geometryDecoder.decodeGeometry( link.geometry, p1, targetNode, isReverse );
: rc.geometryDecoder.decodeGeometry(link.geometry, p1, targetNode, isReverse);
int lon2;
int lat2;
if ( transferNode == null )
{
if (transferNode == null) {
lon2 = targetNode.ilon;
lat2 = targetNode.ilat;
}
else
{
} else {
lon2 = transferNode.ilon;
lat2 = transferNode.ilat;
}
int dist = rc.calcDistance( lon1, lat1, lon2, lat2 );
int dist = rc.calcDistance(lon1, lat1, lon2, lat2);
angle = rc.anglemeter.calcAngle( lon0, lat0, lon1, lat1, lon2, lat2 );
priorityclassifier = (int)rc.expctxWay.getPriorityClassifier();
classifiermask = (int)rc.expctxWay.getClassifierMask();
angle = rc.anglemeter.calcAngle(lon0, lat0, lon1, lat1, lon2, lat2);
priorityclassifier = (int) rc.expctxWay.getPriorityClassifier();
classifiermask = (int) rc.expctxWay.getClassifierMask();
}
}

View file

@ -6,15 +6,13 @@
package btools.router;
final class MessageData implements Cloneable
{
final class MessageData implements Cloneable {
int linkdist = 0;
int linkelevationcost = 0;
int linkturncost = 0;
int linknodecost = 0;
int linkinitcost = 0;
float costfactor;
int priorityclassifier;
int classifiermask;
@ -25,7 +23,7 @@ final class MessageData implements Cloneable
int lon;
int lat;
short ele;
float time;
float energy;
@ -37,84 +35,70 @@ final class MessageData implements Cloneable
int vnode1 = 999;
int extraTime = 0;
String toMessage()
{
if ( wayKeyValues == null )
{
String toMessage() {
if (wayKeyValues == null) {
return null;
}
int iCost = (int)(costfactor*1000 + 0.5f);
return (lon-180000000) + "\t"
+ (lat-90000000) + "\t"
+ ele/4 + "\t"
+ linkdist + "\t"
+ iCost + "\t"
+ linkelevationcost
+ "\t" + linkturncost
+ "\t" + linknodecost
+ "\t" + linkinitcost
+ "\t" + wayKeyValues
+ "\t" + ( nodeKeyValues == null ? "" : nodeKeyValues )
+ "\t" + ((int)time)
+ "\t" + ((int)energy);
int iCost = (int) (costfactor * 1000 + 0.5f);
return (lon - 180000000) + "\t"
+ (lat - 90000000) + "\t"
+ ele / 4 + "\t"
+ linkdist + "\t"
+ iCost + "\t"
+ linkelevationcost
+ "\t" + linkturncost
+ "\t" + linknodecost
+ "\t" + linkinitcost
+ "\t" + wayKeyValues
+ "\t" + (nodeKeyValues == null ? "" : nodeKeyValues)
+ "\t" + ((int) time)
+ "\t" + ((int) energy);
}
void add( MessageData d )
{
void add(MessageData d) {
linkdist += d.linkdist;
linkelevationcost += d.linkelevationcost;
linkturncost += d.linkturncost;
linknodecost += d.linknodecost;
linkinitcost+= d.linkinitcost;
linkinitcost += d.linkinitcost;
}
MessageData copy()
{
try
{
return (MessageData)clone();
}
catch( CloneNotSupportedException e )
{
throw new RuntimeException( e );
MessageData copy() {
try {
return (MessageData) clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
@Override
public String toString()
{
public String toString() {
return "dist=" + linkdist + " prio=" + priorityclassifier + " turn=" + turnangle;
}
public int getPrio()
{
public int getPrio() {
return priorityclassifier;
}
public boolean isBadOneway()
{
return ( classifiermask & 1 ) != 0;
public boolean isBadOneway() {
return (classifiermask & 1) != 0;
}
public boolean isGoodOneway()
{
return ( classifiermask & 2 ) != 0;
public boolean isGoodOneway() {
return (classifiermask & 2) != 0;
}
public boolean isRoundabout()
{
return ( classifiermask & 4 ) != 0;
public boolean isRoundabout() {
return (classifiermask & 4) != 0;
}
public boolean isLinktType()
{
return ( classifiermask & 8 ) != 0;
public boolean isLinktType() {
return (classifiermask & 8) != 0;
}
public boolean isGoodForCars()
{
return ( classifiermask & 16 ) != 0;
public boolean isGoodForCars() {
return (classifiermask & 16) != 0;
}
}

View file

@ -1,103 +1,98 @@
/**
* Container for an osm node
*
* @author ab
*/
package btools.router;
import btools.mapaccess.OsmNode;
import btools.util.CheapRuler;
public class OsmNodeNamed extends OsmNode
{
public String name;
public double radius; // radius of nogopoint (in meters)
public double nogoWeight; // weight for nogopoint
public boolean isNogo = false;
public OsmNodeNamed()
{
}
public OsmNodeNamed( OsmNode n)
{
super( n.ilon, n.ilat );
}
@Override
public String toString()
{
if ( Double.isNaN(nogoWeight ) ) {
return ilon + "," + ilat + "," + name;
} else {
return ilon + "," + ilat + "," + name + "," + nogoWeight;
}
}
public double distanceWithinRadius(int lon1, int lat1, int lon2, int lat2, double totalSegmentLength) {
double[] lonlat2m = CheapRuler.getLonLatToMeterScales( (lat1 + lat2) >> 1 );
boolean isFirstPointWithinCircle = CheapRuler.distance(lon1, lat1, ilon, ilat) < radius;
boolean isLastPointWithinCircle = CheapRuler.distance(lon2, lat2, ilon, ilat) < radius;
// First point is within the circle
if (isFirstPointWithinCircle) {
// Last point is within the circle
if (isLastPointWithinCircle) {
return totalSegmentLength;
}
// Last point is not within the circle
// Just swap points and go on with first first point not within the
// circle now.
// Swap longitudes
int tmp = lon2;
lon2 = lon1;
lon1 = tmp;
// Swap latitudes
tmp = lat2;
lat2 = lat1;
lat1 = tmp;
// Fix boolean values
isLastPointWithinCircle = isFirstPointWithinCircle;
isFirstPointWithinCircle = false;
}
// Distance between the initial point and projection of center of
// the circle on the current segment.
double initialToProject = (
(lon2 - lon1) * (ilon - lon1) * lonlat2m[0] * lonlat2m[0]
+ (lat2 - lat1) * (ilat - lat1) * lonlat2m[1] * lonlat2m[1]
) / totalSegmentLength;
// Distance between the initial point and the center of the circle.
double initialToCenter = CheapRuler.distance(ilon, ilat, lon1, lat1);
// Half length of the segment within the circle
double halfDistanceWithin = Math.sqrt(
radius*radius - (
initialToCenter*initialToCenter -
initialToProject*initialToProject
)
);
// Last point is within the circle
if (isLastPointWithinCircle) {
return halfDistanceWithin + (totalSegmentLength - initialToProject);
}
return 2 * halfDistanceWithin;
}
public static OsmNodeNamed decodeNogo( String s )
{
OsmNodeNamed n = new OsmNodeNamed();
int idx1 = s.indexOf( ',' );
n.ilon = Integer.parseInt( s.substring( 0, idx1 ) );
int idx2 = s.indexOf( ',', idx1+1 );
n.ilat = Integer.parseInt( s.substring( idx1+1, idx2 ) );
int idx3 = s.indexOf( ',', idx2+1 );
if ( idx3 == -1) {
n.name = s.substring( idx2 + 1 );
n.nogoWeight = Double.NaN;
} else {
n.name = s.substring( idx2+1, idx3 );
n.nogoWeight = Double.parseDouble( s.substring( idx3 + 1 ) );
}
n.isNogo = true;
return n;
}
}
/**
* Container for an osm node
*
* @author ab
*/
package btools.router;
import btools.mapaccess.OsmNode;
import btools.util.CheapRuler;
public class OsmNodeNamed extends OsmNode {
public String name;
public double radius; // radius of nogopoint (in meters)
public double nogoWeight; // weight for nogopoint
public boolean isNogo = false;
public OsmNodeNamed() {
}
public OsmNodeNamed(OsmNode n) {
super(n.ilon, n.ilat);
}
@Override
public String toString() {
if (Double.isNaN(nogoWeight)) {
return ilon + "," + ilat + "," + name;
} else {
return ilon + "," + ilat + "," + name + "," + nogoWeight;
}
}
public double distanceWithinRadius(int lon1, int lat1, int lon2, int lat2, double totalSegmentLength) {
double[] lonlat2m = CheapRuler.getLonLatToMeterScales((lat1 + lat2) >> 1);
boolean isFirstPointWithinCircle = CheapRuler.distance(lon1, lat1, ilon, ilat) < radius;
boolean isLastPointWithinCircle = CheapRuler.distance(lon2, lat2, ilon, ilat) < radius;
// First point is within the circle
if (isFirstPointWithinCircle) {
// Last point is within the circle
if (isLastPointWithinCircle) {
return totalSegmentLength;
}
// Last point is not within the circle
// Just swap points and go on with first first point not within the
// circle now.
// Swap longitudes
int tmp = lon2;
lon2 = lon1;
lon1 = tmp;
// Swap latitudes
tmp = lat2;
lat2 = lat1;
lat1 = tmp;
// Fix boolean values
isLastPointWithinCircle = isFirstPointWithinCircle;
isFirstPointWithinCircle = false;
}
// Distance between the initial point and projection of center of
// the circle on the current segment.
double initialToProject = (
(lon2 - lon1) * (ilon - lon1) * lonlat2m[0] * lonlat2m[0]
+ (lat2 - lat1) * (ilat - lat1) * lonlat2m[1] * lonlat2m[1]
) / totalSegmentLength;
// Distance between the initial point and the center of the circle.
double initialToCenter = CheapRuler.distance(ilon, ilat, lon1, lat1);
// Half length of the segment within the circle
double halfDistanceWithin = Math.sqrt(
radius * radius - (
initialToCenter * initialToCenter -
initialToProject * initialToProject
)
);
// Last point is within the circle
if (isLastPointWithinCircle) {
return halfDistanceWithin + (totalSegmentLength - initialToProject);
}
return 2 * halfDistanceWithin;
}
public static OsmNodeNamed decodeNogo(String s) {
OsmNodeNamed n = new OsmNodeNamed();
int idx1 = s.indexOf(',');
n.ilon = Integer.parseInt(s.substring(0, idx1));
int idx2 = s.indexOf(',', idx1 + 1);
n.ilat = Integer.parseInt(s.substring(idx1 + 1, idx2));
int idx3 = s.indexOf(',', idx2 + 1);
if (idx3 == -1) {
n.name = s.substring(idx2 + 1);
n.nogoWeight = Double.NaN;
} else {
n.name = s.substring(idx2 + 1, idx3);
n.nogoWeight = Double.parseDouble(s.substring(idx3 + 1));
}
n.isNogo = true;
return n;
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,524 +1,444 @@
/**
* Container for link between two Osm nodes
*
* @author ab
*/
package btools.router;
import java.io.IOException;
import btools.mapaccess.OsmLink;
import btools.mapaccess.OsmLinkHolder;
import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmTransferNode;
import btools.mapaccess.TurnRestriction;
import btools.util.CheapRuler;
abstract class OsmPath implements OsmLinkHolder
{
/**
* The cost of that path (a modified distance)
*/
public int cost = 0;
// 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
protected OsmNode sourceNode;
protected OsmNode targetNode;
protected OsmLink link;
public OsmPathElement originElement;
public OsmPathElement myElement;
protected float traffic;
private OsmLinkHolder nextForLink = null;
public int treedepth = 0;
// the position of the waypoint just before
// this path position (for angle calculation)
public int originLon;
public int originLat;
// the classifier of the segment just before this paths position
protected float lastClassifier;
protected float lastInitialCost;
protected int priorityclassifier;
private static final int PATH_START_BIT = 1;
private static final int CAN_LEAVE_DESTINATION_BIT = 2;
private static final int IS_ON_DESTINATION_BIT = 4;
private static final int HAD_DESTINATION_START_BIT = 8;
protected int bitfield = PATH_START_BIT;
private boolean getBit( int mask )
{
return (bitfield & mask ) != 0;
}
private void setBit( int mask, boolean bit )
{
if ( getBit( mask ) != bit )
{
bitfield ^= mask;
}
}
public boolean didEnterDestinationArea()
{
return !getBit( HAD_DESTINATION_START_BIT ) && getBit( IS_ON_DESTINATION_BIT );
}
public MessageData message;
public void unregisterUpTree( RoutingContext rc )
{
try
{
OsmPathElement pe = originElement;
while( pe instanceof OsmPathElementWithTraffic && ((OsmPathElementWithTraffic)pe).unregister(rc) )
{
pe = pe.origin;
}
}
catch( IOException ioe )
{
throw new RuntimeException( ioe );
}
}
public void registerUpTree()
{
if ( originElement instanceof OsmPathElementWithTraffic )
{
OsmPathElementWithTraffic ot = (OsmPathElementWithTraffic)originElement;
ot.register();
ot.addTraffic( traffic );
}
}
public void init( OsmLink link )
{
this.link = link;
targetNode = link.getTarget( null );
selev = targetNode.getSElev();
originLon = -1;
originLat = -1;
}
public void init( OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode, RoutingContext rc )
{
if ( origin.myElement == null )
{
origin.myElement = OsmPathElement.create( origin, rc.countTraffic );
}
this.originElement = origin.myElement;
this.link = link;
this.sourceNode = origin.targetNode;
this.targetNode = link.getTarget( sourceNode );
this.cost = origin.cost;
this.lastClassifier = origin.lastClassifier;
this.lastInitialCost = origin.lastInitialCost;
this.bitfield = origin.bitfield;
init( origin );
addAddionalPenalty(refTrack, detailMode, origin, link, rc );
}
protected abstract void init( OsmPath orig );
protected abstract void resetState();
protected void addAddionalPenalty(OsmTrack refTrack, boolean detailMode, OsmPath origin, OsmLink link, RoutingContext rc )
{
byte[] description = link.descriptionBitmap;
if ( description == null )
{
return; // could be a beeline path
}
boolean recordTransferNodes = detailMode || rc.countTraffic;
rc.nogoCost = 0.;
// extract the 3 positions of the first section
int lon0 = origin.originLon;
int lat0 = origin.originLat;
int lon1 = sourceNode.getILon();
int lat1 = sourceNode.getILat();
short ele1 = origin.selev;
int linkdisttotal = 0;
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;
int lastpriorityclassifier = priorityclassifier;
priorityclassifier = (int)rc.expctxWay.getPriorityClassifier();
// *** add initial cost if the classifier changed
float newClassifier = rc.expctxWay.getInitialClassifier();
float newInitialCost = rc.expctxWay.getInitialcost();
float classifierDiff = newClassifier - lastClassifier;
if ( newClassifier != 0. && lastClassifier != 0. && ( classifierDiff > 0.0005 || classifierDiff < -0.0005 ) )
{
float initialcost = rc.inverseDirection ? lastInitialCost : newInitialCost;
if ( initialcost >= 1000000. )
{
cost = -1;
return;
}
int iicost = (int)initialcost;
if ( message != null )
{
message.linkinitcost += iicost;
}
cost += iicost;
}
lastClassifier = newClassifier;
lastInitialCost = newInitialCost;
// *** destination logic: no destination access in between
int classifiermask = (int)rc.expctxWay.getClassifierMask();
boolean newDestination = (classifiermask & 64) != 0;
boolean oldDestination = getBit( IS_ON_DESTINATION_BIT );
if ( getBit( PATH_START_BIT ) )
{
setBit( PATH_START_BIT, false );
setBit( CAN_LEAVE_DESTINATION_BIT, newDestination );
setBit( HAD_DESTINATION_START_BIT, newDestination );
}
else
{
if ( oldDestination && !newDestination )
{
if ( getBit( CAN_LEAVE_DESTINATION_BIT ) )
{
setBit( CAN_LEAVE_DESTINATION_BIT, false );
}
else
{
cost = -1;
return;
}
}
}
setBit( IS_ON_DESTINATION_BIT, newDestination );
OsmTransferNode transferNode = link.geometry == null ? null
: rc.geometryDecoder.decodeGeometry( link.geometry, sourceNode, targetNode, isReverse );
for(int nsection=0; ;nsection++)
{
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;
}
boolean isStartpoint = lon0 == -1 && lat0 == -1;
// check turn restrictions (n detail mode (=final pass) no TR to not mess up voice hints)
if ( nsection == 0 && rc.considerTurnRestrictions && !detailMode&& !isStartpoint )
{
if ( rc.inverseDirection
? TurnRestriction.isTurnForbidden( sourceNode.firstRestriction, lon2, lat2, lon0, lat0, rc.bikeMode || rc.footMode, rc.carMode )
: TurnRestriction.isTurnForbidden( sourceNode.firstRestriction, lon0, lat0, lon2, lat2, rc.bikeMode || rc.footMode, rc.carMode ) )
{
cost = -1;
return;
}
}
// if recording, new MessageData for each section (needed for turn-instructions)
if ( message != null && message.wayKeyValues != null )
{
originElement.message = message;
message = 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 everything
cost = 0;
resetState();
lon0 = -1; // reset turncost-pipe
lat0 = -1;
isStartpoint = true;
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 ( rc.checkPendingEndpoint() )
{
dist = rc.calcDistance( rc.ilonshortest, rc.ilatshortest, lon2, lat2 );
if ( rc.shortestmatch )
{
stopAtEndpoint = true;
ele2 = interpolateEle( ele1, ele2, rc.wayfraction );
}
}
}
}
if ( message != null )
{
message.linkdist += dist;
}
linkdisttotal += dist;
// apply a start-direction if appropriate (by faking the origin position)
if ( isStartpoint )
{
if ( rc.startDirectionValid )
{
double dir = rc.startDirection.intValue() * CheapRuler.DEG_TO_RAD;
double[] lonlat2m = CheapRuler.getLonLatToMeterScales( (lon0 + lat1) >> 1 );
lon0 = lon1 - (int) ( 1000. * Math.sin( dir ) / lonlat2m[0] );
lat0 = lat1 - (int) ( 1000. * Math.cos( dir ) / lonlat2m[1] );
}
else
{
lon0 = lon1 - (lon2-lon1);
lat0 = lat1 - (lat2-lat1);
}
}
double angle = rc.anglemeter.calcAngle( lon0, lat0, lon1, lat1, lon2, lat2 );
double cosangle = rc.anglemeter.getCosAngle();
// *** elevation stuff
double delta_h = 0.;
if ( ele2 == Short.MIN_VALUE ) ele2 = ele1;
if ( ele1 != Short.MIN_VALUE )
{
delta_h = (ele2 - ele1)/4.;
if ( rc.inverseDirection )
{
delta_h = -delta_h;
}
}
double elevation = ele2 == Short.MIN_VALUE ? 100. : ele2/4.;
double sectionCost = processWaySection( rc, dist, delta_h, elevation, angle, cosangle, isStartpoint, nsection, lastpriorityclassifier );
if ( ( sectionCost < 0. || costfactor > 9998. && !detailMode ) || sectionCost + cost >= 2000000000. )
{
cost = -1;
return;
}
if ( isTrafficBackbone )
{
sectionCost = 0.;
}
cost += (int)sectionCost;
// 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);
}
// compute kinematic
computeKinematic( rc, dist, delta_h, detailMode );
if ( message != null )
{
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 )
{
if ( recordTransferNodes )
{
originElement = OsmPathElement.create( rc.ilonshortest, rc.ilatshortest, ele2, originElement, rc.countTraffic );
originElement.cost = cost;
if ( message != null )
{
originElement.message = message;
}
}
if ( rc.nogoCost < 0)
{
cost = -1;
}
else
{
cost += rc.nogoCost;
}
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.nogoCost < 0)
{
cost = -1;
return;
}
else
{
cost += rc.nogoCost;
}
// add target-node costs
double targetCost = processTargetNode( rc );
if ( targetCost < 0. || targetCost + cost >= 2000000000. )
{
cost = -1;
return;
}
cost += (int)targetCost;
}
public short interpolateEle( short e1, short e2, double fraction )
{
if ( e1 == Short.MIN_VALUE || e2 == Short.MIN_VALUE )
{
return Short.MIN_VALUE;
}
return (short)( e1*(1.-fraction) + e2*fraction );
}
protected abstract double processWaySection( RoutingContext rc, double dist, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier );
protected abstract double processTargetNode( RoutingContext rc );
protected void computeKinematic( RoutingContext rc, double dist, double delta_h, boolean detailMode )
{
}
public abstract int elevationCorrection( RoutingContext rc );
public abstract boolean definitlyWorseThan( OsmPath p, RoutingContext rc );
public OsmNode getSourceNode()
{
return sourceNode;
}
public OsmNode getTargetNode()
{
return targetNode;
}
public OsmLink getLink()
{
return link;
}
public void setNextForLink( OsmLinkHolder holder )
{
nextForLink = holder;
}
public OsmLinkHolder getNextForLink()
{
return nextForLink;
}
public double getTotalTime()
{
return 0.;
}
public double getTotalEnergy()
{
return 0.;
}
}
/**
* Container for link between two Osm nodes
*
* @author ab
*/
package btools.router;
import java.io.IOException;
import btools.mapaccess.OsmLink;
import btools.mapaccess.OsmLinkHolder;
import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmTransferNode;
import btools.mapaccess.TurnRestriction;
import btools.util.CheapRuler;
abstract class OsmPath implements OsmLinkHolder {
/**
* The cost of that path (a modified distance)
*/
public int cost = 0;
// 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
protected OsmNode sourceNode;
protected OsmNode targetNode;
protected OsmLink link;
public OsmPathElement originElement;
public OsmPathElement myElement;
protected float traffic;
private OsmLinkHolder nextForLink = null;
public int treedepth = 0;
// the position of the waypoint just before
// this path position (for angle calculation)
public int originLon;
public int originLat;
// the classifier of the segment just before this paths position
protected float lastClassifier;
protected float lastInitialCost;
protected int priorityclassifier;
private static final int PATH_START_BIT = 1;
private static final int CAN_LEAVE_DESTINATION_BIT = 2;
private static final int IS_ON_DESTINATION_BIT = 4;
private static final int HAD_DESTINATION_START_BIT = 8;
protected int bitfield = PATH_START_BIT;
private boolean getBit(int mask) {
return (bitfield & mask) != 0;
}
private void setBit(int mask, boolean bit) {
if (getBit(mask) != bit) {
bitfield ^= mask;
}
}
public boolean didEnterDestinationArea() {
return !getBit(HAD_DESTINATION_START_BIT) && getBit(IS_ON_DESTINATION_BIT);
}
public MessageData message;
public void unregisterUpTree(RoutingContext rc) {
try {
OsmPathElement pe = originElement;
while (pe instanceof OsmPathElementWithTraffic && ((OsmPathElementWithTraffic) pe).unregister(rc)) {
pe = pe.origin;
}
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
public void registerUpTree() {
if (originElement instanceof OsmPathElementWithTraffic) {
OsmPathElementWithTraffic ot = (OsmPathElementWithTraffic) originElement;
ot.register();
ot.addTraffic(traffic);
}
}
public void init(OsmLink link) {
this.link = link;
targetNode = link.getTarget(null);
selev = targetNode.getSElev();
originLon = -1;
originLat = -1;
}
public void init(OsmPath origin, OsmLink link, OsmTrack refTrack, boolean detailMode, RoutingContext rc) {
if (origin.myElement == null) {
origin.myElement = OsmPathElement.create(origin, rc.countTraffic);
}
this.originElement = origin.myElement;
this.link = link;
this.sourceNode = origin.targetNode;
this.targetNode = link.getTarget(sourceNode);
this.cost = origin.cost;
this.lastClassifier = origin.lastClassifier;
this.lastInitialCost = origin.lastInitialCost;
this.bitfield = origin.bitfield;
init(origin);
addAddionalPenalty(refTrack, detailMode, origin, link, rc);
}
protected abstract void init(OsmPath orig);
protected abstract void resetState();
protected void addAddionalPenalty(OsmTrack refTrack, boolean detailMode, OsmPath origin, OsmLink link, RoutingContext rc) {
byte[] description = link.descriptionBitmap;
if (description == null) {
return; // could be a beeline path
}
boolean recordTransferNodes = detailMode || rc.countTraffic;
rc.nogoCost = 0.;
// extract the 3 positions of the first section
int lon0 = origin.originLon;
int lat0 = origin.originLat;
int lon1 = sourceNode.getILon();
int lat1 = sourceNode.getILat();
short ele1 = origin.selev;
int linkdisttotal = 0;
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;
int lastpriorityclassifier = priorityclassifier;
priorityclassifier = (int) rc.expctxWay.getPriorityClassifier();
// *** add initial cost if the classifier changed
float newClassifier = rc.expctxWay.getInitialClassifier();
float newInitialCost = rc.expctxWay.getInitialcost();
float classifierDiff = newClassifier - lastClassifier;
if (newClassifier != 0. && lastClassifier != 0. && (classifierDiff > 0.0005 || classifierDiff < -0.0005)) {
float initialcost = rc.inverseDirection ? lastInitialCost : newInitialCost;
if (initialcost >= 1000000.) {
cost = -1;
return;
}
int iicost = (int) initialcost;
if (message != null) {
message.linkinitcost += iicost;
}
cost += iicost;
}
lastClassifier = newClassifier;
lastInitialCost = newInitialCost;
// *** destination logic: no destination access in between
int classifiermask = (int) rc.expctxWay.getClassifierMask();
boolean newDestination = (classifiermask & 64) != 0;
boolean oldDestination = getBit(IS_ON_DESTINATION_BIT);
if (getBit(PATH_START_BIT)) {
setBit(PATH_START_BIT, false);
setBit(CAN_LEAVE_DESTINATION_BIT, newDestination);
setBit(HAD_DESTINATION_START_BIT, newDestination);
} else {
if (oldDestination && !newDestination) {
if (getBit(CAN_LEAVE_DESTINATION_BIT)) {
setBit(CAN_LEAVE_DESTINATION_BIT, false);
} else {
cost = -1;
return;
}
}
}
setBit(IS_ON_DESTINATION_BIT, newDestination);
OsmTransferNode transferNode = link.geometry == null ? null
: rc.geometryDecoder.decodeGeometry(link.geometry, sourceNode, targetNode, isReverse);
for (int nsection = 0; ; nsection++) {
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;
}
boolean isStartpoint = lon0 == -1 && lat0 == -1;
// check turn restrictions (n detail mode (=final pass) no TR to not mess up voice hints)
if (nsection == 0 && rc.considerTurnRestrictions && !detailMode && !isStartpoint) {
if (rc.inverseDirection
? TurnRestriction.isTurnForbidden(sourceNode.firstRestriction, lon2, lat2, lon0, lat0, rc.bikeMode || rc.footMode, rc.carMode)
: TurnRestriction.isTurnForbidden(sourceNode.firstRestriction, lon0, lat0, lon2, lat2, rc.bikeMode || rc.footMode, rc.carMode)) {
cost = -1;
return;
}
}
// if recording, new MessageData for each section (needed for turn-instructions)
if (message != null && message.wayKeyValues != null) {
originElement.message = message;
message = 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 everything
cost = 0;
resetState();
lon0 = -1; // reset turncost-pipe
lat0 = -1;
isStartpoint = true;
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 (rc.checkPendingEndpoint()) {
dist = rc.calcDistance(rc.ilonshortest, rc.ilatshortest, lon2, lat2);
if (rc.shortestmatch) {
stopAtEndpoint = true;
ele2 = interpolateEle(ele1, ele2, rc.wayfraction);
}
}
}
}
if (message != null) {
message.linkdist += dist;
}
linkdisttotal += dist;
// apply a start-direction if appropriate (by faking the origin position)
if (isStartpoint) {
if (rc.startDirectionValid) {
double dir = rc.startDirection.intValue() * CheapRuler.DEG_TO_RAD;
double[] lonlat2m = CheapRuler.getLonLatToMeterScales((lon0 + lat1) >> 1);
lon0 = lon1 - (int) (1000. * Math.sin(dir) / lonlat2m[0]);
lat0 = lat1 - (int) (1000. * Math.cos(dir) / lonlat2m[1]);
} else {
lon0 = lon1 - (lon2 - lon1);
lat0 = lat1 - (lat2 - lat1);
}
}
double angle = rc.anglemeter.calcAngle(lon0, lat0, lon1, lat1, lon2, lat2);
double cosangle = rc.anglemeter.getCosAngle();
// *** elevation stuff
double delta_h = 0.;
if (ele2 == Short.MIN_VALUE) ele2 = ele1;
if (ele1 != Short.MIN_VALUE) {
delta_h = (ele2 - ele1) / 4.;
if (rc.inverseDirection) {
delta_h = -delta_h;
}
}
double elevation = ele2 == Short.MIN_VALUE ? 100. : ele2 / 4.;
double sectionCost = processWaySection(rc, dist, delta_h, elevation, angle, cosangle, isStartpoint, nsection, lastpriorityclassifier);
if ((sectionCost < 0. || costfactor > 9998. && !detailMode) || sectionCost + cost >= 2000000000.) {
cost = -1;
return;
}
if (isTrafficBackbone) {
sectionCost = 0.;
}
cost += (int) sectionCost;
// 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);
}
// compute kinematic
computeKinematic(rc, dist, delta_h, detailMode);
if (message != null) {
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) {
if (recordTransferNodes) {
originElement = OsmPathElement.create(rc.ilonshortest, rc.ilatshortest, ele2, originElement, rc.countTraffic);
originElement.cost = cost;
if (message != null) {
originElement.message = message;
}
}
if (rc.nogoCost < 0) {
cost = -1;
} else {
cost += rc.nogoCost;
}
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.nogoCost < 0) {
cost = -1;
return;
} else {
cost += rc.nogoCost;
}
// add target-node costs
double targetCost = processTargetNode(rc);
if (targetCost < 0. || targetCost + cost >= 2000000000.) {
cost = -1;
return;
}
cost += (int) targetCost;
}
public short interpolateEle(short e1, short e2, double fraction) {
if (e1 == Short.MIN_VALUE || e2 == Short.MIN_VALUE) {
return Short.MIN_VALUE;
}
return (short) (e1 * (1. - fraction) + e2 * fraction);
}
protected abstract double processWaySection(RoutingContext rc, double dist, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier);
protected abstract double processTargetNode(RoutingContext rc);
protected void computeKinematic(RoutingContext rc, double dist, double delta_h, boolean detailMode) {
}
public abstract int elevationCorrection(RoutingContext rc);
public abstract boolean definitlyWorseThan(OsmPath p, RoutingContext rc);
public OsmNode getSourceNode() {
return sourceNode;
}
public OsmNode getTargetNode() {
return targetNode;
}
public OsmLink getLink() {
return link;
}
public void setNextForLink(OsmLinkHolder holder) {
nextForLink = holder;
}
public OsmLinkHolder getNextForLink() {
return nextForLink;
}
public double getTotalTime() {
return 0.;
}
public double getTotalEnergy() {
return 0.;
}
}

View file

@ -1,136 +1,116 @@
package btools.router;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmPos;
import btools.util.CheapRuler;
/**
* Container for link between two Osm nodes
*
* @author ab
*/
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;
// interface OsmPos
public final int getILat()
{
return ilat;
}
public final int getILon()
{
return ilon;
}
public final short getSElev()
{
return selev;
}
public final double getElev()
{
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 float getEnergy()
{
return message == null ? 0.f : message.energy;
}
public final void setEnergy( float e )
{
if ( message != null )
{
message.energy = e;
}
}
public final long getIdFromPos()
{
return ((long)ilon)<<32 | ilat;
}
public final int calcDistance( OsmPos p )
{
return (int)(CheapRuler.distance(ilon, ilat, p.getILon(), p.getILat()) + 1.0 );
}
public OsmPathElement origin;
// construct a path element from a path
public static final OsmPathElement create( OsmPath path, boolean countTraffic )
{
OsmNode n = path.getTargetNode();
OsmPathElement pe = create( n.getILon(), n.getILat(), path.selev, path.originElement, countTraffic );
pe.cost = path.cost;
pe.message = path.message;
return pe;
}
public static final OsmPathElement create( int ilon, int ilat, short selev, OsmPathElement origin, boolean countTraffic )
{
OsmPathElement pe = countTraffic ? new OsmPathElementWithTraffic() : new OsmPathElement();
pe.ilon = ilon;
pe.ilat = ilat;
pe.selev = selev;
pe.origin = origin;
return pe;
}
protected OsmPathElement()
{
}
public void addTraffic( float traffic )
{
}
public String toString()
{
return ilon + "_" + ilat;
}
public void writeToStream( DataOutput dos ) throws IOException
{
dos.writeInt( ilat );
dos.writeInt( ilon );
dos.writeShort( selev );
dos.writeInt( cost );
}
public static OsmPathElement readFromStream( DataInput dis ) throws IOException
{
OsmPathElement pe = new OsmPathElement();
pe.ilat = dis.readInt();
pe.ilon = dis.readInt();
pe.selev = dis.readShort();
pe.cost = dis.readInt();
return pe;
}
}
package btools.router;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmPos;
import btools.util.CheapRuler;
/**
* Container for link between two Osm nodes
*
* @author ab
*/
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;
// interface OsmPos
public final int getILat() {
return ilat;
}
public final int getILon() {
return ilon;
}
public final short getSElev() {
return selev;
}
public final double getElev() {
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 float getEnergy() {
return message == null ? 0.f : message.energy;
}
public final void setEnergy(float e) {
if (message != null) {
message.energy = e;
}
}
public final long getIdFromPos() {
return ((long) ilon) << 32 | ilat;
}
public final int calcDistance(OsmPos p) {
return (int) (CheapRuler.distance(ilon, ilat, p.getILon(), p.getILat()) + 1.0);
}
public OsmPathElement origin;
// construct a path element from a path
public static final OsmPathElement create(OsmPath path, boolean countTraffic) {
OsmNode n = path.getTargetNode();
OsmPathElement pe = create(n.getILon(), n.getILat(), path.selev, path.originElement, countTraffic);
pe.cost = path.cost;
pe.message = path.message;
return pe;
}
public static final OsmPathElement create(int ilon, int ilat, short selev, OsmPathElement origin, boolean countTraffic) {
OsmPathElement pe = countTraffic ? new OsmPathElementWithTraffic() : new OsmPathElement();
pe.ilon = ilon;
pe.ilat = ilat;
pe.selev = selev;
pe.origin = origin;
return pe;
}
protected OsmPathElement() {
}
public void addTraffic(float traffic) {
}
public String toString() {
return ilon + "_" + ilat;
}
public void writeToStream(DataOutput dos) throws IOException {
dos.writeInt(ilat);
dos.writeInt(ilon);
dos.writeShort(selev);
dos.writeInt(cost);
}
public static OsmPathElement readFromStream(DataInput dis) throws IOException {
OsmPathElement pe = new OsmPathElement();
pe.ilat = dis.readInt();
pe.ilon = dis.readInt();
pe.selev = dis.readShort();
pe.cost = dis.readInt();
return pe;
}
}

View file

@ -9,19 +9,15 @@ import java.io.IOException;
* @author ab
*/
public final class OsmPathElementWithTraffic extends OsmPathElement
{
public final class OsmPathElementWithTraffic extends OsmPathElement {
private int registerCount;
private float farTraffic;
private float nearTraffic;
public void register()
{
if ( registerCount++ == 0 )
{
if ( origin instanceof OsmPathElementWithTraffic )
{
OsmPathElementWithTraffic ot = (OsmPathElementWithTraffic)origin;
public void register() {
if (registerCount++ == 0) {
if (origin instanceof OsmPathElementWithTraffic) {
OsmPathElementWithTraffic ot = (OsmPathElementWithTraffic) origin;
ot.register();
ot.farTraffic += farTraffic;
ot.nearTraffic += nearTraffic;
@ -30,42 +26,36 @@ public final class OsmPathElementWithTraffic extends OsmPathElement
}
}
}
@Override
public void addTraffic( float traffic )
{
public void addTraffic(float traffic) {
this.farTraffic += traffic;
this.nearTraffic += traffic;
}
// unregister from origin if our registercount is 0, else do nothing
public static double maxtraffic = 0.;
public static double maxtraffic = 0.;
public boolean unregister( RoutingContext rc ) throws IOException
{
if ( --registerCount == 0 )
{
if ( origin instanceof OsmPathElementWithTraffic )
{
OsmPathElementWithTraffic ot = (OsmPathElementWithTraffic)origin;
int costdelta = cost-ot.cost;
ot.farTraffic += farTraffic*Math.exp(-costdelta/rc.farTrafficDecayLength);
ot.nearTraffic += nearTraffic*Math.exp(-costdelta/rc.nearTrafficDecayLength);
public boolean unregister(RoutingContext rc) throws IOException {
if (--registerCount == 0) {
if (origin instanceof OsmPathElementWithTraffic) {
OsmPathElementWithTraffic ot = (OsmPathElementWithTraffic) origin;
if ( costdelta > 0 && farTraffic > maxtraffic ) maxtraffic = farTraffic;
int t2 = cost == ot.cost ? -1 : (int)(rc.farTrafficWeight*farTraffic + rc.nearTrafficWeight*nearTraffic);
if ( t2 > 4000 || t2 == -1 )
{
int costdelta = cost - ot.cost;
ot.farTraffic += farTraffic * Math.exp(-costdelta / rc.farTrafficDecayLength);
ot.nearTraffic += nearTraffic * Math.exp(-costdelta / rc.nearTrafficDecayLength);
if (costdelta > 0 && farTraffic > maxtraffic) maxtraffic = farTraffic;
int t2 = cost == ot.cost ? -1 : (int) (rc.farTrafficWeight * farTraffic + rc.nearTrafficWeight * nearTraffic);
if (t2 > 4000 || t2 == -1) {
// System.out.println( "unregistered: " + this + " origin=" + ot + " farTraffic =" + farTraffic + " nearTraffic =" + nearTraffic + " cost=" + cost );
if ( rc.trafficOutputStream != null )
{
rc.trafficOutputStream.writeLong( getIdFromPos());
rc.trafficOutputStream.writeLong( ot.getIdFromPos());
rc.trafficOutputStream.writeInt( t2 );
if (rc.trafficOutputStream != null) {
rc.trafficOutputStream.writeLong(getIdFromPos());
rc.trafficOutputStream.writeLong(ot.getIdFromPos());
rc.trafficOutputStream.writeInt(t2);
}
}
farTraffic = 0;

View file

@ -11,11 +11,10 @@ import btools.expressions.BExpressionContextNode;
import btools.expressions.BExpressionContextWay;
abstract class OsmPathModel
{
abstract class OsmPathModel {
public abstract OsmPrePath createPrePath();
public abstract OsmPath createPath();
public abstract void init( BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String,String> keyValues );
public abstract void init(BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String, String> keyValues);
}

View file

@ -9,21 +9,19 @@ import btools.mapaccess.OsmLink;
import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmTransferNode;
public abstract class OsmPrePath
{
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 )
{
public void init(OsmPath origin, OsmLink link, RoutingContext rc) {
this.link = link;
this.sourceNode = origin.getTargetNode();
this.targetNode = link.getTarget( sourceNode );
initPrePath(origin, rc );
this.targetNode = link.getTarget(sourceNode);
initPrePath(origin, rc);
}
protected abstract void initPrePath(OsmPath origin, RoutingContext rc );
protected abstract void initPrePath(OsmPath origin, RoutingContext rc);
}

File diff suppressed because it is too large Load diff

View file

@ -11,146 +11,125 @@ import btools.expressions.BExpressionContextNode;
import btools.expressions.BExpressionContextWay;
import btools.expressions.BExpressionMetaData;
public final class ProfileCache
{
public final class ProfileCache {
private static File lastLookupFile;
private static long lastLookupTimestamp;
private BExpressionContextWay expctxWay;
private BExpressionContextNode expctxNode;
private File lastProfileFile;
private long lastProfileTimestamp;
private long lastProfileTimestamp;
private boolean profilesBusy;
private long lastUseTime;
private static ProfileCache[] apc = new ProfileCache[1];
private static boolean debug = Boolean.getBoolean( "debugProfileCache" );
public static synchronized void setSize( int size )
{
private static ProfileCache[] apc = new ProfileCache[1];
private static boolean debug = Boolean.getBoolean("debugProfileCache");
public static synchronized void setSize(int size) {
apc = new ProfileCache[size];
}
public static synchronized boolean parseProfile( RoutingContext rc )
{
String profileBaseDir = System.getProperty( "profileBaseDir" );
File profileDir;
File profileFile;
if ( profileBaseDir == null )
{
profileDir = new File( rc.localFunction ).getParentFile();
profileFile = new File( rc.localFunction ) ;
}
else
{
profileDir = new File( profileBaseDir );
profileFile = new File( profileDir, rc.localFunction + ".brf" ) ;
}
public static synchronized boolean parseProfile(RoutingContext rc) {
String profileBaseDir = System.getProperty("profileBaseDir");
File profileDir;
File profileFile;
if (profileBaseDir == null) {
profileDir = new File(rc.localFunction).getParentFile();
profileFile = new File(rc.localFunction);
} else {
profileDir = new File(profileBaseDir);
profileFile = new File(profileDir, rc.localFunction + ".brf");
}
rc.profileTimestamp = profileFile.lastModified() + rc.getKeyValueChecksum()<<24;
File lookupFile = new File( profileDir, "lookups.dat" );
// invalidate cache at lookup-table update
if ( !(lookupFile.equals( lastLookupFile ) && lookupFile.lastModified() == lastLookupTimestamp ) )
{
if ( lastLookupFile != null )
{
System.out.println( "******** invalidating profile-cache after lookup-file update ******** " );
}
apc = new ProfileCache[apc.length];
lastLookupFile = lookupFile;
lastLookupTimestamp = lookupFile.lastModified();
}
ProfileCache lru = null;
int unusedSlot =-1;
rc.profileTimestamp = profileFile.lastModified() + rc.getKeyValueChecksum() << 24;
File lookupFile = new File(profileDir, "lookups.dat");
// check for re-use
for( int i=0; i<apc.length; i++)
{
ProfileCache pc = apc[i];
if ( pc != null )
{
if ( (!pc.profilesBusy) && profileFile.equals( pc.lastProfileFile ) )
{
if ( rc.profileTimestamp == pc.lastProfileTimestamp )
{
rc.expctxWay = pc.expctxWay;
rc.expctxNode = pc.expctxNode;
rc.readGlobalConfig();
pc.profilesBusy = true;
return true;
}
lru = pc; // name-match but timestamp-mismatch -> we overide this one
unusedSlot = -1;
break;
}
if ( lru == null || lru.lastUseTime > pc.lastUseTime )
{
lru = pc;
// invalidate cache at lookup-table update
if (!(lookupFile.equals(lastLookupFile) && lookupFile.lastModified() == lastLookupTimestamp)) {
if (lastLookupFile != null) {
System.out.println("******** invalidating profile-cache after lookup-file update ******** ");
}
apc = new ProfileCache[apc.length];
lastLookupFile = lookupFile;
lastLookupTimestamp = lookupFile.lastModified();
}
ProfileCache lru = null;
int unusedSlot = -1;
// check for re-use
for (int i = 0; i < apc.length; i++) {
ProfileCache pc = apc[i];
if (pc != null) {
if ((!pc.profilesBusy) && profileFile.equals(pc.lastProfileFile)) {
if (rc.profileTimestamp == pc.lastProfileTimestamp) {
rc.expctxWay = pc.expctxWay;
rc.expctxNode = pc.expctxNode;
rc.readGlobalConfig();
pc.profilesBusy = true;
return true;
}
lru = pc; // name-match but timestamp-mismatch -> we overide this one
unusedSlot = -1;
break;
}
else if ( unusedSlot < 0 )
{
unusedSlot = i;
if (lru == null || lru.lastUseTime > pc.lastUseTime) {
lru = pc;
}
} else if (unusedSlot < 0) {
unusedSlot = i;
}
BExpressionMetaData meta = new BExpressionMetaData();
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" ) );
}
rc.expctxWay.parseFile( profileFile, "global" );
rc.expctxNode.parseFile( profileFile, "global" );
BExpressionMetaData meta = new BExpressionMetaData();
rc.readGlobalConfig();
if ( rc.processUnusedTags )
{
rc.expctxWay.setAllTagsUsed();
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"));
rc.expctxWay.parseFile(profileFile, "global");
rc.expctxNode.parseFile(profileFile, "global");
rc.readGlobalConfig();
if (rc.processUnusedTags) {
rc.expctxWay.setAllTagsUsed();
}
if (lru == null || unusedSlot >= 0) {
lru = new ProfileCache();
if (unusedSlot >= 0) {
apc[unusedSlot] = lru;
if (debug)
System.out.println("******* adding new profile at idx=" + unusedSlot + " for " + profileFile);
}
}
if ( lru == null || unusedSlot >= 0 )
{
lru = new ProfileCache();
if ( unusedSlot >= 0 )
{
apc[unusedSlot] = lru;
if ( debug ) System.out.println( "******* adding new profile at idx=" + unusedSlot + " for " + profileFile );
}
}
if (lru.lastProfileFile != null) {
if (debug)
System.out.println("******* replacing profile of age " + ((System.currentTimeMillis() - lru.lastUseTime) / 1000L) + " sec " + lru.lastProfileFile + "->" + profileFile);
}
if ( lru.lastProfileFile != null )
{
if ( debug ) System.out.println( "******* replacing profile of age " + ((System.currentTimeMillis()-lru.lastUseTime)/1000L) + " sec " + lru.lastProfileFile + "->" + profileFile );
}
lru.lastProfileTimestamp = rc.profileTimestamp;
lru.lastProfileFile = profileFile;
lru.expctxWay = rc.expctxWay;
lru.expctxNode = rc.expctxNode;
lru.profilesBusy = true;
lru.lastUseTime = System.currentTimeMillis();
return false;
lru.lastProfileTimestamp = rc.profileTimestamp;
lru.lastProfileFile = profileFile;
lru.expctxWay = rc.expctxWay;
lru.expctxNode = rc.expctxNode;
lru.profilesBusy = true;
lru.lastUseTime = System.currentTimeMillis();
return false;
}
public static synchronized void releaseProfile( RoutingContext rc )
{
for( int i=0; i<apc.length; i++)
{
public static synchronized void releaseProfile(RoutingContext rc) {
for (int i = 0; i < apc.length; i++) {
ProfileCache pc = apc[i];
if ( pc != null )
{
if (pc != null) {
// only the thread that holds the cached instance can release it
if ( rc.expctxWay == pc.expctxWay && rc.expctxNode == pc.expctxNode )
{
if (rc.expctxWay == pc.expctxWay && rc.expctxNode == pc.expctxNode) {
pc.profilesBusy = false;
break;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -9,41 +9,33 @@ import java.io.File;
import btools.mapaccess.StorageConfigHelper;
public final class RoutingHelper
{
public static File getAdditionalMaptoolDir( File segmentDir )
{
return StorageConfigHelper.getAdditionalMaptoolDir(segmentDir);
}
public final class RoutingHelper {
public static File getAdditionalMaptoolDir(File segmentDir) {
return StorageConfigHelper.getAdditionalMaptoolDir(segmentDir);
}
public static File getSecondarySegmentDir( File segmentDir )
{
return StorageConfigHelper.getSecondarySegmentDir(segmentDir);
}
public static boolean hasDirectoryAnyDatafiles( File segmentDir )
{
if ( hasAnyDatafiles( segmentDir ) )
{
return true;
}
// check secondary, too
File secondary = StorageConfigHelper.getSecondarySegmentDir( segmentDir );
if ( secondary != null )
{
return hasAnyDatafiles( secondary );
}
return false;
}
public static File getSecondarySegmentDir(File segmentDir) {
return StorageConfigHelper.getSecondarySegmentDir(segmentDir);
}
private static boolean hasAnyDatafiles( File dir )
{
String[] fileNames = dir.list();
for( String fileName : fileNames )
{
if ( fileName.endsWith( ".rd5" ) ) return true;
}
return false;
public static boolean hasDirectoryAnyDatafiles(File segmentDir) {
if (hasAnyDatafiles(segmentDir)) {
return true;
}
// check secondary, too
File secondary = StorageConfigHelper.getSecondarySegmentDir(segmentDir);
if (secondary != null) {
return hasAnyDatafiles(secondary);
}
return false;
}
private static boolean hasAnyDatafiles(File dir) {
String[] fileNames = dir.list();
for (String fileName : fileNames) {
if (fileName.endsWith(".rd5")) return true;
}
return false;
}
}

View file

@ -1,5 +1,4 @@
package btools.router;
public class RoutingIslandException extends RuntimeException
{
public class RoutingIslandException extends RuntimeException {
}

View file

@ -8,83 +8,80 @@ package btools.router;
import btools.mapaccess.OsmNode;
public final class SearchBoundary
{
public final class SearchBoundary {
private int minlon0;
private int minlat0;
private int maxlon0;
private int maxlat0;
private int minlon0;
private int minlat0;
private int maxlon0;
private int maxlat0;
private int minlon;
private int minlat;
private int maxlon;
private int maxlat;
private int radius;
private OsmNode p;
private int minlon;
private int minlat;
private int maxlon;
private int maxlat;
private int radius;
private OsmNode p;
int direction;
int direction;
/**
* @param radius Search radius in meters.
*/
public SearchBoundary( OsmNode n, int radius, int direction )
{
this.radius = radius;
this.direction = direction;
/**
* @param radius Search radius in meters.
*/
public SearchBoundary(OsmNode n, int radius, int direction) {
this.radius = radius;
this.direction = direction;
p = new OsmNode( n.ilon, n.ilat );
p = new OsmNode(n.ilon, n.ilat);
int lon = (n.ilon / 5000000 ) * 5000000;
int lat = (n.ilat / 5000000 ) * 5000000;
int lon = (n.ilon / 5000000) * 5000000;
int lat = (n.ilat / 5000000) * 5000000;
minlon0 = lon - 5000000;
minlat0 = lat - 5000000;
maxlon0 = lon + 10000000;
maxlat0 = lat + 10000000;
minlon0 = lon - 5000000;
minlat0 = lat - 5000000;
maxlon0 = lon + 10000000;
maxlat0 = lat + 10000000;
minlon = lon - 1000000;
minlat = lat - 1000000;
maxlon = lon + 6000000;
maxlat = lat + 6000000;
minlon = lon - 1000000;
minlat = lat - 1000000;
maxlon = lon + 6000000;
maxlat = lat + 6000000;
}
public static String getFileName(OsmNode n) {
int lon = (n.ilon / 5000000) * 5000000;
int lat = (n.ilat / 5000000) * 5000000;
int dlon = lon / 1000000 - 180;
int dlat = lat / 1000000 - 90;
String slon = dlon < 0 ? "W" + (-dlon) : "E" + dlon;
String slat = dlat < 0 ? "S" + (-dlat) : "N" + dlat;
return slon + "_" + slat + ".trf";
}
public boolean isInBoundary(OsmNode n, int cost) {
if (radius > 0) {
return n.calcDistance(p) < radius;
}
public static String getFileName( OsmNode n )
{
int lon = (n.ilon / 5000000 ) * 5000000;
int lat = (n.ilat / 5000000 ) * 5000000;
int dlon = lon / 1000000 -180;
int dlat = lat / 1000000 - 90;
String slon = dlon < 0 ? "W" + (-dlon) : "E" + dlon;
String slat = dlat < 0 ? "S" + (-dlat) : "N" + dlat;
return slon + "_" + slat + ".trf";
if (cost == 0) {
return n.ilon > minlon0 && n.ilon < maxlon0 && n.ilat > minlat0 && n.ilat < maxlat0;
}
return n.ilon > minlon && n.ilon < maxlon && n.ilat > minlat && n.ilat < maxlat;
}
public boolean isInBoundary( OsmNode n, int cost )
{
if ( radius > 0 )
{
return n.calcDistance( p ) < radius;
}
if ( cost == 0 )
{
return n.ilon > minlon0 && n.ilon < maxlon0 && n.ilat > minlat0 && n.ilat < maxlat0;
}
return n.ilon > minlon && n.ilon < maxlon && n.ilat > minlat && n.ilat < maxlat;
}
public int getBoundaryDistance( OsmNode n )
{
switch( direction )
{
case 0: return n.calcDistance( new OsmNode( n.ilon, minlat ) );
case 1: return n.calcDistance( new OsmNode( minlon, n.ilat ) );
case 2: return n.calcDistance( new OsmNode( n.ilon, maxlat ) );
case 3: return n.calcDistance( new OsmNode( maxlon, n.ilat ) );
default: throw new IllegalArgumentException( "undefined direction: "+ direction );
}
public int getBoundaryDistance(OsmNode n) {
switch (direction) {
case 0:
return n.calcDistance(new OsmNode(n.ilon, minlat));
case 1:
return n.calcDistance(new OsmNode(minlon, n.ilat));
case 2:
return n.calcDistance(new OsmNode(n.ilon, maxlat));
case 3:
return n.calcDistance(new OsmNode(maxlon, n.ilat));
default:
throw new IllegalArgumentException("undefined direction: " + direction);
}
}
}

View file

@ -12,15 +12,12 @@ import btools.expressions.BExpressionContextNode;
import btools.expressions.BExpressionContextWay;
final class StdModel extends OsmPathModel
{
public OsmPrePath createPrePath()
{
final class StdModel extends OsmPathModel {
public OsmPrePath createPrePath() {
return null;
}
public OsmPath createPath()
{
public OsmPath createPath() {
return new StdPath();
}
@ -29,11 +26,10 @@ final class StdModel extends OsmPathModel
@Override
public void init( BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String,String> keyValues )
{
public void init(BExpressionContextWay expctxWay, BExpressionContextNode expctxNode, Map<String, String> keyValues) {
ctxWay = expctxWay;
ctxNode = expctxNode;
BExpressionContext expctxGlobal = expctxWay; // just one of them...
}

View file

@ -7,8 +7,7 @@ package btools.router;
import btools.util.FastMath;
final class StdPath extends OsmPath
{
final class StdPath extends OsmPath {
/**
* The elevation-hysteresis-buffer (0-10 m)
*/
@ -23,9 +22,8 @@ final class StdPath extends OsmPath
private static final double GRAVITY = 9.81; // in meters per second^(-2)
@Override
public void init( OsmPath orig )
{
StdPath origin = (StdPath)orig;
public void init(OsmPath orig) {
StdPath origin = (StdPath) orig;
this.ehbd = origin.ehbd;
this.ehbu = origin.ehbu;
this.totalTime = origin.totalTime;
@ -34,8 +32,7 @@ final class StdPath extends OsmPath
}
@Override
protected void resetState()
{
protected void resetState() {
ehbd = 0;
ehbu = 0;
totalTime = 0.f;
@ -44,8 +41,7 @@ final class StdPath extends OsmPath
}
@Override
protected double processWaySection( RoutingContext rc, double distance, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier )
{
protected double processWaySection(RoutingContext rc, double distance, double delta_h, double elevation, double angle, double cosangle, boolean isStartpoint, int nsection, int lastpriorityclassifier) {
// calculate the costfactor inputs
float turncostbase = rc.expctxWay.getTurncost();
float cfup = rc.expctxWay.getUphillCostfactor();
@ -54,14 +50,13 @@ final class StdPath extends OsmPath
cfup = cfup == 0.f ? cf : cfup;
cfdown = cfdown == 0.f ? cf : cfdown;
int dist = (int)distance; // legacy arithmetics needs int
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 )
{
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;
message.turnangle = (float) angle;
}
double sectionCost = turncost;
@ -70,81 +65,66 @@ final class StdPath extends OsmPath
// 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);
int delta_h_micros = (int) (1000000. * delta_h);
ehbd += -delta_h_micros - dist * rc.downhillcutoff;
ehbu += delta_h_micros - dist * rc.uphillcutoff;
ehbu += delta_h_micros - dist * rc.uphillcutoff;
float downweight = 0.f;
if ( ehbd > rc.elevationpenaltybuffer )
{
if (ehbd > rc.elevationpenaltybuffer) {
downweight = 1.f;
int excess = ehbd - rc.elevationpenaltybuffer;
int reduce = dist * rc.elevationbufferreduce;
if ( reduce > excess )
{
downweight = ((float)excess)/reduce;
if (reduce > excess) {
downweight = ((float) excess) / reduce;
reduce = excess;
}
excess = ehbd - rc.elevationmaxbuffer;
if ( reduce < excess )
{
if (reduce < excess) {
reduce = excess;
}
ehbd -= reduce;
if ( rc.downhillcostdiv > 0 )
{
int elevationCost = reduce/rc.downhillcostdiv;
if (rc.downhillcostdiv > 0) {
int elevationCost = reduce / rc.downhillcostdiv;
sectionCost += elevationCost;
if ( message != null )
{
if (message != null) {
message.linkelevationcost += elevationCost;
}
}
}
else if ( ehbd < 0 )
{
} else if (ehbd < 0) {
ehbd = 0;
}
float upweight = 0.f;
if ( ehbu > rc.elevationpenaltybuffer )
{
if (ehbu > rc.elevationpenaltybuffer) {
upweight = 1.f;
int excess = ehbu - rc.elevationpenaltybuffer;
int reduce = dist * rc.elevationbufferreduce;
if ( reduce > excess )
{
upweight = ((float)excess)/reduce;
if (reduce > excess) {
upweight = ((float) excess) / reduce;
reduce = excess;
}
excess = ehbu - rc.elevationmaxbuffer;
if ( reduce < excess )
{
if (reduce < excess) {
reduce = excess;
}
ehbu -= reduce;
if ( rc.uphillcostdiv > 0 )
{
int elevationCost = reduce/rc.uphillcostdiv;
if (rc.uphillcostdiv > 0) {
int elevationCost = reduce / rc.uphillcostdiv;
sectionCost += elevationCost;
if ( message != null )
{
if (message != null) {
message.linkelevationcost += elevationCost;
}
}
}
else if ( ehbu < 0 )
{
} else if (ehbu < 0) {
ehbu = 0;
}
// get the effective costfactor (slope dependent)
float costfactor = cfup*upweight + cf*(1.f - upweight - downweight) + cfdown*downweight;
float costfactor = cfup * upweight + cf * (1.f - upweight - downweight) + cfdown * downweight;
if ( message != null )
{
if (message != null) {
message.costfactor = costfactor;
}
@ -154,22 +134,18 @@ final class StdPath extends OsmPath
}
@Override
protected double processTargetNode( RoutingContext rc )
{
protected double processTargetNode(RoutingContext rc) {
// finally add node-costs for target node
if ( targetNode.nodeDescription != null )
{
if (targetNode.nodeDescription != null) {
boolean nodeAccessGranted = rc.expctxWay.getNodeAccessGranted() != 0.;
rc.expctxNode.evaluate( nodeAccessGranted , targetNode.nodeDescription );
rc.expctxNode.evaluate(nodeAccessGranted, targetNode.nodeDescription);
float initialcost = rc.expctxNode.getInitialcost();
if ( initialcost >= 1000000. )
{
if (initialcost >= 1000000.) {
return -1.;
}
if ( message != null )
{
message.linknodecost += (int)initialcost;
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription( nodeAccessGranted, targetNode.nodeDescription );
if (message != null) {
message.linknodecost += (int) initialcost;
message.nodeKeyValues = rc.expctxNode.getKeyValueDescription(nodeAccessGranted, targetNode.nodeDescription);
}
return initialcost;
}
@ -177,118 +153,96 @@ final class StdPath extends OsmPath
}
@Override
public int elevationCorrection( RoutingContext rc )
{
return ( rc.downhillcostdiv > 0 ? ehbd/rc.downhillcostdiv : 0 )
+ ( rc.uphillcostdiv > 0 ? ehbu/rc.uphillcostdiv : 0 );
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;
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;
}
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;
return cost > c;
}
private double calcIncline( double dist )
{
private double calcIncline(double dist) {
double min_delta = 3.;
double shift;
if ( elevation_buffer > min_delta )
{
if (elevation_buffer > min_delta) {
shift = -min_delta;
}
else if ( elevation_buffer < min_delta )
{
} else if (elevation_buffer < min_delta) {
shift = -min_delta;
}
else
{
} else {
return 0.;
}
double decayFactor = FastMath.exp( - dist / 100. );
float new_elevation_buffer = (float)( (elevation_buffer+shift) * decayFactor - shift);
double incline = ( elevation_buffer - new_elevation_buffer ) / dist;
double decayFactor = FastMath.exp(-dist / 100.);
float new_elevation_buffer = (float) ((elevation_buffer + shift) * decayFactor - shift);
double incline = (elevation_buffer - new_elevation_buffer) / dist;
elevation_buffer = new_elevation_buffer;
return incline;
}
@Override
protected void computeKinematic( RoutingContext rc, double dist, double delta_h, boolean detailMode )
{
if ( !detailMode )
{
protected void computeKinematic(RoutingContext rc, double dist, double delta_h, boolean detailMode) {
if (!detailMode) {
return;
}
// compute incline
elevation_buffer += delta_h;
double incline = calcIncline( dist );
double incline = calcIncline(dist);
double wayMaxspeed;
wayMaxspeed = rc.expctxWay.getMaxspeed() / 3.6f;
if (wayMaxspeed == 0)
{
wayMaxspeed = rc.maxSpeed;
if (wayMaxspeed == 0) {
wayMaxspeed = rc.maxSpeed;
}
wayMaxspeed = Math.min(wayMaxspeed,rc.maxSpeed);
wayMaxspeed = Math.min(wayMaxspeed, rc.maxSpeed);
double speed; // Travel speed
double f_roll = rc.totalMass * GRAVITY * ( rc.defaultC_r + incline );
if (rc.footMode || rc.expctxWay.getCostfactor() > 4.9 )
{
double f_roll = rc.totalMass * GRAVITY * (rc.defaultC_r + incline);
if (rc.footMode || rc.expctxWay.getCostfactor() > 4.9) {
// Use Tobler's hiking function for walking sections
speed = rc.maxSpeed * 3.6;
speed = (speed * FastMath.exp(-3.5 * Math.abs( incline + 0.05))) / 3.6;
}
else if (rc.bikeMode)
{
speed = solveCubic( rc.S_C_x, f_roll, rc.bikerPower );
speed = (speed * FastMath.exp(-3.5 * Math.abs(incline + 0.05))) / 3.6;
} else if (rc.bikeMode) {
speed = solveCubic(rc.S_C_x, f_roll, rc.bikerPower);
speed = Math.min(speed, wayMaxspeed);
}
else // all other
} else // all other
{
speed = wayMaxspeed;
}
float dt = (float) ( dist / speed );
float dt = (float) (dist / speed);
totalTime += dt;
// Calc energy assuming biking (no good model yet for hiking)
// (Count only positive, negative would mean breaking to enforce maxspeed)
double energy = dist*(rc.S_C_x*speed*speed + f_roll);
if ( energy > 0. )
{
double energy = dist * (rc.S_C_x * speed * speed + f_roll);
if (energy > 0.) {
totalEnergy += energy;
}
}
private static double solveCubic( double a, double c, double d )
{
private static double solveCubic(double a, double c, double d) {
// Solves a * v^3 + c * v = d with a Newton method
// to get the speed v for the section.
double v = 8.;
boolean findingStartvalue = true;
for ( int i = 0; i < 10; i++ )
{
double y = ( a * v * v + c ) * v - d;
if ( y < .1 )
{
if ( findingStartvalue )
{
for (int i = 0; i < 10; i++) {
double y = (a * v * v + c) * v - d;
if (y < .1) {
if (findingStartvalue) {
v *= 2.;
continue;
}
@ -302,14 +256,12 @@ final class StdPath extends OsmPath
}
@Override
public double getTotalTime()
{
public double getTotalTime() {
return totalTime;
}
@Override
public double getTotalEnergy()
{
public double getTotalEnergy() {
return totalEnergy;
}
}

View file

@ -2,65 +2,58 @@ package btools.router;
import java.util.Map;
public class SuspectInfo
{
public static final int TRIGGER_DEAD_END = 1;
public static final int TRIGGER_DEAD_START = 2;
public static final int TRIGGER_NODE_BLOCK = 4;
public static final int TRIGGER_BAD_ACCESS = 8;
public static final int TRIGGER_UNK_ACCESS = 16;
public static final int TRIGGER_SHARP_EXIT = 32;
public class SuspectInfo {
public static final int TRIGGER_DEAD_END = 1;
public static final int TRIGGER_DEAD_START = 2;
public static final int TRIGGER_NODE_BLOCK = 4;
public static final int TRIGGER_BAD_ACCESS = 8;
public static final int TRIGGER_UNK_ACCESS = 16;
public static final int TRIGGER_SHARP_EXIT = 32;
public static final int TRIGGER_SHARP_ENTRY = 64;
public static final int TRIGGER_SHARP_LINK = 128;
public static final int TRIGGER_BAD_TR = 256;
public static final int TRIGGER_SHARP_LINK = 128;
public static final int TRIGGER_BAD_TR = 256;
public int prio;
public int triggers;
public static void addSuspect( Map<Long,SuspectInfo> map, long id, int prio, int trigger )
{
Long iD = Long.valueOf( id );
SuspectInfo info = map.get( iD );
if ( info == null )
{
public static void addSuspect(Map<Long, SuspectInfo> map, long id, int prio, int trigger) {
Long iD = Long.valueOf(id);
SuspectInfo info = map.get(iD);
if (info == null) {
info = new SuspectInfo();
map.put( iD, info );
map.put(iD, info);
}
info.prio = Math.max( info.prio, prio );
info.prio = Math.max(info.prio, prio);
info.triggers |= trigger;
}
public static SuspectInfo addTrigger( SuspectInfo old, int prio, int trigger )
{
if ( old == null )
{
public static SuspectInfo addTrigger(SuspectInfo old, int prio, int trigger) {
if (old == null) {
old = new SuspectInfo();
}
old.prio = Math.max( old.prio, prio );
old.prio = Math.max(old.prio, prio);
old.triggers |= trigger;
return old;
}
public static String getTriggerText( int triggers )
{
public static String getTriggerText(int triggers) {
StringBuilder sb = new StringBuilder();
addText( sb, "dead-end" , triggers, TRIGGER_DEAD_END );
addText( sb, "dead-start" , triggers, TRIGGER_DEAD_START );
addText( sb, "node-block" , triggers, TRIGGER_NODE_BLOCK );
addText( sb, "bad-access" , triggers, TRIGGER_BAD_ACCESS );
addText( sb, "unkown-access", triggers, TRIGGER_UNK_ACCESS );
addText( sb, "sharp-exit" , triggers, TRIGGER_SHARP_EXIT );
addText( sb, "sharp-entry" , triggers, TRIGGER_SHARP_ENTRY );
addText( sb, "sharp-link" , triggers, TRIGGER_SHARP_LINK );
addText( sb, "bad-tr" , triggers, TRIGGER_BAD_TR );
addText(sb, "dead-end", triggers, TRIGGER_DEAD_END);
addText(sb, "dead-start", triggers, TRIGGER_DEAD_START);
addText(sb, "node-block", triggers, TRIGGER_NODE_BLOCK);
addText(sb, "bad-access", triggers, TRIGGER_BAD_ACCESS);
addText(sb, "unkown-access", triggers, TRIGGER_UNK_ACCESS);
addText(sb, "sharp-exit", triggers, TRIGGER_SHARP_EXIT);
addText(sb, "sharp-entry", triggers, TRIGGER_SHARP_ENTRY);
addText(sb, "sharp-link", triggers, TRIGGER_SHARP_LINK);
addText(sb, "bad-tr", triggers, TRIGGER_BAD_TR);
return sb.toString();
}
private static void addText( StringBuilder sb, String text, int mask, int bit )
{
if ( ( bit & mask ) == 0 ) return;
if ( sb.length() > 0 ) sb.append( "," );
sb.append( text );
private static void addText(StringBuilder sb, String text, int mask, int bit) {
if ((bit & mask) == 0) return;
if (sb.length() > 0) sb.append(",");
sb.append(text);
}
}

View file

@ -9,8 +9,7 @@ package btools.router;
import java.util.ArrayList;
import java.util.List;
public class VoiceHint
{
public class VoiceHint {
static final int C = 1; // continue (go straight)
static final int TL = 2; // turn left
static final int TSLL = 3; // turn slightly left
@ -36,8 +35,7 @@ public class VoiceHint
double distanceToNext;
int indexInTrack;
public float getTime()
{
public float getTime() {
return oldWay == null ? 0.f : oldWay.time;
}
@ -47,269 +45,281 @@ public class VoiceHint
int roundaboutExit;
boolean isRoundabout()
{
boolean isRoundabout() {
return roundaboutExit != 0;
}
public void addBadWay( MessageData badWay )
{
if ( badWay == null )
{
public void addBadWay(MessageData badWay) {
if (badWay == null) {
return;
}
if ( badWays == null )
{
if (badWays == null) {
badWays = new ArrayList<MessageData>();
}
badWays.add( badWay );
badWays.add(badWay);
}
public int getCommand()
{
public int getCommand() {
return cmd;
}
public int getExitNumber()
{
public int getExitNumber() {
return roundaboutExit;
}
public String getCommandString()
{
switch ( cmd )
{
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 );
}
}
public String getSymbolString()
{
switch ( cmd )
{
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 );
public String getCommandString() {
switch (cmd) {
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);
}
}
public String getMessageString()
{
switch ( cmd )
{
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 );
public String getSymbolString() {
switch (cmd) {
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);
}
}
public int getLocusAction()
{
switch ( cmd )
{
case TU : return 13;
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 14;
case RNDB : return 26 + roundaboutExit;
case RNLB : return 26 - roundaboutExit;
default : throw new IllegalArgumentException( "unknown command: " + cmd );
public String getMessageString() {
switch (cmd) {
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);
}
}
public int getOruxAction()
{
switch ( cmd )
{
case TU : return 1003;
case TSHL : return 1019;
case TL : return 1000;
case TSLL : return 1017;
case KL : return 1015; // ?
case C : return 1002;
case KR : return 1014; // ?
case TSLR : return 1016;
case TR : return 1001;
case TSHR : return 1018;
case TRU : return 1003;
case RNDB : return 1008 + roundaboutExit;
case RNLB : return 1008 + roundaboutExit;
default : throw new IllegalArgumentException( "unknown command: " + cmd );
}
public int getLocusAction() {
switch (cmd) {
case TU:
return 13;
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 14;
case RNDB:
return 26 + roundaboutExit;
case RNLB:
return 26 - roundaboutExit;
default:
throw new IllegalArgumentException("unknown command: " + cmd);
}
}
public void calcCommand()
{
public int getOruxAction() {
switch (cmd) {
case TU:
return 1003;
case TSHL:
return 1019;
case TL:
return 1000;
case TSLL:
return 1017;
case KL:
return 1015; // ?
case C:
return 1002;
case KR:
return 1014; // ?
case TSLR:
return 1016;
case TR:
return 1001;
case TSHR:
return 1018;
case TRU:
return 1003;
case RNDB:
return 1008 + roundaboutExit;
case RNLB:
return 1008 + roundaboutExit;
default:
throw new IllegalArgumentException("unknown command: " + cmd);
}
}
public void calcCommand() {
float lowerBadWayAngle = -181;
float higherBadWayAngle = 181;
if ( badWays != null )
{
for ( MessageData badWay : badWays )
{
if ( badWay.isBadOneway() )
{
if (badWays != null) {
for (MessageData badWay : badWays) {
if (badWay.isBadOneway()) {
continue;
}
if ( lowerBadWayAngle < badWay.turnangle && badWay.turnangle < goodWay.turnangle )
{
if (lowerBadWayAngle < badWay.turnangle && badWay.turnangle < goodWay.turnangle) {
lowerBadWayAngle = badWay.turnangle;
}
if ( higherBadWayAngle > badWay.turnangle && badWay.turnangle > goodWay.turnangle )
{
if (higherBadWayAngle > badWay.turnangle && badWay.turnangle > goodWay.turnangle) {
higherBadWayAngle = badWay.turnangle;
}
}
}
float cmdAngle= angle;
float cmdAngle = angle;
// fall back to local angle if otherwise inconsistent
if ( lowerBadWayAngle > angle || higherBadWayAngle < angle )
{
if (lowerBadWayAngle > angle || higherBadWayAngle < angle) {
cmdAngle = goodWay.turnangle;
}
if (roundaboutExit > 0)
{
if (roundaboutExit > 0) {
cmd = RNDB;
}
else if (roundaboutExit < 0)
{
} else if (roundaboutExit < 0) {
cmd = RNLB;
}
else if ( cmdAngle < -159. )
{
} else if (cmdAngle < -159.) {
cmd = TU;
}
else if ( cmdAngle < -135. )
{
} else if (cmdAngle < -135.) {
cmd = TSHL;
}
else if ( cmdAngle < -45. )
{
} else if (cmdAngle < -45.) {
// a TL can be pushed in either direction by a close-by alternative
if ( higherBadWayAngle > -90. && higherBadWayAngle < -15. && lowerBadWayAngle < -180. )
{
if (higherBadWayAngle > -90. && higherBadWayAngle < -15. && lowerBadWayAngle < -180.) {
cmd = TSHL;
}
else if ( lowerBadWayAngle > -180. && lowerBadWayAngle < -90. && higherBadWayAngle > 0. )
{
} else if (lowerBadWayAngle > -180. && lowerBadWayAngle < -90. && higherBadWayAngle > 0.) {
cmd = TSLL;
}
else
{
} else {
cmd = TL;
}
}
else if ( cmdAngle < -21. )
{
if ( cmd != KR ) // don't overwrite KR with TSLL
} else if (cmdAngle < -21.) {
if (cmd != KR) // don't overwrite KR with TSLL
{
cmd = TSLL;
}
}
else if ( cmdAngle < 21. )
{
if ( cmd != KR && cmd != KL ) // don't overwrite KL/KR hints!
} else if (cmdAngle < 21.) {
if (cmd != KR && cmd != KL) // don't overwrite KL/KR hints!
{
cmd = C;
}
}
else if ( cmdAngle < 45. )
{
if ( cmd != KL ) // don't overwrite KL with TSLR
} else if (cmdAngle < 45.) {
if (cmd != KL) // don't overwrite KL with TSLR
{
cmd = TSLR;
}
}
else if ( cmdAngle < 135. )
{
} else if (cmdAngle < 135.) {
// a TR can be pushed in either direction by a close-by alternative
if ( higherBadWayAngle > 90. && higherBadWayAngle < 180. && lowerBadWayAngle < 0. )
{
if (higherBadWayAngle > 90. && higherBadWayAngle < 180. && lowerBadWayAngle < 0.) {
cmd = TSLR;
}
else if ( lowerBadWayAngle > 15. && lowerBadWayAngle < 90. && higherBadWayAngle > 180. )
{
} else if (lowerBadWayAngle > 15. && lowerBadWayAngle < 90. && higherBadWayAngle > 180.) {
cmd = TSHR;
}
else
{
} else {
cmd = TR;
}
}
else if ( cmdAngle < 159. )
{
} else if (cmdAngle < 159.) {
cmd = TSHR;
}
else
{
} else {
cmd = TRU;
}
}
public String formatGeometry()
{
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 );
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) );
private void appendTurnGeometry(StringBuilder sb, MessageData msg) {
sb.append("(").append((int) (msg.turnangle + 0.5)).append(")").append((int) (msg.priorityclassifier));
}
}

View file

@ -9,30 +9,24 @@ package btools.router;
import java.util.ArrayList;
import java.util.List;
public class VoiceHintList
{
public class VoiceHintList {
private String transportMode;
int turnInstructionMode;
ArrayList<VoiceHint> list = new ArrayList<VoiceHint>();
public void setTransportMode( boolean isCar, boolean isBike )
{
transportMode = isCar ? "car" : ( isBike ? "bike" : "foot" );
public void setTransportMode(boolean isCar, boolean isBike) {
transportMode = isCar ? "car" : (isBike ? "bike" : "foot");
}
public String getTransportMode()
{
public String getTransportMode() {
return transportMode;
}
public int getLocusRouteType()
{
if ( "car".equals( transportMode ) )
{
public int getLocusRouteType() {
if ("car".equals(transportMode)) {
return 0;
}
if ( "bike".equals( transportMode ) )
{
if ("bike".equals(transportMode)) {
return 5;
}
return 3; // foot

View file

@ -8,26 +8,21 @@ package btools.router;
import java.util.ArrayList;
import java.util.List;
public final class VoiceHintProcessor
{
public final class VoiceHintProcessor {
private double catchingRange; // range to catch angles and merge turns
private boolean explicitRoundabouts;
public VoiceHintProcessor( double catchingRange, boolean explicitRoundabouts )
{
public VoiceHintProcessor(double catchingRange, boolean explicitRoundabouts) {
this.catchingRange = catchingRange;
this.explicitRoundabouts = explicitRoundabouts;
}
private float sumNonConsumedWithinCatchingRange( List<VoiceHint> inputs, int offset )
{
private float sumNonConsumedWithinCatchingRange(List<VoiceHint> inputs, int offset) {
double distance = 0.;
float angle = 0.f;
while( offset >= 0 && distance < catchingRange )
{
VoiceHint input = inputs.get( offset-- );
if ( input.turnAngleConsumed )
{
while (offset >= 0 && distance < catchingRange) {
VoiceHint input = inputs.get(offset--);
if (input.turnAngleConsumed) {
break;
}
angle += input.goodWay.turnangle;
@ -44,10 +39,10 @@ public final class VoiceHintProcessor
* order (from target to start), but output is
* returned in travel-direction and only for
* those nodes that trigger a voice hint.
*
* <p>
* Input objects are expected for every segment
* of the track, also for those without a junction
*
* <p>
* VoiceHint objects in the output list are enriched
* by the voice-command, the total angle and the distance
* to the next hint
@ -55,54 +50,46 @@ public final class VoiceHintProcessor
* @param inputs tracknodes, un reverse order
* @return voice hints, in forward order
*/
public List<VoiceHint> process( List<VoiceHint> inputs )
{
public List<VoiceHint> process(List<VoiceHint> inputs) {
List<VoiceHint> results = new ArrayList<VoiceHint>();
double distance = 0.;
float roundAboutTurnAngle = 0.f; // sums up angles in roundabout
int roundaboutExit = 0;
for ( int hintIdx = 0; hintIdx < inputs.size(); hintIdx++ )
{
VoiceHint input = inputs.get( hintIdx );
for (int hintIdx = 0; hintIdx < inputs.size(); hintIdx++) {
VoiceHint input = inputs.get(hintIdx);
float turnAngle = input.goodWay.turnangle;
distance += input.goodWay.linkdist;
int currentPrio = input.goodWay.getPrio();
int oldPrio = input.oldWay.getPrio();
int minPrio = Math.min( oldPrio, currentPrio );
int minPrio = Math.min(oldPrio, currentPrio);
boolean isLink2Highway = input.oldWay.isLinktType() && !input.goodWay.isLinktType();
if ( input.oldWay.isRoundabout() )
{
roundAboutTurnAngle += sumNonConsumedWithinCatchingRange( inputs, hintIdx );
if (input.oldWay.isRoundabout()) {
roundAboutTurnAngle += sumNonConsumedWithinCatchingRange(inputs, hintIdx);
boolean isExit = roundaboutExit == 0; // exit point is always exit
if ( input.badWays != null )
{
for ( MessageData badWay : input.badWays )
{
if ( !badWay.isBadOneway() && badWay.isGoodForCars() && Math.abs( badWay.turnangle ) < 120. )
{
if (input.badWays != null) {
for (MessageData badWay : input.badWays) {
if (!badWay.isBadOneway() && badWay.isGoodForCars() && Math.abs(badWay.turnangle) < 120.) {
isExit = true;
}
}
}
if ( isExit )
{
if (isExit) {
roundaboutExit++;
}
continue;
}
if ( roundaboutExit > 0 )
{
roundAboutTurnAngle += sumNonConsumedWithinCatchingRange( inputs, hintIdx );
if (roundaboutExit > 0) {
roundAboutTurnAngle += sumNonConsumedWithinCatchingRange(inputs, hintIdx);
input.angle = roundAboutTurnAngle;
input.distanceToNext = distance;
input.roundaboutExit = turnAngle < 0 ? -roundaboutExit : roundaboutExit;
input.roundaboutExit = turnAngle < 0 ? -roundaboutExit : roundaboutExit;
distance = 0.;
results.add( input );
results.add(input);
roundAboutTurnAngle = 0.f;
roundaboutExit = 0;
continue;
@ -114,91 +101,77 @@ public final class VoiceHintProcessor
float minAngle = 180.f;
float minAbsAngeRaw = 180.f;
if ( input.badWays != null )
{
for ( MessageData badWay : input.badWays )
{
if (input.badWays != null) {
for (MessageData badWay : input.badWays) {
int badPrio = badWay.getPrio();
float badTurn = badWay.turnangle;
boolean isHighway2Link = !input.oldWay.isLinktType() && badWay.isLinktType();
if ( badPrio > maxPrioAll && !isHighway2Link )
{
if (badPrio > maxPrioAll && !isHighway2Link) {
maxPrioAll = badPrio;
}
if ( badWay.costfactor < 20.f && Math.abs( badTurn ) < minAbsAngeRaw )
{
minAbsAngeRaw = Math.abs( badTurn );
if (badWay.costfactor < 20.f && Math.abs(badTurn) < minAbsAngeRaw) {
minAbsAngeRaw = Math.abs(badTurn);
}
if ( badPrio < minPrio )
{
if (badPrio < minPrio) {
continue; // ignore low prio ways
}
if ( badWay.isBadOneway() )
{
if (badWay.isBadOneway()) {
continue; // ignore wrong oneways
}
if ( Math.abs( badTurn ) - Math.abs( turnAngle ) > 80.f )
{
if (Math.abs(badTurn) - Math.abs(turnAngle) > 80.f) {
continue; // ways from the back should not trigger a slight turn
}
if ( badPrio > maxPrioCandidates )
{
if (badPrio > maxPrioCandidates) {
maxPrioCandidates = badPrio;
}
if ( badTurn > maxAngle )
{
if (badTurn > maxAngle) {
maxAngle = badTurn;
}
if ( badTurn < minAngle )
{
if (badTurn < minAngle) {
minAngle = badTurn;
}
}
}
boolean hasSomethingMoreStraight = Math.abs( turnAngle ) - minAbsAngeRaw > 20.;
boolean hasSomethingMoreStraight = Math.abs(turnAngle) - minAbsAngeRaw > 20.;
// 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 = hasSomethingMoreStraight || ( maxPrioAll > minPrio && !isLink2Highway ) || ( maxPrioCandidates > currentPrio );
boolean unconditionalTrigger = hasSomethingMoreStraight || (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 )
{
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 ) )
{
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 ) )
{
if (minAngle > turnAngle && minAngle < turnAngle + 45.f - (turnAngle < 0.f ? turnAngle : 0.f)) {
input.cmd = VoiceHint.KL;
}
input.angle = sumNonConsumedWithinCatchingRange( inputs, hintIdx );
input.angle = sumNonConsumedWithinCatchingRange(inputs, hintIdx);
input.distanceToNext = distance;
distance = 0.;
results.add( input );
results.add(input);
}
if ( results.size() > 0 && distance < catchingRange )
{
results.get( results.size()-1 ).angle += sumNonConsumedWithinCatchingRange( inputs, hintIdx );
if (results.size() > 0 && distance < catchingRange) {
results.get(results.size() - 1).angle += sumNonConsumedWithinCatchingRange(inputs, hintIdx);
}
}
@ -207,25 +180,21 @@ public final class VoiceHintProcessor
List<VoiceHint> results2 = new ArrayList<VoiceHint>();
int i = results.size();
while( i > 0 )
{
while (i > 0) {
VoiceHint hint = results.get(--i);
if ( hint.cmd == 0 )
{
if (hint.cmd == 0) {
hint.calcCommand();
}
if ( ! ( hint.needsRealTurn && hint.cmd == VoiceHint.C ) )
{
if (!(hint.needsRealTurn && hint.cmd == VoiceHint.C)) {
double dist = hint.distanceToNext;
// sum up other hints within the catching range (e.g. 40m)
while( dist < catchingRange && i > 0 )
{
VoiceHint h2 = results.get(i-1);
while (dist < catchingRange && i > 0) {
VoiceHint h2 = results.get(i - 1);
dist = h2.distanceToNext;
hint.distanceToNext+= dist;
hint.distanceToNext += dist;
hint.angle += h2.angle;
i--;
if ( h2.isRoundabout() ) // if we hit a roundabout, use that as the trigger
if (h2.isRoundabout()) // if we hit a roundabout, use that as the trigger
{
h2.angle = hint.angle;
hint = h2;
@ -233,12 +202,11 @@ public final class VoiceHintProcessor
}
}
if ( !explicitRoundabouts )
{
if (!explicitRoundabouts) {
hint.roundaboutExit = 0; // use an angular hint instead
}
hint.calcCommand();
results2.add( hint );
results2.add(hint);
}
}
return results2;

View file

@ -12,11 +12,11 @@ import btools.util.CheapRuler;
public class OsmNodeNamedTest {
static int toOsmLon(double lon) {
return (int)( ( lon + 180. ) / CheapRuler.ILATLNG_TO_LATLNG + 0.5);
return (int) ((lon + 180.) / CheapRuler.ILATLNG_TO_LATLNG + 0.5);
}
static int toOsmLat(double lat) {
return (int)( ( lat + 90. ) / CheapRuler.ILATLNG_TO_LATLNG + 0.5);
return (int) ((lat + 90.) / CheapRuler.ILATLNG_TO_LATLNG + 0.5);
}
@Test

View file

@ -1,6 +1,6 @@
/**********************************************************************************************
Copyright (C) 2018 Norbert Truchsess norbert.truchsess@t-online.de
**********************************************************************************************/
Copyright (C) 2018 Norbert Truchsess norbert.truchsess@t-online.de
**********************************************************************************************/
package btools.router;
import static org.junit.Assert.assertEquals;
@ -22,26 +22,26 @@ public class OsmNogoPolygonTest {
static OsmNogoPolygon polygon;
static OsmNogoPolygon polyline;
static final double[] lons = { 1.0, 1.0, 0.5, 0.5, 1.0, 1.0, -1.1, -1.0 };
static final double[] lats = { -1.0, -0.1, -0.1, 0.1, 0.1, 1.0, 1.1, -1.0 };
static final double[] lons = {1.0, 1.0, 0.5, 0.5, 1.0, 1.0, -1.1, -1.0};
static final double[] lats = {-1.0, -0.1, -0.1, 0.1, 0.1, 1.0, 1.1, -1.0};
static int toOsmLon(double lon, int offset_x) {
return (int)( ( lon + 180. ) *1000000. + 0.5)+offset_x; // see ServerHandler.readPosition()
return (int) ((lon + 180.) * 1000000. + 0.5) + offset_x; // see ServerHandler.readPosition()
}
static int toOsmLat(double lat, int offset_y) {
return (int)( ( lat + 90. ) *1000000. + 0.5)+offset_y;
return (int) ((lat + 90.) * 1000000. + 0.5) + offset_y;
}
@BeforeClass
public static void setUp() throws Exception {
polygon = new OsmNogoPolygon(true);
for (int i = 0; i<lons.length; i++) {
polygon.addVertex(toOsmLon(lons[i], OFFSET_X),toOsmLat(lats[i], OFFSET_Y));
for (int i = 0; i < lons.length; i++) {
polygon.addVertex(toOsmLon(lons[i], OFFSET_X), toOsmLat(lats[i], OFFSET_Y));
}
polyline = new OsmNogoPolygon(false);
for (int i = 0; i<lons.length; i++) {
polyline.addVertex(toOsmLon(lons[i], OFFSET_X),toOsmLat(lats[i], OFFSET_Y));
for (int i = 0; i < lons.length; i++) {
polyline.addVertex(toOsmLon(lons[i], OFFSET_X), toOsmLat(lats[i], OFFSET_Y));
}
}
@ -51,162 +51,162 @@ public class OsmNogoPolygonTest {
@Test
public void testCalcBoundingCircle() {
double[] lonlat2m = CheapRuler.getLonLatToMeterScales( polygon.ilat );
double[] lonlat2m = CheapRuler.getLonLatToMeterScales(polygon.ilat);
double dlon2m = lonlat2m[0];
double dlat2m = lonlat2m[1];
polygon.calcBoundingCircle();
double r = polygon.radius;
for (int i=0; i<lons.length; i++) {
for (int i = 0; i < lons.length; i++) {
double dpx = (toOsmLon(lons[i], OFFSET_X) - polygon.ilon) * dlon2m;
double dpy = (toOsmLat(lats[i], OFFSET_Y) - polygon.ilat) * dlat2m;
double r1 = Math.sqrt(dpx * dpx + dpy * dpy);
double diff = r-r1;
assertTrue("i: "+i+" r("+r+") >= r1("+r1+")", diff >= 0);
double diff = r - r1;
assertTrue("i: " + i + " r(" + r + ") >= r1(" + r1 + ")", diff >= 0);
}
polyline.calcBoundingCircle();
r = polyline.radius;
for (int i=0; i<lons.length; i++) {
for (int i = 0; i < lons.length; i++) {
double dpx = (toOsmLon(lons[i], OFFSET_X) - polyline.ilon) * dlon2m;
double dpy = (toOsmLat(lats[i], OFFSET_Y) - polyline.ilat) * dlat2m;
double r1 = Math.sqrt(dpx * dpx + dpy * dpy);
double diff = r-r1;
assertTrue("i: "+i+" r("+r+") >= r1("+r1+")", diff >= 0);
double diff = r - r1;
assertTrue("i: " + i + " r(" + r + ") >= r1(" + r1 + ")", diff >= 0);
}
}
@Test
public void testIsWithin() {
double[] plons = { 0.0, 0.5, 1.0, -1.5, -0.5, 1.0, 1.0, 0.5, 0.5, 0.5, };
double[] plats = { 0.0, 1.5, 0.0, 0.5, -1.5, -1.0, -0.1, -0.1, 0.0, 0.1, };
boolean[] within = { true, false, false, false, false, true, true, true, true, true, };
double[] plons = {0.0, 0.5, 1.0, -1.5, -0.5, 1.0, 1.0, 0.5, 0.5, 0.5,};
double[] plats = {0.0, 1.5, 0.0, 0.5, -1.5, -1.0, -0.1, -0.1, 0.0, 0.1,};
boolean[] within = {true, false, false, false, false, true, true, true, true, true,};
for (int i=0; i<plons.length; i++) {
assertEquals("("+plons[i]+","+plats[i]+")",within[i],polygon.isWithin(toOsmLon(plons[i], OFFSET_X), toOsmLat(plats[i], OFFSET_Y)));
for (int i = 0; i < plons.length; i++) {
assertEquals("(" + plons[i] + "," + plats[i] + ")", within[i], polygon.isWithin(toOsmLon(plons[i], OFFSET_X), toOsmLat(plats[i], OFFSET_Y)));
}
}
@Test
public void testIntersectsPolygon() {
double[] p0lons = { 0.0, 1.0, -0.5, 0.5, 0.7, 0.7, 0.7, -1.5, -1.5, 0.0 };
double[] p0lats = { 0.0, 0.0, 0.5, 0.5, 0.5, 0.05, 0.05, -1.5, 0.2, 0.0 };
double[] p1lons = { 0.0, 1.0, 0.5, 1.0, 0.7, 0.7, 0.7, -0.5, -0.2, 0.5 };
double[] p1lats = { 0.0, 0.0, 0.5, 0.5, -0.5, -0.5, -0.05, -0.5, 1.5, -1.5 };
boolean[] within = { false, false, false, true, true, true, false, true, true, true };
double[] p0lons = {0.0, 1.0, -0.5, 0.5, 0.7, 0.7, 0.7, -1.5, -1.5, 0.0};
double[] p0lats = {0.0, 0.0, 0.5, 0.5, 0.5, 0.05, 0.05, -1.5, 0.2, 0.0};
double[] p1lons = {0.0, 1.0, 0.5, 1.0, 0.7, 0.7, 0.7, -0.5, -0.2, 0.5};
double[] p1lats = {0.0, 0.0, 0.5, 0.5, -0.5, -0.5, -0.05, -0.5, 1.5, -1.5};
boolean[] within = {false, false, false, true, true, true, false, true, true, true};
for (int i=0; i<p0lons.length; i++) {
assertEquals("("+p0lons[i]+","+p0lats[i]+")-("+p1lons[i]+","+p1lats[i]+")",within[i],polygon.intersects(toOsmLon(p0lons[i], OFFSET_X), toOsmLat(p0lats[i], OFFSET_Y), toOsmLon(p1lons[i], OFFSET_X), toOsmLat(p1lats[i], OFFSET_Y)));
for (int i = 0; i < p0lons.length; i++) {
assertEquals("(" + p0lons[i] + "," + p0lats[i] + ")-(" + p1lons[i] + "," + p1lats[i] + ")", within[i], polygon.intersects(toOsmLon(p0lons[i], OFFSET_X), toOsmLat(p0lats[i], OFFSET_Y), toOsmLon(p1lons[i], OFFSET_X), toOsmLat(p1lats[i], OFFSET_Y)));
}
}
@Test
public void testIntersectsPolyline() {
double[] p0lons = { 0.0, 1.0, -0.5, 0.5, 0.7, 0.7, 0.7, -1.5, -1.5, 0.0 };
double[] p0lats = { 0.0, 0.0, 0.5, 0.5, 0.5, 0.05, 0.05, -1.5, 0.2, 0.0 };
double[] p1lons = { 0.0, 1.0, 0.5, 1.0, 0.7, 0.7, 0.7, -0.5, -0.2, 0.5 };
double[] p1lats = { 0.0, 0.0, 0.5, 0.5, -0.5, -0.5, -0.05, -0.5, 1.5, -1.5 };
boolean[] within = { false, false, false, true, true, true, false, true, true, false };
double[] p0lons = {0.0, 1.0, -0.5, 0.5, 0.7, 0.7, 0.7, -1.5, -1.5, 0.0};
double[] p0lats = {0.0, 0.0, 0.5, 0.5, 0.5, 0.05, 0.05, -1.5, 0.2, 0.0};
double[] p1lons = {0.0, 1.0, 0.5, 1.0, 0.7, 0.7, 0.7, -0.5, -0.2, 0.5};
double[] p1lats = {0.0, 0.0, 0.5, 0.5, -0.5, -0.5, -0.05, -0.5, 1.5, -1.5};
boolean[] within = {false, false, false, true, true, true, false, true, true, false};
for (int i=0; i<p0lons.length; i++) {
assertEquals("("+p0lons[i]+","+p0lats[i]+")-("+p1lons[i]+","+p1lats[i]+")",within[i],polyline.intersects(toOsmLon(p0lons[i], OFFSET_X), toOsmLat(p0lats[i], OFFSET_Y), toOsmLon(p1lons[i], OFFSET_X), toOsmLat(p1lats[i], OFFSET_Y)));
for (int i = 0; i < p0lons.length; i++) {
assertEquals("(" + p0lons[i] + "," + p0lats[i] + ")-(" + p1lons[i] + "," + p1lats[i] + ")", within[i], polyline.intersects(toOsmLon(p0lons[i], OFFSET_X), toOsmLat(p0lats[i], OFFSET_Y), toOsmLon(p1lons[i], OFFSET_X), toOsmLat(p1lats[i], OFFSET_Y)));
}
}
@Test
public void testBelongsToLine() {
assertTrue(OsmNogoPolygon.isOnLine(10,10, 10,10, 10,20));
assertTrue(OsmNogoPolygon.isOnLine(10,10, 10,10, 20,10));
assertTrue(OsmNogoPolygon.isOnLine(10,10, 20,10, 10,10));
assertTrue(OsmNogoPolygon.isOnLine(10,10, 10,20, 10,10));
assertTrue(OsmNogoPolygon.isOnLine(10,15, 10,10, 10,20));
assertTrue(OsmNogoPolygon.isOnLine(15,10, 10,10, 20,10));
assertTrue(OsmNogoPolygon.isOnLine(10,10, 10,10, 20,30));
assertTrue(OsmNogoPolygon.isOnLine(20,30, 10,10, 20,30));
assertTrue(OsmNogoPolygon.isOnLine(15,20, 10,10, 20,30));
assertFalse(OsmNogoPolygon.isOnLine(11,11, 10,10, 10,20));
assertFalse(OsmNogoPolygon.isOnLine(11,11, 10,10, 20,10));
assertFalse(OsmNogoPolygon.isOnLine(15,21, 10,10, 20,30));
assertFalse(OsmNogoPolygon.isOnLine(15,19, 10,10, 20,30));
assertFalse(OsmNogoPolygon.isOnLine(0,-10, 10,10, 20,30));
assertFalse(OsmNogoPolygon.isOnLine(30,50, 10,10, 20,30));
assertTrue(OsmNogoPolygon.isOnLine(10, 10, 10, 10, 10, 20));
assertTrue(OsmNogoPolygon.isOnLine(10, 10, 10, 10, 20, 10));
assertTrue(OsmNogoPolygon.isOnLine(10, 10, 20, 10, 10, 10));
assertTrue(OsmNogoPolygon.isOnLine(10, 10, 10, 20, 10, 10));
assertTrue(OsmNogoPolygon.isOnLine(10, 15, 10, 10, 10, 20));
assertTrue(OsmNogoPolygon.isOnLine(15, 10, 10, 10, 20, 10));
assertTrue(OsmNogoPolygon.isOnLine(10, 10, 10, 10, 20, 30));
assertTrue(OsmNogoPolygon.isOnLine(20, 30, 10, 10, 20, 30));
assertTrue(OsmNogoPolygon.isOnLine(15, 20, 10, 10, 20, 30));
assertFalse(OsmNogoPolygon.isOnLine(11, 11, 10, 10, 10, 20));
assertFalse(OsmNogoPolygon.isOnLine(11, 11, 10, 10, 20, 10));
assertFalse(OsmNogoPolygon.isOnLine(15, 21, 10, 10, 20, 30));
assertFalse(OsmNogoPolygon.isOnLine(15, 19, 10, 10, 20, 30));
assertFalse(OsmNogoPolygon.isOnLine(0, -10, 10, 10, 20, 30));
assertFalse(OsmNogoPolygon.isOnLine(30, 50, 10, 10, 20, 30));
}
@Test
public void testDistanceWithinPolygon() {
// Testing polygon
final double[] lons = { 2.333523, 2.333432, 2.333833, 2.333983, 2.334815, 2.334766 };
final double[] lats = { 48.823778, 48.824091, 48.82389, 48.824165, 48.824232, 48.82384 };
OsmNogoPolygon polygon = new OsmNogoPolygon(true);
for (int i = 0; i < lons.length; i++) {
polygon.addVertex(toOsmLon(lons[i], 0), toOsmLat(lats[i], 0));
}
OsmNogoPolygon polyline = new OsmNogoPolygon(false);
for (int i = 0; i < lons.length; i++) {
polyline.addVertex(toOsmLon(lons[i], 0), toOsmLat(lats[i], 0));
}
// Testing polygon
final double[] lons = {2.333523, 2.333432, 2.333833, 2.333983, 2.334815, 2.334766};
final double[] lats = {48.823778, 48.824091, 48.82389, 48.824165, 48.824232, 48.82384};
OsmNogoPolygon polygon = new OsmNogoPolygon(true);
for (int i = 0; i < lons.length; i++) {
polygon.addVertex(toOsmLon(lons[i], 0), toOsmLat(lats[i], 0));
}
OsmNogoPolygon polyline = new OsmNogoPolygon(false);
for (int i = 0; i < lons.length; i++) {
polyline.addVertex(toOsmLon(lons[i], 0), toOsmLat(lats[i], 0));
}
// Check with a segment with a single intersection with the polygon
int lon1 = toOsmLon(2.33308732509613, 0);
int lat1 = toOsmLat(48.8238790443901, 0);
int lon2 = toOsmLon(2.33378201723099, 0);
int lat2 = toOsmLat(48.8239585098974, 0);
assertEquals(
"Should give the correct length for a segment with a single intersection",
17.5,
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * 17.5
);
// Check with a segment with a single intersection with the polygon
int lon1 = toOsmLon(2.33308732509613, 0);
int lat1 = toOsmLat(48.8238790443901, 0);
int lon2 = toOsmLon(2.33378201723099, 0);
int lat2 = toOsmLat(48.8239585098974, 0);
assertEquals(
"Should give the correct length for a segment with a single intersection",
17.5,
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * 17.5
);
// Check with a segment crossing multiple times the polygon
lon2 = toOsmLon(2.33488172292709, 0);
lat2 = toOsmLat(48.8240891862353, 0);
assertEquals(
"Should give the correct length for a segment with multiple intersections",
85,
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * 85
);
// Check with a segment crossing multiple times the polygon
lon2 = toOsmLon(2.33488172292709, 0);
lat2 = toOsmLat(48.8240891862353, 0);
assertEquals(
"Should give the correct length for a segment with multiple intersections",
85,
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * 85
);
// Check that it works when a point is within the polygon
lon2 = toOsmLon(2.33433187007904, 0);
lat2 = toOsmLat(48.8240238480664, 0);
assertEquals(
"Should give the correct length when last point is within the polygon",
50,
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * 50
);
lon1 = toOsmLon(2.33433187007904, 0);
lat1 = toOsmLat(48.8240238480664, 0);
lon2 = toOsmLon(2.33488172292709, 0);
lat2 = toOsmLat(48.8240891862353, 0);
assertEquals(
"Should give the correct length when first point is within the polygon",
35,
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * 35
);
// Check that it works when a point is within the polygon
lon2 = toOsmLon(2.33433187007904, 0);
lat2 = toOsmLat(48.8240238480664, 0);
assertEquals(
"Should give the correct length when last point is within the polygon",
50,
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * 50
);
lon1 = toOsmLon(2.33433187007904, 0);
lat1 = toOsmLat(48.8240238480664, 0);
lon2 = toOsmLon(2.33488172292709, 0);
lat2 = toOsmLat(48.8240891862353, 0);
assertEquals(
"Should give the correct length when first point is within the polygon",
35,
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * 35
);
lon1 = toOsmLon(2.333523, 0);
lat1 = toOsmLat(48.823778, 0);
lon2 = toOsmLon(2.333432, 0);
lat2 = toOsmLat(48.824091, 0);
assertEquals(
"Should give the correct length if the segment overlaps with an edge of the polygon",
CheapRuler.distance(lon1, lat1, lon2, lat2),
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * CheapRuler.distance(lon1, lat1, lon2, lat2)
);
lon1 = toOsmLon(2.333523, 0);
lat1 = toOsmLat(48.823778, 0);
lon2 = toOsmLon(2.333432, 0);
lat2 = toOsmLat(48.824091, 0);
assertEquals(
"Should give the correct length if the segment overlaps with an edge of the polygon",
CheapRuler.distance(lon1, lat1, lon2, lat2),
polygon.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * CheapRuler.distance(lon1, lat1, lon2, lat2)
);
lon1 = toOsmLon(2.333523, 0);
lat1 = toOsmLat(48.823778, 0);
lon2 = toOsmLon(2.3334775, 0);
lat2 = toOsmLat(48.8239345, 0);
assertEquals(
"Should give the correct length if the segment overlaps with a polyline",
CheapRuler.distance(lon1, lat1, lon2, lat2),
polyline.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * CheapRuler.distance(lon1, lat1, lon2, lat2)
);
lon1 = toOsmLon(2.333523, 0);
lat1 = toOsmLat(48.823778, 0);
lon2 = toOsmLon(2.3334775, 0);
lat2 = toOsmLat(48.8239345, 0);
assertEquals(
"Should give the correct length if the segment overlaps with a polyline",
CheapRuler.distance(lon1, lat1, lon2, lat2),
polyline.distanceWithinPolygon(lon1, lat1, lon2, lat2),
0.05 * CheapRuler.distance(lon1, lat1, lon2, lat2)
);
}
}