var PLACES_URL_BASE = '/themes/subjects/military/woking_boroughs_first_world_war_memorials/';
var PLACES_START_ZOOM = 12;
var PLACES_START_LAT = -0.569388;
var PLACES_START_LNG = 51.317055;


var PLACES = [{NAME:"Brookwood_hospital",LINK:"brookwood_hospital_memorial",RANK:1,EASTING:496050,NORTHING:158150,IMG:"/assets/userfiles/Woking_Memorials/Brookwood_Hospital.jpg"},

{NAME:"Woking_Crematorium",LINK:"woking_crematorium",RANK:1,EASTING:497386,NORTHING:157959,IMG:"/assets/userfiles/Woking_Memorials/Woking_Crematorium___combined.JPG"},

{NAME:"Woking_Town_Memorial",LINK:"woking_town_memorial",RANK:1,EASTING:500392,NORTHING:158881,IMG:"/assets/userfiles/Woking_Memorials//Woking_Town/Main.jpg"},

{NAME:"Brookwood_Military_Cemetery",LINK:"brookwood_military_cemetery",RANK:1,EASTING:494990,NORTHING:156819,IMG:"/assets/userfiles/Woking_Memorials/"},

{NAME:"Brookwood_Memorial_Hall",LINK:"brookwood_memorial_hall",RANK:1,EASTING:494777,NORTHING:157184,IMG:"/assets/userfiles/Woking_Memorials/Brookwood_War_Memorial_Hall_combined.jpg"},

{NAME:"Byfleet_War_Memorial",LINK:"Byfleet_War_Memorial",RANK:1,EASTING:505888,NORTHING:161243,IMG:"/assets/userfiles/Woking_Memorials/Byfleet_1.jpg"},

{NAME:"Byfleet_St_Marys_Church",LINK:"Byfleet_St_Marys_Church",RANK:1,EASTING:506185,NORTHING:160475,IMG:"/assets/userfiles/Woking_Memorials/Byfleet_St_Marys___2.jpg"},

{NAME:"Byfleet_St_Marys_School",LINK:"Byfleet_St_Marys_School",RANK:1,EASTING:506131,NORTHING:160639,IMG:"/assets/userfiles/Woking_Memorials/Byfleet_St_Marys_Primary_School___1.jpg"},

{NAME:"West_Byfleet_St_John_the_Baptist_Church",LINK:"st_john_the_baptist_church_west_byfleet",RANK:1,EASTING:504323,NORTHING:161036,IMG:"/assets/userfiles/Woking_Memorials/West_Byfleet_St_John___2.jpg"},

{NAME:"West_Byfleet_War_Memorial",LINK:"West_Byfleet_War_Memorial",RANK:1,EASTING:504249,NORTHING:160988,IMG:"/assets/userfiles/Woking_Memorials/West_Byfleet___1.jpg"},

{NAME:"Goldsworth_Nursery_War_Memorial",LINK:"Goldsworth_Nursery_War_Memorial",RANK:1,EASTING:496747,NORTHING:159425,IMG:"/assets/userfiles/Woking_Memorials/Goldsworth_Nursery___1.jpg"},

{NAME:"Knaphill_Holy_Trinity_Memorial",LINK:"Knaphill_Holy_Trinity_Memorial",RANK:1,EASTING:495849,NORTHING:158423,IMG:"/assets/userfiles/Woking_Memorials/Knaphill_Holy_Trinity___2.jpg"},

{NAME:"Mayford_Industrial_School_Memorial_(Original_site)",LINK:"mayford_industrial_school_memorial_orignal_site",RANK:1,EASTING:499372,NORTHING:156186,IMG:"/assets/userfiles/Woking_Memorials/Mayford_Industrial_School1_2.jpg"},

{NAME:"Old_Woking_St_Peters_Church_Memorial",LINK:"Old_Woking_St_Peters_Church_Memorial",RANK:1,EASTING:501983,NORTHING:156908,IMG:"/assets/userfiles/Woking_Memorials/Old_Woking_St_Peters___2.jpg"},

{NAME:"Pyrford_Church_War_Memorial",LINK:"Pyrford_Church_War_Memorial",RANK:1,EASTING:503902,NORTHING:158271,IMG:"/assets/userfiles/Woking_Memorials/Pyrford.jpg"},

{NAME:"Pyrford_War_Memorial_Hall",LINK:"Pyrford_War_Memorial_Hall",RANK:1,EASTING:503886,NORTHING:159336,IMG:"/assets/userfiles/Woking_Memorials/Pyrford_Memorial_Hall.jpg"},

{NAME:"Sutton_Green_All_Souls_Church",LINK:"Sutton_Green_All_Souls_Church",RANK:1,EASTING:500390,NORTHING:154964,IMG:"/assets/userfiles/Woking_Memorials/Sutton_Green_All_Souls___1.jpg"},

{NAME:"Westfield_School_Banner",LINK:"Westfield_School_Banner",RANK:1,EASTING:500234,NORTHING:156731,IMG:"/assets/userfiles/Woking_Memorials/Westfield_School.jpg"},

{NAME:"Woodham_War_Memorial_and_Wooden_Shrine",LINK:"Woodham_War_Memorial_and_Wooden_Shrine",RANK:1,EASTING:501458,NORTHING:160568,IMG:"/assets/userfiles/Woking_Memorials/Woodham1_All_Saints2.jpg"},

{NAME:"Sutton_Green_Memorial_Hall",LINK:"sutton_green_memorial_hall",RANK:1,EASTING:500380,NORTHING:155040,IMG:"/assets/userfiles/Woking_Memorials/Sutton_Green_Village_Hall.jpg"},

{NAME:"Bridley_Manor_War_Memorial",LINK:"bridley_manor_war_memorial",RANK:1,EASTING:496345,NORTHING:154992,IMG:"/assets/userfiles/Woking_Memorials/Bridley_Manor_1.jpg"},

{NAME:"Horsell_Village_Memorial",LINK:"Horsell_Village_Memorial",RANK:1,EASTING:499750,NORTHING:159350,IMG:"/assets/userfiles/Woking_Memorials/Horsell___1.jpg"},

{NAME:"Horsell_St_Marys_Church",LINK:"Horsell_St_Marys_Church",RANK:1,EASTING:499617,NORTHING:159210,IMG:"/assets/userfiles/Woking_Memorials/Horsell_St_Marys___2.jpg"},

{NAME:"Maybury_School_Memorial_(in_Maybury_Centre)",LINK:"Maybury_School_Memorial",RANK:1,EASTING:500909,NORTHING:159286,IMG:"/assets/userfiles/Woking_Memorials/Maybury_School.jpg"},

{NAME:"Maybury_St_Pauls_Church",LINK:"Maybury_St_Pauls_Church",RANK:1,EASTING:501557,NORTHING:159175,IMG:"/assets/userfiles/Woking_Memorials/Maybury_St_Paul.jpg"},

{NAME:"St_Johns_Church_War_Memorial",LINK:"St_Johns_Church_War_Memorial",RANK:1,EASTING:498063,NORTHING:157814,IMG:"/assets/userfiles/Woking_Memorials/St_Johns.jpg"},

{NAME:"Woking_Co_Op_Memorial_(Percy_Street_Demolished)",LINK:"woking_co_op_memorial_percy_street_demolished",RANK:1,EASTING:500229,NORTHING:158767,IMG:"/assets/userfiles/Woking_Memorials/Co_op_Great_War_Plaque.jpg"},

{NAME:"Woking_Post_Office",LINK:"Woking_Post_Office",RANK:1,EASTING:500627,NORTHING:158688,IMG:"/assets/userfiles/Woking_Memorials/G_P_O.jpg"},

{NAME:"Woking_Christ_Church",LINK:"christ_church_woking",RANK:1,EASTING:500441,NORTHING:158880,IMG:"/assets/userfiles/Woking_Memorials/Woking_Christ_Church.jpg"},

{NAME:"Woking_Coign_Baptist_Church",LINK:"Woking_Coign_Baptist_Church",RANK:1,EASTING:500081,NORTHING:158617,IMG:"/assets/userfiles/Woking_Memorials/Woking_Coign_Church.jpg"},

{NAME:"Woking_St_Mary_of_Bethany",LINK:"Woking_St_Mary_of_Bethany",RANK:1,EASTING:499817,NORTHING:157887,IMG:"/assets/userfiles/Woking_Memorials/St_Mary_of_Bethany_Woking.jpg"},

{NAME:"Jarman_Court",LINK:"jarman_court_woking",RANK:1,EASTING:500951,NORTHING:159096,IMG:"/assets/userfiles/Woking_Memorials/Jarman_Court.jpg"},

{NAME:"Horsell_Common_Muslim_Cemetery",LINK:"Horsell_Common_Muslim_Cemetery",RANK:1,EASTING:501650,NORTHING:159950,IMG:"/assets/userfiles/Woking_Memorials/Woking_Muslim_Burial_Ground___1.jpg"}];	

