PDA

View Full Version : Sunrise and sunset according to longitude and latitude

OxygenThief
06-26-2012, 06:37 AM
<html>
<title>Sunrise and sunset</title>

<script type="text/javascript">

function sunupdown(longitude, ew, latitude, ns, localoffset){

var d = new Date();
var dyear = d.getUTCFullYear();
var dmonth = d.getUTCMonth();
var dday = d.getUTCday();
var zenith = 90.83;
var lnghour = longitude / 15;
var sindec1, sindec2, cosdec1, cosdec2, cosh1, cosh2, h1, h2, T1, T2, UT1, UT2, localT1, localT2;
var output = " ", output1 = " ", output2 = " ";

// Set coords as negative if West or South.
if(ew == 'w' || ew == 'W'){longitude -= (2*longitude);}
if(ns == 's' || ew == 'S'){latitude -= (2*latitude);}

// Calculate the day of the year.
n1 = Math.floor(275 * dmonth / 9);
n2 = Math.floor((dmonth + 9) / 12);
n3 = (1 + Math.floor((dyear - 4 * Math.floor(dyear / 4) + 2) / 3));
n = n1 - (n2 * n3) + dday - 30;
// Get sunrise and sunset times.
t1 = n + ((6 - lnghour) / 24);
t2 = n + ((18 - lnghour) / 24);
// Sun's mean anomaly.
m1 = (0.9856 * t1) - 3.289;
m2 = (0.9856 * t2) - 3.289;
// Sun's true longitude.
l1 = m1 + (1.916 * Math.sin(m1)) + (0.020 * Math.sin(2 * m1)) + 282.634;
if(l1 < 0){l1 += 360;}
if(l1 > 360){l1 -= 360;}
l2 = m2 + (1.916 * Math.sin(m2)) + (0.020 * Math.sin(2 * m2)) + 282.634;
if(l2 < 0){l2 += 360;}
if(l2 > 360){l2 -= 360;}
// Sun's right ascension.
ra1 = Math.atan(0.91764 * Math.tan(l1));
if(ra1 < 0){ra1 += 360;}
if(ra1 > 360){ra1 -= 360;}
ra2 = Math.atan(0.91764 * Math.tan(l2));
if(ra2 < 0){ra2 += 360;}
if(ra2 > 360){ra2 -= 360;}
// Convert right ascension into hours.
ra1 = ra1 / 15;
ra2 = ra2 / 15;
// Get sun's declination.
sindec1 = 0.39782 * Math.sin(l1);
sindec2 = 0.39782 * Math.sin(l2);
cosdec1 = Math.cos(Math.asin(sindec1));
cosdec2 = Math.cos(Math.asin(sindec2));
// Sun's local hour angle.
cosh1 = (Math.cos(zenith) - (sindec1 * Math.sin(latitude))) / (cosdec1 * Math.cos(latitude));
cosh2 = (Math.cos(zenith) - (sindec2 * Math.sin(latitude))) / (cosdec2 * Math.cos(latitude));
if(cosh1 > 1){output1 = "No sunrise.";}
if(cosh2 < -1){output2 = "No sunset.";}
// Get hour.
h1 = 360 - Math.acos(cosh1);
h2 = Math.acos(cosh2);
h1 = h1 / 15;
h2 = h2 / 15;
// Local mean time for rise and set.
T1 = ra1 - (0.06571 * t1) - 6.622;
T2 = ra2 - (0.06571 * t2) - 6.622;
UT1 = T1 - lnghour;
if(UT1 < 0){UT1 += 24;}
if(UT1 > 24){UT1 -= 24;}
UT2 = T2 - lnghour;
if(UT2 < 0){UT2 += 24;}
if(UT2 > 24){UT2 -= 24;}
// Convert UTC value to local based on longitude and latitude.
localT1 = UT1 + localoffset;
localT2 = UT2 + localoffset;
// Output sunrise and sunset times as a string.
if(output1 != " "){localT1 = output1;}
if(output2 != " "){localT2 = output2;}
document.write("<p>Sunrise: "+localT1+"<br>Sunset: "+localT2+"</p>");
}
</script>

