Better distance computation (revisited)

This commit is contained in:
Arndt Brenschede 2018-12-07 00:45:12 +01:00
parent e7afb236a6
commit ab2f5e3ae0
12 changed files with 101 additions and 543 deletions

View file

@ -75,8 +75,6 @@ public class OsmNogoPolygon extends OsmNodeNamed
*/ */
public void calcBoundingCircle() public void calcBoundingCircle()
{ {
CheapRulerSingleton cr = CheapRulerSingleton.getInstance();
int cxmin, cxmax, cymin, cymax; int cxmin, cxmax, cymin, cymax;
cxmin = cymin = Integer.MAX_VALUE; cxmin = cymin = Integer.MAX_VALUE;
cxmax = cymax = Integer.MIN_VALUE; cxmax = cymax = Integer.MIN_VALUE;
@ -103,9 +101,13 @@ public class OsmNogoPolygon extends OsmNodeNamed
} }
} }
double cx = (cxmax+cxmin) / 2.0; // center of circle int cx = (cxmax+cxmin) / 2; // center of circle
double cy = (cymax+cymin) / 2.0; int cy = (cymax+cymin) / 2;
double ccoslat = cr.cosIlat((int) cy); // cosin at latitude of center
double[] lonlat2m = CheapRulerSingleton.getLonLatToMeterScales( cy );
double dlon2m = lonlat2m[0];
double dlat2m = lonlat2m[1];
double rad = 0; // radius double rad = 0; // radius
double rad2 = 0; // radius squared; double rad2 = 0; // radius squared;
@ -119,8 +121,8 @@ public class OsmNogoPolygon extends OsmNodeNamed
for (int i = 0; i < points.size();i++) for (int i = 0; i < points.size();i++)
{ {
final Point p = points.get(i); final Point p = points.get(i);
final double dpix = (p.x - cx) * ccoslat; final double dpix = (p.x - cx) * dlon2m;
final double dpiy = p.y-cy; final double dpiy = (p.y - cy) * dlat2m;
final double dist2 = dpix * dpix + dpiy * dpiy; final double dist2 = dpix * dpix + dpiy * dpiy;
if (dist2 <= rad2) if (dist2 <= rad2)
{ {
@ -129,8 +131,8 @@ public class OsmNogoPolygon extends OsmNodeNamed
if (dist2 > dmax2) if (dist2 > dmax2)
{ {
dmax2 = dist2; // new maximum distance found dmax2 = dist2; // new maximum distance found
dpx = dpix; dpx = dpix / dlon2m;
dpy = dpiy; dpy = dpiy / dlat2m;
i_max = i; i_max = i;
} }
} }
@ -141,25 +143,21 @@ public class OsmNogoPolygon extends OsmNodeNamed
final double dist = Math.sqrt(dmax2); final double dist = Math.sqrt(dmax2);
final double dd = 0.5 * (dist - rad) / dist; final double dd = 0.5 * (dist - rad) / dist;
cx = cx + dd * dpx; // shift center toward point cx += (int)(dd * dpx + 0.5); // shift center toward point
cy = cy + dd * dpy; cy += (int)(dd * dpy + 0.5);
ccoslat = cr.cosIlat((int) cy);
final Point p = points.get(i_max); // calculate new radius to just include this point final Point p = points.get(i_max); // calculate new radius to just include this point
final double dpix = (p.x - cx) * ccoslat; final double dpix = (p.x - cx) * dlon2m;
final double dpiy = p.y-cy; final double dpiy = (p.y - cy) * dlat2m;
dmax2 = rad2 = dpix * dpix + dpiy * dpiy; dmax2 = rad2 = dpix * dpix + dpiy * dpiy;
rad = Math.sqrt(rad2); rad = Math.sqrt(rad2);
i_max = -1; i_max = -1;
} }
while (true); while (true);
ilon = (int) Math.round(cx); ilon = cx;
ilat = (int) Math.round(cy); ilat = cy;
dpx = cx - ilon; // rounding error radius = rad;
dpy = cy - ilat;
// compensate rounding error of center-point
radius = (rad + Math.sqrt(dpx * dpx + dpy * dpy)) * cr.ILATLNG_TO_LATLNG;
return; return;
} }

View file

