/*
Geocaching OS Linker - 0.74 22/08/2008
Copyright (c) 2005-2007, Paul Dixon <paul@elphin.com>
Released under the GPL http://www.gnu.org/copyleft/gpl.html

Contains additional contributions by Thomas "Edgemaster" Wood

This is a Greasemonkey user script, see http://greasemonkey.mozdev.org/.

This provides cache information pages with links to OS Maps for UK
caches, and links to Google Maps and Google Earth for everyone. It 
also changes the OS Get-a-Map popup to include an indicator arrow for 
the cache position.

The parseGridRef function is an adaptation of code Neil Jones
wrote for his map substitution script.

*/

// ==UserScript==
// @name          Geocaching Map Linker
// @namespace     http://files.dixo.net
// @description	  Links cache pages to maps, e.g. Google Maps and UK Ordnance Survey
// @include       http://www.geocaching.com/seek/cache_details.aspx*
// @include       http://getamap.ordnancesurvey.co.uk/getamap/map.htm
// ==/UserScript==

var win=window;
if (unsafeWindow)
{
	win=unsafeWindow;
}

//get position of a DOM element
//(from http://www.quirksmode.org/js/findpos.html)
win._findPosX=function(obj)
{
	var curleft = 0;
	if (obj.offsetParent)
	{
		while (obj.offsetParent)
		{
			curleft += obj.offsetLeft
			obj = obj.offsetParent;
		}
	}
	else if (obj.x)
		curleft += obj.x;
	return curleft;
}

win._findPosY=function(obj)
{
	var curtop = 0;
	if (obj.offsetParent)
	{
		while (obj.offsetParent)
		{
			curtop += obj.offsetTop
			obj = obj.offsetParent;
		}
	}
	else if (obj.y)
		curtop += obj.y;
	return curtop;
}


//get url to OS Get-a-Map
win._getOSMapUrl=function(gridref)
{
	if (gridref.length > 0)
		return 'http://getamap.ordnancesurvey.co.uk/getamap/frames.htm?mapAction=gaz&gazName=g&gazString='+gridref;
	else
		return 'http://getamap.ordnancesurvey.co.uk/getamap/frames.htm';
}

//display a OS Get-a-Map popup
win._popupOSMap=function(gridref)
{
        //remember the grid ref
        if (GM_setValue)
        	GM_setValue('gridref', gridref);
        
        var wWidth = 740;
        var wHeight = 520;
        var wLeft = Math.round(0.5 * (screen.availWidth - wWidth));
        var wTop = Math.round(0.5 * (screen.availHeight - wHeight)) - 20;
        var url=win._getOSMapUrl(gridref);
		var newWin = window.open(url, 
			'gam',
			'left='+wLeft+',screenX='+wLeft+',top='+wTop+',screenY='+wTop+',width='+wWidth+',height='+wHeight+',status,scrolling=no');
		newWin.focus();
}

win.getOSMapAnchor=function(gridref, text,title)
{
	var url=win._getOSMapUrl(gridref);

	return '<a target="_blank" title="'+title+'" href="'+url+'" onclick="window._popupOSMap(\''+gridref+'\');return false;">'+text+'</a>';
	
}

win.getGoogleEarthLinkUrl=function(latitude, longitude, name, desc)
{
	//build the kml data
	var kml=new String;
	kml='<?xml version="1.0" encoding="UTF-8"?>';
	kml=kml+'<kml xmlns="http://earth.google.com/kml/2.0">';
	kml=kml+'<Placemark>';
	kml=kml+'<name><![CDATA['+name+']]></name>';

	desc=desc+'<br><a href="'+document.location+'">Cache Details</a>';
	kml=kml+'<description><![CDATA['+desc+']]></description>';



	kml=kml+'<styleUrl>#geocache</styleUrl>';

	kml=kml+'<Style id="geocache"><IconStyle><Icon>';
	kml=kml+'<href>root://icons/palette-2.png</href>';
	kml=kml+'<x>160</x><y>192</y><w>32</w><h>32</h>';
	kml=kml+'</Icon></IconStyle></Style>';



	kml=kml+'<Point>';
	kml=kml+'<coordinates>'+longitude+','+latitude+',0</coordinates>';
	kml=kml+'</Point>';


	kml=kml+'</Placemark>';
	kml=kml+'</kml>';
	
	
	
	//data:[<mediatype>][;base64],<data>
	return "data:application/vnd.google-earth.kml+xml,"+encodeURIComponent(kml);
}

win.getGoogleEarthAnchor=function(latitude, longitude, name, desc, text, title)
{
	var url=win.getGoogleEarthLinkUrl(latitude, longitude, name, desc);
	return '<a href="'+url+'" title="'+title+'">'+text+'</a>';
}


win.getGoogleMapUrl=function(latitude, longitude, sat)
{
	var url="http://maps.google.com/?q="+latitude+"%2C"+longitude;
	if (sat)
	{
		url=url+"&amp;spn=0.055120,0.055120&amp;t=k";
	}
	
	return url
}

win.getGoogleMapAnchor=function(latitude, longitude, sat, text, title)
{
	var url=win.getGoogleMapUrl(latitude, longitude, sat);
	return '<a href="'+url+'" title="'+title+'">'+text+'</a>';
}
win.getFlashEarthAnchor=function(latitude, longitude, text, title)
{
	var url="http://www.flashearth.com/?z=15&amp;lat="+latitude+"&amp;lon="+longitude;
	return '<a href="'+url+'" title="'+title+'">'+text+'</a>';
}


win.enhanceWGS84Strings=function(elem)
{
	var longdesc=elem.innerHTML;


	//match N52 03.270 W000 17.675
	//N 52*12.948 W 000*07.754
	//N 51ý 56.593 E 000ý 02.014
	longdesc=longdesc.replace(/([ns])\s*(\d+)[^\d]+(\d+\.\d+)\s+([we])\s*(\d+)[^\d]+(\d+\.\d+)/ig,
	   function (match, lat1,lat2,lat3,long1,long2,long3) 
	   {
		  
		  var reformatted=match;
		  var wgs84=new GT_WGS84();
		  if (wgs84.parseString(match))
		  {
			if (wgs84.isGreatBritain())
			{
				//link to OSGB map
				var osgb=wgs84.getOSGB();
				var gridref=osgb.getGridRef(5);

				var anchor=win.getOSMapAnchor(gridref, '<span style="font-size:small;font-weight:normal">'+gridref+'</span>','Click for OS Map');
				reformatted=match+ " "+anchor;

			}
			else
			{
				//link to google maps
			}

		  }



		  return reformatted;
	   }
	                   
	                
        );
        
        elem.innerHTML=longdesc;       

}


