<?xml version="1.0" encoding="UTF-8"?>
<Module>
<ModulePrefs title="DCist Metro Map"
                description="Provides an overlay of the Washington, D.C. subway system"
				directory_title="DCist Metro Map"
                author="Tom Lee"
                author_email="tomREMOVETHIS@dcist.com"
                author_location="Washington, D.C."
                author_affiliation="DCist.com"
                author_photo="http://dcist.com/attachments/dcist_tom/20060107_staff_tom.jpg"
                author_link="http://www.manifestdensity.net"
                author_aboutme="Tom Lee is a web developer living in Washington, D.C.  He serves as the tech director at DCist.com and is a senior software architect at EchoDitto.  Visit his blog at http://www.manifestdensity.net."
                screenshot="http://dcist.com/attachments/dcist_tom/mapplet_screenshot_big.png"
                thumbnail="http://dcist.com/attachments/dcist_tom/mapplet_screenshot.png"
                title_url="http://www.dcist.com"
                height="150">
  <Require feature="sharedmap"/>
</ModulePrefs>
<Content type="html"><![CDATA[

<div style="width:100%; background: #fff url(http://dcist.com/attachments/dcist_tom/dcist_mapplet_bg.gif) 0px 0px repeat-x; font-family: Arial,Helvetica,sans-serif">

	<a href="http://www.dcist.com"><img src="http://dcist.com/attachments/dcist_tom/dcist_logo_transparent.gif" border="0" alt="DCist Logo" border="0"></a>
	
	<p><small><a href="http://www.dcist.com">DCist</a> is a weblog about Washington, D.C.  This mapplet displays the Washington subway system.  To center the map on D.C., click <a href="javascript:map.setCenter(new GLatLng(38.900652178533,-77.02995300293), 11);">here</a>.</small></p>
	
	<p><small>Mapplet by Tom Lee (<a href="http://www.dcist.com/staff.php#dcist_tom">bio</a> | <a href="http://www.manifestdensity.net">blog</a>)</small></p>

</div>

<script type="text/javascript" language="javascript">
var station_xml_url = "http://www.dcist.com/map/stations.xml";

var map = new GMap2();

var LINE_WIDTH = 10;
var MAP_WIDTH = 0;
var MAP_HEIGHT = 0;
var xlineScale = 0;
var ylineScale = 0;

var stations = [];
var lineNodes = [];
var lineColors = [];
var drawnLines = [];
var mapDrawn = false;

//click handling variables
var inAddMarkerMode = false;
var inRemoveMarkerMode = false;
var clickListener;

function RecalculateWidthConstants()
{
	MAP_WIDTH = 0;
	MAP_HEIGHT = 0;
	xlineScale = 0;
	ylineScale = 0;
	map.getSizeAsync(function(size)
	{
		MAP_WIDTH = size.width;
		MAP_HEIGHT = size.height;
		
		// always recalculate scale constants too, since they're defined w/ WIDTH and HEIGHT
		RecalculateScaleConstants();		
	});	
}

function RecalculateScaleConstants()
{
	xlineScale = 0;
	ylineScale = 0;
	
	map.getBoundsAsync(function(mapGLatLng) 
	{
		var mapSpan = mapGLatLng.toSpan();
	
		xlineScale = (((LINE_WIDTH)/2.0)/MAP_WIDTH) * mapSpan.lng();
		ylineScale = (((LINE_WIDTH)/2.0)/MAP_HEIGHT) * mapSpan.lat();
	});
}

function Station(name,lat,lng,type){
	this.name = name;
	this.lat = lat;
	this.lng = lng;
	this.stationtype = type;
}

function LineNode(name,lat,lng,offset){
	this.name = name;
	this.lat = lat;
	this.lng = lng;
	this.offset = offset;
}

function CreateStationMarker(name, point, stationIcon){

	var marker = new GMarker(point,{icon:stationIcon});

	// Show this marker's index in the info window when it is clicked
	var html = "<div class='stationoverlay'><strong>" + name + "</strong></div>";
	GEvent.addListener(marker, "click", function() 
	{
		map.panTo(point);
		marker.openInfoWindowHtml(html);
	});

	return marker;
}

function GetIconSuffixFromZoom(zoomLevel)
{
	if(zoomLevel>=15)
		return '';
	else if((zoomLevel==14)||(zoomLevel==13))
		return '_smaller';
	else if((zoomLevel==11)||(zoomLevel==12))
		return '_smallest';
	else
		return 'NONE';
}

function DrawStations(){
	
	map.getZoomAsync(function(zoomlevel) {

		var iconSuffix = GetIconSuffixFromZoom(zoomlevel);
		
		if(iconSuffix!='NONE')
		{
			var normalstationiconurl = "ns" + iconSuffix;
			var normalstationiconsize = -1;
	
			var transferstationiconurl = "ts" + iconSuffix;
			var transferstationiconsize = -1;
	
			if(iconSuffix=='')
			{
				normalstationiconsize = 18;
				transferstationiconsize = 30;
			}
			else if(iconSuffix=='_smaller')
			{
				normalstationiconsize = 9;
				transferstationiconsize = 15;
			}
			else if(iconSuffix=='_smallest')
			{
				transferstationiconsize = 9;
				normalstationiconsize = 5;
			}
		
			if(navigator.userAgent.toLowerCase().indexOf('msie')==-1){
				transferstationiconurl = 'http://www.dcist.com/map/images/' + transferstationiconurl + '.png';
				normalstationiconurl = 'http://www.dcist.com/map/images/' + normalstationiconurl + '.png';
			}else{
				transferstationiconurl = 'http://www.dcist.com/map/images/' + transferstationiconurl + '.gif';
				normalstationiconurl = 'http://www.dcist.com/map/images/' + normalstationiconurl + '.gif';
			}
	
			if(normalstationiconsize!=-1){
	
				var halfnormalstationsize = Math.floor(normalstationiconsize / 2);
				var halftransferstationsize = Math.floor(transferstationiconsize / 2);
	
				//create the station icons
				var normalStationIcon = new GIcon();
				normalStationIcon.image = normalstationiconurl;
				normalStationIcon.iconSize = new GSize(normalstationiconsize,normalstationiconsize);
				normalStationIcon.iconAnchor = new GPoint(halfnormalstationsize,halfnormalstationsize);
				normalStationIcon.infoWindowAnchor = new GPoint(halfnormalstationsize,halfnormalstationsize);
				normalStationIcon.infoShadowAnchor = new GPoint(0,0);
				normalStationIcon.shadow = "http://www.dcist.com/map/images/blank.gif";
				normalStationIcon.shadowSize = new GSize(10,10);
	
				var transferStationIcon = new GIcon();
				transferStationIcon.image = transferstationiconurl;
				transferStationIcon.iconSize = new GSize(transferstationiconsize,transferstationiconsize);
				transferStationIcon.iconAnchor = new GPoint(halftransferstationsize,halftransferstationsize);
				transferStationIcon.infoWindowAnchor = new GPoint(halftransferstationsize,halftransferstationsize);
				transferStationIcon.shadow = "http://www.dcist.com/map/images/blank.gif";
				transferStationIcon.shadowSize = new GSize(10,10);
				transferStationIcon.infoShadowAnchor = new GPoint(0,0);
			
				for(var i=0;i<stations.length;i++)
				{	
					var point = new GLatLng( parseFloat(stations[i].lat), parseFloat(stations[i].lng) );						
					var marker = CreateStationMarker(stations[i].name,point,((stations[i].stationtype=='transfer') ? transferStationIcon : normalStationIcon));						
					map.addOverlay(marker);				
				}
			}
		}
		
		mapDrawn = true;

	});
}

function DrawLines()
{
	drawnLines = [];
	for(var lcv=0;lcv<lineNodes.length;lcv++)
		drawnLines.push(DrawLine(lineNodes[lcv],lineColors[lcv]));

}

function DrawLine(linenodes,color)
{
	var metrolinePoints = [];
	for(var i=0;i<(linenodes.length-1);i++)
	{
		var offsetFlag1 = parseFloat(linenodes[i].offset);
		var offsetFlag2 = parseFloat(linenodes[i+1].offset);


		var point1x = parseFloat(linenodes[i].lat);
		var point1y = parseFloat(linenodes[i].lng);
		var point2x = parseFloat(linenodes[i+1].lat);
		var point2y = parseFloat(linenodes[i+1].lng);


		if((offsetFlag1!=0)&&(offsetFlag2!=0))
		{

			var rise = (point2y - point1y);
			var run = (point2x - point1x);

			var xoffset, yoffset;

			//horizontal line
			if(rise==0){

				xoffset = 0;
				yoffset = (-1) * offsetFlag1;

			}
			//vertical line
			else if(run==0){

				xoffset = (-1) * offsetFlag1;
				yoffset = 0;

			}
			//all others
			else
			{
				var slope = (rise / run);

				var normalslope = (-1 / slope);

				xoffset = Math.sqrt( 1 / (1 + (normalslope*normalslope)) );
				yoffset = xoffset * normalslope;

				//flip offsetflag if slope is negative (due to Math.sqrt always returning positive)
				if(slope<0)					
					offsetFlag1 = offsetFlag1 * -1;				

				//apply offset flag
				xoffset = xoffset * offsetFlag1;
				yoffset = yoffset * offsetFlag1;
			}

			//scale offset by previously determined factor
			xoffset = xoffset * xlineScale;
			yoffset = yoffset * ylineScale;

			point1x = point1x + xoffset;
			point1y = point1y + yoffset;

			point2x = point2x + xoffset;
			point2y = point2y + yoffset;

		}

		var point1 = new GPoint(point1x,point1y);
		var point2 = new GPoint(point2x,point2y);

		metrolinePoints.push(point1);
		metrolinePoints.push(point2);
	}

	var newLine = new GPolyline(metrolinePoints,color,LINE_WIDTH);
	map.addOverlay(newLine);
	
	return newLine;
}

function DoSetup()
{
	// calculate the scaling constants based on map bounds
	RecalculateWidthConstants();

	_IG_FetchXmlContent(station_xml_url, function(response) {
		
		//get station data
		var markers = response.getElementsByTagName("station");
		for (var i = 0; i < markers.length; i++) 
		{
			var marker = markers.item(i);
			stations.push( new Station(
				marker.getAttribute("name"),
				parseFloat(marker.getAttribute("lat")),
				parseFloat(marker.getAttribute("lng")),
				marker.getAttribute("stationtype")
			) );
		}

		//get names of the line node types to retrieve
		var nodeTagNames = [];
		markers = response.getElementsByTagName("line");
		for (var i = 0; i < markers.length; i++) {
			var marker = markers.item(i);
			nodeTagNames.push(marker.getAttribute("nodetagname"));
			lineColors.push(marker.getAttribute("color"));
		}

		//retrieve each of those line node types into a multidimensional array
		for(var lcv=0;lcv<nodeTagNames.length;lcv++){
			var tempNodes = [];
			markers = response.getElementsByTagName(nodeTagNames[lcv]);
			for (var i = 0; i < markers.length; i++) {
				var marker = markers.item(i);
				tempNodes.push( new LineNode(
						marker.getAttribute("name"),
						parseFloat(marker.getAttribute("lng")),
						parseFloat(marker.getAttribute("lat")),
						marker.getAttribute("offset")
				) );
			}
			lineNodes.push(tempNodes);
		}			
				
		map.clearOverlays();
		
		DrawMapWhenReady(true,true);		
	});		
}

function DrawMapWhenReady(lines, stations)
{
	if((MAP_WIDTH!=0)&&(MAP_HEIGHT!=0)&&(xlineScale!=0)&&(ylineScale!=0))
	{					
		if(lines==true) 
			DrawLines();
			
		if(stations==true) 
			DrawStations();
	}
	else
	{
		setTimeout(function()
			{				
				DrawMapWhenReady(lines,stations);
			}, 100
		);		
	}
}


//center and zoom on DC - for debugging
//map.setCenter(new GLatLng(38.900652178533,-77.02995300293), 13);

DoSetup();

GEvent.addListener(map, 'zoomend', function(oldZoom, newZoom)
{
	// we'll always need to change the scale constants
	RecalculateScaleConstants();
	
	// have the zoom levels changed such that we have to redraw everything?
	if(GetIconSuffixFromZoom(oldZoom)!=GetIconSuffixFromZoom(newZoom))
	{
		map.clearOverlays();
		DrawMapWhenReady(true,true);
	}
	// no? then just redrawn the lines, not the stations
	else
	{
		// remove the lines
		for(var i=0;i<drawnLines.length;i++)
			map.removeOverlay(drawnLines[i]);

		// redrawn the lines only, not the stations
		DrawMapWhenReady(true,false);		
	}
});

</script>
]]></Content>
</Module>
