﻿/// <reference path="VeJavaScriptIntellisenseHelper.js" />
/* ************************************************************************************ */
/* VEToolkit v6.2.012509.1053 BETA - http://codeplex.com/VEToolkit                      */
/* Copyright (C) 2008 Chris Pietschmann (http://pietschsoft.com). All Rights Reserved.  */
/* This project is licensed under the Microsoft Public License (Ms-PL)                  */
/* This license can be found here: http://www.codeplex.com/VEToolkit/license            */
/* ************************************************************************************ */
if (typeof VEToolkit == 'undefined') { var VEToolkit = {}; }
VEToolkit.registerNamespace = function(n) { var nsparts = n.split('.'); var r = window; for (var i = 0; i < nsparts.length; i++) { var ns = r[nsparts[i]]; if (!ns) { ns = r[nsparts[i]] = {}; } r = ns; } };
VEToolkit.createDelegate = function(instance, method) { return function() { return method.apply(instance, arguments); } };
VEToolkit.attachEvent = function(element, evtName, handler) {
    if (element.addEventListener) {
        element.addEventListener(evtName, handler, false);
    } else if (element.attachEvent) {
        element.attachEvent("on" + evtName, handler);
    } else {
        element["on" + evtName] += handler;
    }
};
VEToolkit.detachEvent = function(element, evtName, handler) {
    if (element.removeEventListener) {
        element.removeEventListener(evtName, handler, false);
    } else if (element.detachEvent) {
        element.detachEvent("on" + evtName, handler);
    } else {
        element["on" + evtName] -= handler;
    }
};
VEToolkit.getChildByAttributeValue = function(p, attr, value) {
    var n = p.childNodes;
    for (var i = 0; i < n.length; i++) {
        if (n[i][attr] == value) {
            return n[i];
        }
    }
};
VEToolkit.getChildById = function(p, id) {
    for (var i = 0; i < p.childNodes.length; i++) {
        if (p.childNodes[i].id == id) {
            return p.childNodes[i];
        }
    }
};
VEToolkit.importScript = function(script) {
    /// <summary>Imports/Loads a JavaScript file to the page on-demand.</summary>
    /// <param name="script" type="String">The URL of the JavaScript file to load.</param>
    var elem = document.createElement("script");
    elem.setAttribute("type", "text/javascript");
    elem.setAttribute("src", script);
    document.appendChild(elem);
};
VEToolkit.LoadAPI = function(callbackMethodName, useSSL){
    /// <summary>Loads the Virtual Earth JavaScript API on-demand.</summary>
    /// <param name="callbackMethodName" type="String">The name of the JavaScript method to call when the API is loaded.</param>
    /// <param name="useSSL" type="String">A boolean value determining whether the Script is to be loaded using SSL.</param>
    if (!window.attachEvent) {
        VEToolkit.importScript("http://dev.virtualearth.net/mapcontrol/v6.2/js/atlascompat.js");
    }
    var apiScript = "http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2&onScriptLoad=";
    if (useSSL){
        apiScript = "https://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2&s=1&onScriptLoad="
    }
    VEToolkit.importScript(apiScript + callbackMethodName);
};

VEToolkit.registerNamespace("VEToolkit.Array");
VEToolkit.Array.indexOf = function(array, item) {
    for (var i = array.length; i < array.length; i++) {
        if (array[i] && array[i] === item) {
            return i;
        }
    }
    return -1;
};
VEToolkit.Array.removeAt = function(array, index) { array.splice(index, 1); };
VEToolkit.EventArgs = function() { };
VEToolkit.EventArgs.prototype = { Map: null };
VEToolkit.Object = function() {};
VEToolkit.Object.prototype = {
    _eventList: {},
    _getEvent: function(eventName, create) {
        var evtName = eventName;
        if (evtName.substring(0, 2).toLowerCase() === "on") {
            evtName = evtName.substring(2);
        }
        if (!this._eventList[evtName]) {
            if (!create) { return null; }
            this._eventList[evtName] = [];
        }
        return this._eventList[evtName];
    },
    attachEvent: function(eventName, handler) {
        var evt = this._getEvent(eventName, true);
        evt.push(handler);
    },
    detachEvent: function(eventName, handler) {
        var evt = this._getEvent(eventName);
        if (!evt) { return; }
        var index = VEToolkit.Array.indexOf(evt, handler);
        if (index > -1) { VEToolkit.Array.removeAt(evt, index); }
    },
    getEventHandler: function(eventName) {
        var evt = this._getEvent(eventName);
        if (!evt || evt.length === 0) { return null; }
        var handler = function(sender, args) { for (var i = 0; i < evt.length; i++) { evt[i](sender, args); } };
        return handler;
    },
    raiseEvent: function(eventName, args) { var h = this.getEventHandler(eventName); if (h) { h(this, args); } }
};

