// Set up variables
var objMap;
var objOSMap;
var objOSGrid;

var arrZoomMapping = new Array(6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16);

var arrWalkLines = new Array();
var arrTubeLines = new Array();
var arrStationLabels = new Array();
var arrMarkerLabels = new Array();
var arrMarkers = new Array();
var arrPhotoLabels = new Array();
var arrPhotos = new Array();
var arrDropdownSettings = new Array();
var boolGoogleMapShowing = true;
var boolDisplayShowing = false;
var boolWalkShowing = true;
var boolTubeShowing = false;
var boolLabelShowing = false;
var boolMarkerShowing = false;
var boolPhotoShowing = false;
var boolFullSize = false;
var boolGoogleEarth = false;
var strButtonHeight = "19px";
var strBorderHeight = "17px";
var strButtonTextSize = "0.915em";
var strDropdownTextSize = "0.8em";
var strButtonLineHeight = "1.43em"
var intButtonWidth = 63;
var intDropdownPosition = 275;
var intDropdownWidth = 120;
var intDropdownOffset = 0;

if ($.browser.safari) {
	strButtonHeight = "18px";
	strBorderHeight = "16px";
	intDropdownOffset = -1;
}

var strStationImage = "../images/google_earth/placemark_circle.png";
var intStationWidth = 16;
var intStationHeight = 16;
var intStationAnchorX = 8;
var intStationAnchorY = 8;
var intStationWindowAnchorX = 8;
var intStationWindowAnchorY = 8;

var strMarkerImage = "http://labs.google.com/ridefinder/images/mm_20_blue.png";
var intMarkerWidth = 12;
var intMarkerHeight = 20;
var intMarkerAnchorX = 6;
var intMarkerAnchorY = 20;
var intMarkerWindowAnchorX = 5;
var intMarkerWindowAnchorY = 1;

var strPhotoImage = "http://labs.google.com/ridefinder/images/mm_20_white.png";
var intPhotoWidth = 12;
var intPhotoHeight = 20;
var intPhotoAnchorX = 6;
var intPhotoAnchorY = 20;
var intPhotoWindowAnchorX = 5;
var intPhotoWindowAnchorY = 1;

function toggleWalk() {
	if (boolWalkShowing) {
		for (var i = 0; i < arrWalkLines.length; i++) {
			arrWalkLines[i].hide();
		}
		boolWalkShowing = false;
	} else {
		for (var i = 0; i < arrWalkLines.length; i++) {
			arrWalkLines[i].show();
		}
		boolWalkShowing = true;
	}
}

function toggleTube() {
	if (boolTubeShowing) {
		for (var i = 0; i < arrTubeLines.length; i++) {
			arrTubeLines[i].hide();
		}
		boolTubeShowing = false;
	} else {
		for (var i = 0; i < arrTubeLines.length; i++) {
			arrTubeLines[i].show();
		}
		boolTubeShowing = true;
	}
}

function toggleMarkers() {
	if (boolMarkerShowing) {
		for (var i = 0; i < arrMarkers.length; i++) {
			arrMarkers[i].hide();
		}
		if (boolLabelShowing) {
			for (var i = 0; i < arrMarkerLabels.length; i++) {
				arrMarkerLabels[i].hide();
			}
		}
		boolMarkerShowing = false;
	} else {
		for (var i = 0; i < arrMarkers.length; i++) {
			arrMarkers[i].show();
		}
		if (boolLabelShowing) {
			for (var i = 0; i < arrMarkerLabels.length; i++) {
				arrMarkerLabels[i].show();
			}
		}
		boolMarkerShowing = true;
	}
}

function togglePhotos() {
	if (boolPhotoShowing) {
		for (var i = 0; i < arrPhotos.length; i++) {
			arrPhotos[i].hide();
		}
		if (boolLabelShowing) {
			for (var i = 0; i < arrPhotoLabels.length; i++) {
				arrPhotoLabels[i].hide();
			}
		}
		boolPhotoShowing = false;
	} else {
		for (var i = 0; i < arrPhotos.length; i++) {
			arrPhotos[i].show();
		}
		if (boolLabelShowing) {
			for (var i = 0; i < arrPhotoLabels.length; i++) {
				arrPhotoLabels[i].show();
			}
		}
		boolPhotoShowing = true;
	}
}

function toggleLabels() {
	if (boolLabelShowing) {
		for (var i = 0; i < arrStationLabels.length; i++) {
			arrStationLabels[i].hide();
		}
		if (boolMarkerShowing) {
			for (var i = 0; i < arrMarkerLabels.length; i++) {
				arrMarkerLabels[i].hide();
			}
		}
		if (boolPhotoShowing) {
			for (var i = 0; i < arrPhotoLabels.length; i++) {
				arrPhotoLabels[i].hide();
			}
		}
		boolLabelShowing = false;
	} else {
		for (var i = 0; i < arrStationLabels.length; i++) {
			arrStationLabels[i].show();
		}
		if (boolMarkerShowing) {
			for (var i = 0; i < arrMarkerLabels.length; i++) {
				arrMarkerLabels[i].show();
			}
		}
		if (boolPhotoShowing) {
			for (var i = 0; i < arrPhotoLabels.length; i++) {
				arrPhotoLabels[i].show();
			}
		}
		boolLabelShowing = true;
	}
}

$(window).resize(function(){
	if (boolFullSize && !$.browser.msie) expandMap();
});



// Prototype for Display button
function DisplayControl(boolPhotos, boolTube) {
	this.photos = boolPhotos;
	this.tube = boolTube;
}

DisplayControl.prototype = new google.maps.Control();

