~ubuntu-branches/ubuntu/raring/geotranz/raring

« back to all changes in this revision

Viewing changes to dt_cc/gars/gars.c

  • Committer: Bazaar Package Importer
  • Author(s): Roberto Lumbreras
  • Date: 2008-10-17 14:43:09 UTC
  • Revision ID: james.westby@ubuntu.com-20081017144309-jb7uzfi1y1lvez8j
Tags: upstream-2.4.2
ImportĀ upstreamĀ versionĀ 2.4.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************/
 
2
/* RSC IDENTIFIER: GARS
 
3
 *
 
4
 * ABSTRACT
 
5
 *
 
6
 *    This component provides conversions from Geodetic coordinates (latitude
 
7
 *    and longitude in radians) to a GARS coordinate string.
 
8
 *
 
9
 * ERROR HANDLING
 
10
 *
 
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:
 
15
 *
 
16
 *          GARS_NO_ERROR          : No errors occurred in function
 
17
 *          GARS_LAT_ERROR         : Latitude outside of valid range 
 
18
 *                                      (-90 to 90 degrees)
 
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 
 
32
 *                                      inclusive.
 
33
 *
 
34
 *
 
35
 * REUSE NOTES
 
36
 *
 
37
 *    GARS is intended for reuse by any application that performs a 
 
38
 *    conversion between Geodetic and GARS coordinates.
 
39
 *    
 
40
 * REFERENCES
 
41
 *
 
42
 *    Further information on GARS can be found in the Reuse Manual.
 
43
 *
 
44
 *    GARS originated from : 
 
45
 *                              
 
46
 *                   http://earth-info.nga.mil/GandG/coordsys/grids/gars.html           
 
47
 *                              
 
48
 *
 
49
 * LICENSES
 
50
 *
 
51
 *    None apply to this component.
 
52
 *
 
53
 * RESTRICTIONS
 
54
 *
 
55
 *    GARS has no restrictions.
 
56
 *
 
57
 * ENVIRONMENT
 
58
 *
 
59
 *    GARS was tested and certified in the following environments:
 
60
 *
 
61
 *    1. Solaris 2.5 with GCC version 2.8.1
 
62
 *    2. Windows XP with MS Visual C++ version 6
 
63
 *
 
64
 * MODIFICATIONS
 
65
 *
 
66
 *    Date              Description
 
67
 *    ----              -----------
 
68
 *    07-10-06          Original Code
 
69
 */
 
70
 
 
71
 
 
72
/***************************************************************************/
 
73
/*
 
74
 *                               INCLUDES
 
75
 */
 
76
 
 
77
#include <ctype.h>
 
78
//#include <math.h>
 
79
#include <stdio.h>
 
80
#include <stdlib.h>
 
81
#include <string.h>
 
82
#include "gars.h"
 
83
/*
 
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
 
90
 */
 
91
 
 
92
 
 
93
/***************************************************************************/
 
94
/*
 
95
 *                              DEFINES
 
96
 */
 
97
 
 
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
 
111
 
 
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                                         */
 
118
 
 
119
#define _1                '1'
 
120
#define _2                '2'
 
121
#define _3                '3'
 
122
#define _4                '4'
 
123
#define _5                '5'
 
124
#define _6                '6'
 
125
#define _7                '7'
 
126
#define _8                '8'
 
127
#define _9                '9'
 
128
 
 
129
/***************************************************************************/
 
130
/*
 
131
 *                              FUNCTIONS     
 
132
 */
 
133
 
 
134
long Convert_GARS_To_Geodetic( char *gars,
 
135
                               double *latitude, 
 
136
                               double *longitude )
 
