Wednesday, July 24, 2013

Calculating distances in meatspace

I'm working on an automated provisioning system for a very large VPN network. For each new VPN client, I need to select a headend site where VPN tunnels should land. The only data available is that which I can get from the sales and billing systems. This system offers me the zip code of the install site.

Using the zip code of the install site, and the known zip codes of my various head-end sites, I'm able to select the destination for the primary and secondary VPN tunnels.

It's not perfect (physical location often has little to do with network path), but it's better than nothing. I haven't decided how to handle non-US sites yet.

I'm using a database of US zip codes found here, and a very dirty perl script. The script grabs the latitude and longitude of two zip codes from the database, and prints the mileage between them as calculated using the Haversine formula for great circle distance.

It runs like this:


Christophers-MacBook-Pro:scripts chris$ zipdistance.pl 95134 60614
1837 miles
Christophers-MacBook-Pro:scripts chris$

The script:


#!/opt/local/bin/perl
use GIS::Distance;

my $dbfile="/Users/chris/Downloads/zipcode.csv";
my $lat1,$lon1,$lat2,$lon2;

sub usage{
  printf "Usage: $0 <zipcode> <zipcode>\n";
  exit;
}

if (@ARGV != 2) {usage;};
unless ($ARGV[0] =~ /[0-9]{5}/) {usage;}
unless ($ARGV[1] =~ /[0-9]{5}/) {usage;}

my @sorted = sort @ARGV;

open(DB, '<', $dbfile);

FIRST: while (<DB>) {
  if ($_ =~ /^.$sorted[0]/) {
    (my $trash,my $trash,my $trash,$lat1,$lon1)=split(",",$_);
    my $i;
    ($i) = $lat1 =~ /"([^"]*)"/; $lat1 = $i;
    ($i) = $lon1 =~ /"([^"]*)"/; $lon1 = $i;
    last FIRST;
  }
}

SECOND: while (<DB>) {
  if ($_ =~ /^.$sorted[1]/) {
    (my $trash,my $trash,my $trash,$lat2,$lon2)=split(",",$_);
    my $i;
    ($i) = $lat2 =~ /"([^"]*)"/; $lat2 = $i;
    ($i) = $lon2 =~ /"([^"]*)"/; $lon2 = $i;
    last SECOND;
  }
}

if ("$lat1" == "" || "$lat2" == "") {
  printf "Unknown distance\n";
  exit 1;
}

my $gis = GIS::Distance->new();
my $distance = $gis->distance( $lat1,$lon1 => $lat2,$lon2 );
printf ("%d miles\n",$distance->miles());