UNCLASSIFIED

GeographicTranslator
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Macros
GARS.cpp
Go to the documentation of this file.
1 // CLASSIFICATION: UNCLASSIFIED
2 
3 /***************************************************************************/
4 /* RSC IDENTIFIER: GARS
5  *
6  * ABSTRACT
7  *
8  * This component provides conversions from Geodetic coordinates (latitude
9  * and longitude in radians) to a GARS coordinate string.
10  *
11  * ERROR HANDLING
12  *
13  * This component checks parameters for valid values. If an invalid value
14  * is found, the error code is combined with the current error code using
15  * the bitwise or. This combining allows multiple error codes to be
16  * returned. The possible error codes are:
17  *
18  * GARS_NO_ERROR : No errors occurred in function
19  * GARS_LAT_ERROR : Latitude outside of valid range
20  * (-90 to 90 degrees)
21  * GARS_LON_ERROR : Longitude outside of valid range
22  * (-180 to 360 degrees)
23  * GARS_STR_ERROR : A GARS string error: string too long,
24  * string too short, invalid numbers/letters
25  * GARS_STR_LAT_ERROR : The latitude part of the GARS string
26  * (fourth and fifth characters) is invalid.
27  * GARS_STR_LON_ERROR : The longitude part of the GARS string
28  * (first three characters) is invalid.
29  * GARS_STR_15_MIN_ERROR : The 15 minute part of the GARS
30  * string is less than 1 or greater than 4.
31  * GARS_STR_5_MIN_ERROR : The 5 minute part of the GARS
32  * string is less than 1 or greater than 9.
33  * GARS_PRECISION_ERROR : The precision must be between 0 and 5
34  * inclusive.
35  *
36  *
37  * REUSE NOTES
38  *
39  * GARS is intended for reuse by any application that performs a
40  * conversion between Geodetic and GARS coordinates.
41  *
42  * REFERENCES
43  *
44  * Further information on GARS can be found in the Reuse Manual.
45  *
46  * GARS originated from :
47  *
48  * http://earth-info.nga.mil/GandG/coordsys/grids/gars.html
49  *
50  *
51  * LICENSES
52  *
53  * None apply to this component.
54  *
55  * RESTRICTIONS
56  *
57  * GARS has no restrictions.
58  *
59  * ENVIRONMENT
60  *
61  * GARS was tested and certified in the following environments:
62  *
63  * 1. Solaris 2.5 with GCC version 2.8.1
64  * 2. Windows XP with MS Visual C++ version 6
65  *
66  * MODIFICATIONS
67  *
68  * Date Description
69  * ---- -----------
70  * 07-10-06 Original Code
71  * 03-02-07 Original C++ Code
72  */
73 
74 
75 /***************************************************************************/
76 /*
77  * INCLUDES
78  */
79 
80 #include <ctype.h>
81 #include <stdio.h>
82 #include <stdlib.h>
83 #include <string.h>
84 #include "GARS.h"
85 #include "GARSCoordinates.h"
87 #include "GeodeticCoordinates.h"
89 #include "ErrorMessages.h"
90 
91 /*
92  * ctype.h - Standard C character handling library
93  * stdio.h - Standard C input/output library
94  * stdlib.h - Standard C general utility library
95  * string.h - Standard C string handling library
96  * GARS.h - for prototype error checking
97  * GARSCoordinates.h - defines gars coordinates
98  * MapProjectionCoordinates.h - defines map projection coordinates
99  * GeodeticCoordinates.h - defines geodetic coordinates
100  * CoordinateConversionException.h - Exception handler
101  * ErrorMessages.h - Contains exception messages
102  */
103 
104 
105 using namespace MSP::CCS;
106 
107 
108 /***************************************************************************/
109 /* DEFINES
110  *
111  */
112 
113 const double MIN_LATITUDE = -90.0; /* Minimum latitude */
114 const double MAX_LATITUDE = 90.0; /* Maximum latitude */
115 const double MIN_LONGITUDE = -180.0; /* Minimum longitude */
116 const double MAX_LONGITUDE = 360.0; /* Maximum longitude */
117 const double MIN_PER_DEG = 60; /* Number of minutes per degree */
118 const int GARS_MINIMUM = 5; /* Minimum number of chars for GARS */
119 const int GARS_MAXIMUM = 7; /* Maximum number of chars for GARS */
120 const int GARS_LETTERS = 4; /* Number of letters in GARS string */
121 const int MAX_PRECISION = 5; /* Maximum precision of minutes part */
122 const int LETTER_A_OFFSET = 65; /* Letter A offset in character set */
123 const double PI = 3.14159265358979323e0; /* PI */
124 const double PI_OVER_180 = PI / 180.0;
125 const double RADIAN_TO_DEGREE = 180.0e0 / PI;
126 
127 /* A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7 */
128 const int LETTER_I = 8; /* ARRAY INDEX FOR LETTER I */
129 /* J = 9, K = 10, L = 11, M = 12, N = 13 */
130 const int LETTER_O = 14; /* ARRAY INDEX FOR LETTER O */
131 /* P = 15, Q = 16, R = 17, S = 18, T = 19, U = 20, V = 21, */
132 /* W = 22, X = 23, Y = 24, Z = 25 */
133 
134 const char _1 = '1';
135 const char _2 = '2';
136 const char _3 = '3';
137 const char _4 = '4';
138 const char _5 = '5';
139 const char _6 = '6';
140 const char _7 = '7';
141 const char _8 = '8';
142 const char _9 = '9';
143 
144 
145 /************************************************************************/
146 /* FUNCTIONS
147  *
148  */
149 
151  CoordinateSystem( 0, 0 )
152 {
153 }
154 
155 
156 GARS::GARS( const GARS &g )
157 {
160 }
161 
162 
164 {
165 }
166 
167 
169 {
170  if( this != &g )
171  {
174  }
175 
176  return *this;
177 }
178 
179 
181 {
182 /*
183  * The function convertFromGeodetic converts Geodetic (latitude and longitude in radians)
184  * coordinates to a GARS coordinate string. Precision specifies the
185  * number of digits in the GARS string for latitude and longitude:
186  * 0: 30 minutes (5 characters)
187  * 1: 15 minutes (6 characters)
188  * 2: 5 minutes (7 characters)
189  *
190  * longitude : Longitude in radians. (input)
191  * latitude : Latitude in radians. (input)
192  * precision : Precision specified by the user. (input)
193  * GARSString : GARS coordinate string. (output)
194  *
195  */
196 
197  long ew_value;
198  long letter_index[GARS_LETTERS + 1]; /* GARS letters */
199  char _15_minute_value_str[2] = "";
200  char _5_minute_value_str[2] = "";
201  double round_error = 5.0e-11;
202  char* _15_minute_array[2][2] = {{"3", "1"}, {"4", "2"}};
203  char* _5_minute_array[3][3] = {{"7", "4", "1"}, {"8", "5", "2"}, {"9", "6", "3"}};
204  double long_minutes, lat_minutes;
205  double long_remainder, lat_remainder;
206  long horiz_index_30, vert_index_30;
207  long horiz_index_15, vert_index_15;
208  long horiz_index_5, vert_index_5;
209  char GARSString[8];
210 
211  double latitude = geodeticCoordinates->latitude() * RADIAN_TO_DEGREE;
212  double longitude = geodeticCoordinates->longitude() * RADIAN_TO_DEGREE;
213 
214  if ( ( latitude < MIN_LATITUDE ) || ( latitude > MAX_LATITUDE ) )
216  if ( ( longitude < MIN_LONGITUDE ) || ( longitude > MAX_LONGITUDE ) )
218  if ( ( precision < 0 ) || ( precision > MAX_PRECISION ) )
220 
221  /* North pole is an exception, read over and down */
222  if( latitude == MAX_LATITUDE )
223  latitude = 89.99999999999;
224 
225  if( longitude >= 180.0 )
226  longitude -= 360.0;
227 
228  /* Convert longitude and latitude from degrees to minutes */
229  /* longitude assumed in -180 <= long < +180 range */
230  long_minutes = ( longitude - MIN_LONGITUDE ) * 60.0 + round_error;
231  lat_minutes = ( latitude - MIN_LATITUDE ) * 60.0 + round_error;
232  /* now we have a positive number of minutes */
233 
234  /* Find 30-min cell indices 0-719 and 0-359 */
235  horiz_index_30 = ( long )( long_minutes / 30.0 );
236  vert_index_30 = ( long )( lat_minutes / 30.0 );
237 
238  /* Compute remainders 0 <= x < 30.0 */
239  long_remainder = long_minutes - ( horiz_index_30 ) * 30.0;
240  lat_remainder = lat_minutes - ( vert_index_30 ) * 30.0;
241 
242  /* Find 15-min cell indices 0 or 1 */
243  horiz_index_15 = ( long )( long_remainder / 15.0 );
244  vert_index_15 = ( long )( lat_remainder / 15.0 );
245 
246  /* Compute remainders 0 <= x < 15.0 */
247  long_remainder = long_remainder - ( horiz_index_15 ) * 15.0;
248  lat_remainder = lat_remainder - ( vert_index_15 ) * 15.0;
249 
250  /* Find 5-min cell indices 0, 1, or 2 */
251  horiz_index_5 = ( long )( long_remainder / 5.0 );
252  vert_index_5 = ( long )( lat_remainder / 5.0 );
253 
254  /* Calculate 30 minute east/west value, 1-720 */
255  ew_value = horiz_index_30 + 1;
256 
257  /* Calculate 30 minute north/south first letter, A-Q */
258  letter_index[0] = ( long )( vert_index_30 / 24.0 );
259 
260  /* Calculate 30 minute north/south second letter, A-Z */
261  letter_index[1] = ( long )( vert_index_30 - letter_index[0] * 24.0 );
262 
263  /* Letters I and O are invalid, so skip them */
264  if( letter_index[0] >= LETTER_I )
265  letter_index[0]++;
266  if( letter_index[0] >= LETTER_O )
267  letter_index[0] ++;
268 
269  if( letter_index[1] >= LETTER_I )
270  letter_index[1]++;
271  if( letter_index[1] >= LETTER_O )
272  letter_index[1] ++;
273 
274  /* Get 15 minute quadrant value, 1-4 */
275  strcpy( _15_minute_value_str, _15_minute_array[horiz_index_15][vert_index_15] );
276 
277  /* Get 5 minute keypad value, 1-9 */
278  strcpy( _5_minute_value_str, _5_minute_array[horiz_index_5][vert_index_5] );
279 
280  /* Form the gars string */
281  if( ew_value < 10 )
282  sprintf ( GARSString, "00%d%c%c", ew_value, ( char )( letter_index[0] + LETTER_A_OFFSET ), ( char )( letter_index[1] + LETTER_A_OFFSET ) );
283  else if( ew_value < 100 )
284  sprintf ( GARSString, "0%d%c%c", ew_value, ( char )( letter_index[0] + LETTER_A_OFFSET ), ( char )( letter_index[1] + LETTER_A_OFFSET ) );
285  else
286  sprintf ( GARSString, "%d%c%c", ew_value, ( char )( letter_index[0] + LETTER_A_OFFSET ), ( char )( letter_index[1] + LETTER_A_OFFSET ) );
287 
288  if( precision > 0 )
289  {
290  strcat( GARSString, _15_minute_value_str);
291 
292  if( precision > 1 )
293  strcat( GARSString, _5_minute_value_str);
294  }
295 
296  GARSString[7] = '\0';
297 
299 }
300 
301 
303 {
304 /*
305  * The function convertToGeodetic converts a GARS coordinate string to Geodetic (latitude
306  * and longitude in radians) coordinates.
307  *
308  * GARSString : GARS coordinate string. (input)
309  * longitude : Longitude in radians. (output)
310  * latitude : Latitude in radians. (output)
311  *
312  */
313 
314  long gars_length; /* length of GARS string */
315  int index = 0;
316  char ew_str[4];
317  int ew_value = 0;
318  char letter = ' ';
319  int ns_str[3];
320  char _15_minute_value = 0;
321  char _5_minute_value = 0;
322  double lat_minutes = 0;
323  double lon_minutes = 0;
324  double longitude, latitude;
325 
326  char* GARSString = garsCoordinates->GARSString();
327 
328  gars_length = strlen( GARSString );
329  if ( ( gars_length < GARS_MINIMUM ) || ( gars_length > GARS_MAXIMUM ) )
331 
332  while( isdigit( GARSString[index] ) )
333  {
334  ew_str[index] = GARSString[index];
335  index++;
336  }
337 
338  if( index != 3 )
340 
341  /* Get 30 minute east/west value, 1-720 */
342  ew_value = atoi( ew_str );
343 
344  letter = GARSString[index];
345  if( !isalpha( letter ) )
347 
348  /* Get first 30 minute north/south letter, A-Q */
349  ns_str[0] = toupper( letter ) - LETTER_A_OFFSET;
350  letter = GARSString[++index];
351  if( !isalpha( letter ) )
353 
354  /* Get second 30 minute north/south letter, A-Z */
355  ns_str[1] = toupper( letter ) - LETTER_A_OFFSET;
356 
357  if( index + 1 < gars_length )
358  {
359  /* Get 15 minute quadrant value, 1-4 */
360  _15_minute_value = GARSString[++index];
361  if( !isdigit( _15_minute_value ) || _15_minute_value < _1 || _15_minute_value > _4 )
363  else
364  {
365  if( index + 1 < gars_length )
366  {
367  /* Get 5 minute quadrant value, 1-9 */
368  _5_minute_value = GARSString[++index];
369  if( !isdigit( _5_minute_value ) || _5_minute_value < _1 || _5_minute_value > _9 )
371  }
372  }
373  }
374 
375  longitude = ( ( ( ew_value - 1.0 ) / 2.0 ) - 180.0 );
376 
377  /* Letter I and O are invalid */
378  if( ns_str[0] >= LETTER_O )
379  ns_str[0] --;
380  if( ns_str[0] >= LETTER_I )
381  ns_str[0] --;
382 
383  if( ns_str[1] >= LETTER_O )
384  ns_str[1] --;
385  if( ns_str[1] >= LETTER_I )
386  ns_str[1] --;
387 
388  latitude = ( ( -90.0 + ( ns_str[0] * 12.0 ) ) + ( ns_str[1] / 2.0 ) );
389 
390  switch( _15_minute_value )
391  {
392  case _1:
393  lat_minutes = 15.0;
394  break;
395  case _4:
396  lon_minutes = 15.0;
397  break;
398  case _2:
399  lat_minutes = 15.0;
400  lon_minutes = 15.0;
401  break;
402  case _3:
403  default:
404  break;
405  }
406 
407  switch( _5_minute_value )
408  {
409  case _4:
410  lat_minutes += 5.0;
411  break;
412  case _1:
413  lat_minutes += 10.0;
414  break;
415  case _8:
416  lon_minutes += 5.0;
417  break;
418  case _5:
419  lon_minutes += 5.0;
420  lat_minutes += 5.0;
421  break;
422  case _2:
423  lon_minutes += 5.0;
424  lat_minutes += 10.0;
425  break;
426  case _9:
427  lon_minutes += 10.0;
428  break;
429  case _6:
430  lon_minutes += 10.0;
431  lat_minutes += 5.0;
432  break;
433  case _3:
434  lon_minutes += 10.0;
435  lat_minutes += 10.0;
436  break;
437  case _7:
438  default:
439  break;
440  }
441 
442  /* Add these values to force the reference point to be the center of the box */
443  if( _5_minute_value )
444  {
445  lat_minutes += 2.5;
446  lon_minutes += 2.5;
447  }
448  else if( _15_minute_value )
449  {
450  lat_minutes += 7.5;
451  lon_minutes += 7.5;
452  }
453  else
454  {
455  lat_minutes += 15.0;
456  lon_minutes += 15.0;
457  }
458 
459  latitude += lat_minutes / MIN_PER_DEG;
460  longitude += lon_minutes / MIN_PER_DEG;
461  longitude *= PI_OVER_180;
462  latitude *= PI_OVER_180;
463 
464  return new GeodeticCoordinates( CoordinateType::geodetic, longitude, latitude );
465 }
466 
467 
468 
469 // CLASSIFICATION: UNCLASSIFIED