﻿//Google Maps Mapping Library
//By Craig Wardman

//load dependancies

//this method of loading does not work in exploder, so load the script in the HTML or ASP.NET
//var GoogleLib=document.createElement("script");
//GoogleLib.type="text/javascript";
//GoogleLib.src="http://maps.google.com/maps?file=api&v=2&key=ABQIAAAAzr2EBOXUKnm_jVnk0OJI7xSosDVG8KKPE1-m51RBrvYughuyMxQ-i1QfUnH94QxWIa6N4U6MouMmBA";
//document.getElementsByTagName("head")[0].appendChild(GoogleLib);

var GoogleMapLabelLib=document.createElement("script");
GoogleMapLabelLib.type="text/javascript";
GoogleMapLabelLib.src="GoogleMapLabel.js";
document.getElementsByTagName("head")[0].appendChild(GoogleMapLabelLib);

var map, mapCtrlId;
var latLongCtrlId;
var viewingWidth, viewingHeight;

function WaitForGoogle(andThenCall) {

    if (typeof (GMap2) != 'undefined') {
        andThenCall();
    }
    else {
        setTimeout(function() { WaitForGoogle(andThenCall); andThenCall=null;}, 200)
    }
}

function WaitForMap(andThenCall) {
    if(typeof(map)!='undefined') {
        andThenCall();
    }
    else {
        setTimeout(function() { WaitForMap(andThenCall); andThenCall=null;}, 200)
    }
}


//Call this function to initialise the map in the given div
function GetMap(targetDivId, initLat, initLong, initZoomLevel){
    var targetDiv=document.getElementById(targetDivId);
    
    if(targetDiv==null) return null;

    mapCtrlId=targetDivId;

    if (GBrowserIsCompatible()) {
        map = new GMap2(targetDiv);
        map.setCenter(new GLatLng(initLat, initLong), initZoomLevel);
        map.enableDoubleClickZoom();
        map.enableScrollWheelZoom();
        map.addControl(new GLargeMapControl());
        map.addControl(new GScaleControl());
        map.addControl(new GMapTypeControl());
    }

    	
    //save the height/width of the div
    viewingWidth=document.getElementById(mapCtrlId).style.width;
    viewingHeight=document.getElementById(mapCtrlId).style.height;
    
    //insert a div for showing messages
    if(document.getElementById("divDistance")==null){
        var divDist=document.createElement("div");
        divDist.setAttribute("id", "divDistance");
        divDist.style.visibility = "hidden";
        divDist.style.display = "none";
        divDist.style.position="absolute";
        divDist.style.color="#FFFFFF";
        divDist.style.backgroundColor="#4E739E";
        divDist.style.border="solid 1px #3D628D";
        divDist.style.fontSize="8px";
        divDist.style.textAlign="center";
        divDist.style.width="32px";
        
        document.forms[0].appendChild(divDist);
    }
    
    //run any extensions
    try{
        GetMapEx();
    }
    catch(e){
        //ignore
    }
    
    return map;
}


//this returns the div control containing the map
function GetMapCtrl(){
    return document.getElementById(mapCtrlId);
}

//this zooms the map to best position to view the entire requested range
function ZoomMapToFit(minLat, minLong, maxLat, maxLong){
    var ptBounds;
    
    //google provides a built in function for calculating the zoom level based on the bounds
    ptBounds=new GLatLngBounds(new GLatLng(minLat, minLong), new GLatLng(maxLat,maxLong));
    
    map.setCenter(new GLatLng((maxLat+minLat)/2, (maxLong+minLong)/2), map.getBoundsZoomLevel(ptBounds));
}

//this enables tracking the mouse position and recording the lat/long in the provided div
function TrackLatLong(showInDivId){
    if(document.getElementById(showInDivId)!=null){
        latLongCtrlId=showInDivId;
        GEvent.addListener(map,"mousemove",_mouseMoveTrackLatLong);
    }
}

//draws a polygon on the map
function DrawPolygon(latLongArray, strokeColor, fillColor, opacity){
    
    var myCurrentShape = new GPolygon(_latLonArrToGLatLngArr(latLongArray),"#"+strokeColor,1,opacity,"#"+fillColor,opacity);
    
    map.addOverlay(myCurrentShape);
         
}

//draws a circle on the map
function DrawCircle(centerLat, centerLong, radiusKm, strokeColor, fillColor, opacity){
    DrawPolygon(_getCircleShape(centerLat, centerLong, radiusKm), strokeColor, fillColor, opacity);
}

