File Coverage

blib/lib/Geo/Coordinates/KKJ.pm
Criterion Covered Total %
statement 107 107 100.0
branch 29 32 90.6
condition 3 6 50.0
subroutine 13 13 100.0
pod 8 8 100.0
total 160 166 96.3


line stmt bran cond sub pod time code
1             package Geo::Coordinates::KKJ;
2              
3 3     3   113474 use 5.008006;
  3         13  
  3         128  
4 3     3   18 use strict;
  3         8  
  3         126  
5 3     3   17 use warnings;
  3         10  
  3         96  
6 3     3   17 use Carp;
  3         6  
  3         310  
7 3     3   18258 use Math::Trig;
  3         95090  
  3         6513  
8              
9             require Exporter;
10              
11             our $VERSION = '0.01';
12              
13             our @ISA = qw(Exporter);
14              
15             our @EXPORT = qw(
16             KKJxy_to_WGS84lalo
17             WGS84lalo_to_KKJxy
18             KKJxy_to_KKJlalo
19             KKJlalo_to_KKJxy
20             KKJlalo_to_WGS84lalo
21             WGS84lalo_to_KKJlalo
22             KKJ_Zone_I
23             KKJ_Zone_Lo
24             );
25              
26             my $KKJ_ZONE_INFO = {
27             '0' => [ '18.0', '500_000.0' ],
28             '1' => [ '21.0', '1_500_000.0' ],
29             '2' => [ '24.0', '2_500_000.0' ],
30             '3' => [ '27.0', '3_500_000.0' ],
31             '4' => [ '30.0', '4_500_000.0' ],
32             '5' => [ '33.0', '5_500_000.0' ],
33             };
34              
35             sub KKJxy_to_WGS84lalo {
36 7 100   7 1 6450 croak 'Geo::Coordinates::KKJ::KKJxy_to_WGS84lalo needs two arguments'
37             if @_ != 2;
38              
39 6         16 my ( $x, $y ) = @_;
40 6         17 my ( $lat, $lon ) = KKJxy_to_KKJlalo( $x, $y );
41 6         16 my ( $wlat, $wlon ) = KKJlalo_to_WGS84lalo( $lat, $lon );
42              
43 6         31 return ( $wlat, $wlon );
44             }
45              
46             sub WGS84lalo_to_KKJxy {
47 7 100   7 1 6995 croak 'Geo::Coordinates::KKJ::WGS84lalo_to_KKJxy needs two arguments'
48             if @_ != 2;
49              
50 6         12 my ( $lat, $lon ) = @_;
51 6         15 my ( $kkjlat, $kkjlon ) = WGS84lalo_to_KKJlalo( $lat, $lon );
52              
53 6         17 my $zone = KKJ_Zone_Lo($kkjlon);
54              
55 6         19 my $LALO = {
56             'La' => $kkjlat,
57             'Lo' => $kkjlon,
58             };
59              
60 6         16 my $foo = KKJlalo_to_KKJxy( $LALO, $zone );
61              
62 6         30 return ( $foo->{'P'}, $foo->{'I'} );
63              
64             }
65              
66             sub KKJxy_to_KKJlalo {
67 13 100   13 1 5595 croak 'Geo::Coordinates::KKJ::KKJxy_to_KKJlalo needs two arguments'
68             if @_ != 2;
69              
70 12         23 my ( $p, $i ) = @_;
71              
72 12 50       53 croak "Wrong coordenates" if ( $p < $i );
73              
74 12         27 my $zone = KKJ_Zone_I($i);
75 12         20 my $LALO = {};
76              
77 12         36 my $min_la = deg2rad('59.0');
78 12         109 my $max_la = deg2rad('70.5');
79 12         84 my $min_lo = deg2rad('18.5');
80 12         83 my $max_lo = deg2rad('32.0');
81              
82             #Scan iteratively the target area, until find matching
83             #KKJ coordinate value. Area is defined with Hayford Ellipsoid.
84 12         90 for ( my $count = 1 ; $count < 35 ; $count++ ) {
85 408         474 my $delta_la = $max_la - $min_la;
86 408         567 my $delta_lo = $max_lo - $min_lo;
87              
88 408         948 $LALO->{'La'} = rad2deg( $min_la + 0.5 * $delta_la );
89 408         3070 $LALO->{'Lo'} = rad2deg( $min_lo + 0.5 * $delta_lo );
90              
91 408         2658 my $KKJt = KKJlalo_to_KKJxy( $LALO, $zone );
92              
93 408 100       782 if ( $KKJt->{'P'} < $p ) {
94 190         298 $min_la = $min_la + 0.45 * $delta_la;
95             }
96             else {
97 218         269 $max_la = $min_la + 0.55 * $delta_la;
98             }
99              
100 408 100       668 if ( $KKJt->{'I'} < $i ) {
101 204         609 $min_lo = $min_lo + 0.45 * $delta_lo;
102             }
103             else {
104 204         603 $max_lo = $min_lo + 0.55 * $delta_lo;
105             }
106             }
107              
108 12         51 return ( $LALO->{'La'}, $LALO->{'Lo'} );
109             }
110              
111             sub KKJlalo_to_KKJxy {
112 415 100   415 1 1653 croak 'Geo::Coordinates::KKJ::KKJlalo_to_KKJxy needs two arguments'
113             if @_ != 2;
114              
115 414         506 my ( $INP, $zone ) = @_;
116              
117 414         936 my $Lo = deg2rad( $INP->{'Lo'} ) - deg2rad( $KKJ_ZONE_INFO->{$zone}->[0] );
118              
119             # Hayford ellipsoid
120 414         4654 my $a = '6378388.0';
121 414         394 my $f = 1 / 297.0;
122              
123 414         680 my $b = ( 1.0 - $f ) * $a;
124 414         462 my $bb = $b * $b;
125 414         450 my $c = ( $a / $b ) * $a;
126 414         458 my $ee = ( $a * $a - $bb ) / $bb;
127 414         559 my $n = ( $a - $b ) / ( $a + $b );
128 414         511 my $nn = $n * $n;
129              
130 414         892 my $cosLa = cos( deg2rad( $INP->{'La'} ) );
131              
132 414         2611 my $NN = $ee * $cosLa * $cosLa;
133              
134 414         884 my $LaF =
135             Math::Trig::atan( Math::Trig::tan( deg2rad( $INP->{'La'} ) ) /
136             cos( $Lo * sqrt( 1 + $NN ) ) );
137              
138 414         7231 my $cosLaF = cos($LaF);
139              
140 414         835 my $t =
141             ( Math::Trig::tan($Lo) * $cosLaF ) / sqrt( 1 + $ee * $cosLaF * $cosLaF );
142              
143 414         4126 my $A = $a / ( 1 + $n );
144 414         699 my $A1 = $A * ( 1 + $nn / 4 + $nn * $nn / 64 );
145 414         545 my $A2 = $A * 1.5 * $n * ( 1 - $nn / 8 );
146 414         507 my $A3 = $A * 0.9375 * $nn * ( 1 - $nn / 4 );
147 414         514 my $A4 = $A * 35 / 48.0 * $nn * $n;
148              
149 414         556 my $OUT = {};
150              
151 414         1261 $OUT->{'P'} =
152             $A1 * $LaF -
153             $A2 * sin( 2 * $LaF ) +
154             $A3 * sin( 4 * $LaF ) -
155             $A4 * sin( 6 * $LaF );
156              
157 414         977 $OUT->{'I'} =
158             $c * log( $t + sqrt( 1 + $t * $t ) ) + 500_000.0 + $zone * 1_000_000.0;
159              
160 414         843 return $OUT;
161             }
162              
163             sub KKJlalo_to_WGS84lalo {
164 13 100   13 1 8244 croak 'Geo::Coordinates::KKJ::KKJlalo_to_WGS84lalo needs two arguments'
165             if @_ != 2;
166              
167 12         20 my ( $La, $Lo ) = @_;
168              
169 12         58 my $d_la =
170             deg2rad( 0.124867E+01 +
171             -0.269982E+00 * $La +
172             0.191330E+00 * $Lo +
173             0.356119E-02 * $La * $La +
174             -0.122312E-02 * $La * $Lo +
175             -0.335514E-03 * $Lo * $Lo ) / 3600.0;
176              
177 12         120 my $d_lo =
178             deg2rad( -0.286111E+02 +
179             0.114183E+01 * $La +
180             -0.581428E+00 * $Lo +
181             -0.152421E-01 * $La * $La +
182             0.118177E-01 * $La * $Lo +
183             0.826646E-03 * $Lo * $Lo ) / 3600.0;
184              
185 12         83 my $WGS = {};
186 12         39 $WGS->{'La'} = rad2deg( deg2rad($La) + $d_la );
187 12         152 $WGS->{'Lo'} = rad2deg( deg2rad($Lo) + $d_lo );
188              
189 12         160 return ( $WGS->{'La'}, $WGS->{'Lo'} );
190             }
191              
192             sub WGS84lalo_to_KKJlalo {
193 13 100   13 1 7627 croak 'Geo::Coordinates::KKJ::WGS84lalo_to_KKJlalo needs two arguments'
194             if @_ != 2;
195              
196 12         18 my ( $La, $Lo ) = @_;
197 12 50       30 croak "Wrong parameters"
198             if ( $La < $Lo );
199              
200 12         57 my $d_la =
201             deg2rad( -0.124766E+01 +
202             0.269941E+00 * $La +
203             -0.191342E+00 * $Lo +
204             -0.356086E-02 * $La * $La +
205             0.122353E-02 * $La * $Lo +
206             0.335456E-03 * $Lo * $Lo ) / 3600.0;
207              
208 12         174 my $d_lo =
209             deg2rad( 0.286008E+02 +
210             -0.114139E+01 * $La +
211             0.581329E+00 * $Lo +
212             0.152376E-01 * $La * $La +
213             -0.118166E-01 * $La * $Lo +
214             -0.826201E-03 * $Lo * $Lo ) / 3600.0;
215              
216 12         92 my $KKJ = {};
217 12         28 $KKJ->{'La'} = rad2deg( deg2rad($La) + $d_la );
218 12         160 $KKJ->{'Lo'} = rad2deg( deg2rad($Lo) + $d_lo );
219              
220 12         160 return ( $KKJ->{'La'}, $KKJ->{'Lo'} );
221             }
222              
223             sub KKJ_Zone_I {
224 13 100   13 1 1118 croak 'Geo::Coordinates::KKJ::KKJ_Zone_I needs one argument'
225             if @_ != 1;
226              
227 12         20 my $i = shift;
228 12         26 my $zone = int( $i / 1_000_000.0 );
229 12 50 33     73 croak "Zone value ($zone) invalid."
230             if ( $zone < 0 || $zone > 5 );
231 12         23 return $zone;
232             }
233              
234             sub KKJ_Zone_Lo {
235 13 100   13 1 5606 croak 'Geo::Coordinates::KKJ::KKJ_Zone_Lo needs one argument'
236             if @_ != 1;
237              
238 10         17 my $Lo = shift;
239              
240 10 100       37 croak 'Longitude is undefined' unless defined($Lo);
241              
242 9         12 my $zone = 5;
243 9         30 while ( $zone >= 0 ) {
244 34 100       115 if ( abs( $Lo - $KKJ_ZONE_INFO->{$zone}->[0] ) <= 1.5 ) {
245 8         27 last;
246             }
247 26         55 $zone--;
248             }
249              
250 9 100 66     57 if ( $zone >= 0 && $zone <= 5 ) {
251 8         25 return $zone;
252             }
253             else {
254 1         30 croak "Zone outside range";
255             }
256             }
257              
258             1;
259              
260             __END__