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             $Geo::Coordinates::GMap::VERSION = '0.07';
3 1     1   25704 use strictures 1;
  1         918  
  1         39  
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   816 use Math::Trig;
  1         23830  
  1         227  
32              
33 1     1   11 use Exporter qw( import );
  1         8  
  1         349  
34             our @EXPORT = qw(
35             coord_to_gmap_tile
36             zoom_gmap_tile
37             gmap_tile_xy
38             );
39              
40             =head1 FUNCTIONS
41              
42             =head2 coord_to_gmap_tile
43              
44             my ($tile_x, $tile_y) = coord_to_gmap_tile( $lat, $lon, $zoom );
45              
46             Given a decimal latitude and longitude, and a Google Maps zoom level (0 being farthest away
47             and 20 being the closest that I'm aware of that you can get), this function will return the
48             GMap tile location as a fractional x and y coordinate.
49              
50             =cut
51              
52             # Inspired by some C# code at:
53             # http://groups.google.co.in/group/Google-Maps-API/browse_thread/thread/d2103ac29e95696f
54              
55             sub coord_to_gmap_tile {
56 1     1 1 12 my ($lat, $lon, $zoom) = @_;
57              
58             # The C# code did this, but I don't know why, so I'm not going to enable it.
59             #return if abs($lat) > 85.0511287798066;
60              
61 1         20 my $sin_phi = sin( $lat * pi / 180 );
62              
63 1         3 my $norm_x = $lon / 180;
64 1         9 my $norm_y = (0.5 * log((1 + $sin_phi) / (1 - $sin_phi))) / pi;
65              
66 1         5 my $tile_x = (2 ** $zoom) * (($norm_x + 1) / 2);
67 1         5 my $tile_y = (2 ** $zoom) * ((1 - $norm_y) / 2);
68              
69             return(
70 1         4 $tile_x,
71             $tile_y,
72             );
73             }
74              
75             =head2 zoom_gmap_tile
76              
77             my ($new_tile_x, $new_tile_y) = zoom_gmap_tile( $tile_x, $tile_y, $old_zoom, $new_zoom );
78              
79             Converts fractional tile coordinates, as created by coord_to_gmap_tile(), from one
80             zoom level to another.
81              
82             =cut
83              
84             sub zoom_gmap_tile {
85 2     2 1 1955 my ($tile_x, $tile_y, $old_zoom, $new_zoom) = @_;
86              
87 2 100       54 if ($new_zoom < $old_zoom) {
    50          
88 1         6 foreach ($new_zoom .. ($old_zoom-1)) {
89 3         5 $tile_x = $tile_x / 2;
90 3         6 $tile_y = $tile_y / 2;
91             }
92             }
93             elsif ($new_zoom > $old_zoom) {
94 1         4 foreach (($old_zoom+1) .. $new_zoom) {
95 1         2 $tile_x = $tile_x * 2;
96 1         4 $tile_y = $tile_y * 2;
97             }
98             }
99              
100 2         11 return( $tile_x, $tile_y );
101             }
102              
103             =head2 gmap_tile_xy
104              
105             my ($x, $y) = gmap_tile_xy( $tile_x, $tile_y, $scale );
106              
107             Given a tile's x and y coordinate as provided by coord_to_gmap_tile(), this function
108             will return the pixel location within the tile.
109              
110             The C<$scale> argument may be supplied which can be used to produce high-res tiles.
111             At this time Google states that the scale can be C<1>, C<2>, or C<4> (only Google
112             Maps API for Work customers can use C<4> with the Google Maps API). If not specified
113             the scale will default to C<1>.
114              
115             =cut
116              
117             sub gmap_tile_xy {
118 2     2 1 1649 my ($tile_x, $tile_y, $scale) = @_;
119              
120 2   100     10 $scale ||= 1;
121              
122             return(
123 2         16 int( (($tile_x - int($tile_x)) * 256 * $scale) + 0.5 ),
124             int( (($tile_y - int($tile_y)) * 256 * $scale) + 0.5 ),
125             );
126             }
127              
128             1;
129             __END__