/**
 * Initialise the map
 */
var PLACES_MAP;
var PLACES_MGR;
function places_mapInit() 
{
	if (GBrowserIsCompatible()) 
	{
		PLACES_MAP = new GMap2(document.getElementById("map_canvas"));
		PLACES_MAP.addControl(new GLargeMapControl());
		PLACES_MAP.addControl(new GMapTypeControl());
		PLACES_MAP.setCenter(new GLatLng(PLACES_START_LNG,PLACES_START_LAT),PLACES_START_ZOOM);
		PLACES_MAP.setMapType(G_HYBRID_MAP);
		window.setTimeout(places_setupMarkers, 0);
	}
}

function places_setupMarkers() 
{
	PLACES_MGR = new MarkerManager(PLACES_MAP);
	PLACES_MGR.addMarkers(places_getMarkers(1), 3);
	PLACES_MGR.addMarkers(places_getMarkers(2), 11);
	PLACES_MGR.addMarkers(places_getMarkers(3), 12);
	PLACES_MGR.addMarkers(places_getMarkers(4), 13);
	PLACES_MGR.addMarkers(places_getMarkers(5), 14);
	PLACES_MGR.addMarkers(places_getMarkers(6), 15);
	PLACES_MGR.refresh();
}
function places_getMarkers(maxRank)
{
	var batch = [];
	for(var i in PLACES)
	{
		if(PLACES[i].RANK<=maxRank)
		{
			batch.push(places_createMarker(PLACES[i]));
		}
	}
	return batch;
}
function places_createMarker(place)
{	
	var osref = new OSRef(place.EASTING,place.NORTHING);
	var latlng = osref.toLatLng();			
	var point = new GLatLng(latlng.lat,latlng.lng);
    var marker = new GMarker(point);
    GEvent.addListener(marker, "click", function()
	{
		var imageexists = '&nbsp;';
		if(place.IMG!='')
		{
			imageexists = '<img src='+ place.IMG + ' height=100 />';
		}
		var fulllink = '/themes/subjects/military/woking_boroughs_first_world_war_memorials/'+place.LINK;
		
		marker.openInfoWindowHtml("<a href=\"" + fulllink + "\" class=\"placeTitle\">" + place.NAME + "</a><br/><br/><div align=center>" + imageexists + "<ul><li class=\"district\"><strong>Click the link to read more about this memorial</strong>");
	});
	return marker;
}



