DisplayControl.prototype.initialize = function(map) {
	var objContainer = document.createElement("div");

	var objButtonDiv = document.createElement("div");
	this.setButtonStyle_(objButtonDiv);
	var objButtonBorderDiv = document.createElement("div");
	this.setBorderStyle_(objButtonBorderDiv);
	objButtonDiv.appendChild(objButtonBorderDiv);
	objButtonBorderDiv.appendChild(document.createTextNode("Show..."));
	objContainer.appendChild(objButtonDiv);

	var objDropdownDiv = document.createElement("div");
	this.addInput_(objDropdownDiv, "Walking route", boolWalkShowing, toggleWalk);
	if (this.tube) { this.addInput_(objDropdownDiv, "Tube line", boolTubeShowing, toggleTube); }
	this.addInput_(objDropdownDiv, "Points of interest", boolMarkerShowing, toggleMarkers);
	if (this.photos) { this.addInput_(objDropdownDiv, "Photos", boolPhotoShowing, togglePhotos); }
	this.addInput_(objDropdownDiv, "Labels", boolLabelShowing, toggleLabels);
	this.setDropdownStyle_(objDropdownDiv);
	var objPosition = new google.maps.ControlPosition(G_ANCHOR_TOP_RIGHT, new google.maps.Size(intDropdownPosition - intDropdownWidth - 6, 27 + intDropdownOffset));
	objPosition.apply(objDropdownDiv);

	google.maps.Event.addDomListener(objButtonDiv, "click", function() {
		if (boolDisplayShowing) {
			objButtonBorderDiv.style.borderTop = "1px solid #fff";
			objButtonBorderDiv.style.borderLeft = "1px solid #fff";
			objButtonBorderDiv.style.borderRight = "1px solid #b0b0b0";
			objButtonBorderDiv.style.borderBottom = "1px solid #b0b0b0";
			objButtonBorderDiv.style.font = strButtonTextSize + " Arial";
			objButtonBorderDiv.style.lineHeight = strButtonLineHeight;
			objDropdownDiv.style.display = "none";
			boolDisplayShowing = false;
		} else {
			objButtonBorderDiv.style.borderTop = "1px solid #345684";
			objButtonBorderDiv.style.borderLeft = "1px solid #345684";
			objButtonBorderDiv.style.borderRight = "1px solid #6c9ddf";
			objButtonBorderDiv.style.borderBottom = "1px solid #6c9ddf";
			objButtonBorderDiv.style.font = "bold " + strButtonTextSize + " Arial";
			objButtonBorderDiv.style.lineHeight = strButtonLineHeight;
			objDropdownDiv.style.display = "block";
			boolDisplayShowing = true;
		}
	});

	map.getContainer().appendChild(objContainer);
	map.getContainer().appendChild(objDropdownDiv);
	return objContainer;
}

DisplayControl.prototype.getDefaultPosition = function() {
	return new google.maps.ControlPosition(G_ANCHOR_TOP_RIGHT, new google.maps.Size(intDropdownPosition - intButtonWidth, 7));
}

DisplayControl.prototype.setButtonStyle_ = function(button) {
	button.style.textDecoration = "none";
	button.style.color = "black";
	button.style.backgroundColor = "white";
	button.style.font = strButtonTextSize + " Arial";
	button.style.lineHeight = strButtonLineHeight;
	button.style.border = "1px solid black";
	button.style.padding = "0px";
	button.style.marginTop = "0px";
	button.style.marginBottom = "0px";
	button.style.textAlign = "center";
	button.style.width = (intButtonWidth + 2) + "px";
	button.style.cursor = "pointer";
	button.style.height = strButtonHeight;
}

DisplayControl.prototype.setBorderStyle_ = function(button) {
	button.style.textDecoration = "none";
	button.style.color = "black";
	button.style.backgroundColor = "white";
	button.style.font = strButtonTextSize + " Arial";
	button.style.lineHeight = strButtonLineHeight;
	button.style.borderTop = "1px solid #fff";
	button.style.borderLeft = "1px solid #fff";
	button.style.borderRight = "1px solid #b0b0b0";
	button.style.borderBottom = "1px solid #b0b0b0";
	button.style.padding = "0px";
	button.style.marginTop = "0px";
	button.style.marginBottom = "0px";
	button.style.textAlign = "center";
	button.style.width = intButtonWidth + "px";
	button.style.cursor = "pointer";
	button.style.height = strBorderHeight;
}

DisplayControl.prototype.addInput_ = function(dropdown, text, checked, toggleFunction) {
	var objInput = document.createElement("input");
	objInput.type = "checkbox";
	objInput.padding = "0px";
	objInput.margin = "0px";
	objInput.onclick = toggleFunction;
	var objLabel = document.createElement("label");
	objLabel.appendChild(objInput);
	if (checked) { objInput.checked = true; }
	objLabel.appendChild(document.createTextNode(text));
	dropdown.appendChild(objLabel);
	dropdown.appendChild(document.createElement("br"));
}

DisplayControl.prototype.setDropdownStyle_ = function(dropdown) {
	dropdown.style.display = "none";
	dropdown.style.textDecoration = "none";
	dropdown.style.color = "black";
	dropdown.style.backgroundColor = "white";
	dropdown.style.font = strDropdownTextSize + " Arial";
	dropdown.style.border = "1px solid black";
	dropdown.style.padding = "4px";
	dropdown.style.marginTop = "0px";
	dropdown.style.marginBottom = "0px";
	dropdown.style.textAlign = "left";
	dropdown.style.width = intDropdownWidth + "px";
}