//this is called to enhance a geocaching.com cache listing page with OS Get-a-Map links
//and Google Maps links
win.enhanceGeocacheListing=function()
{
	var isUK=false;


	//collect useful information about the cache
	
        //<span id="CacheName">Take A Hike Adventure 1 - Lyke Wake Walk</span>
	var cachename=document.getElementById('CacheName').innerHTML;
	
	
	var spanLatLon = document.getElementById("LatLon"); 
	var tdCoords=spanLatLon.parentNode;

	if (tdCoords)
	{
		var linkGMap='';
		var linkGSat='';
		var linkGEarth='';
		var linkFEarth='';
		
		
		//<a id="lnkConversions" href="../wpt/?lat=51.997233&amp;lon=-0.739383&amp;detail=1" target="_blank">other conversions...</a><br />
		var lnkConversions=document.getElementById("lnkConversions"); 
		var pattern = /lat=(-?[\d\.]+).*lon=(-?[\d\.]+)/;
		var matches=lnkConversions.href.match(pattern);
		if (matches)
		{
			var latitude=matches[1];
			var longitude=matches[2];

			linkGMap=win.getGoogleMapAnchor(latitude, longitude, false, 'Google Map', 'Click for Google Map');
			linkGSat=win.getGoogleMapAnchor(latitude, longitude, true, '(inc Satellite)', 'Click for Google Map with Satellite Images (if available)');
			linkGEarth=win.getGoogleEarthAnchor(latitude, longitude, cachename, shortdesc, 'Google Earth', 'Click to open Google Earth waypoint');
			linkFEarth=win.getFlashEarthAnchor(latitude, longitude, 'Flash Earth', 'Click for Flash Earth imagery from multiple providers')

		}
		
		pattern = /British Grid: ([A-Z]{2}) (\d{5}) (\d{5})/;
		matches=tdCoords.innerHTML.match(pattern);
		if (matches)
		{
			isUK=true;

			var gridref=matches[1]+matches[2]+matches[3];

			//store grid reference - GreaseMonkey'd Get-a-Map picks this up
			if (GM_setValue)
				GM_setValue('gridref', gridref);

			var maplink=win.getOSMapAnchor(gridref, matches[1]+' '+matches[2]+' '+matches[3],'Click for OS Map');
			tdCoords.innerHTML = 
				tdCoords.innerHTML.replace(pattern, 
				'British Grid '+maplink+" "+linkFEarth+" "+linkGMap+" "+linkGSat+" "+linkGEarth);
		}
	
	}
	
	
	var spanShort=document.getElementById("ShortDescription");
	var shortdesc="";
	if (spanShort)
	{
		shortdesc=spanShort.innerHTML;
	}
	
	
	
	//lets see if we can hyperlink any other mentioned coordinates to a map
	var spanLong=document.getElementById("LongDescription");
	if (spanLong)
	{
		win.enhanceWGS84Strings(spanLong);	
	}
	/*
	spanLong=document.getElementById("LatLon");
	if (spanLong)
	{
	win.enhanceWGS84Strings(spanLong);	
	}
	*/
	
	
}




win.minimiseMapInfo=function()
{
	var wgs84=document.getElementById('wgs84');
	
	if (wgs84.getAttribute('_minimised')==1)
	{
		wgs84.style.height=370;
		wgs84.style.top=115;
	
		wgs84.setAttribute('_minimised', 0);
	}
	else
	{
		wgs84.style.height=16;
		wgs84.style.top=490;
		wgs84.setAttribute('_minimised', 1);
	}
	
	
	return true;
}

//this is called to enhance OS Get-a-Map - it overrides some of their
//JS functions with cheeky monkey versions
win.enhanceOSMap=function()
{
	win.original_updateInfo=win.updateInfo;
	win.updateInfo=win.super_updateInfo;
	
	win.original_setRetImg=win.setRetImg;
	win.setRetImg=win.super_setRetImg;
	
	win.original_submitFormXY=win.submitFormXY;
	win.submitFormXY=win.super_submitFormXY;
	
	win.original_showCoords=win.showCoords;
	win.showCoords=win.super_showCoords;
	
	
	//var elem=document.getElementById('lastSearchForNS4');
	
	var infoDiv = document.createElement("div");
	infoDiv.innerHTML = '<div id="wgs84" style="position:absolute;left:584px;top:115px;width:135px;height:370px;padding:3px;'+
		'border:2px #336666 solid; background-color:#336666; color:white;font-family:Arial;font-size:8pt;-moz-border-radius:15px;">' +
    '<b>WGS84 Coordinates</b>' +
    '&nbsp;&nbsp;<a href="#" title="Hide this toolbox" onclick="return window.minimiseMapInfo()">[X]</a>' +
    '<div><span id="gct_northing">&nbsp;</span> '+
    '<span id="gct_easting">&nbsp;</span></div>'+
    '<br/><b>Your List</b><span style="font-size:7pt;"> (ctrl+click to add)</span>' +
    '<ul style="padding-left:0;margin-top:0;list-style: none;" id="gct_coordlist"><li><i>empty</i></li></ul><div id="gpxlink"></div>'
    '</div>';
	document.body.insertBefore(infoDiv, document.body.firstChild);


	
}


/*****************************************************************************

 CoordinateList object allows creation of GPX files

<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<gpx
 version="1.1"
 creator="EasyGPS 1.3.7 - http://www.topografix.com"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns="http://www.topografix.com/GPX/1/1"
 xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">
<metadata>
<time>2005-10-25T16:36:44Z</time>
<bounds minlat="51.895136" minlon="-0.036559" maxlat="51.895136" maxlon="-0.036559"/>
</metadata>
<wpt lat="51.895135900" lon="-0.036558800">
 <name>POINT1</name>
 <desc>point 1</desc>
 <sym>Waypoint</sym>
</wpt>
<extensions>
</extensions>
</gpx>
*****************************************************************************/

function CoordinateList()
{
	this.waypoints=new Array();
}

CoordinateList.prototype.addWaypoint = function(c)
{
	this.waypoints.push(c);
}

CoordinateList.prototype.getGPX = function()
{
	//build waypoint list and bounds
	var minlon=999;
	var maxlon=-999;
	var minlat=999;
	var maxlat=-999;
	
	var waypoints="";
	for (var w=0; w<this.waypoints.length; w++)
	{
		var c=this.waypoints[w];
		if (c)
		{
			waypoints=waypoints+'<wpt lat="'+c.latitude+'" lon="'+c.longitude+'">';
			waypoints=waypoints+'<name>POINT'+w+'</name>';
			waypoints=waypoints+'<desc>point '+w+'</desc>';
			waypoints=waypoints+'<sym>Waypoint</sym>';
			waypoints=waypoints+'</wpt>\n';
			
			minlon=Math.min(minlon, c.longitude);
			maxlon=Math.max(maxlon, c.longitude);
			minlat=Math.min(minlat, c.latitude);
			maxlat=Math.max(maxlat, c.latitude);
			
		}
				
	}
	
	
	var gpx="";
	gpx='<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>';
	
	gpx=gpx+'<gpx version="1.1" creator="GeocachingMapLinker" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/1" xsi:schemaLocation="http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd">\n';
	
	gpx=gpx+'<metadata>';
	
	//TODO - put real date here!
	gpx=gpx+'<time>2005-10-25T16:36:44Z</time>';
	gpx=gpx+'<bounds minlat="'+minlat+'" minlon="'+minlon+'" maxlat="'+maxlat+'" maxlon="'+maxlon+'"/>';
	gpx=gpx+'</metadata>\n';

	gpx=gpx+waypoints;

	gpx=gpx+'<extensions></extensions>';
	gpx=gpx+'</gpx>';
	
	return gpx;
}


win.super_submitFormXY=function(e)
{
	var result=false;
	
	if (e.ctrlKey)
	{
		var d1=document.getElementById('gct_northing');
		var d2=document.getElementById('gct_easting');
		var ul=document.getElementById('gct_coordlist');
	
		//create and push coordinate onto our array of coords
		var c=new Object;
		
		c.latitude=d1.getAttribute('latitude');
		c.longitude=d2.getAttribute('longitude');
		c.northing=d1.getAttribute('northing');
		c.easting=d2.getAttribute('easting');
		c.fmtLatitude=d1.innerHTML;
		c.fmtLongitude=d2.innerHTML;
		
		//remove first dummy item?
		var fHasChild=true;
		var li=ul.firstChild;
		
		if (!li.getAttribute('latitude'))
		{
			ul.removeChild(li);
			fHasChild=false;
			
			GM_log("removing child "+li.innerHTML+" which has no attached coordinate");
		}
		
		//create and insert new item
		var li = document.createElement("li");
		li.innerHTML=c.fmtLatitude + " "+c.fmtLongitude;
		li.style.color='silver';
		
		li.setAttribute('latitude', c.latitude);
		li.setAttribute('longitude', c.longitude);
		li.setAttribute('northing', c.northing);
		li.setAttribute('easting', c.easting);
		li.setAttribute('fmtLatitude', c.fmtLatitude);
		li.setAttribute('fmtLongitude', c.fmtLongitude);
		
		if (fHasChild)
		{
			ul.insertBefore(li, ul.firstChild);
		}
		else
		{
			ul.appendChild(li);
		}
		
		
		//ensure only 20 entries are in the list
		var max_entries=20;
		
		//scan first x entries...
		var x=0;
		var gpx=new CoordinateList;
		for (var li=ul.firstChild; li && x<max_entries; li=li.nextSibling)
		{
			var c=new Object;
			
			c.latitude=li.getAttribute('latitude');
			c.longitude=li.getAttribute('longitude');
			c.northing=li.getAttribute('northing');
			c.easting=li.getAttribute('easting');
			c.fmtLatitude=li.getAttribute('fmtLatitude');
			c.fmtLongitude=li.getAttribute('fmtLongitude');
			
			x++;
			gpx.addWaypoint(c);
			
		
		}
		
		
		//remove all that remain
		while (li)
		{
			var del=li;
			li=li.nextSibling;	
			ul.removeChild(del);
		}
		
		//update gpx download
		var gpxdata="data:application/xml-gpx,"+encodeURIComponent(gpx.getGPX());
		
		var gpxlink=document.getElementById('gpxlink');
		gpxlink.innerHTML='<a href="'+gpxdata+'" title="Download list of coordinates as a GPX file">Download GPX</a>';
		
		
	}
	else
	{
		//ctrl key not held, do normal os action
		result=win.original_submitFormXY(e);
	}
	
	
	return result;
}

win.super_showCoords=function(e)
{
	// Calculate easting and northing coordinates
	var im = win.getInfo(e, "i");
	var imageOffsetX = win.getInfo(e, "x");
	var imageOffsetY = win.getInfo(e, "y");
	var easting = Math.round(win.minX + (win.maxX - win.minX) * (imageOffsetX / im.width));
	var northing = Math.round(win.minY + (win.maxY - win.minY) * ((im.height - imageOffsetY) / im.height));

	var d1=document.getElementById('gct_northing');
	var d2=document.getElementById('gct_easting');
	
	
	
	var c=new GT_OSGB;
	c.setGridCoordinates(easting, northing);
	
	var w=c.getWGS84();
	
	
	d1.innerHTML=w.getDMLatitude();
	d1.setAttribute('latitude', w.latitude);
	d1.setAttribute('northing', northing);
	
	d2.innerHTML=w.getDMLongitude();
	d2.setAttribute('longitude', w.longitude);
	d2.setAttribute('easting', easting);
	
	
	
}


//Get-A-Map calls this to hide the map, we take opportunity to hide our
//indicator marker too...
win.super_setRetImg=function()
{
	var result=win.original_setRetImg();
	
	//hide our marker
	var indicator=document.getElementById('monkeymagic');
	if (indicator)
	{
		indicator.style.display='none';
	}
	
	return result;
}

