1
/***************************************************************************/
2
/* RSC IDENTIFIER: USNG
6
* This component converts between geodetic coordinates (latitude and
7
* longitude) and United States National Grid (USNG) coordinates.
11
* This component checks parameters for valid values. If an invalid value
12
* is found, the error code is combined with the current error code using
13
* the bitwise or. This combining allows multiple error codes to be
14
* returned. The possible error codes are:
16
* USNG_NO_ERROR : No errors occurred in function
17
* USNG_LAT_ERROR : Latitude outside of valid range
19
* USNG_LON_ERROR : Longitude outside of valid range
20
* (-180 to 360 degrees)
21
* USNG_STR_ERROR : An USNG string error: string too long,
22
* too short, or badly formed
23
* USNG_PRECISION_ERROR : The precision must be between 0 and 5
25
* USNG_A_ERROR : Semi-major axis less than or equal to zero
26
* USNG_INV_F_ERROR : Inverse flattening outside of valid range
28
* USNG_EASTING_ERROR : Easting outside of valid range
29
* (100,000 to 900,000 meters for UTM)
30
* (0 to 4,000,000 meters for UPS)
31
* USNG_NORTHING_ERROR : Northing outside of valid range
32
* (0 to 10,000,000 meters for UTM)
33
* (0 to 4,000,000 meters for UPS)
34
* USNG_ZONE_ERROR : Zone outside of valid range (1 to 60)
35
* USNG_HEMISPHERE_ERROR : Invalid hemisphere ('N' or 'S')
39
* USNG is intended for reuse by any application that does conversions
40
* between geodetic coordinates and USNG coordinates.
44
* Further information on USNG can be found in the Reuse Manual.
46
* USNG originated from : Federal Geographic Data Committee
48
* 12201 Sunrise Valley Drive
53
* None apply to this component.
60
* USNG was tested and certified in the following environments:
62
* 1. Solaris 2.5 with GCC version 2.8.1
63
* 2. Windows XP with MS Visual C++ version 6
69
* 06-05-06 Original Code (cloned from MGRS)
73
/***************************************************************************/
86
* ctype.h - Standard C character handling library
87
* math.h - Standard C math library
88
* stdio.h - Standard C input/output library
89
* string.h - Standard C string handling library
90
* ups.h - Universal Polar Stereographic (UPS) projection
91
* utm.h - Universal Transverse Mercator (UTM) projection
92
* usng.h - function prototype error checking
96
/***************************************************************************/
100
#define DEG_TO_RAD 0.017453292519943295 /* PI/180 */
101
#define RAD_TO_DEG 57.29577951308232087 /* 180/PI */
102
#define LETTER_A 0 /* ARRAY INDEX FOR LETTER A */
103
#define LETTER_B 1 /* ARRAY INDEX FOR LETTER B */
104
#define LETTER_C 2 /* ARRAY INDEX FOR LETTER C */
105
#define LETTER_D 3 /* ARRAY INDEX FOR LETTER D */
106
#define LETTER_E 4 /* ARRAY INDEX FOR LETTER E */
107
#define LETTER_F 5 /* ARRAY INDEX FOR LETTER F */
108
#define LETTER_G 6 /* ARRAY INDEX FOR LETTER G */
109
#define LETTER_H 7 /* ARRAY INDEX FOR LETTER H */
110
#define LETTER_I 8 /* ARRAY INDEX FOR LETTER I */
111
#define LETTER_J 9 /* ARRAY INDEX FOR LETTER J */
112
#define LETTER_K 10 /* ARRAY INDEX FOR LETTER K */
113
#define LETTER_L 11 /* ARRAY INDEX FOR LETTER L */
114
#define LETTER_M 12 /* ARRAY INDEX FOR LETTER M */
115
#define LETTER_N 13 /* ARRAY INDEX FOR LETTER N */
116
#define LETTER_O 14 /* ARRAY INDEX FOR LETTER O */
117
#define LETTER_P 15 /* ARRAY INDEX FOR LETTER P */
118
#define LETTER_Q 16 /* ARRAY INDEX FOR LETTER Q */
119
#define LETTER_R 17 /* ARRAY INDEX FOR LETTER R */
120
#define LETTER_S 18 /* ARRAY INDEX FOR LETTER S */
121
#define LETTER_T 19 /* ARRAY INDEX FOR LETTER T */
122
#define LETTER_U 20 /* ARRAY INDEX FOR LETTER U */
123
#define LETTER_V 21 /* ARRAY INDEX FOR LETTER V */
124
#define LETTER_W 22 /* ARRAY INDEX FOR LETTER W */
125
#define LETTER_X 23 /* ARRAY INDEX FOR LETTER X */
126
#define LETTER_Y 24 /* ARRAY INDEX FOR LETTER Y */
127
#define LETTER_Z 25 /* ARRAY INDEX FOR LETTER Z */
128
#define USNG_LETTERS 3 /* NUMBER OF LETTERS IN USNG */
129
#define ONEHT 100000.e0 /* ONE HUNDRED THOUSAND */
130
#define TWOMIL 2000000.e0 /* TWO MILLION */
131
#define TRUE 1 /* CONSTANT VALUE FOR TRUE VALUE */
132
#define FALSE 0 /* CONSTANT VALUE FOR FALSE VALUE */
133
#define PI 3.14159265358979323e0 /* PI */
134
#define PI_OVER_2 (PI / 2.0e0)
136
#define MIN_EASTING 100000
137
#define MAX_EASTING 900000
138
#define MIN_NORTHING 0
139
#define MAX_NORTHING 10000000
140
#define MAX_PRECISION 5 /* Maximum precision of easting & northing */
141
#define MIN_UTM_LAT ( (-80 * PI) / 180.0 ) /* -80 degrees in radians */
142
#define MAX_UTM_LAT ( (84 * PI) / 180.0 ) /* 84 degrees in radians */
144
#define MIN_EAST_NORTH 0
145
#define MAX_EAST_NORTH 4000000
148
/* Ellipsoid parameters, default to WGS 84 */
149
double USNG_a = 6378137.0; /* Semi-major axis of ellipsoid in meters */
150
double USNG_f = 1 / 298.257223563; /* Flattening of ellipsoid */
151
double USNG_recpf = 298.257223563;
152
char USNG_Ellipsoid_Code[3] = {'W','E',0};
155
typedef struct Latitude_Band_Value
157
long letter; /* letter representing latitude band */
158
double min_northing; /* minimum northing for latitude band */
159
double north; /* upper latitude for latitude band */
160
double south; /* lower latitude for latitude band */
161
double northing_offset; /* latitude band northing offset */
164
static const Latitude_Band Latitude_Band_Table[20] =
165
{{LETTER_C, 1100000.0, -72.0, -80.5, 0.0},
166
{LETTER_D, 2000000.0, -64.0, -72.0, 2000000.0},
167
{LETTER_E, 2800000.0, -56.0, -64.0, 2000000.0},
168
{LETTER_F, 3700000.0, -48.0, -56.0, 2000000.0},
169
{LETTER_G, 4600000.0, -40.0, -48.0, 4000000.0},
170
{LETTER_H, 5500000.0, -32.0, -40.0, 4000000.0},
171
{LETTER_J, 6400000.0, -24.0, -32.0, 6000000.0},
172
{LETTER_K, 7300000.0, -16.0, -24.0, 6000000.0},
173
{LETTER_L, 8200000.0, -8.0, -16.0, 8000000.0},
174
{LETTER_M, 9100000.0, 0.0, -8.0, 8000000.0},
175
{LETTER_N, 0.0, 8.0, 0.0, 0.0},
176
{LETTER_P, 800000.0, 16.0, 8.0, 0.0},
177
{LETTER_Q, 1700000.0, 24.0, 16.0, 0.0},
178
{LETTER_R, 2600000.0, 32.0, 24.0, 2000000.0},
179
{LETTER_S, 3500000.0, 40.0, 32.0, 2000000.0},
180
{LETTER_T, 4400000.0, 48.0, 40.0, 4000000.0},
181
{LETTER_U, 5300000.0, 56.0, 48.0, 4000000.0},
182
{LETTER_V, 6200000.0, 64.0, 56.0, 6000000.0},
183
{LETTER_W, 7000000.0, 72.0, 64.0, 6000000.0},
184
{LETTER_X, 7900000.0, 84.5, 72.0, 6000000.0}};
187
typedef struct UPS_Constant_Value
189
long letter; /* letter representing latitude band */
190
long ltr2_low_value; /* 2nd letter range - low number */
191
long ltr2_high_value; /* 2nd letter range - high number */
192
long ltr3_high_value; /* 3rd letter range - high number (UPS) */
193
double false_easting; /* False easting based on 2nd letter */
194
double false_northing; /* False northing based on 3rd letter */
197
static const UPS_Constant UPS_Constant_Table[4] =
198
{{LETTER_A, LETTER_J, LETTER_Z, LETTER_Z, 800000.0, 800000.0},
199
{LETTER_B, LETTER_A, LETTER_R, LETTER_Z, 2000000.0, 800000.0},
200
{LETTER_Y, LETTER_J, LETTER_Z, LETTER_P, 800000.0, 1300000.0},
201
{LETTER_Z, LETTER_A, LETTER_J, LETTER_P, 2000000.0, 1300000.0}};
203
/***************************************************************************/
208
long USNG_Get_Latitude_Band_Min_Northing(long letter, double* min_northing, double* northing_offset)
210
* The function USNG_Get_Latitude_Band_Min_Northing receives a latitude band letter
211
* and uses the Latitude_Band_Table to determine the minimum northing and northing offset
212
* for that latitude band letter.
214
* letter : Latitude band letter (input)
215
* min_northing : Minimum northing for that letter (output)
217
{ /* USNG_Get_Latitude_Band_Min_Northing */
218
long error_code = USNG_NO_ERROR;
220
if ((letter >= LETTER_C) && (letter <= LETTER_H))
222
*min_northing = Latitude_Band_Table[letter-2].min_northing;
223
*northing_offset = Latitude_Band_Table[letter-2].northing_offset;
225
else if ((letter >= LETTER_J) && (letter <= LETTER_N))
227
*min_northing = Latitude_Band_Table[letter-3].min_northing;
228
*northing_offset = Latitude_Band_Table[letter-3].northing_offset;
230
else if ((letter >= LETTER_P) && (letter <= LETTER_X))
232
*min_northing = Latitude_Band_Table[letter-4].min_northing;
233
*northing_offset = Latitude_Band_Table[letter-4].northing_offset;
236
error_code |= USNG_STRING_ERROR;
239
} /* USNG_Get_Latitude_Band_Min_Northing */
242
long USNG_Get_Latitude_Range(long letter, double* north, double* south)
244
* The function USNG_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.
248
* letter : Latitude band letter (input)
249
* north : Northern latitude boundary for that letter (output)
250
* north : Southern latitude boundary for that letter (output)
252
{ /* USNG_Get_Latitude_Range */
253
long error_code = USNG_NO_ERROR;
255
if ((letter >= LETTER_C) && (letter <= LETTER_H))
257
*north = Latitude_Band_Table[letter-2].north * DEG_TO_RAD;
258
*south = Latitude_Band_Table[letter-2].south * DEG_TO_RAD;
260
else if ((letter >= LETTER_J) && (letter <= LETTER_N))
262
*north = Latitude_Band_Table[letter-3].north * DEG_TO_RAD;
263
*south = Latitude_Band_Table[letter-3].south * DEG_TO_RAD;
265
else if ((letter >= LETTER_P) && (letter <= LETTER_X))
267
*north = Latitude_Band_Table[letter-4].north * DEG_TO_RAD;
268
*south = Latitude_Band_Table[letter-4].south * DEG_TO_RAD;
271
error_code |= USNG_STRING_ERROR;
274
} /* USNG_Get_Latitude_Range */
277
long USNG_Get_Latitude_Letter(double latitude, int* letter)
279
* The function USNG_Get_Latitude_Letter receives a latitude value
280
* and uses the Latitude_Band_Table to determine the latitude band
281
* letter for that latitude.
283
* latitude : Latitude (input)
284
* letter : Latitude band letter (output)
286
{ /* USNG_Get_Latitude_Letter */
288
long error_code = USNG_NO_ERROR;
289
double lat_deg = latitude * RAD_TO_DEG;
291
if (lat_deg >= 72 && lat_deg < 84.5)
293
else if (lat_deg > -80.5 && lat_deg < 72)
295
temp = ((latitude + (80.0 * DEG_TO_RAD)) / (8.0 * DEG_TO_RAD)) + 1.0e-12;
296
*letter = Latitude_Band_Table[(int)temp].letter;
299
error_code |= USNG_LAT_ERROR;
302
} /* USNG_Get_Latitude_Letter */
305
long USNG_Check_Zone(char* USNG, long* zone_exists)
307
* The function USNG_Check_Zone receives a USNG coordinate string.
308
* If a zone is given, TRUE is returned. Otherwise, FALSE
311
* USNG : USNG coordinate string (input)
312
* zone_exists : TRUE if a zone is given,
313
* FALSE if a zone is not given (output)
315
{ /* USNG_Check_Zone */
319
long error_code = USNG_NO_ERROR;
321
/* skip any leading blanks */
322
while (USNG[i] == ' ')
325
while (isdigit(USNG[i]))
332
*zone_exists = FALSE;
334
error_code |= USNG_STRING_ERROR;
337
} /* USNG_Check_Zone */
340
long Make_USNG_String (char* USNG,
342
int Letters[USNG_LETTERS],
347
* The function Make_USNG_String constructs a USNG string
348
* from its component parts.
350
* USNG : USNG coordinate string (output)
351
* Zone : UTM Zone (input)
352
* Letters : USNG coordinate string letters (input)
353
* Easting : Easting value (input)
354
* Northing : Northing value (input)
355
* Precision : Precision level of USNG string (input)
357
{ /* Make_USNG_String */
363
char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
364
long error_code = USNG_NO_ERROR;
368
i = sprintf (USNG+i,"%2.2ld",Zone);
370
strncpy(USNG, " ", 2); // 2 spaces
373
USNG[i++] = alphabet[Letters[j]];
374
divisor = pow (10.0, (5 - Precision));
375
Easting = fmod (Easting, 100000.0);
376
if (Easting >= 99999.5)
378
east = (long)(Easting/divisor);
379
i += sprintf (USNG+i, "%*.*ld", Precision, Precision, east);
380
Northing = fmod (Northing, 100000.0);
381
if (Northing >= 99999.5)
383
north = (long)(Northing/divisor);
384
i += sprintf (USNG+i, "%*.*ld", Precision, Precision, north);
386
} /* Make_USNG_String */
389
long Break_USNG_String (char* USNG,
391
long Letters[USNG_LETTERS],
396
* The function Break_USNG_String breaks down a USNG
397
* coordinate string into its component parts.
399
* USNG : USNG coordinate string (input)
400
* Zone : UTM Zone (output)
401
* Letters : USNG coordinate string letters (output)
402
* Easting : Easting value (output)
403
* Northing : Northing value (output)
404
* Precision : Precision level of USNG string (output)
406
{ /* Break_USNG_String */
411
long error_code = USNG_NO_ERROR;
413
while (USNG[i] == ' ')
414
i++; /* skip any leading blanks */
416
while (isdigit(USNG[i]))
424
strncpy (zone_string, USNG+j, 2);
426
sscanf (zone_string, "%ld", Zone);
427
if ((*Zone < 1) || (*Zone > 60))
428
error_code |= USNG_STRING_ERROR;
433
error_code |= USNG_STRING_ERROR;
436
while (isalpha(USNG[i]))
439
if (num_letters == 3)
442
Letters[0] = (toupper(USNG[j]) - (long)'A');
443
if ((Letters[0] == LETTER_I) || (Letters[0] == LETTER_O))
444
error_code |= USNG_STRING_ERROR;
445
Letters[1] = (toupper(USNG[j+1]) - (long)'A');
446
if ((Letters[1] == LETTER_I) || (Letters[1] == LETTER_O))
447
error_code |= USNG_STRING_ERROR;
448
Letters[2] = (toupper(USNG[j+2]) - (long)'A');
449
if ((Letters[2] == LETTER_I) || (Letters[2] == LETTER_O))
450
error_code |= USNG_STRING_ERROR;
453
error_code |= USNG_STRING_ERROR;
455
while (isdigit(USNG[i]))
458
if ((num_digits <= 10) && (num_digits%2 == 0))
462
char north_string[6];
466
/* get easting & northing */
471
strncpy (east_string, USNG+j, n);
473
sscanf (east_string, "%ld", &east);
474
strncpy (north_string, USNG+j+n, n);
476
sscanf (north_string, "%ld", &north);
477
multiplier = pow (10.0, 5 - n);
478
*Easting = east * multiplier;
479
*Northing = north * multiplier;
488
error_code |= USNG_STRING_ERROR;
491
} /* Break_USNG_String */
494
void USNG_Get_Grid_Values (long zone,
495
long* ltr2_low_value,
496
long* ltr2_high_value,
497
double *pattern_offset)
499
* The function USNG_Get_Grid_Values sets the letter range used for
500
* the 2nd letter in the USNG coordinate string, based on the set
501
* number of the utm zone. It also sets the pattern offset using a
502
* value of A for the second letter of the grid square, based on
503
* the grid pattern and set number of the utm zone.
505
* zone : Zone number (input)
506
* ltr2_low_value : 2nd letter low number (output)
507
* ltr2_high_value : 2nd letter high number (output)
508
* pattern_offset : Pattern offset (output)
510
{ /* BEGIN USNG_Get_Grid_Values */
511
long set_number; /* Set number (1-6) based on UTM zone number */
513
set_number = zone % 6;
518
if ((set_number == 1) || (set_number == 4))
520
*ltr2_low_value = LETTER_A;
521
*ltr2_high_value = LETTER_H;
523
else if ((set_number == 2) || (set_number == 5))
525
*ltr2_low_value = LETTER_J;
526
*ltr2_high_value = LETTER_R;
528
else if ((set_number == 3) || (set_number == 6))
530
*ltr2_low_value = LETTER_S;
531
*ltr2_high_value = LETTER_Z;
534
/* False northing at A for second letter of grid square */
535
if ((set_number % 2) == 0)
536
*pattern_offset = 500000.0;
538
*pattern_offset = 0.0;
540
} /* END OF USNG_Get_Grid_Values */
543
long UTM_To_USNG (long Zone,
550
* The function UTM_To_USNG calculates a USNG coordinate string
551
* based on the zone, latitude, easting and northing.
553
* Zone : Zone number (input)
554
* Latitude : Latitude in radians (input)
555
* Easting : Easting (input)
556
* Northing : Northing (input)
557
* Precision : Precision (input)
558
* USNG : USNG coordinate string (output)
560
{ /* BEGIN UTM_To_USNG */
561
double pattern_offset; /* Pattern offset for 3rd letter */
562
double grid_northing; /* Northing used to derive 3rd letter of USNG */
563
long ltr2_low_value; /* 2nd letter range - low number */
564
long ltr2_high_value; /* 2nd letter range - high number */
565
int letters[USNG_LETTERS]; /* Number location of 3 letters in alphabet */
567
long error_code = USNG_NO_ERROR;
569
/* Round easting and northing values */
570
divisor = pow (10.0, (5 - Precision));
571
Easting = (long)(Easting/divisor) * divisor;
572
Northing = (long)(Northing/divisor) * divisor;
574
if( Latitude <= 0.0 && Northing == 1.0e7)
580
USNG_Get_Grid_Values(Zone, <r2_low_value, <r2_high_value, &pattern_offset);
582
error_code = USNG_Get_Latitude_Letter(Latitude, &letters[0]);
586
grid_northing = Northing;
588
while (grid_northing >= TWOMIL)
590
grid_northing = grid_northing - TWOMIL;
592
grid_northing = grid_northing + pattern_offset;
593
if(grid_northing >= TWOMIL)
594
grid_northing = grid_northing - TWOMIL;
596
letters[2] = (long)(grid_northing / ONEHT);
597
if (letters[2] > LETTER_H)
598
letters[2] = letters[2] + 1;
600
if (letters[2] > LETTER_N)
601
letters[2] = letters[2] + 1;
603
letters[1] = ltr2_low_value + ((long)(Easting / ONEHT) -1);
604
if ((ltr2_low_value == LETTER_J) && (letters[1] > LETTER_N))
605
letters[1] = letters[1] + 1;
607
Make_USNG_String (USNG, Zone, letters, Easting, Northing, Precision);
610
} /* END UTM_To_USNG */
613
long Set_USNG_Parameters (double a,
615
char *Ellipsoid_Code)
617
* The function SET_USNG_PARAMETERS receives the ellipsoid parameters and sets
618
* the corresponding state variables. If any errors occur, the error code(s)
619
* are returned by the function, otherwise USNG_NO_ERROR is returned.
621
* a : Semi-major axis of ellipsoid in meters (input)
622
* f : Flattening of ellipsoid (input)
623
* Ellipsoid_Code : 2-letter code for ellipsoid (input)
625
{ /* Set_USNG_Parameters */
627
double inv_f = 1 / f;
628
long Error_Code = USNG_NO_ERROR;
631
{ /* Semi-major axis must be greater than zero */
632
Error_Code |= USNG_A_ERROR;
634
if ((inv_f < 250) || (inv_f > 350))
635
{ /* Inverse flattening must be between 250 and 350 */
636
Error_Code |= USNG_INV_F_ERROR;
643
strcpy (USNG_Ellipsoid_Code, Ellipsoid_Code);
646
} /* Set_USNG_Parameters */
649
void Get_USNG_Parameters (double *a,
651
char* Ellipsoid_Code)
653
* The function Get_USNG_Parameters returns the current ellipsoid
656
* a : Semi-major axis of ellipsoid, in meters (output)
657
* f : Flattening of ellipsoid (output)
658
* Ellipsoid_Code : 2-letter code for ellipsoid (output)
660
{ /* Get_USNG_Parameters */
663
strcpy (Ellipsoid_Code, USNG_Ellipsoid_Code);
665
} /* Get_USNG_Parameters */
668
long Convert_Geodetic_To_USNG (double Latitude,
673
* The function Convert_Geodetic_To_USNG converts Geodetic (latitude and
674
* longitude) coordinates to a USNG coordinate string, according to the
675
* current ellipsoid parameters. If any errors occur, the error code(s)
676
* are returned by the function, otherwise USNG_NO_ERROR is returned.
678
* Latitude : Latitude in radians (input)
679
* Longitude : Longitude in radians (input)
680
* Precision : Precision level of USNG string (input)
681
* USNG : USNG coordinate string (output)
684
{ /* Convert_Geodetic_To_USNG */
689
long temp_error_code = USNG_NO_ERROR;
690
long error_code = USNG_NO_ERROR;
692
if ((Latitude < -PI_OVER_2) || (Latitude > PI_OVER_2))
693
{ /* Latitude out of range */
694
error_code |= USNG_LAT_ERROR;
696
if ((Longitude < -PI) || (Longitude > (2*PI)))
697
{ /* Longitude out of range */
698
error_code |= USNG_LON_ERROR;
700
if ((Precision < 0) || (Precision > MAX_PRECISION))
701
error_code |= USNG_PRECISION_ERROR;
704
if ((Latitude < MIN_UTM_LAT) || (Latitude > MAX_UTM_LAT))
706
temp_error_code = Set_UPS_Parameters (USNG_a, USNG_f);
709
temp_error_code |= Convert_Geodetic_To_UPS (Latitude, Longitude, &hemisphere, &easting, &northing);
711
error_code |= Convert_UPS_To_USNG (hemisphere, easting, northing, Precision, USNG);
714
if(temp_error_code & UPS_LAT_ERROR)
715
error_code |= USNG_LAT_ERROR;
716
if(temp_error_code & UPS_LON_ERROR)
717
error_code |= USNG_LON_ERROR;
722
if(temp_error_code & UPS_A_ERROR)
723
error_code |= USNG_A_ERROR;
724
if(temp_error_code & UPS_INV_F_ERROR)
725
error_code |= USNG_INV_F_ERROR;
730
temp_error_code = Set_UTM_Parameters (USNG_a, USNG_f, 0);
733
temp_error_code |= Convert_Geodetic_To_UTM (Latitude, Longitude, &zone, &hemisphere, &easting, &northing);
735
error_code |= UTM_To_USNG (zone, Latitude, easting, northing, Precision, USNG);
738
if(temp_error_code & UTM_LAT_ERROR)
739
error_code |= USNG_LAT_ERROR;
740
if(temp_error_code & UTM_LON_ERROR)
741
error_code |= USNG_LON_ERROR;
742
if(temp_error_code & UTM_ZONE_OVERRIDE_ERROR)
743
error_code |= USNG_ZONE_ERROR;
744
if(temp_error_code & UTM_EASTING_ERROR)
745
error_code |= USNG_EASTING_ERROR;
746
if(temp_error_code & UTM_NORTHING_ERROR)
747
error_code |= USNG_NORTHING_ERROR;
752
if(temp_error_code & UTM_A_ERROR)
753
error_code |= USNG_A_ERROR;
754
if(temp_error_code & UTM_INV_F_ERROR)
755
error_code |= USNG_INV_F_ERROR;
756
if(temp_error_code & UTM_ZONE_OVERRIDE_ERROR)
757
error_code |= USNG_ZONE_ERROR;
762
} /* Convert_Geodetic_To_USNG */
765
long Convert_USNG_To_Geodetic (char* USNG,
769
* The function Convert_USNG_To_Geodetic converts a USNG coordinate string
770
* to Geodetic (latitude and longitude) coordinates
771
* according to the current ellipsoid parameters. If any errors occur,
772
* the error code(s) are returned by the function, otherwise UTM_NO_ERROR
775
* USNG : USNG coordinate string (input)
776
* Latitude : Latitude in radians (output)
777
* Longitude : Longitude in radians (output)
780
{ /* Convert_USNG_To_Geodetic */
786
long temp_error_code = USNG_NO_ERROR;
787
long error_code = USNG_NO_ERROR;
789
error_code = USNG_Check_Zone(USNG, &zone_exists);
794
error_code |= Convert_USNG_To_UTM (USNG, &zone, &hemisphere, &easting, &northing);
795
if(!error_code || (error_code & USNG_LAT_WARNING))
797
temp_error_code = Set_UTM_Parameters (USNG_a, USNG_f, 0);
800
temp_error_code |= Convert_UTM_To_Geodetic (zone, hemisphere, easting, northing, Latitude, Longitude);
803
if((temp_error_code & UTM_ZONE_ERROR) || (temp_error_code & UTM_HEMISPHERE_ERROR))
804
error_code |= USNG_STRING_ERROR;
805
if(temp_error_code & UTM_EASTING_ERROR)
806
error_code |= USNG_EASTING_ERROR;
807
if(temp_error_code & UTM_NORTHING_ERROR)
808
error_code |= USNG_NORTHING_ERROR;
813
if(temp_error_code & UTM_A_ERROR)
814
error_code |= USNG_A_ERROR;
815
if(temp_error_code & UTM_INV_F_ERROR)
816
error_code |= USNG_INV_F_ERROR;
817
if(temp_error_code & UTM_ZONE_OVERRIDE_ERROR)
818
error_code |= USNG_ZONE_ERROR;
824
error_code |= Convert_USNG_To_UPS (USNG, &hemisphere, &easting, &northing);
827
temp_error_code = Set_UPS_Parameters (USNG_a, USNG_f);
830
temp_error_code |= Convert_UPS_To_Geodetic (hemisphere, easting, northing, Latitude, Longitude);
833
if(temp_error_code & UPS_HEMISPHERE_ERROR)
834
error_code |= USNG_STRING_ERROR;
835
if(temp_error_code & UPS_EASTING_ERROR)
836
error_code |= USNG_EASTING_ERROR;
837
if(temp_error_code & UPS_LAT_ERROR)
838
error_code |= USNG_NORTHING_ERROR;
843
if(temp_error_code & UPS_A_ERROR)
844
error_code |= USNG_A_ERROR;
845
if(temp_error_code & UPS_INV_F_ERROR)
846
error_code |= USNG_INV_F_ERROR;
852
} /* END OF Convert_USNG_To_Geodetic */
855
long Convert_UTM_To_USNG (long Zone,
862
* The function Convert_UTM_To_USNG converts UTM (zone, easting, and
863
* northing) coordinates to a USNG coordinate string, according to the
864
* current ellipsoid parameters. If any errors occur, the error code(s)
865
* are returned by the function, otherwise USNG_NO_ERROR is returned.
867
* Zone : UTM zone (input)
868
* Hemisphere : North or South hemisphere (input)
869
* Easting : Easting (X) in meters (input)
870
* Northing : Northing (Y) in meters (input)
871
* Precision : Precision level of USNG string (input)
872
* USNG : USNG coordinate string (output)
874
{ /* Convert_UTM_To_USNG */
875
double latitude; /* Latitude of UTM point */
876
double longitude; /* Longitude of UTM point */
877
long utm_error_code = USNG_NO_ERROR;
878
long error_code = USNG_NO_ERROR;
880
if ((Zone < 1) || (Zone > 60))
881
error_code |= USNG_ZONE_ERROR;
882
if ((Hemisphere != 'S') && (Hemisphere != 'N'))
883
error_code |= USNG_HEMISPHERE_ERROR;
884
if ((Easting < MIN_EASTING) || (Easting > MAX_EASTING))
885
error_code |= USNG_EASTING_ERROR;
886
if ((Northing < MIN_NORTHING) || (Northing > MAX_NORTHING))
887
error_code |= USNG_NORTHING_ERROR;
888
if ((Precision < 0) || (Precision > MAX_PRECISION))
889
error_code |= USNG_PRECISION_ERROR;
892
Set_UTM_Parameters (USNG_a, USNG_f, 0);
893
utm_error_code = Convert_UTM_To_Geodetic (Zone, Hemisphere, Easting, Northing, &latitude, &longitude);
897
if((utm_error_code & UTM_ZONE_ERROR) || (utm_error_code & UTM_HEMISPHERE_ERROR))
898
error_code |= USNG_STRING_ERROR;
899
if(utm_error_code & UTM_EASTING_ERROR)
900
error_code |= USNG_EASTING_ERROR;
901
if(utm_error_code & UTM_NORTHING_ERROR)
902
error_code |= USNG_NORTHING_ERROR;
905
error_code |= UTM_To_USNG (Zone, latitude, Easting, Northing, Precision, USNG);
908
} /* Convert_UTM_To_USNG */
911
long Convert_USNG_To_UTM (char *USNG,
917
* The function Convert_USNG_To_UTM converts a USNG coordinate string
918
* to UTM projection (zone, hemisphere, easting and northing) coordinates
919
* according to the current ellipsoid parameters. If any errors occur,
920
* the error code(s) are returned by the function, otherwise UTM_NO_ERROR
923
* USNG : USNG coordinate string (input)
924
* Zone : UTM zone (output)
925
* Hemisphere : North or South hemisphere (output)
926
* Easting : Easting (X) in meters (output)
927
* Northing : Northing (Y) in meters (output)
929
{ /* Convert_USNG_To_UTM */
931
double northing_offset;
933
long ltr2_high_value;
934
double pattern_offset;
935
double upper_lat_limit; /* North latitude limits based on 1st letter */
936
double lower_lat_limit; /* South latitude limits based on 1st letter */
937
double grid_easting; /* Easting for 100,000 meter grid square */
938
double grid_northing; /* Northing for 100,000 meter grid square */
939
long letters[USNG_LETTERS];
941
double latitude = 0.0;
942
double longitude = 0.0;
943
double divisor = 1.0;
944
long utm_error_code = USNG_NO_ERROR;
945
long error_code = USNG_NO_ERROR;
947
error_code = Break_USNG_String (USNG, Zone, letters, Easting, Northing, &in_precision);
949
error_code |= USNG_STRING_ERROR;
954
if ((letters[0] == LETTER_X) && ((*Zone == 32) || (*Zone == 34) || (*Zone == 36)))
955
error_code |= USNG_STRING_ERROR;
958
if (letters[0] < LETTER_N)
963
USNG_Get_Grid_Values(*Zone, <r2_low_value, <r2_high_value, &pattern_offset);
965
/* Check that the second letter of the USNG string is within
966
* the range of valid second letter values
967
* Also check that the third letter is valid */
968
if ((letters[1] < ltr2_low_value) || (letters[1] > ltr2_high_value) || (letters[2] > LETTER_V))
969
error_code |= USNG_STRING_ERROR;
973
double row_letter_northing = (double)(letters[2]) * ONEHT;
974
grid_easting = (double)((letters[1]) - ltr2_low_value + 1) * ONEHT;
975
if ((ltr2_low_value == LETTER_J) && (letters[1] > LETTER_O))
976
grid_easting = grid_easting - ONEHT;
978
if (letters[2] > LETTER_O)
979
row_letter_northing = row_letter_northing - ONEHT;
981
if (letters[2] > LETTER_I)
982
row_letter_northing = row_letter_northing - ONEHT;
984
if (row_letter_northing >= TWOMIL)
985
row_letter_northing = row_letter_northing - TWOMIL;
987
error_code = USNG_Get_Latitude_Band_Min_Northing(letters[0], &min_northing, &northing_offset);
990
grid_northing = row_letter_northing - pattern_offset;
991
if(grid_northing < 0)
992
grid_northing += TWOMIL;
994
grid_northing += northing_offset;
996
if(grid_northing < min_northing)
997
grid_northing += TWOMIL;
999
*Easting = grid_easting + *Easting;
1000
*Northing = grid_northing + *Northing;
1002
/* check that point is within Zone Letter bounds */
1003
utm_error_code = Set_UTM_Parameters(USNG_a, USNG_f, 0);
1004
if (!utm_error_code)
1006
utm_error_code = Convert_UTM_To_Geodetic(*Zone,*Hemisphere,*Easting,*Northing,&latitude,&longitude);
1007
if (!utm_error_code)
1009
divisor = pow (10.0, in_precision);
1010
error_code = USNG_Get_Latitude_Range(letters[0], &upper_lat_limit, &lower_lat_limit);
1013
if (!(((lower_lat_limit - DEG_TO_RAD/divisor) <= latitude) && (latitude <= (upper_lat_limit + DEG_TO_RAD/divisor))))
1014
error_code |= USNG_LAT_ERROR;
1019
if((utm_error_code & UTM_ZONE_ERROR) || (utm_error_code & UTM_HEMISPHERE_ERROR))
1020
error_code |= USNG_STRING_ERROR;
1021
if(utm_error_code & UTM_EASTING_ERROR)
1022
error_code |= USNG_EASTING_ERROR;
1023
if(utm_error_code & UTM_NORTHING_ERROR)
1024
error_code |= USNG_NORTHING_ERROR;
1029
if(utm_error_code & UTM_A_ERROR)
1030
error_code |= USNG_A_ERROR;
1031
if(utm_error_code & UTM_INV_F_ERROR)
1032
error_code |= USNG_INV_F_ERROR;
1033
if(utm_error_code & UTM_ZONE_OVERRIDE_ERROR)
1034
error_code |= USNG_ZONE_ERROR;
1041
return (error_code);
1042
} /* Convert_USNG_To_UTM */
1045
long Convert_UPS_To_USNG (char Hemisphere,
1051
* The function Convert_UPS_To_USNG converts UPS (hemisphere, easting,
1052
* and northing) coordinates to a USNG coordinate string according to
1053
* the current ellipsoid parameters. If any errors occur, the error
1054
* code(s) are returned by the function, otherwise UPS_NO_ERROR is
1057
* Hemisphere : Hemisphere either 'N' or 'S' (input)
1058
* Easting : Easting/X in meters (input)
1059
* Northing : Northing/Y in meters (input)
1060
* Precision : Precision level of USNG string (input)
1061
* USNG : USNG coordinate string (output)
1063
{ /* Convert_UPS_To_USNG */
1064
double false_easting; /* False easting for 2nd letter */
1065
double false_northing; /* False northing for 3rd letter */
1066
double grid_easting; /* Easting used to derive 2nd letter of USNG */
1067
double grid_northing; /* Northing used to derive 3rd letter of USNG */
1068
long ltr2_low_value; /* 2nd letter range - low number */
1069
int letters[USNG_LETTERS]; /* Number location of 3 letters in alphabet */
1072
long error_code = USNG_NO_ERROR;
1074
if ((Hemisphere != 'N') && (Hemisphere != 'S'))
1075
error_code |= USNG_HEMISPHERE_ERROR;
1076
if ((Easting < MIN_EAST_NORTH) || (Easting > MAX_EAST_NORTH))
1077
error_code |= USNG_EASTING_ERROR;
1078
if ((Northing < MIN_EAST_NORTH) || (Northing > MAX_EAST_NORTH))
1079
error_code |= USNG_NORTHING_ERROR;
1080
if ((Precision < 0) || (Precision > MAX_PRECISION))
1081
error_code |= USNG_PRECISION_ERROR;
1084
divisor = pow (10.0, (5 - Precision));
1085
Easting = (long)(Easting/divisor + 1.0e-9) * divisor;
1086
Northing = (long)(Northing/divisor) * divisor;
1088
if (Hemisphere == 'N')
1090
if (Easting >= TWOMIL)
1091
letters[0] = LETTER_Z;
1093
letters[0] = LETTER_Y;
1095
index = letters[0] - 22;
1096
ltr2_low_value = UPS_Constant_Table[index].ltr2_low_value;
1097
false_easting = UPS_Constant_Table[index].false_easting;
1098
false_northing = UPS_Constant_Table[index].false_northing;
1102
if (Easting >= TWOMIL)
1103
letters[0] = LETTER_B;
1105
letters[0] = LETTER_A;
1107
ltr2_low_value = UPS_Constant_Table[letters[0]].ltr2_low_value;
1108
false_easting = UPS_Constant_Table[letters[0]].false_easting;
1109
false_northing = UPS_Constant_Table[letters[0]].false_northing;
1112
grid_northing = Northing;
1113
grid_northing = grid_northing - false_northing;
1114
letters[2] = (long)(grid_northing / ONEHT);
1116
if (letters[2] > LETTER_H)
1117
letters[2] = letters[2] + 1;
1119
if (letters[2] > LETTER_N)
1120
letters[2] = letters[2] + 1;
1122
grid_easting = Easting;
1123
grid_easting = grid_easting - false_easting;
1124
letters[1] = ltr2_low_value + ((long)(grid_easting / ONEHT));
1126
if (Easting < TWOMIL)
1128
if (letters[1] > LETTER_L)
1129
letters[1] = letters[1] + 3;
1131
if (letters[1] > LETTER_U)
1132
letters[1] = letters[1] + 2;
1136
if (letters[1] > LETTER_C)
1137
letters[1] = letters[1] + 2;
1139
if (letters[1] > LETTER_H)
1140
letters[1] = letters[1] + 1;
1142
if (letters[1] > LETTER_L)
1143
letters[1] = letters[1] + 3;
1146
Make_USNG_String (USNG, 0, letters, Easting, Northing, Precision);
1148
return (error_code);
1149
} /* Convert_UPS_To_USNG */
1152
long Convert_USNG_To_UPS ( char *USNG,
1157
* The function Convert_USNG_To_UPS converts a USNG coordinate string
1158
* to UPS (hemisphere, easting, and northing) coordinates, according
1159
* to the current ellipsoid parameters. If any errors occur, the error
1160
* code(s) are returned by the function, otherwide UPS_NO_ERROR is returned.
1162
* USNG : USNG coordinate string (input)
1163
* Hemisphere : Hemisphere either 'N' or 'S' (output)
1164
* Easting : Easting/X in meters (output)
1165
* Northing : Northing/Y in meters (output)
1167
{ /* Convert_USNG_To_UPS */
1168
long ltr2_high_value; /* 2nd letter range - high number */
1169
long ltr3_high_value; /* 3rd letter range - high number (UPS) */
1170
long ltr2_low_value; /* 2nd letter range - low number */
1171
double false_easting; /* False easting for 2nd letter */
1172
double false_northing; /* False northing for 3rd letter */
1173
double grid_easting; /* easting for 100,000 meter grid square */
1174
double grid_northing; /* northing for 100,000 meter grid square */
1176
long letters[USNG_LETTERS];
1179
long error_code = USNG_NO_ERROR;
1181
error_code = Break_USNG_String (USNG, &zone, letters, Easting, Northing, &in_precision);
1183
error_code |= USNG_STRING_ERROR;
1188
if (letters[0] >= LETTER_Y)
1192
index = letters[0] - 22;
1193
ltr2_low_value = UPS_Constant_Table[index].ltr2_low_value;
1194
ltr2_high_value = UPS_Constant_Table[index].ltr2_high_value;
1195
ltr3_high_value = UPS_Constant_Table[index].ltr3_high_value;
1196
false_easting = UPS_Constant_Table[index].false_easting;
1197
false_northing = UPS_Constant_Table[index].false_northing;
1203
ltr2_low_value = UPS_Constant_Table[letters[0]].ltr2_low_value;
1204
ltr2_high_value = UPS_Constant_Table[letters[0]].ltr2_high_value;
1205
ltr3_high_value = UPS_Constant_Table[letters[0]].ltr3_high_value;
1206
false_easting = UPS_Constant_Table[letters[0]].false_easting;
1207
false_northing = UPS_Constant_Table[letters[0]].false_northing;
1210
/* Check that the second letter of the USNG string is within
1211
* the range of valid second letter values
1212
* Also check that the third letter is valid */
1213
if ((letters[1] < ltr2_low_value) || (letters[1] > ltr2_high_value) ||
1214
((letters[1] == LETTER_D) || (letters[1] == LETTER_E) ||
1215
(letters[1] == LETTER_M) || (letters[1] == LETTER_N) ||
1216
(letters[1] == LETTER_V) || (letters[1] == LETTER_W)) ||
1217
(letters[2] > ltr3_high_value))
1218
error_code = USNG_STRING_ERROR;
1222
grid_northing = (double)letters[2] * ONEHT + false_northing;
1223
if (letters[2] > LETTER_I)
1224
grid_northing = grid_northing - ONEHT;
1226
if (letters[2] > LETTER_O)
1227
grid_northing = grid_northing - ONEHT;
1229
grid_easting = (double)((letters[1]) - ltr2_low_value) * ONEHT + false_easting;
1230
if (ltr2_low_value != LETTER_A)
1232
if (letters[1] > LETTER_L)
1233
grid_easting = grid_easting - 300000.0;
1235
if (letters[1] > LETTER_U)
1236
grid_easting = grid_easting - 200000.0;
1240
if (letters[1] > LETTER_C)
1241
grid_easting = grid_easting - 200000.0;
1243
if (letters[1] > LETTER_I)
1244
grid_easting = grid_easting - ONEHT;
1246
if (letters[1] > LETTER_L)
1247
grid_easting = grid_easting - 300000.0;
1250
*Easting = grid_easting + *Easting;
1251
*Northing = grid_northing + *Northing;
1255
return (error_code);
1256
} /* Convert_USNG_To_UPS */