File Coverage

blib/lib/Geo/Coder/CA.pm
Criterion Covered Total %
statement 41 80 51.2
branch 10 32 31.2
condition 3 8 37.5
subroutine 9 12 75.0
pod 5 5 100.0
total 68 137 49.6


line stmt bran cond sub pod time code
1             package Geo::Coder::CA;
2              
3             # See also https://geocoding.geo.census.gov/geocoder/Geocoding_Services_API.html for the US for the future
4              
5 5     5   792876 use strict;
  5         55  
  5         142  
6 5     5   36 use warnings;
  5         9  
  5         123  
7              
8 5     5   25 use Carp;
  5         10  
  5         252  
9 5     5   2875 use Encode;
  5         75061  
  5         407  
10 5     5   2460 use JSON::MaybeXS;
  5         28829  
  5         298  
11 5     5   3571 use LWP::UserAgent;
  5         254877  
  5         179  
12 5     5   57 use URI;
  5         13  
  5         3918  
13              
14             =head1 NAME
15              
16             Geo::Coder::CA - Provides a Geo-Coding functionality using L for both Canada and the US.
17              
18             =head1 VERSION
19              
20             Version 0.14
21              
22             =cut
23              
24             our $VERSION = '0.14';
25              
26             =head1 SYNOPSIS
27              
28             use Geo::Coder::CA;
29              
30             my $geo_coder = Geo::Coder::CA->new();
31             my $location = $geo_coder->geocode(location => '9235 Main St, Richibucto, New Brunswick, Canada');
32              
33             =head1 DESCRIPTION
34              
35             Geo::Coder::CA provides an interface to geocoder.ca.
36             L no longer seems to work.
37              
38             =head1 METHODS
39              
40             =head2 new
41              
42             $geo_coder = Geo::Coder::CA->new();
43             my $ua = LWP::UserAgent->new();
44             $ua->env_proxy(1);
45             $geo_coder = Geo::Coder::CA->new(ua => $ua);
46              
47             =cut
48              
49             sub new {
50 3     3 1 2940 my($class, %args) = @_;
51              
52 3 100       16 if(!defined($class)) {
    50          
53             # Geo::Coder::CA::new() used rather than Geo::Coder::CA->new()
54 1         3 $class = __PACKAGE__;
55             } elsif(ref($class)) {
56             # clone the given object
57 0         0 return bless { %{$class}, %args }, ref($class);
  0         0  
58             }
59              
60 3         9 my $ua = $args{ua};
61 3 50       9 if(!defined($ua)) {
62 3         24 $ua = LWP::UserAgent->new(agent => __PACKAGE__ . "/$VERSION");
63 3         6205 $ua->default_header(accept_encoding => 'gzip,deflate');
64             }
65 3   50     235 my $host = $args{host} || 'geocoder.ca';
66              
67 3         24 return bless { ua => $ua, host => $host }, $class;
68             }
69              
70             =head2 geocode
71              
72             $location = $geo_coder->geocode(location => $location);
73             # @location = $geo_coder->geocode(location => $location);
74              
75             print 'Latitude: ', $location->{'latt'}, "\n";
76             print 'Longitude: ', $location->{'longt'}, "\n";
77              
78             =cut
79              
80             sub geocode {
81 6     6 1 1682 my $self = shift;
82 6         10 my %param;
83              
84 6 100       37 if(ref($_[0]) eq 'HASH') {
    50          
    100          
85 2         3 %param = %{$_[0]};
  2         7  
86             } elsif(ref($_[0])) {
87 0         0 Carp::croak('Usage: geocode(location => $location)');
88 0         0 return;
89             } elsif(@_ % 2 == 0) {
90 3         9 %param = @_;
91             } else {
92 1         3 $param{location} = shift;
93             }
94              
95 6         12 my $location = $param{location};
96 6 50 66     30 if((!defined($location)) || (length($location) == 0)) {
97 6         14 Carp::croak('Usage: geocode(location => $location)');
98 5         1751 return;
99             }
100              
101 0 0         if (Encode::is_utf8($location)) {
102 0           $location = Encode::encode_utf8($location);
103             }
104              
105 0           my $uri = URI->new("https://$self->{host}/some_location");
106 0           $location =~ s/\s/+/g;
107 0           my %query_parameters = ('locate' => $location, 'json' => 1, 'strictmode' => 1);
108 0           $uri->query_form(%query_parameters);
109 0           my $url = $uri->as_string();
110              
111 0           my $res = $self->{ua}->get($url);
112              
113 0 0         if($res->is_error()) {
114 0           Carp::carp("$url API returned error: ", $res->status_line());
115 0           return;
116             }
117             # $res->content_type('text/plain'); # May be needed to decode correctly
118              
119 0           my $json = JSON::MaybeXS->new()->utf8();
120 0 0         if(my $rc = $json->decode($res->decoded_content())) {
121 0 0         if($rc->{'error'}) {
122             # Sorry - you lose the error code, but HTML::GoogleMaps::V3 relies on this
123             # TODO - send patch to the H:G:V3 author
124 0           return;
125             }
126 0 0 0       if(defined($rc->{'latt'}) && defined($rc->{'longt'})) {
127 0           return $rc; # No support for list context, yet
128             }
129              
130             # if($location =~ /^(\w+),\+*(\w+),\+*(USA|US|United States)$/i) {
131             # $query_parameters{'locate'} = "$1 County, $2, $3";
132             # $uri->query_form(%query_parameters);
133             # $url = $uri->as_string();
134             #
135             # $res = $self->{ua}->get($url);
136             #
137             # if($res->is_error()) {
138             # Carp::croak("geocoder.ca API returned error: " . $res->status_line());
139             # return;
140             # }
141             # return $json->decode($res->content());
142             # }
143             }
144              
145             # my @results = @{ $data || [] };
146             # wantarray ? @results : $results[0];
147             }
148              
149             =head2 ua
150              
151             Accessor method to get and set UserAgent object used internally. You
152             can call I for example, to get the proxy information from
153             environment variables:
154              
155             $geo_coder->ua()->env_proxy(1);
156              
157             You can also set your own User-Agent object:
158              
159             my $ua = LWP::UserAgent::Throttled->new();
160             $ua->throttle('geocoder.ca' => 1);
161             $geo_coder->ua($ua);
162              
163             =cut
164              
165             sub ua {
166 0     0 1   my $self = shift;
167 0 0         if (@_) {
168 0           $self->{ua} = shift;
169             }
170 0           $self->{ua};
171             }
172              
173             =head2 reverse_geocode
174              
175             $location = $geo_coder->reverse_geocode(latlng => '37.778907,-122.39732');
176              
177             Similar to geocode except it expects a latitude/longitude parameter.
178              
179             =cut
180              
181             sub reverse_geocode {
182 0     0 1   my $self = shift;
183              
184 0           my %param;
185 0 0         if (@_ % 2 == 0) {
186 0           %param = @_;
187             } else {
188 0           $param{latlng} = shift;
189             }
190              
191             my $latlng = $param{latlng}
192 0 0         or Carp::croak('Usage: reverse_geocode(latlng => $latlng)');
193              
194 0           return $self->geocode(location => $latlng, reverse => 1);
195             }
196              
197             =head2 run
198              
199             You can also run this module from the command line:
200              
201             perl CA.pm 1600 Pennsylvania Avenue NW, Washington DC
202              
203             =cut
204              
205             __PACKAGE__->run(@ARGV) unless caller();
206              
207             sub run {
208 0     0 1   require Data::Dumper;
209              
210 0           my $class = shift;
211              
212 0           my $location = join(' ', @_);
213              
214 0           my @rc = $class->new()->geocode($location);
215              
216 0 0         if(scalar(@rc)) {
217 0           print Data::Dumper->new([\@rc])->Dump();
218             } else {
219 0           die "$0: geo-coding failed";
220             }
221             }
222              
223             =head1 AUTHOR
224              
225             Nigel Horne, C<< >>
226              
227             Based on L.
228              
229             This library is free software; you can redistribute it and/or modify
230             it under the same terms as Perl itself.
231              
232             Lots of thanks to the folks at geocoder.ca.
233              
234             =head1 BUGS
235              
236             Should be called Geo::Coder::NA for North America.
237              
238             =head1 SEE ALSO
239              
240             L, L
241              
242             =head1 LICENSE AND COPYRIGHT
243              
244             Copyright 2017-2023 Nigel Horne.
245              
246             This program is released under the following licence: GPL2
247              
248             =cut
249              
250             1;