//--------------------------------------------------------------------------
// JScoord
// jscoord.js
//
// (c) 2005 Jonathan Stott
//
// Created on 21-Dec-2005
//
// 1.1.1 - 16 Jan 2006
//  - Fixed radix bug in getOSRefFromSixFigureReference that would return
//    the incorrect reference if either the northing or the easting started
//    with a leading zero.
// 1.1 - 23 Dec 2005
//  - Added getOSRefFromSixFigureReference function.
// 1.0 - 11 Aug 2005
//  - Initial version created from PHPcoord v1.1
//--------------------------------------------------------------------------


// ================================================================== LatLng

function LatLng(lat, lng) {
  this.lat = lat;
  this.lng = lng;
     
  this.distance = LatLngDistance;
      
  this.toOSRef = LatLngToOSRef;
  this.toUTMRef = LatLngToUTMRef;
      
  this.WGS84ToOSGB36 = WGS84ToOSGB36;
  this.OSGB36ToWGS84 = OSGB36ToWGS84;
      
  this.toString = LatLngToString;
}

function LatLngToString() {
  return "(" + this.lat + ", " + this.lng + ")";
}


// =================================================================== OSRef

// References given with OSRef are accurate to 1m.
function OSRef(easting, northing) {
  this.easting  = easting;
  this.northing = northing;
    
  this.toLatLng = OSRefToLatLng;
    
  this.toString = OSRefToString;
  this.toSixFigureString = OSRefToSixFigureString;
}


