Advertisement
presto8

Untitled

May 1st, 2011
412
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // SunriseSunset Class (2011-05-01)
  2. //   Implementation of http://williams.best.vwh.net/sunrise_sunset_algorithm.htm
  3. //
  4. //   Copyright (c) 2011, Preston Hunt <me@prestonhunt.com>
  5. //   All rights reserved.
  6. //
  7. //   (BSD License)
  8. //
  9. //   Redistribution and use in source and binary forms, with or without
  10. //   modification, are permitted provided that the following conditions
  11. //   are met:
  12. //
  13. //   Redistributions of source code must retain the above copyright
  14. //   notice, this list of conditions and the following disclaimer.
  15. //   Redistributions in binary form must reproduce the above copyright
  16. //   notice, this list of conditions and the following disclaimer in the
  17. //   documentation and/or other materials provided with the
  18. //   distribution.
  19. //
  20. //   The name of Preston Hunt may not be used to endorse or promote
  21. //   products derived from this software without specific prior written
  22. //   permission.
  23. //
  24. //   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  25. //   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  26. //   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  27. //   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  28. //   COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
  29. //   INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  30. //   (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  31. //   SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  32. //   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  33. //   STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  34. //   ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  35. //   OF THE POSSIBILITY OF SUCH DAMAGE.
  36. //
  37. //   Provides sunrise and sunset times for specified date and position.
  38. //   All dates are UTC.  Year is 4-digit.  Month is 1-12.  Day is 1-31.
  39. //   Longitude is positive for east, negative for west. Latitude is
  40. //   positive for north, negative for south.
  41. //
  42. //   Sample usage:
  43. //   var tokyo = new SunriseSunset( 2011, 1, 19, 35+40/60, 139+45/60);
  44. //   tokyo.sunriseUtcHours()      --> 21.8199 = 21:49 GMT
  45. //   tokyo.sunsetUtcHours()       --> 7.9070  = 07:54 GMT
  46. //   tokyo.sunriseLocalHours(9)   --> 6.8199  = 06:49 at GMT+9
  47. //   tokyo.sunsunsetLocalHours(9) --> 16.9070 = 16:54 at GMT+9
  48. //   tokyo.isDaylight(1.5)        --> true
  49. //
  50. //   var losangeles = new SunriseSunset( 2011, 1, 19, 34.05, -118.233333333 );
  51. //   etc.
  52.  
  53. var SunriseSunset = function( utcFullYear, utcMonth, utcDay, latitude, longitude ) {
  54.     this.zenith = 90 + 50/60; //   offical      = 90 degrees 50'
  55.                               //   civil        = 96 degrees
  56.                               //   nautical     = 102 degrees
  57.                               //   astronomical = 108 degrees
  58.  
  59.     this.utcFullYear = utcFullYear;
  60.     this.utcMonth = utcMonth;
  61.     this.utcDay = utcDay;
  62.     this.latitude = latitude;
  63.     this.longitude = longitude;
  64.  
  65.     this.rising = true; // set to true for sunrise, false for sunset
  66.     this.lngHour = this.longitude / 15;
  67. };
  68.  
  69. SunriseSunset.prototype = {
  70.     sin: function( deg ) { return Math.sin( deg * Math.PI / 180 ); },
  71.     cos: function( deg ) { return Math.cos( deg * Math.PI / 180 ); },
  72.     tan: function( deg ) { return Math.tan( deg * Math.PI / 180 ); },
  73.     asin: function( x ) { return (180/Math.PI) * Math.asin(x); },
  74.     acos: function( x ) { return (180/Math.PI) * Math.acos(x); },
  75.     atan: function( x ) { return (180/Math.PI) * Math.atan(x); },
  76.  
  77.     getDOY: function() {
  78.         var month = this.utcMonth,
  79.             year = this.utcFullYear,
  80.             day = this.utcDay;
  81.  
  82.         var N1 = Math.floor( 275 * month / 9 );
  83.         var N2 = Math.floor( (month + 9) / 12 );
  84.         var N3 = (1 + Math.floor((year - 4 * Math.floor(year / 4 ) + 2) / 3));
  85.         var N = N1 - (N2 * N3) + day - 30;
  86.         return N;
  87.     },
  88.  
  89.     approximateTime: function() {
  90.         var doy = this.getDOY();
  91.         if ( this.rising ) {
  92.             return doy + ((6 - this.lngHour) / 24);
  93.         } else {
  94.             return doy + ((18 - this.lngHour) / 24);
  95.         }
  96.     },
  97.  
  98.     meanAnomaly: function() {
  99.         var t = this.approximateTime();
  100.         return (0.9856 * t) - 3.289;
  101.     },
  102.  
  103.     trueLongitude: function() {
  104.         var M = this.meanAnomaly();
  105.         var L = M + (1.916 * this.sin(M)) + (0.020 * this.sin(2 * M)) + 282.634;
  106.         return L % 360;
  107.     },
  108.  
  109.     rightAscension: function() {
  110.         var L = this.trueLongitude();
  111.         var RA = this.atan(0.91764 * this.tan(L));
  112.         RA %= 360;
  113.  
  114.         var Lquadrant  = (Math.floor( L/90)) * 90;
  115.         var RAquadrant = (Math.floor(RA/90)) * 90;
  116.         RA = RA + (Lquadrant - RAquadrant);
  117.         RA /= 15;
  118.  
  119.         return RA;
  120.     },
  121.  
  122.     sinDec: function() {
  123.         var L = this.trueLongitude(),
  124.             sinDec = 0.39782 * this.sin(L);
  125.  
  126.         return sinDec;
  127.     },
  128.  
  129.     cosDec: function() {
  130.         return this.cos(this.asin(this.sinDec()));
  131.     },
  132.  
  133.     localMeanTime: function() {
  134.         var cosH = (this.cos(this.zenith) - (this.sinDec() * this.sin(this.latitude)))
  135.             / (this.cosDec() * this.cos(this.latitude));
  136.  
  137.         if (cosH >  1) {
  138.             return "the sun never rises on this location (on the specified date)";
  139.         } else if (cosH < -1) {
  140.             return "the sun never sets on this location (on the specified date)";
  141.         } else {
  142.             var H = this.rising ? 360 - this.acos(cosH) : this.acos(cosH);
  143.             H /= 15;
  144.             var RA = this.rightAscension();
  145.             var t = this.approximateTime();
  146.             var T = H + RA - (0.06571 * t) - 6.622;
  147.             return T;
  148.         }
  149.     },
  150.  
  151.     hoursRange: function( h ) {
  152.         return (h+24) % 24;
  153.     },
  154.  
  155.     UTCTime: function() {
  156.         var T = this.localMeanTime();
  157.         var UT = T - this.lngHour;
  158.         return this.hoursRange( UT );
  159.         //if ( UT < 0 ) UT += 24;
  160.         //return UT % 24;
  161.     },
  162.  
  163.     sunriseUtcHours: function() {
  164.         this.rising = true;
  165.         return this.UTCTime();
  166.     },
  167.  
  168.     sunsetUtcHours: function() {
  169.         this.rising = false;
  170.         return this.UTCTime();
  171.     },
  172.  
  173.     sunriseLocalHours: function(gmt) {
  174.         return this.hoursRange( gmt + this.sunriseUtcHours() );
  175.     },
  176.  
  177.     sunsetLocalHours: function(gmt) {
  178.         return this.hoursRange( gmt + this.sunsetUtcHours() );
  179.     },
  180.  
  181.     isDaylight: function( utcCurrentHours ) {
  182.         var sunriseHours = this.sunriseUtcHours(),
  183.             sunsetHours = this.sunsetUtcHours();
  184.  
  185.         if ( sunsetHours < sunriseHours ) {
  186.             // Either the sunrise or sunset time is for tomorrow
  187.             if ( utcCurrentHours > sunriseHours ) {
  188.                 return true;
  189.             } else if ( utcCurrentHours < sunsetHours ) {
  190.                 return true;
  191.             } else {
  192.                 return false;
  193.             }
  194.         }
  195.  
  196.         if ( utcCurrentHours >= sunriseHours ) {
  197.             return utcCurrentHours < sunsetHours;
  198.         }
  199.  
  200.         return false;
  201.     }
  202. };
  203.  
  204. function SunriseSunsetTest() {
  205.     var testcases = {
  206.         'Los Angeles': {
  207.             'lat': 34.05, 'lon': -118.23333333,
  208.             'tests': [
  209.                 { 'year': 2011, 'month': 1, 'day': 22, 'utcHours': 19.6666666, 'isDaylight': true }
  210.             ]
  211.         },
  212.         'Berlin': {
  213.             'lat': 52.5, 'lon': 13.366666667,
  214.             'tests': [
  215.                 { 'year': 2011, 'month': 1, 'day': 25, 'utcHours': 1.25, 'isDaylight': false },
  216.                 { 'year': 2011, 'month': 2, 'day': 22, 'utcHours': 2.5, 'isDaylight': false }
  217.             ]
  218.         },
  219.         'Tokyo': {
  220.             'lat': 35+40/60, 'lon': 139+45/60,
  221.             'tests': [
  222.                 { 'year': 2011, 'month': 1, 'day': 23, 'utcHours': 1.5, 'isDaylight': true },
  223.                 { 'year': 2011, 'month': 1, 'day': 23, 'utcHours': 22.5, 'isDaylight': true }
  224.             ]
  225.         },
  226.         'Seoul': {
  227.             'lat': 37.55, 'lon': 126.966666667,
  228.             'tests': [
  229.                 { 'year': 2011, 'month': 4, 'day': 10, 'utcHours': 15+30/60, 'isDaylight': false }
  230.             ]
  231.         },
  232.         'New Delhi': {
  233.             'lat': 35+40/60, 'lon': 139+45/60,
  234.             'tests': [
  235.             ]
  236.         },
  237.         'Sydney': {
  238.             'lat': -(33+55/60), 'lon': 151+17/60,
  239.             'tests': [
  240.                 { 'year': 2011, 'month': 5, 'day': 1, 'utcHours': 17+53/60, 'isDaylight': false }
  241.             ]
  242.         },
  243.         'Santiago': {
  244.             'lat': -(33+26/60), 'lon': -(70+40/60),
  245.             'tests': [
  246.                 { 'year': 2011, 'month': 5, 'day': 1, 'utcHours': 17+54/60, 'isDaylight': true }
  247.             ]
  248.         }
  249.     };
  250.  
  251.     var tests_run = 0;
  252.     var tests_failed = 0;
  253.  
  254.     for ( var city_name in testcases ) {
  255.         var city = testcases[ city_name ];
  256.         for ( var i=0; i<city.tests.length; i++ ) {
  257.             var t = city.tests[i];
  258.             var ss = new SunriseSunset( t.year, t.month, t.day, city.lat, city.lon );
  259.             var expected = t.isDaylight;
  260.             var result = ss.isDaylight( t.utcHours );
  261.             var passed = result === expected;
  262.  
  263.             tests_run++;
  264.             if ( ! passed ) tests_failed++;
  265.            
  266.             /*jsl:ignore*/
  267.             print( city_name, t.year, t.month, t.day, t.utcHours, "passed:", passed );
  268.             if ( ! passed ) {
  269.                 print( "sunriseUtcHours=" + ss.sunriseUtcHours() +
  270.                         ", sunsetUtcHours=" + ss.sunsetUtcHours() );
  271.             }
  272.  
  273.             /*jsl:end*/
  274.         }
  275.     }
  276.  
  277.     /*jsl:ignore*/
  278.     print( "tests: " + tests_run, "failed: " + tests_failed );
  279.     /*jsl:end*/
  280. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement