#lang scheme/base
(require scheme/math
scheme/contract
(planet dyoo/infix/infix))
(provide/contract [compute-distance (number? number? number? number? . -> . number?)])
(define (to-rad deg)
(/ (* deg pi) 180.0))
(define (compute-distance lat1 lon1 lat2 lon2)
(define MAXITERS 20)
(set! lat1 (to-rad lat1))
(set! lat2 (to-rad lat2))
(set! lon1 (to-rad lon1))
(set! lon2 (to-rad lon2))
(let* ([a 6378137.0] [b 6356752.3142] [f (/ (- a b) a)]
[aSqMinusBSqOverBSq (/ (- (sqr a) (sqr b))
(sqr b))]
[L (- lon2 lon1)]
[A 0.0]
[U1 (atan (* (- 1.0 f)
(tan lat1)))]
[U2 (atan (* (- 1.0 f)
(tan lat2)))]
[cosU1 (cos U1)]
[cosU2 (cos U2)]
[sinU1 (sin U1)]
[sinU2 (sin U2)]
[cosU1cosU2 (* cosU1 cosU2)]
[sinU1sinU2 (* sinU1 sinU2)]
[sigma 0.0]
[deltaSigma 0.0]
[cosSqAlpha 0.0]
[cos2SM 0.0]
[cosSigma 0.0]
[sinSigma 0.0]
[cosLambda 0.0]
[sinLambda 0.0]
[lambda L]) (let/ec break
(for ([iter (in-range 0 MAXITERS)])
(let ([lambdaOrig lambda])
(set! cosLambda (cos lambda))
(set! sinLambda (sin lambda))
(let* ([t1 (* cosU2 sinLambda)]
[t2 (- (* cosU1 sinU2) (* sinU1 cosU2 cosLambda))]
[sinSqSigma (+ (* t1 t1)
(* t2 t2))]) (set! sinSigma (sqrt sinSqSigma))
(set! cosSigma (+ sinU1sinU2 (* cosU1cosU2 cosLambda))) (set! sigma (atan sinSigma cosSigma)) (let ([sinAlpha
(if (= sinSigma 0)
0.0
(/ (* cosU1cosU2 sinLambda)
sinSigma))]) (set! cosSqAlpha (- 1.0 (* sinAlpha sinAlpha)))
(set! cos2SM (if (= cosSqAlpha 0)
0.0
(- cosSigma (/ (* 2.0 sinU1sinU2) cosSqAlpha)))) (let ([uSquared (* cosSqAlpha aSqMinusBSqOverBSq)]) (set! A (infix 1 + (uSquared / 16384.0) * (4096.0 + uSquared *
(-768 + uSquared * (320.0 - 175.0 * uSquared)))))
(let ([B (infix (uSquared / 1024.0) * (256.0 + uSquared *
(-128.0 + uSquared * (74.0 - 47.0 * uSquared))))]
[C (infix
(f / 16.0) *
cosSqAlpha *
(4.0 + f * (4.0 - 3.0 * cosSqAlpha)))]
[cos2SMSq (* cos2SM cos2SM)])
(set! deltaSigma (infix B * sinSigma * (cos2SM + (B / 4.0) *
(cosSigma * (-1.0 + 2.0 * cos2SMSq) -
(B / 6.0) * cos2SM *
(-3.0 + 4.0 * sinSigma * sinSigma) *
(-3.0 + 4.0 * cos2SMSq)))))
(set! lambda
(infix L +
(1.0 - C) * f * sinAlpha *
(sigma + C * sinSigma *
(cos2SM + C * cosSigma *
(-1.0 + 2.0 * cos2SM * cos2SM))))) (let ([delta (infix (lambda - lambdaOrig) / lambda)])
(when (infix (abs(delta) < 1.0e-12))
(break))))))))))
(let ([distance
(infix (b * A * (sigma - deltaSigma)))])
distance)))