<body>

<h1>Sunrise and sunset at location X.</h1>

<script type="text/javascript">sunupdown(50, 'e', 50, 's', 5);</script>

</body>
</html>

Based on: http://williams.best.vwh.net/sunrise_sunset_algorithm.htm

Basically it's not working yet, and I'd appreicate any help. There's no output, and I'm not sure why. @_@

Philip M
06-26-2012, 08:28 AM
Syntax error:-

var dday = d.getUTCDay(); // must be capital D in Day

I think that there must be an error in the calculations as I get the improbable results

Sunrise: 11.026954690764316
Sunset: No sunset.

All advice is supplied packaged by intellectual weight, and not by volume. Contents may settle slightly in transit.

OxygenThief
06-27-2012, 04:01 AM
Ah, thanks.

As for the numbers, I believe I have to sort out the conversions between decimal and DMS. That may be the issue now.

vk2xv
07-06-2012, 08:00 PM
I have this code working.

I found a couple of errors...

'var dday = d.getUTCday();' should be 'var dday = d.getUTCDay();'

'if(ns == 's' || ew == 'S'){latitude -= (2*latitude);}' should be 'if(ns == 's' || ns == 'S'){latitude -= (2*latitude);}'

I have actually used the form 'if(ns == 's' || ns == 'S'){latitude *= -1;}' for both longitude and latitude negations.

The algorithm uses degrees throughout, but the trig functions in javascript run in 'radians mode'. This is warned about in the heading at

http://williams.best.vwh.net/sunrise_sunset_algorithm.htm

Here are the edits needed for the sunrise bits which converts degrees to radians and vice-versa. Use them as a template for the sunset bits (l2, ra2, etc..)...

l1 = m1 + (1.916 * Math.sin((Math.PI/180)*m1)) + (0.020 * Math.sin((Math.PI/180)*2 * m1)) + 282.634;

ra1 = (180/Math.PI)*Math.atan(0.91764 * Math.tan((Math.PI/180)*l1));

sindec1 = 0.39782 * Math.sin((Math.PI/180)*l1);

cosh1 = (Math.cos((Math.PI/180)*zenith) - (sindec1 * Math.sin((Math.PI/180)*latitude))) / (cosdec1 * Math.cos((Math.PI/180)*latitude));

h1 = 360 - (180/Math.PI)*Math.acos(cosh1);

I think that is all of them. If not you should be able to work out any I have missed.

Note that this expression does NOT require editing...

cosdec1 = Math.cos(Math.asin(sindec1));

because 'sindec1' is not in degrees and Math.asin() returns radians into Math.cos() which returns 'cosdec1' which is not in degrees. Clear ? - :-)

Note also that localT1 and localT2 have to be checked and corrected for underflow (<0) and overflow (>24).

Thanks for the reference and code.

Philip M
07-06-2012, 09:04 PM
Why not post your complete code for others to enjoy and learn from?

vk2xv
07-07-2012, 12:09 AM
Well - it is basically just OxygenThief's code with the edits as given - not really my code.

I could post it if wanted...

vk2xv
07-07-2012, 12:37 AM
Here goes... I am a newbie to the forum so I hope I don't break something...

