Vincenty vs Haversine vs Spherical Law of Cosines vs Equirectangular approximation (v5)

Revision 5 of this benchmark created on


Description

Test of Vincenty

Setup

var from = {
      lat: 40,
      lon: -70
    },
        to = {
        lat: 40.7419,
        lon: -73.9930
        },
        EARTH_RADIUS = 6371000;
    
    function toRadian(deg) {
      return deg / 180 * Math.PI;
    }

Test runner

Ready to run.

Testing in
TestOps/sec
Haversine
var dLat = toRadian((to.lat - from.lat));
var dLon = toRadian((to.lon - from.lon));
var fromLat = toRadian(from.lat);
var toLat = toRadian(to.lat);

var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(fromLat) * Math.cos(toLat);

var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = EARTH_RADIUS * c;
ready
Spherical Law of Cosines
var d = Math.PI / 2 - toRadian(from.lat)) * Math.sin(toRadian(to.lat)) + Math.cos(toRadian(from.lat)) * Math.cos(toRadian(to.lat)) * Math.cos(toRadian(to.lon - from.lon))) * EARTH_RADIUS;
ready
Equirectangular approximation
var dLat = toRadian((to.lat - from.lat));
var dLon = toRadian((to.lon - from.lon));

var x = (dLon) * Math.cos(toRadian((from.lat + to.lat) / 2));
var d = Math.sqrt(x * x + dLat * dLat) * EARTH_RADIUS;
ready
Vincenty approximation
var a = 6378137,
    b = 6356752.314245,
    f = 1 / 298.257223563; // WGS-84 ellipsoid params
var L = toRadian(lon2 - lon1);
var U1 = Math.atan((1 - f) * Math.tan(toRadian(lat1)));
var U2 = Math.atan((1 - f) * Math.tan(toRadian(lat2)));
var sinU1 = Math.sin(U1),
    cosU1 = Math.cos(U1);
var sinU2 = Math.sin(U2),
    cosU2 = Math.cos(U2);

var lambda = L;
var lambdaP;
var iterLimit = 100;

do {
  var sinLambda = Math.sin(lambda);
  var cosLambda = Math.cos(lambda);
  var sinSigma = Math.sqrt((cosU2 * sinLambda) * (cosU2 * sinLambda) + (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda) * (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda));

  if (sinSigma == 0) return 0; // co-incident points

  var cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
  var sigma = Math.atan2(sinSigma, cosSigma);
  var sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
  var cosSqAlpha = 1 - sinAlpha * sinAlpha;
  var cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha;

  if (isNaN(cos2SigmaM)) cos2SigmaM = 0; // equatorial line:// cosSqAlpha=0 (§6)

  var C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));

  lambdaP = lambda;
  lambda = L + (1 - C) * f * sinAlpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM)));

} while (Math.abs(lambda - lambdaP) > 1e-12 && --iterLimit > 0);

if (iterLimit == 0) return NaN; // formula failed to converge

var uSq = cosSqAlpha * (a * a - b * b) / (b * b);
var A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
var B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
var deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
var s = b * A * (sigma - deltaSigma);

s = (s / 1000).toFixed(6); // round to 1nm precision
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.