Reputation: 812
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
Upvotes: 48
Views: 117884
Reputation: 6224
The usual reference https://www.igismap.com/map-tool/bearing-angle gives negative values. But as per https://youtu.be/mMrf1Wn6-XA?t=112 it should be counted clockwise from N.
if bearing_angle < 0: #count it clockwise
# Normalize the bearing angle to a value between 0° and 360°
bearing_angle = (bearing_angle + 360) % 360
That's the only change from @nbeuchats answer (https://stackoverflow.com/a/45424986/429476)
Updated answer;
def calculate_bearing_angle(point1, point2):
Calculate the bearing angle between two points on the Earth's surface.
Parameters:
- point1: tuple of (latitude, longitude) for the first point.
- point2: tuple of (latitude, longitude) for the second point.
Returns:
- bearing angle in degrees.
"""
# Convert degrees to radians
lat1 = math.radians(point1[0])
lon1 = math.radians(point1[1])
lat2 = math.radians(point2[0])
lon2 = math.radians(point2[1])
# Calculate the difference in longitudes
delta_lon = lon2 - lon1
# Calculate the bearing angle
x = math.atan2(math.sin(delta_lon) * math.cos(lat2),
math.cos(lat1) * math.sin(lat2) - math.sin(lat1) * math.cos(lat2) * math.cos(delta_lon))
#log.debug(f"bearing angle in radians={x}")
# Convert the bearing angle from radians to degrees
bearing_angle = math.degrees(x)
log.debug(f"bearing angle without normalisation={bearing_angle}")
if bearing_angle < 0: #count it clockwise see https://youtu.be/mMrf1Wn6-XA?t=112
# Normalize the bearing angle to a value between 0° and 360°
bearing_angle = (bearing_angle + 360) % 360
return bearing_angle
Testing it
receiver_loc = (30.1225, -101.09655555555555)
transmitter_loc = (29.774166666666666, -101.16900000000001)
calculate_bearing_angle(receiver_loc,transmitter_loc)
output
190.2334434245182
Putting it here to test http://www.geomidpoint.com/destination/
we got the transmitter back
However if we use the value from https://www.igismap.com/map-tool/bearing-angle, it gives as -169.767 which does not work with the above tool (should be between (0 to 360)
So the code above seems right
Upvotes: 0
Reputation: 119
private fun angleFromCoordinate(
lat1: Double, long1: Double, lat2: Double,
long2: Double
): Double {
val dLon = long2 - long1
val y = sin(dLon) * cos(lat2)
val x = cos(lat1) * sin(lat2) - (sin(lat1)
* cos(lat2) * cos(dLon))
var brng = atan2(y, x)
brng = Math.toDegrees(brng)
brng = (brng + 360) % 360
brng = 360 - brng
return brng
}
on kotlin
Upvotes: 0
Reputation: 7091
Based on Nayanesh Gupte's answer, here is a Python implementation of how to calculate the angle between two points defined by their latitudes and longitudes if anyone needs it:
import math
def angleFromCoordinate(lat1, long1, lat2, long2):
dLon = (long2 - long1)
y = math.sin(dLon) * math.cos(lat2)
x = math.cos(lat1) * math.sin(lat2) - math.sin(lat1) * math.cos(lat2) * math.cos(dLon)
brng = math.atan2(y, x)
brng = math.degrees(brng)
brng = (brng + 360) % 360
brng = 360 - brng # count degrees clockwise - remove to make counter-clockwise
return brng
Where an angle of 0 degrees indicates a northward heading.
Upvotes: 9
Reputation: 3444
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
}
Upvotes: 0
Reputation: 61
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.
Upvotes: 0
Reputation: 2873
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);
Upvotes: 0
Reputation: 3046
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);
}
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.
Upvotes: 4
Reputation: 2522
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>
Upvotes: 7
Reputation: 43612
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.
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 lon2 → azi1 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.
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 lon2 → azi12 s12
These parameters are the same as GeodSolve, except that azi12 is a constant angle between the points.
Upvotes: 2
Reputation: 165
function calculateAngle(lat, lng) {
var checkLengthInterval = 2;
// Calculate Angle
//If ObjFeed == [] add first object.
if (ObjFeed.length == 0) {
ObjFeed.push({ 'lat': lat, 'lng': lng });
} else {
// Get last object from list to calculate angle betwn last and latest.
var tempNode = ObjFeed[ObjFeed.length - 1];
// If last lat and lng is same as current it will always return 0 angle.so only push lat lng in obj which is diff than last one.
if (!(tempNode.lat == lat && tempNode.lng == lng)) {
ObjFeed.push({ 'lat': lat, 'lng': lng });
} else {
console.log('exact match for lat lng');
}
}
// this is for to keep only few objects in the list and remove other
if (ObjFeed.length >= checkLengthInterval) {
// calculating angle only if previous data point is available
ObjFeed = ObjFeed.slice(-1 * checkLengthInterval); // remove all items in array except last two
var point1 = ObjFeed[ObjFeed.length - checkLengthInterval];
var point2 = ObjFeed[ObjFeed.length - 1];
console.log('previous point1', point1);
console.log('next point2', point2);
var dLng = (point2.lng - point1.lng);
var dLat = (point2.lat - point1.lat);
dLng = dLng * 10000;
dLat = dLat * 10000;
var dlat_by_dlan = 0;
try {
dlat_by_dlan = dLng / dLat;
} catch (err) {
dlat_by_dlan = NaN;
console.log('Exception: dLat == 0');
}
var angleDegreeBearing = 0, angleBearingRad = 0;
angleBearingRad = Math.atan(dlat_by_dlan);
angleDegreeBearing = angleBearingRad * 180 / Math.PI;
if (dLat < 0 && dLng < 0) {
angleDegreeBearing = angleDegreeBearing + 180;
} else if (dLat < 0 && dLng > 0) {
angleDegreeBearing = angleDegreeBearing + 180;
} else if (dLat == 0 && dLng == 0) {
angleDegreeBearing = prevVechicleAngle;
} else if (dlat_by_dlan == NaN) {
angleDegreeBearing = prevVechicleAngle;
}
console.log('angleDegreeBearing', angleDegreeBearing);
} else {
// setting up default angle to 0 if previous data point is not available to calculate actual anglle
console.log('feedArray default angle 0');
angleDegreeBearing = 0;
}
prevVechicleAngle = angleDegreeBearing;
return angleDegreeBearing;
}
Upvotes: 2
Reputation: 2775
using this referance to calculate Angle:
private double angleFromCoordinate(double lat1, double long1, double lat2,
double long2) {
double dLon = (long2 - long1);
double y = Math.sin(dLon) * Math.cos(lat2);
double x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1)
* Math.cos(lat2) * Math.cos(dLon);
double brng = Math.atan2(y, x);
brng = Math.toDegrees(brng);
brng = (brng + 360) % 360;
brng = 360 - brng; // count degrees counter-clockwise - remove to make clockwise
return brng;
}
Upvotes: 65
Reputation: 4117
The general formula for calculating the angle(bearing) between two points is as follows:
θ = atan2(sin(Δlong)*cos(lat2), cos(lat1)*sin(lat2) − sin(lat1)*cos(lat2)*cos(Δlong))
Note that the angle(θ) should be converted to radians before using this formula and Δlong = long2 - long1.
atan2 is a common function found in almost all programming languages (mostly in the Math package/library). Usually there are also functions for conversion between degrees and radians(also in the Math package/library).
Remember that atan2 returns values in the range of -π ... +π, to convert the result to a compass bearing, you need to multiply θ by 180/π then use (θ+360) % 360, where % is modulus division operation returning the remainder of the division.
The following link is a good resource for formulas involving latitudes and longitudes. They also provide Javascript implementation of their formulas. In fact, this answer is based on the information from this page:
http://www.yourhomenow.com/house/haversine.html
Upvotes: 15
Reputation: 5136
Considering Nayanesh Gupte's answer and its comments. I've changed some part of the code and wrote it in PHP
.
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;
}
Upvotes: 1
Reputation: 1441
To provide heading you have to compute bearing.
To understand bearing read this article.
According to this article (section bearing) the formula is :
θ = atan2( sin Δλ ⋅ cos φ2 , cos φ1 ⋅ sin φ2 − sin φ1 ⋅ cos φ2 ⋅ cos Δλ )
where φ1, λ1 is the start point,
φ2, λ2 the end point,
Δλ is the difference in longitude`
Here's a sample on how to compute the angle (in degrees) between two points expressed in Lat/Lon. (done in C#)
Let's say Point
is a simple class with two double
attributes X (for longitude) and Y (for latitude).
public double ComputeBearing(Point start,Point end)
{
var φ1 = start.Y; //latitude 1
var λ1 = start.X; //longitude 1
var φ2 = end.Y; //latitude 2
var λ2 = end.X; //longitude 2
var y = Math.Sin(this.degreeToRadian(λ2 - λ1)) * Math.Cos(this.degreeToRadian(φ2));
var x = Math.Cos(this.degreeToRadian(φ1)) * Math.Sin(this.degreeToRadian(φ2)) - Math.Sin(this.degreeToRadian(φ1)) * Math.Cos(this.degreeToRadian(φ2)) * Math.Cos(this.degreeToRadian(λ2 - λ1));
var θ = Math.Atan2(y, x);
θ = this.radianToDegree(θ);
return θ;
}
Using the following methods :
public double degreeToRadian(double angle)
{
return Math.PI * angle / 180.0;
}
public double radianToDegree(double angle)
{
return angle * (180.0 / Math.PI);
}
By using ComputeBearing
you will easily get an angle expressed in degrees easily usable as heading
Upvotes: 3
Reputation: 658
In case someone need PHP
code for this functionality:
/**
* Calculate angle between 2 given latLng
* @param float $lat1
* @param float $lat2
* @param float $lng1
* @param float $lng2
* @return integer
*/
function angle($lat1, $lat2, $lng1, $lng2) {
$dLon = $lng2 - $lng1;
$y = sin($dLon) * cos($lat2);
$x = cos($lat1) * sin($lat2) - sin($lat1) * cos($lat2) * cos($dLon);
return 360 - ((rad2deg(atan2($y, $x)) + 360) % 360);
}
Upvotes: 1
Reputation: 69958
For those who use C/C++, below is the tested code:
static const auto PI = 3.14159265358979323846, diameterOfEarthMeters = 6371.0 * 2 * 1000;
double degreeToRadian (double degree) { return (degree * PI / 180); };
double radianToDegree (double radian) { return (radian * 180 / PI); };
double CoordinatesToAngle (const double latitude1,
const double longitude1,
const double latitude2,
const double longitude2)
{
const auto longitudeDifferenceRadians = degreeToRadian(longitude2 - longitude1);
auto latitude1Radian = degreeToRadian(latitude1),
latitude2Radian = degreeToRadian(latitude2);
const auto x = std::cos(latitude1Radian) * std::sin(latitude2Radian) -
std::sin(latitude1Radian) * std::cos(latitude2Radian) *
std::cos(longitudeDifferenceRadians);
const auto y = std::sin(longitudeDifferenceRadians) * std::cos(latitude2Radian);
return radianToDegree(std::atan2(y, x));
}
double CoordinatesToMeters (const double latitude1,
const double longitude1,
const double latitude2,
const double longitude2)
{
auto latitude1Radian = degreeToRadian(latitude1),
longitude1Radian = degreeToRadian(longitude1),
latitude2Radian = degreeToRadian(latitude2),
longitude2Radian = degreeToRadian(longitude2);
auto x = std::sin((latitude2Radian - latitude1Radian) / 2),
y = std::sin((longitude2Radian - longitude1Radian) / 2);
return diameterOfEarthMeters *
std::asin(std::sqrt((x * x) +
(std::cos(latitude1Radian) * std::cos(latitude2Radian) * y * y)));
}
Upvotes: 1
Reputation: 15
Make sure its a rhumb line bearing NOT a great circle bearing as the initial bearing changes according to distance
double angle= Math.min((pbearingf-tbearingf) < 0 ? pbearingf-tbearingf+360:pbearingf-tbearingf, (tbearingf-pbearingf)<0?tbearingf-pbearingf+360:tbearingf-pbearingf);
Upvotes: 0
Reputation: 1756
Sample javascript code if the distance between points is less -
brng = Math.atan2(newLat - oldLat, newLong - oldLong);
brng = brng * (180 / Math.PI);
brng = (brng + 360) % 360;
brng = 360 - brng;
Upvotes: 4
Reputation: 725
You just can use the google maps computeHeading:
var point1 = new google.maps.LatLng(lat1, lng1);
var point2 = new google.maps.LatLng(lat2, lng2);
var heading = google.maps.geometry.spherical.computeHeading(point1,point2);
Upvotes: 19
Reputation: 11
Maybe this is what you want:
cos(say) = (cosd(90-lat(1))) * (cos(90-lat(2)))
+ (sin(90-lat(1))) * (sind(90-lat(2)) * (cosd(abs(Landa(2)-landa(1)))));
Upvotes: 1
Reputation: 78306
I think you want the calculations for the Great Circle bearing.
Upvotes: 1