File Coverage

blib/lib/Geo/Cloudmade.pm
Criterion Covered Total %
statement 53 101 52.4
branch 6 24 25.0
condition 2 6 33.3
subroutine 13 29 44.8
pod 5 8 62.5
total 79 168 47.0


line stmt bran cond sub pod time code
1             package Geo::Cloudmade;
2              
3 2     2   7949 use 5.006000;
  2         6  
  2         119  
4             our $VERSION = '0.8';
5              
6             =head1 NAME
7              
8             Geo::Cloudmade - An extended interface to Cloudmade's Geo API (geocoding, routing, drawing tiles)
9              
10             =head1 DESCRIPTION
11            
12             L is a provider of free map of the world using data
13             collected by L project.
14              
15             This module implements an OO wrapper around Cloudmade's Geo API.
16              
17             The following capabilities are implemented:
18             - geocoding and geosearching
19             - routing
20             - obtaining tiles
21              
22             Please note that some features of the HTTP API aren't wrapped yet by this module.
23             If you have any questions or/and suggestions feel free to drop me a mail or create a ticked in rt.cpan.org.
24              
25             =head1 SYNOPSIS
26              
27             use Geo::Cloudmade;
28            
29             #use api key for access to service
30             my $geo = Geo::Cloudmade->new('BC9A493B41014CAABB98F0471D759707');
31            
32             #find coordinates of geo object
33             my @arr = $geo->find("Potsdamer Platz,Berlin,Germany", {results=>5, skip=>0});
34              
35             print $geo->error(), "\n" unless @arr;
36              
37             print "Number of results: ", scalar (@arr), "\n";
38             foreach (@arr) {
39             print $_->name,":\n", $_->centroid->lat, "/", $_->centroid->long, "\n"
40             }
41              
42             # finding closest POI (Point of Interest)
43             # for list all available objects please look at http://developers.cloudmade.com/projects/show/geocoding-http-api
44             @arr = $geo->find_closest('library', [59.12, 81.1]);
45             print "No closest variants\n" unless @arr;
46              
47             # reverse geocoding
48             my ($reverse) = $geo->find_closest('address', [52.4870,13.4248]);
49             if (defined $reverse) {
50             print join ' ', $reverse->properties('addr:housenumber', 'addr:street', 'addr:postcode', 'addr:city'), "\n";
51             } else { print "No results, sorry\n" }
52              
53             #calculate route
54             my $route = $geo->get_route([47.25976, 9.58423], [47.66117, 9.99882], { type=>'car', method=>'shortest' } );
55             print "Distance: ", $route->total_distance, "\n";
56             print "Start: ", $route->start, "\n";
57             print "End: ", $route->end, "\n";
58              
59             print "Route instructions:\n";
60             print join (',', @$_), "\n" foreach (@{$route->instructions});
61              
62             #get tile
63             my $tile = $geo->get_tile([47.26117, 9.59882], {zoom=>10, tile_size=>256});
64             open (my $fh, '>', 'test.png') or die "Cannot open file $!\n";
65             binmode $fh;
66             print $fh $tile;
67              
68             =cut
69              
70 2     2   10 use strict;
  2         3  
  2         56  
71 2     2   9 use warnings;
  2         7  
  2         51  
72 2     2   7503 use LWP::UserAgent;
  2         201950  
  2         77  
73 2     2   24 use URI;
  2         6  
  2         48  
74 2     2   2580 use JSON;
  2         54785  
  2         13  
75 2     2   11057 use Math::Trig;
  2         47621  
  2         431  
76              
77 2     2   28 use constant HOST => 'cloudmade.com';
  2         3  
  2         173  
78 2     2   12 use constant DEBUG => $ENV{GEO_CLOUDMADE_DEBUG};
  2         4  
  2         3105  
