緯度経度から距離を計算する

# TL;DR

- 以下のサイトなどを参考（現在はリンク先消失）に作成した緯度経度から距離計算の方法
    - http://vldb.gsi.go.jp/sokuchi/
    - [この記事](https://zenn.dev/yonda/articles/c0003d90e52b3e)で知ったが以下に移動していた。
	    -  https://vldb.gsi.go.jp/sokuchi/surveycalc/surveycalc/algorithm/bl2st/bl2st.htm
- また、緯度経度を表す様々な形式の相互変換を行う方法
- 今のところ北緯、東経だけしか考えていない（＋だけしか考えていない）

# 地球は丸い

地球は丸い。しかも完全な球体ではなく、楕円体となっている。

これで何が起こるかというと、距離計算は単純ではなくなる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204150/5ea813b4-a78c-39ac-e329-91154e213ada.png)

平面であれば、単純に2点間の距離を求めればよいが、実際に人間が感じる距離は、球面にそった経路の距離となる。

ゼンリン住宅地図などで使用されているのは、平面直角座標系となる。これは、地図を通常利用するならば2次元と考えるほうが人間が理解しやすく、狭い範囲であれば問題はないため、使われている。

平面直角座標系を使用した地図での距離であれば、座標単位もmであるため単純に2点間の距離を求めればよいのだが、緯度経度から距離を求めるということであれば、別の工夫が必要になってくる。

# 緯度経度から距離を計算する

緯度経度から距離を計算するには、球面を気にしないで計算してしまうか、球面を気にして細かい計算を行うか、ということになる。

## BLCGetDistance

この関数では、地球を楕円体とみなしたうえで、2点間の距離を求めている。

ただし、2点間の直線距離であり、球面に沿った距離ではない。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204150/d0c346e1-e83c-d32f-b5c0-6210b2671a46.png)

距離が短いなら、これでもそれほど変わらないが、球面上の2点間の距離としては正確ではない

## BLCDistTwoPoint

この関数では、地球を楕円体とみなすことに加え、2点間の距離を球面に沿って計算している。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/204150/99112e12-00fe-660c-d1cb-c8721a76eeea.png)

地球には実際には凹凸があるが、その点を気にしなければ、かなり正確な距離が求められる。

# 緯度経度の表示形式

