diff options
Diffstat (limited to 'Geocoding.pm')
-rw-r--r-- | Geocoding.pm | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/Geocoding.pm b/Geocoding.pm new file mode 100644 index 0000000..c64c590 --- /dev/null +++ b/Geocoding.pm @@ -0,0 +1,182 @@ +package Geo::USCensus::Geocoding; + +use strict; +use warnings; + +use LWP::UserAgent; +use JSON; +use URI; +use Geo::USCensus::Geocoding::Match; + +=head1 NAME + +Geo::USCensus::Geocoding - The U.S. Census Bureau geocoding service + +=head1 VERSION + +Version 0.01 + +=cut + +our $VERSION = '0.01'; +our $DEBUG = 1; + +=head1 SYNOPSIS + + use Geo::USCensus::Geocoding; + + my $request = { + # required fields + street => '123 Main Street', + city => 'San Francisco', # city + state => 'CA', # state/province + zip => '93102', # zip/postal code + # optional fields + benchmark => 'Public_AR_ACS2013', # default is "Public_AR_Current" + vintage => 'Census2010_ACS2013', # default is "Current_Current" + }; + my $result = Geo::USCensus::Geocoding->query($request); + + if ($result->matches) { + my $match = $result->match(0); + print $match->matchedAddress,"\n", + $match->coordinates->{x},',',$match->coordinates->{y},"\n", + $match->censustract,"\n"; + } + +=head1 CLASS METHODS + +=head2 query HASHREF + +Send a request to the web service. See +L<http://geocoding.geo.census.gov/geocoder> for API documentation. This +package will always use the JSON data format and the Geographies return type. + +Returns an object of class Geo::USCensus::Geocoding. + +=cut + +my $ua = LWP::UserAgent->new; +my $api_uri = 'http://geocoding.geo.census.gov/geocoder/geographies/address'; + +sub query { + my $class = shift; + my %opt = ( + benchmark => 'Public_AR_Current', + vintage => 'Current_Current', + ); + if (ref $_[0] eq 'HASH') { + %opt = (%opt, %{ $_[0] }); + } else { + %opt = (%opt, @_); + } + + $opt{format} = 'json'; + + foreach (qw(street city state zip)) { + die "$_ required\n" unless length($opt{$_}); + } + + my $uri = URI->new($api_uri); + $uri->query_form(\%opt); + warn "$class->query\n$uri\n\n" if $DEBUG; + my $http_req = HTTP::Request->new(GET => $uri->as_string); + my $resp = $ua->request($http_req); + my $self = { addr_response => $resp }; + bless $self, $class; + if ( $resp->is_success ) { + local $@; + my $tree = eval { from_json($resp->content) }; + if ($@) { + $self->message("Unable to parse response:\n$@"); + return $self; + } + if (!exists $tree->{result}) { + $self->message("Response does not contain geocoding results."); + warn $self->message. "\n".$resp->content."\n\n"; + return $self; + } + $tree = $tree->{result}; + + my @matches; + if (exists( $tree->{addressMatches} )) { + foreach my $am (@{ $tree->{addressMatches} }) { + push @matches, Geo::USCensus::Geocoding::Match->new($am); + } + } # else what? does this happen if there's no match? a proper REST + # interface should throw a 404 + $self->{matches} = \@matches; + } else { + $self->message( $resp->status_line ); + } + $self; +} + +=head1 METHODS + +=head2 message + +Sets/gets an explicit error status. + +=cut + +sub message { + my $self = shift; + if (@_) { + $self->{_message} = shift; + } + $self->{_message} || ''; +} + +=head2 matches + +Returns the number of matches found. + +=cut + +sub matches { + my $self = shift; + $self->{matches} ? scalar @{ $self->{matches} } : 0; +} + +=head2 match NUMBER + +Returns a specific match (starting from zero). Matches are returned +as L<Geo::USCensus::Geocoding::Match> objects, in the order they were +returned by the service. + +=cut + +sub match { + my $self = shift; + my $i = shift; + $self->{matches}->[$i]; +} + +=head1 AUTHOR + +Mark Wells, C<< <mark at freeside.biz> >> + +=head1 SUPPORT + +Commercial support for this module is available from Freeside Internet +Services: + + L<http://www.freeside.biz/> + +=back + + +=head1 LICENSE AND COPYRIGHT + +Copyright (C) 2014 Mark Wells. + +This program is free software; you can redistribute it and/or modify it +under the terms of either: the GNU General Public License as published +by the Free Software Foundation; or the Artistic License. + +See http://dev.perl.org/licenses/ for more information. + +=cut + +1; |