File Coverage

blib/lib/Geo/Coder/XYZ.pm
Criterion Covered Total %
statement 33 85 38.8
branch 1 32 3.1
condition 2 11 18.1
subroutine 10 13 76.9
pod 4 4 100.0
total 50 145 34.4


line stmt bran cond sub pod time code
1             package Geo::Coder::XYZ;
2              
3 6     6   557633 use strict;
  6         59  
  6         157  
4 6     6   28 use warnings;
  6         11  
  6         181  
5              
6 6     6   29 use Carp;
  6         18  
  6         276  
7 6     6   2823 use Encode;
  6         48679  
  6         344  
8 6     6   2825 use JSON;
  6         46924  
  6         30  
9 6     6   2824 use HTTP::Request;
  6         99063  
  6         193  
10 6     6   3129 use LWP::UserAgent;
  6         121198  
  6         199  
11 6     6   2447 use LWP::Protocol::https;
  6         472494  
  6         263  
12 6     6   47 use URI;
  6         10  
  6         3581  
13              
14             =head1 NAME
15              
16             Geo::Coder::XYZ - Provides a Geo-Coding functionality using L
17              
18             =head1 VERSION
19              
20             Version 0.09
21              
22             =cut
23              
24             our $VERSION = '0.09';
25              
26             =head1 SYNOPSIS
27              
28             use Geo::Coder::XYZ;
29              
30             my $geo_coder = Geo::Coder::XYZ->new();
31             my $location = $geo_coder->geocode(location => '10 Downing St., London, UK');
32              
33             =head1 DESCRIPTION
34              
35             Geo::Coder::XYZ provides an interface to geocode.xyz, a free Geo-Coding database covering many countries.
36              
37             =head1 METHODS
38              
39             =head2 new
40              
41             $geo_coder = Geo::Coder::XYZ->new();
42             my $ua = LWP::UserAgent->new();
43             $ua->env_proxy(1);
44             $geo_coder = Geo::Coder::XYZ->new(ua => $ua);
45              
46             =cut
47              
48             sub new {
49 1     1 1 76 my($class, %param) = @_;
50              
51 1   33     13 my $ua = delete $param{ua} || LWP::UserAgent->new(agent => __PACKAGE__ . "/$VERSION");
52 1 50       2186 if(!defined($param{'host'})) {
53 1         5 $ua->ssl_opts(verify_hostname => 0); # Yuck
54             }
55 1   50     28 my $host = delete $param{host} || 'geocode.xyz';
56              
57 1         8 return bless { ua => $ua, host => $host }, $class;
58             }
59              
60             =head2 geocode
61              
62             $location = $geo_coder->geocode(location => $location);
63              
64             print 'Latitude: ', $location->{'latt'}, "\n";
65             print 'Longitude: ', $location->{'longt'}, "\n";
66              
67             @locations = $geo_coder->geocode('Portland, USA');
68             diag 'There are Portlands in ', join (', ', map { $_->{'state'} } @locations);
69              
70             =cut
71              
72             sub geocode {
73 0     0 1   my $self = shift;
74 0           my %param;
75              
76 0 0         if(ref($_[0]) eq 'HASH') {
    0          
    0          
77 0           %param = %{$_[0]};
  0            
78             } elsif(ref($_[0])) {
79 0           Carp::croak('Usage: geocode(location => $location)');
80             } elsif(@_ % 2 == 0) {
81 0           %param = @_;
82             } else {
83 0           $param{location} = shift;
84             }
85              
86             my $location = $param{location}
87 0 0         or Carp::croak('Usage: geocode(location => $location)');
88              
89 0 0         if (Encode::is_utf8($location)) {
90 0           $location = Encode::encode_utf8($location);
91             }
92              
93 0           my $uri = URI->new("https://$self->{host}/");
94 0 0         if($location =~ /(.+),\s*England$/i) {
95 0           $location = "$1, United Kingdom"; # geocode.xyz gets confused between England and New England
96             }
97 0           $location =~ s/\s/+/g;
98 0           my %query_parameters = ('locate' => $location, 'json' => 1);
99 0 0         if(wantarray) {
100             # moreinfo is needed to find alternatives when the given location is ambiguous
101 0           $query_parameters{'moreinfo'} = 1;
102             }
103 0           $uri->query_form(%query_parameters);
104 0           my $url = $uri->as_string();
105              
106 0           my $res = $self->{ua}->get($url);
107              
108 0 0         if ($res->is_error) {
109 0           Carp::carp("API returned error: on $url ", $res->status_line());
110 0           return { };
111             }
112              
113 0           my $json = JSON->new()->utf8();
114 0           my $rc;
115 0           eval {
116 0           $rc = $json->decode($res->content());
117             };
118 0 0         if(!defined($rc)) {
119 0 0         if($@) {
120 0           Carp::carp("$url: $@");
121 0           return { };
122             }
123 0           Carp::carp("$url: can't decode the JSON ", $res->content());
124 0           return { };
125             }
126              
127 0 0 0       if($rc->{'otherlocations'} && $rc->{'otherlocations'}->{'loc'} &&
      0        
128             (ref($rc->{'otherlocations'}->{'loc'}) eq 'ARRAY')) {
129 0           my @rc = @{$rc->{'otherlocations'}->{'loc'}};
  0            
130 0 0         if(wantarray) {
131 0           return @rc;
132             }
133 0           return $rc[0];
134             }
135 0           return $rc;
136              
137             # my @results = @{ $data || [] };
138             # wantarray ? @results : $results[0];
139             }
140              
141             =head2 ua
142              
143             Accessor method to get and set UserAgent object used internally. You
144             can call I for example, to get the proxy information from
145             environment variables:
146              
147             $geo_coder->ua()->env_proxy(1);
148              
149             You can also set your own User-Agent object:
150              
151             use LWP::UserAgent::Throttled;
152             $geo_coder->ua(LWP::UserAgent::Throttled->new());
153              
154             =cut
155              
156             sub ua {
157 0     0 1   my $self = shift;
158 0 0         if (@_) {
159 0           $self->{ua} = shift;
160             }
161 0           $self->{ua};
162             }
163              
164             =head2 reverse_geocode
165              
166             $location = $geo_coder->reverse_geocode(latlng => '37.778907,-122.39732');
167              
168             Similar to geocode except it expects a latitude/longitude parameter.
169              
170             =cut
171              
172             sub reverse_geocode {
173 0     0 1   my $self = shift;
174              
175 0           my %param;
176 0 0         if (@_ % 2 == 0) {
177 0           %param = @_;
178             } else {
179 0           $param{latlng} = shift;
180             }
181              
182             my $latlng = $param{latlng}
183 0 0         or Carp::carp('Usage: reverse_geocode(latlng => $latlng)');
184              
185 0           return $self->geocode(location => $latlng, reverse => 1);
186             }
187              
188             =head1 AUTHOR
189              
190             Nigel Horne
191              
192             Based on L.
193              
194             This library is free software; you can redistribute it and/or modify
195             it under the same terms as Perl itself.
196              
197             Lots of thanks to the folks at geocode.xyz.
198              
199             =head1 SEE ALSO
200              
201             L, L
202              
203             =head1 LICENSE AND COPYRIGHT
204              
205             Copyright 2017-2018 Nigel Horne.
206              
207             This program is released under the following licence: GPL2
208              
209             =cut
210              
211             1;