File Coverage

blib/lib/Geo/Coder/OSM.pm
Criterion Covered Total %
statement 39 80 48.7
branch 9 42 21.4
condition 2 6 33.3
subroutine 9 14 64.2
pod 5 5 100.0
total 64 147 43.5


line stmt bran cond sub pod time code
1             package Geo::Coder::OSM;
2              
3 2     2   64403 use strict;
  2         7  
  2         100  
4 2     2   10 use warnings;
  2         4  
  2         70  
5              
6 2     2   11 use Carp qw(croak);
  2         28  
  2         163  
7 2     2   2727 use Encode ();
  2         32173  
  2         50  
8 2     2   2309 use JSON;
  2         46516  
  2         14  
9 2     2   3591 use LWP::UserAgent;
  2         146451  
  2         81  
10 2     2   25 use URI;
  2         4  
  2         2170  
11              
12             our $VERSION = '0.03';
13             $VERSION = eval $VERSION;
14              
15             our %SOURCES = (
16             osm => 'http://nominatim.openstreetmap.org',
17             mapquest => 'http://open.mapquestapi.com/nominatim/v1',
18             );
19              
20             sub new {
21 2     2 1 947 my ($class, @params) = @_;
22 2 50       10 my %params = (@params % 2) ? (key => @params) : @params;
23              
24 2         6 my $self = bless \ %params, $class;
25              
26 2   33     44 $self->ua(
27             $params{ua} || LWP::UserAgent->new(agent => "$class/$VERSION")
28             );
29              
30 2 50       9 if (exists $self->{sources}) {
31 0         0 my $sources = $self->{sources};
32 0 0       0 $self->{sources} = $sources = [$sources] unless ref $sources;
33 0         0 for my $source (@$sources) {
34 0 0       0 croak qq(unknown source '$source')
35             unless exists $SOURCES{$source};
36             }
37             }
38             else {
39 2         7 $self->{sources} = ['osm'];
40             }
41              
42 2         5 $self->{source_idx} = 0;
43              
44 2 50       11 if ($self->{debug}) {
    100          
    50          
45 1     0   5 my $dump_sub = sub { $_[0]->dump(maxlength => 0); return };
  0         0  
  0         0  
46 1         4 $self->ua->set_my_handler(request_send => $dump_sub);
47 1         107 $self->ua->set_my_handler(response_done => $dump_sub);
48             }
49             elsif (exists $self->{compress} ? $self->{compress} : 1) {
50 1         4 $self->ua->default_header(accept_encoding => 'gzip,deflate');
51             }
52              
53 2         102 return $self;
54             }
55              
56 0     0 1 0 sub response { $_[0]->{response} }
57              
58             sub ua {
59 5     5 1 3796 my ($self, $ua) = @_;
60 5 100       14 if ($ua) {
61 2 50 33     27 croak q('ua' must be (or derived from) an LWP::UserAgent')
62             unless ref $ua and $ua->isa(q(LWP::UserAgent));
63 2         8 $self->{ua} = $ua;
64             }
65 5         18 return $self->{ua};
66             }
67              
68             sub geocode {
69 0     0 1   my ($self, @params) = @_;
70 0 0         my %params = (@params % 2) ? (location => @params) : @params;
71              
72 0 0         my $location = delete $params{location} or return;
73 0           $location = Encode::encode('utf-8', $location);
74              
75             # Cycle throught the list of sources.
76 0           my $idx = ($self->{source_idx} %= @{ $self->{sources} })++;
  0            
77              
78 0           my $uri = URI->new($SOURCES{ $self->{sources}[$idx] } . '/search');
79 0           $uri->query_form(
80             q => $location,
81             format => 'json',
82             addressdetails => 1,
83             'accept-language' => 'en',
84             %params,
85             );
86              
87 0           return $self->_request($uri);
88             }
89              
90             sub reverse_geocode {
91 0     0 1   my ($self, @params) = @_;
92 0 0         my %params = (@params % 2) ? (latlng => @params) : @params;
93              
94             # Maintain api compatibility with other geocoders.
95 0           my ($lat, $lon);
96 0 0         if (my $latlon = delete $params{latlng}) {
97 0           ($lat, $lon) = split '\s*,\s*', $latlon;
98             }
99             else {
100 0           $lat = delete $params{lat};
101 0           ($lon) = grep defined, delete @params{qw(lon lng)};
102             }
103 0 0         return unless 2 == grep defined, $lat, $lon;
104              
105             # Cycle throught the list of sources.
106 0           my $idx = ($self->{source_idx} %= @{ $self->{sources} })++;
  0            
107              
108 0           my $uri = URI->new($SOURCES{ $self->{sources}[$idx] } . '/reverse');
109 0           $uri->query_form(
110             lat => $lat,
111             lon => $lon,
112             format => 'json',
113             addressdetails => 1,
114             'accept-language' => 'en',
115             %params,
116             );
117              
118 0           return $self->_request($uri);
119             }
120              
121             sub _request {
122 0     0     my ($self, $uri) = @_;
123              
124 0 0         return unless $uri;
125              
126 0           my $res = $self->{response} = $self->ua->get($uri);
127 0 0         return unless $res->is_success;
128              
129             # Change the content type of the response (if necessary) so
130             # HTTP::Message will decode the character encoding.
131 0 0         $res->content_type('text/plain')
132             unless $res->content_type =~ /^text/;
133              
134 0           my $content = $res->decoded_content;
135 0 0         return unless $content;
136              
137 0           my $data = eval { from_json($content) };
  0            
138 0 0         return unless $data;
139              
140 0 0         my @results = 'ARRAY' eq ref $data ? @$data : ($data);
141 0 0         return wantarray ? @results : $results[0];
142             }
143              
144              
145             1;
146              
147             __END__