@ -1,13 +1,13 @@
package btools.router; package btools.router;
import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmPos;
import btools.util.CheapRulerSingleton;
import java.io.DataInput; import java.io.DataInput;
import java.io.DataOutput; import java.io.DataOutput;
import java.io.IOException; import java.io.IOException;
import btools.mapaccess.OsmNode;
import btools.mapaccess.OsmPos;
import btools.util.CheapRulerSingleton;
/** /**
* Container for link between two Osm nodes * Container for link between two Osm nodes
* *
@ -78,8 +78,7 @@ public class OsmPathElement implements OsmPos
public final int calcDistance( OsmPos p ) public final int calcDistance( OsmPos p )
{ {
CheapRulerSingleton cr = CheapRulerSingleton.getInstance(); return (int)(CheapRulerSingleton.distance(ilon, ilat, p.getILon(), p.getILat()) + 1.0 );
return (int)(cr.distance(ilon, ilat, p.getILon(), p.getILat()) + 1.0 );
} }
public OsmPathElement origin; public OsmPathElement origin;

View file

@ -288,8 +288,7 @@ public final class RoutingContext
OsmNodeNamed nogo = nogopoints.get(i); OsmNodeNamed nogo = nogopoints.get(i);
cs[0] += nogo.ilon; cs[0] += nogo.ilon;
cs[1] += nogo.ilat; cs[1] += nogo.ilat;
// TODO[Phyks] cs[2] += (long) ( nogo.radius*10.);
cs[2] += (long) ( nogo.radius*111894.*10.);
} }
return cs; return cs;
} }
@ -311,13 +310,11 @@ public final class RoutingContext
public int calcDistance( int lon1, int lat1, int lon2, int lat2 ) public int calcDistance( int lon1, int lat1, int lon2, int lat2 )
{ {
CheapRulerSingleton cr = CheapRulerSingleton.getInstance(); double[] lonlat2m = CheapRulerSingleton.getLonLatToMeterScales( (lat1+lat2) >> 1 );
double dlon2m = lonlat2m[0];
coslat = cr.cosIlat(lat2); double dlat2m = lonlat2m[1];
double coslat6 = coslat*cr.ILATLNG_TO_LATLNG; double dx = (lon2 - lon1 ) * dlon2m;
double dy = (lat2 - lat1 ) * dlat2m;
double dx = (lon2 - lon1 ) * coslat6;
double dy = (lat2 - lat1 ) * cr.ILATLNG_TO_LATLNG;
double d = Math.sqrt( dy*dy + dx*dx ); double d = Math.sqrt( dy*dy + dx*dx );
shortestmatch = false; shortestmatch = false;
@ -327,10 +324,10 @@ public final class RoutingContext
for( int ngidx = 0; ngidx < nogopoints.size(); ngidx++ ) for( int ngidx = 0; ngidx < nogopoints.size(); ngidx++ )
{ {
OsmNodeNamed nogo = nogopoints.get(ngidx); OsmNodeNamed nogo = nogopoints.get(ngidx);
double x1 = (lon1 - nogo.ilon) * coslat6; double x1 = (lon1 - nogo.ilon) * dlon2m;
double y1 = (lat1 - nogo.ilat) * cr.ILATLNG_TO_LATLNG; double y1 = (lat1 - nogo.ilat) * dlat2m;
double x2 = (lon2 - nogo.ilon) * coslat6; double x2 = (lon2 - nogo.ilon) * dlon2m;
double y2 = (lat2 - nogo.ilat) * cr.ILATLNG_TO_LATLNG; double y2 = (lat2 - nogo.ilat) * dlat2m;
double r12 = x1*x1 + y1*y1; double r12 = x1*x1 + y1*y1;
double r22 = x2*x2 + y2*y2; double r22 = x2*x2 + y2*y2;
double radius = Math.abs( r12 < r22 ? y1*dx - x1*dy : y2*dx - x2*dy ) / d; double radius = Math.abs( r12 < r22 ? y1*dx - x1*dy : y2*dx - x2*dy ) / d;
@ -365,8 +362,8 @@ public final class RoutingContext
wayfraction = -s2 / (d*d); wayfraction = -s2 / (d*d);
double xm = x2 - wayfraction*dx; double xm = x2 - wayfraction*dx;
double ym = y2 - wayfraction*dy; double ym = y2 - wayfraction*dy;
ilonshortest = (int)(xm / coslat6 + nogo.ilon); ilonshortest = (int)(xm / dlon2m + nogo.ilon);
ilatshortest = (int)(ym / cr.ILATLNG_TO_LATLNG + nogo.ilat); ilatshortest = (int)(ym / dlat2m + nogo.ilat);
} }
else if ( s1 > s2 ) else if ( s1 > s2 )
{ {
@ -397,14 +394,14 @@ public final class RoutingContext
lon1 = ilonshortest; lon1 = ilonshortest;
lat1 = ilatshortest; lat1 = ilatshortest;
} }
dx = (lon2 - lon1 ) * coslat6; dx = (lon2 - lon1 ) * dlon2m;
dy = (lat2 - lat1 ) * cr.ILATLNG_TO_LATLNG; dy = (lat2 - lat1 ) * dlat2m;
d = Math.sqrt( dy*dy + dx*dx ); d = Math.sqrt( dy*dy + dx*dx );
} }
} }
} }
} }
return (int)(cr.distance(lon1, lat1, lon2, lat2) + 1.0 ); return (int)(d + 1.0 );
} }
// assumes that calcDistance/calcCosAngle called in sequence, so coslat valid // assumes that calcDistance/calcCosAngle called in sequence, so coslat valid

View file

@ -742,7 +742,7 @@ public class RoutingEngine extends Thread
OsmPath startPath = routingContext.createPath( startLink ); OsmPath startPath = routingContext.createPath( startLink );
startLink.addLinkHolder( startPath, null ); startLink.addLinkHolder( startPath, null );
if ( wp != null ) wp.radius = 1e-5; if ( wp != null ) wp.radius = 0.1;
return routingContext.createPath( startPath, link, null, guideTrack != null ); return routingContext.createPath( startPath, link, null, guideTrack != null );
} }
@ -1064,7 +1064,7 @@ public class RoutingEngine extends Thread
{ {
if ( isFinalLink ) if ( isFinalLink )
{ {
endPos.radius = 1e-5; endPos.radius = 0.1;
routingContext.setWaypoint( endPos, true ); routingContext.setWaypoint( endPos, true );
} }
OsmPath testPath = routingContext.createPath( otherPath, link, refTrack, guideTrack != null ); OsmPath testPath = routingContext.createPath( otherPath, link, refTrack, guideTrack != null );

View file

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

View file

@ -42,12 +42,12 @@ public final class WaypointMatcherImpl implements WaypointMatcher
{ {
// todo: bounding-box pre-filter // todo: bounding-box pre-filter
CheapRulerSingleton cr = CheapRulerSingleton.getInstance(); double[] lonlat2m = CheapRulerSingleton.getLonLatToMeterScales( (lat1+lat2) >> 1 );
double coslat = cr.cosIlat(lat2); double dlon2m = lonlat2m[0];
double coslat6 = coslat * cr.ILATLNG_TO_LATLNG; double dlat2m = lonlat2m[1];
double dx = ( lon2 - lon1 ) * coslat6; double dx = ( lon2 - lon1 ) * dlon2m;
double dy = ( lat2 - lat1 ) * cr.ILATLNG_TO_LATLNG; double dy = ( lat2 - lat1 ) * dlat2m;
double d = Math.sqrt( dy * dy + dx * dx ); double d = Math.sqrt( dy * dy + dx * dx );
if ( d == 0. ) if ( d == 0. )
return; return;
@ -56,10 +56,10 @@ public final class WaypointMatcherImpl implements WaypointMatcher
{ {
OsmNodeNamed wp = mwp.waypoint; OsmNodeNamed wp = mwp.waypoint;
double x1 = ( lon1 - wp.ilon ) * coslat6; double x1 = ( lon1 - wp.ilon ) * dlon2m;
double y1 = ( lat1 - wp.ilat ) * cr.ILATLNG_TO_LATLNG; double y1 = ( lat1 - wp.ilat ) * dlat2m;
double x2 = ( lon2 - wp.ilon ) * coslat6; double x2 = ( lon2 - wp.ilon ) * dlon2m;
double y2 = ( lat2 - wp.ilat ) * cr.ILATLNG_TO_LATLNG; double y2 = ( lat2 - wp.ilat ) * dlat2m;
double r12 = x1 * x1 + y1 * y1; double r12 = x1 * x1 + y1 * y1;
double r22 = x2 * x2 + y2 * y2; double r22 = x2 * x2 + y2 * y2;
double radius = Math.abs( r12 < r22 ? y1 * dx - x1 * dy : y2 * dx - x2 * dy ) / d; double radius = Math.abs( r12 < r22 ? y1 * dx - x1 * dy : y2 * dx - x2 * dy ) / d;
@ -92,8 +92,8 @@ public final class WaypointMatcherImpl implements WaypointMatcher
double wayfraction = -s2 / ( d * d ); double wayfraction = -s2 / ( d * d );
double xm = x2 - wayfraction * dx; double xm = x2 - wayfraction * dx;
double ym = y2 - wayfraction * dy; double ym = y2 - wayfraction * dy;
mwp.crosspoint.ilon = (int) ( xm / coslat6 + wp.ilon ); mwp.crosspoint.ilon = (int) ( xm / dlon2m + wp.ilon );
mwp.crosspoint.ilat = (int) ( ym / cr.ILATLNG_TO_LATLNG + wp.ilat ); mwp.crosspoint.ilat = (int) ( ym / dlat2m + wp.ilat );
} }
else if ( s1 > s2 ) else if ( s1 > s2 )
{ {

View file

@ -65,25 +65,25 @@ public class OsmNogoPolygonTest {
@Test @Test
public void testCalcBoundingCircle() { public void testCalcBoundingCircle() {
CheapRulerSingleton cr = CheapRulerSingleton.getInstance(); double[] lonlat2m = CheapRulerSingleton.getLonLatToMeterScales( polygon.ilat );
double dlon2m = lonlat2m[0];
double dlat2m = lonlat2m[1];
polygon.calcBoundingCircle(); polygon.calcBoundingCircle();
double r = polygon.radius; double r = polygon.radius;
for (int i=0; i<lons.length; i++) { for (int i=0; i<lons.length; i++) {
double py = toOsmLat(lats[i]); double dpx = (toOsmLon(lons[i]) - polygon.ilon) * dlon2m;
double dpx = (toOsmLon(lons[i]) - polygon.ilon) * cr.cosIlat(polygon.ilat); double dpy = (toOsmLon(lats[i]) - polygon.ilon) * dlat2m;
double dpy = py - polygon.ilat; double r1 = Math.sqrt(dpx * dpx + dpy * dpy);
double r1 = Math.sqrt(dpx * dpx + dpy * dpy) * cr.ILATLNG_TO_LATLNG;
double diff = r-r1; double diff = r-r1;
assertTrue("i: "+i+" r("+r+") >= r1("+r1+")", diff >= 0); assertTrue("i: "+i+" r("+r+") >= r1("+r1+")", diff >= 0);
} }
polyline.calcBoundingCircle(); polyline.calcBoundingCircle();
r = polyline.radius; r = polyline.radius;
for (int i=0; i<lons.length; i++) { for (int i=0; i<lons.length; i++) {
double py = toOsmLat(lats[i]); double dpx = (toOsmLon(lons[i]) - polyline.ilon) * dlon2m;
double dpx = (toOsmLon(lons[i]) - polyline.ilon) * cr.cosIlat(polyline.ilat); double dpy = (toOsmLon(lats[i]) - polyline.ilon) * dlat2m;
double dpy = py - polyline.ilat; double r1 = Math.sqrt(dpx * dpx + dpy * dpy);
double r1 = Math.sqrt(dpx * dpx + dpy * dpy) * cr.ILATLNG_TO_LATLNG;
double diff = r-r1; double diff = r-r1;
assertTrue("i: "+i+" r("+r+") >= r1("+r1+")", diff >= 0); assertTrue("i: "+i+" r("+r+") >= r1("+r1+")", diff >= 0);
} }

View file

@ -1,6 +1,5 @@
package btools.mapcreator; package btools.mapcreator;
import btools.util.CheapRulerSingleton;
import btools.util.ReducedMedianFilter; import btools.util.ReducedMedianFilter;
/** /**
@ -229,8 +228,7 @@ public class SrtmRaster
private static Weights[][] calcWeights( int latIndex ) private static Weights[][] calcWeights( int latIndex )
{ {
CheapRulerSingleton cr = CheapRulerSingleton.getInstance(); double coslat = Math.cos( latIndex * 5. / 57.3 );
double coslat = cr.cosLat(latIndex * 5.);
// radius in pixel units // radius in pixel units
double ry = filterDiscRadius; double ry = filterDiscRadius;

View file

@ -7,7 +7,6 @@ package btools.mapaccess;
import btools.codec.MicroCache; import btools.codec.MicroCache;
import btools.codec.MicroCache2; import btools.codec.MicroCache2;
import btools.expressions.BExpressionContextWay;
import btools.util.ByteArrayUnifier; import btools.util.ByteArrayUnifier;
import btools.util.CheapRulerSingleton; import btools.util.CheapRulerSingleton;
import btools.util.IByteArrayUnifier; import btools.util.IByteArrayUnifier;
@ -103,8 +102,7 @@ public class OsmNode extends OsmLink implements OsmPos
public final int calcDistance( OsmPos p ) public final int calcDistance( OsmPos p )
{ {
CheapRulerSingleton cr = CheapRulerSingleton.getInstance(); return (int)(CheapRulerSingleton.distance(ilon, ilat, p.getILon(), p.getILat()) + 1.0 );
return (int) (cr.distance(ilon, ilat, p.getILon(), p.getILat()) + 1.0);
} }
public String toString() public String toString()

View file

@ -103,8 +103,7 @@ public class OsmNodeP extends OsmLinkP implements Comparable<OsmNodeP>, OsmPos
@Override @Override
public int calcDistance( OsmPos p ) public int calcDistance( OsmPos p )
{ {
CheapRulerSingleton cr = CheapRulerSingleton.getInstance(); return (int)(CheapRulerSingleton.distance(ilon, ilat, p.getILon(), p.getILat()) + 1.0 );
return (int)(cr.distance(ilon, ilat, p.getILon(), p.getILat()) + 1.0 );
} }
@Override @Override

View file

@ -509,17 +509,17 @@ public class BRouterView extends View
centerLon = ( maxlon + minlon ) / 2; centerLon = ( maxlon + minlon ) / 2;
centerLat = ( maxlat + minlat ) / 2; centerLat = ( maxlat + minlat ) / 2;
CheapRulerSingleton cr = CheapRulerSingleton.getInstance(); double[] lonlat2m = CheapRulerSingleton.getLonLatToMeterScales( centerLat );
double coslat = cr.cosIlat(centerLat); double dlon2m = lonlat2m[0];
double difflon = maxlon - minlon; double dlat2m = lonlat2m[1];
double difflat = maxlat - minlat; double difflon = (maxlon - minlon)*dlon2m;
double difflat = (maxlat - minlat)*dlat2m;
scaleLon = imgw / ( difflon * 1.5 ); scaleLon = imgw / ( difflon * 1.5 );
scaleLat = imgh / ( difflat * 1.5 ); scaleLat = imgh / ( difflat * 1.5 );
if ( scaleLon < scaleLat * coslat ) double scaleMin = scaleLon < scaleLat ? scaleLon : scaleLat;
scaleLat = scaleLon / coslat; scaleLat *= dlon2m;
else scaleLon *= dlat2m;
scaleLon = scaleLat * coslat;
startTime = System.currentTimeMillis(); startTime = System.currentTimeMillis();
RoutingContext.prepareNogoPoints( nogoList ); RoutingContext.prepareNogoPoints( nogoList );

View file

@ -12,69 +12,49 @@ public final class CheapRulerSingleton {
* This is implemented as a Singleton to have a unique cache for the cosine * This is implemented as a Singleton to have a unique cache for the cosine
* values across all the code. * values across all the code.
*/ */
private static volatile CheapRulerSingleton instance = null;
// Conversion constants // Conversion constants
public final static double ILATLNG_TO_LATLNG = 1e-6; // From integer to degrees public final static double ILATLNG_TO_LATLNG = 1e-6; // From integer to degrees
public final static int KILOMETERS_TO_METERS = 1000; public final static int KILOMETERS_TO_METERS = 1000;
public final static double DEG_TO_RAD = Math.PI / 180.; public final static double DEG_TO_RAD = Math.PI / 180.;
// Cosine cache constants // Cosine cache constants
private final static int COS_CACHE_LENGTH = 8192; private final static int SCALE_CACHE_LENGTH = 1800;
private final static double COS_CACHE_MAX_DEGREES = 90.0; private final static int SCALE_CACHE_INCREMENT = 100000;
// COS_CACHE_LENGTH cached values between 0 and COS_CACHE_MAX_DEGREES degrees. // COS_CACHE_LENGTH cached values between 0 and COS_CACHE_MAX_DEGREES degrees.
double[] COS_CACHE = new double[COS_CACHE_LENGTH]; private final static double[][] SCALE_CACHE = new double[SCALE_CACHE_LENGTH][];
/** /**
* Helper to build the cache of cosine values. * build the cache of cosine values.
*/ */
private void buildCosCache() { static {
double increment = DEG_TO_RAD * COS_CACHE_MAX_DEGREES / COS_CACHE_LENGTH; for (int i = 0; i < SCALE_CACHE_LENGTH; i++) {
for (int i = 0; i < COS_CACHE_LENGTH; i++) { SCALE_CACHE[i] = calcKxKyFromILat( i*SCALE_CACHE_INCREMENT + SCALE_CACHE_INCREMENT/2 );
COS_CACHE[i] = Math.cos(i * increment);
} }
} }
private CheapRulerSingleton() { private static double[] calcKxKyFromILat(int ilat) {
super(); double lat = DEG_TO_RAD*(ilat-90000000)/1000000.;
// Build the cache of cosine values. double cos = Math.cos(lat);
buildCosCache(); double cos2 = 2 * cos * cos - 1;
double cos3 = 2 * cos * cos2 - cos;
double cos4 = 2 * cos * cos3 - cos2;
double cos5 = 2 * cos * cos4 - cos3;
// Multipliers for converting integer longitude and latitude into distance
// (http://1.usa.gov/1Wb1bv7)
double[] kxky = new double[2];
kxky[0] = (111.41513 * cos - 0.09455 * cos3 + 0.00012 * cos5) * ILATLNG_TO_LATLNG * KILOMETERS_TO_METERS;
kxky[1] = (111.13209 - 0.56605 * cos2 + 0.0012 * cos4) * ILATLNG_TO_LATLNG * KILOMETERS_TO_METERS;
return kxky;
} }
/** /**
* Get an instance of this singleton class. * Calculate the degree->meter scale for given latitude
*
* @result [lon->meter,lat->meter]
*/ */
public final static CheapRulerSingleton getInstance() { public static double[] getLonLatToMeterScales( int ilat ) {
if (CheapRulerSingleton.instance == null) { return SCALE_CACHE[ ilat / SCALE_CACHE_INCREMENT ];
synchronized(CheapRulerSingleton.class) {
if (CheapRulerSingleton.instance == null) {
CheapRulerSingleton.instance = new CheapRulerSingleton();
}
}
}
return CheapRulerSingleton.instance;
}
/**
* Helper to compute the cosine of an integer latitude.
*/
public double cosIlat(int ilat) {
double latDegrees = ilat * ILATLNG_TO_LATLNG;
if (ilat > 90000000) {
// Use the symmetry of the cosine.
latDegrees -= 90;
}
return COS_CACHE[(int) (latDegrees * COS_CACHE_LENGTH / COS_CACHE_MAX_DEGREES)];
}
/**
* Helper to compute the cosine of a latitude (in degrees).
*/
public double cosLat(double lat) {
if (lat < 0) {
lat += 90.;
}
return COS_CACHE[(int) (lat * COS_CACHE_LENGTH / COS_CACHE_MAX_DEGREES)];
} }
/** /**
@ -89,20 +69,10 @@ public final class CheapRulerSingleton {
* @note Integer longitude is ((longitude in degrees) + 180) * 1e6. * @note Integer longitude is ((longitude in degrees) + 180) * 1e6.
* Integer latitude is ((latitude in degrees) + 90) * 1e6. * Integer latitude is ((latitude in degrees) + 90) * 1e6.
*/ */
public double distance(int ilon1, int ilat1, int ilon2, int ilat2) { public static double distance(int ilon1, int ilat1, int ilon2, int ilat2) {
double cos = cosIlat(ilat1); double[] kxky = getLonLatToMeterScales( ( ilat1 + ilat2 ) >> 1 );
double cos2 = 2 * cos * cos - 1; double dlon = (ilon1 - ilon2) * kxky[0];
double cos3 = 2 * cos * cos2 - cos; double dlat = (ilat1 - ilat2) * kxky[1];
double cos4 = 2 * cos * cos3 - cos2;
double cos5 = 2 * cos * cos4 - cos3;
// Multipliers for converting integer longitude and latitude into distance
// (http://1.usa.gov/1Wb1bv7)
double kx = (111.41513 * cos - 0.09455 * cos3 + 0.00012 * cos5) * ILATLNG_TO_LATLNG * KILOMETERS_TO_METERS;
double ky = (111.13209 - 0.56605 * cos2 + 0.0012 * cos4) * ILATLNG_TO_LATLNG * KILOMETERS_TO_METERS;
double dlat = (ilat1 - ilat2) * ky;
double dlon = (ilon1 - ilon2) * kx;
return Math.sqrt(dlat * dlat + dlon * dlon); // in m return Math.sqrt(dlat * dlat + dlon * dlon); // in m
} }
} }