Jason
Jason

Reputation: 11363

Issue with calculating compass bearing between two GPS coordinates

In my webapp, have a JSON data response from a database query that includes the lat/long coordinates of 1 to n locations. I want to calculate the bearing from the data[i] location to the current position.

I've been adapting the code here, but the bearing returned is incorrect.

//starting lat/long along with converting lat to rads
var endLat = toRad(location.lat());        
var endLong = location.lng();

//loop over response, calculate new headings for links and add link to array
for(var i=0; i<data.length; i++){

  //this link's lat/long coordinates, convert lat to rads
  var startLat = toRad(data[i].lat);
  var startLong = data[i].lon;

  //get the delta values between start and end coordinates in rads
  var dLong = toRad(endLong - startLong);

  //calculate 
  var y = Math.sin(dLong)*Math.cos(endLong);
  var x = Math.cos(startLat)*Math.sin(endLat)-Math.sin(startLat)*Math.cos(endLat)*Math.cos(dLong);
  var bearing = Math.atan(y, x);
  bearing = (toDeg(bearing) + 360) % 360;

  panoLinks.push({'heading': bearing, 'description': data[i].description, 'pano': data[i].description});
}

//radian/degree conversions
function toRad(convert){
  return convert * Math.PI/180;
}

function toDeg(convert){
  return convert * 180/Math.PI;
}

Using the functions above and the values

startLat= 43.6822, converts to 0.7623982145146669 radians
startLong= -70.450769

endLat= 43.682211, converts to 0.7623984065008848 radians
endLong= -70.45070

dLong = startLong - endLong, converts to 0.0000011170107216805305 radians

results in a compass degree of

bearing= 0.000014910023935499339

which is definitely off. Where have I gone wrong?

Upvotes: 8

Views: 17340

Answers (3)

RobG
RobG

Reputation: 147383

If you want a very rough method for short distances, you can use an Earth radius of 6,378,137m (the length of the semi-major axis of the WGS84 spheroid) to calculate the sides of the triangle based on the difference in latitude and longitude. Then calculate the appropriate bearing. It will be a true bearing, but likely close enough over short distances.

You'll need to leave it up to users to work out the local magnetic declination.

e.g. for your example:

startLat  = 43.6822
startLong = -70.450769

endLat  = 43.682211
endLong = -70.45070

diff lat  = 0.000011 = 1.22m
diff long = 0.000069 = 7.68m

The end point is north and east of the start, so the bearing can be found by:

tan a = 7.68 / 1.22
    a = 81°

So the direction is about East by North.

This should probably be in a mapping and surveying thread. Once you've got the maths worked out, come here for the solution.

Edit

To convert degrees of latitude to metres, first calculate the Earth circumference at the equator (or any great circle):

c = 2πR where r = 6378137m
  = 40,075,000 (approx)

Then get the ratio of the circumference out of 360°:

dist = c * deg / 360
     = 40,075,000m * 0.000011° / 360°
     = 1.223m

For longitude, the distance narrows as the latitude approaches the pole, so the same formula is used and the result multiplied by the cosine of the latitude:

     = 40,075,000m * 0.000069° / 360° * cos(0.000011°)
     = 7.681m

The value for the Earth radius is not necessarily accurate, the Earth isn't a perfect sphere (it's an oblate spheroid, sort of pear shaped). Different approximations are used in different places for greater accuracy, but the one I've used should be good enough.

Upvotes: 3

Chaudhry Junaid
Chaudhry Junaid

Reputation: 412

This is an edit of the accepted answer with some modifications which made it work for me (mainly the use of toRad function on lat,lng values).

    var geo = {
        /**
         * Calculate the bearing between two positions as a value from 0-360
         *
         * @param lat1 - The latitude of the first position
         * @param lng1 - The longitude of the first position
         * @param lat2 - The latitude of the second position
         * @param lng2 - The longitude of the second position
         *
         * @return int - The bearing between 0 and 360
         */
        bearing : function (lat1,lng1,lat2,lng2) {
            var dLon = this._toRad(lng2-lng1);
            var y = Math.sin(dLon) * Math.cos(this._toRad(lat2));
            var x = Math.cos(this._toRad(lat1))*Math.sin(this._toRad(lat2)) - Math.sin(this._toRad(lat1))*Math.cos(this._toRad(lat2))*Math.cos(dLon);
            var brng = this._toDeg(Math.atan2(y, x));
            return ((brng + 360) % 360);
        },

       /**
         * Since not all browsers implement this we have our own utility that will
         * convert from degrees into radians
         *
         * @param deg - The degrees to be converted into radians
         * @return radians
         */
        _toRad : function(deg) {
             return deg * Math.PI / 180;
        },

        /**
         * Since not all browsers implement this we have our own utility that will
         * convert from radians into degrees
         *
         * @param rad - The radians to be converted into degrees
         * @return degrees
         */
        _toDeg : function(rad) {
            return rad * 180 / Math.PI;
        },
    };

    /** Usage **/
    var myInitialBearing = geo.bearing(0,0,45,45);

Find theory and online calculator at: http://www.movable-type.co.uk/scripts/latlong.html

Upvotes: 7

Manatok
Manatok

Reputation: 5706

Give this a try, I can't for the life of me remember where I got it though...

    /**
     * Calculate the bearing between two positions as a value from 0-360
     *
     * @param lat1 - The latitude of the first position
     * @param lng1 - The longitude of the first position
     * @param lat2 - The latitude of the second position
     * @param lng2 - The longitude of the second position
     *
     * @return int - The bearing between 0 and 360
     */
    bearing : function (lat1,lng1,lat2,lng2) {
        var dLon = (lng2-lng1);
        var y = Math.sin(dLon) * Math.cos(lat2);
        var x = Math.cos(lat1)*Math.sin(lat2) - Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
        var brng = this._toDeg(Math.atan2(y, x));
        return 360 - ((brng + 360) % 360);
    },

   /**
     * Since not all browsers implement this we have our own utility that will
     * convert from degrees into radians
     *
     * @param deg - The degrees to be converted into radians
     * @return radians
     */
    _toRad : function(deg) {
         return deg * Math.PI / 180;
    },

    /**
     * Since not all browsers implement this we have our own utility that will
     * convert from radians into degrees
     *
     * @param rad - The radians to be converted into degrees
     * @return degrees
     */
    _toDeg : function(rad) {
        return rad * 180 / Math.PI;
    },

Upvotes: 17

Related Questions