// googleMap.js: Javascript code to control the display and control the google map.

// This stand-alone function is called by the Google Map marker code:
function selectedIndex(marker,b)
{
  return 1000;
}

var googleMap = new Object();
googleMap.loaded = false;
googleMap.map = null;  // A reference to the actual Google Map object
googleMap.defaultZoom = 7;
googleMap.defaultLat = 42.0;
googleMap.defaultLon = -111.0;
googleMap.markers = new Array();
googleMap.markerCount = 0;
googleMap.selectedMarker = null;
googleMap.selectedMarkerIndex = -1;
googleMap.polygon = null;
googleMap.geoXml; 
googleMap.toggleState = 0;

//googleMap.legendName = ''; //used to determin the current legend being shown

// icon colors (only using first three now - white, red, green); first color is when multiple types are clustered
colorTypes = ["#ffffff", "#ff0000", "#00ff00", "#ff00ff", "#ff0066", "#ff6600", "#6600ff"];

// Create colored icons at three different sizes
googleMap.baseIcons = new Array();
for(var i=0; i<colorTypes.length; i++){
	googleMap.baseIcons[i] = new Array();
	// small icons
	googleMap.baseIcons[i][0]= MapIconMaker.createFlatIcon(
		{width:10, height:10, primaryColor:colorTypes[i], label:"", labelSize:0, labelColor:"#000000"});
	// medium icons
	googleMap.baseIcons[i][1]= MapIconMaker.createFlatIcon(
		{width:12, height:12, primaryColor:colorTypes[i], label:"", labelSize:0, labelColor:"#000000"});
	// large icons
	googleMap.baseIcons[i][2]= MapIconMaker.createFlatIcon(
		{width:14, height:14, primaryColor:colorTypes[i], label:"", labelSize:0, labelColor:"#000000"});
}

// reference to actual GMap2 object to make prototyping easier
var map = null;

