Skip to content
Snippets Groups Projects

Haversine-based Great Circle Distance between two points on earth

  • Clone with SSH
  • Clone with HTTPS
  • Embed
  • Share
    The snippet can be accessed without any authentication.
    Authored by mirabilos

    Rough distance calculation. Call like this:

    sh dist.sh lat1 lon1 lat2 lon2

    For example, using the coordinates from the Rosetta Code site:

    $ sh dist.sh 36.12 -86.67 33.94 -118.4
    2886448.417

    The result is in metres, rounded mathematically to the closest millimetre… not that the formula is this precise. It ignores altitude, which may or may not be desired by the user (it is, for what I’m planning to use it). Use GeographicLib or something else if you need actual precision.

    Edited
    dist.sh 1.71 KiB
    #!/bin/sh
    #-
    # © 2021 mirabilos Ⓕ CC0; implementation of Haversine GCD from public sources
    #
    # now developed online:
    # https://evolvis.org/plugins/scmgit/cgi-bin/gitweb.cgi?p=useful-scripts/mirkarte.git;a=blob;f=geo.sh;hb=HEAD
    
    if test "$#" -ne 4; then
    	echo >&2 "E: syntax: $0 lat1 lon1 lat2 lon2"
    	exit 1
    fi
    
    set -e
    
    # make GNU bc use POSIX mode and shut up
    BC_ENV_ARGS=-qs
    export BC_ENV_ARGS
    
    # assignment of constants, variables and functions
    # p: multiply with to convert from degrees to radians (π/180)
    # r: earth radius in metres
    # d: distance
    # h: haversine intermediate
    # i,j: (lat,lon) point 1
    # x,y: (lat,lon) point 2
    # k: delta lat
    # l: delta lon
    # m: sin(k/2) (square root of hav(k))
    # n: sin(l/2) (  partial haversine  )
    # n(x): arcsin(x)
    # r(x,n): round x to n decimal digits
    # v(x): sign (Vorzeichen)
    # w(x): min(1, sqrt(x)) (Wurzel)
    
    bc -l <<-EOF
    scale=64
    define n(x) {
    	if (x == -1) return (-2 * a(1))
    	if (x == 1) return (2 * a(1))
    	return (a(x / sqrt(1 - x*x)))
    }
    define v(x) {
    	if (x < 0) return (-1)
    	if (x > 0) return (1)
    	return (0)
    }
    define r(x, n) {
    	auto o
    	o = scale
    	if (scale < (n + 1)) scale = (n + 1)
    	x += v(x) * 0.5 * A^-n
    	scale = n
    	x /= 1
    	scale = o
    	return (x)
    }
    define w(x) {
    	if (x >= 1) return (1)
    	return (sqrt(x))
    }
    /* WGS84 reference ellipsoid: große Halbachse (metres), Abplattung */
    i = 6378137.000
    x = 1/298.257223563
    /* other axis */
    j = i * (1 - x)
    /* mean radius resulting */
    r = (2 * i + j) / 3
    /* coordinates */
    p = (4 * a(1) / 180)
    i = (p * $1)
    j = (p * $2)
    x = (p * $3)
    y = (p * $4)
    /* calculation */
    k = (x - i)
    l = (y - j)
    m = s(k / 2)
    n = s(l / 2)
    h = ((m * m) + (c(i) * c(x) * n * n))
    d = 2 * r * n(w(h))
    r(d, 3)
    EOF
    
    # output is in metres, rounded to millimetres, error < ¼% in WGS84
    0% Loading or .
    You are about to add 0 people to the discussion. Proceed with caution.
    Finish editing this message first!
    Please register or to comment