#!/usr/bin/perl -w

# script to plot graph of daylight for a given month/year for a given location by name
# uses sunrisesunset.com website info for data
# usage: daygraph.pl <city> <monthnum> <jpgfile>
# if monthnum != [1-12] it plots for the whole year
#
# author: deepak <http://puggy.symonds.net/~deep/>
# 14/dec/2006

use strict;
use GD::Graph::mixed;

# plot graph for a month
# i've hacked it to plot for one year if $yrmode is 1 (with goto! :< )
sub drawmonth {
    my ( $city, $month, $file, $yrmode ) = @_;

    my $DEBUG = 0;

    if ( $yrmode == 1 ) {
        $month = 1;
    }

  NEXT_MONTH:
    my $hfile =
`wget -q -O - "http://www.sunrisesunset.com/calendar.asp?comb_city_info=${city}&month=${month}&year=2007&time_type=0&use_dst=0"`;

    print $hfile if $DEBUG;

    my @lines = split( /\n/, $hfile );
    my @days;
    my @rises;
    my @sets;

    my $n = -1;
    foreach my $line (@lines) {
        if ( $line =~ /Sun[r]*[i]*[s]*[e]*:/ ) {
            $line =~ s/<[^>]*>/ /g;    #remove tags
            $line =~ s/&\w*;/ /g;      #remove esc seqs
            my @cells = split( /\s+/, $line );
            foreach my $cell (@cells) {
                next if ( $cell =~ /:$/ );
                next if ( $cell eq "" );
                if ( $cell =~ /pm$/ ) {
                    $cell =~ s/pm//;
                    $cell =~ s/://;
                    $cell += 1200 if $cell < 1200;
                    $n++;
                }
                elsif ( $cell =~ /am$/ ) {
                    $cell =~ s/am//;
                    $cell =~ s/://;
                    $cell -= 1200 if $cell > 1200;
                    $n++;
                }
                elsif ( $cell =~ /down/i ) {
                    push( @rises, 0 );
                    push( @sets,  0 );
                    $n = -1;
                    next;
                }
                elsif ( $cell =~ /up/i ) {
                    push( @rises, 0 );
                    push( @sets,  2400 );
                    $n = -1;
                    next;
                }
                elsif ( $cell =~ /[A-Za-z]/ ) {
                    next;    #stuff like "DST On"
                }
                else {
                    $n++;
                }
                if ( $n == 0 ) {
                    push( @days, $cell );
                }
                elsif ( $n == 1 ) {
                    push( @rises, $cell );
                }
                elsif ( $n == 2 ) {
                    push( @sets, $cell );
                    $n = -1;
                }
            }
        }
    }

    if ( $yrmode == 1 ) {
        $month++;
        if ( $month <= 12 ) {
            goto NEXT_MONTH;
        }
    }

    # compute duration of daylight for each day
    my $i = 0;
    my @durations;
    my $max     = 0;
    my $min     = 2400;
    my $mindate = "";
    my $maxdate = "";
    $month = 0 if $yrmode;
    foreach my $day (@days) {
        $month++ if ( $day == 1 && $yrmode );

        # for printing
        my $risehm = $rises[$i];
        my $sethm  = $sets[$i];

        # convert hhmm to decimal and subtract, save it for plotting too
        my $set  = int( $sethm / 100 ) +  ( $sethm % 100 ) / 60;
        my $rise = int( $risehm / 100 ) + ( $risehm % 100 ) / 60;
        my $duration = int( ( $set - $rise ) * 100 );
        $durations[$i] = $duration;
        $rises[$i]     = int( $rise * 100 );
        $sets[$i]      = int( $set * 100 );
        print $day . ": "
          . $rises[$i] . " "
          . $sets[$i] . " "
          . $durations[$i] . "\n"
          if $DEBUG;

        # convert to hhmm for printing
        my $durationhm =
          int( $duration / 100 ) * 100 + int( ( $duration % 100 ) * ( 3 / 5 ) );
        if ( $duration < $min ) {
            $mindate =
              "Shortest day: $day/$month: $risehm-$sethm ($durationhm)";
            $min = $duration;
        }
        elsif ( $duration > $max ) {
            $maxdate = "Longest day: $day/$month: $risehm-$sethm ($durationhm)";
            $max     = $duration;
        }
        $i++;
    }

    # we draw two graphs:
    # 1. a bar graph, each bar representing time between sunrise and set
    # 2. a line graph showing daylight duration
    # we use cumulative graph for bar graph, so we are plotting,
    # effectively, rise and rise+duration (=set)

    my @data = ( \@days, \@rises, \@durations, \@durations );
    my $graph;
    if ($yrmode) {
        $graph = new GD::Graph::mixed( 1800, 360 );
        $month = "1-12";    # for label
    }
    else {
        $graph = new GD::Graph::mixed( 480, 360 );
    }

    # strip off lat/long data from city and trim it up a bit
    $city =~ s/;.*//g;
    $city =~ s/,.*//g;
    $city =~ s/%20/ /g;

    $graph->set_legend( undef, 'Daytime', 'Sunlight duration' );
    if ($yrmode) {
        $graph->set(
            x_label_skip => 30,    # print after 30 days
            bar_spacing  => 2,
        );
    }
    else {
        $graph->set( bar_spacing => 3, );
    }
    $graph->set(
        x_label    => "Day (month: $month)",
        y_label    => '24 hour period',
        title      => "$city: Sunrise, sunset, and daytime. $mindate. $maxdate",
        long_ticks => 1,
        y_max_value   => 2400,
        y_tick_number => 8,                       # no. of y-axis divisions
        x_ticks       => 1,
        cumulate      => 1,
        line_width    => 2,
        types         => [qw(bars bars lines)],

        # we are making first bar graph white, effectively hiding the rise part
        dclrs       => [qw(white yellow green)],
        fgclr       => "lgray",
        borderclrs  => [ undef, "yellow", "green" ],
        transparent => 1,
    ) or warn $graph->error;

    $graph->plot( \@data ) or die $graph->error;

    open( GRAPH, "> $file" ) || die "can't open file: $!\n";
    print GRAPH $graph->gd->jpeg(100);
    close(GRAPH);
}

( defined $ARGV[0] && defined $ARGV[1] && defined $ARGV[2] )
  || die "Usage: daygraph.pl <city> <month no.> <jpg filename>";

# get location database
my $cityfile =
  `wget -q -O - "http://www.sunrisesunset.com/custom_srss_calendar.asp"`;
my @clines = split( /\n/, $cityfile );
my @cities;
foreach my $cline (@clines) {
    if ( $cline =~ /OPTION VALUE=\"([^\"]+)\"/ ) {
        my $city = $1;
        push( @cities, $city );
    }
}

# you can add more cities like this
# format: name;long;lat;timediff;dst
push( @cities, "Bangalore;-77.5;13;5.5;0" );
push( @cities, "Near North Pole;0;79;0;0" );
push( @cities, "Antarctica;0;-79;0;0" );

# find input city in database
my $city = "";
foreach (@cities) {
    $city = $_ if (/$ARGV[0]/i);
}
die "can't find $ARGV[0]" if $city eq "";
$city =~ s/ /%20/g;

my $month = $ARGV[1];
my $file  = $ARGV[2];

# if user passes invalid month we draw for whole year
if ( $month < 1 || $month > 12 ) {
    drawmonth( $city, $month, $file, 1 );
}
else {
    drawmonth( $city, $month, $file, 0 );
}

exit 0;