googleMap.loadMap = function()
{
	if(GBrowserIsCompatible()) {

	//map = new GMap2(document.getElementById("map_canvas"), {backgroundColor: "#ffffff"} );
	map = new GMap2(document.getElementById("mapview"), {backgroundColor: "#ffffff"} );
	
	var mapTypes = map.getMapTypes();
	for (var  n = mapTypes.length-1 ; n >= 0 ; n-- )
		if(mapTypes[n]==G_HYBRID_MAP)
			map.removeMapType(mapTypes[n]);
	
	//add counties KML
	//map.geoXml = new GGeoXml("http://www.solventtech.com/OregonCountyLines.kml");
	map.geoXml = new GGeoXml("http://www.oregonflora.org/includes/atlas_v2/oregonCountyLines.kml");
    //map.addOverlay(map.geoXml);

	
	//used to show/hide the legend 
	map.legend= null;

	googleMap.legendName = 'me';
	
	
	//toggle counties function
	map.toggleCounties = function()
{
	if (this.toggleState == 1) {
		this.removeOverlay(this.geoXml);
		this.toggleState = 0;
	} else {
		this.addOverlay(this.geoXml);
		this.toggleState = 1;
	}
};

	
	// center on Oregon
	map.setCenter(new GLatLng(44.084133,-120.4), googleMap.defaultZoom);
	
	// path to the images folder
	//$ATLAS_IMAGES_PATH="http://floradev.nacse.org/newatlas/images/";
	$ATLAS_IMAGES_PATH="http://www.oregonflora.org/atlas_images/";
	// place the ground overlays to the map
	var oregonBounds = new GLatLngBounds(new GLatLng(41.999679,-124.509023), new GLatLng(46.297358,-116.461258));
	
	var oregon = new GGroundOverlay($ATLAS_IMAGES_PATH+"oregon.png", oregonBounds); oregon.hide(); map.addOverlay(oregon);
	var percip = new GGroundOverlay($ATLAS_IMAGES_PATH+"percip.png", oregonBounds); percip.hide(); map.addOverlay(percip);
	var ecoReg = new GGroundOverlay($ATLAS_IMAGES_PATH+"ecoReg.png", oregonBounds); ecoReg.hide(); map.addOverlay(ecoReg);
	var oregonCounty = new GGroundOverlay($ATLAS_IMAGES_PATH+"oregonBounds.png", oregonBounds); oregonCounty.hide(); map.addOverlay(oregonCounty);
	var oregonCountyBgd = new GGroundOverlay($ATLAS_IMAGES_PATH+"oregonBoundsBgd.png", oregonBounds);oregonCountyBgd.hide();map.addOverlay(oregonCountyBgd);
	
	// Change the ground overlay when the map changes
	GEvent.addListener(map, "maptypechanged", function(o,n) {map.updateGroundOverlay();});
	GEvent.addListener(map, "dragend", function() {
      googleMap.showMarkers();
    });
    GEvent.addListener(map, "zoomend", function() {
      googleMap.showMarkers();
    });
    
    //clears selected marker when clicking the map screen
    GEvent.addListener(map, "click", function() {
      if(googleMap.lastMarkerIndex) googleMap.lastMarkerIndex=0;
      else googleMap.removeSelectedMarker();
    });    
    
    
	// add custom prototype to the GMap2 construct
	GMap2.prototype.updateGroundOverlay = function() {
		var currentMapType = map.getCurrentMapType();
		
		//hide all ground overlays
		oregon.hide(); percip.hide(); ecoReg.hide(); oregonCounty.hide(); oregonCountyBgd.hide();
		
		//show selected ground overlay
		switch(currentMapType.getName()){
			case "Oregon Relief Map": oregon.show(); break;
			case "Oregon Percipitation": percip.show(); break;
			case "Oregon Eco Regions": ecoReg.show(); break;
			case "Zip Code Borders": oregonCounty.show(); break;
			case "Zip Borders on White": oregonCountyBgd.show(); break;		
		}	
	}
	
	// define specific map layers
	/*var NormalLayer = G_NORMAL_MAP.getTileLayers()[0];
	var SatelliteLayer = G_SATELLITE_MAP.getTileLayers()[0];
	var LabelsLayer = G_HYBRID_MAP.getTileLayers()[0];
	var PhysicalLayer = G_PHYSICAL_MAP.getTileLayers()[0];
	
	var satProj = G_SATELLITE_MAP.getProjection();	
	var normalProj = G_NORMAL_MAP.getProjection();	
	*/
	// define copyright information
	var cRight = new GCopyrightCollection('OFP');
	var copyright = new GCopyright(1, new GLatLngBounds(new GLatLng(-90, -180), new GLatLng(90, 180)), 0, "OregonFloraProject©2009");
	cRight.addCopyright(copyright);
	
	// percipitation custom map
	var copyCollection3 = new GCopyrightCollection('');
	var copyright3 = new GCopyright(1, new GLatLngBounds(new GLatLng(-90, -180), new GLatLng(90, 180)), 0, "<span style='font-style:italic;' title='Map reproduced from the Atlas of Oregon, second edition, (University of Oregon Press, 2001)'>Precipitation Map</span> ©2009 A. Jon Kimerling");
	copyCollection3.addCopyright(copyright3);
	
	var tilelayers3 = [new GTileLayer(copyCollection3, 3, 12)];
	//alert(CustomGetTileUrl(map.getCenter(), map.getZoom()));
	//alert(map.getBounds());
	tilelayers3[0].getTileUrl = function (a,b) {
		var f = "http://www.oregonflora.org/maptiles/precipOregon/precipOregon"+b+"_"+a.x+"_"+a.y+".gif";
		//var f = "C:/Users/hoffmanc/Documents/OFP/summer2009/GoogleMap/KitsTestMap"+b+"_"+a.x+"_"+a.y+".gif";
		return f;
	};
	
	var custommap3 = new GMapType(tilelayers3, new GMercatorProjection(19), "Precipitation", {errorMessage:""});
	map.addMapType(custommap3);
	
	// landform custom map
	var copyCollection = new GCopyrightCollection('');
	var copyright2 = new GCopyright(1, new GLatLngBounds(new GLatLng(-90, -180), new GLatLng(90, 180)), 0, "<span style='font-style:italic;' title='Map reproduced from the Atlas of Oregon, second edition, (University of Oregon Press, 2001)'>Landform Map</span> ©2009 A. Jon Kimerling");
	copyCollection.addCopyright(copyright2);
	
	var tilelayers = [new GTileLayer(copyCollection, 3, 12)];
	//alert(CustomGetTileUrl(map.getCenter(), map.getZoom()));
	//alert(map.getBounds());
	tilelayers[0].getTileUrl = function (a,b) {
		var f = "http://www.oregonflora.org/maptiles/landformOregon/landformOregon"+b+"_"+a.x+"_"+a.y+".gif";
		//var f = "C:/Users/hoffmanc/Documents/OFP/summer2009/GoogleMap/KitsTestMap"+b+"_"+a.x+"_"+a.y+".gif";
		return f;
	};
	
	var custommap = new GMapType(tilelayers, new GMercatorProjection(19), "Landform", {errorMessage:""});
	map.addMapType(custommap);
	map.setMapType(custommap);
	
	// add the custom maps
	function addCustomMaps() {
		// -------- Layers -------------
		var zipTileLayer = createCustomLayer(5,9,'zip');
		var zipTileLayer2 = createCustomLayer(5,9,'zip2');
		var oregonTileLayer = createCustomLayer(5,9,'oregon');
		var percipTileLayer = createCustomLayer(10,12,'percip');
		var ecoRegionTileLayer = createCustomLayer(10,12,'ecoReg');

		//var KitsTestMap = createCustomLayer(10,12,'ecoReg');
	
		/* -------- Maps -------------
		var layers = [oregonTileLayer]; addCustomMapType("Oregon Relief Map",layers,9,3);
		var layers = [percipTileLayer]; addCustomMapType("Oregon precipitation",layers,9,3);
		var layers = [percipTileLayer]; addCustomMapType("Oregon Eco Regions",layers,9,3);
		var layers = [zipTileLayer]; addCustomMapType("Zip Code Borders",layers,9,3);
		var layers = [zipTileLayer2]; addCustomMapType("Zip Borders on White",layers,9,3);
		var layers = [NormalLayer,oregonTileLayer]; addCustomMapType("Oregon Relief Map",layers,9,3);
		var layers = [NormalLayer,percipTileLayer]; addCustomMapType("Oregon precipitation",layers,9,3);
		var layers = [NormalLayer,percipTileLayer]; addCustomMapType("Oregon Eco Regions",layers,9,3);
		var layers = [NormalLayer,zipTileLayer]; addCustomMapType("Zip Code Borders",layers,9,3);
		var layers = [NormalLayer,zipTileLayer2]; addCustomMapType("Zip Borders on White",layers,9,3);
		*/
		//var layers = [KitsTestMap]; addCustomMapType("Kit's Test Map",layers,10,3);
		
	}
	
	function createCustomLayer(minRes,maxRes,theme) {
		var newLayer = new GTileLayer(cRight,minRes,maxRes);
		newLayer.getTileUrl = function (a,b) {return 'http://'+window.location.host+'/cgi-bin/getTiles.pl?t='+theme+'&x='+a.x+'&y='+a.y+'&z='+b;};
		newLayer.getOpacity = function () {return 0.5};
		return newLayer;
	}
	
	function addCustomMapType(mName,layers,maxRes,minRes) {
		//var cMap = new GMapType(layers, normalProj, mName, {maxResolution:maxRes, minResolution:minRes, errorMessage:''}); 
		//map.addMapType(cMap);
	}
	//-------------- End Custom maps  ---------------------------
	
	
	//-------------- Start OFP Logo  ---------------------------
	function MyPane() {}
	MyPane.prototype = new GControl;
		
	MyPane.prototype.initialize = function(map) {
	  var me = this;
	  me.panel = document.createElement("img");
	  me.panel.setAttribute('class', 'ps');
	  me.panel.setAttribute('src', 'images/ofp_logo.png');
	  me.panel.setAttribute('alt', 'copyright Oregon Flora Project');
	  me.panel.setAttribute('width', '124');
	  me.panel.setAttribute('height', '25');
	  map.getContainer().appendChild(me.panel);
	 	
	  return me.panel;
	};
	
	MyPane.prototype.getDefaultPosition = function() {
	  return new GControlPosition(
		  G_ANCHOR_BOTTOM_RIGHT, new GSize(4, 20));
		  //Should be _ and not &#95;
	};
	
	MyPane.prototype.getPanel = function() {
	  return me.panel;
	}
	map.addControl(new MyPane());
	
	//-------------- End OFP Logo  ---------------------------
	
	//-------------- Start Legend  ---------------------------
	function Legend() {}
	Legend.prototype = new GControl;
		
	Legend.prototype.initialize = function(map) {
	  var me = this;
	  
	  me.panel = document.createElement("img");
	  me.panel.setAttribute('class', 'ph');
	  me.panel.setAttribute('src', 'atlas_images/percipLegendSmall.png');
	  me.panel.setAttribute('alt', 'copyright Oregon Flora Project');
	  me.panel.setAttribute('width', '146');
	  me.panel.setAttribute('height', '394');
	  map.getContainer().appendChild(me.panel);
	 	
	  return me.panel;
	};
	
	Legend.prototype.getDefaultPosition = function() {
	  return new GControlPosition(
		  G_ANCHOR_BOTTOM_RIGHT, new GSize(4, 57));
		  //Should be _ and not &#95;
	};
	
	Legend.prototype.getPanel = function() {
	  return me.panel;
	}
	
	Legend.prototype.hide = function () {
		this.panel.style.display = 'none';
	}
	
	Legend.prototype.show = function () {
		this.panel.style.display = 'block';
	}

	map.legend = new Legend();
	map.addControl(map.legend);
	
	//-------------- End Legend  ---------------------------
	
	
	// add Oregon county lines kml file
	//var oregonCountyLines = new GGeoXml('maptiles/oregonCountyLines.kml');
	//oregonCountyLines.show();
	//map.addOverlay(new GGeoXml('maptiles/oregonCountyLines.kml'));
	
	var geoXml = new GGeoXml("http://picasaweb.google.com/data/feed/base/user/picasateam/albumid/5080138758345059425?kind=photo&alt=kml&hl=en_US");
    map.addOverlay(geoXml);
	
	// add zoom controls
	map.addControl(new GLargeMapControl());
	map.addControl(new GScaleControl());
	
	//addCustomMaps();
	
	

	mapTypeControl = new MMapTypeControl({background:'#860055',direction:'V'});
	map.addControl(mapTypeControl);
	// overwrite the getMinimumResolution() and getMaximumResolution() methods for each map type
	var mapTypes = map.getMapTypes();
	for (var i=0; i<mapTypes.length; i++) {
		mapTypes[i].getMinimumResolution = function() {return 7;}
		mapTypes[i].getMaximumResolution = function() {return 12;}
	}
	if(map.isLoaded()) {
		googleMap.loaded = true;	
		googleMap.map = map;
		//googleMap.legendName = googleMap.map.legendName;
	}
}
	
};

