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