File Coverage

blib/lib/Geo/Coder/US/Census.pm
Criterion Covered Total %
statement 39 80 48.7
branch 4 26 15.3
condition 2 5 40.0
subroutine 12 15 80.0
pod 5 5 100.0
total 62 131 47.3


line stmt bran cond sub pod time code
1             package Geo::Coder::US::Census;
2              
3 4     4   407658 use strict;
  4         29  
  4         112  
4 4     4   19 use warnings;
  4         6  
  4         90  
5              
6 4     4   17 use Carp;
  4         7  
  4         167  
7 4     4   2068 use Encode;
  4         33105  
  4         234  
8 4     4   2277 use JSON;
  4         34678  
  4         16  
9 4     4   1999 use HTTP::Request;
  4         71244  
  4         108  
10 4     4   2412 use LWP::UserAgent;
  4         88621  
  4         167  
11 4     4   1687 use LWP::Protocol::https;
  4         339157  
  4         186  
12 4     4   50 use URI;
  4         8  
  4         80  
13 4     4   2182 use Geo::StreetAddress::US;
  4         183762  
  4         2796  
14              
15             =head1 NAME
16              
17             Geo::Coder::US::Census - Provides a Geo-Coding functionality for the US using L
18              
19             =head1 VERSION
20              
21             Version 0.05
22              
23             =cut
24              
25             our $VERSION = '0.05';
26              
27             =head1 SYNOPSIS
28              
29             use Geo::Coder::US::Census;
30              
31             my $geo_coder = Geo::Coder::US::Census->new();
32             my $location = $geo_coder->geocode(location => '4600 Silver Hill Rd., Suitland, MD');
33             # Sometimes the server gives a 500 error on this
34             $location = $geo_coder->geocode(location => '4600 Silver Hill Rd., Suitland, MD, USA');
35              
36             =head1 DESCRIPTION
37              
38             Geo::Coder::US::Census provides an interface to geocoding.geo.census.gov. Geo::Coder::US no longer seems to work.
39              
40             =head1 METHODS
41              
42             =head2 new
43              
44             $geo_coder = Geo::Coder::US::Census->new();
45             my $ua = LWP::UserAgent->new();
46             $ua->env_proxy(1);
47             $geo_coder = Geo::Coder::US::Census->new(ua => $ua);
48              
49             =cut
50              
51             sub new {
52 2     2 1 2467 my($class, %param) = @_;
53              
54 2   33     26 my $ua = delete $param{ua} || LWP::UserAgent->new(agent => __PACKAGE__ . "/$VERSION");
55 2   50     5267 my $host = delete $param{host} || 'geocoding.geo.census.gov/geocoder/locations/address';
56              
57 2         17 return bless { ua => $ua, host => $host }, $class;
58             }
59              
60             =head2 geocode
61              
62             $location = $geo_coder->geocode(location => $location);
63             # @location = $geo_coder->geocode(location => $location);
64              
65             print 'Latitude: ', $location->{'latt'}, "\n";
66             print 'Longitude: ', $location->{'longt'}, "\n";
67              
68             =cut
69              
70             sub geocode {
71 1     1 1 637 my $self = shift;
72 1         2 my %param;
73              
74 1 50       8 if(ref($_[0]) eq 'HASH') {
    50          
    50          
75 0         0 %param = %{$_[0]};
  0         0  
76             } elsif(ref($_[0])) {
77 0         0 Carp::croak('Usage: geocode(location => $location)');
78             } elsif(@_ % 2 == 0) {
79 1         3 %param = @_;
80             } else {
81 0         0 $param{location} = shift;
82             }
83              
84             my $location = $param{location}
85 1 50       21 or Carp::croak('Usage: geocode(location => $location)');
86              
87 0 0         if (Encode::is_utf8($location)) {
88 0           $location = Encode::encode_utf8($location);
89             }
90              
91 0 0         if($location =~ /,?(.+),\s*(United States|US|USA)$/i) {
92 0           $location = $1;
93             }
94              
95             # Remove county from the string, if that's included
96             # Assumes not more than one town in a state with the same name
97             # in different counties - but the census Geo-Coding doesn't support that
98             # anyway
99 0 0         if($location =~ /^(\d+\s+[\w\s]+),\s*([\w\s]+),\s*[\w\s]+,\s*([A-Za-z]+)$/) {
100 0           $location = "$1, $2, $3";
101             }
102              
103 0           my $uri = URI->new("https://$self->{host}");
104 0           $location =~ s/\s/+/g;
105 0           my $hr = Geo::StreetAddress::US->parse_address($location);
106              
107 0           my %query_parameters = ('format' => 'json', 'benchmark' => 'Public_AR_Current');
108 0 0         if($hr->{'street'}) {
109 0 0         if($hr->{'number'}) {
110 0           $query_parameters{'street'} = $hr->{'number'} . ' ' . $hr->{'street'} . ' ' . $hr->{'type'};
111             } else {
112 0           $query_parameters{'street'} = $hr->{'street'} . ' ' . $hr->{'type'};
113             }
114 0 0         if($hr->{'suffix'}) {
115 0           $query_parameters{'street'} .= ' ' . $hr->{'suffix'};
116             }
117             }
118 0           $query_parameters{'city'} = $hr->{'city'};
119 0           $query_parameters{'state'} = $hr->{'state'};
120              
121 0           $uri->query_form(%query_parameters);
122 0           my $url = $uri->as_string();
123              
124 0           my $res = $self->{ua}->get($url);
125              
126 0 0         if($res->is_error()) {
127 0           Carp::croak("$url API returned error: " . $res->status_line());
128 0           return;
129             }
130              
131 0           my $json = JSON->new->utf8();
132 0           return $json->decode($res->content());
133              
134             # my @results = @{ $data || [] };
135             # wantarray ? @results : $results[0];
136             }
137              
138             =head2 ua
139              
140             Accessor method to get and set UserAgent object used internally. You
141             can call I for example, to get the proxy information from
142             environment variables:
143              
144             $geo_coder->ua()->env_proxy(1);
145              
146             You can also set your own User-Agent object:
147              
148             $geo_coder->ua(LWP::UserAgent::Throttled->new());
149              
150             =cut
151              
152             sub ua {
153 0     0 1   my $self = shift;
154 0 0         if (@_) {
155 0           $self->{ua} = shift;
156             }
157 0           $self->{ua};
158             }
159              
160             =head2 reverse_geocode
161              
162             # $location = $geo_coder->reverse_geocode(latlng => '37.778907,-122.39732');
163              
164             # Similar to geocode except it expects a latitude/longitude parameter.
165              
166             Not supported.
167              
168             =cut
169              
170             sub reverse_geocode {
171             # my $self = shift;
172              
173             # my %param;
174             # if (@_ % 2 == 0) {
175             # %param = @_;
176             # } else {
177             # $param{latlng} = shift;
178             # }
179              
180             # my $latlng = $param{latlng}
181             # or Carp::croak("Usage: reverse_geocode(latlng => \$latlng)");
182              
183             # return $self->geocode(location => $latlng, reverse => 1);
184 0     0 1   Carp::croak('Reverse geocode is not supported');
185             }
186              
187             =head2 run
188              
189             You can also run this module from the command line:
190              
191             perl Census.pm 1600 Pennsylvania Avenue NW, Washington DC
192              
193             =cut
194              
195             __PACKAGE__->run(@ARGV) unless caller();
196              
197             sub run {
198 0     0 1   require Data::Dumper;
199              
200 0           my $class = shift;
201              
202 0           my $location = join(' ', @_);
203              
204 0           my @rc = $class->new()->geocode($location);
205              
206 0 0         die "$0: geocoding failed" unless(scalar(@rc));
207              
208 0           print Data::Dumper->new([\@rc])->Dump();
209             }
210              
211             =head1 AUTHOR
212              
213             Nigel Horne
214              
215             Based on L.
216              
217             This library is free software; you can redistribute it and/or modify
218             it under the same terms as Perl itself.
219              
220             Lots of thanks to the folks at geocoding.geo.census.gov.
221              
222             =head1 BUGS
223              
224             Should be called Geo::Coder::NA for North America.
225              
226             =head1 SEE ALSO
227              
228             L, L
229              
230             https://www.census.gov/data/developers/data-sets/Geocoding-services.html
231              
232             =head1 LICENSE AND COPYRIGHT
233              
234             Copyright 2017,2018 Nigel Horne.
235              
236             This program is released under the following licence: GPL2
237              
238             =cut
239              
240             1;