// Load a map from an XML file
function load(strPath, strXmlFile, strPostfix, boolSimple, boolPhotos, boolTube, strEndPoint, boolMarkers, boolOSMap) {
	// Make sure that SVG is on (for encoded polylines)
	if(document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#SVG","1.1")){ 
	  _mSvgEnabled = true;
	  _mSvgForced  = true;
	}

	// Insert 'Expand map', 'Google Earth' and 'OS map' links
	if (boolSimple) {
		var strMapKey = '';
	} else {
		var strMapKey = '<div id="mapKey"><img src="http://labs.google.com/ridefinder/images/mm_20_blue.png" style="vertical-align: middle" width="12" height="20" alt="Blue marker" /> = Point of Interest&nbsp;';
		if (boolPhotos) strMapKey += '&nbsp;<img src="http://labs.google.com/ridefinder/images/mm_20_white.png" style="vertical-align: middle" width="12" height="20" alt="White marker" /> = Photo&nbsp;';
		strMapKey += '<img style="vertical-align: top;" src="../images/google_earth/placemark_circle.png" width="20" height="20" alt="White circle" />= ' + strEndPoint + '</div>';
		if (boolOSMap && !boolSimple) {
			strMapKey += '<div id="mapOSKey"><img src="http://labs.google.com/ridefinder/images/mm_20_blue.png" style="vertical-align: middle" width="12" height="20" alt="Blue marker" /> = Point of Interest&nbsp;';
			if (boolPhotos) strMapKey += '&nbsp;<img src="http://labs.google.com/ridefinder/images/mm_20_white.png" style="vertical-align: middle" width="12" height="20" alt="White marker" /> = Photo&nbsp;';
			strMapKey += '&nbsp;<img src="http://labs.google.com/ridefinder/images/mm_20_red.png" style="vertical-align: middle" width="12" height="20" alt="Red marker" /> = ' + strEndPoint + '</div>';
		}
	}

	if (boolOSMap && !boolSimple) {
		var strOSMapLink = ' | <a id="OSMap">OS map</a></div>';
	} else {
		var strOSMapLink = '';
	}

	if (strXmlFile == 'google_maps_data') {
		$("#map").before('<div id="mapFullScreen">' + strMapKey + '<a href="../downloads/load.php/kmzs/london_underground.kmz">View in Google Earth</a> | <a id="expandMap">Full screen</a>' + strOSMapLink);
	} else {
		$("#map").before('<div id="mapFullScreen">' + strMapKey + '<a href="' + strPath + strXmlFile + '&amp;kml=1">View in Google Earth</a> | <a id="expandMap">Full screen</a>' + strOSMapLink);
	}

	// Hide OS map key
	$("#mapOSKey").hide();

	// Activate 'Expand map' link
	$('a#expandMap').toggle(function() {
		expandMap();
	}, function() {
		shrinkMap();
	});

	// Activate 'OS map' link
	if (boolOSMap && !boolSimple) {
		$('a#OSMap').toggle(function() {
			var objToggleCentre = latLongOpenSpace(objMap.getCenter().lat(), objMap.getCenter().lng());
			var intToggleZoom = objMap.getZoom() - 6;
			if (intToggleZoom < 0) intToggleZoom = 0;
			if (intToggleZoom > 10) intToggleZoom = 10;
			objOSMap.setCenter(objToggleCentre, intToggleZoom);
			$("#map").hide();
			$("#mapKey").hide();
			$("#mapOS").show();
			$("#mapOSKey").show();
			$('a#OSMap').text("Google map");
			boolGoogleMapShowing = false;
		}, function() {
			var objToggleCentre = openSpaceLatLong(objOSMap.getCenter());
			var intToggleZoom = objOSMap.getZoom() + 6;
			objMap.setCenter(new google.maps.LatLng(objToggleCentre.lat, objToggleCentre.lon));
			objMap.setZoom(intToggleZoom);
			$("#mapOS").hide();
			$("#mapOSKey").hide();
			$("#map").show();
			$("#mapKey").show();
			$('a#OSMap').text("OS map");
			boolGoogleMapShowing = true;
		});
	}

	// If we need to show markers, tick checkbox
	boolMarkerShowing = boolMarkers;

	if (google.maps.BrowserIsCompatible()) {
		// Create Google map
		objMap = new google.maps.Map2(document.getElementById("map"));
		objMap.addControl(new google.maps.LargeMapControl());
		objMap.addControl(new google.maps.ScaleControl());
		objMap.addMapType(G_PHYSICAL_MAP);
		if (google.earth.isSupported()) {
			objMap.addMapType(G_SATELLITE_3D_MAP);
			intDropdownPosition += 63;
		}
		objMap.addControl(new google.maps.HierarchicalMapTypeControl());
		if (!boolSimple) objMap.addControl(new DisplayControl(boolPhotos, boolTube));
		objMap.enableDoubleClickZoom();

		// Create OS map
		if (boolOSMap && !boolSimple) {
			objOSMap = new OpenSpace.Map('mapOS');
			objOSGrid = new OpenSpace.GridProjection();
			var objVectorLayer = objOSMap.getVectorLayer();
			objOSMap.addLayer(objVectorLayer);
		}

		// Create loading message
		var objLoadingDiv = document.createElement("div");
		objLoadingDiv.setAttribute("id", "loadingMessage");
		var objLoadingMessage = document.createElement("div");
		if (boolSimple || !boolTube) {
			objLoadingMessage.innerHTML = "<strong>Drawing map...</strong><br /><br /><img src=\"../images/google_maps/loading.gif\" />";
			boolTubeShowing = true;
		} else {
			objLoadingMessage.innerHTML = "<strong>Drawing map...</strong><br /><br /><img src=\"../images/google_maps/loading.gif\" /><br /><br />This may take some time; if you're in a rush, <a href=\"../route/map.php?simple=1&amp;route=" + strXmlFile + strPostfix + "\">try this simpler version</a>";
		}
		objLoadingDiv.appendChild(objLoadingMessage); 
		var objPosition = new google.maps.ControlPosition(G_ANCHOR_BOTTOM_LEFT, new  google.maps.Size((objMap.getSize().width / 2) - 80, objMap.getSize().height / 2)); 
		objPosition.apply(objLoadingDiv);
		objMap.getContainer().appendChild(objLoadingDiv);

		// Set up bounds object for calculating Google map size
		var objBounds = new google.maps.LatLngBounds;

		// If we're not loading a data file, pass on simple parameter
		if (strXmlFile == 'google_maps_data') {
			var strSimpleParameter =  '';
		} else {
			var strSimpleParameter = '&amp;simple=' + (boolSimple ? '1' : '0');
		}

		// Load map XML
		google.maps.DownloadUrl(strPath + strXmlFile + strPostfix + strSimpleParameter, function(objData, intResponseCode) {
			var objXml = google.maps.Xml.parse(objData);
			var objPolylineEncoder = new PolylineEncoder();

			// Skip to the path node with id=strXmlFile
			var arrPaths = objXml.documentElement.getElementsByTagName("path");
			for (var intPath = 0; intPath < arrPaths.length; intPath++) {
				if (strXmlFile == 'google_maps_data') {
					if (arrPaths[intPath].getAttribute("id") == 'all') break;
				} else {
					if (arrPaths[intPath].getAttribute("id") == strXmlFile) break;
				}
			}

			// Pull out position node and centre maps
			var arrPosition = arrPaths[intPath].getElementsByTagName("position");
			objMap.setCenter(new google.maps.LatLng(parseFloat(arrPosition[0].getAttribute("lat")), parseFloat(arrPosition[0].getAttribute("lng"))));
			if (boolOSMap && !boolSimple) {
				var objOSCentrePoint = latLongOpenSpace(parseFloat(arrPosition[0].getAttribute("lat")), parseFloat(arrPosition[0].getAttribute("lng")));
				objOSMap.setCenter(objOSCentrePoint, 5);
			}

			// Set Google map to hybrid
			objMap.setMapType(G_PHYSICAL_MAP);

			// Pull out walk metadata and display
			var arrMetadata = arrPaths[intPath].getElementsByTagName("meta");
			displayMetadata(arrMetadata[0].getAttribute("stations"), arrMetadata[0].getAttribute("markers"), arrMetadata[0].getAttribute("photos"), arrMetadata[0].getAttribute("walk"), arrMetadata[0].getAttribute("tube"), arrMetadata[0].getAttribute("crow"), boolPhotos, boolTube, strEndPoint);

			// Get station nodes and add to map
			var arrStations = arrPaths[intPath].getElementsByTagName("station");
			for (var i = 0; i < arrStations.length; i++) {
				var objPoint = new google.maps.LatLng(parseFloat(arrStations[i].getAttribute("lat")), parseFloat(arrStations[i].getAttribute("lng")));
				var arrName = arrStations[i].getElementsByTagName("name");
				var arrDescription = arrStations[i].getElementsByTagName("description");
				//createStation(objPoint, arrName[0].getAttribute("value"), arrName[0].getAttribute("value"));
				createStation(objPoint, arrName[0].getAttribute("value") + '<br /><br />' + arrDescription[0].childNodes[0].nodeValue, arrName[0].getAttribute("value"));
				if (boolOSMap && !boolSimple) {
					var objOSPoint = latLongOpenSpace(parseFloat(arrStations[i].getAttribute("lat")), parseFloat(arrStations[i].getAttribute("lng")));
					createOSStation(objOSPoint, arrName[0].getAttribute("value") + '<br /><br />' + arrDescription[0].childNodes[0].nodeValue, arrName[0].getAttribute("value"));
				}
			}

			if (!boolSimple) {
				// Get marker nodes and add to map
				var arrMarkers = arrPaths[intPath].getElementsByTagName("marker");
				for (var i = 0; i < arrMarkers.length; i++) {
					var objPoint = new google.maps.LatLng(parseFloat(arrMarkers[i].getAttribute("lat")), parseFloat(arrMarkers[i].getAttribute("lng")));
					var arrName = arrMarkers[i].getElementsByTagName("name");
					var arrDescription = arrMarkers[i].getElementsByTagName("description");
					//createMarker(objPoint, arrName[0].getAttribute("value"), arrName[0].getAttribute("value"));
					createMarker(objPoint, arrName[0].getAttribute("value") + '<br /><br />' + arrDescription[0].childNodes[0].nodeValue, arrName[0].getAttribute("value"));
					if (boolOSMap && !boolSimple && boolMarkers) {
						var objOSPoint = latLongOpenSpace(parseFloat(arrMarkers[i].getAttribute("lat")), parseFloat(arrMarkers[i].getAttribute("lng")));
						createOSMarker(objOSPoint, arrName[0].getAttribute("value") + '<br /><br />' + arrDescription[0].childNodes[0].nodeValue, arrName[0].getAttribute("value"));
					}
				}
	
				if (boolPhotos) {
					// Get photo nodes and add to map
					var arrPhotos = arrPaths[intPath].getElementsByTagName("photo");
					for (var i = 0; i < arrPhotos.length; i++) {
						var objPoint = new google.maps.LatLng(parseFloat(arrPhotos[i].getAttribute("lat")), parseFloat(arrPhotos[i].getAttribute("lng")));
						var arrName = arrPhotos[i].getElementsByTagName("name");
						var arrDescription = arrPhotos[i].getElementsByTagName("description");
						createPhoto(objPoint, arrName[0].getAttribute("value") + '<br /><br />' + arrDescription[0].childNodes[0].nodeValue, arrName[0].getAttribute("value"));
					}
				}

				// Get walk sections and add polyline to map
				var arrSections = arrPaths[intPath].getElementsByTagName("section");
				for (var i = 0; i < arrSections.length; i++) {
					var arrPointObjects = [];
					var arrOSPointObjects = [];
					var arrPoints = arrSections[i].getElementsByTagName("point");
					for (var j = 0; j < arrPoints.length; j++) {
						var objPoint = new google.maps.LatLng(parseFloat(arrPoints[j].getAttribute("lat")), parseFloat(arrPoints[j].getAttribute("lng")));
						arrPointObjects.push(objPoint);
						objBounds.extend(objPoint);
						if (boolOSMap && !boolSimple) {
							var objOSPoint = latLongOpenLayers(parseFloat(arrPoints[j].getAttribute("lat")), parseFloat(arrPoints[j].getAttribute("lng")));
							arrOSPointObjects.push(objOSPoint);
							if (objOSBounds) {
								objOSBounds.extend(objOSPoint);
							} else {
								var objOSBounds = new OpenLayers.Bounds(objOSPoint.lon, objOSPoint.lat, objOSPoint.lon, objOSPoint.lat);
							}
						}
					}
					var strLine = arrSections[i].getAttribute("line");
					var strColour = '#0000ff';
					var fltOpacity = 0.8;
					switch (strLine) {
						case 'bakerloo':
							strColour = '#ab6612';
							break;
						case 'central':
							strColour = '#df002c';
							break;
						case 'circle':
							strColour = '#f7dc7f';
							break;
						case 'district':
							strColour = '#0d6928';
							break;
						case 'east_london':
							strColour = '#f3ba22';
							break;
						case 'jubilee':
							strColour = '#363b3f';
							break;
						case 'hammersmith_and_city':
							strColour = '#f5a6b3';
							break;
						case 'metropolitan':
							strColour = '#8b004c';
							break;
						case 'northern':
							strColour = '#000000';
							break;
						case 'piccadilly':
							strColour = '#002d73';
							break;
						case 'victoria':
							strColour = '#0076bd';
							break;
						case 'waterloo_and_city':
							strColour = '#89cbc1';
							break;
						default:
							strColour = '#0000ff';
							fltOpacity = 0.4;
					}
					objWalkLine = objPolylineEncoder.dpEncodeToGPolyline(arrPointObjects, strColour, 5, fltOpacity);
					objMap.addOverlay(objWalkLine);
					arrWalkLines.push(objWalkLine);
					if (boolOSMap && !boolSimple) {
						var objOSStyle = {strokeColor: strColour, strokeOpacity: fltOpacity, strokeWidth: 5};
						var objOSLineString = new OpenLayers.Geometry.LineString(arrOSPointObjects);
						var objOSLineFeature = new OpenLayers.Feature.Vector(objOSLineString, null, objOSStyle);
						objVectorLayer.addFeatures([objOSLineFeature]);
					}
				}
			}

			if (boolTube) {
				// Get tube sections and add polyline to map
				var arrSections = arrPaths[intPath].getElementsByTagName("tube");
				for (var i = 0; i < arrSections.length; i++) {
					var arrPointObjects = [];
					var arrPoints = arrSections[i].getElementsByTagName("point");
					for (var j = 0; j < arrPoints.length; j++) {
						var objPoint = new google.maps.LatLng(parseFloat(arrPoints[j].getAttribute("lat")), parseFloat(arrPoints[j].getAttribute("lng")));
						arrPointObjects.push(objPoint);
						objBounds.extend(objPoint);
					}
					if (boolSimple) {
						var strLine = arrSections[i].getAttribute("line");
						var strColour = '#ff0000';
						var fltOpacity = 0.8;
						switch (strLine) {
							case 'bakerloo':
								strColour = '#ab6612';
								break;
							case 'central':
								strColour = '#df002c';
								break;
							case 'circle':
								strColour = '#f7dc7f';
								break;
							case 'district':
								strColour = '#0d6928';
								break;
							case 'east_london':
								strColour = '#f3ba22';
								break;
							case 'jubilee':
								strColour = '#363b3f';
								break;
							case 'hammersmith_and_city':
								strColour = '#f5a6b3';
								break;
							case 'metropolitan':
								strColour = '#8b004c';
								break;
							case 'northern':
								strColour = '#000000';
								break;
							case 'piccadilly':
								strColour = '#002d73';
								break;
							case 'victoria':
								strColour = '#0076bd';
								break;
							case 'waterloo_and_city':
								strColour = '#89cbc1';
								break;
							default:
								strColour = '#ff0000';
						}
					} else {
						var strColour = '#ff0000';
						var fltOpacity = 0.4;
					}
					objTubeLine = objPolylineEncoder.dpEncodeToGPolyline(arrPointObjects, strColour, 5, fltOpacity);
					if (!boolTubeShowing) objTubeLine.hide();
					objMap.addOverlay(objTubeLine);
					arrTubeLines.push(objTubeLine);
				}
			}

			// Zoom
			if (arrPosition[0].getAttribute("zoom")) {
				objMap.setZoom(parseFloat(arrPosition[0].getAttribute("zoom")));
				if (boolOSMap && !boolSimple) objOSMap.setCenter(objOSCentrePoint, parseFloat(arrPosition[0].getAttribute("zoom")));
			} else {
				var objSW = objBounds.getSouthWest();
				var objNE = objBounds.getNorthEast();
				var fltHeight = 0.1 * (objNE.lat() - objSW.lat());
				var fltWidth = 0.1 * (objNE.lng() - objSW.lng());
				objBounds = new google.maps.LatLngBounds(new google.maps.LatLng(objSW.lat() - fltHeight, objSW.lng() - fltWidth), new google.maps.LatLng(objNE.lat() + fltHeight, objNE.lng() + fltWidth));
				objMap.setZoom(objMap.getBoundsZoomLevel(objBounds));
				if (boolOSMap && !boolSimple) objOSMap.setCenter(objOSBounds.getCenterLonLat(), objOSMap.getZoomForExtent(objOSBounds));
			}

			// Add overview map, but only for IE, Firefox and Opera (it breaks on resizing in Safari and Chrome)
			if ($.browser.msie || $.browser.mozilla || $.browser.opera) {
				var objOverviewControl = new google.maps.OverviewMapControl(new google.maps.Size(100,100));
				objMap.addControl(objOverviewControl);
			}

			// Show markers if applicable
			if (boolMarkers) { 
				boolMarkerShowing = false;
				toggleMarkers();
			}

			// Add listener for when map type is changed to/from Earth
			google.maps.Event.addListener(objMap, "maptypechanged", function() {
				if (boolGoogleEarth && objMap.getCurrentMapType().getName() != 'Earth') {
					boolGoogleEarth = false;
					if (!arrDropdownSettings[0]) toggleWalk();
					// if (!arrDropdownSettings[1]) toggleTube();
					if (!arrDropdownSettings[2]) toggleMarkers();
					if (!arrDropdownSettings[3]) togglePhotos();
				} else if (objMap.getCurrentMapType().getName() == 'Earth') {
					boolGoogleEarth = true;
					arrDropdownSettings = [boolWalkShowing, boolTubeShowing, boolMarkerShowing, boolPhotoShowing, boolLabelShowing];
					if (!boolWalkShowing) toggleWalk();
					// if (!boolTubeShowing) toggleTube();
					if (!boolMarkerShowing) toggleMarkers();
					if (!boolPhotoShowing) togglePhotos();
				}
			});

			// Remove loading message
			loadingMessage(objLoadingDiv, objLoadingMessage, "");

			// Hide OS map
			$("#mapOS").hide();
		});
	}
}


