/lib/DDG/Goodie/SunInfo.pm

https://gitlab.com/jslee1/zeroclickinfo-goodies · Perl · 135 lines · 112 code · 15 blank · 8 comment · 9 complexity · b69d17ce7061679600d6a918c55cc5fd MD5 · raw file

  1. package DDG::Goodie::SunInfo;
  2. # ABSTRACT: sunrise and sunset information for the client location
  3. use strict;
  4. use DDG::Goodie;
  5. with 'DDG::GoodieRole::Dates';
  6. use DateTime::Event::Sunrise;
  7. use utf8;
  8. zci answer_type => "sun_info";
  9. zci is_cached => 0;
  10. triggers startend => 'sunrise', 'sunset', 'what time is sunset', 'what time is sunrise';
  11. my $time_format = '%l:%M %p';
  12. my $datestring_regex = datestring_regex();
  13. my $goodieVersion = $DDG::GoodieBundle::OpenSourceDuckDuckGo::VERSION // 999;
  14. my $lat_lon_regex = qr/[\+\-]?[0-9]+(?:
  15. (?:\.[0-9]+[°]?)
  16. |(?:°?
  17. (?:[0-9]{1,2}')?
  18. (?:[0-9]{1,2}(?:''|"))?
  19. )
  20. )?/x;
  21. handle remainder => sub {
  22. my $remainder = shift // '';
  23. $remainder =~ s/\?//g; # Strip question marks.
  24. return unless $remainder =~ qr/^
  25. (?:at\s
  26. (?<lat>$lat_lon_regex[NS])\s
  27. (?<lon>$lat_lon_regex[EW])\s?
  28. )?
  29. (?:on|for)?\s?
  30. (?<when>$datestring_regex)?
  31. $/xi;
  32. my ($lat, $lon, $tz) = ($loc->latitude, $loc->longitude, $loc->time_zone);
  33. my $where = where_string();
  34. return unless (($lat || $lon) && $tz && $where); # We'll need a real location and time zone.
  35. my $dt = DateTime->now;;
  36. $dt = parse_datestring_to_date($+{'when'}) if($+{'when'});
  37. return unless $dt; # Also going to need to know which day.
  38. $dt->set_time_zone($tz) unless ($+{'lat'} && $+{'lon'});
  39. $lon = parse_arc($+{'lon'}) if ($+{'lon'});
  40. $lat = parse_arc($+{'lat'}) if ($+{'lat'});
  41. $where = "Coordinates ${lat}°N ${lon}°E" if($+{'lat'} && $+{'lon'});
  42. my $sun_at_loc = DateTime::Event::Sunrise->new(
  43. longitude => $lon,
  44. latitude => $lat,
  45. precise => 1, # Slower but more precise.
  46. silent => 1, # Don't fill up STDERR with noise, if we have trouble.
  47. );
  48. # We don't care for which one they asked, we compute both sunrise and sunset
  49. my $sunrise = $sun_at_loc->sunrise_datetime($dt)->strftime($time_format);
  50. my $sunset = $sun_at_loc->sunset_datetime($dt)->strftime($time_format);
  51. return pretty_output($where, date_output_string($dt), $sunrise, $sunset);
  52. };
  53. sub where_string {
  54. my @where_bits;
  55. if (my $city = $loc->city) {
  56. # If we have the city we can abbrev the region or country, if avail.
  57. # - Phoenixville, Pennsylvania
  58. @where_bits = ($city, $loc->region_name || $loc->country_code3);
  59. } elsif (my $region_name = $loc->region_name) {
  60. # No city, but a region name; abbreviate the country or continent
  61. # - Pennsylvania, USA
  62. @where_bits = ($region_name, $loc->country_code3 || $loc->continent_code);
  63. } elsif (my $country = $loc->country_name) {
  64. # Country and continent, then, I guess.
  65. # United States, NA
  66. @where_bits = ($loc->country_name, $loc->continent_code);
  67. }
  68. return join(', ', @where_bits);
  69. }
  70. sub parse_arc {
  71. my ($arc_string) = @_;
  72. return unless $arc_string =~ qr/
  73. (?<sign>[\+\-])?(?<deg>[0-9]+)(?:
  74. ((?<dec_deg>\.[0-9]+)[°]?)
  75. |(?:°?
  76. (?:(?<min>[0-9]{1,2})')?
  77. (?:(?<sec>[0-9]{1,2})(?:''|"))?
  78. )
  79. )?(?<dir>[NSEW])/x;
  80. my $decimal_degrees = $+{'deg'};
  81. $decimal_degrees += $+{'dec_deg'} if $+{'dec_deg'};
  82. $decimal_degrees += $+{'min'}/60 if $+{'min'};
  83. $decimal_degrees += $+{'sec'}/3600 if $+{'sec'};
  84. $decimal_degrees *= -1 if $+{'sign'} && $+{'sign'} eq '-' || $+{'dir'} =~ /[SW]/;
  85. return $decimal_degrees;
  86. }
  87. sub pretty_output {
  88. my ($where, $when, $rise, $set) = @_;
  89. $rise =~ s/^\s+//g; # strftime puts a space in front for single-digits.
  90. $set =~ s/^\s+//g;
  91. my $text = "On $when, sunrise in $where is at $rise; sunset at $set.";
  92. return $text,
  93. structured_answer => {
  94. data => {
  95. where => $where,
  96. when_data => $when,
  97. sunrise_svg => "/share/goodie/sun_info/$goodieVersion/sunrise.svg",
  98. rise => $rise,
  99. sunset_svg => "/share/goodie/sun_info/$goodieVersion/sunset.svg",
  100. set_data => $set
  101. },
  102. templates => {
  103. group => 'text',
  104. item => 0,
  105. options => {
  106. title_content => 'DDH.sun_info.title',
  107. content => 'DDH.sun_info.content'
  108. }
  109. }
  110. };
  111. }
  112. 1;