//<![CDATA[

// (c) 2006-2009 matt mikul  and veloroutes.org
// feel free to copy, just list veloroutes.org as a source.

//initialize click-handling and route building
function initClickHandling(map) {
	this.map = map;
	this.route = new Array();

	//route recording
	this.recording = false;
	toggleRecording(); //on by default

	var rec = document.getElementById("recording");
	if(rec){rec.checked = "checked";}

	var mtr = document.getElementById("metric");
	if(mtr){mtr.checked = useMetric;}

	var _eu = getElem("eleUnit");
	if(_eu) {_eu.value = distUnit;}

	var saveR = document.getElementById("saveRoute");
	if(saveR) {saveR.disabled = true;}

	//var g = document.getElementById("export");
	//if(g) {g.disabled = true; }

	var disa = document.getElementById("clearRoute");
	if(disa) {disa.disabled = true;}

	var del = document.getElementById("deleteLastPoint");
	if(del){del.disabled = true;}

	var oc = getElem("centerOnClick");
	if(oc){oc.checked = false; }
	this.recenterOnClick = false;

	oc = getElem("useScrollWheel");
	if(oc){oc.checked=false;}
	this.useScrollWheel = false;

	/*setVal('totalDist','');
	setVal('routeName','');
	setVal('routeComm','');
	setVal('routeTags','');*/
}

function loadRouteJSON(noMileMarkers) {
try{
	var r = getElem("json").innerHTML;
	r = eval('('+ r +')');
	if(r && r.route) {
		this.route = r.route;
		var gain = r.route.gain;
		var oGain = r.route.oGain;
		if(gain != null && oGain != null) 
		{ 
			this.totalEleGain = parseFloat(gain);
			this.origEleGain = parseFloat(oGain);
		}
		else { this.totalEleGain = 0.0; this.origEleGain=0.0; }
		this.totalDist = parseFloat(r.route.dist);

		setVal("feedbackBar", buildSpeedAvgs(this.totalDist));

		loadRouteFromString(r.route.pts.split('|'),routeColor,0.75,true);
		var eleData = r.route.ele.split(',');
		loadElevationGraphFromString(eleData);
		if(!noMileMarkers) {
			loadMileMarkers();
		}
	}
	else {
		dbg("no route json-data?!");
	}
}
catch(e) {alert("Error loading route: " + e);}
}

//toggle the use of metric measurements (default is off)
function toggleUseMetric() {
	this.useMetric= !this.useMetric;
	var ele = getElem("elevation").value;
	this.lastEle = ele;
	if(this.useMetric) {
		distUnit = " km";
		speedUnit = " kph";
		eleUnit = 'm';
		metricRatio = 1.609344;
		footRatio = .3048;
		feetInMile = 1000;
		if(ele > 0) {
			getElem("elevation").value = myParseFloat(lastEle*footRatio);
		}
	}
	else {
		distUnit = " mi";
		speedUnit = " mph";
		metricRatio = 1.0;
		feetInMile = 5280;
		if(ele > 0) {
			getElem("elevation").value = myParseFloat(lastEle/footRatio);
		}
		footRatio = 1.0;
		eleUnit = 'ft';
	}

	if(this.totalDist > 0) {
		getElem('totalDist').innerHTML = (totalDist * metricRatio).toFixed(2).toString() + " " + distUnit;
		setVal("feedbackBar", buildSpeedAvgs(this.totalDist));
	}

	getElem('eleUnit').innerHTML = eleUnit;
		
	if(this.elevationPts.length > 0) {
		setVal("elevationGraph", buildElevationGraph(this.elevationPts, this.currElevation) + buildEleDetailsString());
	}
}

//load a route passed in by URL
function loadRouteFromURL(url) {
	//route may come entirely from querystring or be saved serverside 
	var id = getQuerystringParameter('route', url);

	if (id.length > 0) {
		this.currRouteID = id;

		//turn off recording when loading a route
		this.recording = true;
		toggleRecording(); 

		var rec = document.getElementById("recording");
		if(rec){rec.checked = "";}



		//called from gmap.js
		loadRouteJSON();
		//loadRouteByID(id,true);
		
		getElem("export").disabled = false;
	
		var cue = getQuerystringParameter('c',url);
		if(cue) { window.location=getGCue();return;}
	
		return 1;
	}
	return 0;
}

