Created
September 17, 2011 18:49
-
-
Save bewest/1224222 to your computer and use it in GitHub Desktop.
d3 sunrise sunset
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
sunrise sunset stuff in d3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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° north or less than 72° 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:webmaster-srrb.gmd@noaa.gov">Chris Cornwall</a>, | |
Aaron Horiuchi and Chris Lehman<br> | |
<SCRIPT> | |
document.write("Last Updated on " + document.lastModified + "."); | |
</SCRIPT> | |
</center> | |
</BODY> | |
</HTML> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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