Skip to content

Commit

Permalink
Created t/openaddr.t
Browse files Browse the repository at this point in the history
  • Loading branch information
nigelhorne committed Feb 23, 2018
1 parent 0b32530 commit 313bda2
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 18 deletions.
1 change: 1 addition & 0 deletions Changes
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ Revision history for Geo-Coder-Free
0.06
Fix Github issue 14
Updated to latest MaxMind databases
Started support for a local copy of results.openaddresses.io

0.05 Sun Jan 28 17:33:33 EST 2018
Fix http://www.cpantesters.org/cpan/report/b10c956b-6bf9-1014-9a47-dc46d49c4260
Expand Down
1 change: 1 addition & 0 deletions MANIFEST
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ t/admin2.t
t/cities.t
t/lib/MyLogger.pm
t/lookup.t
t/openaddr.t
t/pod-cm.t
t/pod.t
t/spelling.t
Expand Down
120 changes: 109 additions & 11 deletions lib/Geo/Coder/Free.pm
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use Error::Simple;
use File::Spec;
use Locale::US;
use CHI;
use Locale::Country;

our %admin1cache;
our %admin2cache;
Expand Down Expand Up @@ -40,10 +41,14 @@ our $VERSION = '0.05';

=head1 SYNOPSIS
use Geo::Coder::Free;
use Geo::Coder::Free;
my $geocoder = Geo::Coder::Free->new();
my $location = $geocoder->geocode(location => 'Ramsgate, Kent, UK');
my $geocoder = Geo::Coder::Free->new();
my $location = $geocoder->geocode(location => 'Ramsgate, Kent, UK');
# Use a local download of http://results.openaddresses.io/
my $openaddr_geocoder = Geo::Coder::Freee->new(openaddr => $ENV{'OPENADDR_HOME'});
$location = $openaddr_geocoder->geocode(location => '1600 Pennsylvania Avenue NW, Washington DC, USA');
=head1 DESCRIPTION
Expand Down Expand Up @@ -122,6 +127,10 @@ sub geocode {
my $location = $param{location}
or Carp::croak("Usage: geocode(location => \$location)");

if($location =~ /^(.+),\s*Washington\s*DC,(.+)$/) {
$location = "$1, Washington, DC, $2";
}

if($known_locations{$location}) {
return $known_locations{$location};
}
Expand All @@ -130,6 +139,7 @@ sub geocode {
my $state;
my $country;
my $country_code;
my $street;
my $concatenated_codes;

if($location =~ /^([\w\s\-]+)?,([\w\s]+),([\w\s]+)?$/) {
Expand Down Expand Up @@ -160,37 +170,125 @@ sub geocode {
$state =~ s/\s$//g;
$country =~ s/^\s//g;
$country =~ s/\s$//g;
} elsif($self->{openaddr}) {
if($location =~ /^([\w\s\-]+)?,([\w\s]+),([\w\s]+),([\w\s]+),\s*(Canada|United States|USA|US)?$/) {
$street = $1;
$location = $2;
$county = $3;
$state = $4;
$country = $5;
$location =~ s/^\s//g;
$location =~ s/\s$//g;
$county =~ s/^\s//g;
$county =~ s/\s$//g;
$state =~ s/^\s//g;
$state =~ s/\s$//g;
$country =~ s/^\s//g;
$country =~ s/\s$//g;
} else {
# TODO: Parse full postal address
die 'TODO - add support for full addresses on openaddr';
}
} else {
Carp::croak(__PACKAGE__, ' only supports towns, not full addresses');
Carp::croak(__PACKAGE__, ' only supports towns, not full addresses when openaddr is not given');
return;
}

if($country) {
if($self->{openaddr}) {
my $openaddr_db;
my $countrydir = File::Spec->catfile($self->{openaddr}, lc($country));
my $countrydir = File::Spec->catfile($self->{openaddr}, lc(country2code($country)));
# TODO: Don't use statewide if the county can be determined, since that file will be much smaller
if($state && (-d $countrydir)) {
# TODO: Locale::CA for Canadian provinces
if(($state =~ /^(United States|USA|US)$/) && (length($state) > 2)) {
if(my $twoletterstate = Locale::US->new()->{state2code}{uc($state)}) {
$state = $twoletterstate;
}
} elsif($country =~ /^(United States|USA|US)$/) {
if(my $twoletterstate = Locale::US->new()->{state2code}{uc($state)}) {
$state = $twoletterstate;
}
my $l = length($state);
if($l > 2) {
if(my $twoletterstate = Locale::US->new()->{state2code}{uc($state)}) {
$state = $twoletterstate;
}
} elsif($l == 2) {
$state = lc($state);
}
}
my $statedir = File::Spec->catfile($countrydir, lc($state));
my $statedir = File::Spec->catfile($countrydir, $state);
if(-d $statedir) {
$openaddr_db = Geo::Coder::Free::DB::OpenAddr->new(directory => $statedir);
$openaddr_db = $self->{$statedir} || Geo::Coder::Free::DB::OpenAddr->new(directory => $statedir, table => 'statewide');
if($location) {
$self->{$statedir} = $openaddr_db;
my $rc;
if($street) {
$rc = $openaddr_db->fetchrow_hashref('street' => uc($street), 'city' => uc($location));
} else {
$rc = $openaddr_db->fetchrow_hashref('city' => uc($location));
}
if($rc && defined($rc->{'lat'})) {
$rc->{'latitude'} = $rc->{'lat'};
$rc->{'longitude'} = $rc->{'lon'};
return $rc;
}
if($location =~ /^(\d+)\s+(.+)$/) {
$rc = $openaddr_db->fetchrow_hashref('number' => $1, 'street' => uc($2), 'city' => uc($county));
} else {
$rc = $openaddr_db->fetchrow_hashref('street' => uc($location), 'city' => uc($county));
}
if($rc && defined($rc->{'lat'})) {
$rc->{'latitude'} = $rc->{'lat'};
$rc->{'longitude'} = $rc->{'lon'};
return $rc;
}
}
die $statedir;
}
} elsif($county && (-d $countrydir)) {
my $is_state;
if($country =~ /^(United States|USA|US)$/) {
my $l = length($county);
if($l > 2) {
if(my $twoletterstate = Locale::US->new()->{state2code}{uc($county)}) {
$county = $twoletterstate;
$is_state = 1;
}
} elsif($l == 2) {
$county = lc($county);
$is_state = 1;
}
}
my $countydir = File::Spec->catfile($countrydir, lc($county));
if(-d $countydir) {
$openaddr_db = Geo::Coder::Free::DB::OpenAddr->new(directory => $countydir);
if($is_state) {
$openaddr_db = $self->{$countydir} || Geo::Coder::Free::DB::OpenAddr->new(directory => $countydir, table => 'statewide');
$self->{$countydir} = $openaddr_db;
if($location) {
my $rc = $openaddr_db->fetchrow_hashref('city' => uc($location));
if($rc && defined($rc->{'lat'})) {
$rc->{'latitude'} = $rc->{'lat'};
$rc->{'longitude'} = $rc->{'lon'};
return $rc;
}
}
die;
} else {
$openaddr_db = Geo::Coder::Free::DB::OpenAddr->new(directory => $countydir);
die $countydir;
}
}
} else {
$openaddr_db = Geo::Coder::Free::DB::OpenAddr->new(directory => $countrydir);
die;
}
if($openaddr_db) {
# Store the handle in $self.
die "TBD";
}
die;
}
if($state && $admin1cache{$state}) {
$concatenated_codes = $admin1cache{$state};
Expand Down Expand Up @@ -412,13 +510,13 @@ it under the same terms as Perl itself.
Lots of lookups fail at the moment.
=head1 TODO
The openaddresses.io code has yet to be compeleted. There are die()s where the code path has yet to be written.
Add support for a local copy of results.openaddresses.io.
The MaxMind data only contains cities. The openaddresses data doesn't cover the globe.
=head1 SEE ALSO
VWF, MaxMind and geonames.
VWF, openaddresses, MaxMind and geonames.
=head1 LICENSE AND COPYRIGHT
Expand Down
15 changes: 8 additions & 7 deletions lib/Geo/Coder/Free/DB.pm
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ sub new {
return bless {
logger => $args{'logger'} || $logger,
directory => $args{'directory'} || $directory,
cache => $args{'cache'} || $cache
cache => $args{'cache'} || $cache,
table => $args{'table'}
}, $class;
}

Expand Down Expand Up @@ -85,7 +86,7 @@ sub _open {
((ref($_[0]) eq 'HASH') ? %{$_[0]} : @_)
);

my $table = ref($self);
my $table = $self->{table} || ref($self);
$table =~ s/.*:://;

if($self->{'logger'}) {
Expand Down Expand Up @@ -217,7 +218,7 @@ sub selectall_hash {
my $self = shift;
my %params = (ref($_[0]) eq 'HASH') ? %{$_[0]} : @_;

my $table = ref($self);
my $table = $self->{table} || ref($self);
$table =~ s/.*:://;

$self->_open() if(!$self->{$table});
Expand Down Expand Up @@ -270,10 +271,10 @@ sub fetchrow_hashref {
my $self = shift;
my %params = (ref($_[0]) eq 'HASH') ? %{$_[0]} : @_;

my $table = ref($self);
my $table = $self->{table} || ref($self);
$table =~ s/.*:://;

$self->_open() if(!$self->{table});
$self->_open() if(!$self->{$table});

my $query = "SELECT DISTINCT * FROM $table";
my @args;
Expand All @@ -300,7 +301,7 @@ sub execute {
my $self = shift;
my %args = (ref($_[0]) eq 'HASH') ? %{$_[0]} : @_;

my $table = ref($self);
my $table = $self->{table} || ref($self);
$table =~ s/.*:://;

$self->_open() if(!$self->{table});
Expand Down Expand Up @@ -342,7 +343,7 @@ sub AUTOLOAD {

my $self = shift or return undef;

my $table = ref($self);
my $table = $self->{table} || ref($self);
$table =~ s/.*:://;

$self->_open() if(!$self->{$table});
Expand Down
6 changes: 6 additions & 0 deletions lib/Geo/Coder/Free/DB/OpenAddr.pm
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,10 @@ use Geo::Coder::Free::DB;

our @ISA = ('Geo::Coder::Free::DB');

sub _open {
my $self = shift;

return $self->SUPER::_open(sep_char => ',', column_names => ['lon', 'lat', 'number', 'street', 'unit', 'city', 'district', 'region', 'postcode', 'id', 'hash']);
}

1;
89 changes: 89 additions & 0 deletions t/openaddr.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#!perl -w

use warnings;
use strict;
use Test::Most tests => 36;
use Test::Number::Delta;
use Test::Carp;

BEGIN {
use_ok('Geo::Coder::Free');
}

OPENADDR: {
SKIP: {
if($ENV{'OPENADDR_HOME'}) {
diag('This will take some time and memory');

my $geocoder = new_ok('Geo::Coder::Free' => [ openaddr => $ENV{'OPENADDR_HOME'} ]);

my $location = $geocoder->geocode('Indianapolis, Indiana, USA');
ok(defined($location));
delta_within($location->{latitude}, 38.62, 1e-2);
delta_within($location->{longitude}, -87.18, 1e-2);

$location = $geocoder->geocode('Silver Spring, Maryland, USA');
ok(defined($location));
delta_within($location->{latitude}, 38.99, 1e-2);
delta_within($location->{longitude}, -76.99, 1e-2);

$location = $geocoder->geocode('Silver Spring, MD, USA');
ok(defined($location));
delta_within($location->{latitude}, 38.99, 1e-2);
delta_within($location->{longitude}, -76.99, 1e-2);

$location = $geocoder->geocode('Silver Spring, Montgomery, MD, USA');
ok(defined($location));
delta_within($location->{latitude}, 38.99, 1e-2);
delta_within($location->{longitude}, -76.99, 1e-2);

$location = $geocoder->geocode('Silver Spring, Maryland, United States');
ok(defined($location));
delta_within($location->{latitude}, 38.99, 1e-2);
delta_within($location->{longitude}, -76.99, 1e-2);

$location = $geocoder->geocode('Silver Spring, Montgomery County, Maryland, USA');
ok(defined($location));
delta_within($location->{latitude}, 38.99, 1e-2);
delta_within($location->{longitude}, -76.99, 1e-2);

$location = $geocoder->geocode('Rockville Pike, Rockville, Montgomery County, MD, USA');
ok(defined($location));
delta_within($location->{latitude}, 39.09, 1e-2);
delta_within($location->{longitude}, -77.15, 1e-2);

$location = $geocoder->geocode('Rockville Pike, Rockville, MD, USA');
ok(defined($location));
delta_within($location->{latitude}, 39.09, 1e-2);
delta_within($location->{longitude}, -77.15, 1e-2);

$location = $geocoder->geocode({ location => 'Rockville, Montgomery County, MD, USA' });
ok(defined($location));
delta_within($location->{latitude}, 39.08, 1e-2);
delta_within($location->{longitude}, -77.14, 1e-2);

$location = $geocoder->geocode(location => 'Rockville, Montgomery County, Maryland, USA');
ok(defined($location));
delta_within($location->{latitude}, 39.08, 1e-2);
delta_within($location->{longitude}, -77.14, 1e-2);

$location = $geocoder->geocode(location => '1600 Pennsylvania Avenue NW, Washington DC, USA');
delta_within($location->{latitude}, 38.90, 1e-2);
delta_within($location->{longitude}, -77.04, 1e-2);

# my $address = $geocoder->reverse_geocode(latlng => '51.50,-0.13');
# like($address->{'city'}, qr/^London$/i, 'test reverse');

does_croak(sub {
$location = $geocoder->geocode();
});

does_croak(sub {
$location = $geocoder->reverse_geocode();
});
} else {
diag('Set OPENADDR_HOME to enable openaddresses.io testing');
skip 'OPENADDR_HOME not defined', 35;
}
}
}

0 comments on commit 313bda2

Please sign in to comment.