//draws a rectangular label, with associated hover info on the map
function DrawLabel(atLat, atLong, title, text, bgColorRGB, borderColorRGB){
    var lbl=GetLabel(atLat, atLong, title, text, bgColorRGB, borderColorRGB);
    map.addOverlay(lbl);
    
    return lbl;
}

function GetLabel(atLat, atLong, title, text, bgColorRGB, borderColorRGB){
    var pnt = new GLatLng(atLat, atLong);
    var titleBox="<div style='color: #000000; background-color: #"+bgColorRGB+"; border: solid 1px #"+borderColorRGB+"; font-size: 8px; text-align: center; width: 32px;' title='"+title+"'>"+text+"</div>"
            
    return new ELabel(pnt,titleBox,null,null,100,false);
}

function DrawPin(atLat, atLong, text, customIcon){ 
    var pin=GetPin(atLat, atLong, text, customIcon);
    map.addOverlay(pin);
    
    return pin;
}

function GetPin(atLat, atLong, text, customIcon){
    var latLong = new GLatLng(atLat, atLong);
    var ico=new GIcon(G_DEFAULT_ICON);
    
    if(customIcon!=null) ico.image=customIcon;
        
    return new GMarker(latLong,{icon:ico});
            
}

function DrawDraggablePin(atLat, atLong, text, customIcon){ 
    var pin=GetDraggablePin(atLat, atLong, text, customIcon);
    map.addOverlay(pin);
    
    return pin;
}

function GetDraggablePin(atLat, atLong, text, customIcon){
    var latLong = new GLatLng(atLat, atLong);
    var ico=new GIcon(G_DEFAULT_ICON);
    
    if(customIcon!=null) ico.image=customIcon;
        
    return new GMarker(latLong,{icon:ico,title:text,draggable:true});
            
}

function DrawClusteredLabels(onLayer,objWithLatLongArray,titleColumnName,bgColorRGB, borderColorRGB, forMinLat, forMinLong, forMaxLat, forMaxLong){
    var pinGridResult=_getClusterGrid(objWithLatLongArray, titleColumnName, forMinLat, forMinLong, forMaxLat, forMaxLong);
    var i;
    
    for(i=0;i<pinGridResult.length;i++){
        if(pinGridResult[i]!=null){
            for(p=0;p<pinGridResult[i].length;p++){
                if(pinGridResult[i][p]!=null){
                    var grpName=pinGridResult[i][p].Text.substring(0,pinGridResult[i][p].Text.indexOf("<br/>"));
                    if(grpName=="") grpName=pinGridResult[i][p].Text;
                    DrawLabel(pinGridResult[i][p].lat,pinGridResult[i][p].lng,grpName,pinGridResult[i][p].Text,bgColorRGB, borderColorRGB);
                }
            }
        }
    }
    
}

function DrawClusteredPins(onLayer,objWithLatLongArray,titleColumnName,icon, forMinLat, forMinLong, forMaxLat, forMaxLong){
    
    var pinGridResult=_getClusterGrid(objWithLatLongArray, titleColumnName, forMinLat, forMinLong, forMaxLat, forMaxLong);
    var i,p;
    
    for(i=0;i<pinGridResult.length;i++){
        if(pinGridResult[i]!=null){
            for(p=0;p<pinGridResult[i].length;p++){
                if(pinGridResult[i][p]!=null){
                    DrawPin(pinGridResult[i][p].lat, pinGridResult[i][p].lng,pinGridResult[i][p].Text,icon);
                }
            }
        }
    }

}

function GetVisibleRegion(){
    //lat/lng starts at the bottom/left most visible point on the map and stops at the top/right
    var googleBounds=map.getBounds();  
    var visibleRegionData=new Object();
    
    visibleRegionData.MinLat=googleBounds.getSouthWest().lat(); //bottom
    visibleRegionData.MinLong=googleBounds.getSouthWest().lng(); //left
    visibleRegionData.MaxLat=googleBounds.getNorthEast().lat(); //top
    visibleRegionData.MaxLong=googleBounds.getNorthEast().lng(); //right
    
    
    return visibleRegionData;
}



