File Coverage

blib/lib/Geo/Coder/Geocoder/US.pm
Criterion Covered Total %
statement 50 91 54.9
branch 5 26 19.2
condition 0 3 0.0
subroutine 12 17 70.5
pod 5 5 100.0
total 72 142 50.7


line stmt bran cond sub pod time code
1             package Geo::Coder::Geocoder::US;
2              
3 2     2   1015 use 5.006002;
  2         5  
4              
5 2     2   8 use strict;
  2         3  
  2         31  
6 2     2   7 use warnings;
  2         3  
  2         35  
7              
8 2     2   8 use Carp;
  2         2  
  2         99  
9 2     2   540 use LWP::UserAgent;
  2         36508  
  2         48  
10 2     2   1260 use Text::CSV;
  2         30364  
  2         67  
11 2     2   14 use URI;
  2         3  
  2         70  
12             # use URI::Escape qw{ uri_escape };
13              
14             our $VERSION = '0.009';
15              
16 2         607 use constant RETRACTION_MESSAGE =>
17 2     2   9 'Geo::Coder::Geocoder::US is being retracted, because the underlying web site no longer exists';
  2         3  
18              
19             Carp::cluck( RETRACTION_MESSAGE );
20             {
21              
22             ## my %valid_arg = map { $_ => 1 } qw{ debug interface ua };
23             my %valid_arg = map { $_ => 1 } qw{ debug ua };
24              
25             sub new {
26 1     1 1 1412 my ( $class, %args ) = @_;
27 1         110 carp( RETRACTION_MESSAGE );
28              
29 1 50       8 ref $class
30             and $class = ref $class;
31              
32             ## exists $args{interface}
33             ## or $args{interface} = 'namedcsv';
34              
35             exists $args{ua}
36 1 50       8 or $args{ua} = LWP::UserAgent->new();
37              
38 1         2394 my $self = \%args;
39 1         3 bless $self, $class;
40              
41 1         5 foreach my $key ( sort keys %args ) {
42 1 50       13 exists $valid_arg{$key}
43             or croak "Argument $key is invalid";
44 1         8 $self->$key( $args{$key} );
45             }
46              
47             # Fake up the interface attribute, which I would like to keep
48             # internally for a bit, but am not sure I want to expose.
49              
50 1         2 $args{interface} = 'namedcsv';
51 1         2 $args{_interface} = \&_geocode_namedcsv;
52              
53 1         3 return $self;
54             }
55              
56             }
57              
58             sub debug {
59 0     0 1 0 my ( $self, @args ) = @_;
60 0 0       0 if ( @args ) {
61 0         0 my $val = $self->{debug} = shift @args;
62 0         0 my $ua = $self->ua();
63 0 0       0 my ( $method, @args ) = $val ?
64             ( add_handler => \&_dump ) :
65             ( 'remove_handler' );
66 0         0 $ua->$method( request_send => @args );
67 0         0 $ua->$method( response_done => @args );
68 0         0 return $self;
69             } else {
70 0         0 return $self->{debug};
71             }
72             }
73              
74             {
75              
76 2     2   13 use constant BASE_URL => 'http://geocoder.us/';
  2         3  
  2         85  
77 2     2   17 use constant DELAY => 15;
  2         4  
  2         1137  
78              
79             my $wait_for = time - DELAY;
80             ## my %valid_arg = map { $_ => 1 } qw{ location };
81              
82             sub geocode {
83 0     0 1 0 my ( $self, @args ) = @_;
84 0 0       0 my %parm = @args % 2 ? ( location => @args ) : @args;
85             defined $parm{location}
86 0 0       0 or croak "You must provide a location to geocode";
87              
88 0         0 my $uri = URI->new( BASE_URL );
89 0         0 $uri->path_segments( service => $self->{interface} );
90 0         0 $uri->query_form( address => $parm{location} );
91              
92             # $parm{location} = uri_escape( $parm{location} );
93              
94 0         0 my $now = time;
95 0         0 while ( $wait_for > $now ) {
96 0         0 sleep $wait_for - $now;
97 0         0 $now = time;
98             }
99 0         0 $wait_for = $now + DELAY;
100              
101             # my $rslt = $self->{response} = $self->{ua}->get(
102             # BASE_URL. 'service/' . $self->{interface} .
103             # '?address=' .
104             # $parm{location}
105             # );
106 0         0 my $rslt = $self->{response} = $self->{ua}->get( $uri );
107 0 0       0 $rslt->is_success()
108             or return;
109              
110 0         0 return $self->{_interface}->( $self, $rslt->content() );
111             }
112              
113             }
114              
115             =begin comment
116              
117             sub _geocode_csv {
118             my ( $self, $content ) = @_;
119             my $csv = $self->{_CSV} ||= Text::CSV->new( { binary => 1 } );
120             my @rtn;
121             foreach ( split qr{ \r \n? | \n }smx, $content ) {
122             $csv->parse( $_ )
123             or croak $csv->error_diag();
124             my %data;
125             # TODO field names consistent with Geo::Coder::Many.
126             @data{ qw< lat long address city state zip > } =
127             $csv->fields();
128             defined $data{long}
129             or %data = ( error => $data{lat} );
130             push @rtn, \%data;
131             }
132             return wantarray ? @rtn : $rtn[0];
133             }
134              
135             =end comment
136              
137             =cut
138              
139             sub _geocode_namedcsv {
140 0     0   0 my ( $self, $content ) = @_;
141 0   0     0 my $csv = $self->{_CSV} ||= Text::CSV->new( { binary => 1 } );
142 0         0 my @rtn;
143 0         0 foreach ( split qr{ \r \n? | \n }smx, $content ) {
144 0 0       0 $csv->parse( $_ )
145             or croak $csv->error_diag();
146 0         0 my %data;
147 0         0 foreach ( $csv->fields() ) {
148 0 0       0 s/ \A ( \w+ ) = //smx
149             or next;
150 0         0 $data{$1} = $_;
151             }
152 0         0 push @rtn, \%data;
153             }
154 0 0       0 return wantarray ? @rtn : $rtn[0];
155             }
156              
157             =begin comment
158              
159             sub _geocode_rest {
160             my ( $self, $content ) = @_;
161             my $rslt;
162             eval {
163             $rslt = $self->_get_xml_parser->parse( $content );
164             1;
165             } or return [ { error => $content } ];
166             _mung_tree( $rslt );
167             my @rtn = _extract_point( $rslt );
168             return wantarray ? @rtn : $rtn[0];
169             }
170              
171             sub _extract_point {
172             my ( $list ) = @_;
173             my @pts;
174             foreach my $tag ( @{ $list } ) {
175             'ARRAY' eq ref $tag
176             or next;
177             if ( $tag->[0] =~ m/ \b Point \z /smx ) {
178             my %pt;
179             foreach my $datum ( @{ $tag }[ 2 .. $#$tag ] ) {
180             my $name = $datum->[0];
181             $name =~ s/ [^:]* : //smx;
182             $pt{$name} = $datum->[2];
183             }
184             push @pts, \%pt;
185             } else {
186             push @pts, _extract_point( $tag );
187             }
188             }
189             return @pts;
190             }
191              
192             sub _mung_tree {
193             my ( $list ) = @_;
194             my @xfrm;
195             my $inx = 0;
196             while ( $inx <= $#$list ) {
197             my $tag = $list->[$inx++];
198             my $val = $list->[$inx++];
199             if ( 'ARRAY' eq ref $val ) {
200             my @info = @{ $val };
201             my $attr = shift @info;
202             _mung_tree( \@info );
203             splice @info, 0, 0, $tag, $attr;
204             push @xfrm, \@info;
205             } elsif ( ! ref $val && $val =~ m/ \S /smx ) {
206             $val =~ s/ \s+ / /smx;
207             $val =~ s/ \A \s+ //smx;
208             $val =~ s/ \s+ \z //smx;
209             if ( @xfrm && ! ref $xfrm[-1] ) {
210             $xfrm[-1] .= ' ' . $val;
211             } else {
212             push @xfrm, $val;
213             }
214             }
215             }
216             @{ $list } = @xfrm;
217             return $list;
218             }
219              
220             # $ curl 'http://geocoder.us/service/rest?address=1600+Pennsylvania+Ave,+Washington+DC'
221             #
222             #
223             # xmlns:dc="http://purl.org/dc/elements/1.1/"
224             # xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
225             # xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
226             # >
227             #
228             # 1600 Pennsylvania Ave NW, Washington DC 20502
229             # -77.037684
230             # 38.898748
231             #
232             #
233              
234             {
235              
236             my $xml_parser_loaded;
237             sub _get_xml_parser {
238             my ( $self ) = @_;
239             return ( $self->{_XML_PARSER} ||= do {
240             defined $xml_parser_loaded
241             or eval {
242             require XML::Parser;
243             $xml_parser_loaded = 0;
244             1;
245             }
246             or $xml_parser_loaded = $@;
247             $xml_parser_loaded
248             and croak 'Unable to load XML::Parser';
249              
250             XML::Parser->new( Style => 'Tree' );
251             } );
252             }
253              
254             }
255              
256             sub interface {
257             my ( $self, @args ) = @_;
258             if ( @args ) {
259             my $interface = shift @args;
260             my $code = $self->can( "_geocode_$interface" )
261             or croak "'interface' style '$interface' is not supported";
262             $self->{interface} = $interface;
263             $self->{_interface} = $code;
264             return $self;
265             } else {
266             return $self->{interface};
267             }
268             }
269              
270             =end comment
271              
272             =cut
273              
274             sub response {
275 0     0 1 0 my ( $self ) = @_;
276 0         0 return $self->{response};
277             }
278              
279             sub ua {
280 1     1 1 3 my ( $self, @args ) = @_;
281 1 50       3 if ( @args ) {
282 1         1 my $ua = shift @args;
283 1         2 local $@ = undef;
284 1 50       1 eval { $ua->isa( 'LWP::UserAgent' ); 1 }
  1         6  
  1         2  
285             or croak "'ua' must be an LWP::UserAgent object";
286 1         4 $self->{ua} = $ua;
287 1         2 return $self;
288             } else {
289 0           return $self->{ua};
290             }
291             }
292              
293             sub _dump {
294 0     0     my ( $msg ) = @_;
295 0           print STDERR "\n", ref $msg, "\n";
296 0           print STDERR $msg->dump();
297 0           return;
298             }
299              
300              
301             1;
302              
303             __END__