mgrs-pole-test
Version:
Get MGRS coordinate for North Pole/South Pole
1,213 lines (1,071 loc) • 99.1 kB
JavaScript
const MGRS_NO_ERROR = 0x0000
const MGRS_LAT_ERROR = 0x0001
const MGRS_LON_ERROR = 0x0002
const MGRS_STRING_ERROR = 0x0004
const MGRS_PRECISION_ERROR = 0x0008
const MGRS_A_ERROR = 0x0010
const MGRS_INV_F_ERROR = 0x0020
const MGRS_EASTING_ERROR = 0x0040
const MGRS_NORTHING_ERROR = 0x0080
const MGRS_ZONE_ERROR = 0x0100
const MGRS_HEMISPHERE_ERROR = 0x0200
const MGRS_LAT_WARNING = 0x0400
const UPS_NO_ERROR = 0x0000
const UPS_LAT_ERROR = 0x0001
const UPS_LON_ERROR = 0x0002
const UPS_HEMISPHERE_ERROR = 0x0004
const UPS_EASTING_ERROR = 0x0008
const UPS_NORTHING_ERROR = 0x0010
const UPS_A_ERROR = 0x0020
const UPS_INV_F_ERROR = 0x0040
const POLAR_NO_ERROR = 0x0000
const POLAR_LAT_ERROR = 0x0001
const POLAR_LON_ERROR = 0x0002
const POLAR_ORIGIN_LAT_ERROR = 0x0004
const POLAR_ORIGIN_LON_ERROR = 0x0008
const POLAR_EASTING_ERROR = 0x0010
const POLAR_NORTHING_ERROR = 0x0020
const POLAR_A_ERROR = 0x0040
const POLAR_INV_F_ERROR = 0x0080
const POLAR_RADIUS_ERROR = 0x0100
const TRANMERC_NO_ERROR = 0x0000
const TRANMERC_LAT_ERROR = 0x0001
const TRANMERC_LON_ERROR = 0x0002
const TRANMERC_EASTING_ERROR = 0x0004
const TRANMERC_NORTHING_ERROR = 0x0008
const TRANMERC_ORIGIN_LAT_ERROR = 0x0010
const TRANMERC_CENT_MER_ERROR = 0x0020
const TRANMERC_A_ERROR = 0x0040
const TRANMERC_INV_F_ERROR = 0x0080
const TRANMERC_SCALE_FACTOR_ERROR = 0x0100
const TRANMERC_LON_WARNING = 0x0200
const UTM_NO_ERROR = 0x0000
const UTM_LAT_ERROR = 0x0001
const UTM_LON_ERROR = 0x0002
const UTM_EASTING_ERROR = 0x0004
const UTM_NORTHING_ERROR = 0x0008
const UTM_ZONE_ERROR = 0x0010
const UTM_HEMISPHERE_ERROR = 0x0020
const UTM_ZONE_OVERRIDE_ERROR = 0x0040
const UTM_A_ERROR = 0x0080
const UTM_INV_F_ERROR = 0x0100
const DEG_TO_RAD = 0.017453292519943295 /* PI/180 */
const RAD_TO_DEG = 57.29577951308232087 /* 180/PI */
const LETTER_A = 0 /* ARRAY INDEX FOR LETTER A */
const LETTER_B = 1 /* ARRAY INDEX FOR LETTER B */
const LETTER_C = 2 /* ARRAY INDEX FOR LETTER C */
const LETTER_D = 3 /* ARRAY INDEX FOR LETTER D */
const LETTER_E = 4 /* ARRAY INDEX FOR LETTER E */
const LETTER_F = 5 /* ARRAY INDEX FOR LETTER F */
const LETTER_G = 6 /* ARRAY INDEX FOR LETTER G */
const LETTER_H = 7 /* ARRAY INDEX FOR LETTER H */
const LETTER_I = 8 /* ARRAY INDEX FOR LETTER I */
const LETTER_J = 9 /* ARRAY INDEX FOR LETTER J */
const LETTER_K = 10 /* ARRAY INDEX FOR LETTER K */
const LETTER_L = 11 /* ARRAY INDEX FOR LETTER L */
const LETTER_M = 12 /* ARRAY INDEX FOR LETTER M */
const LETTER_N = 13 /* ARRAY INDEX FOR LETTER N */
const LETTER_O = 14 /* ARRAY INDEX FOR LETTER O */
const LETTER_P = 15 /* ARRAY INDEX FOR LETTER P */
const LETTER_Q = 16 /* ARRAY INDEX FOR LETTER Q */
const LETTER_R = 17 /* ARRAY INDEX FOR LETTER R */
const LETTER_S = 18 /* ARRAY INDEX FOR LETTER S */
const LETTER_T = 19 /* ARRAY INDEX FOR LETTER T */
const LETTER_U = 20 /* ARRAY INDEX FOR LETTER U */
const LETTER_V = 21 /* ARRAY INDEX FOR LETTER V */
const LETTER_W = 22 /* ARRAY INDEX FOR LETTER W */
const LETTER_X = 23 /* ARRAY INDEX FOR LETTER X */
const LETTER_Y = 24 /* ARRAY INDEX FOR LETTER Y */
const LETTER_Z = 25 /* ARRAY INDEX FOR LETTER Z */
const MGRS_LETTERS = 3 /* NUMBER OF LETTERS IN MGRS */
const ONEHT = 100000.e0 /* ONE HUNDRED THOUSAND */
const TWOMIL = 2000000.e0 /* TWO MILLION */
const TRUE = 1 /* CONSTANT VALUE FOR TRUE VALUE */
const FALSE = 0 /* CONSTANT VALUE FOR FALSE VALUE */
const PI = 3.14159265358979323e0 /* PI */
const PI_OVER_2 = (PI / 2.0e0)
const MIN_EASTING = 100000
const MAX_EASTING = 900000
const MIN_NORTHING = 0
const MAX_NORTHING = 10000000
const MAX_PRECISION = 5 /* Maximum precision of easting & northing */
const MIN_UTM_LAT = ((-80 * PI) / 180.0 ) /* -80 degrees in radians */
const MAX_UTM_LAT = ((84 * PI) / 180.0 ) /* 84 degrees in radians */
const MIN_EAST_NORTH = 0
const MAX_EAST_NORTH = 4000000
const PI_OVER = (PI/2.0e0) /* PI over 2 */
const MIN_LAT = ((-80.5 * PI) / 180.0) /* -80.5 degrees in radians */
const MAX_LAT = ((PI * 90)/180.0) /* 90 degrees in radians */
const MAX_ORIGIN_LAT = ((81.114528 * PI) / 180.0)
const MIN_NORTH_LAT = (83.5*PI/180.0)
const MIN_SOUTH_LAT = (-79.5*PI/180.0)
const TWO_PI = (2.0 * PI)
const MAX_DELTA_LONG = ((PI * 90)/180.0) /* 90 degrees in radians */
const MIN_SCALE_FACTOR = 0.3
const MAX_SCALE_FACTOR = 3.0
const NORTH_POLE_RADIUS = 666727;
const SOUTH_POLE_RADIUS = 1112950.7;
let MGRS_a = 6378137.0; /* Semi-major axis of ellipsoid in meters */
let MGRS_f = 1 / 298.257223563; /* Flattening of ellipsoid */
let UPS_a = 6378137.0; /* Semi-major axis of ellipsoid in meters */
let UPS_f = 1 / 298.257223563; /* Flattening of ellipsoid */
/* Ellipsoid Parameters, default to WGS 84 */
let UPS_False_Easting = 2000000;
let UPS_False_Northing = 2000000;
let UPS_Origin_Latitude = MAX_ORIGIN_LAT; /*set default = North Hemisphere */
let UPS_Origin_Longitude = 0.0;
const PI_Over_4 = (PI / 4.0);
/* Ellipsoid Parameters, default to WGS 84 */
let Polar_a = 6378137.0; /* Semi-major axis of ellipsoid in meters */
let Polar_f = 1 / 298.257223563; /* Flattening of ellipsoid */
let es = 0.08181919084262188000; /* Eccentricity of ellipsoid */
let es_OVER_2 = .040909595421311; /* es / 2.0 */
let Southern_Hemisphere = 0; /* Flag variable */
let tc = 1.0;
let e4 = 1.0033565552493;
let Polar_a_mc = 6378137.0; /* Polar_a * mc */
let two_Polar_a = 12756274.0; /* 2.0 * Polar_a */
/* Polar Stereographic projection Parameters */
let Polar_Origin_Lat = ((PI * 90) / 180); /* Latitude of origin in radians */
let Polar_Origin_Long = 0.0; /* Longitude of origin in radians */
let Polar_False_Easting = 0.0; /* False easting in meters */
let Polar_False_Northing = 0.0; /* False northing in meters */
/* Maximum variance for easting and northing values for WGS 84. */
let Polar_Delta_Easting = 12713601.0;
let Polar_Delta_Northing = 12713601.0;
/* Ellipsoid Parameters, default to WGS 84 */
let TranMerc_a = 6378137.0; /* Semi-major axis of ellipsoid in meters */
let TranMerc_f = 1 / 298.257223563; /* Flattening of ellipsoid */
let TranMerc_es = 0.0066943799901413800; /* Eccentricity (0.08181919084262188000) squared */
let TranMerc_ebs = 0.0067394967565869; /* Second Eccentricity squared */
/* Transverse_Mercator projection Parameters */
let TranMerc_Origin_Lat = 0.0; /* Latitude of origin in radians */
let TranMerc_Origin_Long = 0.0; /* Longitude of origin in radians */
let TranMerc_False_Northing = 0.0; /* False northing in meters */
let TranMerc_False_Easting = 0.0; /* False easting in meters */
let TranMerc_Scale_Factor = 1.0; /* Scale factor */
/* Isometeric to geodetic latitude parameters, default to WGS 84 */
let TranMerc_ap = 6367449.1458008;
let TranMerc_bp = 16038.508696861;
let TranMerc_cp = 16.832613334334;
let TranMerc_dp = 0.021984404273757;
let TranMerc_ep = 3.1148371319283e-005;
/* Maximum variance for easting and northing values for WGS 84. */
let TranMerc_Delta_Easting = 40000000.0;
let TranMerc_Delta_Northing = 40000000.0;
let UTM_a = 6378137.0; /* Semi-major axis of ellipsoid in meters */
let UTM_f = 1 / 298.257223563; /* Flattening of ellipsoid */
let UTM_Override = 0; /* Zone override flag */
const UPS_Constant_Table = [
{
letter: LETTER_A,
ltr2_low_value: LETTER_J,
ltr2_high_value: LETTER_Z,
ltr3_high_value: LETTER_Z,
false_easting: 800000.0,
false_northing: 800000.0
},
{
letter: LETTER_B,
ltr2_low_value: LETTER_A,
ltr2_high_value: LETTER_R,
ltr3_high_value: LETTER_Z,
false_easting: 2000000.0,
false_northing: 800000.0
},
{
letter: LETTER_Y,
ltr2_low_value: LETTER_J,
ltr2_high_value: LETTER_Z,
ltr3_high_value: LETTER_P,
false_easting: 800000.0,
false_northing: 1300000.0
},
{
letter: LETTER_Z,
ltr2_low_value: LETTER_A,
ltr2_high_value: LETTER_J,
ltr3_high_value: LETTER_P,
false_easting: 2000000.0,
false_northing: 1300000.0
}
];
const Latitude_Band_Table = [
{letter: LETTER_C, min_northing: 1100000.0, north: -72.0, south: -80.5, northing_offset: 0.0 },
{letter: LETTER_D, min_northing: 2000000.0, north: -64.0, south: -72.0, northing_offset: 2000000.0},
{letter: LETTER_E, min_northing: 2800000.0, north: -56.0, south: -64.0, northing_offset: 2000000.0},
{letter: LETTER_F, min_northing: 3700000.0, north: -48.0, south: -56.0, northing_offset: 2000000.0},
{letter: LETTER_G, min_northing: 4600000.0, north: -40.0, south: -48.0, northing_offset: 4000000.0},
{letter: LETTER_H, min_northing: 5500000.0, north: -32.0, south: -40.0, northing_offset: 4000000.0},
{letter: LETTER_J, min_northing: 6400000.0, north: -24.0, south: -32.0, northing_offset: 6000000.0},
{letter: LETTER_K, min_northing: 7300000.0, north: -16.0, south: -24.0, northing_offset: 6000000.0},
{letter: LETTER_L, min_northing: 8200000.0, north: -8.0, south: -16.0, northing_offset: 8000000.0},
{letter: LETTER_M, min_northing: 9100000.0, north: 0.0, south: -8.0, northing_offset: 8000000.0},
{letter: LETTER_N, min_northing: 0.0, north: 8.0, south: 0.0, northing_offset: 0.0 },
{letter: LETTER_P, min_northing: 800000.0, north: 16.0, south: 8.0, northing_offset: 0.0},
{letter: LETTER_Q, min_northing: 1700000.0, north: 24.0, south: 16.0, northing_offset: 0.0},
{letter: LETTER_R, min_northing: 2600000.0, north: 32.0, south: 24.0, northing_offset: 2000000.0},
{letter: LETTER_S, min_northing: 3500000.0, north: 40.0, south: 32.0, northing_offset: 2000000.0},
{letter: LETTER_T, min_northing: 4400000.0, north: 48.0, south: 40.0, northing_offset: 4000000.0},
{letter: LETTER_U, min_northing: 5300000.0, north: 56.0, south: 48.0, northing_offset: 4000000.0},
{letter: LETTER_V, min_northing: 6200000.0, north: 64.0, south: 56.0, northing_offset: 6000000.0},
{letter: LETTER_W, min_northing: 7000000.0, north: 72.0, south: 64.0, northing_offset: 6000000.0},
{letter: LETTER_X, min_northing: 7900000.0, north: 84.5, south: 72.0, northing_offset: 6000000.0}
];
class MgrsError {
constructor(message) {
this.message = message;
}
}
class MgrsPole {
fmod(a,b) { return Number((a - (Math.floor(a / b) * b)).toPrecision(8)); };
zfill(num, len) {return (Array(len).join("0") + num).slice(-len);}
POLAR_POW(EsSin) {
return Math.pow((1.0 - EsSin) / (1.0 + EsSin), es_OVER_2);
}
SPHTMD(Latitude) {
return (TranMerc_ap * Latitude - TranMerc_bp * Math.sin(2.e0 * Latitude) + TranMerc_cp * Math.sin(4.e0 * Latitude) - TranMerc_dp * Math.sin(6.e0 * Latitude) + TranMerc_ep * Math.sin(8.e0 * Latitude));
}
SPHSN(Latitude) {
return (TranMerc_a / Math.sqrt( 1.e0 - TranMerc_es * Math.pow(Math.sin(Latitude), 2)));
}
SPHSR(Latitude) {
return (TranMerc_a * (1.e0 - TranMerc_es) / Math.pow(this.DENOM(Latitude), 3));
}
DENOM(Latitude) {
return (Math.sqrt(1.e0 - TranMerc_es * Math.pow(Math.sin(Latitude), 2)));
}
Set_UPS_Parameters(a, f)
{
/*
* The function SET_UPS_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 UPS_NO_ERROR is returned.
*
* a : Semi-major axis of ellipsoid in meters (input)
* f : Flattening of ellipsoid (input)
*/
const inv_f = 1 / f;
let Error_Code = UPS_NO_ERROR;
if (a <= 0.0) { /* Semi-major axis must be greater than zero */
Error_Code |= UPS_A_ERROR;
throw new MgrsError(`Semi-major axis = ‘${a}’ must be greater than zero`);
}
if ((inv_f < 250) || (inv_f > 350)) { /* Inverse flattening must be between 250 and 350 */
Error_Code |= UPS_INV_F_ERROR;
throw new MgrsError(`Inverse flattening ${inv_f} must be between 250 and 350`);
}
if (!Error_Code){ /* no errors */
UPS_a = a;
UPS_f = f;
}
return (Error_Code);
} /* END of Set_UPS_Parameters */
Convert_Geodetic_To_Polar_Stereographic (Latitude, Longitude)
{ /* BEGIN Convert_Geodetic_To_Polar_Stereographic */
/*
* The function Convert_Geodetic_To_Polar_Stereographic converts geodetic
* coordinates (latitude and longitude) to Polar Stereographic coordinates
* (easting and northing), according to the current ellipsoid
* and Polar Stereographic projection parameters. If any errors occur, error
* code(s) are returned by the function, otherwise POLAR_NO_ERROR is returned.
*
* Latitude : Latitude, in radians (input)
* Longitude : Longitude, in radians (input)
* Easting : Easting (X), in meters (output)
* Northing : Northing (Y), in meters (output)
*/
let Easting = '';
let Northing = '';
let dlam;
let slat;
let essin;
let t;
let rho;
let pow_es;
let Error_Code = POLAR_NO_ERROR;
if ((Latitude < -PI_OVER_2) || (Latitude > PI_OVER_2))
{ /* Latitude out of range */
Error_Code |= POLAR_LAT_ERROR;
throw new MgrsError(`Latitude ‘${Latitude * RAD_TO_DEG}’ out of range`);
}
if ((Latitude < 0) && (Southern_Hemisphere == 0))
{ /* Latitude and Origin Latitude in different hemispheres */
Error_Code |= POLAR_LAT_ERROR;
throw new MgrsError(`Latitude ‘${Latitude * RAD_TO_DEG}’ out of range`);
}
if ((Latitude > 0) && (Southern_Hemisphere == 1))
{ /* Latitude and Origin Latitude in different hemispheres */
Error_Code |= POLAR_LAT_ERROR;
throw new MgrsError(`Latitude ‘${Latitude * RAD_TO_DEG}’ out of range`);
}
if ((Longitude < -PI) || (Longitude > TWO_PI))
{ /* Longitude out of range */
Error_Code |= POLAR_LON_ERROR;
throw new MgrsError(`Longitude ‘${Longitude * RAD_TO_DEG}’ out of range`);
}
if (!Error_Code)
{ /* no errors */
if (Math.abs(Math.abs(Latitude) - PI_OVER_2) < 1.0e-10)
{
Easting = Polar_False_Easting;
Northing = Polar_False_Northing;
}
else
{
if (Southern_Hemisphere != 0)
{
Longitude *= -1.0;
Latitude *= -1.0;
}
dlam = Longitude - Polar_Origin_Long;
if (dlam > PI)
{
dlam -= TWO_PI;
}
if (dlam < -PI)
{
dlam += TWO_PI;
}
slat = Math.sin(Latitude);
essin = es * slat;
pow_es = this.POLAR_POW(essin);
t = Math.tan(PI_Over_4 - Latitude / 2.0) / pow_es;
if (Math.abs(Math.abs(Polar_Origin_Lat) - PI_OVER_2) > 1.0e-10)
rho = Polar_a_mc * t / tc;
else
rho = two_Polar_a * t / e4;
if (Southern_Hemisphere != 0)
{
Easting = -(rho * Math.sin(dlam) - Polar_False_Easting);
// *Easting *= -1.0;
Northing = rho * Math.cos(dlam) + Polar_False_Northing;
}
else
{
Easting = rho * Math.sin(dlam) + Polar_False_Easting;
Northing = -rho * Math.cos(dlam) + Polar_False_Northing;
}
}
}
return [Error_Code, Easting, Northing];
} /* END OF Convert_Geodetic_To_Polar_Stereographic */
Set_Polar_Stereographic_Parameters (a,
f,
Latitude_of_True_Scale,
Longitude_Down_from_Pole,
False_Easting,
False_Northing)
{ /* BEGIN Set_Polar_Stereographic_Parameters */
/*
* The function Set_Polar_Stereographic_Parameters receives the ellipsoid
* parameters and Polar Stereograpic projection parameters as inputs, and
* sets the corresponding state variables. If any errors occur, error
* code(s) are returned by the function, otherwise POLAR_NO_ERROR is returned.
*
* a : Semi-major axis of ellipsoid, in meters (input)
* f : Flattening of ellipsoid (input)
* Latitude_of_True_Scale : Latitude of true scale, in radians (input)
* Longitude_Down_from_Pole : Longitude down from pole, in radians (input)
* False_Easting : Easting (X) at center of projection, in meters (input)
* False_Northing : Northing (Y) at center of projection, in meters (input)
*/
let es2;
let slat, clat;
let essin;
let one_PLUS_es, one_MINUS_es;
let pow_es;
let temp, temp_northing;
let inv_f = 1 / f;
let mc;
// const double epsilon = 1.0e-2;
let Error_Code = POLAR_NO_ERROR;
if (a <= 0.0)
{ /* Semi-major axis must be greater than zero */
Error_Code |= POLAR_A_ERROR;
throw new MgrsError(`Semi-major axis = ‘${a}’ must be greater than zero`);
}
if ((inv_f < 250) || (inv_f > 350))
{ /* Inverse flattening must be between 250 and 350 */
Error_Code |= POLAR_INV_F_ERROR;
throw new MgrsError(`Inverse flattening ${inv_f} must be between 250 and 350`);
}
if ((Latitude_of_True_Scale < -PI_OVER_2) || (Latitude_of_True_Scale > PI_OVER_2))
{ /* Origin Latitude out of range */
Error_Code |= POLAR_ORIGIN_LAT_ERROR;
throw new MgrsError(`Latitude ‘${Latitude_of_True_Scale * RAD_TO_DEG}’ out of range`);
}
if ((Longitude_Down_from_Pole < -PI) || (Longitude_Down_from_Pole > TWO_PI))
{ /* Origin Longitude out of range */
Error_Code |= POLAR_ORIGIN_LON_ERROR;
throw new MgrsError(`Longitude ‘${Longitude_Down_from_Pole * RAD_TO_DEG}’ out of range`);
}
if (!Error_Code)
{ /* no errors */
Polar_a = a;
two_Polar_a = 2.0 * Polar_a;
Polar_f = f;
if (Longitude_Down_from_Pole > PI)
Longitude_Down_from_Pole -= TWO_PI;
if (Latitude_of_True_Scale < 0)
{
Southern_Hemisphere = 1;
Polar_Origin_Lat = -Latitude_of_True_Scale;
Polar_Origin_Long = -Longitude_Down_from_Pole;
}
else
{
Southern_Hemisphere = 0;
Polar_Origin_Lat = Latitude_of_True_Scale;
Polar_Origin_Long = Longitude_Down_from_Pole;
}
Polar_False_Easting = False_Easting;
Polar_False_Northing = False_Northing;
es2 = 2 * Polar_f - Polar_f * Polar_f;
es = Math.sqrt(es2);
es_OVER_2 = es / 2.0;
if (Math.abs(Math.abs(Polar_Origin_Lat) - PI_OVER_2) > 1.0e-10)
{
slat = Math.sin(Polar_Origin_Lat);
essin = es * slat;
pow_es = this.POLAR_POW(essin);
clat = Math.cos(Polar_Origin_Lat);
mc = clat / Math.sqrt(1.0 - essin * essin);
Polar_a_mc = Polar_a * mc;
tc = Math.tan(PI_Over_4 - Polar_Origin_Lat / 2.0) / pow_es;
}
else
{
one_PLUS_es = 1.0 + es;
one_MINUS_es = 1.0 - es;
e4 = Math.sqrt(Math.pow(one_PLUS_es, one_PLUS_es) * Math.pow(one_MINUS_es, one_MINUS_es));
}
/* Calculate Radius */
const [local_Error_Code, temp, temp_northing] = this.Convert_Geodetic_To_Polar_Stereographic(0, Longitude_Down_from_Pole);
Polar_Delta_Northing = temp_northing;
if(Polar_False_Northing)
Polar_Delta_Northing -= Polar_False_Northing;
if (Polar_Delta_Northing < 0)
Polar_Delta_Northing = -Polar_Delta_Northing;
Polar_Delta_Northing *= 1.01;
Polar_Delta_Easting = Polar_Delta_Northing;
/* Polar_Delta_Easting = temp_northing;
if(Polar_False_Easting)
Polar_Delta_Easting -= Polar_False_Easting;
if (Polar_Delta_Easting < 0)
Polar_Delta_Easting = -Polar_Delta_Easting;
Polar_Delta_Easting *= 1.01;*/
}
return (Error_Code);
} /* END OF Set_Polar_Stereographic_Parameters */
Convert_Geodetic_To_UPS (Latitude, Longitude)
{
/*
* The function Convert_Geodetic_To_UPS converts geodetic (latitude and
* longitude) coordinates 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.
*
* Latitude : Latitude in radians (input)
* Longitude : Longitude in radians (input)
* Hemisphere : Hemisphere either 'N' or 'S' (output)
* Easting : Easting/X in meters (output)
* Northing : Northing/Y in meters (output)
*/
let Easting;
let Northing;
let tempEasting, tempNorthing;
let Error_Code = UPS_NO_ERROR;
let Hemisphere ='';
if ((Latitude < -MAX_LAT) || (Latitude > MAX_LAT))
{ /* latitude out of range */
Error_Code |= UPS_LAT_ERROR;
throw new MgrsError(`Latitude ‘${Latitude * RAD_TO_DEG}’ out of range`);
}
if ((Latitude < 0) && (Latitude > MIN_SOUTH_LAT)) {
Error_Code |= UPS_LAT_ERROR;
throw new MgrsError(`Latitude ‘${Latitude * RAD_TO_DEG}’ out of range`);
}
if ((Latitude >= 0) && (Latitude < MIN_NORTH_LAT)) {
Error_Code |= UPS_LAT_ERROR;
throw new MgrsError(`Latitude ‘${Latitude * RAD_TO_DEG}’ out of range`);
}
if ((Longitude < -PI) || (Longitude > (2 * PI))) { /* slam out of range */
Error_Code |= UPS_LON_ERROR;
throw new MgrsError(`Longitude ‘${Longitude * RAD_TO_DEG}’ out of range`);
}
if (!Error_Code)
{ /* no errors */
if (Latitude < 0)
{
UPS_Origin_Latitude = -MAX_ORIGIN_LAT;
Hemisphere = 'S';
}
else
{
UPS_Origin_Latitude = MAX_ORIGIN_LAT;
Hemisphere = 'N';
}
this.Set_Polar_Stereographic_Parameters( UPS_a,
UPS_f,
UPS_Origin_Latitude,
UPS_Origin_Longitude,
UPS_False_Easting,
UPS_False_Northing);
const [localError, tempEasting, tempNorthing] = this.Convert_Geodetic_To_Polar_Stereographic(Latitude, Longitude);
// &tempEasting,
// &tempNorthing);
Easting = tempEasting;
Northing = tempNorthing;
} /* END of if(!Error_Code) */
return [Error_Code, Hemisphere, Easting, Northing];
} /* END OF Convert_Geodetic_To_UPS */
Convert_UPS_To_MGRS (Hemisphere, Easting, Northing, Precision)
/*
* 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 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 MGRS string (input)
* MGRS : MGRS coordinate string (output)
*/
{ /* Convert_UPS_To_MGRS */
let MGRS ='';
let false_easting; /* False easting for 2nd letter */
let false_northing; /* False northing for 3rd letter */
let grid_easting; /* Easting used to derive 2nd letter of MGRS */
let grid_northing; /* Northing used to derive 3rd letter of MGRS */
let ltr2_low_value; /* 2nd letter range - low number */
let letters = [0, 0, 0]; /* Number location of 3 letters in alphabet */
let index = 0;
let error_code = MGRS_NO_ERROR;
if ((Hemisphere != 'N') && (Hemisphere != 'S')) {
error_code |= MGRS_HEMISPHERE_ERROR;
throw new MgrsError(`MGRS Hemisphre Error at ${Hemisphere}`);
}
if ((Easting < MIN_EAST_NORTH) || (Easting > MAX_EAST_NORTH)) {
error_code |= MGRS_EASTING_ERROR;
throw new MgrsError(`MGRS Easting Error at ${Easting}`);
}
if ((Northing < MIN_EAST_NORTH) || (Northing > MAX_EAST_NORTH)) {
error_code |= MGRS_NORTHING_ERROR;
throw new MgrsError(`MGRS Northing Error at ${Northing}`);
}
if ((Precision < 0) || (Precision > MAX_PRECISION)) {
error_code |= MGRS_PRECISION_ERROR;
throw new MgrsError(`MGRS Precision Error at ${Precision}`);
}
if (!error_code)
{
let start = 0
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] = Math.floor(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 + (Math.floor(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;
}
const [ localError, mgrs ] = this.Make_MGRS_String (0, letters, Easting, Northing, Precision);
error_code |= localError;
MGRS = mgrs;
}
return [error_code, MGRS];
} /* Convert_UPS_To_MGRS */
Make_MGRS_String (Zone, Letters, Easting, Northing, Precision)
/*
* The function Make_MGRS_String constructs an MGRS string
* from its component parts.
*
* MGRS : MGRS coordinate string (output)
* Zone : UTM Zone (input)
* Letters : MGRS coordinate string letters (input)
* Easting : Easting value (input)
* Northing : Northing value (input)
* Precision : Precision level of MGRS string (input)
*/
{ /* Make_MGRS_String */
let MGRS = '';
let i;
let j;
let divisor;
let east;
let north;
let alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
let error_code = MGRS_NO_ERROR;
i = 0;
if (Zone)
MGRS = `${Zone}`;
// i = sprintf (MGRS+i,"%2.2ld",Zone);
// else
// strncpy(MGRS, " ", 2); // 2 spaces
for (j = 0; j < 3; j++) {
MGRS = MGRS.concat(alphabet[Letters[j]]);
}
divisor = Math.pow (10.0, (5 - Precision));
Easting = this.fmod(Easting, 100000.0);
if (Easting >= 99999.5)
Easting = 99999.0;
east = Math.floor(Easting/divisor);
// i += sprintf (MGRS+i, "%*.*ld", (int)Precision, (int)Precision, east);
MGRS = MGRS.concat(this.zfill(east,5));
Northing = this.fmod (Northing, 100000.0);
if (Northing >= 99999.5)
Northing = 99999.0;
north = Math.floor(Northing/divisor);
// i += sprintf (MGRS+i, "%*.*ld", (int)Precision, (int)Precision, north);
MGRS = MGRS.concat(this.zfill(north,5));
return [error_code, MGRS];
} /* Make_MGRS_String */
isdigit(c) {
return c >= '0' && c <= '9';
}
isalpha(c) {
return c >= 'A' && c <= 'Z';
}
Check_Zone(MGRS)
/*
* The function Check_Zone receives an MGRS coordinate string.
* If a zone is given, TRUE is returned. Otherwise, FALSE
* is returned.
*
* MGRS : MGRS coordinate string (input)
* zone_exists : TRUE if a zone is given,
* FALSE if a zone is not given (output)
*/
{ /* Check_Zone */
let zone_exists = false;
let i = 0;
let j = 0;
let num_digits = 0;
let error_code = MGRS_NO_ERROR;
/* skip any leading blanks */
while (MGRS[i] == ' ')
i++;
j = i;
while (this.isdigit(MGRS[i]))
i++;
num_digits = i - j;
if (num_digits <= 2) {
if (num_digits > 0)
zone_exists = true;
else
zone_exists = false;
}
else {
error_code |= MGRS_STRING_ERROR;
throw new MgrsError(`MGRS Zone String error at ${MGRS}`);
}
return [error_code, zone_exists];
} /* Check_Zone */
Convert_Geodetic_To_MGRS (Latitude, Longitude, Precision) {
let MGRS;
let zone;
let hemisphere;
let easting;
let northing;
let temp_error_code = MGRS_NO_ERROR;
let error_code = MGRS_NO_ERROR;
if ((Latitude < -PI_OVER_2) || (Latitude > PI_OVER_2)) { /* Latitude out of range */
error_code |= MGRS_LAT_ERROR;
throw new MgrsError(`Latitude ‘${Latitude * RAD_TO_DEG}’ out of range`);
}
if ((Longitude < -PI) || (Longitude > (2*PI))) { /* Longitude out of range */
error_code |= MGRS_LON_ERROR;
throw new MgrsError(`Longitude ‘${Longitude * RAD_TO_DEG}’ out of range`);
}
if ((Precision < 0) || (Precision > MAX_PRECISION)) {
error_code |= MGRS_PRECISION_ERROR;
throw new MgrsError(`MGRS Precision Error at ${Precision}`);
}
if (!error_code) {
if ((Latitude < MIN_UTM_LAT) || (Latitude > MAX_UTM_LAT)) {
temp_error_code = this.Set_UPS_Parameters (MGRS_a, MGRS_f);
if(!temp_error_code)
{
const [temp_error_code, hemisphere, easting, northing] = this.Convert_Geodetic_To_UPS (Latitude, Longitude); //, &hemisphere, &easting, &northing);
if(!temp_error_code)
{
const [local_error_code, mgrs] = this.Convert_UPS_To_MGRS (hemisphere, easting, northing, Precision);
error_code = error_code | local_error_code;
MGRS = mgrs;
}
else
{
if (temp_error_code & UPS_LAT_ERROR) {
error_code |= MGRS_LAT_ERROR;
throw new MgrsError(`Latitude ‘${Latitude * RAD_TO_DEG}’ out of range`);
}
if (temp_error_code & UPS_LON_ERROR) {
error_code |= MGRS_LON_ERROR;
throw new MgrsError(`Longitude ‘${Longitude * RAD_TO_DEG}’ out of range`);
}
}
}
}
}
return [error_code, MGRS];
}
Break_MGRS_String (MGRS)
/*
* 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 */
let Zone;
let Letters =[0,0,0];
let Easting;
let Northing;
let Precision;
let num_digits;
let num_letters;
let i = 0;
let j = 0;
let error_code = MGRS_NO_ERROR;
while (MGRS[i] == ' ')
i++; /* skip any leading blanks */
j = i;
while (this.isdigit(MGRS[i]))
i++;
num_digits = i - j;
if (num_digits <= 2) {
if (num_digits > 0)
{
let zone_string = MGRS.substring(j, j+num_digits);
Zone = Number(zone_string);
if ((Zone < 1) || (Zone > 60)) {
error_code |= MGRS_STRING_ERROR;
throw new MgrsError(`MGRS Zone String error at ${MGRS}`);
}
}
else
Zone = 0;
} else {
error_code |= MGRS_STRING_ERROR;
throw new MgrsError(`MGRS Zone String error at ${MGRS}`);
}
j = i;
while (this.isalpha(MGRS[i]))
i++;
num_letters = i - j;
if (num_letters == 3)
{
/* get letters */
Letters[0] = MGRS[j].toUpperCase().charCodeAt(0) - 'A'.charCodeAt(0);
if ((Letters[0] == LETTER_I) || (Letters[0] == LETTER_O)) {
error_code |= MGRS_STRING_ERROR;
throw new MgrsError(`MGRS Zone String error at ${MGRS}`);
}
Letters[1] = MGRS[j+1].toUpperCase().charCodeAt(0) - 'A'.charCodeAt(0);
if ((Letters[1] == LETTER_I) || (Letters[1] == LETTER_O)) {
error_code |= MGRS_STRING_ERROR;
throw new MgrsError(`MGRS Zone String error at ${MGRS}`);
}
Letters[2] = MGRS[j+2].toUpperCase().charCodeAt(0) - 'A'.charCodeAt(0);
if ((Letters[2] == LETTER_I) || (Letters[2] == LETTER_O)) {
error_code |= MGRS_STRING_ERROR;
throw new MgrsError(`MGRS Zone String error at ${MGRS}`);
}
}
else {
error_code |= MGRS_STRING_ERROR;
throw new MgrsError(`MGRS Zone String error at ${MGRS}`);
}
j = i;
while (this.isdigit(MGRS[i]))
i++;
num_digits = i - j;
if ((num_digits <= 10) && (num_digits%2 == 0))
{
let n;
let east_string = []; // east_string[6];
let north_string = []; // north_string[6];
let east;
let north;
let multiplier;
/* get easting & northing */
n = num_digits/2;
Precision = n;
if (n > 0)
{
east_string[n] = 0;
east_string = MGRS.substring(j, j+n);
east = Number(east_string);
north_string[n] = 0;
north_string = MGRS.substring(j+n);
north = Number(north_string);
multiplier = Math.pow (10.0, 5 - n);
Easting = east * multiplier;
Northing = north * multiplier;
} else {
Easting = 0.0;
Northing = 0.0;
}
}
else {
error_code |= MGRS_STRING_ERROR;
throw new MgrsError(`MGRS Zone String error at ${MGRS}`);
}
return [error_code, Zone, Letters, Easting, Northing, Precision];
} /* Break_MGRS_String */
Get_Grid_Values (zone)
/*
* The function getGridValues 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 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 Get_Grid_Values */
let set_number; /* Set number (1-6) based on UTM zone number */
let aa_pattern; /* Pattern based on ellipsoid code */
let ltr2_low_value;
let ltr2_high_value;
let pattern_offset;
set_number = zone % 6;
if (!set_number) {
set_number = 6;
}
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)
pattern_offset = 500000.0;
else
pattern_offset = 0.0;
}
else
{
if ((set_number % 2) == 0)
pattern_offset = 1500000.0;
else
pattern_offset = 1000000.00;
}
return [ltr2_low_value, ltr2_high_value, pattern_offset];
} /* END OF Get_Grid_Values */
Set_UTM_Parameters(a, f, override)
{
/*
* The function Set_UTM_Parameters receives the ellipsoid parameters and
* UTM zone override parameter as inputs, and sets the corresponding state
* variables. If any errors occur, the error code(s) are returned by the
* function, otherwise UTM_NO_ERROR is returned.
*
* a : Semi-major axis of ellipsoid, in meters (input)
* f : Flattening of ellipsoid (input)
* override : UTM override zone, zero indicates no override (input)
*/
let inv_f = 1 / f;
let Error_Code = UTM_NO_ERROR;
if (a <= 0.0)
{ /* Semi-major axis must be greater than zero */
Error_Code |= UTM_A_ERROR;
}
if ((inv_f < 250) || (inv_f > 350))
{ /* Inverse flattening must be between 250 and 350 */
Error_Code |= UTM_INV_F_ERROR;
}
if ((override < 0) || (override > 60))
{
Error_Code |= UTM_ZONE_OVERRIDE_ERROR;
}
if (!Error_Code)
{ /* no errors */
UTM_a = a;
UTM_f = f;
UTM_Override = override;
}
return (Error_Code);
} /* END OF Set_UTM_Parameters */
Convert_MGRS_To_UTM (MGRS)
/*
* 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 */
let Zone;
let Hemisphere;
let Easting;
let Northing;
let min_northing;
let northing_offset;
let ltr2_low_value;
let ltr2_high_value;
let pattern_offset;
let upper_lat_limit; /* North latitude limits based on 1st letter */
let lower_lat_limit; /* South latitude limits based on 1st letter */
let grid_easting; /* Easting for 100,000 meter grid square */
let grid_northing; /* Northing for 100,000 meter grid square */
let letters;
let in_precision;
let latitude = 0.0;
let longitude = 0.0;
let divisor = 1.0;
let utm_error_code = MGRS_NO_ERROR;
let error_code = MGRS_NO_ERROR;
// error_code = Break_MGRS_String (MGRS, Zone, letters, Easting, Northing, &in_precision);
const [local_error_code, local_zone, local_letters, local_Easting, local_Northing, local_in_precision ] = this.Break_MGRS_String (MGRS);
error_code |= local_error_code;
Zone = local_zone;
letters = local_letters;
Easting = local_Easting;
Northing = local_Northing;
in_precision = local_in_precision;
if (!Zone) {
error_code |= MGRS_STRING_ERROR;
throw new MgrsError(`MGRS Zone String error at ${MGRS}`);
}
else
{
if (!error_code)
{
if ((letters[0] == LETTER_X) && ((Zone == 32) || (Zone == 34) || (Zone == 36))) {
error_code |= MGRS_STRING_ERROR;
throw new MgrsError(`MGRS Zone String error at ${MGRS}`);
}
else
{
if (letters[0] < LETTER_N) {
Hemisphere = 'S';
} else {
Hemisphere = 'N';
}
// Get_Grid_Values(*Zone, <r2_low_value, <r2_high_value, &pattern_offset);
const [local_ltr2_low_value, local_ltr2_high_value, local_pattern_offset] = this.Get_Grid_Values(Zone);
ltr2_low_value = local_ltr2_low_value;
ltr2_high_value = local_ltr2_high_value;
pattern_offset = local_pattern_offset;
/* 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;
throw new MgrsError(`MGRS Zone String error at ${MGRS}`);
}
if (!error_code)
{
let row_letter_northing = Number((letters[2]) * ONEHT);
grid_easting = ((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;
const [local_error_code, local_min_northing, local_northing_offset] = this.Get_Latitude_Band_Min_Northing(letters[0]);
min_northing = local_min_northing;
northing_offset = local_northing_offset;
error_code |= local_error_code;
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 = this.Set_UTM_Parameters(MGRS_a,MGRS_f,0);
if (!utm_error_code)
{
// utm_error_code = Convert_UTM_To_Geodetic(*Zone,*Hemisphere,*Easting,*Northing,&latitude,&longitude);
const [local_temp_error_code, local_Latitude, local_Longitude] = this.Convert_UTM_To_Geodetic (Zone, Hemisphere, Easting, Northing);
utm_error_code = local_temp_error_code;
latitude = local_Latitude;
longitude = local_Longitude;
if (!utm_error_code)
{
divisor = Math.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_WARNING;
}