
BRdCList = 
{	search:
		function(needle,haystack)
		{	for (var i=0;i<haystack.length;i++)
				if (haystack[i]==needle)
					return i;
			return -1;
		},
	add:
		function(link,array)			
		{	if (BRdCList.search(link,array)==-1)
				array.push(link);
			return array;
		},
	remove:
		function(link,array)
		{	var loc = BRdCList.search(link,array);
			if (loc>-1)
			{	if (loc==0)
					array.shift();
				else if (loc==array.length-1)
					array.pop();
				else
					return array.slice(0,loc).concat( array.slice(loc+1) );
			}
			return array;
		}
}

BRdCConv =
{	mileInMeters: 1609.344,
	mileInYards: 1760,
	mileInFeet: 5280,
	yardInMeters: 0.9144,
	yardInFeet: 3,
	feetInMeters: 0.3048,
	kilometerInMeters: 1000,
	
	metersToMiles:
		function(meters)
		{	return (meters/1609.344);
		},
	metersToYards:
		function(meters)
		{	return (meters/0.9144);
		},
	metersToKilometers:
		function(meters)
		{	return (meters/1000.0);
		},
	distanceImperial:
		function(meters)
		{	if (meters>=BRdCConv.mileInMeters/5)
				return (meters/BRdCConv.mileInMeters).toFixed(2) + 'mi';
			else
				return (meters/BRdCConv.feetInMeters).toFixed(0) + 'ft';
		},
	distanceMetric:
		function(meters)
		{	if (meters>=1000)
				return (meters/1000).toFixed(2) + 'km';
			else if (meters<1)
				return (meters).toFixed(1) + 'm';
			else
				return (meters).toFixed(0) + 'm';
		}
}



/**	BRdCPoint
	--	coord		GLatLng			*The coordinates of this BRdCPoint
	--	ptAddr?		string			*If created w/ a GeoCode we can keep that here
	--	storedID?	int				*ID Number for retrieval in database
	--	inSteps		BRdCRouteStep[]	*A listing of steps where this is an endpoint
	--	inPaths		BRdCPath[]		*A listing of paths that include this point
	
*/
function BRdCPoint(ptcoord,ptaddr,ptID)
{	this.coord = ptcoord;
	this.ptAddr= ptaddr;
	if (typeof ptID!='undefined')
		this.storedID = ptID;
	else
		this.storedID = null;
	this.inSteps = new Array();
	this.inPaths = new Array();
}
BRdCPoint.prototype.setAddress = function(addrstring)
{	this.ptAddr = addrstring;
}
BRdCPoint.prototype.getAddress = function()
{	if (typeof this.ptAddr=='string')
		return this.ptAddr;
	else
		return '';
}
BRdCPoint.prototype.linkStep = function(tolink)
{	this.inSteps = BRdCList.add(tolink,this.inSteps);
}
BRdCPoint.prototype.unlinkStep = function(toremove)
{	this.inSteps = BRdCList.remove(toremove,this.inSteps);
}
BRdCPoint.prototype.linkPath = function(tolink)
{	this.inPaths = BRdCList.add(tolink,this.inPaths);
}
BRdCPoint.prototype.unlinkPath = function(toremove)
{	this.inPaths = BRdCList.remove(toremove,this.Paths);
}
BRdCPoint.prototype.addMarker = function(markerType,markerOpts)
{	var newMarker = new GMarker(this.coord, markerOpts);
	if (typeof this.markers=='undefined')
		this.markers = { defaultMarker:markerType };
	this.markers[markerType] = newMarker;
	return newMarker;
}
BRdCPoint.prototype.removeMarker = function(markerType)
{	if (typeof this.markers=='object' && typeof this.markers[markerType]=='object')
		delete this.markers[markerType];
}
BRdCPoint.prototype.setMarkerDefault = function(markerType)
{	if (typeof this.markers=='object' && typeof this.markers[markerType]=='object')
	{	this.markers.defaultMarker = markerType;
		return true;
	}	else
		return false;
}
BRdCPoint.prototype.getMarker = function(markerType)
{	if (typeof this.markers=='object')
	{	if (typeof markerType=='undefined' && typeof this.markers.defaultMarker=='string')
				markerType = this.markers.defaultMarker;
		if (typeof this.markers[markerType]=='object')
			return this.markers[markerType];
	}
	return null;
}
BRdCPoint.prototype.movePt = function(newCoord)
{	var i;
	this.coord == newCoord;
	//updateMarkers
	if (typeof this.markers=='object')
	{	for (i in this.markers)
			if (typeof this.markers[i]=='object')
			{	this.markers[i].setLatLng(newCoord);
			}
	}
	//update Paths
	//...not handling paths now
	//update RouteSteps
	for (i in inSteps)
	{	inSteps[i].pointChange(this);
	}
}
BRdCPoint.prototype.isMarker = function()
{	return (typeof this.ptDescr!='undefined');
}
BRdCPoint.prototype.enableAsMarker = function()
{	if (typeof this.ptDescr=='undefined')
		this.ptDescr='';
}
BRdCPoint.prototype.disableAsMarker = function()
{	if (typeof this.ptDescr!='undefined')
		delete this.ptDescr;
}