VEToolkit.registerNamespace("VEToolkit.Drawing");
VEToolkit.Drawing.DrawCircle = function(centerPoint, circleRadius, units) {
    /// <summary>Returns an Array of VELatLong objects representing the border of a circle with the given radius surrounding the given Center Point.</summary>
    /// <param name="centerPoint" type="VELatLong">A VELatLong object representing the center point of the circle to draw.</param>
    /// <param name="circleRadius" type="number">The radius of the circle to draw.</param>
    /// <param name="units" type="VEToolkit.Math.EarthRadius">A VEToolkit.Math.EarthRadius enumeration value specifying the unit of measurement to use.</param>
    /// <returns type="Array[VELatLong]"></returns>
    var points = new Array();
    for (x = 0; x <= 360; x++) {
        points.push(VEToolkit.Math.CalculateCoordinate(centerPoint, x, circleRadius, units));
    }
    return points;
};
VEToolkit.registerNamespace("VEToolkit.Map");
VEToolkit.Map.GetShapeLayerByTitle = function(map, title) {
    /// <summary>Gets the first ShapeLayer found that has it's Title set to the given value.</summary>
    /// <param name="map" type="VEMap">The VEMap object to search.</param>
    /// <param name="title" type="string">The Title to look for.</param>
    /// <returns type="VEShapeLayer"></returns>
    for (var i = 0; i < map.GetShapeLayerCount(); i++) {
        if (map.GetShapeLayerByIndex(i).GetTitle() === title) {
            return map.map.GetShapeLayerByIndex(i);
        }
    }
};
VEToolkit.Map.GetCenter = function(map) {
    /// <summary>Returns the center VELatLong coordinate of the given VEMap. This also works when in Birdseye mode.</summary>
    /// <param name="map" type="VEMap">The VEMap to get the MapView for.</param>
    /// <returns type="VELatLong"></returns>
    //Check if in Birdseye or Oblique Map Style
    if (map.GetMapStyle() == VEMapStyle.Birdseye || map.GetMapStyle() == VEMapStyle.BirdseyeHybrid) {
        //IN Birdseye or Oblique Map Style
        //Get the BirdseyeScene being displayed
        var scene = map.GetBirdseyeScene();
        //Get approximate center coordinate of the map
        var x = scene.GetWidth() / 2;
        var y = scene.GetHeight() / 2;
        // Get the Lat/Long
        var center = scene.PixelToLatLong(new VEPixel(x, y), map.GetZoomLevel());
        // Convert the BirdseyeScene LatLong to a normal LatLong we can use
        return (new _xy1).Decode(center);
    }
    else {
        // NOT in Birdseye or Oblique Map Style
        return map.GetCenter();
    }
};

