From cc9e47fd9cc3679180e42b3dbc526222d28e8dac Mon Sep 17 00:00:00 2001 From: "Marco Lechner, GeoBoink" Date: Wed, 20 Nov 2019 20:52:29 +0100 Subject: [PATCH] reverted changes in dist/mgrs.js --- dist/mgrs.js | 1344 ++++++++++++++++++++++++++------------------------ 1 file changed, 689 insertions(+), 655 deletions(-) diff --git a/dist/mgrs.js b/dist/mgrs.js index fc0e092..48f8d63 100644 --- a/dist/mgrs.js +++ b/dist/mgrs.js @@ -1,724 +1,758 @@ (function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : - typeof define === 'function' && define.amd ? define(['exports'], factory) : - (global = global || self, factory(global.mgrs = {})); -}(this, function (exports) { 'use strict'; - - /** - * UTM zones are grouped, and assigned to one of a group of 6 - * sets. - * - * {int} @private - */ - const NUM_100K_SETS = 6; - - /** - * The column letters (for easting) of the lower left value, per - * set. - * - * {string} @private - */ - const SET_ORIGIN_COLUMN_LETTERS = 'AJSAJS'; - - /** - * The row letters (for northing) of the lower left value, per - * set. - * - * {string} @private - */ - const SET_ORIGIN_ROW_LETTERS = 'AFAFAF'; - - const A = 65; // A - const I = 73; // I - const O = 79; // O - const V = 86; // V - const Z = 90; // Z - - /** - * Convert lat/lon to MGRS. - * - * @param {[number, number]} ll Array with longitude and latitude on a - * WGS84 ellipsoid. - * @param {number} [accuracy=5] Accuracy in digits (5 for 1 m, 4 for 10 m, 3 for - * 100 m, 2 for 1 km, 1 for 10 km or 0 for 100 km). Optional, default is 5. - * @return {string} the MGRS string for the given location and accuracy. - */ - function forward(ll, accuracy) { - accuracy = typeof accuracy === 'number' ? accuracy : 5; // default accuracy 1m - - if (!Array.isArray(ll)) { - throw new TypeError('forward did not receive an array'); - } - - if (typeof ll[0] === 'string' || typeof ll[1] === 'string') { - throw new TypeError('forward received an array of strings, but it only accepts an array of numbers.'); - } - - const [ lon, lat ] = ll; - if (lon < -180 || lon > 180) { - throw new TypeError(`forward received an invalid longitude of ${lon}`); - } - if (lat < -90 || lat > 90) { - throw new TypeError(`forward received an invalid latitude of ${lat}`); - } + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : + typeof define === 'function' && define.amd ? define(['exports'], factory) : + (factory((global.mgrs = global.mgrs || {}))); +}(this, (function (exports) { 'use strict'; + +/** + * UTM zones are grouped, and assigned to one of a group of 6 + * sets. + * + * {int} @private + */ +var NUM_100K_SETS = 6; + +/** + * The column letters (for easting) of the lower left value, per + * set. + * + * {string} @private + */ +var SET_ORIGIN_COLUMN_LETTERS = 'AJSAJS'; + +/** + * The row letters (for northing) of the lower left value, per + * set. + * + * {string} @private + */ +var SET_ORIGIN_ROW_LETTERS = 'AFAFAF'; + +var A = 65; // A +var I = 73; // I +var O = 79; // O +var V = 86; // V +var Z = 90; // Z +var mgrs = { + forward: forward, + inverse: inverse, + toPoint: toPoint +}; +/** + * Conversion of lat/lon to MGRS. + * + * @param {object} ll Object literal with lat and lon properties on a + * WGS84 ellipsoid. + * @param {int} accuracy Accuracy in digits (5 for 1 m, 4 for 10 m, 3 for + * 100 m, 2 for 1000 m or 1 for 10000 m). Optional, default is 5. + * @return {string} the MGRS string for the given location and accuracy. + */ +function forward(ll, accuracy) { + accuracy = accuracy || 5; // default accuracy 1m + return encode(LLtoUTM({ + lat: ll[1], + lon: ll[0] + }), accuracy); +} + +/** + * Conversion of MGRS to lat/lon. + * + * @param {string} mgrs MGRS string. + * @return {array} An array with left (longitude), bottom (latitude), right + * (longitude) and top (latitude) values in WGS84, representing the + * bounding box for the provided MGRS reference. + */ +function inverse(mgrs) { + var bbox = UTMtoLL(decode(mgrs.toUpperCase())); + if (bbox.lat && bbox.lon) { + return [bbox.lon, bbox.lat, bbox.lon, bbox.lat]; + } + return [bbox.left, bbox.bottom, bbox.right, bbox.top]; +} - if (lat < -80 || lat > 84) { - throw new TypeError(`forward received a latitude of ${lat}, but this library does not support conversions of points in polar regions below 80°S and above 84°N`); - } +function toPoint(mgrs) { + var bbox = UTMtoLL(decode(mgrs.toUpperCase())); + if (bbox.lat && bbox.lon) { + return [bbox.lon, bbox.lat]; + } + return [(bbox.left + bbox.right) / 2, (bbox.top + bbox.bottom) / 2]; +} +/** + * Conversion from degrees to radians. + * + * @private + * @param {number} deg the angle in degrees. + * @return {number} the angle in radians. + */ +function degToRad(deg) { + return (deg * (Math.PI / 180.0)); +} + +/** + * Conversion from radians to degrees. + * + * @private + * @param {number} rad the angle in radians. + * @return {number} the angle in degrees. + */ +function radToDeg(rad) { + return (180.0 * (rad / Math.PI)); +} + +/** + * Converts a set of Longitude and Latitude co-ordinates to UTM + * using the WGS84 ellipsoid. + * + * @private + * @param {object} ll Object literal with lat and lon properties + * representing the WGS84 coordinate to be converted. + * @return {object} Object literal containing the UTM value with easting, + * northing, zoneNumber and zoneLetter properties, and an optional + * accuracy property in digits. Returns null if the conversion failed. + */ +function LLtoUTM(ll) { + var Lat = ll.lat; + var Long = ll.lon; + var a = 6378137.0; //ellip.radius; + var eccSquared = 0.00669438; //ellip.eccsq; + var k0 = 0.9996; + var LongOrigin; + var eccPrimeSquared; + var N, T, C, A, M; + var LatRad = degToRad(Lat); + var LongRad = degToRad(Long); + var LongOriginRad; + var ZoneNumber; + // (int) + ZoneNumber = Math.floor((Long + 180) / 6) + 1; + + //Make sure the longitude 180.00 is in Zone 60 + if (Long === 180) { + ZoneNumber = 60; + } - return encode(LLtoUTM({ lat, lon }), accuracy); - } - - /** - * Convert MGRS to lat/lon bounding box. - * - * @param {string} mgrs MGRS string. - * @return {[number,number,number,number]} An array with left (longitude), - * bottom (latitude), right - * (longitude) and top (latitude) values in WGS84, representing the - * bounding box for the provided MGRS reference. - */ - function inverse(mgrs) { - const bbox = UTMtoLL(decode(mgrs.toUpperCase())); - if (bbox.lat && bbox.lon) { - return [bbox.lon, bbox.lat, bbox.lon, bbox.lat]; - } - return [bbox.left, bbox.bottom, bbox.right, bbox.top]; + // Special zone for Norway + if (Lat >= 56.0 && Lat < 64.0 && Long >= 3.0 && Long < 12.0) { + ZoneNumber = 32; } - function toPoint(mgrs) { - if (mgrs === '') { - throw new TypeError('toPoint received a blank string'); + // Special zones for Svalbard + if (Lat >= 72.0 && Lat < 84.0) { + if (Long >= 0.0 && Long < 9.0) { + ZoneNumber = 31; } - const bbox = UTMtoLL(decode(mgrs.toUpperCase())); - if (bbox.lat && bbox.lon) { - return [bbox.lon, bbox.lat]; + else if (Long >= 9.0 && Long < 21.0) { + ZoneNumber = 33; } - return [(bbox.left + bbox.right) / 2, (bbox.top + bbox.bottom) / 2]; - } - - /** - * Conversion from degrees to radians. - * - * @private - * @param {number} deg the angle in degrees. - * @return {number} the angle in radians. - */ - function degToRad(deg) { - return (deg * (Math.PI / 180)); - } - - /** - * Conversion from radians to degrees. - * - * @private - * @param {number} rad the angle in radians. - * @return {number} the angle in degrees. - */ - function radToDeg(rad) { - return (180 * (rad / Math.PI)); - } - - /** - * Converts a set of Longitude and Latitude co-ordinates to UTM - * using the WGS84 ellipsoid. - * - * @private - * @param {object} ll Object literal with lat and lon properties - * representing the WGS84 coordinate to be converted. - * @return {object} Object literal containing the UTM value with easting, - * northing, zoneNumber and zoneLetter properties, and an optional - * accuracy property in digits. Returns null if the conversion failed. - */ - function LLtoUTM(ll) { - const Lat = ll.lat; - const Long = ll.lon; - const a = 6378137; //ellip.radius; - const eccSquared = 0.00669438; //ellip.eccsq; - const k0 = 0.9996; - const LatRad = degToRad(Lat); - const LongRad = degToRad(Long); - let ZoneNumber; - // (int) - ZoneNumber = Math.floor((Long + 180) / 6) + 1; - - //Make sure the longitude 180 is in Zone 60 - if (Long === 180) { - ZoneNumber = 60; + else if (Long >= 21.0 && Long < 33.0) { + ZoneNumber = 35; } - - // Special zone for Norway - if (Lat >= 56 && Lat < 64 && Long >= 3 && Long < 12) { - ZoneNumber = 32; - } - - // Special zones for Svalbard - if (Lat >= 72 && Lat < 84) { - if (Long >= 0 && Long < 9) { - ZoneNumber = 31; - } - else if (Long >= 9 && Long < 21) { - ZoneNumber = 33; - } - else if (Long >= 21 && Long < 33) { - ZoneNumber = 35; - } - else if (Long >= 33 && Long < 42) { - ZoneNumber = 37; - } + else if (Long >= 33.0 && Long < 42.0) { + ZoneNumber = 37; } + } - const LongOrigin = (ZoneNumber - 1) * 6 - 180 + 3; //+3 puts origin - // in middle of - // zone - const LongOriginRad = degToRad(LongOrigin); + LongOrigin = (ZoneNumber - 1) * 6 - 180 + 3; //+3 puts origin + // in middle of + // zone + LongOriginRad = degToRad(LongOrigin); - const eccPrimeSquared = (eccSquared) / (1 - eccSquared); + eccPrimeSquared = (eccSquared) / (1 - eccSquared); - const N = a / Math.sqrt(1 - eccSquared * Math.sin(LatRad) * Math.sin(LatRad)); - const T = Math.tan(LatRad) * Math.tan(LatRad); - const C = eccPrimeSquared * Math.cos(LatRad) * Math.cos(LatRad); - const A = Math.cos(LatRad) * (LongRad - LongOriginRad); + N = a / Math.sqrt(1 - eccSquared * Math.sin(LatRad) * Math.sin(LatRad)); + T = Math.tan(LatRad) * Math.tan(LatRad); + C = eccPrimeSquared * Math.cos(LatRad) * Math.cos(LatRad); + A = Math.cos(LatRad) * (LongRad - LongOriginRad); - const M = a * ((1 - eccSquared / 4 - 3 * eccSquared * eccSquared / 64 - 5 * eccSquared * eccSquared * eccSquared / 256) * LatRad - (3 * eccSquared / 8 + 3 * eccSquared * eccSquared / 32 + 45 * eccSquared * eccSquared * eccSquared / 1024) * Math.sin(2 * LatRad) + (15 * eccSquared * eccSquared / 256 + 45 * eccSquared * eccSquared * eccSquared / 1024) * Math.sin(4 * LatRad) - (35 * eccSquared * eccSquared * eccSquared / 3072) * Math.sin(6 * LatRad)); + M = a * ((1 - eccSquared / 4 - 3 * eccSquared * eccSquared / 64 - 5 * eccSquared * eccSquared * eccSquared / 256) * LatRad - (3 * eccSquared / 8 + 3 * eccSquared * eccSquared / 32 + 45 * eccSquared * eccSquared * eccSquared / 1024) * Math.sin(2 * LatRad) + (15 * eccSquared * eccSquared / 256 + 45 * eccSquared * eccSquared * eccSquared / 1024) * Math.sin(4 * LatRad) - (35 * eccSquared * eccSquared * eccSquared / 3072) * Math.sin(6 * LatRad)); - const UTMEasting = (k0 * N * (A + (1 - T + C) * A * A * A / 6 + (5 - 18 * T + T * T + 72 * C - 58 * eccPrimeSquared) * A * A * A * A * A / 120) + 500000); + var UTMEasting = (k0 * N * (A + (1 - T + C) * A * A * A / 6.0 + (5 - 18 * T + T * T + 72 * C - 58 * eccPrimeSquared) * A * A * A * A * A / 120.0) + 500000.0); - let UTMNorthing = (k0 * (M + N * Math.tan(LatRad) * (A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24 + (61 - 58 * T + T * T + 600 * C - 330 * eccPrimeSquared) * A * A * A * A * A * A / 720))); - if (Lat < 0) { - UTMNorthing += 10000000; //10000000 meter offset for - // southern hemisphere - } + var UTMNorthing = (k0 * (M + N * Math.tan(LatRad) * (A * A / 2 + (5 - T + 9 * C + 4 * C * C) * A * A * A * A / 24.0 + (61 - 58 * T + T * T + 600 * C - 330 * eccPrimeSquared) * A * A * A * A * A * A / 720.0))); + if (Lat < 0.0) { + UTMNorthing += 10000000.0; //10000000 meter offset for + // southern hemisphere + } - return { - northing: Math.trunc(UTMNorthing), - easting: Math.trunc(UTMEasting), - zoneNumber: ZoneNumber, - zoneLetter: getLetterDesignator(Lat) - }; + return { + northing: Math.trunc(UTMNorthing), + easting: Math.trunc(UTMEasting), + zoneNumber: ZoneNumber, + zoneLetter: getLetterDesignator(Lat) + }; +} + +/** + * Converts UTM coords to lat/long, using the WGS84 ellipsoid. This is a convenience + * class where the Zone can be specified as a single string eg."60N" which + * is then broken down into the ZoneNumber and ZoneLetter. + * + * @private + * @param {object} utm An object literal with northing, easting, zoneNumber + * and zoneLetter properties. If an optional accuracy property is + * provided (in meters), a bounding box will be returned instead of + * latitude and longitude. + * @return {object} An object literal containing either lat and lon values + * (if no accuracy was provided), or top, right, bottom and left values + * for the bounding box calculated according to the provided accuracy. + * Returns null if the conversion failed. + */ +function UTMtoLL(utm) { + + var UTMNorthing = utm.northing; + var UTMEasting = utm.easting; + var zoneLetter = utm.zoneLetter; + var zoneNumber = utm.zoneNumber; + // check the ZoneNummber is valid + if (zoneNumber < 0 || zoneNumber > 60) { + return null; } - /** - * Converts UTM coords to lat/long, using the WGS84 ellipsoid. This is a convenience - * class where the Zone can be specified as a single string eg."60N" which - * is then broken down into the ZoneNumber and ZoneLetter. - * - * @private - * @param {object} utm An object literal with northing, easting, zoneNumber - * and zoneLetter properties. If an optional accuracy property is - * provided (in meters), a bounding box will be returned instead of - * latitude and longitude. - * @return {object} An object literal containing either lat and lon values - * (if no accuracy was provided), or top, right, bottom and left values - * for the bounding box calculated according to the provided accuracy. - * Returns null if the conversion failed. - */ - function UTMtoLL(utm) { - - const UTMNorthing = utm.northing; - const UTMEasting = utm.easting; - const { zoneLetter, zoneNumber } = utm; - // check the ZoneNummber is valid - if (zoneNumber < 0 || zoneNumber > 60) { - return null; - } + var k0 = 0.9996; + var a = 6378137.0; //ellip.radius; + var eccSquared = 0.00669438; //ellip.eccsq; + var eccPrimeSquared; + var e1 = (1 - Math.sqrt(1 - eccSquared)) / (1 + Math.sqrt(1 - eccSquared)); + var N1, T1, C1, R1, D, M; + var LongOrigin; + var mu, phi1Rad; + + // remove 500,000 meter offset for longitude + var x = UTMEasting - 500000.0; + var y = UTMNorthing; + + // We must know somehow if we are in the Northern or Southern + // hemisphere, this is the only time we use the letter So even + // if the Zone letter isn't exactly correct it should indicate + // the hemisphere correctly + if (zoneLetter < 'N') { + y -= 10000000.0; // remove 10,000,000 meter offset used + // for southern hemisphere + } - const k0 = 0.9996; - const a = 6378137; //ellip.radius; - const eccSquared = 0.00669438; //ellip.eccsq; - const e1 = (1 - Math.sqrt(1 - eccSquared)) / (1 + Math.sqrt(1 - eccSquared)); - - // remove 500,000 meter offset for longitude - const x = UTMEasting - 500000; - let y = UTMNorthing; - - // We must know somehow if we are in the Northern or Southern - // hemisphere, this is the only time we use the letter So even - // if the Zone letter isn't exactly correct it should indicate - // the hemisphere correctly - if (zoneLetter < 'N') { - y -= 10000000; // remove 10,000,000 meter offset used - // for southern hemisphere - } + // There are 60 zones with zone 1 being at West -180 to -174 + LongOrigin = (zoneNumber - 1) * 6 - 180 + 3; // +3 puts origin + // in middle of + // zone + + eccPrimeSquared = (eccSquared) / (1 - eccSquared); + + M = y / k0; + mu = M / (a * (1 - eccSquared / 4 - 3 * eccSquared * eccSquared / 64 - 5 * eccSquared * eccSquared * eccSquared / 256)); + + phi1Rad = mu + (3 * e1 / 2 - 27 * e1 * e1 * e1 / 32) * Math.sin(2 * mu) + (21 * e1 * e1 / 16 - 55 * e1 * e1 * e1 * e1 / 32) * Math.sin(4 * mu) + (151 * e1 * e1 * e1 / 96) * Math.sin(6 * mu); + // double phi1 = ProjMath.radToDeg(phi1Rad); + + N1 = a / Math.sqrt(1 - eccSquared * Math.sin(phi1Rad) * Math.sin(phi1Rad)); + T1 = Math.tan(phi1Rad) * Math.tan(phi1Rad); + C1 = eccPrimeSquared * Math.cos(phi1Rad) * Math.cos(phi1Rad); + R1 = a * (1 - eccSquared) / Math.pow(1 - eccSquared * Math.sin(phi1Rad) * Math.sin(phi1Rad), 1.5); + D = x / (N1 * k0); + + var lat = phi1Rad - (N1 * Math.tan(phi1Rad) / R1) * (D * D / 2 - (5 + 3 * T1 + 10 * C1 - 4 * C1 * C1 - 9 * eccPrimeSquared) * D * D * D * D / 24 + (61 + 90 * T1 + 298 * C1 + 45 * T1 * T1 - 252 * eccPrimeSquared - 3 * C1 * C1) * D * D * D * D * D * D / 720); + lat = radToDeg(lat); + + var lon = (D - (1 + 2 * T1 + C1) * D * D * D / 6 + (5 - 2 * C1 + 28 * T1 - 3 * C1 * C1 + 8 * eccPrimeSquared + 24 * T1 * T1) * D * D * D * D * D / 120) / Math.cos(phi1Rad); + lon = LongOrigin + radToDeg(lon); + + var result; + if (utm.accuracy) { + var topRight = UTMtoLL({ + northing: utm.northing + utm.accuracy, + easting: utm.easting + utm.accuracy, + zoneLetter: utm.zoneLetter, + zoneNumber: utm.zoneNumber + }); + result = { + top: topRight.lat, + right: topRight.lon, + bottom: lat, + left: lon + }; + } + else { + result = { + lat: lat, + lon: lon + }; + } + return result; +} + +/** + * Calculates the MGRS letter designator for the given latitude. + * + * @private + * @param {number} lat The latitude in WGS84 to get the letter designator + * for. + * @return {char} The letter designator. + */ +function getLetterDesignator(lat) { + //This is here as an error flag to show that the Latitude is + //outside MGRS limits + var LetterDesignator = 'Z'; + + if ((84 >= lat) && (lat >= 72)) { + LetterDesignator = 'X'; + } + else if ((72 > lat) && (lat >= 64)) { + LetterDesignator = 'W'; + } + else if ((64 > lat) && (lat >= 56)) { + LetterDesignator = 'V'; + } + else if ((56 > lat) && (lat >= 48)) { + LetterDesignator = 'U'; + } + else if ((48 > lat) && (lat >= 40)) { + LetterDesignator = 'T'; + } + else if ((40 > lat) && (lat >= 32)) { + LetterDesignator = 'S'; + } + else if ((32 > lat) && (lat >= 24)) { + LetterDesignator = 'R'; + } + else if ((24 > lat) && (lat >= 16)) { + LetterDesignator = 'Q'; + } + else if ((16 > lat) && (lat >= 8)) { + LetterDesignator = 'P'; + } + else if ((8 > lat) && (lat >= 0)) { + LetterDesignator = 'N'; + } + else if ((0 > lat) && (lat >= -8)) { + LetterDesignator = 'M'; + } + else if ((-8 > lat) && (lat >= -16)) { + LetterDesignator = 'L'; + } + else if ((-16 > lat) && (lat >= -24)) { + LetterDesignator = 'K'; + } + else if ((-24 > lat) && (lat >= -32)) { + LetterDesignator = 'J'; + } + else if ((-32 > lat) && (lat >= -40)) { + LetterDesignator = 'H'; + } + else if ((-40 > lat) && (lat >= -48)) { + LetterDesignator = 'G'; + } + else if ((-48 > lat) && (lat >= -56)) { + LetterDesignator = 'F'; + } + else if ((-56 > lat) && (lat >= -64)) { + LetterDesignator = 'E'; + } + else if ((-64 > lat) && (lat >= -72)) { + LetterDesignator = 'D'; + } + else if ((-72 > lat) && (lat >= -80)) { + LetterDesignator = 'C'; + } + return LetterDesignator; +} + +/** + * Encodes a UTM location as MGRS string. + * + * @private + * @param {object} utm An object literal with easting, northing, + * zoneLetter, zoneNumber + * @param {number} accuracy Accuracy in digits (1-5). + * @return {string} MGRS string for the given UTM location. + */ +function encode(utm, accuracy) { + // prepend with leading zeroes + var seasting = "00000" + utm.easting, + snorthing = "00000" + utm.northing; + + return utm.zoneNumber + utm.zoneLetter + get100kID(utm.easting, utm.northing, utm.zoneNumber) + seasting.substr(seasting.length - 5, accuracy) + snorthing.substr(snorthing.length - 5, accuracy); +} + +/** + * Get the two letter 100k designator for a given UTM easting, + * northing and zone number value. + * + * @private + * @param {number} easting + * @param {number} northing + * @param {number} zoneNumber + * @return {string} the two letter 100k designator for the given UTM location. + */ +function get100kID(easting, northing, zoneNumber) { + var setParm = get100kSetForZone(zoneNumber); + var setColumn = Math.floor(easting / 100000); + var setRow = Math.floor(northing / 100000) % 20; + return getLetter100kID(setColumn, setRow, setParm); +} + +/** + * Given a UTM zone number, figure out the MGRS 100K set it is in. + * + * @private + * @param {number} i An UTM zone number. + * @return {number} the 100k set the UTM zone is in. + */ +function get100kSetForZone(i) { + var setParm = i % NUM_100K_SETS; + if (setParm === 0) { + setParm = NUM_100K_SETS; + } - // There are 60 zones with zone 1 being at West -180 to -174 - const LongOrigin = (zoneNumber - 1) * 6 - 180 + 3; // +3 puts origin - // in middle of - // zone - - const eccPrimeSquared = (eccSquared) / (1 - eccSquared); - - const M = y / k0; - const mu = M / (a * (1 - eccSquared / 4 - 3 * eccSquared * eccSquared / 64 - 5 * eccSquared * eccSquared * eccSquared / 256)); - - const phi1Rad = mu + (3 * e1 / 2 - 27 * e1 * e1 * e1 / 32) * Math.sin(2 * mu) + (21 * e1 * e1 / 16 - 55 * e1 * e1 * e1 * e1 / 32) * Math.sin(4 * mu) + (151 * e1 * e1 * e1 / 96) * Math.sin(6 * mu); - // double phi1 = ProjMath.radToDeg(phi1Rad); - - const N1 = a / Math.sqrt(1 - eccSquared * Math.sin(phi1Rad) * Math.sin(phi1Rad)); - const T1 = Math.tan(phi1Rad) * Math.tan(phi1Rad); - const C1 = eccPrimeSquared * Math.cos(phi1Rad) * Math.cos(phi1Rad); - const R1 = a * (1 - eccSquared) / Math.pow(1 - eccSquared * Math.sin(phi1Rad) * Math.sin(phi1Rad), 1.5); - const D = x / (N1 * k0); - - let lat = phi1Rad - (N1 * Math.tan(phi1Rad) / R1) * (D * D / 2 - (5 + 3 * T1 + 10 * C1 - 4 * C1 * C1 - 9 * eccPrimeSquared) * D * D * D * D / 24 + (61 + 90 * T1 + 298 * C1 + 45 * T1 * T1 - 252 * eccPrimeSquared - 3 * C1 * C1) * D * D * D * D * D * D / 720); - lat = radToDeg(lat); - - let lon = (D - (1 + 2 * T1 + C1) * D * D * D / 6 + (5 - 2 * C1 + 28 * T1 - 3 * C1 * C1 + 8 * eccPrimeSquared + 24 * T1 * T1) * D * D * D * D * D / 120) / Math.cos(phi1Rad); - lon = LongOrigin + radToDeg(lon); - - let result; - if (typeof utm.accuracy === 'number') { - const topRight = UTMtoLL({ - northing: utm.northing + utm.accuracy, - easting: utm.easting + utm.accuracy, - zoneLetter: utm.zoneLetter, - zoneNumber: utm.zoneNumber - }); - result = { - top: topRight.lat, - right: topRight.lon, - bottom: lat, - left: lon - }; - } - else { - result = { - lat, - lon - }; - } - return result; - } - - /** - * Calculates the MGRS letter designator for the given latitude. - * - * @private (Not intended for public API, only exported for testing.) - * @param {number} latitude The latitude in WGS84 to get the letter designator - * for. - * @return {string} The letter designator. - */ - function getLetterDesignator(latitude) { - if (latitude <= 84 && latitude >= 72) { - // the X band is 12 degrees high - return 'X'; - } else if (latitude < 72 && latitude >= -80) { - // Latitude bands are lettered C through X, excluding I and O - const bandLetters = 'CDEFGHJKLMNPQRSTUVWX'; - const bandHeight = 8; - const minLatitude = -80; - const index = Math.floor((latitude - minLatitude) / bandHeight); - return bandLetters[index]; - } else if (latitude > 84 || latitude < -80) { - //This is here as an error flag to show that the Latitude is - //outside MGRS limits - return 'Z'; - } + return setParm; +} + +/** + * Get the two-letter MGRS 100k designator given information + * translated from the UTM northing, easting and zone number. + * + * @private + * @param {number} column the column index as it relates to the MGRS + * 100k set spreadsheet, created from the UTM easting. + * Values are 1-8. + * @param {number} row the row index as it relates to the MGRS 100k set + * spreadsheet, created from the UTM northing value. Values + * are from 0-19. + * @param {number} parm the set block, as it relates to the MGRS 100k set + * spreadsheet, created from the UTM zone. Values are from + * 1-60. + * @return {string} two letter MGRS 100k code. + */ +function getLetter100kID(column, row, parm) { + // colOrigin and rowOrigin are the letters at the origin of the set + var index = parm - 1; + var colOrigin = SET_ORIGIN_COLUMN_LETTERS.charCodeAt(index); + var rowOrigin = SET_ORIGIN_ROW_LETTERS.charCodeAt(index); + + // colInt and rowInt are the letters to build to return + var colInt = colOrigin + column - 1; + var rowInt = rowOrigin + row; + var rollover = false; + + if (colInt > Z) { + colInt = colInt - Z + A - 1; + rollover = true; } - /** - * Encodes a UTM location as MGRS string. - * - * @private - * @param {object} utm An object literal with easting, northing, - * zoneLetter, zoneNumber - * @param {number} accuracy Accuracy in digits (0-5). - * @return {string} MGRS string for the given UTM location. - */ - function encode(utm, accuracy) { - // prepend with leading zeroes - const seasting = '00000' + utm.easting, - snorthing = '00000' + utm.northing; - - return utm.zoneNumber + utm.zoneLetter + get100kID(utm.easting, utm.northing, utm.zoneNumber) + seasting.substr(seasting.length - 5, accuracy) + snorthing.substr(snorthing.length - 5, accuracy); - } - - /** - * Get the two letter 100k designator for a given UTM easting, - * northing and zone number value. - * - * @private - * @param {number} easting - * @param {number} northing - * @param {number} zoneNumber - * @return {string} the two letter 100k designator for the given UTM location. - */ - function get100kID(easting, northing, zoneNumber) { - const setParm = get100kSetForZone(zoneNumber); - const setColumn = Math.floor(easting / 100000); - const setRow = Math.floor(northing / 100000) % 20; - return getLetter100kID(setColumn, setRow, setParm); - } - - /** - * Given a UTM zone number, figure out the MGRS 100K set it is in. - * - * @private - * @param {number} i An UTM zone number. - * @return {number} the 100k set the UTM zone is in. - */ - function get100kSetForZone(i) { - let setParm = i % NUM_100K_SETS; - if (setParm === 0) { - setParm = NUM_100K_SETS; - } + if (colInt === I || (colOrigin < I && colInt > I) || ((colInt > I || colOrigin < I) && rollover)) { + colInt++; + } - return setParm; - } - - /** - * Get the two-letter MGRS 100k designator given information - * translated from the UTM northing, easting and zone number. - * - * @private - * @param {number} column the column index as it relates to the MGRS - * 100k set spreadsheet, created from the UTM easting. - * Values are 1-8. - * @param {number} row the row index as it relates to the MGRS 100k set - * spreadsheet, created from the UTM northing value. Values - * are from 0-19. - * @param {number} parm the set block, as it relates to the MGRS 100k set - * spreadsheet, created from the UTM zone. Values are from - * 1-60. - * @return {string} two letter MGRS 100k code. - */ - function getLetter100kID(column, row, parm) { - // colOrigin and rowOrigin are the letters at the origin of the set - const index = parm - 1; - const colOrigin = SET_ORIGIN_COLUMN_LETTERS.charCodeAt(index); - const rowOrigin = SET_ORIGIN_ROW_LETTERS.charCodeAt(index); - - // colInt and rowInt are the letters to build to return - let colInt = colOrigin + column - 1; - let rowInt = rowOrigin + row; - let rollover = false; - - if (colInt > Z) { - colInt = colInt - Z + A - 1; - rollover = true; - } + if (colInt === O || (colOrigin < O && colInt > O) || ((colInt > O || colOrigin < O) && rollover)) { + colInt++; - if (colInt === I || (colOrigin < I && colInt > I) || ((colInt > I || colOrigin < I) && rollover)) { + if (colInt === I) { colInt++; } + } - if (colInt === O || (colOrigin < O && colInt > O) || ((colInt > O || colOrigin < O) && rollover)) { - colInt++; - - if (colInt === I) { - colInt++; - } - } + if (colInt > Z) { + colInt = colInt - Z + A - 1; + } - if (colInt > Z) { - colInt = colInt - Z + A - 1; - } + if (rowInt > V) { + rowInt = rowInt - V + A - 1; + rollover = true; + } + else { + rollover = false; + } - if (rowInt > V) { - rowInt = rowInt - V + A - 1; - rollover = true; - } - else { - rollover = false; - } + if (((rowInt === I) || ((rowOrigin < I) && (rowInt > I))) || (((rowInt > I) || (rowOrigin < I)) && rollover)) { + rowInt++; + } - if (((rowInt === I) || ((rowOrigin < I) && (rowInt > I))) || (((rowInt > I) || (rowOrigin < I)) && rollover)) { - rowInt++; - } + if (((rowInt === O) || ((rowOrigin < O) && (rowInt > O))) || (((rowInt > O) || (rowOrigin < O)) && rollover)) { + rowInt++; - if (((rowInt === O) || ((rowOrigin < O) && (rowInt > O))) || (((rowInt > O) || (rowOrigin < O)) && rollover)) { + if (rowInt === I) { rowInt++; - - if (rowInt === I) { - rowInt++; - } - } - - if (rowInt > V) { - rowInt = rowInt - V + A - 1; } - - const twoLetter = String.fromCharCode(colInt) + String.fromCharCode(rowInt); - return twoLetter; } - /** - * Decode the UTM parameters from a MGRS string. - * - * @private - * @param {string} mgrsString an UPPERCASE coordinate string is expected. - * @return {object} An object literal with easting, northing, zoneLetter, - * zoneNumber and accuracy (in meters) properties. - */ - function decode(mgrsString) { - - if (mgrsString && mgrsString.length === 0) { - throw new TypeError('MGRSPoint coverting from nothing'); - } - - //remove any spaces in MGRS String - mgrsString = mgrsString.replace(/ /g, ''); + if (rowInt > V) { + rowInt = rowInt - V + A - 1; + } - const { length } = mgrsString; + var twoLetter = String.fromCharCode(colInt) + String.fromCharCode(rowInt); + return twoLetter; +} + +/** + * Decode the UTM parameters from a MGRS string. + * + * @private + * @param {string} mgrsString an UPPERCASE coordinate string is expected. + * @return {object} An object literal with easting, northing, zoneLetter, + * zoneNumber and accuracy (in meters) properties. + */ +function decode(mgrsString) { + + if (mgrsString && mgrsString.length === 0) { + throw ("MGRSPoint coverting from nothing"); + } - let hunK = null; - let sb = ''; - let testChar; - let i = 0; + var length = mgrsString.length; - // get Zone number - while (!(/[A-Z]/).test(testChar = mgrsString.charAt(i))) { - if (i >= 2) { - throw new Error(`MGRSPoint bad conversion from: ${mgrsString}`); - } - sb += testChar; - i++; - } - - const zoneNumber = parseInt(sb, 10); + var hunK = null; + var sb = ""; + var testChar; + var i = 0; - if (i === 0 || i + 3 > length) { - // A good MGRS string has to be 4-5 digits long, - // ##AAA/#AAA at least. - throw new Error(`MGRSPoint bad conversion from ${mgrsString}`); + // get Zone number + while (!(/[A-Z]/).test(testChar = mgrsString.charAt(i))) { + if (i >= 2) { + throw ("MGRSPoint bad conversion from: " + mgrsString); } + sb += testChar; + i++; + } - const zoneLetter = mgrsString.charAt(i++); + var zoneNumber = parseInt(sb, 10); - // Should we check the zone letter here? Why not. - if (zoneLetter <= 'A' || zoneLetter === 'B' || zoneLetter === 'Y' || zoneLetter >= 'Z' || zoneLetter === 'I' || zoneLetter === 'O') { - throw new Error(`MGRSPoint zone letter ${zoneLetter} not handled: ${mgrsString}`); - } + if (i === 0 || i + 3 > length) { + // A good MGRS string has to be 4-5 digits long, + // ##AAA/#AAA at least. + throw ("MGRSPoint bad conversion from: " + mgrsString); + } - hunK = mgrsString.substring(i, i += 2); + var zoneLetter = mgrsString.charAt(i++); - const set = get100kSetForZone(zoneNumber); + // Should we check the zone letter here? Why not. + if (zoneLetter <= 'A' || zoneLetter === 'B' || zoneLetter === 'Y' || zoneLetter >= 'Z' || zoneLetter === 'I' || zoneLetter === 'O') { + throw ("MGRSPoint zone letter " + zoneLetter + " not handled: " + mgrsString); + } - const east100k = getEastingFromChar(hunK.charAt(0), set); - let north100k = getNorthingFromChar(hunK.charAt(1), set); + hunK = mgrsString.substring(i, i += 2); - // We have a bug where the northing may be 2000000 too low. - // How - // do we know when to roll over? + var set = get100kSetForZone(zoneNumber); - while (north100k < getMinNorthing(zoneLetter)) { - north100k += 2000000; - } + var east100k = getEastingFromChar(hunK.charAt(0), set); + var north100k = getNorthingFromChar(hunK.charAt(1), set); - // calculate the char index for easting/northing separator - const remainder = length - i; + // We have a bug where the northing may be 2000000 too low. + // How + // do we know when to roll over? - if (remainder % 2 !== 0) { - throw new Error(`MGRSPoint has to have an even number -of digits after the zone letter and two 100km letters - front -half for easting meters, second half for -northing meters ${mgrsString}`); - } + while (north100k < getMinNorthing(zoneLetter)) { + north100k += 2000000; + } - const sep = remainder / 2; - - let sepEasting = 0; - let sepNorthing = 0; - let accuracyBonus, sepEastingString, sepNorthingString; - if (sep > 0) { - accuracyBonus = 100000 / Math.pow(10, sep); - sepEastingString = mgrsString.substring(i, i + sep); - sepEasting = parseFloat(sepEastingString) * accuracyBonus; - sepNorthingString = mgrsString.substring(i + sep); - sepNorthing = parseFloat(sepNorthingString) * accuracyBonus; - } + // calculate the char index for easting/northing separator + var remainder = length - i; - const easting = sepEasting + east100k; - const northing = sepNorthing + north100k; + if (remainder % 2 !== 0) { + throw ("MGRSPoint has to have an even number \nof digits after the zone letter and two 100km letters - front \nhalf for easting meters, second half for \nnorthing meters" + mgrsString); + } - return { - easting, - northing, - zoneLetter, - zoneNumber, - accuracy: accuracyBonus - }; + var sep = remainder / 2; + + var sepEasting = 0.0; + var sepNorthing = 0.0; + var accuracyBonus, sepEastingString, sepNorthingString, easting, northing; + if (sep > 0) { + accuracyBonus = 100000.0 / Math.pow(10, sep); + sepEastingString = mgrsString.substring(i, i + sep); + sepEasting = parseFloat(sepEastingString) * accuracyBonus; + sepNorthingString = mgrsString.substring(i + sep); + sepNorthing = parseFloat(sepNorthingString) * accuracyBonus; } - /** - * Given the first letter from a two-letter MGRS 100k zone, and given the - * MGRS table set for the zone number, figure out the easting value that - * should be added to the other, secondary easting value. - * - * @private - * @param {string} e The first letter from a two-letter MGRS 100´k zone. - * @param {number} set The MGRS table set for the zone number. - * @return {number} The easting value for the given letter and set. - */ - function getEastingFromChar(e, set) { - // colOrigin is the letter at the origin of the set for the - // column - let curCol = SET_ORIGIN_COLUMN_LETTERS.charCodeAt(set - 1); - let eastingValue = 100000; - let rewindMarker = false; - - while (curCol !== e.charCodeAt(0)) { + easting = sepEasting + east100k; + northing = sepNorthing + north100k; + + return { + easting: easting, + northing: northing, + zoneLetter: zoneLetter, + zoneNumber: zoneNumber, + accuracy: accuracyBonus + }; +} + +/** + * Given the first letter from a two-letter MGRS 100k zone, and given the + * MGRS table set for the zone number, figure out the easting value that + * should be added to the other, secondary easting value. + * + * @private + * @param {char} e The first letter from a two-letter MGRS 100´k zone. + * @param {number} set The MGRS table set for the zone number. + * @return {number} The easting value for the given letter and set. + */ +function getEastingFromChar(e, set) { + // colOrigin is the letter at the origin of the set for the + // column + var curCol = SET_ORIGIN_COLUMN_LETTERS.charCodeAt(set - 1); + var eastingValue = 100000.0; + var rewindMarker = false; + + while (curCol !== e.charCodeAt(0)) { + curCol++; + if (curCol === I) { curCol++; - if (curCol === I) { - curCol++; - } - if (curCol === O) { - curCol++; - } - if (curCol > Z) { - if (rewindMarker) { - throw new Error(`Bad character: ${e}`); - } - curCol = A; - rewindMarker = true; + } + if (curCol === O) { + curCol++; + } + if (curCol > Z) { + if (rewindMarker) { + throw ("Bad character: " + e); } - eastingValue += 100000; + curCol = A; + rewindMarker = true; } + eastingValue += 100000.0; + } - return eastingValue; - } - - /** - * Given the second letter from a two-letter MGRS 100k zone, and given the - * MGRS table set for the zone number, figure out the northing value that - * should be added to the other, secondary northing value. You have to - * remember that Northings are determined from the equator, and the vertical - * cycle of letters mean a 2000000 additional northing meters. This happens - * approx. every 18 degrees of latitude. This method does *NOT* count any - * additional northings. You have to figure out how many 2000000 meters need - * to be added for the zone letter of the MGRS coordinate. - * - * @private - * @param {string} n Second letter of the MGRS 100k zone - * @param {number} set The MGRS table set number, which is dependent on the - * UTM zone number. - * @return {number} The northing value for the given letter and set. - */ - function getNorthingFromChar(n, set) { - - if (n > 'V') { - throw new TypeError(`MGRSPoint given invalid Northing ${n}`); - } + return eastingValue; +} + +/** + * Given the second letter from a two-letter MGRS 100k zone, and given the + * MGRS table set for the zone number, figure out the northing value that + * should be added to the other, secondary northing value. You have to + * remember that Northings are determined from the equator, and the vertical + * cycle of letters mean a 2000000 additional northing meters. This happens + * approx. every 18 degrees of latitude. This method does *NOT* count any + * additional northings. You have to figure out how many 2000000 meters need + * to be added for the zone letter of the MGRS coordinate. + * + * @private + * @param {char} n Second letter of the MGRS 100k zone + * @param {number} set The MGRS table set number, which is dependent on the + * UTM zone number. + * @return {number} The northing value for the given letter and set. + */ +function getNorthingFromChar(n, set) { + + if (n > 'V') { + throw ("MGRSPoint given invalid Northing " + n); + } - // rowOrigin is the letter at the origin of the set for the - // column - let curRow = SET_ORIGIN_ROW_LETTERS.charCodeAt(set - 1); - let northingValue = 0; - let rewindMarker = false; + // rowOrigin is the letter at the origin of the set for the + // column + var curRow = SET_ORIGIN_ROW_LETTERS.charCodeAt(set - 1); + var northingValue = 0.0; + var rewindMarker = false; - while (curRow !== n.charCodeAt(0)) { + while (curRow !== n.charCodeAt(0)) { + curRow++; + if (curRow === I) { curRow++; - if (curRow === I) { - curRow++; - } - if (curRow === O) { - curRow++; - } - // fixing a bug making whole application hang in this loop - // when 'n' is a wrong character - if (curRow > V) { - if (rewindMarker) { // making sure that this loop ends - throw new Error(`Bad character: ${n}`); - } - curRow = A; - rewindMarker = true; - } - northingValue += 100000; - } - - return northingValue; - } - - /** - * The function getMinNorthing returns the minimum northing value of a MGRS - * zone. - * - * Ported from Geotrans' c Lattitude_Band_Value structure table. - * - * @private - * @param {string} zoneLetter The MGRS zone to get the min northing for. - * @return {number} - */ - function getMinNorthing(zoneLetter) { - let northing; - switch (zoneLetter) { - case 'C': - northing = 1100000; - break; - case 'D': - northing = 2000000; - break; - case 'E': - northing = 2800000; - break; - case 'F': - northing = 3700000; - break; - case 'G': - northing = 4600000; - break; - case 'H': - northing = 5500000; - break; - case 'J': - northing = 6400000; - break; - case 'K': - northing = 7300000; - break; - case 'L': - northing = 8200000; - break; - case 'M': - northing = 9100000; - break; - case 'N': - northing = 0; - break; - case 'P': - northing = 800000; - break; - case 'Q': - northing = 1700000; - break; - case 'R': - northing = 2600000; - break; - case 'S': - northing = 3500000; - break; - case 'T': - northing = 4400000; - break; - case 'U': - northing = 5300000; - break; - case 'V': - northing = 6200000; - break; - case 'W': - northing = 7000000; - break; - case 'X': - northing = 7900000; - break; - default: - northing = -1; } - if (northing >= 0) { - return northing; + if (curRow === O) { + curRow++; } - else { - throw new TypeError(`Invalid zone letter: ${zoneLetter}`); + // fixing a bug making whole application hang in this loop + // when 'n' is a wrong character + if (curRow > V) { + if (rewindMarker) { // making sure that this loop ends + throw ("Bad character: " + n); + } + curRow = A; + rewindMarker = true; } + northingValue += 100000.0; + } + return northingValue; +} + +/** + * The function getMinNorthing returns the minimum northing value of a MGRS + * zone. + * + * Ported from Geotrans' c Lattitude_Band_Value structure table. + * + * @private + * @param {char} zoneLetter The MGRS zone to get the min northing for. + * @return {number} + */ +function getMinNorthing(zoneLetter) { + var northing; + switch (zoneLetter) { + case 'C': + northing = 1100000.0; + break; + case 'D': + northing = 2000000.0; + break; + case 'E': + northing = 2800000.0; + break; + case 'F': + northing = 3700000.0; + break; + case 'G': + northing = 4600000.0; + break; + case 'H': + northing = 5500000.0; + break; + case 'J': + northing = 6400000.0; + break; + case 'K': + northing = 7300000.0; + break; + case 'L': + northing = 8200000.0; + break; + case 'M': + northing = 9100000.0; + break; + case 'N': + northing = 0.0; + break; + case 'P': + northing = 800000.0; + break; + case 'Q': + northing = 1700000.0; + break; + case 'R': + northing = 2600000.0; + break; + case 'S': + northing = 3500000.0; + break; + case 'T': + northing = 4400000.0; + break; + case 'U': + northing = 5300000.0; + break; + case 'V': + northing = 6200000.0; + break; + case 'W': + northing = 7000000.0; + break; + case 'X': + northing = 7900000.0; + break; + default: + northing = -1.0; } + if (northing >= 0.0) { + return northing; + } + else { + throw ("Invalid zone letter: " + zoneLetter); + } + +} - exports.forward = forward; - exports.getLetterDesignator = getLetterDesignator; - exports.inverse = inverse; - exports.toPoint = toPoint; +exports['default'] = mgrs; +exports.forward = forward; +exports.inverse = inverse; +exports.toPoint = toPoint; - Object.defineProperty(exports, '__esModule', { value: true }); +Object.defineProperty(exports, '__esModule', { value: true }); -})); +})));