/**	BRdCPath	//left blank -- we're not supporting paths at the moment
	--	pathName	string
	--	storedID?	int
	--	points		BRdCPoint[]		*The ordered list of BRdCPoints that make up the path
	--	inSteps		BRdCRouteStep[]	*A listing of steps which use this path
		
*/

/** BRdCRouteStep
	--	dataobj		BRdCDataStore
	--	route		BRdCRoute
	--	ptA			BRdCPoint
	--	ptB			BRdCPoint
	--	path		BRdCPath
	--	pathPts		BRdCPoint[]
	--  pathCoords	GLatLng[]
	--	distance	float
	--	stepFlags	string
	--	overlay		GPolyLine
	
*/
function BRdCRouteStep(route,ptA,ptB,pathid,pathPts,markerid,instr)
{	if ((ptA == ptB)||(ptA.coord.equals(ptB.coord)))
		throw("Illegal Empty Step Definition -- To and From can not be the same point!");
	this.route = route;
	this.ptA = ptA;
	this.ptA.linkStep(this);
	this.ptB = ptB;
	this.ptB.linkStep(this);
	if (typeof pathPts=='undefined')
	{	this.path = null;
		this.pathPts = new Array(ptA,ptB);
	}	else
	{	this.path = pathid;
		this.pathPts = pathPts;
	}
	if (typeof markerid=='undefined')
	{	this.marker = false;
	}	else
	{	this.setmarkerinfo(markerid,instr);
	}
	this.calculateDistance();
}
BRdCRouteStep.prototype.setmarkerinfo=function(markerid,instr)
{	this.marker = true;
	this.markerid = markerid;
	this.stepInstruction = instr;
}
BRdCRouteStep.prototype.calculateDistance = function()
{	this.distance = 0.0;
	for (var i=1;i<this.pathPts.length;i++)
		this.distance += this.pathPts[i].coord.distanceFrom( this.pathPts[i-1].coord );
	return this.distance;
}
BRdCRouteStep.prototype.makeOverlay = function(color, weight, opacity)
{	var pathCoords = new Array( this.pathPts.length );
	for (var i=0;i<this.pathPts.length;i++)
		pathCoords[i] = this.pathPts[i].coord;
	if (typeof this.overlay != 'undefined')
		delete this.overlay;
	this.overlay = new GPolyline( pathCoords, color, weight, opacity );
	return this.overlay;
}
BRdCRouteStep.prototype.splitStep = function(insertPt)
{	for (var i=0;i<pointIDs.length;i++)
	{	if (pointIDs[i]==insertPt)
		{	var newStep = new BRdCRouteStep(this.route,insertPt,this.ptB,this.path,pathPts.slice(i));
			pathPts = pathPts.slice(0,i+1);
			this.ptB.unlinkStep(this);
			this.ptB = insertPt;
			this.ptB.linkStep(this);
			//updates
			this.calculateDistance();
			return newStep;
		}
	}
	var newStep = new BRdCRouteStep(this.route,insertPt,this.ptB);
	delete this.pathPts;
	this.path = null;
	ptB.unlinkStep(this);
	this.ptB = insertPt;
	this.ptB.linkStep(this);
	//updates
	this.pathPts= new Array(this.ptA,insertPt);	
	this.calculateDistance();
	return newStep;
}
BRdCRouteStep.prototype.pointChange = function(chgPt)	//we don't seem to need the point actually
{	this.calculateDistance();
	this.route.stepChange( this );	
}
BRdCRouteStep.prototype.pathChange = function (chgPath,reloadPath)
{	if (this.path == chgPath)
	{	if (reloadPath)
		try
		{	var new_pathPts = chgPath.getPathPts(this.ptA,this.ptB);	//should throw something about the points not being found if they aren't valid
			delete this.pathPts;
			this.pathPts= new_pathPts;
		}	catch (e)
			{	throw ("RouteStep would be illegal -- not updated" + e);
			}
		this.pointChange();	//this.calculateDistance() && this.routestepChange(this)
	}
}


/** BRdCRoute
	--	dataobj		BRdCDataStore
	--	routeName	string
	--	routeDescr	string
	--	startPt		BRdCPtID (int)
	--	endPt		BRdCPtID	(int)
	--	steps		BRdCRouteStep[]
	--	distance	float
	--	inDisplays	[]
*/