//take in a list of lat/long coords delimited by '|' and commas
function loadRouteFromString(routeArr,color,opacity,moveMap) {
	var lastLeg = new Array();
	this.route.length = 0;
	var len = routeArr.length;
	var doSkip = (len > 500); //7/25/07 - long routes (id=197 are timing out in ff on ubuntu)

//	dbg("len = " + len);

	var doEle = moveMap;
	for (i in routeArr) {
		if(!doSkip || (i % 4 != 0)) {
 
		var pt = routeArr[i].split(",");
		  if(pt.length == 2) {
				var point = new GLatLng(parseFloat(pt[0]), parseFloat(pt[1]));
				lastLeg.push(point);
	          }	
		} //end skip if
		

	} //end for
		
	if(moveMap) {
		var lat;
		if(lastLeg.length > 0) {
			lat = lastLeg[0];
			if(lat == null) { dbg("???lat=null"); }
			getElem('elevationGraph').innerHTML="";
		}

	}

	if(lastLeg.length > 0) {
		this.currPolyline = new GPolyline(lastLeg, color, 5, 0.75);//(opacity ? opacity : 1.0));
		if(moveMap == true) {
			map.setCenter(lastLeg[0],13);
			map.clearOverlays();
			try {
				map.panTo(new GLatLng(parseFloat(lastLeg[0].y),parseFloat(lastLeg[0].x)));	
				
				//9/13/07 - add marker on first point
				var marker = new GMarker(lastLeg[0], createBikeIcon());
				map.addOverlay(marker);
				
			} catch(e){}
		}
		map.addOverlay(this.currPolyline);

		var bounds = this.currPolyline.getBounds();
                map.setZoom(map.getBoundsZoomLevel(bounds));
                map.setCenter(bounds.getCenter());

		this._route = lastLeg;
		/*window.setTimeout(function() {
				loadMarkers();
				}, 100);*/
	}

	/*if(doSkip) {
		dbg("NOTE: This route is very detailed (" + len + " points), and has been drawn\nless accurately due to limitations of the Google Maps API.");
		dbg("Download the route in Google Earth (KML) or GPX to see its full detail.");
	}*/


} //end func

//build an elevation graph
function loadElevationGraphFromString(eles) {

try {
	getElem("elevationGraph").value = "";
	this.currMaxEle = 0;
	this.currMinEle = 0;
	this.currentElevation = 0;
	this.lastElevation = 0;
	this.maxGrade = 0;
	var table = [];
	var e;
	this.elevationPts = new Array();
	this.currMaxEle = 0;
	var ele = 0;

	if(eles.length > 0) { this.currMinEle = eles[0]; this.lastElevation = eles[0]; }
	
	for(e in eles) {
		
		ele = eles[e].toString().replace(",","");
		if(ele != "" && ele != null && ele >= 0 && ele != NaN ) {
			ele = parseFloat(ele);
			if(ele > this.currMaxEle) {
				this.currMaxEle = ele;
			}
	
			if(ele < this.currMinEle) { this.currMinEle = ele;  }

			if(ele && parseFloat(ele)) {
				this.currentElevation = parseFloat(ele);
				
				buildEleDetailsString(true);
				this.ele = ele.toFixed(1);
				this.elevationPts.push(ele);
				this.lastElevation = this.currentElevation;
			}	
		}
		else { //in the db they're saved with a , at the end...
			continue;
		}
	}
	}catch(e) { alert("error building ele** graph: " + e); }
	showElevation(null,this.ele);
	
	getElem("elevationGraph").innerHTML += "<span class=\"smallText\"><b>total elevation gain:</b> " + this.totalEleGain.toFixed(1) + " ft</span>&nbsp;(raw value: " + this.origEleGain.toFixed(1)+")";
}

//returns a string used for a link back to a map state
function createPageLink() {
	var latLng = map.getCenter();
	var z = map.getZoom();
	var t = map.getCurrentMapType().getUrlArg();
	var link = siteURL + '/?x=' + latLng.lng().toFixed(5) + '&y=' 
		+ latLng.lat().toFixed(5) + '&z=' + z 
		+ '&t=' + t;

	setVal("feedbackBar", "<span class=\"largeText\">link: </span><a href=\"" + link  + "\">" + link + "</a>");
}

//load a route passed in by URL
function loadMapStateFromURL(url) {
	//route may come entirely from querystring or be saved serverside 
	var lat = parseFloat(getQuerystringParameter('y', url));
	var lng = parseFloat(getQuerystringParameter('x', url));
	var z = parseInt(getQuerystringParameter('z', url));
	var t = getQuerystringParameter('t', url);
	setVal("elevationGraph", "");
	if(t == "m") {	t = G_NORMAL_MAP; }
	else if(t == "k") { t = G_SATELLITE_MAP; }	
	else if(t == "h") { t = G_HYBRID_MAP;	}
	else if(t == "o" || t == "t") {t = WMS_TOPO_MAP; }
	else if(t == "n") { t = WMS_NEXRAD_MAP; }
	else if(t == "p") { t = G_PHYSICAL_MAP; } //new for 2.94
	else {	t = G_NORMAL_MAP; }
	//these don't apply to most places
	getElem("localonly").checked = false;
	getElem("addrTB").value = '';
	if (lat) {
		map.setCenter(new GLatLng(lat,lng),z,t);
		return 1;
	}

	return 0;
}