function OSRefToString() {
  return "(" + this.easting + ", " + this.northing + ")";
}


function OSRefToSixFigureString() {
  var hundredkmE = Math.floor(this.easting / 100000);
  var hundredkmN = Math.floor(this.northing / 100000);
  var firstLetter = "";
  if (hundredkmN < 5) {
    if (hundredkmE < 5) {
      firstLetter = "S";
    } else {
      firstLetter = "T";
    }
  } else if (hundredkmN < 10) {
    if (hundredkmE < 5) {
      firstLetter = "N";
    } else {
      firstLetter = "O";
    }
  } else {
    firstLetter = "H";
  }

  var secondLetter = "";
  var index = 65 + ((4 - (hundredkmN % 5)) * 5) + (hundredkmE % 5);
  var ti = index;
  if (index >= 73) index++;
  secondLetter = chr(index);

  var e = Math.floor((this.easting - (100000 * hundredkmE)) / 100);
  var n = Math.floor((this.northing - (100000 * hundredkmN)) / 100);
  var es = e;
  if (e < 100) es = "0" + es;
  if (e < 10)  es = "0" + es;
  var ns = n;
  if (n < 100) ns = "0" + ns;
  if (n < 10)  ns = "0" + ns;

  return firstLetter + secondLetter + es + ns;
}


// ================================================================== UTMRef

function UTMRef(easting, northing, latZone, lngZone) {
  this.easting  = easting;
  this.northing = northing;
  this.latZone  = latZone;
  this.lngZone  = lngZone;
    
  this.toLatLng = UTMRefToLatLng;
    
  this.toString = UTMRefToString;
}


function UTMRefToString() {
  return this.lngZone + this.latZone + " " +
         this.easting + " " + this.northing;
}


// ================================================================== RefEll

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;
}


// ========================================================= Other Functions

function LatLngDistance(to) {
  var er = 6366.707;

  var latFrom = deg2rad(this.lat);
  var latTo   = deg2rad(to.lat);
  var lngFrom = deg2rad(this.lng);
  var lngTo   = deg2rad(to.lng);

  var x1 = er * Math.cos(lngFrom) * Math.sin(latFrom);
  var y1 = er * Math.sin(lngFrom) * Math.sin(latFrom);
  var z1 = er * Math.cos(latFrom);

  var x2 = er * Math.cos(lngTo) * Math.sin(latTo);
  var y2 = er * Math.sin(lngTo) * Math.sin(latTo);
  var z2 = er * Math.cos(latTo);

  var d = Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)+(z1-z2)*(z1-z2));

  return d;
}


// ==================================================== 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 WGS84ToOSGB36() {
  var wgs84 = new RefEll(6378137.000, 6356752.3141);
  var a        = wgs84.maj;
  var b        = wgs84.min;
  var eSquared = wgs84.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 airy1830 = new RefEll(6377563.396, 6356256.909);
  a        = airy1830.maj;
  b        = airy1830.min;
  eSquared = airy1830.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));

  return new LatLng(rad2deg(phi), rad2deg(lambda));
}


/**
 * Convert a latitude and longitude into an OSGB grid reference
 *
 * @param latitudeLongitude the latitude and longitude to convert
 * @return the OSGB grid reference
 * @since 0.1
 */