// Load an OS map from an XML file
function loadOSMap(strPath, strXmlFile, strPostfix, boolSimple, boolPhotos, boolTube, strEndPoint) {
	objOSMap = new OpenSpace.Map('mapOS');
	objOSGrid = new OpenSpace.GridProjection();
	var objVectorLayer = objOSMap.getVectorLayer();
	objOSMap.addLayer(objVectorLayer);

	if (strXmlFile == 'google_maps_data') {
		var strSimpleParameter =  '';
	} else {
		var strSimpleParameter = '&amp;simple=' + (boolSimple ? '1' : '0');
	}

	var request = OpenLayers.Request.POST({
		url: strPath + strXmlFile + strPostfix + strSimpleParameter,
		callback: function(request) {
			var format = new OpenLayers.Format.XML();
			var objXml = format.read(request.responseText);

			// Skip to the path node with id=strXmlFile
			var arrPaths = objXml.documentElement.getElementsByTagName("path");
			for (var intPath = 0; intPath < arrPaths.length; intPath++) {
				if (strXmlFile == 'google_maps_data') {
					if (arrPaths[intPath].getAttribute("id") == 'all') break;
				} else {
					if (arrPaths[intPath].getAttribute("id") == strXmlFile) break;
				}
			}

			// Pull out position node and centre map
			var arrPosition = arrPaths[intPath].getElementsByTagName("position");
			var objCentrePoint = latLongOpenSpace(parseFloat(arrPosition[0].getAttribute("lat")), parseFloat(arrPosition[0].getAttribute("lng")));
			objOSMap.setCenter(objCentrePoint, 5);

			// Get station nodes and add to map
			var arrStations = arrPaths[intPath].getElementsByTagName("station");
			for (var i = 0; i < arrStations.length; i++) {
				var objPoint = latLongOpenSpace(parseFloat(arrStations[i].getAttribute("lat")), parseFloat(arrStations[i].getAttribute("lng")));
				var arrName = arrStations[i].getElementsByTagName("name");
				var arrDescription = arrStations[i].getElementsByTagName("description");
				createOSStation(objPoint, arrName[0].getAttribute("value") + '<br /><br />' + arrDescription[0].childNodes[0].nodeValue, arrName[0].getAttribute("value"));
			}

			if (!boolSimple) {
				// Get marker nodes and add to map
				var arrMarkers = arrPaths[intPath].getElementsByTagName("marker");
				for (var i = 0; i < arrMarkers.length; i++) {
					var objPoint = latLongOpenSpace(parseFloat(arrMarkers[i].getAttribute("lat")), parseFloat(arrMarkers[i].getAttribute("lng")));
					var arrName = arrMarkers[i].getElementsByTagName("name");
					var arrDescription = arrMarkers[i].getElementsByTagName("description");
					createOSMarker(objPoint, arrName[0].getAttribute("value") + '<br /><br />' + arrDescription[0].childNodes[0].nodeValue, arrName[0].getAttribute("value"));
				}
	
				// Get walk sections and add polyline to map
				var arrSections = arrPaths[intPath].getElementsByTagName("section");
				for (var i = 0; i < arrSections.length; i++) {
					var arrPointObjects = [];
					var arrPoints = arrSections[i].getElementsByTagName("point");
					for (var j = 0; j < arrPoints.length; j++) {
						var objPoint = latLongOpenLayers(parseFloat(arrPoints[j].getAttribute("lat")), parseFloat(arrPoints[j].getAttribute("lng")));
						arrPointObjects.push(objPoint);
						if (objBounds) {
							objBounds.extend(objPoint);
						} else {
							var objBounds = new OpenLayers.Bounds(objPoint.lon, objPoint.lat, objPoint.lon, objPoint.lat);
						}
					}
					var strLine = arrSections[i].getAttribute("line");
					var strColour = '#0000ff';
					var fltOpacity = 0.8;
					switch (strLine) {
						case 'bakerloo':
							strColour = '#ab6612';
							break;
						case 'central':
							strColour = '#df002c';
							break;
						case 'circle':
							strColour = '#f7dc7f';
							break;
						case 'district':
							strColour = '#0d6928';
							break;
						case 'east_london':
							strColour = '#f3ba22';
							break;
						case 'jubilee':
							strColour = '#363b3f';
							break;
						case 'hammersmith_and_city':
							strColour = '#f5a6b3';
							break;
						case 'metropolitan':
							strColour = '#8b004c';
							break;
						case 'northern':
							strColour = '#000000';
							break;
						case 'piccadilly':
							strColour = '#002d73';
							break;
						case 'victoria':
							strColour = '#0076bd';
							break;
						case 'waterloo_and_city':
							strColour = '#89cbc1';
							break;
						default:
							strColour = '#0000ff';
							fltOpacity = 0.4;
					}

					var objStyle = {strokeColor: strColour, strokeOpacity: fltOpacity, strokeWidth: 5};
					var objLineString = new OpenLayers.Geometry.LineString(arrPointObjects);
					var objLineFeature = new OpenLayers.Feature.Vector(objLineString, null, objStyle);
					objVectorLayer.addFeatures([objLineFeature]);
				}
			}

			// Zoom
			if (arrPosition[0].getAttribute("zoom")) {
				objOSMap.setCenter(objCentrePoint, parseFloat(arrPosition[0].getAttribute("zoom")));
			} else {
				objOSMap.setCenter(objBounds.getCenterLonLat(), objOSMap.getZoomForExtent(objBounds));
			}
		}
	});
}