function BRdCRoute(rtdata)
{	this.altered = false;
	this.inDisplays=new Array();
	if (typeof rtdata=='undefined')
	{	this.distance= 0.0;
		this.steps = new Array();
		this.routeName = "Untitled Route";
		this.routeDescr = '';
		this.permission = '';
	}	else
	{	//load it from the route Data Obj see BRdC_loadRoute.php for a description
		var rtparse = rtdata.split('\n');
		var ptstable = new Object();
		for (var ln in rtparse)
		{	var line = rtparse[ln];
			var cmd = line.split(":");
			switch (cmd[0])
			{	case 'STARTROUTE':
					this.steps = new Array();
					if (cmd.length==1)
						this.editsess = cmd[1];
					break;
				case 'ROUTEID':
					this.storeid = parseInt(cmd[1]);
					this.permission = cmd[2];
					if (cmd.length>4)
						this.routeName = cmd.slice(3).join(':');
					else
						this.routeName = cmd[3];
					break;
				case 'ROUTEOWN':
					this.creator = parseInt(cmd[1]);
					if (cmd.length>3)
						this.creatorName = cmd.slice(2).join(':');
					else
						this.creatorName = cmd[2];
					break;
				case 'ROUTETIME':
					this.created = new Date(parseInt(cmd[1])*1000);
					this.lastsave= new Date(parseInt(cmd[2])*1000);
					break;
				case 'ROUTEDESC':
					if (cmd.length>2)
						this.routeDescr = cmd.slice(1).join(':');
					else
						this.routeDescr = cmd[1];
					break;
				case 'ROUTEBOX':
					this.topLat		= parseFloat(cmd[1]);
					this.leftLng	= parseFloat(cmd[2]);
					this.bottomLat	= parseFloat(cmd[3]);
					this.rightLng	= parseFloat(cmd[4]);
					break;
				case 'ROUTELEN':
					this.distance = parseFloat(cmd[1]);
					if (cmd.length>3)
						this.startInstruction = cmd.slice(2).join(':');
					else
						this.startInstruction = cmd[2];
					break;
				case 'POINTS':
					cmd.shift();
					for (var i in cmd)
					{	var ptvals = cmd[i].split(",");
						ptstable[ parseInt(ptvals[0]) ] = new BRdCPoint( new GLatLng( parseFloat(ptvals[1]), parseFloat(ptvals[2]) ), '', parseInt(ptvals[0]) );
					}
					break;
				case 'STEP':
					var pthpts;
					var stpinst = (cmd.length>8) ? cmd.slice(7).join(':') : cmd[7];
					if (cmd[5]!='-' && cmd[6]!='-')
					{	var cppts = cmd[6].split(",");
						var pthpts = new Array(cppts.length);
						for (var i in cppts)
							pthpts[i] = ptstable[parseInt(cppts[i])];
						this.appendStep( ptstable[parseInt(cmd[2])], ptstable[parseInt(cmd[3])], parseInt(cmd[5]), pthpts );
					}	else
						this.appendStep( ptstable[parseInt(cmd[2])], ptstable[parseInt(cmd[3])] );						
					this.steps[this.steps.length-1].setmarkerinfo( cmd[1], stpinst );
					this.steps[this.steps.length-1].distance = parseFloat(cmd[4]);
					break;
				case 'ENDROUTE':
					delete ptstable;
					return this;
				default:
					//bad.
			}	//end switch
		}	//end for
	}
}
BRdCRoute.prototype.haspermkey = function(perm)
{	return (this.permission.indexOf(perm)!=-1);
}
BRdCRoute.prototype.setpermkey = function(perm)
{	if (!this.haspermkey(perm))
		this.permission += perm;
}
BRdCRoute.prototype.clearpermkey = function(perm)
{	var i;
	while ((i = this.permission.indexOf(perm)) != -1)
	{	if (i==0)
			this.permission = (this.permission.length>1) ? this.permission.substr(i) : '';
		else
			this.permission = (this.permission.length>i+1) ?
				this.permission.substr(0,i)+this.permission.substr(i+1) :
				this.permission.substr(0,i);
	}
}
BRdCRoute.prototype.setPrivate = function(isPrivate)
{	(isPrivate) ? this.setpermkey('p') : this.clearpermkey('p');
}
BRdCRoute.prototype.isPrivate = function()
{	return this.haspermkey('p');
}
BRdCRoute.prototype.setLocked = function(isLocked)
{	(isLocked) ? this.setpermkey('l') : this.clearpermkey('l');
}
BRdCRoute.prototype.isLocked = function()
{	return this.haspermkey('l');
}
BRdCRoute.prototype.setRouteName = function(newName)
{	if (this.routeName!=newName)
	{	this.routeName = newName;
		this.altered = true;
	}
	for (var i in this.inDisplays)
		this.inDisplays[i].updateName( this.routeName );
}
BRdCRoute.prototype.setRouteDescr = function( newDesc )
{	if (this.routeDescr != newDesc)
	{	this.routeDescr = newDesc;
		this.altered = true;
	}
}
BRdCRoute.prototype.appendStep = function(ptA,ptB,pathid,pts)
{	if (this.steps.length>0)
	{	if (this.endPt != ptA)
		{	throw new Error("Route Step Match Error -- From Point doesn't match previous To Point!");
		}
	}	else
		this.startPt = ptA;
	var newStep = new BRdCRouteStep(this,ptA,ptB,pathid,pts);
	this.steps.push( newStep );
	this.endPt = ptB;
	this.distance += newStep.distance;
	newStep.rdist = this.distance;
	this.altered = true;
	this.updateDisplays(this.steps.length-1);
}
BRdCRoute.prototype.insertStep = function(insideStep,ptA,ptB,ptC)
{	if (this.steps.length>insideStep)
	{	var inStep = this.steps[insideStep];
		if (inStep.ptA != ptA)
			throw new Error("Route Step Match Error -- From Point doesn't match previous To Point!");
		if (inStep.ptB != ptC)
			throw new Error("Route Step Match Error -- Inserted Point Range doesn't match with previous To Point!");
		this.distance -= inStep.distance;
		var newStep = inStep.splitStep(ptB);
		if (this.steps.length>insideStep+1)
			this.steps.splice(insideStep+1,0,newStep);
		else
			this.steps.push(newStep);
		this.routeStepsDistances( insideStep );
		this.altered = true;
		this.updateDisplays(insideStep);
	}
}
BRdCRoute.prototype.deleteStepEndPt = function(insideStep, ptB)
{	if (this.steps.length>insideStep)
	{	var inStep;
		if (insideStep==this.steps.length-1)
		{	inStep = this.steps.pop();
			if (inStep.ptB != ptB)
				throw new Error("Route Step Match Error -- Delete Point doesn't match step point B!");
			this.distance -= inStep.distance;
			this.altered = true;
			this.endPt = inStep.ptA;
			this.stepRemoveOverlay( inStep );
			delete inStep;
			this.updateDisplays( (insideStep-1>=0) ? (insideStep-1) : 0);
		}	else
		{	inStep = this.steps[insideStep];
			if (inStep.ptB != ptB)
				throw new Error("Route Step Match Error -- Delete Point doesn't match step point B!");
			var bStep = this.steps[insideStep+1];
			if (bStep.ptA != ptB)
				throw new Error("Route Step Match Error -- Delete Point doesn't match step point A!");
			this.altered = true;
			this.distance -= inStep.distance;
			this.distance -= bStep.distance;
			inStep.ptB = bStep.ptB;
			delete inStep.pathPts;
			inStep.pathPts = new Array( inStep.ptA, bStep.ptB );
			inStep.path = null;
			inStep.pathChange( inStep.path, false );
			this.steps.splice( insideStep+1, 1 );
			this.stepRemoveOverlay( bStep );
			delete bStep;
			this.routeStepsDistances( insideStep );
			this.updateDisplays( insideStep );
		}
	}
}
BRdCRoute.prototype.moveStepPoint = function(firstStep, newB)
{	if (firstStep>-1 && firstStep<this.steps.length)
	{	var stepA = this.steps[firstStep];
		stepA.ptB = newB;
		stepA.pathPts[stepA.pathPts.length-1]=newB;
		stepA.calculateDistance();
		this.stepChange( stepA );
		
	}
	firstStep++;
	if (firstStep<this.steps.length)
	{	var stepB = this.steps[firstStep];
		stepB.ptA = newB;
		stepB.pathPts[0] = newB;
		stepB.calculateDistance();
		this.stepChange( stepB );
		if (firstStep==0)
		{	for (i in this.inDisplays)
				this.inDisplays[i].setStartPt( newB );
		}
	}	else
		this.endPt = newB;
	firstStep = (firstStep>0) ? firstStep-1 : 0;
	this.routeStepsDistances(firstStep);
	this.updateDisplays(firstStep);
}
BRdCRoute.prototype.deleteStartPt= function()
{	if (typeof this.startPt=='undefined')
		return;
	var i;
	for (i in this.inDisplays)
	{	var marker = this.startPt.getMarker('start');
		if (marker!=null)
		{	this.inDisplays[i].map.removeOverlay( marker );
			this.startPt.removeMarker('start');
		}
	}
	if (this.steps.length>0)
	{	var inStep = this.steps.shift();
		var i;
		var newStart = (this.steps.length==0) ? inStep.ptB : this.steps[0].ptA;
		for (i in this.inDisplays)
		{	this.inDisplays[i].setStartPt( newStart );
		}
		this.distance -= inStep.distance;
		this.stepRemoveOverlay( inStep );
		delete inStep;
	}	else
		delete this.startPt;
	this.routeStepsDistances(0);
	this.updateDisplays(0);
}
BRdCRoute.prototype.routeStepsDistances = function( step )
{	if (step>=this.steps.length)
		return;
	this.distance = (step==0) ? 0.0 : this.steps[step-1].rdist;
	while (step<this.steps.length)
	{	this.distance += this.steps[step].distance;
		this.steps[step].rdist = this.distance;
		step++;
	}
}
BRdCRoute.prototype.stepRemoveOverlay = function( step )
{	if (typeof step.overlay != 'undefined')
	{	for (var i in this.inDisplays)
			this.inDisplays[i].map.removeOverlay( step.overlay );
	}
}
BRdCRoute.prototype.stepChange = function( step )
{	var i;
	if (typeof step.overlay != 'undefined')
	{	for (i in this.inDisplays)
			this.inDisplays[i].map.removeOverlay( step.overlay );
	}
	step.makeOverlay('#0000AA',4,.75);
	if (typeof step.overlay != 'undefined')
	{	for (i in this.inDisplays)
			this.inDisplays[i].map.addOverlay( step.overlay );
	}
}
BRdCRoute.prototype.linkDisplay = function(display)
{	BRdCList.add(display,this.inDisplays);
}
BRdCRoute.prototype.unlinkDisplay=function(display)
{	BRdCList.remove(display,this.inDisplays);
}
BRdCRoute.prototype.updateDisplays=function(inStepNum)
{	for (var i in this.inDisplays)
		this.inDisplays[i].updateFromRoute(this,(inStepNum>0)?inStepNum-1:0);
}

