27 const double RADIANS_PER_DEGREE = M_PI / 180.0;
28 const double DEGREES_PER_RADIAN = 180.0 / M_PI;
31 const double WGS84_A = 6378137.0;
32 const double WGS84_B = 6356752.31424518;
33 const double WGS84_F = 0.0033528107;
34 const double WGS84_E = 0.0818191908;
35 const double WGS84_EP = 0.0820944379;
38 const double UTM_K0 = 0.9996;
39 const double UTM_FE = 500000.0;
40 const double UTM_FN_N = 0.0;
41 const double UTM_FN_S = 10000000.0;
42 const double UTM_E2 = (WGS84_E * WGS84_E);
43 const double UTM_E4 = (UTM_E2 * UTM_E2);
44 const double UTM_E6 = (UTM_E4 * UTM_E2);
45 const double UTM_EP2 = (UTM_E2 / (1 - UTM_E2));
54 static inline void UTM(
double lat,
double lon,
double* x,
double* y) {
56 const static double m0 = (1 - UTM_E2 / 4 - 3 * UTM_E4 / 64 - 5 * UTM_E6 / 256);
57 const static double m1 = -(3 * UTM_E2 / 8 + 3 * UTM_E4 / 32 + 45 * UTM_E6 / 1024);
58 const static double m2 = (15 * UTM_E4 / 256 + 45 * UTM_E6 / 1024);
59 const static double m3 = -(35 * UTM_E6 / 3072);
62 int cm = ((lon >= 0.0) ? ((int)lon - ((
int)lon) % 6 + 3) : ((int)lon - ((
int)lon) % 6 - 3));
65 double rlat = lat * RADIANS_PER_DEGREE;
66 double rlon = lon * RADIANS_PER_DEGREE;
67 double rlon0 = cm * RADIANS_PER_DEGREE;
70 double slat = sin(rlat);
71 double clat = cos(rlat);
72 double tlat = tan(rlat);
75 double fn = (lat > 0) ? UTM_FN_N : UTM_FN_S;
77 double T = tlat * tlat;
78 double C = UTM_EP2 * clat * clat;
79 double A = (rlon - rlon0) * clat;
80 double M = WGS84_A * (m0 * rlat + m1 * sin(2 * rlat) + m2 * sin(4 * rlat) + m3 * sin(6 * rlat));
81 double V = WGS84_A / sqrt(1 - UTM_E2 * slat * slat);
84 *x = UTM_FE + UTM_K0 * V * (A + (1 - T + C) * pow(A, 3) / 6 + (5 - 18 * T + T * T + 72 * C - 58 * UTM_EP2) * pow(A, 5) / 120);
87 (M + V * tlat * (A * A / 2 + (5 - T + 9 * C + 4 * C * C) * pow(A, 4) / 24 + ((61 - 58 * T + T * T + 600 * C - 330 * UTM_EP2) * pow(A, 6) / 720)));
101 static inline char UTMLetterDesignator(
double Lat) {
102 char LetterDesignator;
104 if ((84 >= Lat) && (Lat >= 72))
105 LetterDesignator =
'X';
106 else if ((72 > Lat) && (Lat >= 64))
107 LetterDesignator =
'W';
108 else if ((64 > Lat) && (Lat >= 56))
109 LetterDesignator =
'V';
110 else if ((56 > Lat) && (Lat >= 48))
111 LetterDesignator =
'U';
112 else if ((48 > Lat) && (Lat >= 40))
113 LetterDesignator =
'T';
114 else if ((40 > Lat) && (Lat >= 32))
115 LetterDesignator =
'S';
116 else if ((32 > Lat) && (Lat >= 24))
117 LetterDesignator =
'R';
118 else if ((24 > Lat) && (Lat >= 16))
119 LetterDesignator =
'Q';
120 else if ((16 > Lat) && (Lat >= 8))
121 LetterDesignator =
'P';
122 else if ((8 > Lat) && (Lat >= 0))
123 LetterDesignator =
'N';
124 else if ((0 > Lat) && (Lat >= -8))
125 LetterDesignator =
'M';
126 else if ((-8 > Lat) && (Lat >= -16))
127 LetterDesignator =
'L';
128 else if ((-16 > Lat) && (Lat >= -24))
129 LetterDesignator =
'K';
130 else if ((-24 > Lat) && (Lat >= -32))
131 LetterDesignator =
'J';
132 else if ((-32 > Lat) && (Lat >= -40))
133 LetterDesignator =
'H';
134 else if ((-40 > Lat) && (Lat >= -48))
135 LetterDesignator =
'G';
136 else if ((-48 > Lat) && (Lat >= -56))
137 LetterDesignator =
'F';
138 else if ((-56 > Lat) && (Lat >= -64))
139 LetterDesignator =
'E';
140 else if ((-64 > Lat) && (Lat >= -72))
141 LetterDesignator =
'D';
142 else if ((-72 > Lat) && (Lat >= -80))
143 LetterDesignator =
'C';
146 LetterDesignator =
'Z';
147 return LetterDesignator;
159 static inline void LLtoUTM(
const double Lat,
const double Long,
double& UTMNorthing,
double& UTMEasting,
char* UTMZone) {
161 double eccSquared = UTM_E2;
165 double eccPrimeSquared;
166 double N, T, C, A, M;
169 double LongTemp = (Long + 180) -
int((Long + 180) / 360) * 360 - 180;
171 double LatRad = Lat * RADIANS_PER_DEGREE;
172 double LongRad = LongTemp * RADIANS_PER_DEGREE;
173 double LongOriginRad;
176 ZoneNumber = int((LongTemp + 180) / 6) + 1;
184 if (Lat >= 56.0 && Lat < 64.0 && LongTemp >= 3.0 && LongTemp < 12.0)
188 if (Lat >= 72.0 && Lat < 84.0) {
189 if (LongTemp >= 0.0 && LongTemp < 9.0)
191 else if (LongTemp >= 9.0 && LongTemp < 21.0)
193 else if (LongTemp >= 21.0 && LongTemp < 33.0)
195 else if (LongTemp >= 33.0 && LongTemp < 42.0)
199 LongOrigin = (ZoneNumber - 1) * 6 - 180 + 3;
200 LongOriginRad = LongOrigin * RADIANS_PER_DEGREE;
203 snprintf(UTMZone, 4,
"%d%c", ZoneNumber, UTMLetterDesignator(Lat));
205 eccPrimeSquared = (eccSquared) / (1 - eccSquared);
207 N = a / sqrt(1 - eccSquared * sin(LatRad) * sin(LatRad));
208 T = tan(LatRad) * tan(LatRad);
209 C = eccPrimeSquared * cos(LatRad) * cos(LatRad);
210 A = cos(LatRad) * (LongRad - LongOriginRad);
212 M = a * ((1 - eccSquared / 4 - 3 * eccSquared * eccSquared / 64 - 5 * eccSquared * eccSquared * eccSquared / 256) * LatRad -
213 (3 * eccSquared / 8 + 3 * eccSquared * eccSquared / 32 + 45 * eccSquared * eccSquared * eccSquared / 1024) * sin(2 * LatRad) +
214 (15 * eccSquared * eccSquared / 256 + 45 * eccSquared * eccSquared * eccSquared / 1024) * sin(4 * LatRad) -
215 (35 * eccSquared * eccSquared * eccSquared / 3072) * sin(6 * LatRad));
218 (double)(k0 * N * (A + (1 - T + C) * A * A * A / 6 + (5 - 18 * T + T * T + 72 * C - 58 * eccPrimeSquared) * A * A * A * A * A / 120) + 500000.0);
220 UTMNorthing = (double)(k0 * (M + N * tan(LatRad) *
221 (A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24 +
222 (61 - 58 * T + T * T + 600 * C - 330 * eccPrimeSquared) * A * A * A * A * A * A / 720)));
224 UTMNorthing += 10000000.0;
227 static inline void LLtoUTM(
const double Lat,
const double Long,
double& UTMNorthing,
double& UTMEasting, std::string& UTMZone) {
228 char zone_buf[] = {0, 0, 0, 0};
230 LLtoUTM(Lat, Long, UTMNorthing, UTMEasting, zone_buf);
245 static inline void UTMtoLL(
const double UTMNorthing,
const double UTMEasting,
const char* UTMZone,
double& Lat,
double& Long) {
248 double eccSquared = UTM_E2;
249 double eccPrimeSquared;
250 double e1 = (1 - sqrt(1 - eccSquared)) / (1 + sqrt(1 - eccSquared));
251 double N1, T1, C1, R1, D, M;
254 [[maybe_unused]]
double phi1;
258 [[maybe_unused]]
int NorthernHemisphere;
260 x = UTMEasting - 500000.0;
263 ZoneNumber = strtoul(UTMZone, &ZoneLetter, 10);
264 if ((*ZoneLetter -
'N') >= 0)
265 NorthernHemisphere = 1;
267 NorthernHemisphere = 0;
271 LongOrigin = (ZoneNumber - 1) * 6 - 180 + 3;
273 eccPrimeSquared = (eccSquared) / (1 - eccSquared);
276 mu = M / (a * (1 - eccSquared / 4 - 3 * eccSquared * eccSquared / 64 - 5 * eccSquared * eccSquared * eccSquared / 256));
278 phi1Rad = mu + (3 * e1 / 2 - 27 * e1 * e1 * e1 / 32) * sin(2 * mu) + (21 * e1 * e1 / 16 - 55 * e1 * e1 * e1 * e1 / 32) * sin(4 * mu) +
279 (151 * e1 * e1 * e1 / 96) * sin(6 * mu);
280 phi1 = phi1Rad * DEGREES_PER_RADIAN;
282 N1 = a / sqrt(1 - eccSquared * sin(phi1Rad) * sin(phi1Rad));
283 T1 = tan(phi1Rad) * tan(phi1Rad);
284 C1 = eccPrimeSquared * cos(phi1Rad) * cos(phi1Rad);
285 R1 = a * (1 - eccSquared) / pow(1 - eccSquared * sin(phi1Rad) * sin(phi1Rad), 1.5);
288 Lat = phi1Rad - (N1 * tan(phi1Rad) / R1) * (D * D / 2 - (5 + 3 * T1 + 10 * C1 - 4 * C1 * C1 - 9 * eccPrimeSquared) * D * D * D * D / 24 +
289 (61 + 90 * T1 + 298 * C1 + 45 * T1 * T1 - 252 * eccPrimeSquared - 3 * C1 * C1) * D * D * D * D * D * D / 720);
290 Lat = Lat * DEGREES_PER_RADIAN;
292 Long = (D - (1 + 2 * T1 + C1) * D * D * D / 6 + (5 - 2 * C1 + 28 * T1 - 3 * C1 * C1 + 8 * eccPrimeSquared + 24 * T1 * T1) * D * D * D * D * D / 120) /
294 Long = LongOrigin + Long * DEGREES_PER_RADIAN;
297 static inline void UTMtoLL(
const double UTMNorthing,
const double UTMEasting, std::string UTMZone,
double& Lat,
double& Long) {
298 UTMtoLL(UTMNorthing, UTMEasting, UTMZone.c_str(), Lat, Long);