// Creates a station at the given point with the given text label
function createStation(objPoint, strText, strTitle) {
	var objTubeIcon = new google.maps.Icon();
	objTubeIcon.image = strStationImage;
	objTubeIcon.shadow = "";
	objTubeIcon.iconSize = new google.maps.Size(intStationWidth, intStationHeight);
	objTubeIcon.iconAnchor = new google.maps.Point(intStationAnchorX, intStationAnchorY);
	objTubeIcon.infoWindowAnchor = new google.maps.Point(intStationWindowAnchorX, intStationWindowAnchorY);
	objMarkerOptions = { icon:objTubeIcon };

	var objMarker = new google.maps.Marker(objPoint, objMarkerOptions);
	objMap.addOverlay(objMarker);

	var objLabel = new ELabel(objPoint, strTitle, "markerTooltip", new google.maps.Size(6, 0), 50);
	objMap.addOverlay(objLabel);
	arrStationLabels.push(objLabel);
	objLabel.hide();

	// Add info window to stations
	google.maps.Event.addListener(objMarker, "click", function() {
		objMarker.openInfoWindowHtml(strText);
	});
	google.maps.Event.addListener(objMarker,"mouseover", function() {
		objLabel.setOpacity(100);
		objLabel.show();
	});
	google.maps.Event.addListener(objMarker,"mouseout", function() {
		if (!boolLabelShowing) objLabel.hide();
		objLabel.setOpacity(50);
	});
}


