/src/gdal/frmts/nitf/mgrs.c
Line | Count | Source |
1 | | /*************************************************************************** |
2 | | * |
3 | | * Project: MGRS Converter |
4 | | * Purpose: Geotrans code for MGRS translation (slightly adapted) |
5 | | * Author: Unknown (NIMA) |
6 | | * |
7 | | *************************************************************************** |
8 | | *************************************************************************** |
9 | | * RSC IDENTIFIER: MGRS |
10 | | * |
11 | | * ABSTRACT |
12 | | * |
13 | | * This component converts between geodetic coordinates (latitude and |
14 | | * longitude) and Military Grid Reference System (MGRS) coordinates. |
15 | | * |
16 | | * ERROR HANDLING |
17 | | * |
18 | | * This component checks parameters for valid values. If an invalid value |
19 | | * is found, the error code is combined with the current error code using |
20 | | * the bitwise or. This combining allows multiple error codes to be |
21 | | * returned. The possible error codes are: |
22 | | * |
23 | | * MGRS_NO_ERROR : No errors occurred in function |
24 | | * MGRS_LAT_ERROR : Latitude outside of valid range |
25 | | * (-90 to 90 degrees) |
26 | | * MGRS_LON_ERROR : Longitude outside of valid range |
27 | | * (-180 to 360 degrees) |
28 | | * MGRS_STR_ERROR : An MGRS string error: string too long, |
29 | | * too short, or badly formed |
30 | | * MGRS_PRECISION_ERROR : The precision must be between 0 and 5 |
31 | | * inclusive. |
32 | | * MGRS_A_ERROR : Semi-major axis less than or equal to zero |
33 | | * MGRS_INV_F_ERROR : Inverse flattening outside of valid range |
34 | | * (250 to 350) |
35 | | * MGRS_EASTING_ERROR : Easting outside of valid range |
36 | | * (100,000 to 900,000 meters for UTM) |
37 | | * (0 to 4,000,000 meters for UPS) |
38 | | * MGRS_NORTHING_ERROR : Northing outside of valid range |
39 | | * (0 to 10,000,000 meters for UTM) |
40 | | * (0 to 4,000,000 meters for UPS) |
41 | | * MGRS_ZONE_ERROR : Zone outside of valid range (1 to 60) |
42 | | * MGRS_HEMISPHERE_ERROR : Invalid hemisphere ('N' or 'S') |
43 | | * |
44 | | * REUSE NOTES |
45 | | * |
46 | | * MGRS is intended for reuse by any application that does conversions |
47 | | * between geodetic coordinates and MGRS coordinates. |
48 | | * |
49 | | * REFERENCES |
50 | | * |
51 | | * Further information on MGRS can be found in the Reuse Manual. |
52 | | * |
53 | | * MGRS originated from : U.S. Army Topographic Engineering Center |
54 | | * Geospatial Information Division |
55 | | * 7701 Telegraph Road |
56 | | * Alexandria, VA 22310-3864 |
57 | | * |
58 | | * LICENSES |
59 | | * |
60 | | * None apply to this component. |
61 | | * |
62 | | * RESTRICTIONS |
63 | | * |
64 | | * |
65 | | * ENVIRONMENT |
66 | | * |
67 | | * MGRS was tested and certified in the following environments: |
68 | | * |
69 | | * 1. Solaris 2.5 with GCC version 2.8.1 |
70 | | * 2. Windows 95 with MS Visual C++ version 6 |
71 | | * |
72 | | * MODIFICATIONS |
73 | | * |
74 | | * Date Description |
75 | | * ---- ----------- |
76 | | * 16-11-94 Original Code |
77 | | * 15-09-99 Reengineered upper layers |
78 | | * 02-05-03 Corrected latitude band bug in GRID_UTM |
79 | | * 08-20-03 Reengineered lower layers |
80 | | */ |
81 | | |
82 | | /***************************************************************************/ |
83 | | /* |
84 | | * INCLUDES |
85 | | */ |
86 | | #include <ctype.h> |
87 | | #include <math.h> |
88 | | #include <stdio.h> |
89 | | #include <string.h> |
90 | | #include "mgrs.h" |
91 | | |
92 | | /* |
93 | | * ctype.h - Standard C character handling library |
94 | | * math.h - Standard C math library |
95 | | * stdio.h - Standard C input/output library |
96 | | * string.h - Standard C string handling library |
97 | | * ups.h - Universal Polar Stereographic (UPS) projection |
98 | | * utm.h - Universal Transverse Mercator (UTM) projection |
99 | | * mgrs.h - function prototype error checking |
100 | | */ |
101 | | |
102 | | /***************************************************************************/ |
103 | | /* |
104 | | * GLOBAL DECLARATIONS |
105 | | */ |
106 | | #define DEG_TO_RAD 0.017453292519943296 /* PI/180 */ |
107 | | #define RAD_TO_DEG 57.29577951308232088 /* 180/PI */ |
108 | 0 | #define LETTER_A 0 /* ARRAY INDEX FOR LETTER A */ |
109 | 0 | #define LETTER_B 1 /* ARRAY INDEX FOR LETTER B */ |
110 | 0 | #define LETTER_C 2 /* ARRAY INDEX FOR LETTER C */ |
111 | 0 | #define LETTER_D 3 /* ARRAY INDEX FOR LETTER D */ |
112 | 0 | #define LETTER_E 4 /* ARRAY INDEX FOR LETTER E */ |
113 | | #define LETTER_F 5 /* ARRAY INDEX FOR LETTER E */ |
114 | | #define LETTER_G 6 /* ARRAY INDEX FOR LETTER H */ |
115 | 0 | #define LETTER_H 7 /* ARRAY INDEX FOR LETTER H */ |
116 | 72 | #define LETTER_I 8 /* ARRAY INDEX FOR LETTER I */ |
117 | 0 | #define LETTER_J 9 /* ARRAY INDEX FOR LETTER J */ |
118 | | #define LETTER_K 10 /* ARRAY INDEX FOR LETTER J */ |
119 | 0 | #define LETTER_L 11 /* ARRAY INDEX FOR LETTER L */ |
120 | 0 | #define LETTER_M 12 /* ARRAY INDEX FOR LETTER M */ |
121 | 0 | #define LETTER_N 13 /* ARRAY INDEX FOR LETTER N */ |
122 | 64 | #define LETTER_O 14 /* ARRAY INDEX FOR LETTER O */ |
123 | 0 | #define LETTER_P 15 /* ARRAY INDEX FOR LETTER P */ |
124 | | #define LETTER_Q 16 /* ARRAY INDEX FOR LETTER Q */ |
125 | 0 | #define LETTER_R 17 /* ARRAY INDEX FOR LETTER R */ |
126 | 0 | #define LETTER_S 18 /* ARRAY INDEX FOR LETTER S */ |
127 | | #define LETTER_T 19 /* ARRAY INDEX FOR LETTER S */ |
128 | 0 | #define LETTER_U 20 /* ARRAY INDEX FOR LETTER U */ |
129 | 0 | #define LETTER_V 21 /* ARRAY INDEX FOR LETTER V */ |
130 | 0 | #define LETTER_W 22 /* ARRAY INDEX FOR LETTER W */ |
131 | 0 | #define LETTER_X 23 /* ARRAY INDEX FOR LETTER X */ |
132 | 0 | #define LETTER_Y 24 /* ARRAY INDEX FOR LETTER Y */ |
133 | 0 | #define LETTER_Z 25 /* ARRAY INDEX FOR LETTER Z */ |
134 | | #define MGRS_LETTERS 3 /* NUMBER OF LETTERS IN MGRS */ |
135 | 0 | #define ONEHT 100000.e0 /* ONE HUNDRED THOUSAND */ |
136 | 0 | #define TWOMIL 2000000.e0 /* TWO MILLION */ |
137 | 0 | #define TRUE 1 /* CONSTANT VALUE FOR TRUE VALUE */ |
138 | 0 | #define FALSE 0 /* CONSTANT VALUE FOR FALSE VALUE */ |
139 | | #define PI_OVER_2 (M_PI / 2.0e0) |
140 | | |
141 | | #define MIN_EASTING 100000 |
142 | | #define MAX_EASTING 900000 |
143 | | #define MIN_NORTHING 0 |
144 | | #define MAX_NORTHING 10000000 |
145 | 0 | #define MAX_PRECISION 5 /* Maximum precision of easting & northing */ |
146 | | #define MIN_UTM_LAT ((-80 * M_PI) / 180.0) /* -80 degrees in radians */ |
147 | | #define MAX_UTM_LAT ((84 * M_PI) / 180.0) /* 84 degrees in radians */ |
148 | | |
149 | 0 | #define MIN_EAST_NORTH 0 |
150 | 0 | #define MAX_EAST_NORTH 4000000 |
151 | | |
152 | | /* Ellipsoid parameters, default to WGS 84 */ |
153 | | static const double MGRS_a = |
154 | | 6378137.0; /* Semi-major axis of ellipsoid in meters */ |
155 | | static const double MGRS_f = 1 / 298.257223563; /* Flattening of ellipsoid */ |
156 | | #ifdef unused |
157 | | static const double MGRS_recpf = 298.257223563; |
158 | | #endif |
159 | | static const char MGRS_Ellipsoid_Code[3] = {'W', 'E', 0}; |
160 | | |
161 | | /* |
162 | | * CLARKE_1866 : Ellipsoid code for CLARKE_1866 |
163 | | * CLARKE_1880 : Ellipsoid code for CLARKE_1880 |
164 | | * BESSEL_1841 : Ellipsoid code for BESSEL_1841 |
165 | | * BESSEL_1841_NAMIBIA : Ellipsoid code for BESSEL 1841 (NAMIBIA) |
166 | | */ |
167 | | static const char *const CLARKE_1866 = "CC"; |
168 | | static const char *const CLARKE_1880 = "CD"; |
169 | | static const char *const BESSEL_1841 = "BR"; |
170 | | static const char *const BESSEL_1841_NAMIBIA = "BN"; |
171 | | |
172 | | typedef struct Latitude_Band_Value |
173 | | { |
174 | | /* cppcheck-suppress unusedStructMember */ |
175 | | long letter; /* letter representing latitude band */ |
176 | | double min_northing; /* minimum northing for latitude band */ |
177 | | /* cppcheck-suppress unusedStructMember */ |
178 | | double north; /* upper latitude for latitude band */ |
179 | | /* cppcheck-suppress unusedStructMember */ |
180 | | double south; /* lower latitude for latitude band */ |
181 | | } Latitude_Band; |
182 | | |
183 | | static const Latitude_Band Latitude_Band_Table[20] = { |
184 | | {LETTER_C, 1100000.0, -72.0, -80.5}, {LETTER_D, 2000000.0, -64.0, -72.0}, |
185 | | {LETTER_E, 2800000.0, -56.0, -64.0}, {LETTER_F, 3700000.0, -48.0, -56.0}, |
186 | | {LETTER_G, 4600000.0, -40.0, -48.0}, {LETTER_H, 5500000.0, -32.0, -40.0}, |
187 | | {LETTER_J, 6400000.0, -24.0, -32.0}, {LETTER_K, 7300000.0, -16.0, -24.0}, |
188 | | {LETTER_L, 8200000.0, -8.0, -16.0}, {LETTER_M, 9100000.0, 0.0, -8.0}, |
189 | | {LETTER_N, 0.0, 8.0, 0.0}, {LETTER_P, 800000.0, 16.0, 8.0}, |
190 | | {LETTER_Q, 1700000.0, 24.0, 16.0}, {LETTER_R, 2600000.0, 32.0, 24.0}, |
191 | | {LETTER_S, 3500000.0, 40.0, 32.0}, {LETTER_T, 4400000.0, 48.0, 40.0}, |
192 | | {LETTER_U, 5300000.0, 56.0, 48.0}, {LETTER_V, 6200000.0, 64.0, 56.0}, |
193 | | {LETTER_W, 7000000.0, 72.0, 64.0}, {LETTER_X, 7900000.0, 84.5, 72.0}}; |
194 | | |
195 | | typedef struct UPS_Constant_Value |
196 | | { |
197 | | /* cppcheck-suppress unusedStructMember */ |
198 | | long letter; /* letter representing latitude band */ |
199 | | long ltr2_low_value; /* 2nd letter range - high number */ |
200 | | long ltr2_high_value; /* 2nd letter range - low number */ |
201 | | long ltr3_high_value; /* 3rd letter range - high number (UPS) */ |
202 | | double false_easting; /* False easting based on 2nd letter */ |
203 | | double false_northing; /* False northing based on 3rd letter */ |
204 | | } UPS_Constant; |
205 | | |
206 | | static const UPS_Constant UPS_Constant_Table[4] = { |
207 | | {LETTER_A, LETTER_J, LETTER_Z, LETTER_Z, 800000.0, 800000.0}, |
208 | | {LETTER_B, LETTER_A, LETTER_R, LETTER_Z, 2000000.0, 800000.0}, |
209 | | {LETTER_Y, LETTER_J, LETTER_Z, LETTER_P, 800000.0, 1300000.0}, |
210 | | {LETTER_Z, LETTER_A, LETTER_J, LETTER_P, 2000000.0, 1300000.0}}; |
211 | | |
212 | | /***************************************************************************/ |
213 | | /* |
214 | | * FUNCTIONS |
215 | | */ |
216 | | |
217 | | static long Get_Latitude_Band_Min_Northing(long letter, double *min_northing) |
218 | | /* |
219 | | * The function Get_Latitude_Band_Min_Northing receives a latitude band letter |
220 | | * and uses the Latitude_Band_Table to determine the minimum northing for that |
221 | | * latitude band letter. |
222 | | * |
223 | | * letter : Latitude band letter (input) |
224 | | * min_northing : Minimum northing for that letter (output) |
225 | | */ |
226 | 0 | { /* Get_Latitude_Band_Min_Northing */ |
227 | 0 | long error_code = MGRS_NO_ERROR; |
228 | |
|
229 | 0 | if ((letter >= LETTER_C) && (letter <= LETTER_H)) |
230 | 0 | *min_northing = Latitude_Band_Table[letter - 2].min_northing; |
231 | 0 | else if ((letter >= LETTER_J) && (letter <= LETTER_N)) |
232 | 0 | *min_northing = Latitude_Band_Table[letter - 3].min_northing; |
233 | 0 | else if ((letter >= LETTER_P) && (letter <= LETTER_X)) |
234 | 0 | *min_northing = Latitude_Band_Table[letter - 4].min_northing; |
235 | 0 | else |
236 | 0 | error_code |= MGRS_STRING_ERROR; |
237 | |
|
238 | 0 | return error_code; |
239 | 0 | } /* Get_Latitude_Band_Min_Northing */ |
240 | | |
241 | | #ifdef unused |
242 | | static long Get_Latitude_Range(long letter, double *north, double *south) |
243 | | /* |
244 | | * The function Get_Latitude_Range receives a latitude band letter |
245 | | * and uses the Latitude_Band_Table to determine the latitude band |
246 | | * boundaries for that latitude band letter. |
247 | | * |
248 | | * letter : Latitude band letter (input) |
249 | | * north : Northern latitude boundary for that letter (output) |
250 | | * north : Southern latitude boundary for that letter (output) |
251 | | */ |
252 | | { /* Get_Latitude_Range */ |
253 | | long error_code = MGRS_NO_ERROR; |
254 | | |
255 | | if ((letter >= LETTER_C) && (letter <= LETTER_H)) |
256 | | { |
257 | | *north = Latitude_Band_Table[letter - 2].north * DEG_TO_RAD; |
258 | | *south = Latitude_Band_Table[letter - 2].south * DEG_TO_RAD; |
259 | | } |
260 | | else if ((letter >= LETTER_J) && (letter <= LETTER_N)) |
261 | | { |
262 | | *north = Latitude_Band_Table[letter - 3].north * DEG_TO_RAD; |
263 | | *south = Latitude_Band_Table[letter - 3].south * DEG_TO_RAD; |
264 | | } |
265 | | else if ((letter >= LETTER_P) && (letter <= LETTER_X)) |
266 | | { |
267 | | *north = Latitude_Band_Table[letter - 4].north * DEG_TO_RAD; |
268 | | *south = Latitude_Band_Table[letter - 4].south * DEG_TO_RAD; |
269 | | } |
270 | | else |
271 | | error_code |= MGRS_STRING_ERROR; |
272 | | |
273 | | return error_code; |
274 | | } /* Get_Latitude_Range */ |
275 | | #endif |
276 | | |
277 | | #ifdef unusued |
278 | | static long Get_Latitude_Letter(double latitude, int *letter) |
279 | | /* |
280 | | * The function Get_Latitude_Letter receives a latitude value |
281 | | * and uses the Latitude_Band_Table to determine the latitude band |
282 | | * letter for that latitude. |
283 | | * |
284 | | * latitude : Latitude (input) |
285 | | * letter : Latitude band letter (output) |
286 | | */ |
287 | | { /* Get_Latitude_Letter */ |
288 | | double temp = 0.0; |
289 | | long error_code = MGRS_NO_ERROR; |
290 | | double lat_deg = latitude * RAD_TO_DEG; |
291 | | |
292 | | if (lat_deg >= 72 && lat_deg < 84.5) |
293 | | *letter = LETTER_X; |
294 | | else if (lat_deg > -80.5 && lat_deg < 72) |
295 | | { |
296 | | temp = |
297 | | ((latitude + (80.0 * DEG_TO_RAD)) / (8.0 * DEG_TO_RAD)) + 1.0e-12; |
298 | | *letter = Latitude_Band_Table[(int)temp].letter; |
299 | | } |
300 | | else |
301 | | error_code |= MGRS_LAT_ERROR; |
302 | | |
303 | | return error_code; |
304 | | } /* Get_Latitude_Letter */ |
305 | | #endif |
306 | | |
307 | | #ifdef unused |
308 | | static long Check_Zone(char *MGRS, long *zone_exists) |
309 | | /* |
310 | | * The function Check_Zone receives an MGRS coordinate string. |
311 | | * If a zone is given, TRUE is returned. Otherwise, FALSE |
312 | | * is returned. |
313 | | * |
314 | | * MGRS : MGRS coordinate string (input) |
315 | | * zone_exists : TRUE if a zone is given, |
316 | | * FALSE if a zone is not given (output) |
317 | | */ |
318 | | { /* Check_Zone */ |
319 | | int i = 0; |
320 | | int j = 0; |
321 | | int num_digits = 0; |
322 | | long error_code = MGRS_NO_ERROR; |
323 | | |
324 | | /* skip any leading blanks */ |
325 | | while (MGRS[i] == ' ') |
326 | | i++; |
327 | | j = i; |
328 | | while (isdigit((unsigned char)MGRS[i])) |
329 | | i++; |
330 | | num_digits = i - j; |
331 | | if (num_digits <= 2) |
332 | | if (num_digits > 0) |
333 | | *zone_exists = TRUE; |
334 | | else |
335 | | *zone_exists = FALSE; |
336 | | else |
337 | | error_code |= MGRS_STRING_ERROR; |
338 | | |
339 | | return error_code; |
340 | | } /* Check_Zone */ |
341 | | #endif |
342 | | |
343 | | static long Round_MGRS(double value) |
344 | | /* |
345 | | * The function Round_MGRS rounds the input value to the |
346 | | * nearest integer, using the standard engineering rule. |
347 | | * The rounded integer value is then returned. |
348 | | * |
349 | | * value : Value to be rounded (input) |
350 | | */ |
351 | 0 | { /* Round_MGRS */ |
352 | 0 | double ivalue; |
353 | 0 | long ival; |
354 | 0 | double fraction = modf(value, &ivalue); |
355 | 0 | ival = (long)(ivalue); |
356 | 0 | if ((fraction > 0.5) || ((fraction == 0.5) && (ival % 2 == 1))) |
357 | 0 | ival++; |
358 | 0 | return (ival); |
359 | 0 | } /* Round_MGRS */ |
360 | | |
361 | | static long Make_MGRS_String(char *MGRS, long Zone, int Letters[MGRS_LETTERS], |
362 | | double Easting, double Northing, long Precision) |
363 | | /* |
364 | | * The function Make_MGRS_String constructs an MGRS string |
365 | | * from its component parts. |
366 | | * |
367 | | * MGRS : MGRS coordinate string (output) |
368 | | * Zone : UTM Zone (input) |
369 | | * Letters : MGRS coordinate string letters (input) |
370 | | * Easting : Easting value (input) |
371 | | * Northing : Northing value (input) |
372 | | * Precision : Precision level of MGRS string (input) |
373 | | */ |
374 | 0 | { /* Make_MGRS_String */ |
375 | 0 | long i; |
376 | 0 | long j; |
377 | 0 | double divisor; |
378 | 0 | long east; |
379 | 0 | long north; |
380 | 0 | char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; |
381 | 0 | long error_code = MGRS_NO_ERROR; |
382 | |
|
383 | 0 | i = 0; |
384 | 0 | if (Zone) |
385 | 0 | i = sprintf(MGRS + i, "%2.2ld", Zone); |
386 | 0 | else |
387 | 0 | memcpy(MGRS, " ", 2); // 2 spaces |
388 | |
|
389 | 0 | for (j = 0; j < 3; j++) |
390 | 0 | MGRS[i++] = alphabet[Letters[j]]; |
391 | 0 | divisor = pow(10.0, (5 - Precision)); |
392 | 0 | Easting = fmod(Easting, 100000.0); |
393 | 0 | if (Easting >= 99999.5) |
394 | 0 | Easting = 99999.0; |
395 | 0 | east = (long)(Easting / divisor); |
396 | 0 | i += sprintf(MGRS + i, "%*.*ld", (int)Precision, (int)Precision, east); |
397 | 0 | Northing = fmod(Northing, 100000.0); |
398 | 0 | if (Northing >= 99999.5) |
399 | 0 | Northing = 99999.0; |
400 | 0 | north = (long)(Northing / divisor); |
401 | 0 | /*i += */ sprintf(MGRS + i, "%*.*ld", (int)Precision, (int)Precision, |
402 | 0 | north); |
403 | 0 | return (error_code); |
404 | 0 | } /* Make_MGRS_String */ |
405 | | |
406 | | static long Break_MGRS_String(char *MGRS, long *Zone, |
407 | | long Letters[MGRS_LETTERS], double *Easting, |
408 | | double *Northing, long *Precision) |
409 | | /* |
410 | | * The function Break_MGRS_String breaks down an MGRS |
411 | | * coordinate string into its component parts. |
412 | | * |
413 | | * MGRS : MGRS coordinate string (input) |
414 | | * Zone : UTM Zone (output) |
415 | | * Letters : MGRS coordinate string letters (output) |
416 | | * Easting : Easting value (output) |
417 | | * Northing : Northing value (output) |
418 | | * Precision : Precision level of MGRS string (output) |
419 | | */ |
420 | 644 | { /* Break_MGRS_String */ |
421 | 644 | long num_digits; |
422 | 644 | long num_letters; |
423 | 644 | long i = 0; |
424 | 644 | long j = 0; |
425 | 644 | long error_code = MGRS_NO_ERROR; |
426 | | |
427 | 683 | while (MGRS[i] == ' ') |
428 | 39 | i++; /* skip any leading blanks */ |
429 | 644 | j = i; |
430 | 644 | while (isdigit((unsigned char)MGRS[i])) |
431 | 682 | i++; |
432 | 644 | num_digits = i - j; |
433 | 644 | if (num_digits <= 2) |
434 | 546 | if (num_digits > 0) |
435 | 80 | { |
436 | 80 | char zone_string[3]; |
437 | | /* get zone */ |
438 | 80 | strncpy(zone_string, MGRS + j, 2); |
439 | 80 | zone_string[2] = 0; |
440 | 80 | sscanf(zone_string, "%ld", Zone); |
441 | 80 | if ((*Zone < 1) || (*Zone > 60)) |
442 | 11 | error_code |= MGRS_STRING_ERROR; |
443 | 80 | } |
444 | 466 | else |
445 | 466 | *Zone = 0; |
446 | 98 | else |
447 | 98 | error_code |= MGRS_STRING_ERROR; |
448 | 644 | j = i; |
449 | | |
450 | 644 | while (isalpha((unsigned char)MGRS[i])) |
451 | 2.95k | i++; |
452 | 644 | num_letters = i - j; |
453 | 644 | if (num_letters == 3) |
454 | 24 | { |
455 | | /* get letters */ |
456 | 24 | Letters[0] = (toupper((unsigned char)MGRS[j]) - (long)'A'); |
457 | 24 | if ((Letters[0] == LETTER_I) || (Letters[0] == LETTER_O)) |
458 | 0 | error_code |= MGRS_STRING_ERROR; |
459 | 24 | Letters[1] = (toupper((unsigned char)MGRS[j + 1]) - (long)'A'); |
460 | 24 | if ((Letters[1] == LETTER_I) || (Letters[1] == LETTER_O)) |
461 | 6 | error_code |= MGRS_STRING_ERROR; |
462 | 24 | Letters[2] = (toupper((unsigned char)MGRS[j + 2]) - (long)'A'); |
463 | 24 | if ((Letters[2] == LETTER_I) || (Letters[2] == LETTER_O)) |
464 | 2 | error_code |= MGRS_STRING_ERROR; |
465 | 24 | } |
466 | 620 | else |
467 | 620 | error_code |= MGRS_STRING_ERROR; |
468 | 644 | j = i; |
469 | 644 | while (isdigit((unsigned char)MGRS[i])) |
470 | 865 | i++; |
471 | 644 | num_digits = i - j; |
472 | 644 | if ((num_digits <= 10) && (num_digits % 2 == 0)) |
473 | 577 | { |
474 | 577 | long n; |
475 | 577 | char east_string[6]; |
476 | 577 | char north_string[6]; |
477 | 577 | long east; |
478 | 577 | long north; |
479 | 577 | double multiplier; |
480 | | /* get easting & northing */ |
481 | 577 | n = num_digits / 2; |
482 | 577 | *Precision = n; |
483 | 577 | if (n > 0) |
484 | 69 | { |
485 | 69 | strncpy(east_string, MGRS + j, n); |
486 | 69 | east_string[n] = 0; |
487 | 69 | sscanf(east_string, "%ld", &east); |
488 | 69 | strncpy(north_string, MGRS + j + n, n); |
489 | 69 | north_string[n] = 0; |
490 | 69 | sscanf(north_string, "%ld", &north); |
491 | 69 | multiplier = pow(10.0, 5 - n); |
492 | 69 | *Easting = east * multiplier; |
493 | 69 | *Northing = north * multiplier; |
494 | 69 | } |
495 | 508 | else |
496 | 508 | { |
497 | 508 | *Easting = 0.0; |
498 | 508 | *Northing = 0.0; |
499 | 508 | } |
500 | 577 | } |
501 | 67 | else |
502 | 67 | error_code |= MGRS_STRING_ERROR; |
503 | | |
504 | 644 | return (error_code); |
505 | 644 | } /* Break_MGRS_String */ |
506 | | |
507 | | static void Get_Grid_Values(long zone, long *ltr2_low_value, |
508 | | long *ltr2_high_value, double *false_northing) |
509 | | /* |
510 | | * The function Get_Grid_Values sets the letter range used for |
511 | | * the 2nd letter in the MGRS coordinate string, based on the set |
512 | | * number of the utm zone. It also sets the false northing using a |
513 | | * value of A for the second letter of the grid square, based on |
514 | | * the grid pattern and set number of the utm zone. |
515 | | * |
516 | | * zone : Zone number (input) |
517 | | * ltr2_low_value : 2nd letter low number (output) |
518 | | * ltr2_high_value : 2nd letter high number (output) |
519 | | * false_northing : False northing (output) |
520 | | */ |
521 | 0 | { /* BEGIN Get_Grid_Values */ |
522 | 0 | long set_number; /* Set number (1-6) based on UTM zone number */ |
523 | 0 | long aa_pattern; /* Pattern based on ellipsoid code */ |
524 | |
|
525 | 0 | set_number = zone % 6; |
526 | |
|
527 | 0 | if (!set_number) |
528 | 0 | set_number = 6; |
529 | |
|
530 | 0 | if (!strcmp(MGRS_Ellipsoid_Code, CLARKE_1866) || |
531 | 0 | !strcmp(MGRS_Ellipsoid_Code, CLARKE_1880) || |
532 | 0 | !strcmp(MGRS_Ellipsoid_Code, BESSEL_1841) || |
533 | 0 | !strcmp(MGRS_Ellipsoid_Code, BESSEL_1841_NAMIBIA)) |
534 | 0 | aa_pattern = FALSE; |
535 | 0 | else |
536 | 0 | aa_pattern = TRUE; |
537 | |
|
538 | 0 | if ((set_number == 1) || (set_number == 4)) |
539 | 0 | { |
540 | 0 | *ltr2_low_value = LETTER_A; |
541 | 0 | *ltr2_high_value = LETTER_H; |
542 | 0 | } |
543 | 0 | else if ((set_number == 2) || (set_number == 5)) |
544 | 0 | { |
545 | 0 | *ltr2_low_value = LETTER_J; |
546 | 0 | *ltr2_high_value = LETTER_R; |
547 | 0 | } |
548 | 0 | else if ((set_number == 3) || (set_number == 6)) |
549 | 0 | { |
550 | 0 | *ltr2_low_value = LETTER_S; |
551 | 0 | *ltr2_high_value = LETTER_Z; |
552 | 0 | } |
553 | | |
554 | | /* False northing at A for second letter of grid square */ |
555 | 0 | if (aa_pattern) |
556 | 0 | { |
557 | 0 | if ((set_number % 2) == 0) |
558 | 0 | *false_northing = 1500000.0; |
559 | 0 | else |
560 | 0 | *false_northing = 0.0; |
561 | 0 | } |
562 | 0 | else |
563 | 0 | { |
564 | 0 | if ((set_number % 2) == 0) |
565 | 0 | *false_northing = 500000.0; |
566 | 0 | else |
567 | 0 | *false_northing = 1000000.00; |
568 | 0 | } |
569 | 0 | } /* END OF Get_Grid_Values */ |
570 | | |
571 | | #ifdef unused |
572 | | static long UTM_To_MGRS(long Zone, double Latitude, double Easting, |
573 | | double Northing, long Precision, char *MGRS) |
574 | | /* |
575 | | * The function UTM_To_MGRS calculates an MGRS coordinate string |
576 | | * based on the zone, latitude, easting and northing. |
577 | | * |
578 | | * Zone : Zone number (input) |
579 | | * Latitude : Latitude in radians (input) |
580 | | * Easting : Easting (input) |
581 | | * Northing : Northing (input) |
582 | | * Precision : Precision (input) |
583 | | * MGRS : MGRS coordinate string (output) |
584 | | */ |
585 | | { /* BEGIN UTM_To_MGRS */ |
586 | | double false_northing; /* False northing for 3rd letter */ |
587 | | double grid_easting; /* Easting used to derive 2nd letter of MGRS */ |
588 | | double grid_northing; /* Northing used to derive 3rd letter of MGRS */ |
589 | | long ltr2_low_value; /* 2nd letter range - low number */ |
590 | | long ltr2_high_value; /* 2nd letter range - high number */ |
591 | | int letters[MGRS_LETTERS]; /* Number location of 3 letters in alphabet */ |
592 | | double divisor; |
593 | | long error_code = MGRS_NO_ERROR; |
594 | | |
595 | | /* Round easting and northing values */ |
596 | | divisor = pow(10.0, (5 - Precision)); |
597 | | Easting = Round_MGRS(Easting / divisor) * divisor; |
598 | | Northing = Round_MGRS(Northing / divisor) * divisor; |
599 | | |
600 | | Get_Grid_Values(Zone, <r2_low_value, <r2_high_value, &false_northing); |
601 | | |
602 | | error_code = Get_Latitude_Letter(Latitude, &letters[0]); |
603 | | |
604 | | if (!error_code) |
605 | | { |
606 | | grid_northing = Northing; |
607 | | if (grid_northing == 1.e7) |
608 | | grid_northing = grid_northing - 1.0; |
609 | | |
610 | | while (grid_northing >= TWOMIL) |
611 | | { |
612 | | grid_northing = grid_northing - TWOMIL; |
613 | | } |
614 | | grid_northing = grid_northing - false_northing; |
615 | | |
616 | | if (grid_northing < 0.0) |
617 | | grid_northing = grid_northing + TWOMIL; |
618 | | |
619 | | letters[2] = (long)(grid_northing / ONEHT); |
620 | | if (letters[2] > LETTER_H) |
621 | | letters[2] = letters[2] + 1; |
622 | | |
623 | | if (letters[2] > LETTER_N) |
624 | | letters[2] = letters[2] + 1; |
625 | | |
626 | | grid_easting = Easting; |
627 | | if (((letters[0] == LETTER_V) && (Zone == 31)) && |
628 | | (grid_easting == 500000.0)) |
629 | | grid_easting = grid_easting - 1.0; /* SUBTRACT 1 METER */ |
630 | | |
631 | | letters[1] = ltr2_low_value + ((long)(grid_easting / ONEHT) - 1); |
632 | | if ((ltr2_low_value == LETTER_J) && (letters[1] > LETTER_N)) |
633 | | letters[1] = letters[1] + 1; |
634 | | |
635 | | Make_MGRS_String(MGRS, Zone, letters, Easting, Northing, Precision); |
636 | | } |
637 | | return error_code; |
638 | | } /* END UTM_To_MGRS */ |
639 | | #endif |
640 | | |
641 | | #ifdef unused |
642 | | long Set_MGRS_Parameters(double a, double f, char *Ellipsoid_Code) |
643 | | /* |
644 | | * The function SET_MGRS_PARAMETERS receives the ellipsoid parameters and sets |
645 | | * the corresponding state variables. If any errors occur, the error code(s) |
646 | | * are returned by the function, otherwise MGRS_NO_ERROR is returned. |
647 | | * |
648 | | * a : Semi-major axis of ellipsoid in meters (input) |
649 | | * f : Flattening of ellipsoid (input) |
650 | | * Ellipsoid_Code : 2-letter code for ellipsoid (input) |
651 | | */ |
652 | | { /* Set_MGRS_Parameters */ |
653 | | |
654 | | double inv_f = 1 / f; |
655 | | long Error_Code = MGRS_NO_ERROR; |
656 | | |
657 | | if (a <= 0.0) |
658 | | { /* Semi-major axis must be greater than zero */ |
659 | | Error_Code |= MGRS_A_ERROR; |
660 | | } |
661 | | if ((inv_f < 250) || (inv_f > 350)) |
662 | | { /* Inverse flattening must be between 250 and 350 */ |
663 | | Error_Code |= MGRS_INV_F_ERROR; |
664 | | } |
665 | | if (!Error_Code) |
666 | | { /* no errors */ |
667 | | MGRS_a = a; |
668 | | MGRS_f = f; |
669 | | MGRS_recpf = inv_f; |
670 | | strncpy(MGRS_Ellipsoid_Code, Ellipsoid_Code, |
671 | | sizeof(MGRS_Ellipsoid_Code)); |
672 | | MGRS_Ellipsoid_Code[sizeof(MGRS_Ellipsoid_Code) - 1] = '\0'; |
673 | | } |
674 | | return (Error_Code); |
675 | | } /* Set_MGRS_Parameters */ |
676 | | #endif |
677 | | |
678 | | void Get_MGRS_Parameters(double *a, double *f, char *Ellipsoid_Code) |
679 | | /* |
680 | | * The function Get_MGRS_Parameters returns the current ellipsoid |
681 | | * parameters. |
682 | | * |
683 | | * a : Semi-major axis of ellipsoid, in meters (output) |
684 | | * f : Flattening of ellipsoid (output) |
685 | | * Ellipsoid_Code : 2-letter code for ellipsoid (output) |
686 | | */ |
687 | 0 | { /* Get_MGRS_Parameters */ |
688 | 0 | *a = MGRS_a; |
689 | 0 | *f = MGRS_f; |
690 | 0 | strcpy(Ellipsoid_Code, MGRS_Ellipsoid_Code); |
691 | 0 | return; |
692 | 0 | } /* Get_MGRS_Parameters */ |
693 | | |
694 | | #ifndef GDAL_COMPILATION |
695 | | long Convert_UTM_To_MGRS(long Zone, char Hemisphere, double Easting, |
696 | | double Northing, long Precision, char *MGRS) |
697 | | /* |
698 | | * The function Convert_UTM_To_MGRS converts UTM (zone, easting, and |
699 | | * northing) coordinates to an MGRS coordinate string, according to the |
700 | | * current ellipsoid parameters. If any errors occur, the error code(s) |
701 | | * are returned by the function, otherwise MGRS_NO_ERROR is returned. |
702 | | * |
703 | | * Zone : UTM zone (input) |
704 | | * Hemisphere : North or South hemisphere (input) |
705 | | * Easting : Easting (X) in meters (input) |
706 | | * Northing : Northing (Y) in meters (input) |
707 | | * Precision : Precision level of MGRS string (input) |
708 | | * MGRS : MGRS coordinate string (output) |
709 | | */ |
710 | | { /* Convert_UTM_To_MGRS */ |
711 | | double latitude; /* Latitude of UTM point */ |
712 | | double longitude; /* Longitude of UTM point */ |
713 | | /*long temp_error = MGRS_NO_ERROR; */ |
714 | | long error_code = MGRS_NO_ERROR; |
715 | | |
716 | | if ((Zone < 1) || (Zone > 60)) |
717 | | error_code |= MGRS_ZONE_ERROR; |
718 | | if ((Hemisphere != 'S') && (Hemisphere != 'N')) |
719 | | error_code |= MGRS_HEMISPHERE_ERROR; |
720 | | if ((Easting < MIN_EASTING) || (Easting > MAX_EASTING)) |
721 | | error_code |= MGRS_EASTING_ERROR; |
722 | | if ((Northing < MIN_NORTHING) || (Northing > MAX_NORTHING)) |
723 | | error_code |= MGRS_NORTHING_ERROR; |
724 | | if ((Precision < 0) || (Precision > MAX_PRECISION)) |
725 | | error_code |= MGRS_PRECISION_ERROR; |
726 | | if (!error_code) |
727 | | { |
728 | | Set_UTM_Parameters(MGRS_a, MGRS_f, 0); |
729 | | /*temp_error =*/Convert_UTM_To_Geodetic( |
730 | | Zone, Hemisphere, Easting, Northing, &latitude, &longitude); |
731 | | |
732 | | /* Special check for rounding to (truncated) eastern edge of zone 31V */ |
733 | | if ((Zone == 31) && (latitude >= 56.0 * DEG_TO_RAD) && |
734 | | (latitude < 64.0 * DEG_TO_RAD) && (longitude >= 3.0 * DEG_TO_RAD)) |
735 | | { /* Reconvert to UTM zone 32 */ |
736 | | Set_UTM_Parameters(MGRS_a, MGRS_f, 32); |
737 | | /*temp_error =*/Convert_Geodetic_To_UTM( |
738 | | latitude, longitude, &Zone, &Hemisphere, &Easting, &Northing); |
739 | | } |
740 | | |
741 | | error_code = |
742 | | UTM_To_MGRS(Zone, latitude, Easting, Northing, Precision, MGRS); |
743 | | } |
744 | | return (error_code); |
745 | | } /* Convert_UTM_To_MGRS */ |
746 | | #endif |
747 | | |
748 | | long Convert_MGRS_To_UTM(char *MGRS, long *Zone, char *Hemisphere, |
749 | | double *Easting, double *Northing) |
750 | | /* |
751 | | * The function Convert_MGRS_To_UTM converts an MGRS coordinate string |
752 | | * to UTM projection (zone, hemisphere, easting and northing) coordinates |
753 | | * according to the current ellipsoid parameters. If any errors occur, |
754 | | * the error code(s) are returned by the function, otherwise UTM_NO_ERROR |
755 | | * is returned. |
756 | | * |
757 | | * MGRS : MGRS coordinate string (input) |
758 | | * Zone : UTM zone (output) |
759 | | * Hemisphere : North or South hemisphere (output) |
760 | | * Easting : Easting (X) in meters (output) |
761 | | * Northing : Northing (Y) in meters (output) |
762 | | */ |
763 | 644 | { /* Convert_MGRS_To_UTM */ |
764 | 644 | double scaled_min_northing; |
765 | 644 | double min_northing; |
766 | 644 | long ltr2_low_value = 0; |
767 | 644 | long ltr2_high_value = 0; |
768 | 644 | double false_northing; |
769 | 644 | double grid_easting; /* Easting for 100,000 meter grid square */ |
770 | 644 | double grid_northing; /* Northing for 100,000 meter grid square */ |
771 | 644 | long letters[MGRS_LETTERS]; |
772 | 644 | long in_precision; |
773 | | #ifndef GDAL_COMPILATION |
774 | | double upper_lat_limit; /* North latitude limits based on 1st letter */ |
775 | | double lower_lat_limit; /* South latitude limits based on 1st letter */ |
776 | | double latitude = 0.0; |
777 | | double longitude = 0.0; |
778 | | double divisor = 1.0; |
779 | | #endif |
780 | 644 | long error_code = MGRS_NO_ERROR; |
781 | | |
782 | 644 | error_code = Break_MGRS_String(MGRS, Zone, letters, Easting, Northing, |
783 | 644 | &in_precision); |
784 | 644 | if (!*Zone) |
785 | 564 | error_code |= MGRS_STRING_ERROR; |
786 | 80 | else |
787 | 80 | { |
788 | 80 | if (!error_code) |
789 | 0 | { |
790 | 0 | if ((letters[0] == LETTER_X) && |
791 | 0 | ((*Zone == 32) || (*Zone == 34) || (*Zone == 36))) |
792 | 0 | error_code |= MGRS_STRING_ERROR; |
793 | 0 | else |
794 | 0 | { |
795 | 0 | if (letters[0] < LETTER_N) |
796 | 0 | *Hemisphere = 'S'; |
797 | 0 | else |
798 | 0 | *Hemisphere = 'N'; |
799 | |
|
800 | 0 | Get_Grid_Values(*Zone, <r2_low_value, <r2_high_value, |
801 | 0 | &false_northing); |
802 | | |
803 | | /* Check that the second letter of the MGRS string is within |
804 | | * the range of valid second letter values |
805 | | * Also check that the third letter is valid */ |
806 | 0 | if ((letters[1] < ltr2_low_value) || |
807 | 0 | (letters[1] > ltr2_high_value) || (letters[2] > LETTER_V)) |
808 | 0 | error_code |= MGRS_STRING_ERROR; |
809 | |
|
810 | 0 | if (!error_code) |
811 | 0 | { |
812 | 0 | grid_northing = |
813 | 0 | (double)(letters[2]) * ONEHT + false_northing; |
814 | 0 | grid_easting = |
815 | 0 | (double)((letters[1]) - ltr2_low_value + 1) * ONEHT; |
816 | 0 | if ((ltr2_low_value == LETTER_J) && (letters[1] > LETTER_O)) |
817 | 0 | grid_easting = grid_easting - ONEHT; |
818 | |
|
819 | 0 | if (letters[2] > LETTER_O) |
820 | 0 | grid_northing = grid_northing - ONEHT; |
821 | |
|
822 | 0 | if (letters[2] > LETTER_I) |
823 | 0 | grid_northing = grid_northing - ONEHT; |
824 | |
|
825 | 0 | if (grid_northing >= TWOMIL) |
826 | 0 | grid_northing = grid_northing - TWOMIL; |
827 | |
|
828 | 0 | error_code = Get_Latitude_Band_Min_Northing(letters[0], |
829 | 0 | &min_northing); |
830 | 0 | if (!error_code) |
831 | 0 | { |
832 | 0 | scaled_min_northing = min_northing; |
833 | 0 | while (scaled_min_northing >= TWOMIL) |
834 | 0 | { |
835 | 0 | scaled_min_northing = scaled_min_northing - TWOMIL; |
836 | 0 | } |
837 | |
|
838 | 0 | grid_northing = grid_northing - scaled_min_northing; |
839 | 0 | if (grid_northing < 0.0) |
840 | 0 | grid_northing = grid_northing + TWOMIL; |
841 | |
|
842 | 0 | grid_northing = min_northing + grid_northing; |
843 | |
|
844 | 0 | *Easting = grid_easting + *Easting; |
845 | 0 | *Northing = grid_northing + *Northing; |
846 | | #ifndef GDAL_COMPILATION |
847 | | /* check that point is within Zone Letter bounds */ |
848 | | error_code = Set_UTM_Parameters(MGRS_a, MGRS_f, *Zone); |
849 | | if (!error_code) |
850 | | { |
851 | | error_code = Convert_UTM_To_Geodetic( |
852 | | *Zone, *Hemisphere, *Easting, *Northing, |
853 | | &latitude, &longitude); |
854 | | if (!error_code) |
855 | | { |
856 | | divisor = pow(10.0, in_precision); |
857 | | error_code = Get_Latitude_Range( |
858 | | letters[0], &upper_lat_limit, |
859 | | &lower_lat_limit); |
860 | | if (!error_code) |
861 | | { |
862 | | if (!(((lower_lat_limit - |
863 | | DEG_TO_RAD / divisor) <= |
864 | | latitude) && |
865 | | (latitude <= (upper_lat_limit + |
866 | | DEG_TO_RAD / divisor)))) |
867 | | error_code |= MGRS_LAT_ERROR; |
868 | | } |
869 | | } |
870 | | } |
871 | | #endif /* notdef */ |
872 | 0 | } |
873 | 0 | } |
874 | 0 | } |
875 | 0 | } |
876 | 80 | } |
877 | 644 | return (error_code); |
878 | 644 | } /* Convert_MGRS_To_UTM */ |
879 | | |
880 | | long Convert_UPS_To_MGRS(char Hemisphere, double Easting, double Northing, |
881 | | long Precision, char *MGRS) |
882 | | /* |
883 | | * The function Convert_UPS_To_MGRS converts UPS (hemisphere, easting, |
884 | | * and northing) coordinates to an MGRS coordinate string according to |
885 | | * the current ellipsoid parameters. If any errors occur, the error |
886 | | * code(s) are returned by the function, otherwise UPS_NO_ERROR is |
887 | | * returned. |
888 | | * |
889 | | * Hemisphere : Hemisphere either 'N' or 'S' (input) |
890 | | * Easting : Easting/X in meters (input) |
891 | | * Northing : Northing/Y in meters (input) |
892 | | * Precision : Precision level of MGRS string (input) |
893 | | * MGRS : MGRS coordinate string (output) |
894 | | */ |
895 | 0 | { /* Convert_UPS_To_MGRS */ |
896 | 0 | double false_easting; /* False easting for 2nd letter */ |
897 | 0 | double false_northing; /* False northing for 3rd letter */ |
898 | 0 | double grid_easting; /* Easting used to derive 2nd letter of MGRS */ |
899 | 0 | double grid_northing; /* Northing used to derive 3rd letter of MGRS */ |
900 | 0 | long ltr2_low_value; /* 2nd letter range - low number */ |
901 | 0 | int letters[MGRS_LETTERS] = { |
902 | 0 | 0}; /* Number location of 3 letters in alphabet */ |
903 | 0 | double divisor; |
904 | 0 | int l_index = 0; |
905 | 0 | long error_code = MGRS_NO_ERROR; |
906 | |
|
907 | 0 | if ((Hemisphere != 'N') && (Hemisphere != 'S')) |
908 | 0 | error_code |= MGRS_HEMISPHERE_ERROR; |
909 | 0 | if ((Easting < MIN_EAST_NORTH) || (Easting > MAX_EAST_NORTH)) |
910 | 0 | error_code |= MGRS_EASTING_ERROR; |
911 | 0 | if ((Northing < MIN_EAST_NORTH) || (Northing > MAX_EAST_NORTH)) |
912 | 0 | error_code |= MGRS_NORTHING_ERROR; |
913 | 0 | if ((Precision < 0) || (Precision > MAX_PRECISION)) |
914 | 0 | error_code |= MGRS_PRECISION_ERROR; |
915 | 0 | if (!error_code) |
916 | 0 | { |
917 | 0 | divisor = pow(10.0, (5 - Precision)); |
918 | 0 | Easting = Round_MGRS(Easting / divisor) * divisor; |
919 | 0 | Northing = Round_MGRS(Northing / divisor) * divisor; |
920 | |
|
921 | 0 | if (Hemisphere == 'N') |
922 | 0 | { |
923 | 0 | if (Easting >= TWOMIL) |
924 | 0 | letters[0] = LETTER_Z; |
925 | 0 | else |
926 | 0 | letters[0] = LETTER_Y; |
927 | |
|
928 | 0 | l_index = letters[0] - 22; |
929 | 0 | ltr2_low_value = UPS_Constant_Table[l_index].ltr2_low_value; |
930 | 0 | false_easting = UPS_Constant_Table[l_index].false_easting; |
931 | 0 | false_northing = UPS_Constant_Table[l_index].false_northing; |
932 | 0 | } |
933 | 0 | else |
934 | 0 | { |
935 | 0 | if (Easting >= TWOMIL) |
936 | 0 | letters[0] = LETTER_B; |
937 | 0 | else |
938 | 0 | letters[0] = LETTER_A; |
939 | |
|
940 | 0 | ltr2_low_value = UPS_Constant_Table[letters[0]].ltr2_low_value; |
941 | 0 | false_easting = UPS_Constant_Table[letters[0]].false_easting; |
942 | 0 | false_northing = UPS_Constant_Table[letters[0]].false_northing; |
943 | 0 | } |
944 | |
|
945 | 0 | grid_northing = Northing; |
946 | 0 | grid_northing = grid_northing - false_northing; |
947 | 0 | letters[2] = (int)(grid_northing / ONEHT); |
948 | |
|
949 | 0 | if (letters[2] > LETTER_H) |
950 | 0 | letters[2] = letters[2] + 1; |
951 | |
|
952 | 0 | if (letters[2] > LETTER_N) |
953 | 0 | letters[2] = letters[2] + 1; |
954 | |
|
955 | 0 | grid_easting = Easting; |
956 | 0 | grid_easting = grid_easting - false_easting; |
957 | 0 | letters[1] = (int)(ltr2_low_value + ((long)(grid_easting / ONEHT))); |
958 | |
|
959 | 0 | if (Easting < TWOMIL) |
960 | 0 | { |
961 | 0 | if (letters[1] > LETTER_L) |
962 | 0 | letters[1] = letters[1] + 3; |
963 | |
|
964 | 0 | if (letters[1] > LETTER_U) |
965 | 0 | letters[1] = letters[1] + 2; |
966 | 0 | } |
967 | 0 | else |
968 | 0 | { |
969 | 0 | if (letters[1] > LETTER_C) |
970 | 0 | letters[1] = letters[1] + 2; |
971 | |
|
972 | 0 | if (letters[1] > LETTER_H) |
973 | 0 | letters[1] = letters[1] + 1; |
974 | |
|
975 | 0 | if (letters[1] > LETTER_L) |
976 | 0 | letters[1] = letters[1] + 3; |
977 | 0 | } |
978 | |
|
979 | 0 | Make_MGRS_String(MGRS, 0, letters, Easting, Northing, Precision); |
980 | 0 | } |
981 | 0 | return (error_code); |
982 | 0 | } /* Convert_UPS_To_MGRS */ |
983 | | |
984 | | long Convert_MGRS_To_UPS(char *MGRS, char *Hemisphere, double *Easting, |
985 | | double *Northing) |
986 | | /* |
987 | | * The function Convert_MGRS_To_UPS converts an MGRS coordinate string |
988 | | * to UPS (hemisphere, easting, and northing) coordinates, according |
989 | | * to the current ellipsoid parameters. If any errors occur, the error |
990 | | * code(s) are returned by the function, otherwise UPS_NO_ERROR is returned. |
991 | | * |
992 | | * MGRS : MGRS coordinate string (input) |
993 | | * Hemisphere : Hemisphere either 'N' or 'S' (output) |
994 | | * Easting : Easting/X in meters (output) |
995 | | * Northing : Northing/Y in meters (output) |
996 | | */ |
997 | 0 | { /* Convert_MGRS_To_UPS */ |
998 | 0 | long ltr2_high_value; /* 2nd letter range - high number */ |
999 | 0 | long ltr3_high_value; /* 3rd letter range - high number (UPS) */ |
1000 | 0 | long ltr2_low_value; /* 2nd letter range - low number */ |
1001 | 0 | double false_easting; /* False easting for 2nd letter */ |
1002 | 0 | double false_northing; /* False northing for 3rd letter */ |
1003 | 0 | double grid_easting; /* easting for 100,000 meter grid square */ |
1004 | 0 | double grid_northing; /* northing for 100,000 meter grid square */ |
1005 | 0 | long zone = 0; |
1006 | 0 | long letters[MGRS_LETTERS]; |
1007 | 0 | long in_precision; |
1008 | 0 | int l_index = 0; |
1009 | 0 | long error_code = MGRS_NO_ERROR; |
1010 | |
|
1011 | 0 | error_code = Break_MGRS_String(MGRS, &zone, letters, Easting, Northing, |
1012 | 0 | &in_precision); |
1013 | 0 | if (zone) |
1014 | 0 | error_code |= MGRS_STRING_ERROR; |
1015 | 0 | else |
1016 | 0 | { |
1017 | 0 | if (!error_code) |
1018 | 0 | { |
1019 | 0 | if (letters[0] >= LETTER_Y) |
1020 | 0 | { |
1021 | 0 | *Hemisphere = 'N'; |
1022 | |
|
1023 | 0 | l_index = (int)(letters[0] - 22); |
1024 | 0 | ltr2_low_value = UPS_Constant_Table[l_index].ltr2_low_value; |
1025 | 0 | ltr2_high_value = UPS_Constant_Table[l_index].ltr2_high_value; |
1026 | 0 | ltr3_high_value = UPS_Constant_Table[l_index].ltr3_high_value; |
1027 | 0 | false_easting = UPS_Constant_Table[l_index].false_easting; |
1028 | 0 | false_northing = UPS_Constant_Table[l_index].false_northing; |
1029 | 0 | } |
1030 | 0 | else |
1031 | 0 | { |
1032 | 0 | *Hemisphere = 'S'; |
1033 | |
|
1034 | 0 | ltr2_low_value = UPS_Constant_Table[letters[0]].ltr2_low_value; |
1035 | 0 | ltr2_high_value = |
1036 | 0 | UPS_Constant_Table[letters[0]].ltr2_high_value; |
1037 | 0 | ltr3_high_value = |
1038 | 0 | UPS_Constant_Table[letters[0]].ltr3_high_value; |
1039 | 0 | false_easting = UPS_Constant_Table[letters[0]].false_easting; |
1040 | 0 | false_northing = UPS_Constant_Table[letters[0]].false_northing; |
1041 | 0 | } |
1042 | | |
1043 | | /* Check that the second letter of the MGRS string is within |
1044 | | * the range of valid second letter values |
1045 | | * Also check that the third letter is valid */ |
1046 | 0 | if ((letters[1] < ltr2_low_value) || |
1047 | 0 | (letters[1] > ltr2_high_value) || |
1048 | 0 | ((letters[1] == LETTER_D) || (letters[1] == LETTER_E) || |
1049 | 0 | (letters[1] == LETTER_M) || (letters[1] == LETTER_N) || |
1050 | 0 | (letters[1] == LETTER_V) || (letters[1] == LETTER_W)) || |
1051 | 0 | (letters[2] > ltr3_high_value)) |
1052 | 0 | error_code = MGRS_STRING_ERROR; |
1053 | |
|
1054 | 0 | if (!error_code) |
1055 | 0 | { |
1056 | 0 | grid_northing = (double)letters[2] * ONEHT + false_northing; |
1057 | 0 | if (letters[2] > LETTER_I) |
1058 | 0 | grid_northing = grid_northing - ONEHT; |
1059 | |
|
1060 | 0 | if (letters[2] > LETTER_O) |
1061 | 0 | grid_northing = grid_northing - ONEHT; |
1062 | |
|
1063 | 0 | grid_easting = (double)((letters[1]) - ltr2_low_value) * ONEHT + |
1064 | 0 | false_easting; |
1065 | 0 | if (ltr2_low_value != LETTER_A) |
1066 | 0 | { |
1067 | 0 | if (letters[1] > LETTER_L) |
1068 | 0 | grid_easting = grid_easting - 300000.0; |
1069 | |
|
1070 | 0 | if (letters[1] > LETTER_U) |
1071 | 0 | grid_easting = grid_easting - 200000.0; |
1072 | 0 | } |
1073 | 0 | else |
1074 | 0 | { |
1075 | 0 | if (letters[1] > LETTER_C) |
1076 | 0 | grid_easting = grid_easting - 200000.0; |
1077 | |
|
1078 | 0 | if (letters[1] > LETTER_I) |
1079 | 0 | grid_easting = grid_easting - ONEHT; |
1080 | |
|
1081 | 0 | if (letters[1] > LETTER_L) |
1082 | 0 | grid_easting = grid_easting - 300000.0; |
1083 | 0 | } |
1084 | |
|
1085 | 0 | *Easting = grid_easting + *Easting; |
1086 | 0 | *Northing = grid_northing + *Northing; |
1087 | 0 | } |
1088 | 0 | } |
1089 | 0 | } |
1090 | 0 | return (error_code); |
1091 | 0 | } /* Convert_MGRS_To_UPS */ |