/** routeDisplay

	distStyle	--	function to display the distance
	map		-- the GMap to draw the route on
	markerManage -- the manager for the GMap
	route	-- the route datat
*/

function routeDisplay(map,route,metric)
{	if (typeof map=='object')
	{	this.map = map;
		//Prefer the open source MarkerManager vs google's GMarkerManager 
		//if only because it has the capability to REMOVE markers once added.
		//it requires a seperate script to be loaded of course:
		//<script src="http://gmaps-utility-library.googlecode.com/svn/trunk/markermanager/release/src/markermanager.js" >
		this.markerManager = new MarkerManager( this.map, {trackMarkers:true, borderPadding:40} );
	}
	if (typeof route=='object')
		this.linkToRoute(route);

	this.distMarkers = new Array();
	if (typeof metric!='undefined' && metric==true)
	{	this.distStyle= BRdCConv.distanceMetric;
		this.distCrawler = new distanceCrawler( this.distMarkers, BRdCConv.kilometerInMeters, makeDistanceMarkerMetric );
	}	else
	{	this.distStyle= BRdCConv.distanceImperial;
		this.distCrawler = new distanceCrawler( this.distMarkers, BRdCConv.mileInMeters, makeDistanceMarkerImperial );
	}
}
routeDisplay.prototype.updateStepCount = function(val)
{	
}
routeDisplay.prototype.updateDistance= function(val)
{	
}
routeDisplay.prototype.updateName = function(rname)
{	
}
routeDisplay.prototype.linkToRoute = function( route )
{	if (typeof this.route=='object')
		this.route.unlinkDisplay(this);
	this.route = route;
	this.route.linkDisplay(this);
}
routeDisplay.prototype.setStartPt = function(BRdCPt)
{	if (typeof this.route.startPt=='object')
	{	if (this.route.startPt==BRdCPt)
			return;
	}
	this.route.startPt = BRdCPt;
	BRdCPt.enableAsMarker();
	BRdCPt.addMarker('start',{clickable:true, icon:route_startmark});
	this.map.addOverlay( BRdCPt.getMarker('start') );
}
routeDisplay.prototype.updateFromRoute=function(BRdCRoute,inStepNum)
{	if (this.route==BRdCRoute)
	{	if (!this.hideMarkers)
			this.makeDistanceMarkers( inStepNum, this.distCrawler );
	}
}
routeDisplay.prototype.makeDistanceMarkers = function (fromStep,distCrawl)
{	if (fromStep>=this.route.steps.length)
		return;
	var curDist = (fromStep>0) ? this.route.steps[fromStep-1].rdist : 0.0;
	distCrawl.setFromDistance(curDist);
	
	while (fromStep<this.route.steps.length)
	{	var inStep = this.route.steps[fromStep++];
		var remStepDist = inStep.distance;
		for (var cpp = 1;curDist+remStepDist>distCrawl.nextDist && cpp<inStep.pathPts.length;cpp++)
		{	var psegDist =	(cpp==inStep.pathPts.length-1) ? remStepDist :
					( inStep.pathPts[cpp-1].coord.distanceFrom(inStep.pathPts[cpp].coord) );
			remStepDist -= psegDist;
			if (curDist + psegDist > distCrawl.nextDist)
			{	var remSegDist = psegDist;
				var slopeLat = (inStep.pathPts[cpp].coord.lat() - inStep.pathPts[cpp-1].coord.lat())/psegDist;
				var slopeLng = (inStep.pathPts[cpp].coord.lng() - inStep.pathPts[cpp-1].coord.lng())/psegDist;
				var baseCoord = inStep.pathPts[cpp-1].coord;
				while (curDist + remSegDist > distCrawl.nextDist)
				{	var ptdist = distCrawl.nextDist - curDist;
					remSegDist -= ptdist;
					curDist += ptdist;
					baseCoord = new GLatLng( baseCoord.lat()+slopeLat*ptdist, baseCoord.lng()+slopeLng*ptdist );
					distCrawl.setMarkerLoc( baseCoord );
				}
				curDist += remSegDist;
			}	else
				curDist += psegDist;
		}
		curDist += remStepDist;
	}
	
	distCrawl.clearUp(this.markerManager);
}


