【问题标题】:Calculating distance in kilometers between coordinates计算坐标之间的公里距离
【发布时间】:2017-12-28 13:10:06
【问题描述】:

我正在尝试使用半正弦公式计算两个地理坐标之间的公里距离。

代码:

Dim dbl_dLat As Double
Dim dbl_dLon As Double
Dim dbl_a As Double

dbl_P = WorksheetFunction.Pi / 180
dbl_dLat = dbl_P * (dbl_Latitude2 - dbl_Latitude1)
dbl_dLon = dbl_P * (dbl_Longitude2 - dbl_Longitude1)

dbl_a = Sin(dbl_dLat / 2) * Sin(dbl_dLat / 2) + Cos(dbl_Latitude1 * dbl_P) * Cos(dbl_Latitude2 * dbl_P) * Sin(dbl_dLon / 2) * Sin(dbl_dLon / 2)

dbl_Distance_KM = 6371 * 2 * WorksheetFunction.Atan2(Sqr(dbl_a), Sqr(1 - dbl_a))

我正在使用这些坐标进行测试:

dbl_Longitude1 = 55.629178
dbl_Longitude2 = 29.846686
dbl_Latitude1 = 37.659466
dbl_Latitude2 = 30.24441

并且代码返回20015.09,这显然是错误的。根据 Yandex 地图,应该是 642 公里。

我哪里错了?经度和纬度的格式是否错误?

【问题讨论】:

  • 为什么不用Lon 而不是dbl_dLon?那些伪匈牙利语前缀真的有助于提高代码的可靠性吗?这绝对对可读性部门没有帮助。
  • 欢迎来到本站!查看tour 了解有关该站点如何运行的更多信息。您可以edit your question 包含更多信息。在这种情况下,给定的坐标是2512 km apart。请您再次检查您的输入坐标好吗?
  • @cxw 对不起,dbl_Longitude2 应该读作 59.846686(那么这些点距离 642 公里)。无论如何,正如您所指出的,问题在于 Atan2 函数的参数放置错误。
  • @User24112017 感谢您的更新!顺便说一句,请不要在 at 符号后加空格 --- @cxw 应该通知我,但 @ cxw 没有。

标签: vba excel worksheet-function geographic-distance


【解决方案1】:

据我所知,问题在于 atan2() 的参数顺序因语言而异。以下对我有用:

Option Explicit

Public Sub Distance()
    Dim dbl_Longitude1 As Double, dbl_Longitude2 As Double, dbl_Latitude1 As Double, dbl_Latitude2 As Double

    dbl_Longitude1 = 55.629178
    dbl_Longitude2 = 29.846686
    dbl_Latitude1 = 37.659466
    dbl_Latitude2 = 30.24441

    Dim dbl_dLat As Double
    Dim dbl_dLon As Double
    Dim dbl_a As Double
    Dim dbl_P As Double

    dbl_P = WorksheetFunction.Pi / 180
    dbl_dLat = dbl_P * (dbl_Latitude2 - dbl_Latitude1)      'to radians
    dbl_dLon = dbl_P * (dbl_Longitude2 - dbl_Longitude1)    'to radians

    dbl_a = Sin(dbl_dLat / 2) * Sin(dbl_dLat / 2) + _
            Cos(dbl_Latitude1 * dbl_P) * Cos(dbl_Latitude2 * dbl_P) * Sin(dbl_dLon / 2) * Sin(dbl_dLon / 2)

    Dim c As Double
    Dim dbl_Distance_KM As Double
    c = 2 * WorksheetFunction.Atan2(Sqr(1 - dbl_a), Sqr(dbl_a))  ' *** swapped arguments to Atan2
    dbl_Distance_KM = 6371 * c

    Debug.Print dbl_Distance_KM
End Sub

*输出:2507.26205401321,尽管gcmap.com 说答案是 2512 公里。这可能是一个精度问题——我认为它足够接近可以算作工作。 (编辑也可能是 gcmap 使用当地地球半径而不是平均半径;我不确定。)

说明

我发现this description 的大圆距离的haversine 公式,这就是你正在实施的。该页面上的 JavaScript 实现为 c 提供了这种计算:

var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));

在 JavaScript 中,atan2() 接受参数 yx。但是,在 Excel VBA 中,WorksheetFunction.Atan2 采用参数 xy。您的原始代码将 Sqr(dbl_a) 作为第一个参数传递,就像在 JavaScript 中一样。但是,Sqr(dbl_a) 需要是 Excel VBA 中的 second 参数。

关于命名的评论

基于@JohnColeman 的观点,有很多方法可以命名变量。在这种情况下,我建议使用单位而不是类型的前缀:例如,deg_Latitude1RadPerDeg = Pi/180rad_dLat = RadPerDeg * (deg_Latitude2 - deg_Latitude1)。我个人认为这有助于避免unit-conversion mishaps

【讨论】:

    【解决方案2】:

    我的 VBA 代码以英尺为单位返回答案;然而,“d”是以公里为单位的答案。

    Imports System.Math
    Module Haversine
    Public Function GlobalAddressDistance(sLat1 As String, sLon1 As String, sLat2 As String, sLon2 As String) As String
        Const R As Integer = 6371
        Const cMetersToFeet As Single = 3.2808399
        Const cKiloMetersToMeters As Integer = 1000
        Dim a As Double = 0, c As Double = 0, d As Double = 0
    
        'Convert strings to numberic double values
        Dim dLat1 As Double = Val(sLat1)
        Dim dLat2 As Double = Val(sLat2)
        Dim dLatDiff As Double = DegreesToRadians(CDbl(sLat2) - CDbl(sLat1))
        Dim dLonDiff As Double = DegreesToRadians(CDbl(sLon2) - CDbl(sLon1))
    
        a = Pow(Sin(dLatDiff / 2), 2) + Cos(DegreesToRadians(dLat1)) * Cos(DegreesToRadians(dLat2)) * Pow(Sin(dLonDiff / 2), 2)
        c = 2 * Atan2(Sqrt(a), Sqrt(1 - a))
        d = R * c
    
        'Convert kilometers to feet
        Return Format((d * cKiloMetersToMeters * cMetersToFeet), "0.##").ToString
    End Function
    
    Private Function DegreesToRadians(ByVal dDegrees As Double) As Double
        Return (dDegrees * PI) / 180
    End Function
    

    结束模块

    【讨论】:

      猜你喜欢
      • 2021-09-08
      • 2021-04-05
      • 1970-01-01
      • 1970-01-01
      • 2020-06-02
      • 2010-09-26
      • 2020-02-12
      • 1970-01-01
      • 2018-06-12
      相关资源
      最近更新 更多