﻿//used for calculating distance between lat/long coordinates
var EARTH_RADIUS = 3959;
//Returns a number object parsed from the string parameter provided. Either traditional format or digital format accepted
function parseLatitude(inLat) {
    try {
        var latDecRegex = /^[\-\+]?[0-8]\d\.\d*$/;
        var latTradRegex = /^([\-\+])?(\d{1,2})º{0,1}\s*([0-5]\d)'{0,1}(\s*(([0-5]\d)(\.\d*)?)"{0,1}){0,1}(\s+[NS]){0,1}$/;
        if (latTradRegex.test(inLat)) {
            var sign = "";
            if (RegExp.$1) {
                sign = RegExp.$1;
            }
            var lat = parseFloat(RegExp.$2);
            lat += parseFloat(RegExp.$3) / 60;
            if (RegExp.$5) {
                lat += parseFloat(RegExp.$5) / 3600;
            }
            return sign == "" ? lat : -1 * lat;
        } else if (latDecRegex.test(inLat)) {
            return parseFloat(inLat);
        } else {
            return 0;
        }
    } catch (e) {
        return 0;
    }
}
//Returns a number object after parsing the string provided. Either traditional or else digital format accepted
function parseLongitude(inLong) {
    try {
        var longTradRegex = /^([\-\+])?(1?\d{2})º{0,1}\s*([0-5]\d)'{0,1}(\s*(([0-5]\d)(\.\d*)?)"{0,1}){0,1}(\s+[EW]){0,1}$/;
        var longDecRegex = /^([\-\+]?((\d{1,3}(,\d{3})?)?)|(\d*))(\.\d*)?$/;

        if (longTradRegex.test(inLong)) {
            var sign = "";
            if (RegExp.$1) {
                sign = RegExp.$1;
            }
            var lgt = parseFloat(RegExp.$2);
            lgt += parseFloat(RegExp.$3) / 60;
            if (RegExp.$5) {
                lgt += parseFloat(RegExp.$6) / 3600;
            }
            if (sign == "-" || RegExp.$8 == "W") {
                return -1 * lgt;
            } else {
                return lgt;
            }
        } else if (longDecRegex.test(inLong)) {
            return parseFloat(inLong);
        } else {
            return 0;
        }
    } catch (e) {
        return 0;
    }
}
//Accepts a digital lat/long and returns a string with traditional degrees/minutes/seconds format
function convertDecimalCoord2Traditional(coord) {
    var coordAbs = Math.abs(coord);
    var degrees = Math.floor(coordAbs);
    var fraction = coordAbs - degrees;
    var minutes = Math.floor(60 * fraction);
    var seconds = Math.round(360000 * (fraction - (minutes / 60))) / 100;

    var minStr = minutes < 10 ? '0' + minutes.toString() : minutes.toString();
    var secStr = seconds < 10 ? '0' + seconds.toString() : seconds.toString();
    var result = degrees.toString() + 'º ' + minStr + '\' ' + secStr + '\"';
    if (coord < 0) {
        return '-' + result;
    } else {
        return result;
    }
}
//returns true if the point represented by test lat/lng is inside the polygon, false otherwise
//The polygon array is expected to contain Google GLatLng objects
function pointInside(polygon, testLat, testLng) {
    //first compute the bounding box and see if point is inside it (computationally cheaper)
    var sPt = 90;
    var wPt = 180;
    var nPt = -90;
    var ePt = -180;
    for (var i = 0; i < polygon.length; i++) {
        var lat = polygon[i].lat();
        if (lat < sPt) {
            sPt = lat;
        }
        if (lat > nPt) {
            nPt = lat;
        }
        var lng = polygon[i].lng();
        if (wPt > lng) {
            wPt = lng;
        }
        if (ePt < lng) {
            ePt = lng;
        }
    }
    //now that we've computed the edges of our bounding box, see if the point falls outside:
    if (testLat < sPt || testLat > nPt || testLng < wPt || testLng > ePt) {
        return false;
    }
    //at this point we know the point is within our bounding box, so
    //evaluate whether point is inside by drawing an infinite imaginary line
    //that intersects the polygon and count how many times it crosses the
    //boundary; odd number of crossings means the point is contained
    var containsPoint = false;
    //walk the list, using polygon[j] and polygon[i], where j represents the
    //point before i in the list (except for the first pass, where it is the last point in the list)
    for (var i = 0, j = polygon.length - 1; i < polygon.length; j = i++) {
        if (((polygon[i].lng() <= testLng && testLng < polygon[j].lng()) ||
                     (polygon[j].lng() <= testLng && testLng < polygon[i].lng()))
                     &&
                     testLat < ((polygon[j].lat() - polygon[i].lat()) *
                                (testLng - polygon[i].lng()) /
                                (polygon[j].lng() - polygon[i].lng())) +
                                polygon[i].lat()) {
            containsPoint = !containsPoint;
        }
    }
    return containsPoint;
}
//Computes the coordinates of the point specified by the starting point using the bearing and distance
//Useful for many things, including when you want to make a bounding box around a center point
//Return object has properties latitude and longitude
function getLatLongFromDirectionAndDistance(startLat, startLon, distance, bearing) {
    var MI_PER_NAUTICAL = 1.150779;
    var EPS = 0.00000000005;
    var radLat = startLat * Math.PI / 180;
    var radLon = startLon * Math.PI / 180;
    var radBearing = bearing * Math.PI / 180;
    var nauticalDist = distance / MI_PER_NAUTICAL;
    nauticalDist /= (180 * 60 / Math.PI);

    var answerLon, dlon;
    var answerLat = Math.asin(Math.sin(radLat) * Math.cos(nauticalDist)
                       + Math.cos(radLat) * Math.sin(nauticalDist) * Math.cos(radBearing));
    if (Math.abs(Math.cos(answerLat)) < EPS) {
        answerLon = 0;
    } else {
        dlon = Math.atan2(Math.sin(radBearing) * Math.sin(nauticalDist) * Math.cos(radLat),
                                 Math.cos(nauticalDist / EARTH_RADIUS) - (Math.sin(radLat) * Math.sin(answerLat)));
        answerLon = (radLon - dlon + Math.PI) % (2 * Math.PI) - Math.PI;
    }
    return { latitude: answerLat * 180 / Math.PI, longitude: answerLon * 180 / Math.PI };
}
//Calculates the great circle distance for two arbitrary points on the Earth's surface
function earthDistance(lat1, long1, lat2, long2) {
    var deltaLat = degrees2Radians(lat2 - lat1);
    var deltaLong = degrees2Radians(long2 - long1);
    var term1 = Math.pow(Math.sin(deltaLat / 2), 2) +
                        (Math.cos(degrees2Radians(lat1)) * Math.cos(degrees2Radians(lat2)) *
                        Math.sin(deltaLong / 2) * Math.sin(deltaLong / 2));
    var term2 = 2 * Math.atan2(Math.sqrt(term1), Math.sqrt(1 - term1));
    return EARTH_RADIUS * term2;
}
function degrees2Radians(degrees) {
    return (degrees / 180) * Math.PI;
}