File Coverage

blib/lib/Geo/Coder/Mapbox.pm
Criterion Covered Total %
statement 36 86 41.8
branch 5 32 15.6
condition 1 9 11.1
subroutine 10 13 76.9
pod 4 4 100.0
total 56 144 38.8


line stmt bran cond sub pod time code
1             package Geo::Coder::Mapbox;
2              
3 3     3   668951 use strict;
  3         7  
  3         125  
4 3     3   16 use warnings;
  3         6  
  3         149  
5              
6 3     3   16 use Carp;
  3         6  
  3         233  
7 3     3   2129 use Encode;
  3         64824  
  3         433  
8 3     3   1778 use JSON::MaybeXS;
  3         43616  
  3         265  
9 3     3   1875 use HTTP::Request;
  3         85343  
  3         164  
10 3     3   2662 use LWP::UserAgent;
  3         137359  
  3         162  
11 3     3   1903 use LWP::Protocol::https;
  3         490641  
  3         203  
12 3     3   27 use URI;
  3         6  
  3         2964  
13              
14             =head1 NAME
15              
16             Geo::Coder::Mapbox - Provides a Geo-Coding functionality using L
17              
18             =head1 VERSION
19              
20             Version 0.02
21              
22             =cut
23              
24             our $VERSION = '0.02';
25              
26             =head1 SYNOPSIS
27              
28             use Geo::Coder::Mapbox;
29              
30             my $geo_coder = Geo::Coder::Mapbox->new(access_token => $ENV{'MAPBOX_KEY'});
31             my $location = $geo_coder->geocode(location => 'Washington, DC');
32              
33             =head1 DESCRIPTION
34              
35             Geo::Coder::Mapbox provides an interface to mapbox.com, a Geo-Coding database covering many countries.
36              
37             =head1 METHODS
38              
39             =head2 new
40              
41             $geo_coder = Geo::Coder::Mapbox->new();
42             my $ua = LWP::UserAgent->new();
43             $ua->env_proxy(1);
44             $geo_coder = Geo::Coder::Mapbox->new(ua => $ua);
45              
46             =cut
47              
48             sub new {
49 4     4 1 293669 my $class = shift;
50 4 50       21 my %args = (ref($_[0]) eq 'HASH') ? %{$_[0]} : @_;
  0         0  
51              
52             # Use Geo::Coder::Mapbox->new(), not Geo::Coder::Mapbox::new()
53 4 100       51 if(!defined($class)) {
    100          
54             # carp(__PACKAGE__, ' use ->new() not ::new() to instantiate');
55             # return;
56              
57             # FIXME: this only works when no arguments are given
58 1         4 $class = __PACKAGE__;
59             } elsif(ref($class)) {
60             # clone the given object
61 1         3 return bless { %{$class}, %args }, ref($class);
  1         13  
62             }
63              
64 3   33     42 my $ua = $args{ua} || LWP::UserAgent->new(agent => __PACKAGE__ . "/$VERSION");
65             # if(!defined($args{'host'})) {
66             # $ua->ssl_opts(verify_hostname => 0); # Yuck
67             # }
68 3         5406 my %defaults = (
69             host => 'api.mapbox.com',
70             access_token => ''
71             );
72              
73             # Re-seen keys take precedence, so defaults come first
74 3         36 return bless { %defaults, %args, ua => $ua }, $class;
75             }
76              
77             =head2 geocode
78              
79             $location = $geo_coder->geocode(location => 'Toronto, Ontario, Canada');
80              
81             print 'Latitude: ', $location->{features}[0]->{center}[1], "\n"; # Latitude
82             print 'Longitude: ', $location->{features}[0]->{center}[0], "\n"; # Longitude
83              
84             @locations = $geo_coder->geocode('Portland, USA');
85             print 'There are Portlands in ', join (', ', map { $_->{'state'} } @locations), "\n";
86              
87             =cut
88              
89             sub geocode {
90 0     0 1   my $self = shift;
91 0           my %param;
92              
93 0 0         if(ref($_[0]) eq 'HASH') {
    0          
    0          
94 0           %param = %{$_[0]};
  0            
95             } elsif(ref($_[0])) {
96 0           Carp::croak('Usage: geocode(location => $location)');
97 0           return; # Not sure why this is needed, but t/carp.t fails without it
98             } elsif(@_ % 2 == 0) {
99 0           %param = @_;
100             } else {
101 0           $param{location} = shift;
102             }
103              
104             my $location = $param{location}
105 0 0         or Carp::croak('Usage: geocode(location => $location)');
106              
107 0 0         if (Encode::is_utf8($location)) {
108 0           $location = Encode::encode_utf8($location);
109             }
110              
111 0           my $uri = URI->new("https://$self->{host}/geocoding/v5/mapbox.places/$location.json");
112 0           $location =~ s/\s/+/g;
113 0           my %query_parameters = ('access_token' => $self->{'access_token'});
114 0           $uri->query_form(%query_parameters);
115 0           my $url = $uri->as_string();
116              
117             # ::diag($url);
118              
119 0           my $res = $self->{ua}->get($url);
120              
121 0 0         if ($res->is_error) {
122 0           Carp::carp("API returned error: on $url ", $res->status_line());
123 0           return { };
124             }
125              
126 0           my $json = JSON::MaybeXS->new()->utf8();
127 0           my $rc;
128 0           eval {
129 0           $rc = $json->decode($res->content());
130             };
131 0 0         if(!defined($rc)) {
132 0 0         if($@) {
133 0           Carp::carp("$url: $@");
134 0           return { };
135             }
136 0           Carp::carp("$url: can't decode the JSON ", $res->content());
137 0           return { };
138             }
139              
140 0 0 0       if($rc->{'otherlocations'} && $rc->{'otherlocations'}->{'loc'} &&
      0        
141             (ref($rc->{'otherlocations'}->{'loc'}) eq 'ARRAY')) {
142 0           my @rc = @{$rc->{'otherlocations'}->{'loc'}};
  0            
143 0 0         if(wantarray) {
144 0           return @rc;
145             }
146 0           return $rc[0];
147             }
148 0           return $rc;
149              
150             # my @results = @{ $data || [] };
151             # wantarray ? @results : $results[0];
152             }
153              
154             =head2 ua
155              
156             Accessor method to get and set UserAgent object used internally. You
157             can call I for example, to get the proxy information from
158             environment variables:
159              
160             $geo_coder->ua()->env_proxy(1);
161              
162             You can also set your own User-Agent object:
163              
164             use LWP::UserAgent::Throttled;
165             my $ua = LWP::UserAgent::Throttled->new();
166             $ua->throttle({ 'api.mapbox.com' => 2 });
167             $geo_coder->ua($ua);
168              
169             =cut
170              
171             sub ua {
172 0     0 1   my $self = shift;
173 0 0         if (@_) {
174 0           $self->{ua} = shift;
175             }
176 0           $self->{ua};
177             }
178              
179             =head2 reverse_geocode
180              
181             $location = $geo_coder->reverse_geocode(lnglat => '-122.39732,37.778907');
182              
183             Similar to geocode except it expects a longitude/latitude (note the order) parameter.
184              
185             =cut
186              
187             sub reverse_geocode {
188 0     0 1   my $self = shift;
189              
190 0           my %param;
191 0 0         if (@_ % 2 == 0) {
192 0           %param = @_;
193             } else {
194 0           $param{lnglat} = shift;
195             }
196              
197             my $lnglat = $param{lnglat}
198 0 0         or Carp::carp('Usage: reverse_geocode(location => $lnglat)');
199              
200             # return $self->geocode(location => $lnglat, reverse => 1);
201 0           return $self->geocode(location => $lnglat);
202             }
203              
204             =head1 AUTHOR
205              
206             Nigel Horne, C<< >>
207              
208             Based on L.
209              
210             This library is free software; you can redistribute it and/or modify
211             it under the same terms as Perl itself.
212              
213             Lots of thanks to the folks at mapbox.com.
214              
215             =head1 SEE ALSO
216              
217             L, L, L
218              
219             =head1 LICENSE AND COPYRIGHT
220              
221             Copyright 2021-2024 Nigel Horne.
222              
223             This program is released under the following licence: GPL2
224              
225             =cut
226              
227             1;