File Coverage

blib/lib/Geo/Cloudmade.pm
Criterion Covered Total %
statement 27 101 26.7
branch 0 24 0.0
condition 0 6 0.0
subroutine 9 29 31.0
pod 5 8 62.5
total 41 168 24.4


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