File Coverage

blib/lib/Geo/Coder/Mapbox.pm
Criterion Covered Total %
statement 36 86 41.8
branch 3 30 10.0
condition 3 12 25.0
subroutine 10 13 76.9
pod 4 4 100.0
total 56 145 38.6


line stmt bran cond sub pod time code
1             package Geo::Coder::Mapbox;
2              
3 3     3   521875 use strict;
  3         25  
  3         86  
4 3     3   13 use warnings;
  3         12  
  3         82  
5              
6 3     3   13 use Carp;
  3         4  
  3         150  
7 3     3   1385 use Encode;
  3         60089  
  3         284  
8 3     3   3506 use JSON;
  3         37861  
  3         21  
9 3     3   1763 use HTTP::Request;
  3         68145  
  3         110  
10 3     3   2011 use LWP::UserAgent;
  3         84748  
  3         126  
11 3     3   1521 use LWP::Protocol::https;
  3         430279  
  3         145  
12 3     3   23 use URI;
  3         7  
  3         2011  
13              
14             =head1 NAME
15              
16             Geo::Coder::Mapbox - Provides a Geo-Coding functionality using L
17              
18             =head1 VERSION
19              
20             Version 0.01
21              
22             =cut
23              
24             our $VERSION = '0.01';
25              
26             =head1 SYNOPSIS
27              
28             use Geo::Coder::Mapbox;
29              
30             my $geo_coder = Geo::Coder::Mapbox->new(access_token => $ENV{'MAPBOX'});
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 2     2 1 103 my $proto = shift;
50 2   66     14 my $class = ref($proto) || $proto;
51              
52             # Use Geo::Coder::Mapbox->new(), not Geo::Coder::Mapbox::new()
53 2 100       5 if(!defined($class)) {
54 1         32 carp(__PACKAGE__, ' use ->new() not ::new() to instantiate');
55 1         216 return;
56             }
57              
58 1 50       5 my %args = (ref($_[0]) eq 'HASH') ? %{$_[0]} : @_;
  0         0  
59              
60 1   33     15 my $ua = delete $args{ua} || LWP::UserAgent->new(agent => __PACKAGE__ . "/$VERSION");
61             # if(!defined($args{'host'})) {
62             # $ua->ssl_opts(verify_hostname => 0); # Yuck
63             # }
64 1         3257 my %defaults = (
65             host => 'api.mapbox.com',
66             access_token => ''
67             );
68              
69 1         16 return bless { %defaults, %args, ua => $ua }, $class;
70             }
71              
72             =head2 geocode
73              
74             $location = $geo_coder->geocode(location => 'Toronto, Ontario, Canada');
75              
76             print 'Latitude: ', $location->{features}[0]->{center}[1], "\n"; # Latitude
77             print 'Longitude: ', $location->{features}[0]->{center}[0], "\n"; # Longitude
78              
79             @locations = $geo_coder->geocode('Portland, USA');
80             print 'There are Portlands in ', join (', ', map { $_->{'state'} } @locations), "\n";
81              
82             =cut
83              
84             sub geocode {
85 0     0 1   my $self = shift;
86 0           my %param;
87              
88 0 0         if(ref($_[0]) eq 'HASH') {
    0          
    0          
89 0           %param = %{$_[0]};
  0            
90             } elsif(ref($_[0])) {
91 0           Carp::croak('Usage: geocode(location => $location)');
92 0           return; # Not sure why this is needed, but t/carp.t fails without it
93             } elsif(@_ % 2 == 0) {
94 0           %param = @_;
95             } else {
96 0           $param{location} = shift;
97             }
98              
99             my $location = $param{location}
100 0 0         or Carp::croak('Usage: geocode(location => $location)');
101              
102 0 0         if (Encode::is_utf8($location)) {
103 0           $location = Encode::encode_utf8($location);
104             }
105              
106 0           my $uri = URI->new("https://$self->{host}/geocoding/v5/mapbox.places/$location.json");
107 0           $location =~ s/\s/+/g;
108 0           my %query_parameters = ('access_token' => $self->{'access_token'});
109 0           $uri->query_form(%query_parameters);
110 0           my $url = $uri->as_string();
111              
112             # ::diag($url);
113              
114 0           my $res = $self->{ua}->get($url);
115              
116 0 0         if ($res->is_error) {
117 0           Carp::carp("API returned error: on $url ", $res->status_line());
118 0           return { };
119             }
120              
121 0           my $json = JSON->new()->utf8();
122 0           my $rc;
123 0           eval {
124 0           $rc = $json->decode($res->content());
125             };
126 0 0         if(!defined($rc)) {
127 0 0         if($@) {
128 0           Carp::carp("$url: $@");
129 0           return { };
130             }
131 0           Carp::carp("$url: can't decode the JSON ", $res->content());
132 0           return { };
133             }
134              
135 0 0 0       if($rc->{'otherlocations'} && $rc->{'otherlocations'}->{'loc'} &&
      0        
136             (ref($rc->{'otherlocations'}->{'loc'}) eq 'ARRAY')) {
137 0           my @rc = @{$rc->{'otherlocations'}->{'loc'}};
  0            
138 0 0         if(wantarray) {
139 0           return @rc;
140             }
141 0           return $rc[0];
142             }
143 0           return $rc;
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             use LWP::UserAgent::Throttled;
160             my $ua = LWP::UserAgent::Throttled->new();
161             $ua->throttle({ 'mapbox.com' => 2 });
162             $geo_coder->ua($ua);
163              
164             =cut
165              
166             sub ua {
167 0     0 1   my $self = shift;
168 0 0         if (@_) {
169 0           $self->{ua} = shift;
170             }
171 0           $self->{ua};
172             }
173              
174             =head2 reverse_geocode
175              
176             $location = $geo_coder->reverse_geocode(lnglat => '-122.39732,37.778907');
177              
178             Similar to geocode except it expects a longitude/latitude (note the order) parameter.
179              
180             =cut
181              
182             sub reverse_geocode {
183 0     0 1   my $self = shift;
184              
185 0           my %param;
186 0 0         if (@_ % 2 == 0) {
187 0           %param = @_;
188             } else {
189 0           $param{lnglat} = shift;
190             }
191              
192             my $lnglat = $param{lnglat}
193 0 0         or Carp::carp('Usage: reverse_geocode(location => $lnglat)');
194              
195             # return $self->geocode(location => $lnglat, reverse => 1);
196 0           return $self->geocode(location => $lnglat);
197             }
198              
199             =head1 AUTHOR
200              
201             Nigel Horne, C<< >>
202              
203             Based on L.
204              
205             This library is free software; you can redistribute it and/or modify
206             it under the same terms as Perl itself.
207              
208             Lots of thanks to the folks at mapbox.com.
209              
210             =head1 SEE ALSO
211              
212             L, L, L
213              
214             =head1 LICENSE AND COPYRIGHT
215              
216             Copyright 2021 Nigel Horne.
217              
218             This program is released under the following licence: GPL2
219              
220             =cut
221              
222             1;