function LatLngToOSRef() {
  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 = deg2rad(this.lat);
  var lambda = deg2rad(this.lng);
  var E = 0.0;
  var N = 0.0;
  var n = (a - b) / (a + b);
  var v = a * OSGB_F0 * Math.pow(1.0 - eSquared * sinSquared(phi), -0.5);
  var rho =
    a * OSGB_F0 * (1.0 - eSquared) * Math.pow(1.0 - eSquared * sinSquared(phi), -1.5);
  var etaSquared = (v / rho) - 1.0;
  var M =
    (b * OSGB_F0)
      * (((1 + n + ((5.0 / 4.0) * n * n) + ((5.0 / 4.0) * n * n * n))
        * (phi - phi0))
        - (((3 * n) + (3 * n * n) + ((21.0 / 8.0) * n * n * n))
          * Math.sin(phi - phi0)
          * Math.cos(phi + phi0))
        + ((((15.0 / 8.0) * n * n) + ((15.0 / 8.0) * n * n * n))
          * Math.sin(2.0 * (phi - phi0))
          * Math.cos(2.0 * (phi + phi0)))
        - (((35.0 / 24.0) * n * n * n)
          * Math.sin(3.0 * (phi - phi0))
          * Math.cos(3.0 * (phi + phi0))));
  var I = M + N0;
  var II = (v / 2.0) * Math.sin(phi) * Math.cos(phi);
  var III =
    (v / 24.0)
      * Math.sin(phi)
      * Math.pow(Math.cos(phi), 3.0)
      * (5.0 - tanSquared(phi) + (9.0 * etaSquared));
  var IIIA =
    (v / 720.0)
      * Math.sin(phi)
      * Math.pow(Math.cos(phi), 5.0)
      * (61.0 - (58.0 * tanSquared(phi)) + Math.pow(Math.tan(phi), 4.0));
  var IV = v * Math.cos(phi);
  var V = (v / 6.0) * Math.pow(Math.cos(phi), 3.0) * ((v / rho) - tanSquared(phi));
  var VI =
    (v / 120.0)
      * Math.pow(Math.cos(phi), 5.0)
      * (5.0
        - (18.0 * tanSquared(phi))
        + (Math.pow(Math.tan(phi), 4.0))
        + (14 * etaSquared)
        - (58 * tanSquared(phi) * etaSquared));

  N =
    I
      + (II * Math.pow(lambda - lambda0, 2.0))
      + (III * Math.pow(lambda - lambda0, 4.0))
      + (IIIA * Math.pow(lambda - lambda0, 6.0));
  E =
    E0
      + (IV * (lambda - lambda0))
      + (V * Math.pow(lambda - lambda0, 3.0))
      + (VI * Math.pow(lambda - lambda0, 5.0));

  return new OSRef(E, N);
}


/**
 * Convert an UTM reference to a latitude and longitude
 *
 * @param ellipsoid A reference ellipsoid to use
 * @param utm the UTM reference to convert
 * @return the converted latitude and longitude
 * @since 0.2
 */