function sunriseSunset(longitude, ew, latitude, ns, localoffset)
{
var d = new Date();
var dyear = d.getUTCFullYear();
var dmonth = d.getUTCMonth() + 1;
var dday = d.getUTCDate();
var zenith = 90.83;
var lnghour = longitude / 15.0;
var sindec1, sindec2, cosdec1, cosdec2, cosh1, cosh2, h1, h2, T1, T2, UT1, UT2, localT1, localT2;
var output = " ", output1 = " ", output2 = " ";

// Set coords as negative if West or South.
if(ew == 'w' || ew == 'W'){longitude *= -1;}
if(ns == 's' || ns == 'S'){latitude *= -1;}

// Calculate the day of the year.
n1 = Math.floor(275 * dmonth / 9);
n2 = Math.floor((dmonth + 9) / 12);
n3 = (1 + Math.floor((dyear - 4 * Math.floor(dyear / 4) + 2) / 3));
n = n1 - (n2 * n3) + dday - 30;
// Get sunrise and sunset times.
t1 = n + ((6 - lnghour) / 24);
t2 = n + ((18 - lnghour) / 24);
// Sun's mean anomaly.
m1 = (0.9856 * t1) - 3.289;
m2 = (0.9856 * t2) - 3.289;
// Sun's true longitude.
l1 = m1 + (1.916 * Math.sin((Math.PI/180)*m1)) + (0.020 * Math.sin((Math.PI/180)*2 * m1)) + 282.634;
if(l1 < 0){l1 += 360;}
if(l1 > 360){l1 -= 360;}
l2 = m2 + (1.916 * Math.sin((Math.PI/180)*m2)) + (0.020 * Math.sin(2 * (Math.PI/180)*m2)) + 282.634;
if(l2 < 0){l2 += 360;}
if(l2 > 360){l2 -= 360;}
// Sun's right ascension.
ra1 = (180/Math.PI)*Math.atan(0.91764 * Math.tan((Math.PI/180)*l1));
if(ra1 < 0){ra1 += 360;}
if(ra1 > 360){ra1 -= 360;}
ra2 = (180/Math.PI)*Math.atan(0.91764 * Math.tan((Math.PI/180)*l2));
if(ra2 < 0){ra2 += 360;}
if(ra2 > 360){ra2 -= 360;}
// Convert right ascension into hours.
ra1 = ra1 / 15.0;
ra2 = ra2 / 15.0;
// Get sun's declination.
sindec1 = 0.39782 * Math.sin((Math.PI/180)*l1);
sindec2 = 0.39782 * Math.sin((Math.PI/180)*l2);
cosdec1 = Math.cos(Math.asin(sindec1));
cosdec2 = Math.cos(Math.asin(sindec2));
// Sun's local hour angle.
cosh1 = (Math.cos((Math.PI/180)*zenith) - (sindec1 * Math.sin((Math.PI/180)*latitude))) / (cosdec1 * Math.cos((Math.PI/180)*latitude));
cosh2 = (Math.cos((Math.PI/180)*zenith) - (sindec2 * Math.sin((Math.PI/180)*latitude))) / (cosdec2 * Math.cos((Math.PI/180)*latitude));
if(cosh1 > 1){output1 = "No sunrise.";}
if(cosh2 < -1){output2 = "No sunset.";}
// Get hour.
h1 = 360 - (180/Math.PI)*Math.acos(cosh1);
h2 = (180/Math.PI)*Math.acos(cosh2);
h1 = h1 / 15;
h2 = h2 / 15;
// Local mean time for rise and set.
T1 = h1 + ra1 - (0.06571 * t1) - 6.622;
T2 = h2 + ra2 - (0.06571 * t2) - 6.622;
UT1 = T1 - lnghour;
if(UT1 < 0){UT1 += 24;}
if(UT1 > 24){UT1 -= 24;}
UT2 = T2 - lnghour;
if(UT2 < 0){UT2 += 24;}
if(UT2 > 24){UT2 -= 24;}
// Convert UTC value to local based on longitude and latitude.
localT1 = UT1 + localoffset;
localT1 = (localT1 > 24) ? localT1-24:localT1;
localT1 = (localT1 < 0) ? localT1+24:localT1;
localT2 = UT2 + localoffset;
localT2 = (localT2 > 24) ? localT2-24:localT2;
localT2 = (localT2 < 0) ? localT2+24:localT2;
// Output sunrise and sunset times as a string.
if(output1 != " "){localT1 = output1;}
if(output2 != " "){localT2 = output2;}
// Correct underflow/overflow and convert to 12 hour - am/pm format
var localT1Hour = Math.floor(localT1);
var localT1Minute = Math.floor((localT1 - localT1Hour)*60);
localT1Minute = (localT1Minute < 10) ? "0" + localT1Minute: localT1Minute;
localT1Minute = (localT1Hour < 12) ? localT1Minute + " AM":localT1Minute + " PM";
localT1Hour = (localT1Hour > 12) ? localT1Hour - 12: localT1Hour;
var localT2Hour = Math.floor(localT2);
var localT2Minute = Math.floor((localT2 - localT2Hour)*60);
localT2Minute = (localT2Minute < 10) ? "0" + localT2Minute: localT2Minute;
localT2Minute = (localT2Hour < 12) ? localT2Minute + " AM":localT2Minute + " PM";
localT2Hour = (localT2Hour > 12) ? localT2Hour - 12: localT2Hour;

document.getElementById('sunrise').innerHTML = "*** Night time hours here are currently from " +localT2Hour+':'+localT2Minute+ " in the evening to "+localT1Hour+':'+localT1Minute + " the<br/>next morning <i><b> - my local time<\/b><\/i>, so you will only see a black photo during those hours...";

}