googleMap.clearMap = function()
{
	window.alert('implement googleMap.clearMap()');
	/*
  if(!this.loaded) return;

  var htmltag = document.getElementById("mapheadertext");
  if("innerHTML" in htmltag) htmltag.innerHTML = "Specimen map";
  else if("firstChild" in htmltag && "data" in htmltag.firstChild) htmltag.firstChild.data = "Specimen map";

  this.clearMarkers();
  */
};

googleMap.clearMarkers = function()
{
  if(!this.loaded) return;

  this.markers = new Array();
  this.markerCount = 0;
  this.selectedMarker = null;
  this.selectedMarkerIndex = -1;
  this.polygon = null;
  this.map.clearOverlays();
  if(cache.polygonVertexCount > 0) this.showPolygon();
};

googleMap.showMarkers = function()
{
  if(!cache.mapPointsLoaded || !this.loaded) return false;
  if(cache.clusterCount == 0) return false;

  //Figure out which zoom list to use:
  var newZoom = this.map.getZoom();
  var newList = newZoom;
  if(newList < cache.minZoom) newList = cache.minZoom;
  if(newList > cache.maxZoom) newList = cache.maxZoom;
  newList = newList - cache.minZoom;

  // Find the bounds of the current map:
  var bounds = this.map.getBounds();
  var southWest = bounds.getSouthWest();
  var northEast = bounds.getNorthEast();
  var scaleMult = Math.pow(2,newZoom);
  var south = southWest.lat() - (0.703125/scaleMult)*256;  // Pad by one tile on all sides (256 pixels)
  var west = southWest.lng() - (1.40625/scaleMult)*256;
  var north = northEast.lat() + (0.703125/scaleMult)*256;
  var east = northEast.lng() + (1.40625/scaleMult)*256;

  // If the zoom level changed, remove all the currently displayed markers (to be replaced by markers for new zoom level):
  if(newZoom != this.defaultZoom)
  {
    this.markers = new Array();
    this.markerCount = 0;
    this.selectedMarker = null;
    this.selectedMarkerIndex = -1;
    this.polygon = null;
    this.map.clearOverlays();
    //alert('implement clear overlays() here');
    if(cache.polygonVertexCount > 0) this.showPolygon();
  }

  // Add any new markers that are now visible at the current zoom level within the bounds calculated above:
  // And remove any currently displayed markers at the current zoom level that are no longer visible:
  var i = 0;
  //n 47
  //s 40
  //e -112
  //w -127
  for (c = 0; c < cache.zoomListCount[newList]; c++)
  {
    i = cache.zoomLists[newList][c]-1;
    lat = cache.clusters[i][0];
    long = cache.clusters[i][1];
    //alert ("Cluster "+i+" latitude "+lat+"> south "+south+" and < north "+north+"\nlongitude "+long+"> west "+west+" and < east "+east);
    //checks to see if the cache.cluster lat is between the south and north bounds
    if(!this.markers[i] && cache.clusters[i][0] > south && cache.clusters[i][0] < north && cache.clusters[i][1] > west && cache.clusters[i][1] < east)
    	this.map.addOverlay(this.createMarker(i));
     //not between bounds, but show anyways for testing
      //else this.map.addOverlay(this.createMarker(i));
    else if(this.markers[i] && (cache.clusters[i][0] < south || cache.clusters[i][0] > north) || (cache.clusters[i][1] < west && cache.clusters[i][1] > east))
    {
      this.map.removeOverlay(this.markers[i]);
      this.markers[i] = null;
      this.markerCount--;
    }
  }

  // Make sure the selected marker is shown even if the corresponding cluster was merged or split:
  // But only if the zoom level changed
  if(cache.selectedRecord >= 0 && newZoom != this.defaultZoom)
  {
    c = this.findSpecimenCluster(cache.index[cache.selectedRecord]);
    this.selectMarker(c, false);
  }

  // Make sure the polygon is redrawn after any call to map.clearOverlays():
  //if(this.polygon = null && cache.polygonVertexCount > 0) this.showPolygon();

  this.defaultZoom = newZoom;
};

