File Coverage

blib/lib/Geo/Coordinates/Converter/Datum.pm
Criterion Covered Total %
statement 72 78 92.3
branch 6 12 50.0
condition 2 5 40.0
subroutine 12 15 80.0
pod 0 9 0.0
total 92 119 77.3


line stmt bran cond sub pod time code
1             package Geo::Coordinates::Converter::Datum;
2 6     6   8788 use strict;
  6         110  
  6         300  
3 6     6   30 use warnings;
  6         11  
  6         1332  
4              
5 6     6   35 use Carp;
  6         9  
  6         562  
6 6     6   41 use String::CamelCase qw( camelize );
  6         12  
  6         269  
7 6     6   34 use Module::Load ();
  6         8  
  6         427  
8              
9 6     6   33 use constant RADIAN => 4 * atan2(1, 1) / 180;
  6         9  
  6         7539  
10              
11 0     0 0 0 sub name { '' }
12 0     0 0 0 sub radius { 0 }
13 0     0 0 0 sub rate { 0 }
14 24     24 0 211 sub translation { +{ x => 0, y => 0, z => 0 } }
15              
16             sub new {
17 84     84 0 192 my($class, $args) = @_;
18 84 50       423 $args = +{} unless defined $args;
19 84         160 bless { %{ $args } }, $class;
  84         565  
20             }
21              
22             sub load_datum {
23 16     16 0 111 my($self, $datum) = @_;
24              
25 16 50       41 unless (ref $datum) {
26 16 50       49 if ($datum =~ s/^\+//) {
27 0         0 Module::Load::load($datum);
28             } else {
29 16         25 my $name = $datum;
30 16         58 $datum = sprintf '%s::%s', ref $self, camelize($name);
31 16         255 local $@;
32 16         35 eval { Module::Load::load($datum) };
  16         53  
33 16 50 33     660 if ($@ && ref $self ne __PACKAGE__) {
34 0         0 $datum = sprintf '%s::%s', __PACKAGE__, camelize($name);
35 0         0 Module::Load::load($datum);
36             }
37             }
38 16         125 $datum = $datum->new;
39             }
40 16         91 $self->{datums}->{$datum->name} = $datum;
41             }
42              
43             sub convert {
44 8     8 0 17 my($self, $point, $datum) = @_;
45              
46 8 50       59 $self->load_datum($point->datum) unless $self->{datums}->{$point->datum};
47 8 50       45 $self->load_datum($datum) unless $self->{datums}->{$datum};
48              
49 8         38 $self->{datums}->{$point->datum}->to_datum($point);
50 8         74 $self->{datums}->{$datum}->datum_from($point);
51              
52 8         23 $point;
53             }
54              
55             sub to_datum {
56 8     8 0 89 my($self, $point) = @_;
57              
58 8   50     32 my $height = $point->height || 0;
59              
60 8         82 my $lat_sin = sin($point->lat * RADIAN);
61 8         122 my $lat_cos = cos($point->lat * RADIAN);
62 8         109 my $radius_rate = $self->radius / sqrt(1 - $self->rate * $lat_sin * $lat_sin);
63              
64 8         23 my $xy_base = ($radius_rate + $height) * $lat_cos;
65 8         27 my $x = $xy_base * cos($point->lng * RADIAN);
66 8         60 my $y = $xy_base * sin($point->lng * RADIAN);
67 8         62 my $z = ($radius_rate * (1 - $self->rate) + $height) * $lat_sin;
68              
69 8         34 $point->lat($x + (-1 * $self->translation->{x}));
70 8         126 $point->lng($y + (-1 * $self->translation->{y}));
71 8         61 $point->height($z + (-1 * $self->translation->{z}));
72 8         67 $point->datum('datum');
73              
74 8         44 $point;
75             }
76              
77             sub datum_from {
78 8     8 0 19 my($self, $point) = @_;
79              
80 8         27 my $x = $point->lat + $self->translation->{x};
81 8         30 my $y = $point->lng + $self->translation->{y};
82 8         30 my $z = $point->height + $self->translation->{z};
83              
84 8         32 my $rate_sqrt = sqrt(1 - $self->rate);
85              
86 8         19 my $xy_sqrt = sqrt($x * $x + $y * $y);
87 8         64 my $atan_base = atan2($z, $xy_sqrt * $rate_sqrt);
88 8         15 my $atan_sin = sin($atan_base);
89 8         13 my $atan_cos = cos($atan_base);
90 8         35 my $lat = atan2($z + $self->rate * $self->radius / $rate_sqrt * $atan_sin * $atan_sin * $atan_sin,
91             $xy_sqrt - $self->rate * $self->radius * $atan_cos * $atan_cos * $atan_cos);
92 8         24 my $lng = atan2($y, $x);
93              
94 8         15 my $lat_sin = sin($lat);
95 8         27 my $radius_rate = $self->radius / sqrt(1 - $self->rate * ($lat_sin * $lat_sin));
96              
97 8         31 $point->height($xy_sqrt / cos($lat) - $radius_rate);
98 8         54 $point->lat($lat / RADIAN);
99 8         48 $point->lng($lng / RADIAN);
100 8         49 $point->datum($self->name);
101              
102 8         48 $point;
103             }
104              
105             1;
106              
107             __END__