From 7cb4a408e87ca1923d40f3c4ae679c80f585a768 Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Fri, 16 May 2014 21:27:34 +0300 Subject: [PATCH 1/3] add SunCalc.getMoonTimes, close #7 --- suncalc.js | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/suncalc.js b/suncalc.js index 43c3a774..b149ade7 100644 --- a/suncalc.js +++ b/suncalc.js @@ -231,6 +231,65 @@ SunCalc.getMoonIllumination = function (date) { }; +function hoursLater(date, h) { + return new Date(date.valueOf() + h * dayMs / 24); +} + +// calculations for moon rise/set times are based on http://www.stargazing.net/kepler/moonrise.html article + +SunCalc.getMoonTimes = function (date, lat, lng) { + var t = new Date(date); + t.setHours(0); + t.setMinutes(0); + t.setSeconds(0); + t.setMilliseconds(0); + + var hc = 0.133 * rad, + h0 = SunCalc.getMoonPosition(t, lat, lng).altitude - hc, + h1, h2, rise, set, a, b, xe, ye, d, roots, x1, x2, dx; + + // go in 2-hour chunks, each time seeing if a 3-point quadratic curve crosses zero (which means rise or set) + for (var i = 1; i <= 24; i += 2) { + h1 = SunCalc.getMoonPosition(hoursLater(t, i), lat, lng).altitude - hc; + h2 = SunCalc.getMoonPosition(hoursLater(t, i + 1), lat, lng).altitude - hc; + + a = (h0 + h2) / 2 - h1; + b = (h2 - h0) / 2; + xe = -b / (2 * a); + ye = (a * xe + b) * xe + h1; + d = b * b - 4 * a * h1; + roots = 0; + + if (d >= 0) { + dx = Math.sqrt(d) / (Math.abs(a) * 2); + x1 = xe - dx; + x2 = xe + dx; + if (Math.abs(x1) <= 1) roots++; + if (Math.abs(x2) <= 1) roots++; + if (x1 < -1) x1 = x2; + } + + if (roots === 1) { + if (h0 < 0) rise = i + x1; + else set = i + x1; + + } else if (roots === 2) { + rise = i + (ye < 0 ? x2 : x1); + set = i + (ye < 0 ? x1 : x2); + } + + if (rise && set) break; + + h0 = h2; + } + + return { + rise: hoursLater(t, rise), + set: hoursLater(t, set) + }; +}; + + // export as AMD module / Node module / browser variable if (typeof define === 'function' && define.amd) define(SunCalc); else if (typeof module !== 'undefined') module.exports = SunCalc; From bf3577776bf8f17ca9b652d10e27d5fca2667b9a Mon Sep 17 00:00:00 2001 From: Vladimir Agafonkin Date: Fri, 16 May 2014 22:04:47 +0300 Subject: [PATCH 2/3] add alwaysUp/alwaysDown to getMoonTimes results --- suncalc.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/suncalc.js b/suncalc.js index b149ade7..fa576e52 100644 --- a/suncalc.js +++ b/suncalc.js @@ -283,10 +283,14 @@ SunCalc.getMoonTimes = function (date, lat, lng) { h0 = h2; } - return { - rise: hoursLater(t, rise), - set: hoursLater(t, set) - }; + var result = {}; + + if (rise) result.rise = hoursLater(t, rise); + if (set) result.set = hoursLater(t, set); + + if (!rise && !set) result[ye > 0 ? 'alwaysUp' : 'alwaysDown'] = true; + + return result; }; From ee593f7fa84c806d70ed11d91523382c903effb6 Mon Sep 17 00:00:00 2001 From: Kadir Yuecel Date: Fri, 18 Jul 2014 19:50:28 +0200 Subject: [PATCH 3/3] toDays, moonCoords and sunCoords now public from SunCalc --- suncalc.js | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/suncalc.js b/suncalc.js index fa576e52..18195dcb 100644 --- a/suncalc.js +++ b/suncalc.js @@ -26,9 +26,11 @@ var dayMs = 1000 * 60 * 60 * 24, J1970 = 2440588, J2000 = 2451545; +var SunCalc = {}; + function toJulian(date) { return date.valueOf() / dayMs - 0.5 + J1970; } function fromJulian(j) { return new Date((j + 0.5 - J1970) * dayMs); } -function toDays(date) { return toJulian(date) - J2000; } +SunCalc.toDays = function (date) { return toJulian(date) - J2000; } // general calculations for position @@ -56,7 +58,7 @@ function eclipticLongitude(M) { return M + C + P + PI; } -function sunCoords(d) { +SunCalc.sunCoords = function (d) { var M = solarMeanAnomaly(d), L = eclipticLongitude(M); @@ -68,18 +70,15 @@ function sunCoords(d) { } -var SunCalc = {}; - - // calculates sun position for a given date and latitude/longitude SunCalc.getPosition = function (date, lat, lng) { var lw = rad * -lng, phi = rad * lat, - d = toDays(date), + d = SunCalc.toDays(date), - c = sunCoords(d), + c = SunCalc.sunCoords(d), H = siderealTime(d, lw) - c.ra; return { @@ -134,7 +133,7 @@ SunCalc.getTimes = function (date, lat, lng) { var lw = rad * -lng, phi = rad * lat, - d = toDays(date), + d = SunCalc.toDays(date), n = julianCycle(d, lw), ds = approxTransit(0, lw, n), @@ -168,7 +167,7 @@ SunCalc.getTimes = function (date, lat, lng) { // moon calculations, based on http://aa.quae.nl/en/reken/hemelpositie.html formulas -function moonCoords(d) { // geocentric ecliptic coordinates of the moon +SunCalc.moonCoords = function (d) { // geocentric ecliptic coordinates of the moon var L = rad * (218.316 + 13.176396 * d), // ecliptic longitude M = rad * (134.963 + 13.064993 * d), // mean anomaly @@ -189,9 +188,9 @@ SunCalc.getMoonPosition = function (date, lat, lng) { var lw = rad * -lng, phi = rad * lat, - d = toDays(date), + d = SunCalc.toDays(date), - c = moonCoords(d), + c = SunCalc.moonCoords(d), H = siderealTime(d, lw) - c.ra, h = altitude(H, phi, c.dec); @@ -212,9 +211,9 @@ SunCalc.getMoonPosition = function (date, lat, lng) { SunCalc.getMoonIllumination = function (date) { - var d = toDays(date), - s = sunCoords(d), - m = moonCoords(d), + var d = SunCalc.toDays(date), + s = SunCalc.sunCoords(d), + m = SunCalc.moonCoords(d), sdist = 149598000, // distance from Earth to Sun in km