// this is a Google Maps functions collection for GLASS TrailWise data, works with gmap.htm
//	#### Change Control ####
//	Date 		Author		Change
//	15/11/06	RK			Initial development
//	19/4/07		SS			Rework for Trailwise site
//	10/07		RK			Redeveloped as version 2
//	2/09		RK			change max radius sizing, remove 'assoc tracks' button
//	this version 2.0.7 - picklist disabled, assoc list disabled
//  ########################################


// ==== to populate previously declared global things, and enter reqd mode
function SetUp(){

 //test if we can work with this browser
	if (GBrowserIsCompatible()) { 

 //global template marker icon
		iconx=new GIcon();
		iconx.image=imagelib+"mm_20_info.png";
		iconx.shadow=imagelib+"mm_20_shadow.png";
		iconx.iconSize=new GSize(12, 20);
		iconx.shadowSize=new GSize(22, 20);
		iconx.iconAnchor=new GPoint(6, 20);
		iconx.infoWindowAnchor=new GPoint(5, 1);
		iconx.transparent=imagelib+"mm_20_trans.png";	//improve clicks in IE
		iconx.imageMap=new Array(0,0,12,0,12,20,0,20); //improve clicks in others
 //other globals
		maxbounds=new GLatLngBounds(); // for autozoom

// ==== Display the base map, with user controls, and set an initial default location 
		TWmap = new GMap2(document.getElementById('mapcanvas'), {draggableCursor:'crosshair'});
		TWmap.setCenter(new GLatLng(55.0,-2.0),5);
		TWmap.addMapType(G_PHYSICAL_MAP);
	  	TWmap.addControl(new GLargeMapControl());
		TWmap.enableGoogleBar(); // for local search
		TWmap.addControl(new GScaleControl(), new GControlPosition(G_ANCHOR_BOTTOM_LEFT,new GSize(0,32)));
		TWmap.addControl(new GratControl()); //custom control
			//make sure maptype gets 'in front' of the custom for drop-down
		TWmap.addControl(new GMenuMapTypeControl()); // undocumented feature
//		TWmap.addControl(new GMapTypeControl()); //documented alternative
		TWmap.GratOn=false;
		TWmap.PinsOn=true; //for the toggles
		TWmap.PickMode=false;
		TWmap.oldButts='';

	// ===== set up general map click listener
		GEvent.addListener(TWmap, 'click', function(overlay,point) {
			if (point && !overlay) { //--clicked bare map
				mapClicked(point)
			} else if (overlay.uid && !point) {	//--clicked a line but await point being fed
			} else if (overlay.uid && point) { //--clicked a TW line
				if (!TWmap.PickMode) { //--normal TW line infos
					var i=overlay.infodex;
					var tabs=[];
					tabs.push(new GInfoWindowTab('info',infowin[i]));
					if (infowin2[i]) { tabs.push(new GInfoWindowTab('records',infowin2[i])) }
					TWmap.openInfoWindowTabsHtml(point,tabs,{autoScroll:true, maxWidth:250});
					document.getElementById("location").innerHTML=bottmess;
					document.getElementById("sidepanel").innerHTML=panel_html[overlay.paneldex];
				} else { //--TW line for pick list
					PickThis(overlay);
				}
			} else if (overlay.infodex) { //--must be a TW marker then
				var i=overlay.infodex;
				var tabs=[];
				tabs.push(new GInfoWindowTab('info',infowin[i]));
				if (infowin2[i]) { tabs.push(new GInfoWindowTab('more',infowin2[i])) }
				overlay.openInfoWindowTabsHtml(tabs,{autoScroll:true, maxWidth:250});
				document.getElementById("location").innerHTML=bottmess;
				if (overlay.paneldex) {
					document.getElementById("sidepanel").innerHTML=panel_html[overlay.paneldex];
				} else {
					document.getElementById("sidepanel").innerHTML=sidemess;
				}
			} else if (overlay.grid) { //--must be a graticule then
					//like to do gridref but cant get point yet
				document.getElementById("sidepanel").innerHTML=sidemess;
				document.getElementById("location").innerHTML='Clicked a gridline';
			} else { //else that click wasnt for us then
			}
		} ) //end listener;

	// ==== set up end-move listener - a kludge for grid redraw errors
		GEvent.addListener(TWmap, "moveend", function() {
			if (TWmap.GratOn) { osGrat.realdraw() }
		} ) //end listener


	// ==== Build the initial fetch target from HTML querystring, min validation
		var q_uid=getQS("uid");
		var q_ref=getQS("ref");
		var q_sq=getQS("sq");
		var q_assoc=getQS("assoc");
		if (q_uid) {	//single track mode
			var xmlfetch = xmlsource + '&uid=' + q_uid;
		} else if (q_ref) {	//group mode
			var xmlfetch = xmlsource + '&ref=' + q_ref;
		} else if (q_sq) { //must be area
			var q_e = getQS("est");
			var q_n = getQS("nth");
			var q_dist = getQS("distance");
			var xmlfetch = xmlsource + '&grid_sq='+q_sq+'&grid_est='+q_e+'&grid_nth='+q_n+'&distance='+q_dist;
		} else { //setup browse mode
			BrowseArea();
		}
		if (q_assoc) {
			xmlfetch = xmlfetch + "&assoc=1";
		}

		var q_level=getQS("ulevel"); // cosmetic only, actual limiting by server
		if (q_level=='1') {maxsearch=10} // adjust for limited
		if (q_level=='2') {maxsearch=20} // adjust for full

		document.getElementById("paneltitle").innerHTML = helpButt;
		document.getElementById("sidepanel").innerHTML = sidemess;

		if (xmlfetch) {ProcessXML(xmlfetch)} // lets do the whole thing, otherwise idle awaiting click

		// nothing after here that relies on xml data, it is asychronous

		window.focus();

	} else {
		alert("Sorry, the Google Maps feature of TrailWise is not compatible with this browser");
	}

} // end SetUp()