// Creates a station at the given point on an OS map with the given text label
function createOSStation(objPoint, strText, strTitle) {
	var objSize = new OpenLayers.Size(12, 20);
	var objOffset = new OpenLayers.Pixel(-6, -20);
	var objInfoWindowAnchor = new OpenLayers.Pixel(6, 10);
	var objIcon = new OpenSpace.Icon('http://labs.google.com/ridefinder/images/mm_20_red.png', objSize, objOffset, null, objInfoWindowAnchor);
	var objMarker = objOSMap.createMarker(objPoint, objIcon, strText, new OpenLayers.Size(250, 360));
}


// Creates a marker at the given point with the given text label
function createMarker(objPoint, strText, strTitle) {
	var objMarkerIcon = new google.maps.Icon();
	objMarkerIcon.image = strMarkerImage;
	objMarkerIcon.shadow = "";
	objMarkerIcon.iconSize = new google.maps.Size(intMarkerWidth, intMarkerHeight);
	objMarkerIcon.iconAnchor = new google.maps.Point(intMarkerAnchorX, intMarkerAnchorY);
	objMarkerIcon.infoWindowAnchor = new google.maps.Point(intMarkerWindowAnchorX, intMarkerWindowAnchorY);
	objMarkerOptions = { icon:objMarkerIcon };

	var objMarker = new google.maps.Marker(objPoint, objMarkerOptions);
	objMap.addOverlay(objMarker, 13, 17);
	arrMarkers.push(objMarker);
	objMarker.hide();

	var objLabel = new ELabel(objPoint, strTitle, "markerTooltip", new google.maps.Size(7, -7), 50);
	objMap.addOverlay(objLabel, 13, 17);
	arrMarkerLabels.push(objLabel);
	objLabel.hide();

	// Add info window to markers
	google.maps.Event.addListener(objMarker, "click", function() {
		objMarker.openInfoWindowHtml(strText);
	});
	google.maps.Event.addListener(objMarker,"mouseover", function() {
		objLabel.setOpacity(100);
		objLabel.show();
	});
	google.maps.Event.addListener(objMarker,"mouseout", function() {
		if (!boolLabelShowing) objLabel.hide();
		objLabel.setOpacity(50);
	});
}


