Pythonで度と度分秒を相互に変換する
Pythonのmathとdecimalを用いて、測量などで用いられる角度の度と度分秒の変換を行います。なお、計算方法および結果の正しさについては保証をいたしかねますので、実務等にご利用の際は各自の責任において、内容をご確認の上でご使用ください。
- math …数学的処理を行うためのPython標準モジュール(公式ドキュメント)
- decimal …浮動小数点と固定小数点を扱うためのPython標準モジュール(公式ドキュメント)
- 度分秒 …30°25'40"のように、角度を時間の時・分・秒のように60ごとに分割して表示すること
…
手計算での度分秒 <-> 度の変換について触れます。基本的には、時を分や秒に変換するときと同様の計算を行います。
度分秒から度
上記の例(30°25'40")の場合は、分に相当する25を60で割ります(= 0.41666… ≒ 0.4167)。
次に、秒に相当する40を3600で割ります(= 0.01111… ≒ 0.0111)。
その後、得られた結果を30に足すことで、度として30.4278°が得られます。
度から度分秒
上記の結果の30.4278の場合は、まず小数部分に60を掛けます(= 25.668)。
得られた商の整数部分を分として、再び小数部分(0.668)に60を掛けます(= 40.08)。
得られた商の整数部分を秒とすることで、度分秒として30°25'40"が得られます。
今回のコードについても同様の作業を行わせます。
index
コード全体
動作環境:
- python 3.6.8
- windows10 64bit
input | output |
---|---|
度、度分秒 | 度分秒、度 |
import math
from decimal import Decimal, ROUND_HALF_UP
def dms2deg(dms):
# 度分秒から度への変換
h = dms[0]
m = dms[1]
s = dms[2]
deg = Decimal(str(h + (m / 60) + (s / 3600))).quantize(Decimal('0.0001'), rounding=ROUND_HALF_UP)
return deg
def deg2dms(deg):
# 度から度分秒への変換
h = math.modf(deg)[1]
m = math.modf(math.modf(deg)[0] * 60)[1]
s = math.modf(math.modf(deg)[0] * 60)[0]*60
if Decimal(str(s)).quantize(Decimal('0'), rounding=ROUND_HALF_UP) == 60:
s = 0
m = m + 1
if Decimal(str(m)).quantize(Decimal('0'), rounding=ROUND_HALF_UP) == 60:
m = 0
h = h + 1
dms_tap = (int(Decimal(h).quantize(Decimal('0'), rounding=ROUND_HALF_UP)),
int(Decimal(m).quantize(Decimal('0'), rounding=ROUND_HALF_UP)),
int(Decimal(s).quantize(Decimal('0'), rounding=ROUND_HALF_UP)))
return dms_tap
print(dms2deg((30, 25, 40)))
print(deg2dms(30.4278))
print(dms2deg(deg2dms(30.4278)))
重要な箇所を以下に示します。
度分秒から度
def dms2deg(dms):
# 度分秒から度への変換
h = dms[0]
m = dms[1]
s = dms[2]
deg = Decimal(str(h + (m / 60) + (s / 3600))).quantize(Decimal('0.0001'), rounding=ROUND_HALF_UP)
return deg
関数としてdms2deg()を定義します。dmsはdegrees-minutes-secondsの略称としています。
ここでは、度分秒(dms)はタプルとしています。すなわち、30°25'40"の場合には、dms = (30, 25, 40)として、dms[0] = 度、dms[1] = 分、dms[2] = 秒として得ています。
角度の計算のために、h + (m / 60) + (s / 3600)とすることで、先ほどの60で割っていく操作を導入しています。
その際に、Decimalモジュール(公式ドキュメント)を使用し、浮動小数点(float)ではなく固定小数点として扱うことで、計算による誤差を抑えたうえで四捨五入を行っていきます。
quantize()では、Decimal(‘0.0001’)を指定することで、指定した引数と同じ指数に丸め込むことができます。また、rounding=ROUND_HALF_UPを指定して、いわゆる四捨五入を行います。
Decimal()の引数をstrとすることで、floatを引数とする場合の誤差を抑えることができます。
返り値として度(deg)を返します。
度から度分秒
def deg2dms(deg):
# 度から度分秒への変換
h = math.modf(deg)[1]
m = math.modf(math.modf(deg)[0] * 60)[1]
s = math.modf(math.modf(deg)[0] * 60)[0]*60
if Decimal(str(s)).quantize(Decimal('0'), rounding=ROUND_HALF_UP) == 60:
s = 0
m = m + 1
if Decimal(str(m)).quantize(Decimal('0'), rounding=ROUND_HALF_UP) == 60:
m = 0
h = h + 1
dms_tap = (int(Decimal(h).quantize(Decimal('0'), rounding=ROUND_HALF_UP)),
int(Decimal(m).quantize(Decimal('0'), rounding=ROUND_HALF_UP)),
int(Decimal(s).quantize(Decimal('0'), rounding=ROUND_HALF_UP)))
return dms_tap
関数としてdeg2dms()を定義します。
商のうち整数部分と小数部分を分けるために、math.modf()(公式ドキュメント)を用いています。これにより得られる結果は、(小数部分, 整数部分)のタプルとして得られます。
そのため、h = math.modf(deg)[1]のようにすることで、整数部分を度・分・秒として得ています。
if Decimal(str(s)).quantize(Decimal('0'), rounding=ROUND_HALF_UP) == 60:
s = 0
m = m + 1
if Decimal(str(m)).quantize(Decimal('0'), rounding=ROUND_HALF_UP) == 60:
m = 0
h = h + 1
計算結果で整数部分がちょうど60だった時には、繰り上がりを行わせます。
dms_tap = (int(Decimal(h).quantize(Decimal('0'), rounding=ROUND_HALF_UP)),
int(Decimal(m).quantize(Decimal('0'), rounding=ROUND_HALF_UP)),
int(Decimal(s).quantize(Decimal('0'), rounding=ROUND_HALF_UP)))
計算結果を度分秒のタプルとして取得します。四捨五入はdms2degと同様にDecimal()とROUND_HALF_UPを用いており、得られた値はint型にしています。
出力
出力のテストを行います。入力は各関数の引数に直接指定していますが、先ほど手計算でおこなった30°25'40" = (30, 25, 40)を入力しています。
print(dms2deg((30, 25, 40)))
print(deg2dms(30.4278))
print(dms2deg(deg2dms(30.4278)))
一つ目は、(30, 25, 40)を度に変換します。
二つ目は、手計算の結果30°25'40" =30.4278となった値を用いて、度を度分秒に変換します。
三つ目は、二つ目の結果を再帰的にdms2degの引数として指定しており、度 -> 度分秒 -> 度の計算をさせています。
これにより得られる出力は以下の通りです。
30.4278
(30, 25, 40)
30.4278
それぞれ相互に変換することが出来ました。