Works for me...

BTW - forgot to mention in the previous post.... getUTCMonth() is zero-based - so the algorithmn need it to be incremented before use...
Also note the use of 'getUTCDay()' is incorrect - that returns from 0-6, use getUTCDate() instead which returns the correct 1-31 values (I only found this after I saw the days were getting shorter lately, when they should be getting longer)...

vk2xv
07-09-2012, 02:17 AM
Just found another error - the calculation of 'lnghour' should be done AFTER the adjustment of sign for E/W. Not a problem for me as I am East of GM, but would have been an error for those West of GM.

Change code from this...

.....
var lnghour = longitude / 15.0;
var sindec1, sindec2, cosdec1, cosdec2, cosh1, cosh2, h1, h2, T1, T2, UT1, UT2, localT1, localT2;
var output = " ", output1 = " ", output2 = " ";

// Set coords as negative if West or South.
if(ew == 'w' || ew == 'W'){longitude *= -1;}
if(ns == 's' || ns == 'S'){latitude *= -1;}
.....

to this...

.....
var lnghour;
var sindec1, sindec2, cosdec1, cosdec2, cosh1, cosh2, h1, h2, T1, T2, UT1, UT2, localT1, localT2;
var output = " ", output1 = " ", output2 = " ";

// Set coords as negative if West or South.
if(ew == 'w' || ew == 'W'){longitude *= -1;}
if(ns == 's' || ns == 'S'){latitude *= -1;}

lnghour = longitude / 15.0;
....

Philip M
07-09-2012, 08:55 AM
I don't think it is working properly! :(

*** Night time hours here are currently from 1:16 AM in the evening to 8:57 AM the nnext morning - my local time, so you will only see a black photo during those hours...

I entered 0.5610° W, 51.3162° N

vk2xv
07-09-2012, 09:40 AM
I don't think it is working properly! :(

*** Night time hours here are currently from 1:16 AM in the evening to 8:57 AM the nnext morning - my local time, so you will only see a black photo during those hours...

I entered 0.5610° W, 51.3162° N

Mmmm - I entered sunriseSunset(.5, 'w', 51.3, 'n', 0);
and it returned...

localT1Hour/Minute = 3:56AM
localT2Hour/Minute = 8:17PM

This is correct I believe.

In any case, with those coordinates "It must be Woking..." ;-)

Philip M
07-09-2012, 09:46 AM
I must have made a wrong entry! :o

But my result is slightly different:

sunriseSunset(.56, 'w', 51.3, 'n', 0);

*** Night time hours here are currently from 8:16 PM in the evening to 3:57 AM the next morning - my local time, so you will only see a black photo during those hours...

