0

I wish to get the distance to a line and started using haversine code.

 private static final double _eQuatorialEarthRadius = 6378.1370D;
    private static final double _d2r = (Math.PI / 180D);
    private static double PRECISION = 0.001;

    // 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[] ttlatlng) {


        double [] a = aalatlng;
        double [] b = bblatlng;
        double [] c = ttlatlng;


        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;
    }

and had many errors so wrote this to calculate using bearings to get the angle then using distance (point a, target) to calculate the target to (point a, point b) line. Point A and B are the line and I want the distance from target to line. No great circle. sin angle (difference in bearings) *point a to target distance= Length of the side of a right angle triangle from the right angle on AB line to target.

 public  double pointlinedistancetest(){

     //set latlng location point a
     Location apoint=new Location("");

     apoint.setLatitude(lata);
     apoint.setLongitude(lona);


     //set latlng location point b
     Location bpoint=new Location("");

     bpoint.setLatitude(latb);
     bpoint.setLongitude(lonb);


     //set latlng location target to get dis to line
     Location tpoint=new Location("");

     tpoint.setLatitude(lat);
     tpoint.setLongitude(lon);


     float pbearingf = apoint.bearingTo(bpoint);

     float tbearingf= apoint.bearingTo(tpoint);

     double tb=tbearingf;
     double ab=pbearingf;



             //get angle degree difference 
    float angle= Math.min((pbearingf-tbearingf)<0?pbearingf-tbearingf+360:pbearingf-tbearingf, (tbearingf-pbearingf)<0?tbearingf-pbearingf+360:tbearingf-pbearingf);
//   float angle= Math.min((tbearingf-pbearingf)<0?tbearingf-pbearingf+360:tbearingf-pbearingf, (pbearingf-tbearingf)<0?pbearingf-tbearingf+360:pbearingf-tbearingf); 

    // min((a1-a2)<0?a1-a2+360:a1-a2, (a2-a1)<0?a2-a1+360:a2-a1)


     double aabearing=angle;

              float atot=apoint.distanceTo(tpoint);

     double atotdis=atot;

             //right angle triangle formula
     dis=(Math.sin(aabearing))*atotdis;

     return (dis);

}

Now both are still showing massive errors of up to 30%. Does this code look correct and is there any better way of doing this or are my errors else where. My GPS is showing 4 metre accuracy and my formulas(code) is showing errors of 10 to 20 metres and does not seem to be remotely the same.

Tomsmith
  • 15
  • 7
  • have you tried `distanceTo()` or `distanceBetween()` methods of Location class.... – Michael Shrestha Sep 07 '14 at 06:41
  • @MichaelShrestha I used distanceTo() to get my distance from point a to (target ....*sin angle = opposite side of point a).. of a right angle triangle. Point B just provides a bearing of a line and the triangle is made from the target to a right angle on the a,b line as http://www.mathsisfun.com/algebra/trig-finding-side-right-triangle.html. I don't actually know where it intersects the line I just want the distance to it. – Tomsmith Sep 07 '14 at 07:31

1 Answers1

1

You can use Google Geometry library to find length of line and its bearing .

From documentation

computeDistanceBetween(from:LatLng, to:LatLng, radius?:number) Returns the distance between two LatLngs as a number in meters. The radius defaults to the Earth's radius in meters(6378137).

computeHeading(from:LatLng, to:LatLng) Returns the heading from one LatLng to another LatLng. Headings are expressed in degrees clockwise from North within the range [-180,180) as a number.

computeOffset(from:LatLng, distance:number, heading:number, radius?:number) Returns the LatLng resulting from moving a distance from an origin in the specified heading (expressed in degrees clockwise from north) as LatLng.

enter image description here

First find distance between start and end of line

var spherical = google.maps.geometry.spherical; 
var length = google.maps.geometry.spherical.computeDistanceBetween(A,B);

Then find bearing of line

 var heading = google.maps.geometry.spherical.computeHeading(A,B);

From image 120 degrees

Then find distance and bearing between start of line A and point C

var length2 = google.maps.geometry.spherical.computeDistanceBetween(A,C);
var heading2 = google.maps.geometry.spherical.computeHeading(A,C);

From image 50 degrees

var angleBAC = heading1-heading2

Angle BAC = 120 - 50 degrees = 70 degrees

As you now know angle BAC and distance from A to C you can use triginometry to find length CD

sineBAC = opp/Hypotenuese

var CD = Math.sin(angleBAC) * length2;

From Image

opp = sine 70 degrees X length AC

From image 0.9397 X 5 = 4.6984

david strachan
  • 7,174
  • 2
  • 23
  • 33
  • I would still have problems going from say 10 to -10 degrees and I have done more testing and this what gives grief I think as sometimes my code works then it doesn't. You have explained it a lot better. – Tomsmith Sep 09 '14 at 05:39
  • True but I got it wrong it is 10 -350 in my code. Added this but me thinks further testing required if (bearingr>360){ bearingr=bearingr-360; } if (bearingr<0){ bearingr=bearingr+360; } if (bearingl>360){ bearingl=bearingl-360; } if (bearingl<0){ bearingl=bearingl+360; } – Tomsmith Sep 10 '14 at 11:48
  • Computeoffset fixes that – Tomsmith Sep 11 '14 at 21:38
  • My code gives initial bearing in great circle route. It does not tie in with other app requirements and gives a incorrect answer as the initial bearing varies. Use rhumb line nav or this answer. – Tomsmith Sep 16 '14 at 08:34