		//---------------------------------------------------------------------------------------------------------------------
		//This JavaScript contains functions which support the plotting of client side points
		//over top of a WMS map image. The app currently has different "views" which are comprised
		//of a certain subset of points (defined by bounding extents, and thematic symbology)
		//This application parses a config file, which contains information about both the WMS map
		//and each thematic point layer. This code also performs silent http requests for the points for a given area
		//The points need to be returned in a GML format, which is fairly simple XML
		//The code makes a request for each subset of points (for each view in the drop down) only once. It stores the information
		//on the client, so that if the user chooses a layer they have already viewed, the information is accessed without a client-server trip
		//
		//Status - It works, but needs to be cleaned up and commented. The GML formats need to be agreed upon, between what the code is
		//currently looking for, and what PBO will assemble using PHP. The gmlPoint content URL is specified in the config file. It can either be
		//a PHP/JSP/CFM file, or a direct XML file. If a server-side file is being used, the app should automatically append the bounding extents to the
		//request, so that only the points available in the current view, are requested
		//
		//Author - Dan Deneau, IAGT
		//----------------------------------------------------------------------------------------------------------------------
		var mapLog = log4javascript.getLogger("Mapping");
        mapLog.setLevel(log4javascript.Level.ERROR);

        var isMSIE = ( navigator.userAgent != null && navigator.userAgent.toLowerCase().indexOf( "msie" ) != -1 );

        var configFileURL="Config/PBOConfigPOD.xml";

        // theOtherWMSLayerYesNo
		//var configFileXML;
        var configXMLDoc;
        var theConfigLayers,theRegions,theMapStyles,theOtherMapStyles,theClusters,theWMSLayersInfo,gmlPointContentURL,ptSize,scalePtSize;
        var pointCanvas,wmsCanvas,selectionCanvas,otherWMSCanvas,iPath,fieldNameArray,uniquePtField;
		var lastMapStyle, lastPointLayer, lastWMSInfo;
		var lastOtherMapStyle="";
		//Node lists
		//var theFeatures,theGeometrys;
        var featureManager;
        var theOtherWMSLayer = false;

		//Populate these arrays from reading the PBOConfig, storing feature and symbol information
		//var pointLayerArray = new Array();
		var staticPointXML="";
		var dynamicPointXML="";
		var gmlPointsName;
		var queryType;
		var symbolFieldNameArray= new Array();
		var symbolValuesArray=new Array();
        var symbolLookup = new Array();
        var xmlhttp;
        var xmlhttpLyrId;

		var symbolNameArray = new Object();
		
		//These two arrays hold the point selection information
		//The first holds an array of unique station ids
		var statSelArray = new Array();
		statSelArray.length=0;
		//The second (with matching indexes) holds the corresponding feature XML (attribute info)
		//var statSelXMLArray = new Array();
		//statSelXMLArray.length=0;

		var selImage="crosshair_red.png";
        //var POINT_TAG="TheGeometry";
        var NAMESPACE="";
        //var POINT_TAG=addNamespace("loc");
        var POINT_TAG="geom";
        var FID_SEP=".";

		var updateSelectionFunctionList;
		var updateBBoxFunctionList;
		var updateFeatureCountFunctionList;
        var afterQueryPointsFunctionList;
        var showPointInfoFunctionList = "showPointInfoHover";
        var hidePointInfoFunctionList = "hidePointInfoHover";

		var ptLegendArray= new Array();
        var showDiscrepWarn = false;

		//WMS Variables
		var pixelWidth,pixelHeight,wmsCoordSysId,wmsVersion,wmsServiceURL,wmsServiceName,wmsLayerString,wmsBaseString,otherWMSBaseString,mapLeft,mapRight,mapBottom,mapTop;
		var mapXDistance,mapYDistance;
		var zoomFactor=2;
		var mapSizeRatio;
		var numZoomLevels; //,navToolsYesNo,legendYesNo,coordDisplayYesNo,wmsLayerInfoYesNo,clusterSupressYesNo,statisticsYesNo,ptSelectionYesNo;
        var statistics;
        var ptSelection;
        var navTools;
        var legend;
        var coordDisplay;
        var stnInfoDisplay;
        var wmsLayerInfo;
        var clusterSupress;

		//Default Zoom Level is zoomed WAY out.
		var currentZoomLevel=1;
		//Zoom and Re-Center is the default map click action...change it to 2 for Re-Center
		var mapClickAction=1;
		//Show station info (manually control mouse over attribute table), used with radio buttons
		var showStationInfo=1;
		//This variable is used to hold the value of the station click mode radio button (http or additive selection)
		var stationClickMode=1;
		//**********Hard code the zoom level x distances for now (in decimal degrees)...it just seems easier to hardcode them
		//and then the more zoom levels that people use...the more they can zoom in. The NUMBER of zoom levels is defined in the config file...which I think is all that's
		//necessary...unless we want to dynamically create the zoom distances...(i.e. if there are only two levels, one is zoomed WAY out..and the other is zoomed WAY in...
		var zoomLevelDistanceArray=new Array();
		zoomLevelDistanceArray[0]=80;
		zoomLevelDistanceArray[1]=40;
		zoomLevelDistanceArray[2]=10;
		zoomLevelDistanceArray[3]=5;
		zoomLevelDistanceArray[4]=2;
		zoomLevelDistanceArray[5]=0.50;
		zoomLevelDistanceArray[6]=0.10;
		zoomLevelDistanceArray[7]=0.05;
		zoomLevelDistanceArray[8]=0.025;
		zoomLevelDistanceArray[9]=0.012;

		//These arrays will contain the clustered area information for each zoom level.
		//These numbers (1,2,3,4,5,etc) match up with the current zoom level
		//These aren't used if the cluster suppress is "no" in the config (it should stay no most of the time anyways)
		var clusZoomArray1 = new Array();
		clusZoomArray1.length=0;
		var clusZoomArray2 = new Array();
		clusZoomArray2.length=0;
		var clusZoomArray3 = new Array();
		clusZoomArray3.length=0;
		var clusZoomArray4 = new Array();
		clusZoomArray4.length=0;
		var clusZoomArray5 = new Array();
		clusZoomArray5.length=0;
		var clusZoomArray6 = new Array();
		clusZoomArray6.length=0;
		var clusZoomArray7 = new Array();
		clusZoomArray7.length=0;
		var clusZoomArray8 = new Array();
		clusZoomArray8.length=0;
		var clusZoomArray9 = new Array();
		clusZoomArray9.length=0;
		var clusZoomArray10 = new Array();
		clusZoomArray10.length=0;

		//Service Available Code value which tells us which map service is going to be used (1=primary, 2=backup, 3=other, perhaps geoserver map service)
		//var serviceAvailableCode=1;
		//var wmsBackupServiceURL;
		//var wmsBackupServiceName;
		wmsFailsafeURL = "";
		wmsFailsafeLayers = "";
		var failsafe=false;

        function initMapLogging(appender, level) {
            // Should add failure logic
            if (level != null) {
                mapLog.setLevel(level);
            }
            if (appender == null) {
                appender = new log4javascript.AlertAppender();
            }
            mapLog.addAppender(appender);
        }

		//Call this function to plot the default layer (defined in config PBOConfig.txt file)
		function iagt_map_onLoad(newConfigFileURL,namespace,defaultAOIName) {
            mapLog.debug("iagt_map_onLoad: start with defaultAOIName" + defaultAOIName);

            var defaultLayer;
            if (newConfigFileURL!="" && newConfigFileURL!=null) {
                configFileURL = newConfigFileURL
            }

            //Cheap hack since I couldn't get it to work using the config file
            if (namespace != null) {
                NAMESPACE = namespace;
            }

			//Store the config file entries in an array
			parseConfigFile();

			//Create the base WMS getmap request...from the config file entry. The result is false, if no working map services are detected.
			wmsBaseString=prepareWMSUrl();
			//This is false, if no services are available....here we could do the failsafe maybe
			if (wmsBaseString!=false) {
				
				if (theOtherWMSLayer) {
					otherWMSBaseString=prepareOtherWMSUrl();
				}

			    //Add Elements (map containers, zoom buttons, loading sign...perhaps add everything here once we understand what the final system will look like
			    addElements();

			    //Define the canvases for the map and the points, this must occur after the elements have been added to the page ('map', 'layers')
			    wmsCanvas = new jsGraphics("map");
			    pointCanvas = new jsGraphics("layers");
			    if (theOtherWMSLayer) {
			    	otherWMSCanvas = new jsGraphics("otherLayer");
			    }
			    selectionCanvas = new jsGraphics("stationSelection");
			    //The loading screen was added above with all the other elements...so it can be used now
			    showLoading();
                
			    //Register for map click event (for zoom or whatever)...this must be registered after the addElements function (so that the element exists)
			    if (navTools) {
			    	document.getElementById("mapContainer").onclick=mapClick;
			    }
			    if (coordDisplay) {
			    	document.getElementById("mapContainer").onmousemove=mapMouseMove;
			    }
                
			    //Dynamically set the dimensions of the mapping container (including the select box), the whole map component, and the map specifically.
			    pageElementLayout();
                
			    //Preload imagery, based on info in config file			
			    prepareSymbology();
                
			    //Prepare cluster area array
			    prepareClusterAreas();

			    //Waiting for each of the point images to download fully.
                if (defaultAOIName) {
                    setTimeout("delayForPreload('" + defaultAOIName + "');",0);
                } else {
                    setTimeout("delayForPreload();",0);
                }
			    //var timeout = setTimeout("delayForPreload();",1900);
			    //wmsBaseString=prepareWMSUrl();
			    //defaultLayer = prepareLayerSelections();
                
			    //Open Default selection
			    //updateWMSImgAndPoints(defaultLayer);
                mapLog.debug("iagt_map_onLoad: Completed");
            } else {
                alert("All available map servers are down temporarily, please check back later.");
			}
		}

		//Parse the config file
		function parseConfigFile() {
			var xmlhttp, myParser, xmlText; //, xmlDoc;
			var theLayers,layer,pointLayersElement;
			var layerArray = new Array();
			//xmlhttp = new createXMLHttpRequest();
            //var xmlReq = new XMLRequestWrapper();
            //xmlReq.createXMLHttpRequest();
            var xmlReq = new XMLRequestWrapper();
            xmlReq.createXMLHttpRequest();
            xmlhttp = xmlReq.getReq();

            if (xmlhttp) {
                xmlhttp.open("GET", configFileURL, false);
                xmlhttp.send(null);
                //configFileXML = xmlhttp.responseText;	
                configXMLDoc = xmlhttp.responseXML;
            }

			//myParser = new DOMParser();
			//xmlDoc = Sarissa.getDomDocument();
			//xmlDoc = myParser.parseFromString(configFileXML,"text/xml");

			//Get a node list of the map styles in the config file
			theMapStyles = configXMLDoc.getElementsByTagName("MapStyle");
			//if (configFileXML.indexOf("OtherWMSLayers")!=-1){
			//	theOtherWMSLayer = true;
			//	theOtherMapStyles = xmlDoc.getElementsByTagName("OtherMapStyle");
			//}
            theOtherMapStyles = configXMLDoc.getElementsByTagName("OtherMapStyle");
            if (theOtherMapStyles) {
                theOtherWMSLayer = true;
            }
            
			//Get a node list of the wms layer info
			theWMSLayersInfo = configXMLDoc.getElementsByTagName("WMSLayer");

			//Get a node list of the regions in the config file
			theRegions = configXMLDoc.getElementsByTagName("Region");

			//Store node list of point layers in the config file
			theConfigLayers = configXMLDoc.getElementsByTagName("PointLayer");

			//Store the node list of clustered areas
			theClusters = configXMLDoc.getElementsByTagName("ClusterArea");

			//Also need to get some top level info about all the point layers (point size, etc)
			pointLayersElement = configXMLDoc.getElementsByTagName("PointLayers");
            if (pointLayersElement != null && pointLayersElement.length > 0) {
                //Set some global variables that hold information on where the point data is coming from, and their size, and path to their icons
                gmlPointContentURL = pointLayersElement[0].getAttribute("gmlPointContentURL");
    
                //Hack to get around Safari bug
                gmlPointContentURL = gmlPointContentURL.replace("&#38;","&");
                
                // Move to a function
                if (gmlPointContentURL.substring(0,4)=="HOST") {
                    gmlPointContentURL = location.protocol + "//" + location.host + gmlPointContentURL.slice(4);
                }
                
                gmlPointsName = pointLayersElement[0].getAttribute("gmlPointsName");
    
                var fileExt = gmlPointContentURL.substring(gmlPointContentURL.lastIndexOf("."),gmlPointContentURL.length);
                if (fileExt.toUpperCase()==".XML") {
                    queryType="STATIC";
                } else {
                    queryType="DYNAMIC";
                }
    
                //the image path, in the config file
                iPath = pointLayersElement[0].getAttribute("ptImagePath");
                //add a slash to the path, if there isn't one in the config file
                if (iPath.substring(iPath.length-1,iPath.length)!="/") iPath=iPath+"/";
                ptSize = pointLayersElement[0].getAttribute("pointPixelSize");
            }
            mapLog.debug("Completed parsing Config File");
		}

	//THIS FUNCTION (this app actually) IS ONLY SETUP FOR ONE WMS MAP REQUEST. IF MORE ARE NEEDED, IT WILL NEED TO BE UPDATED
	//Currently, one more WMS map is allowed but is handled separately for now. multiple map services is "on the list" for later
		function prepareWMSUrl() {
		    var wmsMapElements,wmsElement,tempChildNodeList,tempChildNode,wmsBase,mapStyle,id,mapStyleName,mapStyleLayers;
			var wmsServersList,wmsServicesList;
			var serviceAvailable=false;
		    //var myParser = new DOMParser();
			//Get the map's pixel height/width...this may not be needed here-----------------------------------------------------------------
			//xmlDoc = Sarissa.getDomDocument();
			//xmlDoc = myParser.parseFromString(configFileXML,"text/xml");
			wmsMapElements = configXMLDoc.getElementsByTagName("WMSMap");

			for (i=0;i<wmsMapElements.length;i++) {
				wmsElement = wmsMapElements[i];
				//Populate Globals wms request
                if (pixelWidth == null) {
                    pixelWidth = wmsElement.getAttribute("pixelWidth");
                }
                if (pixelHeight == null) {
                    pixelHeight = wmsElement.getAttribute("pixelHeight");
                }
				mapSizeRatio = parseInt(pixelHeight)/parseInt(pixelWidth);
				//Populate globals for the application (layout, tools,registered functions)
				numZoomLevels=parseInt(wmsElement.getAttribute("numZoomLevels"));
                
				//navToolsYesNo=wmsElement.getAttribute("navToolsYesNo");
                navTools = getBooleanAttribute(wmsElement, "navToolsYesNo", true);
                
				//legendYesNo=wmsElement.getAttribute("legend");
                legend = getBooleanAttribute(wmsElement, "legend", true);
                
				//coordDisplayYesNo=wmsElement.getAttribute("coordDisplay");
                coordDisplay = getBooleanAttribute(wmsElement, "coordDisplay", true);
                stnInfoDisplay = getBooleanAttribute(wmsElement, "stnInfoDisplay", true);
                
				//wmsLayerInfoYesNo=wmsElement.getAttribute("layerInfo");
                wmsLayerInfo = getBooleanAttribute(wmsElement, "layerInfo", false);
                
				//clusterSupressYesNo=wmsElement.getAttribute("clusterSupress");
				//alert(clusterSupressYesNo);
				//if (clusterSupressYesNo=="" || clusterSupressYesNo==null) clusterSupressYesNo="NO";
                clusterSupress = getBooleanAttribute(wmsElement, "clusterSupress", false);
                
				//statisticsYesNo=wmsElement.getAttribute("statistics");
                statistics = getBooleanAttribute(wmsElement, "statistics", false);
                
				//ptSelectionYesNo=wmsElement.getAttribute("ptSelectionYesNo");
                ptSelection = getBooleanAttribute(wmsElement, "ptSelectionYesNo",true);
				
                updateSelectionFunctionList=wmsElement.getAttribute("updateSelectionFunctionList");
				updateBBoxFunctionList=wmsElement.getAttribute("updateBBoxFunctionList");
                updateFeatureCountFunctionList=wmsElement.getAttribute("updateFeatureCountFunctionList");
                afterQueryPointsFunctionList=wmsElement.getAttribute("afterQueryPointsFunctionList");
                
                var funcList = wmsElement.getAttribute("showPointInfoFunctionList");
                if (funcList != null) {
                    showPointInfoFunctionList=funcList;
                }
                funcList = wmsElement.getAttribute("hidePointInfoFunctionList");
                if (funcList != null) {
                    hidePointInfoFunctionList=funcList;
                }

				//Get the default layers to request from the map service
				for (i=0;i<theMapStyles.length;i++){
					mapStyle=theMapStyles[i];
					defaultYesNo = mapStyle.getAttribute("default");
					if (defaultYesNo.toUpperCase()=="YES") {
	                    mapLog.debug("prepareWMSUrl: found default map style" + mapStyle.getAttribute('name'));
						wmsLayerString = mapStyle.getAttribute("wmsLayerString");
						//alert(wmsLayerString);
					}				
				}

				tempChildNodeList = wmsElement.childNodes;

				for (j=0;j<tempChildNodeList.length;j++) {
					tempChildNode = tempChildNodeList[j];
					if (tempChildNode.nodeName.toUpperCase()=="SERVICENAME") {
						//wmsServiceName = Sarissa.getText(tempChildNode);
					} else if (tempChildNode.nodeName.toUpperCase()=="WMSVERSION") {
						wmsVersion = Sarissa.getText(tempChildNode);
					//} else if (tempChildNode.nodeName.toUpperCase()=="LAYERS") {
						//wmsLayerString = Sarissa.getText(tempChildNode);
					} else if (tempChildNode.nodeName.toUpperCase()=="COORDSYS") {
						wmsCoordSysId = Sarissa.getText(tempChildNode);
					} else if (tempChildNode.nodeName.toUpperCase()=="FAILSAFEURL") {
						wmsFailsafeURL = Sarissa.getText(tempChildNode);
					} else if (tempChildNode.nodeName.toUpperCase()=="FAILSAFELAYERS") {
						wmsFailsafeLayers = Sarissa.getText(tempChildNode);
					}
				}//end for WMS tag's child nodes

				//Node lists with the server urls and corresponding map service names
				wmsServersList = configXMLDoc.getElementsByTagName("ServiceURL");
				wmsServicesList = configXMLDoc.getElementsByTagName("ServiceName");
				
				//Loop through the servers/services, to find a working map service (they are listed in order of priority in the config file).
				for (c=0;c<wmsServersList.length;c++) {
					serviceAvailable = checkServiceAvailability(Sarissa.getText(wmsServersList[c]),Sarissa.getText(wmsServicesList[c]));
					if (serviceAvailable==true) {
						//alert("this one is good: " + Sarissa.getText(wmsServersList[c]));
						//Set the global variables for the working wms server and map service
						wmsServiceURL = Sarissa.getText(wmsServersList[c]);
						wmsServiceName = Sarissa.getText(wmsServicesList[c]);
                        wmsBase = wmsServiceURL +"?"+"SERVICENAME="+wmsServiceName+"&"+"VERSION="+wmsVersion+"&Service=wms&Request=GetMap&"+"LAYERS="+wmsLayerString+"&"+"SRS=EPSG:"+wmsCoordSysId+"&FORMAT=jpeg&BBOX=";
                        mapLog.debug("Base WMS URL: " + wmsBase);
						//If the map services are not available...return false...instead of the wmsBaseString
						//alert(wmsBase);
						return wmsBase;
					} else {
						//This if statement checks to see if the last "real" server failed, if so, we need to use the failsafe information.
						if (c==wmsServersList.length-1) {
						//alert("failsafe");
							wmsServiceURL = wmsFailsafeURL;
							wmsLayerString = wmsFailsafeLayers;
							wmsServiceName = "";
							//alert(wmsFailsafeURL);
							serviceAvailable = checkServiceAvailability(wmsFailsafeURL,"");
							if (serviceAvailable==true) {
								wmsBase = wmsServiceURL +"?"+"styles=&SERVICENAME="+wmsServiceName+"&"+"VERSION="+wmsVersion+"&Service=wms&Request=GetMap&"+"LAYERS="+wmsLayerString+"&"+"SRS=EPSG:"+wmsCoordSysId+"&FORMAT=image/png&BBOX=";
					           // alert(wmsBase);
								mapLog.debug("Base WMS URL: " + wmsBase);
								failsafe=true;
								//If the map services are not available...return false...instead of the wmsBaseString
								return wmsBase;
							} else {
								return false;
							}
						}
						//alert ("this one is NOT good: " + Sarissa.getText(wmsServersList[c]));
					}
				}
			}//end for each WMS Map element (just one for now)
		}//end construct WMS base URL (another function will concatenate the BBOX extents of the current image needed)

	//THIS FUNCTION is for adding an optional third layer (wms) (in between points and map). We have been using this for velocity vectors, it's a hidden feature
		function prepareOtherWMSUrl() {
		    var wmsMapElements,wmsElement,tempChildNodeList,tempChildNode,wmsBase,mapStyle,id,mapStyleName,mapStyleLayers;
			var otherWMSVersion,otherWMSServiceURL,otherWMSServiceName,otherWMSArgs;
		    //var myParser = new DOMParser();
			//Get the map's pixel height/width...this may not be needed here-----------------------------------------------------------------
			//xmlDoc = Sarissa.getDomDocument();
			//xmlDoc = myParser.parseFromString(configFileXML,"text/xml");
			wmsMapElements = configXMLDoc.getElementsByTagName("OtherWMSLayer");

			for (i=0;i<wmsMapElements.length;i++) {
				wmsElement = wmsMapElements[i];
				tempChildNodeList = wmsElement.childNodes;

				for (j=0;j<tempChildNodeList.length;j++) {
					tempChildNode = tempChildNodeList[j];
					if (tempChildNode.nodeName.toUpperCase()=="SERVICEURL") {
						otherWMSServiceURL = Sarissa.getText(tempChildNode);
					} else if (tempChildNode.nodeName.toUpperCase()=="SERVICENAME") {
						otherWMSServiceName = Sarissa.getText(tempChildNode);
					} else if (tempChildNode.nodeName.toUpperCase()=="ARGS") {
						otherWMSArgs = Sarissa.getText(tempChildNode);
					} else if (tempChildNode.nodeName.toUpperCase()=="WMSVERSION") {
						otherWMSVersion = Sarissa.getText(tempChildNode);
					//} else if (tempChildNode.nodeName.toUpperCase()=="LAYERS") {
						//wmsLayerString = Sarissa.getText(tempChildNode);
					//} else if (tempChildNode.nodeName.toUpperCase()=="COORDSYS") {
						//wmsCoordSysId = Sarissa.getText(tempChildNode);
					}
				}//end for WMS tag's child nodes

			}//end for each WMS Map element (just one for now)
			//Get the default layers to request from the map service
			for (i=0;i<theOtherMapStyles.length;i++){
				mapStyle=theOtherMapStyles[i];
				defaultYesNo = mapStyle.getAttribute("default");
				if (defaultYesNo.toUpperCase()=="YES") {
					wmsLayerString = mapStyle.getAttribute("wmsLayerString");
					//alert(wmsLayerString);
				}				
			}
			wmsBase = otherWMSServiceURL +"?";
            if (otherWMSArgs!=null && otherWMSArgs != '') {
                wmsBase = wmsBase+otherWMSArgs+"&";
            }
            wmsBase = wmsBase+"SERVICENAME="+otherWMSServiceName+"&"+"VERSION="+otherWMSVersion+"&Service=wms&Request=GetMap&TRANSPARENT=true&"+"LAYERS="+wmsLayerString+"&"+"SRS=EPSG:"+wmsCoordSysId+"&FORMAT=image/png&BBOX=";
            mapLog.debug("'Other' WMS URL: " + wmsBase);
			return wmsBase;
		}//end construct WMS base URL (another function will concatenate the BBOX extents of the current image needed)

        function addNavTools() {
            var div = document.getElementById('aoiListDiv');
            if (div != null) {
                div.innerHTML='<select name="aoiList" id="aoiList" onChange="updateMap(this.value,lastPointLayer,lastOtherMapStyle);"/>';
            }
            div = document.getElementById('mapStyleListDiv');
            if (div != null) {
                div.innerHTML='<select name="mapStyleList" id="mapStyleList" onChange="changeMap(this.value);"/>';
            }
            div = document.getElementById('lyrListDiv');
            if (div != null) {
                if (theConfigLayers.length == 1) {
                    div.innerHTML = theConfigLayers[0].getAttribute('name')
                } else {
                    div.innerHTML = '<select name="lyrList" onChange="updateLayers(this.value,\'new\');"/>';
                }
            }
            mapLog.debug("addNavTools: completed");
        }


        function addMapComponents() {
			//Put together the DHTML for the mapping component
			var mapComp = document.getElementById('mapComponent');
			var htmlString="";
			htmlString = "<div id=\"navigation\" style=\"position:relative;\">";
			if (navTools) {
				htmlString += "<a href=\"javascript:zoomInOutMap(1);\"><IMG NAME=\"zoomin\" ID=\"zoomin\" SRC=\"./Images/map/zoomin.png\" onmouseout=\"javascript:this.src=\'./Images/map/zoomin.png\'\" onmouseover=\"javascript:hidePointInfo();this.src=\'./Images/map/zoomin_on.png\'\" ALT=\"Zoom In\" TITLE=\"Zoom In\" BORDER=\"0\" WIDTH=\"24\" HEIGHT=\"24\" STYLE=\"z-index:505;position:absolute;\"><\/a>";
				htmlString+="<a href=\"javascript:zoomInOutMap(2);\"><IMG NAME=\"zoomout\" ID=\"zoomout\" SRC=\"./Images/map/zoomout.png\" onmouseout=\"javascript:this.src=\'./Images/map/zoomout.png\'\" onmouseover=\"javascript:hidePointInfo();this.src=\'./Images/map/zoomout_on.png\'\" ALT=\"Zoom Out\" TITLE=\"Zoom Out\" BORDER=\"0\" WIDTH=\"24\" HEIGHT=\"24\" STYLE=\"z-index:505;position:absolute;\"><\/a>";
				for (i=1;i<numZoomLevels+1;i++) {
					htmlString+="<a href=\"javascript:zoomLevel("+i+");\"><IMG NAME=\"zoomlevel"+i+"\" ID=\"zoomlevel"+i+"\" SRC=\"./Images/map/zoomlevel.png\" onmouseout=\"javascript:mouseOutLevel(this.id.substring(this.id.length-1,this.id.length));\" onmouseover=\"javascript:hidePointInfo();this.src=\'./Images/map/zoomlevel_on.png\'\" ALT=\"Zoom Level "+i+"\" TITLE=\"Zoom Level "+i+"\" BORDER=\"0\" WIDTH=\"24\" HEIGHT=\"13\" STYLE=\"z-index:505;position:absolute;\"><\/a>";
				}
				htmlString+="<a href=\"javascript:panMap(\'w\');\"><IMG NAME=\"westnav\" ID=\"west\" SRC=\"./Images/map/west1.png\" onmouseout=\"javascript:this.src=\'./Images/map/west1.png\'\" onmouseover=\"javascript:hidePointInfo();this.src=\'./Images/map/west_on.png\'\" ALT=\"West\" TITLE=\"West\" BORDER=\"0\" WIDTH=\"8\" HEIGHT=\"50\" STYLE=\"z-index:0;position:absolute;\"><\/a>";
				htmlString+="<a href=\"javascript:panMap(\'e\');\"><IMG NAME=\"eastnav\" ID=\"east\" SRC=\"./Images/map/east1.png\" onmouseout=\"javascript:this.src=\'./Images/map/east1.png\'\" onmouseover=\"javascript:hidePointInfo();this.src=\'./Images/map/east_on.png\'\" ALT=\"East\" TITLE=\"East\" BORDER=\"0\" WIDTH=\"8\" HEIGHT=\"50\" STYLE=\"z-index:0;position:absolute;\"><\/a>";
				htmlString+="<a href=\"javascript:panMap(\'n\');\"><IMG NAME=\"northnav\" ID=\"north\" SRC=\"./Images/map/north1.png\" onmouseout=\"javascript:this.src=\'./Images/map/north1.png\'\" onmouseover=\"javascript:hidePointInfo();this.src=\'./Images/map/north_on.png\'\"ALT=\"North\" TITLE=\"North\" BORDER=\"0\" WIDTH=\"50\" HEIGHT=\"8\" STYLE=\"z-index:0;position:absolute;\"><\/a>";
				htmlString+="<a href=\"javascript:panMap(\'s\');\"><IMG NAME=\"southnav\" ID=\"south\" SRC=\"./Images/map/south1.png\" onmouseout=\"javascript:this.src=\'./Images/map/south1.png\'\" onmouseover=\"javascript:hidePointInfo();this.src=\'./Images/map/south_on.png\'\"ALT=\"South\" TITLE=\"South\" BORDER=\"0\" WIDTH=\"50\" HEIGHT=\"8\" STYLE=\"z-index:0;position:absolute;\"><\/a>";
				htmlString+="<a href=\"javascript:panMap(\'sw\');\"><IMG NAME=\"southwestnav\" ID=\"southwest\" SRC=\"./Images/map/southwest2.png\" onmouseout=\"javascript:this.src=\'./Images/map/southwest2.png\'\" onmouseover=\"javascript:hidePointInfo();this.src=\'./Images/map/southwest_on.png\'\"ALT=\"Southwest\" TITLE=\"Southwest\" BORDER=\"0\" WIDTH=\"50\" HEIGHT=\"50\" STYLE=\"z-index:0;position:absolute;\"><\/a>";
				htmlString+="<a href=\"javascript:panMap(\'se\');\"><IMG NAME=\"southeastnav\" ID=\"southeast\" SRC=\"./Images/map/southeast2.png\" onmouseout=\"javascript:this.src=\'./Images/map/southeast2.png\'\" onmouseover=\"javascript:hidePointInfo();this.src=\'./Images/map/southeast_on.png\'\"ALT=\"Southeast\" TITLE=\"Southeast\" BORDER=\"0\" WIDTH=\"50\" HEIGHT=\"50\" STYLE=\"z-index:0;position:absolute;\"><\/a>";
				htmlString+="<a href=\"javascript:panMap(\'nw\');\"><IMG NAME=\"northwestnav\" ID=\"northwest\" SRC=\"./Images/map/northwest2.png\" onmouseout=\"javascript:this.src=\'./Images/map/northwest2.png\'\" onmouseover=\"javascript:hidePointInfo();this.src=\'./Images/map/northwest_on.png\'\" ALT=\"Northwest\" TITLE=\"Northwest\" BORDER=\"0\" WIDTH=\"50\" HEIGHT=\"50\" STYLE=\"z-index:0;position:absolute;\"><\/a>";
				htmlString+="<a href=\"javascript:panMap(\'ne\');\"><IMG NAME=\"northeastnav\" ID=\"northeast\" SRC=\"./Images/map/northeast2.png\" onmouseout=\"javascript:this.src=\'./Images/map/northeast2.png\'\" onmouseover=\"javascript:hidePointInfo();this.src=\'./Images/map/northeast_on.png\'\"ALT=\"Northeast\" TITLE=\"Northeast\" BORDER=\"0\" WIDTH=\"50\" HEIGHT=\"50\" STYLE=\"z-index:0;position:absolute;\"><\/a>";
			}
			if (wmsLayerInfo) {
			//alert("creating the wmsLayerInfo table");
				htmlString+="<div id=\"wmsLayerInfo\" style=\"overflow:visible;position:absolute;visibility:hidden;z-index:501;\"><\/div>";
			}
			if (statistics) {
			//alert("creating the wmsLayerInfo table");
				htmlString+="<div id=\"statistics\" style=\"overflow:visible;position:absolute;visibility:hidden;z-index:501;\"><\/div>";
			}
			htmlString+="<div id=\"mapContainer\" style=\"position:absolute;overflow:hidden;\">";

			htmlString+="<div id=\"map\" style=\"position:absolute;z-index:501;\"><\/div>";
			htmlString+="<div id=\"otherLayer\" style=\"z-index:502;position:absolute;\"><\/div>";
			htmlString+="<div id=\"layers\" style=\"z-index:503;position:absolute;\"><\/div>";
			//Another layer holds the selected points
			htmlString+="<div id=\"stationSelection\" style=\"z-index:504;position:absolute;\"><\/div>";
			htmlString+="<div onmouseout=\"hidePointInfo();\" id=\"hoverDot\" style=\"z-index:505;visibility:hidden;position:absolute;\"><\/div>";
			htmlString+="<div onmouseout=\"hidePointInfo();\" width=\"100\" id=\"attTableMain\" style=\"z-index:505;visibility:hidden;position:absolute;\"><\/div>";
			/*
			htmlString+="<div id=\"imageMap\" style=\"position:absolute;\">";
				htmlString+="<MAP NAME=\"image-map\">";
				htmlString+="<AREA SHAPE=\"RECT\" COORDS=\"15,15,150,150\" onmouseover=\"regionMouseOver();\" onmouseout=\"cluster='';\" HREF=\"javascript:alert('red')\">";
				htmlString+="</MAP>";
			htmlString+="<\/div>";

			htmlString+="<div id=\"pointClusters\" style=\"z-index:502;position:absolute;\">";
				htmlString+="<IMG NAME=\"clusterImg\" ID=\"clusterImg\" SRC=\"./Images/map/cluster_trans.gif\" USEMAP=\"#image-map\" BORDER=\"0\" WIDTH=\""+pixelWidth+"\" HEIGHT=\""+pixelHeight+"\">";
			htmlString+="</div>";
			*/

			if (legend) {
				htmlString+="<div onmouseout=\"closeLegend();\" onmouseover=\"openLegend();\" onclick=\"openLegend();\" id=\"legendTitle\" style=\"border-right:3px solid black;font-weight:bold;font-family:Arial;background-color:#000000;color:#FFFFFF;z-index:505;visibility:visible;position:absolute;\">";
				htmlString+="<a><img style=\"border:none;\" height=\"21px\" width=\"48px\" src=\""+iPath+"legend.png"+"\"</a>";
				htmlString+="<\/div>";
				htmlString+="<div onmouseover=\"closeLegend();\" onmousemove=\"closeLegend();\" id=\"legend\" style=\"font-weight:bold;font-family:Arial;background-color:#000000;color:#FFFFFF;z-index:505;visibility:hidden;position:absolute;\">";
				htmlString+="<\/div>";
			}
			if (coordDisplay) {
				htmlString+="<div onmouseover=\"hidePointInfo();\" onmousemove=\"closeLegend();\" id=\"coordDisplay\" style=\"width:50px;height:21px;border:none;font-family:Arial;font-size:9px;background-color:#000000;z-index:505;visibility:visible;position:absolute;\">";
				htmlString+="<div style=\"color:#000000;font-weight:bold;\" width=\"100%\"><a id=\"coord\" style=\"text-decoration:none;color:#FFFFFF;\">X:<br>Y:</a></div>";
				htmlString+="<\/div>";
			}
			htmlString+="<div id=\"loading\" style=\"z-index:506;visibility:hidden;position:absolute;\">";
			htmlString+="<IMG NAME=\"loadingImg\" ID=\"loadingImg\" SRC=\"./Images/map/loading-progress.gif\" ALT=\"Loading...\" TITLE=\"Loading...\" BORDER=\"0\" WIDTH=\"221\" HEIGHT=\"21\">";
			htmlString+="<\/div>";
			htmlString+="<\/div>";
			htmlString+="<\/div>";
			//htmlString+="<SCRIPT TYPE=\"text/javascript\" LANGUAGE=\"JavaScript\" SRC=\"JavaScript/wz_tooltip.js\"></SCRIPT>";
			mapComp.innerHTML=htmlString;
        }

        function addStationInteractionControls() {
            var div = null;
            if (theOtherWMSLayer) {
                div = document.getElementById('theOtherWMSLayerControlDiv');
                if (div != null) {
                    div.style.visibility="visible";
                    div = document.getElementById('theOtherWMSLayerDiv');
                    if (div != null) {
                        div.innerHTML='<select name="otherWMSLayerStyle" onChange="changeOtherMap(this.value);"></select>';
                    }
                }
            }
            
            if (navTools) {
                div = document.getElementById('zoomDiv');
                if (div != null) {
                    div.innerHTML='<input type="radio" name="mapRadio" value="1" checked onclick="javascript:mapClickAction=1;">Zoom + Re-Center' +
                                  '<input type="radio" name="mapRadio" value="2" onclick="javascript:mapClickAction=2">Re-Center';
                }
            }
            
            if (!clusterSupress) {
                div = document.getElementById('statInterObj');
                if (div != null) {
                    div.innerHTML = '<input type="radio" name="stationInter" value="1" checked onclick="javascript:setStatInteractive(1);">On' +
                                    '<input type="radio" name="stationInter" value="2" onclick="javascript:setStatInteractive(2);">Off';             
                }
            }
            
            if (ptSelection) {
                div = document.getElementById('statClickModeObj');
                if (div != null) {
                    div.innerHTML = '<input type="radio" name="stationClick" value="1" checked onclick="setStatClickMode(1);">Homepage' +
                                    '<input type="radio" name="stationClick" value="2" onclick="javascript:setStatClickMode(2);">Select/Deselect' +
                                    '<div id="selOptions" style="visibility:hidden;margin-bottom:0px;margin-right:6px" align="right">' +
                                    '<a href="javascript:selectAllInView();">Select Visible Stations</a> or <a href="javascript:clickClear();">Clear Selection</a></div>';
                }
            }
        }

		//This function adds the elements for the map component, including select boxes,etc. It uses the settings in the config file
		function addElements() {
            addNavTools();
            addMapComponents();
            addStationInteractionControls();
            mapLog.debug("Done adding html elements");
		}

		//This function preloads images, and stores symbology information
		function prepareSymbology() {
			var symbolInfo, layer, lyrName, theLayers,symbolFieldName;
			var imageArray = new Array();
			var symbolImage, tempChildNodeList, tempChildNode, tempSymbol;
			//Preload the image icons for all layers, and populate symbology values arrays
			for (i=0;i<theConfigLayers.length;i++){
				layer=theConfigLayers[i];
				lyrName = layer.getAttribute("name");
				ptLegendArray[i]=lyrName+",";
				//Xpath expressions in Sarissa, are conflicting with something in the bottomHeader.inc
				//So, instead of changing the include file, I chose not to use the secondary Sarissa libraries
				//the one that conflicted with the include file, was sarissa_ieemu_xpath.js. Not a big deal for now,
				//but should revisit later. For now, the fix is just getting a childNode list of each element, and,
				//if the nodeName is "SYMBOL INFO", then we get the info...
				tempChildNodeList = layer.childNodes;

				//symNode = layer.selectSingleNode("SymbolInfo");
				for (b=0;b<tempChildNodeList.length;b++) {
					tempChildNode = tempChildNodeList[b];
					if (tempChildNode.nodeName.toUpperCase()=="SYMBOLINFO") {
						symbolInfo = Sarissa.getText(tempChildNode);
						ptLegendArray[i]+=Trim(symbolInfo);
					}

				}

                var symbolArray = Trim(symbolInfo).split(",");
                var symbolHash = new Array();
                for (j=0;j<symbolArray.length;j++) {
                    var kvp = symbolArray[j].split('=');
                    symbolHash[kvp[0]]=kvp[1];
                }
                symbolLookup[i]=symbolHash;
                
                // TODO: Deprecated, remove.
				symbolValuesArray[i]=Trim(symbolInfo);
                
				symbolFieldName = addNamespace(layer.getAttribute("symbolFieldName"));
                //alert("Adding symbolFiledName(" + i + "): " + symbolFieldName);
				symbolFieldNameArray[i]=symbolFieldName;

				//Preload image icons
                if(document.images) {
                    preloadImage("attributeTable.png", iPath);
                    preloadImage("crosshair_red.png", iPath);
                }
                for (var symbolName in symbolHash) {
                    // If we want this then we have to have a good way to reinit all values.
					//if (statistics){
					//	symbolNameArray[symbolName]=0;
					//}
                    if(document.images) {
                        preloadImage(symbolHash[symbolName], iPath);
					}
                }
			}//end for each config layer
		}//end function preloadImages and store symbology information.

		//Prepare the cluster area array, which contains a delimited list of cluster areas for each zoom level
		function prepareClusterAreas() {
			var cluster,clusterAreaString, clusterName,minX,maxX,minY,maxY,clusterZoomLevel;
			clusterAreaString="";
			for (i=0;i<theClusters.length;i++){
				clusterAreaString="";
				cluster = theClusters[i];
				clusterZoomLevel = cluster.getAttribute("zoomLevel");
				clusterName = cluster.getAttribute("name");
				minX = cluster.getAttribute("minx");
				maxX = cluster.getAttribute("maxx");
				minY = cluster.getAttribute("miny");
				maxY = cluster.getAttribute("maxy");

				clusterAreaString = clusterZoomLevel+","+clusterName+","+minX+","+maxX+","+minY+","+maxY;
				eval('clusZoomArray'+clusterZoomLevel+'[clusZoomArray'+clusterZoomLevel+'.length]=clusterAreaString;');
			}
		}

		//This function is just a continuation of the map_onLoad() function. We need to wait a second
		//so that each point image has been downloaded once, before we use them in the app 
		function delayForPreload(defaultAOIName) {
			var defaultLayer, defaultMapStyle, defaultOtherMapStyle;
			defaultMapStyle = prepareMapStyles();
			lastMapStyle = defaultMapStyle;

			if (theOtherWMSLayer) {
				defaultOtherMapStyle = prepareOtherMapStyles();
                mapLog.debug("delayForPreload: defaultOtherMapStyle " + defaultOtherMapStyle);
			} else {
				defaultOtherMapStyle = "";
			}
            lastOtherMapStyle = defaultOtherMapStyle;

			if (wmsLayerInfo) {
				displayWMSLayerInfo(defaultMapStyle);
			}

			defaultRegion = prepareRegions(defaultAOIName);
			defaultLayer = prepareLayers();
			lastPointLayer=defaultLayer;

			//Open Default selection on startup
			//if (theOtherWMSLayerYesNo=="YES") {
				updateMap(defaultRegion,defaultLayer,defaultOtherMapStyle);
			//}

			//Seems to affect the default we start with: updateDefaultSelections();
			startMapAndLayerSelect();
			if (statistics) {
                //FIXME: Why doesn't this update?
				displayStatistics();
			}
			hideLoading();

			if (failsafe==true){
				//Don't allow selection of map styles (because they are unavailable
                if (document.getElementById("mapStyleList") != null) {
                    document.getElementById("mapStyleList").disabled=true;
                }
			
			}
			//ADD_DHTML("wmsLayerInfo");
		}

		//This function parses the map style information and adds an option in the select box for each one
		function prepareMapStyles() {
			var mapStyle,id,mapStyleName,wmsLayerString,defaultYesNo,defaultMapStyleString,valueString;
            if (document.getElementById("mapStyleList") != null) {
                for (i=0;i<theMapStyles.length;i++) {
                    mapStyle=theMapStyles[i];
                    id = mapStyle.getAttribute("id");
                    mapStyleName = mapStyle.getAttribute("name");
                    mapLog.debug("prepareMapStyles: mapStyle " + mapStyleName);
                    wmsLayerString = mapStyle.getAttribute("wmsLayerString");
                    defaultYesNo = mapStyle.getAttribute("default");
                    valueString = '"'+wmsLayerString+'"';
                    //alert(defaultYesNo);
                    if (defaultYesNo.toUpperCase()=="NO") {
                        document.getElementById("mapStyleList").options[i] = new Option(mapStyleName,valueString,false,false);
                    } else {
                        document.getElementById("mapStyleList").options[i] = new Option(mapStyleName,valueString,false,true);
                        defaultMapStyleString = valueString;
                    }//end if default startup option
                }//end for each map style in config file
                //eval('document.getElementById("mapStyleList").options[theMapStyles.length] = new Option("Map Style-------------------->>","999",false,false)');
                document.getElementById("mapStyleList").style.width="200px";;
            }
            mapLog.debug("perpareMapStyles: completed");
			return defaultMapStyleString;
		}//end prepare map styles

		//This function parses the map style information and adds an option in the select box for each one
		function prepareOtherMapStyles() {
			var mapStyle,id,mapStyleName,wmsLayerString,defaultYesNo,defaultMapStyleString,valueString;
            //if (document.navForm != null) {
                var elem = document.getElementById("otherWMSLayerStyle");
                for (i=0;i<theOtherMapStyles.length;i++) {
                    mapStyle=theOtherMapStyles[i];
                    id = mapStyle.getAttribute("id");
                    mapStyleName = mapStyle.getAttribute("name");
                    wmsLayerString = mapStyle.getAttribute("wmsLayerString");
                    defaultYesNo = mapStyle.getAttribute("default");
                    valueString = '"'+wmsLayerString+'"';
                    //alert(defaultYesNo);
                    if (defaultYesNo.toUpperCase()=="NO") {
                        if (elem != null) {
                            elem.options[i] = new Option(mapStyleName,valueString,false,false)
                            //eval('document.navForm.otherWMSLayerStyle.options[i] = new Option(mapStyleName,valueString,false,false)');
                        }
                    } else {
                        if (elem != null) {
                            elem.options[i] = new Option(mapStyleName,valueString,false,true);
                            //eval('document.navForm.otherWMSLayerStyle.options[i] = new Option(mapStyleName,valueString,false,true)');
                        }
                        defaultMapStyleString = valueString;
                    }//end if default startup option
                }//end for each map style in config file
                if (elem != null) {
                    elem.otherWMSLayerStyle.options[theOtherMapStyles.length] = new Option("Additional Layers----->>","999",false,false);
                    elem.otherWMSLayerStyle.style.width="200px";
                // eval('document.navForm.otherWMSLayerStyle.options[theOtherMapStyles.length] = new Option("GPS Velocities Test----->>","999",false,false)');
                // eval('document.navForm.otherWMSLayerStyle.style.width="200px";');
                }
            //}
			return defaultMapStyleString;
		}//end prepare map styles

		//This function parses the region information and adds an option in the select box for each one
		function prepareRegions(defaultRegionName) {
			var region,minx,maxx,miny,maxy,id,regionName,defaultRegionString,defaultYesNo;
			var new_option, extentRatio,diffRatio,yShift,yShiftHalf;

	      //Add the options from the config file to the select box
			for (i=0;i<theRegions.length;i++){
				region=theRegions[i];
				id = region.getAttribute("id");
				regionName = region.getAttribute("name");
				minx = parseFloat(region.getAttribute("minx"));
				maxx = parseFloat(region.getAttribute("maxx"));
				miny = parseFloat(region.getAttribute("miny"));
				maxy = parseFloat(region.getAttribute("maxy"));
                if (defaultRegionName) {
                    if (defaultRegionName == regionName) {
                        defaultYesNo = 'yes';
                    } else {
                        defaultYesNo = 'no';
                    }
                } else {
				    defaultYesNo = region.getAttribute("default");
                }
				//Correct for mistakes in aspect ratio (of jump to location map extents, vs, the map height/width)
				extentRatio = Math.round((Math.abs(maxy-miny))/(Math.abs(minx-maxx))*100000)/100000;
                
				diffRatio = Math.abs(extentRatio-mapSizeRatio);
				yShift = diffRatio*(Math.abs(minx-maxx));
				yShiftHalf=yShift/2;
				if (extentRatio<mapSizeRatio) {
					miny=miny-yShiftHalf;
					maxy=maxy+yShiftHalf;
				} else if (extentRatio>mapSizeRatio) {
					miny=miny+yShiftHalf;
					maxy=maxy-yShiftHalf;
				}

				valueString = '"'+id + "," + minx + "," + maxx + "," + miny + "," + maxy+'"';			
				//alert(valueString);
				//Check for the default region (it will be returned from this function, and executed on startup)
                if (document.getElementById("aoiList") != null) {
                    if (defaultYesNo.toUpperCase()=="NO") {
                        document.getElementById("aoiList").options[i] = new Option(regionName,valueString,false,false);
                    } else {
                        document.getElementById("aoiList").options[i] = new Option(regionName,valueString,false,true);
                        defaultRegionString = valueString;
                    }//end if default startup option
                } else {
                    //FIXME: Shouldn't this always be the case?
                    defaultRegionString = valueString;
                }
			}//end for each config layer
			//alert(theRegions.length);
			//eval('document.frmMapSelect.aoiList.options[theRegions.length] = new Option("Jump to Location---------->>","0",false,false)');
            if (document.getElementById("aoiList") != null) {
                document.getElementById("aoiList").options[theRegions.length] = new Option("-","0",false,false);
                document.getElementById("aoiList").style.width="200px";
            }
		return defaultRegionString;
		}

	//This function parses the point layers information and adds an option in the select box for each one
		function prepareLayers() {
			var layer,id,lyrName,defaultLayerString,defaultYesNo,unique;
			var new_option;

	      //Add the options from the config file to the select box
			for (i=0;i<theConfigLayers.length;i++){
				layer=theConfigLayers[i];
				id = layer.getAttribute("id");
				//This unique field, which is in the config file for each point layer, is the field name which contains the unique ids (using station ids,etc)
				unique = addNamespace(layer.getAttribute("uniquePtField"));
				lyrName = layer.getAttribute("name");
				lyrFieldName = layer.getAttribute("symbolFieldName");
				defaultYesNo = layer.getAttribute("default");
				valueString = '"'+id + "," + lyrFieldName + "," + unique+'"';			

				//Check for the default point layer (it will be returned from this function, and executed on startup
				if (defaultYesNo.toUpperCase()=="NO") {
                    if (theConfigLayers.length != 1) {
                        eval('document.navForm.lyrList.options[i] = new Option(lyrName,valueString,false,false)');
                    }
				} else {
                    if (theConfigLayers.length != 1) {
                        eval('document.navForm.lyrList.options[i] = new Option(lyrName,valueString,false,true)');
                    }
					uniquePtField = unique;
					defaultLayerString = valueString;
				}//end if default startup option
				//Populate the pointLayerArray with a placemark. If point layer was selected once, the content will be cached in this array
//**Erik's problem//This really is only useful when using a php file to dynamically query a database.
				//pointLayerArray[i]="nil";
			}//end for each config layer
			//eval('document.frmMapSelect.lyrList.options[theConfigLayers.length] = new Option("Station Layer---------------->>","999",false,false)');
            if (theConfigLayers.length > 1) {
                var lyrList = document.getElementById('lyrList');
                if (lyrList != null) {
                    lyrList.style.width="200px";
                }
                //eval('document.navForm.lyrList.style.width="200px";');
            }
            mapLog.debug("prepareLayers: " + defaultLayerString);
            return defaultLayerString;
		}

		//Display a small table showing how many of each point are on the map
		function displayStatistics() {			
			var statObj = document.getElementById("statistics");
			var htmlString="<table width=\"100%\" height=\"100%\" cellpadding=\"1\" cellspacing=\"1\" style=\"border-spacing:0;margin:0;border:2px solid #CC0000;background-color:black;color:white;\">";
			htmlString+="<tr align=\"center\"><td style=\"font-family:Arial;font-weight:bold;font-size:12px;\" colspan=\"3\" align=\"center\">Station Summary</td></tr>";
			htmlString+="<tr align=\"center\" style=\"font-family:Arial;font-size:10px;font-weight:bold;border:1px solid #CC0000;\"><td style=\"border:1px solid #CC0000;\">Station Type</td><td style=\"border:1px solid #CC0000;\"><b>Total</b></td></tr>";
			for (var symbolName in symbolNameArray) {
				htmlString+="<tr>";
				htmlString+="<td nowrap style=\"background-color:black;color:white;font-family:Arial;font-size:10px;border:1px solid #CC0000;\">"+symbolName+"</td>";
				htmlString+="<td align=\"right\" nowrap style=\"background-color:black;color:white;font-family:Arial;font-size:10px;border:1px solid #CC0000;\">"+CommaFormatted(symbolNameArray[symbolName])+"</td>";
				htmlString+="</tr>";
			}
			htmlString+="</table>";
			//alert(htmlString);
			//theWMSLayersInfo is the node list
			statObj.innerHTML=htmlString;
			statObj.style.visibility="visible";
		}

		//Display the WMS layer information, stored in the config file. Each map style has different wms layers, linked to the metadata by ID
		function displayWMSLayerInfo(mapStyleString) {
		//alert(mapStyleString);
			var mapStyles=mapStyleString.substring(1,mapStyleString.length-1).split(",");
			var id,lyrName,lyrNotes,layer;
			var layerInfoObj = document.getElementById("wmsLayerInfo");
			var htmlString="<table width=\"100%\" height=\"100%\" cellpadding=\"1\" cellspacing=\"1\" style=\"border-spacing:0;margin:0;border:2px solid black;background-color:#FFFFFF;color:black;\">";
			htmlString+="<tr align=\"center\"><td colspan=\"3\" align=\"center\"><b>Current WMS Layers</b></td></tr>";
			htmlString+="<tr align=\"center\" style=\"border:1px solid black;\"><td style=\"border:1px solid black;\"><b>ID</b></td><td style=\"border:1px solid black;\"><b>Name</b></td><td style=\"border:1px solid black;\"><b>Notes/Comments</b></td></tr>";
			for (i=0;i<mapStyles.length;i++) {
				//alert(id);
				for(j=0;j<theWMSLayersInfo.length;j++) {
					layer=theWMSLayersInfo[j];
					id = layer.getAttribute("id");
					lyrName = layer.getAttribute("name");
					//alert(lyrName);
					lyrNotes = layer.getAttribute("notes");
					if (parseInt(id)==mapStyles[i]) {
					//alert("match");
						htmlString+="<tr>";
						htmlString+="<td nowrap style=\"border:1px solid black;\">"+id+"</td>";
						htmlString+="<td nowrap style=\"border:1px solid black;\">"+lyrName+"</td>";
						htmlString+="<td nowrap style=\"border:1px solid black;\">"+lyrNotes+"</td>";
						htmlString+="</tr>";
					}
				}

			}
			htmlString+="</table>";
			//alert(htmlString);
			//theWMSLayersInfo is the node list
			layerInfoObj.innerHTML=htmlString;
			layerInfoObj.style.visibility="visible";
		}

		//This function handles the mouse move event over the map(getting coordinate display,etc)
		function mapMouseMove(e) {

			var posx = 0;
			var posy = 0;
			var mapObj=document.getElementById("mapContainer");
			var coordObj=document.getElementById("coord");
			var offsetX=findPosX(mapObj);
			var offsetY=findPosY(mapObj);
			if (!e) var e = window.event;
			if (e.pageX || e.pageY)
			{
				posx = e.pageX;
				posy = e.pageY;
			}
			else if (e.clientX || e.clientY)
			{
				posx = e.clientX + document.body.scrollLeft;
				posy = e.clientY + document.body.scrollTop;
			}

			//Use the offset position figures of the element and subtract them from the x,y locations, which are relative to the document
			//This gives us the true x,y location, inside the div element, of the mouse click (we need to use this to convert to map coords)
			var mouseX=posx-offsetX;
			var mouseY=posy-offsetY
			//window.status=mouseX + "," + mouseY;
			var mapXYString = getMapXY(mouseX,mouseY);
			mapXYArray=mapXYString.split(",");

			var mapX=parseFloat(mapXYArray[0]);
			var mapY=parseFloat(mapXYArray[1]);

			coordObj.innerHTML="X: " + mapX.toString().slice(0,7)+"<br>"+"Y: " + mapY.toString().slice(0,6);
			if (clusterSupress) {
				if (showAttTable(mapX,mapY)==true) {
					window.status="";
				} else {
					window.status="Stations are clustered at this scale. Click to zoom-in for more detail and station attribute table.";
				}
			}
			//window.status="X: " + mapX.toString().slice(0,7) + "  Y: " + mapY.toString().slice(0,6);
		}

		//Get the true x,y of the mouse click, inside the map div element, and then use it to ZOOM in and re-center the image
		function mapClick(e) {
			var posx = 0;
			var posy = 0;
			var mapObj=document.getElementById("mapContainer");
			//Get the offset position of the map div element, for use to figure out the real x,y location in the element
			var offsetX=findPosX(mapObj);
			var offsetY=findPosY(mapObj);
			//Get the mouse click x,y position RELATIVE TO THE DOCUMENT
			if (!e) var e = window.event;
			if (e.pageX || e.pageY)
			{
				posx = e.pageX;
				posy = e.pageY;
			}
			else if (e.clientX || e.clientY)
			{
				posx = e.clientX + document.body.scrollLeft;
				posy = e.clientY + document.body.scrollTop;
			}
			//alert(mapObj.style.top);
			//alert(posx);
			//alert(posy);

			//Use the offset position figures of the element and subtract them from the x,y locations, which are relative to the document
			//This gives us the true x,y location, inside the div element, of the mouse click (we need to use this to convert to map coords)
			var mouseX=posx-offsetX;
			var mouseY=posy-offsetY

			var mapXYString = getMapXY(mouseX,mouseY);
			mapXYArray=mapXYString.split(",");

			var mapX=parseFloat(mapXYArray[0]);
			var mapY=parseFloat(mapXYArray[1]);

			var halfX = parseFloat(Math.abs(mapXDistance/2));
			var halfY = parseFloat(Math.abs(mapYDistance/2));

			var tempRight,tempLeft,tempTop,tempBottom,mapString;
			var tempXDistance, tempYDistance,tempHalfX,tempHalfY;
			//If user wants to zoom and re-center
			if (mapClickAction==1) {

				//If the level chosen is different from the previously selected level, and the level requested is greater than 1 and less than the number of levels
				if (currentZoomLevel<numZoomLevels) {
					showLoading();
					currentZoomLevel=currentZoomLevel+1;
					//Reset the jump to location extent select box...because the user is using the zoom tools
					updateDefaultSelections();

					//alert("Before->\n\nleft: " + mapLeft + "\n"+"right: " + mapRight+ "\n"+ "bottom: " + mapBottom+ "\n" + "top: " + mapTop);
					//Change the zoom level's width distance here, hard-coded in an array (defined at the top of this file, or thereabouts...use the config file for defining these things, if possible)
					tempXDistance=zoomLevelDistanceArray[currentZoomLevel-1];

					tempYDistance=tempXDistance*mapSizeRatio;
					tempHalfX = parseFloat(Math.abs(tempXDistance/2));
					tempHalfY = parseFloat(Math.abs(tempYDistance/2));
					//alert(tempYDistance);
					//alert("center point: " + mapX + "," + mapY);
					tempLeft = mapX-(tempHalfX);
					tempRight = mapX+(tempHalfX);
					tempTop = mapY+(tempHalfY);
					tempBottom = mapY-(tempHalfY);		

					//Correct the extent, if the map extent requested is beyond the "flat map extent" i.e. past -180, and above 180, etc
					if (tempTop>90) {
						tempTop=90;
						tempBottom=90-tempYDistance;
					}
					if (tempBottom<-90) {
						tempBottom=-90;
						tempTop=-90 + tempYDistance;
					}
					if (tempLeft<-180) {
						tempLeft=-180;
						tempRight=-180+tempXDistance;
					}
					if (tempRight>180) {
						tempRight=180;
						tempLeft=180-tempXDistance;
					}
					//Update the zoom level, variable and icons
					if (navTools) {
						updateActiveZoom(currentZoomLevel);
					}
					//Put together the value string needed by the updateMap function
					mapString='"'+'999' + "," + tempLeft + "," + tempRight + "," + tempBottom + "," + tempTop+'"';
					//Call this function and redraw the map, and the currently selected point layer
					var timeout=setTimeout("updateMap("+mapString+",lastPointLayer,lastOtherMapStyle);",0);
					//updateMap(mapString,lastPointLayer,lastOtherMapStyle);

				}//end if check that the level chosen is different
			} else if (mapClickAction==2) {
				showLoading();
				//Reset the jump to location extent select box...because the user is using the zoom tools
				updateDefaultSelections();
				//Just recenter around clicked coordinate
				tempLeft = mapX-(halfX);
				tempRight = mapX+(halfX);
				tempTop = mapY+(halfY);
				tempBottom = mapY-(halfY);		

				//Correct the extent, if the map extent requested is beyond the "flat map extent" i.e. past -180, and above 180, etc
				if (tempTop>90) {
					tempTop=90;
					tempBottom=90-mapYDistance;
				}
				if (tempBottom<-90) {
					tempBottom=-90;
					tempTop=-90 + mapYDistance;
				}
				if (tempLeft<-180) {
					tempLeft=-180;
					tempRight=-180+mapXDistance;
				}
				if (tempRight>180) {
					tempRight=180;
					tempLeft=180-mapXDistance;
				}
				//Put together the value string needed by the updateMap function
				mapString='"'+'999' + "," + tempLeft + "," + tempRight + "," + tempBottom + "," + tempTop+'"';
				//Call this function and redraw the map, and the currently selected point layer
				var timeout=setTimeout("updateMap("+mapString+",lastPointLayer,lastOtherMapStyle);",0);

			}//if user wants to zoom AND re-center

		}//end function mapClick


		//These functions are a cross-browser way to help get the offset of the current object (useful for finding the position of an element)
		//Since our map windows, etc are relative, getting the position of the element requires this script (for non-ie browsers)
		function findPosX(obj)
		{
			var curleft = 0;
			if (obj.offsetParent)
			{
				while (obj.offsetParent)
				{
					curleft += obj.offsetLeft
					obj = obj.offsetParent;
				}
			}
			else if (obj.x)
				curleft += obj.x;
			return curleft;
		}

		function findPosY(obj)
		{
			var curtop = 0;
			if (obj.offsetParent)
			{
				while (obj.offsetParent)
				{
					curtop += obj.offsetTop
					obj = obj.offsetParent;
				}
			}
			else if (obj.y)
				curtop += obj.y;
			return curtop;
		}

		function zoomInOutMap(zoomType) {
			switch (zoomType) {
				//Zoom in
				case 1:
					zoomLevel(currentZoomLevel+1);
				break;

				//Zoom Out
				case 2:
					zoomLevel(currentZoomLevel-1);
				break;
				default:
			}
		}//end function zoom

		//Zoom to a specific level
		function zoomLevel(level) {
            if (level < 1 ) level = 1;
            else if (level > numZoomLevels) level = numZoomLevels;
			//If the level chosen is different from the previously selected level, and the level requested is greater than 1 and less than the number of levels
			if (level!=currentZoomLevel && level>=1 && level<=numZoomLevels) {
				var tempRight,tempLeft,tempTop,tempBottom,mapString;
				var tempXDistance, tempYDistance,tempHalfX,tempHalfY;
				var halfX = parseFloat(Math.abs(mapXDistance/2));
				var halfY = parseFloat(Math.abs(mapYDistance/2));
				//Get center point, so that we can zoom in or out from it
				var mapX = parseFloat(mapRight-halfX);
				var mapY = parseFloat(mapTop-halfY);

				showLoading();
				//Reset the jump to location extent select box...because the user is using the zoom tools
				updateDefaultSelections();

				//Change the zoom level's width distance here
				tempXDistance=zoomLevelDistanceArray[level-1];

				tempYDistance=tempXDistance*mapSizeRatio;
				tempHalfX = parseFloat(Math.abs(tempXDistance/2));
				tempHalfY = parseFloat(Math.abs(tempYDistance/2));
				//alert(tempYDistance);
				//alert("center point: " + mapX + "," + mapY);
				tempLeft = mapX-(tempHalfX);
				tempRight = mapX+(tempHalfX);
				tempTop = mapY+(tempHalfY);
				tempBottom = mapY-(tempHalfY);		

				//Correct the extent, if the map extent requested is beyond the "flat map extent" i.e. past -180, and above 180, etc
				if (tempTop>90) {
					tempTop=90;
					tempBottom=90-tempYDistance;
				}
				if (tempBottom<-90) {
					tempBottom=-90;
					tempTop=-90 + tempYDistance;
				}
				if (tempLeft<-180) {
					tempLeft=-180;
					tempRight=-180+tempXDistance;
				}
				if (tempRight>180) {
					tempRight=180;
					tempLeft=180-tempXDistance;
				}

				//Update the zoom level, variable and icons
				if (navTools) {
					updateActiveZoom(level);
				}
					//alert("After->\n\nleft: " + tempLeft + "\n"+"right: " + tempRight+ "\n"+ "bottom: " + tempBottom+ "\n" + "top: " + tempTop);
				//Put together the value string needed by the updateMap function
				mapString='"'+'999' + "," + tempLeft + "," + tempRight + "," + tempBottom + "," + tempTop+'"';
				//Call this function and redraw the map, and the currently selected point layer
				var timeout=setTimeout("updateMap("+mapString+",lastPointLayer,lastOtherMapStyle);",0);

			}//end if check that the level chosen is different
		}//end function zoom level

		//Pan the map.
		function panMap(panType) {
		showLoading();
		var tempRight,tempLeft,tempTop,tempBottom,mapString;
		var halfX = parseFloat(Math.abs(mapXDistance/2));
		var halfY = parseFloat(Math.abs(mapYDistance/2));
		//Update the jump to location select box, because we are
		updateDefaultSelections();
			switch (panType.toUpperCase()) {
				case "W":
					tempLeft=mapLeft-halfX;
					tempRight=mapRight-halfX;
					tempBottom=mapBottom;
					tempTop=mapTop;
				break;

				case "E":
					tempLeft=mapLeft+halfX;
					tempRight=mapRight+halfX;
					tempBottom=mapBottom;
					tempTop=mapTop;
				break;

				case "S":
					tempLeft=mapLeft;
					tempRight=mapRight;
					tempBottom=mapBottom-halfY;
					tempTop=mapTop-halfY;
				break;

				case "N":
					tempLeft=mapLeft;
					tempRight=mapRight;
					tempBottom=mapBottom+halfY;
					tempTop=mapTop+halfY;
				break;

				case "SW":
					tempLeft=mapLeft-halfX;
					tempRight=mapRight-halfX;
					tempBottom=mapBottom-halfY;
					tempTop=mapTop-halfY;
				break;

				case "SE":
					tempLeft=mapLeft+halfX;
					tempRight=mapRight+halfX;
					tempBottom=mapBottom-halfY;
					tempTop=mapTop-halfY;
				break;

				case "NW":
					tempLeft=mapLeft-halfX;
					tempRight=mapRight-halfX;
					tempBottom=mapBottom+halfY;
					tempTop=mapTop+halfY;
				break;

				case "NE":
					tempLeft=mapLeft+halfX;
					tempRight=mapRight+halfX;
					tempBottom=mapBottom+halfY;
					tempTop=mapTop+halfY;
				break;

				default:
			}//end switch
			//This code checks if the user is trying to pan outside the extents, if so...it stops panning. This can be improved at some point, so that you could
			//for example, pan west around the world (past -180) and it would be seamless. Just need to figure out the math to do so (use 360 degree model perhaps)
				if (tempTop>90) {
					tempTop=90;
					tempBottom=90-mapYDistance;
				}
				if (tempBottom<-90) {
					tempBottom=-90;
					tempTop=-90 + mapYDistance;
				}
				if (tempLeft<-180) {
					tempLeft=-180;
					tempRight=-180+mapXDistance;
				}
				if (tempRight>180) {
					tempRight=180;
					tempLeft=180-mapXDistance;
				}

				mapString='"'+'999' + "," + tempLeft + "," + tempRight + "," + tempBottom + "," + tempTop+'"';
				var timeout=setTimeout("updateMap("+mapString+",lastPointLayer,lastOtherMapStyle);",0);

		}//end pan

		//This function switches out the layers that we are selecting from the map service
		//This is called when a different map style is chosen.
		//In the future, if more than one map service is used, then this function should also switch the map service URL, rather than just the layers.
		function changeMap(wmsLayers) {
			if (wmsLayers!=999) {
                lastMapStyle=wmsLayers;
				var tempBefore,tempAfter,indexOfLayers;
				var wmsLayerSelection = wmsLayers.substring(1,wmsLayers.length-1);
				//Hide the hover dot and the attribute table
				hidePointInfo();
				//Clear the map image, and the map image, because we are about to redraw with new extents
				var indexOfLayers = wmsBaseString.toUpperCase().indexOf("LAYERS=");
				var tempBefore = wmsBaseString.substring(0,indexOfLayers+7);
				var tempAfter = wmsBaseString.substring(tempBefore.length,wmsBaseString.length);
				tempAfter = tempAfter.substring(tempAfter.indexOf("&"),tempAfter.length);

				wmsBaseString=tempBefore+wmsLayerSelection+tempAfter;

				var queryImgStr = wmsBaseString + mapLeft + "," + mapBottom + "," + mapRight + "," + mapTop + "&WIDTH=" + pixelWidth + "&HEIGHT=" + pixelHeight;
				wmsCanvas.clear();
		        wmsCanvas.drawImage(queryImgStr, 0,0, pixelWidth+"px", pixelHeight+"px");
		        wmsCanvas.setColor("#0000ff");
				//Draw map
				wmsCanvas.paint();
				if (wmsLayerInfo) {
					displayWMSLayerInfo(document.getElementById("mapStyleList").value);
				}
			}
            mapLog.debug("changeMap: completed");
		}
		//This function is called when changing the map styles for the "other" wms map service (this will need to be
		//revisited if we ever adopt a true "multi-service" approach
		function changeOtherMap(wmsLayers) {
            mapLog.debug("changeOtherMap: start " + wmsLayers);
			if (wmsLayers!=999) {
				if (wmsLayers!='""') {
					lastOtherMapStyle=wmsLayers;
					var tempBefore,tempAfter,indexOfLayers;
					var wmsLayerSelection = wmsLayers.substring(1,wmsLayers.length-1);
					//Hide the hover dot and the attribute table
					hidePointInfo();
					//Clear the map image, and the map image, because we are about to redraw with new extents
					otherWMSCanvas.clear();
					var indexOfLayers = otherWMSBaseString.toUpperCase().indexOf("LAYERS=");
					var tempBefore = otherWMSBaseString.substring(0,indexOfLayers+7);
					var tempAfter = otherWMSBaseString.substring(tempBefore.length,otherWMSBaseString.length);
					tempAfter = tempAfter.substring(tempAfter.indexOf("&"),tempAfter.length);

					otherWMSBaseString=tempBefore+wmsLayerSelection+tempAfter;

					var queryImgStr = otherWMSBaseString + mapLeft + "," + mapBottom + "," + mapRight + "," + mapTop + "&WIDTH=" + pixelWidth + "&HEIGHT=" + pixelHeight;
                    mapLog.debug("changeOtherMap: queryImgStr " + queryImgStr);
                    otherWMSCanvas.drawImage(queryImgStr, 0,0, pixelWidth+"px", pixelHeight+"px");
                    otherWMSCanvas.setColor("#0000ff");
					//Draw map
					otherWMSCanvas.paint();
				} else {
					otherWMSCanvas.clear();
				}
			}
		}

		//This function is called when the user changes the area of interest.
		function updateMap(wmsInfo,lyrInfo,otherWMSInfo) {
            mapLog.debug("updateMap: invoked with: " + wmsInfo + ":" + lyrInfo + ":" + otherWMSInfo);
            var wmsInfoArray;
            
            // Default to last layer used.
            if (lyrInfo == null) {
                lyrInfo = lastPointLayer;
            }
            if (wmsInfo == null) {
                wmsInfoArray = lastWMSInfo;
            } else {
                //Changing from string to array
                wmsInfoArray = checkWMSInfo(wmsInfo);
                mapLog.debug("updateMap: after check: " + wmsInfoArray[0] + "," + wmsInfoArray[1] + "," + wmsInfoArray[2] + "," + wmsInfoArray[3] + "," + wmsInfoArray[4] );
                if (wmsInfoArray) {
                    lastWMSInfo = wmsInfoArray;
                } else {
                    mapLog.error("updateMap: checkWMSInfo found invalid info: " + wmsInfo);
                }
            }
            mapLog.debug("updateMap: after updating defaults: " + wmsInfo + ":" + lyrInfo + ":" + otherWMSInfo);
			if (lyrInfo!=999) {
                lastPointLayer=lyrInfo;
				if (wmsInfoArray){
                    updateBBoxFromWMSString(wmsInfoArray);
                    //alert("About to update Layers");
                    //alert("updateMap: calling updateLayers with: " + lastPointLayer + ", 'same'");
					updateLayers(lastPointLayer,"same");
                    //alert("About to update WMSMap");
					updateWMSMap(wmsInfoArray);
				}
			}
            //mapLog.debug("updateMap: checking for other: " + theOtherWMSLayer + ":::" + otherWMSInfo);
			if (theOtherWMSLayer && otherWMSInfo) {
				lastOtherMapStyle=otherWMSInfo;
				changeOtherMap(otherWMSInfo);
			}
			//If there are any selected points, we must re-calc and re-draw them
			if (statSelArray.length!=0) {
				redrawSelection();
			}
			hideLoading();
		}
        
        function checkWMSInfo(wmsInfo) {
            // Make sure we have a least four ',' values
            if (wmsInfo == null || wmsInfo.length < 4) {
                return null;
            } else {
                // Looks like we sometimes have a quoted string and sometimes don't
                // could be careful and only pull " off of beginning and end of string, but for now we'll kill 'em all
                wmsInfo = wmsInfo.replace(/"/g, '');
                var values = wmsInfo.split(',');
                //alert(values.length + ": " + values[0]);
                //Public call to this function, use some arbitrary mode, not map clicking
                if (values.length==4) values.unshift(998);
                //alert(values.length + ":" + values[0]);
                if (values.length != 5) {
                    //alert("Incomplete WMS map information: " + wmsInfo);
                    mapLog.error("updateWMSMap: Incomplete WMS map information: " + wmsInfo);
                    return null;
                }
            }
            return values;
        }

        function updateBBoxFromWMSString(wmsInfo) {
            //Set global map extents
            mapLeft=parseFloat(wmsInfo[1]);
            mapRight=parseFloat(wmsInfo[2]);
            mapBottom=parseFloat(wmsInfo[3]);
            mapTop=parseFloat(wmsInfo[4]);            
            mapXDistance = Math.abs(mapRight-mapLeft);
            mapYDistance = Math.abs(mapTop-mapBottom);
            mapLog.debug("updateBBox: " + mapLeft + "," + mapRight + "," + mapBottom + "," + mapTop);
        }
        
		//This function finishes the assembly of the WMS request, and is used to get a map (for a given area)
		function updateWMSMap(wmsInfo) {
            //This if statement checks to see if the user is using the dynamic zoom tools (999) rather than the jump to location
            //The for loop is checking the array which has the xDistance values for each zoom level...if the "jump to region" is in between a zoom level
            //then...the next zoomed out level, is made to be the active one.
            //if (navToolsYesNo.toUpperCase()=="YES") {
                mapLog.debug("updateWMSMap: zoom level based on " + wmsInfo[0] + ":" + wmsInfo[0].type);
                if (!(wmsInfo[0]==999 || wmsInfo[0]=='999') ) {
                    var i;
                    for (i=0;i<zoomLevelDistanceArray.length;i++) {
                        //if (i<zoomLevelDistanceArray.length-1){
                            if (mapXDistance<=parseFloat(zoomLevelDistanceArray[i]) && mapXDistance>parseFloat(zoomLevelDistanceArray[i+1])) {
                                var level=i+1;
                                mapLog.debug("updateWMSMap: decided to update zoom to level " + level);
                                updateActiveZoom(level);
                                break;
                            }
                        //}
                    }
                    //Ensure that the zoom levels are not messed up, if some areas of  interest are outside the minimum and maximum zoom distances
                    //If the width of the map is larger than the largest zoom  level...change the current level to the largest 
                    if (mapXDistance >  parseFloat(zoomLevelDistanceArray[0])) {
                        var level = 1;
                        updateActiveZoom(level);
                    }
                    //Same thing as above, but for areas of interest smaller than the smallest zoom level (further zoomed in) 
                    if (mapXDistance <  parseFloat(zoomLevelDistanceArray[zoomLevelDistanceArray.length-1])) {
                        var level = zoomLevelDistanceArray.length;
                        updateActiveZoom(level);
                    }
                }
            //}
            //Hide the hover dot and the attribute table
            hidePointInfo();
            //Clear the map image, and the map image, because we are about to redraw with new extents
            wmsCanvas.clear();
            var queryImgStr = wmsBaseString + wmsInfo[1] + "," + wmsInfo[3] + "," + wmsInfo[2] + "," + wmsInfo[4] + "&WIDTH=" + pixelWidth + "&HEIGHT=" + pixelHeight;
            mapLog.debug("updateWMSMap: " + queryImgStr);
            wmsCanvas.drawImage(queryImgStr, 0,0, pixelWidth+"px", pixelHeight+"px");
            wmsCanvas.setColor("#0000ff");
            //Draw map
            wmsCanvas.paint();
            updateBBoxCallbacks();
            mapLog.debug("updateWMSMap: completed.");
		}

        //This function is called when the lyr selection changes (or if someone changes the region of interest). The points for the current selection are drawn
        function updateLayers(lyrInfo,newOrSame) {
            mapLog.debug("updateLayers: started with lyrInfo: "  + lyrInfo);
			if (lyrInfo != null && lyrInfo!=999) {
                if (newOrSame.toUpperCase()=="NEW") {
                    clearSelection();
                    updateSelectionCallbacks("'Clear'");
                }
				lastPointLayer=lyrInfo;
				lyrInfo=lyrInfo.substring(1,lyrInfo.length-1);
				var values = lyrInfo.split(',');
				 if (values.length != 3) {
	                //alert("Incomplete point layer information: " + lyrInfo);
                    mapLog.error("Incomplete point layer information: " + lyrInfo);
	            } else {
					//Clear the points before re-drawing
					hidePointInfo();
					pointCanvas.clear();
					//Check to see if we have already downloaded point content for this layer yet, if not...must do a query
					//Keep in mind that if a static XML file is being used (for testing), the entire file is downloaded, and stored
					//in each array element in pointLayerArray...which isn't the most efficient (but static files are for testing currently)
					//if (pointLayerArray[values[0]]=="nil") {
					if (queryType=="DYNAMIC") {
						//If the point GML for the current selection has not been downloaded yet, then query the points and store content in an array
						//Currently, we are not caching server-side requests for GML...so this should be called everytime (unless using a static file)
                        mapLog.debug("updateLayers: dynamic");
						queryPoints(values[0],values[1]);
			            //drawPoints(values[0]);
					} else if (queryType=="STATIC" && staticPointXML=="") {
                        mapLog.debug("updateLayers: static1");
						//The point GML is available client-side...just draw it
						queryPoints(values[0],values[1]);
						//drawPoints(values[0]);
					} else if (queryType=="STATIC" && staticPointXML!="") {
                        mapLog.debug("updateLayers: static2");
						drawPoints(values[0]);
					}
					//Set the global variable which holds the field name which holds the info that uniquely identifies each point (station id)
					uniquePtField = values[2];
	            }
			}
            mapLog.debug("updateLayers: completed");
        }

        //This function is called when a new point layer has been selected for the first time, the database must be queried for the points
		//So, an AJAX query is performed, and passes the coordinate extents which we want to use to query the points. If we are using a static
		//XML file for testing, the entire file is downloaded and stored in each array element (not the greatest efficiency, but only meant for
		//testing at this point)
		function queryPoints(lyrID,symbolFieldName) {
            mapLog.debug("queryPoints: started");
			//var xmlhttp;
            var queryURL;
			//xmlhttp = new createXMLHttpRequest();
            //if (xmlhttp) {
            //    alert("already processing.");
            //    return;
            //}
            var xmlReq = new XMLRequestWrapper();
            xmlReq.createXMLHttpRequest();
            xmlhttp = xmlReq.getReq();

			//Check the file extension of the URL in the config file...if it is pointing to a server side file (to perform a query), then
			//attach the selected bounding extents. If not, just download the static file.
			if (queryType=="DYNAMIC") {
            mapLog.debug("queryPoints: 1");
				//Loading dynamically generated GML/XML from a server (need bounding extents to filter points on server)
                queryURL = gmlPointContentURL;
                if (queryURL.indexOf("?") < 0) {
                    queryURL += "?";
                } else {
                    queryURL += "&";
                }
				queryURL += "FIELD="+symbolFieldName+"&LEFT="+mapLeft+"&RIGHT="+mapRight+"&BOTTOM="+mapBottom+"&TOP="+mapTop+"&geom="+POINT_TAG;
                mapLog.debug("queryPoints: " + queryURL);
			} else if (queryType=="STATIC") {
				//Loading an XML file directly (testing)
				queryURL = gmlPointContentURL;
			}
            
            //queryURL += "&disableGzip=true";
            
			//alert(queryURL);
            xmlhttpLyrId = lyrID;
            xmlhttp.onreadystatechange = queryPointsCallback;
            // Still more race conditions to resolve before making async...
            //alert(queryURL);
			xmlhttp.open("GET", queryURL, false);
			//xmlhttp.onreadystatechange = function() {
			//	if http request for each file
			//}//end http function
			//alert("here");
			xmlhttp.send(null);
            queryPointsProcess();
            showLoading();
		}

        function queryPointsCallback() {
            if (xmlhttp.readyState == 4) {
                hideLoading();
                if (xmlhttp.status == 200) { 
                    mapLog.debug("queryPointsCallback: Successful response");
                    queryPointsProcess();
                } else { 
                    // Warn the user? 
                    mapLog.error("queryPointsCallback: Failure status code " + filterXMLReq.status);
                    //alert("problem receiving data"); 
                    return;
                    //xmlhttp = null;
                }
            }
            return;
        }
        
        function queryPointsProcess() {
            //alert(lyrID);
			//Re-populate the "dynamic" variable, if we are using a server-side language, or just store the static file's content in the "static" varaible once
            if (queryType=="DYNAMIC") {
                var contentType = xmlhttp.getResponseHeader("Content-Type");
                mapLog.debug("queryPointsCallback: " + contentType);
                if (contentType!=null && "TEXT/XML"==contentType.substring(0,8).toUpperCase()) {
                    featureManager = new GMLFeatureManager(symbolFieldNameArray[xmlhttpLyrId].toUpperCase(), 
                                                           addNamespace(POINT_TAG).toUpperCase());
                    featureManager.setXMLDoc(xmlhttp.responseXML);
                } else if (contentType!=null && ("TEXT/PLAIN"==contentType.substring(0,10).toUpperCase() || "TEXT/CSV"==contentType.substring(0,8).toUpperCase())) {
                    featureManager = new CSVFeatureManager(symbolFieldNameArray[xmlhttpLyrId].toUpperCase(), 
                                                           addNamespace(POINT_TAG).toUpperCase());
                    featureManager.processCSV(xmlhttp.responseText);
                } else {
                    alert("Unsupported content-type: " + contentType);
                }
                //dynamicPointXML=xmlhttp.responseText;
				//alert(dynamicPointXML);
			} else if (queryType=="STATIC") {
				staticPointXML=xmlhttp.responseText;
				//alert(staticPointXML);
			}
            
            updateQueryPointsCallbacks();
            
            mapLog.debug("queryPointsCallback: completed processing points");
            drawPoints(xmlhttpLyrId);
            //xmlhttp = null;
            //alert("Added points after query");
        }
        
	    //This function is called to redraw the client-side points. Features are parsed from the downloaded content, and the coordinates and symbology
		//are determined for each point...and then the coordinates are translated into pixel coords, and drawn on map using WZ graphics library
        function drawPoints(layerID) {
			var feature,geometry;
			var problem=false;
			var imageName;
			var lat,lon,featureSymbolVal,xyArray,symbolVal,imageSymbol,pointFID;
            var inXCoord, inYCoord, endXCoordPos, imagePixelX, imagePixelY, imagePixelString, endXPos;
            var xDistance, yDistance;
			var discrepWarn = false;
//            var d1,d2,d3,d4,d5,d6=0,d7=0,d8=0;

            var validFeatureCount=0;
            
            // (Re)Initialize the global associative array of features.
            //features = new Array();
            
            //Figure out the width and height of the map, in map coordinates
            xDistance = Math.abs(mapRight-mapLeft);
            yDistance = Math.abs(mapTop-mapBottom);
            //------------------------------------------------
	        //Get the XML for the current point layer, which is stored in the pointLayerArray
//d1 = Date();
            // var featureManager = new GMLFeatureManager(symbolFieldNameArray[layerID].toUpperCase(), 
                                                       // addNamespace(POINT_TAG).toUpperCase());
            //theFeatures = featureManager.getFeatures();
//d2 = Date();
			//theGeometrys = featureManager.getGeometries();
//d3 = Date();
            if (currentZoomLevel>3) {
                scalePtSize=parseInt(ptSize)+2;
            } else if (currentZoomLevel<=1) {
                scalePtSize=parseInt(ptSize)-2;
            } else {
                scalePtSize=ptSize;
            }
            
            var symbols = symbolLookup[layerID];
			//Loop through each point record to symbolize and draw
			//for (i=0;i<theFeatures.length;i++){
            mapLog.debug("drawPoints: about to get points");
            var points = featureManager.getPoints();
            mapLog.debug("drawPoints: begin draw");

            pointCanvas.clear();

            for (var pointID in points) {
				feature=points[pointID];
                if (feature != null) {
    
    //d5 = new Date().getTime();
    //d6 += new Date().getTime() - d5;
                    //Make sure that a symbol exists
                    featureSymbolVal = feature.getSymbol();
                    if (featureSymbolVal!="" && featureSymbolVal!=null) {
                        problem=false;
                    } else {
                        problem=true;
                    }
                    //alert("pointId: " + pointID + ":" + problem + ":" + featureSymbolVal);
    //d5 = new Date().getTime();
    
    //d7 += new Date().getTime() - d5;                 
                    //Draw each image on the canvas (map) if there is no problem (i.e. symbology exists). Don't paint the layer until all points are drawn
                    //The points are painted back in updateWMSImgAndPoints after everything is done downloading,etc.
                    if (problem==false) {
                        lat = feature.getY();
                        lon = feature.getX();
                        imageName = getImageByName(symbols, feature.getSymbol());
                        //Convert these extracted map coordinates into Pixel Coordinates
                        imagePixelString="";
                        imagePixelString = toPixelXY(lon,lat,pixelWidth,pixelHeight,xDistance,yDistance,mapLeft,mapBottom);
                        endXPos = imagePixelString.indexOf(",");
                        imagePixelX = imagePixelString.substring(0,endXPos);
                        imagePixelY = imagePixelString.substring(endXPos + 1, imagePixelString.length);
                        var displayImagePixelX = imagePixelX - 2;
                        var displayImagePixelY = imagePixelY - 2;

                        var mouseOver = '';
                        if (stnInfoDisplay) {                            
                            //onmouseout="hidePointInfo();"  causes 'flashing' ui
                            mouseOver = 'onmouseover="showPointInfo(' + displayImagePixelX +','+displayImagePixelY+','+lon+','+lat+','+pointID+')"';
                        }
                        //alert("Setting mouseover" + mouseOver);
                        pointCanvas.drawImage(iPath + imageName, displayImagePixelX, displayImagePixelY, scalePtSize+"px", scalePtSize+"px",mouseOver);
                        validFeatureCount++;
                    } else {
                        if ( !discrepWarn && showDiscrepWarn ) {
                            discrepWarn = true;
                            alert("Problem occurred on or around point number: "+i+"\n\n"+"There is a discrepancy between the symbol information in the config file, and the values in the XML file.\n\nCheck your XML file, and the config file, to ensure that things are matched up correctly.");
                        }
                    }
                }  // else { alert("Invalid point! " + pointID);
            }
//d4 = Date();
        mapLog.debug("drawPoints: valid feature count after drawing " + validFeatureCount);
        updateFeatureCountCallbacks(validFeatureCount);
//alert("1: " + d1 + "\n2: " + d2 + "\n3: " + d3 + "\n4: " + d4 + "\n6: " + d6 + "\n7: " + d7 + "\n" + Date());
        //Draw the points
        pointCanvas.paint();
	}//end function

	//Store the total number of each type of symbol in an array
	function statsCount(symbolVal) {
        var count = symbolNameArray[symbolVal];
        if (count == null) {
            count = 0;
        }
        count++;
        symbolNameArray[symbolVal] = count;
	}

	//This function checks to see if a point is in a defined cluster (in the config file)
	function showAttTable(mapX,mapY) {
		if (clusterSupress) { 
			var tempArray = new Array();
				if (eval('clusZoomArray'+currentZoomLevel+'.length')!=0) {
					for (i=0;i<eval('clusZoomArray'+currentZoomLevel+'.length');i++) {
						tempArray.length=0;
						tempArray = eval('clusZoomArray'+currentZoomLevel+'[i].split(",");');

						if (mapX>=tempArray[2] && mapX<=tempArray[3] &&mapY>=tempArray[4] && mapY<=tempArray[5]) {
							//Put Tooltip here, and do something with mapclick event
							//isInCluster=true;
							hidePointInfo();
							window.status="Stations are clustered at this scale. Click to zoom-in for more detail and station attribute table.";
							return false;
							//break;
						} else {
							return true;
						}
					}
				}
		} else {
		    if (showStationInfo==1) {
			    window.status="";
				return true;
			} else if (showStationInfo==2) {
			    window.status="";
				return false;
			}
			//return false;
		}
	}
	//This function changes the attribute table HTML on mouseover of a specific point
	//This saves having to draw a separate HTML table for each point.
	//Currently, the for loop, which searches for the current mouseover information, is not searching all records, but rather,
	//it is using an incremental unique ID, which each feature has. the first feature in the returned content has an id of 1 or 0 or whatever
	//as long as the next feature has an id which is one more than the feature before. This is done so that when we have hundreds or thousands of points,
	//The for loop can start in the general vicinity of the feature list, because it is using its point id.
	//In the for loop below, it is starting to search the "theFeatures" list, 5 records below the input pointID and 5 records above...so that if for some reason
	//the point ids in the gml content were not perfectly aligned with the loop index in the feature list..it should be with 5 records below and above.
	//*This kind of smart looping results in a snappy quick mouseover effect, and is recommended that we use it.
	//The point ids are extracted from the "fid" attribute for each point (they should always start at zero...but just in case they don't..use the smart loop
	function showPointInfo(xPos,yPos,lon,lat,inPointID,hover) {
        if (showPointInfoFunctionList!="" && showPointInfoFunctionList!=null) {
		    var funcArray = showPointInfoFunctionList.split(",");
		    for (i=0;i<funcArray.length;i++) {
			    var theFunc=funcArray[i]+'(' + xPos + ',' + yPos + ',' + lon + ',' + lat + ',' + inPointID + ',' + hover + ');';
			    setTimeout(theFunc, 0);
            }
		}
    }
    
    function showPointInfoHover(xPos,yPos,lon,lat,inPointID,hover) {
        //alert("showPointInfo" + inPointID);
		if (showAttTable(lon,lat)==true) {
            //var newX, newY;
			//Take off onclick event when we are on a point (if we click, we just want the hyperlink)
			if (navTools) {
				document.getElementById("mapContainer").onclick=null;
			}
			var httpLink,feature,featurePtID,hoverSize

            feature=featureManager.getPoint(inPointID);

            // FIXME: In general we shouldn't hardcode the element name
            var pointDiv = document.getElementById("map");
            xPos += Number(pointDiv.style.left.slice(0,3));
            yPos += Number(pointDiv.style.top.slice(0,3));

            tempHTML=feature.getHTML(xPos, yPos);

            //Change the main table's content, and make the table visible
            var table = document.getElementById("attTableMain");
            table.innerHTML=tempHTML;
            //DYNAMIC POSITIONING of the table. ...uses the pixelHeight/pixelWidth of the map (in the config file)
            //The attribute table should "try" to stay in plain view, and not be cutoff by the edge of the map (this could use improvement)
            //If the map window is going to be somewhat small, perhaps a locked area for the attribute table is appropriate. If the window is 800x800
            //for example...then it should be fine. If the window is 450x450..then sometimes, points in the middle can be a problem.
            //Do X first
            // if (xPos<pixelWidth*0.45) {
                // newX = (xPos + 10) + "px";
            // } else if (xPos>pixelWidth*0.45 && xPos<pixelWidth*0.55) {
                // newX = (xPos/2) + "px";
            // } else if (xPos>pixelWidth*0.55 && xPos<pixelWidth) {
                // newX = (xPos - (feature.getMaxFieldNameLength()+feature.getMaxFieldLength()+130)) + "px";
            // }
// 
            // if (yPos<pixelHeight*0.40) {
                // newY = (yPos + 10) + "px";
            // } else if (yPos>pixelHeight*0.40 && yPos<pixelHeight*0.55) {
                // newY = (yPos/2.2) + "px";
            // } else if (yPos>pixelHeight*0.55 && yPos<pixelHeight) {
                // newY = (yPos - (feature.getFieldCount()*11.5)) + "px";
            // }
// 
            // if ((xPos>pixelWidth*0.45 && xPos<pixelWidth*0.55) && (yPos>pixelHeight*0.40 && yPos<pixelHeight*0.55)) {
                // if (xPos<pixelWidth*0.50) newX = (xPos + 10) + "px";
                // if (xPos>pixelWidth*0.50) newX = (xPos - (feature.getMaxFieldNameLength()+feature.getMaxFieldLength()+130)) + "px";
            // }
            
            
            var estimatedWindowWidth = (feature.getMaxFieldNameLength()+feature.getMaxFieldLength()) * 6.2;
            if (feature._imageLink) {
                estimatedWindowWidth += 150;
                //alert(estimatedWindowWidth + '=' + feature.getMaxFieldNameLength() + '+' + feature.getMaxFieldLength() + '*' + '6.2' + '+' + '150');
            } else {
                //alert(estimatedWindowWidth + '=' + feature.getMaxFieldNameLength() + '+' + feature.getMaxFieldLength() + '*' + '6.2');
            }
            var estimatedWindowHeight = feature.getFieldCount()*11.5;
            //alert(estimatedWindowHeight + '=' + feature.getFieldCount() + '*' + '11.5');
            
            var left, top, bottom;
            
            // If the cursor is on the left, but the image on the right
            if (xPos < pixelWidth / 2) {
                //alert('Left');
                left = (pixelWidth - estimatedWindowWidth) + 'px';
                if (left < 0) {
                    left = 0;
                }
            } else {
                //alert('Right');
                left = '0px';
            }
            
            // 0,0         w,0
            // 
            // ---Y---
            // _____  |
            // |    | X
            // h,0__| |    w,h

            //alert('' + xPos + '-' + estimatedWindowWidth + '=' + (xPos - estimatedWindowWidth) +  ":::" + pixelHeight + '-' + estimatedWindowHeight + '-' + yPos + '=' + (pixelHeight - estimatedWindowHeight - yPos) );
            if (xPos - estimatedWindowWidth > 25 || (pixelHeight - estimatedWindowHeight) - yPos > 25) {
                left = '0px';
            } else {
                left = (pixelWidth - estimatedWindowWidth);
                // Could of course put these in the first if clause above
                if (left < 0) {
                    left = 0;
                } else if (left < xPos + 100 ) {  // Random fudge factor for tables that might not be full width due to say text wrap
                    //alert('adjusting: ' + left +  '<' + xPos + '+' + estimatedWindowWidth);
                    left = 0;
                }
                left += 'px';
            }

             
            
            // For now we always put it in the bottom half of the screen.
            bottom = '0px';
            // if (yPos < pixelHeight / 2) {
                // //alert('top');
                // bottom = '0px';
            // } else {
                // //alert('bottom');
                // bottom = '0px';
                // //top = '0px';
            // }
            //alert(left + ', ' + top + '(' + bottom + ')' + table.style.left + "," + table.style.top + "," + table.style.right + " : " + estimatedWindowWidth + "::" + estimatedWindowHeight);
            table.style.left = left;
            if (bottom) {
                table.style.bottom = bottom;
            } else {
                table.style.top = top;
            }
            //alert(xPos + "(" + newX + ") " + yPos + "(" + newY + ") - " + feature.getMaxFieldNameLength() + ":" + feature.getMaxFieldLength() + ":" + feature.getFieldCount());
            
            //newX = (newX > 0 ? newX : 0);
            //newY = (newY > 0 ? newY : 0);
            
            // table.style.left = newX;
            // table.style.top = newY;
            //table.style.left = 0;
            //table.style.bottom = 0;

            //Now that table is all taken care of, plot the mouse over point layer-------------------------------------------------------------------------------
            var theX=xPos;
            var theY=yPos;

            //Creating offset values for the mouseover dot, centering the dot over the symbols
            var yPointOffset = theY-1;
            var xPointOffset = theX -2;

            //Get the hoverDot object (div)
            if (hover == null || hover) {
                var hoverDot = document.getElementById("hoverDot");
    
                hoverDot.style.left=xPointOffset + "px";
                hoverDot.style.top=yPointOffset + "px";
                hoverDot.style.width="20px";
                //Make the hover dot a little bit bigger than the regular dots (this is also a good thing because the crosshair image is 11x11 pixels)
                hoverSize=parseInt(scalePtSize)+3;
    
                //Change the innerHTML of the hoverDot object, based on which symbol the user has their mouse over...use the httpLink defined above
                hoverDot.innerHTML = '<img style="border:0;width:'+hoverSize+'px;height:'+hoverSize+'px;" src="' + iPath + 'crosshair_red.png">';
                
                httpLink = feature.getLink();
                if (httpLink != null && httpLink.length) {
                    if(stationClickMode==1) {
                        hoverDot.innerHTML='<a target="_blank" href="'+httpLink+'">' + hoverDot.innerHTML + '</a>';
                    } else if (stationClickMode==2) {
                        hoverDot.innerHTML='<a href="'+httpLink+'">' + hoverDot.innerHTML + '</a>';
                    }
                }
                hoverDot.style.visibility="visible";
            }
            //MAKE BOTH layers visible at pretty much the same time
            table.style.visibility="visible";
		} else {
		//PUT A TOOLTIP IF NEEDED
			//window.status="Stations are clustered at this scale. Click to zoom-in for more detail and station attribute table.";
		}
	}//end showPointInfo

	function hidePointInfo() {
        if (hidePointInfoFunctionList!="" && hidePointInfoFunctionList!=null) {
		    var funcArray = hidePointInfoFunctionList.split(",");
		    for (i=0;i<funcArray.length;i++) {
			    var theFunc=funcArray[i]+'();';
			    setTimeout(theFunc, 0);
            }
		}
    }
    
	//This function hides the mouse over information and hover dot
	function hidePointInfoHover() {
        //Put the onclick event back on the mapContainer (for dynamic zooming)
        if (navTools) {
            document.getElementById("mapContainer").onclick=mapClick;
        }
        //Hide information when the user moves mouse off of point
		document.getElementById("hoverDot").style.visibility='hidden';
		document.getElementById("attTableMain").innerHTML="";
		document.getElementById("attTableMain").style.visibility='hidden';	
	}

	//Show a legend for the current point layer
	function openLegend() {
		hidePointInfo();
		if (navTools) {
			document.getElementById("mapContainer").onclick=null;
		}
		var currentLayerInfo = lastPointLayer;
		var currentLayerID = currentLayerInfo.substring(1,currentLayerInfo.indexOf(","));
		var htmlString = createLegend(currentLayerID);
		document.getElementById("legend").innerHTML=htmlString;
		document.getElementById("legend").style.visibility="visible";
	}
	//create the legend using the symbol information from the config file
	function createLegend(lyrID) {
		var ptLayerInfo = ptLegendArray[lyrID];
		var symbolName,imageName;
		//var imageNameArray = new Array();
		var lyrName = ptLegendArray[lyrID].substring(0,ptLegendArray[lyrID].indexOf(","));
		//alert(ptLegendArray[lyrID]);
		var symbolInfo = ptLegendArray[lyrID].substring(ptLegendArray[lyrID].indexOf(",")+1,ptLegendArray[lyrID].length);
		var tempSymbolValsArray = symbolInfo.split(",");

		var htmlString = "<table onmouseover=\"closeLegend();\" cellpadding=\"1\" cellspacing=\"1\" style=\"border-spacing:0;border-collapse:separate;margin:0;border:2px solid #CC0000;background-color:#000000;color:#FFFFFF;\">";
		htmlString+="<tr><td style=\"font-family:Arial;font-size:12px;border-bottom:2px solid #CC0000\" align=\"center\" colspan=\"2\">"+lyrName+"</td></tr>";
		for (j=0;j<tempSymbolValsArray.length;j++) {

			htmlString+="<tr>";
		    symbolName = tempSymbolValsArray[j].substring(0,tempSymbolValsArray[j].indexOf('='));
			imageName = tempSymbolValsArray[j].substring(tempSymbolValsArray[j].indexOf('=')+1,tempSymbolValsArray[j].length);

			htmlString+="<td style=\"border-left:1px solid #CC0000;border-right:1px solid #CC0000;border-bottom:1px solid #CC0000;\" align=\"center\"><img width=\""+scalePtSize+"\" height=\""+scalePtSize+"\" src=\""+iPath+imageName+"\"></td>";
			htmlString+="<td style=\"font-family:Arial;font-size:10px;border-right:1px solid #CC0000;border-bottom:1px solid #CC0000;\">"+symbolName+"</td></tr>";
		}//end for each symbol pair
		htmlString+="</table>";

		return htmlString;
	}

	function closeLegend() {
		if (navTools) {
			document.getElementById("mapContainer").onclick=mapClick;
		}
		document.getElementById("legend").style.visibility="hidden";
	}

	//Position HTML elements, based on sizes in config, and other sizes of items in html file
	function pageElementLayout() {
        var frmContainer = document.getElementById("frmContainer");
        if (frmContainer != null) {
            frmContainer.style.width=pixelWidth+"px";
        }

        document.getElementById("mapComponent").height=pixelHeight+"px";
        document.getElementById("mapComponent").width=pixelWidth+"px";	
        if (statistics) {
            document.getElementById("statistics").style.left=(parseInt(pixelWidth)+65)+"px";
        }
        if (wmsLayerInfo) {
            document.getElementById("wmsLayerInfo").style.left=(parseInt(pixelWidth)+75)+"px";
        }
        document.getElementById("navigation").style.width=pixelWidth+"px";
        document.getElementById("navigation").style.height=pixelHeight+"px";
    
        if (navTools) {
            //Position the zoomin/zoomout and calculate the position of the zoom levels
            var zoomInOutImgHeight = document.getElementById("zoomin").height;
            document.getElementById("zoomin").style.top=-6+"px";
            document.getElementById("zoomin").style.left=(parseInt(pixelWidth)+20)+"px";
            document.getElementById("zoomout").style.top=(parseInt(pixelHeight)-zoomInOutImgHeight)+"px";
            document.getElementById("zoomout").style.left=(parseInt(pixelWidth)+20)+"px";
            //Calculating the position of the zoom level images, using the height of the zoomin/zoomout images (assumed to be the same size)
            var tempImg,dividedDistance,runningTotal;
            var imgLvlHeight = document.getElementById("zoomlevel1").height;
            var start=-6+zoomInOutImgHeight;
            var end=pixelHeight-zoomInOutImgHeight;
            var layoutZoomLvlDist = end-start;
            var imgPixelHeightTotal=numZoomLevels*imgLvlHeight;
            var spaceInBetweenTotal=layoutZoomLvlDist-imgPixelHeightTotal;
            var spaceInBetweenEach=parseInt(spaceInBetweenTotal/(numZoomLevels+1));
            //Update the running total, each time you place a zoom level image (from bottom to top)
            runningTotal=end;
            for (i=1;i<numZoomLevels+1;i++) {
                tempImg = document.getElementById("zoomlevel"+i);
                tempImg.style.left=(parseInt(pixelWidth)+20)+"px";
                //--------------
                tempImg.style.top=parseInt(runningTotal-spaceInBetweenEach-imgLvlHeight)+"px";
                runningTotal=runningTotal-spaceInBetweenEach-imgLvlHeight;
            }
            //Set the default zoom level, based on the currentZoomLevel variable
            document.getElementById("zoomlevel"+currentZoomLevel).src=iPath+"zoomlevel_on.png";
    
            document.getElementById("west").style.top=((pixelHeight/2)-25)+"px";
            document.getElementById("west").style.left=-8+"px";
            document.getElementById("east").style.top=((pixelHeight/2)-25)+"px";
            document.getElementById("east").style.left=pixelWidth+"px";
            document.getElementById("north").style.top=-8+"px";
            document.getElementById("north").style.left=((pixelWidth/2)-25)+"px";
            document.getElementById("south").style.top=pixelHeight+"px";
            document.getElementById("south").style.left=((pixelWidth/2)-25)+"px";
            document.getElementById("southwest").style.top=(pixelHeight-42)+"px";
            document.getElementById("southwest").style.left=-8+"px";
            document.getElementById("southeast").style.top=(pixelHeight-42)+"px";
            document.getElementById("southeast").style.left=(pixelWidth-42)+"px";
            document.getElementById("northwest").style.top=-8+"px";
            document.getElementById("northwest").style.left=-8+"px";
            document.getElementById("northeast").style.top=-8+"px";
            document.getElementById("northeast").style.left=(pixelWidth-42)+"px";
        }
        document.getElementById("mapContainer").style.width=pixelWidth+"px";
        document.getElementById("mapContainer").style.height=pixelHeight+"px";
        document.getElementById("map").style.width=pixelWidth+"px";
        document.getElementById("map").style.height=pixelHeight+"px";
        document.getElementById("layers").style.width=pixelWidth+"px";
        document.getElementById("layers").style.height=pixelHeight+"px";
        if (legend) {
        document.getElementById("legend").style.top="23px";
        }
        if (coordDisplay) {
        document.getElementById("coordDisplay").style.top=(0)+"px";
            if (window.ActiveXObject) {
                document.getElementById("coordDisplay").style.left=(pixelWidth-48)+"px"
            } else {
                document.getElementById("coordDisplay").style.left=(pixelWidth-50)+"px"
            }
        }
        //Position the loading sign
        var ldHt = document.getElementById("loadingImg").height;
        var ldWd = document.getElementById("loadingImg").width;
        document.getElementById("loading").style.top = parseInt((pixelHeight/2)-(ldHt/2))+"px";
        document.getElementById("loading").style.left = parseInt((pixelWidth/2)-(ldWd/2))+"px";
    
        //Setup the station interactivity correctly (check if checked,etc)
        //If station interactivity is checked...then show the station click mode radio buttons (http,select)
        var statInterObj = (document.getElementById("statInterObj")) ? document.getElementById("statInterObj") : null;
        if (statInterObj!=null) {
            if (getCheckedValue(document.stationSelectionForm.stationInter)==1) setStatInteractive(1);
        }

	}//end function layout positioning

	function mouseOutLevel(level) {
		if (level!=currentZoomLevel) {
			document.getElementById("zoomlevel"+level).src=iPath+"zoomlevel.png";
		}
	}

	function updateActiveZoom(level) {
        mapLog.debug("updateActiveZoom: " + level);
        //if (level < 1) level = 1;
        //if (level > numZoomLevels+1) level = numZoomLevels+1;
		currentZoomLevel=level;
        mapLog.debug("updateActiveZoom: set currentZoomLevel to " + currentZoomLevel);
		//Turn all zoom levels to be unselected
		if (navTools) {
			for (i=1;i<numZoomLevels+1;i++) {
				var img = document.getElementById("zoomlevel"+i);
				if (img.src=iPath+"zoomlevel_on.png") img.src=iPath+"zoomlevel.png";
			}
			//Make the selected level known with the mouseover image
            var zoomToSelect = document.getElementById("zoomlevel"+level);
            if (zoomToSelect != null) {
                zoomToSelect.src=iPath+"zoomlevel_on.png";
            }
		}
	}
	//reset default area selection
	function updateDefaultSelections() {
        if (document.getElementById("aoiList") != null) {
            document.getElementById("aoiList").options[theRegions.length].selected=true;
        }
		//eval('document.getElementById(\"aoiList\").options[theRegions.length].selected=true;');
	}

	function startMapAndLayerSelect() {
        var elem;
        if (theConfigLayers.length != 1) {
            //eval('document.frmMapSelect.lyrList.options[theConfigLayers.length].selected=true;');
            elem = document.getElementById("lyrList");
            if (elem != null) {
                elem.options[theConfigLayers.length].selected=true;
                //eval('document.getElementById(\"navForm\").lyrList.options;');
                //eval('theConfigLayers.length;');
            }
        }
        //eval('document.frmMapSelect.mapStyleList.options[theMapStyles.length].selected=true;');
        //eval('document.navForm.mapStyleList.options;');
        //eval('theMapStyles.length;');
		if (theOtherWMSLayer) {
            elem = document.getElementById("otherWMSLayerStyle");
            if (elem != null) {
                elem.options[theOtherMapStyles.length].selected=true;
            }
			//eval('document.getElementById(\"navForm\").otherWMSLayerStyle.options[theOtherMapStyles.length].selected=true;');
		}
	}

	function showLoading() {
        document.getElementById("loading").style.visibility="visible";
	}

	function hideLoading() {
		document.getElementById("loading").style.visibility="hidden";
	}

//This function is called when the user chooses to interact (or not) with the points
function setStatInteractive(choice) {
    var clickModeObj = (document.getElementById("statClickModeObj")) ? document.getElementById("statClickModeObj") : null;
	//If choice is On
	if (choice==1) {
		showStationInfo=1;
		//If the point selection functionality is chosen in the config file, the user get's more than just an attribute table
		if (ptSelection) {
			if (clickModeObj!=null) clickModeObj.style.visibility="visible";
			if (stationClickMode==2) document.getElementById("selOptions").style.visibility="visible";
		}
	//If choice is Off
	} else if (choice==2) {
		showStationInfo=2;
		if (ptSelection) {
			if (clickModeObj!=null) clickModeObj.style.visibility="hidden";
			document.getElementById("selOptions").style.visibility="hidden";
		}		
	}
}
//This function happens when the user chooses which station interactivity mode they want (http, or selections)
function setStatClickMode(mode) {
    stationClickMode=mode;
    //If HTTP was chosen, clear the selection
	if (mode==1) {
		clearSelection();
		updateSelectionCallbacks("'Clear'");
		document.getElementById("selOptions").style.visibility="hidden";
	} else if (mode==2) {
		document.getElementById("selOptions").style.visibility="visible";
	}
}

//This function is called when the user interactively clicks to select a point, or deselect a point
function modifySelection(stationID,xPos,yPos,lon,lat) {
	var dupIndex,theID,temp;
	var dupYesNo="NO";
	var size = parseInt(scalePtSize)+2;
    var modType = "'Unknown'";
    var i;
	//Check to see if the station is already selected
	for (i=0;i<statSelArray.length;i++) {
		//if (stationID.toUpperCase()==statSelArray[i].toUpperCase()) {
		if (stationID==statSelArray[i]) {
			dupYesNo="YES";
			dupIndex=i;
			break;
		}
	}
	//If it is not a duplicate
	if (dupYesNo=="NO") {
		//Draw the selection image
        if (stnInfoDisplay) {
            selectionCanvas.drawImage(iPath + selImage, xPos, yPos, size+"px", size+"px",'onmouseout="hidePointInfo();" onmouseover="showPointInfo('+xPos+','+yPos+','+lon+','+lat+','+stationID+',false);" onclick="removeSelItem(\''+stationID + '\');"',stationID);
        } else {
            selectionCanvas.drawImage(iPath + selImage, xPos, yPos, size+"px", size+"px",'',stationID);
        }
		//selectionCanvas.drawImage(iPath + selImage, xPos, yPos, size+"px", size+"px",'onmouseout="hidePointInfo();" onmouseover="selMouseOver(this,\''+stationID+'\','+xPos+','+yPos+');" onclick="removeSelItem(\''+eval("stationID")+'\');"',stationID);
		//Add ids to first array, and the corresponding feature XML to the other array (same element number)
		statSelArray[statSelArray.length]=stationID;
		//statSelXMLArray[statSelXMLArray.length]=selFeatureXML;
        modType = "'Add', " + stationID + "," + i;
	} else {
		//It's a duplicate, so de-select the point
		//use dupindex? instead of looping through station id?
		removeSelItem(stationID);
        modType = "'Remove', " + stationID + "," + i;
	}
	//Update the selection graphics
	paintSelection();
	//Update the external components (whatever functions are registered in the config file
	updateSelectionCallbacks(modType);

}

//De-select a point
function removeSelItem(theStationID) {
	var selParent = document.getElementById('stationSelection');
	var byeDiv = document.getElementById(theStationID);
    var modType = "'Remove', " + theStationID;
	if (byeDiv){
		//remove the point object
		selParent.removeChild(byeDiv);
		//update the selection graphics to see it disappear
		paintSelection();

	}
	//Remove the id and the xml from the arrays
	for (i=0;i<statSelArray.length;i++) {
		if (theStationID.toUpperCase()==statSelArray[i].toUpperCase()) {
			statSelArray.splice(i,1);
			//statSelXMLArray.splice(i,1);
            modType += ", " + i;
			break;
		}
	}
	//Update the external components (whatever functions are registered in the config file
	updateSelectionCallbacks(modType);

}

//Clear the selected points,and arrays
function clearSelection() {
	selectionCanvas.clear();
	statSelArray.length=0;
	//statSelXMLArray.length=0;
}

//Redraw the selection when the map extent changes
function redrawSelection() {
	var imagePixelString,endXPos,imagePixelX,imagePixelY,geometryList,statID;
	var xyString,lat,lon,xyArray;
	var myParser = new DOMParser();
	//var featureDoc = Sarissa.getDomDocument();
	//Make the selection point a couple of pixels bigger
	var size = parseInt(scalePtSize)+2;
	//Before re-drawing, clear the canvas, and start fresh
	selectionCanvas.clear();

	//For each selected record
	for (i=0;i<statSelArray.length;i++) {
		statID = statSelArray[i];

        var feature = featureManager.getPoint(statID);
        if (feature != null) {
            lat = feature.getY();
            lon = feature.getX();
            //Convert these extracted map coordinates into Pixel Coordinates
            imagePixelString="";
            imagePixelString = toPixelXY(lon,lat,pixelWidth,pixelHeight,mapXDistance,mapYDistance,mapLeft,mapBottom);
            endXPos = imagePixelString.indexOf(",");
            imagePixelX = imagePixelString.substring(0,endXPos);
            imagePixelY = imagePixelString.substring(endXPos + 1, imagePixelString.length);
            var displayImagePixelX = imagePixelX - 2;
            var displayImagePixelY = imagePixelY - 2;

            //Draw the selection image for the current point
            if (stnInfoDisplay) {
                selectionCanvas.drawImage(iPath + selImage, imagePixelX-3, imagePixelY-2, size+"px", size+"px",'onmouseout="hidePointInfo();" onmouseover="showPointInfo('+displayImagePixelX+','+displayImagePixelY+','+lon+','+lat+','+statID+',false);" onclick="removeSelItem(\''+statID+'\');"',statID);
            } else {
                selectionCanvas.drawImage(iPath + selImage, imagePixelX-3, imagePixelY-2, size+"px", size+"px",'',statID);
            }
        } else {
            mapLog.debug("redrawSelection: Unable to find feature with Id " + statID + " for redraw, skipping.");
        }
		//selectionCanvas.drawImage(iPath + selImage, imagePixelX-3, imagePixelY-2, size+"px", size+"px",'onmouseout="hidePointInfo();" onmouseover="selMouseOver(this,\''+statID+'\','+ eval("imagePixelX-2")+','+eval("imagePixelY-2")+');" onclick="removeSelItem(\''+eval("statID")+'\');"',statID);
		// pointCanvas.drawImage(iPath + imageName, imagePixelX-2, imagePixelY-2, scalePtSize+"px", scalePtSize+"px",'onmouseover="showPointInfo(' + eval("imagePixelX-2")+','+eval("imagePixelY-2")+','+lon+','+lat+','+pointID+')"');
	}
	//After all selection images have been added to the canvas, paint the canvas
	paintSelection();

}
//Paint the selection layer
function paintSelection() {
	selectionCanvas.paint();
}

function updateMapSize(sizeString) {
    var newSize=sizeString.split(',');
    mapLog.debug('updateMapSize: new w:' + newSize[0] + ' h:' + newSize[1]);
    pixelWidth=newSize[0];
    pixelHeight=newSize[1];

    // Steps below definitely do more than is needed.
    // Need to refactor functions and more careful determination
    // of what is needed.

    wmsBaseString = prepareWMSUrl();

    wmsCanvas.clear();
    pointCanvas.clear();
    wmsCanvas = new jsGraphics("map");
    pointCanvas = new jsGraphics("layers");

    
    pageElementLayout();

    // delayForPreload is a bit heavy handed, in addition to doing too much
    // work, it resets current layer and aoi
    // FIXME:  Loses default region.
    delayForPreload();
    mapLog.debug('updateMapSize:w:' + pixelWidth + ' h:' + pixelHeight + " ratio:" + mapSizeRatio);
}

//Clear the points, and update external components
function clickClear() {
	clearSelection();
	updateSelectionCallbacks("'Clear'");
}

//This function uses the point node list for the current view, and populates the selection arrays (both the id array and the matching xml array)
function selectAllInView() {
	clearSelection();
	var xyString="";
	var xyArray;
	var featureAttributeNodes,geometry,lat,lon;
	var imagePixelString,endXPos,imagePixelX,imagePixelY,geometryList,statID;
    var feature;
	//For all of the features in the current view (this is a global that is updated when moving around the map)
    var points = featureManager.getPoints();
    for (var featureId in points) {
        feature = points[featureId];
        statSelArray[statSelArray.length]=featureId;
	}//end for each feature in view
	redrawSelection();
	//Update the external components to show that all records are now selected (for the current view)
	updateSelectionCallbacks("'AddAll'");
}//end function

//This function uses the registered list (csv) of call back functions, and is called whenever the mapping component changes the selection set
//All external components which are added to the application, and who want to keep aprised of the current selection, need to be
//added to the list in the config file. These functions, which should just access the current selection array, and update the display of the components,
//should be reachable by this updateSelectionCallbacks function.
function updateSelectionCallbacks(callbackArgs) {
    mapLog.debug("updateSelectionCallbacks: " + callbackArgs);
	if (updateSelectionFunctionList!="" && updateSelectionFunctionList!=null) {
        mapLog.debug("updateSelectionCallbacks: " + updateSelectionFunctionList);
		var funcArray = updateSelectionFunctionList.split(",");
        //alert(updateSelectionFunctionList + ":" + funcArray.length);
		for (var i=0;i<funcArray.length;i++) {
            //alert(i + " : " + funcArray[i]);
			var theFunc=funcArray[i]+"(" + callbackArgs + ");";
            //alert("About to eval: " + theFunc);
            mapLog.debug("updateSelectionCallbacks: about to invoke - " + theFunc);
			//eval(theFunc);
            setTimeout(theFunc, 0);
		}
	}
}

function updateBBoxCallbacks() {
	if (updateBBoxFunctionList!="" && updateBBoxFunctionList!=null) {
		var funcArray = updateBBoxFunctionList.split(",");
		for (i=0;i<funcArray.length;i++) {
			var theFunc=funcArray[i]+"(" + mapLeft + "," + mapTop + "," + mapRight + "," + mapBottom + ");";
			setTimeout(theFunc, 0);
		}
	}
}

function updateFeatureCountCallbacks(featureCount) {
	if (updateFeatureCountFunctionList!="" && updateFeatureCountFunctionList!=null) {
		var funcArray = updateFeatureCountFunctionList.split(",");
		for (i=0;i<funcArray.length;i++) {
			var theFunc=funcArray[i]+"(" + featureCount + ");";
			setTimeout(theFunc, 0);
		}
	}
}

function updateQueryPointsCallbacks() {
	if (afterQueryPointsFunctionList!="" && afterQueryPointsFunctionList!=null) {
		var funcArray = afterQueryPointsFunctionList.split(",");
		for (i=0;i<funcArray.length;i++) {
			var theFunc=funcArray[i]+"();";
			setTimeout(theFunc, 0);
		}
	}
}

//This function is now called in a loop, passing a wms url and map service name, checking to see if it is available
function checkServiceAvailability(url,svc) {
		var urlRequestStr="";
		var service;
		var urlParams;
		var serviceAvailable=true;
		
		urlRequestStr = url;
		service = svc;
		urlParams = "&SERVICENAME="+service+"&VERSION="+wmsVersion+"&REQUEST=GetCapabilities&SERVICE=WMS";					
		
		try {
		
			serviceAvailable = isAvailable(urlRequestStr,urlParams);
			//alert(serviceAvailable);
			if (serviceAvailable==true) {
				return true;
			} else {
				return false;
			}
			
		}
		catch (ex1) {
            mapLog.debug("checkServiceAvailability: " + ex1);
			//the server is not reachable...which means the service is no good either
			serviceAvailable=false;
			
		}
}
//get capabilities
function isAvailable(url,urlParams) {
	var xmlhttp;
	var proxyURL = "checkWMS.php?url=" + url+urlParams;
	//alert(proxyURL);
	//xmlhttp = new createXMLHttpRequest();
    var xmlReq = new XMLRequestWrapper();
    xmlReq.createXMLHttpRequest();
    xmlhttp = xmlReq.getReq();
	xmlhttp.open("GET", proxyURL, false);
	xmlhttp.send(null);
	
	var available = xmlhttp.responseText;
	mapLog.debug("isAvailable: " + available + " (" + proxyURL + ")");
	if (available=="true") {
		return true;
	} else {
		return false;
	}
}

function getImageByName(symbols, symbolName) {
    var imageName = symbols[symbolName];
    if (imageName == null || imageName == "") {
        var symbolArray = symbolName.split('_');
        imageName = symbols[symbolArray[0] + '_other'];
        if (imageName == null || imageName == "") {
            imageName='unknown.png';
        }
        //alert("Could not find image for: " + symbolName);
    }
    return imageName;
}

