#!/usr/bin/perl # Filename: latlong # Author: David Ljung Madison # See License: http://MarginalHacks.com/License/ # Description: Latitude/longitude lookup from google maps # Uses a cache if desired. use strict; ################################################## # Setup the variables ################################################## my $PROGNAME = $0; $PROGNAME =~ s|.*/||; my $KEYFILE = "google_api_key"; $KEYFILE = ".google_api_key" unless -f $KEYFILE; $KEYFILE = "$ENV{HOME}/.google_api_key" unless -f $KEYFILE; my %CACHE; ################################################## # Usage ################################################## sub fatal { foreach my $msg (@_) { print STDERR "[$PROGNAME] ERROR: $msg\n"; } exit(-1); } sub usage { foreach my $msg (@_) { print STDERR "ERROR: $msg\n"; } print STDERR < Lookup latitude and longitude using Google Maps API -key Specify google maps API key -keyfile Specify file holding google maps API key -cache Address->long/lat cache to avoid wasting bandwidth -d Set debug mode USAGE exit -1; } sub parseArgs { my $opt = { }; my @addr; while (my $arg=shift(@ARGV)) { if ($arg =~ /^-h$/) { usage(); } if ($arg =~ /^-d$/) { $MAIN::DEBUG=1; next; } if ($arg =~ /^-key$/) { $opt->{key} = shift(@ARGV); next; } if ($arg =~ /^-keyfile$/) { $opt->{keyfile} = shift(@ARGV); next; } if ($arg =~ /^-cache$/) { $opt->{cache} = shift(@ARGV); next; } if ($arg =~ /^-/) { usage("Unknown option: $arg"); } push(@addr,$arg); } usage("No address defined") unless @addr; $opt->{addr} = join(' ',@addr); getKey($opt); $opt; } sub debug { return unless $MAIN::DEBUG; foreach my $msg (@_) { print STDERR "[$PROGNAME] $msg\n"; } } ################################################## # The "cache" is just a simple flat file # Feel free to replace with a database. # Furthermore it doesn't do much to handle # identical addresses other than what "escape" does ################################################## sub readCache { my ($opt) = @_; return unless $opt->{cache}; return unless -f $opt->{cache}; usage("Can't open cache [$opt->{cache}]") unless open(CACHE,"<$opt->{cache}"); debug("Reading cache [$opt->{cache}]"); while () { $CACHE{$1}=$2 if /^(.+)\t(.+)$/; } close CACHE; } sub writeCache { my ($opt) = @_; return unless $opt->{cache}; my $write = "$opt->{cache}.tmp"; usage("Can't write cache [$write]") unless open(CACHE,">$write"); debug("Writing cache [$write]"); foreach my $key ( keys %CACHE ) { print CACHE "$key\t$CACHE{$key}\n"; } close CACHE; rename($write,$opt->{cache}); } ################################################## # The maps request (using HTTP) ################################################## sub escape { my($toencode) = @_; $toencode=~s/([^a-zA-Z0-9_\-. ])/uc sprintf("%%%02x",ord($1))/eg; $toencode =~ tr/ /+/; # spaces become pluses return $toencode; } sub getCoords { my ($opt,$addr) = @_; my $eaddr = escape($addr); # Check cache return $CACHE{$eaddr} if $CACHE{$eaddr}; my $url = "http://maps.google.com/maps/geo?q=$eaddr&sensor=false&key=$opt->{key}"; debug("URL: $url"); my $var = qx(GET "$url"); usage("Couldn't find co-ords [$addr]") unless $var =~ /coordinates.*:\s*\[\s*(-?\d+\.\d+),\s*(-?\d+\.\d+)/; $CACHE{$eaddr} = "$1,$2"; return "$1,$2"; } ################################################## # Google Maps API Key ################################################## sub getKey { my ($opt) = @_; return if $opt->{key}; my $keyfile = $opt->{keyfile} || $KEYFILE; if (open(KEYFILE,"<$keyfile")) { debug("Reading keyfile: $keyfile"); $opt->{key} = ; close KEYFILE; chomp($opt->{key}); } else { usage("Couldn't open keyfile: $keyfile") if $opt->{keyfile}; } usage(<{key}; You need a Google Maps API Key. If you don't have one, visit: http://code.google.com/apis/maps/signup.html If you have one, either save it in a keyfile: $keyfile Or supply it with the -key argument. MISSING_KEY debug("Found key: $opt->{key}"); } ################################################## # Main code ################################################## sub main { my $opt = parseArgs(); readCache($opt); print getCoords($opt,$opt->{addr}),"\n"; writeCache($opt); } main();