- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

# Y:/msp/devkits/temp_geotrans/master/geotrans3.1/CCS/src/dtcc/CoordinateSystems/gars/GARS.cpp

Go to the documentation of this file.

    00001 // CLASSIFICATION: UNCLASSIFIED
    00002
    00003 /***************************************************************************/
    00004 /* RSC IDENTIFIER: GARS
    00005  *
    00006  * ABSTRACT
    00007  *
    00008  *    This component provides conversions from Geodetic coordinates (latitude
    00009  *    and longitude in radians) to a GARS coordinate string.
    00010  *
    00011  * ERROR HANDLING
    00012  *
    00013  *    This component checks parameters for valid values.  If an invalid value
    00014  *    is found, the error code is combined with the current error code using
    00015  *    the bitwise or.  This combining allows multiple error codes to be
    00016  *    returned. The possible error codes are:
    00017  *
    00018  *          GARS_NO_ERROR          : No errors occurred in function
    00019  *          GARS_LAT_ERROR         : Latitude outside of valid range
    00020  *                                      (-90 to 90 degrees)
    00021  *          GARS_LON_ERROR         : Longitude outside of valid range
    00022  *                                      (-180 to 360 degrees)
    00023  *          GARS_STR_ERROR         : A GARS string error: string too long,
    00024  *                                      string too short, invalid numbers/letters
    00025  *          GARS_STR_LAT_ERROR     : The latitude part of the GARS string
    00026  *                                     (fourth and fifth  characters) is invalid.
    00027  *          GARS_STR_LON_ERROR     : The longitude part of the GARS string
    00028  *                                     (first three characters) is invalid.
    00029  *          GARS_STR_15_MIN_ERROR  : The 15 minute part of the GARS
    00030  *                                      string is less than 1 or greater than 4.
    00031  *          GARS_STR_5_MIN_ERROR   : The 5 minute part of the GARS
    00032  *                                      string is less than 1 or greater than 9.
    00033  *          GARS_PRECISION_ERROR   : The precision must be between 0 and 5
    00034  *                                      inclusive.
    00035  *
    00036  *
    00037  * REUSE NOTES
    00038  *
    00039  *    GARS is intended for reuse by any application that performs a
    00040  *    conversion between Geodetic and GARS coordinates.
    00041  *
    00042  * REFERENCES
    00043  *
    00044  *    Further information on GARS can be found in the Reuse Manual.
    00045  *
    00046  *    GARS originated from :
    00047  *
    00048  *                   http://earth-info.nga.mil/GandG/coordsys/grids/gars.html
    00049  *
    00050  *
    00051  * LICENSES
    00052  *
    00053  *    None apply to this component.
    00054  *
    00055  * RESTRICTIONS
    00056  *
    00057  *    GARS has no restrictions.
    00058  *
    00059  * ENVIRONMENT
    00060  *
    00061  *    GARS was tested and certified in the following environments:
    00062  *
    00063  *    1. Solaris 2.5 with GCC version 2.8.1
    00064  *    2. Windows XP with MS Visual C++ version 6
    00065  *
    00066  * MODIFICATIONS
    00067  *
    00068  *    Date              Description
    00069  *    ----              -----------
    00070  *    07-10-06          Original Code
    00071  *    03-02-07          Original C++ Code
    00072  */
    00073
    00074
    00075 /***************************************************************************/
    00076 /*
    00077  *                               INCLUDES
    00078  */
    00079
    00080 #include <ctype.h>
    00081 #include <stdio.h>
    00082 #include <stdlib.h>
    00083 #include <string.h>
    00084 #include "GARS.h"
    00085 #include "GARSCoordinates.h"
    00086 #include "MapProjectionCoordinates.h"
    00087 #include "GeodeticCoordinates.h"
    00088 #include "CoordinateConversionException.h"
    00089 #include "ErrorMessages.h"
    00090
    00091 /*
    00092  *  ctype.h    - Standard C character handling library
    00093  *  stdio.h    - Standard C input/output library
    00094  *  stdlib.h   - Standard C general utility library
    00095  *  string.h   - Standard C string handling library
    00096  *  GARS.h     - for prototype error checking
    00097  *  GARSCoordinates.h   - defines gars coordinates
    00098  *  MapProjectionCoordinates.h   - defines map projection coordinates
    00099  *  GeodeticCoordinates.h   - defines geodetic coordinates
    00100  *  CoordinateConversionException.h - Exception handler
    00101  *  ErrorMessages.h  - Contains exception messages
    00102  */
    00103
    00104
    00105 using namespace MSP::CCS;
    00106
    00107
    00108 /***************************************************************************/
    00109 /*                               DEFINES
    00110  *
    00111  */
    00112
    00113 const double MIN_LATITUDE = -90.0;    /* Minimum latitude                      */
    00114 const double MAX_LATITUDE = 90.0;     /* Maximum latitude                      */
    00115 const double MIN_LONGITUDE = -180.0;  /* Minimum longitude                     */
    00116 const double MAX_LONGITUDE = 360.0;   /* Maximum longitude                     */
    00117 const double MIN_PER_DEG = 60;        /* Number of minutes per degree          */
    00118 const int GARS_MINIMUM = 5;           /* Minimum number of chars for GARS      */
    00119 const int GARS_MAXIMUM = 7;           /* Maximum number of chars for GARS      */
    00120 const int GARS_LETTERS = 4;           /* Number of letters in GARS string      */
    00121 const int MAX_PRECISION = 5;          /* Maximum precision of minutes part     */
    00122 const int LETTER_A_OFFSET = 65;       /* Letter A offset in character set      */
    00123 const double PI = 3.14159265358979323e0;     /* PI                             */
    00124 const double PI_OVER_180 = PI / 180.0;
    00125 const double RADIAN_TO_DEGREE = 180.0e0 / PI;
    00126
    00127 /* A = 0, B = 1, C = 2, D = 3, E = 4, F = 5, G = 6, H = 7                 */
    00128 const int LETTER_I = 8;                /* ARRAY INDEX FOR LETTER I        */
    00129 /* J = 9, K = 10, L = 11, M = 12, N = 13                                  */
    00130 const int LETTER_O = 14;              /* ARRAY INDEX FOR LETTER O         */
    00131 /* P = 15, Q = 16, R = 17, S = 18, T = 19, U = 20, V = 21,                */
    00132 /* W = 22, X = 23, Y = 24, Z = 25                                         */
    00133
    00134 const char _1 = '1';
    00135 const char _2 = '2';
    00136 const char _3 = '3';
    00137 const char _4 = '4';
    00138 const char _5 = '5';
    00139 const char _6 = '6';
    00140 const char _7 = '7';
    00141 const char _8 = '8';
    00142 const char _9 = '9';
    00143
    00144
    00145 /************************************************************************/
    00146 /*                              FUNCTIONS
    00147  *
    00148  */
    00149
    00150 GARS::GARS() :
    00151   CoordinateSystem( 0, 0 )
    00152 {
    00153 }
    00154
    00155
    00156 GARS::GARS( const GARS &g )
    00157 {
    00158   semiMajorAxis = g.semiMajorAxis;
    00159   flattening = g.flattening;
    00160 }
    00161
    00162
    00163 GARS::~GARS()
    00164 {
    00165 }
    00166
    00167
    00168 GARS& GARS::operator=( const GARS &g )
    00169 {
    00170   if( this != &g )
    00171   {
    00172     semiMajorAxis = g.semiMajorAxis;
    00173     flattening = g.flattening;
    00174   }
    00175
    00176   return *this;
    00177 }
    00178
    00179
    00180 MSP::CCS::GARSCoordinates* GARS::convertFromGeodetic( MSP::CCS::GeodeticCoordinates* geodeticCoordinates, long precision )
    00181 {
    00182 /*
    00183  *  The function convertFromGeodetic converts Geodetic (latitude and longitude in radians)
    00184  *  coordinates to a GARS coordinate string.  Precision specifies the
    00185  *  number of digits in the GARS string for latitude and longitude:
    00186  *                                  0: 30 minutes (5 characters)
    00187  *                                  1: 15 minutes (6 characters)
    00188  *                                  2: 5 minutes (7 characters)
    00189  *
    00190  *    longitude   : Longitude in radians.                  (input)
    00191  *    latitude    : Latitude in radians.                   (input)
    00192  *    precision   : Precision specified by the user.       (input)
    00193  *    GARSString  : GARS coordinate string.                (output)
    00194  *
    00195  */
    00196
    00197   long ew_value;
    00198   long letter_index[GARS_LETTERS + 1];    /* GARS letters                 */
    00199   char _15_minute_value_str[2] = "";
    00200   char _5_minute_value_str[2] = "";
    00201   double round_error = 5.0e-11;
    00202   char* _15_minute_array[2][2] = {{"3", "1"}, {"4", "2"}};
    00203   char* _5_minute_array[3][3] = {{"7", "4", "1"}, {"8", "5", "2"}, {"9", "6", "3"}};
    00204   double long_minutes, lat_minutes;
    00205   double long_remainder, lat_remainder;
    00206   long horiz_index_30, vert_index_30;
    00207   long horiz_index_15, vert_index_15;
    00208   long horiz_index_5, vert_index_5;
    00209   char GARSString[8];
    00210
    00211   double latitude = geodeticCoordinates->latitude() * RADIAN_TO_DEGREE;
    00212   double longitude = geodeticCoordinates->longitude() *  RADIAN_TO_DEGREE;
    00213
    00214   if ( ( latitude < MIN_LATITUDE ) || ( latitude > MAX_LATITUDE ) )
    00215     throw CoordinateConversionException( ErrorMessages::latitude );
    00216   if ( ( longitude < MIN_LONGITUDE ) || ( longitude > MAX_LONGITUDE ) )
    00217     throw CoordinateConversionException( ErrorMessages::longitude );
    00218   if ( ( precision < 0 ) || ( precision > MAX_PRECISION ) )
    00219     throw CoordinateConversionException( ErrorMessages::precision );
    00220
    00221   /* North pole is an exception, read over and down */
    00222   if( latitude == MAX_LATITUDE )
    00223     latitude = 89.99999999999;
    00224
    00225   if( longitude >= 180.0 )
    00226     longitude -= 360.0;
    00227
    00228   /* Convert longitude and latitude from degrees to minutes */
    00229   /* longitude assumed in -180 <= long < +180 range */
    00230   long_minutes = ( longitude - MIN_LONGITUDE ) * 60.0 + round_error;
    00231   lat_minutes = ( latitude - MIN_LATITUDE ) * 60.0 + round_error;
    00232   /* now we have a positive number of minutes */
    00233
    00234   /* Find 30-min cell indices 0-719 and 0-359 */
    00235   horiz_index_30 = ( long )( long_minutes / 30.0 );
    00236   vert_index_30 = ( long )( lat_minutes / 30.0 );
    00237
    00238   /* Compute remainders 0 <= x < 30.0 */
    00239   long_remainder = long_minutes - ( horiz_index_30 ) * 30.0;
    00240   lat_remainder = lat_minutes - ( vert_index_30 ) * 30.0;
    00241
    00242   /* Find 15-min cell indices 0 or 1 */
    00243   horiz_index_15 = ( long )( long_remainder / 15.0 );
    00244   vert_index_15 = ( long )( lat_remainder / 15.0 );
    00245
    00246   /* Compute remainders 0 <= x < 15.0 */
    00247   long_remainder = long_remainder - ( horiz_index_15 ) * 15.0;
    00248   lat_remainder = lat_remainder - ( vert_index_15 ) * 15.0;
    00249
    00250   /* Find 5-min cell indices 0, 1, or 2 */
    00251   horiz_index_5 = ( long )( long_remainder / 5.0 );
    00252   vert_index_5 = ( long )( lat_remainder / 5.0 );
    00253
    00254   /* Calculate 30 minute east/west value, 1-720 */
    00255   ew_value = horiz_index_30 + 1;
    00256
    00257   /* Calculate 30 minute north/south first letter, A-Q */
    00258   letter_index[0] = ( long )( vert_index_30 / 24.0 );
    00259
    00260   /* Calculate 30 minute north/south second letter, A-Z */
    00261   letter_index[1] = ( long )( vert_index_30 - letter_index[0] * 24.0 );
    00262
    00263   /* Letters I and O are invalid, so skip them */
    00264   if( letter_index[0] >= LETTER_I )
    00265     letter_index[0]++;
    00266   if( letter_index[0] >= LETTER_O )
    00267     letter_index[0] ++;
    00268
    00269   if( letter_index[1] >= LETTER_I )
    00270     letter_index[1]++;
    00271   if( letter_index[1] >= LETTER_O )
    00272     letter_index[1] ++;
    00273
    00274   /* Get 15 minute quadrant value, 1-4 */
    00275   strcpy( _15_minute_value_str, _15_minute_array[horiz_index_15][vert_index_15] );
    00276
    00277   /* Get 5 minute keypad value, 1-9 */
    00278   strcpy( _5_minute_value_str, _5_minute_array[horiz_index_5][vert_index_5] );
    00279
    00280   /* Form the gars string */
    00281   if( ew_value < 10 )
    00282     sprintf ( GARSString, "00%d%c%c", ew_value, ( char )( letter_index[0] + LETTER_A_OFFSET ), ( char )( letter_index[1] + LETTER_A_OFFSET ) );
    00283   else if( ew_value < 100 )
    00284     sprintf ( GARSString, "0%d%c%c", ew_value, ( char )( letter_index[0] + LETTER_A_OFFSET ), ( char )( letter_index[1] + LETTER_A_OFFSET ) );
    00285   else
    00286     sprintf ( GARSString, "%d%c%c", ew_value, ( char )( letter_index[0] + LETTER_A_OFFSET ), ( char )( letter_index[1] + LETTER_A_OFFSET ) );
    00287
    00288   if( precision > 0 )
    00289   {
    00290     strcat( GARSString, _15_minute_value_str);
    00291
    00292     if( precision > 1 )
    00293       strcat( GARSString, _5_minute_value_str);
    00294   }
    00295
    00296   GARSString[7] = '\0';
    00297
    00298   return new GARSCoordinates( CoordinateType::globalAreaReferenceSystem, GARSString );
    00299 }
    00300
    00301
    00302 MSP::CCS::GeodeticCoordinates* GARS::convertToGeodetic( MSP::CCS::GARSCoordinates* garsCoordinates )
    00303 {
    00304 /*
    00305  *  The function convertToGeodetic converts a GARS coordinate string to Geodetic (latitude
    00306  *  and longitude in radians) coordinates.
    00307  *
    00308  *    GARSString  : GARS coordinate string.     (input)
    00309  *    longitude   : Longitude in radians.       (output)
    00310  *    latitude    : Latitude in radians.        (output)
    00311  *
    00312  */
    00313
    00314   long gars_length;          /* length of GARS string                      */
    00315   int index = 0;
    00316   char ew_str[4];
    00317   int ew_value = 0;
    00318   char letter = ' ';
    00319   int ns_str[3];
    00320   char _15_minute_value = 0;
    00321   char _5_minute_value = 0;
    00322   double lat_minutes = 0;
    00323   double lon_minutes = 0;
    00324   double longitude, latitude;
    00325
    00326   char* GARSString = garsCoordinates->GARSString();
    00327
    00328   gars_length = strlen( GARSString );
    00329   if ( ( gars_length < GARS_MINIMUM ) || ( gars_length > GARS_MAXIMUM ) )
    00330     throw CoordinateConversionException( ErrorMessages::garsString );
    00331
    00332   while( isdigit( GARSString[index] ) )
    00333   {
    00334   ew_str[index] = GARSString[index];
    00335   index++;
    00336   }
    00337
    00338   if( index != 3 )
    00339   throw CoordinateConversionException( ErrorMessages::garsString );
    00340
    00341   /* Get 30 minute east/west value, 1-720 */
    00342   ew_value = atoi( ew_str );
    00343
    00344   letter = GARSString[index];
    00345   if( !isalpha( letter ) )
    00346   throw CoordinateConversionException( ErrorMessages::longitude );
    00347
    00348   /* Get first 30 minute north/south letter, A-Q */
    00349   ns_str[0] = toupper( letter ) - LETTER_A_OFFSET;
    00350   letter = GARSString[++index];
    00351   if( !isalpha( letter ) )
    00352   throw CoordinateConversionException( ErrorMessages::latitude );
    00353
    00354   /* Get second 30 minute north/south letter, A-Z */
    00355   ns_str[1] = toupper( letter ) - LETTER_A_OFFSET;
    00356
    00357   if( index + 1 < gars_length )
    00358   {
    00359     /* Get 15 minute quadrant value, 1-4 */
    00360     _15_minute_value = GARSString[++index];
    00361     if( !isdigit( _15_minute_value ) || _15_minute_value < _1 || _15_minute_value > _4 )
    00362       throw CoordinateConversionException( ErrorMessages::longitude_min );
    00363     else
    00364     {
    00365       if( index + 1 < gars_length )
    00366       {
    00367         /* Get 5 minute quadrant value, 1-9 */
    00368         _5_minute_value = GARSString[++index];
    00369         if( !isdigit( _5_minute_value ) || _5_minute_value < _1 || _5_minute_value > _9 )
    00370           throw CoordinateConversionException( ErrorMessages::latitude_min );
    00371       }
    00372     }
    00373   }
    00374
    00375   longitude = ( ( ( ew_value - 1.0 ) / 2.0 ) - 180.0 );
    00376
    00377   /* Letter I and O are invalid */
    00378   if( ns_str[0] >= LETTER_O )
    00379     ns_str[0] --;
    00380   if( ns_str[0] >= LETTER_I )
    00381     ns_str[0] --;
    00382
    00383   if( ns_str[1] >= LETTER_O )
    00384     ns_str[1] --;
    00385   if( ns_str[1] >= LETTER_I )
    00386     ns_str[1] --;
    00387
    00388   latitude = ( ( -90.0 + ( ns_str[0] * 12.0 ) ) + ( ns_str[1] / 2.0 ) );
    00389
    00390   switch( _15_minute_value )
    00391   {
    00392     case _1:
    00393       lat_minutes = 15.0;
    00394       break;
    00395     case _4:
    00396       lon_minutes = 15.0;
    00397       break;
    00398     case _2:
    00399       lat_minutes = 15.0;
    00400       lon_minutes = 15.0;
    00401       break;
    00402     case _3:
    00403     default:
    00404       break;
    00405   }
    00406
    00407   switch( _5_minute_value )
    00408   {
    00409     case _4:
    00410       lat_minutes += 5.0;
    00411       break;
    00412     case _1:
    00413       lat_minutes += 10.0;
    00414       break;
    00415     case _8:
    00416       lon_minutes += 5.0;
    00417       break;
    00418     case _5:
    00419       lon_minutes += 5.0;
    00420       lat_minutes += 5.0;
    00421       break;
    00422     case _2:
    00423       lon_minutes += 5.0;
    00424       lat_minutes += 10.0;
    00425       break;
    00426     case _9:
    00427       lon_minutes += 10.0;
    00428       break;
    00429     case _6:
    00430       lon_minutes += 10.0;
    00431       lat_minutes += 5.0;
    00432       break;
    00433     case _3:
    00434       lon_minutes += 10.0;
    00435       lat_minutes += 10.0;
    00436       break;
    00437     case _7:
    00438     default:
    00439       break;
    00440   }
    00441
    00442   /* Add these values to force the reference point to be the center of the box */
    00443   if( _5_minute_value )
    00444   {
    00445     lat_minutes += 2.5;
    00446     lon_minutes += 2.5;
    00447   }
    00448   else if( _15_minute_value )
    00449   {
    00450     lat_minutes += 7.5;
    00451     lon_minutes += 7.5;
    00452   }
    00453   else
    00454   {
    00455     lat_minutes += 15.0;
    00456     lon_minutes += 15.0;
    00457   }
    00458
    00459   latitude += lat_minutes / MIN_PER_DEG;
    00460   longitude += lon_minutes / MIN_PER_DEG;
    00461   longitude *= PI_OVER_180;
    00462   latitude *= PI_OVER_180;
    00463
    00464   return new GeodeticCoordinates( CoordinateType::geodetic, longitude, latitude );
    00465 }
    00466
    00467
    00468
    00469 // CLASSIFICATION: UNCLASSIFIED