<!--
// JavaScript Document
/*
    Location details for Barton
    OSGB36
    TA 02963 22784
    
    WGS84 
    W 00 26 26
    N 53 41 28
    
    functions: ecc, ll_to_cartesin, cartesian_to_ll,
               switch_ellisoids, wgs84_osgb36, 
               find_gridsquare and ngr_data
    are adapted from http://www.carabus.co.uk/lltongr.html
    
    dms_to_dec concatenating and giving two decimal points.
*/
var deg2rad = Math.PI / 180;
var rad2deg = 180.0 / Math.PI;
function DegreesMinutesSeconds (pos, deg, min, sec) {
   this.positive = Number(pos);
   this.degrees = Number(deg);
   this.minutes = Number(min);
   this.seconds = Number(sec);
}
function DMSCoordinate (lat_dms_obj, lon_dms_obj) {
   this.lat_dms_obj = lat_dms_obj;
   this.lon_dms_obj = lon_dms_obj;
}
function DegreesDecimalCoordinate (lat, lon) {
   this.latitude = Number(lat);
   this.longitude = Number(lon);
   this.asString = function() {
      return 'lat:'+this.latitude + ' lon:'+this.longitude;
   };
}
function GridReference (eing, ning) {
   this.easting = Number(eing);
   this.northing = Number(ning);
   this.accuracy = 1;
}
function NationalGridReference (sq, eing, ning) {
   this.square = sq.toUpperCase();
   this.easting = Number(eing);
   this.northing = Number(ning);
   this.accuracy = 1;
   this.asString = function() {
      var e,n;
      var length = 5 - (String(this.accuracy).length - 1);
      e = formatToXPlaces(Math.floor(this.easting / this.accuracy),length,0);
      n = formatToXPlaces(Math.floor(this.northing / this.accuracy),length,0);
      return this.square + e + n;
   };
}
function CartesianCoordinate (x, y, z) {
    this.x = Number(x);
    this.y = Number(y);
    this.z = Number(z);
}
function Extents (top,left,bottom,right) {
   this.top = top;
   this.left = left;
   this.bottom = bottom;
   this.right = right;
}

