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