solar-calc
Version:
A sunrise/sunset/moonrise/moonset calculator
293 lines (252 loc) • 8.8 kB
JavaScript
"use strict";
var _createClass = (function () { function defineProperties(target, props) { for (var key in props) { var prop = props[key]; prop.configurable = true; if (prop.value) prop.writable = true; } Object.defineProperties(target, props); } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } };
var Sun = (function () {
function Sun(date, latitude, longitude) {
_classCallCheck(this, Sun);
this.date = date;
this.latitude = latitude;
this.longitude = longitude;
this.julianDate = getJD(date);
}
_createClass(Sun, {
solarNoon: {
get: function () {
return calcSolNoon(this.julianDate, this.longitude, this.date);
}
},
timeAtAngle: {
value: function timeAtAngle(angle, rising) {
return calcSunriseSet(rising, angle, this.julianDate, this.date, this.latitude, this.longitude);
}
}
});
return Sun;
})();
var formatDate = function formatDate(date, minutes) {
var seconds = (minutes - Math.floor(minutes)) * 60;
return new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), 0, minutes, seconds));
};
function calcTimeJulianCent(jd) {
var T = (jd - 2451545) / 36525;
return T;
}
function isLeapYear(yr) {
return yr % 4 === 0 && yr % 100 !== 0 || yr % 400 === 0;
}
function calcDoyFromJD(jd) {
var z = Math.floor(jd + 0.5);
var f = jd + 0.5 - z;
var A;
if (z < 2299161) {
A = z;
} else {
var alpha = Math.floor((z - 1867216.25) / 36524.25);
A = z + 1 + alpha - Math.floor(alpha / 4);
}
var B = A + 1524;
var C = Math.floor((B - 122.1) / 365.25);
var D = Math.floor(365.25 * C);
var E = Math.floor((B - D) / 30.6001);
var day = B - D - Math.floor(30.6001 * E) + f;
var month = E < 14 ? E - 1 : E - 13;
var year = month > 2 ? C - 4716 : C - 4715;
var k = isLeapYear(year) ? 1 : 2;
var doy = Math.floor(275 * month / 9) - k * Math.floor((month + 9) / 12) + day - 30;
return doy;
}
function radToDeg(angleRad) {
return 180 * angleRad / Math.PI;
}
function degToRad(angleDeg) {
return Math.PI * angleDeg / 180;
}
function calcGeomMeanLongSun(t) {
var L0 = 280.46646 + t * (36000.76983 + t * 0.0003032);
while (L0 > 360) {
L0 -= 360;
}
while (L0 < 0) {
L0 += 360;
}
return L0; // in degrees
}
function calcGeomMeanAnomalySun(t) {
var M = 357.52911 + t * (35999.05029 - 0.0001537 * t);
return M; // in degrees
}
function calcEccentricityEarthOrbit(t) {
var e = 0.016708634 - t * (0.000042037 + 1.267e-7 * t);
return e; // unitless
}
function calcSunEqOfCenter(t) {
var m = calcGeomMeanAnomalySun(t);
var mrad = degToRad(m);
var sinm = Math.sin(mrad);
var sin2m = Math.sin(mrad + mrad);
var sin3m = Math.sin(mrad + mrad + mrad);
var C = sinm * (1.914602 - t * (0.004817 + 0.000014 * t)) + sin2m * (0.019993 - 0.000101 * t) + sin3m * 0.000289;
return C; // in degrees
}
function calcSunTrueLong(t) {
var l0 = calcGeomMeanLongSun(t);
var c = calcSunEqOfCenter(t);
var O = l0 + c;
return O; // in degrees
}
function calcSunApparentLong(t) {
var o = calcSunTrueLong(t);
var omega = 125.04 - 1934.136 * t;
var lambda = o - 0.00569 - 0.00478 * Math.sin(degToRad(omega));
return lambda; // in degrees
}
function calcMeanObliquityOfEcliptic(t) {
var seconds = 21.448 - t * (46.815 + t * (0.00059 - t * 0.001813));
var e0 = 23 + (26 + seconds / 60) / 60;
return e0; // in degrees
}
function calcObliquityCorrection(t) {
var e0 = calcMeanObliquityOfEcliptic(t);
var omega = 125.04 - 1934.136 * t;
var e = e0 + 0.00256 * Math.cos(degToRad(omega));
return e; // in degrees
}
function calcSunDeclination(t) {
var e = calcObliquityCorrection(t);
var lambda = calcSunApparentLong(t);
var sint = Math.sin(degToRad(e)) * Math.sin(degToRad(lambda));
var theta = radToDeg(Math.asin(sint));
return theta; // in degrees
}
function calcEquationOfTime(t) {
var epsilon = calcObliquityCorrection(t);
var l0 = calcGeomMeanLongSun(t);
var e = calcEccentricityEarthOrbit(t);
var m = calcGeomMeanAnomalySun(t);
var y = Math.tan(degToRad(epsilon) / 2);
y *= y;
var sin2l0 = Math.sin(2 * degToRad(l0));
var sinm = Math.sin(degToRad(m));
var cos2l0 = Math.cos(2 * degToRad(l0));
var sin4l0 = Math.sin(4 * degToRad(l0));
var sin2m = Math.sin(2 * degToRad(m));
var Etime = y * sin2l0 - 2 * e * sinm + 4 * e * y * sinm * cos2l0 - 0.5 * y * y * sin4l0 - 1.25 * e * e * sin2m;
return radToDeg(Etime) * 4; // in minutes of time
}
function calcHourAngle(angle, lat, solarDec) {
var latRad = degToRad(lat);
var sdRad = degToRad(solarDec);
var HAarg = Math.cos(degToRad(90 + angle)) / (Math.cos(latRad) * Math.cos(sdRad)) - Math.tan(latRad) * Math.tan(sdRad);
var HA = Math.acos(HAarg);
return HA; // in radians (for sunset, use -HA)
}
function isNumber(inputVal) {
var oneDecimal = false;
var inputStr = "" + inputVal;
for (var i = 0; i < inputStr.length; i++) {
var oneChar = inputStr.charAt(i);
if (i === 0 && (oneChar === "-" || oneChar === "+")) {
continue;
}
if (oneChar === "." && !oneDecimal) {
oneDecimal = true;
continue;
}
if (oneChar < "0" || oneChar > "9") {
return false;
}
}
return true;
}
function getJD(date) {
var year = date.getFullYear();
var month = date.getMonth() + 1;
var day = date.getDate();
var A = Math.floor(year / 100);
var B = 2 - A + Math.floor(A / 4);
var JD = Math.floor(365.25 * (year + 4716)) + Math.floor(30.6001 * (month + 1)) + day + B - 1524.5;
return JD;
}
function calcSolNoon(jd, longitude, date) {
var tnoon = calcTimeJulianCent(jd - longitude / 360);
var eqTime = calcEquationOfTime(tnoon);
var solNoonOffset = 720 - longitude * 4 - eqTime; // in minutes
var newt = calcTimeJulianCent(jd + solNoonOffset / 1440);
eqTime = calcEquationOfTime(newt);
var solNoonLocal = 720 - longitude * 4 - eqTime; // in minutes
while (solNoonLocal < 0) {
solNoonLocal += 1440;
}
while (solNoonLocal >= 1440) {
solNoonLocal -= 1440;
}
return formatDate(date, solNoonLocal);
// return timeString(solNoonLocal, 3);
}
function dayString(jd) {
if (jd < 900000 || jd > 2817000) {
return "error";
} else {
var z = Math.floor(jd + 0.5);
var f = jd + 0.5 - z;
var A;
if (z < 2299161) {
A = z;
} else {
var alpha = Math.floor((z - 1867216.25) / 36524.25);
A = z + 1 + alpha - Math.floor(alpha / 4);
}
var B = A + 1524;
var C = Math.floor((B - 122.1) / 365.25);
var D = Math.floor(365.25 * C);
var E = Math.floor((B - D) / 30.6001);
var day = B - D - Math.floor(30.6001 * E) + f;
var month = E < 14 ? E - 1 : E - 13;
var year = month > 2 ? C - 4716 : C - 4715;
return new Date(Date.UTC(year, month - 1, day, 0, 0, 0));
}
}
function calcSunriseSetUTC(rise, angle, JD, latitude, longitude) {
var t = calcTimeJulianCent(JD);
var eqTime = calcEquationOfTime(t);
var solarDec = calcSunDeclination(t);
var hourAngle = calcHourAngle(angle, latitude, solarDec);
//alert("HA = " + radToDeg(hourAngle));
if (!rise) hourAngle = -hourAngle;
var delta = longitude + radToDeg(hourAngle);
var timeUTC = 720 - 4 * delta - eqTime; // in minutes
return timeUTC;
}
function calcSunriseSet(rise, angle, JD, date, latitude, longitude)
// rise = 1 for sunrise, 0 for sunset
{
var timeUTC = calcSunriseSetUTC(rise, angle, JD, latitude, longitude);
var newTimeUTC = calcSunriseSetUTC(rise, angle, JD + timeUTC / 1440, latitude, longitude);
if (isNumber(newTimeUTC)) {
return formatDate(date, newTimeUTC);
} else {
// no sunrise/set found
var doy = calcDoyFromJD(JD);
var jdy;
if (latitude > 66.4 && doy > 79 && doy < 267 || latitude < -66.4 && (doy < 83 || doy > 263)) {
//previous sunrise/next sunset
jdy = calcJDofNextPrevRiseSet(!rise, rise, angle, JD, latitude, longitude);
return dayString(jdy);
} else {
//previous sunset/next sunrise
jdy = calcJDofNextPrevRiseSet(rise, rise, angle, JD, latitude, longitude);
return dayString(jdy);
}
}
}
function calcJDofNextPrevRiseSet(next, rise, type, JD, latitude, longitude) {
var julianday = JD;
var increment = next ? 1 : -1;
var time = calcSunriseSetUTC(rise, type, julianday, latitude, longitude);
while (!isNumber(time)) {
julianday += increment;
time = calcSunriseSetUTC(rise, type, julianday, latitude, longitude);
}
return julianday;
}
module.exports = Sun;