Is that because it is a different day here in UK compared to the antipodes? But the times are UTC dates/times.

Yes, you are right! I entered the coordinates for my office in Woking. Google Maps is a wonderful thing!

vk2xv
07-09-2012, 10:13 AM
I changed to sunriseSunset(.56, 'w', 51.3, 'n', 0); [had entered .5 for long before]
and it returned 8:17PM and 3:57AM this time.

I am not sure of why the original code used the UTC variations of getXXXX(). The page which contains the algorithm seems to indicate local time/dates.

I changed the code to this...

var d = new Date();
var dyear = d.getFullYear();
var dmonth = d.getMonth() + 1;
var dday = d.getDate();

and it still returned 8:17PM and 3:57AM. What do you get when you use the above code ?

Philip M
07-09-2012, 10:17 AM
I changed to sunriseSunset(.56, 'w', 51.3, 'n', 0); [had entered .5 for long before]
and it returned 8:17PM and 3:57AM this time.

I am not sure of why the original code used the UTC variations of getXXXX(). The page which contains the algorithm seems to indicate local time/dates.

I changed the code to this...

var d = new Date();
var dyear = d.getFullYear();
var dmonth = d.getMonth() + 1;
var dday = d.getDate();

and it still returned 8:17PM and 3:57AM. What do you get when you use the above code ?

Night time hours here are currently from 8:16 PM in the evening to 3:57 Could it be maths differences between browsers? I am using IE9, but same result in Chrome.

Your calculations are in UTC and take no account of DST. Local times of sunrise/sunset for London on 9 Jul 2012 04:55 and 21:16

vk2xv
07-09-2012, 11:07 AM
The day number is 191.

http://www.calendardate.com/todays.htm

the leap year is taken into account here...

n3 = (1 + Math.floor((dyear - 4 * Math.floor(dyear / 4) + 2) / 3));

I know some calculations say 192 (your script returns 191 on a pencil and paper walk-thru. ??? i.e., 31+29+30+31+30+31+9 = 191), but I will stick with the astronomical version which I have also hand-calculated as 191.

BTW - it is probably a good time to reiterate I am only trying to get someone else's coding of yet another person's algorithm going. So it their code/algorithm which doesn't take into account DST. And I have changed the original code to not use UTC.

I leave it up to the reader to fathom out how to code in DST - I assumed that you would do that manually by putting in the right offset (for here it would be 10 for no DST and 11 for DST).

BTW - the small discrepancy in times (1 minute) is due to a local geographic correction in my code I need to make here to account for mountains....

When I take that out and apply manual DST correction (+1) it returns 9:16PM and 4:57AM.

So all's well....

Philip M
07-09-2012, 11:40 AM
You are right - the day number is indeed 191.

sunriseSunset(.56, 'w', 51.3, 'n', 1);
results in
Night time hours here are currently from 9:16 PM UTC in the evening to 4:57 AM UTC

Just to complete the topic, here is a script to identify whether DST is used and in operation in a location:-

<html>

<script type = "text/javascript">

var now =new Date(); // today
var yr = now.getFullYear();
var DSTused = false;

var jan = new Date(yr,0,1); // 1st January
var janoff = jan.getTimezoneOffset();
var jul = new Date(yr,6,1); // 1st July
var juloff = jul.getTimezoneOffset();
//alert ("January " + janoff + " July " + juloff); // for testing
if (janoff != juloff) {
DSTused = true;
}
else {
alert ("This country does NOT use DST");
}

if (DSTused) {
var maxval = Math.max(janoff, juloff); // Note that JavaScript returns the offset in minutes and reverses the sign (so for example time zone +10 will return an offset of -600).
var ntzo = now.getTimezoneOffset();
//alert (ntzo + " "+ maxval); // for testing
if (ntzo != maxval) {
alert ("DST is in operation on this day")
}
else {
alert ("The country uses DST but DST is NOT in operation on this day");
}
}

</script>

</body>
</html>