//Get-A-Map calls this to update the display, so we do the magic to place an
//indicator marker onto the map
win.super_updateInfo=function(winWid, srcDat, coordStr, lastSearch, a, b, c, d, e)
{
	//GM_log("winWid="+winWid+" srcDat="+srcDat+" coordStr="+coordStr+" lastSearch="+lastSearch+" a="+a+" b="+b+" c="+c+" d="+d+" e="+e);
	
	//call original function
	var result=win.original_updateInfo(winWid, srcDat, coordStr, lastSearch, a, b, c, d, e);
	
	
	//get grid reference to plot
	var gridref=GM_getValue('gridref', '');
	if (gridref.length)
	{
		var osgb=new GT_OSGB();
		osgb.parseGridRef(gridref);
		
		//for TL1801127713
		//gives     520737, 226196
		//should be 518011, 227713
		//out by 2726,-1517
	
		//GM_log("gridref="+gridref);

		//get the info original code pasted in
		var mapInfo=document.getElementById('mapInfo')
		var info=mapInfo.innerHTML;

		//parse the scale
		var metres_per_pixel=5;
		var pattern = /Window width - (\d+) km/;
		if (matches=info.match(pattern))
		{
			var w=parseInt(matches[1]);
			metres_per_pixel=(w*1000)/400;
		}


		//GM_log("metres per pixel "+metres_per_pixel);

		//point 0,0 on the image can be obtained from the OS script
		//variables minX,maxY - frm this we can work out the offset
		//in metres to the indicator point
		var mx=osgb.eastings - win.minX;
		var my=win.maxY - osgb.northings;

		//GM_log("osgb.eastings="+osgb.eastings+" osgb.northings="+osgb.northings);
		//GM_log("minX="+win.minX+" maxY="+win.maxY);
		//GM_log("mx="+mx+" my="+my);

		//and now it's just a simple conversion to pixels
		var px=mx/metres_per_pixel;
		var py=my/metres_per_pixel;

		//get the map img and calculate the absolute position of our marker
		//<img src="images/ret.gif" name="imgMap" width="400" height="400" border="0">
		
		//in recent combinations of FF/GM this fails to get the map image
		var mapImg = document.images['imgMap'];	
		
		//if the above fails, we can iterate through and find it ourselves
		if (!mapImg)
		{
			var i;
			for (i=0; i<document.images.length; i++)
			{
				var img=document.images[i];
				if (img.name=='imgMap')
				{
					mapImg=img;
					break;
				}
			}
		}
		
		if (mapImg)
		{
			var imgx=win._findPosX(mapImg) + px;
			var imgy=win._findPosY(mapImg) +py;
	
			
			
	
			var indicator=document.getElementById('monkeymagic');
			if (!indicator)
			{
				//create our indicator - a transparent image, absolutely positioned
				indicator=document.createElement('img');
				indicator.src='data:image/gif,GIF89a%0D%00%0D%00%80%00%00%FF%00%00%FF%FF%FF!%F9%04%01%00%00%01%00%2C%00%00%00%00%0D%00%0D%00%00%02%1A%84%8F%A9%18%CB%ED%40%98q%D2zq%3Cp%A7%CEp%99qY%99%25%99%94Y%00%00%3B';
				indicator.id='monkeymagic';
				indicator.style.position="absolute";
				indicator.style.display='none';
				
				//insert into document
				mapInfo.parentNode.insertBefore(indicator, mapInfo);
			}
	
			//set the indicator position
			indicator.style.top=imgy;
			indicator.style.left=imgx;
			indicator.style.display='';
		}
		else
		{
			GM_log("map not found!");
		}

	}
	
	
	
	
	
	
	return result;
}


/*****************************************************************************
*
* GeoTools script inlined here....
* Contact paul@elphin.com for most recent version of this script...
*
*****************************************************************************/


/*****************************************************************************
*
* GT_OSGB holds OSGB grid coordinates
*
*****************************************************************************/

function GT_OSGB()
{
	this.northings=0;
	this.eastings=0;
	this.status="Undefined";
}

GT_OSGB.prefixes = new Array (
	new Array("SV","SW","SX","SY","SZ","TV","TW"),
	new Array("SQ","SR","SS","ST","SU","TQ","TR"),
	new Array("SL","SM","SN","SO","SP","TL","TM"),
	new Array("SF","SG","SH","SJ","SK","TF","TG"),
	new Array("SA","SB","SC","SD","SE","TA","TB"),
	new Array("NV","NW","NX","NY","NZ","OV","OW"),
	new Array("NQ","NR","NS","NT","NU","OQ","OR"),
	new Array("NL","NM","NN","NO","NP","OL","OM"),
	new Array("NF","NG","NH","NJ","NK","OF","OG"),
	new Array("NA","NB","NC","ND","NE","OA","OB"),
	new Array("HV","HW","HX","HY","HZ","JV","JW"),
	new Array("HQ","HR","HS","HT","HU","JQ","JR"),
	new Array("HL","HM","HN","HO","HP","JL","JM"));
			

GT_OSGB.prototype.setGridCoordinates = function(eastings,northings)
{
	this.northings=northings;
	this.eastings=eastings;
	this.status="OK";
}

GT_OSGB.prototype.setError = function(msg)
{
	this.status=msg;
}

GT_OSGB.prototype._zeropad = function(num, len)
{
	var str=new String(num);
	while (str.length<len)
	{
		str='0'+str;
	}
	return str;
}

GT_OSGB.prototype.getGridRef = function(precision)
{
	
	

	if (precision<0)
		precision=0;
	if (precision>5)
		precision=5;
		
	var e="";

	var n="";
	if (precision>0)
	{
		var y=Math.floor(this.northings/100000);
		var x=Math.floor(this.eastings/100000);


		var e=Math.round(this.eastings%100000);
		var n=Math.round(this.northings%100000);


		var div=(5-precision);
		e=Math.round(e / Math.pow(10, div));
		n=Math.round(n / Math.pow(10, div));
	}
	
	var prefix=GT_OSGB.prefixes[y][x];
	
    return prefix+this._zeropad(e, precision)+this._zeropad(n, precision);
}

GT_OSGB.prototype.parseGridRef = function(landranger)
{
	var ok=false;

	
	this.northings=0;
	this.eastings=0;
	
	var precision;

	for (precision=5; precision>=1; precision--)
	{
		var pattern = new RegExp("^([A-Z]{2})\\s*(\\d{"+precision+"})\\s*(\\d{"+precision+"})$", "i")
		var gridRef = landranger.match(pattern);
		if (gridRef)
		{
			var gridSheet = gridRef[1];
			var gridEast=0;
			var gridNorth=0;
			
			//5x1 4x10 3x100 2x1000 1x10000 
			if (precision>0)
			{
				var mult=Math.pow(10, 5-precision);
				gridEast=parseInt(gridRef[2],10) * mult;
				gridNorth=parseInt(gridRef[3],10) * mult;
			}
			
			var x,y;
			search: for(y=0; y<GT_OSGB.prefixes.length; y++) 
			{
				for(x=0; x<GT_OSGB.prefixes[y].length; x++)
					if (GT_OSGB.prefixes[y][x] == gridSheet) {
						this.eastings = (x * 100000)+gridEast;
						this.northings = (y * 100000)+gridNorth;
						ok=true;
						break search;
					}
			
			}
		
		}
	}

	

	return ok;
}


