function NEtoLL(north, east)
{
// converts NGR easting and northing to lat, lon.
// input metres, output radians
var nX = Number(north);
var eX = Number(east);
var a = 6377563.396; // OSI semi-major
var b = 6356256.91; // OSI semi-minor
var e0 = 400000; // easting of false origin
var n0 = -100000; // northing of false origin
var f0 = 0.9996012717; // OSI scale factor on central meridian
var e2 = 0.0066705397616; // OSI eccentricity squared
var lam0 = -0.034906585039886591; // OSI false east
var phi0 = 0.85521133347722145; // OSI false north
var af0 = a * f0;
var bf0 = b * f0;
var n = (af0 - bf0) / (af0 + bf0);
var Et = east - e0;
var phid = InitialLat(north, n0, af0, phi0, n, bf0);
var nu = af0 / (Math.sqrt(1 - (e2 * (Math.sin(phid) * Math.sin(phid)))));
var rho = (nu * (1 - e2)) / (1 - (e2 * (Math.sin(phid)) * (Math.sin(phid))));
var eta2 = (nu / rho) - 1;
var tlat2 = Math.tan(phid) * Math.tan(phid);
var tlat4 = Math.pow(Math.tan(phid), 4);
var tlat6 = Math.pow(Math.tan(phid), 6);
var clatm1 = Math.pow(Math.cos(phid), -1);
var VII = Math.tan(phid) / (2 * rho * nu);
var VIII = (Math.tan(phid) / (24 * rho * (nu * nu * nu))) * (5 + (3 * tlat2) + eta2 - (9 * eta2 * tlat2));
var IX = ((Math.tan(phid)) / (720 * rho * Math.pow(nu, 5))) * (61 + (90 * tlat2) + (45 * Math.pow(Math.tan(phid), 4) ));
var phip = (phid - ((Et * Et) * VII) + (Math.pow(Et, 4) * VIII) - (Math.pow(Et, 6) * IX));
var X = Math.pow(Math.cos(phid), -1) / nu;
var XI = (clatm1 / (6 * (nu * nu * nu))) * ((nu / rho) + (2 * (tlat2)));
var XII = (clatm1 / (120 * Math.pow(nu, 5))) * (5 + (28 * tlat2) + (24 * tlat4));
var XIIA = clatm1 / (5040 * Math.pow(nu, 7)) * (61 + (662 * tlat2) + (1320 * tlat4) + (720 * tlat6));
var lambdap = (lam0 + (Et * X) - ((Et * Et * Et) * XI) + (Math.pow(Et, 5) * XII) - (Math.pow(Et, 7) * XIIA));
return { latitude: phip, longitude: lambdap };
}
function Marc(bf0, n, phi0, phi)
{
var Marc = bf0 * (((1 + n + ((5 / 4) * (n * n)) + ((5 / 4) * (n * n * n))) * (phi - phi0))
- (((3 * n) + (3 * (n * n)) + ((21 / 8) * (n * n * n))) * (Math.sin(phi - phi0)) * (Math.cos(phi + phi0)))
+ ((((15 / 8) * (n * n)) + ((15 / 8) * (n * n * n))) * (Math.sin(2 * (phi - phi0))) * (Math.cos(2 * (phi + phi0))))
- (((35 / 24) * (n * n * n)) * (Math.sin(3 * (phi - phi0))) * (Math.cos(3 * (phi + phi0)))));
return Marc;
}
function InitialLat(north, n0, af0, phi0, n, bf0)
{
var phi1 = ((north - n0) / af0) + phi0;
var M = Marc(bf0, n, phi0, phi1);
var phi2 = ((north - n0 - M) / af0) + phi1;
for(var i = 0; (Math.abs(north - n0 - M) > 0.00001) && (i < 20); i++) {
phi2 = ((north - n0 - M) / af0) + phi1;
M = Marc(bf0, n, phi0, phi2);
phi1 = phi2;
}
return phi2;
}
function ngr2ings(ngr)
{
var east = 0;
var north = 0;
if(/^W[AV][0-]/.test(ngr)) {
var nci;
var eci = "5" + ngr.substring(2,7);
if (ngr.charAt(1) == "V")
nci = "54" + ngr.substring(7,12);
else
nci = "55" + ngr.substring(7,12);
east = Number(eci);
north = Number(nci);
} else {
north = Number(ngr.substring(7));
east = Number(ngr.substring(2,7));
var t1 = ngr.charCodeAt(0) - 65;
if(t1 < 0) // probably a space, so Irish
t1 = 18; // S assumed for Irish (83-65)
if(t1 > 8)
t1 -= 1;
var t2 = Math.floor(t1 / 5);
north = north + 500000 * (3 - t2);
east = east + 500000 * (t1 - 5 * t2 - 2);
t1 = ngr.charCodeAt(1) - 65;
if (t1 > 8)
t1 -= 1;
t2 = Math.floor(t1 / 5);
north = north + 100000 * ( 4 - t2);
east = east + 100000 * ( t1 - 5 * t2);
}
return {north: north, east: east};
}
function ngr2ll(ngr)
{
var t;
ngr = ngr.replace(/\s+/, '');
switch(ngr.length) {
case 4:
t = ngr.substring(0,3) + "0000" + ngr.substring(3) + "0000";
break;
case 6:
t = ngr.substring(0,4) + "000" + ngr.substring(4) + "000";
break;
case 8:
t = ngr.substring(0,5) + "00" + ngr.substring(5) + "00";
break;
case 10:
t = ngr.substring(0,6) + "0" + ngr.substring(6) + "0";
break;
case 12:
t = ngr;
break;
default:
return {latitude: 0, longitude: 0};
}
ngr = t;
var ne = ngr2ings(ngr);
var ll = NEtoLL(ne.north, ne.east);
//var deg2rad = Math.PI / 180;
var rad2deg = 180.0 / Math.PI;
return {
latitude: ll.latitude * rad2deg,
longitude: ll.longitude * rad2deg
};
}