1/// 2/// A UTM -> Lat/Long (or vice versa) converter adapted from the script used at 3/// http://www.uwgb.edu/dutchs/UsefulData/ConvertUTMNoOZ.HTM 4/// I've taken the calculations portion of his script and turned it into a singleton 5/// javascript object so that it is no longer dependent on the controls used on the page 6/// to use this script, call setDatum with the index of the datum to be used and then 7/// call the various conversion functions latLngToUtm(), utmToLatLng() or natoToLatLng(), utmToNato(), natoToUtm() 8/// to convert between the various coordinate systems, hopefully accurately! 9/// 10/// NOTE: no attempt is made to compensate for the irregular grid in the area around the southern coast of Norway and 11/// Svalbard (zones 32V and 31X, 33X, 35X and 37X) because of this results returned for NATO coordinates for lat/lng or 12/// UTM values located in these regions will not be correct. 13/// 14var utmconv = { 15 // 16 // constants taken from or calculated from the datum 17 // 18 a: 0, // equatorial radius in meters 19 f: 0, // polar flattening 20 b: 0, // polar radius in meters 21 e: 0, // eccentricity 22 e0: 0, // e' 23 24 25 // 26 // constants used in calculations 27 // 28 k: 1, 29 k0: 0.9996, 30 drad: Math.PI / 180, 31 32 digraphLettersE: "ABCDEFGHJKLMNPQRSTUVWXYZ", 33 digraphLettersN: "ABCDEFGHJKLMNPQRSTUV", 34 digraphLettersAll: "ABCDEFGHJKLMNPQRSTUVABCDEFGHJKLMNPQRSTUVABCDEFGHJKLMNPQRSTUVABCDEFGHJKLMNPQRSTUVABCDEFGHJKLMNPQRSTUVABCDEFGHJKLMNPQRSTUVABCDEFGHJKLMNPQRSTUVABCDEFGHJKLMNPQRSTUVABCDEFGHJKLMNPQRSTUVABCDEFGHJKLMNPQRSTUV", 35 datumTable: [ 36 { eqRad: 6378137.0, flat: 298.2572236 }, // WGS 84 37 { eqRad: 6378137.0, flat: 298.2572236 }, // NAD 83 38 { eqRad: 6378137.0, flat: 298.2572215 }, // GRS 80 39 { eqRad: 6378135.0, flat: 298.2597208 }, // WGS 72 40 { eqRad: 6378160.0, flat: 298.2497323 }, // Austrailian 1965 41 { eqRad: 6378245.0, flat: 298.2997381 }, // Krasovsky 1940 42 { eqRad: 6378206.4, flat: 294.9786982 }, // North American 1927 43 { eqRad: 6378388.0, flat: 296.9993621 }, // International 1924 44 { eqRad: 6378388.0, flat: 296.9993621 }, // Hayford 1909 45 { eqRad: 6378249.1, flat: 293.4660167 }, // Clarke 1880 46 { eqRad: 6378206.4, flat: 294.9786982 }, // Clarke 1866 47 { eqRad: 6377563.4, flat: 299.3247788 }, // Airy 1830 48 { eqRad: 6377397.2, flat: 299.1527052 }, // Bessel 1841 49 { eqRad: 6377276.3, flat: 300.8021499 } // Everest 1830 50 ], 51 52 /// 53 /// calculate constants used for doing conversions using a given map datum 54 /// 55 setDatum: function (index) { 56 var datum = this.datumTable[index]; 57 this.a = datum.eqRad; 58 this.f = 1 / datum.flat; 59 this.b = this.a * (1 - this.f); // polar radius 60 this.e = Math.sqrt(1 - Math.pow(this.b, 2) / Math.pow(this.a, 2)); 61 this.e0 = this.e / Math.sqrt(1 - Math.pow(this.e, 1)); 62 }, 63 64 /// 65 /// given a lat/lng pair, returns both global UTM and NATO UTM in the following form: 66 /// utm: 67 /// { 68 /// global: { northing: n, easting: e, zone: z, southern: x }, 69 /// nato: { northing: n, easting: e, latzone: z0, lngzone: z1, digraph: xx } 70 /// } 71 /// 72 /// this function assumes that all data validation has been performed prior to calling 73 /// it. 74 /// 75 latLngToUtm: function (lat, lngd) { 76 var phi = lat * this.drad; // convert latitude to radians 77 var lng = lngd * this.drad; // convert longitude to radians 78 var utmz = 1 + Math.floor((lngd + 180) / 6); // longitude to utm zone 79 var zcm = 3 + 6 * (utmz - 1) - 180; // central meridian of a zone 80 var latz = 0; // this gives us zone A-B for below 80S 81 var esq = (1 - (this.b / this.a) * (this.b / this.a)); 82 var e0sq = this.e * this.e / (1 - Math.pow(this.e, 2)); 83 var M = 0; 84 85 // convert latitude to latitude zone for nato 86 if (lat > -80 && lat < 72) { 87 latz = Math.floor((lat + 80) / 8) + 2; // zones C-W in this range 88 } if (lat > 72 && lat < 84) { 89 latz = 21; // zone X 90 } else if (lat > 84) { 91 latz = 23; // zone Y-Z 92 } 93 94 var N = this.a / Math.sqrt(1 - Math.pow(this.e * Math.sin(phi), 2));
95 var T = Math.pow(Math.tan(phi), 2); 96 var C = e0sq * Math.pow(Math.cos(phi), 2); 97 var A = (lngd - zcm) * this.drad * Math.cos(phi); 98 99 // calculate M (USGS style) 100 M = phi * (1 - esq * (1 / 4 + esq * (3 / 64 + 5 * esq / 256))); 101 M = M - Math.sin(2 * phi) * (esq * (3 / 8 + esq * (3 / 32 + 45 * esq / 1024))); 102 M = M + Math.sin(4 * phi) * (esq * esq * (15 / 256 + esq * 45 / 1024)); 103 M = M - Math.sin(6 * phi) * (esq * esq * esq * (35 / 3072)); 104 M = M * this.a; //Arc length along standard meridian 105 106 M0 = 0; // if another point of origin is used than the equator 107 108 // now we are ready to calculate the UTM values... 109 // first the easting 110 var x = this.k0 * N * A * (1 + A * A * ((1 - T + C) / 6 + A * A * (5 - 18 * T + T * T + 72 * C - 58 * e0sq) / 120)); //Easting relative to CM 111 x = x + 500000; // standard easting 112 113 // now the northing 114 y = this.k0 * (M - M0 + N * Math.tan(phi) * (A * A * (1 / 2 + A * A * ((5 - T + 9 * C + 4 * C * C) / 24 + A * A * (61 - 58 * T + T * T + 600 * C - 330 * e0sq) / 720)))); // first from the equator 115 yg = y + 10000000; //yg = y global, from S. Pole 116 if (y < 0) { 117 y = 10000000 + y; // add in false northing if south of the equator 118 } 119 120 var digraph = this.makeDigraph(x, y, utmz); 121 var rv = { 122 global: { 123 easting: Math.round(10*(x))/10, 124 northing: Math.round(10*y)/10, 125 zone: utmz, 126 southern: phi < 0 127 }, 128 nato: { 129 easting: Math.round(10*(x-100000*Math.floor(x/100000)))/10, 130 northing: Math.round(10*(y-100000*Math.floor(y/100000)))/10, 131 latZone: this.digraphLettersN[latz], 132 lngZone: utmz, 133 digraph: digraph 134 } 135 } 136 137 return rv; 138 }, 139 140 /// 141 /// convert a set of global UTM coordinates to lat/lng returned as follows 142 /// 143 /// { lat: y, lng: x } 144 /// 145 /// inputs: 146 /// x: easting 147 /// y: northing 148 /// utmz: utm zone 149 /// southern: bool indicating coords are in southern hemisphere 150 /// 151 utmToLatLng: function(x, y, utmz, southern) { 152 var esq = (1 - (this.b / this.a) * (this.b / this.a)); 153 var e0sq = this.e * this.e / (1 - Math.pow(this.e, 2)); 154 var zcm = 3 + 6 * (utmz - 1) - 180; // Central meridian of zone 155 var e1 = (1 - Math.sqrt(1 - Math.pow(this.e, 2))) / (1 + Math.sqrt(1 - Math.pow(this.e, 2))); 156 var M0 = 0; 157 var M = 0; 158 159 if (!southern) 160 M = M0 + y / this.k0; // Arc length along standard meridian. 161 else 162 M = M0 + (y - 10000000) / this.k; 163 164 var mu = M / (this.a * (1 - esq * (1 / 4 + esq * (3 / 64 + 5 * esq / 256)))); 165 var phi1 = mu + e1 * (3 / 2 - 27 * e1 * e1 / 32) * Math.sin(2 * mu) + e1 * e1 * (21 / 16 - 55 * e1 * e1 / 32) * Math.sin(4 * mu); //Footprint Latitude 166 phi1 = phi1 + e1 * e1 * e1 * (Math.sin(6 * mu) * 151 / 96 + e1 * Math.sin(8 * mu) * 1097 / 512); 167 var C1 = e0sq * Math.pow(Math.cos(phi1), 2); 168 var T1 = Math.pow(Math.tan(phi1), 2); 169 var N1 = this.a / Math.sqrt(1 - Math.pow(this.e * Math.sin(phi1), 2)); 170 var R1 = N1 * (1 - Math.pow(this.e, 2)) / (1 - Math.pow(this.e * Math.sin(phi1), 2)); 171 var D = (x - 500000) / (N1 * this.k0); 172 var phi = (D * D) * (1 / 2 - D * D * (5 + 3 * T1 + 10 * C1 - 4 * C1 * C1 - 9 * e0sq) / 24); 173 phi = phi + Math.pow(D, 6) * (61 + 90 * T1 + 298 * C1 + 45 * T1 * T1 - 252 * e0sq - 3 * C1 * C1) / 720; 174 phi = phi1 - (N1 * Math.tan(phi1) / R1) * phi; 175 176 var lat = Math.floor(1000000 * phi / this.drad) / 1000000; 177 var lng = D * (1 + D * D * ((-1 - 2 * T1 - C1) / 6 + D * D * (5 - 2 * C1 + 28 * T1 - 3 * C1 * C1 + 8 * e0sq + 24 * T1 * T1) / 120)) / Math.cos(phi1); 178 lng = lngd = zcm + lng / this.drad; 179 180 return { lat: lat, lng: lng }; 181 }, 182 183 /// 184 /// takes a set of NATO style UTM coordinates and converts them to a lat/lng pair. 185 /// 186 /// { lat: y, lng: x } 187 ///
188 /// inputs: 189 /// utme: easting 190 /// utmn: northing 191 /// utmz: longitudinal zone 192 /// latz: character representing latitudinal zone 193 /// digraph: string representing grid 194 /// 195 /// 196 natoToLatLng: function (utme, utmn, utmz, latz, digraph) { 197 var coords = this.natoToUtm(utme, utmn, utmz, latz, digraph); 198 return this.utmToLatLng(coords.easting, coords.northing, coords.zone, coords.southern); 199 }, 200 201 /// 202 /// convert a set of nato coordinates to the global system. returns a structure 203 /// 204 /// { norhting: y, easting: x, zone: zone, southern: hemisphere } 205 /// 206 /// inputs: 207 /// utme: easting 208 /// utmn: northing 209 /// utmz: longitudinal zone 210 /// latz: character representing latitudinal zone 211 /// digraph: string representing grid 212 /// 213 /// checks for digraph validity 214 /// 215 natoToUtm: function (utme, utmn, utmz, latz, digraph) { 216 latz = latz.toUpperCase(); 217 digraph = digraph.toUpperCase(); 218 219 var eltr = digraph.charAt(0); 220 var nltr = digraph.charAt(1); 221 222 /// 223 /// make sure the digraph is consistent 224 /// 225 if (nltr == "I" || eltr == "O") 226 throw "I and O are not legal digraph characters"; 227 228 if (nltr == "W" || nltr == "X" || nltr == "Y" || nltr == "Z") 229 throw "W, X, Y and Z are not legal second characters in a digraph"; 230 231 var eidx = this.digraphLettersE.indexOf(eltr); 232 var nidx = this.digraphLettersN.indexOf(nltr); 233 234 if (utmz / 2 == Math.floor(utmz / 2)) { 235 nidx -= 5; // correction for even numbered zones 236 } 237 238 var ebase = 100000*(1 + eidx - 8 * Math.floor(eidx / 8)); 239 240 var latBand = this.digraphLettersE.indexOf(latz); 241 var latBandLow = 8 * latBand - 96; 242 var latBandHigh = 8 * latBand - 88; 243 244 if (latBand < 2) { 245 latBandLow = -90; 246 latBandHigh = -80; 247 } else if (latBand == 21) { 248 latBandLow = 72; 249 latBandHigh = 84; 250 } else if (latBand > 21) { 251 latBandLow = 84; 252 latBandHigh = 90; 253 } 254 255 var lowLetter = Math.floor(100 + 1.11 * latBandLow); 256 var highLetter = Math.round(100 + 1.11 * latBandHigh); 257 var latBandLetters = null; 258 if (utmz / 2 == Math.floor(utmz / 2)) { 259 latBandLetters = this.digraphLettersAll.slice(lowLetter + 5, highLetter + 5); 260 } else { 261 latBandLetters = this.digraphLettersAll.slice(lowLetter, highLetter); 262 } 263 var nbase = 100000 * (lowLetter + latBandLetters.indexOf(nltr)); 264 265 var x = ebase + utme; 266 var y = nbase + utmn; 267 if (y > 10000000) { 268 y = y - 10000000; 269 } 270 if (nbase >= 10000000) { 271 y = nbase + utmn - 10000000; 272 } 273 274 var southern = nbase < 10000000; 275 276 return { northing: y, easting: x, zone: utmz, southern: southern }; 277 }, 278 279 /// 280 /// returns a set of nato coordinates from a set of global UTM coordinates 281 /// return is an object with the following structure: 282 /// { northing: n, easting: e, latZone: z0, lngZone: z1, digraph: xx } 283 /// 284 /// inputs: 285 /// x: easting 286 /// y: northing 287 /// utmz: the utm zone 288 /// southern: hemisphere indicator 289 /// 290 utmToNato: function (x, y, utmz, southern) { 291 var esq = (1 - (this.b / this.a) * (this.b / this.a)); 292 var e0sq = this.e * this.e / (1 - Math.pow(this.e, 2)); 293 var e1 = (1 - Math.sqrt(1 - Math.pow(this.e, 2))) / (1 + Math.sqrt(1 - Math.pow(this.e, 2))); 294 var M0 = 0; 295 296 if (!southern) 297 M = M0 + y / this.k0; // Arc length along standard meridian. 298 else 299 M = M0 + (y - 10000000) / this.k; 300 301 // 302 // calculate the latitude so that we can derive the latitude zone 303 // 304 var mu = M / (this.a * (1 - esq * (1 / 4 + esq * (3 / 64 + 5 * esq / 256)))); 305 var phi1 = mu + e1 * (3 / 2 - 27 * e1 * e1 / 32) * Math.sin(2 * mu) + e1 * e1 * (21 / 16 - 55 * e1 * e1 / 32) * Math.sin(4 * mu); //Footprint Latitude 306 phi1 = phi1 + e1 * e1 * e1 * (Math.sin(6 * mu) * 151 / 96 + e1 * Math.sin(8 * mu) * 1097 / 512); 307 var C1 = e0sq * Math.pow(Math.cos(phi1), 2); 308 var T1 = Math.pow(Math.tan(phi1), 2); 309 var N1 = this.a / Math.sqrt(1 - Math.pow(this.e * Math.sin(phi1), 2));
310 var R1 = N1 * (1 - Math.pow(this.e, 2)) / (1 - Math.pow(this.e * Math.sin(phi1), 2)); 311 var D = (x - 500000) / (N1 * this.k0); 312 var phi = (D * D) * (1 / 2 - D * D * (5 + 3 * T1 + 10 * C1 - 4 * C1 * C1 - 9 * e0sq) / 24); 313 phi = phi + Math.pow(D, 6) * (61 + 90 * T1 + 298 * C1 + 45 * T1 * T1 - 252 * e0sq - 3 * C1 * C1) / 720; 314 phi = phi1 - (N1 * Math.tan(phi1) / R1) * phi; 315 var lat = Math.floor(1000000 * phi / this.drad) / 1000000; 316 317 // convert latitude to latitude zone for nato 318 if (lat > -80 && lat < 72) { 319 latz = Math.floor((lat + 80) / 8) + 2; // zones C-W in this range 320 } if (lat > 72 && lat < 84) { 321 latz = 21; // zone X 322 } else if (lat > 84) { 323 latz = 23; // zone Y-Z 324 } 325 326 var digraph = this.makeDigraph(x, y, utmz); 327 x = Math.round(10 * (x - 100000 * Math.floor(x / 100000))) / 10; 328 y = Math.round(10 * (y - 100000 * Math.floor(y / 100000))) / 10; 329 330 return { 331 easting: x, 332 northing: y, 333 latZone: this.digraphLettersN[latz], 334 lngZone: utmz, 335 digraph: digraph 336 } 337 }, 338 339 /// 340 /// create a nato grid digraph. 341 /// 342 /// inputs: 343 /// x: easting 344 /// y: northing 345 /// utmz: utm zone 346 /// 347 makeDigraph: function (x, y, utmz) { 348 // 349 // first get the east digraph letter 350 // 351 var letter = Math.floor((utmz - 1) * 8 + (x) / 100000); 352 letter = letter - 24 * Math.floor(letter / 24) - 1; 353 var digraph = this.digraphLettersE.charAt(letter); 354 355 letter = Math.floor(y / 100000); 356 if (utmz / 2 == Math.floor(utmz / 2)) { letter = letter + 5; } 357 letter = letter - 20 * Math.floor(letter / 20); 358 digraph = digraph + this.digraphLettersN.charAt(letter); 359 360 return digraph; 361 } 362} 363window.utmconv = utmconv;
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.