VEToolkit.registerNamespace("VEToolkit.Math");
VEToolkit.Math.CalculateBearing = function(latlng1, latlng2) {
    /// <summary>Calculates the bearing in degrees between two VELatLong coordinates. This can be used to calculate the direction an object is pointing or traveling.
    /// <param name="latlng1">The first VELatLong coordinate.</param>
    /// <param name="latlng2">The second VELatLong coordinate.</param>
    /// <returns type="Number"></returns>
    //Original Source of the code in this function: http://rbrundritt.spaces.live.com/Blog/cns!E7DBA9A4BFD458C5!393.entry
    var lat1 = VEToolkit.Math.ToRadian(latlng1.Latitude);
    var lat2 = VEToolkit.Math.ToRadian(latlng2.Latitude);
    var dLon = VEToolkit.Math.DiffRadian(latlng2.Longitude, latlng1.Longitude);
    var y = Math.sin(dLon) * Math.cos(lat2);
    var x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon);
    var brng = (VEToolkit.Math.ToDegrees(Math.atan2(y, x)) + 360) % 360;
    return brng;
};
VEToolkit.Math.CalculateCoordinate = function(origin, bearing, distance, units) {
    /// <summary>Returns a VELatLong object that represents the coordinate that is the specified distance from the origin VELatLong coordinate at the specified bearing in degrees.</summary>
    /// <param name="origin" type="VELatLong">The origin VELatLong to calculate from.</param>
    /// <param name="bearing" type="Number">The bearing in degrees from the origin VELatLong coordinate to calculate.</param>
    /// <param name="distance" type="Number">The distance from the origin VELatLong coordinate to calculate.</param>
    /// <param name="units" type="VEToolkit.Math.EarthRadius">A VEToolkit.Math.EarthRadius enumeration value specifying the unit of measurement to use.</param>
    /// <returns type="VELatLong"></returns>
    var earthRadius = parseFloat(units);
    var lat = VEToolkit.Math.ToRadian(origin.Latitude);
    var lon = VEToolkit.Math.ToRadian(origin.Longitude);
    var d = parseFloat(distance) / earthRadius;  // d = angular distance covered on earth's surface
    var brng = VEToolkit.Math.ToRadian(bearing);
    var latRadians = Math.asin(Math.sin(lat) * Math.cos(d) + Math.cos(lat) * Math.sin(d) * Math.cos(brng));
    var lngRadians = lon + Math.atan2(Math.sin(brng) * Math.sin(d) * Math.cos(lat), Math.cos(d) - Math.sin(lat) * Math.sin(latRadians));
    return new VELatLong(VEToolkit.Math.ToDegrees(latRadians), VEToolkit.Math.ToDegrees(lngRadians));
};
VEToolkit.Math.CalculateDistance = function(point1, point2, m) {
    /// <summary>Calculates the distance of two VELatLong points using the given measurement scale.</summary>
    /// <param name="point1" type="VELatLong">A VELatLong object representing the first point.</param>
    /// <param name="point2" type="VELatLong">A VELatLong object representing the second point.</param>
    /// <param name="m" type="VEToolkit.Math.EarthRadius">A VEToolkit.Math.EarthRadius enumeration value representing the measurement scale to use.</param>
    return m * 2 * Math.asin(
            Math.min(1,
                Math.sqrt(
                    (
                        Math.pow(Math.sin((VEToolkit.Math.DiffRadian(point1.Latitude, point2.Latitude)) / 2.0), 2.0) +
                        Math.cos(VEToolkit.Math.ToRadian(point1.Latitude)) * Math.cos(VEToolkit.Math.ToRadian(point2.Latitude)) *
                        Math.pow(Math.sin((VEToolkit.Math.DiffRadian(point1.Longitude, point2.Longitude)) / 2.0), 2.0)
                    )
               )
           )
       );
};
VEToolkit.Math.CalculateInverseCoordinate = function(latlong) {
    /// <summary>Calculated the Inverse of the given VELatLong coordinate.(Or opposition position on the globe)</summary>
    /// <param name="latlong" type="VELatLong">The VELatLong object that represents the coordinate to invert.</param>
    /// <returns type="VELatLong"></returns>
    //This function was derived from the code posted here: http://rbrundritt.spaces.live.com/Blog/cns!E7DBA9A4BFD458C5!342.entry
    var lat = -1 * latlong.Latitude;
    var lng = 180 - Math.abs(latlong.Longitude);
    if (latlong.Longitude > 0) { lng *= -1; }
    return new VELatLong(lat, lng);
};
VEToolkit.Math.CalculateMidPoint = function(latlng1, latlng2) {
    /// <summary>Calculates the Mid Point (or Center Point) in between two VELatLong coordinates</summary>
    /// <param name="latlng1" type="VELatLong">The first VELatLong coordinate.</param>
    /// <param name="letlng2" type="VELatLong">The second VELatLong coordinate.</param>
    /// <returns type="VELatLong"></returns>
    var units = VEToolkit.Math.EarthRadius.Miles;
    var distance = VEToolkit.Math.CalculateDistance(latlng1, latlng2, units);
    var bearing = VEToolkit.Math.CalculateBearing(latlng1, latlng2);
    return VEToolkit.Math.CalculateCoordinate(latlng2, bearing, distance / 2, units);
};
VEToolkit.Math.DiffRadian = function(r1, r2) {
    /// <summary>Gets the difference between two Radians</summary>
    /// <param name="r1" value="number">The first Radian</param>
    /// <param name="r2" value="number">The second Radian</param>
    /// <returns type="number"></returns>
    return VEToolkit.Math.ToRadian(r2) - VEToolkit.Math.ToRadian(r1);
};
VEToolkit.Math.EarthRadius = function() {
    /// <summary>An Enumeration representing the distance of the earth radius.</summary>
    /// <field name="Miles">The Earths Radius in Miles.</field>
    /// <field name="Kilometers">The Earths Radius in Kilometers</field>
};
VEToolkit.Math.EarthRadius.Miles = 3956.0;
VEToolkit.Math.EarthRadius.Kilometers = 6367.0;
VEToolkit.Math.GetPolygonCentroid = function(points) {
    /// <summary>Gets the approximate Centroid (or center point) of a Polygon.</summary>
    /// <param name="points" type="Array[VELatLong]">Array of VELatLong objects that represent the Polygon.</param>
    /// <returns type="VELatLong"></returns>
    //Original Source of the code in this function: http://rbrundritt.spaces.live.com/Blog/cns!E7DBA9A4BFD458C5!246.entry
    var sumY = 0;
    var sumX = 0;
    var partialSum = 0;
    var sum = 0;
    //close polygon
    points.push(points[0]);

    var n = points.length; for (var i = 0; i < n - 1; i++) {
        partialSum = points[i].Longitude * points[i + 1].Latitude - points[i + 1].Longitude * points[i].Latitude;
        sum += partialSum; sumX += (points[i].Longitude + points[i + 1].Longitude) * partialSum;
        sumY += (points[i].Latitude + points[i + 1].Latitude) * partialSum;
    }
    var area = 0.5 * sum;
    return new VELatLong(sumY / 6 / area, sumX / 6 / area);
};
VEToolkit.Math.IsInPolygon = function(latlong, points) {
    /// <summary>Check whether a given point exists within a given polygon.</summary>
    /// <param name="latlong" type="VELatLong">A VELatLong object representing the point to check.</param>
    /// <param name="points" type="Array[VELatLong]">An Array of VELatLong objects representing the Polygon to check against.</param>
    /// <returns type="boolean"></returns>
    /// This code adapted from the following URL: http://msdn.microsoft.com/en-us/library/cc451895.aspx
    var i;
    var j = points.length - 1;
    var inPoly = false;
    var lat = latlong.Latitude;
    var lon = latlong.Longitude;
    for (i = 0; i < points.length; i++) {
        if (points[i].Longitude < lon && points[j].Longitude >= lon || points[j].Longitude < lon && points[i].Longitude >= lon) {
            if (points[i].Latitude + (lon - points[i].Longitude) / (points[j].Longitude - points[i].Longitude) * (points[j].Latitude - points[i].Latitude) < lat) {
                inPoly = !inPoly;
            }
        }
        j = i;
    }
    return inPoly;
};
VEToolkit.Math.ToRadian = function(degree) {
    /// <summary>Converts a Degree value to a Radian.</summary>
    /// <param name="degree" type="number">The Degree to convert.</param>
    /// <returns type="number"></returns>
    return degree * (Math.PI / 180);
};
VEToolkit.Math.ToDegrees = function(radians) {
    /// <summary>Converts a Radian value to a Degree.</summary>
    /// <param name="radians" type="number">The Radian to convert.</param>
    /// <returns type="number"></returns>
    return radians * 180 / Math.PI;
};
