File Coverage

blib/lib/Geo/Coordinates/GMap.pm
Criterion Covered Total %
statement 30 30 100.0
branch 3 4 75.0
condition 2 2 100.0
subroutine 7 7 100.0
pod 3 3 100.0
total 45 46 97.8


line stmt bran cond sub pod time code
1             package Geo::Coordinates::GMap;
2 1     1   191888 use 5.008001;
  1         4  
3 1     1   6 use strictures 2;
  1         9  
  1         45  
4             our $VERSION = '0.09';
5              
6             =head1 NAME
7              
8             Geo::Coordinates::GMap - Routines for converting decimal lat/lon to Google
9             Map tiles, and back again.
10              
11             =head1 SYNOPSIS
12              
13             use Geo::Coordinates::GMap;
14             my ($tile_x, $tile_y) = coord_to_gmap_tile( $lat, $lon, $zoom );
15             my ($new_tile_x, $new_tile_y) = zoom_gmap_tile( $tile_x, $tile_y, $old_zoom, $new_zoom );
16             my ($x, $y) = gmap_tile_xy( $tile_x, $tile_y, $scale );
17              
18             =head1 DESCRIPTION
19              
20             While working on the mapping tools on toxicrisk.com I came to the conclusion
21             that we were dealing with too much data to make everything a GMarker, even
22             when using the marker manager.
23              
24             So, I needed to generate static map tile images. But, to do this, I needed a
25             way to convert my decimal lat/lon points in to tile numbers, and the pixel
26             values on those tiles.
27              
28             This module makes this process simple and accurate.
29              
30             =cut
31              
32 1     1   737 use Math::Trig;
  1         11378  
  1         123  
33              
34 1     1   10 use Exporter qw( import );
  1         2  
  1         280  
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 77 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         25 my $sin_phi = sin( $lat * pi / 180 );
63              
64 1         4 my $norm_x = $lon / 180;
65 1         6 my $norm_y = (0.5 * log((1 + $sin_phi) / (1 - $sin_phi))) / pi;
66              
67 1         5 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 6364 my ($tile_x, $tile_y, $old_zoom, $new_zoom) = @_;
87              
88 2 100       9 if ($new_zoom < $old_zoom) {
    50          
89 1         4 foreach ($new_zoom .. ($old_zoom-1)) {
90 3         6 $tile_x = $tile_x / 2;
91 3         5 $tile_y = $tile_y / 2;
92             }
93             }
94             elsif ($new_zoom > $old_zoom) {
95 1         4 foreach (($old_zoom+1) .. $new_zoom) {
96 1         4 $tile_x = $tile_x * 2;
97 1         3 $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 1301 my ($tile_x, $tile_y, $scale) = @_;
120              
121 2   100     13 $scale ||= 1;
122              
123             return(
124 2         13 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__