VB.NETで、
緯度:DDMMSS.SSSS
経度:DDDMMSS.SSSS
のDouble型数値があります。
これを例えば
136°40'10.123
と表示するため、DDとMMとSS.SSSSに分離する方法を
探しています。
一旦String型にしてRightを使おうかと考えましたが、
地球上の位置によっては負の値もとり得るので、
どうしようか考えています。
お勧めの関数などご存知のかたいらっしゃいますか。
> 緯度:DDMMSS.SSSS
> 経度:DDDMMSS.SSSS
> のDouble型数値があります。
> これを例えば
> 136°40'10.123
> と表示するため、
小数部は 3 桁でしょうか? 4 桁でしょうか?
Decimal 型とは異なり、Double 型は小数点以下の桁数を保持しないため、
10.1230 と 10.123 を区別することはできません。
> DDとMMとSS.SSSSに分離する方法を
桁数固定で良いなら、書式指定して文字列化してから切り出せば良いと思います。
または、割り算して商と余りを求めるとか。
Dim dbl As Double = 1355010.123R
MsgBox((dbl \ 10000).ToString("+0°;-0°") & _
CStr(Math.Abs(dbl \ 10000)) & "'" & _
Math.Abs(dbl Mod 100.0R).ToString("F3"))
If dbl = 0.0R Then
MsgBox("赤道直下")
Else
MsgBox(String.Format("{0}経 {1}°{2}'{3:F3}", _
If(dbl > 0, "東", "西"), _
dbl \ 10000, _
(dbl \ 100) Mod 100, _
dbl Mod 100.0R))
End If
魔界の仮面弁士さん、ありがとうございます。
小数部は4桁です。
もともとNMEAセンテンスで得られた数値が小数点以下4桁まで
のデータでしたが、
構文解析の過程で結果がDouble型にキャストされたものです。
やはり有効桁が明らかであるならば、一旦
String型に直してから加工するほうがよさそうですね。
ご提案いただいた方法はかねてからイメージしていた対処に
近いものなので、これがベストとして検討します。
たまに○○関数を知らないばかりに長々としたコードを
書いてることが多いもので(汗
というより、もうできちゃってますよね・・・しかも境界条件
分岐まで考慮されてるし・・・
駄目だ、怠けてしまいます、
もっと工夫してもっといいコードに改良します!
> というより、もうできちゃってますよね・・・しかも境界条件
> 分岐まで考慮されてるし・・・
先のコードは、まだ完成していなかったりします(西経値を入力してみてください)。
個々の演算子やメソッドが、どういう働きをしているのかを調べながら、
正しい結果になるように手を加えてみてください。
質問の趣旨と異なるかと思いますが、
生データddmm.mmmmmから処理する関数を作りました。
経度も同じ手順でできるはずです。
魔界の仮面弁士の提示いただいた文字列処理で作り直したら
違うものになってしまいました。
切捨て関数であるToRoundDown関数はネットにあったものを拝借しました。
' ddmm.mmmmm型の緯度データをdd°mm’ss.sssss’’に変換
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim minusFlag As Boolean
Dim dmmLatDbl As Double
Dim dmmLatDec As Decimal
Dim dmsLatStr As String
Dim deg As Decimal
Dim min As Decimal
Dim sec As Decimal
Dim minInt As Integer
dmmLatDbl = 4344.77894
If (dmmLatDbl < 0) Then ' 負の場合はフラグで処理
minusFlag = True
dmmLatDbl = Math.Abs(dmmLatDbl)
Else
minusFlag = False
End If
dmmLatDec = CDec(dmmLatDbl) ' Double型をDecimal型に変換
dmmLatDec = dmmLatDec
If (dmmLatDec <> 0.0) Then
deg = Math.Truncate(dmmLatDec / 100.0) ' 度をdegに
min = ToRoundDown(dmmLatDec - (deg * 100.0), 5) ' 分をminに
minInt = Fix(min) ' 分の整数部
sec = (min - Math.Truncate(min)) * 60 ' minの小数点以下をsecに
dmsLatStr = Convert.ToString(deg)
dmsLatStr = dmsLatStr & "°"
dmsLatStr = dmsLatStr & Convert.ToString(minInt)
dmsLatStr = dmsLatStr & "’"
dmsLatStr = dmsLatStr & Convert.ToString(sec)
dmsLatStr = dmsLatStr & "’’"
If (minusFlag = False) Then
dmsLatStr = "N " & dmsLatStr
Else
dmsLatStr = "S " & dmsLatStr
End If
MessageBox.Show(dmsLatStr)
Else
dmsLatStr = Convert.ToString(0.0)
MessageBox.Show(dmsLatStr)
End If
End Sub
''' ------------------------------------------------------------------------
''' <summary>
''' 指定した精度の数値に切り捨てします。</summary>
''' <param name="dValue">
''' 丸め対象の倍精度浮動小数点数。</param>
''' <param name="iDigits">
''' 戻り値の有効桁数の精度。</param>
''' <returns>
''' iDigits に等しい精度の数値に切り捨てられた数値。</returns>
''' ------------------------------------------------------------------------
Public Shared Function ToRoundDown(ByVal dValue As Double, ByVal iDigits As Integer) As Double
Dim dCoef As Double = System.Math.Pow(10, iDigits)
If dValue > 0 Then
Return System.Math.Floor(dValue * dCoef) / dCoef
Else
Return System.Math.Ceiling(dValue * dCoef) / dCoef
End If
End Function