function drawAndReviewXHROnMapAddToVisible(xhr, review, map, visible)
{
	// generate array with xml data
	var arrayLength = request.responseXML.getElementsByTagName("point").length;

	var yCoords = request.responseXML.getElementsByTagName("y_coord");
	var xCoords = request.responseXML.getElementsByTagName("x_coord");
	var pointNames = request.responseXML.getElementsByTagName("point_name");
	var pointURLs = request.responseXML.getElementsByTagName("point_url");
	var routeColor = request.responseXML.getElementsByTagName("color")[0].firstChild.nodeValue;
	var routeName = request.responseXML.getElementsByTagName("name")[0].firstChild.nodeValue;
	var routeURL = request.responseXML.getElementsByTagName("url")[0].firstChild.nodeValue;
	var routeType = parseInt(request.responseXML.getElementsByTagName("type")[0].firstChild.nodeValue);

	var routePoints = [];
	var a = 0;
	
	var minLng = parseFloat(xCoords[0].firstChild.nodeValue);
	var maxLng = parseFloat(xCoords[0].firstChild.nodeValue);
	var minLat = parseFloat(yCoords[0].firstChild.nodeValue);
	var maxLat = parseFloat(yCoords[0].firstChild.nodeValue);
	
	while(a < arrayLength)
	{
		var newY = parseFloat(yCoords[a].firstChild.nodeValue);
		var newX = parseFloat(xCoords[a].firstChild.nodeValue);

		routePoints[a] = new GLatLng(newY, newX);

		if(newX < minLng) minLng = newX;
		else if(newX > maxLng) maxLng = newX;

		if(newY < minLat) minLat = newY;
		else if(newY > maxLat) maxLat = newY;
		
		++a;
	}
		
	// route review
	if(review) reviewAreaOnMap(minLat, maxLat, minLng, maxLng, map);

	// draw route
	var routePolyline = new GPolyline(routePoints, "#" + routeColor, 4, 0.7);
	map.addOverlay(routePolyline);
	visible.push(routePolyline);
	
	// draw route label
	var labelIcon = new GIcon();
	labelIcon.image = "http://maps.dtcurrie.net/createIcon.php?text=" + routeName + "&color=" + routeColor;
	labelIcon.iconAnchor = new GPoint(9, 9);
	labelIcon.infoWindowAnchor = new GPoint(9, 9);

	if(routeType <= 3)
	{
		var low;
		if(routePoints[0].lng() == routePoints[routePoints.length - 1].lng() && 
		routePoints[0].lat() == routePoints[routePoints.length - 1].lat())		// if route is a loop
		{
			low = Math.floor(routePoints.length / 4);
		}
		else low = Math.floor(routePoints.length / 2);

		var up = low + 1;
		
		var labelLat = (routePoints[up].lat() - routePoints[low].lat()) * 0.5 + routePoints[low].lat();
		var labelLng = (routePoints[up].lng() - routePoints[low].lng()) * 0.5 + routePoints[low].lng();
		var labelPoint = new GLatLng(labelLat, labelLng);

		var routeLabel = new GMarker(labelPoint, {icon: labelIcon});
		map.addOverlay(routeLabel);
		visible.push(routeLabel);

		GEvent.addListener(routeLabel, "click", function()
		{
			routeLabel.openInfoWindowHtml(routeName + "<br/><a style='cursor: pointer;' target='_blank' href=\'" + routeURL + "\'>Information Page</a>");
		});

		// draw station markers for subway and rail
		if(routeType == 1 || routeType == 3)
		{
			// draw direction arrows on subway routes
			drawRouteArrowsWithPeriodAddToVisible(routePoints, map, 1, visible);
		
			var stationIcon = new GIcon();
			stationIcon.image = "http://maps.dtcurrie.net/img/route_point.png";
			stationIcon.iconSize = new GSize(11, 11);
			stationIcon.iconAnchor = new GPoint(5, 5);
			stationIcon.infoWindowAnchor = new GPoint(5, 5);

			var b = 0;
			while(b < routePoints.length)
			{
				var stationClickable = true;
				if(pointNames[b].firstChild.nodeValue == "null" && pointURLs[b].firstChild.nodeValue == "null") stationClickable = false;
				
				var stationMarker = new GMarker(routePoints[b], {icon: stationIcon, clickable: stationClickable});
				stationMarker.type = "station";
				stationMarker.name = pointNames[b].firstChild.nodeValue;
				stationMarker.url = pointURLs[b].firstChild.nodeValue;
				
				map.addOverlay(stationMarker);
				visible.push(stationMarker);
				
				++b;
			}
		}
		// draw direction arrows on bus routes
		else if(routeType == 2) drawRouteArrowsWithPeriodAddToVisible(routePoints, map, 4, visible);
	}
	else if(routeType >= 4)
	{
		var labelLat = (maxLat - minLat) / 2 + minLat;
		var labelLng = (maxLng - minLng) / 2 + minLng;

		var labelPoint = new GLatLng(labelLat, labelLng);

		var routeLabel = new GMarker(labelPoint, {icon: labelIcon, clickable: false});
		map.addOverlay(routeLabel);
		visible.push(routeLabel);

		if(routeType == 5)
		{
			GEvent.addListener(routeLabel, "click", function()
			{
				routeLabel.openInfoWindowHtml("<a style='cursor: pointer;' target='_blank' href=\'" + routeURL + "\'>Information Page</a>");
			});
		}
	}
}

