

 This page uses content from Wikipedia. The original article was at Haversine formula. The list of authors can be seen in the page history. As with Rosetta Code, the text of Wikipedia is available under the GNU FDL. (See links for details on variance)



The haversine formula is an equation important in navigation, giving great-circle distances between two points on a sphere from their longitudes and latitudes.

It is a special case of a more general formula in spherical trigonometry, the law of haversines, relating the sides and angles of spherical "triangles".



Task

Implement a great-circle distance function, or use a library function,
to show the great-circle distance between:

 Nashville International Airport (BNA)   in Nashville, TN, USA,   which is: 
   N 36°7.2',   W 86°40.2'     (36.12,   -86.67)           -and-

 Los Angeles International Airport (LAX)  in Los Angeles, CA, USA,   which is:
   N 33°56.4',  W 118°24.0'    (33.94,  -118.40)   



User Kaimbridge clarified on the Talk page:

 -- 6371.0 km is the authalic radius based on/extracted from surface area;
 -- 6372.8 km is an approximation of the radius of the average circumference
    (i.e., the average great-elliptic or great-circle radius), where the
     boundaries are the meridian (6367.45 km) and the equator (6378.14 km).

Using either of these values results, of course, in differing distances:

 6371.0 km -> 2886.44444283798329974715782394574671655 km;
 6372.8 km -> 2887.25995060711033944886005029688505340 km;
 (results extended for accuracy check:  Given that the radii are only
  approximations anyways, .01' ≈ 1.0621333 km and .001" ≈ .00177 km,
  practical precision required is certainly no greater than about
  .0000001——i.e., .1 mm!)

As distances are segments of great circles/circumferences, it is
recommended that the latter value (r = 6372.8 km) be used (which
most of the given solutions have already adopted, anyways). 

Most of the examples below adopted Kaimbridge's recommended value of
6372.8 km for the earth radius. However, the derivation of this
ellipsoidal quadratic mean radius
is wrong (the averaging over azimuth is biased). When applying these
examples in real applications, it is better to use the
mean earth radius,
6371 km. This value is recommended by the International Union of
Geodesy and Geophysics and it minimizes the RMS relative error between the
great circle and geodesic distance.






use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "JavaScriptCore"
use scripting additions
 
property js : missing value
 
 
-- haversine :: (Num, Num) -> (Num, Num) -> Num
on haversine(latLong, latLong2)
    set {lat, lon} to latLong
    set {lat2, lon2} to latLong2
 
    set {rlat1, rlat2, rlon1, rlon2} to ¬
        map(my radians, {lat, lat2, lon, lon2})
 
    set dLat to rlat2 - rlat1
    set dLon to rlon2 - rlon1
    set radius to 6372.8 -- km
 
    set asin to math("asin")
    set sin to math("sin")
    set cos to math("cos")
 
    |round|((2 * radius * ¬
        (asin's |λ|((sqrt(((sin's |λ|(dLat / 2)) ^ 2) + ¬
            (((sin's |λ|(dLon / 2)) ^ 2) * ¬
                (cos's |λ|(rlat1)) * (cos's |λ|(rlat2)))))))) * 100) / 100
end haversine
 
 
-- math :: String -> Num -> Num
on math(f)
    script
        on |λ|(x)
            if missing value is js then ¬
                set js to current application's JSContext's new()
            (js's evaluateScript:("Math." & f & "(" & x & ")"))'s toDouble()
        end |λ|
    end script
end math
 
 
-------------------------- TEST ---------------------------
on run
    set distance to haversine({36.12, -86.67}, {33.94, -118.4})
 
    set js to missing value -- Clearing a c pointer.
    return distance
end run
 
 
-------------------- GENERIC FUNCTIONS --------------------
 
-- map :: (a -> b) -> [a] -> [b]
on map(f, xs)
    -- The list obtained by applying f
    -- to each element of xs.
    tell mReturn(f)
        set lng to length of xs
        set lst to {}
        repeat with i from 1 to lng
            set end of lst to |λ|(item i of xs, i, xs)
        end repeat
        return lst
    end tell
end map
 
 
-- mReturn :: First-class m => (a -> b) -> m (a -> b)
on mReturn(f)
    -- 2nd class handler function lifted into 1st class script wrapper. 
    if script is class of f then
        f
    else
        script
            property |λ| : f
        end script
    end if
end mReturn
 
 
-- radians :: Float x => Degrees x -> Radians x
on radians(x)
    (pi / 180) * x
end radians
 
 
-- round :: a -> Int
on |round|(n)
    round n
end |round|
 
 
-- sqrt :: Num -> Num
on sqrt(n)
    if n ≥ 0 then
        n ^ (1 / 2)
    else
        missing value
    end if
end sqrt