Related
I'm trying to use the Haversine Distance Formula (as found here: http://www.movable-type.co.uk/scripts/latlong.html) but I can't get it to work, please see the following code
function test() {
var lat2 = 42.741;
var lon2 = -71.3161;
var lat1 = 42.806911;
var lon1 = -71.290611;
var R = 6371; // km
//has a problem with the .toRad() method below.
var dLat = (lat2-lat1).toRad();
var dLon = (lon2-lon1).toRad();
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
Math.sin(dLon/2) * Math.sin(dLon/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
alert(d);
}
And the error is:
Uncaught TypeError: Object -0.06591099999999983 has no method 'toRad'
Which I understand to be because it needs to do the following:
Number.prototype.toRad = function() {
return this * Math.PI / 180;
}
But when I put this below the function, it still comes back with the same error message. How do I make it use the helper method? Or is there an alternative way to code this to get it to work? Thanks!
This code is working:
Number.prototype.toRad = function() {
return this * Math.PI / 180;
}
var lat2 = 42.741;
var lon2 = -71.3161;
var lat1 = 42.806911;
var lon1 = -71.290611;
var R = 6371; // km
//has a problem with the .toRad() method below.
var x1 = lat2-lat1;
var dLat = x1.toRad();
var x2 = lon2-lon1;
var dLon = x2.toRad();
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
Math.sin(dLon/2) * Math.sin(dLon/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
alert(d);
Notice how I defined x1 and x2.
Play with it at: https://tinker.io/3f794
Here's a refactored function based on 3 of the other answers!
Please note that the coords arguments are [longitude, latitude].
function haversineDistance(coords1, coords2, isMiles) {
function toRad(x) {
return x * Math.PI / 180;
}
var lon1 = coords1[0];
var lat1 = coords1[1];
var lon2 = coords2[0];
var lat2 = coords2[1];
var R = 6371; // km
var x1 = lat2 - lat1;
var dLat = toRad(x1);
var x2 = lon2 - lon1;
var dLon = toRad(x2)
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c;
if(isMiles) d /= 1.60934;
return d;
}
ES6 JavaScript/NodeJS refactored version:
/**
* Calculates the haversine distance between point A, and B.
* #param {number[]} latlngA [lat, lng] point A
* #param {number[]} latlngB [lat, lng] point B
* #param {boolean} isMiles If we are using miles, else km.
*/
const haversineDistance = ([lat1, lon1], [lat2, lon2], isMiles = false) => {
const toRadian = angle => (Math.PI / 180) * angle;
const distance = (a, b) => (Math.PI / 180) * (a - b);
const RADIUS_OF_EARTH_IN_KM = 6371;
const dLat = distance(lat2, lat1);
const dLon = distance(lon2, lon1);
lat1 = toRadian(lat1);
lat2 = toRadian(lat2);
// Haversine Formula
const a =
Math.pow(Math.sin(dLat / 2), 2) +
Math.pow(Math.sin(dLon / 2), 2) * Math.cos(lat1) * Math.cos(lat2);
const c = 2 * Math.asin(Math.sqrt(a));
let finalDistance = RADIUS_OF_EARTH_IN_KM * c;
if (isMiles) {
finalDistance /= 1.60934;
}
return finalDistance;
};
See codepen for tests against accepted answer: https://codepen.io/harrymt/pen/dyYvLpJ?editors=1011
Why not try the straight forward solution? Instead of extending Number prototype, just define toRad as a regular function:
function toRad(x) {
return x * Math.PI / 180;
}
and then call toRad everywhere:
var dLat = toRad(lat2-lat1);
Extending the Number prototype does not always work as expected. For example calling 123.toRad() does not work. I think that if you do var x1 = lat2 - lat1; x1.toRad(); works better than doing (lat2-lat1).toRad()
when I put this below the function
You only need to put it above the point where you call test(). Where the test function itself is declared does not matter.
You need to extend the Number prototype, before calling those extensions in a function.
So just ensure
Number.prototype.toRad = function() {
return this * Math.PI / 180;
}
is called before your function is called.
Another variant to reduce redundancy and also compatible with Google LatLng objects:
function haversine_distance(coords1, coords2) {
function toRad(x) {
return x * Math.PI / 180;
}
var dLat = toRad(coords2.latitude - coords1.latitude);
var dLon = toRad(coords2.longitude - coords1.longitude)
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(toRad(coords1.latitude)) *
Math.cos(toRad(coords2.latitude)) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
return 12742 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
Here's another refactored answer in JavaScript:
getHaversineDistance = (firstLocation, secondLocation) => {
const earthRadius = 6371; // km
const diffLat = (secondLocation.lat-firstLocation.lat) * Math.PI / 180;
const diffLng = (secondLocation.lng-firstLocation.lng) * Math.PI / 180;
const arc = Math.cos(
firstLocation.lat * Math.PI / 180) * Math.cos(secondLocation.lat * Math.PI / 180)
* Math.sin(diffLng/2) * Math.sin(diffLng/2)
+ Math.sin(diffLat/2) * Math.sin(diffLat/2);
const line = 2 * Math.atan2(Math.sqrt(arc), Math.sqrt(1-arc));
const distance = earthRadius * line;
return distance;
}
const philly = { lat: 39.9526, lng: -75.1652 }
const nyc = { lat: 40.7128, lng: -74.0060 }
const losAngeles = { lat: 34.0522, lng: -118.2437 }
console.log(getHaversineDistance(philly, nyc)) //129.61277152662188
console.log(getHaversineDistance(philly, losAngeles)) //3843.4534005980404
This is a java implemetation of talkol's solution above. His or her solution worked very well for us. I'm not trying to answer the question, since the original question was for javascript. I'm just sharing our java implementation of the given javascript solution in case others find it of use.
// this was a pojo class we used internally...
public class GisPostalCode {
private String country;
private String postalCode;
private double latitude;
private double longitude;
// getters/setters, etc.
}
public static double distanceBetweenCoordinatesInMiles2(GisPostalCode c1, GisPostalCode c2) {
double lat2 = c2.getLatitude();
double lon2 = c2.getLongitude();
double lat1 = c1.getLatitude();
double lon1 = c1.getLongitude();
double R = 6371; // km
double x1 = lat2 - lat1;
double dLat = x1 * Math.PI / 180;
double x2 = lon2 - lon1;
double dLon = x2 * Math.PI / 180;
double a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(lat1*Math.PI/180) * Math.cos(lat2*Math.PI/180) *
Math.sin(dLon/2) * Math.sin(dLon/2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
double d = R * c;
// convert to miles
return d / 1.60934;
}
i tried this
function distance(lat1,lon1,lat2,lon2)
{
var R = 6371; // km (change this constant to get miles)
var dLat = (lat2-lat1) * Math.PI / 180;
var dLon = (lon2-lon1) * Math.PI / 180;
var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(lat1 * Math.PI / 180 ) * Math.cos(lat2 * Math.PI / 180 ) * Math.sin(dLon/2) * Math.sin(dLon/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
if (d>1) return Math.round(d)+"km";
else if (d<=1) return Math.round(d*1000)+"m";
return d;
}
distance(21.1702401,72.83106070000008,22.3038945,70.80215989999999);
using above function i m getting this output : 245km
result coming for Straight Distance :245km
but i need Driving mode ditance of that location and its output should be 448km
From value is : Surat, Gujarat, India
to value is :Rajkot, Gujarat, India
so pls help me to solve this..
Thanks
Driving directions cannot be "simply" calculated. You need to know the roads, how long they are, where you can turn off and lots more.
It is too complicated to do in a single function with no underlying data.
I would suggest you look into Google maps API or find if TomTom/Apple/Garmin have APIs for their map data.
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>
This is my code for the Haversine formula based off of this and the answer of this question:
$scope.getCoordDistance = function (myLat, myLon, locLat, locLon) {
var lat2 = 41.894993;
var lon2 = -88.459239;
var lat1 = $scope.locLat;
var lon1 = $scope.locLon;
var R = 3959;
var x1 = lat1 - lat2;
var dLat = x1 * Math.PI / 180;
var x2 = lon1 - lon2;
var dLon = x2 * Math.PI / 180;
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
d = R * c;
return d;
}
I tested this with 3 different locations an they are all way off, the first location says its 14790.7 miles away when it is actually 44.1 miles away, every location is off by a different amount. Some are off by a small amount, others a very large amount. Is something wrong with my math here? If not, why wont this code work correctly?
Here is a link to a plunk with my full project: http://plnkr.co/edit/nRQc7Ym0lsaK6jQwd626?p=preview Thanks in advanced for any help!!!
The actual calculation works out the distance. The problem is how you use it in your function.
You pass 4 parameters in function myLat, myLon, locLat, locLon and then don't use them. I assume that you are still calling function in a forEach loop. Try this
Outside forEach
var myLat = 41.894993;
var myLon = -88.459239;
Inside forEach
var locLat = $scope.locLat;
var locLon = $scope.locLon;
getCoordDistance(myLat, myLon, locLat, locLon)
etc.
Function
$scope.getCoordDistance = function (myLat, myLon, locLat, locLon) {
var R = 3959;
var x1 = lat1 - lat2;
var dLat = x1 * Math.PI / 180;
var x2 = lon1 - lon2;
var dLon = x2 * Math.PI / 180;
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
Math.sin(dLon / 2) * Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
d = R * c;
return d;
}
I'm trying to find the distance between two points (for which I've latitudes & longitudes) using the technique described here at Calculate distance between two latitude-longitude points? (Haversine formula)
The codes are as below
Javascript:
var R = 6371; // Radius of the earth in km
var dLat = (lat2-lat1).toRad(); // Javascript functions in radians
var dLon = (lon2-lon1).toRad();
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) *
Math.sin(dLon/2) * Math.sin(dLon/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c; // Distance in km
But when I try to implement it, an error shows up saying Uncaught TypeError: Object 20 has no Method 'toRad'.
Do I need a special library or something to get .toRad() working? because it seems to be
screwing up on the second line.
You are missing a function declaration.
In this case toRad() must be defined first as:
/** Converts numeric degrees to radians */
if (typeof(Number.prototype.toRad) === "undefined") {
Number.prototype.toRad = function() {
return this * Math.PI / 180;
}
}
according to the code segment all at the bottom of the page
Or in my case this didn't work. It may because i needed to call toRad() inside jquery. Im not 100% sure, so i did this:
function CalcDistanceBetween(lat1, lon1, lat2, lon2) {
//Radius of the earth in: 1.609344 miles, 6371 km | var R = (6371 / 1.609344);
var R = 3958.7558657440545; // Radius of earth in Miles
var dLat = toRad(lat2-lat1);
var dLon = toRad(lon2-lon1);
var a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
Math.sin(dLon/2) * Math.sin(dLon/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c;
return d;
}
function toRad(Value) {
/** Converts numeric degrees to radians */
return Value * Math.PI / 180;
}
I needed to calculate a lot of distances between the points for my project, so I went ahead and tried to optimize the code, I have found here. On average in different browsers my new implementation runs almost 3 times faster than mentioned here.
function distance(lat1, lon1, lat2, lon2) {
var R = 6371; // Radius of the earth in km
var dLat = (lat2 - lat1) * Math.PI / 180; // deg2rad below
var dLon = (lon2 - lon1) * Math.PI / 180;
var a =
0.5 - Math.cos(dLat)/2 +
Math.cos(lat1 * Math.PI / 180) * Math.cos(lat2 * Math.PI / 180) *
(1 - Math.cos(dLon))/2;
return R * 2 * Math.asin(Math.sqrt(a));
}
You can play with my jsPerf (which was vastly improved thanks to Bart) and see the results here.
Why not simplify the above equation and same a few computations?
Math.sin(dLat/2) * Math.sin(dLat/2) = (1.0-Math.cos(dLat))/2.0
Math.sin(dLon/2) * Math.sin(dLon/2) = (1.0-Math.cos(dLon))/2.0
I was having the same problem.. looking at Casper's answer, I just did a quick-fix: Ctrl+H (Find and Replace),
replaced all instances of .toRad()
with * Math.PI / 180 . That worked for me.
No idea on the browser performance speeds etc, though.. My use case only needs this when the user clicks on a map.
I changed a couple of things:
if (!Number.prototype.toRad || (typeof(Number.prototype.toRad) === undefined)) {
and, I noticed there was no checking for the arguments. You should make sure the args are defined AND probably do a parseInt(arg, 10) / parseFloat on there.