﻿//--------------------------------------------------------------------------
// jscoord.js
// (c) 2005 Jonathan Stott
// Amended 2006 CD9 Design
//--------------------------------------------------------------------------

function LatLng(lat, lng) {
    this.lat = lat;
    this.lng = lng;
    this.OSGB36ToWGS84 = OSGB36ToWGS84;
}

function OSRef(easting, northing) {
    this.easting = easting;
    this.northing = northing;
    this.toLatLng = OSRefToLatLng;
}

function RefEll(maj, min) {
    this.maj = maj;
    this.min = min;
    this.ecc = ((maj * maj) - (min * min)) / (maj * maj);
}

//--------------------------------------------------------------------------
// Mathematical Functions

function sinSquared(x) {
    return Math.sin(x) * Math.sin(x);
}

function cosSquared(x) {
    return Math.cos(x) * Math.cos(x);
}

function tanSquared(x) {
    return Math.tan(x) * Math.tan(x);
}

function sec(x) {
    return 1.0 / Math.cos(x);
}

function deg2rad(x) {
    return x * (Math.PI / 180);
}

function rad2deg(x) {
    return x * (180 / Math.PI);
}

function chr(x) {
    var h = x.toString(16);
    if (h.length == 1)
        h = "0" + h;
    h = "%" + h;
    return unescape(h);
}

function ord(x) {
    var c = x.charAt(0);
    var i;
    for (i = 0; i < 256; ++i) {
        var h = i.toString(16);
        if (h.length == 1)
            h = "0" + h;
        h = "%" + h;
        h = unescape(h);
        if (h == c)
            break;
    }
    return i;
}

//--------------------------------------------------------------------------
// Conversion Functions

function OSGB36ToWGS84() {
    var airy1830 = new RefEll(6377563.396, 6356256.909);
    var a = airy1830.maj;
    var b = airy1830.min;
    var eSquared = airy1830.ecc;
    var phi = deg2rad(this.lat);
    var lambda = deg2rad(this.lng);
    var v = a / (Math.sqrt(1 - eSquared * sinSquared(phi)));
    var H = 0; // height
    var x = (v + H) * Math.cos(phi) * Math.cos(lambda);
    var y = (v + H) * Math.cos(phi) * Math.sin(lambda);
    var z = ((1 - eSquared) * v + H) * Math.sin(phi);

    var tx = 446.448;
    var ty = -124.157;
    var tz = 542.060;
    var s = -0.0000204894;
    var rx = deg2rad(0.00004172222);
    var ry = deg2rad(0.00006861111);
    var rz = deg2rad(0.00023391666);

    var xB = tx + (x * (1 + s)) + (-rx * y) + (ry * z);
    var yB = ty + (rz * x) + (y * (1 + s)) + (-rx * z);
    var zB = tz + (-ry * x) + (rx * y) + (z * (1 + s));

    var wgs84 = new RefEll(6378137.000, 6356752.3141);
    a = wgs84.maj;
    b = wgs84.min;
    eSquared = wgs84.ecc;

    var lambdaB = rad2deg(Math.atan(yB / xB));
    var p = Math.sqrt((xB * xB) + (yB * yB));
    var phiN = Math.atan(zB / (p * (1 - eSquared)));
    for (var i = 1; i < 10; i++) {
        v = a / (Math.sqrt(1 - eSquared * sinSquared(phiN)));
        phiN1 = Math.atan((zB + (eSquared * v * Math.sin(phiN))) / p);
        phiN = phiN1;
    }

    var phiB = rad2deg(phiN);

    this.lat = phiB;
    this.lng = lambdaB;
}


function OSRefToLatLng() {
    var airy1830 = new RefEll(6377563.396, 6356256.909);
    var OSGB_F0 = 0.9996012717;
    var N0 = -100000.0;
    var E0 = 400000.0;
    var phi0 = deg2rad(49.0);
    var lambda0 = deg2rad(-2.0);
    var a = airy1830.maj;
    var b = airy1830.min;
    var eSquared = airy1830.ecc;
    var phi = 0.0;
    var lambda = 0.0;
    var E = this.easting;
    var N = this.northing;
    var n = (a - b) / (a + b);
    var M = 0.0;
    var phiPrime = ((N - N0) / (a * OSGB_F0)) + phi0;
    do {
        M =
      (b * OSGB_F0)
        * (((1 + n + ((5.0 / 4.0) * n * n) + ((5.0 / 4.0) * n * n * n))
          * (phiPrime - phi0))
          - (((3 * n) + (3 * n * n) + ((21.0 / 8.0) * n * n * n))
            * Math.sin(phiPrime - phi0)
            * Math.cos(phiPrime + phi0))
          + ((((15.0 / 8.0) * n * n) + ((15.0 / 8.0) * n * n * n))
            * Math.sin(2.0 * (phiPrime - phi0))
            * Math.cos(2.0 * (phiPrime + phi0)))
          - (((35.0 / 24.0) * n * n * n)
            * Math.sin(3.0 * (phiPrime - phi0))
            * Math.cos(3.0 * (phiPrime + phi0))));
        phiPrime += (N - N0 - M) / (a * OSGB_F0);
    } while ((N - N0 - M) >= 0.001);
    var v = a * OSGB_F0 * Math.pow(1.0 - eSquared * sinSquared(phiPrime), -0.5);
    var rho =
    a
      * OSGB_F0
      * (1.0 - eSquared)
      * Math.pow(1.0 - eSquared * sinSquared(phiPrime), -1.5);
    var etaSquared = (v / rho) - 1.0;
    var VII = Math.tan(phiPrime) / (2 * rho * v);
    var VIII =
    (Math.tan(phiPrime) / (24.0 * rho * Math.pow(v, 3.0)))
      * (5.0
        + (3.0 * tanSquared(phiPrime))
        + etaSquared
        - (9.0 * tanSquared(phiPrime) * etaSquared));
    var IX =
    (Math.tan(phiPrime) / (720.0 * rho * Math.pow(v, 5.0)))
      * (61.0
        + (90.0 * tanSquared(phiPrime))
        + (45.0 * tanSquared(phiPrime) * tanSquared(phiPrime)));
    var X = sec(phiPrime) / v;
    var XI =
    (sec(phiPrime) / (6.0 * v * v * v))
      * ((v / rho) + (2 * tanSquared(phiPrime)));
    var XII =
    (sec(phiPrime) / (120.0 * Math.pow(v, 5.0)))
      * (5.0
        + (28.0 * tanSquared(phiPrime))
        + (24.0 * tanSquared(phiPrime) * tanSquared(phiPrime)));
    var XIIA =
    (sec(phiPrime) / (5040.0 * Math.pow(v, 7.0)))
      * (61.0
        + (662.0 * tanSquared(phiPrime))
        + (1320.0 * tanSquared(phiPrime) * tanSquared(phiPrime))
        + (720.0
          * tanSquared(phiPrime)
          * tanSquared(phiPrime)
          * tanSquared(phiPrime)));
    phi =
    phiPrime
      - (VII * Math.pow(E - E0, 2.0))
      + (VIII * Math.pow(E - E0, 4.0))
      - (IX * Math.pow(E - E0, 6.0));
    lambda =
    lambda0
      + (X * (E - E0))
      - (XI * Math.pow(E - E0, 3.0))
      + (XII * Math.pow(E - E0, 5.0))
      - (XIIA * Math.pow(E - E0, 7.0));


    // create new object to return and shift to WGS84 system	 	
    var pLatLng = new LatLng(rad2deg(phi), rad2deg(lambda)); ;
    pLatLng.OSGB36ToWGS84();
    return pLatLng;
}



