File Coverage

blib/lib/Imager/Bing/MapLayer.pm
Criterion Covered Total %
statement 41 45 91.1
branch n/a
condition 1 2 50.0
subroutine 27 28 96.4
pod 1 1 100.0
total 70 76 92.1


line stmt bran cond sub pod time code
1             package Imager::Bing::MapLayer;
2              
3 2     2   191102 use v5.10.1;
  2         7  
  2         76  
4              
5 2     2   1591 use Moose;
  2         1032003  
  2         16  
6             with 'Imager::Bing::MapLayer::Role::TileClass';
7             with 'Imager::Bing::MapLayer::Role::FileHandling';
8             with 'Imager::Bing::MapLayer::Role::Centroid';
9             with 'Imager::Bing::MapLayer::Role::Misc';
10              
11 2     2   13367 use Carp qw/ confess /;
  2         9  
  2         104  
12 2     2   10 use Class::MOP::Method;
  2         3  
  2         63  
13 2     2   1877 use Const::Fast;
  2         2051  
  2         12  
14 2     2   143 use Moose::Util::TypeConstraints;
  2         3  
  2         20  
15 2     2   6282 use MooseX::StrictConstructor;
  2         46448  
  2         14  
16              
17 2         283 use Imager::Bing::MapLayer::Utils qw/
18             $MIN_ZOOM_LEVEL $MAX_ZOOM_LEVEL
19 2     2   19726 /;
  2         8  
20              
21 2     2   1342 use Imager::Bing::MapLayer::Level;
  2         6  
  2         114  
22              
23             =head1 NAME
24              
25             Imager::Bing::MapLayer - create a map layer for Bing Maps
26              
27             =cut
28              
29 2     2   21 use version 0.77; our $VERSION = version->declare('v0.1.9');
  2         64  
  2         17  