// ==== to read querystring params
function getQS(para){
	para=para.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
	var work="[\\?&]"+para+"=([^&#]*)";
	var regex=new RegExp(work);
	var qval=regex.exec(window.location.href.replace(/&amp;/g,"&"));
	if(qval==null) { return "" }
	else {return qval[1]}
}


// ==== to create a clickable Polyline from a prepared object and link to infowin/panel
function createPolyline(lineobj) {
	maxbounds.extend(lineobj.gpts[0]); //for autozoom
	maxbounds.extend(lineobj.gpts[lineobj.gpts.length-1]);
	var colcode=colname[lineobj.lcolour.toLowerCase()];
	if (!colcode) {colcode='#999999'} //default
	var thickness=5; //default
	if (lineobj.lstyle=='thin') {thickness=3}
	if (lineobj.lstyle=='thick') {thickness=8}
	var linkline=new GPolyline(lineobj.gpts,colcode,thickness,0.7);
		// store pointers so that we can get em later
	linkline.paneldex=lineobj.paneldex;
	linkline.infodex=lineobj.infodex;
	linkline.uid=lineobj.uid;
	linkline.colcode=colcode;
	TWmap.addOverlay(linkline);
		//listener for a kludge, to feed the gpoint to the general clicklistener
	linkline.clicker=GEvent.addListener(linkline,"click",function(point){
		GEvent.trigger(TWmap,"click",linkline,point);
	}); //endkludge
	gm_polylines.push(linkline); //save this line handle
}


// ==== to create a clickable marker
function createMarker(mkrobject) {
	maxbounds.extend(mkrobject.gpt); //for autozoom
	var icoimg
	if (mkrobject.icotype) {
		icoimg = iconame[(mkrobject.icotype.substring(0,4)).toLowerCase()];
	}
	if (!icoimg) {icoimg='info'} //default
	iconx.image=imagelib+"mm_20_"+icoimg +".png"; //select from image lib
	var marker= new GMarker(mkrobject.gpt,{icon:iconx,title:mkrobject.hover});
	if (marker) {TWmap.addOverlay(marker)}
		//store pointers
	marker.paneldex=mkrobject.paneldex;
	marker.infodex=mkrobject.infodex;
	gm_markers.push(marker);
}


// ==== to draw a circle-ish on lat/long
function drawCircle(cent,radkm){
	if(polycirc) {TWmap.removeOverlay(polycirc)} //rub old
	var pts=[];
	var M=Math.PI/180;
	var aspect=1.7;	//approx for UK
	var r=radkm*0.01; //approx degs for UK
	for(var i=0; i<=360; i+=10){ //Loop around by step 10
		var P=new GLatLng(cent.lat()+(r*Math.sin(i*M)),cent.lng()+(r*Math.cos(i*M))*aspect);
		pts.push(P);
		maxbounds.extend(P); //for autozoom
	}
	polycirc=new GPolyline(pts,'#FF66CC',2);
	TWmap.addOverlay(polycirc);
}


