Vincenty vs Haversine vs Sferical Law of Cosines Distance Calculations

JavaScript performance comparison

Revision 5 of this test case created

Info

This test compares JavaScript implementations of the three main different point-to-point distance calculation equations, Haversine, Vincenty and Spherical Law of Cosines.

Among geodetics enthusiasts, Haversine is perhaps slightly looked-down upon because its accuracy can be out by 0.3% or so, whereas Vincenty is apparently accurate to 0.5mm. Of course, both methods use a theoretical spheroid, and because no-one can actually travel on either theoretical spheroid, both are only an approximation :-)

The Spherical Law of Cosines has the same accuracy as Haversine. And is nowadays due to high computational precision an equivalent alternative.

More information on Haversine

More information on Vincenty

More information on Spherical Law of Cosines

Preparation code

 
<script>
Benchmark.prototype.setup = function() {
    /** Converts numeric degrees to radians */
    if (typeof(Number.prototype.toRad) === "undefined") {
      Number.prototype.toRad = function() {
        return this * Math.PI / 180;
      }
    }
   
    /**
     * Calculates geodetic distance between two points specified by latitude/longitude using
     * Vincenty inverse formula for ellipsoids
     *
     * @param   {Number} lat1, lon1: first point in decimal degrees
     * @param   {Number} lat2, lon2: second point in decimal degrees
     * @returns (Number} distance in metres between points
     */

   
    function distVincenty(lat1, lon1, lat2, lon2) {
      var a = 6378137,
          b = 6356752.314245,
          f = 1 / 298.257223563; // WGS-84 ellipsoid params
      var L = (lon2 - lon1).toRad();
      var U1 = Math.atan((1 - f) * Math.tan(lat1.toRad()));
      var U2 = Math.atan((1 - f) * Math.tan(lat2.toRad()));
      var sinU1 = Math.sin(U1),
          cosU1 = Math.cos(U1);
      var sinU2 = Math.sin(U2),
          cosU2 = Math.cos(U2);
   
      var lambda = L,
          lambdaP, iterLimit = 100;
      do {
        var sinLambda = Math.sin(lambda),
            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);
   
      return Math.round(s);
    }
   
    // Distance in kilometers between two points using the Haversine algo.
    function haversine(lat1, lon1, lat2, lon2) {
      var R = 6371;
      var dLat = (lat2 - lat1).toRad();
      var dLong = (lon2 - lon1).toRad();
   
      var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(lat1.toRad()) * Math.cos(lat2.toRad()) * Math.sin(dLong / 2) * Math.sin(dLong / 2);
      var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
      var d = R * c;
   
      return Math.round(d);
    }
   
   
    //The Spherical Law of Cosines
    //Original source: "http://www.movable-type.co.uk/scripts/latlong.html"
    function SphericalCosinus(lat1, lon1, lat2, lon2) {
        var R = 6371; // km
        var dLon = (lon2-lon1).toRad();
        var lat1 = lat1.toRad();
        var lat2 = lat2.toRad();
        var d = Math.acos(Math.sin(lat1)*Math.sin(lat2) +
                                                Math.cos(lat1)*Math.cos(lat2) *
                                                Math.cos(dLon)) * R;
   
        return Math.round(d);
    }
};
</script>

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in unknown unknown
Test Ops/sec
Vincenty formula
distVincenty(1.284263,103.851145,1.299561,103.847033);
pending…
Haversine formula
haversine(1.284263,103.851145,1.299561,103.847033);
pending…
Spherical Law of Cosines
SphericalCosinus(1.284263,103.851145,1.299561,103.847033);
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL. Here’s a list of current revisions for this page:

0 comments

Add a comment