File Coverage

blib/lib/Geo/Coder/GoogleMaps.pm
Criterion Covered Total %
statement 25 27 92.5
branch n/a
condition n/a
subroutine 9 9 100.0
pod n/a
total 34 36 94.4


line stmt bran cond sub pod time code
1             package Geo::Coder::GoogleMaps;
2              
3 2     2   53582 use warnings;
  2         7  
  2         67  
4 2     2   11 use strict;
  2         4  
  2         65  
5 2     2   10 use Carp;
  2         9  
  2         141  
6 2     2   2053 use Encode;
  2         24727  
  2         202  
7 2     2   1553 use JSON::Syck;
  2         6898  
  2         85  
8 2     2   1444 use HTTP::Request;
  2         53828  
  2         77  
9 2     2   2651 use LWP::UserAgent;
  2         49106  
  2         65  
10 2     2   22 use URI;
  2         5  
  2         47  
11 2     2   954 use XML::LibXML ;
  0            
  0            
12             use Geo::Coder::GoogleMaps::Location;
13             use Geo::Coder::GoogleMaps::Response;
14              
15             =encoding utf-8
16              
17             =head1 NAME
18              
19             Geo::Coder::GoogleMaps - Google Maps Geocoding API
20              
21             =head1 VERSION
22              
23             Version 0.4
24              
25             =cut
26              
27             our $VERSION = '0.4';
28              
29             =head1 SYNOPSIS
30              
31             WARNING WARNING WARNING
32              
33             There is a huge API change between version 0.2 and 0.3 ! Please see the documentation of the geocode() method !
34              
35             WARNING WARNING WARNING
36              
37             This module provide Google Maps API. Please note that this module use Tatsuhiko Miyagawa's work on L as base (L).
38              
39             In fact it's a fork of Mr Miyagawa's module. Geo::Coder::GoogleMaps use the default JSON data type as default output but also support XML/KML.
40              
41             The direct output of the geocode() method is no longer a L but a Geo::Coder::GoogleMaps::Response. This one contains a list of L objects which can be, individually, exported to any of the supported format.
42              
43              
44             use Geo::Coder::GoogleMaps;
45            
46             my $gmap = Geo::Coder::GoogleMaps->new( apikey => 'abcd' , output => 'xml');
47             my $response = $gmap->geocode(location => '88 rue du chateau, 92600, Asnières sur seine, France');
48             if( $response->is_success() ){
49             my $location = $response->placemarks()->[0];
50             print $location->latitude,',',$location->longitude,"\n";
51             $location->toKML()->toString(); # is absolutly equivalent to $location->toKML(1);
52             }
53              
54             =head1 FUNCTIONS
55              
56             =head2 new
57              
58             The object constructor it takes the following parameters :
59              
60             apikey : your Google API key (only parameter mandatory).
61             ua : a LWP::UserAgent object. If not provided a new user agent is instanciates.
62             host : the google map service url (default is: maps.google.com)
63             output : the output method between xml, kml and json (csv support plan for futur release). Default is json.
64              
65             Example:
66              
67             my $gmap = Geo::Coder::GoogleMaps->new( apikey => 'abcdef', host => 'maps.google.fr', output => 'xml');
68              
69             =cut
70              
71              
72             sub new {
73             my($class, %param) = @_;
74            
75             my $key = delete $param{apikey}
76             or Carp::croak("Usage: new(apikey => \$apikey)");
77            
78             my $ua = delete $param{ua} || LWP::UserAgent->new(agent => "Mozilla/5.0 (compatible;Geo::Coder::GoogleMaps/$Geo::Coder::GoogleMaps::VERSION");
79             my $host = delete $param{host} || 'maps.google.com';
80             my $output = delete $param{output} || 'json';
81            
82             bless { key => $key, ua => $ua, host => $host, output => $output }, $class;
83             }
84              
85             =head2 geocode
86              
87             WARNING WARNING WARNING
88              
89             There is a huge API change between version 0.2 and 0.3 ! This method do not returns placemarks directly anymore !!
90              
91             WARNING WARNING WARNING
92              
93             Get a location from the Google Maps API. It return a L object.
94              
95             my $response = $gmap->geocode(location => '88 rue du chateau, 92600, Asnières sur seine, France');
96             print $response->placemarks()->[0]->Serialize(1) if( $response->is_success() ) ;
97              
98             Please note that for the moment the geocode methode rely on JSON::Syck to parse the Google's output and ask for result in JSON format.
99              
100             In futur release the 'output' from the constructor will mainly be used to define the way you want this module get the data.
101              
102             The dependency to L and L will be removed to be optionnal and dynamically load.
103              
104             =cut
105              
106             sub geocode {
107             my $self = shift;
108            
109             my %param;
110             if (@_ % 2 == 0) {
111             %param = @_;
112             } else {
113             $param{location} = shift;
114             }
115            
116             my $location = $param{location}
117             or Carp::croak("Usage: geocode(location => \$location)");
118            
119             if (Encode::is_utf8($location)) {
120             $location = Encode::encode_utf8($location);
121             }
122            
123             my $uri = URI->new("http://$self->{host}/maps/geo");
124             $uri->query_form(q => $location, key => $self->{key},sensor => "false", output => "json");
125             # $uri->query_form(q => $location, output => $self->{output}, key => $self->{key});
126            
127             my $res = $self->{ua}->get($uri);
128            
129             if ($res->is_error) {
130             Carp::croak("Google Maps API returned error: " . $res->status_line);
131             }
132            
133             # Ugh, Google Maps returns so stupid HTTP header
134             # Content-Type: text/javascript; charset=UTF-8; charset=Shift_JIS
135             my @ctype = $res->content_type;
136             my $charset = ($ctype[1] =~ /charset=([\w\-]+)$/)[0] || "utf-8";
137            
138             my $content = Encode::decode($charset, $res->content);
139             local $JSON::Syck::ImplicitUnicode = 1;
140             my $data = JSON::Syck::Load($content);
141            
142             # print "[Debug] JSON::Syck::Load()-ed data:\n",Data::Dumper::Dumper( $data ),"\n";
143            
144             my $response = Geo::Coder::GoogleMaps::Response->new(status_code => $data->{Status}->{code}, status_request => $data->{Status}->{request} );
145             my @placemark=();
146             foreach my $Placemark (@{$data->{Placemark}}){
147             my $loc = Geo::Coder::GoogleMaps::Location->new(output => $self->{output});
148             $loc->_setData($Placemark);
149             # print "[Debug] JSON::Syck::Load()-ed data:\n",Data::Dumper::Dumper( $Placemark ),"\n";
150             $response->add_placemark($loc);
151             push @placemark, $loc;
152             }
153             # print "[debug] new response object:\n",Data::Dumper::Dumper($response),"\n";
154             return $response;
155             # wantarray ? @placemark : $placemark[0];
156             }
157              
158             1;
159             __END__