function distanceCrawler(markerArray,convDistance,markerFunc)
{	this.Markers = markerArray;
	this.convDistance= convDistance;
	this.markerFunc = markerFunc;

	this.nextMarker = 0;
	this.nextUnit = 1;
	this.nextDist = convDistance;
}
/**	DISTANCE MARKERS ARE SET ON THE FOLLOWING UNIT MEASURES
   (lo < hi)  (mInt) (lo/mInt)  [mlo,mhi]:cnt
	Between   Marker   1st  	 Marker		
	 range   Interval divided   Elements

    0<d<50:		+1		 0		[0..49]:50		/   0 += 50		
   50<d<100		+5		10		[50..59]:10		/  50 += 10
  100<d<500		+10		10		[60..99]:40		/  60 += 40
  500<d<1000	+20		25		[100..124]:25	/ 100 += 25
 1000<d<5000	+100	10		[125..164]:40	/ 125 += 40
 5000<d<10000	+200	25		[165..189]:25	/ 165 += 25
10000<d			+1000	10		[190..***]		/ 190 += *

**/
distanceCrawler.prototype.setFromDistance = function(pDist)
{	this.nextUnit = (pDist>0) ? Math.floor(pDist/this.convDistance) : 0;
	this.nextTarget();
	this.holdMark = this.nextMarker;
}
distanceCrawler.prototype.nextTarget = function()
{	var pDist = this.nextUnit;
	var cmp, offset;
	if (pDist<50)
	{	cmp = 1;
		offset = 0;
	}	else if (pDist<100)
	{	cmp = 5;
		offset = 40;
	}	else if (pDist<500)
	{	cmp = 10;
		offset = 50;
	}	else if (pDist<1000)
	{	cmp = 20;
		offset = 75;
	}	else if (pDist<5000)
	{	cmp = 100;
		offset = 115;
	}	else if (pDist<10000)
	{	cmp = 200;
		offset = 140;
	}	else
	{	cmp = 1000;
		offset = 180;
	}
	this.nextUnit = (pDist - (pDist%cmp) + cmp);
	this.nextDist = this.nextUnit*this.convDistance;
	this.nextMarker = offset + Math.floor(pDist/cmp);
	return this.nextDist;
}
distanceCrawler.prototype.setMarkerLoc = function( coord )
{	if (typeof this.Markers[this.nextMarker]!='object')
	{	this.Markers[this.nextMarker] = this.markerFunc( coord, this.nextUnit );
	}	else
		this.Markers[this.nextMarker].setLatLng( coord );
	var ret = this.Markers[this.nextMarker];
	this.nextTarget();
	return ret;
}
distanceCrawler.prototype.clearUp = function( markManager )
{	while (this.nextMarker<this.Markers.length)
	{	var mark = this.Markers.pop();
		markManager.removeMarker( mark );
		delete(mark);
	}
	for(;this.holdMark<this.nextMarker;this.holdMark++)
		if (this.Markers[this.holdMark].distMarker_added==false)
		{	var addMarkerArray = new Array();
			for (var i=this.holdMark;i<this.nextMarker;i++)
				if (this.Markers[i].distMarker_added==false && this.Markers[i].distMarker_zoomlvl==this.Markers[this.holdMark].distMarker_zoomlvl)
				{	this.Markers[i].distMarker_added= true;
					addMarkerArray.push(this.Markers[i]);
				}
			markManager.addMarkers( addMarkerArray, this.Markers[this.holdMark].distMarker_zoomlvl );
			delete(addMarkerArray);
		}
	markManager.refresh();
}