function ecc(major, minor) {
	return (major*major - minor*minor) / (major*major);
}
/*
    var min_AXIS = 6356256.910;
    var maj_AXIS = 6377563.396;
    ECCENTRIC = ecc(maj_AXIS, min_AXIS);
*/
function ll_to_cartesian(lat, lon, axis, ecc, height) {
    var v = axis / (Math.sqrt (1 - ecc * (Math.pow (Math.sin(lat), 2))));
    var x = (v + height) * Math.cos(lat) * Math.cos(lon);
    var y = (v + height) * Math.cos(lat) * Math.sin(lon);
    var z = ((1 - ecc) * v + height) * Math.sin(lat);
    var cart = new CartesianCoordinate (x, y, z );
    return (cart);
}
function cartesian_to_ll(x, y, z, ecc, axis) {
    var lon = Math.atan(y / x);
    var p = Math.sqrt((x * x) + (y * y));
    var lat = Math.atan(z / (p * (1 - ecc)));
    var v = axis / (Math.sqrt(1 - ecc * (Math.sin(lat) * Math.sin(lat))));
    var errvalue = 1.0;
    var lat0 = 0;
    while (errvalue > 0.001)
    {
      lat0 = Math.atan((z + ecc * v * Math.sin(lat)) / p);  
      errvalue = Math.abs(lat0 - lat);
      lat=lat0;
    }
    var height = p / Math.cos(lat) - v;
    var geo = new DegreesDecimalCoordinate(lat, lon);
    return(geo);
}
function switch_ellipsoids  (dd_coord_obj) {
    var lat = dd_coord_obj.latitude;
    var lon = dd_coord_obj.longitude;
    lat *= deg2rad;  // GRS80/WGS84 input in radians
    lon *= deg2rad;  // ditto
    var axis = 6378137.0;         // GRS80/WGS84 major axis
    var ecc = 0.00669438;         // ditto eccentricity  
    var height = 0;               // height above datum
    var cart = ll_to_cartesian(lat, lon, axis, ecc, height);
    var x = cart.x - 371;
    var y = cart.y + 112;
    var z = cart.z - 434;
    axis = 6377563.396;
    ecc = 0.00667054;
    geo = cartesian_to_ll(x, y, z, ecc, axis);
    lat = geo.latitude * rad2deg; 
    lon = geo.longitude * rad2deg;//alert ('lat:' + lat + '\nlon:' + lon);
    var dd_coord = new DegreesDecimalCoordinate(lat,lon);
    return (dd_coord);
}
function wgs84_to_osgb36(dd_coord_obj) {
    dd_coord_obj = switch_ellipsoids(dd_coord_obj);
    var lat = dd_coord_obj.latitude;
    var lon = dd_coord_obj.longitude;
    var phip = lat * deg2rad;      // convert latitude to radians
    var lambdap = lon * deg2rad;   // convert longitude to radians
    var AXIS = 6377563.396;        // the Airy major axis
    var ECCENTRIC = 0.00667054;    // Airy eccentricity
    var LAT_ORIG = 49 * deg2rad;   
    var lon_ORIG = -2 * deg2rad;
    var FALSE_EAST = 400000.0;
    var FALSE_NORTH = -100000.0;
    var SCALE = 0.9996012717;
    var east = '';                 // variable to hold NGR Eastings
    var north = '';                 // variable to hold NGR Northings
    //
    // The five uncommented constants relate to the British National Grid. This has a
    // reference point (origin) based on 49 degrees north, 2 degrees west (west is negative). 
    // The FALSE_EAST and FALSE_NORTH are just offsets to ensure that the grid reference is
    // always positive.
    // Now we convert the latitude and longitude to National Grid Northings and Eastings.
    //
    var j = (lambdap - lon_ORIG) * Math.cos(phip);
    var nsqd = ECCENTRIC * (Math.cos(phip) * Math.cos(phip)) / (1 - ECCENTRIC);
    var tsqd = Math.pow(Math.sin(phip) / (Math.cos(phip)), 2);
    var up = AXIS / Math.sqrt(1 - (ECCENTRIC * (Math.sin(phip) * Math.sin(phip))));
    var p = ECCENTRIC / 8.0;
    var a = AXIS * (1 - (2 * p) - (3 * p * p) - (10 * p * p * p));
    var b = AXIS * ((6 * p) + (12 * p * p) + (45 * p * p * p)) / 2;
    var c = AXIS * ((15 * p * p) + (90 * p * p * p)) / 4;
    var mo = (a * LAT_ORIG) - (b * Math.sin(2 * LAT_ORIG)) + (c * Math.sin(4 * LAT_ORIG));
    var mp = (a * phip) - (b * Math.sin(2 * phip)) + (c * Math.sin(4 * phip));
    east = 0.5 + FALSE_EAST + ((SCALE * up) * (j + ((j * j * j) / 6) * (1 - tsqd + nsqd)));
    north = 0.5 + FALSE_NORTH + (SCALE * (mp - mo)) + (SCALE * (up) * ((Math.sin(phip)) / (Math.cos(phip)))) * (((j * j)/ 2) + ((j * j * j * j) / 24 * (5 - tsqd)));
    east = Math.round(east);       // round to whole number
    north = Math.round(north);     // round to whole number
    var gr_obj = new GridReference (east, north);
    return gr_obj;
}
function find_gridsquare (gr_obj) {
    var east = gr_obj.easting;
    var north = gr_obj.northing;
    var eX = east / 500000;
    var nX = north / 500000;
    var tmp = Math.floor(eX)-5.0 * Math.floor(nX)+17.0;
    nX = 5 * (nX - Math.floor(nX));
    eX = 20 - 5.0 * Math.floor(nX) + Math.floor(5.0 * (eX - Math.floor(eX)));
    if (eX > 7.5)
      eX = eX + 1;
    if (tmp > 7.5)
      tmp = tmp + 1;
    var eing = String(east);
    var ning = String(north);
    var lnth = eing.length;
    eing = eing.substring(lnth - 5, lnth);
    lnth = ning.length;
    ning = ning.substring(lnth - 5, lnth);
    var sq = String.fromCharCode(tmp + 65) + String.fromCharCode(eX + 65);
    var ngr_obj = new NationalGridReference(sq, eing, ning);
    ngr_obj.accuracy = gr_obj.accuracy;
    return ngr_obj;
}
function conv_ngr_to_ings(ngr_obj) {
    var sq = ngr_obj.square;
    var north = ngr_obj.northing;
    var east = ngr_obj.easting;

    var t1 = sq.charCodeAt(0) - 65;
    
    if (t1 > 8) t1 = t1 -1;
    var t2 = Math.floor(t1 / 5);
    north = north + 500000 * (3 - t2);
    east = east + 500000 * (t1 - 5 * t2 - 2);

    t1 = sq.charCodeAt(1) - 65;
    if (t1 > 8) t1 = t1 - 1;
    t2 = Math.floor(t1 / 5);
    north = north + 100000 * ( 4 - t2);
    east = east + 100000 * ( t1 - 5 * t2);
    gr_obj = new GridReference(east,north);
    gr_obj.accuracy = ngr_obj.accuracy;
    return (gr_obj);
}
function conv_uk_ings_to_ll(gr_obj) {
    var east = gr_obj.easting;
    var north = gr_obj.northing;
    var wgs_axis = 6378137;
    var wgs_eccent = 0.00669438;
    var latorig = 49 * deg2rad;
    var lonorig = -2 * deg2rad;
    var scale = 0.9996012717;
    var falseeast = 400000;
    var falsenorth = -100000;
    var axis = 6377563.396;
    var eccent = 0.00667054;
    var ep = Number(east);
    var np = Number(north);
    var p = eccent / 8;
    var a = axis * (1 - (2 * p) - (3 * p * p) - (10 * p * p * p));
    var b = axis * ((6 * p) + (12 * p * p) + (45 * p * p * p)) / 2;
    var c = axis * ((15 * p * p) + (90 * p * p * p)) / 4;
    var mo = a * latorig - b * Math.sin(2 * latorig) + c * Math.sin(4 * latorig);
    var mp = mo + ((np - falsenorth) / scale);
    var phidash = mp / a;
    var phif = phidash + ((b * Math.sin(2 * phidash)) - (c * Math.sin(4 * phidash))) / (a - (2 * b * Math.cos(2 * phidash)));

    var uf = axis / Math.sqrt(1 - (eccent * (Math.sin(phif) * Math.sin(phif))));
    var h = (ep - falseeast) / (scale * uf);

    var nsqd = eccent * (Math.cos(phif) * Math.cos(phif)) / (1 - eccent);
    var tsqd = Math.pow(Math.sin(phif) / Math.cos(phif), 2);

    var lambdap = lonorig + ( 1 / Math.cos(phif)) * ((h - ((h * h * h) / 6) * (1 + (2 * tsqd) + nsqd)));
    var phip = phif - ((1 + nsqd) * (Math.sin(phif)/Math.cos(phif)) * (((h * h) / 2) - ((h * h * h * h) / 24) * (5 + 3 * tsqd))); 

    var cart = ll_to_cartesian(phip, lambdap, axis, eccent, 0);

    var x = cart.x + 371;
    var y = cart.y - 112;
    var z = cart.z + 434;

    var geo = cartesian_to_ll(x, y, z, wgs_eccent, wgs_axis);
    var lat = geo.latitude * rad2deg;
    var lon = geo.longitude * rad2deg;
    lat = formatToXPlaces(lat,2,6);
    lon = formatToXPlaces(lon,2,6);
    var ddc = new DegreesDecimalCoordinate (lat,lon);
    return (ddc);
}
function dms_coord_to_dec_coord (dms_coord_obj) {
    var lat = dms_to_dec(dms_coord_obj.lat_dms_obj);
    var lon = dms_to_dec(dms_coord_obj.lon_dms_obj);
    var dd_coord = new DegreesDecimalCoordinate(lat, lon);
    return (dd_coord);
}
function dms_to_dec (dms_obj) {
    var positive = dms_obj.positive;
    var deg = dms_obj.degrees;
    var min = dms_obj.minutes;
    var sec = dms_obj.seconds;
    var a_sixtieth = 1/60;
    var min_dec = (min * a_sixtieth);
    var sec_dec = (sec * (Math.pow(a_sixtieth,2)));
    var dec = deg.valueOf() + min_dec.valueOf() + sec_dec.valueOf();
    if (!positive) {
        dec *= -1;
    }
    dec = formatToXPlaces(dec, 2, 6);
    return dec;
}
function formatToXPlaces (number, leftOfDecimal, rightOfDecimal) {
//alert ("FUNCTION: formatToXPlaces()");
//alert("number: " + number);
   
   number = Math.round(number*Math.pow(10,rightOfDecimal))/Math.pow(10,rightOfDecimal);
   var string = String(number);
   var decimal = string.indexOf('.');
   if (decimal == -1) {
       string = string + '.';
       decimal = string.length - 1;
   }
   for (i=0;i<(leftOfDecimal-decimal);i++) {
       string = '0' + string;
   }
   var desiredLength = leftOfDecimal + rightOfDecimal + 1;
   var length = string.length;
   if (length > desiredLength) {
      string = string.substring(0,desiredLength);
   } else {
      for (i=length;i<desiredLength;i++) {
         string = string + '0';
      }
   }
   if (rightOfDecimal == 0) {
      // remove decimal point
      string = string.substring(0, string.length-1);
   }
//alert("formatted: " + string);
   return string;
}

