| 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__ |