/*
routeDisplay.prototype.makeMileageMarkers = function( fromStep )
{	if (typeof this.mileMarkers=='undefined')
		this.mileMarkers=new Array(  );
	if ( fromStep >= this.route.steps.length )
		return;
	var cdist = (fromStep==0) ? 0.0 : BRdCConv.metersToMiles(this.route.steps[fromStep-1].rdist);
	var odist=cdist;
	var cmile = Math.floor(cdist);
	cmile += (	(cmile<50)	? 1 :
				(cmile<100)	? (5-(cmile%5)) :
				(cmile<500)	? (10-(cmile%10)) :
				(cmile<1000)? (20-(cmile%20)) :
				(cmile<5000)? (100-(cmile%100)) :
				(cmile<10000)?(200-(cmile%200)) :
							  (1000-(cmile%1000)) );
	var cmarker = Math.floor((cmile<=50)	? cmile :
						 	(cmile<=100)	? ((cmile-50)/5 + 50) :
						 	(cmile<=500)	? ((cmile-100)/10 + 60) :
						 	(cmile<=1000)	? ((cmile-500)/20 + 100) :
						 	(cmile<=5000)	? ((cmile-1000)/100 + 125) :
						 	(cmile<=10000)	? ((cmile-5000)/200 + 175) :
						 					  ((cmile-10000)/1000 + 200)) - 1;
	for (var cstep=fromStep;cstep<this.route.steps.length;cstep++)
	{	var stepdist = BRdCConv.metersToMiles(this.route.steps[cstep].distance);
		if (stepdist>0)
		{	cdist += stepdist;
			var ptCoords = this.route.steps[cstep].ptA.coord;
			var slopeLat = (this.route.steps[cstep].ptB.coord.lat() - ptCoords.lat())/stepdist;
			var slopeLng = (this.route.steps[cstep].ptB.coord.lng() - ptCoords.lng())/stepdist;
			while (stepdist>0 && cdist>=cmile)
			{	var ddist = Math.floor(cmile)-odist;
				var markerPt = new GLatLng(	ptCoords.lat() + slopeLat*ddist, ptCoords.lng() + slopeLng*ddist );
								
				if (typeof this.mileMarkers[cmarker]=='undefined')
				{	var markerOpts = { title:(cmile+"mi"),icon:new mileMarkerIcon(cmile),clickable:false };
					this.mileMarkers[cmarker] = new GMarker( markerPt, markerOpts);
					var lvl;
					if (cmile%1000==0)
						lvl =3;
					else if (cmile%200==0)
						lvl =5;
					else if (cmile%100==0)
						lvl =7;
					else if (cmile%20==0)
						lvl =8;
					else if (cmile%10==0)
						lvl =10;
					else if (cmile%5==0)
						lvl = 11;
					else
						lvl = 12;
					
					this.markerManager.addMarker( this.mileMarkers[cmarker], lvl );
				}	else
				{	this.mileMarkers[cmarker].setLatLng(markerPt);
	//				this.mileMarkers[cmarker].show();
				}
				cmarker++;
				cmile += ((cmile<50)?1:(cmile<100)?5:(cmile<500)?10:(cmile<1000)?20:(cmile<5000)?100:(cmile<10000)?200:1000);
				ptCoords=markerPt;
				stepdist -= ddist;
				odist += ddist;			
			}
			odist += stepdist;
		}
	}
	while (cmarker<this.mileMarkers.length)
	{	var toremove = this.mileMarkers.pop();
		this.markerManager.removeMarker( toremove );
	}
}
routeDisplay.prototype.makeKilometerMarkers = function( fromStep )
{	if (typeof this.kmMarkers=='undefined')
		this.kmMarkers=new Array(  );
	if ( fromStep >= this.route.steps.length )
		return;
	var cdist = (fromStep==0) ? 0.0 : (this.route.steps[fromStep-1].rdist/1000.0);
	var odist=cdist;
	var ckm = Math.floor(cdist);
	ckm += (	(ckm<50)	? 1 :
				(ckm<100)	? (5-(ckm%5)) :
				(ckm<500)	? (10-(ckm%10)) :
				(ckm<1000)	? (20-(ckm%20)) :
				(ckm<5000)	? (100-(ckm%100)) :
				(ckm<10000)	? (200-(ckm%200)) :
							  (1000-(ckm%1000)) );
	var cmarker = Math.floor((ckm<=50)	? ckm :
						 	(ckm<=100)	? ((ckm-50)/5 + 50) :
						 	(ckm<=500)	? ((ckm-100)/10 + 60) :
						 	(ckm<=1000)	? ((ckm-500)/20 + 100) :
						 	(ckm<=5000)	? ((ckm-1000)/100 + 125) :
						 	(ckm<=10000)? ((ckm-5000)/200 + 175) :
						 				  ((ckm-10000)/1000 + 200)) - 1;
	for (var cstep=fromStep;cstep<this.route.steps.length;cstep++)
	{	var stepdist = (this.route.steps[cstep].distance/1000.0);
		if (stepdist>0)
		{	cdist += stepdist;
			var ptCoords = this.route.steps[cstep].ptA.coord;
			var slopeLat = (this.route.steps[cstep].ptB.coord.lat() - ptCoords.lat())/stepdist;
			var slopeLng = (this.route.steps[cstep].ptB.coord.lng() - ptCoords.lng())/stepdist;
			while (stepdist>0 && cdist>=ckm)
			{	var ddist = Math.floor(ckm)-odist;
				var markerPt = new GLatLng(	ptCoords.lat() + slopeLat*ddist, ptCoords.lng() + slopeLng*ddist );
								
				if (typeof this.kmMarkers[cmarker]=='undefined')
				{	var markerOpts = { title:(ckm+"km"),icon:new kmMarkerIcon(ckm),clickable:false };
					this.kmMarkers[cmarker] = new GMarker( markerPt, markerOpts);
					var lvl;
					if (ckm%1000==0)
						lvl =4;
					else if (ckm%200==0)
						lvl =6;
					else if (ckm%100==0)
						lvl =8;
					else if (ckm%20==0)
						lvl =9;
					else if (ckm%10==0)
						lvl =11;
					else if (ckm%5==0)
						lvl = 12;
					else
						lvl = 13;
					
					this.markerManager.addMarker( this.kmMarkers[cmarker], lvl );
				}	else
				{	this.kmMarkers[cmarker].setLatLng(markerPt);
				}
				cmarker++;
				ckm += ((ckm<50)?1:(ckm<100)?5:(ckm<500)?10:(ckm<1000)?20:(ckm<5000)?100:(ckm<10000)?200:1000);
				ptCoords=markerPt;
				stepdist -= ddist;
				odist += ddist;			
			}
			odist += stepdist;
		}
	}
	while (cmarker<this.kmMarkers.length)
	{	var toremove = this.kmMarkers.pop();
		this.markerManager.removeMarker( toremove );
	}
}
*/