79              
80             =head1 CONSTRUCTOR
81              
82             =head2 new API-KEY
83              
84             Usage : my $geo = Geo::Cloudmade->new('your-ip-key');
85             Function : Constructs and returns a new Geo::Cloudmade object
86             Returns : a Geo::Cloudmade object
87             API-KEY : api key provided by Cloudmade. For request api key please visit http://developers.cloudmade.com/projects>
88              
89             =cut
90             sub new {
91 2     2 1 42 my ($class, $key) = @_;
92 2         20 bless {
93             key => $key,
94             ua => LWP::UserAgent->new( keep_alive => 2 ),
95             error => '',
96             http_status => 0,
97             }, $class
98             }
99              
100             # internal method
101             # TODO - add comment
102             sub call_service {
103 2     2 0 7 my ($self, $path, $params, $subdomain) = @_;
104 2 50       12 my $host = defined $subdomain ? "$subdomain.".HOST : HOST;
105 2         22 my $uri = URI->new;
106 2         14616 $uri->scheme('http');
107 2         8436 $uri->host($host);
108 2         369 $uri->path("$self->{key}/$path");
109 2         142 $uri->query_form($params);
110              
111 2         463 print "uri=", $uri->as_string, "\n" if DEBUG;
112 2         23 my $request = new HTTP::Request(GET => $uri->as_string);
113 2         345 my $response = $self->{ua}->request($request);
114              
115 2         714198 $self->{http_status} = $response->code;
116 2 50       51 if ($response->is_success) {
117 0         0 $self->{error} = '';
118 0         0 return $response->content;
119             }
120             else {
121 2         33 $self->{error} = "HTTP request error: ".$response->status_line."\n";
122 2         84 return undef;
123             }
124             }
125              
126             =head1 OBJECT METHODS
127              
128             =head2 find QUERY, PARAMS
129              
130             Usage : my @arr = $geo->find("Potsdamer Platz,Berlin,Germany", {results=>5, skip=>0});
131             Function: Returns geo objects (bound box and\or location) by query or nothing
132             Returns : Array of Geo::Cloudmade::Result objects or one Geo::Cloudmade::Results if scalar value was expected
133             QUERY : Query in format POI, House Number, Street, City, County like "Potsdamer Platz, Berlin, Germany".
134             Also near is supported in queries, e.g. "hotel near Potsdamer Platz, Berlin, Germany"
135             PARAMS : Hash for control ouptut. Valid elements are bbox, results, skip, bbox_only, return_geometry, return_location
136             For more info about parameters please look at http://developers.cloudmade.com/wiki/geocoding-http-api/Documentation
137              
138             =cut
139              
140             sub find {
141 1     1 1 11933 my ($self, $name, $opt) = @_;
142 1         9 my %params = ( query=>$name, return_geometry => 'true', %$opt );
143 1         7 my $content = $self->call_service("geocoding/v2/find.js", [%params], 'geocoding');
144              
145 1 50       13 return unless $content;
146 0         0 my $ref = from_json($content, {utf8 => 1});
147 0         0 my @objs;
148 0         0 push @objs, bless $_, 'Geo::Cloudmade::Result' foreach (@{$ref->{features}});
  0         0  
149 0 0       0 return @objs if wantarray;
150 0         0 return bless [ objs=>\@objs ], 'Geo::Cloudmade::Results'
151             }
152              
153             =head2 find_closest OBJECT POINT PARAMS
154              
155             Usage: @arr = $geo->find_closest('library', [59.12, 81.1]);
156             Function: Find closest object(s).
157             Returns array of Geo::Cloudmade::Result objects like find method
158             OBJECT - point of interest, list of supported objects located at http://developers.cloudmade.com/projects/show/geocoding-http-api
159             POINT - reference of array of [$lattitude, $longtitude]
160             PARAMS - optional parameters like return_geometry and return_location
161             =cut
162              
163             sub find_closest {
164 0     0 1 0 my ($self, $objType, $point, $opt) = @_;
165 0 0       0 $opt = {} unless defined $opt;
166 0         0 my %params = ( return_geometry => 'true', return_location=>'true',
167             around => $point->[0].','.$point->[1],
168             object_type => $objType,
169             distance => 'closest',
170             %$opt );
171 0         0 my $content = $self->call_service("geocoding/v2/find.js", [%params], 'geocoding');
172              
173 0 0       0 return unless $content;
174 0         0 my $ref = from_json($content, {utf8 => 1});
175 0         0 my @objs;
176 0         0 push @objs, bless $_, 'Geo::Cloudmade::Result' foreach (@{$ref->{features}});
  0         0  
177 0 0       0 return @objs if wantarray;
178 0         0 return bless [ objs=>\@objs ], 'Geo::Cloudmade::Results'
179             }
180              
181             =head2 get_route START_POINT END_POINT PARAMS
182            
183             Allowed parameters:
184             type => 'car' // 'foot' // 'bicycle'
185             method => 'shortest' // fastest' - only for route type 'car', default is 'shortest'
186             lang => iso 2 characters code for language for the route instructions, default is en.
187             Possible values are: de, en, es, fr, hu, it, nl, ro, ru, se, vi, zh.
188             units => (measure units for distance calculation) 'km' // 'miles' (default 'km')
189              
190             Usage:
191             my $route = $geo->get_route(
192             [47.25976, 9.58423], [47.66117, 9.99882],
193             { type=>'car', method=>'shortest' } );
194              
195             Returns: Geo::Cloudmade::Route object from server or undef if communication with server was unsuccessful.
196             Function: Calculates route from START to END and returns Geo::Cloudmade::Route object
197             See also: Cloudmade's documentation about API for routing.
198             http://developers.cloudmade.com/wiki/routing-http-api/Documentation
199             =cut
200              
201             sub get_route {
202 1     1 1 6462 my ($self, $start, $end, $opt) = @_;
203 1         9 my %params = ( type => 'car', method=>'shortest', lang=>'en', units=>'km', %$opt );
204 1         4 my ($type, $method) = delete @params{qw/type method/};
205 1 50 33     14 warn "Unexpected type $type" unless ($type eq 'car' or $type eq 'foot' or $type eq 'bicycle');
      33        
206              
207 1 50       14 my $content = $self->call_service("api/0.3/".$start->[0].','.$start->[1].','.$end->[0].','.$end->[1].
208             ($type eq 'car' ? "/$type/$method.js" : "/$type.js") , [%params], 'routes');
209 1 50       15 return unless $content;
210 0           my $ref = from_json($content, {utf8 => 1});
211 0           return bless $ref, 'Geo::Cloudmade::Route';
212             }
213              
214             =head2 get_tile CENTER PARAMS
215            
216             Returns raw png data of specified map point
217             CENTER array reference to latitude and longtitude
218             PARAMS optional parameters. Allowed parameters are zoom, stile, size
219             For more info please look at official documentation from Cloudmade
220              
221             =cut
222              
223             sub get_tile {
224 0     0 1   my ($self, $center, $opt) = @_;
225 0           my %params = ( zoom=>10, style=>1, size=>256, %$opt );
226              
227 0           my ($lat, $long) = @$center;
228             #get xytile
229 0           my $factor = 2 ** ($params{zoom} - 1 );
230 0           $_ = deg2rad($_) foreach ($lat, $long);
231              
232 0           my $xtile = 1 + $long / pi;
233 0           my $ytile = 1 - log(tan($lat) + 1 / cos ($lat)) / pi;
234              
235 0           $_ = int (.5 + $_ * $factor) foreach ($xtile, $ytile);
236 0           my $content = $self->call_service(join('/', (@params{qw{style size zoom}}, $xtile, $ytile)).'.png', undef, 'tile');
237              
238 0           return $content;
239             }
240              
241 0     0 0   sub error { $_[0]->{error} }
242 0     0 0   sub http_status { $_[0]->{http_status} }
243              
244             package Geo::Cloudmade::Route;
245             =head1 Geo::Cloudmade::Route
246              
247             Geo::Cloudmade::Route represents responce of routing request in decoded JSON.
248             More details available here http://developers.cloudmade.com/wiki/routing-http-api/Response_structure .
249              
250             The following helper functions were added:
251             - total_distance - distance in meters. (Probably it should be in requested units, see parameters of get_route)
252             - start - name of the start point of the route,
253             - end - name of the end point of the route,
254             - valid - returns 1 if status is 0 (OK)
255             - status_message - text description in case of error
256             - instructions - array of detailed instructions,
257             see more details in http://developers.cloudmade.com/wiki/routing-http-api/Response_structure
258              
259             =cut
260              
261 0     0     sub total_distance { $_[0]->{route_summary}{total_distance} }
262 0     0     sub start { $_[0]->{route_summary}{start_point} }
263 0     0     sub end { $_[0]->{route_summary}{end_point} }
264 0 0   0     sub valid { $_[0]->{status} ? 0 : 1 }
265 0     0     sub status_message { $_[0]->{status_message} }
266 0     0     sub instructions { $_[0]->{route_instructions} }
267              
268             package Geo::Cloudmade::Results;
269 0     0     sub objs { $_[0]->{objs} }
270              
271             package Geo::Cloudmade::Result;
272             sub name {
273 0     0     $_[0]->{properties}{name}
274             }
275             sub properties {
276 0     0     my ($this, @names) = @_;
277 0           @{$this->{properties}}{@names};
  0            
278             }
279             sub centroid {
280 0 0   0     return undef unless $_[0]->{centroid}{type} eq 'POINT';
281 0           bless $_[0]->{centroid}{coordinates}, 'Geo::Cloudmade::Point'
282             }
283             package Geo::Cloudmade::Point;
284             sub lat {
285 0     0     $_[0]->[0]
286             }
287             sub long {
288 0     0     $_[0]->[1]
289             }
290              
291             1;
292              
293             =head1 SEE ALSO
294              
295             Official documentation about services from Cloudmade http://developers.cloudmade.com/projects
296             You can find more specific info about:
297             - geocoding and geosearching in http://developers.cloudmade.com/projects/show/geocoding-http-api
298             - routing in http://developers.cloudmade.com/projects/show/routing-http-api
299             - obtaining tiles in http://developers.cloudmade.com/projects/tiles/documents
300              
301             =head1 AUTHOR
302              
303             Dmytro Gorbunov, Edmitro.gorbunov@gmail.comE
304              
305              
306             =head1 COPYRIGHT AND LICENSE
307              
308             Copyright (C) 2010 by Dmytro Gorbunov
309              
310             This library is free software; you can redistribute it and/or modify
311             it under the same terms as Perl itself, either Perl version 5.8.9 or,
312             at your option, any later version of Perl 5 you may have available.
313              
314              
315             =cut
316              
317