/* mgrs_to_utm - convert MGRS coordinates to UTM * * http://svn.stellman-greene.com/mgrs_to_utm/trunk/ * * to install and run: * - download mgrs_to_utm.c and mgrs_to_utm.h * - build: gcc -o mgrs_to_utm mgrs_to_utm.c * - run mgrs_to_utm * * For more information on MGRS and UTM coordinates: * Military Grid Reference System * http://en.wikipedia.org/wiki/Military_grid_reference_system * Universal Transverse Mercator coordinate system * http://en.wikipedia.org/wiki/Universal_Transverse_Mercator_coordinate_system * * This project was adapted from Geotrans 2.4.1 * http://earth-info.nga.mil/GandG/geotrans/ * */ #include #include #include #include #include "mgrs_to_utm.h"; int main(int argc, char *argv[]) { long Zone; char Hemisphere; double Easting; double Northing; long ret; if (argc != 2) { printf("usage: mgrs_to_utm MGRS\n"); printf("example: mgrs_to_utm 49PAN730040\n"); return 0; } ret = Convert_MGRS_To_UTM(argv[1], &Zone, &Hemisphere, &Easting, &Northing); printf("Zone: %d Hemisphere: %c Easting: %0.0f Northing: %0.0f\n", Zone, Hemisphere, Easting, Northing); }; /* Ellipsoid parameters, default to WGS 84 */ double MGRS_a = 6378137.0; /* Semi-major axis of ellipsoid in meters */ double MGRS_f = 1 / 298.257223563; /* Flattening of ellipsoid */ double MGRS_recpf = 298.257223563; char MGRS_Ellipsoid_Code[3] = {'W','E',0}; /* * CLARKE_1866 : Ellipsoid code for CLARKE_1866 * CLARKE_1880 : Ellipsoid code for CLARKE_1880 * BESSEL_1841 : Ellipsoid code for BESSEL_1841 * BESSEL_1841_NAMIBIA : Ellipsoid code for BESSEL 1841 (NAMIBIA) */ const char* CLARKE_1866 = "CC"; const char* CLARKE_1880 = "CD"; const char* BESSEL_1841 = "BR"; const char* BESSEL_1841_NAMIBIA = "BN"; 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 */ } Latitude_Band; static const Latitude_Band Latitude_Band_Table[20] = {{LETTER_C, 1100000.0, -72.0, -80.5}, {LETTER_D, 2000000.0, -64.0, -72.0}, {LETTER_E, 2800000.0, -56.0, -64.0}, {LETTER_F, 3700000.0, -48.0, -56.0}, {LETTER_G, 4600000.0, -40.0, -48.0}, {LETTER_H, 5500000.0, -32.0, -40.0}, {LETTER_J, 6400000.0, -24.0, -32.0}, {LETTER_K, 7300000.0, -16.0, -24.0}, {LETTER_L, 8200000.0, -8.0, -16.0}, {LETTER_M, 9100000.0, 0.0, -8.0}, {LETTER_N, 0.0, 8.0, 0.0}, {LETTER_P, 800000.0, 16.0, 8.0}, {LETTER_Q, 1700000.0, 24.0, 16.0}, {LETTER_R, 2600000.0, 32.0, 24.0}, {LETTER_S, 3500000.0, 40.0, 32.0}, {LETTER_T, 4400000.0, 48.0, 40.0}, {LETTER_U, 5300000.0, 56.0, 48.0}, {LETTER_V, 6200000.0, 64.0, 56.0}, {LETTER_W, 7000000.0, 72.0, 64.0}, {LETTER_X, 7900000.0, 84.5, 72.0}}; long Convert_MGRS_To_UTM (char *MGRS, long *Zone, char *Hemisphere, double *Easting, double *Northing) /* * The function Convert_MGRS_To_UTM converts an MGRS 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. * * MGRS : MGRS 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_MGRS_To_UTM */ double scaled_min_northing; double min_northing; long ltr2_low_value; long ltr2_high_value; double false_northing; double grid_easting; /* Easting for 100,000 meter grid square */ double grid_northing; /* Northing for 100,000 meter grid square */ long letters[3]; long in_precision; #ifdef notdef double upper_lat_limit; /* North latitude limits based on 1st letter */ double lower_lat_limit; /* South latitude limits based on 1st letter */ double latitude = 0.0; double longitude = 0.0; double divisor = 1.0; #endif long error_code = MGRS_NO_ERROR; error_code = Break_MGRS_String (MGRS, Zone, letters, Easting, Northing, &in_precision); if (!*Zone) error_code |= MGRS_STRING_ERROR; else { if (!error_code) { if ((letters[0] == LETTER_X) && ((*Zone == 32) || (*Zone == 34) || (*Zone == 36))) error_code |= MGRS_STRING_ERROR; else { if (letters[0] < LETTER_N) *Hemisphere = 'S'; else *Hemisphere = 'N'; Get_Grid_Values(*Zone, <r2_low_value, <r2_high_value, &false_northing); /* Check that the second letter of the MGRS 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 |= MGRS_STRING_ERROR; if (!error_code) { grid_northing = (double)(letters[2]) * ONEHT + false_northing; 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) grid_northing = grid_northing - ONEHT; if (letters[2] > LETTER_I) grid_northing = grid_northing - ONEHT; if (grid_northing >= TWOMIL) grid_northing = grid_northing - TWOMIL; error_code = Get_Latitude_Band_Min_Northing(letters[0], &min_northing); if (!error_code) { scaled_min_northing = min_northing; while (scaled_min_northing >= TWOMIL) { scaled_min_northing = scaled_min_northing - TWOMIL; } grid_northing = grid_northing - scaled_min_northing; if (grid_northing < 0.0) grid_northing = grid_northing + TWOMIL; grid_northing = min_northing + grid_northing; *Easting = grid_easting + *Easting; *Northing = grid_northing + *Northing; #ifdef notdef /* check that point is within Zone Letter bounds */ error_code = Set_UTM_Parameters(MGRS_a,MGRS_f,*Zone); if (!error_code) { error_code = Convert_UTM_To_Geodetic(*Zone,*Hemisphere,*Easting,*Northing,&latitude,&longitude); if (!error_code) { divisor = pow (10.0, in_precision); error_code = 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 |= MGRS_LAT_ERROR; } } } #endif /* notdef */ } } } } } return (error_code); } /* Convert_MGRS_To_UTM */ long Break_MGRS_String (char* MGRS, long* Zone, long Letters[3], double* Easting, double* Northing, long* Precision) /* * The function Break_MGRS_String breaks down an MGRS * coordinate string into its component parts. * * MGRS : MGRS coordinate string (input) * Zone : UTM Zone (output) * Letters : MGRS coordinate string letters (output) * Easting : Easting value (output) * Northing : Northing value (output) * Precision : Precision level of MGRS string (output) */ { /* Break_MGRS_String */ long num_digits; long num_letters; long i = 0; long j = 0; long error_code = MGRS_NO_ERROR; while (MGRS[i] == ' ') i++; /* skip any leading blanks */ j = i; while (isdigit(MGRS[i])) i++; num_digits = i - j; if (num_digits <= 2) if (num_digits > 0) { char zone_string[3]; /* get zone */ strncpy (zone_string, MGRS+j, 2); zone_string[2] = 0; sscanf (zone_string, "%ld", Zone); if ((*Zone < 1) || (*Zone > 60)) error_code |= MGRS_STRING_ERROR; } else *Zone = 0; else error_code |= MGRS_STRING_ERROR; j = i; while (isalpha(MGRS[i])) i++; num_letters = i - j; if (num_letters == 3) { /* get letters */ Letters[0] = (toupper(MGRS[j]) - (long)'A'); if ((Letters[0] == LETTER_I) || (Letters[0] == LETTER_O)) error_code |= MGRS_STRING_ERROR; Letters[1] = (toupper(MGRS[j+1]) - (long)'A'); if ((Letters[1] == LETTER_I) || (Letters[1] == LETTER_O)) error_code |= MGRS_STRING_ERROR; Letters[2] = (toupper(MGRS[j+2]) - (long)'A'); if ((Letters[2] == LETTER_I) || (Letters[2] == LETTER_O)) error_code |= MGRS_STRING_ERROR; } else error_code |= MGRS_STRING_ERROR; j = i; while (isdigit(MGRS[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, MGRS+j, n); east_string[n] = 0; sscanf (east_string, "%ld", &east); strncpy (north_string, MGRS+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 |= MGRS_STRING_ERROR; return (error_code); } /* Break_MGRS_String */ long Get_Latitude_Band_Min_Northing(long letter, double* min_northing) /* * The function Get_Latitude_Band_Min_Northing receives a latitude band letter * and uses the Latitude_Band_Table to determine the minimum northing for that * latitude band letter. * * letter : Latitude band letter (input) * min_northing : Minimum northing for that letter(output) */ { /* Get_Latitude_Band_Min_Northing */ long error_code = MGRS_NO_ERROR; if ((letter >= LETTER_C) && (letter <= LETTER_H)) *min_northing = Latitude_Band_Table[letter-2].min_northing; else if ((letter >= LETTER_J) && (letter <= LETTER_N)) *min_northing = Latitude_Band_Table[letter-3].min_northing; else if ((letter >= LETTER_P) && (letter <= LETTER_X)) *min_northing = Latitude_Band_Table[letter-4].min_northing; else error_code |= MGRS_STRING_ERROR; return error_code; } /* Get_Latitude_Band_Min_Northing */ void Get_Grid_Values (long zone, long* ltr2_low_value, long* ltr2_high_value, double *false_northing) /* * The function Get_Grid_Values sets the letter range used for * the 2nd letter in the MGRS coordinate string, based on the set * number of the utm zone. It also sets the false northing 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) * false_northing : False northing (output) */ { /* BEGIN Get_Grid_Values */ long set_number; /* Set number (1-6) based on UTM zone number */ long aa_pattern; /* Pattern based on ellipsoid code */ set_number = zone % 6; if (!set_number) set_number = 6; if (!strcmp(MGRS_Ellipsoid_Code,CLARKE_1866) || !strcmp(MGRS_Ellipsoid_Code, CLARKE_1880) || !strcmp(MGRS_Ellipsoid_Code,BESSEL_1841) || !strcmp(MGRS_Ellipsoid_Code,BESSEL_1841_NAMIBIA)) aa_pattern = FALSE; else aa_pattern = TRUE; 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 (aa_pattern) { if ((set_number % 2) == 0) *false_northing = 1500000.0; else *false_northing = 0.0; } else { if ((set_number % 2) == 0) *false_northing = 500000.0; else *false_northing = 1000000.00; } } /* END OF Get_Grid_Values */