function UTMRefToLatLng() {
  var wgs84 = new RefEll(6378137, 6356752.314);
  var UTM_F0   = 0.9996;
  var a = wgs84.maj;
  var eSquared = wgs84.ecc;
  var ePrimeSquared = eSquared / (1.0 - eSquared);
  var e1 = (1 - Math.sqrt(1 - eSquared)) / (1 + Math.sqrt(1 - eSquared));
  var x = this.easting - 500000.0;;
  var y = this.northing;
  var zoneNumber = this.lngZone;
  var zoneLetter = this.latZone;

  var longitudeOrigin = (zoneNumber - 1.0) * 6.0 - 180.0 + 3.0;

  // Correct y for southern hemisphere
  if ((ord(zoneLetter) - ord("N")) < 0) {
    y -= 10000000.0;
  }

  var m = y / UTM_F0;
  var mu =
    m
      / (a
        * (1.0
          - eSquared / 4.0
          - 3.0 * eSquared * eSquared / 64.0
          - 5.0
            * Math.pow(eSquared, 3.0)
            / 256.0));

  var phi1Rad =
    mu
      + (3.0 * e1 / 2.0 - 27.0 * Math.pow(e1, 3.0) / 32.0) * Math.sin(2.0 * mu)
      + (21.0 * e1 * e1 / 16.0 - 55.0 * Math.pow(e1, 4.0) / 32.0)
        * Math.sin(4.0 * mu)
      + (151.0 * Math.pow(e1, 3.0) / 96.0) * Math.sin(6.0 * mu);

  var n =
    a
      / Math.sqrt(1.0 - eSquared * Math.sin(phi1Rad) * Math.sin(phi1Rad));
  var t = Math.tan(phi1Rad) * Math.tan(phi1Rad);
  var c = ePrimeSquared * Math.cos(phi1Rad) * Math.cos(phi1Rad);
  var r =
    a
      * (1.0 - eSquared)
      / Math.pow(
        1.0 - eSquared * Math.sin(phi1Rad) * Math.sin(phi1Rad),
        1.5);
  var d = x / (n * UTM_F0);

  var latitude = (
    phi1Rad
      - (n * Math.tan(phi1Rad) / r)
        * (d * d / 2.0
          - (5.0
            + (3.0 * t)
            + (10.0 * c)
            - (4.0 * c * c)
            - (9.0 * ePrimeSquared))
            * Math.pow(d, 4.0)
            / 24.0
          + (61.0
            + (90.0 * t)
            + (298.0 * c)
            + (45.0 * t * t)
            - (252.0 * ePrimeSquared)
            - (3.0 * c * c))
            * Math.pow(d, 6.0)
            / 720.0)) * (180.0 / Math.PI);

  var longitude = longitudeOrigin + (
    (d
      - (1.0 + 2.0 * t + c) * Math.pow(d, 3.0) / 6.0
      + (5.0
        - (2.0 * c)
        + (28.0 * t)
        - (3.0 * c * c)
        + (8.0 * ePrimeSquared)
        + (24.0 * t * t))
        * Math.pow(d, 5.0)
        / 120.0)
      / Math.cos(phi1Rad)) * (180.0 / Math.PI);

  return new LatLng(latitude, longitude);
}


/**
 * Convert a latitude and longitude to an UTM reference
 *
 * @param ellipsoid A reference ellipsoid to use
 * @param latitudeLongitude The latitude and longitude to convert
 * @return the converted UTM reference
 * @since 0.2
 */
function LatLngToUTMRef() {
  var wgs84 = new RefEll(6378137, 6356752.314);
  var UTM_F0   = 0.9996;
  var a = wgs84.maj;
  var eSquared = wgs84.ecc;
  var longitude = this.lng;
  var latitude = this.lat;

  var latitudeRad = latitude * (Math.PI / 180.0);
  var longitudeRad = longitude * (Math.PI / 180.0);
  var longitudeZone = Math.floor((longitude + 180.0) / 6.0) + 1;

  // Special zone for Norway
  if (latitude >= 56.0
    && latitude < 64.0
    && longitude >= 3.0
    && longitude < 12.0) {
    longitudeZone = 32;
  }

  // Special zones for Svalbard
  if (latitude >= 72.0 && latitude < 84.0) {
    if (longitude >= 0.0 && longitude < 9.0) {
      longitudeZone = 31;
    } else if (longitude >= 9.0 && longitude < 21.0) {
      longitudeZone = 33;
    } else if (longitude >= 21.0 && longitude < 33.0) {
      longitudeZone = 35;
    } else if (longitude >= 33.0 && longitude < 42.0) {
      longitudeZone = 37;
    }
  }

  var longitudeOrigin = (longitudeZone - 1) * 6 - 180 + 3;
  var longitudeOriginRad = longitudeOrigin * (Math.PI / 180.0);

  var UTMZone = getUTMLatitudeZoneLetter(latitude);

  ePrimeSquared = (eSquared) / (1 - eSquared);

  var n = a / Math.sqrt(1 - eSquared * Math.sin(latitudeRad) * Math.sin(latitudeRad));
  var t = Math.tan(latitudeRad) * Math.tan(latitudeRad);
  var c = ePrimeSquared * Math.cos(latitudeRad) * Math.cos(latitudeRad);
  var A = Math.cos(latitudeRad) * (longitudeRad - longitudeOriginRad);

  var M =
    a
      * ((1
        - eSquared / 4
        - 3 * eSquared * eSquared / 64
        - 5 * eSquared * eSquared * eSquared / 256)
        * latitudeRad
        - (3 * eSquared / 8
          + 3 * eSquared * eSquared / 32
          + 45 * eSquared * eSquared * eSquared / 1024)
          * Math.sin(2 * latitudeRad)
        + (15 * eSquared * eSquared / 256
          + 45 * eSquared * eSquared * eSquared / 1024)
          * Math.sin(4 * latitudeRad)
        - (35 * eSquared * eSquared * eSquared / 3072)
          * Math.sin(6 * latitudeRad));

  var UTMEasting =
    (UTM_F0
      * n
      * (A
        + (1 - t + c) * Math.pow(A, 3.0) / 6
        + (5 - 18 * t + t * t + 72 * c - 58 * ePrimeSquared)
          * Math.pow(A, 5.0)
          / 120)
      + 500000.0);

  var UTMNorthing =
    (UTM_F0
      * (M
        + n
          * Math.tan(latitudeRad)
          * (A * A / 2
            + (5 - t + (9 * c) + (4 * c * c)) * Math.pow(A, 4.0) / 24
            + (61 - (58 * t) + (t * t) + (600 * c) - (330 * ePrimeSquared))
              * Math.pow(A, 6.0)
              / 720)));

  // Adjust for the southern hemisphere
  if (latitude < 0) {
    UTMNorthing += 10000000.0;
  }

  return new UTMRef(UTMEasting, UTMNorthing, UTMZone, longitudeZone);
}