// Creates a marker at the given point on an OS map with the given text label
function createOSMarker(objPoint, strText, strTitle) {
	var objSize = new OpenLayers.Size(12, 20);
	var objOffset = new OpenLayers.Pixel(-6, -20);
	var objInfoWindowAnchor = new OpenLayers.Pixel(6, 10);
	var objIcon = new OpenSpace.Icon('http://labs.google.com/ridefinder/images/mm_20_blue.png', objSize, objOffset, null, objInfoWindowAnchor);
	var objMarker = objOSMap.createMarker(objPoint, objIcon, strText, new OpenLayers.Size(250, 360));
}


// Creates a photo at the given point with the given text label
function createPhoto(objPoint, strText, strTitle) {
	var objPhotoIcon = new google.maps.Icon();
	objPhotoIcon.image = strPhotoImage;
	objPhotoIcon.shadow = "";
	objPhotoIcon.iconSize = new google.maps.Size(intPhotoWidth, intPhotoHeight);
	objPhotoIcon.iconAnchor = new google.maps.Point(intPhotoAnchorX, intPhotoAnchorY);
	objPhotoIcon.infoWindowAnchor = new google.maps.Point(intPhotoWindowAnchorX, intPhotoWindowAnchorY);
	objPhotoOptions = { icon:objPhotoIcon };

	var objPhoto = new google.maps.Marker(objPoint, objPhotoOptions);
	objMap.addOverlay(objPhoto, 13, 17);
	arrPhotos.push(objPhoto);
	objPhoto.hide();

	// Uncomment to show labels for photos
	//var objLabel = new ELabel(objPoint, strTitle, "markerTooltip", new google.maps.Size(7, -7), 50);
	//objMap.addOverlay(objLabel, 13, 17);
	//arrPhotoLabels.push(objLabel);
	//objLabel.hide();

	google.maps.Event.addListener(objPhoto, "click", function() {
		objPhoto.openInfoWindowHtml(strText);
	});
	google.maps.Event.addListener(objPhoto,"mouseover", function() {
		objLabel.setOpacity(100);
		objLabel.show();
	});
	google.maps.Event.addListener(objPhoto,"mouseout", function() {
		if (!boolLabelShowing) objLabel.hide();
		objLabel.setOpacity(50);
	});
}


