Line data Source code
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 : #define LETTER_A 0 /* ARRAY INDEX FOR LETTER A */
109 : #define LETTER_B 1 /* ARRAY INDEX FOR LETTER B */
110 : #define LETTER_C 2 /* ARRAY INDEX FOR LETTER C */
111 : #define LETTER_D 3 /* ARRAY INDEX FOR LETTER D */
112 : #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 : #define LETTER_H 7 /* ARRAY INDEX FOR LETTER H */
116 : #define LETTER_I 8 /* ARRAY INDEX FOR LETTER I */
117 : #define LETTER_J 9 /* ARRAY INDEX FOR LETTER J */
118 : #define LETTER_K 10 /* ARRAY INDEX FOR LETTER J */
119 : #define LETTER_L 11 /* ARRAY INDEX FOR LETTER L */
120 : #define LETTER_M 12 /* ARRAY INDEX FOR LETTER M */
121 : #define LETTER_N 13 /* ARRAY INDEX FOR LETTER N */
122 : #define LETTER_O 14 /* ARRAY INDEX FOR LETTER O */
123 : #define LETTER_P 15 /* ARRAY INDEX FOR LETTER P */
124 : #define LETTER_Q 16 /* ARRAY INDEX FOR LETTER Q */
125 : #define LETTER_R 17 /* ARRAY INDEX FOR LETTER R */
126 : #define LETTER_S 18 /* ARRAY INDEX FOR LETTER S */
127 : #define LETTER_T 19 /* ARRAY INDEX FOR LETTER S */
128 : #define LETTER_U 20 /* ARRAY INDEX FOR LETTER U */
129 : #define LETTER_V 21 /* ARRAY INDEX FOR LETTER V */
130 : #define LETTER_W 22 /* ARRAY INDEX FOR LETTER W */
131 : #define LETTER_X 23 /* ARRAY INDEX FOR LETTER X */
132 : #define LETTER_Y 24 /* ARRAY INDEX FOR LETTER Y */
133 : #define LETTER_Z 25 /* ARRAY INDEX FOR LETTER Z */
134 : #define MGRS_LETTERS 3 /* NUMBER OF LETTERS IN MGRS */
135 : #define ONEHT 100000.e0 /* ONE HUNDRED THOUSAND */
136 : #define TWOMIL 2000000.e0 /* TWO MILLION */
137 : #define TRUE 1 /* CONSTANT VALUE FOR TRUE VALUE */
138 : #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 : #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 : #define MIN_EAST_NORTH 0
150 : #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 8 : 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 : { /* Get_Latitude_Band_Min_Northing */
227 8 : long error_code = MGRS_NO_ERROR;
228 :
229 8 : if ((letter >= LETTER_C) && (letter <= LETTER_H))
230 0 : *min_northing = Latitude_Band_Table[letter - 2].min_northing;
231 8 : else if ((letter >= LETTER_J) && (letter <= LETTER_N))
232 0 : *min_northing = Latitude_Band_Table[letter - 3].min_northing;
233 8 : else if ((letter >= LETTER_P) && (letter <= LETTER_X))
234 8 : *min_northing = Latitude_Band_Table[letter - 4].min_northing;
235 : else
236 0 : error_code |= MGRS_STRING_ERROR;
237 :
238 8 : return error_code;
239 : } /* 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 0 : 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 : { /* Round_MGRS */
352 : double ivalue;
353 : 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 : } /* Round_MGRS */
360 :
361 0 : 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 : { /* Make_MGRS_String */
375 : long i;
376 : long j;
377 : double divisor;
378 : long east;
379 : 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 : 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 : north);
403 0 : return (error_code);
404 : } /* Make_MGRS_String */
405 :
406 8 : 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 : { /* Break_MGRS_String */
421 : long num_digits;
422 : long num_letters;
423 8 : long i = 0;
424 8 : long j = 0;
425 8 : long error_code = MGRS_NO_ERROR;
426 :
427 8 : while (MGRS[i] == ' ')
428 0 : i++; /* skip any leading blanks */
429 8 : j = i;
430 24 : while (isdigit((unsigned char)MGRS[i]))
431 16 : i++;
432 8 : num_digits = i - j;
433 8 : if (num_digits <= 2)
434 8 : if (num_digits > 0)
435 : {
436 : char zone_string[3];
437 : /* get zone */
438 8 : strncpy(zone_string, MGRS + j, 2);
439 8 : zone_string[2] = 0;
440 8 : sscanf(zone_string, "%ld", Zone);
441 8 : if ((*Zone < 1) || (*Zone > 60))
442 0 : error_code |= MGRS_STRING_ERROR;
443 : }
444 : else
445 0 : *Zone = 0;
446 : else
447 0 : error_code |= MGRS_STRING_ERROR;
448 8 : j = i;
449 :
450 32 : while (isalpha((unsigned char)MGRS[i]))
451 24 : i++;
452 8 : num_letters = i - j;
453 8 : if (num_letters == 3)
454 : {
455 : /* get letters */
456 8 : Letters[0] = (toupper((unsigned char)MGRS[j]) - (long)'A');
457 8 : if ((Letters[0] == LETTER_I) || (Letters[0] == LETTER_O))
458 0 : error_code |= MGRS_STRING_ERROR;
459 8 : Letters[1] = (toupper((unsigned char)MGRS[j + 1]) - (long)'A');
460 8 : if ((Letters[1] == LETTER_I) || (Letters[1] == LETTER_O))
461 0 : error_code |= MGRS_STRING_ERROR;
462 8 : Letters[2] = (toupper((unsigned char)MGRS[j + 2]) - (long)'A');
463 8 : if ((Letters[2] == LETTER_I) || (Letters[2] == LETTER_O))
464 0 : error_code |= MGRS_STRING_ERROR;
465 : }
466 : else
467 0 : error_code |= MGRS_STRING_ERROR;
468 8 : j = i;
469 88 : while (isdigit((unsigned char)MGRS[i]))
470 80 : i++;
471 8 : num_digits = i - j;
472 8 : if ((num_digits <= 10) && (num_digits % 2 == 0))
473 8 : {
474 : long n;
475 : char east_string[6];
476 : char north_string[6];
477 : long east;
478 : long north;
479 : double multiplier;
480 : /* get easting & northing */
481 8 : n = num_digits / 2;
482 8 : *Precision = n;
483 8 : if (n > 0)
484 : {
485 8 : strncpy(east_string, MGRS + j, n);
486 8 : east_string[n] = 0;
487 8 : sscanf(east_string, "%ld", &east);
488 8 : strncpy(north_string, MGRS + j + n, n);
489 8 : north_string[n] = 0;
490 8 : sscanf(north_string, "%ld", &north);
491 8 : multiplier = pow(10.0, 5 - n);
492 8 : *Easting = east * multiplier;
493 8 : *Northing = north * multiplier;
494 : }
495 : else
496 : {
497 0 : *Easting = 0.0;
498 0 : *Northing = 0.0;
499 : }
500 : }
501 : else
502 0 : error_code |= MGRS_STRING_ERROR;
503 :
504 8 : return (error_code);
505 : } /* Break_MGRS_String */
506 :
507 8 : 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 : { /* BEGIN Get_Grid_Values */
522 : long set_number; /* Set number (1-6) based on UTM zone number */
523 : long aa_pattern; /* Pattern based on ellipsoid code */
524 :
525 8 : set_number = zone % 6;
526 :
527 8 : if (!set_number)
528 0 : set_number = 6;
529 :
530 8 : if (!strcmp(MGRS_Ellipsoid_Code, CLARKE_1866) ||
531 8 : !strcmp(MGRS_Ellipsoid_Code, CLARKE_1880) ||
532 8 : !strcmp(MGRS_Ellipsoid_Code, BESSEL_1841) ||
533 8 : !strcmp(MGRS_Ellipsoid_Code, BESSEL_1841_NAMIBIA))
534 : aa_pattern = FALSE;
535 : else
536 8 : aa_pattern = TRUE;
537 :
538 8 : if ((set_number == 1) || (set_number == 4))
539 : {
540 8 : *ltr2_low_value = LETTER_A;
541 8 : *ltr2_high_value = LETTER_H;
542 : }
543 0 : else if ((set_number == 2) || (set_number == 5))
544 : {
545 0 : *ltr2_low_value = LETTER_J;
546 0 : *ltr2_high_value = LETTER_R;
547 : }
548 0 : else if ((set_number == 3) || (set_number == 6))
549 : {
550 0 : *ltr2_low_value = LETTER_S;
551 0 : *ltr2_high_value = LETTER_Z;
552 : }
553 :
554 : /* False northing at A for second letter of grid square */
555 8 : if (aa_pattern)
556 : {
557 8 : if ((set_number % 2) == 0)
558 0 : *false_northing = 1500000.0;
559 : else
560 8 : *false_northing = 0.0;
561 : }
562 : else
563 : {
564 0 : if ((set_number % 2) == 0)
565 0 : *false_northing = 500000.0;
566 : else
567 0 : *false_northing = 1000000.00;
568 : }
569 8 : } /* 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 0 : 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 : { /* 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 : } /* 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 8 : 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 : { /* Convert_MGRS_To_UTM */
764 : double scaled_min_northing;
765 : double min_northing;
766 8 : long ltr2_low_value = 0;
767 8 : long ltr2_high_value = 0;
768 : double false_northing;
769 : double grid_easting; /* Easting for 100,000 meter grid square */
770 : double grid_northing; /* Northing for 100,000 meter grid square */
771 : long letters[MGRS_LETTERS];
772 : 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 8 : long error_code = MGRS_NO_ERROR;
781 :
782 8 : error_code = Break_MGRS_String(MGRS, Zone, letters, Easting, Northing,
783 : &in_precision);
784 8 : if (!*Zone)
785 0 : error_code |= MGRS_STRING_ERROR;
786 : else
787 : {
788 8 : if (!error_code)
789 : {
790 8 : if ((letters[0] == LETTER_X) &&
791 0 : ((*Zone == 32) || (*Zone == 34) || (*Zone == 36)))
792 0 : error_code |= MGRS_STRING_ERROR;
793 : else
794 : {
795 8 : if (letters[0] < LETTER_N)
796 0 : *Hemisphere = 'S';
797 : else
798 8 : *Hemisphere = 'N';
799 :
800 8 : Get_Grid_Values(*Zone, <r2_low_value, <r2_high_value,
801 : &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 8 : if ((letters[1] < ltr2_low_value) ||
807 8 : (letters[1] > ltr2_high_value) || (letters[2] > LETTER_V))
808 0 : error_code |= MGRS_STRING_ERROR;
809 :
810 8 : if (!error_code)
811 : {
812 8 : grid_northing =
813 8 : (double)(letters[2]) * ONEHT + false_northing;
814 8 : grid_easting =
815 8 : (double)((letters[1]) - ltr2_low_value + 1) * ONEHT;
816 8 : if ((ltr2_low_value == LETTER_J) && (letters[1] > LETTER_O))
817 0 : grid_easting = grid_easting - ONEHT;
818 :
819 8 : if (letters[2] > LETTER_O)
820 8 : grid_northing = grid_northing - ONEHT;
821 :
822 8 : if (letters[2] > LETTER_I)
823 8 : grid_northing = grid_northing - ONEHT;
824 :
825 8 : if (grid_northing >= TWOMIL)
826 0 : grid_northing = grid_northing - TWOMIL;
827 :
828 8 : error_code = Get_Latitude_Band_Min_Northing(letters[0],
829 : &min_northing);
830 8 : if (!error_code)
831 : {
832 8 : scaled_min_northing = min_northing;
833 24 : while (scaled_min_northing >= TWOMIL)
834 : {
835 16 : scaled_min_northing = scaled_min_northing - TWOMIL;
836 : }
837 :
838 8 : grid_northing = grid_northing - scaled_min_northing;
839 8 : if (grid_northing < 0.0)
840 0 : grid_northing = grid_northing + TWOMIL;
841 :
842 8 : grid_northing = min_northing + grid_northing;
843 :
844 8 : *Easting = grid_easting + *Easting;
845 8 : *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 : }
873 : }
874 : }
875 : }
876 : }
877 8 : return (error_code);
878 : } /* Convert_MGRS_To_UTM */
879 :
880 0 : 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 : { /* Convert_UPS_To_MGRS */
896 : double false_easting; /* False easting for 2nd letter */
897 : double false_northing; /* False northing for 3rd letter */
898 : double grid_easting; /* Easting used to derive 2nd letter of MGRS */
899 : double grid_northing; /* Northing used to derive 3rd letter of MGRS */
900 : long ltr2_low_value; /* 2nd letter range - low number */
901 0 : int letters[MGRS_LETTERS] = {
902 : 0}; /* Number location of 3 letters in alphabet */
903 : 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 : {
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 : {
923 0 : if (Easting >= TWOMIL)
924 0 : letters[0] = LETTER_Z;
925 : 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 : }
933 : else
934 : {
935 0 : if (Easting >= TWOMIL)
936 0 : letters[0] = LETTER_B;
937 : 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 : }
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 : {
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 : }
967 : else
968 : {
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 : }
978 :
979 0 : Make_MGRS_String(MGRS, 0, letters, Easting, Northing, Precision);
980 : }
981 0 : return (error_code);
982 : } /* Convert_UPS_To_MGRS */
983 :
984 0 : 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 : { /* Convert_MGRS_To_UPS */
998 : long ltr2_high_value; /* 2nd letter range - high number */
999 : long ltr3_high_value; /* 3rd letter range - high number (UPS) */
1000 : long ltr2_low_value; /* 2nd letter range - low number */
1001 : double false_easting; /* False easting for 2nd letter */
1002 : double false_northing; /* False northing for 3rd letter */
1003 : double grid_easting; /* easting for 100,000 meter grid square */
1004 : double grid_northing; /* northing for 100,000 meter grid square */
1005 0 : long zone = 0;
1006 : long letters[MGRS_LETTERS];
1007 : 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 : &in_precision);
1013 0 : if (zone)
1014 0 : error_code |= MGRS_STRING_ERROR;
1015 : else
1016 : {
1017 0 : if (!error_code)
1018 : {
1019 0 : if (letters[0] >= LETTER_Y)
1020 : {
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 : }
1030 : else
1031 : {
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 : }
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 : {
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 : false_easting;
1065 0 : if (ltr2_low_value != LETTER_A)
1066 : {
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 : }
1073 : else
1074 : {
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 : }
1084 :
1085 0 : *Easting = grid_easting + *Easting;
1086 0 : *Northing = grid_northing + *Northing;
1087 : }
1088 : }
1089 : }
1090 0 : return (error_code);
1091 : } /* Convert_MGRS_To_UPS */
|