googleMap.createMarker = function(c)
{
  if(!cache.mapPointsLoaded || !this.loaded) return false;
  if(c < 0) return;
  //alert (c+" >= "+cache.clusterCount+"?");
  if(c >= cache.clusterCount) return; //alert ("cache.clusters[c][0]=null? "+cache.clusters[c][0]);
  if(cache.clusters[c][0] == null) return;
  var latlng = new GLatLng(cache.clusters[c][0], cache.clusters[c][1]);
  var new_icon = new GIcon();
  if(cache.clusters[c][2] == 1) // only 1 record for this dot
	{ 
    new_icon.size = new GSize(8,8);
    new_icon.iconAnchor = new GPoint(4,4);
    new_icon.infoWindowAnchor = new GPoint(4,4);
	    if(cache.type[cache.clusters[c][3]]=='o'){ 
	    	// cache.type = {'o','s','a'}, where 'o' = observation;
	    	new_icon.image = "atlas_images/amos"+cache.mColor[cache.clusters[c][3]]+".png"; 
	    }else{
	    	// cache.type was either 's' or 'a'
	    	new_icon.image = "atlas_images/ams"+cache.mColor[cache.clusters[c][3]]+".png";
		}
    }
    
  else 
  	{ var mColor = "gray";
      new_icon.size = new GSize(11,11);
      new_icon.iconAnchor = new GPoint(5,5);
      new_icon.infoWindowAnchor = new GPoint(5,5);
      new_icon.image = "atlas_images/amb"+checkClusterColor(c)+".png"; 
    } //medium white
  markerOptions = { icon:new_icon };
  
  //click event listener implementation
  var marker = new GMarker(latlng, markerOptions);
   GEvent.addListener(marker, "click", function() {
    if (googleMap.selectedMarkerIndex == c) return false; 
    googleMap.selectMarker(c, true);
    map.closeInfoWindow(); // Hide the info window, if it is open
    if(cache.clusters[c][2] > 1) {
    	cache.selectedRecord=-1;
    	var sIndex=0;
    	var s = new Array(); //specimen array
    	var oIndex=0;
    	var o = new Array(); //observation array
    	var i = 0;
    	var maxChars = 0;
    	for(i = 0; i < cache.clusters[c][2]; i++) {
   			if (cache.clusters[c][4][i].length>maxChars) maxChars=cache.clusters[c][4][i].length;
    		if(cache.type[cache.clusters[c][3][i]]=='s') { //specimen
    			sIndex++;
    			s[sIndex]=cache.clusters[c][4][i]+","+cache.clusters[c][3][i]; //taxon, cluster
    		}
    		else {
    		  var type=cache.type[cache.clusters[c][3][i]];
          if (type=="a")
		        type="assoc. sp.";
        	else if (type=="o")
		        type="obs";
        	else if (type=="p")
		        type="photo";
    			oIndex++;
    			o[oIndex]=cache.clusters[c][4][i]+" ("+type+"),"+cache.clusters[c][3][i]; //taxon, cluster
    		}
		}
		s=s.sort();
		o=o.sort();
		var width=208;
		if (maxChars*6>208) width=maxChars*6;
		var infoWindowHTML = "<div style=\"font-size:10px; width:" + width + "px; max-height: 100px; overflow-y: auto;\">"; 
		var link = new Array();
		if(sIndex>0){
			infoWindowHTML = infoWindowHTML + "<b>" + sIndex + " specimen";
			if(sIndex>1) infoWindowHTML = infoWindowHTML + "s";
			infoWindowHTML = infoWindowHTML + ":</b><br>";
			for (i = 0; i<sIndex; i++) {
				link = s[i].split(",");
				infoWindowHTML = infoWindowHTML + "<a href=\"#\" onclick=\"googleMap.showMarkerInfo(" + c + ", " + link[1] + "); return false;\">" + link[0] + "</a><br>";
				}
		}
		if(oIndex>0){
			if(sIndex>0) infoWindowHTML = infoWindowHTML + "<br>";
			infoWindowHTML = infoWindowHTML + "<b>" + oIndex + " observation";
			if(oIndex>1) infoWindowHTML = infoWindowHTML + "s";
			infoWindowHTML = infoWindowHTML + ":</b><br>";
			for (i = 0; i<oIndex; i++) {
				link = o[i].split(",");
				infoWindowHTML = infoWindowHTML + "<a href=\"#\" onclick=\"googleMap.showMarkerInfo(" + c + ", " + link[1] + "); return false;\">" + link[0] + "</a><br>";
				}
		}
    	infoWindowHTML = infoWindowHTML + "</div>";
	    marker.openInfoWindowHtml(infoWindowHTML);
	}
    else { //just one point in "cluster" 
    	googleMap.showMarkerInfo(c, cache.clusters[c][3][0]);
    }
  });
  GEvent.addListener(marker, "mouseover", function() {
     /*if(cache.clusters[c][2] == 1) // only 1 record for this dot
		marker.setImage("atlas_images/ams"+mColor+".png"); // small
	else marker.setImage("atlas_images/amb"+mColor+".png"); // large*/
  });
  GEvent.addListener(marker, "mouseout", function() {
    if(this.selectedMarkerIndex != c)
		if(cache.clusters[c][2] == 1){ // only 1 record for this dot
			//marker.setImage("atlas_images/ams"+cache.mColor[cache.clusters[c][3]]+".png"); // small
			if(cache.type[cache.clusters[c][3]]=='o'){ 
		    	// cache.type = {'o','s','a'}, where 'o' = observation;
		    	marker.setImage("atlas_images/amos"+cache.mColor[cache.clusters[c][3]]+".png"); 
		    }else{
		    	// cache.type was either 's' or 'a'
		    	marker.setImage("atlas_images/ams"+cache.mColor[cache.clusters[c][3]]+".png"); 
			}
		}
		else marker.setImage("atlas_images/amb"+checkClusterColor(c)+".png"); // large
  });

  this.markers[c] = marker;
  this.markerCount++;
  return marker;
};