//serialize the route
function saveRoute() {
	var args;
	   try {

		var route_name = document.getElementById("routeName").value;
		if(!route_name) {
			alert("Enter a route name (use the textbox on the left side).");
			return;
		}

		//disable during save
		document.getElementById("saveRoute").disabled = true;


		var tags = getVal("routeTags");
		var comm = getVal("routeComm");
		var ajaxObj = createAjaxObj();
		var output = "";
		for (point in this.route)
		{
			output += toFixedPrecision(route[point]) + "|";
		}
		var eleData = "";
		var ele = "";
		for(e in this.elevationPts) {
			ele = this.elevationPts[e];
			if(ele && ele != null && ele != "") {
				try {
					eleData += parseFloat(this.elevationPts[e]).toFixed(3) + ",";
				} catch(e) { }
			}
		}

		//clear out spaces, parens
		output = output.replace(/[\(\) ]/g, "");	
	
		//NOTE: changing the names/order means changing the backend too..
		args = "route_dist=" + this.totalDist.toFixed(4).toString()
				+ "&route_points=" + output 
				+ "&route_name=" + route_name
				+ "&route_tags=" + tags
				+ "&route_comments=" + comm
				+ "&ele_data=" + eleData
				+ "&ele_gain=" + this.totalEleGain;
				
                var doUrchin = 1;
		//edit, note create
		if(this.editMode == 1 && this.currRouteID > 0) {
			args += "&route_id=" + this.currRouteID;	
			doUrchin = 0;
		}

		this.editMode = true;
		var tmpID = 0;
		ajaxObj.open("POST", "/cgi-bin/save_route3.cgi",true);
		ajaxObj.onreadystatechange = 

function() {
try {
	if (ajaxObj.readyState == 4) {
		if (ajaxObj.status==200 || ajaxObj.status==302 || ajaxObj.status==304)
		{ //if request was successful
			var xmldata=ajaxObj.responseXML;
			
			if (xmldata && xmldata.getElementsByTagName("routeID").length==0)
			{
				alert("Error saving route..." + ajaxObj.status);
			}
			else  if(xmldata) 
			{
				var id = xmldata.getElementsByTagName("routeID")[0].firstChild.nodeValue;
				
				tmpID =  parseInt(id);
				setCurrID(id);
				//save btn becomes update btn
				setVal("saveRoute","update");
				var newUrl = siteURL + "/?route=" + id.toString();
				
				//give link to route and cue-sheet
				setVal("feedbackBar", "<span class=\"medText\">route saved.</span> link:<br/> <a href=\"" + newUrl + "\">" + newUrl + "</a><p/>The start city of your route will be automatically set, check the homepage.");
				//cuesheets:  + "<p/><span class=\"medText\">cue-sheet</span>: <a href=\"http://veloroutes.org/cuesheets/"+id+".html\" target=\"about:blank\">" + "http://veloroutes.org/cuesheets/"+id + ".html</a> 
				
				fadeBlueToWhite("feedbackBar");
				getElem("export").disabled=false;
				
				var c = document.getElementById("asCue");
				if(c) {c.disabled = false; }
				
				//we need to rebuild this so they can click the link
				//showElevation(route[route.length-1]);

				if(doUrchin) {
					//7-30-07 analytics tracking of saves
					urchinTracker('/bikemaps/save');
				}
			}
			else { alert("Error! " + xmldata); }
		}
		else {
			alert('There was a problem saving the route!\nError Code: ' + ajaxObj.status);
		}
		
	} 
}
catch(e) { alert("Error saving data: " + e); }
} //end anon func;
	
	//show 'loading' message	
	setVal("feedbackBar", "<span class=\"largeText\">saving route...</span>");
		
	//send the post
	ajaxObj.send(args);
	
} catch(e) { alert("ERROR saving route: " + e); }
} //end func

function setCurrID(id) {
	this.currRouteID = id;
}