//  ==== to toggle the graticule on/off
function toggleOSG() {
	if (!TWmap.GratOn) {
		TWmap.GratOn=true;
		osGrat=new OGBGraticule(TWmap);
		TWmap.addOverlay(osGrat);
		osGrat.realdraw();
		document.getElementById("GratButt").innerHTML='Hide OS grid';
	} else {
		clearOSG();
	}
}

function clearOSG() {
	TWmap.GratOn=false; //do first to guard against clickbounce
	TWmap.removeOverlay(osGrat);
	document.getElementById("GratButt").innerHTML='Show OS grid';
}


//  ==== to toggle the info markers on/off
function togglePins() {
	for (var i=0; i<gm_markers.length; i++) {
		if (gm_markers[i].isHidden()) { gm_markers[i].show() }
		else { gm_markers[i].hide() }
	}
	if (!TWmap.PinsOn) { setPins() }
	else {
		document.getElementById("PinsButt").innerHTML='Show extra info';
		TWmap.PinsOn=false;
	}
}

function setPins() {
	document.getElementById("PinsButt").innerHTML = 'Hide extra info';
	TWmap.PinsOn=true;
}


// ==== to create the graticule + extras hide buttons
function GratControl() { }
	GratControl.prototype=new GControl();
    // Create DIV for the buttons,  place in a container DIV, return it as our control element.
	GratControl.prototype.initialize=function(TWmap) {
		var GCcont=document.createElement("div");
		var GratDiv=document.createElement("div");
		var PinsDiv=document.createElement("div");
		var KeyDiv=document.createElement("div");
		GCcont.style.backgroundColor="white";
		GCcont.style.color="black";
		GCcont.style.font="small Arial";
		GCcont.style.cursor="pointer" ;
		GCcont.appendChild(GratDiv);
		GratDiv.style.border="2px ridge";
		GratDiv.appendChild(document.createTextNode("Show OS Grid"));
		GratDiv.id="GratButt";
		GratDiv.title="Show/Hide Landranger gridlines";
		GEvent.addDomListener(GratDiv,"click",function(){toggleOSG()});
		PinsDiv.style.border="2px ridge";
		GCcont.appendChild(PinsDiv);
		PinsDiv.appendChild(document.createTextNode("Hide xtra info"));
		PinsDiv.id="PinsButt";
		PinsDiv.title="Show/Hide additional info markers";
		GEvent.addDomListener(PinsDiv,"click",function(){togglePins()});
		KeyDiv.style.border="2px ridge";
		GCcont.appendChild(KeyDiv);
		KeyDiv.appendChild(document.createTextNode("Overlay key"));
		KeyDiv.id="KeyButt";
		KeyDiv.title="Pop up the key to coloured lines";
		Keyimg = imagelib + 'key_horiz.gif';
		GEvent.addDomListener(KeyDiv,"click",function(){imagePopUp(Keyimg)});
		TWmap.getContainer().appendChild(GCcont);
		return GCcont;
	}

		// button position for the map class to action
	GratControl.prototype.getDefaultPosition = function() {
		return new GControlPosition(G_ANCHOR_TOP_RIGHT,new GSize(8,26));
	}


// ==== to do the map-click -> OS ngr display
function mapClicked(point) {
	var osgb=WGS84toNGR(point.lat(),point.lng());
	var currzoom = TWmap.getZoom();
	if (currzoom < 9) { TWmap.setCenter(point, 9) }// centre and zoom
	var clickhtml='Mouse last clicked at GridRef:  <B>' + osgb + '</B> &nbsp; &nbsp; &nbsp; see an OS map at: ';
	clickhtml += '<A href="http://www.multimap.com/map/browse.cgi?lat=' + point.lat() + '&lon=' + point.lng() + '&scale=25000&icon=x" target="multm">';
	clickhtml += '<IMG src="'+imagelib+'multimap_logo.gif" width="72" height="20" border="0" /></A>';
	document.getElementById("location").innerHTML=clickhtml;
	document.getElementById("sidepanel").innerHTML=sidemess;
}


