8

I'm building a virtual tour application that uses custom panoramas in conjunction with Google's StreetView service. With StreetView, you can have 1 to n links at each particular location to move to, and these links are based on compass bearings.

Each panorama has an associated GPS location to six decimal places acquired via iTouchMaps. My issue is when I calculate the compass bearings from each link location, it returns a bearing that is a tiny (as in 0.00001 degree difference) from true north.

Given a map like

                       4
                       |
                       |
                       |
                       2
                       |
                       |
                       |
                       1

where each number is a location and has the following values:

1- 43.682213, -70.450696
2- 43.682194, -70.450769
4- 43.682179, -70.450837

What I'd like is a method to determine programatically that the bearing from 1 to 2 and 2 to 4 is 70 degrees, and 2 to 1 is 250 degrees. Using the code from here as a starting point, I wrote the following function

//starting lat/long along with converting degrees to radius
    var endLat = location.lat();  //for rhumb calc
    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
      var startLat = data[i].lat;
      var startLong = data[i].lon;

      //get the delta values between start and end coordinates
      var dLong = endLong - startLong;

      //calculate rhumb bearing to
      var dPhi = Math.log(Math.tan(endLat/2+Math.PI/4)/Math.tan(startLat/2+Math.PI/4));
      if (Math.abs(dLong) > Math.PI ) dLong = dLong > 0 ? -(2*Math.PI-dLong) : (2*Math.PI+dLong);
      var bearing = (toDeg(Math.atan(dLong, dPhi)) + 360) % 360;

Using the above function and substituting

location for position 2
data[0] for position 1
data[1] for position 4

I get the bearings

359.9958174081038 degrees to 4 (should be 70 degrees)

and

0.0038961130024404156 degrees to 1 (should be 250)

Why is this function returning such different values than expected?

2
  • 1
    The use of the spherical Mercator projection for the calculation is interesting. It should be fine for such short distances among points away from the poles, but why didn't you just use the (more generally correct) formula for bearing given earlier on that same Web page?
    – whuber
    Commented Jul 10, 2012 at 16:39
  • @MerseyViking and pacofvf's answers seem to be the best solution, you needed to use radians in this case
    – mekk33
    Commented Dec 19, 2014 at 13:57

2 Answers 2

18

To save you some time here is @MerseyViking answer in javascript:

function radians(n) {
  return n * (Math.PI / 180);
}
function degrees(n) {
  return n * (180 / Math.PI);
}

function getBearing(startLat,startLong,endLat,endLong){
  startLat = radians(startLat);
  startLong = radians(startLong);
  endLat = radians(endLat);
  endLong = radians(endLong);

  var dLong = endLong - startLong;

  var dPhi = Math.log(Math.tan(endLat/2.0+Math.PI/4.0)/Math.tan(startLat/2.0+Math.PI/4.0));
  if (Math.abs(dLong) > Math.PI){
    if (dLong > 0.0)
       dLong = -(2.0 * Math.PI - dLong);
    else
       dLong = (2.0 * Math.PI + dLong);
  }

  return (degrees(Math.atan2(dLong, dPhi)) + 360.0) % 360.0;
}
14

It looks to me like you need to perform the trigonometry in radians not degrees. You use a function toDeg() so presumably you have one called toRad() (or possibly fromDeg() if you're odd). Call that function with your latitude and longitude values before the calculations, and you should be set.

Edit

I just tried this in Python (the syntax isn't dissimilar to JavaScript) using radians and your coordinates:

startLat = math.radians(43.682213)
startLong = math.radians(-70.450696)
endLat = math.radians(43.682194)
endLong = math.radians(-70.450769)

dLong = endLong - startLong

dPhi = math.log(math.tan(endLat/2.0+math.pi/4.0)/math.tan(startLat/2.0+math.pi/4.0))
if abs(dLong) > math.pi:
     if dLong > 0.0:
         dLong = -(2.0 * math.pi - dLong)
     else:
         dLong = (2.0 * math.pi + dLong)

bearing = (math.degrees(math.atan2(dLong, dPhi)) + 360.0) % 360.0;

print(bearing)

and I get 250.20613449 as expected.

2
  • 3
    The solution was to use the Math.atan2 function, rather the straight atan. Also, I had my start and end locations flipped.
    – Jason
    Commented Jul 10, 2012 at 17:07
  • I have a similar problem. I am tracking the user's location based on GPS readings. The GPS reading gives me a "course" value, which determines the direction due north in degrees. When I calculate the difference of angle between two coordinates, things get ugly if the user moves from 0 to 359 degree. In real time, it could have been a 1 degree change, but my code is giving 359 degree change. Any ideas?
    – devdoe
    Commented Feb 28, 2017 at 14:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.