line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Geo::Coordinates::RDNAP; |
2
|
|
|
|
|
|
|
|
3
|
3
|
|
|
3
|
|
87324
|
use strict; |
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
465
|
|
4
|
3
|
|
|
3
|
|
22
|
use warnings; |
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
130
|
|
5
|
|
|
|
|
|
|
|
6
|
3
|
|
|
3
|
|
20
|
use vars qw($VERSION); |
|
3
|
|
|
|
|
9
|
|
|
3
|
|
|
|
|
200
|
|
7
|
|
|
|
|
|
|
$VERSION = '0.11'; |
8
|
|
|
|
|
|
|
|
9
|
3
|
|
|
3
|
|
19
|
use Carp; |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
467
|
|
10
|
3
|
|
|
3
|
|
4057
|
use Params::Validate qw/validate BOOLEAN SCALAR/; |
|
3
|
|
|
|
|
45009
|
|
|
3
|
|
|
|
|
253
|
|
11
|
|
|
|
|
|
|
|
12
|
3
|
|
|
3
|
|
30
|
use Exporter; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
102
|
|
13
|
3
|
|
|
3
|
|
17
|
use vars qw/@ISA @EXPORT_OK/; |
|
3
|
|
|
|
|
4
|
|
|
3
|
|
|
|
|
5582
|
|
14
|
|
|
|
|
|
|
@ISA = qw/Exporter/; |
15
|
|
|
|
|
|
|
@EXPORT_OK = qw/from_rd to_rd deg dms/; |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
sub deg { |
18
|
2
|
|
|
2
|
1
|
1681
|
my (@in) = @_; |
19
|
2
|
|
|
|
|
3
|
my @out; |
20
|
|
|
|
|
|
|
|
21
|
2
|
|
|
|
|
10
|
while (my($d, $m, $s) = splice (@in, 0, 3)) { |
22
|
3
|
|
50
|
|
|
19
|
push @out, $d + ($m||0)/60 + ($s||0)/3600; |
|
|
|
50
|
|
|
|
|
23
|
|
|
|
|
|
|
} |
24
|
2
|
|
|
|
|
5
|
return @out; |
25
|
|
|
|
|
|
|
} |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
sub dms { |
28
|
2
|
|
|
2
|
1
|
1136
|
return map {int($_), int($_*60)%60, ($_-int($_*60)/60)*3600} @_; |
|
3
|
|
|
|
|
18
|
|
29
|
|
|
|
|
|
|
} |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
my %a = ( |
32
|
|
|
|
|
|
|
'01' => 3236.0331637, |
33
|
|
|
|
|
|
|
20 => -32.5915821, |
34
|
|
|
|
|
|
|
'02' => -0.2472814, |
35
|
|
|
|
|
|
|
21 => -0.8501341, |
36
|
|
|
|
|
|
|
'03' => -0.0655238, |
37
|
|
|
|
|
|
|
22 => -0.0171137, |
38
|
|
|
|
|
|
|
40 => 0.0052771, |
39
|
|
|
|
|
|
|
23 => -0.0003859, |
40
|
|
|
|
|
|
|
41 => 0.0003314, |
41
|
|
|
|
|
|
|
'04' => 0.0000371, |
42
|
|
|
|
|
|
|
42 => 0.0000143, |
43
|
|
|
|
|
|
|
24 => -0.0000090, |
44
|
|
|
|
|
|
|
); |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
my %b = ( |
47
|
|
|
|
|
|
|
10 => 5261.3028966, |
48
|
|
|
|
|
|
|
11 => 105.9780241, |
49
|
|
|
|
|
|
|
12 => 2.4576469, |
50
|
|
|
|
|
|
|
30 => -0.8192156, |
51
|
|
|
|
|
|
|
31 => -0.0560092, |
52
|
|
|
|
|
|
|
13 => 0.0560089, |
53
|
|
|
|
|
|
|
32 => -0.0025614, |
54
|
|
|
|
|
|
|
14 => 0.0012770, |
55
|
|
|
|
|
|
|
50 => 0.0002574, |
56
|
|
|
|
|
|
|
33 => -0.0000973, |
57
|
|
|
|
|
|
|
51 => 0.0000293, |
58
|
|
|
|
|
|
|
15 => 0.0000291, |
59
|
|
|
|
|
|
|
); |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
my %c = ( |
62
|
|
|
|
|
|
|
'01' => 190066.98903, |
63
|
|
|
|
|
|
|
11 => -11830.85831, |
64
|
|
|
|
|
|
|
21 => -114.19754, |
65
|
|
|
|
|
|
|
'03' => -32.38360, |
66
|
|
|
|
|
|
|
31 => -2.34078, |
67
|
|
|
|
|
|
|
13 => -0.60639, |
68
|
|
|
|
|
|
|
23 => 0.15774, |
69
|
|
|
|
|
|
|
41 => -0.04158, |
70
|
|
|
|
|
|
|
'05' => -0.00661, |
71
|
|
|
|
|
|
|
); |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
my %d = ( |
74
|
|
|
|
|
|
|
10 => 309020.31810, |
75
|
|
|
|
|
|
|
'02' => 3638.36193, |
76
|
|
|
|
|
|
|
12 => -157.95222, |
77
|
|
|
|
|
|
|
20 => 72.97141, |
78
|
|
|
|
|
|
|
30 => 59.79734, |
79
|
|
|
|
|
|
|
22 => -6.43481, |
80
|
|
|
|
|
|
|
'04' => 0.09351, |
81
|
|
|
|
|
|
|
32 => -0.07379, |
82
|
|
|
|
|
|
|
14 => -0.05419, |
83
|
|
|
|
|
|
|
40 => -0.03444, |
84
|
|
|
|
|
|
|
); |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
my %bessel = ( |
87
|
|
|
|
|
|
|
a => 6377397.155, |
88
|
|
|
|
|
|
|
e2 => 6674372e-9, |
89
|
|
|
|
|
|
|
f_i => 299.1528128, |
90
|
|
|
|
|
|
|
); |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
my %etrs89 = ( |
93
|
|
|
|
|
|
|
a => 6378137, |
94
|
|
|
|
|
|
|
e2 => 6694380e-9, |
95
|
|
|
|
|
|
|
f_i => 298.257222101, |
96
|
|
|
|
|
|
|
); |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# Transformation parameters from Bessel to ETRS89 with respect to |
99
|
|
|
|
|
|
|
# Amersfoort. |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
my %b2e = ( |
102
|
|
|
|
|
|
|
tx => 593.032, |
103
|
|
|
|
|
|
|
ty => 26, |
104
|
|
|
|
|
|
|
tz => 478.741, |
105
|
|
|
|
|
|
|
a => 1.9848e-6, |
106
|
|
|
|
|
|
|
b => -1.7439e-6, |
107
|
|
|
|
|
|
|
c => 9.0587e-6, |
108
|
|
|
|
|
|
|
d => 4.0772e-6, |
109
|
|
|
|
|
|
|
); |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
my %e2b = map {$_ => -$b2e{$_}} keys %b2e; |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
my @amersfoort_b = ( 3903_453.148, 368_135.313, 5012_970.306 ); |
114
|
|
|
|
|
|
|
my @amersfoort_e = ( 3904_046.180, 368_161.313, 5013_449.047 ); |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
sub from_rd { |
117
|
4
|
50
|
33
|
4
|
1
|
3464
|
croak 'Geo::Coordinates::RDNAP::from_rd needs two or three arguments' |
118
|
|
|
|
|
|
|
if (@_ !=2 && @_ != 3); |
119
|
|
|
|
|
|
|
|
120
|
4
|
|
|
|
|
11
|
my ($x, $y, $h) = (@_, 0); |
121
|
|
|
|
|
|
|
|
122
|
4
|
50
|
33
|
|
|
27
|
croak "Geo::Coordinates::RDNAP::from_rd: X out of bounds: $x" |
123
|
|
|
|
|
|
|
if ($x < -7_000 or $x > 300_000); |
124
|
4
|
50
|
33
|
|
|
22
|
croak "Geo::Coordinates::RDNAP::from_rd: Y out of bounds: $y" |
125
|
|
|
|
|
|
|
if ($y < 289_000 or $y > 629_000); |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
# Use the approximated transformation. |
128
|
|
|
|
|
|
|
# Step 1: RD -> Bessel (spherical coords) |
129
|
|
|
|
|
|
|
|
130
|
4
|
|
|
|
|
9
|
$x = ($x/100_000) - 1.55; |
131
|
4
|
|
|
|
|
7
|
$y = ($y/100_000) - 4.63; |
132
|
|
|
|
|
|
|
|
133
|
4
|
|
|
|
|
6
|
my $lat = (52*60*60) + (9*60) + 22.178; |
134
|
4
|
|
|
|
|
6
|
my $lon = (5 *60*60) + (23*60) + 15.5; |
135
|
|
|
|
|
|
|
|
136
|
4
|
|
|
|
|
21
|
foreach my $i (keys %a) { |
137
|
48
|
|
|
|
|
112
|
my ($m, $n) = split //, $i; |
138
|
48
|
|
|
|
|
194
|
$lat += $a{$i} * ($x**$m) * ($y**$n); |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
4
|
|
|
|
|
18
|
foreach my $i (keys %b) { |
142
|
48
|
|
|
|
|
117
|
my ($m, $n) = split //, $i; |
143
|
48
|
|
|
|
|
297
|
$lon += $b{$i} * ($x**$m) * ($y**$n); |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
# Step 2: spherical coords -> X, Y, Z |
147
|
4
|
|
|
|
|
21
|
my @coords = _ellipsoid_to_cartesian($lat/3600, $lon/3600, $h, \%bessel); |
148
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
# Step 3: Bessel -> ETRS89 |
150
|
4
|
|
|
|
|
14
|
@coords = _transform_datum( @coords, \%b2e, \@amersfoort_b ); |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# Step 4: X, Y, Z -> spherical coords |
153
|
4
|
|
|
|
|
15
|
return _cartesian_to_ellipsoid(@coords, \%etrs89); |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
sub to_rd { |
157
|
4
|
50
|
33
|
4
|
1
|
3466
|
croak 'Geo::Coordinates::RDNAP::to_rd needs two or three arguments' |
158
|
|
|
|
|
|
|
if (@_ !=2 && @_ != 3); |
159
|
|
|
|
|
|
|
|
160
|
4
|
|
|
|
|
10
|
my ($lat, $lon, $h) = (@_, 0); |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
# Use the approximated transformation. |
163
|
|
|
|
|
|
|
# Step 1: spherical coords -> X, Y, Z |
164
|
4
|
|
|
|
|
14
|
my @coords = _ellipsoid_to_cartesian($lat, $lon, $h, \%etrs89); |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
# Step 2: ETRS89 -> Bessel |
167
|
4
|
|
|
|
|
16
|
@coords = _transform_datum( @coords, \%e2b, \@amersfoort_e ); |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
# Step 3: X, Y, Z -> spherical coords |
170
|
4
|
|
|
|
|
14
|
($lat, $lon, $h) = _cartesian_to_ellipsoid(@coords, \%bessel); |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
# Step 4: Bessel -> RD' |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
# Convert to units of 10_000 secs; as deltas from Amersfoort. |
175
|
4
|
|
|
|
|
10
|
$lat = ($lat * 3600 - ((52*60*60) + (9*60) + 22.178))/10_000; |
176
|
4
|
|
|
|
|
8
|
$lon = ($lon * 3600 - ((5 *60*60) + (23*60) + 15.5))/10_000; |
177
|
|
|
|
|
|
|
|
178
|
4
|
|
|
|
|
6
|
my $x = 155e3; |
179
|
4
|
|
|
|
|
5
|
my $y = 463e3; |
180
|
|
|
|
|
|
|
|
181
|
4
|
|
|
|
|
16
|
foreach my $i (keys %c) { |
182
|
36
|
|
|
|
|
90
|
my ($m, $n) = split //, $i; |
183
|
36
|
|
|
|
|
156
|
$x += $c{$i} * ($lat**$m) * ($lon**$n); |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
|
186
|
4
|
|
|
|
|
19
|
foreach my $i (keys %d) { |
187
|
40
|
|
|
|
|
92
|
my ($m, $n) = split //, $i; |
188
|
40
|
|
|
|
|
120
|
$y += $d{$i} * ($lat**$m) * ($lon**$n); |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
|
191
|
4
|
50
|
33
|
|
|
38
|
croak "Geo::Coordinates::RDNAP::to_rd: X out of bounds: $x" |
192
|
|
|
|
|
|
|
if ($x < -7_000 or $x > 300_000); |
193
|
4
|
50
|
33
|
|
|
23
|
croak "Geo::Coordinates::RDNAP::to_rd: Y out of bounds: $y" |
194
|
|
|
|
|
|
|
if ($y < 289_000 or $y > 629_000); |
195
|
|
|
|
|
|
|
|
196
|
4
|
|
|
|
|
17
|
return ($x, $y, $h); |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
sub _to_rads { |
200
|
32
|
|
|
32
|
|
202
|
return $_[0] * 2*3.14159_26535_89793 /360; |
201
|
|
|
|
|
|
|
} |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
sub _from_rads { |
204
|
16
|
|
|
16
|
|
59
|
return $_[0] / (2*3.14159_26535_89793) *360; |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
sub _ellipsoid_to_cartesian { |
208
|
8
|
|
|
8
|
|
17
|
my ($lat, $lon, $h, $para) = @_; |
209
|
|
|
|
|
|
|
|
210
|
8
|
|
|
|
|
21
|
my $sinphi = sin(_to_rads($lat)); |
211
|
8
|
|
|
|
|
16
|
my $cosphi = cos(_to_rads($lat)); |
212
|
8
|
|
|
|
|
37
|
my $n = $para->{a}/sqrt(1 - $para->{e2}*$sinphi*$sinphi); |
213
|
|
|
|
|
|
|
|
214
|
8
|
|
|
|
|
21
|
return (($n+$h)*$cosphi*cos(_to_rads($lon)), |
215
|
|
|
|
|
|
|
($n+$h)*$cosphi*sin(_to_rads($lon)), |
216
|
|
|
|
|
|
|
($n*(1-$para->{e2})+$h)*$sinphi ); |
217
|
|
|
|
|
|
|
} |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
# Returns (lat, lon, h) in degrees. |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
sub _cartesian_to_ellipsoid { |
222
|
8
|
|
|
8
|
|
14
|
my ($x, $y, $z, $para) = @_; |
223
|
|
|
|
|
|
|
|
224
|
8
|
|
|
|
|
69
|
my $lon = atan2($y, $x); |
225
|
|
|
|
|
|
|
|
226
|
8
|
|
|
|
|
19
|
my $r = sqrt($x*$x+$y*$y); |
227
|
8
|
|
|
|
|
10
|
my $phi = 0; |
228
|
8
|
|
|
|
|
21
|
my $n_sinphi = $z; |
229
|
8
|
|
|
|
|
12
|
my $n; |
230
|
|
|
|
|
|
|
my $oldphi; |
231
|
|
|
|
|
|
|
|
232
|
8
|
|
|
|
|
13
|
do { |
233
|
32
|
|
|
|
|
38
|
$oldphi = $phi; |
234
|
32
|
|
|
|
|
77
|
$phi = atan2($z + $para->{e2}*$n_sinphi, $r); |
235
|
32
|
|
|
|
|
67
|
my $sinphi = sin($phi); |
236
|
32
|
|
|
|
|
60
|
$n = $para->{a}/sqrt(1-$para->{e2}*$sinphi*$sinphi); |
237
|
32
|
|
|
|
|
98
|
$n_sinphi = $n*$sinphi; |
238
|
|
|
|
|
|
|
} while (abs($oldphi-$phi) > 1e-8); |
239
|
|
|
|
|
|
|
|
240
|
8
|
|
|
|
|
17
|
my $h = $r/cos($phi) - $n; |
241
|
|
|
|
|
|
|
|
242
|
8
|
|
|
|
|
22
|
return (_from_rads($phi), _from_rads($lon), $h); |
243
|
|
|
|
|
|
|
} |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
sub _transform_datum { |
246
|
8
|
|
|
8
|
|
36
|
my ($x, $y, $z, $t, $centre) = @_; |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
return ( |
249
|
8
|
|
|
|
|
94
|
$x + $t->{d}*($x-$centre->[0]) + $t->{c}*($y-$centre->[1]) |
250
|
|
|
|
|
|
|
- $t->{b}*($z-$centre->[2]) + $t->{tx}, |
251
|
|
|
|
|
|
|
$y - $t->{c}*($x-$centre->[0]) + $t->{d}*($y-$centre->[1]) |
252
|
|
|
|
|
|
|
+ $t->{a}*($z-$centre->[2]) + $t->{ty}, |
253
|
|
|
|
|
|
|
$z + $t->{b}*($x-$centre->[0]) - $t->{a}*($y-$centre->[1]) |
254
|
|
|
|
|
|
|
+ $t->{d}*($z-$centre->[2]) + $t->{tz} |
255
|
|
|
|
|
|
|
); |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
1; |
259
|
|
|
|
|
|
|
__END__ |