// ==== to setup search request for current map centre
function newArea() {
	var c_here=TWmap.getCenter();
	var c_ngr=WGS84toNGR(c_here.lat(),c_here.lng());
	var bl=TWmap.getBounds().getSouthWest();
	var tr=TWmap.getBounds().getNorthEast();
	var dist=bl.distanceFrom(tr)/2500;
	dist=Math.round(dist+0.55); //round up
	if (dist>maxsearch) {dist=maxsearch} //max radius km
		//show proposal
	maxbounds=new GLatLngBounds();
	drawCircle(c_here,dist);
	TWmap.setCenter(c_here,TWmap.getBoundsZoomLevel(maxbounds)); //rezoom
	TWmap.oldButts=document.getElementById("oddsbox").innerHTML;
	sidemess='<P>Proposing to search the circled area; proceed?</P><P>(select button below)</P>';
	document.getElementById("sidepanel").innerHTML = sidemess;
	document.getElementById("oddsbox").innerHTML = '<input type="button" value="Search '+dist+'km around '+c_ngr+'" onclick="DoNewArea('+dist+',&quot;'+c_ngr+'&quot;)" '+ButtClass+'>';
	document.getElementById("oddsbox").innerHTML += '<BR><input type="button" value="Cancel that search" onclick="BrowseArea()" '+ButtClass+'>';
}

// ==== to fetch xml data for new area by radius+NGR
function DoNewArea(rad,ngr) {
	var sq=ngr.substr(0,2);
	var est=ngr.substr(3,3);
	var nth=ngr.substr(8,3);
	document.getElementById("sidepanel").innerHTML = 'Searching for '+rad+'km around '+sq+' '+est+' '+nth;
	document.getElementById("oddsbox").innerHTML = '&nbsp;';
	xmlfetch=xmlsource+'&grid_sq='+sq+'&grid_est='+est+'&grid_nth='+nth+'&distance='+rad;
	sidemess=sidedef; //reset for next message
	ProcessXML(xmlfetch);
}

// ==== to fetch xml data for assoc links
function DoAssocs(uid) {
	document.getElementById("sidepanel").innerHTML = 'Searching for tracks associated with ' + uid;
	document.getElementById("oddsbox").innerHTML = '&nbsp;';
	xmlfetch=xmlsource+'&uid='+uid+'&assoc=1';
	sidemess=sidedef; //reset for next message
	ProcessXML(xmlfetch);
}

// ==== to retry fetch xml
function retryArea() {
	ProcessXML(xmlfetch);
}

// ==== to browse around for target area
function BrowseArea() {
	if(polycirc) { //clear old target
		TWmap.removeOverlay(polycirc);
		polycirc=[];
		maxbounds=new GLatLngBounds();
	}
	sidemess=sidebrowse;
	document.getElementById("maptitle").innerHTML='TrailWise map based browsing';
	document.getElementById("sidepanel").innerHTML=sidebrowse;
	if (TWmap.oldButts.length > 0) {
		document.getElementById("oddsbox").innerHTML=TWmap.oldButts;
	} else {
		document.getElementById("oddsbox").innerHTML=researchButt;
	}
	xmlfetch='';
}

// ==== enter pick-list mode - uses undocumented Google API features
function DoPickMode() {
	for (var i=0; i<gm_polylines.length; i++) {
		gm_polylines[i].picked=false;
		gm_polylines[i].color='#999999'; //grey 
		gm_polylines[i].redraw(true);
	}
	TWmap.oldTitle=document.getElementById("maptitle").innerHTML; //save
	TWmap.oldButts=document.getElementById("oddsbox").innerHTML;
	document.getElementById("maptitle").innerHTML='TrailWise track selection';
	sidemess=sidepick;
	document.getElementById("sidepanel").innerHTML=sidemess;
	document.getElementById("oddsbox").innerHTML='<input type="button" value="Submit this list" onclick="SendList()" '+ButtClass+'>';
	document.getElementById("oddsbox").innerHTML+='<BR><input type="button" value="Cancel this selection" onclick="NormMode()" '+ButtClass+'>';
	TWmap.PickMode=true;
}


