File Coverage

blib/lib/Geo/Hex1.pm
Criterion Covered Total %
statement 133 133 100.0
branch 37 48 77.0
condition 10 15 66.6
subroutine 16 16 100.0
pod 5 5 100.0
total 201 217 92.6


line stmt bran cond sub pod time code
1             package Geo::Hex1;
2              
3 6     6   347062 use warnings;
  6         17  
  6         180  
4 6     6   34 use strict;
  6         10  
  6         169  
5 6     6   33 use Carp;
  6         18  
  6         612  
6              
7 6     6   9331 use POSIX qw/floor ceil/;
  6         60144  
  6         46  
8 6     6   14556 use Math::Round qw/round/;
  6         33935  
  6         894  
9              
10 6     6   5442 use version; our $VERSION = qv('0.0.2');
  6         13754  
  6         38  
11 6     6   523 use vars qw(@ISA @EXPORT);
  6         14  
  6         277  
12 6     6   33 use Exporter;
  6         13  
  6         10935  
13             @ISA = qw(Exporter);
14             @EXPORT = qw(latlng2geohex geohex2latlng geohex2polygon geohex2distance distance2geohexes);
15              
16             # Constants
17              
18             my $h_key = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWX';
19             my @h_key = split( //, $h_key );
20             my $h_customize = 0;
21             my $h_grid = 1000;
22             my $h_size = 0.5;
23             my $min_x_lon = 122930; #与那国島
24             my $min_x_lat = 24448;
25             my $min_y_lon = 141470; #南硫黄島
26             my $min_y_lat = 24228;
27              
28             # Some internal functions
29              
30             sub __geohex2level {
31 29     29   36 my $code = shift;
32            
33 29         95 my @code = split( //, $code );
34 29 50       82 my $c_length = @code or croak 'GeoHex code must be set';
35 29         35 my $level;
36              
37 29 100       64 if ( $c_length > 4 ) {
38 10         29 $level = index( $h_key, shift @code );
39 10 50       27 croak 'Code format is something wrong' if ( $level == -1 );
40 10 100       33 $level = 60 if ( $level == 0 );
41             } else {
42 19         25 $level = 7;
43             }
44 29         153 return ( $level, $c_length, @code );
45             }
46              
47             sub __geohex2hyhx {
48 29     29   43 my $code = shift;
49              
50 29         36 my ( $level, $c_length, @code ) = eval { __geohex2level( $code ) };
  29         88  
51 29 50       74 croak $@ if ( $@ );
52              
53 29         77 my $unit_x = 6.0 * $level * $h_size;
54 29         44 my $unit_y = 2.8 * $level * $h_size;
55 29         99 my $h_k = ( round( ( 1.4 / 3 ) * $h_grid ) ) / $h_grid;
56 29         401 my $base_x = floor( ( $min_x_lon + $min_x_lat / $h_k ) / $unit_x );
57 29         92 my $base_y = floor( ( $min_y_lat - $h_k * $min_y_lon ) / $unit_y );
58              
59 29         56 my ( $h_x, $h_y );
60 29 100       75 if ( $c_length > 5 ) {
61 2         5 $h_x = index( $h_key, $code[0] ) * 3600 + index( $h_key, $code[2] ) * 60 + index( $h_key, $code[4] );
62 2         5 $h_y = index( $h_key, $code[1] ) * 3600 + index( $h_key, $code[3] ) * 60 + index( $h_key, $code[5] );
63             } else {
64 27         81 $h_x = index( $h_key, $code[0] ) * 60 + index( $h_key, $code[2] );
65 27         46 $h_y = index( $h_key, $code[1] ) * 60 + index( $h_key, $code[3] );
66             }
67            
68 29         123 return ( $h_y, $h_x, $level, $unit_x, $unit_y, $h_k, $base_x, $base_y );
69             }
70              
71             sub __hyhx2geohex {
72 40     40   58 my ( $h_y, $h_x, $level ) = @_;
73              
74 40         93 my $h_x_100 = floor( $h_x / 3600);
75 40         93 my $h_x_10 = floor(($h_x % 3600) / 60);
76 40         85 my $h_x_1 = floor(($h_x % 3600) % 60);
77 40         81 my $h_y_100 = floor( $h_y / 3600);
78 40         75 my $h_y_10 = floor(($h_y % 3600) / 60);
79 40         76 my $h_y_1 = floor(($h_y % 3600) % 60);
80              
81 40         45 my $code;
82 40 100       102 if ( $level < 7 ) {
    100          
83 4         21 $code = $h_key[ $level % 60 ] . $h_key[ $h_x_100 ] . $h_key[ $h_y_100 ] . $h_key[ $h_x_10 ] . $h_key[ $h_y_10 ] . $h_key[ $h_x_1 ] . $h_key[ $h_y_1 ];
84             } elsif ( $level == 7 ) {
85 4         17 $code = $h_key[ $h_x_10 ] . $h_key[ $h_y_10 ] . $h_key[ $h_x_1 ] . $h_key[ $h_y_1 ];
86             } else {
87 32         94 $code = $h_key[ $level % 60 ] . $h_key[ $h_x_10 ] . $h_key[ $h_y_10 ] . $h_key[ $h_x_1 ] . $h_key[ $h_y_1 ];
88             }
89 40         155 return $code;
90             }
91              
92             # Export function for GeoHex
93              
94             sub latlng2geohex {
95 16     16 1 32045 my $lat = shift;
96 16         26 my $lon = shift;
97 16         27 my $level = shift;
98              
99 16 100       39 $level = defined( $level ) ? $level : 7;
100 16 50 33     178 croak 'Level must be between 1 and 60' if ( $level !~ /^\d+$/ || $level < 1 || $level > 60 );
      33        
101              
102 16         41 my $lon_grid = $lon * $h_grid;
103 16         28 my $lat_grid = $lat * $h_grid;
104 16         30 my $unit_x = 6.0 * $level * $h_size;
105 16         23 my $unit_y = 2.8 * $level * $h_size;
106 16         53 my $h_k = ( round( (1.4 / 3) * $h_grid) ) / $h_grid;
107 16         209 my $base_x = floor( ($min_x_lon + $min_x_lat / $h_k ) / $unit_x);
108 16         40 my $base_y = floor( ($min_y_lat - $h_k * $min_y_lon) / $unit_y);
109 16         36 my $h_pos_x = ( $lon_grid + $lat_grid / $h_k ) / $unit_x - $base_x;
110 16         29 my $h_pos_y = ( $lat_grid - $h_k * $lon_grid) / $unit_y - $base_y;
111 16         33 my $h_x_0 = floor($h_pos_x);
112 16         27 my $h_y_0 = floor($h_pos_y);
113 16         43 my $h_x_q = floor(($h_pos_x - $h_x_0) * 100) / 100;
114 16         51 my $h_y_q = floor(($h_pos_y - $h_y_0) * 100) / 100;
115 16         42 my $h_x = round($h_pos_x);
116 16         133 my $h_y = round($h_pos_y);
117 16 100       209 if ( $h_y_q > -$h_x_q + 1 ) {
    50          
118 5 100 66     26 if( ($h_y_q < 2 * $h_x_q ) && ( $h_y_q > 0.5 * $h_x_q ) ){
119 4         7 $h_x = $h_x_0 + 1;
120 4         5 $h_y = $h_y_0 + 1;
121             }
122             } elsif ( $h_y_q < -$h_x_q + 1 ) {
123 11 100 100     84 if( ($h_y_q > (2 * $h_x_q ) - 1 ) && ( $h_y_q < ( 0.5 * $h_x_q ) + 0.5 ) ) {
124 9         12 $h_x = $h_x_0;
125 9         13 $h_y = $h_y_0;
126             }
127             }
128              
129 16         43 return __hyhx2geohex( $h_y, $h_x, $level );
130             }
131              
132             sub geohex2latlng{
133 9     9 1 32002 my $code = shift;
134              
135 9         12 my ( $lat, $lon );
136 9         15 my ( $h_y, $h_x, $level, $unit_x, $unit_y, $h_k, $base_x, $base_y ) = eval { __geohex2hyhx( $code ) };
  9         27  
137 9 50       29 croak $@ if ( $@ );
138            
139 9         28 my $h_lat = ( $h_k * ( $h_x + $base_x ) * $unit_x + ( $h_y + $base_y ) * $unit_y ) / 2;
140 9         15 my $h_lon = ( $h_lat - ( $h_y + $base_y ) * $unit_y ) / $h_k;
141 9         14 $lat = $h_lat / $h_grid;
142 9         11 $lon = $h_lon / $h_grid;
143              
144 9         25 return ( $lat, $lon, $level );
145             }
146              
147              
148             sub geohex2polygon{
149 1     1 1 8606 my $code = shift;
150 1         5 my ( $lat, $lon, $level ) = geohex2latlng( $code );
151              
152 1         3 my $d = $level * $h_size / $h_grid;
153            
154             return [
155 1         13 [ $lat , $lon - 2.0 * $d ],
156             [ $lat + 1.4 * $d, $lon - 1.0 * $d ],
157             [ $lat + 1.4 * $d, $lon + 1.0 * $d ],
158             [ $lat , $lon + 2.0 * $d ],
159             [ $lat - 1.4 * $d, $lon + 1.0 * $d ],
160             [ $lat - 1.4 * $d, $lon - 1.0 * $d ],
161             [ $lat , $lon - 2.0 * $d ],
162             ];
163             }
164              
165             sub geohex2distance {
166 9     9 1 22145 my ( $code1, $code2 ) = @_;
167            
168 9         15 my ( $h_y1, $h_x1, $level1 ) = eval { __geohex2hyhx( $code1 ) };
  9         20  
169 9 50       25 croak $@ if ( $@ );
170 9         9 my ( $h_y2, $h_x2, $level2 ) = eval { __geohex2hyhx( $code2 ) };
  9         13  
171 9 50       19 croak $@ if ( $@ );
172 9 50       18 croak 'Level of codes are must same value' unless ( $level1 == $level2 );
173            
174 9         10 my $dh_y = $h_y1 - $h_y2;
175 9         11 my $dh_x = $h_x1 - $h_x2;
176 9         9 my $ah_y = abs( $dh_y );
177 9         7 my $ah_x = abs( $dh_x );
178            
179 9 100       20 if ( $dh_y * $dh_x > 0 ) {
180 4 50       14 return $ah_x > $ah_y ? $ah_x : $ah_y;
181             } else {
182 5         17 return $ah_x + $ah_y;
183             }
184             }
185              
186             sub distance2geohexes {
187 2     2 1 14732 my ( $code, $dist ) = @_;
188            
189 2         5 my ( $h_y, $h_x, $level ) = eval { __geohex2hyhx( $code ) };
  2         11  
190 2 50       7 croak $@ if ( $@ );
191            
192 2         4 my @results;
193 2         8 foreach my $d_y ( -1 * $dist .. $dist ) {
194 8         11 my $dh_y = $h_y + $d_y;
195 8 100       18 my $dmn_x = $d_y > 0 ? -1 * $dist + $d_y : -1 * $dist;
196 8 100       12 my $dmx_x = $d_y < 0 ? $dist + $d_y : $dist;
197            
198 8         17 foreach my $d_x ( $dmn_x .. $dmx_x ) {
199 26 100 100     75 next if ( $d_y == 0 && $d_x == 0 );
200            
201 24         56 push @results, __hyhx2geohex( $h_y + $d_y, $h_x + $d_x, $level );
202             }
203             }
204            
205 2         17 return \@results;
206             }
207              
208             1; # Magic true value required at end of module
209             __END__