﻿/**
*
* Calculates sunrise and sunset times. Based on 
* NOAA's algorithm.
*
* The calculations in the NOAA Sunrise/Sunset and Solar Position Calculators
* are based on equations from Astronomical Algorithms, by Jean Meeus. The
* sunrise and sunset results have been verified to be accurate to within a
* minute for locations between +/- 72° latitude, and within 10 minutes outside
* of those latitudes.
*
* This is an implementation of NOAA's low accuracy calculations
*
* Tested against http://www.srrb.noaa.gov/highlights/sunrise/sunrise.html
* Results within 2-3 minutes for places in UK, US and Australia
*
* This javascript port by Bill Chadwick, November 2008 - free for any use.
*
*/

function SunRiseSunSet() {

    /** Converts day of year to fractional year in radians.
    * @return fractional year (radians)
    */
    this.gamma = function(dayOfYear) {
    
        return ((2 * Math.PI) / 365.0) * (dayOfYear - 1);
    }

    // equation of time - minutes
    this.eqtimeMins = function (dayOfYear) {

        var g = this.gamma(dayOfYear);
        return 229.18 * (0.000075
        + (0.001868 * Math.cos(g))
        - (0.032077 * Math.sin(g))
        - (0.014615 * Math.cos(g * 2.0))
        - (0.040849 * Math.sin(g * 2.0))
        );
    }

    /** Calculates solar declination angle in radians.
    * @return solar declination angle (radians)
    */
    this.declRads = function (dayOfYear) {
    
        var g = this.gamma(dayOfYear);
        return 0.006918
        - 0.399912 * Math.cos(g)
        + 0.070257 * Math.sin(g)
        - 0.006758 * Math.cos(g * 2.0)
        + 0.000907 * Math.sin(g * 2.0)
        - 0.002697 * Math.cos(g * 3.0)
        + 0.00148 * Math.sin(g * 3.0);
    }

    // sunrise/sunset hour angle, +ha = sunrise, -ha = sunset

    this.haDegrees = function (latitudeDegrees, dayOfYear) {

        var la = latitudeDegrees * (Math.PI / 180.0);
        var decl = this.declRads(dayOfYear);
        return Math.acos(
        (Math.cos(90.833 * (Math.PI / 180.0)) / (Math.cos(la) * Math.cos(decl)))
        - (Math.tan(la) * Math.tan(decl))
        ) * (180.0 / Math.PI);
    }

    //Java Script date to day of year
    this.dayOfYear = function(d){
        var onejan = new Date(d.getUTCFullYear(),0,1);
        return Math.ceil((d - onejan) / 86400000);
    }

    //Java script adjust date from decimal UTC hours
    this.dateFromHours = function(hrs, d){
        var r = new Date(d.getUTCFullYear(),d.getUTCMonth(),d.getUTCDate());
        var mins = (hrs-Math.floor(hrs)) * 60.0;
        hrs = Math.floor(hrs);  
        if (hrs >= 24){
            r = new Date(r.getTime() + (24 * 60 * 60 * 1000));
            hrs -= 24;
        }
        else if (hrs < 0){
            r = new Date(r.getTime() - (24 * 60 * 60 * 1000));
            hrs += 24;
        }
        else{
            r = new Date(d.getUTCFullYear(),d.getUTCMonth(),d.getUTCDate(),hrs,Math.round(mins),0);
        }
        r.setUTCHours(hrs);
        r.setUTCMinutes(Math.round(mins));              

        return r;
    }

// Public methods use - for West and South

    this.sunRise = function(latitudeDegrees, longitudeDegrees, jsDate) {
    
        var doy = this.dayOfYear(jsDate);
        var hrs = (720.0
        + (4 * (-longitudeDegrees - this.haDegrees(latitudeDegrees, doy)))
        - this.eqtimeMins(doy)
        ) / 60.0;
        // -ve hours means the previous day, > 24 hrs means the next day
        return this.dateFromHours(hrs,jsDate); 
    }

    this.sunSet = function(latitudeDegrees, longitudeDegrees, jsDate) {
    
        var doy = this.dayOfYear(jsDate);
        var hrs = (720.0
        + (4 * (-longitudeDegrees + this.haDegrees(latitudeDegrees, doy)))
        - this.eqtimeMins(doy)
        ) / 60.0;
        // -ve hours means the previous day, > 24 hrs means the next day
        return this.dateFromHours(hrs,jsDate); 
    }

    this.sunNoon = function(latitudeDegrees, longitudeDegrees, jsDate) {

        var hrs = (720.0
        + (4 * (-longitudeDegrees))
        - this.eqtimeMins(this.dayOfYear(jsDate))
        ) / 60.0;
        // -ve hours means the previous day, > 24 hrs means the next day
        return this.dateFromHours(hrs,jsDate);         
    }

}



 