Code: Select all
#Version = "1.01"
#MiddleEarthRadius = 6371.000785
;GRS 80 WGS84
#ERadius = 6378.1370
#PRadius = 6356.752314
Enumeration
#CalcButton = 100
#Lat1Gadget
#Long1Gadget
#Lat2Gadget
#Long2Gadget
#HaversineGadget
#SphericalGadget
#VincentyGadget
EndEnumeration
Procedure.f Haversine(lat1.f, lon1.f, lat2.f, lon2.f)
dLat.f = Radian(lat2 - lat1)
dLon.f = Radian(lon2 - lon1)
lat1 = Radian(lat1)
lat2 = Radian(lat2)
a.f = (Sin(dLat / 2) * Sin(dLat / 2)) + ((Cos(lat1) * Cos(lat2)) * (Sin(dLon / 2) * Sin(dLon / 2)))
ProcedureReturn 2 * (ATan2(Sqr(1 - a), Sqr(a))) * #MiddleEarthRadius
EndProcedure
Procedure.d SphericalLaw(Lat1.d, Lon1.d, Lat2.d, Lon2.d)
ProcedureReturn ACos(Sin(Radian(Lat1)) * Sin(Radian(Lat2)) + Cos(Radian(Lat1)) * Cos(Radian(Lat2)) * Cos(Radian(Lon2 - Lon1))) * #MiddleEarthRadius
EndProcedure
Procedure.d Vincenty(lat1.d, lon1.d, lat2.d, lon2.d)
Protected a.d=#ERadius, b.d=#PRadius, f.d=(a-b)/a
Protected rlat1.d=Radian(lat1)
Protected rlat2.d=Radian(lat2)
Protected rlon1.d=Radian(lon1)
Protected rlon2.d=Radian(lon2)
Protected L.d=Radian(lon2-lon1)
Protected U1.d= ATan((1-f)*Tan(rlat1))
Protected U2.d= ATan((1-f)*Tan(rlat2))
Protected sinU1.d=Sin(U1)
Protected cosU1.d=Cos(U1)
Protected sinU2.d=Sin(U2)
Protected cosU2.d=Cos(U2)
Protected lambda.d=L, lambdaP.d, iterLimit.i=100
Repeat
Protected sinLambda.d=Sin(lambda)
Protected cosLambda.d=Cos(lambda)
Protected sinSigma.d=Sqr((cosU2*sinLambda) * (cosU2*sinLambda) + (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda))
If sinSigma=0
ProcedureReturn 0
EndIf
Protected cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda
Protected sigma.d=ATan2(cosSigma, sinSigma)
Protected sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma
Protected cosSqAlpha = 1 - sinAlpha*sinAlpha
Protected cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha
If (IsNAN(cos2SigmaM))
cos2SigmaM=0
EndIf
Protected C.d=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)))
iterLimit-1
Until Abs(lambda-lambdaP)<1e-12 Or iterLimit<1
If iterLimit=0
ProcedureReturn NaN()
EndIf
Protected uSq.d=cosSqAlpha * (a*a - b*b) / (b*b)
Protected AA.d=1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)))
Protected BB.d=uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)))
Protected deltaSigma.d=BB*sinSigma*(cos2SigmaM+BB/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-BB/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)))
Protected s.d = b*AA*(sigma-deltaSigma)
ProcedureReturn s
EndProcedure
Procedure.f ConvertGeoToDeg(Value$)
Protected Result.f = 0.0
Protected Direction$
Protected Direction = 1
Protected Pos
ReplaceString(Value$, ",", ".", #PB_String_InPlace)
If FindString(Value$, "°", 1) = 0
ProcedureReturn ValF(Value$)
EndIf
ReplaceString(Value$, "''", #DQUOTE$ + " ", #PB_String_InPlace)
Direction$ = Left(Value$, 1)
If Direction$ > "A"
Value$ = Mid(Value$, 2)
Else
Direction$ = Right(Value$, 1)
EndIf
If Direction$ = "S" Or Direction$ = "W"
Direction = -1
EndIf
Pos = FindString(Value$, "°", 1)
If Pos
Result = Val(Value$)
Value$ = Mid(Value$, Pos + 1)
Pos = FindString(Value$, "'", 1)
If Pos
Result + (Val(Value$) / 60)
Value$ = Mid(Value$, Pos + 1)
Pos = FindString(Value$, #DQUOTE$, 1)
If Pos
Result + (ValF(Value$) / 3600)
EndIf
EndIf
EndIf
ProcedureReturn Result * Direction
EndProcedure
Lat1.f = 0
Long1.f = 0
Lat2.f = 0
Long2.f = 0
OpenWindow(0, 0, 0, 290, 260, "Geographic distance calculator V" + #VERSION, #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
TextGadget(1, 60, 10, 50, 20, "Latitude")
TextGadget(2, 170, 10, 50, 20, "Longitude")
TextGadget(3, 10, 40, 50, 20, "Point 1")
StringGadget(#Lat1Gadget, 60, 40, 100, 20, "")
StringGadget(#Long1Gadget, 170, 40, 100, 20, "")
TextGadget(4, 10, 70, 50, 20, "Point 2")
StringGadget(#Lat2Gadget, 60, 70, 100, 20, "")
StringGadget(#Long2Gadget, 170, 70, 100, 20, "")
ButtonGadget(#CalcButton, 10, 110, 260, 30, "Calculate")
TextGadget(5, 10, 160, 100, 20, "Haversine:")
StringGadget(#HaversineGadget, 120, 160, 80, 20, "")
TextGadget(6, 210, 160, 20, 20, "km")
TextGadget(7, 10, 190, 100, 20, "Spherical law:")
StringGadget(#SphericalGadget, 120, 190, 80, 20, "")
TextGadget(8, 210, 190, 20, 20, "km")
TextGadget(9, 10, 220, 100, 20, "Vincenty:")
StringGadget(#VincentyGadget, 120, 220, 80, 20, "")
TextGadget(10, 210, 220, 20, 20, "km")
SetActiveGadget(#Lat1Gadget)
Exit = #False
Repeat
Event = WaitWindowEvent()
Select Event
Case #PB_Event_Gadget
If EventGadget() = #CalcButton
Lat1 = ConvertGeoToDeg(GetGadgetText(#Lat1Gadget))
Long1 = ConvertGeoToDeg(GetGadgetText(#Long1Gadget))
Lat2 = ConvertGeoToDeg(GetGadgetText(#Lat2Gadget))
Long2 = ConvertGeoToDeg(GetGadgetText(#Long2Gadget))
SetGadgetText(#HaversineGadget, StrF(Haversine(Lat1, Long1, Lat2, Long2)))
SetGadgetText(#SphericalGadget, StrF(SphericalLaw(Lat1, Long1, Lat2, Long2)))
SetGadgetText(#VincentyGadget, StrF(Vincenty(Lat1, Long1, Lat2, Long2)))
EndIf
Case #PB_Event_CloseWindow
Exit = #True
EndSelect
Until Exit
P.S.: I don't know why Vincenty gives a bit other results.