/***************************************************************************/ /* RSC IDENTIFIER: USNG * * ABSTRACT * * This component converts between geodetic coordinates (latitude and * longitude) and United States National Grid (USNG) coordinates. * * ERROR HANDLING * * This component checks parameters for valid values. If an invalid value * is found, the error code is combined with the current error code using * the bitwise or. This combining allows multiple error codes to be * returned. The possible error codes are: * * USNG_NO_ERROR : No errors occurred in function * USNG_LAT_ERROR : Latitude outside of valid range * (-90 to 90 degrees) * USNG_LON_ERROR : Longitude outside of valid range * (-180 to 360 degrees) * USNG_STR_ERROR : An USNG string error: string too long, * too short, or badly formed * USNG_PRECISION_ERROR : The precision must be between 0 and 5 * inclusive. * USNG_A_ERROR : Semi-major axis less than or equal to zero * USNG_INV_F_ERROR : Inverse flattening outside of valid range * (250 to 350) * USNG_EASTING_ERROR : Easting outside of valid range * (100,000 to 900,000 meters for UTM) * (0 to 4,000,000 meters for UPS) * USNG_NORTHING_ERROR : Northing outside of valid range * (0 to 10,000,000 meters for UTM) * (0 to 4,000,000 meters for UPS) * USNG_ZONE_ERROR : Zone outside of valid range (1 to 60) * USNG_HEMISPHERE_ERROR : Invalid hemisphere ('N' or 'S') * * REUSE NOTES * * USNG is intended for reuse by any application that does conversions * between geodetic coordinates and USNG coordinates. * * REFERENCES * * Further information on USNG can be found in the Reuse Manual. * * USNG originated from : Federal Geographic Data Committee * 590 National Center * 12201 Sunrise Valley Drive * Reston, VA 22092 * * LICENSES * * None apply to this component. * * RESTRICTIONS * * * ENVIRONMENT * * USNG was tested and certified in the following environments: * * 1. Solaris 2.5 with GCC version 2.8.1 * 2. Windows XP with MS Visual C++ version 6 * * MODIFICATIONS * * Date Description * ---- ----------- * 06-05-06 Original Code (cloned from MGRS) */ /***************************************************************************/ /* * INCLUDES */ #include #include #include #include #include "ups.h" #include "utm.h" #include "usng.h" /* * ctype.h - Standard C character handling library * math.h - Standard C math library * stdio.h - Standard C input/output library * string.h - Standard C string handling library * ups.h - Universal Polar Stereographic (UPS) projection * utm.h - Universal Transverse Mercator (UTM) projection * usng.h - function prototype error checking */ /***************************************************************************/ /* * GLOBAL DECLARATIONS */ #define DEG_TO_RAD 0.017453292519943295 /* PI/180 */ #define RAD_TO_DEG 57.29577951308232087 /* 180/PI */ #define LETTER_A 0 /* ARRAY INDEX FOR LETTER A */ #define LETTER_B 1 /* ARRAY INDEX FOR LETTER B */ #define LETTER_C 2 /* ARRAY INDEX FOR LETTER C */ #define LETTER_D 3 /* ARRAY INDEX FOR LETTER D */ #define LETTER_E 4 /* ARRAY INDEX FOR LETTER E */ #define LETTER_F 5 /* ARRAY INDEX FOR LETTER F */ #define LETTER_G 6 /* ARRAY INDEX FOR LETTER G */ #define LETTER_H 7 /* ARRAY INDEX FOR LETTER H */ #define LETTER_I 8 /* ARRAY INDEX FOR LETTER I */ #define LETTER_J 9 /* ARRAY INDEX FOR LETTER J */ #define LETTER_K 10 /* ARRAY INDEX FOR LETTER K */ #define LETTER_L 11 /* ARRAY INDEX FOR LETTER L */ #define LETTER_M 12 /* ARRAY INDEX FOR LETTER M */ #define LETTER_N 13 /* ARRAY INDEX FOR LETTER N */ #define LETTER_O 14 /* ARRAY INDEX FOR LETTER O */ #define LETTER_P 15 /* ARRAY INDEX FOR LETTER P */ #define LETTER_Q 16 /* ARRAY INDEX FOR LETTER Q */ #define LETTER_R 17 /* ARRAY INDEX FOR LETTER R */ #define LETTER_S 18 /* ARRAY INDEX FOR LETTER S */ #define LETTER_T 19 /* ARRAY INDEX FOR LETTER T */ #define LETTER_U 20 /* ARRAY INDEX FOR LETTER U */ #define LETTER_V 21 /* ARRAY INDEX FOR LETTER V */ #define LETTER_W 22 /* ARRAY INDEX FOR LETTER W */ #define LETTER_X 23 /* ARRAY INDEX FOR LETTER X */ #define LETTER_Y 24 /* ARRAY INDEX FOR LETTER Y */ #define LETTER_Z 25 /* ARRAY INDEX FOR LETTER Z */ #define USNG_LETTERS 3 /* NUMBER OF LETTERS IN USNG */ #define ONEHT 100000.e0 /* ONE HUNDRED THOUSAND */ #define TWOMIL 2000000.e0 /* TWO MILLION */ #define TRUE 1 /* CONSTANT VALUE FOR TRUE VALUE */ #define FALSE 0 /* CONSTANT VALUE FOR FALSE VALUE */ #define PI 3.14159265358979323e0 /* PI */ #define PI_OVER_2 (PI / 2.0e0) #define MIN_EASTING 100000 #define MAX_EASTING 900000 #define MIN_NORTHING 0 #define MAX_NORTHING 10000000 #define MAX_PRECISION 5 /* Maximum precision of easting & northing */ #define MIN_UTM_LAT ( (-80 * PI) / 180.0 ) /* -80 degrees in radians */ #define MAX_UTM_LAT ( (84 * PI) / 180.0 ) /* 84 degrees in radians */ #define MIN_EAST_NORTH 0 #define MAX_EAST_NORTH 4000000 /* Ellipsoid parameters, default to WGS 84 */ double USNG_a = 6378137.0; /* Semi-major axis of ellipsoid in meters */ double USNG_f = 1 / 298.257223563; /* Flattening of ellipsoid */ double USNG_recpf = 298.257223563; char USNG_Ellipsoid_Code[3] = {'W','E',0}; typedef struct Latitude_Band_Value { long letter; /* letter representing latitude band */ double min_northing; /* minimum northing for latitude band */ double north; /* upper latitude for latitude band */ double south; /* lower latitude for latitude band */ double northing_offset; /* latitude band northing offset */ } Latitude_Band; static const Latitude_Band Latitude_Band_Table[20] = {{LETTER_C, 1100000.0, -72.0, -80.5, 0.0}, {LETTER_D, 2000000.0, -64.0, -72.0, 2000000.0}, {LETTER_E, 2800000.0, -56.0, -64.0, 2000000.0}, {LETTER_F, 3700000.0, -48.0, -56.0, 2000000.0}, {LETTER_G, 4600000.0, -40.0, -48.0, 4000000.0}, {LETTER_H, 5500000.0, -32.0, -40.0, 4000000.0}, {LETTER_J, 6400000.0, -24.0, -32.0, 6000000.0}, {LETTER_K, 7300000.0, -16.0, -24.0, 6000000.0}, {LETTER_L, 8200000.0, -8.0, -16.0, 8000000.0}, {LETTER_M, 9100000.0, 0.0, -8.0, 8000000.0}, {LETTER_N, 0.0, 8.0, 0.0, 0.0}, {LETTER_P, 800000.0, 16.0, 8.0, 0.0}, {LETTER_Q, 1700000.0, 24.0, 16.0, 0.0}, {LETTER_R, 2600000.0, 32.0, 24.0, 2000000.0}, {LETTER_S, 3500000.0, 40.0, 32.0, 2000000.0}, {LETTER_T, 4400000.0, 48.0, 40.0, 4000000.0}, {LETTER_U, 5300000.0, 56.0, 48.0, 4000000.0}, {LETTER_V, 6200000.0, 64.0, 56.0, 6000000.0}, {LETTER_W, 7000000.0, 72.0, 64.0, 6000000.0}, {LETTER_X, 7900000.0, 84.5, 72.0, 6000000.0}}; typedef struct UPS_Constant_Value { long letter; /* letter representing latitude band */ long ltr2_low_value; /* 2nd letter range - low number */ long ltr2_high_value; /* 2nd letter range - high number */ long ltr3_high_value; /* 3rd letter range - high number (UPS) */ double false_easting; /* False easting based on 2nd letter */ double false_northing; /* False northing based on 3rd letter */ } UPS_Constant; static const UPS_Constant UPS_Constant_Table[4] = {{LETTER_A, LETTER_J, LETTER_Z, LETTER_Z, 800000.0, 800000.0}, {LETTER_B, LETTER_A, LETTER_R, LETTER_Z, 2000000.0, 800000.0}, {LETTER_Y, LETTER_J, LETTER_Z, LETTER_P, 800000.0, 1300000.0}, {LETTER_Z, LETTER_A, LETTER_J, LETTER_P, 2000000.0, 1300000.0}}; /***************************************************************************/ /* * FUNCTIONS */ long USNG_Get_Latitude_Band_Min_Northing(long letter, double* min_northing, double* northing_offset) /* * The function USNG_Get_Latitude_Band_Min_Northing receives a latitude band letter * and uses the Latitude_Band_Table to determine the minimum northing and northing offset * for that latitude band letter. * * letter : Latitude band letter (input) * min_northing : Minimum northing for that letter (output) */ { /* USNG_Get_Latitude_Band_Min_Northing */ long error_code = USNG_NO_ERROR; if ((letter >= LETTER_C) && (letter <= LETTER_H)) { *min_northing = Latitude_Band_Table[letter-2].min_northing; *northing_offset = Latitude_Band_Table[letter-2].northing_offset; } else if ((letter >= LETTER_J) && (letter <= LETTER_N)) { *min_northing = Latitude_Band_Table[letter-3].min_northing; *northing_offset = Latitude_Band_Table[letter-3].northing_offset; } else if ((letter >= LETTER_P) && (letter <= LETTER_X)) { *min_northing = Latitude_Band_Table[letter-4].min_northing; *northing_offset = Latitude_Band_Table[letter-4].northing_offset; } else error_code |= USNG_STRING_ERROR; return error_code; } /* USNG_Get_Latitude_Band_Min_Northing */ long USNG_Get_Latitude_Range(long letter, double* north, double* south) /* * The function USNG_Get_Latitude_Range receives a latitude band letter * and uses the Latitude_Band_Table to determine the latitude band * boundaries for that latitude band letter. * * letter : Latitude band letter (input) * north : Northern latitude boundary for that letter (output) * north : Southern latitude boundary for that letter (output) */ { /* USNG_Get_Latitude_Range */ long error_code = USNG_NO_ERROR; if ((letter >= LETTER_C) && (letter <= LETTER_H)) { *north = Latitude_Band_Table[letter-2].north * DEG_TO_RAD; *south = Latitude_Band_Table[letter-2].south * DEG_TO_RAD; } else if ((letter >= LETTER_J) && (letter <= LETTER_N)) { *north = Latitude_Band_Table[letter-3].north * DEG_TO_RAD; *south = Latitude_Band_Table[letter-3].south * DEG_TO_RAD; } else if ((letter >= LETTER_P) && (letter <= LETTER_X)) { *north = Latitude_Band_Table[letter-4].north * DEG_TO_RAD; *south = Latitude_Band_Table[letter-4].south * DEG_TO_RAD; } else error_code |= USNG_STRING_ERROR; return error_code; } /* USNG_Get_Latitude_Range */ long USNG_Get_Latitude_Letter(double latitude, int* letter) /* * The function USNG_Get_Latitude_Letter receives a latitude value * and uses the Latitude_Band_Table to determine the latitude band * letter for that latitude. * * latitude : Latitude (input) * letter : Latitude band letter (output) */ { /* USNG_Get_Latitude_Letter */ double temp = 0.0; long error_code = USNG_NO_ERROR; double lat_deg = latitude * RAD_TO_DEG; if (lat_deg >= 72 && lat_deg < 84.5) *letter = LETTER_X; else if (lat_deg > -80.5 && lat_deg < 72) { temp = ((latitude + (80.0 * DEG_TO_RAD)) / (8.0 * DEG_TO_RAD)) + 1.0e-12; *letter = Latitude_Band_Table[(int)temp].letter; } else error_code |= USNG_LAT_ERROR; return error_code; } /* USNG_Get_Latitude_Letter */ long USNG_Check_Zone(char* USNG, long* zone_exists) /* * The function USNG_Check_Zone receives a USNG coordinate string. * If a zone is given, TRUE is returned. Otherwise, FALSE * is returned. * * USNG : USNG coordinate string (input) * zone_exists : TRUE if a zone is given, * FALSE if a zone is not given (output) */ { /* USNG_Check_Zone */ int i = 0; int j = 0; int num_digits = 0; long error_code = USNG_NO_ERROR; /* skip any leading blanks */ while (USNG[i] == ' ') i++; j = i; while (isdigit(USNG[i])) i++; num_digits = i - j; if (num_digits <= 2) if (num_digits > 0) *zone_exists = TRUE; else *zone_exists = FALSE; else error_code |= USNG_STRING_ERROR; return error_code; } /* USNG_Check_Zone */ long Make_USNG_String (char* USNG, long Zone, int Letters[USNG_LETTERS], double Easting, double Northing, long Precision) /* * The function Make_USNG_String constructs a USNG string * from its component parts. * * USNG : USNG coordinate string (output) * Zone : UTM Zone (input) * Letters : USNG coordinate string letters (input) * Easting : Easting value (input) * Northing : Northing value (input) * Precision : Precision level of USNG string (input) */ { /* Make_USNG_String */ long i; long j; double divisor; long east; long north; char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; long error_code = USNG_NO_ERROR; i = 0; if (Zone) i = sprintf (USNG+i,"%2.2ld",Zone); else strncpy(USNG, " ", 2); // 2 spaces for (j=0;j<3;j++) USNG[i++] = alphabet[Letters[j]]; divisor = pow (10.0, (5 - Precision)); Easting = fmod (Easting, 100000.0); if (Easting >= 99999.5) Easting = 99999.0; east = (long)(Easting/divisor); i += sprintf (USNG+i, "%*.*ld", (int)Precision, (int)Precision, east); Northing = fmod (Northing, 100000.0); if (Northing >= 99999.5) Northing = 99999.0; north = (long)(Northing/divisor); i += sprintf (USNG+i, "%*.*ld", (int)Precision, (int)Precision, north); return (error_code); } /* Make_USNG_String */ long Break_USNG_String (char* USNG, long* Zone, long Letters[USNG_LETTERS], double* Easting, double* Northing, long* Precision) /* * The function Break_USNG_String breaks down a USNG * coordinate string into its component parts. * * USNG : USNG coordinate string (input) * Zone : UTM Zone (output) * Letters : USNG coordinate string letters (output) * Easting : Easting value (output) * Northing : Northing value (output) * Precision : Precision level of USNG string (output) */ { /* Break_USNG_String */ long num_digits; long num_letters; long i = 0; long j = 0; long error_code = USNG_NO_ERROR; while (USNG[i] == ' ') i++; /* skip any leading blanks */ j = i; while (isdigit(USNG[i])) i++; num_digits = i - j; if (num_digits <= 2) if (num_digits > 0) { char zone_string[3]; /* get zone */ strncpy (zone_string, USNG+j, 2); zone_string[2] = 0; sscanf (zone_string, "%ld", Zone); if ((*Zone < 1) || (*Zone > 60)) error_code |= USNG_STRING_ERROR; } else *Zone = 0; else error_code |= USNG_STRING_ERROR; j = i; while (isalpha(USNG[i])) i++; num_letters = i - j; if (num_letters == 3) { /* get letters */ Letters[0] = (toupper(USNG[j]) - (long)'A'); if ((Letters[0] == LETTER_I) || (Letters[0] == LETTER_O)) error_code |= USNG_STRING_ERROR; Letters[1] = (toupper(USNG[j+1]) - (long)'A'); if ((Letters[1] == LETTER_I) || (Letters[1] == LETTER_O)) error_code |= USNG_STRING_ERROR; Letters[2] = (toupper(USNG[j+2]) - (long)'A'); if ((Letters[2] == LETTER_I) || (Letters[2] == LETTER_O)) error_code |= USNG_STRING_ERROR; } else error_code |= USNG_STRING_ERROR; j = i; while (isdigit(USNG[i])) i++; num_digits = i - j; if ((num_digits <= 10) && (num_digits%2 == 0)) { long n; char east_string[6]; char north_string[6]; long east; long north; double multiplier; /* get easting & northing */ n = num_digits/2; *Precision = n; if (n > 0) { strncpy (east_string, USNG+j, n); east_string[n] = 0; sscanf (east_string, "%ld", &east); strncpy (north_string, USNG+j+n, n); north_string[n] = 0; sscanf (north_string, "%ld", &north); multiplier = pow (10.0, 5 - n); *Easting = east * multiplier; *Northing = north * multiplier; } else { *Easting = 0.0; *Northing = 0.0; } } else error_code |= USNG_STRING_ERROR; return (error_code); } /* Break_USNG_String */ void USNG_Get_Grid_Values (long zone, long* ltr2_low_value, long* ltr2_high_value, double *pattern_offset) /* * The function USNG_Get_Grid_Values sets the letter range used for * the 2nd letter in the USNG coordinate string, based on the set * number of the utm zone. It also sets the pattern offset using a * value of A for the second letter of the grid square, based on * the grid pattern and set number of the utm zone. * * zone : Zone number (input) * ltr2_low_value : 2nd letter low number (output) * ltr2_high_value : 2nd letter high number (output) * pattern_offset : Pattern offset (output) */ { /* BEGIN USNG_Get_Grid_Values */ long set_number; /* Set number (1-6) based on UTM zone number */ set_number = zone % 6; if (!set_number) set_number = 6; if ((set_number == 1) || (set_number == 4)) { *ltr2_low_value = LETTER_A; *ltr2_high_value = LETTER_H; } else if ((set_number == 2) || (set_number == 5)) { *ltr2_low_value = LETTER_J; *ltr2_high_value = LETTER_R; } else if ((set_number == 3) || (set_number == 6)) { *ltr2_low_value = LETTER_S; *ltr2_high_value = LETTER_Z; } /* False northing at A for second letter of grid square */ if ((set_number % 2) == 0) *pattern_offset = 500000.0; else *pattern_offset = 0.0; } /* END OF USNG_Get_Grid_Values */ long UTM_To_USNG (long Zone, double Latitude, double Easting, double Northing, long Precision, char *USNG) /* * The function UTM_To_USNG calculates a USNG coordinate string * based on the zone, latitude, easting and northing. * * Zone : Zone number (input) * Latitude : Latitude in radians (input) * Easting : Easting (input) * Northing : Northing (input) * Precision : Precision (input) * USNG : USNG coordinate string (output) */ { /* BEGIN UTM_To_USNG */ double pattern_offset; /* Pattern offset for 3rd letter */ double grid_northing; /* Northing used to derive 3rd letter of USNG */ long ltr2_low_value; /* 2nd letter range - low number */ long ltr2_high_value; /* 2nd letter range - high number */ int letters[USNG_LETTERS]; /* Number location of 3 letters in alphabet */ double divisor; long error_code = USNG_NO_ERROR; /* Round easting and northing values */ divisor = pow (10.0, (5 - Precision)); Easting = (long)(Easting/divisor) * divisor; Northing = (long)(Northing/divisor) * divisor; if( Latitude <= 0.0 && Northing == 1.0e7) { Latitude = 0.0; Northing = 0.0; } ltr2_low_value = LETTER_A; // Make compiler shut up about possibly uninitialized value. // It should be set by the following but compiler doesn't know. USNG_Get_Grid_Values(Zone, <r2_low_value, <r2_high_value, &pattern_offset); error_code = USNG_Get_Latitude_Letter(Latitude, &letters[0]); if (!error_code) { grid_northing = Northing; while (grid_northing >= TWOMIL) { grid_northing = grid_northing - TWOMIL; } grid_northing = grid_northing + pattern_offset; if(grid_northing >= TWOMIL) grid_northing = grid_northing - TWOMIL; letters[2] = (long)(grid_northing / ONEHT); if (letters[2] > LETTER_H) letters[2] = letters[2] + 1; if (letters[2] > LETTER_N) letters[2] = letters[2] + 1; letters[1] = ltr2_low_value + ((long)(Easting / ONEHT) -1); if ((ltr2_low_value == LETTER_J) && (letters[1] > LETTER_N)) letters[1] = letters[1] + 1; Make_USNG_String (USNG, Zone, letters, Easting, Northing, Precision); } return error_code; } /* END UTM_To_USNG */ long Set_USNG_Parameters (double a, double f, char *Ellipsoid_Code) /* * The function SET_USNG_PARAMETERS receives the ellipsoid parameters and sets * the corresponding state variables. If any errors occur, the error code(s) * are returned by the function, otherwise USNG_NO_ERROR is returned. * * a : Semi-major axis of ellipsoid in meters (input) * f : Flattening of ellipsoid (input) * Ellipsoid_Code : 2-letter code for ellipsoid (input) */ { /* Set_USNG_Parameters */ double inv_f = 1 / f; long Error_Code = USNG_NO_ERROR; if (a <= 0.0) { /* Semi-major axis must be greater than zero */ Error_Code |= USNG_A_ERROR; } if ((inv_f < 250) || (inv_f > 350)) { /* Inverse flattening must be between 250 and 350 */ Error_Code |= USNG_INV_F_ERROR; } if (!Error_Code) { /* no errors */ USNG_a = a; USNG_f = f; USNG_recpf = inv_f; strcpy (USNG_Ellipsoid_Code, Ellipsoid_Code); } return (Error_Code); } /* Set_USNG_Parameters */ void Get_USNG_Parameters (double *a, double *f, char* Ellipsoid_Code) /* * The function Get_USNG_Parameters returns the current ellipsoid * parameters. * * a : Semi-major axis of ellipsoid, in meters (output) * f : Flattening of ellipsoid (output) * Ellipsoid_Code : 2-letter code for ellipsoid (output) */ { /* Get_USNG_Parameters */ *a = USNG_a; *f = USNG_f; strcpy (Ellipsoid_Code, USNG_Ellipsoid_Code); return; } /* Get_USNG_Parameters */ long Convert_Geodetic_To_USNG (double Latitude, double Longitude, long Precision, char* USNG) /* * The function Convert_Geodetic_To_USNG converts Geodetic (latitude and * longitude) coordinates to a USNG coordinate string, according to the * current ellipsoid parameters. If any errors occur, the error code(s) * are returned by the function, otherwise USNG_NO_ERROR is returned. * * Latitude : Latitude in radians (input) * Longitude : Longitude in radians (input) * Precision : Precision level of USNG string (input) * USNG : USNG coordinate string (output) * */ { /* Convert_Geodetic_To_USNG */ long zone; char hemisphere; double easting; double northing; long temp_error_code = USNG_NO_ERROR; long error_code = USNG_NO_ERROR; if ((Latitude < -PI_OVER_2) || (Latitude > PI_OVER_2)) { /* Latitude out of range */ error_code |= USNG_LAT_ERROR; } if ((Longitude < -PI) || (Longitude > (2*PI))) { /* Longitude out of range */ error_code |= USNG_LON_ERROR; } if ((Precision < 0) || (Precision > MAX_PRECISION)) error_code |= USNG_PRECISION_ERROR; if (!error_code) { if ((Latitude < MIN_UTM_LAT) || (Latitude > MAX_UTM_LAT)) { temp_error_code = Set_UPS_Parameters (USNG_a, USNG_f); if(!temp_error_code) { temp_error_code |= Convert_Geodetic_To_UPS (Latitude, Longitude, &hemisphere, &easting, &northing); if(!temp_error_code) error_code |= Convert_UPS_To_USNG (hemisphere, easting, northing, Precision, USNG); else { if(temp_error_code & UPS_LAT_ERROR) error_code |= USNG_LAT_ERROR; if(temp_error_code & UPS_LON_ERROR) error_code |= USNG_LON_ERROR; } } else { if(temp_error_code & UPS_A_ERROR) error_code |= USNG_A_ERROR; if(temp_error_code & UPS_INV_F_ERROR) error_code |= USNG_INV_F_ERROR; } } else { temp_error_code = Set_UTM_Parameters (USNG_a, USNG_f, 0); if(!temp_error_code) { temp_error_code |= Convert_Geodetic_To_UTM (Latitude, Longitude, &zone, &hemisphere, &easting, &northing); if(!temp_error_code) error_code |= UTM_To_USNG (zone, Latitude, easting, northing, Precision, USNG); else { if(temp_error_code & UTM_LAT_ERROR) error_code |= USNG_LAT_ERROR; if(temp_error_code & UTM_LON_ERROR) error_code |= USNG_LON_ERROR; if(temp_error_code & UTM_ZONE_OVERRIDE_ERROR) error_code |= USNG_ZONE_ERROR; if(temp_error_code & UTM_EASTING_ERROR) error_code |= USNG_EASTING_ERROR; if(temp_error_code & UTM_NORTHING_ERROR) error_code |= USNG_NORTHING_ERROR; } } else { if(temp_error_code & UTM_A_ERROR) error_code |= USNG_A_ERROR; if(temp_error_code & UTM_INV_F_ERROR) error_code |= USNG_INV_F_ERROR; if(temp_error_code & UTM_ZONE_OVERRIDE_ERROR) error_code |= USNG_ZONE_ERROR; } } } return (error_code); } /* Convert_Geodetic_To_USNG */ long Convert_USNG_To_Geodetic (char* USNG, double *Latitude, double *Longitude) /* * The function Convert_USNG_To_Geodetic converts a USNG coordinate string * to Geodetic (latitude and longitude) coordinates * according to the current ellipsoid parameters. If any errors occur, * the error code(s) are returned by the function, otherwise UTM_NO_ERROR * is returned. * * USNG : USNG coordinate string (input) * Latitude : Latitude in radians (output) * Longitude : Longitude in radians (output) * */ { /* Convert_USNG_To_Geodetic */ long zone; char hemisphere = '?'; double easting; double northing; long zone_exists; long temp_error_code = USNG_NO_ERROR; long error_code = USNG_NO_ERROR; error_code = USNG_Check_Zone(USNG, &zone_exists); if (!error_code) { if (zone_exists) { error_code |= Convert_USNG_To_UTM (USNG, &zone, &hemisphere, &easting, &northing); if(!error_code || (error_code & USNG_LAT_WARNING)) { temp_error_code = Set_UTM_Parameters (USNG_a, USNG_f, 0); if(!temp_error_code) { temp_error_code |= Convert_UTM_To_Geodetic (zone, hemisphere, easting, northing, Latitude, Longitude); if(temp_error_code) { if((temp_error_code & UTM_ZONE_ERROR) || (temp_error_code & UTM_HEMISPHERE_ERROR)) error_code |= USNG_STRING_ERROR; if(temp_error_code & UTM_EASTING_ERROR) error_code |= USNG_EASTING_ERROR; if(temp_error_code & UTM_NORTHING_ERROR) error_code |= USNG_NORTHING_ERROR; } } else { if(temp_error_code & UTM_A_ERROR) error_code |= USNG_A_ERROR; if(temp_error_code & UTM_INV_F_ERROR) error_code |= USNG_INV_F_ERROR; if(temp_error_code & UTM_ZONE_OVERRIDE_ERROR) error_code |= USNG_ZONE_ERROR; } } } else { error_code |= Convert_USNG_To_UPS (USNG, &hemisphere, &easting, &northing); if(!error_code) { temp_error_code = Set_UPS_Parameters (USNG_a, USNG_f); if(!temp_error_code) { temp_error_code |= Convert_UPS_To_Geodetic (hemisphere, easting, northing, Latitude, Longitude); if(temp_error_code) { if(temp_error_code & UPS_HEMISPHERE_ERROR) error_code |= USNG_STRING_ERROR; if(temp_error_code & UPS_EASTING_ERROR) error_code |= USNG_EASTING_ERROR; if(temp_error_code & UPS_LAT_ERROR) error_code |= USNG_NORTHING_ERROR; } } else { if(temp_error_code & UPS_A_ERROR) error_code |= USNG_A_ERROR; if(temp_error_code & UPS_INV_F_ERROR) error_code |= USNG_INV_F_ERROR; } } } } return (error_code); } /* END OF Convert_USNG_To_Geodetic */ long Convert_UTM_To_USNG (long Zone, char Hemisphere, double Easting, double Northing, long Precision, char* USNG) /* * The function Convert_UTM_To_USNG converts UTM (zone, easting, and * northing) coordinates to a USNG coordinate string, according to the * current ellipsoid parameters. If any errors occur, the error code(s) * are returned by the function, otherwise USNG_NO_ERROR is returned. * * Zone : UTM zone (input) * Hemisphere : North or South hemisphere (input) * Easting : Easting (X) in meters (input) * Northing : Northing (Y) in meters (input) * Precision : Precision level of USNG string (input) * USNG : USNG coordinate string (output) */ { /* Convert_UTM_To_USNG */ double latitude; /* Latitude of UTM point */ double longitude; /* Longitude of UTM point */ long utm_error_code = USNG_NO_ERROR; long error_code = USNG_NO_ERROR; if ((Zone < 1) || (Zone > 60)) error_code |= USNG_ZONE_ERROR; if ((Hemisphere != 'S') && (Hemisphere != 'N')) error_code |= USNG_HEMISPHERE_ERROR; if ((Easting < MIN_EASTING) || (Easting > MAX_EASTING)) error_code |= USNG_EASTING_ERROR; if ((Northing < MIN_NORTHING) || (Northing > MAX_NORTHING)) error_code |= USNG_NORTHING_ERROR; if ((Precision < 0) || (Precision > MAX_PRECISION)) error_code |= USNG_PRECISION_ERROR; if (!error_code) { Set_UTM_Parameters (USNG_a, USNG_f, 0); utm_error_code = Convert_UTM_To_Geodetic (Zone, Hemisphere, Easting, Northing, &latitude, &longitude); if(utm_error_code) { if((utm_error_code & UTM_ZONE_ERROR) || (utm_error_code & UTM_HEMISPHERE_ERROR)) error_code |= USNG_STRING_ERROR; if(utm_error_code & UTM_EASTING_ERROR) error_code |= USNG_EASTING_ERROR; if(utm_error_code & UTM_NORTHING_ERROR) error_code |= USNG_NORTHING_ERROR; } error_code |= UTM_To_USNG (Zone, latitude, Easting, Northing, Precision, USNG); } return (error_code); } /* Convert_UTM_To_USNG */ long Convert_USNG_To_UTM (char *USNG, long *Zone, char *Hemisphere, double *Easting, double *Northing) /* * The function Convert_USNG_To_UTM converts a USNG coordinate string * to UTM projection (zone, hemisphere, easting and northing) coordinates * according to the current ellipsoid parameters. If any errors occur, * the error code(s) are returned by the function, otherwise UTM_NO_ERROR * is returned. * * USNG : USNG coordinate string (input) * Zone : UTM zone (output) * Hemisphere : North or South hemisphere (output) * Easting : Easting (X) in meters (output) * Northing : Northing (Y) in meters (output) */ { /* Convert_USNG_To_UTM */ double min_northing; double northing_offset; long ltr2_low_value; long ltr2_high_value; double pattern_offset; double upper_lat_limit; /* North latitude limits based on 1st letter */ double lower_lat_limit; /* South latitude limits based on 1st letter */ double grid_easting; /* Easting for 100,000 meter grid square */ double grid_northing; /* Northing for 100,000 meter grid square */ long letters[USNG_LETTERS]; long in_precision; double latitude = 0.0; double longitude = 0.0; double divisor = 1.0; long utm_error_code = USNG_NO_ERROR; long error_code = USNG_NO_ERROR; error_code = Break_USNG_String (USNG, Zone, letters, Easting, Northing, &in_precision); if (!*Zone) error_code |= USNG_STRING_ERROR; else { if (!error_code) { if ((letters[0] == LETTER_X) && ((*Zone == 32) || (*Zone == 34) || (*Zone == 36))) error_code |= USNG_STRING_ERROR; else { if (letters[0] < LETTER_N) *Hemisphere = 'S'; else *Hemisphere = 'N'; ltr2_low_value = LETTER_A; // Make compiler shut up about possibly uninitialized values. ltr2_high_value = LETTER_Z; // They should be set by the following but compiler doesn't know. USNG_Get_Grid_Values(*Zone, <r2_low_value, <r2_high_value, &pattern_offset); /* Check that the second letter of the USNG string is within * the range of valid second letter values * Also check that the third letter is valid */ if ((letters[1] < ltr2_low_value) || (letters[1] > ltr2_high_value) || (letters[2] > LETTER_V)) error_code |= USNG_STRING_ERROR; if (!error_code) { double row_letter_northing = (double)(letters[2]) * ONEHT; grid_easting = (double)((letters[1]) - ltr2_low_value + 1) * ONEHT; if ((ltr2_low_value == LETTER_J) && (letters[1] > LETTER_O)) grid_easting = grid_easting - ONEHT; if (letters[2] > LETTER_O) row_letter_northing = row_letter_northing - ONEHT; if (letters[2] > LETTER_I) row_letter_northing = row_letter_northing - ONEHT; if (row_letter_northing >= TWOMIL) row_letter_northing = row_letter_northing - TWOMIL; error_code = USNG_Get_Latitude_Band_Min_Northing(letters[0], &min_northing, &northing_offset); if (!error_code) { grid_northing = row_letter_northing - pattern_offset; if(grid_northing < 0) grid_northing += TWOMIL; grid_northing += northing_offset; if(grid_northing < min_northing) grid_northing += TWOMIL; *Easting = grid_easting + *Easting; *Northing = grid_northing + *Northing; /* check that point is within Zone Letter bounds */ utm_error_code = Set_UTM_Parameters(USNG_a, USNG_f, 0); if (!utm_error_code) { utm_error_code = Convert_UTM_To_Geodetic(*Zone,*Hemisphere,*Easting,*Northing,&latitude,&longitude); if (!utm_error_code) { divisor = pow (10.0, in_precision); error_code = USNG_Get_Latitude_Range(letters[0], &upper_lat_limit, &lower_lat_limit); if (!error_code) { if (!(((lower_lat_limit - DEG_TO_RAD/divisor) <= latitude) && (latitude <= (upper_lat_limit + DEG_TO_RAD/divisor)))) error_code |= USNG_LAT_ERROR; } } else { if((utm_error_code & UTM_ZONE_ERROR) || (utm_error_code & UTM_HEMISPHERE_ERROR)) error_code |= USNG_STRING_ERROR; if(utm_error_code & UTM_EASTING_ERROR) error_code |= USNG_EASTING_ERROR; if(utm_error_code & UTM_NORTHING_ERROR) error_code |= USNG_NORTHING_ERROR; } } else { if(utm_error_code & UTM_A_ERROR) error_code |= USNG_A_ERROR; if(utm_error_code & UTM_INV_F_ERROR) error_code |= USNG_INV_F_ERROR; if(utm_error_code & UTM_ZONE_OVERRIDE_ERROR) error_code |= USNG_ZONE_ERROR; } } } } } } return (error_code); } /* Convert_USNG_To_UTM */ long Convert_UPS_To_USNG (char Hemisphere, double Easting, double Northing, long Precision, char* USNG) /* * The function Convert_UPS_To_USNG converts UPS (hemisphere, easting, * and northing) coordinates to a USNG coordinate string according to * the current ellipsoid parameters. If any errors occur, the error * code(s) are returned by the function, otherwise UPS_NO_ERROR is * returned. * * Hemisphere : Hemisphere either 'N' or 'S' (input) * Easting : Easting/X in meters (input) * Northing : Northing/Y in meters (input) * Precision : Precision level of USNG string (input) * USNG : USNG coordinate string (output) */ { /* Convert_UPS_To_USNG */ double false_easting; /* False easting for 2nd letter */ double false_northing; /* False northing for 3rd letter */ double grid_easting; /* Easting used to derive 2nd letter of USNG */ double grid_northing; /* Northing used to derive 3rd letter of USNG */ long ltr2_low_value; /* 2nd letter range - low number */ int letters[USNG_LETTERS]; /* Number location of 3 letters in alphabet */ double divisor; int index = 0; long error_code = USNG_NO_ERROR; if ((Hemisphere != 'N') && (Hemisphere != 'S')) error_code |= USNG_HEMISPHERE_ERROR; if ((Easting < MIN_EAST_NORTH) || (Easting > MAX_EAST_NORTH)) error_code |= USNG_EASTING_ERROR; if ((Northing < MIN_EAST_NORTH) || (Northing > MAX_EAST_NORTH)) error_code |= USNG_NORTHING_ERROR; if ((Precision < 0) || (Precision > MAX_PRECISION)) error_code |= USNG_PRECISION_ERROR; if (!error_code) { divisor = pow (10.0, (5 - Precision)); Easting = (long)(Easting/divisor + 1.0e-9) * divisor; Northing = (long)(Northing/divisor) * divisor; if (Hemisphere == 'N') { if (Easting >= TWOMIL) letters[0] = LETTER_Z; else letters[0] = LETTER_Y; index = letters[0] - 22; ltr2_low_value = UPS_Constant_Table[index].ltr2_low_value; false_easting = UPS_Constant_Table[index].false_easting; false_northing = UPS_Constant_Table[index].false_northing; } else { if (Easting >= TWOMIL) letters[0] = LETTER_B; else letters[0] = LETTER_A; ltr2_low_value = UPS_Constant_Table[letters[0]].ltr2_low_value; false_easting = UPS_Constant_Table[letters[0]].false_easting; false_northing = UPS_Constant_Table[letters[0]].false_northing; } grid_northing = Northing; grid_northing = grid_northing - false_northing; letters[2] = (long)(grid_northing / ONEHT); if (letters[2] > LETTER_H) letters[2] = letters[2] + 1; if (letters[2] > LETTER_N) letters[2] = letters[2] + 1; grid_easting = Easting; grid_easting = grid_easting - false_easting; letters[1] = ltr2_low_value + ((long)(grid_easting / ONEHT)); if (Easting < TWOMIL) { if (letters[1] > LETTER_L) letters[1] = letters[1] + 3; if (letters[1] > LETTER_U) letters[1] = letters[1] + 2; } else { if (letters[1] > LETTER_C) letters[1] = letters[1] + 2; if (letters[1] > LETTER_H) letters[1] = letters[1] + 1; if (letters[1] > LETTER_L) letters[1] = letters[1] + 3; } Make_USNG_String (USNG, 0, letters, Easting, Northing, Precision); } return (error_code); } /* Convert_UPS_To_USNG */ long Convert_USNG_To_UPS ( char *USNG, char *Hemisphere, double *Easting, double *Northing) /* * The function Convert_USNG_To_UPS converts a USNG coordinate string * to UPS (hemisphere, easting, and northing) coordinates, according * to the current ellipsoid parameters. If any errors occur, the error * code(s) are returned by the function, otherwide UPS_NO_ERROR is returned. * * USNG : USNG coordinate string (input) * Hemisphere : Hemisphere either 'N' or 'S' (output) * Easting : Easting/X in meters (output) * Northing : Northing/Y in meters (output) */ { /* Convert_USNG_To_UPS */ long ltr2_high_value; /* 2nd letter range - high number */ long ltr3_high_value; /* 3rd letter range - high number (UPS) */ long ltr2_low_value; /* 2nd letter range - low number */ double false_easting; /* False easting for 2nd letter */ double false_northing; /* False northing for 3rd letter */ double grid_easting; /* easting for 100,000 meter grid square */ double grid_northing; /* northing for 100,000 meter grid square */ long zone = 0; long letters[USNG_LETTERS]; long in_precision = 0; int index = 0; long error_code = USNG_NO_ERROR; error_code = Break_USNG_String (USNG, &zone, letters, Easting, Northing, &in_precision); if (zone) error_code |= USNG_STRING_ERROR; else { if (!error_code) { if (letters[0] >= LETTER_Y) { *Hemisphere = 'N'; index = letters[0] - 22; ltr2_low_value = UPS_Constant_Table[index].ltr2_low_value; ltr2_high_value = UPS_Constant_Table[index].ltr2_high_value; ltr3_high_value = UPS_Constant_Table[index].ltr3_high_value; false_easting = UPS_Constant_Table[index].false_easting; false_northing = UPS_Constant_Table[index].false_northing; } else { *Hemisphere = 'S'; ltr2_low_value = UPS_Constant_Table[letters[0]].ltr2_low_value; ltr2_high_value = UPS_Constant_Table[letters[0]].ltr2_high_value; ltr3_high_value = UPS_Constant_Table[letters[0]].ltr3_high_value; false_easting = UPS_Constant_Table[letters[0]].false_easting; false_northing = UPS_Constant_Table[letters[0]].false_northing; } /* Check that the second letter of the USNG string is within * the range of valid second letter values * Also check that the third letter is valid */ if ((letters[1] < ltr2_low_value) || (letters[1] > ltr2_high_value) || ((letters[1] == LETTER_D) || (letters[1] == LETTER_E) || (letters[1] == LETTER_M) || (letters[1] == LETTER_N) || (letters[1] == LETTER_V) || (letters[1] == LETTER_W)) || (letters[2] > ltr3_high_value)) error_code = USNG_STRING_ERROR; if (!error_code) { grid_northing = (double)letters[2] * ONEHT + false_northing; if (letters[2] > LETTER_I) grid_northing = grid_northing - ONEHT; if (letters[2] > LETTER_O) grid_northing = grid_northing - ONEHT; grid_easting = (double)((letters[1]) - ltr2_low_value) * ONEHT + false_easting; if (ltr2_low_value != LETTER_A) { if (letters[1] > LETTER_L) grid_easting = grid_easting - 300000.0; if (letters[1] > LETTER_U) grid_easting = grid_easting - 200000.0; } else { if (letters[1] > LETTER_C) grid_easting = grid_easting - 200000.0; if (letters[1] > LETTER_I) grid_easting = grid_easting - ONEHT; if (letters[1] > LETTER_L) grid_easting = grid_easting - 300000.0; } *Easting = grid_easting + *Easting; *Northing = grid_northing + *Northing; } } } return (error_code); } /* Convert_USNG_To_UPS */