ที่จริงแล้ว ผมได้สูตรคำนวนนี้มานานแล้ว ซึ่งมีอยู่สองสูตรคือ
1.แปลงพิกัดภูมิศาสตร์(geographic) เป็น พิกัดกริด(UTM,MGRS)
2.แปลงพิกัดกริด(UTM,MGRS) เป็น พิกัดภูมิศาสตร์(geographic)
สูตรแรกผมเอามาเรียบเรียงและทดลอง จนเขียนเป็นโปรแกรมไว้ใช้งาน ตั้งแต่เมื่อสองปีที่แล้ว
ส่วนสูตรที่ 2 ดองไว้ก่อนเพราะยังไม่จำเป็นต้องใช้ แต่พอดีว่าวันนี้ว่างๆเลยเข้าไปดูเนื้อหาในแฟนเพจของตัวเอง (เพจก็เน่า)
ซึ่งผมได้เขียนบันทึกเกี่ยวกับสูตรนี้ไว้ และก็เริ่มนึกขึ้นมาได้ว่า ตอนที่เราคัดลอกสูตรนี้มา เขาใช้เครื่องหมายทางคณิตศาสตร์ค่อนข้างจะเยอะ
บางอันก็เอามาเรียบเรียงใหม่ไม่ได้ตรงกับของเดิมเปะๆ ก็เลยเกิดความสงสัยว่า มีพิมพ์ผิดบ้างเปล่าน๊า เพราะตั้งแต่ลอกไว้ก็ยังไม่ได้ตรวจสอบสักครั้ง
และวิธีตรวจสอบที่ชัดเจนที่สุดคือ ลองเอามาคำนวนดู แต่จะให้มากดเครื่องคิดเลข หรือ พิมพ์ลงเอ็กเซลก็คงไม่เวิร์คนะ งั้นเขียนโปรแกรมเลยแล้วกัน
วันนี้ว่างๆอยู่ด้วย
ผมออกแบบตามภาพนี้ครับ
กล่องข้อความสองอันบนผมตั้งชื่อว่า X_Text และ Y_Text ตามลำดับครับ
คอมโบบ๊อก ตั้งชื่อว่า zone_text และในรายการค่า ผมใส่ตัวเลขไว้ คือ 1 ถึง 60 (โลกแบ่งเป็น 60 โซน ในระบบ UTM)
กล่องข้อความอันล่างสุดเอาไว้แสดงผมลัพธ์ ผมตั้งชื่อว่า Text_result
และด้านล่างนี้คือโค้ดของโปรแกรมนี้ครับ
Public Class Form1
Public X, Y, K0, a, b, ec, e1sq, arc, mu, ei _
, Ca, Cb, Ccc, Cd, phi1 _
, C1, T1, N1, R1, D _
, fact1, fact2, fact3, fact4 _
, lof1, lof2, lof3 _
, Latitude, Longitude As Double
Public Zone, ZoneCM As Integer
Public target As String
Public Function convert()
'X คือ ระยะ Easting
'Y คือ ระยะ Northing
K0 = 0.9996
'อ่านค่า X และ Y
X = X_Text.Text
Y = Y_Text.Text
'อ่านค่าโซน
Zone = zone_text.Text
'ถ้าใช้ค่า Datum WGS 84 เราจะต้องใช้ค่า
a = 6378137.0
b = 6356752.314
ec = Math.Sqrt(1 - (b / a) ^ 2)
e1sq = ec * ec / (1 - ec * ec)
arc = Y / K0
mu = arc / (a * (1 - ec ^ 2 / 4 - 3 * ec ^ 4 / 64 - 5 * ec ^ 6 / 256))
ei = (1 - (1 - ec * ec) ^ (1 / 2)) / (1 + (1 - ec * ec) ^ (1 / 2))
Ca = 3 * ei / 2 - 27 * ei ^ 3 / 32
Cb = 21 * ei ^ 2 / 16 - 55 * ei ^ 4 / 32
Ccc = 151 * ei ^ 3 / 96
Cd = 1097 * ei ^ 4 / 512
phi1 = mu + Ca * Math.Sin(2 * mu) + Cb * Math.Sin(4 * mu) + Ccc * Math.Sin(6 * mu) + Cd * Math.Sin(8 * mu)
C1 = e1sq * Math.Cos(phi1) ^ 2 'C1 ในต้นฉบับใช้ Q0
T1 = Math.Tan(phi1) ^ 2 'ในต้นฉบับใช้ T0
N1 = a / (1 - (ec * Math.Sin(phi1)) ^ 2) ^ (1 / 2) 'ในต้นฉบับใช้ N0
R1 = a * (1 - ec * ec) / (1 - (ec * Math.Sin(phi1)) ^ 2) ^ (3 / 2) 'ในต้นฉบับใช้ R0
D = (500000 - X) / (N1 * K0) 'ในต้นฉบับใช้ dd0 และ 500000 - X ถูกคำนวนไว้ก่อน
fact1 = N1 * Math.Tan(phi1) / R1
fact2 = D * D / 2
fact3 = (5 + 3 * T1 + 10 * C1 - 4 * C1 * C1 - 9 * e1sq) * D ^ 4 / 24
fact4 = (61 + 90 * T1 + 298 * C1 + 45 * T1 * T1 - 252 * e1sq - 3 * C1 * C1) * D ^ 6 / 720
lof1 = D
lof2 = (1 + 2 * T1 + C1) * D ^ 3 / 6
lof3 = (5 - 2 * C1 + 28 * T1 - 3 * C1 ^ 2 + 8 * e1sq + 24 * T1 ^ 2) * D ^ 5 / 120
'ส่วนนี้ทางต้นฉบับหาค่าไว้ก่อนในเมนเพจ
If Zone > 0 Then
ZoneCM = 6 * Zone - 183
Else
ZoneCM = 3
End If
Latitude = 180 * (phi1 - fact1 * (fact2 + fact3 + fact4)) / Math.PI
Longitude = ZoneCM - (((lof1 - lof2 + lof3) / Math.Cos(phi1)) * 180 / Math.PI)
Text_result.Text = Latitude & "," & Longitude
target = "
https://www.google.com/maps/place/" & Latitude & "," & Longitude
Return ""
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
convert()
End Sub
Private Sub LinkLabel1_LinkClicked(sender As Object, e As LinkLabelLinkClickedEventArgs) Handles LinkLabel1.LinkClicked
System.Diagnostics.Process.Start(target)
End Sub
Private Sub X_Text_TextChanged(sender As Object, e As EventArgs) Handles X_Text.TextChanged
On Error Resume Next
convert()
End Sub
Private Sub Y_Text_TextChanged(sender As Object, e As EventArgs) Handles Y_Text.TextChanged
On Error Resume Next
convert()
End Sub
End Class
โปรแกรมแปลงพิกัด แบบเขียนเอง
1.แปลงพิกัดภูมิศาสตร์(geographic) เป็น พิกัดกริด(UTM,MGRS)
2.แปลงพิกัดกริด(UTM,MGRS) เป็น พิกัดภูมิศาสตร์(geographic)
สูตรแรกผมเอามาเรียบเรียงและทดลอง จนเขียนเป็นโปรแกรมไว้ใช้งาน ตั้งแต่เมื่อสองปีที่แล้ว
ส่วนสูตรที่ 2 ดองไว้ก่อนเพราะยังไม่จำเป็นต้องใช้ แต่พอดีว่าวันนี้ว่างๆเลยเข้าไปดูเนื้อหาในแฟนเพจของตัวเอง (เพจก็เน่า)
ซึ่งผมได้เขียนบันทึกเกี่ยวกับสูตรนี้ไว้ และก็เริ่มนึกขึ้นมาได้ว่า ตอนที่เราคัดลอกสูตรนี้มา เขาใช้เครื่องหมายทางคณิตศาสตร์ค่อนข้างจะเยอะ
บางอันก็เอามาเรียบเรียงใหม่ไม่ได้ตรงกับของเดิมเปะๆ ก็เลยเกิดความสงสัยว่า มีพิมพ์ผิดบ้างเปล่าน๊า เพราะตั้งแต่ลอกไว้ก็ยังไม่ได้ตรวจสอบสักครั้ง
และวิธีตรวจสอบที่ชัดเจนที่สุดคือ ลองเอามาคำนวนดู แต่จะให้มากดเครื่องคิดเลข หรือ พิมพ์ลงเอ็กเซลก็คงไม่เวิร์คนะ งั้นเขียนโปรแกรมเลยแล้วกัน
วันนี้ว่างๆอยู่ด้วย
ผมออกแบบตามภาพนี้ครับ
กล่องข้อความสองอันบนผมตั้งชื่อว่า X_Text และ Y_Text ตามลำดับครับ
คอมโบบ๊อก ตั้งชื่อว่า zone_text และในรายการค่า ผมใส่ตัวเลขไว้ คือ 1 ถึง 60 (โลกแบ่งเป็น 60 โซน ในระบบ UTM)
กล่องข้อความอันล่างสุดเอาไว้แสดงผมลัพธ์ ผมตั้งชื่อว่า Text_result
และด้านล่างนี้คือโค้ดของโปรแกรมนี้ครับ
Public Class Form1
Public X, Y, K0, a, b, ec, e1sq, arc, mu, ei _
, Ca, Cb, Ccc, Cd, phi1 _
, C1, T1, N1, R1, D _
, fact1, fact2, fact3, fact4 _
, lof1, lof2, lof3 _
, Latitude, Longitude As Double
Public Zone, ZoneCM As Integer
Public target As String
Public Function convert()
'X คือ ระยะ Easting
'Y คือ ระยะ Northing
K0 = 0.9996
'อ่านค่า X และ Y
X = X_Text.Text
Y = Y_Text.Text
'อ่านค่าโซน
Zone = zone_text.Text
'ถ้าใช้ค่า Datum WGS 84 เราจะต้องใช้ค่า
a = 6378137.0
b = 6356752.314
ec = Math.Sqrt(1 - (b / a) ^ 2)
e1sq = ec * ec / (1 - ec * ec)
arc = Y / K0
mu = arc / (a * (1 - ec ^ 2 / 4 - 3 * ec ^ 4 / 64 - 5 * ec ^ 6 / 256))
ei = (1 - (1 - ec * ec) ^ (1 / 2)) / (1 + (1 - ec * ec) ^ (1 / 2))
Ca = 3 * ei / 2 - 27 * ei ^ 3 / 32
Cb = 21 * ei ^ 2 / 16 - 55 * ei ^ 4 / 32
Ccc = 151 * ei ^ 3 / 96
Cd = 1097 * ei ^ 4 / 512
phi1 = mu + Ca * Math.Sin(2 * mu) + Cb * Math.Sin(4 * mu) + Ccc * Math.Sin(6 * mu) + Cd * Math.Sin(8 * mu)
C1 = e1sq * Math.Cos(phi1) ^ 2 'C1 ในต้นฉบับใช้ Q0
T1 = Math.Tan(phi1) ^ 2 'ในต้นฉบับใช้ T0
N1 = a / (1 - (ec * Math.Sin(phi1)) ^ 2) ^ (1 / 2) 'ในต้นฉบับใช้ N0
R1 = a * (1 - ec * ec) / (1 - (ec * Math.Sin(phi1)) ^ 2) ^ (3 / 2) 'ในต้นฉบับใช้ R0
D = (500000 - X) / (N1 * K0) 'ในต้นฉบับใช้ dd0 และ 500000 - X ถูกคำนวนไว้ก่อน
fact1 = N1 * Math.Tan(phi1) / R1
fact2 = D * D / 2
fact3 = (5 + 3 * T1 + 10 * C1 - 4 * C1 * C1 - 9 * e1sq) * D ^ 4 / 24
fact4 = (61 + 90 * T1 + 298 * C1 + 45 * T1 * T1 - 252 * e1sq - 3 * C1 * C1) * D ^ 6 / 720
lof1 = D
lof2 = (1 + 2 * T1 + C1) * D ^ 3 / 6
lof3 = (5 - 2 * C1 + 28 * T1 - 3 * C1 ^ 2 + 8 * e1sq + 24 * T1 ^ 2) * D ^ 5 / 120
'ส่วนนี้ทางต้นฉบับหาค่าไว้ก่อนในเมนเพจ
If Zone > 0 Then
ZoneCM = 6 * Zone - 183
Else
ZoneCM = 3
End If
Latitude = 180 * (phi1 - fact1 * (fact2 + fact3 + fact4)) / Math.PI
Longitude = ZoneCM - (((lof1 - lof2 + lof3) / Math.Cos(phi1)) * 180 / Math.PI)
Text_result.Text = Latitude & "," & Longitude
target = "https://www.google.com/maps/place/" & Latitude & "," & Longitude
Return ""
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
convert()
End Sub
Private Sub LinkLabel1_LinkClicked(sender As Object, e As LinkLabelLinkClickedEventArgs) Handles LinkLabel1.LinkClicked
System.Diagnostics.Process.Start(target)
End Sub
Private Sub X_Text_TextChanged(sender As Object, e As EventArgs) Handles X_Text.TextChanged
On Error Resume Next
convert()
End Sub
Private Sub Y_Text_TextChanged(sender As Object, e As EventArgs) Handles Y_Text.TextChanged
On Error Resume Next
convert()
End Sub
End Class