//clear routes
function clearRoute() {
   var answer = confirm("clear all current route points?");
   if(answer) {
	map.clearOverlays();
	setVal('totalDist', '');
	this.route = new Array();
	getElem("deleteLastPoint").disabled = true;
	getElem("saveRoute").disabled = true;
	getElem("clearRoute").disabled = true;
	setVal("saveRoute", "save");
	setVal("feedbackBar", "<span class=\"medText\">route cleared.</a>");
	fadeBlueToWhite("feedbackBar");
	//setVal("feedbackBarEle", "");
	getElem("elevationGraph").innerHTML="";
	getElem("elevationDetails").innerHTML="";
	this.currMaxEle = 0;
	this.currMinEle = 0;
	this.maxGrade = 0;
	this.currentElevation = 0;
	this.totalEleGain =0;
	this.lastElevation = null;
	this.elevationPts = new Array();
	this.route = new Array();
	this.totalDist = 0;
	this.currRouteID=0;
   }
}

//toggle recording value
function toggleRecording() {
	if(this.recording)
	{
		this.recording = false;
		GEvent.clearListeners(map, "click");
		document.getElementById("saveRoute").disabled = true;
   		document.getElementById("clearRoute").disabled = true;
   		document.getElementById("deleteLastPoint").disabled = true;
	}
	else
	{
		GEvent.addListener(map, "click", clickHandler); 
		this.recording = true; 
		if(this.route.length > 0) {
			document.getElementById("saveRoute").disabled = false;
			document.getElementById("clearRoute").disabled = false;
			document.getElementById("deleteLastPoint").disabled = false;
		}
	}
}

//handles clicks within the map, adds to or creates a route
function clickHandler(overlay, point) {
	if(overlay) { }
	else if(point) {
		if(route == null || route.length == 0) { startRoute(point); }
		else if(route[route.length-1].x != point.x || route[route.length-1].y != point.y)
		{
			route.push(point);
			addPointToRoute(route);
		}
	}
}

//parse float, return zero for nulls
function myParseFloat(num) {
 if(!num) { return 0.0; }
  return parseFloat(num).toFixed(2);
}

//returns a string of avg speed & time taken
function buildSpeedAvgs(dist) {
	var spds = new Array();
	spds.push((10*metricRatio).toFixed(1));
	spds.push((13*metricRatio).toFixed(1));
	spds.push((16*metricRatio).toFixed(1));
	spds.push((19*metricRatio).toFixed(1));
	spds.push((22*metricRatio).toFixed(1));

	var ret = "";

	for (spd in spds)
	{
		var t = (dist*metricRatio/spds[spd]).toFixed(4);
		var two = t.toString().split(".");
		ret += "at " + spds[spd] + speedUnit + " ";
		if(t != "0" && (dist*metricRatio) >= spds[spd]) {
			ret += "<b>" + two[0] + " hr" + (two[0] == 1 ? " " : "s ") +   (two[1]*.006).toFixed(2) + " mins.</b><br>";
		}
		else { //list in minutes
			ret += "<b>" +  (two[1]*.006).toFixed(2) + " min(s)</b><br>";
		}
	}
	
	return ret;
}

function toggleRecenter() { 
	this.recenterOnClick = !this.recenterOnClick; 
}

//init the route member, push on the first point
function startRoute(point) {
   
   this.route = new Array();
   route.push(point);
   getElem("elevationGraph").innerHTML="";
   getElem("elevationDetails").innerHTML="";
   getElem("routeName").value="";
   getElem("routeComm").value="";
   getElem("routeTags").value="";
   this.currMaxEle = 0;
   this.elevationPts = new Array();
   setVal("feedbackBar", '<span class="medText">route started.</span> remember to click save when done. Please note that when you save your route it will be available for anyone to view.');
   map.clearOverlays();
   this.totalDist = 0;
   this.currMinEle = 0;
   this.currMaxEle = 0;
   this.currRouteID =0;
   this.lastElevation = 0;
   this.currElevation = 0;
   this.totalEleGain = 0;
   addPointToRoute(route);
}

//draw points, one by one as you go
function addPointToRoute(route) {
	
	//enable these btns
	getElem("saveRoute").disabled = false;
	getElem("clearRoute").disabled = false;
	var i = route.length - 1;
	var point;
	if(i > 0) {
		this.currSegDist = calcDist(route[i-1].lng(), route[i-1].lat(), route[i].lng(), route[i].lat());
		this.totalDist += this.currSegDist;
		
		getElem('totalDist').innerHTML = (totalDist * metricRatio).toFixed(2).toString() + " " + distUnit;

		setVal("feedbackBar", buildSpeedAvgs(this.totalDist));
		point = new GPoint(route[i].lng(), route[i].lat());

	}
	else { //first point
		getElem('totalDist').innerHTML = "0 " + distUnit;
		point = new GPoint(route[0].lng(), route[0].lat());
	}

	if(this.recenterOnClick) {
		map.panTo(route[i]);
	}
	
	drawPolyline(point);
	showElevation(route[i]);

	getElem("deleteLastPoint").disabled = false;
}