// ==== Add or remove clicked TW line to picklist
function PickThis(lline) {
	if (lline.uid) { //check its a track
		if(lline.picked) { //unselect
			lline.picked=false;
			lline.color='#999999'; 
			lline.redraw(true);
		} else { //select
			lline.picked=true;
			lline.color='#00ff00';
			lline.redraw(true);
		}
		document.getElementById("sidepanel").innerHTML=sidepick; //display list
		for (var i=0; i<gm_polylines.length; i++) {
			if (gm_polylines[i].picked) {
				document.getElementById("sidepanel").innerHTML += '<BR><A HREF="'+hotlink+'uid='+gm_polylines[i].uid+'" target="tw_win">'+gm_polylines[i].uid+'</A>';
			}
		} //end links loop
			sidemess=document.getElementById("sidepanel").innerHTML
		}
	}

// ==== POST the picklist to TW
function SendList() {
	document.getElementById("oddsbox").innerHTML = '&nbsp;';
	document.getElementById("maptitle").innerHTML = 'Sending list data ... please wait <img src="'+imagelib+'rhombuses.gif" alt="busy">';
	var p_req=GXmlHttp.create();
    p_req.open("POST",listurl,true);
	p_req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
	var idx=0;
	var p_data='';
	for (var i=0; i<gm_polylines.length; i++) {
		if (gm_polylines[i].picked) {
			p_data += 'uid'+idx+'='+gm_polylines[i].uid+'&'; 
			idx++;
		}
	} //end links loop
	p_data=p_data.slice(0,p_data.length-1); //trim last &amp
	p_req.onreadystatechange=function() { //setup response event
		if (p_req.readyState==4) {
			if (p_req.status==200) document.getElementById("maptitle").innerHTML='Sent list data OK!';
	        else alert('Sorry, there was a problem sending that to TrailWise');
		}
	}
	p_req.send(p_data); //kickoff send
	NormMode();
}

// ==== exit pick-list mode
function NormMode() {
	for (var i=0; i<gm_polylines.length; i++) {
		gm_polylines[i].color=gm_polylines[i].colcode; //restore 
		gm_polylines[i].redraw(true);
	}
	document.getElementById("maptitle").innerHTML=TWmap.oldTitle;
	sidemess=sidedef;
	document.getElementById("sidepanel").innerHTML=sidemess;
	document.getElementById("oddsbox").innerHTML=TWmap.oldButts;
	TWmap.oldButts='';
	TWmap.PickMode=false;
}


// ==== to popup the key
function imagePopUp(img) {
	offsetX = 100;
	offsetY = 100;
	var html = "<html><head><title>TrailWise Overlays key</title>" ;
	html += "</head><body style='margin: 0px 0; text-align:center; '>" + "<IMG SRC='" + img + "' BORDER=0 NAME=image " + "onload='window.resizeTo((document.image.width+12),(document.image.height+35))'>" ;
	html += "</body></html>" ;
	settings ='top=' + offsetY + ',left=' + offsetX + ', toolbar=0, location=0, directories=0, menuBar=0, scrollbars=0, resizable=1'
	popup=window.open ("","image", settings) ;
	popup.document.open();
	popup.document.write(html);
	popup.focus();
	popup.document.close();
}


// ==== to clear map, keep centre/zoom
function WashMap() {
	document.getElementById("sidepanel").innerHTML='&nbsp;';
	panel_html=[];
	infowin=[];
	infowin2=[];
	for (var i=0; i<gm_polylines.length; i++) { //clear fixup listeners
		GEvent.removeListener(gm_polylines[i].clicker);
	}
	TWmap.clearOverlays();
	gm_polylines=[];
	gm_markers=[];
	maxbounds=new GLatLngBounds();
	setPins();
}

// ==== to wash up
function WashUp() {
	WashMap();
	GEvent.clearInstanceListeners(TWmap);
	clearOSG();
	GUnload();
}


// ==== the big MAIN PROCESS - fetches XML and processes it ====

