// version beta-2007-12-21
function LatLonToUKNG(WGS84lat, WGS84lon, height, prec) 
{
  var deg2rad = Math.PI / 180;
  var rad2deg = 180.0 / Math.PI;
  var gLat = WGS84lat * deg2rad;
  var gLon = WGS84lon * deg2rad;
  // WGS84 to OSGB36 conversion first
  var a = 6378137;
  var e = 0.00669438037928458;
  var h = height;
  var a2 = 6377563.396;
  var e2 = 0.0066705397616;
  var xp = -446.448;
  var yp = 125.157;
  var zp = -542.06;
  var xr = -0.1502;
  var yr = -0.247;
  var zr = -0.8421;
  var s = 20.4894;
  var sf = s * 0.000001;
  var v = a / (Math.sqrt(1 - (e * (Math.sin(gLat) * Math.sin(gLat)))));
  var x = (v + h) * Math.cos(gLat) * Math.cos(gLon);
  var y = (v + h) * Math.cos(gLat) * Math.sin(gLon);
  var z = ((1 - e) * v + h) * Math.sin(gLat);
  var xrot = (xr / 3600) * deg2rad;
  var yrot = (yr / 3600) * deg2rad;
  var zrot = (zr / 3600) * deg2rad;
  var hx = x + (x * sf) - (y * zrot) + (z * yrot) + xp;
  var hy = (x * zrot) + y + (y * sf) - (z * xrot) + yp;
  var hz = (-1 * x * yrot) + (y * xrot) + z + (z * sf) + zp;
  var lon = Math.atan(hy / hx);
  var p = Math.sqrt((hx * hx) + (hy * hy));
  var lat = Math.atan(hz / (p * (1 - e2)));
  v = a2 / (Math.sqrt(1 - e2 * (Math.sin(lat) * Math.sin(lat))));
  var errval = 1.0;
  var lat0 = 0;
  while (errval > 0.001)
  {
    lat0 = Math.atan((hz + e2 * v * Math.sin(lat)) / p);
    errval = Math.abs(lat0 - lat);
    lat = lat0;
  }
  // now convert OSGB36 lat, lon (radians) to EN
  var phi = lat
  var lam = lon
  var a = 6377563.396;
  var b = 6356256.91;
  var e0 = 400000;
  var n0 = -100000;
  var f0 = 0.9996012717;
  var e2 = 0.0066705397616;
  var lam0 = -0.034906585039886591;
  var phi0 = 0.85521133347722145;
  var af0 = a * f0;
  var bf0 = b * f0;
  // easting
  var slat2 = Math.sin(phi) * Math.sin(phi);
  var nu = af0 / (Math.sqrt(1 - (e2 * (slat2))));
  var rho = (nu * (1 - e2)) / (1 - (e2 * slat2));
  var eta2 = (nu / rho) - 1;
  var p = lam - lam0;
  var IV = nu * Math.cos(phi);
  var clat3 = Math.pow(Math.cos(phi), 3);
  var tlat2 = Math.tan(phi) * Math.tan(phi);
  var V = (nu / 6) * clat3 * ((nu / rho) - tlat2);
  var clat5 = Math.pow(Math.cos(phi), 5);
  var tlat4 = Math.pow(Math.tan(phi), 4);
  var VI = (nu / 120) * clat5 * ((5 - (18 * tlat2)) 
   + tlat4 + (14 * eta2) - (58 * tlat2 * eta2));
  var E = e0 + (p * IV) + (Math.pow(p, 3) * V) + (Math.pow(p, 5) * VI);
  // northing
  var n = (af0 - bf0) / (af0 + bf0);
  var M = 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)))));
  var I = M + (n0);
  var II = (nu / 2) * Math.sin(phi) * Math.cos(phi);
  var III = ((nu / 24) * Math.sin(phi) * Math.pow(Math.cos(phi), 3)) 
   * (5 - Math.pow(Math.tan(phi), 2) + (9 * eta2));
  var IIIA = ((nu / 720) * Math.sin(phi) * clat5)
   * (61 - (58 * tlat2) + tlat4);
  var N = I + ((p * p) * II) + (Math.pow(p, 4) * III)
   + (Math.pow(p, 6) * IIIA);
  E = Math.round(E); // round to whole number of meters
  N = Math.round(N);
  // format as reference
  var e100k = Math.floor(E / 100000);
  var n100k = Math.floor(N / 100000);
  var c1 = (19 - n100k) - (19 - n100k) % 5 + Math.floor((e100k + 10) / 5);
  var c2 = (19 - n100k) * 5 % 25 + e100k % 5;
  if (c1 > 7) c1++; // I is not used
  if (c2 > 7) c2++;
  var c100k = String.fromCharCode(c1 + 'A'.charCodeAt(0),
   c2 + 'A'.charCodeAt(0));
  if (prec == 0) prec = 1;
  var digits = 5 - Math.log(prec) / Math.log(10);
  E = Math.floor((E % 100000) / prec);
  var cE = String(E);
  while (cE.length < digits)
  {
    cE = "0" + cE;
  } 
  N = Math.floor((N % 100000) / prec);
  var cN = String(N);
  while (cN.length < digits)
  {
    cN = "0" + cN;
  }
  var gridref = c100k + cE + cN;
  if (!(e100k >= 0 && e100k < 7 && n100k >= 0 && n100k < 13)) gridref = "";
  return (gridref);
}