function reviewXHROnMap(xhr, map)
{
	var arrayLength = xhr.responseXML.getElementsByTagName("point").length;

	var yCoords = xhr.responseXML.getElementsByTagName("y_coord");
	var xCoords = xhr.responseXML.getElementsByTagName("x_coord");

	var minLng = parseFloat(xCoords[0].firstChild.nodeValue);
	var maxLng = parseFloat(xCoords[0].firstChild.nodeValue);
	var minLat = parseFloat(yCoords[0].firstChild.nodeValue);
	var maxLat = parseFloat(yCoords[0].firstChild.nodeValue);
	
	var d = 0;
	while(d < arrayLength)
	{
		var nextY = parseFloat(yCoords[d].firstChild.nodeValue);
		var nextX = parseFloat(xCoords[d].firstChild.nodeValue);

		if(nextX < minLng) minLng = nextX;
		else if(nextX > maxLng) maxLng = nextX;

		if(nextY < minLat) minLat = nextY;
		else if(nextY > maxLat) maxLat = nextY;

		++d;
	}

	// route review
	reviewAreaOnMap(minLat, maxLat, minLng, maxLng, map);
}

function reviewAreaOnMap(minLat, maxLat, minLng, maxLng, map)
{
	var longCenter = (minLng + maxLng) / 2;
	var latCenter = (minLat + maxLat) / 2;

	var currentSpan = map.getBounds();
	var currentWidth = currentSpan.getNorthEast().lng() - currentSpan.getSouthWest().lng();
	var currentHeight = currentSpan.getNorthEast().lat() - currentSpan.getSouthWest().lat();

	var spanRatio = [];
	if(((maxLng - minLng) / currentWidth) >= ((maxLat - minLat) / currentHeight)) spanRatio = ((maxLng - minLng) / currentWidth);
	else spanRatio = ((maxLat - minLat) / currentHeight);

	var zoomLevel = map.getZoom();

	while(spanRatio > 0.97)		// 0.97 pads the route from the map edges
	{
		zoomLevel = zoomLevel - 1;
		spanRatio = spanRatio / 2;
	}

	while(spanRatio < 0.48)		// 0.48 pads the route from the map edges
	{
		zoomLevel = zoomLevel + 1;
		spanRatio = spanRatio * 2;
	}

	map.setZoom(zoomLevel);
	map.panTo(new GLatLng(latCenter, longCenter));
}

function drawRouteArrowsWithPeriodAddToVisible(points, map, period, visible)
{
	if(points[0].lng() == points[points.length - 1].lng() && 
	points[0].lat() == points[points.length - 1].lat())		// draw arrows only if route is a loop
	{
		var e = 1;		// ignore the first point (point 0), because it is a loop
		while(e < points.length)
		{
			var overlap = false;
			var f = 0;
			while(f < points.length)				
			{		
				if(f != e && 
				points[e].lng() == points[f].lng() &&
				points[e].lat() == points[f].lat())		// if f has same coordinates as e
				{
					if(points[e - 1].lng() == points[f + 1].lng() &&
					points[e - 1].lat() == points[f + 1].lat())		// if f+1 has same coordinates as e-1
					{
						overlap = true;
						break;		// do nothing at point e
					}
				}
				else if((e + 1) < points.length)		// f does not share coordinates with e.  also test if e is a loop endpoint
				{
					if(points[e - 1].lng() == points[e + 1].lng() &&
					points[e - 1].lat() == points[e + 1].lat())		// if e+1 has same coordinates as e-1
					{
						overlap = true;
						break;		// do nothing at point e
					}
				}
		
				++f;
			}
					
			if(overlap == false)
			{
				var dx = points[e].lng() - points[e - 1].lng();
				var dy = points[e].lat() - points[e - 1].lat();

				var arrowIcon = new GIcon();
				arrowIcon.image = "http://maps.dtcurrie.net/createArrow.php?dx=" + dx + "&dy=" + dy;
				arrowIcon.iconSize = new GSize(20, 20);
				arrowIcon.iconAnchor = new GPoint(10, 10);

				var arrowLat = (points[e].lat() - points[e - 1].lat()) * 0.6 + points[e - 1].lat();
				var arrowLng = (points[e].lng() - points[e - 1].lng()) * 0.6 + points[e - 1].lng();

				var arrowPoint = new GLatLng(arrowLat, arrowLng);
				var arrowMarker = new GMarker(arrowPoint, {icon: arrowIcon, clickable: false});
				map.addOverlay(arrowMarker);
				visible.push(arrowMarker);
			}
					
			e += period;
		}
	}
}