Weather-GHCN-Fetch

 view release on metacpan or  search on metacpan

lib/Weather/GHCN/App/StationCounts.pm  view on Meta::CPAN

    my ( $output, $new_fh, $old_fh );
    if ( $Opt->outclip and $USE_WINCLIP ) {
        open $new_fh, '>', \$output
            or die 'Unable to open buffer for write';
        $old_fh = select $new_fh;  ## no critic (ProhibitOneArgSelect)
    }

    my @files = $argv_aref->@*;
    @files = ($DASH) unless @files;

    foreach my $file (@files) {
        my $fh;
        if ($file eq $DASH) {
            $fh = *STDIN;
        } else {
            open $fh, '<', $file or die;
        }

        read_data( $fh, \%count );
    }

    say join "\t", qw(Year Decade Stn_Count);

    foreach my $yr (sort { $a <=> $b } keys %count) {
        my $stn_count = keys %{ $count{$yr} };
        ## no critic [ProhibitMagicNumbers]
        my $decade = (substr $yr, 0, 3) . '0s';
        say sprintf "%3d\t%s\t%d", $yr, $decade, $stn_count;
    }

WRAP_UP:
    # send output to the Windows clipboard
    if ( $Opt->outclip and $USE_WINCLIP ) {
        Win32::Clipboard->new()->Set( $output );
        select $old_fh; ## no critic [ProhibitOneArgSelect]
    }


    return;
}

########################################################################
# Script-specific Subroutines
########################################################################

=head2 read_data( $fh, \%count )

From the file handle $fh, read a list of stations in the format
generated by Fetch.pm, and count the stations that were active in any
given year.

=cut

sub read_data ($fh, $count_href) {
    my $lineno = 0;

    while ( my $data = <$fh> ) {
        chomp $data;
        next if $data =~ m{ \A \s* \Z }xms;

        my ($stnid, $co, $state, $active) = split m{\t}xms, $data;

        $lineno++;
        if ($lineno == 1) {
            die '*E* invalid input data'
                unless  $stnid eq 'StationId' and $active eq 'Active';
            next;
        }

        last if not $active;

        my @rangelist = parse_active_range($stnid, $active);

        next unless @rangelist;

        foreach my $range (@rangelist) {
            my ($from, $to) = split m{-}xms, $range;

            $to //= $from;

            foreach my $yr ($from..$to) {
                $count_href->{$yr}{$stnid}++;
            }
        }
    }

    return;
}

=head2 parse_active_range ($stnid, $active)

Sometime the active range in data retreived from the NOAA station
inventory is malformed. This routine tries to spot these malformed
ranges and fix them.

=cut

sub parse_active_range ($stnid, $active) {

    if ( $active =~ m{ \A \d\d,\d\d\d,\d\d\d \Z }xms ) {
        # misplaced commas, but we can fix it
        my $s = $active;
        $s =~ s{ [,] }{}xmsg;
        if ( $s =~ m{ (\d\d\d\d) (\d\d\d\d) }xms ) {
            $active = $1 . $DASH . $2;
        }
    }

    if ( $active !~ m{ \A $RANGELIST_RE \Z }xms ) {
        warn "*W* unrecognized range list at stnid $stnid: $active\n";
        return;
    }

    my @rangelist = split $COMMA, $active;

    return @rangelist;
}


########################################################################
# Script-standard Subroutines
########################################################################

=head2 get_options ( \@argv )

get_options encapsulates everything we need to process command line
options, or to set options when invoking this script from a test script.

Normally it's called by passing a reference to @ARGV; from a test script
you'd set up a local array variable to specify the options.

By convention, you should set up a file-scoped lexical variable named
$Opt and set it in the mainline using the return value from this function.
Then all options can be accessed used $Opt->option notation.

=cut

sub get_options ($argv_aref) {

    my @options = (
        'outclip',              # output data to the Windows clipboard
        'debug',                # enable debug() statements on stderr
        'help','usage|?',       # help
    );

    my %opt;

    # create a list of option key names by stripping the various adornments
    my @keys = map { (split m{ [!+=:|] }xms)[0] } grep { !ref  } @options;
    # initialize all possible options to undef
    @opt{ @keys } = ( undef ) x @keys;

    GetOptionsFromArray($argv_aref, \%opt, @options)
        or pod2usage(2);

    # Make %opt into an object and name it the same as what we usually
    # call the global options object.  Note that this doesn't set the
    # global -- the script will have to do that using the return value
    # from this function.  But, what this does is allow us to call
    # $Opt->help and other option within this function using the same
    # syntax as what we use in the script.  This is handy if you need
    # to rename option '-foo' to '-bar' because you can do a find/replace
    # on '$Opt->foo' and you'll get any instances of it here as well as
    # in the script.

    ## no critic [Capitalization]
    ## no critic [ProhibitReusedNames]
    my $Opt = _wrap_hash \%opt;

    pod2usage(1)             if $Opt->usage;
    pod2usage(-verbose => 2) if $Opt->help;


    return $Opt;
}

1;  # needed in case we import this as a module (e.g. for testing)

=head1 AUTHOR

Gary Puckering (jgpuckering@rogers.com)

=head1 LICENSE AND COPYRIGHT

Copyright 2022, Gary Puckering

=cut



( run in 1.475 second using v1.01-cache-2.11-cpan-71847e10f99 )