137
{ /* BEGIN Convert_GARS_To_Geodetic */
 
138
/*
 
139
 *  This function converts a GARS coordinate string to Geodetic (latitude
 
140
 *  and longitude in radians) coordinates.
 
141
 *
 
142
 *    gars      : GARS coordinate string.     (input)
 
143
 *    latitude  : Latitude in radians.        (output)
 
144
 *    longitude : Longitude in radians.       (output)
 
145
 *
 
146
 */
 
147
 
 
148
  long gars_length;          /* length of GARS string                      */
 
149
  int index = 0;
 
150
  char ew_str[4];
 
151
  int ew_value = 0;
 
152
  char letter = ' ';
 
153
  int ns_str[3];
 
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;
 
159
 
 
160
  gars_length = strlen( gars );
 
161
  if ( ( gars_length < GARS_MINIMUM ) || ( gars_length > GARS_MAXIMUM ) )
 
162
    error_code |= GARS_STR_ERROR;
 
163
 
 
164
  if ( !error_code )
 
165
  {
 
166
    while( isdigit( gars[index] ) )
 
167
    {
 
168
      ew_str[index] = gars[index];
 
169
      index++;
 
170
    }
 
171
    if( index != 3 )
 
172
      error_code |= GARS_STR_ERROR;
 
173
    else
 
174
    {
 
175
      /* Get 30 minute east/west value, 1-720 */
 
176
      ew_value = atoi( ew_str );
 
177
 
 
178
      letter = gars[index];
 
179
      if( !isalpha( letter ) )
 
180
        error_code |= GARS_STR_LON_ERROR;
 
181
      else
 
182
      {
 
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;
 
188
        else
 
189
        {
 
190
        /* Get second 30 minute north/south letter, A-Z */
 
191
          ns_str[1] = toupper( letter ) - LETTER_A_OFFSET;
 
192
    
 
193
          if( index + 1 < gars_length )
 
194
          {
 
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;
 
199
            else
 
200
            {
 
201
              if( index + 1 < gars_length )
 
202
              {
 
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;
 
207
              }
 
208
            }
 
209
          }
 
210
 
 
211
          if( !error_code )
 
212
          {
 
213
            *longitude = ( ( ( ew_value - 1.0 ) / 2.0 ) - 180.0 );
 
214
 
 
215
            /* Letter I and O are invalid */
 
216
            if( ns_str[0] >= LETTER_O )
 
217
              ns_str[0] --;
 
218
            if( ns_str[0] >= LETTER_I ) 
 
219
              ns_str[0] --;
 
220
 
 
221
            if( ns_str[1] >= LETTER_O )
 
222
              ns_str[1] --;
 
223
            if( ns_str[1] >= LETTER_I )
 
224
              ns_str[1] --;
 
225
 
 
226
            *latitude = ( ( -90.0 + ( ns_str[0] * 12.0 ) ) + ( ns_str[1] / 2.0 ) );
 
227
 
 
228
            switch( _15_minute_value )
 
229
            {
 
230
              case _1:
 
231
                lat_minutes = 15.0;
 
232
                break;
 
233
              case _4:
 
234
                lon_minutes = 15.0;
 
235
                break;
 
236
              case _2:
 
237
                lat_minutes = 15.0;
 
238
                lon_minutes = 15.0;
 
239
                break;
 
240
              case _3:
 
241
              default:
 
242
                break;
 
243
            }
 
244
 
 
245
            switch( _5_minute_value )
 
246
            {
 
247
              case _4:
 
248
                lat_minutes += 5.0;
 
249
                break;
 
250
              case _1:
 
251
                lat_minutes += 10.0;
 
252
                break;
 
253
              case _8:
 
254
                lon_minutes += 5.0;
 
255
                break;
 
256
              case _5:
 
257
                lon_minutes += 5.0;
 
258
                lat_minutes += 5.0;
 
259
                break;
 
260
              case _2:
 
261
                lon_minutes += 5.0;
 
262
                lat_minutes += 10.0;
 
263
                break;
 
264
              case _9:
 
265
                lon_minutes += 10.0;
 
266
                break;
 
267
              case _6:
 
268
                lon_minutes += 10.0;
 
269
                lat_minutes += 5.0;
 
270
                break;
 
271
              case _3:
 
272
                lon_minutes += 10.0;
 
273
                lat_minutes += 10.0;
 
274
                break;
 
275
              case _7:
 
276
              default:
 
277
                break;
 
278
            }
 
279
 
 
280
            /* Add these values to force the reference point to be the center of the box */
 
281
            if( _5_minute_value )
 
282
            {
 
283
              lat_minutes += 2.5;
 
284
              lon_minutes += 2.5;
 
285
            }
 
286
            else if( _15_minute_value )
 
287
            {
 
288
              lat_minutes += 7.5;
 
289
              lon_minutes += 7.5;
 
290
            }
 
291
            else
 
292
            {
 
293
              lat_minutes += 15.0;
 
294
              lon_minutes += 15.0;
 
295
            }
 
296
 
 
297
            *latitude += lat_minutes / MIN_PER_DEG;
 
298
            *longitude += lon_minutes / MIN_PER_DEG;
 
299
            *longitude *= PI_OVER_180;
 
300
            *latitude *= PI_OVER_180;
 
301
          }
 
302
        }
 
303
      }
 
304
    }
 
305
  }
 
306
  return ( error_code );
 
307
} /* END OF Convert_GARS_To_Geodetic */
 
