Advertisement
presto8

Untitled

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