詳細は[こちら](https://qiita.com/tag1216/items/0b38ee5aedea0ef4a058)に譲るが以下の形式がある。

|表記|説明|例|
|---|---|---|
|DMS形式(60進表記、度分秒を別で扱う)|度分秒ミリ秒形式。それぞれを別で扱う|131度27分22秒913ミリ秒|
|DMS形式(60進表記、DDDMMSS.SSS)の文字列|度分秒ミリ秒形式を数値として扱う。小数点以下はミリ秒となる。|1312722.913|
|DEG形式(10進表記、1を1度として表記する)|1を1度として表記する。分と秒は度に換算して小数点以下の数値で表す。|131.456364722222|
|ミリ秒形式（1を1ミリ秒としたInt32型）|度・分・秒を1を1ミリ秒としたInt32に変換した表記|473242913|

# ソース

💡 `BLCGetDistance`と`BLCDistTwoPoint`で緯度経度の順番が不一致となっている（意図したものではないため訂正予定）。

```vb:modBLCalc.cls
#Disable Warning BC40000
Option Strict Off
Option Explicit On
Imports Microsoft.VisualBasic.Compatibility
Public Module modBLCalc
    Private Const PI As Double = 3.14159265358979

    ''' <summary>
    ''' ２点間の距離
    ''' </summary>
    ''' <param name="lngLon1">経度 ミリ秒形式（1を1ミリ秒としたInt32型）</param>
    ''' <param name="lngLat1">緯度 ミリ秒形式（1を1ミリ秒としたInt32型）</param>
    ''' <param name="lngLon2">経度 ミリ秒形式（1を1ミリ秒としたInt32型）</param>
    ''' <param name="lngLat2">緯度 ミリ秒形式（1を1ミリ秒としたInt32型）</param>
    ''' <returns>直交座標に変換した後、距離を求める。狭い範囲の場合、これで十分</returns>
    Public Function BLCGetDistance(ByVal lngLon1 As Integer,
                                   ByVal lngLat1 As Integer,
                                   ByVal lngLon2 As Integer,
                                   ByVal lngLat2 As Integer) As Double
        Dim X(1) As Double
        Dim Y(1) As Double
        Dim Z(1) As Double
        Dim R As Double ' ２地点間の直交距離

        Call BLCBL2SRC(lngLon1, lngLat1, X(0), Y(0), Z(0))
        Call BLCBL2SRC(lngLon2, lngLat2, X(1), Y(1), Z(1))

        ' 直距離(m)
        R = System.Math.Sqrt((X(1) - X(0)) ^ 2 + (Y(1) - Y(0)) ^ 2 + (Z(1) - Z(0)) ^ 2)

        Return R
    End Function

    ''' <summary>
    ''' SRCからBLへ
    ''' </summary>
    ''' <param name="dblX"></param>
    ''' <param name="dblY"></param>
    ''' <param name="dblZ"></param>
    ''' <param name="lngLon">経度 ミリ秒形式（1を1ミリ秒としたInt32型）</param>
    ''' <param name="lngLat">緯度 ミリ秒形式（1を1ミリ秒としたInt32型）</param>
    Public Sub BLCSRC2BL(ByVal dblX As Double,
                         ByVal dblY As Double,
                         ByVal dblZ As Double,
                         ByRef lngLon As Integer,
                         ByRef lngLat As Integer)
        Dim dblLon, dblLat As Double
        Dim R As Double
        Dim delta As Double

        If dblX > 0 Then
            delta = 0
        ElseIf dblX < 0 And dblY > 0 Then
            delta = PI
        Else
            delta = -PI
        End If

        ' SRCからBLへ
        dblLon = (System.Math.Atan(dblY / dblX) + delta) * 180 / PI
        R = System.Math.Sqrt(dblX ^ 2 + dblY ^ 2 + dblZ ^ 2)
        dblLat = (System.Math.Atan((dblZ / R) / System.Math.Sqrt(-(dblZ / R) ^ 2 + 1))) * 180 / PI

        ' BLの単位を変換(度→ミリ秒)
        lngLon = dblLon * 60 * 60 * 1000
        lngLat = dblLat * 60 * 60 * 1000
    End Sub

    ''' <summary>
    ''' BLから地心直交座標(SRC:Space Rectangular Coordinates)へ
    ''' </summary>
    ''' <param name="lngLon">経度 ミリ秒形式（1を1ミリ秒としたInt32型）</param>
    ''' <param name="lngLat">緯度 ミリ秒形式（1を1ミリ秒としたInt32型）</param>
    ''' <param name="dblX"></param>
    ''' <param name="dblY"></param>
    ''' <param name="dblZ"></param>
    Public Sub BLCBL2SRC(ByVal lngLon As Integer,
                         ByVal lngLat As Integer,
                         ByRef dblX As Double,
                         ByRef dblY As Double,
                         ByRef dblZ As Double)
        Dim dblLon, dblLat As Double ' 緯度、経度(単位:度)
        Dim N As Double
        Const a As Double = 6377397.155 ' 赤道半径
        Const E2 As Double = 0.006674372 ' 離心率の自乗
        Const RAD As Double = 0.0174532925199433 ' ラジアン
        '    Const a = 6378136                       ' 赤道半径
        '    Const E2 = 0.00669447                   ' 離心率の自乗
        '    Const RAD = 1.74532925199433E-02        ' ラジアン


        ' BLの単位を変換(ミリ秒→度)
        dblLon = lngLon / 60 / 60 / 1000
        dblLat = lngLat / 60 / 60 / 1000

        ' BLからSRCへ
        N = a / System.Math.Sqrt(1 - E2 * System.Math.Sin(dblLat * RAD) ^ 2)
        dblX = N * System.Math.Cos(dblLon * RAD) * System.Math.Cos(dblLat * RAD)
        dblY = N * System.Math.Sin(dblLon * RAD) * System.Math.Cos(dblLat * RAD)
        dblZ = N * (1 - E2) * System.Math.Sin(dblLat * RAD)
    End Sub

    ''' <summary>
    ''' 歪みも考慮に入れて2つの緯経度から距離を求める
    ''' </summary>
    ''' <param name="plngFromLatMS">出発点の緯度 ミリ秒形式（1を1ミリ秒としたInt32型）</param>
    ''' <param name="plngFromLonMS">出発点の経度 ミリ秒形式（1を1ミリ秒としたInt32型）</param>
    ''' <param name="plngToLatMS">到着点の緯度 ミリ秒形式（1を1ミリ秒としたInt32型）</param>
    ''' <param name="plngToLonMS">到着点の経度 ミリ秒形式（1を1ミリ秒としたInt32型）</param>
    ''' <param name="α1">出発点からみた到着点の角度（ラジアン）を返却</param>
    ''' <param name="α21">到着点から見た出発点の角度（ラジアン）</param>
    ''' <returns>距離　単位[m]</returns>
    Public Function BLCDistTwoPoint(ByVal plngFromLatMS As Integer,
                                    ByVal plngFromLonMS As Integer,
                                    ByVal plngToLatMS As Integer,
                                    ByVal plngToLonMS As Integer,
                                    Optional ByRef α1 As Double = 0,
                                    Optional ByRef α21 As Double = 0) As Double
        Dim α2 As Double
        Dim φ1 As Double
        Dim L1 As Double
        Dim φ2 As Double
        Dim L2 As Double
        Dim f As Double '扁平率
        Dim a As Double '長半径
        Dim L As Double
        Dim Ld As Double
        Dim Δ As Double
        Dim Δd As Double
        Dim Σ As Double
        Dim Σd As Double
        Dim ξ As Double
        Dim ξd As Double
        Dim η As Double
        Dim ηd As Double
        Dim X As Double
        Dim Y As Double
        Dim c As Double
        Dim ε As Double
        Dim u1 As Double
        Dim u2 As Double
        Dim θ As Double
        Dim R As Double
        Dim d1 As Double
        Dim d2 As Double
        Dim f1 As Double
        Dim q As Double
        Dim γ0 As Double
        Dim A0 As Double
        Dim B0 As Double
        Dim ψ As Double
        Dim j As Double
        Dim j1 As Double
        Dim k As Double
        Dim ψd As Double
        Dim ψdd As Double
        Dim α As Double
        Dim Γ As Double
        Dim L_A As Double
        Dim n0 As Double
        Dim s As Double
        Dim ζ As Double
        Dim ζd As Double
        Dim Δα As Double
        Dim g As Double
        Dim h As Double
        Dim L_J As Double
        Dim L_K As Double
        Dim L_D As Double
        Dim L_E As Double
        Dim L_F As Double
        Dim L_G As Double
        Dim L_B As Double
        Dim m As Double
        Dim N As Double
        Dim w As Double
        Dim Tmp1 As Double
        Dim Tmp2 As Double
        Dim Tmp3 As Double
        Dim ValBak As Double
        Dim IsUseFirst As Boolean
        Dim IsRepeat As Boolean
        Dim IsCalced As Boolean
        Dim IsReverse As Boolean

        Const MARUME As Short = 20

        'もし緯度経度が同じ場合には0を返却し終了する。
        If (plngFromLatMS = plngToLatMS) And (plngFromLonMS = plngToLonMS) Then
            Return 0
        End If

        '初期設定
        f = 1.0# / 299.1528 '扁平率
        a = 6377397.155 '長半径

        'ラジアン
        If plngFromLatMS > plngToLatMS Then
            IsReverse = True

            φ1 = BLCLongDegreeToRad(plngToLatMS)
            L1 = BLCLongDegreeToRad(plngToLonMS)
            φ2 = BLCLongDegreeToRad(plngFromLatMS)
            L2 = BLCLongDegreeToRad(plngFromLonMS)
        Else
            φ1 = BLCLongDegreeToRad(plngFromLatMS)
            L1 = BLCLongDegreeToRad(plngFromLonMS)
            φ2 = BLCLongDegreeToRad(plngToLatMS)
            L2 = BLCLongDegreeToRad(plngToLonMS)
        End If

        L = L2 - L1
        Ld = AngleToRad(180.0#) - L
        Δ = (-φ1 + φ2)
        Σ = (φ1 + φ2)

        'Σd =  u1 + u2 = 下の式
        Σd = System.Math.Atan(((1 - f) * System.Math.Sin(Σ)) / (System.Math.Cos(Σ) + f * (2 - f) * System.Math.Sin(φ1) * System.Math.Sin(φ2)))

        'Δd = -u1 + u2 = 下の式
        Δd = System.Math.Atan(((1 - f) * System.Math.Sin(Δ)) / (System.Math.Cos(Δ) - f * (2 - f) * System.Math.Sin(φ1) * System.Math.Sin(φ2)))

        '上記の式からu1とu2を求める。
        u1 = (Σd - Δd) / 2.0#
        u2 = (Σd + Δd) / 2.0#

        ξ = System.Math.Cos(Σd / 2.0#)
        ξd = System.Math.Sin(Σd / 2.0#)

        η = System.Math.Sin(Δd / 2.0#)
        ηd = System.Math.Cos(Δd / 2.0#)

        X = System.Math.Sin(u1) * System.Math.Sin(u2)
        Y = System.Math.Cos(u1) * System.Math.Cos(u2)

        c = (Y * System.Math.Cos(L)) + X

        ε = (f * (2.0# - f)) / ((1.0# - f) ^ 2)


        'θの初期値を求める
        '(1) c >= 0の場合
        If c >= 0# Then
            IsRepeat = True
            IsUseFirst = True

            θ = L * (1.0# + (f * Y))

            '(2) 0 > c >= -cos(3°cosu1)　の場合
        ElseIf (c < 0#) And (c >= (-1.0# * System.Math.Cos(3.0# * System.Math.Cos(u1)))) Then
            IsRepeat = True

            θ = Ld

            '(3) c < -cos(3°cosu1)　の場合
        ElseIf (c < (-1 * System.Math.Cos(3 + System.Math.Cos(u1)))) Then

            '※共通処理
            R = f * PI * (System.Math.Cos(u1) ^ 2) * (1.0# - (1.0# / 4.0#) * f * (1.0# + f) * (System.Math.Sin(u1) ^ 2) + (3.0# / 16.0#) * (f ^ 2) * (System.Math.Sin(u1) ^ 4))

            d1 = (Ld * System.Math.Cos(u1)) - R
            d2 = System.Math.Abs(Σd) + R

            q = Ld / (f * PI)

            f1 = (1.0# / 4.0#) * f * (1.0# + (1.0# / 2.0#) * f)

            γ0 = q + (f1 * q) - (f1 * (q ^ 3))

            '(a)Σ<>0の場合θの初期値の計算
            If Σ <> 0 Then
                IsRepeat = True

                A0 = System.Math.Atan(d1 / d2)
                B0 = Arcsin(R / System.Math.Sqrt((d1 ^ 2) + (d2 ^ 2)))
                ψ = A0 + B0

                j = γ0 / System.Math.Cos(u1)

                k = (1.0# + f1) * System.Math.Abs(Σd) * ((1.0# - f * Y) / (f * PI * Y))

                j1 = j / (1.0# + k * Sec(ψ))

                ψd = Arcsin(j1)
                ψdd = Arcsin((System.Math.Cos(u1) / System.Math.Cos(u2)) * j1)

                θ = 2.0# * System.Math.Atan((System.Math.Tan((ψd + ψdd) / 2.0#) * System.Math.Sin(System.Math.Abs(Σd) / 2.0#)) / System.Math.Cos(Δd / 2.0#))
                '(b)Σ = 厳密に0の場合
            Else
                '(b1) d1 > 0　の場合
                If d1 > 0# Then
                    IsRepeat = True

                    θ = Ld

                    '(b2) d1 = 0　の場合
                ElseIf d1 = 0# Then
                    IsCalced = True

                    '角度と距離
                    α1 = AngleToRad(90.0#)
                    α2 = AngleToRad(90.0#)
                    α21 = AngleToRad(270.0#)
                    Γ = (System.Math.Sin(u1) ^ 2)

                    n0 = (ε * Γ) / ((System.Math.Sqrt(1.0# + ε * Γ) + 1.0#) ^ 2)

                    L_A = (1.0# + n0) * (1 + (5.0# / 4.0#) * (n0 ^ 2))

                    s = (1.0# - f) * a * L_A * PI

                    '(b3) d1 < 0　の場合
                ElseIf d1 < 0 Then
                    IsCalced = True

                    Do
                        ValBak = Γ
                        Γ = q / (1.0# - L_D * Γ)
                        Γ = 1.0# - (Γ ^ 2)
                        L_D = ((1.0# / 4.0#) * f * (1 + f)) - ((3.0# / 16.0#) * (f ^ 2) * Γ)

                        If System.Math.Round(Γ, MARUME) = System.Math.Round(γ0, MARUME) Then
                            Exit Do
                        ElseIf ValBak = Γ Then
                            Exit Do
                        End If
                    Loop

                    m = 1 - q * Sec(u1)
                    N = (L_D * Γ) / (1 - L_D * Γ)
                    w = m - N + m * N


                    '角度と距離
                    If w <= 0# Then
                        α1 = AngleToRad(90.0#)
                    ElseIf w > 0 Then
                        α1 = AngleToRad(90.0#) - 2 * Arcsin(System.Math.Sqrt(w / 2.0#))
                    End If

                    α2 = AngleToRad(180.0#) - α1
                    α21 = AngleToRad(180.0#) * α2

                    L_A = (1.0# + n0) * (1 + (5.0# / 4.0#) * (n0 ^ 2))

                    s = (1 - f) * a * L_A * PI
                End If
            End If
        End If


        '値が求められていない場合は計算を行う
        If Not (IsCalced) Then
            If IsRepeat Then
                Do
                    If IsUseFirst Then
                        g = System.Math.Sqrt(((η ^ 2) * (System.Math.Cos(θ / 2.0#) ^ 2)) + ((ξ ^ 2) * (System.Math.Sin(θ / 2.0#) ^ 2)))
                    Else
                        g = System.Math.Sqrt(((η ^ 2) * (System.Math.Sin(θ / 2.0#) ^ 2)) + ((ξ ^ 2) * (System.Math.Cos(θ / 2.0#) ^ 2)))
                    End If

                    If IsUseFirst Then
                        h = System.Math.Sqrt(((ηd ^ 2) * (System.Math.Cos(θ / 2.0#) ^ 2)) + ((ξd ^ 2) * (System.Math.Sin(θ / 2.0#) ^ 2)))
                    Else
                        h = System.Math.Sqrt(((ηd ^ 2) * (System.Math.Sin(θ / 2.0#) ^ 2)) + ((ξd ^ 2) * (System.Math.Cos(θ / 2.0#) ^ 2)))
                    End If

                    Σ = 2.0# * System.Math.Atan(g / h)

                    L_J = 2.0# * g * h

                    L_K = (h ^ 2) - (g ^ 2)

                    Γ = (Y * System.Math.Sin(θ)) / L_J

                    Γ = 1.0# - (Γ ^ 2)

                    ζ = Γ * L_K - 2 * X
                    ζd = ζ + X

                    L_D = (1.0# / 4.0#) * f * (1.0# + f) - (3.0# / 16.0#) * (f ^ 2) * Γ


                    Tmp1 = (2.0# * (ζ ^ 2) - (Γ ^ 2))
                    Tmp2 = (ζ + L_D * L_K * Tmp1)
                    Tmp3 = (Σ + L_D * L_J * Tmp2)
                    L_E = (1.0# - L_D * Γ) * (f * Γ) * Tmp3

                    '                L_E = (1# - L_D * Γ) * (f * γ) * (σ + L_D * L_J * (ζ + L_D * L_K * (2# * (ζ ^ 2) - (Γ ^ 2))))

                    '値の保存
                    ValBak = L_F
                    If IsUseFirst Then
                        L_F = θ - L - L_E
                    Else
                        L_F = θ - Ld + L_E
                    End If

                    L_G = f * (Γ ^ 2) * (1.0# - 2.0# * L_D * Γ) + f * ζd * (Σ / L_J) * (1 - L_D * Γ + (1.0# / 2.0#) * f * (Γ ^ 2)) + (1.0# / 4.0#) * (f ^ 2) * ζ * ζd

                    θ -= ((L_F) / (1.0# - L_G))

                    If System.Math.Round(L_F, MARUME) = 0 Then
                        Exit Do
                    ElseIf ValBak = L_F Then
                        Exit Do
                    End If
                    '丸めている
                Loop
            End If

            '■方位角の計算
            If IsUseFirst Then
                α = System.Math.Atan(ξ * System.Math.Tan(θ / 2.0#) / η)
            Else
                α = System.Math.Atan(ηd * System.Math.Tan(θ / 2.0#) / ξd)
            End If

            If IsUseFirst Then
                Δα = System.Math.Atan((ξd * System.Math.Tan(θ / 2.0#)) / ηd)
            Else
                Δα = System.Math.Atan((η * System.Math.Tan(θ / 2.0#)) / ξ)
            End If

            α1 = α - (Δα / 2.0#)

            If IsUseFirst Then
                α2 = α + (Δα / 2.0#)
            Else
                α2 = AngleToRad(180.0#) - α - (Δα / 2.0#)
            End If

            α21 = AngleToRad(180.0#) + α2

            '■測地長線の計算
            n0 = (ε * Γ) / ((System.Math.Sqrt(1.0# + ε * Γ) + 1.0#) ^ 2)

            L_A = (1.0# + n0) * (1 + (4.0# / 5.0#) * (n0 ^ 2))

            L_B = (ε * (1.0# - (3.0# / 8.0#) * (n0 ^ 2))) / ((System.Math.Sqrt(1.0# + ε * Γ) + 1.0#) ^ 2)

            Tmp1 = L_K * ((Γ ^ 2) - 2.0# * (ζ ^ 2)) - (1.0# / 6.0#) * L_B * ζ * (1.0# - 4.0# * (L_K ^ 2)) * (3.0# * (Γ ^ 2) - 4.0# * (ζ ^ 2))
            Tmp2 = ζ - (1.0# / 4.0#) * L_B * Tmp1
            Tmp3 = Σ - L_B * L_J * Tmp2

            s = (1.0# - f) * a * L_A * Tmp3

            '        s = (1# - f) * a * L_A * ( _
            'σ - L_B * L_J * ( _
            'ζ - (1# / 4#) * L_B * ( _
            'L_K * ((Γ ^ 2) - 2# * (ζ ^ 2)) - (1# / 6#) * L_B * ζ * (1# - 4# * (L_K ^ 2)) * (3# * (Γ ^ 2) - 4# * (ζ ^ 2)) _
            ') _
            ') _
            ')

        End If


        '値を返却
        If IsReverse Then
            Tmp1 = α1
            α1 = α21
            α21 = Tmp1
        End If

        Return s
    End Function

    ''' <summary>
    ''' ラジアンを求める（ミリ秒形式（1を1ミリ秒としたInt32型）の緯度経度から）
    ''' </summary>
    Public Function BLCLongDegreeToRad(ByVal plngDegree As Integer) As Double
        Return ((plngDegree / 3600000.0#) * (PI / 180.0#))
    End Function


    Private Function Sec(ByVal pX As Double) As Double
        Return 1 / System.Math.Cos(pX)
    End Function

    Private Function Arcsin(ByVal pX As Double) As Double
        Return System.Math.Atan(pX / System.Math.Sqrt(-pX * pX + 1))
    End Function

    ''' <summary>
    ''' ラジアンを求める（角度から）
    ''' </summary>
    Private Function AngleToRad(ByVal pdblAngle As Double) As Double
        Return (pdblAngle * (PI / 180.0#))
    End Function

    ''' <summary>
    ''' ミリ秒形式（1を1ミリ秒としたInt32型）をDMS形式(60進表記、度分秒を別で扱う)に変換する
    ''' </summary>
    Public Sub BLCLongMSToDMSmSec(ByVal plngMS As Integer,
                                  ByRef plngRetD As Integer,
                                  ByRef plngRetM As Integer,
                                  ByRef plngRetS As Integer,
                                  ByRef pdblRetmsec As Double)
        plngRetD = CInt(plngMS \ CInt(3600000)) ' 度
        plngRetM = CInt((plngMS \ CInt(60000)) Mod CInt(60)) ' 分
        plngRetS = CInt((plngMS \ CInt(1000)) Mod CInt(60)) ' 秒
        pdblRetmsec = CDbl(plngMS) Mod CDbl(1000) ' ミリ秒
    End Sub

    ''' <summary>
    ''' DMS形式(60進表記、度分秒を別で扱う)をミリ秒形式（1を1ミリ秒としたInt32型）に変換する
    ''' </summary>
    Public Function BLCDMSmSecToLongMS(ByVal plngD As Integer,
                                       ByVal plngM As Integer,
                                       ByVal plngS As Integer,
                                       ByVal pdblmSec As Double) As Integer
        Return (plngD * CInt(3600000)) + (plngM * CInt(60000)) + (plngS * CInt(1000)) + CInt(pdblmSec)
    End Function

    ''' <summary>
    ''' Double型ミリ秒（60分法）をDMS形式(60進表記、度分秒を別で扱う)に変換する
    ''' </summary>
    Public Sub BLCDoubleMSToDMSmSec(ByVal pdblMS As Double,
                                    ByRef plngRetD As Integer,
                                    ByRef plngRetM As Integer,
                                    ByRef plngRetS As Integer,
                                    ByRef pdblRetmsec As Double)
        plngRetD = Int(pdblMS)
        plngRetM = Int((pdblMS - CDbl(plngRetD)) * CDbl(60))
        plngRetS = Int((pdblMS - (CDbl(plngRetD) + (CDbl(plngRetM) / CDbl(60)))) * CDbl(3600))
        pdblRetmsec = (pdblMS - (CDbl(plngRetD) + (CDbl(plngRetM) / CDbl(60)) + (CDbl(plngRetS) / CDbl(3600)))) * CDbl(3600000)
    End Sub

    ''' <summary>
    ''' DMS形式(60進表記、度分秒を別で扱う)をDEG形式(10進表記、1を1度として表記する)に変換する
    ''' </summary>
    Public Function BLCDMSmSecToDoubleMS(ByVal plngD As Integer,
                                         ByVal plngM As Integer,
                                         ByVal plngS As Integer,
                                         ByVal pdblmSec As Double) As Double
        Return CDbl(plngD) + CDbl(plngM) / CDbl(60) + CDbl(plngS) / CDbl(3600) + pdblmSec / CDbl(3600000)
    End Function

    ''' <summary>
    ''' DEG形式(10進表記、1度を1とした十進法)をミリ秒形式（1を1ミリ秒としたInt32型）に変換する
    ''' </summary>
    Public Function BLCDoubleMSToLongMS(ByVal pdblMS As Double) As Integer
        Dim lngD As Integer
        Dim lngM As Integer
        Dim lngS As Integer
        Dim dblMSec As Double

        Call BLCDoubleMSToDMSmSec(pdblMS, lngD, lngM, lngS, dblMSec)

        Return BLCDMSmSecToLongMS(lngD, lngM, lngS, dblMSec)
    End Function

    ''' <summary>
    ''' ミリ秒形式（1を1ミリ秒としたInt32型）をDEG形式(10進表記、1度を1とした十進法)に変換する
    ''' </summary>
    Public Function BLCLongMSToDoubleMS(ByVal plngMS As Integer) As Double
        Dim lngD As Integer
        Dim lngM As Integer
        Dim lngS As Integer
        Dim dblMSec As Double

        Call BLCLongMSToDMSmSec(plngMS, lngD, lngM, lngS, dblMSec)

        Return BLCDMSmSecToDoubleMS(lngD, lngM, lngS, dblMSec)
    End Function

    ''' <summary>
    ''' DMS形式(60進表記、DDDMMSS.SSS)の文字列をDEG形式(10進表記、1度を1とした十進法)に変換する
    ''' </summary>
    Public Function BLCDDDMMSS_sssToDoubleMS(ByVal insDDDMMSSsss As String) As Double
        Dim dblMM As Double
        Dim dblDD As Double
        Dim dblSS As Double
        Dim dblMSec As Double

        If Not IsNumeric(insDDDMMSSsss) Then
            Return 0
        End If

        '-- 度
        dblDD = CDbl(CDbl(insDDDMMSSsss) \ CInt(10000))

        '-- 分
        dblMM = CInt(CDbl(insDDDMMSSsss) \ CInt(100)) - (CInt(CDbl(insDDDMMSSsss) \ CInt(10000)) * CInt(100))
        dblMM /= (CDbl(60) ^ 1)

        '-- 秒
        dblSS = CInt(CDbl(insDDDMMSSsss) \ CInt(1)) - (CInt(CDbl(insDDDMMSSsss) \ CInt(100)) * CInt(100))
        dblSS /= (CDbl(60) ^ 2)

        '-- ミリ秒
        dblMSec = (CDbl(insDDDMMSSsss) - CDbl(CDbl(insDDDMMSSsss) \ CInt(1))) / (CDbl(60) ^ 2)

        Return dblDD + dblMM + dblSS + dblMSec
    End Function

    ''' <summary>
    ''' DMS形式(60進表記、DDDMMSS.SSS)の文字列をミリ秒形式（1を1ミリ秒としたInt32型）に変換する関数
    ''' </summary>
    Public Function BLCDDDMMSS_sssToLongMS(ByVal insDDDMMSSsss As Double) As Integer
        Dim lngD As Integer
        Dim lngM As Integer
        Dim lngS As Integer
        Dim dblMSec As Double
        Dim lngMSec As Integer

        If Not IsNumeric(insDDDMMSSsss) Then
            Return 0
        End If

        '-- 度
        lngD = CDbl(insDDDMMSSsss) \ CInt(10000)
        lngD = lngD * (CInt(60) ^ 2) * CInt(1000)

        '-- 分
        lngM = CInt(CDbl(insDDDMMSSsss) \ CInt(100)) - (CInt(CDbl(insDDDMMSSsss) \ CInt(10000)) * CInt(100))
        lngM = lngM * (CInt(60) ^ 1) * CInt(1000)

        '-- 秒
        lngS = CInt(CDbl(insDDDMMSSsss) \ CInt(1)) - (CInt(CDbl(insDDDMMSSsss) \ CInt(100)) * CInt(100))
        lngS *= CInt(1000)

        '-- ミリ秒
        dblMSec = CDbl(insDDDMMSSsss) - CDbl(CDbl(insDDDMMSSsss) \ CInt(1))
        lngMSec = CInt(Int(dblMSec * CDbl(1000)))

        Return lngD + lngM + lngS + lngMSec
    End Function

    ''' <summary>
    ''' DEG形式(10進表記、1を1度として表記する)を、DMS形式(60進表記、DDDMMSS.SSS)の文字列に変換する関数
    ''' </summary>
    ''' <returns>小数点4桁以降もとりあえず返す</returns>
    Public Function BLCDoubleMSToDDDMMSS_sss(ByVal indDoubleMS As String) As String
        Dim lngD As Integer
        Dim lngM As Integer
        Dim lngS As Integer
        Dim dblMSec As Double
        Dim strmSec As String
        Dim strSeisu As String
        Dim strShousu As String

        Call BLCDoubleMSToDMSmSec(CDbl(indDoubleMS), lngD, lngM, lngS, dblMSec)

        '' 十進数型に変換する
        strmSec = CStr(CDec(CDbl(dblMSec)))

        If InStr(1, strmSec, ".") > 0 Then
            strSeisu = Mid(strmSec, 1, InStr(1, strmSec, ".") - 1)
            strShousu = Mid(strmSec, InStr(1, strmSec, ".") + 1)
        Else
            strSeisu = strmSec
            strShousu = "0"
        End If

        strSeisu = VB6.Format(strSeisu, "000")

        Return CStr(lngD) & VB6.Format(lngM, "00") & VB6.Format(lngS, "00") & "." & strSeisu & strShousu
    End Function

    ''' <summary>
    ''' ミリ秒形式（1を1ミリ秒としたInt32型）をDMS形式(60進表記、DDDMMSS.SSS)の文字列に変換する関数
    ''' </summary>
    ''' <param name="inlLongMS"></param>
    ''' <returns>小数点3桁まで返す</returns>
    Public Function BLCLongMSToDDDMMSS_sss(ByVal inlLongMS As Double) As String
        Dim lngD As Integer
        Dim lngM As Integer
        Dim lngS As Integer
        Dim dblMSec As Double
        Dim strmSec As String
        Dim strSeisu As String

        Call BLCLongMSToDMSmSec(inlLongMS, lngD, lngM, lngS, dblMSec)

        '' 十進数型に変換する
        strmSec = CStr(CDec(CDbl(dblMSec)))

        If InStr(1, strmSec, ".") > 0 Then
            strSeisu = Mid(strmSec, 1, InStr(1, strmSec, ".") - 1)
        Else
            strSeisu = strmSec
        End If
        strSeisu = VB6.Format(strSeisu, "000")

        Return CStr(lngD) & VB6.Format(lngM, "00") & VB6.Format(lngS, "00") & "." & strSeisu
    End Function

End Module
```