Не говори ГОП, пока не ...
Фигасе, голодранцы с HDS8m + LSS-1, настоящие люмпены предпочитают Etrex + Max10 ))
Немного непонятно, что есть на входе и что должно быть на выходе; если в экселевских столбцах метры в UTM, а должны получиться координаты в WGS84, то тогда вот модуль на VB/VBA, который можно напрямую использовать в ненаглядном Экселе:
Option Explicit
Option Compare Text
' глобальные переменные для пересчета координат и метка выполнения
Public sngDeg As Single
Public sngMin As Single
Public sngSec As Single
' проекция WGS84, долгота - X - longtitude, широта - Y - latitude
'inverse of flattening 1/f = a/(a - b)
'eccentricity e^2 = 2*f - f^2 ' eccentricity square e2^2 = e^2 / (1-e^2)
Public Const a As Double = 6378137 'major
Public Const B As Double = 6356752.3142 'minor
Public Const e As Double = 0.081819190928906
'Public Const B As Double = 6356752.314245 'minor
'Public Const e As Double = 0.081819190842964
Public Const LonNO As Long = 0 'longitude of natural origin, рад
Public Const LatNO As Long = 0 'latitude of natural origin, рад
Public Const ko As Double = 0.9996 'scale factor at the natural origin, Euc
Public Const FE As Long = 0 'false easting, метр
Public Const FN As Long = 0 'false northing, метр
Public Const n As Double = 2.71828182845904 'натуральное число Е
Public Const pi As Double = 3.14159265358979 'число Пи
'rad = grad * pi / 180
'grad = rad * 180 / pi
Public Const LonNOr As Double = 0 'LonNOr = LonNO * pi / 180
Public Const LatNOr As Double = 0 'LatNOr = LatNO * pi / 180
Public Const kko As Double = 1 'kko = Cos(LatNOr) / (1 - (e ^ 2) * (Sin(LatNOr)) ^ 2) ^ 0.5
Public Function Lon2X(xLon As Double) As Double
' Longitude - совпадает полностью
Lon2X = FE + a * kko * ((xLon * pi / 180) - LonNOr)
End Function
Public Function Lat2Y(yLat As Double) As Double
Dim yN As Double
' Latitude - совпадает до целого при .3142 и до седьмого знака при .314245
yN = Tan((pi / 4) + (yLat * pi / 180) / 2) * (((1 - e * Sin(yLat * pi / 180)) / (1 + e * Sin(yLat * pi / 180))) ^ (e / 2))
Lat2Y = FN + a * kko * (Log(yN) / Log(n))
End Function
Public Function X2Lon(xE As Double) As Double
Dim xLon As Double
' Longitude - совпадает полностью
xLon = ((xE - FE) / (a * kko)) + LonNOr
X2Lon = RoundN(xLon * 180 / pi, 6)
End Function
Public Function Y2Lat(yN As Double) As Double
Dim yLat As Double, x As Double, t As Double
' Latitude - совпадает до 8-го знака
t = n ^ ((FN - yN) / (a * kko))
x = (pi / 2) - (2 * Atn(t))
yLat = x + ((e ^ 2) / 2 + 5 * (e ^ 4) / 24 + (e ^ 6) / 12 + 13 * (e ^ 8) / 360) * Sin(2 * x) + _
(7 * (e ^ 4) / 48 + 29 * (e ^ 6) / 240 + 811 * (e ^ 8) / 11520) * Sin(4 * x) + _
(7 * (e ^ 6) / 120 + 81 * (e ^ 8) / 1120) * Sin(6 * x) + (4279 * (e ^ 8) / 161280) * Sin(8 * x)
Y2Lat = RoundN(yLat * 180 / pi, 6)
End Function
Public Function RoundN(Number As Double, Decimals As Integer) As Double
' округляет число до заданного количества десятичных знаков
' 0,5 округляется в сторону увеличения
Dim Factor As Double, temp As Double
Factor = 10 ^ Decimals
temp = Number * Factor + 0.5
RoundN = Int(temp) / Factor
End Function
и еще пара функций преобразования координат:
Public Function NMEA2G(NMEA_Deg As String) As Double
' перевод NMEA вывода ГрадМин.мин в десятичные градусы
If Len(NMEA_Deg) = 9 Then
sngDeg = Val(Left(NMEA_Deg, 2))
ElseIf Len(NMEA_Deg) = 10 Then
sngDeg = Val(Left(NMEA_Deg, 3))
Else
Exit Function
End If
sngMin = Val(Mid(NMEA_Deg, InStr(1, NMEA_Deg, ".") - 2)) / 60
NMEA2G = sngDeg + sngMin
End Function
Public Function GMM2G(ByVal GMM As String) As String
' перевод Град Мин,мин в десятичные градусы
Dim sngMinDec As Single, sngSecDec As Single
On Error GoTo Skip
sngDeg = Val(Left(GMM, InStr(1, GMM, " ") - 1))
sngMin = Val(Mid(GMM, InStr(1, GMM, " ") + 1, InStr(1, GMM, ",") - InStr(1, GMM, " ") - 1))
sngSec = Val("0," & Mid(GMM, InStr(1, GMM, ",") + 1)) * 60
sngMinDec = sngMin / 60
sngSecDec = sngSec / 3600
Skip:
GMM2G = CStr(Int((sngDeg + sngMin + sngSecDec) * 1000000) / 1000000)
GMM2G = FFormat(GMM2G, 6, ",")
End Function
Public Function GMS2G(ByVal GMS As String) As String
' перевод Град°Мин'Сек" в десятичные градусы
Dim sngMinDec As Single, sngSecDec As Single
On Error GoTo Skip
sngDeg = Val(Left(GMS, InStr(1, GMS, "°") - 1))
sngMin = Val(Mid(GMS, InStr(1, GMS, "°") + 1, InStr(1, GMS, "'") - InStr(1, GMS, "°") - 1))
sngSec = Val(Mid(GMS, InStr(1, GMS, "'") + 1, Len(GMS) - InStr(1, GMS, "'") - 1))
sngMinDec = sngMin / 60
sngSecDec = sngSec / 3600
Skip:
GMS2G = CStr(Int((sngDeg + sngMinDec + sngSecDec) * 1000000) / 1000000)
GMS2G = FFormat(GMS2G, 6, ",")
End Function
Public Function G2GMS(ByVal GGG As Single, Optional Ozi As Boolean = True) As String
' перевод десятичных градусов в Град°Мин'Сек"
sngDeg = Int(GGG)
sngMin = (GGG - sngDeg) * 60
sngSec = (sngMin - Int(sngMin)) * 60
'sngSec = Format(((sngMin - Int(sngMin)) * 60), "0")
If Ozi = True Then
' для мап-файла ози
If Int(sngMin) < 10 Then G2GMS = sngDeg & "," & Int(sngMin) & "." & Right(FFormat(CStr(sngMin), 5, ","), 5) _
Else G2GMS = sngDeg & "," & Int(sngMin) & "." & Right(FFormat(CStr(sngMin), 5, ","), 5)
Else
' для обычного текста
G2GMS = sngDeg & "°" & Int(sngMin) & "'" & Int(sngSec * 1000000) / 1000000 & Chr$(34)
End If
End Function
ну и до кучи модули работы с номенклатурными листами ГШ
Public Function XY2GST(xLon As Double, yLat As Double) As String
' поиск номенклатурного листа 100.000 по координатам левого верхнего угла
' пример: N36 - N=52-56гр, 36=30-36гр
Dim LatKm100 As Double, LonKm100 As Long, Km100 As String
On Error GoTo Skip
' поправка при попадании координаты в угол квадрата
If Int(yLat / 4) = yLat / 4 Then yLat = yLat - 0.000001
' координаты находятся за пределами России (? проверка уже проведена...)
If Int(yLat / 4) < 4 Or Int(yLat / 4) > 32 _
Or Int(xLon / 6) + 30 < 21 Or Int(xLon / 6) + 30 > 189 Then GoTo Skip
' определяем лист 1.000.000 - широта 4гр, долгота 6гр
' широта от экватора к полюсам A,B,C - Asc("A")=65 ANSI Chr(65)="A" Asc("-")=45
' долгота не от 0 гринвича, а от 180гр меридиана: те +30 квадратов
XY2GST = Chr(Int(yLat / 4) + 65) & "-" & (Int(xLon / 6) + 31)
' определяем лист 100.000 - широта 20мин, долгота 30мин (144шт)
LonKm100 = Int((xLon - Int(xLon / 6) * 6) / 0.5) + 1
LatKm100 = 11 - Int((yLat - Int(yLat / 4) * 4) / (1 / 3))
' вычисляем номер квадрата
Km100 = 12 * LatKm100 + LonKm100
If Len(Km100) = 1 Then Km100 = "0" & Km100
If Len(Km100) = 2 Then Km100 = "0" & Km100
XY2GST = XY2GST & "-" & Km100
If CLng(Km100) > 144 Then XY2GST = ""
Skip:
End Function
Public Function GST2LL(nList As String) As String
' поиск координат углов снимка по номенклатурному квадрату
Dim LatList As Long, LonList As Long, ListKm100 As Long, Km050 As String, Km500 As String
Dim LonBeg As Double, LatBeg As Double, LonEnd As Double, LatEnd As Double
On Error GoTo Skip
' лист 1'000'000, левый верхний угол
LonBeg = (CLng(Mid(nList, 3, 2)) - 30 - 1) * 6
LatBeg = (Asc(Left(nList, 1)) - 64) * 4
If Len(nList) = 4 Then
' лист 1'000'000, правый нижний угол
LonEnd = LonBeg + 6: LatEnd = LatBeg - 4
ElseIf Len(nList) = 6 Then
' лист 500'000, все углы
Km500 = Right(nList, 1)
If Km500 = "А" Then
LonEnd = LonBeg + 3: LatEnd = LatBeg - 2
ElseIf Km500 = "Б" Then
LonBeg = LonBeg + 3: LonEnd = LonBeg + 3: LatEnd = LatBeg - 2
ElseIf Km500 = "В" Then
LonEnd = LonBeg + 3: LatBeg = LatBeg - 2: LatEnd = LatBeg - 2
ElseIf Km500 = "Г" Then
LonBeg = LonBeg + 3: LonEnd = LonBeg + 3
LatBeg = LatBeg - 2: LatEnd = LatBeg - 2
End If
ElseIf Len(nList) >= 8 Then
' лист 100'000, все углы
ListKm100 = CLng(Mid(nList, 6, 3))
If ListKm100 < 1 Or ListKm100 > 144 Then GoTo Skip
LonBeg = LonBeg + (ListKm100 - (Int((ListKm100 - 1) / 12) * 12)) * 0.5 - 0.5
LatBeg = LatBeg - Int((ListKm100 - 1) / 12) * (1 / 3)
LonEnd = LonBeg + 0.5: LatEnd = LatBeg - (1 / 3)
End If
If Len(nList) = 10 Then
' лист 50'000, все углы
Km050 = Right(nList, 1)
If Km050 = "А" Then
LonEnd = LonBeg + 0.25: LatEnd = LatBeg - (1 / 6)
ElseIf Km050 = "Б" Then
LonBeg = LonBeg + 0.25: LonEnd = LonBeg + 0.25: LatEnd = LatBeg - (1 / 6)
ElseIf Km050 = "В" Then
LonEnd = LonBeg + 0.25: LatBeg = LatBeg - (1 / 6): LatEnd = LatBeg - (1 / 6)
ElseIf Km050 = "Г" Then
LonBeg = LonBeg + 0.25: LonEnd = LonBeg + 0.25
LatBeg = LatBeg - (1 / 6): LatEnd = LatBeg - (1 / 6)
End If
End If
' приводим к текстовому виду с разделителям запятой
GST2LL = FFormat(CStr(LonBeg), 6, ",") & "/" & FFormat(CStr(LatBeg), 6, ",") & "-" & _
FFormat(CStr(LonEnd), 6, ",") & "/" & FFormat(CStr(LatEnd), 6, ",")
Skip:
End Function