GT_OSGB.prototype.getWGS84 = function()
{
	
	var height = 0;

	var lat1 = GT_Math.E_N_to_Lat (this.eastings,this.northings,6377563.396,6356256.910,400000,-100000,0.999601272,49.00000,-2.00000);
	var lon1 = GT_Math.E_N_to_Long(this.eastings,this.northings,6377563.396,6356256.910,400000,-100000,0.999601272,49.00000,-2.00000);

	var x1 = GT_Math.Lat_Long_H_to_X(lat1,lon1,height,6377563.396,6356256.910);
	var y1 = GT_Math.Lat_Long_H_to_Y(lat1,lon1,height,6377563.396,6356256.910);
	var z1 = GT_Math.Lat_H_to_Z     (lat1,      height,6377563.396,6356256.910);

	var x2 = GT_Math.Helmert_X(x1,y1,z1,446.448 ,0.2470,0.8421,-20.4894);
	var y2 = GT_Math.Helmert_Y(x1,y1,z1,-125.157,0.1502,0.8421,-20.4894);
	var z2 = GT_Math.Helmert_Z(x1,y1,z1,542.060 ,0.1502,0.2470,-20.4894);

	var latitude = GT_Math.XYZ_to_Lat(x2,y2,z2,6378137.000,6356752.313);
	var longitude = GT_Math.XYZ_to_Long(x2,y2);

	var wgs84=new GT_WGS84();
	wgs84.setDegrees(latitude, longitude);
	return wgs84;
}

/*****************************************************************************
*
* GT_WGS84 holds WGS84 latitude and longitude
*
*****************************************************************************/

function GT_WGS84()
{
	this.latitude=0;
	this.longitude=0;
}

GT_WGS84.prototype.setDegrees = function(latitude,longitude)
{
	this.latitude=latitude;
	this.longitude=longitude;
}

GT_WGS84.prototype._DMFmt = function(d, posSymbol,negSymbol)
{
	var symbol=posSymbol;
	if (d<0)
	{
		d=Math.abs(d);
		symbol=negSymbol;
	}
	var deg=Math.floor(d);
	var mins=Math.round((d-deg)*60000)/1000;
	
 	var minsfmt=new String(mins);
 	var bits=minsfmt.split(".");
 	if (bits.length>1)
 	{
	 	if (bits[1].length==2)
	 		minsfmt=minsfmt+'0';
		if (bits[1].length==1)
	 		minsfmt=minsfmt+'00';
		if (bits[1].length==0)
	 		minsfmt=minsfmt+'000';
 	}
	return symbol+' '+deg+'&deg;'+' '+minsfmt;
	
}

GT_WGS84.prototype.getDMLatitude = function()
{
	if (isNaN(this.latitude))
		return "n/a";
	else
		return this._DMFmt(this.latitude, 'N', 'S');
}
GT_WGS84.prototype.getDMLongitude = function()
{
	if (isNaN(this.longitude))
		return "";
	else
		return this._DMFmt(this.longitude, 'E', 'W');
}

GT_WGS84.prototype.parseString = function(text)
{
	var ok=false;

	var str=new String(text);

	//N 51ý 53.947 W 000ý 10.018

	var pattern = /([ns])\s*(\d+)[^\d]+(\d+\.\d+)\s+([we])\s*(\d+)[^\d]+(\d+\.\d+)/i;
	var matches=str.match(pattern);
	if (matches)
	{
		ok=true;
		var latsign=(matches[1]=='s' || matches[1]=='S')?-1:1;
		var longsign=(matches[4]=='w' || matches[4]=='W')?-1:1;
		
		var d1=parseFloat(matches[2]);
		var m1=parseFloat(matches[3]);
		var d2=parseFloat(matches[5]);
		var m2=parseFloat(matches[6]);
		
		this.latitude=latsign * (d1 + (m1/60.0));
		this.longitude=longsign * (d2 + (m2/60.0));
		
		
	}
	
	return ok;
}



GT_WGS84.prototype.isGreatBritain = function()
{
	return this.latitude > 49 &&
		this.latitude < 62 &&
		this.longitude > -9.5 &&
		this.longitude < 2.3;
}

GT_WGS84.prototype.isIreland = function()
{
	return this.latitude > 51.2 &&
		this.latitude < 55.73 &&
		this.longitude > -12.2 &&
		this.longitude < -4.8;
}



GT_WGS84.prototype.getOSGB = function()
{
	var osgb=new GT_OSGB();
	if (this.isGreatBritain())
	{
		var height = 0;
		
		var x1 = GT_Math.Lat_Long_H_to_X(this.latitude,this.longitude,height,6378137.00,6356752.313);
		var y1 = GT_Math.Lat_Long_H_to_Y(this.latitude,this.longitude,height,6378137.00,6356752.313);
		var z1 = GT_Math.Lat_H_to_Z     (this.latitude,      height,6378137.00,6356752.313);

		var x2 = GT_Math.Helmert_X(x1,y1,z1,-446.448,-0.2470,-0.8421,20.4894);
		var y2 = GT_Math.Helmert_Y(x1,y1,z1, 125.157,-0.1502,-0.8421,20.4894);
		var z2 = GT_Math.Helmert_Z(x1,y1,z1,-542.060,-0.1502,-0.2470,20.4894);

		var latitude2  = GT_Math.XYZ_to_Lat (x2,y2,z2,6377563.396,6356256.910);
		var longitude2 = GT_Math.XYZ_to_Long(x2,y2);

		var e = GT_Math.Lat_Long_to_East (latitude2,longitude2,6377563.396,6356256.910,400000,0.999601272,49.00000,-2.00000);
		var n = GT_Math.Lat_Long_to_North(latitude2,longitude2,6377563.396,6356256.910,400000,-100000,0.999601272,49.00000,-2.00000);

		osgb.setGridCoordinates(Math.round(e), Math.round(n));
	}
	else
	{
		osgb.setError("Coordinate not within Great Britain");
	}

	return osgb;
}




/*****************************************************************************
*
* GT_Math is a collection of static methods doing all the nasty sums
*
*****************************************************************************/

//GT_Math is just namespace for all the nasty maths functions
function GT_Math()
{
}