//creates a flat GIcon with no transparent or imageMap (ie whole area is clickable)
function FlatIcon(imageref,sizex,sizey,anchorx,anchory,infoanchx,infoanchy)
{	this.image		= imageref;
	this.iconSize	= new GSize(sizex,sizey);
	this.shadow		= null;
	this.shadowSize	= new GSize(0,0);
	if (typeof anchorx!='number' || typeof anchory!='number')
		this.iconAnchor = new GPoint(Math.floor(sizex/2),Math.floor(sizey/2));
	else
		this.iconAnchor	= new GPoint(anchorx,anchory);
	if (typeof infoanchx!='number' || typeof infoanchy!='number')
		this.infoWindowAnchor = this.iconAnchor;
	else
		this.infoWindowAnchor = new GPoint(infoanchx,infoanchy);
	return this;
}
FlatIcon.prototype = new GIcon;
FlatIcon.prototype.constructor = FlatIcon;
FlatIcon.prototype.baseClass = GIcon;
FlatIcon.prototype.setDomImg = function(domimg)
{	domimg.src		= this.image;
	domimg.width	= this.iconSize.width;
	domimg.height	= this.iconSize.height;
	return domimg;
}

function mileMarkerIcon(milecount)
{	var iconHeight = 17+Math.ceil(Math.log(milecount+0.0001)/Math.log(10))*11+((milecount%1>0)?11:0);
	this.image		= "img/mile/"+milecount+".png";
	this.iconSize	= new GSize(26, iconHeight );
	this.shadow		= null;
	this.shadowSize	= new GSize(0,0);
	this.iconAnchor	= new GPoint(13, (iconHeight-2) );
	this.infoWindowAnchor
					= this.iconAnchor;
	return this;
}
mileMarkerIcon.prototype = new FlatIcon;
mileMarkerIcon.prototype.constructor = mileMarkerIcon;