//undo aka remove the last point drawn
function deleteLastPoint() {
  //took out confirm by popular request
   var answer = true; //confirm("clear last point?");
   
   if(answer && this.currMarker) {
   	   	
 	map.clearOverlays();

	var i = this.route.length - 1;
	if(i > 0) {

		var dist = calcDist(route[i-1].lng(), route[i-1].lat(), route[i].lng(), route[i].lat())
		
		if(dist >= 0) {
			//adjust distance
			this.totalDist -= dist;
			setVal('totalDist',totalDist.toFixed(4) + " " + eleUnit);
			this.setVal("feedbackBar", buildSpeedAvgs(totalDist));
		}
	}

	//route.splice(i,1); //remove 1 elem at index i
	route.pop();

	if(i > 0) {
		//dbg("i="+i);
		//this.currMarker = new GMarker(route[i-1]);
		
		this.currPolyline = new GPolyline(route);
			
		drawPolyline(route[i-1],1);
		
		if(route.length >= 1) {
				var marker = new GMarker(route[0], createBikeIcon());
				map.addOverlay(marker);
		}
       }
	        
	//recalc the total gain
	var twoBackEle;
	var eLen = this.elevationPts.length;
	if(eLen >= 1) {
		var change = 0;
		var lastEle = this.elevationPts[eLen-1];
		if(eLen > 1) {
			twoBackEle = this.elevationPts[eLen-2];
			change = lastEle-twoBackEle;
			this.totalEleGain -= change; 
		}
	}

	
	var _lastEle;
	if(eLen > 0) {
		//remove last point
		//this.elevationPts.splice(eLen-1,1);
		this.elevationPts.pop();
		_lastEle = this.elevationPts.pop();
		//dbg("elen="+eLen);
	}

	if(this.route.length > 0) {
		document.getElementById("saveRoute").disabled = false;
		if(this.route.length == 1) {
			document.getElementById("deleteLastPoint").disabled = true;
			this.totalEleGain = 0.0;
			this.totalGain = 0.0;
			this.currMinEle = _lastEle;
			this.currMaxEle = _lastEle;
			this.lastElevation = _lastEle;
		}
	}
	else { //empty route
		document.getElementById("deleteLastPoint").disabled = true;
	}
	
	//redraw ele graph
	if(eLen > 1) {
		showElevation(route[route.length-1], _lastEle);//lastEle will be added back
	}
   }
}

/* used by addPoint, deletePoint */
function drawPolyline(point,all) {

	var icon = createBikeIcon();
	
	if(this.route.length == 1) {
		var marker = new GMarker(point, icon);
		map.addOverlay(marker);
		this.currMarker = marker;
	}
		
	getElem("getEle").disabled = true;
	getElem("getEle").disabled = false;

	var lastLeg = new Array();
	if(route.length > 1) {
		lastLeg.push(route[route.length-2]);
	}
	lastLeg.push(route[route.length-1]);
		
	var line = (all ? route : lastLeg);
	this.currPolyline = new GPolyline(line,routeColor,4,1);
	map.addOverlay(this.currPolyline);
}

function showElevation(latLng,ele) {
	this.done = false;
	getElevationGraph(latLng, "elevationGraph",ele);
}

//call local USGS proxy (using REST),or use passed-in table
function getElevationGraph(latlng,elemName,ele) {

	try {

	if(ele) {
		//use stored/retrieved value
		buildEleGraph(ele,true);
	}
	else { //build dynamic graph
	
		var ajaxObj = createAjaxObj();
	
		//var pts = this.elevationPts;
		ajaxObj.onreadystatechange = function () {
		if (ajaxObj.readyState == 4) {
			if (ajaxObj.status==200 || ajaxObj.status==302 || ajaxObj.status==304)
			{ //if request was successful
				var xmldata=ajaxObj.responseXML;
								
				if (!xmldata || xmldata.getElementsByTagName("string").length==0)
				{ //if no <item> elements found in returned content
					getElem(elemName).value="ERROR: "+ajaxObj.responseText;
				}
				else {
					var ele = xmldata.getElementsByTagName("string")[0].firstChild.nodeValue;
					if(ele < 0) { ele = 0; } //sometimes usgs returns <0 (for valid lat/long)
					
					buildEleGraph(ele);
					
					//8-1-07 analytics tracking of ele queries
					urchinTracker('/bikemaps/elevation_query');
				}
			}
			else {
				alert('There was a problem with the request: ' + ajaxObj.status);
				}	
		} 
		
		} //end anon func
	}

	if(!ele && latlng) {
	
	setVal("elevationDetails",'<img src="/images/loading.gif" alt="loading..."/ width="16" height="16"/> <b style="background:white;font-color:black;" class="medText">Loading elevation...');//give feedback, it could take a few for a bulk req
	
		ajaxObj.open("GET", "/perl/get_elevation.cgi?latLong=" +
 		latlng.lat().toFixed(6) + "," + latlng.lng().toFixed(6),true);//false==sync
		ajaxObj.send(null);
	}

	} catch(e) { alert("Error building elevation graph: " + e); }
	this.done = true;

} //end func