GT_Math.E_N_to_Lat = function(East, North, a, b, e0, n0, f0, PHI0, LAM0)
{
	//Un-project Transverse Mercator eastings and northings back to latitude.
	//Input: - _
	//eastings (East) and northings (North) in meters; _
	//ellipsoid axis dimensions (a & b) in meters; _
	//eastings (e0) and northings (n0) of false origin in meters; _
	//central meridian scale factor (f0) and _
	//latitude (PHI0) and longitude (LAM0) of false origin in decimal degrees.

	//'REQUIRES THE "Marc" AND "InitialLat" FUNCTIONS

	//Convert angle measures to radians
    var Pi = 3.14159265358979;
    var RadPHI0 = PHI0 * (Pi / 180);
    var RadLAM0 = LAM0 * (Pi / 180);

	//Compute af0, bf0, e squared (e2), n and Et
    var af0 = a * f0;
    var bf0 = b * f0;
    var e2 = (Math.pow(af0,2) - Math.pow(bf0,2)) / Math.pow(af0,2);
    var n = (af0 - bf0) / (af0 + bf0);
    var Et = East - e0;

	//Compute initial value for latitude (PHI) in radians
    var PHId = GT_Math.InitialLat(North, n0, af0, RadPHI0, n, bf0);
    
	//Compute nu, rho and eta2 using value for PHId
    var nu = af0 / (Math.sqrt(1 - (e2 * ( Math.pow(Math.sin(PHId),2)))));
    var rho = (nu * (1 - e2)) / (1 - (e2 * Math.pow(Math.sin(PHId),2)));
    var eta2 = (nu / rho) - 1;
    
	//Compute Latitude
    var VII = (Math.tan(PHId)) / (2 * rho * nu);
    var VIII = ((Math.tan(PHId)) / (24 * rho * Math.pow(nu,3))) * (5 + (3 * (Math.pow(Math.tan(PHId),2))) + eta2 - (9 * eta2 * (Math.pow(Math.tan(PHId),2))));
    var IX = ((Math.tan(PHId)) / (720 * rho * Math.pow(nu,5))) * (61 + (90 * ((Math.tan(PHId)) ^ 2)) + (45 * (Math.pow(Math.tan(PHId),4))));
    
    var E_N_to_Lat = (180 / Pi) * (PHId - (Math.pow(Et,2) * VII) + (Math.pow(Et,4) * VIII) - ((Et ^ 6) * IX));
	
	return (E_N_to_Lat);
}

GT_Math.E_N_to_Long = function(East, North, a, b, e0, n0, f0, PHI0, LAM0)
{
	//Un-project Transverse Mercator eastings and northings back to longitude.
	//Input: - _
	//eastings (East) and northings (North) in meters; _
	//ellipsoid axis dimensions (a & b) in meters; _
	//eastings (e0) and northings (n0) of false origin in meters; _
	//central meridian scale factor (f0) and _
	//latitude (PHI0) and longitude (LAM0) of false origin in decimal degrees.

	//REQUIRES THE "Marc" AND "InitialLat" FUNCTIONS

	//Convert angle measures to radians
    var Pi = 3.14159265358979;
    var RadPHI0 = PHI0 * (Pi / 180);
    var RadLAM0 = LAM0 * (Pi / 180);

	//Compute af0, bf0, e squared (e2), n and Et
    var af0 = a * f0;
    var bf0 = b * f0;
    var e2 = (Math.pow(af0,2) - Math.pow(bf0,2)) / Math.pow(af0,2);
    var n = (af0 - bf0) / (af0 + bf0);
    var Et = East - e0;

	//Compute initial value for latitude (PHI) in radians
    var PHId = GT_Math.InitialLat(North, n0, af0, RadPHI0, n, bf0);
    
	//Compute nu, rho and eta2 using value for PHId
   	var nu = af0 / (Math.sqrt(1 - (e2 * (Math.pow(Math.sin(PHId),2)))));
    var rho = (nu * (1 - e2)) / (1 - (e2 * Math.pow(Math.sin(PHId),2)));
    var eta2 = (nu / rho) - 1;

	//Compute Longitude
    var X = (Math.pow(Math.cos(PHId),-1)) / nu;
    var XI = ((Math.pow(Math.cos(PHId),-1)) / (6 * Math.pow(nu,3))) * ((nu / rho) + (2 * (Math.pow(Math.tan(PHId),2))));
    var XII = ((Math.pow(Math.cos(PHId),-1)) / (120 * Math.pow(nu,5))) * (5 + (28 * (Math.pow(Math.tan(PHId),2))) + (24 * (Math.pow(Math.tan(PHId),4))));
    var XIIA = ((Math.pow(Math.cos(PHId),-1)) / (5040 * Math.pow(nu,7))) * (61 + (662 * (Math.pow(Math.tan(PHId),2))) + (1320 * (Math.pow(Math.tan(PHId),4))) + (720 * (Math.pow(Math.tan(PHId),6))));

    var E_N_to_Long = (180 / Pi) * (RadLAM0 + (Et * X) - (Math.pow(Et,3) * XI) + (Math.pow(Et,5) * XII) - (Math.pow(Et,7) * XIIA));
	
	return E_N_to_Long;
}

GT_Math.InitialLat = function(North, n0, afo, PHI0, n, bfo)
{
	//Compute initial value for Latitude (PHI) IN RADIANS.
	//Input: - _
	//northing of point (North) and northing of false origin (n0) in meters; _
	//semi major axis multiplied by central meridian scale factor (af0) in meters; _
	//latitude of false origin (PHI0) IN RADIANS; _
	//n (computed from a, b and f0) and _
	//ellipsoid semi major axis multiplied by central meridian scale factor (bf0) in meters.
 
	//REQUIRES THE "Marc" FUNCTION
	//THIS FUNCTION IS CALLED BY THE "E_N_to_Lat", "E_N_to_Long" and "E_N_to_C" FUNCTIONS
	//THIS FUNCTION IS ALSO USED ON IT'S OWN IN THE  "Projection and Transformation Calculations.xls" SPREADSHEET

	//First PHI value (PHI1)
   	var PHI1 = ((North - n0) / afo) + PHI0;
    
	//Calculate M
    var M = GT_Math.Marc(bfo, n, PHI0, PHI1);
    
	//Calculate new PHI value (PHI2)
    var PHI2 = ((North - n0 - M) / afo) + PHI1;
    
	//Iterate to get final value for InitialLat
	while (Math.abs(North - n0 - M) > 0.00001) 
	{
        PHI2 = ((North - n0 - M) / afo) + PHI1;
        M = GT_Math.Marc(bfo, n, PHI0, PHI2);
        PHI1 = PHI2;
	}    
    return PHI2;
}

GT_Math.Lat_Long_H_to_X = function(PHI, LAM, H, a, b)
{
	// Convert geodetic coords lat (PHI), long (LAM) and height (H) to cartesian X coordinate.
	// Input: - _
	//    Latitude (PHI)& Longitude (LAM) both in decimal degrees; _
	//  Ellipsoidal height (H) and ellipsoid axis dimensions (a & b) all in meters.

	// Convert angle measures to radians
    var Pi = 3.14159265358979;
    var RadPHI = PHI * (Pi / 180);
    var RadLAM = LAM * (Pi / 180);

	// Compute eccentricity squared and nu
    var e2 = (Math.pow(a,2) - Math.pow(b,2)) / Math.pow(a,2);
    var V = a / (Math.sqrt(1 - (e2 * (  Math.pow(Math.sin(RadPHI),2)))));

	// Compute X
    return (V + H) * (Math.cos(RadPHI)) * (Math.cos(RadLAM));
}


