File Coverage

blib/lib/Geo/Coder/Bing.pm
Criterion Covered Total %
statement 40 99 40.4
branch 13 60 21.6
condition 4 11 36.3
subroutine 9 16 56.2
pod 5 5 100.0
total 71 191 37.1


line stmt bran cond sub pod time code
1             package Geo::Coder::Bing;
2              
3 2     2   42725 use strict;
  2         6  
  2         86  
4 2     2   11 use warnings;
  2         3  
  2         69  
5              
6 2     2   9 use Carp qw(carp croak);
  2         14  
  2         166  
7 2     2   1164 use Encode ();
  2         16796  
  2         46  
8 2     2   1340 use JSON;
  2         25602  
  2         12  
9 2     2   2034 use LWP::UserAgent;
  2         82030  
  2         60  
10 2     2   17 use URI;
  2         2  
  2         1755  
11              
12             our $VERSION = '0.13';
13             $VERSION = eval $VERSION;
14              
15             sub new {
16 4     4 1 1136 my ($class, @params) = @_;
17 4 100       19 my %params = (@params % 2) ? (key => @params) : @params;
18              
19 4 50       11 carp 'Provide a Bing Maps key to use the new REST API'
20             unless $params{key};
21              
22 4         10 my $self = bless \ %params, $class;
23              
24 4   33     61 $self->ua(
25             $params{ua} || LWP::UserAgent->new(agent => "$class/$VERSION")
26             );
27              
28 4 100       11 if ($self->{debug}) {
29 2     0   8 my $dump_sub = sub { $_[0]->dump(maxlength => 0); return };
  0         0  
  0         0  
30 2         3 $self->ua->set_my_handler(request_send => $dump_sub);
31 2         56 $self->ua->set_my_handler(response_done => $dump_sub);
32 2   50     51 $self->{compress} ||= 0;
33             }
34 4 100       13 if (exists $self->{compress} ? $self->{compress} : 1) {
    100          
35 2         3 $self->ua->default_header(accept_encoding => 'gzip,deflate');
36             }
37              
38 4 50 33     84 croak q('https' requires LWP::Protocol::https)
39             if $self->{https} and not $self->ua->is_protocol_supported('https');
40              
41 4         11 return $self;
42             }
43              
44 0     0 1 0 sub response { $_[0]->{response} }
45              
46             sub ua {
47 10     10 1 2564 my ($self, $ua) = @_;
48 10 100       17 if ($ua) {
49 4 50 33     27 croak q('ua' must be (or derived from) an LWP::UserAgent')
50             unless ref $ua and $ua->isa(q(LWP::UserAgent));
51 4         10 $self->{ua} = $ua;
52             }
53 10         18 return $self->{ua};
54             }
55              
56             sub geocode {
57 0 0   0 1   return $_[0]->{key} ? &_geocode_rest : &_geocode_ajax;
58             }
59              
60             sub _geocode_rest {
61 0     0     my ($self, @params) = @_;
62 0 0         my %params = (@params % 2) ? (location => @params) : @params;
63              
64 0 0         $params{query} = delete $params{location} or return;
65 0           $_ = Encode::encode('utf-8', $_) for values %params;
66              
67 0           my $uri = URI->new('http://dev.virtualearth.net/REST/v1/Locations');
68 0 0         $uri->scheme('https') if $self->{https};
69 0           $uri->query_form(
70             key => $self->{key},
71             %params,
72             );
73              
74 0           return $self->_rest_request($uri);
75             }
76              
77             # Support AJAX API for backwards compatibility.
78              
79             sub _geocode_ajax {
80 0     0     my ($self, @params) = @_;
81 0 0         my %params = (@params % 2) ? (location => @params) : @params;
82              
83 0 0         my $location = $params{location} or return;
84 0           $location = Encode::encode('utf-8', $location);
85              
86 0           my $uri = URI->new('http://dev.virtualearth.net/');
87 0 0         $uri->scheme('https') if $self->{https};
88 0           $uri->path_segments(qw(
89             services v1 geocodeservice geocodeservice.asmx Geocode
90             ));
91 0           $uri->query_form(
92             format => 'json',
93              
94             # Note: the quotes around the location parameter are required.
95             query => qq("$location"),
96              
97             # These are all required, even if empty.
98 0           map { $_ => '' } qw(
99             addressLine adminDistrict count countryRegion culture
100             curLocAccuracy currentLocation district entityTypes landmark
101             locality mapBounds postalCode postalTown rankBy
102             ),
103             );
104              
105 0           my $res = $self->{response} = $self->ua->get($uri);
106 0 0         return unless $res->is_success;
107              
108             # Change the content type of the response from 'application/json' so
109             # HTTP::Message will decode the character encoding.
110 0           $res->content_type('text/plain');
111              
112 0           my $content = $res->decoded_content;
113 0 0         return unless $content;
114              
115             # Workaround invalid data.
116 0           $content =~ s[ \}\.d $ ][}]x;
117              
118 0           my $data = eval { from_json($content) };
  0            
119 0 0         return unless $data;
120              
121 0 0         my @results = @{ $data->{d}{Results} || [] };
  0            
122 0 0         return wantarray ? @results : $results[0];
123             }
124              
125             sub reverse_geocode {
126 0     0 1   my ($self, @params) = @_;
127 0 0         my %params = (@params % 2) ? (latlng => @params) : @params;
128              
129 0           $_ = Encode::encode('utf-8', $_) for values %params;
130              
131             # Maintain api compatibility with other geocoders.
132 0           my ($lat, $lon);
133 0 0         if (my $latlon = delete $params{latlng}) {
134 0           ($lat, $lon) = split '\s*,\s*', $latlon;
135             }
136             else {
137 0           $lat = delete $params{lat};
138 0           ($lon) = grep defined, delete @params{qw(lon lng long)};
139             }
140 0 0         return unless 2 == grep defined, $lat, $lon;
141              
142 0           my $uri = URI->new("http://dev.virtualearth.net/REST/v1/Locations/$lat,$lon");
143 0 0         $uri->scheme('https') if $self->{https};
144 0           $uri->query_form(
145             key => $self->{key},
146             %params,
147             );
148              
149 0           return $self->_rest_request($uri);
150             }
151              
152             sub _rest_request {
153 0     0     my ($self, $uri) = @_;
154 0 0         return unless $uri;
155              
156 0           my $res = $self->{response} = $self->ua->get($uri);
157 0 0         return unless $res->is_success;
158              
159             # Change the content type of the response from 'application/json' so
160             # HTTP::Message will decode the character encoding.
161 0           $res->content_type('text/plain');
162              
163 0           my $content = $res->decoded_content;
164 0 0         return unless $content;
165              
166 0           my $data = eval { from_json($content) };
  0            
167 0 0         return unless $data;
168              
169 0 0         my @results = @{ $data->{resourceSets}[0]{resources} || [] };
  0            
170 0 0         return wantarray ? @results : $results[0];
171             }
172              
173              
174             1;
175              
176             __END__