function _getClusterGrid(objWithLatLongArray,titleColumnName, forMinLat, forMinLong, forMaxLat, forMaxLong){
    //use a 15% equiv box grid, but no more than 400grid refs
    var gridSize=Math.round(Math.sqrt(objWithLatLongArray.length*0.15))%20;
    var pinGrid=new Array(gridSize);
    var i;
       
       
    //lat/lng starts at the bottom/left most visible point on the map and stops at the top/right
    
    var latMarkerDiff=(forMaxLat-forMinLat)/gridSize;
    var longMarkerDiff=(forMaxLong-forMinLong)/gridSize;
    
    
    //now we process the pins and create a cluster pin in any grid where a pin falls
    for(i=0;i<objWithLatLongArray.length;i++){
    
        //make sure the pin is in the viewable area
        if(objWithLatLongArray[i].lat>=forMinLat && objWithLatLongArray[i].lat<=forMaxLat && objWithLatLongArray[i].lng>=forMinLong && objWithLatLongArray[i].lng<=forMaxLong){
            var xIndex, yIndex;
            
            //generate grid index
            xIndex=Math.round((objWithLatLongArray[i].lat-forMinLat)/latMarkerDiff)
            yIndex=Math.round((objWithLatLongArray[i].lng-forMinLong)/longMarkerDiff)
            
            //create a grid array if one doesnt exist
            if(pinGrid[xIndex]==null) pinGrid[xIndex]=new Array(gridSize);
            
            //see if there is already a cluster pin here
            if(pinGrid[xIndex][yIndex]!=null){
                //add to the current description
                pinGrid[xIndex][yIndex].Text=pinGrid[xIndex][yIndex].Text+"\r\n"+eval("objWithLatLongArray[i]."+titleColumnName);
               
            }
            else{
                //first time pin, create it
                pinGrid[xIndex][yIndex]=new Object();
                pinGrid[xIndex][yIndex].lat=objWithLatLongArray[i].lat;
                pinGrid[xIndex][yIndex].lng=objWithLatLongArray[i].lng;
                pinGrid[xIndex][yIndex].Text=eval("objWithLatLongArray[i]."+titleColumnName);
                
            }
        }
       
    }
    
    return pinGrid;
}

//Gets polygon points of a circle
function _getCircleShape(circleCentreLat, circleCentreLong, radius){
    var circleCentreLL=new GLatLng(circleCentreLat, circleCentreLong);

    var R = 6371; // earth's mean radius in km
    var lat = _degToRad(circleCentreLL.lat()); //rad
    var lon = _degToRad(circleCentreLL.lng()); //rad
    var d = parseFloat(radius) / R;  // d = angular distance covered on earth's surface
    
    var myCirclePoints = new Array();
    for (x = 0; x < 360; x++){ 
        var pt_lat, pt_long;
        brng = _degToRad(x); //rad
        pt_lat = Math.asin(Math.sin(lat)*Math.cos(d) + Math.cos(lat)*Math.sin(d)*Math.cos(brng));
        
        pt_long = _radToDeg(lon + Math.atan2(Math.sin(brng)*Math.sin(d)*Math.cos(lat), Math.cos(d)-Math.sin(lat)*Math.sin(pt_lat)));//deg
        pt_lat=_radToDeg(pt_lat);
        
        myCirclePoints.push(pt_lat, pt_long);
    }
    return myCirclePoints;
}


//calculate distance
function GetDistance(GLatLng1, GLatLng2){
   	p1Lat = GLatLng1.latRadians();
	p1Lon = GLatLng1.lngRadians();
	
	p2Lat = GLatLng2.latRadians();
	p2Lon = GLatLng2.lngRadians();	
	
	var R = 6371; // earth's mean radius in km
	var dLat  = p2Lat - p1Lat;
	var dLong = p2Lon - p1Lon;
	var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(p1Lat) * Math.cos(p2Lat) * Math.sin(dLong/2) * Math.sin(dLong/2);
	var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
	var disKm = R * c;
	//var disMiles = disKm * 0.6214;	
	return (disKm);
}

//the event handler for tracking mouse when recording lat/long
function _mouseMoveTrackLatLong(e){
        
    //show current location on screen
    document.getElementById(latLongCtrlId).innerHTML = "Lat: " + parseInt(e.lat() * 1000)/1000 + ", Long:" + parseInt(e.lng()*1000)/1000;
}

//converts array of lat/long values to GLatLng objects
function _latLonArrToGLatLngArr(points){
    var LLarr=new Array();
    for(i=0;i<points.length;i+=2){
        LLarr.push(new GLatLng(points[i], points[i+1]));
    }
    
    return LLarr;
}

//convert degrees to radians
function _degToRad(number){
	return number * Math.PI / 180;	
}

function _radToDeg(number){
	return number * 180 / Math.PI;	
}


//Clean up all objects
function MapDispose()
{
    GUnload();
}

//set map to run onload and dispose on exit
if (window.attachEvent) {
	//window.attachEvent("onload", GetMap);
	window.attachEvent("onunload", MapDispose);	
} else {
	//window.addEventListener("load", GetMap, false);
	window.addEventListener("unload", MapDispose, false);
}

//notify ASP.NET AJAX if applicable
if (typeof(Sys) != 'undefined') Sys.Application.notifyScriptLoaded();
