Calculate angle between two Latitude/Longitude points

Viewed 98965

Is there a way to calculate angle between two Latitude/Longitude points?

What I am trying to achieve is to know where the user is heading. For example, user is heading North, South,.... South-East, etc.

But I have only two points (Lng/Ltd)

Thx

19 Answers

In The Javascript, I create a function name angleFromCoordinate in which i pass two lat/lng. This function will return angel between that two lat/lng

function angleFromCoordinate(lat1,lon1,lat2,lon2) {
    var p1 = {
        x: lat1,
        y: lon1
    };

    var p2 = {
        x: lat2,
        y: lon2
    };
    // angle in radians
    var angleRadians = Math.atan2(p2.y - p1.y, p2.x - p1.x);
    // angle in degrees
    var angleDeg = Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180 / Math.PI;
    console.log(angleDeg);
    return angleDeg;
}

Working Code Snippet

function angleFromCoordinate(lat1,lon1,lat2,lon2) {
    var p1 = {
        x: lat1,
        y: lon1
    };

    var p2 = {
        x: lat2,
        y: lon2
    };
    // angle in radians
    var angleRadians = Math.atan2(p2.y - p1.y, p2.x - p1.x);
    // angle in degrees
    var angleDeg = Math.atan2(p2.y - p1.y, p2.x - p1.x) * 180 / Math.PI;

    document.getElementById('rotation').innerHTML ="Rotation : "+ angleDeg;
    return angleDeg;
    
}
     angleFromCoordinate(37.330604,-122.028947,37.3322109,-122.0329665);
<html>
<p id="rotation">Rotation : </p>
</html>

If your are using google maps(Android), there is an easy way - Use SphericalUtil

double angle = SphericalUtil.computeHeading(fromLatLng, toLatLng);

Consider we have 2 points and its lat and lng Then create its Latlng object

LatLng latlng = new LatLng(latValue, lngValue);

After getting Latlng of 2 points, use sperical util to get angle

//import com.google.maps.android.SphericalUtil;
double sphericalValue = SphericalUtil.computeHeading(latLng1, latLng2);

SpericalValue is the angle. Consider you have a car icon and turn it accordingly to the direction its going. Here its from latLng1 to latLng2 then

Bitmap vehiclePin = rotateIconBitmap(sphericalValue);
mMap.addMarker(new MarkerOptions().anchor(0.5f, 0.5f).position(latLng2))
               .setIcon(BitmapDescriptorFactory.fromBitmap(vehiclePin));

use the method below to rotate

    Bitmap rotateIconBitmap(double angle) {
       Bitmap source = BitmapFactory.decodeResource(getResources(), 
                                          R.drawable.ic_vehicle_say_car);
       Matrix matrix = new Matrix();
       matrix.postRotate((float) angle);
       return Bitmap.createBitmap(source, 0, 0, 
                    source.getWidth(), source.getHeight(), matrix, true);
    }

enter image description here

Easy way to achieve uber like rotated icons

Note:- You may have to add an offset of say 90 degree if the marker icon is not pointed to zero degree

The android sphericalutil is open source, refer it if you are using java, you can make use of it.

https://github.com/googlemaps/android-maps-utils/blob/master/library/src/com/google/maps/android/SphericalUtil.java

If you require an accurate method on an ellipsoid of revolution (i.e. WGS 84), the algorithms get really heavy. You may benefit from GeographicLib, which has been implemented in C/C++, Java, JavaScript, Python, Matlab/Octave, and others.

For the question, there is either a geodesic or a rhumb line between the first and second point.

Geodesic

A geodesic is the shortest path between two points on a curved surface. It is the most common interpretation of where a "user is heading" (from the question), since it is the shortest and most direct. An inverse geodesic calculation can be solved using GeodSolve. You can also use the online interface. This tool has the input/output:

lat1 lon1 lat2 lon2azi1 azi2 s12

