Find the final latitude longitude after a movement on the globe - javascript

I am using the Haversine formula to calculate the distance from two latitude-longitude pairs.
function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) {
var R = 6371; // Radius of the earth in km
var dLat = deg2rad(lat2-lat1);
var dLon = deg2rad(lon2-lon1);
var lat1 = deg2rad(lat1);
var lat2 = deg2rad(lat2);
var a =
Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.sin(dLon/2) * Math.sin(dLon/2) *
Math.cos(lat1) * Math.cos(lat2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
return d;
}
Given a starting point (lat1, lat2), the distance required to move on a straight line and the angle, I need to determine the endpoint (as in lat2 and lon2).
See my attempt below:
function getFinalLatLon(lat1, lon1, distance, angle) {
var R = 6371; // Radius of the earth in km
var c = distance/R;
// Math.atan2(Math.sqrt(a), Math.sqrt(1-a)) = c/2
var a = // stuck here
// looking for this part of the code
return [lat2, lon2];
}

If you are moving horizontally, you can increment the longitude by distance / (R * cos(lat)). No atan needed.
EDIT: Since you wanted a formula for the general case, consider the following geometric derivation:
Front view:
Side view:
Entire setup:
Notes:
r is the unit vector of your starting position, and s is the endpoint.
a, b, c are intermediate vectors to aid calculation.
(θ, φ) are the (lat, long) coordinates.
γ is the bearing of the direction you are going to travel in.
δ is the angle travelled through (distance / radius R = 6400000m).
We need a, b to be perpendicular to r and also a aligned with North. This gives:
c is given by (simple trigonometry):
And thus we get s (through some very tedious algebra):
Now we can calculate the final (lat, long) coordinates of s using:
Code:
function deg2rad(deg) { return deg * (Math.PI / 180.0) }
function rad2deg(rad) { return rad * (180.0 / Math.PI) }
function getFinalLatLong(lat1, long1, distance, angle, radius) {
// calculate angles
var delta = distance / radius,
theta = deg2rad(lat1),
phi = deg2rad(long1),
gamma = deg2rad(angle);
// calculate sines and cosines
var c_theta = Math.cos(theta), s_theta = Math.sin(theta);
var c_phi = Math.cos(phi) , s_phi = Math.sin(phi) ;
var c_delta = Math.cos(delta), s_delta = Math.sin(delta);
var c_gamma = Math.cos(gamma), s_gamma = Math.sin(gamma);
// calculate end vector
var x = c_delta * c_theta * c_phi - s_delta * (s_theta * c_phi * c_gamma + s_phi * s_gamma);
var y = c_delta * c_theta * s_phi - s_delta * (s_theta * s_phi * c_gamma - c_phi * s_gamma);
var z = s_delta * c_theta * c_gamma + c_delta * s_theta;
// calculate end lat long
var theta2 = Math.asin(z), phi2 = Math.atan2(y, x);
return [rad2deg(theta2), rad2deg(phi2)];
}
Test cases:
Input (lat, long) = (45, 0), angle = 0, distance = radius * deg2rad(90) => (45, 180) (as I said before)
Input (lat, long) = (0, 0), angle = 90, distance = radius * deg2rad(90) => (0, 90) (as expected - start at equator, travel east by 90 longitude)
Input (lat, long) = (54, 29), angle = 36, distance = radius * deg2rad(360) => (54, 29) (as expected - start at any random position and go full-circle in any direction)
Interesting case: input (lat, long) = (30, 0), everything else same. => (0, 90) (we expected (30, 90)? - not starting at equator, travel by 90 degrees to North)
The reason for this is that 90 degrees to North is not East (if you're not at the equator)! This diagram should show why:
As you can see, the path of movement at 90 degrees to the North is not in the direction of East.

I just found a similar question here and I followed the solution to come up with a function that works for my case.
Hope it helps someone else:
function getFinalLatLon(lat1, lon1, distance, angle){
function deg2rad(deg) {
return deg * (Math.PI/180)
}
// dy = R*sin(theta)
var dy = distance * Math.sin(deg2rad(angle))
var delta_latitude = dy/110574
// One degree of latitude on the Earth's surface equals (110574 meters
delta_latitude = parseFloat(delta_latitude.toFixed(6));
// final latitude = start_latitude + delta_latitude
var lat2 = lat1 + delta_latitude
// dx = R*cos(theta)
var dx = distance * Math.cos(deg2rad(angle))
// One degree of longitude equals 111321 meters (at the equator)
var delta_longitude = dx/(111321*Math.cos(deg2rad(lat1)))
delta_longitude = parseFloat(delta_longitude.toFixed(6));
// final longitude = start_longitude + delta_longitude
var lon2 = lon1 + delta_longitude
return [lat2, lon2];
}
The angle is 0 degrees for a horizontal move. You can switch that as you wish. If someone is moving north that would be 90 deg. 135 degrees for north west and so on...

Related

How to calculate the lat/lng of a point a certain distance away from another using Nodejs or javascript

I need to find a square area using a latitude and longitude(x,y) as the following figure
I need to get all the other 3 corner latitude and longitude by adding 10kms to each side. I am using Node.js/javascript to implement this.
Referring to the below geometry diagram, the only co-ordinate you need to calculate is - (x2, y2) and rest of the two co-ordinate you can calculate using current long, lat - (x1, y1) and computed - (x2, y2)
So basically you need a function which will take current lat, long i.e. - (x1, y1), a distance which is √2 * 10km in your example and bearing angle to point (x2, y2) which at 135 degrees.
let llFromDistance = function(latitude, longitude, distance, bearing) {
// taken from: https://stackoverflow.com/a/46410871/13549
// distance in KM, bearing in degrees
const R = 6378.1; // Radius of the Earth
const brng = bearing * Math.PI / 180; // Convert bearing to radian
let lat = latitude * Math.PI / 180; // Current coords to radians
let lon = longitude * Math.PI / 180;
// Do the math magic
lat = Math.asin(Math.sin(lat) * Math.cos(distance / R) + Math.cos(lat) * Math.sin(distance / R) * Math.cos(brng));
lon += Math.atan2(Math.sin(brng) * Math.sin(distance / R) * Math.cos(lat), Math.cos(distance / R) - Math.sin(lat) * Math.sin(lat));
// Coords back to degrees and return
return [(lat * 180 / Math.PI), (lon * 180 / Math.PI)];
}
console.log(llFromDistance(19.0659115, 72.8574557, Math.sqrt(2)*10, 135))
Here's a function I've used - not sure of it's usefulness when close to the poles though
const fn = (latitude, longitude, distanceInKm, bearingInDegrees) => {
const R = 6378.1;
const dr = Math.PI / 180;
const bearing = bearingInDegrees * dr;
let lat = latitude * dr;
let lon = longitude * dr;
lat = Math.asin(Math.sin(lat) * Math.cos(distanceInKm / R) + Math.cos(lat) * Math.sin(distanceInKm / R) * Math.cos(bearing));
lon += Math.atan2(
Math.sin(bearing) * Math.sin(distanceInKm / R) * Math.cos(lat),
Math.cos(distanceInKm / R) - Math.sin(lat) * Math.sin(lat)
);
lat /= dr;
lon /= dr;
return {lat, lon};
}
so, the points would be
fn(y, x, 10, 90), // top right
fn(y, x, 10 * Math.sqrt(2), 135), // bottom right (Pythagoras rules!)
fn(y, x, 10, 180) // bottom left

I cannot get the same accuracy as Google maps when it comes to distance?

I am developing an app which calculate the distance between 2 points. I cannot use the Google Maps API.
I have found the coordinates for each of the markers in the map below.
I am then using the haversine formula to calculate the distance between each points.
e.g. 1 -> 2, 2 -> 3, 3 -> 4... etc up to the final point.
I add up these distances to retrieve the total distance for the route.
The problem is Google maps says it is 950-1000 meters, but my app says the length is 1150-1200 meters. I have tried adding in more coordinates, removing coordinates, but I am still getting approximately 200 meters longer route.
Out of curiosity I calculated the distance between the start and end point (the 2 green stars) and this matched the Google Maps distance (998 metres to be exact).
Does this mean Google Maps calculates its distances without the consideration of roads / paths etc.
Here is my code:
var coordinates = [
[1,51.465097,-3.170893,1,0],
[2,51.465526,-3.170714,0,0],
[3,51.465853,-3.170526,0,0],
[4,51.466168,-3.170338,0,0],
[5,51.466305,-3.170236,0,0],
[6,51.466534,-3.170157,0,0],
[7,51.466798,-3.170159,0,0],
[8,51.467042,-3.170232,0,0],
[9,51.467506,-3.170580,0,0],
[10,51.468076,-3.171532,0,0],
[11,51.468863,-3.172170,0,0],
[12,51.469284,-3.172841,0,0],
[13,51.469910,-3.174732,0,0],
[14,51.470037,-3.174930,0,0],
[15,51.470350,-3.175091,0,0],
[16,51.472447,-3.176151,1,0]
];
function distanceBetweenCoordinates() //calculates the distance between each of the coordinates
{
for (var i=0; i<coordinates.length-1; i++)
{
var firstClosestPoint = [0,0,6371];
var secondClosestPoint = [0,0,6371];
var lng1 = (coordinates[i][1]);
var lat1 = (coordinates[i][2]);
var lng2 = (coordinates[i+1][2]);
var lat2 = (coordinates[i+1][2]);
var d = haversine(lat1, lng1, lat2, lng2);
routeLength = routeLength + d;
}
return distanceBetweenCoordinatesArray; //returns the array which stores the 2 points and the distance between the 2 points
}
EDIT
Here is my haversine forumla to calculate the distance between 2 points:
Source: here
Number.prototype.toRad = function() //to rad function which is used by the haversine formula
{
return this * Math.PI / 180;
}
function haversine(lat1, lng1, lat2, lng2) { //haversine foruma which is used to calculate the distance between 2 coordinates
lon1 = lng1;
lon2 = lng2;
var R = 6371000; // metres
var a = lat1.toRad();
var b = lat2.toRad();
var c = (lat2-lat1).toRad();
var d = (lon2-lon1).toRad();
var a = Math.sin(c/2) * Math.sin(c/2) +
Math.cos(a) * Math.cos(b) *
Math.sin(d/2) * Math.sin(d/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
return d;
}
If I have correctly entered your start and end points, this implementation of the haversine formula (which I have tested in the real world) produces a distance of 895m (straight line).
var lt = 51.472447;
var lt1 = 51.465097;
var ln = -3.176151;
var ln1 = -3.170893;
var dLat = (lt - lt1) * Math.PI / 180;
var dLon = (ln - ln1) * Math.PI / 180;
var a = 0.5 - Math.cos(dLat) / 2 + Math.cos(lt1 * Math.PI / 180) * Math.cos(lt * Math.PI / 180) * (1 - Math.cos(dLon)) / 2;
d = Math.round(6371000 * 2 * Math.asin(Math.sqrt(a)));
$('#distance').html(d);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="distance"></div>

Haversine Formula still not working

I used harversine formula to calculate if points are inside/outside of circle, but still it response that points are inside of the circle, it must be outside the circle. Take a look at my code.
var xp = 7.070562277980709; // point_lat
var yp = 125.60755640475463; // point_long
var radius = 63.942490126300555; // radius
var xc = 7.070479805752504; // circle_lat
var yc = 125.60851603754577; // circle_lon
var r = radius / 1000; // convert meter to kilometer
var dlat = (xp - xc) * (Math.PI / 180);
var dlng = (yp - yc) * (Math.PI / 180);
var a = ((Math.sin(dlat / 2)) * (Math.sin(dlat / 2)));
var b = ((Math.cos(xp * (Math.PI / 180))) * (Math.cos(xc * (Math.PI / 180))));
var c = ((Math.sin(dlng / 2)) * (Math.sin(dlng / 2)));
var d = a + (b * c);
var e = 2 * Math.atan2(Math.sqrt(d), Math.sqrt(1 - d));
var f = r * e;
if (f < r) {
alert('INSIDE');
} else if(f > r) {
alert('OUTSIDE');
}
this function should alert me "OUTSIDE". Whats wrong with this code?? Thanks for your help.
All points are given by google map. And the default unit of radius of google map is meter, thats why I converted it to kilometer.
In the formula you are using, the radius is the Earth's radius. The formula gives the great circle (i.e. shortest) distance between two points on the Earth's surface.
Using 63.942490126300555 for the radius, then dividing it by 1,000 gives an f of 0.0000010667905687961212 km or 0.001 m or 1 mm.
Substituting a more appropriate value for r (e.g. 6,371) gives an f of 0.10629117978319975 km, or 106.291 metres.
Calculating it another way, since the coordinates are close to the equator, you can work out the distances as fractions of the Earth's circumference and use plain trigonometry.
Using a circumference of 40,000 km, the difference in latitude is 0.0000824722282057877 degrees, which is:
dLat = 40,000 km * 0.0000824722282057877 / 360
or
dLat = 0.009163580911754189 km
= 9.164 m
and for longitude:
dLong = 0.0009596327911367553;
dist = 40000 * 0.0009596327911367553 / 360;
= 0.10662586568186169 km
= 106.626 m
And a bit of basic trig:
dist = sqrt(9.164^2 + 106.626^2)
= 0.10629117978319975 km
= 106.291 m
which is pretty close to the other result. You can use that method quite successfully for small distances, just multiply the distance derived from the difference in longitude by the cosine of the latitude (since angular distances get shorter as you get closer to the pole).
My comment was just a dig at your spelling of "metre". :-)
Edit
Here's a function to return the great circle distance based on the Haversine formula at Movable Type Scripts:
// Return the great circle distance between two points on
// the Earth's surface given their latitude and longitude in
// decimal degrees. Only approximate.
function greatCircleDistance(lat0, lon0, lat1, lon1) {
// Approximate Earth radius
var earthRadius = 6.371e3; // 6,371,000 m
// Convert args to radians
for (var i=arguments.length; i; ) {
arguments[--i] = arguments[i] * Math.PI/180;
}
// Do calculation
var dLat = lat1 - lat0;
var dLon = lon1 - lon0;
var a = Math.pow(Math.sin(dLat/2),2) +
Math.cos(lat0) * Math.cos(lat1) *
Math.pow(Math.sin(dLon/2),2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return earthRadius * c;
}
var xp = 7.070562277980709; // point_lat
var yp = 125.60755640475463; // point_long
var xc = 7.070479805752504; // circle_lat
var yc = 125.60851603754577; // circle_lon
console.log(greatCircleDistance(xp, yp, xc, yc)); // 0.10629117978188952
So you can do:
if ( greatCircleDistance(xp, yp, xc, yc) > 63) {
console.log('outside');
} else {
console.log('inside');
}
It is obvious that your Harversine calculation is correct and it is equal to the result returned by google map api static method computeDistanceBetween. Here is a fiddle.
However your logic implies that both the radius of sphere and radius within which you want to check if a point exists, are same.
To get the expected output you must model your problem space as below
R : radius of sphere
p1, p2... : points(lat,long co-ordinates) on the surface of sphere
r: to check whether p2 lies within distance 'r' of p1 where r & R in the same unit
Based on above you need to implement below logic
var e = calculate haversine of the central angle for point p1 and p2
var d = e * R;//where R is the radius of sphere, and the d would be great circle distance
if( d < r){// check whether p1 exists within r distance of p2
//point is inside
}
else{
//point is outside
}
Note that Haversine formula is not only for earth distance, rather it is for spherical body. However the correctness may differ based on size and position of the points under consideration.

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?
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;
},
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
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.

Circle radius on Lat/Lng map

I am trying to draw a circle on a CloudMade map. The center of the circle is expressed in Lat/Lng, while the radius is in meters. Here following is the JavaScript I use, but some tests seem to indictae that the conversion I'm using for the radius gives me a too short radius. Does somebody understand what I', doing wrong?
function DrawCircle (center, radius)
{
var circlePoints = Array();
with (Math)
{
var d = radius/6371000; // radians
var lat1 = (PI/180) * center.lat(); // radians
var lng1 = (PI/180) * center.lng(); // radians
for (var a = 0; a <= 360; a++)
{
var tc = (PI/180) * a;
var y = asin(sin(lat1) * cos(d) + cos(lat1) * sin(d) * cos(tc));
var dlng = atan2(sin(tc) * sin(d) * cos(lat1), cos(d) - sin(lat1) * sin(y));
var x = ((lng1 - dlng + PI) % (2 * PI)) - PI ; // MOD function
var point = new CM.LatLng(parseFloat(y * (180/PI)), parseFloat(x * (180/PI)));
circlePoints.push(point);
}
circle = new CM.Polygon(circlePoints, circleBorderColor, circleBorderWidth, circleBorderOpacity, circleFillColor, circleFillOpacity);
map.addOverlay(circle);
}
}

Categories