GT_Math.Lat_Long_H_to_Y =function(PHI, LAM, H, a, b) 
{
	// Convert geodetic coords lat (PHI), long (LAM) and height (H) to cartesian Y coordinate.
	// Input: - _
	// Latitude (PHI)& Longitude (LAM) both in decimal degrees; _
	// Ellipsoidal height (H) and ellipsoid axis dimensions (a & b) all in meters.

	// Convert angle measures to radians
    var Pi = 3.14159265358979;
    var RadPHI = PHI * (Pi / 180);
    var RadLAM = LAM * (Pi / 180);

	// Compute eccentricity squared and nu
    var e2 = (Math.pow(a,2) - Math.pow(b,2)) / Math.pow(a,2);
    var V = a / (Math.sqrt(1 - (e2 * (  Math.pow(Math.sin(RadPHI),2))) ));

	// Compute Y
    return (V + H) * (Math.cos(RadPHI)) * (Math.sin(RadLAM));
}


GT_Math.Lat_H_to_Z =function(PHI, H, a, b)
{
	// Convert geodetic coord components latitude (PHI) and height (H) to cartesian Z coordinate.
	// Input: - _
	//    Latitude (PHI) decimal degrees; _
	// Ellipsoidal height (H) and ellipsoid axis dimensions (a & b) all in meters.

	// Convert angle measures to radians
    var Pi = 3.14159265358979;
    var RadPHI = PHI * (Pi / 180);

	// Compute eccentricity squared and nu
    var e2 = (Math.pow(a,2) - Math.pow(b,2)) / Math.pow(a,2);
    var V = a / (Math.sqrt(1 - (e2 * (  Math.pow(Math.sin(RadPHI),2)) )));

	// Compute X
    return ((V * (1 - e2)) + H) * (Math.sin(RadPHI));
}


GT_Math.Helmert_X =function(X,Y,Z,DX,Y_Rot,Z_Rot,s) 
{

	// (X, Y, Z, DX, Y_Rot, Z_Rot, s)
	// Computed Helmert transformed X coordinate.
	// Input: - _
	//    cartesian XYZ coords (X,Y,Z), X translation (DX) all in meters ; _
	// Y and Z rotations in seconds of arc (Y_Rot, Z_Rot) and scale in ppm (s).

	// Convert rotations to radians and ppm scale to a factor
	var Pi = 3.14159265358979;
	var sfactor = s * 0.000001;

	var RadY_Rot = (Y_Rot / 3600) * (Pi / 180);

	var RadZ_Rot = (Z_Rot / 3600) * (Pi / 180);

	//Compute transformed X coord
    return  (X + (X * sfactor) - (Y * RadZ_Rot) + (Z * RadY_Rot) + DX);
}


GT_Math.Helmert_Y =function(X,Y,Z,DY,X_Rot,Z_Rot,s)
{
	// (X, Y, Z, DY, X_Rot, Z_Rot, s)
	// Computed Helmert transformed Y coordinate.
	// Input: - _
	//    cartesian XYZ coords (X,Y,Z), Y translation (DY) all in meters ; _
	//  X and Z rotations in seconds of arc (X_Rot, Z_Rot) and scale in ppm (s).

	// Convert rotations to radians and ppm scale to a factor
	var Pi = 3.14159265358979;
	var sfactor = s * 0.000001;
	var RadX_Rot = (X_Rot / 3600) * (Pi / 180);
	var RadZ_Rot = (Z_Rot / 3600) * (Pi / 180);

	// Compute transformed Y coord
	return (X * RadZ_Rot) + Y + (Y * sfactor) - (Z * RadX_Rot) + DY;

}



GT_Math.Helmert_Z =function(X, Y, Z, DZ, X_Rot, Y_Rot, s)
{
	// (X, Y, Z, DZ, X_Rot, Y_Rot, s)
	// Computed Helmert transformed Z coordinate.
	// Input: - _
	//    cartesian XYZ coords (X,Y,Z), Z translation (DZ) all in meters ; _
	// X and Y rotations in seconds of arc (X_Rot, Y_Rot) and scale in ppm (s).
	// 
	// Convert rotations to radians and ppm scale to a factor
	var Pi = 3.14159265358979;
	var sfactor = s * 0.000001;
	var RadX_Rot = (X_Rot / 3600) * (Pi / 180);
	var RadY_Rot = (Y_Rot / 3600) * (Pi / 180);

	// Compute transformed Z coord
	return (-1 * X * RadY_Rot) + (Y * RadX_Rot) + Z + (Z * sfactor) + DZ;
} 






GT_Math.XYZ_to_Lat =function(X, Y, Z, a, b) 
{
	// Convert XYZ to Latitude (PHI) in Dec Degrees.
	// Input: - _
	// XYZ cartesian coords (X,Y,Z) and ellipsoid axis dimensions (a & b), all in meters.

	// THIS FUNCTION REQUIRES THE "Iterate_XYZ_to_Lat" FUNCTION
	// THIS FUNCTION IS CALLED BY THE "XYZ_to_H" FUNCTION

    var RootXYSqr = Math.sqrt(Math.pow(X,2) + Math.pow(Y,2));
    var e2 = (Math.pow(a,2) - Math.pow(b,2)) / Math.pow(a,2);
    var PHI1 = Math.atan2(Z , (RootXYSqr * (1 - e2)) );
    
    var PHI = GT_Math.Iterate_XYZ_to_Lat(a, e2, PHI1, Z, RootXYSqr);
    
    var Pi = 3.14159265358979;
    
    return PHI * (180 / Pi);
}


GT_Math.Iterate_XYZ_to_Lat =function(a, e2, PHI1, Z, RootXYSqr) 
{
	// Iteratively computes Latitude (PHI).
	// Input: - _
	//    ellipsoid semi major axis (a) in meters; _
	//    eta squared (e2); _
	//    estimated value for latitude (PHI1) in radians; _
	//    cartesian Z coordinate (Z) in meters; _
	// RootXYSqr computed from X & Y in meters.

	// THIS FUNCTION IS CALLED BY THE "XYZ_to_PHI" FUNCTION
	// THIS FUNCTION IS ALSO USED ON IT'S OWN IN THE _
	// "Projection and Transformation Calculations.xls" SPREADSHEET


    var V = a / (Math.sqrt(1 - (e2 * Math.pow(Math.sin(PHI1),2))));
    var PHI2 = Math.atan2((Z + (e2 * V * (Math.sin(PHI1)))) , RootXYSqr);
    
    while (Math.abs(PHI1 - PHI2) > 0.000000001) {
        PHI1 = PHI2;
        V = a / (Math.sqrt(1 - (e2 * Math.pow(Math.sin(PHI1),2))));
        PHI2 = Math.atan2((Z + (e2 * V * (Math.sin(PHI1)))) , RootXYSqr);
    }

    return PHI2;
}