function checkClusterColor(c){
	var id = cache.clusters[c][3];
	var lastColor = cache.mColor[id[0]];
	for(var i=0; i<id.length; i++){
		if(lastColor != cache.mColor[id[i]]){
			return 'gray';
		}
	}
	return lastColor;
}

googleMap.selectMarker = function(c, panMap)
{
  // Remove the previously selected marker, if any:
  if(this.selectedMarker) this.removeSelectedMarker();
  
  // Check that there is something to mark:
  if(!cache.mapPointsLoaded || !this.loaded) return false;
  if (c < 0 || c >= cache.clusterCount) return false;
  if(cache.clusters[c][0] == null || cache.clusters[c][1] == null) return false;
  var latlng = new GLatLng(cache.clusters[c][0], cache.clusters[c][1]);

  if(panMap) {
	  this.map.setCenter(latlng); //center on selected point
	  // then adjust if near state boundaries
	  var mostSouth=41.98;
	  var mostWest=-123.79;
	  var mostNorth=46.26;
	  var mostEast=-116.46;
	  var bounds = this.map.getBounds();
	  var southWest = bounds.getSouthWest();
	  var northEast = bounds.getNorthEast();
	  var south = southWest.lat();
	  var west = southWest.lng();
	  var north = northEast.lat();
	  var east = northEast.lng();
	  var newlat=cache.clusters[c][0];
	  var newlong=cache.clusters[c][1];
	  var panAgain = false;
	  if (south<mostSouth) { newlat=mostSouth+((north-south)/2); panAgain = true; }
	  if (west<mostWest) { newlong=mostWest+((east-west)/2); panAgain = true; }
	  if (north>mostNorth) { newlat=mostNorth-((north-south)/2); panAgain = true; }
	  if (east>mostEast) { newlong=mostEast-((east-west)/2); panAgain = true; }
	  if (panAgain) {
	  	var newlatlng = new GLatLng(newlat, newlong);
		  //this.map.setCenter(newlatlng); //center on adjusted lat, long - doesn't work when zooming in?  JC
	  }		  	
  }

  // Create a selected marker and overlay it on the marker defined by index:
  var new_icon = new GIcon();
  if(cache.clusters[c][2] == 1) // only 1 record for this dot
	{ //var mColor = "gray";
    new_icon.size = new GSize(8,8);
    new_icon.iconAnchor = new GPoint(4,4);
    new_icon.infoWindowAnchor = new GPoint(4,4);
    //new_icon.image = "atlas_images/amsselected.png"; 
    
    	if(cache.type[cache.clusters[c][3]]=='o'){ 
	    	// cache.type = {'o','s','a'}, where 'o' = observation;
	    	new_icon.image = "atlas_images/amosselected.png"; 
	    }else{
	    	// cache.type was either 's' or 'a'
	    	new_icon.image = "atlas_images/amsselected.png";
		}

    
    } //small green selected
    
  else 
  	{
      new_icon.size = new GSize(11,11);
      new_icon.iconAnchor = new GPoint(5,5);
      new_icon.infoWindowAnchor = new GPoint(5,5);
      new_icon.image = "atlas_images/ambselected.png"; } //medium green
  markerOptions = { icon:new_icon, zIndexProcess:selectedIndex };
  var newMarker = new GMarker(latlng, markerOptions);

  GEvent.addListener(newMarker, "click", function() {
    if (googleMap.selectedMarkerIndex == c) return false; 
    googleMap.selectMarker(c, true);
    map.closeInfoWindow();
    if(cache.clusters[c][2] > 1) {
      cache.selectedRecord=-1;
    	var sIndex=0;
    	var s = new Array(); //specimen array
    	var oIndex=0;
    	var o = new Array(); //observation array
    	var i = 0;
    	for(i = 0; i < cache.clusters[c][2]; i++) {
   			if (cache.clusters[c][4][i].length>maxChars) maxChars=cache.clusters[c][4][i].length;
    		if(cache.type[cache.clusters[c][3][i]]=='s') { //specimen
    			sIndex++;
    			s[sIndex]=cache.clusters[c][4][i]+","+cache.clusters[c][3][i]; //taxon, cluster
    		}
    		else {
    		  var type=cache.type[cache.clusters[c][3][i]];
          if (type=="a")
		        type="assoc. sp.";
        	else if (type=="o")
		        type="obs";
        	else if (type=="p")
		        type="photo";
    			oIndex++;
    			o[oIndex]=cache.clusters[c][4][i]+" ("+type+"),"+cache.clusters[c][3][i]; //taxon, cluster
    		}
		}
		s=s.sort();
		o=o.sort();
		var width=208;
		if (maxChars*6>208) width=maxChars*6;
		var infoWindowHTML = "<div style=\"font-size:10px; width:" + width + "px; max-height: 100px; overflow-y: auto;\">"; 
		var link = new Array();
		if(sIndex>0){
			infoWindowHTML = infoWindowHTML + "<b>" + sIndex + " specimen";
			if(sIndex>1) infoWindowHTML = infoWindowHTML + "s";
			infoWindowHTML = infoWindowHTML + ":</b><br>";
			for (i = 0; i<sIndex; i++) {
				link = s[i].split(",");
				infoWindowHTML = infoWindowHTML + "<a href=\"#\" onclick=\"googleMap.showMarkerInfo(" + c + ", " + link[1] + "); return false;\">" + link[0] + "</a><br>";
				}
		}
		if(oIndex>0){
			if(sIndex>0) infoWindowHTML = infoWindowHTML + "<br>";
			infoWindowHTML = infoWindowHTML + "<b>" + oIndex + " observation";
			if(oIndex>1) infoWindowHTML = infoWindowHTML + "s";
			infoWindowHTML = infoWindowHTML + ":</b><br>";
			for (i = 0; i<oIndex; i++) {
				link = o[i].split(",");
				infoWindowHTML = infoWindowHTML + "<a href=\"#\" onclick=\"googleMap.showMarkerInfo(" + c + ", " + link[1] + "); return false;\">" + link[0] + "</a><br>";
				}
		}
    	infoWindowHTML = infoWindowHTML + "</div>";
	    marker.openInfoWindowHtml(infoWindowHTML);
	}
    else //just one point in "cluster" 
    	googleMap.showMarkerInfo(c, cache.clusters[c][3][0]);
  });
  this.selectedMarker = newMarker;
  this.lastMarkerIndex = 1;
  this.selectedMarkerIndex = c;
  this.map.addOverlay(newMarker);
};

