/*
Copyright (C) 2001, 2008 United States Government
as represented by the Administrator of the
National Aeronautics and Space Administration.
All Rights Reserved.
*/
package gov.nasa.worldwind.geom.coords;
import gov.nasa.worldwind.globes.Globe;
import gov.nasa.worldwind.geom.Angle;
/**
* Converter used to translate MGRS coordinate strings to and from geodetic latitude and longitude.
*
* @author Patrick Murris
* @version $Id: MGRSCoordConverter.java 5188 2008-04-27 02:05:53Z patrickmurris $
* @see MGRSCoord
*/
/**
* Ported to Java from the NGA GeoTrans mgrs.c and mgrs.h code. Contains routines to convert
* from Geodetic to MGRS and the other direction.
*
* @author Garrett Headley, Patrick Murris
*/
class MGRSCoordConverter
{
public static final int MGRS_NO_ERROR = 0;
private static final int MGRS_LAT_ERROR = 0x0001;
private static final int MGRS_LON_ERROR = 0x0002;
public static final int MGRS_STRING_ERROR = 0x0004;
private static final int MGRS_PRECISION_ERROR = 0x0008;
private static final int MGRS_A_ERROR = 0x0010;
private static final int MGRS_INV_F_ERROR = 0x0020;
private static final int MGRS_EASTING_ERROR = 0x0040;
private static final int MGRS_NORTHING_ERROR = 0x0080;
private static final int MGRS_ZONE_ERROR = 0x0100;
private static final int MGRS_HEMISPHERE_ERROR = 0x0200;
private static final int MGRS_LAT_WARNING = 0x0400;
private static final int MGRS_NOZONE_WARNING = 0x0800;
private static final int MGRS_UTM_ERROR = 0x1000;
private static final double PI = 3.14159265358979323;
private static final double PI_OVER_2 = (PI / 2.0e0);
private static final int MAX_PRECISION = 5;
private static final double MIN_UTM_LAT = (-80 * PI) / 180.0; // -80 degrees in radians
private static final double MAX_UTM_LAT = (84 * PI) / 180.0; // 84 degrees in radians
public static final double DEG_TO_RAD = 0.017453292519943295; // PI/180
private static final double RAD_TO_DEG = 57.29577951308232087; // 180/PI
private static final double MIN_EAST_NORTH = 0;
private static final double MAX_EAST_NORTH = 4000000;
private static final double TWOMIL = 2000000;
private static final double ONEHT = 100000;
private static final String CLARKE_1866 = "CC";
private static final String CLARKE_1880 = "CD";
private static final String BESSEL_1841 = "BR";
private static final String BESSEL_1841_NAMIBIA = "BN";
// Ellipsoid parameters, default to WGS 84
private double MGRS_a = 6378137.0; // Semi-major axis of ellipsoid in meters
private double MGRS_f = 1 / 298.257223563; // Flattening of ellipsoid
private double MGRS_recpf = 298.257223563;
private String MGRS_Ellipsoid_Code = "WE";
private Globe globe;
private String MGRSString = "";
private long ltr2_low_value;
private long ltr2_high_value; // this is only used for doing MGRS to xxx conversions.
private double false_northing;
private long lastLetter;
private long last_error = MGRS_NO_ERROR;
private double north, south, min_northing;
private double latitude;
private double longitude;
private static final int LETTER_A = 0; /* ARRAY INDEX FOR LETTER A */
private static final int LETTER_B = 1; /* ARRAY INDEX FOR LETTER B */
private static final int LETTER_C = 2; /* ARRAY INDEX FOR LETTER C */
private static final int LETTER_D = 3; /* ARRAY INDEX FOR LETTER D */
private static final int LETTER_E = 4; /* ARRAY INDEX FOR LETTER E */
private static final int LETTER_F = 5; /* ARRAY INDEX FOR LETTER E */
private static final int LETTER_G = 6; /* ARRAY INDEX FOR LETTER H */
private static final int LETTER_H = 7; /* ARRAY INDEX FOR LETTER H */
private static final int LETTER_I = 8; /* ARRAY INDEX FOR LETTER I */
private static final int LETTER_J = 9; /* ARRAY INDEX FOR LETTER J */
private static final int LETTER_K = 10; /* ARRAY INDEX FOR LETTER J */
private static final int LETTER_L = 11; /* ARRAY INDEX FOR LETTER L */
private static final int LETTER_M = 12; /* ARRAY INDEX FOR LETTER M */
private static final int LETTER_N = 13; /* ARRAY INDEX FOR LETTER N */
private static final int LETTER_O = 14; /* ARRAY INDEX FOR LETTER O */
private static final int LETTER_P = 15; /* ARRAY INDEX FOR LETTER P */
private static final int LETTER_Q = 16; /* ARRAY INDEX FOR LETTER Q */
private static final int LETTER_R = 17; /* ARRAY INDEX FOR LETTER R */
private static final int LETTER_S = 18; /* ARRAY INDEX FOR LETTER S */
private static final int LETTER_T = 19; /* ARRAY INDEX FOR LETTER S */
private static final int LETTER_U = 20; /* ARRAY INDEX FOR LETTER U */
private static final int LETTER_V = 21; /* ARRAY INDEX FOR LETTER V */
private static final int LETTER_W = 22; /* ARRAY INDEX FOR LETTER W */
private static final int LETTER_X = 23; /* ARRAY INDEX FOR LETTER X */
private static final int LETTER_Y = 24; /* ARRAY INDEX FOR LETTER Y */
private static final int LETTER_Z = 25; /* ARRAY INDEX FOR LETTER Z */
private static final int MGRS_LETTERS = 3; /* NUMBER OF LETTERS IN MGRS */
private static final String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
// UPS Constants are in the following order:
// long letter; /* letter representing latitude band */
// long ltr2_low_value; /* 2nd letter range - high number */
// long ltr2_high_value; /* 2nd letter range - low 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 */
private static final long[][] upsConstants = {
{LETTER_A, LETTER_J, LETTER_Z, LETTER_Z, 800000, 800000},
{LETTER_B, LETTER_A, LETTER_R, LETTER_Z, 2000000, 800000},
{LETTER_Y, LETTER_J, LETTER_Z, LETTER_P, 800000, 1300000},
{LETTER_Z, LETTER_A, LETTER_J, LETTER_P, 2000000, 1300000}};
// Latitude Band Constants are in the following order:
// 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 */
private static final double[][] latitudeBandConstants = {
{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}};
private class MGRSComponents
{
private final int zone;
private final int latitudeBand;
private final int squareLetter1;
private final int squareLetter2;
private final double easting;
private final double northing;
private final int precision;
public MGRSComponents(int zone, int latitudeBand, int squareLetter1, int squareLetter2,
double easting, double northing, int precision)
{
this.zone = zone;
this.latitudeBand = latitudeBand;
this.squareLetter1 = squareLetter1;
this.squareLetter2 = squareLetter2;
this.easting = easting;
this.northing = northing;
this.precision = precision;
}
public String toString()
{
return "MGRS: " + zone + " " +
alphabet.charAt(latitudeBand) + " " +
alphabet.charAt(squareLetter1) + alphabet.charAt(squareLetter2) + " " +
easting + " " +
northing + " " +
"(" + precision + ")";
}
}
MGRSCoordConverter(Globe globe)
{
this.globe = globe;
if(globe != null)
{
double a = globe.getEquatorialRadius();
double f = (globe.getEquatorialRadius() - globe.getPolarRadius()) / globe.getEquatorialRadius();
setMGRSParameters(a, f, MGRS_Ellipsoid_Code);
}
}
/**
* The function setMGRSParameters receives the ellipsoid parameters and sets the corresponding state variables. If
* any errors occur, the error code(s) are returned by the function, otherwise MGRS_NO_ERROR is returned.
*
* @param mgrs_a Semi-major axis of ellipsoid in meters
* @param mgrs_f Flattening of ellipsoid
* @param ellipsoidCode 2-letter code for ellipsoid
* @return error code
*/
public long setMGRSParameters(double mgrs_a, double mgrs_f, String ellipsoidCode)
{
if (mgrs_a <= 0.0)
return MGRS_A_ERROR;
if (mgrs_f == 0.0)
return MGRS_INV_F_ERROR;
double inv_f = 1 / mgrs_f;
if (inv_f < 250 || inv_f > 350)
return MGRS_INV_F_ERROR;
MGRS_a = mgrs_a;
MGRS_f = mgrs_f;
MGRS_Ellipsoid_Code = ellipsoidCode;
return MGRS_NO_ERROR;
}
/**
* @return Flattening of ellipsoid
*/
public double getMGRS_f()
{
return MGRS_f;
}
/**
* @return Semi-major axis of ellipsoid in meters
*/
public double getMGRS_a()
{
return MGRS_a;
}
/**
* @return Latitude band letter
*/
private long getLastLetter()
{
return lastLetter;
}
/**
* @return 2-letter code for ellipsoid
*/
public String getMGRS_Ellipsoid_Code()
{
return MGRS_Ellipsoid_Code;
}
/**
* The function ConvertMGRSToGeodetic converts an MGRS 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.
*
* @param MGRSString MGRS coordinate string.
* @return the error code.
*/
public long convertMGRSToGeodetic (String MGRSString)
{
latitude = 0;
longitude = 0;
long error_code = checkZone(MGRSString);
if (error_code == MGRS_NO_ERROR)
{
UTMCoord UTM = convertMGRSToUTM (MGRSString);
if(UTM != null)
{
latitude = UTM.getLatitude().radians;
longitude = UTM.getLongitude().radians;
}
else
error_code = MGRS_UTM_ERROR;
}
else if (error_code == MGRS_NOZONE_WARNING)
{
// TODO: polar conversion
//UPSCoord UPS = convertMGRSToUPS (MGRSString);
}
return (error_code);
}
public double getLatitude()
{
return latitude;
}
public double getLongitude()
{
return longitude;
}
/**
* The function Break_MGRS_String breaks down an MGRS
* coordinate string into its component parts. Updates last_error.
*
* @param MGRSString the MGRS coordinate string
* @return the corresponding <code>MGRSComponents</code> or <code>null</code>.
*/
private MGRSComponents breakMGRSString (String MGRSString)
{
int num_digits;
int num_letters;
int i = 0;
int j = 0;
long error_code = MGRS_NO_ERROR;
int zone = 0;
int[] letters = new int[3];
long easting = 0;
long northing = 0;
int precision = 0;
while (i < MGRSString.length() && MGRSString.charAt(i) == ' ')
i++; /* skip any leading blanks */
j = i;
while (i < MGRSString.length() && Character.isDigit(MGRSString.charAt(i)))
i++;
num_digits = i - j;
if (num_digits <= 2)
if (num_digits > 0)
{
/* get zone */
zone = Integer.parseInt(MGRSString.substring(j, i));
if ((zone < 1) || (zone > 60))
error_code |= MGRS_STRING_ERROR;
}
else
error_code |= MGRS_STRING_ERROR;
j = i;
while (i < MGRSString.length() && Character.isLetter(MGRSString.charAt(i)))
i++;
num_letters = i - j;
if (num_letters == 3)
{
/* get letters */
letters[0] = alphabet.indexOf(Character.toUpperCase(MGRSString.charAt(j)));
if ((letters[0] == LETTER_I) || (letters[0] == LETTER_O))
error_code |= MGRS_STRING_ERROR;
letters[1] = alphabet.indexOf(Character.toUpperCase(MGRSString.charAt(j + 1)));
if ((letters[1] == LETTER_I) || (letters[1] == LETTER_O))
error_code |= MGRS_STRING_ERROR;
letters[2] = alphabet.indexOf(Character.toUpperCase(MGRSString.charAt(j + 2)));
if ((letters[2] == LETTER_I) || (letters[2] == LETTER_O))
error_code |= MGRS_STRING_ERROR;
}
else
error_code |= MGRS_STRING_ERROR;
j = i;
while (i < MGRSString.length() && Character.isDigit(MGRSString.charAt(i)))
i++;
num_digits = i - j;
if ((num_digits <= 10) && (num_digits % 2 == 0))
{
/* get easting, northing and precision */
int n;
double multiplier;
/* get easting & northing */
n = num_digits / 2;
precision = n;
if (n > 0)
{
easting = Integer.parseInt(MGRSString.substring(j, j + n));
northing = Integer.parseInt(MGRSString.substring(j + n, j + n + n));
multiplier = Math.pow (10.0, 5 - n);
easting *= multiplier;
northing *= multiplier;
}
else
{
easting = 0;
northing = 0;
}
}
else
error_code |= MGRS_STRING_ERROR;
last_error = error_code;
if (error_code == MGRS_NO_ERROR)
return new MGRSComponents(zone, letters[0], letters[1], letters[2], easting, northing, precision);
return null;
}
/**
* The function Check_Zone receives an MGRS coordinate string.
* If a zone is given, MGRS_NO_ERROR is returned. Otherwise, MGRS_NOZONE_WARNING.
* is returned.
*
* @param MGRSString the MGRS coordinate string.
* @return the error code.
*/
private long checkZone(String MGRSString)
{
int i = 0;
int j = 0;
int num_digits = 0;
long error_code = MGRS_NO_ERROR;
/* skip any leading blanks */
while (i < MGRSString.length() && MGRSString.charAt(i) == ' ')
i++;
j = i;
while (i < MGRSString.length() && Character.isDigit(MGRSString.charAt(i)))
i++;
num_digits = i - j;
if (num_digits > 2)
error_code |= MGRS_STRING_ERROR;
else if (num_digits <= 0)
error_code |= MGRS_NOZONE_WARNING;
return error_code;
}
/**
* 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. Updates min_northing.
*
* @param letter Latitude band letter.
* @return the error code.
*/
private long getLatitudeBandMinNorthing(int letter)
{
long error_code = MGRS_NO_ERROR;
if ((letter >= LETTER_C) && (letter <= LETTER_H))
min_northing = latitudeBandConstants[letter-2][1];
else if ((letter >= LETTER_J) && (letter <= LETTER_N))
min_northing = latitudeBandConstants[letter-3][1];
else if ((letter >= LETTER_P) && (letter <= LETTER_X))
min_northing = latitudeBandConstants[letter-4][1];
else
error_code |= MGRS_STRING_ERROR;
return error_code;
}
/**
* The function 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. Updates north and south.
*
* @param letter the Latitude band letter
* @return the error code.
*/
private long getLatitudeRange(int letter)
{
long error_code = MGRS_NO_ERROR;
if ((letter >= LETTER_C) && (letter <= LETTER_H))
{
north = latitudeBandConstants[letter-2][2] * DEG_TO_RAD;
south = latitudeBandConstants[letter-2][3] * DEG_TO_RAD;
}
else if ((letter >= LETTER_J) && (letter <= LETTER_N))
{
north = latitudeBandConstants[letter-3][2] * DEG_TO_RAD;
south = latitudeBandConstants[letter-3][3] * DEG_TO_RAD;
}
else if ((letter >= LETTER_P) && (letter <= LETTER_X))
{
north = latitudeBandConstants[letter-4][2] * DEG_TO_RAD;
south = latitudeBandConstants[letter-4][3] * DEG_TO_RAD;
}
else
error_code |= MGRS_STRING_ERROR;
return error_code;
}
/**
* The function convertMGRSToUTM converts an MGRS coordinate string
* to UTM projection (zone, hemisphere, easting and northing) coordinates
* according to the current ellipsoid parameters. Updates last_error if any
* errors occured.
*
* @param MGRSString the MGRS coordinate string
* @return the corresponding <code>UTMComponents</code> or <code>null</code>.
*/
private UTMCoord convertMGRSToUTM (String MGRSString)
{
double scaled_min_northing;
double grid_easting; /* Easting for 100,000 meter grid square */
double grid_northing; /* Northing for 100,000 meter grid square */
double temp_grid_northing = 0.0;
double fabs_grid_northing = 0.0;
double latitude = 0.0;
double longitude = 0.0;
double divisor = 1.0;
long error_code = MGRS_NO_ERROR;
char hemisphere = 'N';
double easting = 0;
double northing = 0;
UTMCoord UTM = null;
MGRSComponents MGRS = breakMGRSString(MGRSString);
if (MGRS == null)
error_code |= MGRS_STRING_ERROR;
else
{
if (error_code == MGRS_NO_ERROR)
{
if ((MGRS.latitudeBand == LETTER_X) && ((MGRS.zone == 32) || (MGRS.zone == 34) || (MGRS.zone == 36)))
error_code |= MGRS_STRING_ERROR;
else
{
if (MGRS.latitudeBand < LETTER_N)
hemisphere = 'S';
else
hemisphere = 'N';
getGridValues(MGRS.zone);
// 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 ((MGRS.squareLetter1 < ltr2_low_value) || (MGRS.squareLetter1 > ltr2_high_value) || (MGRS.squareLetter2 > LETTER_V))
error_code |= MGRS_STRING_ERROR;
if (error_code == MGRS_NO_ERROR)
{
grid_northing = (double)(MGRS.squareLetter2) * ONEHT + false_northing;
grid_easting = (double)((MGRS.squareLetter1) - ltr2_low_value + 1) * ONEHT;
if ((ltr2_low_value == LETTER_J) && (MGRS.squareLetter1 > LETTER_O))
grid_easting = grid_easting - ONEHT;
if (MGRS.squareLetter2 > LETTER_O)
grid_northing = grid_northing - ONEHT;
if (MGRS.squareLetter2 > LETTER_I)
grid_northing = grid_northing - ONEHT;
if (grid_northing >= TWOMIL)
grid_northing = grid_northing - TWOMIL;
error_code = getLatitudeBandMinNorthing(MGRS.latitudeBand);
if (error_code == MGRS_NO_ERROR)
{
scaled_min_northing = min_northing;// + false_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)
{
temp_grid_northing = grid_northing + TWOMIL;
fabs_grid_northing = Math.abs(grid_northing);
/* northing is too far away, return an error */
if(fabs_grid_northing > TWOMIL && temp_grid_northing > TWOMIL)
{
last_error = MGRS_NORTHING_ERROR;
return null;
}
/* choose the closest northing */
if(fabs_grid_northing < temp_grid_northing)
grid_northing = min_northing + grid_northing;
else
grid_northing = min_northing + temp_grid_northing;
}
else
grid_northing = min_northing + grid_northing;
easting = grid_easting + MGRS.easting;
northing = grid_northing + MGRS.northing;
try
{
UTM = UTMCoord.fromUTM(MGRS.zone, hemisphere, easting, northing, globe);
latitude = UTM.getLatitude().radians;
divisor = Math.pow (10.0, MGRS.precision);
error_code = getLatitudeRange(MGRS.latitudeBand);
if (error_code == MGRS_NO_ERROR)
{
if (!(((south - DEG_TO_RAD / divisor) <= latitude)
&& (latitude <= (north + DEG_TO_RAD/divisor))))
error_code |= MGRS_LAT_WARNING;
}
}
catch (Exception e)
{
error_code = MGRS_UTM_ERROR;
}
}
}
}
}
}
last_error = error_code;
if(error_code == MGRS_NO_ERROR || error_code == MGRS_LAT_WARNING)
return UTM;
return null;
} /* Convert_MGRS_To_UTM */
/**
* The function convertGeodeticToMGRS converts Geodetic (latitude and longitude) coordinates to an MGRS coordinate
* string, according to the current ellipsoid parameters. If any errors occur, the error code(s) are returned by
* the function, otherwise MGRS_NO_ERROR is returned.
*
* @param latitude Latitude in radians
* @param longitude Longitude in radian
* @param precision Precision level of MGRS string
* @return error code
*/
public long convertGeodeticToMGRS(double latitude, double longitude, int precision)
{
String Hemisphere = "N";
double Easting = 0.0;
double Northing = 0.0;
MGRSString = "";
long error_code = MGRS_NO_ERROR;
if ((latitude < -PI_OVER_2) || (latitude > PI_OVER_2))
{ /* Latitude out of range */
error_code = MGRS_LAT_ERROR;
}
if ((longitude < -PI) || (longitude > (2 * PI)))
{ /* Longitude out of range */
error_code = MGRS_LON_ERROR;
}
if ((precision < 0) || (precision > MAX_PRECISION))
error_code = MGRS_PRECISION_ERROR;
if (error_code == MGRS_NO_ERROR)
{
if ((latitude < MIN_UTM_LAT) || (latitude > MAX_UTM_LAT))
{
// TODO: polar
// UPSConverter.SetUPSParameters(MGRS_a, MGRS_f);
// UPSConverter.convertGeodeticToUPS(latitude, longitude);
// error_code |= convertUPSToMGRS(UPSConverter.getHemisphere(), UPSConverter.getEasting(),
// UPSConverter.getNorthing(), precision);
} else
{
try
{
UTMCoord UTM = UTMCoord.fromLatLon(Angle.fromRadians(latitude), Angle.fromRadians(longitude), globe);
error_code |= convertUTMToMGRS(UTM.getZone(), latitude, UTM.getEasting(),
UTM.getNorthing(), precision);
}
catch (Exception e)
{
error_code = MGRS_UTM_ERROR;
}
}
}
return error_code;
}
/**
* @return converted MGRS string
*/
public String getMGRSString()
{
return MGRSString;
}
/**
* The function Convert_UPS_To_MGRS converts UPS (hemisphere, easting, and northing) coordinates to an MGRS
* coordinate string according to the current ellipsoid parameters. If any errors occur, the error code(s) are
* returned by the function, otherwise MGRS_NO_ERROR is returned.
*
* @param Hemisphere Hemisphere either 'N' or 'S'
* @param Easting Easting/X in meters
* @param Northing Northing/Y in meters
* @param Precision Precision level of MGRS string
* @return error value
*/
private long convertUPSToMGRS(char Hemisphere, Double Easting, Double Northing, long Precision)
{
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 MGRS */
double grid_northing; /* Northing used to derive 3rd letter of MGRS */
long ltr2_low_value; /* 2nd letter range - low number */
long[] letters = new long[MGRS_LETTERS]; /* Number location of 3 letters in alphabet */
double divisor;
int index;
long error_code = MGRS_NO_ERROR;
if ((Hemisphere != 'N') && (Hemisphere != 'S'))
error_code |= MGRS_HEMISPHERE_ERROR;
if ((Easting < MIN_EAST_NORTH) || (Easting > MAX_EAST_NORTH))
error_code |= MGRS_EASTING_ERROR;
if ((Northing < MIN_EAST_NORTH) || (Northing > MAX_EAST_NORTH))
error_code |= MGRS_NORTHING_ERROR;
if ((Precision < 0) || (Precision > MAX_PRECISION))
error_code |= MGRS_PRECISION_ERROR;
if (error_code == MGRS_NO_ERROR)
{
divisor = Math.pow(10.0, (5 - Precision));
Easting = roundMGRS(Easting / divisor) * divisor;
Northing = roundMGRS(Northing / divisor) * divisor;
if (Hemisphere == 'N')
{
if (Easting >= TWOMIL)
letters[0] = LETTER_Z;
else
letters[0] = LETTER_Y;
index = (int) (letters[0] - 22);
// ltr2_low_value = UPS_Constant_Table.get(index).ltr2_low_value;
// false_easting = UPS_Constant_Table.get(index).false_easting;
// false_northing = UPS_Constant_Table.get(index).false_northing;
ltr2_low_value = upsConstants[index][1];
false_easting = (double) upsConstants[index][4];
false_northing = (double) upsConstants[index][5];
} else
{
if (Easting >= TWOMIL)
letters[0] = LETTER_B;
else
letters[0] = LETTER_A;
// ltr2_low_value = UPS_Constant_Table.get((int) letters[0]).ltr2_low_value;
// false_easting = UPS_Constant_Table.get((int) letters[0]).false_easting;
// false_northing = UPS_Constant_Table.get((int) letters[0]).false_northing;
ltr2_low_value = upsConstants[0][1];
false_easting = (double) upsConstants[0][4];
false_northing = (double) upsConstants[0][5];
}
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;
}
makeMGRSString(0, letters, Easting, Northing, Precision);
}
return (error_code);
}
/**
* The function UTM_To_MGRS calculates an MGRS coordinate string based on the zone, latitude, easting and northing.
*
* @param Zone Zone number
* @param Latitude Latitude in radians
* @param Easting Easting
* @param Northing Northing
* @param Precision Precision
* @return error code
*/
private long convertUTMToMGRS(long Zone, double Latitude, double Easting, double Northing, long Precision)
{
double grid_easting; /* Easting used to derive 2nd letter of MGRS */
double grid_northing; /* Northing used to derive 3rd letter of MGRS */
long[] letters = new long[MGRS_LETTERS]; /* Number location of 3 letters in alphabet */
double divisor;
long error_code;
/* Round easting and northing values */
divisor = Math.pow(10.0, (5 - Precision));
Easting = roundMGRS(Easting / divisor) * divisor;
Northing = roundMGRS(Northing / divisor) * divisor;
getGridValues(Zone);
error_code = getLatitudeLetter(Latitude);
letters[0] = getLastLetter();
if (error_code == MGRS_NO_ERROR)
{
grid_northing = Northing;
if (grid_northing == 1.e7)
grid_northing = grid_northing - 1.0;
while (grid_northing >= TWOMIL)
{
grid_northing = grid_northing - TWOMIL;
}
grid_northing = grid_northing - false_northing;
if (grid_northing < 0.0)
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;
grid_easting = Easting;
if (((letters[0] == LETTER_V) && (Zone == 31)) && (grid_easting == 500000.0))
grid_easting = grid_easting - 1.0; /* SUBTRACT 1 METER */
letters[1] = ltr2_low_value + ((long) (grid_easting / ONEHT) - 1);
if ((ltr2_low_value == LETTER_J) && (letters[1] > LETTER_N))
letters[1] = letters[1] + 1;
makeMGRSString(Zone, letters, Easting, Northing, Precision);
}
return error_code;
}
/**
* 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.
* <p/>
* Key values that are set in this function include: ltr2_low_value, ltr2_high_value, and false_northing.
*
* @param zone Zone number
*/
private void getGridValues(long zone)
{
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 == 0)
set_number = 6;
if (MGRS_Ellipsoid_Code.compareTo(CLARKE_1866) == 0 || MGRS_Ellipsoid_Code.compareTo(CLARKE_1880) == 0 ||
MGRS_Ellipsoid_Code.compareTo(BESSEL_1841) == 0 || MGRS_Ellipsoid_Code.compareTo(BESSEL_1841_NAMIBIA) == 0)
aa_pattern = 0L;
else
aa_pattern = 1L;
if ((set_number == 1) || (set_number == 4))
{
ltr2_low_value = (long) LETTER_A;
ltr2_high_value = (long) LETTER_H;
} else if ((set_number == 2) || (set_number == 5))
{
ltr2_low_value = (long) LETTER_J;
ltr2_high_value = (long) LETTER_R;
} else if ((set_number == 3) || (set_number == 6))
{
ltr2_low_value = (long) LETTER_S;
ltr2_high_value = (long) LETTER_Z;
}
/* False northing at A for second letter of grid square */
if (aa_pattern == 1L)
{
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;
}
}
/**
* The function Get_Latitude_Letter receives a latitude value and uses the Latitude_Band_Table to determine the
* latitude band letter for that latitude.
*
* @param latitude latitude to turn into code
* @return error code
*/
private long getLatitudeLetter(double latitude)
{
double temp;
long error_code = MGRS_NO_ERROR;
double lat_deg = latitude * RAD_TO_DEG;
if (lat_deg >= 72 && lat_deg < 84.5)
lastLetter = (long) 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;
// lastLetter = Latitude_Band_Table.get((int) temp).letter;
lastLetter = (long) latitudeBandConstants[(int) temp][0];
} else
error_code |= MGRS_LAT_ERROR;
return error_code;
}
/**
* The function Round_MGRS rounds the input value to the nearest integer, using the standard engineering rule. The
* rounded integer value is then returned.
*
* @param value Value to be rounded
* @return rounded double value
*/
private double roundMGRS(double value)
{
double ivalue = Math.floor(value);
long ival;
double fraction = value - ivalue;
// double fraction = modf (value, &ivalue);
ival = (long) (ivalue);
if ((fraction > 0.5) || ((fraction == 0.5) && (ival % 2 == 1)))
ival++;
return (double) ival;
}
/**
* The function Make_MGRS_String constructs an MGRS string from its component parts.
*
* @param Zone UTM Zone
* @param Letters MGRS coordinate string letters
* @param Easting Easting value
* @param Northing Northing value
* @param Precision Precision level of MGRS string
* @return error code
*/
private long makeMGRSString(long Zone, long[] Letters, double Easting, double Northing, long Precision)
{
int j;
double divisor;
long east;
long north;
long error_code = MGRS_NO_ERROR;
if (Zone != 0)
MGRSString = String.format("%02d", Zone);
else
MGRSString = " ";
for (j = 0; j < 3; j++)
{
if (Letters[j] < 0 || Letters[j] > 26)
return MGRS_ZONE_ERROR; // TODO: Find out why this happens
MGRSString = MGRSString + alphabet.charAt((int) Letters[j]);
}
divisor = Math.pow(10.0, (5 - Precision));
Easting = Easting % 100000.0;
if (Easting >= 99999.5)
Easting = 99999.0;
east = (long) (Easting / divisor);
// Here we need to only use the number requesting in the precision
Integer iEast = (int) east;
String sEast = iEast.toString();
if (sEast.length() > Precision)
sEast = sEast.substring(0, (int) Precision - 1);
else
{
int i;
int length = sEast.length();
for (i = 0; i < Precision - length; i++)
{
sEast = "0" + sEast;
}
}
MGRSString = MGRSString + " " + sEast;
Northing = Northing % 100000.0;
if (Northing >= 99999.5)
Northing = 99999.0;
north = (long) (Northing / divisor);
Integer iNorth = (int) north;
String sNorth = iNorth.toString();
if (sNorth.length() > Precision)
sNorth = sNorth.substring(0, (int) Precision - 1);
else
{
int i;
int length = sNorth.length();
for (i = 0; i < Precision - length; i++)
{
sNorth = "0" + sNorth;
}
}
MGRSString = MGRSString + " " + sNorth;
return (error_code);
}
/**
* Get the last error code.
*
* @return the last error code.
*/
public long getError()
{
return last_error;
}
}