30              
31             =head1 SYNOPSIS
32              
33             my $layer = Imager::Bing::MapLayer->new(
34             base_dir => $dir, # base directory (default '.')
35             overwrite => 1, # overwrite existing (default)
36             autosave => 1, # save on exit (default)
37             in_memory => 0, # keep tiles in memory (default false)
38             min_level => 1, # min zoom level (default)
39             max_level => 19, # max zoom level (default)
40             combine => 'darken', # tile composition method (default)
41             );
42              
43             # Plot polygons (e.g. geographic boundaries)
44              
45             $layer->polygon(
46             points => $points, # listref to [ lat, lon ] points
47             fill => Imager::Fill->new( ... ), #
48             );
49              
50             # Plot greyscale gradient circles for heatmaps
51              
52             $layer->radial_circle(
53             r => 100, # radius in meters
54             -min_r => 1, # minimum pixel radius for any zoom level
55             x => $longitude, # longitude (x = east-west)
56             y => $latitude, # latitude (y = north-south)
57             );
58              
59             # Blur filter
60              
61             $layer->filter( type => 'gaussian', stddev => 1 );
62              
63             # Colourise greyscale heatmaps
64              
65             $layer->colourise();
66              
67             =head1 DESCRIPTION
68              
69             This module is a wrapper around the L<Imager::Draw> module, which
70             allows you to create Bing map layers using longitude and latitude
71             coordinates.
72              
73             The module will automatically map them to the appropriate points on
74             tile files.
75              
76             =for readme stop
77              
78             It adds the following options to drawing methods:
79              
80             =over
81              
82             =item C<-min_level>
83              
84             The minimum zoom level to draw on.
85              
86             =item C<-max_level>
87              
88             The maximum zoom level to draw on.
89              
90             =back
91              
92             =head1 ATTRIBUTES
93              
94             =head2 C<in_memory>
95              
96             The timeout for how many seconds a tile is kept in memory. The default
97             is C<0>.
98              
99             When a tile is timed out, it is saved to disk after each L<Imager> drawing
100             operation, and reloaded if it is later needed.
101              
102             Setting this to a non-zero value keeps tiles in memory, but increases
103             the memory requirements.
104              
105             =head2 C<centroid_latitude>
106              
107             =head2 C<centroid_longitude>
108              
109             This is the centroid latitude and longitude for translating
110             points to pixels. It defaults to a point in London.
111              
112             You can probably get away with ignoring this, but if you are
113             generating maps for different regions of the world, then you may
114             consider changing this, or even maininging different map sets with
115             different centroids.
116              
117             =head2 C<overwrite>
118              
119             When true (default), existing tiles will be overwritten rather than
120             edited.
121              
122             Be wary of editing existing tiles, since antialiased lines and opaque
123             fills will darken existing points rather than drawing over them.
124              
125             =head2 C<autosave>
126              
127             When true (default), tiles will be automatically saved.
128              
129             Alternatively, you can use the L</save> method to manually save tiles.
130              
131             Note that any times in memory when a script is interrupted may be
132             lost. An alternative to add something to trap interruptions:
133              
134             local $SIG{INT} = sub {
135             state $int = 0;
136             unless ($int) {
137             $int=1;
138             $image->save();
139             }
140             exit 1;
141             };
142              
143             =head2 C<combine>
144              
145             The tile combination method. It defaults to C<darken>.
146              
147             =head2 C<tile_class>
148              
149             The base class used for tiles.
150              
151             This can be used to subclass the tiles, for instance, to save tiles
152             with a different filename to use with something other than Bing maps,
153             e.g. Google Maps.
154              
155             You might use something like:
156              
157             package MyTile;
158              
159             use Moose;
160             extends 'Imager::Bing::MapLayer::Tile';
161              
162             use Path::Class;
163              
164             override 'build_filename' => sub {
165             my ($self) = @_;
166             my $file = file($self->base_dir, $self->level,
167             join(',', @{ $self->tile_coords }) . '.png');
168             return $file->stringify;
169             };
170              
171             =cut
172              
173             =head1 METHODS
174              
175             =head2 C<levels>
176              
177             my @levels = @{ $layer->levels };
178              
179             This returns a reference to a list of
180             L<Imager::Bing::MapLayer::Level> objects.
181              
182             =cut
183              
184             has 'levels' => (
185             is => 'ro',
186             isa => 'ArrayRef',
187             lazy => 1,
188             default => sub {
189             my ($self) = @_;
190              
191             confess "min_level > max_level"
192             if ( $self->min_level > $self->max_level );
193              
194             my @levels;
195              
196             foreach my $level ( $self->min_level .. $self->max_level ) {
197             push @levels,
198             Imager::Bing::MapLayer::Level->new(
199             level => $level,
200             base_dir => $self->base_dir,
201             centroid_latitude => $self->centroid_latitude,
202             centroid_longitude => $self->centroid_longitude,
203             overwrite => $self->overwrite,
204             autosave => $self->autosave,
205             in_memory => $self->in_memory,
206             combine => $self->combine,
207             tile_class => $self->tile_class,
208             _max_buffer_breadth => $self->_max_buffer_breadth,
209             );
210             }
211              
212             return \@levels;
213             },
214             init_arg => undef,
215             );
216              
217             =head2 C<min_level>
218              
219             The minimum zoom level to generate.
220              
221             =cut
222              
223             has 'min_level' => (
224             is => 'ro',
225             isa => subtype(
226             as 'Int',
227             where { ( $_ >= $MIN_ZOOM_LEVEL ) && ( $_ <= $MAX_ZOOM_LEVEL ) }
228             ),
229             default => sub {$MIN_ZOOM_LEVEL},
230             );
231              
232             =head2 C<max_level>
233              
234             The maximum zoom level to generate.
235              
236             =cut
237              
238             has 'max_level' => (
239             is => 'ro',
240             isa => subtype(
241             as 'Int',
242             where { ( $_ >= $MIN_ZOOM_LEVEL ) && ( $_ <= $MAX_ZOOM_LEVEL ) }
243             ),
244             default => sub {$MAX_ZOOM_LEVEL},
245             );
246              
247             =begin :internal
248              
249             =head2 <_max_buffer_breadth>
250              
251             The maximum width and height of the temporary L<Imager> image.
252              
253             Generally, you do not need to be concerned with this parameter, unless
254             you get C<malloc> errors when rendering tiles.
255              
256             =cut
257              
258             has '_max_buffer_breadth' => (
259             is => 'ro',
260             isa => 'Int',
261             default => 1024 * 4, #
262             );
263              
264             =head2 C<_make_imager_wrapper_method>
265              
266             __PACKAGE__->_make_imager_wrapper_method( { name => $method } );
267              
268             This is an I<internal> method for generating wrapper L<Imagers::Draw>
269             methods that are applied to every level.
270              
271             These methods use latitude and longitude in lieau of C<y> and C<x>
272             parameters. Note that C<points> parameters contain pairs of latitude
273             and longitude coordinates, I<not> longitude and latitude coordinates!
274              
275             See L<Imager::Draw> for documentation of the methods.
276              
277             We've added the following additional arguments:
278              
279             =over
280              
281             =item C<-min_level>
282              
283             The minimum zoom level to draw on.
284              
285             =item C<-max_level>
286              
287             The maximum zoom level to draw on.
288              
289             =back
290              
291             =end :internal
292              
293             =cut
294              
295             sub _make_imager_wrapper_method {
296 28     28   36 my ( $class, $opts ) = @_;
297              
298 28   50     118 $opts->{args} //= [];
299              
300             $class->meta->add_method(
301              
302             $opts->{name} => sub {
303              
304 1     1   1100 my ( $self, %args ) = @_;
        1      
        1      
        1      
        1      
        1      
        1      
        1      
        1      
        1      
        1      
        1      
        1      
        1      
        1      
305              
306 1         2 foreach my $level ( @{ $self->levels } ) {
  1         75  
307              
308 11         80 my $method = $level->can( $opts->{name} );
309              
310 11         64 $level->$method(%args);
311              
312             }
313              
314             }
315              
316 28         86 );
317             }
318              
319             =head2 C<radial_circle>
320              
321             $layer->radial_circle(
322             r => $radius_in_meters,
323             -min_r => $min_radius_in_pixels,
324             x => $longitude,
325             y => $latitude,
326             );
327              
328             This method plots "fuzzy" greyscale circles, which are intended to be
329             used for heatmaps. The radius is scaled appropriately for each zoom
330             level in the layer.
331              
332             If C<-min_r> is specified, then a circle will always be drawn with
333             that minimum radius: this ensures that lower zoom levels will always
334             have a point plotted.
335              
336             =head2 C<colourise>
337              
338             =head2 C<colorize>
339              
340             $layer->colourise();
341              
342             The method colourises greyscale layers. It is intended to be used for
343             heatmaps generated using the L</radial_circle> method.
344              
345             =head2 C<filter>
346              
347             $layer->filter( type => 'gaussian', stddev => 1 );
348              
349             This applies L<Imager::Filters> to every tile on every zoom level of the layer.
350              
351             Be aware that some filter effects may enhance the edges of tiles in
352             each zoom level.
353              
354             =head2 C<setpixel>
355              
356             Draw a pixel at a specific latitude and longitude coordinate.
357              
358             See the corresponding method in L<Imager::Draw> for more information.
359              
360             =head2 C<line>
361              
362             Draw a line between two coordinates.
363              
364             See the corresponding method in L<Imager::Draw> for more information.
365              
366             =head2 C<box>
367              
368             Draw a box bounded by northwest and southeast coordinates.
369              
370             See the corresponding method in L<Imager::Draw> for more information.
371              
372             =head2 C<polyline>
373              
374             Draw a polyline for a set of coordinates.
375              
376             Note that a polyline is not closed. To draw a closed area, use the
377             L</polygon> method.
378              
379             See the corresponding method in L<Imager::Draw> for more information.
380              
381             =head2 C<polygon>
382              
383             Draw a closed polygon for a set of coordinates.
384              
385             See the corresponding method in L<Imager::Draw> for more information.
386              
387             =head2 C<arc>
388              
389             Draw an arc.
390              
391             See the corresponding method in L<Imager::Draw> for more information.
392              
393             =head2 C<circle>
394              
395             Draw a circle.
396              
397             See the corresponding method in L<Imager::Draw> for more information.
398              
399             =head2 C<string>
400              
401             Draw a text string.
402              
403             TODO - the size is not scaled.
404              
405             See the corresponding method in L<Imager::Draw> for more information.
406              
407             =head2 C<align_string>
408              
409             Draw an aligned text string.
410              
411             TODO - the size is not scaled.
412              
413             See the corresponding method in L<Imager::Draw> for more information.
414              
415             =cut
416              
417             foreach my $method (
418             qw/
419             radial_circle colourise colorize
420             filter setpixel line box polyline polygon arc circle flood_fill
421             string align_string
422             /
423             )
424             {
425              
426             __PACKAGE__->_make_imager_wrapper_method( { name => $method } );
427              
428             }
429              
430             =head2 C<save>
431              
432             Save the tiles.
433              
434             =cut
435              
436             sub save {
437 0     0 1   my ( $self, @args ) = @_;
438              
439 0           foreach my $level ( @{ $self->levels } ) {
  0            
440 0           $level->save(@args);
441             }
442             }
443              
444             =head1 VIEWING MAP LAYERS
445              
446             =head2 Bing Maps
447              
448             You can view tiles using the following web page, replacing the
449             C<credentials> option with your Bing Maps Key:
450              
451             <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
452             "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
453             <html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml"
454             xml:lang="en" lang="en">
455             <head>
456             <title>Tiles Test</title>
457             <script src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0&mkt=en-gb"></script>
458             <script>
459             //<![CDATA[
460             var map;
461             function init(){
462             var map_options = {
463             credentials : "YOUR BING MAPS KEY HERE",
464             center : new Microsoft.Maps.Location(51.5171, 0.1062),
465             zoom : 10,
466             showMapTypeSelector : false,
467             useInertia : true,
468             inertiaIntensity : 0,
469             tileBuffer : 1,
470             enableSearchLogo : false,
471             enableClickableLogo : false,
472             showScalebar : false
473             }
474             map = new Microsoft.Maps.Map(document.getElementById('mapviewer'), map_options);
475             addDefaultTileLayer();
476             }
477              
478             function addDefaultTileLayer(){
479             var options = { uriConstructor: 'tiles/{quadkey}.png' };
480             var tileSource = new Microsoft.Maps.TileSource(options);
481             var tilelayer= new Microsoft.Maps.TileLayer({ mercator: tileSource });
482             map.entities.push(tilelayer);
483             }
484             // ]]>
485             </script>
486             </head>
487             <body onload="init();">
488             <div id="mapviewer" style="position:relative;width:100%;height:700px;"></div>
489             </body>
490             </html>
491              
492             You can apply for a Bing Maps Key at L<https://www.bingmapsportal.com>.
493              
494             =for readme continue
495              
496             =head1 SEE ALSO
497              
498             =over
499              
500             * Bing Maps Tile System
501              
502             L<http://msdn.microsoft.com/en-us/library/bb259689.aspx>
503              
504             =back
505              
506             =head1 AUTHOR
507              
508             Robert Rothenberg, C<< <rrwo at cpan.org> >>
509              
510             =head1 BUGS
511              
512             Please report any bugs or feature requests to the author, or through
513             the web interface at
514             L<https://github.com/robrwo/Imager-Bing-MapLayer/issues>.
515              
516             =head1 SUPPORT
517              
518             You can find documentation for this module with the perldoc command.
519              
520             perldoc Imager::Bing::MapLayer
521              
522             You can also look for information at:
523              
524             =over 4
525              
526             =item * GitHub
527              
528             L<https://github.com/robrwo/Imager-Bing-MapLayer>
529              
530             =back
531              
532             =head1 ACKNOWLEDGEMENTS
533              
534             =over
535              
536             =item *
537              
538             Foxtons, Ltd.
539              
540             =back
541              
542             =head1 LICENSE AND COPYRIGHT
543              
544             Copyright 2013-2014 Robert Rothenberg.
545              
546             This program is released under the following license: atistic2
547              
548             =cut
549              
550 2     2   1400 use namespace::autoclean;
  2         4  
  2         17  
551              
552             1; # End of Imager::Bing::MapLayer