308
 
 
309
 
 
310
long Convert_Geodetic_To_GARS( double latitude,
 
311
                               double longitude,
 
312
                               long precision,
 
313
                               char *gars )
 
314
{ /* BEGIN Convert_Geodetic_To_GARS */
 
315
/*   
 
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)
 
322
 *
 
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)
 
327
 *
 
328
 */
 
329
 
 
330
  long ew_value;
 
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;
 
343
 
 
344
  latitude = latitude * RADIAN_TO_DEGREE;
 
345
  longitude = longitude *  RADIAN_TO_DEGREE;
 
346
 
 
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;
 
353
  if ( !error_code )
 
354
  {
 
355
    /* North pole is an exception, read over and down */
 
356
    if( latitude == MAX_LATITUDE )
 
357
      latitude = 89.99999999999;
 
358
 
 
359
    if( longitude >= 180.0 )
 
360
      longitude -= 360.0;
 
361
 
 
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 */ 
 
367
 
 
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 );
 
371
 
 
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; 
 
375
 
 
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 ); 
 
379
 
 
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; 
 
383
 
 
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 );
 
387
 
 
388
    /* Calculate 30 minute east/west value, 1-720 */
 
389
    ew_value = horiz_index_30 + 1;
 
390
 
 
391
    /* Calculate 30 minute north/south first letter, A-Q */
 
392
    letter_index[0] = ( long )( vert_index_30 / 24.0 );
 
393
 
 
394
    /* Calculate 30 minute north/south second letter, A-Z */
 
395
    letter_index[1] = ( long )( vert_index_30 - letter_index[0] * 24.0 );
 
396
 
 
397
    /* Letters I and O are invalid, so skip them */
 
398
    if( letter_index[0] >= LETTER_I )
 
399
      letter_index[0]++;
 
400
    if( letter_index[0] >= LETTER_O )
 
401
      letter_index[0] ++;
 
402
 
 
403
    if( letter_index[1] >= LETTER_I )
 
404
      letter_index[1]++;
 
405
    if( letter_index[1] >= LETTER_O )
 
406
      letter_index[1] ++;
 
407
 
 
408
    /* Get 15 minute quadrant value, 1-4 */
 
409
    strcpy( _15_minute_value_str, _15_minute_array[horiz_index_15][vert_index_15] );
 
410
 
 
411
    /* Get 5 minute keypad value, 1-9 */
 
412
    strcpy( _5_minute_value_str, _5_minute_array[horiz_index_5][vert_index_5] );
 
413
 
 
414
    /* Form the gars string */
 
415
    if( ew_value < 10 )
 
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 ) );
 
419
    else
 
420
      sprintf ( gars, "%d%c%c", ew_value, ( char )( letter_index[0] + LETTER_A_OFFSET ), ( char )( letter_index[1] + LETTER_A_OFFSET ) );
 
421
 
 
422
    if( precision > 0 )
 
423
    {
 
424
      strcat( gars, _15_minute_value_str);
 
425
 
 
426
      if( precision > 1 )
 
427
        strcat( gars, _5_minute_value_str);
 
428
    }
 
429
 
 
430
    gars[7] = '\0';
 
431
  }
 
432
 
 
433
  return ( error_code );
 
434
} /* END OF Convert_Geodetic_To_GARS */