I need to get the distance from a lat/lng point to a line. Of course needs to follow the Great Circle.
I found a great article on this at http://www.movable-type.co.uk/scripts/latlong.html
but the code is not working right. Either I am doing something wrong or there is something missing. Here is the function in question. See the link for the other functions if needed.
var R = 3961.3
LatLon.crossTrack = function(lat1, lon1, lat2, lon2, lat3, lon3) {
var d13 = LatLon.distHaversine(lat1, lon1, lat3, lon3);
var brng12 = LatLon.bearing(lat1, lon1, lat2, lon2);
var brng13 = LatLon.bearing(lat1, lon1, lat3, lon3);
var dXt = Math.asin(Math.sin(d13/R)*Math.sin(brng13-brng12)) * R;
return dXt;
}
lat/lon1 = -94.127592, 41.81762
lat/lon2 = -94.087257, 41.848202
lat/lon3 = -94.046875, 41.791057
This reports 0.865 miles. The actual distance is 4.29905 miles.
Any clues as to how to fix this? I am not a mathematician, just a long in the tooth programmer.
Most trig functions need radians. Are your angular measures in degrees? Perhaps they need to be converted using the usual formula:
2*π radians = 360 degrees
If you look under the formula for Haversine formula, you'll see this:
(Note that angles need to be in radians to pass to trig functions).
Is your function returning same value for these coordinates:
crossTrack(0,0,0,1,0.1,0.5);
crossTrack(0,0,0,1,0.1,0.6);
crossTrack(0,0,0,1,0.1,0.4);
I think it should but mine does not. The 3rd point is always 0.1 to the north from equator. only the longitude changes which should not affect the result. As it seems it does.
I tried this pointlinedistancetest sending it aalatlon etc
private static final double _eQuatorialEarthRadius = 6378.1370D;
private static final double _d2r = (Math.PI / 180D);
private static double PRECISION = 1;
// Haversine Algorithm
// source: http://stackoverflow.com/questions/365826/calculate-distance-between-2-gps-coordinates
private static double HaversineInM(double lat1, double long1, double lat2, double long2) {
return (1000D * HaversineInKM(lat1, long1, lat2, long2));
}
private static double HaversineInKM(double lat1, double long1, double lat2, double long2) {
double dlong = (long2 - long1) * _d2r;
double dlat = (lat2 - lat1) * _d2r;
double a = Math.pow(Math.sin(dlat / 2D), 2D) + Math.cos(lat1 * _d2r) * Math.cos(lat2 * _d2r)
* Math.pow(Math.sin(dlong / 2D), 2D);
double c = 2D * Math.atan2(Math.sqrt(a), Math.sqrt(1D - a));
double d = _eQuatorialEarthRadius * c;
return d;
}
// Distance between a point and a line
public static double pointLineDistanceTest(double[] aalatlng,double[] bblatlng,double[]cclatlng){
double [] a = aalatlng;
double [] b = bblatlng;
double [] c = cclatlng;
double[] nearestNode = nearestPointGreatCircle(a, b, c);
// System.out.println("nearest node: " + Double.toString(nearestNode[0])
+ ","+Double.toString(nearestNode[1]));
double result = HaversineInM(c[0], c[1], nearestNode[0], nearestNode[1]);
// System.out.println("result: " + Double.toString(result));
return (result);
}
// source: http://stackoverflow.com/questions/1299567/how-to-calculate-distance-from-a-point-to-a-line-segment-on-a-sphere
private static double[] nearestPointGreatCircle(double[] a, double[] b, double c[])
{
double[] a_ = toCartsian(a);
double[] b_ = toCartsian(b);
double[] c_ = toCartsian(c);
double[] G = vectorProduct(a_, b_);
double[] F = vectorProduct(c_, G);
double[] t = vectorProduct(G, F);
return fromCartsian(multiplyByScalar(normalize(t), _eQuatorialEarthRadius));
}
@SuppressWarnings("unused")
private static double[] nearestPointSegment (double[] a, double[] b, double[] c)
{
double[] t= nearestPointGreatCircle(a,b,c);
if (onSegment(a,b,t))
return t;
return (HaversineInKM(a[0], a[1], c[0], c[1]) < HaversineInKM(b[0], b[1], c[0], c[1])) ? a : b;
}
private static boolean onSegment (double[] a, double[] b, double[] t)
{
// should be return distance(a,t)+distance(b,t)==distance(a,b),
// but due to rounding errors, we use:
return Math.abs(HaversineInKM(a[0], a[1], b[0], b[1])-HaversineInKM(a[0], a[1], t[0], t[1])-HaversineInKM(b[0], b[1], t[0], t[1])) < PRECISION;
}
// source: http://stackoverflow.com/questions/1185408/converting-from-longitude-latitude-to-cartesian-coordinates
private static double[] toCartsian(double[] coord) {
double[] result = new double[3];
result[0] = _eQuatorialEarthRadius * Math.cos(Math.toRadians(coord[0])) * Math.cos(Math.toRadians(coord[1]));
result[1] = _eQuatorialEarthRadius * Math.cos(Math.toRadians(coord[0])) * Math.sin(Math.toRadians(coord[1]));
result[2] = _eQuatorialEarthRadius * Math.sin(Math.toRadians(coord[0]));
return result;
}
private static double[] fromCartsian(double[] coord){
double[] result = new double[2];
result[0] = Math.toDegrees(Math.asin(coord[2] / _eQuatorialEarthRadius));
result[1] = Math.toDegrees(Math.atan2(coord[1], coord[0]));
return result;
}
// Basic functions
private static double[] vectorProduct (double[] a, double[] b){
double[] result = new double[3];
result[0] = a[1] * b[2] - a[2] * b[1];
result[1] = a[2] * b[0] - a[0] * b[2];
result[2] = a[0] * b[1] - a[1] * b[0];
return result;
}
private static double[] normalize(double[] t) {
double length = Math.sqrt((t[0] * t[0]) + (t[1] * t[1]) + (t[2] * t[2]));
double[] result = new double[3];
result[0] = t[0]/length;
result[1] = t[1]/length;
result[2] = t[2]/length;
return result;
}
private static double[] multiplyByScalar(double[] normalize, double k) {
double[] result = new double[3];
result[0] = normalize[0]*k;
result[1] = normalize[1]*k;
result[2] = normalize[2]*k;
return result;
}