/* ROUNDS DOWN
   order = 2 rounds number to nearest 100, 
   order = -1 rounds number to nearest 0.1 
   
   multiplier is for rounding to the nearest 0.2 or 300 
   so roundToOrder (713, 3, 3) should return 600 */
function roundToOrder(number,multiplier,order) {
   var i;
//alert(number);
   number /= multiplier;
   if (order < 0) {
      for (i=order;i<0;i++) {
         number *= 10;
      } 
      number = Math.floor(number);
      for (i=order;i<0;i++) {
         number /= 10;
      }
   } else {
      for (i=0;i<order;i++) {
         number /= 10;
//alert(number + ' i:' + i + ' order:' + order);
      }
      number = Math.floor(number);
      for (i=0;i<order;i++) {
         number *= 10;
      } 
   }
//alert(number);
   number *= multiplier;
//alert(number);
   return number;
}

/* ROUNDS TO NEAREST */
function roundToNearestOrder(number,multiplier,order) {
   var i;
//alert(number);
   number /= multiplier;
   if (order < 0) {
      for (i=order;i<0;i++) {
         number *= 10;
      } 
      number = Math.round(number);
      for (i=order;i<0;i++) {
         number /= 10;
      }
   } else {
      for (i=0;i<order;i++) {
         number /= 10;
//alert(number + ' i:' + i + ' order:' + order);
      }
      number = Math.round(number);
      for (i=0;i<order;i++) {
         number *= 10;
      } 
   }
//alert(number);
   number *= multiplier;
//alert(number);
   return number;
}