Where lat1 lon1 is the coordinate pair for the first point, and lat2 lon2 is the coordinate pair for the second point. All units are in degrees (not radians). The result, azi1 or α1, is the azimuth (a.k.a. bearing) from the start point, given in degrees clockwise from north. The second azimuth is at the second point, because the angle between the two points along a geodesic is not constant. And s12 is the distance between the two points, in metres, with an accuracy of 15 nm.

Rhumb line

A rhumb line connects two coordinate points with a constant azimuth (or bearing). An inverse rhumb line calculation can be solved using RhumbSolve. You can also use the online interface. This tool has the input/output:

lat1 lon1 lat2 lon2azi12 s12

These parameters are the same as GeodSolve, except that azi12 is a constant angle between the points.

Considering Nayanesh Gupte's answer and its comments. I've changed some part of the code and wrote it in PHP.

  • latitude and longitude have been converted to radians inside the function.

Here is the function:

function angleFromCoordinate($lat1, $long1, $lat2, $long2) {

    $lat1 = deg2rad($lat1);
    $lat2 = deg2rad($lat2);
    $long1 = deg2rad($long1);
    $long2 = deg2rad($long2);

    $dLon = $long2 - $long1;

    $y = sin($dLon) * cos($lat2);
    $x = cos($lat1) * sin($lat2) - sin($lat1) * cos($lat2) * cos($dLon);

    $brng = atan2($y, $x);

    $brng = $brng * 180 / pi();
    $brng = fmod($brng + 360, 360);

    return $brng;
}

With javascript, just use Turf:

var point1 = turf.point([-75.343, 39.984]);
var point2 = turf.point([-75.534, 39.123]);

var bearing = turf.bearing(point1, point2);

Here's how calculated the bearing in Java using Math. In my case, I have lat-long in degrees so I converted them in radians before using them. If you have lat-long in radian you might wanna remove those four lines. I have also commented the formulas that I used to calculate the bearing.

    public float calculateBearing(float currentLat, float currentLon, float destinationLat, float destinationLon) {

    //if lat,lon are in degrees convert them to radians (in my case they are in degrees)
    currentLat = (float) Math.toRadians(currentLat);
    currentLon = (float) Math.toRadians(currentLon);
    destinationLat = (float) Math.toRadians(destinationLat);
    destinationLon = (float) Math.toRadians(destinationLon);

    // a -> current | b -> destination
    // ‘L’ be the longitude in radians,
    // ‘θ’ be latitude in radians,
    // ‘β‘ be Bearing.
    // ∆ be the difference (b - a)

    // ∆L = L b - L a
    // β = atan2(X,Y)
    // X = cos θb * sin ∆L
    // Y = cos θa * sin θb – sin θa * cos θb * cos ∆L
    
    double X, Y;
    X = Math.cos(destinationLat) * Math.sin(destinationLon - currentLon);
    Y = (Math.cos(currentLat) * Math.sin(destinationLat)) -
            (Math.sin(currentLat) * Math.cos(destinationLat) * Math.cos(destinationLon - currentLon));


    float radianBearing = (float) Math.atan2(X, Y);
    //converting bearing in radian to degrees
    return (float) Math.toDegrees(radianBearing );
}

I studied and followed the formulas provided here https://www.igismap.com/formula-to-find-bearing-or-heading-angle-between-two-points-latitude-longitude/. You can further read this for better understanding.

For testing, you can visit this https://www.igismap.com/map-tool/bearing-angle. It has a map where you can select your starting coordinates (A) and end coordinates (B) to get the Bearing.

Here is a javascript version that actually works, taken from leaflet.geometryutil.js

function bearing (latlng1, latlng2) {
  const rad = Math.PI / 180
  const lat1 = latlng1.lat * rad
  const lat2 = latlng2.lat * rad
  const lon1 = latlng1.lng * rad
  const lon2 = latlng2.lng * rad
  const y = Math.sin(lon2 - lon1) * Math.cos(lat2)
  const x = Math.cos(lat1) * Math.sin(lat2) -
      Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1)

  const bearing = ((Math.atan2(y, x) * 180 / Math.PI) + 360) % 360
  return bearing >= 180 ? bearing - 360 : bearing
}
Related