function kmMarkerIcon(kmcount)
{	var iconHeight = 17+Math.ceil(Math.log(kmcount+0.0001)/Math.log(10))*11+((kmcount%1>0)?11:0);
	this.image		= "img/mile/"+kmcount+".png";
	this.iconSize	= new GSize(23, iconHeight );
	this.shadow		= null;
	this.shadowSize	= new GSize(0,0);
	this.iconAnchor	= new GPoint(11, (iconHeight-2) );
	this.infoWindowAnchor
					= this.iconAnchor;
	return this;
}
kmMarkerIcon.prototype = new FlatIcon;
kmMarkerIcon.prototype.constructor = kmMarkerIcon;


function makeDistanceMarkerMetric ( coord, kmdist )
{	var dmark = new GMarker( coord, {	title:	(kmdist+'km'),
										icon:	new kmMarkerIcon(kmdist),
										clckable:false
									} );
	dmark.distMarker_added=false;
	dmark.distMarker_zoomlvl=(	(kmdist%1000==0)? 4 :
								(kmdist%200==0)	? 6 :
								(kmdist%100==0)	? 8 :
								(kmdist%20==0)	? 9 :
								(kmdist%10==0)	? 11:
								(kmdist%5==0)	? 12:
												  13);
	return dmark;
}

function makeDistanceMarkerImperial( coord, midist )
{	var dmark = new GMarker( coord, {	title:	(midist+"mi"),
										icon:	new mileMarkerIcon(midist),
										clickable:false
									} );
	dmark.distMarker_added=false;
	dmark.distMarker_zoomlvl=(	(midist%1000==0)? 3 :
								(midist%200==0)	? 5 :
								(midist%100==0)	? 7 :
								(midist%20==0)	? 8 :
								(midist%10==0)	? 10:
								(midist%5==0)	? 11:
												  12);
	return dmark;
}




