#!/usr/bin/perl # # # Great Circle Distance Batch script # Written by A. Gray 22 September 2001 # # Input file format: # location,locationid,deg min [N/S], deg min [E/W] # ^SP ^SP ^SP ^SP # location is a string which will be used when outputting the matrix # locationid is a arbitary number, not currently used # deg are the degrees of latitude or longitude # deg are the minutes of latitude or longitude # use POSIX; use Math::Trig; # $EARTH_RADIUS = 6371.0; # radius of earth in kilometres $KM_TO_MILES = 0.621; # $infile = "sample.csv"; # &read_datafile; # # &output_human_readable; &output_matrix; exit 0; # sub output_matrix { printf(" ,"); foreach $country ( sort keys %country_code ) { printf("%s,",$country); } print "\n"; foreach $country ( sort keys %country_code ) { print $country,","; if ( $error{$country} == 1 ) { print " *** Input data parse error \r\n"; print "---------------------------------------\r\n"; next; } foreach $country2 ( sort keys %country_code ) { if ( $country eq $country2 ) { print "0,"; } else { if ( $error{$country2} == 1 ) { printf("ERROR,"); } else { $distance = &calc_distance($latitude{$country} ,$longitude{$country}, $latitude{$country2} ,$longitude{$country2}), # $miles = $distance * $KM_TO_MILES; printf("%7.1f,",$distance); } } } print "\r\n"; } } sub output_human_readable { foreach $country ( sort keys %country_code ) { print $country,"\r\n"; if ( $error{$country} == 1 ) { print " *** Input data parse error \r\n"; print "---------------------------------------\r\n"; next; } foreach $country2 ( sort keys %country_code ) { if ( $country eq $country2 ) { next; } if ( $error{$country2} == 1 ) { printf(" %s *** Input data parse error\r\n",$country2); } else { $distance = &calc_distance($latitude{$country} ,$longitude{$country}, $latitude{$country2} ,$longitude{$country2}), $miles = $distance * $KM_TO_MILES; printf(" %-30s, %7.1f, %7.1f\r\n",$country2,$distance,$miles); } } print "---------------------------------------\r\n"; } } # sub read_datafile { open(DATA,$infile) || die "unable to open $infile $! \n"; while( ) { chomp; $_ =~ s/\s+$//; # Need this to handle DOS format txt files @vals = split(/,/); $country = $vals[0]; $code = $vals[1]; $lat = $vals[2]; $long = $vals[3]; $y = $#vals + 1; # # printf(STDERR ">>%s:%s:%s:%s<<\n",$country,$code,$lat,$long); if ( $y eq 4 ) { $country =~ s/^\s+//; $code =~ s/^\s+//; $lat=&parse_lat_long($lat); $long=&parse_lat_long($long); # printf("%4d,%s:%s:%7.2f:%7.2f\n",$y,$country,$code,$lat,$long); $latitude{$country} = $lat; $longitude{$country} = $long; $country_code{$country} = $code; $country_name{$code} = $country; if ( ( $lat eq "ERROR" ) || ( $long eq "ERROR" ) ) { $error{$country} = 1; } else { $error{$country} = 0; } } else { print STDERR "y = ",$y; printf(STDERR "*** Parse error at line %d ;\n",$.); printf(STDERR " %s\n",$_); } } close DATA; } # sub calc_distance() { my($lat1,$lon1,$lat2,$lon2) = @_; my($distance); $p1 = cos($lat1) * cos($lon1) * cos($lat2) * cos($lon2); $p2 = cos($lat1) * sin($lon1) * cos($lat2) * sin($lon2); $p3 = sin($lat1) * sin($lat2); $distance = acos($p1+$p2+$p3)*$EARTH_RADIUS; return($distance); } # sub parse_lat_long { local $y; $x=join(" ",@_); $x =~ s/^\s+//; $y=0; $y = split(/ /,$x); # printf(STDERR "x = >>>%s y = %s ***\n",$x,$y); if ( $y == 3 ) { ($degrees,$minutes,$compass) = split(/ /,$x); $compass = uc($compass); $d = $degrees + ( $minutes / 60.0 ); if ( ( $compass eq "S" ) || ( $compass eq "W" )) { $d = $d * (-1); } $d = deg2rad($d); # printf("%s,%s-%s ... %6.4f\n",$degrees,$minutes,$compass,$d); } else { printf(STDERR "*** Lat/Long parse error at line %d ;\n",$.); printf(STDERR "*** >>>%s<<<",$x); $d = "ERROR"; } return($d); }