/**
 * Take a string formatted as a six-figure OS grid reference (e.g.
 * "TG514131") and return a reference to an OSRef object that represents
 * that grid reference. The first character must be H, N, S, O or T.
 * The second character can be any uppercase character from A through Z
 * excluding I.
 *
 * @param ref
 * @return
 * @since 1.1
 */
function getOSRefFromSixFigureReference(ref) {
  var char1 = ref.substring(0, 1);
  var char2 = ref.substring(1, 2);
  // Thanks to Nick Holloway for pointing out the radix bug here
  var east  = parseInt(ref.substring(2, 5), 10) * 100;
  var north = parseInt(ref.substring(5, 8), 10) * 100;
  if (char1 == 'H') {
    north += 1000000;
  } else if (char1 == 'N') {
    north += 500000;
  } else if (char1 == 'O') {
    north += 500000;
    east  += 500000;
  } else if (char1 == 'T') {
    east += 500000;
  }
  var char2ord = ord(char2);
  if (char2ord > 73) char2ord--; // Adjust for no I
  var nx = ((char2ord - 65) % 5) * 100000;
  var ny = (4 - Math.floor((char2ord - 65) / 5)) * 100000;
  return new OSRef(east + nx, north + ny);
}


/**
 *  Work out the UTM latitude zone from the latitude
 *
 * @param latitude
 * @return
 * @since 0.2
 */
function getUTMLatitudeZoneLetter(latitude) {
  if ((84 >= latitude) && (latitude >= 72)) return "X";
  else if (( 72 > latitude) && (latitude >=  64)) return "W";
  else if (( 64 > latitude) && (latitude >=  56)) return "V";
  else if (( 56 > latitude) && (latitude >=  48)) return "U";
  else if (( 48 > latitude) && (latitude >=  40)) return "T";
  else if (( 40 > latitude) && (latitude >=  32)) return "S";
  else if (( 32 > latitude) && (latitude >=  24)) return "R";
  else if (( 24 > latitude) && (latitude >=  16)) return "Q";
  else if (( 16 > latitude) && (latitude >=   8)) return "P";
  else if ((  8 > latitude) && (latitude >=   0)) return "N";
  else if ((  0 > latitude) && (latitude >=  -8)) return "M";
  else if (( -8 > latitude) && (latitude >= -16)) return "L";
  else if ((-16 > latitude) && (latitude >= -24)) return "K";
  else if ((-24 > latitude) && (latitude >= -32)) return "J";
  else if ((-32 > latitude) && (latitude >= -40)) return "H";
  else if ((-40 > latitude) && (latitude >= -48)) return "G";
  else if ((-48 > latitude) && (latitude >= -56)) return "F";
  else if ((-56 > latitude) && (latitude >= -64)) return "E";
  else if ((-64 > latitude) && (latitude >= -72)) return "D";
  else if ((-72 > latitude) && (latitude >= -80)) return "C";
  else return 'Z';
}