GT_Math.XYZ_to_Long =function (X, Y) 
{
	// Convert XYZ to Longitude (LAM) in Dec Degrees.
	// Input: - _
	// X and Y cartesian coords in meters.

    var Pi = 3.14159265358979;
    return Math.atan2(Y , X) * (180 / Pi);
}

GT_Math.Marc =function (bf0, n, PHI0, PHI) 
{
	//Compute meridional arc.
	//Input: - _
	// ellipsoid semi major axis multiplied by central meridian scale factor (bf0) in meters; _
	// n (computed from a, b and f0); _
	// lat of false origin (PHI0) and initial or final latitude of point (PHI) IN RADIANS.

	//THIS FUNCTION IS CALLED BY THE - _
	// "Lat_Long_to_North" and "InitialLat" FUNCTIONS
	// THIS FUNCTION IS ALSO USED ON IT'S OWN IN THE "Projection and Transformation Calculations.xls" SPREADSHEET

		return bf0 * (((1 + n + ((5 / 4) * Math.pow(n,2)) + ((5 / 4) * Math.pow(n,3))) * (PHI - PHI0)) - (((3 * n) + (3 * Math.pow(n,2)) + ((21 / 8) * Math.pow(n,3))) * (Math.sin(PHI - PHI0)) * (Math.cos(PHI + PHI0))) + ((((15 / 8
	) * Math.pow(n,2)) + ((15 / 8) * Math.pow(n,3))) * (Math.sin(2 * (PHI - PHI0))) * (Math.cos(2 * (PHI + PHI0)))) - (((35 / 24) * Math.pow(n,3)) * (Math.sin(3 * (PHI - PHI0))) * (Math.cos(3 * (PHI + PHI0)))));
}




GT_Math.Lat_Long_to_East =function (PHI, LAM, a, b, e0, f0, PHI0, LAM0)
{
	//Project Latitude and longitude to Transverse Mercator eastings.
	//Input: - _
	//    Latitude (PHI) and Longitude (LAM) in decimal degrees; _
	//    ellipsoid axis dimensions (a & b) in meters; _
	//    eastings of false origin (e0) in meters; _
	//    central meridian scale factor (f0); _
	// latitude (PHI0) and longitude (LAM0) of false origin in decimal degrees.

	// Convert angle measures to radians
    var Pi = 3.14159265358979;
    var RadPHI = PHI * (Pi / 180);
    var RadLAM = LAM * (Pi / 180);
    var RadPHI0 = PHI0 * (Pi / 180);
    var RadLAM0 = LAM0 * (Pi / 180);

    var af0 = a * f0;
    var bf0 = b * f0;
    var e2 = (Math.pow(af0,2) - Math.pow(bf0,2)) / Math.pow(af0,2);
    var n = (af0 - bf0) / (af0 + bf0);
    var nu = af0 / (Math.sqrt(1 - (e2 * Math.pow(Math.sin(RadPHI),2) )));
    var rho = (nu * (1 - e2)) / (1 - (e2 * Math.pow(Math.sin(RadPHI),2) ));
    var eta2 = (nu / rho) - 1;
    var p = RadLAM - RadLAM0;
    
    var IV = nu * (Math.cos(RadPHI));
    var V = (nu / 6) * ( Math.pow(Math.cos(RadPHI),3)) * ((nu / rho) - (Math.pow(Math.tan(RadPHI),2)));
    var VI = (nu / 120) * (Math.pow(Math.cos(RadPHI),5)) * (5 - (18 * (Math.pow(Math.tan(RadPHI),2))) + (Math.pow(Math.tan(RadPHI),4)) + (14 * eta2) - (58 * (Math.pow(Math.tan(RadPHI),2)) * eta2));
    
    return e0 + (p * IV) + (Math.pow(p,3) * V) + (Math.pow(p,5) * VI);
}


GT_Math.Lat_Long_to_North =function (PHI, LAM, a, b, e0, n0, f0, PHI0, LAM0) 
{
	// Project Latitude and longitude to Transverse Mercator northings
	// Input: - _
	// Latitude (PHI) and Longitude (LAM) in decimal degrees; _
	// ellipsoid axis dimensions (a & b) in meters; _
	// eastings (e0) and northings (n0) of false origin in meters; _
	// central meridian scale factor (f0); _
	// latitude (PHI0) and longitude (LAM0) of false origin in decimal degrees.

	// REQUIRES THE "Marc" FUNCTION

	// Convert angle measures to radians
    var Pi = 3.14159265358979;
    var RadPHI = PHI * (Pi / 180);
    var RadLAM = LAM * (Pi / 180);
    var RadPHI0 = PHI0 * (Pi / 180);
    var RadLAM0 = LAM0 * (Pi / 180);
    
    var af0 = a * f0;
    var bf0 = b * f0;
    var e2 = (Math.pow(af0,2) - Math.pow(bf0,2)) / Math.pow(af0,2);
    var n = (af0 - bf0) / (af0 + bf0);
    var nu = af0 / (Math.sqrt(1 - (e2 * Math.pow(Math.sin(RadPHI),2))));
    var rho = (nu * (1 - e2)) / (1 - (e2 * Math.pow(Math.sin(RadPHI),2)));
    var eta2 = (nu / rho) - 1;
    var p = RadLAM - RadLAM0;
    var M = GT_Math.Marc(bf0, n, RadPHI0, RadPHI);
    
    var I = M + n0;
    var II = (nu / 2) * (Math.sin(RadPHI)) * (Math.cos(RadPHI));
    var III = ((nu / 24) * (Math.sin(RadPHI)) * (Math.pow(Math.cos(RadPHI),3))) * (5 - (Math.pow(Math.tan(RadPHI),2)) + (9 * eta2));
    var IIIA = ((nu / 720) * (Math.sin(RadPHI)) * (Math.pow(Math.cos(RadPHI),5))) * (61 - (58 * (Math.pow(Math.tan(RadPHI),2))) + (Math.pow(Math.tan(RadPHI),4)));
    
    return I + (Math.pow(p,2) * II) + (Math.pow(p,4) * III) + (Math.pow(p,6) * IIIA);
}








//we're a multi-domain script, so here we decide what we're going to do...
var url=new String(document.location);
if (url.match(/^http:\/\/www\.geocaching\.com\/seek\/cache_details\.aspx.*/i))
{
	win.enhanceGeocacheListing();
}
else if (url.match(/^http:\/\/getamap\.ordnancesurvey\.co\.uk\/getamap\/map\.htm/i))
{
	win.enhanceOSMap();
}