function buildEleGraph(ele,skip) {
	
	ele = parseFloat(ele);
	this.currElevation = ele;
	var out = "";
	if(ele > currMaxEle) {
		currMaxEle = ele;
	}
	else if(ele < this.currMinEle) {
		this.currMinEle=ele;
	}
	if(ele) {
		elevationPts.push(ele);
	}
	if(elevationPts.length > 0) {
		if(elevationPts.length == 1) { this.currMinEle = ele; }
		out = buildElevationGraph(elevationPts,ele) + buildEleDetailsString(skip);
		getElem("elevationGraph").innerHTML = out;
	}
	
	//6/25/07 - ele gain fix (why this wasn't found earlier is anyone's guess)
	if(this.currElevation > 0) {
		this.lastElevation = this.currElevation;
	}
}

// build the string that contains total gain, grade, etc
function buildEleDetailsString(skipGrade) {	
	
	if(this.totalEleGain == null) { this.totalEleGain = 0.0; }
	if(this.maxGrade == null) { this.maxGrade = 0; }
	var len = this.elevationPts.length;
	var out = '<br>';

		var change = 0.0;
		//8/22/07 - added non-zero check, gain is too big sometimes
		if((this.lastElevation != null) 
			&& (this.lastElevation != 0) && (this.currElevation != 0)) { change = this.currElevation - this.lastElevation; }

		if(change > 0.0 && len > 1) { this.totalEleGain += change; }
		//change only makes sense for recording, not loading a route
		if(!skipGrade && len > 1) {			
			out += "<b>change:</b> " + parseInt(change*this.footRatio) + " " + eleUnit;
			
			out += " <b>total gain:</b> " +  parseInt(this.totalEleGain*footRatio) + " " + eleUnit;

		}

		//grade only makes sense for recording
		if(!skipGrade && len > 1) {
			var grade = Math.abs(this.currElevation-this.lastElevation)/(this.currSegDist*this.feetInMile);
			grade = parseInt(Math.tan(Math.asin(grade))*100);

			if(grade > this.maxGrade && grade <= 30) { this.maxGrade = grade; }
			
			//5/6/07 - fixed grade calc!
			
			if(grade <= 30) {
				out += ' <b>grade:</b> ' + grade + "%";
			}
			else { out += ' <b>grade:</b> 30+%'; } //too big, can't really say 
			
			if(this.maxGrade) {
				out += ' <b>max grade:</b> ' + parseInt(this.maxGrade) + "%";
			}
		}
	return out;
}

//build elevation 'graph'
function buildElevationGraph(points,last) {
	return buildGraph(points,100,last);
}

//build a serializable graph of the profile
function buildGraphImage() {
	var urlStr = 'http://veloroutes.org/cgi-bin/get_elevationgraph.cgi';

	if(this.currRouteID <= 0) { //route hasn't been saved yet
		var g = this.elevationPts;
		var len = g.length;
		var lastPoint = null;
		var calcpercent = 0;
		var grphType = 'smooth&min-m=true&max-m=true&upper=' + parseInt(this.currMaxEle) + '&below-color=blue';
		var step = 10;
	
		urlStr += '?type='+grphType + '&step=' + step.toString() + '&limits=' + parseInt(this.currMinEle) + "," + parseInt(this.currMaxEle) + '&height=150&dist=' + this.totalDist.toFixed(2) + '&d=';

		for(i = 0; i< len; i++) {
			if(g[i] != null) {
				if(i == 0) 
				{ 
					urlStr += parseInt(g[i]).toString();
				}
				else {
					urlStr += "," + parseInt(g[i]).toString();//parseInt(calcpercent).toString();
				}
			}
		}
	}
	else {
		urlStr += '?id='+this.currRouteID;
		if(this.useMetric) {
			urlStr += '&m=1';
		}
	}

	return urlStr;
}

