File Coverage

blib/lib/Geo/Direction/Distance.pm
Criterion Covered Total %
statement 124 124 100.0
branch 8 16 50.0
condition 7 17 41.1
subroutine 12 12 100.0
pod 5 5 100.0
total 156 174 89.6


line stmt bran cond sub pod time code
1             package Geo::Direction::Distance;
2              
3 2     2   169960 use warnings;
  2         6  
  2         69  
4 2     2   11 use strict;
  2         3  
  2         72  
5 2     2   10 use Carp;
  2         8  
  2         353  
6 2     2   10276 use Math::Trig qw(tan);
  2         75706  
  2         257  
7              
8 2     2   5078 use version; our $VERSION = qv('0.0.2');
  2         8062  
  2         13  
9              
10 2     2   277 use vars qw(@ISA @EXPORT);
  2         4  
  2         469  
11 2     2   11 use Exporter;
  2         3  
  2         6597  
12             @ISA = qw(Exporter);
13             @EXPORT = qw(latlng2dirdist dirdist2latlng);
14              
15             my $pi = 4 * atan2(1,1); # PI
16             my $rd = $pi / 180; # [radian/degree]
17              
18             # Datum
19             my %ellps = (
20             'MERIT' => {
21             a => 6378137.0,
22             rf => 298.257,
23             },
24             'SGS85' => {
25             a => 6378136.0,
26             rf => 298.257,
27             },
28             'GRS80' => {
29             a => 6378137.0,
30             rf => 298.257222101,
31             },
32             'IAU76' => {
33             a => 6378140.0,
34             rf => 298.257,
35             },
36             'airy' => {
37             a => 6377563.396,
38             b => 6356256.910,
39             },
40             'APL4.9' => {
41             a => 6378137.0,
42             rf => 298.25,
43             },
44             'NWL9D' => {
45             a => 6378145.0,
46             rf => 298.25,
47             },
48             'mod_airy' => {
49             a => 6377340.189,
50             b => 6356034.446,
51             },
52             'andrae' => {
53             a => 6377104.43,
54             rf => 300.0,
55             },
56             'aust_SA' => {
57             a => 6378160.0,
58             rf => 298.25,
59             },
60             'GRS67' => {
61             a => 6378160.0,
62             rf => 298.2471674270,
63             },
64             'bessel' => {
65             a => 6377397.155,
66             rf => 299.1528128,
67             },
68             'bess_nam' => {
69             a => 6377483.865,
70             rf => 299.1528128,
71             },
72             'clrk66' => {
73             a => 6378206.4,
74             b => 6356583.8,
75             },
76             'clrk80' => {
77             a => 6378249.145,
78             rf => 293.4663,
79             },
80             'CPM' => {
81             a => 6375738.7,
82             rf => 334.29,
83             },
84             'delmbr' => {
85             a => 6376428.0,
86             rf => 311.5,
87             },
88             'engelis' => {
89             a => 6378136.05,
90             rf => 298.2566,
91             },
92             'evrst30' => {
93             a => 6377276.345,
94             rf => 300.8017,
95             },
96             'evrst48' => {
97             a => 6377304.063,
98             rf => 300.8017,
99             },
100             'evrst56' => {
101             a => 6377301.243,
102             rf => 300.8017,
103             },
104             'evrst69' => {
105             a => 6377295.664,
106             rf => 300.8017,
107             },
108             'evrstSS' => {
109             a => 6377298.556,
110             rf => 300.8017,
111             },
112             'fschr60' => {
113             a => 6378166.0,
114             rf => 298.3,
115             },
116             'fschr60m' => {
117             a => 6378155.0,
118             rf => 298.3,
119             },
120             'fschr68' => {
121             a => 6378150.0,
122             rf => 298.3,
123             },
124             'helmert' => {
125             a => 6378200.0,
126             rf => 298.3,
127             },
128             'hough' => {
129             a => 6378270.0,
130             rf => 297.,
131             },
132             'intl' => {
133             a => 6378388.0,
134             rf => 297.,
135             },
136             'krass' => {
137             a => 6378245.0,
138             rf => 298.3,
139             },
140             'kaula' => {
141             a => 6378163.0,
142             rf => 298.24,
143             },
144             'lerch' => {
145             a => 6378139.0,
146             rf => 298.257,
147             },
148             'mprts' => {
149             a => 6397300.0,
150             rf => 191.,
151             },
152             'new_intl' => {
153             a => 6378157.5,
154             b => 6356772.2,
155             },
156             'plessis' => {
157             a => 6376523.0,
158             b => 6355863.0,
159             },
160             'SEasia' => {
161             a => 6378155.0,
162             b => 6356773.3205,
163             },
164             'walbeck' => {
165             a => 6376896.0,
166             b => 6355834.8467,
167             },
168             'WGS60' => {
169             a => 6378165.0,
170             rf => 298.3,
171             },
172             'WGS66' => {
173             a => 6378145.0,
174             rf => 298.25,
175             },
176             'WGS72' => {
177             a => 6378135.0,
178             rf => 298.26,
179             },
180             'WGS84' => {
181             a => 6378137.0,
182             rf => 298.257223563,
183             },
184             'sphere' => {
185             a => 6370997.0,
186             b => 6370997.0,
187             },
188             );
189              
190             sub dirdist2latlng {
191 2     2 1 3178 my ($flat,$flng,$dir,$dist,$opt) = @_;
192 2         52 my ($a,$f) = set_af($opt);
193              
194 2         10 return v2p_pp($f,$a,$rd,$flat,$flng,$dir,$dist);
195             }
196              
197             sub latlng2dirdist {
198 2     2 1 15772 my ($flat,$flng,$tlat,$tlng,$opt) = @_;
199 2         8 my ($a,$f) = set_af($opt);
200              
201 2         5 my ($dir, $dist) = p2v_pp($f,$a,$rd,map { $_ * $rd } ($flat,$flng,$tlat,$tlng));
  8         26  
202              
203 2   66     13 while ( $dir < 0 || $dir >= 360 ) {
204 1 50       9 $dir += $dir < 0 ? 360 : -360;
205             }
206              
207 2         8 return ($dir, $dist);
208             }
209              
210             sub set_af {
211 4   50 4 1 29 my $opt = shift || {};
212              
213 4         6 my ($a,$f);
214              
215 4 50       14 $opt = $ellps{$opt->{ellps}} if ($opt->{ellps});
216 4 50 0     91 $opt = $ellps{WGS84} if (!$opt->{a} || (!$opt->{b} && !$opt->{f} && !$opt->{rf}) );
      33        
217              
218 4         11 $a = $opt->{a};
219 4 50       24 $f = $opt->{b} ? ($a - $opt->{b}) / $a :
    50          
220             $opt->{rf} ? 1 / $opt->{rf} :
221             $opt->{f};
222              
223 4         10 return ($a,$f);
224             }
225              
226             # Engine for vector2point
227             sub v2p_pp{
228 2     2 1 5 my ($f,$a,$rd,$lat,$lng,$dir,$dis) = @_;
229 2         4 ($lat,$lng,$dir) = map{ $_ * $rd } ($lat,$lng,$dir); # Change to radian
  6         19  
230              
231 2         5 my $r = 1 - $f;
232 2         12 my $tu = $r * tan($lat);
233 2         34 my $sf = sin($dir);
234 2         6 my $cf = cos($dir);
235 2 50       18 my $b = ($cf == 0) ? 0.0 : 2.0 * atan2($tu,$cf);
236              
237 2         8 my $cu = 1.0 / sqrt(1 + $tu**2);
238 2         4 my $su = $tu * $cu;
239 2         3 my $sa = $cu * $sf;
240 2         6 my $c2a = 1 - $sa**2;
241 2         8 my $x = 1.0 + sqrt(1.0 + $c2a * (1.0/($r**2)-1.0));
242 2         4 $x = ($x - 2.0) / $x;
243              
244 2         53 my $c = 1.0 - $x;
245 2         6 $c = ($x**2 / 4.0 + 1.0) / $c;
246 2         5 my $d = (0.375 * $x**2 - 1.0)* $x;
247 2         4 $tu = $dis / ($r * $a * $c);
248 2         3 my $y = $tu;
249 2         4 $c = $y + 1;
250              
251 2         4 my ($sy,$cy,$cz,$e) = ();
252 2         8 while (abs($y - $c) > 0.00000000005) {
253 6         9 $sy = sin($y);
254 6         8 $cy = cos($y);
255 6         9 $cz = cos($b + $y);
256 6         12 $e = 2.0 * $cz**2 -1.0;
257 6         8 $c = $y;
258 6         9 $x = $e * $cy;
259 6         8 $y = $e + $e - 1;
260 6         36 $y = ((($sy**2 * 4.0 - 3.0) * $y * $cz * $d / 6.0 + $x) * $d / 4.0 - $cz) * $sy * $d + $tu;
261             }
262            
263 2         5 $b = $cu * $cy * $cf - $su * $sy;
264 2         5 $c = $r * sqrt($sa**2 + $b**2);
265 2         3 $d = $su * $cy + $cu * $sy * $cf;
266 2         4 my $rlat = atan2($d,$c);
267              
268 2         5 $c = $cu * $cy - $su * $sy * $cf;
269 2         3 $x = atan2($sy * $sf, $c);
270 2         12 $c = ((-3.0 * $c2a + 4.0) * $f + 4.0) * $c2a * $f / 16.0;
271 2         13 $d = (($e * $cy * $c + $cz) * $sy * $c + $y) * $sa;
272 2         4 my $rlon = $lng + $x - (1.0 - $c) * $d * $f;
273              
274 2         5 return map { $_/$rd } ($rlat,$rlon);
  4         15  
275             }
276              
277             # Engine for point2vector
278             sub p2v_pp {
279 2     2 1 5 my ($f,$a,$rd,$lat,$lng,$tlat,$tlng) = @_;
280              
281 2 50 33     10 return (180,0) if (($lat == $tlat) && ($lng == $tlng));
282              
283 2         6 my $e2 = 2*$f - $f*$f; # Square of Eccentricity
284 2         5 my $r = 1 - $f;
285              
286 2         11 my $tu1 = $r * tan($lat);
287 2         185 my $tu2 = $r * tan($tlat);
288              
289 2         43 my $cu1 = 1.0 / sqrt(1.0 + $tu1**2);
290 2         5 my $su1 = $cu1 * $tu1;
291 2         6 my $cu2 = 1.0 / sqrt(1.0 + $tu2**2);
292 2         4 my $s1 = $cu1 * $cu2;
293 2         3 my $b1 = $s1 * $tu2;
294 2         4 my $f1 = $b1 * $tu1;
295 2         4 my $x = $tlng - $lng;
296 2         2 my $d = $x + 1; # Force one pass
297              
298 2         4 my $iter =1;
299 2         5 my ($sx,$cx,$sy,$cy,$y,$sa,$c2a,$cz,$e,$c)=();
300              
301 2   66     16 while ((abs($d - $x) > 0.00000000005) && ($iter < 100)) {
302 8         10 $iter++;
303 8         8 $sx = sin($x);
304 8         34 $cx = cos($x);
305 8         9 $tu1 = $cu2 * $sx;
306 8         12 $tu2 = $b1 - $su1 * $cu2 * $cx;
307 8         14 $sy = sqrt($tu1**2 + $tu2**2);
308 8         9 $cy = $s1 * $cx + $f1;
309 8         22 $y = atan2($sy,$cy);
310 8         10 $sa = $s1 * $sx / $sy;
311 8         10 $c2a = 1 - $sa**2;
312 8         8 $cz = $f1 + $f1;
313              
314 8 50       21 if ($c2a > 0.0) {
315 8         9 $cz = $cy - $cz / $c2a;
316             }
317              
318 8         12 $e = $cz**2 * 2.0 - 1.0;
319 8         14 $c = ((-3.0 * $c2a + 4.0) * $f + 4.0) * $c2a * $f / 16.0;
320 8         9 $d = $x;
321 8         14 $x = (($e * $cy * $c + $cz) * $sy * $c + $y) * $sa;
322 8         32 $x = (1.0 - $c) * $x * $f + $tlng - $lng;
323             }
324              
325 2         10 my $dir = atan2($tu1,$tu2) / $rd;
326 2         18 $x = sqrt((1 / ($r**2) -1) * $c2a +1);
327 2         2 $x += 1;
328 2         5 $x = ($x - 2.0) / $x;
329 2         2 $c = 1.0 - $x;
330 2         5 $c = ($x**2 / 4.0 + 1.0) / $c;
331 2         4 $d = (0.375 * $x**2 - 1.0) * $x;
332 2         3 $x = $e * $cy;
333 2         9 my $dis = (((($sy**2 * 4.0 - 3.0) * (1.0 - $e - $e) * $cz * $d / 6.0 - $x) * $d / 4.0 + $cz) * $sy * $d + $y) * $c * $a * $r;
334            
335 2         6 return ($dir,$dis);
336             }
337              
338             1; # Magic true value required at end of module
339             __END__