File Coverage

blib/lib/Geo/Coordinates/GMap.pm
Criterion Covered Total %
statement 28 28 100.0
branch 3 4 75.0
condition 2 2 100.0
subroutine 6 6 100.0
pod 3 3 100.0
total 42 43 97.6


line stmt bran cond sub pod time code
1             package Geo::Coordinates::GMap;
2              
3             $Geo::Coordinates::GMap::VERSION = '0.08';
4              
5             =head1 NAME
6              
7             Geo::Coordinates::GMap - Routines for converting decimal lat/lon to Google
8             Map tiles, and back again.
9              
10             =head1 SYNOPSIS
11              
12             use Geo::Coordinates::GMap;
13             my ($tile_x, $tile_y) = coord_to_gmap_tile( $lat, $lon, $zoom );
14             my ($new_tile_x, $new_tile_y) = zoom_gmap_tile( $tile_x, $tile_y, $old_zoom, $new_zoom );
15             my ($x, $y) = gmap_tile_xy( $tile_x, $tile_y, $scale );
16              
17             =head1 DESCRIPTION
18              
19             While working on the mapping tools on toxicrisk.com I came to the conclusion
20             that we were dealing with too much data to make everything a GMarker, even
21             when using the marker manager.
22              
23             So, I needed to generate static map tile images. But, to do this, I needed a
24             way to convert my decimal lat/lon points in to tile numbers, and the pixel
25             values on those tiles.
26              
27             This module makes this process simple and accurate.
28              
29             =cut
30              
31 1     1   418336 use strictures 2;
  1         7  
  1         37  
32 1     1   626 use Math::Trig;
  1         26781  
  1         120  
33              
34 1     1   9 use Exporter qw( import );
  1         2  
  1         304  
35             our @EXPORT = qw(
36             coord_to_gmap_tile
37             zoom_gmap_tile
38             gmap_tile_xy
39             );
40              
41             =head1 FUNCTIONS
42              
43             =head2 coord_to_gmap_tile
44              
45             my ($tile_x, $tile_y) = coord_to_gmap_tile( $lat, $lon, $zoom );
46              
47             Given a decimal latitude and longitude, and a Google Maps zoom level (0 being farthest away
48             and 20 being the closest that I'm aware of that you can get), this function will return the
49             GMap tile location as a fractional x and y coordinate.
50              
51             =cut
52              
53             # Inspired by some C# code at:
54             # http://groups.google.co.in/group/Google-Maps-API/browse_thread/thread/d2103ac29e95696f
55              
56             sub coord_to_gmap_tile {
57 1     1 1 72 my ($lat, $lon, $zoom) = @_;
58              
59             # The C# code did this, but I don't know why, so I'm not going to enable it.
60             #return if abs($lat) > 85.0511287798066;
61              
62 1         21 my $sin_phi = sin( $lat * pi / 180 );
63              
64 1         3 my $norm_x = $lon / 180;
65 1         7 my $norm_y = (0.5 * log((1 + $sin_phi) / (1 - $sin_phi))) / pi;
66              
67 1         4 my $tile_x = (2 ** $zoom) * (($norm_x + 1) / 2);
68 1         3 my $tile_y = (2 ** $zoom) * ((1 - $norm_y) / 2);
69              
70             return(
71 1         4 $tile_x,
72             $tile_y,
73             );
74             }
75              
76             =head2 zoom_gmap_tile
77              
78             my ($new_tile_x, $new_tile_y) = zoom_gmap_tile( $tile_x, $tile_y, $old_zoom, $new_zoom );
79              
80             Converts fractional tile coordinates, as created by coord_to_gmap_tile(), from one
81             zoom level to another.
82              
83             =cut
84              
85             sub zoom_gmap_tile {
86 2     2 1 5843 my ($tile_x, $tile_y, $old_zoom, $new_zoom) = @_;
87              
88 2 100       10 if ($new_zoom < $old_zoom) {
    50          
89 1         3 foreach ($new_zoom .. ($old_zoom-1)) {
90 3         5 $tile_x = $tile_x / 2;
91 3         5 $tile_y = $tile_y / 2;
92             }
93             }
94             elsif ($new_zoom > $old_zoom) {
95 1         5 foreach (($old_zoom+1) .. $new_zoom) {
96 1         2 $tile_x = $tile_x * 2;
97 1         2 $tile_y = $tile_y * 2;
98             }
99             }
100              
101 2         6 return( $tile_x, $tile_y );
102             }
103              
104             =head2 gmap_tile_xy
105              
106             my ($x, $y) = gmap_tile_xy( $tile_x, $tile_y, $scale );
107              
108             Given a tile's x and y coordinate as provided by coord_to_gmap_tile(), this function
109             will return the pixel location within the tile.
110              
111             The C<$scale> argument may be supplied which can be used to produce high-res tiles.
112             At this time Google states that the scale can be C<1>, C<2>, or C<4> (only Google
113             Maps API for Work customers can use C<4> with the Google Maps API). If not specified
114             the scale will default to C<1>.
115              
116             =cut
117              
118             sub gmap_tile_xy {
119 2     2 1 1090 my ($tile_x, $tile_y, $scale) = @_;
120              
121 2   100     11 $scale ||= 1;
122              
123             return(
124 2         10 int( (($tile_x - int($tile_x)) * 256 * $scale) + 0.5 ),
125             int( (($tile_y - int($tile_y)) * 256 * $scale) + 0.5 ),
126             );
127             }
128              
129             1;
130             __END__