// Zoom map to full screen
function expandMap() {
	var intWidth = 0;
	var intHeight = 0;

	if (typeof(window.innerWidth) == 'number') {
		// Non-IE
		intWidth = window.innerWidth;
		intHeight = window.innerHeight;
	} else if (document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)) {
		// IE 6+ in standards compliant mode
		intWidth = document.documentElement.clientWidth;
		intHeight = document.documentElement.clientHeight;
	} else if (document.body && (document.body.clientWidth || document.body.clientHeight)) {
		// IE 4 compatible
		intWidth = document.body.clientWidth;
		intHeight = document.body.clientHeight;
	}

	intHeight -= 40;
	intWidth -= 40;

	$('#map').show();
	if (objOSMap) $('#mapOS').show();
	var objCentre = objMap.getCenter();
	$('#header').hide();
	$('#mainSearch').hide();
	$('#logo').hide();
	$('#navigation').hide();
	$('#siteNavigation').hide();
	$('#sidebar').hide();
	$('#sidebarAdvert').hide();
	$('#topSlot').hide();
	$('#bottomSlot').hide();
	$('#article p').hide();
	$('#article h2').hide();
	$('#article ul').hide();
	$('#copyrightMessage').hide();
	$('#wrapper').css('margin', '5px auto auto auto');
	$('#wrapper').width(intWidth);
	$('#article').width(intWidth);
	$('#article').css('background', '#fff top no-repeat');
	$('#article').css('margin-left', '0');
	$('#mapDropdown').hide();
	$('#map').css('margin', 0);
	$('#map').width(intWidth);
	$('#map').height(intHeight);
	$('#mapOS').css('margin', 0);
	$('#mapOS').width(intWidth);
	$('#mapOS').height(intHeight);
	$('#mapFullScreen').css('margin-top', '0');
	$('a#expandMap').text('Shrink map');
	objMap.checkResize();
	if (objOSMap) objOSMap.updateSize();
	if (boolGoogleMapShowing) {
		objMap.panTo(objCentre);
		if (objOSMap) $('#mapOS').hide();
	} else {
		objMap.setCenter(objCentre);
		$('#map').hide();
	}
	boolFullSize = true;
}


// Return map to normal size
function shrinkMap() {
	$('#map').show();
	if (objOSMap) $('#mapOS').show();
	var objCentre = objMap.getCenter();
	$('a#expandMap').text('Full screen');
	$('#mapFullScreen').css('margin-top', '1em');
	$('#mapOS').height('600px');
	$('#mapOS').width('580px');
	$('#mapOS').css('margin', '0em 0em 1em 0em');
	$('#map').height('600px');
	$('#map').width('580px');
	$('#map').css('margin', '0em 0em 1em 0em');
	$('#mapDropdown').show();
	$('#article').css('margin-left', '204px');
	$('#article').css('background', 'url(../images/general/article_top.gif) #fff top no-repeat');
	$('#article').width('580px');
	$('#wrapper').width('960px');
	$('#wrapper').css('margin', '33px auto 5px auto');
	$('#copyrightMessage').show();
	$('#article ul').show();
	$('#article h2').show();
	$('#article p').show();
	$('#bottomSlot').show();
	$('#topSlot').show();
	$('#sidebarAdvert').show();
	$('#sidebar').show();
	$('#siteNavigation').show();
	$('#navigation').show();
	$('#logo').show();
	$('#mainSearch').show();
	$('#header').show();
	objMap.checkResize();
	if (objOSMap) objOSMap.updateSize();
	if (boolGoogleMapShowing) {
		objMap.panTo(objCentre);
		if (objOSMap) $('#mapOS').hide();
	} else {
		objMap.setCenter(objCentre);
		$('#map').hide();
	}
	boolFullSize = false;
}


// Update loading message
function loadingMessage(objLoadingDiv, objLoadingMessage, strText) {
	if (strText) {
		objLoadingMessage.nodeValue = strText;
		objLoadingDiv.style.display = "block";
	} else {
		objLoadingMessage.nodeValue = "";
		objLoadingDiv.style.display = "none";
	}
}


// Display walk metadata
function displayMetadata(intStations, intMarkers, intPhotos, fltWalk, fltTube, fltCrow, boolPhotos, boolTube, strEndPoint) {
	strMarkup  = '<ul>';
	strMarkup += '<li><strong>Walking Distance</strong><br />' + fltWalk + ' miles</li>';
	if (boolTube) strMarkup += '<li><strong>Distance by Tube</strong><br />' + fltTube + ' miles</li>';
	strMarkup += '<li><strong>As the Crow Flies</strong><br />' + fltCrow + ' miles</li>';
	if (boolTube) {
		strMarkup += '<li><strong>Tube Stations</strong><br />' + intStations + ' stations</li>';
	} else {
		strMarkup += '<li><strong>Accommodation</strong><br />' + intStations + ' stops</li>';
	}
	strMarkup += '<li><strong>Points of Interest</strong><br />' + intMarkers + ' points</li>';
	if (boolPhotos) strMarkup += '<li><strong>Photographs</strong><br />' + intPhotos + ' photos</li>';
	strMarkup += '</ul>';
	$('#authorInfo').before(strMarkup);
}


// Convert from latitude/longitude to OpenSpace.MapPoint
function latLongOpenSpace(fltLatitude, fltLongitude) {
	var objPoint = objOSGrid.getMapPointFromLonLat(new OpenLayers.LonLat(fltLongitude, fltLatitude));
	return objPoint;
}


// Convert from latitude/longitude to OpenLayers.Geometry.Point
function latLongOpenLayers(fltLatitude, fltLongitude) {
	var objPoint = objOSGrid.getMapPointFromLonLat(new OpenLayers.LonLat(fltLongitude, fltLatitude));
	return new OpenLayers.Geometry.Point(objPoint.lon, objPoint.lat);
}


// Convert from OpenSpace.MapPoint to latitude/longitude
function openSpaceLatLong(objLonLat) {
	var objPoint = objOSGrid.getLonLatFromMapPoint(objLonLat);
	return objPoint;
}
