|
50 | 50 | #include <stdio.h> |
51 | 51 | #include <stdlib.h> |
52 | 52 |
|
| 53 | +#include <GeographicLib/MGRS.hpp> |
| 54 | +#include <GeographicLib/UTMUPS.hpp> |
| 55 | + |
53 | 56 | #include <cmath> |
54 | 57 | #include <string> |
55 | 58 |
|
@@ -139,276 +142,74 @@ static inline void UTM(double lat, double lon, double * x, double * y) |
139 | 142 | } |
140 | 143 |
|
141 | 144 | /** |
142 | | - * Determine the correct UTM letter designator for the |
143 | | - * given latitude |
144 | | - * |
145 | | - * @returns 'Z' if latitude is outside the UTM limits of 84N to 80S |
146 | | - * |
147 | | - * Written by Chuck Gantz- [email protected] |
148 | | - */ |
149 | | -static inline char UTMLetterDesignator(double Lat) |
150 | | -{ |
151 | | - char LetterDesignator; |
152 | | - |
153 | | - if ((84 >= Lat) && (Lat >= 72)) { |
154 | | - LetterDesignator = 'X'; |
155 | | - } else if ((72 > Lat) && (Lat >= 64)) { |
156 | | - LetterDesignator = 'W'; |
157 | | - } else if ((64 > Lat) && (Lat >= 56)) { |
158 | | - LetterDesignator = 'V'; |
159 | | - } else if ((56 > Lat) && (Lat >= 48)) { |
160 | | - LetterDesignator = 'U'; |
161 | | - } else if ((48 > Lat) && (Lat >= 40)) { |
162 | | - LetterDesignator = 'T'; |
163 | | - } else if ((40 > Lat) && (Lat >= 32)) { |
164 | | - LetterDesignator = 'S'; |
165 | | - } else if ((32 > Lat) && (Lat >= 24)) { |
166 | | - LetterDesignator = 'R'; |
167 | | - } else if ((24 > Lat) && (Lat >= 16)) { |
168 | | - LetterDesignator = 'Q'; |
169 | | - } else if ((16 > Lat) && (Lat >= 8)) { |
170 | | - LetterDesignator = 'P'; |
171 | | - } else if ((8 > Lat) && (Lat >= 0)) { |
172 | | - LetterDesignator = 'N'; |
173 | | - } else if ((0 > Lat) && (Lat >= -8)) { |
174 | | - LetterDesignator = 'M'; |
175 | | - } else if ((-8 > Lat) && (Lat >= -16)) { |
176 | | - LetterDesignator = 'L'; |
177 | | - } else if ((-16 > Lat) && (Lat >= -24)) { |
178 | | - LetterDesignator = 'K'; |
179 | | - } else if ((-24 > Lat) && (Lat >= -32)) { |
180 | | - LetterDesignator = 'J'; |
181 | | - } else if ((-32 > Lat) && (Lat >= -40)) { |
182 | | - LetterDesignator = 'H'; |
183 | | - } else if ((-40 > Lat) && (Lat >= -48)) { |
184 | | - LetterDesignator = 'G'; |
185 | | - } else if ((-48 > Lat) && (Lat >= -56)) { |
186 | | - LetterDesignator = 'F'; |
187 | | - } else if ((-56 > Lat) && (Lat >= -64)) { |
188 | | - LetterDesignator = 'E'; |
189 | | - } else if ((-64 > Lat) && (Lat >= -72)) { |
190 | | - LetterDesignator = 'D'; |
191 | | - } else if ((-72 > Lat) && (Lat >= -80)) { |
192 | | - LetterDesignator = 'C'; |
193 | | - } else { |
194 | | - // 'Z' is an error flag, the Latitude is outside the UTM limits |
195 | | - LetterDesignator = 'Z'; |
196 | | - } |
197 | | - return LetterDesignator; |
198 | | -} |
199 | | - |
200 | | -/** |
201 | | - * Convert lat/long to UTM coords. Equations from USGS Bulletin 1532 |
| 145 | + * Convert lat/long to UTM coords. |
202 | 146 | * |
203 | 147 | * East Longitudes are positive, West longitudes are negative. |
204 | 148 | * North latitudes are positive, South latitudes are negative |
205 | 149 | * Lat and Long are in fractional degrees |
206 | | - * Meridian convergence is computed as for Spherical Transverse Mercator, |
207 | | - * which gives quite good approximation. |
208 | 150 | * |
209 | 151 | * @param[out] gamma meridian convergence at point (degrees). |
210 | | - * |
211 | | - * Written by Chuck Gantz- [email protected] |
212 | 152 | */ |
213 | 153 | static inline void LLtoUTM( |
214 | 154 | const double Lat, const double Long, |
215 | 155 | double & UTMNorthing, double & UTMEasting, |
216 | 156 | std::string & UTMZone, double & gamma) |
217 | 157 | { |
218 | | - double a = WGS84_A; |
219 | | - double eccSquared = UTM_E2; |
220 | | - double k0 = UTM_K0; |
221 | | - |
222 | | - double LongOrigin; |
223 | | - double eccPrimeSquared; |
224 | | - double N, T, C, A, M; |
225 | | - |
226 | | - // Make sure the longitude is between -180.00 .. 179.9 |
227 | | - double LongTemp = |
228 | | - (Long + 180) - static_cast<int>((Long + 180) / 360) * 360 - 180; |
229 | | - |
230 | | - double LatRad = Lat * RADIANS_PER_DEGREE; |
231 | | - double LongRad = LongTemp * RADIANS_PER_DEGREE; |
232 | | - double LongOriginRad; |
233 | | - int ZoneNumber; |
234 | | - |
235 | | - ZoneNumber = static_cast<int>((LongTemp + 180) / 6) + 1; |
236 | | - |
237 | | - if (Lat >= 56.0 && Lat < 64.0 && LongTemp >= 3.0 && LongTemp < 12.0) { |
238 | | - ZoneNumber = 32; |
239 | | - } |
240 | | - |
241 | | - // Special zones for Svalbard |
242 | | - if (Lat >= 72.0 && Lat < 84.0) { |
243 | | - if (LongTemp >= 0.0 && LongTemp < 9.0) { |
244 | | - ZoneNumber = 31; |
245 | | - } else if (LongTemp >= 9.0 && LongTemp < 21.0) { |
246 | | - ZoneNumber = 33; |
247 | | - } else if (LongTemp >= 21.0 && LongTemp < 33.0) { |
248 | | - ZoneNumber = 35; |
249 | | - } else if (LongTemp >= 33.0 && LongTemp < 42.0) { |
250 | | - ZoneNumber = 37; |
251 | | - } |
252 | | - } |
253 | | - // +3 puts origin in middle of zone |
254 | | - LongOrigin = (ZoneNumber - 1) * 6 - 180 + 3; |
255 | | - LongOriginRad = LongOrigin * RADIANS_PER_DEGREE; |
256 | | - |
257 | | - // Compute the UTM Zone from the latitude and longitude |
258 | | - char zone_buf[] = {0, 0, 0, 0}; |
259 | | - // We &0x3fU to let GCC know the size of ZoneNumber. In this case, it's under |
260 | | - // 63 (6bits) |
261 | | - snprintf( |
262 | | - zone_buf, sizeof(zone_buf), "%d%c", ZoneNumber & 0x3fU, |
263 | | - UTMLetterDesignator(Lat)); |
264 | | - UTMZone = std::string(zone_buf); |
265 | | - |
266 | | - eccPrimeSquared = (eccSquared) / (1 - eccSquared); |
267 | | - |
268 | | - N = a / sqrt(1 - eccSquared * sin(LatRad) * sin(LatRad)); |
269 | | - T = tan(LatRad) * tan(LatRad); |
270 | | - C = eccPrimeSquared * cos(LatRad) * cos(LatRad); |
271 | | - A = cos(LatRad) * (LongRad - LongOriginRad); |
272 | | - |
273 | | - M = a * |
274 | | - ((1 - eccSquared / 4 - 3 * eccSquared * eccSquared / 64 - |
275 | | - 5 * eccSquared * eccSquared * eccSquared / 256) * |
276 | | - LatRad - |
277 | | - (3 * eccSquared / 8 + 3 * eccSquared * eccSquared / 32 + |
278 | | - 45 * eccSquared * eccSquared * eccSquared / 1024) * |
279 | | - sin(2 * LatRad) + |
280 | | - (15 * eccSquared * eccSquared / 256 + |
281 | | - 45 * eccSquared * eccSquared * eccSquared / 1024) * |
282 | | - sin(4 * LatRad) - |
283 | | - (35 * eccSquared * eccSquared * eccSquared / 3072) * sin(6 * LatRad)); |
284 | | - |
285 | | - UTMEasting = static_cast<double>( |
286 | | - k0 * N * |
287 | | - (A + (1 - T + C) * A * A * A / 6 + |
288 | | - (5 - 18 * T + T * T + 72 * C - 58 * eccPrimeSquared) * A * A * A * |
289 | | - A * A / 120) + |
290 | | - 500000.0); |
291 | | - |
292 | | - UTMNorthing = static_cast<double>( |
293 | | - k0 * |
294 | | - (M + N * tan(LatRad) * |
295 | | - (A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24 + |
296 | | - (61 - 58 * T + T * T + 600 * C - 330 * eccPrimeSquared) * A * |
297 | | - A * A * A * A * A / 720))); |
298 | | - |
299 | | - gamma = atan(tan(LongRad - LongOriginRad) * sin(LatRad)) * DEGREES_PER_RADIAN; |
300 | | - |
301 | | - if (Lat < 0) { |
302 | | - // 10000000 meter offset for southern hemisphere |
303 | | - UTMNorthing += 10000000.0; |
304 | | - } |
| 158 | + int zone; |
| 159 | + bool northp; |
| 160 | + double k_unused; |
| 161 | + GeographicLib::UTMUPS::Forward( |
| 162 | + Lat, Long, zone, northp, UTMEasting, UTMNorthing, gamma, |
| 163 | + k_unused, GeographicLib::UTMUPS::zonespec::MATCH); |
| 164 | + GeographicLib::MGRS::Forward(zone, northp, UTMEasting, UTMNorthing, -1, UTMZone); |
305 | 165 | } |
306 | 166 |
|
307 | 167 | /** |
308 | | - * Convert lat/long to UTM coords. Equations from USGS Bulletin 1532 |
| 168 | + * Convert lat/long to UTM coords. |
309 | 169 | * |
310 | 170 | * East Longitudes are positive, West longitudes are negative. |
311 | 171 | * North latitudes are positive, South latitudes are negative |
312 | 172 | * Lat and Long are in fractional degrees |
313 | | - * |
314 | | - * Written by Chuck Gantz- [email protected] |
315 | 173 | */ |
316 | 174 | static inline void LLtoUTM( |
317 | 175 | const double Lat, const double Long, |
318 | 176 | double & UTMNorthing, double & UTMEasting, |
319 | 177 | std::string & UTMZone) |
320 | 178 | { |
321 | | - double gamma; |
| 179 | + double gamma = 0.0; |
322 | 180 | LLtoUTM(Lat, Long, UTMNorthing, UTMEasting, UTMZone, gamma); |
323 | 181 | } |
324 | 182 |
|
325 | 183 | /** |
326 | | - * Converts UTM coords to lat/long. Equations from USGS Bulletin 1532 |
| 184 | + * Converts UTM coords to lat/long. |
327 | 185 | * |
328 | 186 | * East Longitudes are positive, West longitudes are negative. |
329 | 187 | * North latitudes are positive, South latitudes are negative |
330 | 188 | * Lat and Long are in fractional degrees. |
331 | | - * Meridian convergence is computed as for Spherical Transverse Mercator, |
332 | | - * which gives quite good approximation. |
333 | 189 | * |
334 | 190 | * @param[out] gamma meridian convergence at point (degrees). |
335 | | - * |
336 | | - * Written by Chuck Gantz- [email protected] |
337 | 191 | */ |
338 | 192 | static inline void UTMtoLL( |
339 | 193 | const double UTMNorthing, const double UTMEasting, |
340 | 194 | const std::string & UTMZone, double & Lat, |
341 | 195 | double & Long, double & gamma) |
342 | 196 | { |
343 | | - double k0 = UTM_K0; |
344 | | - double a = WGS84_A; |
345 | | - double eccSquared = UTM_E2; |
346 | | - double eccPrimeSquared; |
347 | | - double e1 = (1 - sqrt(1 - eccSquared)) / (1 + sqrt(1 - eccSquared)); |
348 | | - double N1, T1, C1, R1, D, M; |
349 | | - double LongOrigin; |
350 | | - double mu, phi1Rad; |
351 | | - double x, y; |
352 | | - int ZoneNumber; |
353 | | - char * ZoneLetter; |
354 | | - |
355 | | - x = UTMEasting - 500000.0; // remove 500,000 meter offset for longitude |
356 | | - y = UTMNorthing; |
357 | | - |
358 | | - ZoneNumber = strtoul(UTMZone.c_str(), &ZoneLetter, 10); |
359 | | - if ((*ZoneLetter - 'N') < 0) { |
360 | | - // remove 10,000,000 meter offset used for southern hemisphere |
361 | | - y -= 10000000.0; |
362 | | - } |
363 | | - |
364 | | - // +3 puts origin in middle of zone |
365 | | - LongOrigin = (ZoneNumber - 1) * 6 - 180 + 3; |
366 | | - eccPrimeSquared = (eccSquared) / (1 - eccSquared); |
367 | | - |
368 | | - M = y / k0; |
369 | | - mu = M / (a * (1 - eccSquared / 4 - 3 * eccSquared * eccSquared / 64 - |
370 | | - 5 * eccSquared * eccSquared * eccSquared / 256)); |
371 | | - |
372 | | - phi1Rad = |
373 | | - mu + ((3 * e1 / 2 - 27 * e1 * e1 * e1 / 32) * sin(2 * mu) + |
374 | | - (21 * e1 * e1 / 16 - 55 * e1 * e1 * e1 * e1 / 32) * sin(4 * mu) + |
375 | | - (151 * e1 * e1 * e1 / 96) * sin(6 * mu)); |
376 | | - |
377 | | - N1 = a / sqrt(1 - eccSquared * sin(phi1Rad) * sin(phi1Rad)); |
378 | | - T1 = tan(phi1Rad) * tan(phi1Rad); |
379 | | - C1 = eccPrimeSquared * cos(phi1Rad) * cos(phi1Rad); |
380 | | - R1 = a * (1 - eccSquared) / |
381 | | - pow(1 - eccSquared * sin(phi1Rad) * sin(phi1Rad), 1.5); |
382 | | - D = x / (N1 * k0); |
383 | | - |
384 | | - Lat = phi1Rad - ((N1 * tan(phi1Rad) / R1) * |
385 | | - (D * D / 2 - |
386 | | - (5 + 3 * T1 + 10 * C1 - 4 * C1 * C1 - 9 * eccPrimeSquared) * |
387 | | - D * D * D * D / 24 + |
388 | | - (61 + 90 * T1 + 298 * C1 + 45 * T1 * T1 - |
389 | | - 252 * eccPrimeSquared - 3 * C1 * C1) * |
390 | | - D * D * D * D * D * D / 720)); |
391 | | - |
392 | | - Lat = Lat * DEGREES_PER_RADIAN; |
393 | | - |
394 | | - Long = ((D - (1 + 2 * T1 + C1) * D * D * D / 6 + |
395 | | - (5 - 2 * C1 + 28 * T1 - 3 * C1 * C1 + 8 * eccPrimeSquared + |
396 | | - 24 * T1 * T1) * |
397 | | - D * D * D * D * D / 120) / |
398 | | - cos(phi1Rad)); |
399 | | - Long = LongOrigin + Long * DEGREES_PER_RADIAN; |
400 | | - |
401 | | - gamma = atan(tanh(x / (k0 * a)) * tan(y / (k0 * a))) * DEGREES_PER_RADIAN; |
| 197 | + int zone; |
| 198 | + bool northp; |
| 199 | + double x_unused; |
| 200 | + double y_unused; |
| 201 | + int prec_unused; |
| 202 | + double k_unused; |
| 203 | + GeographicLib::MGRS::Reverse(UTMZone, zone, northp, x_unused, y_unused, prec_unused, true); |
| 204 | + GeographicLib::UTMUPS::Reverse(zone, northp, UTMEasting, UTMNorthing, Lat, Long, gamma, k_unused); |
402 | 205 | } |
403 | 206 |
|
404 | 207 | /** |
405 | | -* Converts UTM coords to lat/long. Equations from USGS Bulletin 1532 |
| 208 | +* Converts UTM coords to lat/long. |
406 | 209 | * |
407 | 210 | * East Longitudes are positive, West longitudes are negative. |
408 | 211 | * North latitudes are positive, South latitudes are negative |
409 | 212 | * Lat and Long are in fractional degrees. |
410 | | -* |
411 | | -* Written by Chuck Gantz- [email protected] |
412 | 213 | */ |
413 | 214 | static inline void UTMtoLL( |
414 | 215 | const double UTMNorthing, const double UTMEasting, |
|
0 commit comments