Global Temperature Anomalies (1851 - 1990)

The data set used here is available by HTTP at http://cdiac.ornl.gov/ndps/ndp020.html.

To put the data in a suitable format for plotting, I first ran the Fortran codes provided for each part (compiled with no problems using g77).

∆T>5 5≥∆T>4 4≥∆T>3 3≥∆T>2 2≥∆T>1
1≥∆T>0 0≥∆T>-1 -1≥∆T>-2 -2≥∆T>-3 -4≥∆T

Then, I ran the following Perl script to drop missing observations and combine the three component files, for the Northern and Southern hemispheres and Antarctica. Finally, I sorted the file using the *nix command line sort utility.

Script : jones-xform.pl

#!/usr/bin/perl

use strict;
use warnings;

use FindBin qw( $Bin );

use File::Basename;
use File::Spec::Functions qw( catfile );

my @input_files = map { catfile $Bin, $_ }
                qw( ant5790.txt nhem1990.txt shem1990.txt );

for my $file ( @input_files ) {
    eval {
        xform_file( $file );
    };

    warn "$@\n" if $@;
}

sub xform_file {
    my ($file) = @_;

    my ($src) = fileparse( $file, '.txt' );

    print STDERR "Processing '$file' ...\n";

    open my $input, '<', $file
        or die "Cannot open '$file': $!";

    my ($year, $month, $anomaly_section);

    my @lattitudes;

    LINE: while ( my $line = <$input> ) {
        chomp $line;
        
        $line =~ s/\A\s+//;
        $line =~ s/\s+\z//;

        next LINE unless length $line;

        if ( $line =~ m{
                \A
                TEMPERATURE\s+ANOMALY\s+DATA
                \s+
                YEAR\s+=\s+(\d{4})
                \s+
                MONTH\s+=\s+(\d{1,2})
            }x ) {
            ($year, $month) = ($1, $2);
            $anomaly_section = 1;

            print STDERR "$year/$month . " 
                unless (12 * $year + $month ) % 36; 
            
            next LINE;
        }

        $anomaly_section = 0 if $line =~ /\ANUMBER/ or $line =~ /\AMEAN/;

        next LINE unless $anomaly_section;

        if ( $line =~ m{\A(?:\d+(?:N|S)\s+){2,}} ) {
            my @lat_centers = split /\s+/, $line;

            for ( my $i = 0; $i < $#lat_centers; ++ $i ) {
                my ($degree, $hemisphere) 
                    = ( $lat_centers[ $i ] =~ /\A(\d{1,2})(N|S)\z/ );

                if ( $hemisphere eq 'N' ) {
                    $lattitudes[ $i ] = { 
                        south => $degree - 2.5, 
                        north => $degree + 2.5,
                    };
                }
                elsif ( $hemisphere eq 'S' ) {
                    $lattitudes[ $i ] = {
                        south => - ( $degree + 2.5),
                        north => - ( $degree - 2.5),
                    };
                }
                else {
                    warn 'How did I get here?';
                }
            }
            next LINE;
        }

        if ( $line =~ /\A(\d{1,3})(E|W)\s+(.+)\z/ ) {
            my ($degree, $hemisphere, $data) = ($1, $2, $3);
            my $longitude;

            if ( $hemisphere eq 'W' ) {
                $longitude = {
                    west => - ( $degree + 5 ),
                    east => - ( $degree - 5 ),
                };
            }
            elsif ( $hemisphere eq 'E' ) {
                $longitude = {
                    west => $degree - 5,
                    east => $degree + 5,
                };
            }
            else {
                warn 'Wot? I am lost.';
            }

            my @celsius = split /\s+/, $data;

            ANOMALY: for ( my $i = 0; $i < $#celsius; ++ $i ) {
                next ANOMALY if $celsius[ $i ] eq '-9999'; 
                printf(
                    "%4.4d|%2.2d|%.1f|%.1f|%.1f|%.1f|%.2f|%s\n",
                    $year, 
                    $month, 
                    $lattitudes[ $i ]->{south},
                    $longitude->{west},
                    $lattitudes[ $i ]->{north},
                    $longitude->{east},
                    $celsius[$i] / 100,
                    $src,
                );
            }
            next LINE;
        }
    }

    print STDERR "\n'$file' done\n";

    close $input or die "Cannot close '$file': $!";

}
__END__

Generating the Frames

The script I used for generating the frames is identical to the one used for the CRUTEM3 data set except for the name of the input file.

Comparison with GISS Data

Here is the the frame for January 1881 along with the same frame from the GISS data set for comparison reduced in both dimensions by 50%:

Jan 1881 (Jones, 1990)Jan 1881 (GISS)
[ Jones (1990) World Temperature Anomalies, January 1881 ][ GISS World Temperature Anomalies, January 1881 ]

The Video

Once I had the sequence of 1680 frames (one frame per month, from January 1851 to December 1990), I used VirtualDub to convert it to an MPEG4 encoded AVI at 4 frames per second (that is, every second in the animation corresponds to four months).

FYI, it takes about 10 minutes to generate all the frames and about two minutes to encode them on a low end laptop. The resulting video file is about 45 Mb.