//code taken and modified to draw elevation graph
function buildGraph(g,gwidth,last){
	var output = "";
	
	getElem("elevationDetails").innerHTML ='<span class="smallText"><b>min:</b> '+ (this.currMinEle*footRatio).toFixed(1) + ' ' + eleUnit + '  <b>max:</b> '+ (this.currMaxEle*footRatio).toFixed(1) + ' ' + eleUnit + '  <b>last:</b> ' + (last*footRatio).toFixed(1) + ' ' + eleUnit + '&nbsp;&nbsp;&nbsp;<b>total dist:</b> ' + (totalDist*metricRatio).toFixed(1) + " " + distUnit +
	'&nbsp;&nbsp;<b><a href="#" onclick=\'window.location="' + buildGraphImage() + '"\'>get elevation image</a>&nbsp;&nbsp;&nbsp;<a href="#" onclick="toggleEleGraph();">[close]</a></b></span><br/><br/>';
		
	if(g.length == 0) { dbg("points list is empty.");return; }

	var i;
	var calcpercent = 0;
	var len = g.length;
	var w = 4;
	var shrink = (len >= 300); //false;
	
//dbg("len=" + len);

	if(shrink) {
		
		//even bigger?
		if(len >= 350) {
			w = 2;
		}
		else {
			w = 3;
		}
	}
	
	var size = .5;
	var lastPoint = null;
	var totalFeet = 0;
	var useImages = false;
	if(useImages) {
		output += '<img src="' + buildGraphImage() + '" alt="ele graph">';
	}
        else {
		for (i=0;i<len;i++){	
			//dbg(g[i]);
			if(g[i] != null) {
		//		dbg(g[i]);
				//in places like denver, the heights are so big that the graph barely shows changes
				
				if(lastPoint == null) { lastPoint = this.route[i]; }
					
				calcpercent=Math.round(g[i]*100.0/currMaxEle);
				if(calcpercent <= 0.0) { calcpercent = 1.0; }

				if(calcpercent == 100.0) {
					output+='<img align="bottom" src="/images/redline.png" width="' + (w+1) + '" height="' +size*calcpercent + '" >';
				}
				else {
					output+='<img align="bottom" src="/images/blueline.png" width="' + w + '" height="' + (size*calcpercent)%100 + '" >';
				}
			}
		}
   	}

	//output contains eleDetails (grade/etc) str
	return output + '<br/>';
}

function setMapCenter(lat,lng) {
if(this.map)  { this.map.setCenter(new GLatLng(lat,lng)) }
return;
}

function toggleEleGraph() {
 var ele=getElem("_eleGraph");
 if(this.eleHidden) { //show
    ele.style.display = '';
    var show = getElem("showEle");
    show.style.display='none';
    this.eleHidden=false;
  }
  else { //hide 
    ele.style.display='none';
    var show = getElem("showEle");
    show.style.display='';
    this.eleHidden=true;   
   }
}

//create bike icon, once
function createBikeIcon() {
	if(!this.bikeIcon) {
		var icon = new GIcon();
		icon.image = "/images/bike_icon.png";
		icon.iconSize = new GSize(18,14);//14,12);
		icon.iconAnchor = new GPoint(6,6);
		icon.infoWindowAnchor= new GPoint(36,32);
		bikeIcon = icon;
	}

	return this.bikeIcon;
}

function deleteMarker(i) {
   var point = this.route[i];
   if(point) {
	map.removeOverlay(point);
	route.splice(i,1); //remove elem at index i
   }
}

/* calcDist() function is from  Adam Howitt, of http://walkjogrun.net
	Copyright Adam Howitt 2005
	Email: adamhowitt@gmail.com
	This work is licensed under a Creative Commons 
	Attribution-NonCommercial-ShareAlike 2.5 License.
	http://creativecommons.org/licenses/by-nc-sa/2.5/
*/
function calcDist(lon1,lat1,lon2,lat2) {
	var r = 3963.0;
	var multiplier = 1;
	return multiplier * r * Math.acos(Math.sin(lat1/57.2958) * Math.sin(lat2/57.2958) +     Math.cos(lat1/57.2958) * Math.cos(lat2/57.2958) * Math.cos(lon2/57.2958 - lon1/57.2958));
}

//borrowed from gmap-pedometer.com
function getQuerystringParameter(paramName, paramString){
		var sReturnStr = '';
		var queryStringObj = new String(paramString);
		var paramNameObj = new String(paramName);
		paramNameObj = paramNameObj.toLowerCase();
		//parameters were sent
		if (queryStringObj.indexOf('?') > -1) {
			var qStringArray = queryStringObj.split('?');
			if (qStringArray[1].length > 0) {
				var allParams = qStringArray[1]
				var paramArray = allParams.split("&");
				for (i=0; i<= paramArray.length-1; i++){
					var origCaseFullParam = paramArray[i];
					var nameValuePairObj = new String(paramArray[i]);
					nameValuePairObj = nameValuePairObj.toLowerCase();
					var nameValuePairArr = nameValuePairObj.split('=');
					var indParamName = nameValuePairArr[0];
					var indParamValue = nameValuePairArr[1];
					if (paramNameObj == indParamName) {
						sReturnStr =  unescape(origCaseFullParam.substr(indParamName.length+1));
						break;
					}	
				}	
			}
		}
		return sReturnStr;
}