function dec_coord_to_dms_coord(dd_coord) {
    var lat_dms_obj = dec_to_dms(dd_coord.latitude);
    var lon_dms_obj = dec_to_dms(dd_coord.longitude);
    var dms_coord = new DMSCoordinate (lat_dms_obj, lon_dms_obj);
    return (dms_coord);
}
function dec_to_dms (dec) {
    var pos = (dec > 0);
    dec = Math.abs(dec);
    var deg = Math.floor(dec);
    var remains = dec - deg;
    remains *= 60;
    var min = Math.floor(remains);
    remains -= min;
    remains *= 60;
    var sec = Math.round(remains);
    var dms_obj = new DegreesMinutesSeconds(pos, deg, min, sec);
    return (dms_obj);
}
function update_coords(id) {
    var tokens = id.split(':');
    if (tokens[1] == 'OSGB36') {
        var valid = validate_os_data();
        if (valid) {
            var ngr_obj = get_os_data();
            var gr_obj = conv_ngr_to_ings(ngr_obj);
            var dd_coord_obj = conv_uk_ings_to_ll(gr_obj);
            set_dec_data(dd_coord_obj);
            var dms_coord_obj = dec_coord_to_dms_coord(dd_coord_obj);
            set_dms_coord(dms_coord_obj);
        }
    } else if (tokens[tokens.length-1] != 'decimal') {
        validate_dms_data();
        var dms_coord_obj = get_dms_coordinate();
        var dd_coord_obj = dms_coord_to_dec_coord (dms_coord_obj);
        set_dec_data(dd_coord);
        update_os_coords(dd_coord_obj);
    } else {
        validate_dec_data();
        var dd_coord_obj = get_dec_coord();
        var dms_coord_obj = dec_coord_to_dms_coord(dd_coord_obj);
        set_dms_coord(dms_coord_obj);
        update_os_coords(dd_coord_obj);
    }
}
function update_os_coords (dd_coord_obj) {
    var gr_obj = wgs84_to_osgb36(dd_coord_obj);
    var ngr_obj = find_gridsquare(gr_obj);
    var sq = ngr_obj.square;
    if (!sq.match(/[A-Z]{2}/)) {
        ngr_obj = new NationalGridReference('',0,0);
    }
    set_os_data(ngr_obj);
}
function get_dms_coordinate () {
    var lat_dms_obj = get_dms_data('latitude');
    var lon_dms_obj = get_dms_data('longitude');
    var dms_coord_obj = new DMSCoordinate(lat_dms_obj, lon_dms_obj);
    return dms_coord_obj;
} 
function get_dms_data (axis) {
    var pos = 0;
    var positive = document.getElementById('input:' + axis + ':positive').value;
    if (axis == 'longitude') {
        positive.match(/E/i)?pos=1:pos=0;
    } else {
        positive.match(/N/i)?pos=1:pos=0;
    }
    var deg = document.getElementById('input:' + axis + ':degrees').value;
    var min = document.getElementById('input:' + axis + ':minutes').value;
    var sec = document.getElementById('input:' + axis + ':seconds').value;
    var dms_obj = new DegreesMinutesSeconds (pos, deg, min, sec);
    return dms_obj;
}
function get_dec_coord () {
    var lat = document.getElementById('input:latitude:decimal').value;
    var lon = document.getElementById('input:longitude:decimal').value;
    var dd_coord = new DegreesDecimalCoordinate(lat,lon);
    return (dd_coord);
}
function get_dec_data (axis) {
   if (axis = 'longitude') {
      return document.getElementById('input:longitude:decimal').value;
   } else {
      return document.getElementById('input:latitude:decimal').value;
   }
}
function get_os_data () {
   var sq = document.getElementById('input:OSGB36:gridsquare').value;
   var east = document.getElementById('input:OSGB36:easting').value;
   var north = document.getElementById('input:OSGB36:northing').value;
   var precision = 5;
   if (String(east).length != String(north).length) {
      alert ('Please enter both references in the same number of digits.');
      return false;
   } else {
      if (!sq || sq == '') {
         precision = 6;
      }
      while (String(east).length < precision 
               && String(north).length < precision) {
         east*=10;
         north*=10;
      }
   }
   var ngr_obj = new NationalGridReference(sq, east, north);
   return ngr_obj;
}
function set_dec_data(dd_coord) {
    var lat = dd_coord.latitude;
    var lon = dd_coord.longitude;
    document.getElementById('input:longitude:decimal').value = lon;
    document.getElementById('input:latitude:decimal').value = lat;
}
function set_dms_coord(dms_coord_obj) {
    set_dms_data('latitude', dms_coord_obj.lat_dms_obj);
    set_dms_data('longitude', dms_coord_obj.lon_dms_obj);
}
function set_dms_data(axis, dms_obj) {
    var pos = dms_obj.positive;
    if (axis == 'longitude') {
        if(pos) {pos='E';} else {pos='W';}
    } else if (axis == 'latitude') {
        if (pos) {pos='N';} else {pos='S';}
    }
    document.getElementById('input:' + axis + ':positive').value = pos;
    document.getElementById('input:' + axis + ':degrees').value = dms_obj.degrees;
    document.getElementById('input:' + axis + ':minutes').value = dms_obj.minutes;
    document.getElementById('input:' + axis + ':seconds').value = dms_obj.seconds;
}
function set_os_data(ngr_obj) {
    var sq = ngr_obj.square;
    var east = ngr_obj.easting;
    var north = ngr_obj.northing;
    document.getElementById('input:OSGB36:gridsquare').value = sq;
    east = formatToXPlaces(east, 5, 0);
    north = formatToXPlaces(north, 5, 0);
    document.getElementById('input:OSGB36:easting').value = east;
    document.getElementById('input:OSGB36:northing').value = north;
}
function validate_dec_data () {
    var lon_max = 180;
    var lat_max = 90;
    var lon_dec = Math.abs(get_dec_data('longitude'));
    var lat_dec = Math.abs(get_dec_data('latitude'));
    var alertString = '';
    if (lon_dec > lon_max) {
        alertString += "Longitude as a decimal cannot be greated than " + lon_max + "&deg;\n";
    }
    if (lat_dec > lat_max) {
        alertString += "Latitude as a decimal cannot be greater than " + lat_max + "&deg;\n";
    }
}
function validate_dms_data () {
    var lon_deg_max = 180;
    var lat_deg_max = 90;
    var min_sec_max = 59;
    var lon_dms_obj = get_dms_data('longitude');
    var isEastOfMeridian = lon_dms_obj.positive;
    var lon_deg = lon_dms_obj.degrees;
    var lon_min = lon_dms_obj.minutes;
    var lon_sec = lon_dms_obj.seconds;
    var alertString = '';
    if (lon_deg >lon_deg_max) {
        alertString += '\nLongitude degrees must not exceed ' + lon_deg_max + '\n';    
    }
    if (lon_min > min_sec_max || lon_sec > min_sec_max) {
        alertString += '\nMinutes and seconds cannot exceed ' + min_sec_max + '\n';
    }
    //set_dms_data('longitude', [isEastOfMeridian, lon_deg, lon_min, lon_sec]);
    var lat_dms_obj = get_dms_data('latitude');
    var isNorthOfEquator = lat_dms_obj.positive;
    var lat_deg = lat_dms_obj.degrees;
    var lat_min = lat_dms_obj.minutes;
    var lat_sec = lat_dms_obj.seconds;
    if (lat_deg >lon_deg_max) {
        alertString += '\nLongitude degrees must not exceed ' + lon_deg_max + '\n';    
    }
    if (lat_min > min_sec_max || lat_sec > min_sec_max) {
        alertString += '\nMinutes and seconds cannot exceed ' + min_sec_max + '\n';
    }
    //set_dms_data('latitude', [isNorthOfEquator, lat_deg, lat_min, lat_sec]);
    if (alertString != '') {
        alert(alertString);
    }
}
function validate_os_data () {
   var ngr_obj = get_os_data();
   var sq = ngr_obj.square;
   var east = ngr_obj.easting;
   var north = ngr_obj.northing;
   var tens,i;
   if (east == '' || north == '') return false;
   if (!sq && String(east).length == 6) {
      gr_obj = new GridReference(ngr_obj.easting, ngr_obj.northing);
      ngr_obj = find_gridsquare(gr_obj);
      sq = ngr_obj.square;
      east = ngr_obj.easting;
      north = ngr_obj.northing;
   } else if (!sq.match(/[A-Z]{2}/)) {
      alert('Grid square must be two uppercase characters.\n' + 
            'Please re-enter coordinates');
      return false;
   }
   ngr_obj = new NationalGridReference(sq, east, north);
   set_os_data(ngr_obj);
   return true;
}
function validate_coords () {
    validate_os_data();
    validate_dms_data();
    validate_dec_data();
}
/*
conv_ngr_to_ings(ngr_obj);
conv_uk_ings_to_ll(gr_obj);
*/
function tetradArray() {
   var tk = new Array();
   tk[0] = new Array();
   tk[1] = new Array();
   tk[2] = new Array();
   tk[3] = new Array();
   tk[4] = new Array();
   tk[0][4] = 'E';tk[1][4] = 'J';tk[2][4] = 'P';tk[3][4] = 'U';tk[4][4] = 'Z';
   tk[0][3] = 'D';tk[1][3] = 'I';tk[2][3] = 'N';tk[3][3] = 'T';tk[4][3] = 'Y';
   tk[0][2] = 'C';tk[1][2] = 'H';tk[2][2] = 'M';tk[3][2] = 'S';tk[4][2] = 'X';
   tk[0][1] = 'B';tk[1][1] = 'G';tk[2][1] = 'L';tk[3][1] = 'R';tk[4][1] = 'W';
   tk[0][0] = 'A';tk[1][0] = 'F';tk[2][0] = 'K';tk[3][0] = 'Q';tk[4][0] = 'V';
   return tk;
}
//-->