googleMap.removeSelectedMarker = function()
{
  if(this.selectedMarker == null) return;
  if(!cache.mapPointsLoaded || !this.loaded) return false;
  
  this.map.removeOverlay(this.selectedMarker);
  this.selectedMarker = null;
  this.selectedMarkerIndex = -1;
};

googleMap.showMarkerInfo = function(c, i)
{
  if(!cache.mapPointsLoaded || !this.loaded) return false;
  if (c < 0 || c >= cache.clusterCount) return false;
  if (i < 0 || i > cache.totalFoundCount) {
  	alert ("Invalid index ("+i+") <0 or >"+cache.totalFoundCount);
  	return false;
  }
  //var recordIndex = googleMap.findSpecimenData(i); // used for polygons?  Don't get it. . .
  //if(recordIndex == -1) return false;  // Failed to find the record, or had to load another page
  //results.updateDetailView(recordIndex, false);
  results.updateDetailView(i, true);
  interface.toggleData(false); //show data pane
  //interface.switchDataTab(3);
};

googleMap.findSpecimenCluster = function(index)
{
  // This function locates the cluster/marker at the current zoom level that contains the specimen with index = i:
  // If successful, returns the index of the cluster in the clusters array; or returns -1 upon failure.

  var defaultZoom = this.map.getZoom();
  if(defaultZoom < cache.minZoom) defaultZoom = cache.minZoom;
  if(defaultZoom > cache.maxZoom) defaultZoom = cache.maxZoom;
  var selectedList = defaultZoom - cache.minZoom;

  var i = 0;
  var s = 0;
  for (var c = 0; c < cache.zoomListCount[selectedList]; c++)
  {
    i = cache.zoomLists[selectedList][c]-1;
    var spCount = cache.clusters[i][3].length;
    for(s = 0; s < spCount; s++) if(cache.clusters[i][3][s] == index) return i;
  }
  return -1;
};

googleMap.findSpecimenData = function(index)
{

  // This function locates the specimen data in the cache object where cache.index = index
  // If successful returns the array index of the specimen data in the cache object; upon failure returns -1.

  for(var i = start; i < end; i++)
  {
    if(cache.index[i] == index)  return i;  // Found the location of the specimen data in the JavaScript data arrays
  }

  return -1;  // Could not find the specimen data.
};