//figure out where to put the mile markers
function loadMileMarkers() {
var _totalDist = 0.0;
var segDist = 0.0;
var lastLL; //last lat long
var currLL;

var soFar = 0;
var floored = 0;
var markerSpots = [];
var magic = .19999999999999;
var lastFloor;
var markerCnt = 0;
var MAX_MARKERS = 100;
var skipCnt = 3;
var markerCnt = 0;

//if(!this._route || this._route.length <= 0) { this._route = this.route; }

for(var i = 1; i < this._route.length; i++) {
   if(markerCnt == MAX_MARKERS) { break; }

   if(!lastLL) { 
	lastLL = this._route[i-1];
    }

   currLL = this._route[i];
   segDist = calcDist(lastLL.lng(), lastLL.lat(),currLL.lng(), currLL.lat());
   _totalDist += segDist;
   
   floored = Math.floor(_totalDist);

   if((_totalDist - floored) <= magic) { 
       if(floored > 0 && floored != lastFloor) {
		//dbg("mm at: " + (_totalDist));	
		if(markerCnt++ % skipCnt == 0) {

		var icon = new GIcon();
		icon.image = "/images/markers/marker" + floored + ".png";
		icon.iconSize = new GSize(32,36);
		icon.infoWindowAnchor= new GPoint(32,36);
		icon.iconAnchor = new GPoint(16,36);
		var marker = new GMarker(currLL, icon);
		if(marker != null) {
			this.map.addOverlay(marker);
		} else { dbg("marker was null."); }

		} //end if
	}
       
   }//end if
   lastLL = currLL;
   lastFloor=floored;//the last rounded-down mile-marker
  }
}

//remove all mile markers from map
function removeMileMarkers() {
 var map = new GMap2(document.getElementById("map"));
 map.clearOverlays();
 this.loadRouteJSON(true); //no mm's
}

//turn markers on/off
function toggleMileMarkers() {
  if(this.showMileMarkers) { removeMileMarkers(); }
  else { loadMileMarkers(); }
  this.showMileMarkers = !this.showMileMarkers;
}

//zoom on scroll wheel feature
function toggleScrollWheel() {

  if(this.useScrollWheel) {
    if(this.map) { map.disableScrollWheelZoom() }
    else { dbg("no map?"); }
  }
  else {
    if(this.map) { map.enableScrollWheelZoom() }
    else { dbg("no map?"); }
  }

  this.useScrollWheel = !this.useScrollWheel;
}

//take a route and get a google cue sheet for it
function getGCue() {

var _route = eval('(' + getElem("json").innerHTML + ')');
_route = _route.route.pts.split("|");
this._route = _route;
var startPt = this._route[0];
var endPt = this._route[_route.length-2];//-2 since the json is sloppy
//added avoid highways too
var urlStr = "http://maps.google.com/maps?f=d&dirflg=w&q=from:";
//saddr="+startPt+"&via=";

//start and end isn't enough - we need to add more points for out of the way routes

 var maxDests = 20;
 if(_route.length < 50) { maxDests = 5 }
 var numDests = 0;
 var divs =  parseInt( _route.length / maxDests ); //split the arr up into X divisions
 var point;
 while(numDests < maxDests) {
   point = _route[numDests * divs];
   urlStr += " " + point + " to: ";
   //urlStr += point.replace(/,/,"%2C") + ", ";
   numDests++;
 }
 urlStr += " " + endPt;
 if(this.useMetric) { urlStr += "&doflg=ptk" }

 urchinTracker('/bikemaps/cuesheet');

 return urlStr;
}

//take text from dropdown and export to that format
function exportRoute() {
   var w = getElem("exportList");//document.export.list.selectedIndex;
   if(w) {w = w.selectedIndex;}
   else { alert("?"); }
   if(w >= 0) {
      var text = getElem("exportList")[w].text;//document.export.list.options[w].text;
      if(text != "" && this.currRouteID > 0) {
	 if(text == "Google Earth (KML)") {text="KML"}
        if(text == "Excel (CSV)") {text="CSV"}
	 if(text == "Garmin (TCX)") {text="tcx"}
	 if(text == "Google Maps") {text="gmaps"}
	 if(text == "Cuesheet (Google Maps)") { window.location=getGCue();return;}
	 if(text == "Bing Maps") {text="bing"}
        window.location = "http://veloroutes.org/cgi-bin/route_to_" + text.toLowerCase() + ".cgi?route_id=" + this.currRouteID;
      }
   }
}

//]]
