1
/***************************************************************************/
2
/* RSC IDENTIFIER: GARS
6
* This component provides conversions from Geodetic coordinates (latitude
7
* and longitude in radians) to a GARS coordinate string.
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
* GARS_NO_ERROR : No errors occurred in function
17
* GARS_LAT_ERROR : Latitude outside of valid range
19
* GARS_LON_ERROR : Longitude outside of valid range
20
* (-180 to 360 degrees)
21
* GARS_STR_ERROR : A GARS string error: string too long,
22
* string too short, invalid numbers/letters
23
* GARS_STR_LAT_ERROR : The latitude part of the GARS string
24
* (fourth and fifth characters) is invalid.
25
* GARS_STR_LON_ERROR : The longitude part of the GARS string
26
* (first three characters) is invalid.
27
* GARS_STR_15_MIN_ERROR : The 15 minute part of the GARS
28
* string is less than 1 or greater than 4.
29
* GARS_STR_5_MIN_ERROR : The 5 minute part of the GARS
30
* string is less than 1 or greater than 9.
31
* GARS_PRECISION_ERROR : The precision must be between 0 and 5
37
* GARS is intended for reuse by any application that performs a
38
* conversion between Geodetic and GARS coordinates.
42
* Further information on GARS can be found in the Reuse Manual.
44
* GARS originated from :
46
* http://earth-info.nga.mil/GandG/coordsys/grids/gars.html
51
* None apply to this component.
55
* GARS has no restrictions.
59
* GARS was tested and certified in the following environments:
61
* 1. Solaris 2.5 with GCC version 2.8.1
62
* 2. Windows XP with MS Visual C++ version 6
68
* 07-10-06 Original Code
72
/***************************************************************************/
84
* ctype.h - Standard C character handling library
85
* math.h - Standard C math library
86
* stdio.h - Standard C input/output library
87
* stdlib.h - Standard C general utility library
88
* string.h - Standard C string handling library
89
* gars.h - for prototype error checking
93
/***************************************************************************/
98
#define MIN_LATITUDE -90.0 /* Minimum latitude */
99
#define MAX_LATITUDE 90.0 /* Maximum latitude */
100
#define MIN_LONGITUDE -180.0 /* Minimum longitude */
101
#define MAX_LONGITUDE 360.0 /* Maximum longitude */
102
#define MIN_PER_DEG 60 /* Number of minutes per degree */
103
#define GARS_MINIMUM 5 /* Minimum number of chars for GARS */
104
#define GARS_MAXIMUM 7 /* Maximum number of chars for GARS */
105
#define GARS_LETTERS 4 /* Number of letters in GARS string */
106
#define MAX_PRECISION 5 /* Maximum precision of minutes part */
107
#define LETTER_A_OFFSET 65 /* Letter A offset in character set */
108
#define PI 3.14159265358979323e0 /* PI */
109
#define PI_OVER_180 PI / 180.0
110
#define RADIAN_TO_DEGREE 180.0e0 / PI
112
/* A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7 */
113
#define LETTER_I 8 /* ARRAY INDEX FOR LETTER I */
114
/* J = 9, K = 10, L = 11, M = 12, N = 13 */
115
#define LETTER_O 14 /* ARRAY INDEX FOR LETTER O */
116
/* P = 15, Q = 16, R = 17, S = 18, T = 19, U = 20, V = 21, */
117
/* W = 22, X = 23, Y = 24, Z = 25 */
129
/***************************************************************************/
134
long Convert_GARS_To_Geodetic( char *gars,
137
{ /* BEGIN Convert_GARS_To_Geodetic */
139
* This function converts a GARS coordinate string to Geodetic (latitude
140
* and longitude in radians) coordinates.
142
* gars : GARS coordinate string. (input)
143
* latitude : Latitude in radians. (output)
144
* longitude : Longitude in radians. (output)
148
long gars_length; /* length of GARS string */
154
char _15_minute_value = 0;
155
char _5_minute_value = 0;
156
double lat_minutes = 0;
157
double lon_minutes = 0;
158
long error_code = GARS_NO_ERROR;
160
gars_length = strlen( gars );
161
if ( ( gars_length < GARS_MINIMUM ) || ( gars_length > GARS_MAXIMUM ) )
162
error_code |= GARS_STR_ERROR;
166
while( isdigit( gars[index] ) )
168
ew_str[index] = gars[index];
172
error_code |= GARS_STR_ERROR;
175
/* Get 30 minute east/west value, 1-720 */
176
ew_value = atoi( ew_str );
178
letter = gars[index];
179
if( !isalpha( letter ) )
180
error_code |= GARS_STR_LON_ERROR;
183
/* Get first 30 minute north/south letter, A-Q */
184
ns_str[0] = toupper( letter ) - LETTER_A_OFFSET;
185
letter = gars[++index];
186
if( !isalpha( letter ) )
187
error_code |= GARS_STR_LAT_ERROR;
190
/* Get second 30 minute north/south letter, A-Z */
191
ns_str[1] = toupper( letter ) - LETTER_A_OFFSET;
193
if( index + 1 < gars_length )
195
/* Get 15 minute quadrant value, 1-4 */
196
_15_minute_value = gars[++index];
197
if( !isdigit( _15_minute_value ) || _15_minute_value < _1 || _15_minute_value > _4 )
198
error_code |= GARS_STR_15_MIN_ERROR;
201
if( index + 1 < gars_length )
203
/* Get 5 minute quadrant value, 1-9 */
204
_5_minute_value = gars[++index];
205
if( !isdigit( _5_minute_value ) || _5_minute_value < _1 || _5_minute_value > _9 )
206
error_code |= GARS_STR_5_MIN_ERROR;
213
*longitude = ( ( ( ew_value - 1.0 ) / 2.0 ) - 180.0 );
215
/* Letter I and O are invalid */
216
if( ns_str[0] >= LETTER_O )
218
if( ns_str[0] >= LETTER_I )
221
if( ns_str[1] >= LETTER_O )
223
if( ns_str[1] >= LETTER_I )
226
*latitude = ( ( -90.0 + ( ns_str[0] * 12.0 ) ) + ( ns_str[1] / 2.0 ) );
228
switch( _15_minute_value )
245
switch( _5_minute_value )
280
/* Add these values to force the reference point to be the center of the box */
281
if( _5_minute_value )
286
else if( _15_minute_value )
297
*latitude += lat_minutes / MIN_PER_DEG;
298
*longitude += lon_minutes / MIN_PER_DEG;
299
*longitude *= PI_OVER_180;
300
*latitude *= PI_OVER_180;
306
return ( error_code );
307
} /* END OF Convert_GARS_To_Geodetic */
310
long Convert_Geodetic_To_GARS( double latitude,
314
{ /* BEGIN Convert_Geodetic_To_GARS */
316
* This function converts Geodetic (latitude and longitude in radians)
317
* coordinates to a GARS coordinate string. Precision specifies the
318
* number of digits in the GARS string for latitude and longitude:
319
* 0: 30 minutes (5 characters)
320
* 1: 15 minutes (6 characters)
321
* 2: 5 minutes (7 characters)
323
* latitude : Latitude in radians. (input)
324
* longitude : Longitude in radians. (input)
325
* precision : Precision specified by the user. (input)
326
* gars : GARS coordinate string. (output)
331
long letter_index[GARS_LETTERS + 1]; /* GARS letters */
332
char _15_minute_value_str[2] = "";
333
char _5_minute_value_str[2] = "";
334
double round_error = 5.0e-11;
335
char* _15_minute_array[2][2] = {{"3", "1"}, {"4", "2"}};
336
char* _5_minute_array[3][3] = {{"7", "4", "1"}, {"8", "5", "2"}, {"9", "6", "3"}};
337
double long_minutes, lat_minutes;
338
double long_remainder, lat_remainder;
339
long horiz_index_30, vert_index_30;
340
long horiz_index_15, vert_index_15;
341
long horiz_index_5, vert_index_5;
342
long error_code = GARS_NO_ERROR;
344
latitude = latitude * RADIAN_TO_DEGREE;
345
longitude = longitude * RADIAN_TO_DEGREE;
347
if ( ( latitude < MIN_LATITUDE ) || ( latitude > MAX_LATITUDE ) )
348
error_code |= GARS_LAT_ERROR;
349
if ( ( longitude < MIN_LONGITUDE ) || ( longitude > MAX_LONGITUDE ) )
350
error_code |= GARS_LON_ERROR;
351
if ( ( precision < 0 ) || ( precision > MAX_PRECISION ) )
352
error_code |= GARS_PRECISION_ERROR;
355
/* North pole is an exception, read over and down */
356
if( latitude == MAX_LATITUDE )
357
latitude = 89.99999999999;
359
if( longitude >= 180.0 )
362
/* Convert longitude and latitude from degrees to minutes */
363
/* longitude assumed in -180 <= long < +180 range */
364
long_minutes = ( longitude - MIN_LONGITUDE ) * 60.0 + round_error;
365
lat_minutes = ( latitude - MIN_LATITUDE ) * 60.0 + round_error;
366
/* now we have a positive number of minutes */
368
/* Find 30-min cell indices 0-719 and 0-359 */
369
horiz_index_30 = ( long )( long_minutes / 30.0 );
370
vert_index_30 = ( long )( lat_minutes / 30.0 );
372
/* Compute remainders 0 <= x < 30.0 */
373
long_remainder = long_minutes - ( horiz_index_30 ) * 30.0;
374
lat_remainder = lat_minutes - ( vert_index_30 ) * 30.0;
376
/* Find 15-min cell indices 0 or 1 */
377
horiz_index_15 = ( long )( long_remainder / 15.0 );
378
vert_index_15 = ( long )( lat_remainder / 15.0 );
380
/* Compute remainders 0 <= x < 15.0 */
381
long_remainder = long_remainder - ( horiz_index_15 ) * 15.0;
382
lat_remainder = lat_remainder - ( vert_index_15 ) * 15.0;
384
/* Find 5-min cell indices 0, 1, or 2 */
385
horiz_index_5 = ( long )( long_remainder / 5.0 );
386
vert_index_5 = ( long )( lat_remainder / 5.0 );
388
/* Calculate 30 minute east/west value, 1-720 */
389
ew_value = horiz_index_30 + 1;
391
/* Calculate 30 minute north/south first letter, A-Q */
392
letter_index[0] = ( long )( vert_index_30 / 24.0 );
394
/* Calculate 30 minute north/south second letter, A-Z */
395
letter_index[1] = ( long )( vert_index_30 - letter_index[0] * 24.0 );
397
/* Letters I and O are invalid, so skip them */
398
if( letter_index[0] >= LETTER_I )
400
if( letter_index[0] >= LETTER_O )
403
if( letter_index[1] >= LETTER_I )
405
if( letter_index[1] >= LETTER_O )
408
/* Get 15 minute quadrant value, 1-4 */
409
strcpy( _15_minute_value_str, _15_minute_array[horiz_index_15][vert_index_15] );
411
/* Get 5 minute keypad value, 1-9 */
412
strcpy( _5_minute_value_str, _5_minute_array[horiz_index_5][vert_index_5] );
414
/* Form the gars string */
416
sprintf ( gars, "00%d%c%c", ew_value, ( char )( letter_index[0] + LETTER_A_OFFSET ), ( char )( letter_index[1] + LETTER_A_OFFSET ) );
417
else if( ew_value < 100 )
418
sprintf ( gars, "0%d%c%c", ew_value, ( char )( letter_index[0] + LETTER_A_OFFSET ), ( char )( letter_index[1] + LETTER_A_OFFSET ) );
420
sprintf ( gars, "%d%c%c", ew_value, ( char )( letter_index[0] + LETTER_A_OFFSET ), ( char )( letter_index[1] + LETTER_A_OFFSET ) );
424
strcat( gars, _15_minute_value_str);
427
strcat( gars, _5_minute_value_str);
433
return ( error_code );
434
} /* END OF Convert_Geodetic_To_GARS */