function ProcessXML(url) {
	TWmap.oldButts='';
	document.getElementById("maptitle").innerHTML = 'Fetching map data ... please wait<img src="'+imagelib+'clock.gif" alt="busy " width="34" height="34" />';
	var xml_req=GXmlHttp.create();
//DEVELOPMENT: alert('simulated call '+url); url="http://www.schafer.org.uk/rm/gmaps2/sampleone.xml";
    xml_req.open("GET", url, true);
	xml_req.onreadystatechange=function() {
		if (xml_req.readyState==4) { //ok
			document.getElementById("maptitle").innerHTML = 'Processing map data ... please wait<img src="'+imagelib+'clock.gif" alt="busy " width="34" height="34" />';
			var xmlDoc=xml_req.responseXML;
				 // ALTERNATE in case of server MIME issues:
				 //	var xmlDoc = GXml.parse(request.responseText); 

			 // get overall map info and display title
			try { var xmlWrapper=xmlDoc.documentElement.getElementsByTagName('trailwise') }
			catch(err) {
				alert('Error fetching XML data from Trailwise:'+err );
				var s_button='<input type="button" value="Re-try track search" onclick="retryArea()">';
				document.getElementById("maptitle").innerHTML="Please try again later "+s_button;
				return;
			}		
			WashMap();
			var x_date=xmlWrapper[0].getAttribute('date');
			var userid=xmlWrapper[0].getAttribute('user');
			if (userid=='0') {userid=null}
			if (userid > 0) {maxsearch=10} // adjust for limited user
			var ulevel=xmlWrapper[0].getAttribute('level');
			if (ulevel > 1) {maxsearch=20} // adjust for normal user
			var mapinfo=xmlDoc.documentElement.getElementsByTagName('map');
			var maptype=mapinfo[0].getAttribute('type'); 
			var searchcentre=mapinfo[0].getAttribute('point'); 
			var searchradius=mapinfo[0].getAttribute('distance'); 
			var toptitle=GXml.value(xmlDoc.getElementsByTagName('map')[0]);
			var subtitle=GXml.value(xmlDoc.getElementsByTagName('subtitle')[0]);
			var messbox=GXml.value(xmlDoc.getElementsByTagName('messagebox')[0]);
			document.getElementById("paneltitle").innerHTML=x_date;
			if (userid) { document.getElementById("paneltitle").innerHTML += ', user '+userid }
			document.getElementById("paneltitle").innerHTML += ' ' + helpButt;

			var infodex=1; // key to the infowin arrays

			var xmlLinks=xmlDoc.documentElement.getElementsByTagName('link');
				//loop through links elements
			if (xmlLinks.length>0) {
				for (var l_idx=0; l_idx<xmlLinks.length; l_idx++) {
					var tw_line=[]; //this line assembly object
					var tw_mark=[]; //reusable marker object
					tw_line.paneldex=l_idx; // note this link's ptrs
					tw_line.infodex=infodex;
					infodex++; //bump ready for next infowin
					tw_line.uid=xmlLinks[l_idx].getAttribute('uid'); // global current link uid
					var linkhtml='<div class=uidlink>UID <A HREF="'+hotlink+'uid='+tw_line.uid+'" target="tw_win">'+tw_line.uid+'</A></div>';
					tw_mark.paneldex=l_idx; //xref for marker use
					tw_mark.uid=tw_line.uid;

					 //start building the infowin and sidepanel
					panel_html[l_idx]='<P>Track '+linkhtml+'</P>';
					infowin[tw_line.infodex]='<P>'+linkhtml+'</P>';
					infowin2[tw_line.infodex]='';

					 //sort any restrictions						
					var x_tro=xmlLinks[l_idx].getElementsByTagName('restriction');
					if (x_tro.length>0) {
						for (var i=0; i<x_tro.length; i++) {
							var r_link = '<A HREF="'+hotlink+'ref='+x_tro[i].getAttribute('id')+'" target="tw_win">';
							r_link += x_tro[i].getAttribute('type').toUpperCase() + '</A>';
							panel_html[l_idx] += '<P class="restrict">' + r_link;
							panel_html[l_idx] += ': ' + GXml.value(x_tro[i]) + '</P>';
						}
						infowin[tw_line.infodex] += '<P class="restrict">Restriction - see panel</P>';
					}

						//this links features, do before (under) way-marks
					var x_Feats=xmlLinks[l_idx].getElementsByTagName('feature');
					for (var j=0; j<x_Feats.length; j++) {
						tw_mark.icotype=x_Feats[j].getAttribute('type');
						if (!tw_mark.icotype) {tw_mark.icotype='feature'}
						tw_mark.hover=tw_mark.icotype; 
						var ftext=GXml.value(x_Feats[j]);
						var fid=x_Feats[j].getAttribute('id');
						var fngr=x_Feats[j].getAttribute('point');
						var workll=NGRtoWGS84(fngr); //do the location
						if (workll) {
							tw_mark.gpt=new GLatLng(workll.lat,workll.lon);
							infowin[infodex]='<A HREF="' + hotlink + 'ref=' + fid + '" target="tw_win">'+tw_mark.icotype+'</A><P>'+ftext+'</P>';
							infowin2[infodex]='On '+linkhtml+'<BR><BR> at grid '+fngr;
							tw_mark.infodex=infodex;
							createMarker(tw_mark);
							infodex++;
						} else {
							alert('Bad Grid Ref '+fngr+' in TW Feature '+fid);
						}
					} //end feats loop

					  //shape drawing info
					var w_typ
					var w_text
					tw_line.ngrs=[];
					tw_line.gpts=[];
					var x_Shapes=xmlLinks[l_idx].getElementsByTagName('shape');
					for (var j=0; j<x_Shapes.length; j++) { //get the NGRs and convert
						var ptr=Number(x_Shapes[j].getAttribute('ord'));
						var workngr=x_Shapes[j].getAttribute('point');
						tw_line.ngrs[ptr]=workngr;
						var workll=NGRtoWGS84(workngr);
						if (workll) {
							tw_line.gpts[ptr]=new GLatLng(workll.lat,workll.lon);
						} else {
								alert('Bad Grid Ref '+workngr+' in TrailWise '+tw_line.uid);
						}
						 //way markers, tar+junc not yet implemented
						tw_mark.icotype='';
						w_typ=x_Shapes[j].getAttribute('type');
						w_text=GXml.value(x_Shapes[j]);
						if(w_typ) { tw_mark.icotype=(w_typ.substring(0,4)).toLowerCase() }
						else { w_typ='waypoint' }
						if (tw_mark.icotype=='dead' || tw_mark.icotype=='nogo' || tw_mark.icotype=='rest' || w_text.length>2) { //only do if important
							infowin[infodex]='<I>'+w_typ+'</I><BR>on '+linkhtml+'<P>'+w_text+'</P>';
							infowin2[infodex] = 'At grid '+workngr;
							tw_mark.gpt=tw_line.gpts[ptr];
							tw_mark.infodex=infodex;
							tw_mark.hover=w_typ;
							createMarker(tw_mark);
							infodex++;
						} //end way marker draw
					} //end shapes loop
						
						//draw the line and attach its refs
					var x_line=xmlLinks[l_idx].getElementsByTagName('line');
					tw_line.lcolour=x_line[0].getAttribute('colour');
					tw_line.lstyle=x_line[0].getAttribute('style');
					createPolyline(tw_line);

						//sort tracknames
					var tw_name=GXml.value(x_line[0]); //option from line element
					if (tw_name) {tw_name+='<BR>'}
					var x_Defd=xmlLinks[l_idx].getElementsByTagName('defined');
					for (var j=0; j<x_Defd.length; j++) {
						var n_id=x_Defd[j].getAttribute('id');
						var d_name=GXml.value(x_Defd[j]);
						if (d_name && n_id) {
							tw_name += '<A HREF="'+hotlink+'ref='+n_id+'" target="tw_win">'+d_name+'</A><BR>';
						}
					}
					infowin[tw_line.infodex] += tw_name;
					panel_html[l_idx] += tw_name+'<BR>';

						//build records infowindow
					infowin2[tw_line.infodex]='Authority records:<BR><BR>';
					var iw2=false;
					var x_DMS=xmlLinks[l_idx].getElementsByTagName('dms');
					if (x_DMS.length) {
						infowin2[tw_line.infodex] += 'DMS Auth: '+GXml.value(x_DMS[0])+'<BR>';
						infowin2[tw_line.infodex] += 'Recorded: '+x_DMS[0].getAttribute('record')+'<BR>';
						var d_id=x_DMS[0].getAttribute('id');
						var d_ref=x_DMS[0].getAttribute('ref');
						if (d_ref) {
							infowin2[tw_line.infodex] += 'Reference: <A HREF="'+hotlink+'ref='+d_id+'" target="tw_win">'+d_ref+'</A><BR>';
							iw2=true;
							}
					} else {
						infowin2[tw_line.infodex] += 'DMS unknown<BR>';
					}

					var x_Str=xmlLinks[l_idx].getElementsByTagName('street');
					if (x_Str.length) {
						infowin2[tw_line.infodex] += '<BR>LoS Auth: '+GXml.value(x_Str[0])+'<BR>';
						infowin2[tw_line.infodex] += 'Recorded: '+x_Str[0].getAttribute('record')+'<BR>';
						var s_id=x_Str[0].getAttribute('id');
						var s_ref=x_Str[0].getAttribute('ref');
						if (s_ref) {
							infowin2[tw_line.infodex] += 'Reference: <A HREF="'+hotlink+'ref='+s_id+'" target="tw_win">'+s_ref+'</A><BR>';
							iw2=true;
						}
					} else {
						infowin2[tw_line.infodex] += '<BR>LoS unknown';
					}
					if (!iw2) {infowin2[tw_line.infodex]=null} //blank if no goodies

					 // do lane comments etc
					var x_Inf=xmlLinks[l_idx].getElementsByTagName('info');
					for (var j=0; j<x_Inf.length; j++) {
						panel_html[l_idx] += '<P>'+GXml.value(x_Inf[j])+'</P>';
					}
					
//					panel_html[l_idx] +='<input type="button" value="Search for associated tracks" onclick="DoAssocs(\''+tw_line.uid+'\')" '+ButtClass+' style="position:absolute; bottom:1px; left:7px';
//					if (ulevel<1) {panel_html[l_idx] += '; color: gray"  disabled="disabled' }
//					panel_html[l_idx] +='">';


				} //end linksloop

				} else {
					 //no links exist
					document.getElementById("noticearea").innerHTML='No Links found here !';
				}

				//sort out unassoc (orphan) features
			var x_Feats=xmlDoc.documentElement.getElementsByTagName('feature');
			var tw_mark=[];
			for (var j=0; j<x_Feats.length; j++) {
				if (x_Feats[j].parentNode.tagName != 'link'){ //an orphan
					tw_mark.icotype=x_Feats[j].getAttribute('type');
					if (!tw_mark.icotype) {tw_mark.icotype='feature'}
					var ftext=GXml.value(x_Feats[j]);
					var fid=x_Feats[j].getAttribute('id');
					var fngr=x_Feats[j].getAttribute('point');
					var workll=NGRtoWGS84(fngr); // do the location
					if (workll) {
						tw_mark.gpt=new GLatLng(workll.lat,workll.lon);
						infowin[infodex] = '<A HREF="'+hotlink+'ref='+fid+'" target="tw_win">'+tw_mark.icotype+'</A><P>'+ftext+'</P>';
						infowin2[infodex] = 'At grid '+fngr;
						tw_mark.infodex=infodex;
						tw_mark.hover=tw_mark.icotype; 
						createMarker(tw_mark);
						infodex++;
					} else {
							alert('Bad Grid Ref '+fngr+' in TW Feature '+fid);
					}
				}
			} //end feats loop

				//do circle if required
			if (searchradius) {
				var workll=NGRtoWGS84(searchcentre);
				if (workll) {
					var c_point=new GLatLng(workll.lat,workll.lon);
					drawCircle(c_point,searchradius);
				} else {
					alert('Bad Grid Ref '+searchcentre+' in TrailWise header');
				}
			}
				
				// autozoom/centre the map
			TWmap.setCenter(maxbounds.getCenter(),TWmap.getBoundsZoomLevel(maxbounds));
				//add titles
			document.getElementById("maptitle").innerHTML=toptitle;
			document.getElementById("subtitle").innerHTML=subtitle;
			if (maptype=='null') {
				document.getElementById("sidepanel").innerHTML='oops!';
				TWmap.setCenter(new GLatLng(55.0,-2.0),5);
			}
			else {document.getElementById("sidepanel").innerHTML=sidemess}
			document.getElementById("noticearea").innerHTML='';
			if (messbox.length>0) {document.getElementById("noticearea").innerHTML = messbox}
			if (infodex>99) {document.getElementById("noticearea").innerHTML += '<BR>Mapped '+infodex+' items, may cause slow operation'}
				//add buttons
			var pickButt = ' ';
//			var pickButt = '<BR><input type="button" value="Select tracks for User Ev input" '+ButtClass;
//			if ((!l_idx) || (ulevel<1)) {pickButt += ' disabled="disabled" style="color: gray"'}
//			pickButt += ' onclick="DoPickMode()">';
			document.getElementById("oddsbox").innerHTML=researchButt+pickButt;
				
		} //end of asynch readystate-4 processing
	} //end readystate check

	xml_req.send(null); // kickoff file fetch

} //end main function
