Skip to content

Instantly share code, notes, and snippets.

@bewest
Created September 17, 2011 18:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bewest/1224222 to your computer and use it in GitHub Desktop.
Save bewest/1224222 to your computer and use it in GitHub Desktop.
d3 sunrise sunset
sunrise sunset stuff in d3
<HTML>
<HEAD>
<TITLE>NOAA Solar Position Calculator</TITLE>
<SCRIPT LANGUAGE="JavaScript">
//***********************************************************************/
//* DATA STRUCTURES */
//***********************************************************************/
function month(name, numdays, abbr)
{
this.name = name;
this.numdays = numdays;
this.abbr = abbr;
}
//*********************************************************************/
function ans(daySave,value)
{
this.daySave = daySave;
this.value = value;
}
//*********************************************************************/
function city(name, lat, lng, zoneHr)
{
this.name = name;
this.lat = lat;
this.lng = lng;
this.zoneHr = zoneHr;
}
//***********************************************************************/
//* Data for Selectbox Controls */
//***********************************************************************/
var monthList = new Array(); // list of months and days for non-leap year
var i = 0;
monthList[i++] = new month("January", 31, "Jan");
monthList[i++] = new month("February", 28, "Feb");
monthList[i++] = new month("March", 31, "Mar");
monthList[i++] = new month("April", 30, "Apr");
monthList[i++] = new month("May", 31, "May");
monthList[i++] = new month("June", 30, "Jun");
monthList[i++] = new month("July", 31, "Jul");
monthList[i++] = new month("August", 31, "Aug");
monthList[i++] = new month("September", 30, "Sep");
monthList[i++] = new month("October", 31, "Oct");
monthList[i++] = new month("November", 30, "Nov");
monthList[i++] = new month("December", 31, "Dec");
//*********************************************************************/
var YesNo = new Array(); //Daylight Saving array
i=0;
YesNo[i++] = new ans("No",0);
YesNo[i++] = new ans("Yes",60);
//*********************************************************************/
var City = new Array();
j = 0;
City[j++] = new city("Enter Lat/Long -->",0,0,0);
City[j++] = new city("",0,0,0);
City[j++] = new city("US CITIES",0,0,0);
City[j++] = new city("Albuquerque, NM", 35.0833,106.65,7);
City[j++] = new city("Anchorage, AK", 61.217, 149.90,9);
City[j++] = new city("Atlanta, GA", 33.733, 84.383, 5);
City[j++] = new city("Austin, TX", 30.283, 97.733, 6);
City[j++] = new city("Birmingham, AL", 33.521, 86.8025, 6);
City[j++] = new city("Bismarck, ND", 46.817, 100.783, 6);
City[j++] = new city("Boston, MA", 42.35, 71.05, 5);
City[j++] = new city("Boulder, CO", 40.125, 105.237, 7);
City[j++] = new city("Chicago, IL", 41.85,87.65,6);
City[j++] = new city("Dallas, TX", 32.46, 96.47,6);
City[j++] = new city("Denver, CO", 39.733, 104.983, 7);
City[j++] = new city("Detroit, MI", 42.333, 83.05, 5);
City[j++] = new city("Honolulu, HI", 21.30, 157.85, 10);
City[j++] = new city("Houston, TX", 29.75, 95.35, 6);
City[j++] = new city("Indianapolis, IN", 39.767, 86.15, 5);
City[j++] = new city("Jackson, MS", 32.283, 90.183, 6);
City[j++] = new city("Kansas City, MO", 39.083, 94.567,6);
City[j++] = new city("Los Angeles, CA",34.05,118.233,8);
City[j++] = new city("Menomonee Falls, WI",43.11,88.10,6);
City[j++] = new city("Miami, FL", 25.767, 80.183,5);
City[j++] = new city("Minneapolis, MN", 44.967, 93.25, 6);
City[j++] = new city("New Orleans, LA", 29.95, 90.067, 6);
City[j++] = new city("New York City, NY", 40.7167, 74.0167, 5);
City[j++] = new city("Oklahoma City, OK", 35.483, 97.533,6);
City[j++] = new city("Philadelphia, PA", 39.95, 75.15, 5);
City[j++] = new city("Phoenix, AZ",33.433,112.067,7);
City[j++] = new city("Pittsburgh, PA",40.433,79.9833,5);
City[j++] = new city("Portland, ME", 43.666, 70.283, 5);
City[j++] = new city("Portland, OR", 45.517, 122.65, 8);
City[j++] = new city("Raleigh, NC", 35.783, 78.65, 5);
City[j++] = new city("Richmond, VA", 37.5667, 77.450, 5);
City[j++] = new city("Saint Louis, MO", 38.6167,90.1833,6);
City[j++] = new city("San Antonio, TX", 29.53, 98.47, 6);
City[j++] = new city("San Diego, CA", 32.7667, 117.2167, 8);
City[j++] = new city("San Francisco, CA",37.7667,122.4167,8);
City[j++] = new city("Seattle, WA",47.60,122.3167,8);
City[j++] = new city("Washington DC", 38.8833, 77.0333,5);
City[j++] = new city("",0,0,0);
City[j++] = new city("WORLD CITIES",0,0,0);
City[j++] = new city("Beijing, China",39.9167, -116.4167,-8);
City[j++] = new city("Berlin, Germany",52.33, -13.30, -1);
City[j++] = new city("Bombay, India", 18.9333, -72.8333, -5.5);
City[j++] = new city("Buenos Aires, Argentina", -34.60,58.45,3);
City[j++] = new city("Cairo, Egypt", 30.10,-31.3667,-2);
City[j++] = new city("Cape Town, South Africa",-33.9167,-18.3667,-2);
City[j++] = new city("Caracas, Venezuela", 10.50,66.9333,4);
City[j++] = new city("Helsinki, Finland", 60.1667, -24.9667,-2);
City[j++] = new city("Hong Kong, China", 22.25,-114.1667, -8);
City[j++] = new city("Jerusalem, Israel", 31.7833, -35.2333, -2);
City[j++] = new city("London, England", 51.50, 0.1667,0);
City[j++] = new city("Mexico City, Mexico", 19.4,99.15,6);
City[j++] = new city("Moscow, Russia", 55.75, -37.5833, -3);
City[j++] = new city("New Delhi, India",28.6, -77.2, -5.5);
City[j++] = new city("Ottawa, Canada", 45.41667,75.7,5);
City[j++] = new city("Paris, France", 48.8667, -2.667, -1);
City[j++] = new city("Rio de Janeiro, Brazil",-22.90,43.2333,3);
City[j++] = new city("Riyadh, Saudi Arabia", 24.633, -46.71667, -3);
City[j++] = new city("Rome, Italy",41.90, -12.4833,-1);
City[j++] = new city("Sydney, Australia",-33.8667,-151.2167,-10);
City[j++] = new city("Tokyo, Japan", 35.70, -139.7667, -9);
City[j++] = new city("Zurich, Switzerland", 47.3833, -8.5333,-1);
City[j++] = new city("",0,0,0);
City[j++] = new city("SURFRAD NETWORK",0,0,0);
City[j++] = new city("Goodwin Creek, MS",34.2544444,89.8738888, 6);
City[j++] = new city("Fort Peck, MT",48.310555,105.1025, 7);
City[j++] = new city("Bondville, IL",40.055277,88.371944, 6);
City[j++] = new city("Table Mountain, CO",40.125,105.23694, 7);
City[j++] = new city("Desert Rock, NV",36.626,116.018,8);
City[j++] = new city("Penn State, PA",40.72,77.93,5);
City[j++] = new city("Canaan Valley, WV", 39.1, 79.4, 5);
City[j++] = new city("Sioux Falls, SD", 43.733, 96.6233, 6);
City[j++] = new city("",0,0,0);
City[j++] = new city("ARM/CART NETWORK",0,0,0);
City[j++] = new city("Atqasuk, AK", 70.47215, 157.4078, 9);
City[j++] = new city("Barrow, AK", 71.30,156.683, 9);
City[j++] = new city("Manus Island, PNG", -2.06, -147.425,-10);
City[j++] = new city("Nauru Island", -0.52, -166.92, -12);
City[j++] = new city("Darwin, Australia", -12.425, -130.891, -9.5);
City[j++] = new city("SGP Central Facility", 36.6167, 97.5, 6);
City[j++] = new city("",0,0,0);
City[j++] = new city("ISIS NETWORK",0,0,0);
City[j++] = new city("Albuquerque, NM", 35.04, 106.62,7);
City[j++] = new city("Bismarck, ND", 46.77, 100.77,6);
City[j++] = new city("Hanford, CA", 36.31, 119.63,8);
City[j++] = new city("Madison, WI", 43.13, 89.33,6);
City[j++] = new city("Oak Ridge, TN", 35.96, 84.37,5);
City[j++] = new city("Salt Lake City, UT", 40.77,111.97,7);
City[j++] = new city("Seattle, WA", 47.68, 122.25,8);
City[j++] = new city("Sterling, VA", 38.98, 77.47,5);
City[j++] = new city("Tallahassee, FL", 30.38, 84.37,5);
//*********************************************************************/
</SCRIPT>
</HEAD>
<BODY BACKGROUND="sky3.jpg" bgcolor="#99CCFF">
<CENTER>
<TABLE BORDER=0 CELLPADDING=5>
<TR><TD>
<A HREF="http://www.doc.gov"><IMG SRC="doclogo.gif" ALT="Dept of Commerce Seal" BORDER=0></A>
</TD>
<TD>
<CENTER>
<FONT SIZE="4"><B><A HREF="/index.html" target="_top">NOAA ESRL</A></B></FONT><BR>
<FONT SIZE="6"><B>Solar Position Calculator</B></FONT>
</CENTER>
</TD>
<TD>
<CENTER>
<A HREF="http://www.noaa.gov"><IMG SRC="noaaemblemt.gif" ALT="National Oceanic and Atmospheric Administration" BORDER=0></A><BR>
<A HREF="http://www.esrl.noaa.gov"><FONT SIZE="2">Earth System<br>Research Lab</FONT></A>
</CENTER>
</TD>
</TR>
</TABLE>
<HR>
<FONT SIZE="6">
*** <A HREF="http://www.esrl.noaa.gov/gmd/grad/solcalc/">Click here to try NOAA's New Solar Calculator</A> ***
</FONT>
</CENTER>
<HR>
<SCRIPT LANGUAGE="JavaScript">
function setLatLong(f, index)
{
//Decimal degrees are passed in the array. Temporarily store these decimal
// degs in lat and lon deg and have convLatLong modify them.
f["latDeg"].value = City[index].lat;
f["lonDeg"].value = City[index].lng;
//These are needed to prevent iterative adding of min and sec when set
//button is clicked.
f["latMin"].value = 0;
f["latSec"].value = 0;
f["lonMin"].value = 0;
f["lonSec"].value = 0;
//call convLatLong to convert decimal degrees into table form.
convLatLong(f);
//Local time zone value set in table
f["hrsToGMT"].value = City[index].zoneHr;
}
// 'isLeapYear' returns '1' if the yr is a leap year, '0' if it is not.
function isLeapYear(yr)
{
return ((yr % 4 == 0 && yr % 100 != 0) || yr % 400 == 0);
}
//*********************************************************************/
// isPosInteger returns false if the value is not a positive integer, true is
// returned otherwise. The code is from taken from Danny Goodman's Javascript
// Handbook, p. 372.
function isPosInteger(inputVal)
{
inputStr = "" + inputVal;
for (var i = 0; i < inputStr.length; i++) {
var oneChar = inputStr.charAt(i);
if (oneChar < "0" || oneChar > "9")
return false;
}
return true;
}
//*********************************************************************/
function isInteger(inputVal)
{
inputStr = "" + inputVal;
if(inputStr == "NaN") return false;
if(inputStr == "-NaN") return false;
for (var i = 0; i < inputStr.length; i++)
{
var oneChar = inputStr.charAt(i);
if (i == 0 && (oneChar == "-" || oneChar == "+"))
{
continue;
}
if (oneChar < "0" || oneChar > "9")
{
return false;
}
}
return true;
}
//*********************************************************************/
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;
}
// 'isValidInput' makes sure valid input is entered before calculating
// the sunrise and sunset. False is returned if an invalid entry was made,
// true if the entry is valid.
function isValidInput(f, index, latLongForm)
{
if (f["day"].value == "") { // see if the day field is empty
alert("You must enter a day before attempting the calculation.");
return false;
}
else if (f["year"].value == "") { // see if year field is empty
alert("You must enter a year before attempting the calculation.");
return false;
}
else if (!isPosInteger(f["day"].value) || f["day"].value == 0)
{
alert("The day must be a positive integer.");
return false;
}
else if (!isPosInteger(f["year"].value)) {
alert("The year must be a positive integer.");
return false;
}
else if (f["hour"].value == "") { // see if hour field is empty
alert("You must enter a time before attempting the calculation.");
return false;
}
else if (!isPosInteger(f["hour"].value) ||
!isPosInteger(f["mins"].value) ||
!isPosInteger(f["secs"].value))
{
alert("The time fields must contain positive integers.");
return false;
}
// else if ( ((f["ampm"].value == "AM") || (f["ampm"].value == "PM")) && (f["hour"].value > 12) )
// {
// alert("AM/PM hour must be between 0 and 12.");
// return false;
// }
// else if ( (f["ampm"].value == "24") && (f["hour"].value > 23) )
// {
// alert("Hour must be between 0 and 23.");
// return false;
// }
else if ( (f["hour"].value > 23) )
{
alert("Hour must be between 0 and 23.");
return false;
}
else if (f["mins"].value > 59)
{
alert("Minutes must be between 0 and 59.");
return false;
}
else if (f["secs"].value > 59)
{
alert("Seconds must be between 0 and 59.");
return false;
}
// For the non-February months see if the day entered is greater than
// the number of days in the selected month
else if ((index != 1) && (f["day"].value > monthList[index].numdays))
{
alert("There are only " + monthList[index].numdays +
" days in " + monthList[index].name + ".");
return false;
}
// First see if the year entered is a leap year. If so we have to make sure
// the days entered is <= 29. If not a leap year we make sure that the days
// entered is <= 28.
else if (index == 1) { // month selected is February
if (isLeapYear(f["year"].value)) { // year is a leap year
if (f["day"].value > (monthList[index].numdays + 1)) {
alert("There are only " +
(monthList[index].numdays + 1) +
" days in " + monthList[index].name + ".");
return false;
}
else
return true;
}
else { // year entered is not a leap year
if (f["day"].value > monthList[index].numdays) {
alert("There are only " +
monthList[index].numdays +
" days in " + monthList[index].name + ".");
return false;
}
else
return true;
}
}
else
return true;
}
//convLatLong converts any type of lat/long input
//into the table form and then handles bad input
//it is nested in the calcSun function.
function convLatLong(f){
if(f["latDeg"].value == "")
{
f["latDeg"].value = 0;
}
if(f["latMin"].value == "")
{
f["latMin"].value = 0;
}
if(f["latSec"].value == "")
{
f["latSec"].value = 0;
}
if(f["lonDeg"].value == "")
{
f["lonDeg"].value = 0;
}
if(f["lonMin"].value == "")
{
f["lonMin"].value = 0;
}
if(f["lonSec"].value == "")
{
f["lonSec"].value = 0;
}
var neg = 0;
if(f["latDeg"].value.charAt(0) == '-') {
neg = 1;
}
if(neg != 1)
{
var latSeconds = (parseFloat(f["latDeg"].value))*3600 + parseFloat(f["latMin"].value)*60 + parseFloat(f["latSec"].value)*1;
f["latDeg"].value = Math.floor(latSeconds/3600);
f["latMin"].value = Math.floor((latSeconds-(parseFloat(f["latDeg"].value)*3600))/60);
f["latSec"].value = Math.floor((latSeconds-(parseFloat(f["latDeg"].value)*3600)- (parseFloat(f["latMin"].value)*60)) + 0.5);
}
else if(parseFloat(f["latDeg"].value) > -1)
{
var latSeconds = parseFloat(f["latDeg"].value)*3600 - parseFloat(f["latMin"].value)*60 - parseFloat(f["latSec"].value)*1;
f["latDeg"].value = "-0";
f["latMin"].value = Math.floor((-latSeconds)/60);
f["latSec"].value = Math.floor( (-latSeconds-(parseFloat(f["latMin"].value)*60)) + 0.5);
}
else
{
var latSeconds = parseFloat(f["latDeg"].value)*3600 - parseFloat(f["latMin"].value)*60 - parseFloat(f["latSec"].value)*1;
f["latDeg"].value = Math.ceil(latSeconds/3600);
f["latMin"].value = Math.floor((-latSeconds+(parseFloat(f["latDeg"].value)*3600))/60);
f["latSec"].value = Math.floor((-latSeconds+(parseFloat(f["latDeg"].value)*3600) - (parseFloat(f["latMin"].value)*60)) + 0.5);
}
neg = 0;
if(f["lonDeg"].value.charAt(0) == '-') {
neg = 1;
}
if(neg != 1)
{
var lonSeconds = parseFloat(f["lonDeg"].value)*3600 + parseFloat(f["lonMin"].value)*60 + parseFloat(f["lonSec"].value)*1;
f["lonDeg"].value = Math.floor(lonSeconds/3600);
f["lonMin"].value = Math.floor((lonSeconds-(parseFloat(f["lonDeg"].value)*3600))/60);
f["lonSec"].value = Math.floor((lonSeconds-(parseFloat(f["lonDeg"].value)*3600)- (parseFloat(f["lonMin"].value))*60) + 0.5);
}
else if(parseFloat(f["lonDeg"].value) > -1)
{
var lonSeconds = parseFloat(f["lonDeg"].value)*3600 - parseFloat(f["lonMin"].value)*60 - parseFloat(f["lonSec"].value)*1;
f["lonDeg"].value = "-0";
f["lonMin"].value = Math.floor((-lonSeconds)/60);
f["lonSec"].value = Math.floor( (-lonSeconds-(parseFloat(f["lonMin"].value)*60)) + 0.5);
}
else
{
var lonSeconds = parseFloat(f["lonDeg"].value)*3600 - parseFloat(f["lonMin"].value)*60 - parseFloat(f["lonSec"].value)*1;
f["lonDeg"].value = Math.ceil(lonSeconds/3600);
f["lonMin"].value = Math.floor((-lonSeconds+(parseFloat(f["lonDeg"].value)*3600))/60);
f["lonSec"].value = Math.floor((-lonSeconds+(parseFloat(f["lonDeg"].value)*3600)-(parseFloat(f["lonMin"].value)*60)) + 0.5);
}
//Test for invalid lat/long input
if(latSeconds > 323280){
alert("You have entered an invalid latitude.\nSetting lat=89.8.");
f["latDeg"].value = 89.8;
f["latMin"].value = 0;
f["latSec"].value = 0;
}
if(latSeconds < -323280){
alert("You have entered an invalid latitude.\n Setting lat= -89.8.");
f["latDeg"].value = -89.8;
f["latMin"].value = 0;
f["latSec"].value = 0;
}
if(lonSeconds > 648000){
alert("You have entered an invalid longitude.\n Setting lon= 180.");
f["lonDeg"].value = 180;
f["lonMin"].value = 0;
f["lonSec"].value = 0;
}
if(lonSeconds < -648000){
alert("You have entered an invalid latitude.\n Setting lon= -180.");
f["lonDeg"].value = -180;
f["lonMin"].value = 0;
f["lonSec"].value =0;
}
}
//***********************************************************************/
//***********************************************************************/
//* */
//*This section contains subroutines used in calculating solar position */
//* */
//***********************************************************************/
//***********************************************************************/
// Convert radian angle to degrees
function radToDeg(angleRad)
{
return (180.0 * angleRad / Math.PI);
}
//*********************************************************************/
// Convert degree angle to radians
function degToRad(angleDeg)
{
return (Math.PI * angleDeg / 180.0);
}
//*********************************************************************/
//***********************************************************************/
//* Name: calcDayOfYear */
//* Type: Function */
//* Purpose: Finds numerical day-of-year from mn, day and lp year info */
//* Arguments: */
//* month: January = 1 */
//* day : 1 - 31 */
//* lpyr : 1 if leap year, 0 if not */
//* Return value: */
//* The numerical day of year */
//***********************************************************************/
function calcDayOfYear(mn, dy, lpyr)
{
var k = (lpyr ? 1 : 2);
var doy = Math.floor((275 * mn)/9) - k * Math.floor((mn + 9)/12) + dy -30;
return doy;
}
//***********************************************************************/
//* Name: calcDayOfWeek */
//* Type: Function */
//* Purpose: Derives weekday from Julian Day */
//* Arguments: */
//* juld : Julian Day */
//* Return value: */
//* String containing name of weekday */
//***********************************************************************/
function calcDayOfWeek(juld)
{
var A = (juld + 1.5) % 7;
var DOW = (A==0)?"Sunday":(A==1)?"Monday":(A==2)?"Tuesday":(A==3)?"Wednesday":(A==4)?"Thursday":(A==5)?"Friday":"Saturday";
return DOW;
}
//***********************************************************************/
//* Name: calcJD */
//* Type: Function */
//* Purpose: Julian day from calendar day */
//* Arguments: */
//* year : 4 digit year */
//* month: January = 1 */
//* day : 1 - 31 */
//* Return value: */
//* The Julian day corresponding to the date */
//* Note: */
//* Number is returned for start of day. Fractional days should be */
//* added later. */
//***********************************************************************/
function calcJD(year, month, day)
{
if (month <= 2) {
year -= 1;
month += 12;
}
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;
}
//***********************************************************************/
//* Name: calcDateFromJD */
//* Type: Function */
//* Purpose: Calendar date from Julian Day */
//* Arguments: */
//* jd : Julian Day */
//* Return value: */
//* String date in the form DD-MONTHNAME-YYYY */
//* Note: */
//***********************************************************************/
function calcDateFromJD(jd)
{
var z = Math.floor(jd + 0.5);
var f = (jd + 0.5) - z;
if (z < 2299161) {
var A = z;
} else {
alpha = Math.floor((z - 1867216.25)/36524.25);
var 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;
// alert ("date: " + day + "-" + monthList[month-1].name + "-" + year);
return (day + "-" + monthList[month-1].name + "-" + year);
}
//***********************************************************************/
//* Name: calcDayFromJD */
//* Type: Function */
//* Purpose: Calendar day (minus year) from Julian Day */
//* Arguments: */
//* jd : Julian Day */
//* Return value: */
//* String date in the form DD-MONTH */
//***********************************************************************/
function calcDayFromJD(jd)
{
var z = Math.floor(jd + 0.5);
var f = (jd + 0.5) - z;
if (z < 2299161) {
var A = z;
} else {
alpha = Math.floor((z - 1867216.25)/36524.25);
var 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 ((day<10 ? "0" : "") + day + monthList[month-1].abbr);
}
//***********************************************************************/
//* Name: calcTimeJulianCent */
//* Type: Function */
//* Purpose: convert Julian Day to centuries since J2000.0. */
//* Arguments: */
//* jd : the Julian Day to convert */
//* Return value: */
//* the T value corresponding to the Julian Day */
//***********************************************************************/
function calcTimeJulianCent(jd)
{
var T = (jd - 2451545.0)/36525.0;
return T;
}
//***********************************************************************/
//* Name: calcJDFromJulianCent */
//* Type: Function */
//* Purpose: convert centuries since J2000.0 to Julian Day. */
//* Arguments: */
//* t : number of Julian centuries since J2000.0 */
//* Return value: */
//* the Julian Day corresponding to the t value */
//***********************************************************************/
function calcJDFromJulianCent(t)
{
var JD = t * 36525.0 + 2451545.0;
return JD;
}
//***********************************************************************/
//* Name: calGeomMeanLongSun */
//* Type: Function */
//* Purpose: calculate the Geometric Mean Longitude of the Sun */
//* Arguments: */
//* t : number of Julian centuries since J2000.0 */
//* Return value: */
//* the Geometric Mean Longitude of the Sun in degrees */
//***********************************************************************/
function calcGeomMeanLongSun(t)
{
var L0 = 280.46646 + t * (36000.76983 + 0.0003032 * t);
while(L0 > 360.0)
{
L0 -= 360.0;
}
while(L0 < 0.0)
{
L0 += 360.0;
}
return L0; // in degrees
}
//***********************************************************************/
//* Name: calGeomAnomalySun */
//* Type: Function */
//* Purpose: calculate the Geometric Mean Anomaly of the Sun */
//* Arguments: */
//* t : number of Julian centuries since J2000.0 */
//* Return value: */
//* the Geometric Mean Anomaly of the Sun in degrees */
//***********************************************************************/
function calcGeomMeanAnomalySun(t)
{
var M = 357.52911 + t * (35999.05029 - 0.0001537 * t);
return M; // in degrees
}
//***********************************************************************/
//* Name: calcEccentricityEarthOrbit */
//* Type: Function */
//* Purpose: calculate the eccentricity of earth's orbit */
//* Arguments: */
//* t : number of Julian centuries since J2000.0 */
//* Return value: */
//* the unitless eccentricity */
//***********************************************************************/
function calcEccentricityEarthOrbit(t)
{
var e = 0.016708634 - t * (0.000042037 + 0.0000001267 * t);
return e; // unitless
}
//***********************************************************************/
//* Name: calcSunEqOfCenter */
//* Type: Function */
//* Purpose: calculate the equation of center for the sun */
//* Arguments: */
//* t : number of Julian centuries since J2000.0 */
//* Return value: */
//* in degrees */
//***********************************************************************/
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
}
//***********************************************************************/
//* Name: calcSunTrueLong */
//* Type: Function */
//* Purpose: calculate the true longitude of the sun */
//* Arguments: */
//* t : number of Julian centuries since J2000.0 */
//* Return value: */
//* sun's true longitude in degrees */
//***********************************************************************/
function calcSunTrueLong(t)
{
var l0 = calcGeomMeanLongSun(t);
var c = calcSunEqOfCenter(t);
var O = l0 + c;
return O; // in degrees
}
//***********************************************************************/
//* Name: calcSunTrueAnomaly */
//* Type: Function */
//* Purpose: calculate the true anamoly of the sun */
//* Arguments: */
//* t : number of Julian centuries since J2000.0 */
//* Return value: */
//* sun's true anamoly in degrees */
//***********************************************************************/
function calcSunTrueAnomaly(t)
{
var m = calcGeomMeanAnomalySun(t);
var c = calcSunEqOfCenter(t);
var v = m + c;
return v; // in degrees
}
//***********************************************************************/
//* Name: calcSunRadVector */
//* Type: Function */
//* Purpose: calculate the distance to the sun in AU */
//* Arguments: */
//* t : number of Julian centuries since J2000.0 */
//* Return value: */
//* sun radius vector in AUs */
//***********************************************************************/
function calcSunRadVector(t)
{
var v = calcSunTrueAnomaly(t);
var e = calcEccentricityEarthOrbit(t);
var R = (1.000001018 * (1 - e * e)) / (1 + e * Math.cos(degToRad(v)));
return R; // in AUs
}
//***********************************************************************/
//* Name: calcSunApparentLong */
//* Type: Function */
//* Purpose: calculate the apparent longitude of the sun */
//* Arguments: */
//* t : number of Julian centuries since J2000.0 */
//* Return value: */
//* sun's apparent longitude 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
}
//***********************************************************************/
//* Name: calcMeanObliquityOfEcliptic */
//* Type: Function */
//* Purpose: calculate the mean obliquity of the ecliptic */
//* Arguments: */
//* t : number of Julian centuries since J2000.0 */
//* Return value: */
//* mean obliquity in degrees */
//***********************************************************************/
function calcMeanObliquityOfEcliptic(t)
{
var seconds = 21.448 - t*(46.8150 + t*(0.00059 - t*(0.001813)));
var e0 = 23.0 + (26.0 + (seconds/60.0))/60.0;
return e0; // in degrees
}
//***********************************************************************/
//* Name: calcObliquityCorrection */
//* Type: Function */
//* Purpose: calculate the corrected obliquity of the ecliptic */
//* Arguments: */
//* t : number of Julian centuries since J2000.0 */
//* Return value: */
//* corrected obliquity 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
}
//***********************************************************************/
//* Name: calcSunRtAscension */
//* Type: Function */
//* Purpose: calculate the right ascension of the sun */
//* Arguments: */
//* t : number of Julian centuries since J2000.0 */
//* Return value: */
//* sun's right ascension in degrees */
//***********************************************************************/
function calcSunRtAscension(t)
{
var e = calcObliquityCorrection(t);
var lambda = calcSunApparentLong(t);
var tananum = (Math.cos(degToRad(e)) * Math.sin(degToRad(lambda)));
var tanadenom = (Math.cos(degToRad(lambda)));
var alpha = radToDeg(Math.atan2(tananum, tanadenom));
return alpha; // in degrees
}
//***********************************************************************/
//* Name: calcSunDeclination */
//* Type: Function */
//* Purpose: calculate the declination of the sun */
//* Arguments: */
//* t : number of Julian centuries since J2000.0 */
//* Return value: */
//* sun's declination 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
}
//***********************************************************************/
//* Name: calcEquationOfTime */
//* Type: Function */
//* Purpose: calculate the difference between true solar time and mean */
//* solar time */
//* Arguments: */
//* t : number of Julian centuries since J2000.0 */
//* Return value: */
//* equation of time in minutes of time */
//***********************************************************************/
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.0);
y *= y;
var sin2l0 = Math.sin(2.0 * degToRad(l0));
var sinm = Math.sin(degToRad(m));
var cos2l0 = Math.cos(2.0 * degToRad(l0));
var sin4l0 = Math.sin(4.0 * degToRad(l0));
var sin2m = Math.sin(2.0 * degToRad(m));
var Etime = y * sin2l0 - 2.0 * e * sinm + 4.0 * e * y * sinm * cos2l0
- 0.5 * y * y * sin4l0 - 1.25 * e * e * sin2m;
return radToDeg(Etime)*4.0; // in minutes of time
}
// Return the hour angle for the given location, decl, and time of day
function calcHourAngle(time, longitude, eqtime)
{
return (15.0*(time - (longitude/15.0) - (eqtime/60.0)));
// in degrees
}
// Returns decimal latitude from deg, min, sec entered in the form
function getLatitude(latLongForm)
{
var neg = 0;
var degs = parseFloat(latLongForm["latDeg"].value);
if (latLongForm["latDeg"].value.charAt(0) == '-') {
neg = 1;
}
var mins = parseFloat(latLongForm["latMin"].value);
var secs = parseFloat(latLongForm["latSec"].value);
if(neg != 1){
var decLat = degs + (mins / 60) + (secs / 3600);
} else if(neg == 1){
var decLat = degs - (mins / 60) - (secs / 3600);
} else {
return -9999;
}
return decLat;
}
// Returns decimal longitude from the deg, min, sec entered in the form
function getLongitude(latLongForm)
{
var neg = 0;
var degs = parseFloat(latLongForm["lonDeg"].value);
if (latLongForm["lonDeg"].value.charAt(0) == '-') {
neg = 1;
}
var mins = parseFloat(latLongForm["lonMin"].value);
var secs = parseFloat(latLongForm["lonSec"].value);
var decLon = degs + (mins / 60) + (secs / 3600);
if(neg != 1){
var decLon = degs + (mins / 60) + (secs / 3600);
} else if(neg == 1){
var decLon = degs - (mins / 60) - (secs / 3600);
} else {
return -9999;
}
return decLon;
}
// *****************************************************
// *****************************************************
// *********** Main calculation routine **************
// *****************************************************
// *****************************************************
//***********************************************************************/
//* Name: calcSun */
//* Type: Main Function called by form controls */
//* Purpose: calculate solar position for the entered date, time and */
//* location. Results are reported in azimuth and elevation */
//* (in degrees) and cosine of solar zenith angle. */
//* Arguments: */
//* riseSetForm : for displaying results */
//* latLongForm : for reading latitude and longitude data */
//* index : daylight saving yes/no select */
//* index2 : city select index */
//* Return value: */
//* none */
//* (fills riseSetForm text fields with results of calculations) */
//***********************************************************************/
function calcSun(riseSetForm, latLongForm, index, index2)
{
if(index2 != 0)
{
setLatLong(latLongForm, index2);
}
var latitude = getLatitude(latLongForm);
var longitude = getLongitude(latLongForm);
var indexRS = riseSetForm.mos.selectedIndex
if (isValidInput(riseSetForm, indexRS, latLongForm))
{
if((latitude >= -90) && (latitude < -89.8))
{
alert("All latitudes between 89.8 and 90 S\n will be set to -89.8.");
latLongForm["latDeg"].value = -89.8;
latitude = -89.8;
}
if ((latitude <= 90) && (latitude > 89.8))
{
alert("All latitudes between 89.8 and 90 N\n will be set to 89.8.");
latLongForm["latDeg"].value = 89.8;
latitude = 89.8;
}
//***** Get calc date/time
// var julDay = calcJulianDay(indexRS,
// parseFloat(riseSetForm["day"].value),
// isLeapYear(riseSetForm["year"].value));
var zone = parseFloat(latLongForm["hrsToGMT"].value);
var daySavings = YesNo[index].value; // = 0 (no) or 60 (yes)
if(zone > 12 || zone < -12.5)
{
alert("The offset must be between -12.5 and 12. \n Setting \"Off-Set\"=0");
zone = "0";
latLongForm["hrsToGMT"].value = zone;
}
var ss = parseFloat(riseSetForm["secs"].value);
var mm = parseFloat(riseSetForm["mins"].value);
var hh = parseFloat(riseSetForm["hour"].value) - (daySavings/60);
if(riseSetForm.ampm[1].checked)
{
hh += 12;
}
while (hh > 23)
{
hh -= 24;
}
riseSetForm["hour"].value = hh + (daySavings/60);
if (mm > 9)
{
riseSetForm["mins"].value = mm;
}
else
{
riseSetForm["mins"].value = "0" + mm;
}
if (ss > 9)
{
riseSetForm["secs"].value = ss;
}
else
{
riseSetForm["secs"].value = "0" + ss;
}
riseSetForm.ampm[2].checked = 1;
// timenow is GMT time for calculation
timenow = hh + mm/60 + ss/3600 + zone; // in hours since 0Z
//alert("timenow = " + timenow);
var JD = (calcJD(parseFloat(riseSetForm["year"].value), indexRS + 1, parseFloat(riseSetForm["day"].value)));
var dow = calcDayOfWeek(JD);
var doy = calcDayOfYear(indexRS + 1, parseFloat(riseSetForm["day"].value), isLeapYear(riseSetForm["year"].value));
var T = calcTimeJulianCent(JD + timenow/24.0);
//var L0 = calcGeomMeanLongSun(T);
//var M = calcGeomMeanAnomalySun(T);
//var e = calcEccentricityEarthOrbit(T);
//var C = calcSunEqOfCenter(T);
//var O = calcSunTrueLong(T);
//var v = calcSunTrueAnomaly(T);
var R = calcSunRadVector(T);
//var lambda = calcSunApparentLong(T);
//var epsilon0 = calcMeanObliquityOfEcliptic(T);
//var epsilon = calcObliquityCorrection(T);
var alpha = calcSunRtAscension(T);
var theta = calcSunDeclination(T);
var Etime = calcEquationOfTime(T);
var eqTime = Etime;
var solarDec = theta; // in degrees
var earthRadVec = R;
riseSetForm["eqTime"].value = (Math.floor(100*eqTime))/100;
riseSetForm["solarDec"].value = (Math.floor(100*(solarDec)))/100;
var solarTimeFix = eqTime - 4.0 * longitude + 60.0 * zone;
var trueSolarTime = hh * 60.0 + mm + ss/60.0 + solarTimeFix;
// in minutes
while (trueSolarTime > 1440)
{
trueSolarTime -= 1440;
}
//var hourAngle = calcHourAngle(timenow, longitude, eqTime);
var hourAngle = trueSolarTime / 4.0 - 180.0;
// Thanks to Louis Schwarzmayr for finding our error,
// and providing the following 4 lines to fix it:
if (hourAngle < -180)
{
hourAngle += 360.0;
}
// alert ("Hour Angle = " + hourAngle);
var haRad = degToRad(hourAngle);
var csz = Math.sin(degToRad(latitude)) *
Math.sin(degToRad(solarDec)) +
Math.cos(degToRad(latitude)) *
Math.cos(degToRad(solarDec)) * Math.cos(haRad);
if (csz > 1.0)
{
csz = 1.0;
} else if (csz < -1.0)
{
csz = -1.0;
}
var zenith = radToDeg(Math.acos(csz));
var azDenom = ( Math.cos(degToRad(latitude)) *
Math.sin(degToRad(zenith)) );
if (Math.abs(azDenom) > 0.001) {
azRad = (( Math.sin(degToRad(latitude)) *
Math.cos(degToRad(zenith)) ) -
Math.sin(degToRad(solarDec))) / azDenom;
if (Math.abs(azRad) > 1.0) {
if (azRad < 0) {
azRad = -1.0;
} else {
azRad = 1.0;
}
}
var azimuth = 180.0 - radToDeg(Math.acos(azRad));
if (hourAngle > 0.0) {
azimuth = -azimuth;
}
} else {
if (latitude > 0.0) {
azimuth = 180.0;
} else {
azimuth = 0.0;
}
}
if (azimuth < 0.0) {
azimuth += 360.0;
}
exoatmElevation = 90.0 - zenith;
if (exoatmElevation > 85.0) {
refractionCorrection = 0.0;
} else {
te = Math.tan (degToRad(exoatmElevation));
if (exoatmElevation > 5.0) {
refractionCorrection = 58.1 / te - 0.07 / (te*te*te) +
0.000086 / (te*te*te*te*te);
} else if (exoatmElevation > -0.575) {
refractionCorrection = 1735.0 + exoatmElevation *
(-518.2 + exoatmElevation * (103.4 +
exoatmElevation * (-12.79 +
exoatmElevation * 0.711) ) );
} else {
refractionCorrection = -20.774 / te;
}
refractionCorrection = refractionCorrection / 3600.0;
}
solarZen = zenith - refractionCorrection;
if(solarZen < 108.0) { // astronomical twilight
riseSetForm["azimuth"].value = (Math.floor(100*
azimuth))/100;
riseSetForm["elevation"].value = (Math.floor(100*
(90.0 - solarZen)))/100;
if (solarZen < 90.0) {
riseSetForm["coszen"].value =
(Math.floor(10000.0*(Math.cos(degToRad(solarZen)))))/10000.0;
} else {
riseSetForm["coszen"].value = 0.0;
}
} else { // do not report az & el after astro twilight
riseSetForm["azimuth"].value = "dark";
riseSetForm["elevation"].value = "dark";
riseSetForm["coszen"].value = 0.0;
}
//***********Conv lat and long
convLatLong(latLongForm);
} else { // end of IF ISVALIDINPUT
riseSetForm["azimuth"].value = "error";
riseSetForm["elevation"].value = "error";
riseSetForm["eqTime"].value = "error";
riseSetForm["solarDec"].value = "error";
riseSetForm["coszen"].value = "error";
// alert("Invalid Input");
}
}
</SCRIPT>
<FORM NAME="cityLatLong">
<CENTER><TABLE BORDER>
<TR>
<TD ALIGN="CENTER"><H5>City:</TD>
<TD></TD>
<TD ALIGN="CENTER"><H5>Deg:</TD>
<TD ALIGN="CENTER"><H5>Min:</TD>
<TD ALIGN="CENTER"><H5>Sec:</TD>
<TD ALIGN="CENTER" COLSPAN="2"><H5><a href="./timezone.html">Time Zone</TD>
</TR>
<TR>
<TD ALIGN="TOP">
<CENTER>
<SELECT NAME="cities" onChange="calcSun(solPosCalc, cityLatLong, cityLatLong.dayAns.selectedIndex, cityLatLong.cities.selectedIndex);">
<SCRIPT LANGUAGE="JavaScript">
for (i = 0; i < City.length; i++) {
if(City[i].name == "Boulder, CO")
{
document.writeln("<OPTION SELECTED>" + City[i].name);
}
else
document.writeln("<OPTION>" + City[i].name);
}
</SCRIPT>
</SELECT>
</CENTER>
</TD>
<TD ALIGN="CENTER"><H5><a href="./glossary.html#latitude">Lat</a>:
<BR>North=+<BR>South=<FONT SIZE = 4>-</FONT>
</TD>
<TD><INPUT TYPE="text" NAME="latDeg" VALUE="40" SIZE="4"></TD>
<TD><INPUT TYPE="text" NAME="latMin" VALUE="7" SIZE="4"></TD>
<TD><INPUT TYPE="text" NAME="latSec" VALUE="30" SIZE="4"></TD>
<TD ROWSPAN=2 ALIGN="CENTER"><H5>Offset<BR>to
<a href="./glossary.html#UTC">UTC</a><BR>(MST=+7):<BR></H5>
<INPUT TYPE="text" NAME="hrsToGMT" VALUE="7" SIZE="5"></TD>
<TD ROWSPAN=2 ALIGN = "CENTER"><H5>
<a href="./glossary.html#daylightsavingtime">Daylight<BR>Saving<BR>Time</a>:
<BR></H5>
<SELECT NAME="dayAns" onChange="calcSun(solPosCalc, cityLatLong, cityLatLong.dayAns.selectedIndex, cityLatLong.cities.selectedIndex);">
<SCRIPT LANGUAGE="JavaScript">
for(i=0; i < YesNo.length; i++)
{
document.writeln("<OPTION>" + YesNo[i].daySave);
}
</SCRIPT>
</SELECT>
</TD>
</TR>
<TR>
<TD ALIGN="CENTER"><H5><a href="./sollinks.html#latlong">Click here for help finding<br>
your lat/long coordinates</a>
</TD>
<TD ALIGN="CENTER"><H5><a href="./glossary.html#longitude">Long</a>:
<BR>East=<FONT SIZE = 4>-</FONT><BR>West=+
</TD>
<TD><INPUT TYPE="text" NAME="lonDeg" VALUE="105" SIZE="4"></TD>
<TD><INPUT TYPE="text" NAME="lonMin" VALUE="14" SIZE="4"></TD>
<TD><INPUT TYPE="text" NAME="lonSec" VALUE="13" SIZE="4"></TD>
</TR>
<TR>
<TD COLSPAN=7>
<b>Note:</b> To manually enter latitude/longitude, select <b>Enter
Lat/Long -></b> from the<br>City pulldown box, and enter the
values in the text boxes to the right.
</TD>
</TR>
</TABLE>
</FORM>
<FORM NAME="solPosCalc">
<CENTER><TABLE BORDER>
<TR>
<TD ALIGN="CENTER"><H5>Month:</TD>
<TD ALIGN="CENTER"><H5>Day:</TD>
<TD ALIGN="CENTER"><H5>Year (e.g. 2000):</TD>
<TD ALIGN="CENTER"><H5>Time: (hh:mm:ss)</TD>
</TR>
<TR>
<TD>
<SELECT NAME="mos" onChange="calcSun(solPosCalc, cityLatLong, cityLatLong.dayAns.selectedIndex, cityLatLong.cities.selectedIndex);">
<SCRIPT LANGUAGE="JavaScript">
dateObj1 = new Date();
thismonth = dateObj1.getMonth();
today = dateObj1.getDate();
for (i = 0; i < monthList.length; i++) {
if (i == thismonth) {
document.writeln("<OPTION SELECTED>" + monthList[i].name);
}
else {
document.writeln("<OPTION>" + monthList[i].name);
}
}
</SCRIPT>
</SELECT>
</TD>
<SCRIPT LANGUAGE="JavaScript">
dateObj2 = new Date();
thisday = dateObj2.getDate();
document.writeln("<TD><CENTER><INPUT TYPE=\"text\" NAME=\"day\" SIZE=\"2\" VALUE=\"" + thisday + "\"></TD>");
</SCRIPT>
<SCRIPT LANGUAGE="JavaScript">
dateObj3 = new Date();
thisYear = dateObj3.getYear();
if(thisYear <=199){
thisYear=(thisYear*1)+1900;
}
document.writeln("<TD><CENTER><INPUT TYPE=\"text\" NAME=\"year\" SIZE=\"4\"VALUE=\"" + thisYear*1 + "\"></TD>");
</SCRIPT>
<TD><CENTER>
<SCRIPT LANGUAGE="JavaScript">
dateObj4 = new Date();
nowhour = dateObj4.getHours();
nowmins = dateObj4.getMinutes();
nowsecs = dateObj4.getSeconds();
document.writeln("<INPUT TYPE=\"text\" NAME=\"hour\" SIZE=\"2\"VALUE=\"" + nowhour*1 + "\"> <b>:</b>");
document.writeln("<INPUT TYPE=\"text\" NAME=\"mins\" SIZE=\"2\"VALUE=\"" + nowmins*1 + "\"> <b>:</b>");
document.writeln("<INPUT TYPE=\"text\" NAME=\"secs\" SIZE=\"2\"VALUE=\"" + nowsecs*1 + "\">");
</SCRIPT>
<INPUT TYPE="radio" NAME="ampm" VALUE="AM">AM
<INPUT TYPE="radio" NAME="ampm" VALUE="PM">PM
<INPUT TYPE="radio" NAME="ampm" VALUE="24" CHECKED>24hr
</TD>
</TR>
</TABLE>
<br>
<INPUT TYPE="button" NAME="RiseSet" VALUE=" Calculate Solar Position " onClick="calcSun(solPosCalc, cityLatLong, cityLatLong.dayAns.selectedIndex, cityLatLong.cities.selectedIndex);">
<BR>
<BR>
<TABLE BORDER>
<TR>
<TD ALIGN="CENTER"><H5>
<a href="./glossary.html#equationoftime">Equation <BR>of Time</a>
<BR>(minutes):</TD>
<TD ALIGN="CENTER"><H5>
<a href="./glossary.html#solardeclination">Solar<BR>Declination</a><BR>
(degrees):</TD>
<TD ALIGN="CENTER"><H5>Solar<BR>
<a href="./glossary.html#azimuthelevation">Azimuth</a>:</TD>
<TD ALIGN="CENTER"><H5>Solar<BR>
<a href="./glossary.html#azimuthelevation">Elevation</a>:</TD>
<TD ALIGN="CENTER"><H5>cosine of<BR>solar
<a href="./glossary.html#zenithangle">zenith<BR>angle</a></TD>
</TR>
<TR>
<TD><CENTER><INPUT TYPE="text" NAME="eqTime" SIZE = "8"></TD>
<TD><CENTER><INPUT TYPE="text" NAME="solarDec" SIZE = "8"></TD>
<TD><CENTER><INPUT TYPE="text" NAME="azimuth" SIZE="8"></TD>
<TD><CENTER><INPUT TYPE="text" NAME="elevation" SIZE="8"></TD>
<TD><CENTER><INPUT TYPE="text" NAME="coszen" SIZE="8"></TD>
</TR>
<TR>
<TD COLSPAN="5" align="center"><H5>Azimuth is measured in degrees clockwise from north.<br>
Elevation is measured in degrees up from the horizon.<br>
Az & El both report <i>dark</i> after <a href="./glossary.html#astronomicaltwilight">astronomical twilight</a>.
</TD>
</TABLE>
</CENTER>
</FORM></FONT>
</CENTER>
<HR>
<B><H3>Directions:</H3></B><BR>
<H4>
<OL>
<LI>
<FONT SIZE=2>
Select a location from the City pulldown menu, <U>OR</U> select <b>"Enter
Lat/Long ->"</b> from the pulldown menu, and manually enter the latitude,
longitude and time zone information in the appropriate text boxes. For this calculator,
latitude is positive to the NORTH, and longitude is positive to the WEST of
the prime meridian.
</P>
<P>
Latitude and Longitude can be in deg/min/sec, or decimal degrees entered in
the "Deg:" field. (If you enter decimal degrees in the degrees field, please
clear the minutes and seconds fields, or they will be added in.) If you
select a city from the pulldown menu, the latitude, longitude and time zone fields
will be filled in by the program. If you want to input latitude, longitude
or time zone manually, <u>be sure to
select "Enter Lat/Long -->" from the City pulldown box, or your numbers will
be overwritten</u> by the selected city's location.
</P>
</FONT>
</LI>
<LI>
<P>
<FONT SIZE=2>You can enter a different time zone for a location by selecting
"Enter Lat/Long -->" in the City pulldown box. Otherwise the time zone
associated with the selected city's Local Standard Time will be automatically
entered.
Selecting "Yes" in the Daylight Saving field will cause the solar position
calculation to assume the current time has been adjusted forward one hour from
standard time. If you are uncertain of the time zone for a location, refer to
our <a href="./timezone.html">Time Zone Table</a>.
</P>
</FONT>
</LI>
<LI>
<P>
<FONT SIZE=2>The program retrieves the current date and time from your computer, and fills
in these values in the date/time fields. To perform calculations for a
different date, simply select the month in the pulldown box, and enter the
day and four digit year in the appropriate input boxes. Time of day for the
calculation can be changed in the same way.
</P>
</FONT>
</LI>
<LI>
<P>
<FONT SIZE=2>Hit the "Calculate Solar Position" button. Once the calculations
are complete, you may use your browser's "Print" function to obtain a hardcopy
of the results. Results are given in the following units: Equation of Time in
minutes of time; Solar Declination in degrees, with positive to the north;
Azimuth in degrees clockwise from north; Elevation in degrees up from the
horizon; Cosine of Solar Zenith Angle is unitless.
</P>
</FONT>
</LI>
<LI>
<P>
<FONT SIZE=2>Note that for latitudes greater than
72&deg; north or less than 72&deg; south, accuracy may be lower due in
part to the effects of
<a href="./glossary.html#atmosphericrefraction">atmospheric refraction</a>.
</P>
</FONT>
</LI>
</OL>
</H4>
</FONT>
<BR>
<HR>
<h4>
<table border=0 width="100%">
<tr>
<td align="left">
<a href="./sunrise.html">Sunrise/Sunset Calculator</a><br>
<a href="./calcdetails.html">Calculation Details</a><br>
<a href="./sollinks.html">Solar Calculator Links</a><br>
</td>
<td align="center">
<a href="http://www.noaa.gov/"><img src="noaaemblemt.gif" border="0" alt="NOAA Emblem"></a>
</td>
<td align="right">
<a href="./timezone.html">Time Zone Table</a><br>
<a href="./glossary.html">Solar Calculator Glossary</a><br>
<a href="/index.html">Back to SRRB Homepage</a><br>
</td>
</tr>
</table>
</h4>
<center>by
<a href="mailto:&#119;&#101;&#98;&#109;&#97;&#115;&#116;&#101;&#114;-&#115;&#114;&#114;&#98;&#46;gm&#100;&#64;n&#111;&#97;&#97;&#46;&#103;&#111;&#118;">Chris Cornwall</a>,
Aaron Horiuchi and Chris Lehman<br>
<SCRIPT>
document.write("Last Updated on " + document.lastModified + ".");
</SCRIPT>
</center>
</BODY>
</HTML>
<!DOCTYPE html>
<html>
<head>
<title>Sunrise sunset</title>
<script type="text/javascript" src="https://raw.github.com/bewest/d3/bewest/d3.js"></script>
<script type="text/javascript" src="https://raw.github.com/bewest/d3/bewest/d3.time.js"></script>
<script type="text/javascript">
var width = 700;
var height = 525;
var padding = 40;
// the vertical axis is a time scale that runs from 00:00 - 23:59
// the horizontal axis is a time scale that runs from the 2011-01-01 to 2011-12-31
var y = d3.time.scale().domain([new Date(2011, 0, 1), new Date(2011, 0, 1, 23, 59)]).range([0, height]);
var x = d3.time.scale().domain([new Date(2011, 0, 1), new Date(2011, 11, 31)]).range([0, width]);
var monthNames = ["Jan", "Feb", "Mar", "April", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
// Sunrise and sun set times for dates in 2011. I have picked the 1st
// and 15th day of every month, plus other important dates like equinoxes
// and solstices and dates around the standard time/DST transition.
var data = [
{date: new Date(2011, 0, 1), sunrise: [7, 51], sunset: [16, 42]},
{date: new Date(2011, 0, 15), sunrise: [7, 48], sunset: [16, 58]},
{date: new Date(2011, 1, 1), sunrise: [7, 33], sunset: [17, 21]},
{date: new Date(2011, 1, 15), sunrise: [7, 14], sunset: [17, 41]},
{date: new Date(2011, 2, 1), sunrise: [6, 51], sunset: [18, 0]},
{date: new Date(2011, 2, 12), sunrise: [6, 32], sunset: [18, 15]}, // dst - 1 day
{date: new Date(2011, 2, 13), sunrise: [7, 30], sunset: [19, 16]}, // dst
{date: new Date(2011, 2, 14), sunrise: [7, 28], sunset: [19, 18]}, // dst + 1 day
{date: new Date(2011, 2, 14), sunrise: [7, 26], sunset: [19, 19]},
{date: new Date(2011, 2, 20), sunrise: [07, 17], sunset: [19, 25]}, // equinox
{date: new Date(2011, 3, 1), sunrise: [6, 54], sunset: [19, 41]},
{date: new Date(2011, 3, 15), sunrise: [6, 29], sunset: [19, 58]},
{date: new Date(2011, 4, 1), sunrise: [6, 3], sunset: [20, 18]},
{date: new Date(2011, 4, 15), sunrise: [5, 44], sunset: [20, 35]},
{date: new Date(2011, 5, 1), sunrise: [5, 30], sunset: [20, 52]},
{date: new Date(2011, 5, 15), sunrise: [5, 26], sunset: [21, 1]},
{date: new Date(2011, 5, 21), sunrise: [5, 26], sunset: [21, 3]}, // solstice
{date: new Date(2011, 6, 1), sunrise: [5, 30], sunset: [21, 3]},
{date: new Date(2011, 6, 15), sunrise: [5, 41], sunset: [20, 57]},
{date: new Date(2011, 7, 1), sunrise: [5, 58], sunset: [20, 40]},
{date: new Date(2011, 7, 15), sunrise: [6, 15], sunset: [20, 20]},
{date: new Date(2011, 8, 1), sunrise: [6, 35], sunset: [19, 51]},
{date: new Date(2011, 8, 15), sunrise: [6, 51], sunset: [19, 24]},
{date: new Date(2011, 8, 23), sunrise: [7, 1], sunset: [19, 9]}, // equinox
{date: new Date(2011, 9, 1), sunrise: [7, 11], sunset: [18, 54]},
{date: new Date(2011, 9, 15), sunrise: [7, 28], sunset: [18, 29]},
{date: new Date(2011, 10, 1), sunrise: [7, 51], sunset: [18, 2]},
{date: new Date(2011, 10, 5), sunrise: [7, 57], sunset: [17, 56]}, // last day of dst
{date: new Date(2011, 10, 6), sunrise: [6, 58], sunset: [16, 55]}, // standard time
{date: new Date(2011, 10, 7), sunrise: [6, 59], sunset: [16, 54]}, // standard time + 1
{date: new Date(2011, 10, 15), sunrise: [7, 10], sunset: [16, 44]},
{date: new Date(2011, 11, 1), sunrise: [7, 31], sunset: [16, 33]},
{date: new Date(2011, 11, 15), sunrise: [7, 44], sunset: [16, 32]},
{date: new Date(2011, 11, 22), sunrise: [7, 49], sunset: [16, 35]}, // solstice
{date: new Date(2011, 11, 31), sunrise: [7, 51], sunset: [16, 41]}
];
function yAxisLabel(d) {
if (d == 12) { return "noon"; }
if (d < 12) { return d; }
return (d - 12);
}
// The labels along the x axis will be positioned on the 15th of the
// month
function midMonthDates() {
return d3.range(0, 12).map(function(i) { return new Date(2011, i, 15) });
}
</script>
<style type="text/css">
/**** STYLES *****/
div#day-length text {
fill: gray;
font-family: Helvetica, sans-serif;
font-size: 10px;
}
body {
}
#vis {
height: 700px;
}
</style>
</head>
<body>
<h1>Sunrise sunset</h1>
<p> Copied from
<ul>
<li><a href="http://www.recursion.org/d3-for-mere-mortals/"
>http://www.recursion.org/d3-for-mere-mortals/</a> </li>
<li><a href="http://www.srrb.noaa.gov/highlights/sunrise/azel.html"
>http://www.srrb.noaa.gov/highlights/sunrise/azel.html</a> </li>
</ul>
</p>
<div id="vis">
<div id="day-length">
</div>
</div>
<div id="todo">
<pre>
* use NOAA's js calculator to plot sun position over time
* pan/zoom support
</pre>
</div>
<script type="text/javascript">
var dayLength = d3.select("#day-length").
append("svg:svg").
attr("width", width + padding * 2).
attr("height", height + padding * 2);
// create a group to hold the axis-related elements
var axisGroup = dayLength.append("svg:g").
attr("transform", "translate("+padding+","+padding+")");
// draw the x and y tick marks. Since they are behind the visualization, they
// can be drawn all the way across it. Because the has been
// translated, they stick out the left side by going negative.
axisGroup.selectAll(".yTicks").
data(d3.range(5, 22)).
enter().append("svg:line").
attr("x1", -5).
// Round and add 0.5 to fix anti-aliasing effects (see above)
attr("y1", function(d) { return d3.round(y(new Date(2011, 0, 1, d))) + 0.5; }).
attr("x2", width+5).
attr("y2", function(d) { return d3.round(y(new Date(2011, 0, 1, d))) + 0.5; }).
attr("stroke", "lightgray").
attr("class", "yTicks");
axisGroup.selectAll(".xTicks").
data(midMonthDates).
enter().append("svg:line").
attr("x1", x).
attr("y1", -5).
attr("x2", x).
attr("y2", height+5).
attr("stroke", "lightgray").
attr("class", "yTicks");
// draw the text for the labels. Since it is the same on top and
// bottom, there is probably a cleaner way to do this by copying the
// result and translating it to the opposite side
axisGroup.selectAll("text.xAxisTop").
data(midMonthDates).
enter().
append("svg:text").
text(function(d, i) { return monthNames[i]; }).
attr("x", x).
attr("y", -8).
attr("text-anchor", "middle").
attr("class", "axis xAxisTop");
axisGroup.selectAll("text.xAxisBottom").
data(midMonthDates).
enter().
append("svg:text").
text(function(d, i) { return monthNames[i]; }).
attr("x", x).
attr("y", height+15).
attr("text-anchor", "middle").
attr("class", "xAxisBottom");
axisGroup.selectAll("text.yAxisLeft").
data(d3.range(5, 22)).
enter().
append("svg:text").
text(yAxisLabel).
attr("x", -7).
attr("y", function(d) { return y(new Date(2011, 0, 1, d)); }).
attr("dy", "3").
attr("class", "yAxisLeft").
attr("text-anchor", "end");
axisGroup.selectAll("text.yAxisRight").
data(d3.range(5, 22)).
enter().
append("svg:text").
text(yAxisLabel).
attr("x", width+7).
attr("y", function(d) { return y(new Date(2011, 0, 1, d)); }).
attr("dy", "3").
attr("class", "yAxisRight").
attr("text-anchor", "start");
// create a group for the sunrise and sunset paths
var lineGroup = dayLength.append("svg:g").
attr("transform", "translate("+ padding + ", " + padding + ")");
// draw the background. The part of this that remains uncovered will
// represent the daylight hours.
lineGroup.append("svg:rect").
attr("x", 0).
attr("y", 0).
attr("height", height).
attr("width", width).
attr("fill", "lightyellow");
// The meat of the visualization is surprisingly simple. sunriseLine
// and sunsetLine are areas (closed svg:path elements) that use the date
// for the x coordinate and sunrise and sunset (respectively) for the y
// coordinate. The sunrise shape is anchored at the top of the chart, and
// sunset area is anchored at the bottom of the chart.
var sunriseLine = d3.svg.area().
x(function(d) { return x(d.date); }).
y1(function(d) { return y(new Date(2011, 0, 1, d.sunrise[0], d.sunrise[1])); }).
interpolate("linear");
lineGroup.
append("svg:path").
attr("d", sunriseLine(data)).
attr("fill", "steelblue");
var sunsetLine = d3.svg.area().
x(function(d) { return x(d.date); }).
y0(height).
y1(function(d) { return y(new Date(2011, 0, 1, d.sunset[0], d.sunset[1])); }).
interpolate("linear");
lineGroup.append("svg:path").
attr("d", sunsetLine(data)).
attr("fill", "steelblue");
// finally, draw a line representing 12:00 across the entire
// visualization
lineGroup.append("svg:line").
attr("x1", 0).
attr("y1", d3.round(y(new Date(2011, 0, 1, 12))) + 0.5).
attr("x2", width).
attr("y2", d3.round(y(new Date(2011, 0, 1, 12))) + 0.5).
attr("stroke", "lightgray");
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment