line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Geo::Forward; |
2
|
3
|
|
|
3
|
|
436656
|
use strict; |
|
3
|
|
|
|
|
9
|
|
|
3
|
|
|
|
|
143
|
|
3
|
3
|
|
|
3
|
|
20
|
use warnings; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
136
|
|
4
|
3
|
|
|
3
|
|
26
|
use base qw{Package::New}; |
|
3
|
|
|
|
|
11
|
|
|
3
|
|
|
|
|
3647
|
|
5
|
3
|
|
|
3
|
|
3163
|
use Geo::Constants 0.04 qw{PI}; |
|
3
|
|
|
|
|
836
|
|
|
3
|
|
|
|
|
209
|
|
6
|
3
|
|
|
3
|
|
1490
|
use Geo::Functions 0.03 qw{deg_rad rad_deg}; |
|
3
|
|
|
|
|
1859
|
|
|
3
|
|
|
|
|
196
|
|
7
|
3
|
|
|
3
|
|
2468
|
use Geo::Ellipsoids 0.09 qw{}; |
|
3
|
|
|
|
|
6405
|
|
|
3
|
|
|
|
|
2207
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
our $VERSION="0.14"; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 NAME |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
Geo::Forward - Calculate geographic location from lat, lon, distance, and heading. |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 SYNOPSIS |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
use Geo::Forward; |
18
|
|
|
|
|
|
|
my $obj = Geo::Forward->new(); # default "WGS84" |
19
|
|
|
|
|
|
|
my ($lat1,$lon1,$faz,$dist)=(38.871022, -77.055874, 62.888507083, 4565.6854); |
20
|
|
|
|
|
|
|
my ($lat2,$lon2,$baz) = $obj->forward($lat1,$lon1,$faz,$dist); |
21
|
|
|
|
|
|
|
print "Input Lat: $lat1 Lon: $lon1\n"; |
22
|
|
|
|
|
|
|
print "Input Forward Azimuth: $faz\n"; |
23
|
|
|
|
|
|
|
print "Input Distance: $dist\n"; |
24
|
|
|
|
|
|
|
print "Output Lat: $lat2 Lon: $lon2\n"; |
25
|
|
|
|
|
|
|
print "Output Back Azimuth: $baz\n"; |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
=head1 DESCRIPTION |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
This module is a pure Perl port of the NGS program in the public domain "forward" by Robert (Sid) Safford and Stephen J. Frakes. |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
=head1 CONSTRUCTOR |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
=head2 new |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
The new() constructor may be called with any parameter that is appropriate to the ellipsoid method which establishes the ellipsoid. |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
my $obj = Geo::Forward->new(); # default "WGS84" |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=head1 METHODS |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
=head2 initialize |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=cut |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub initialize { |
46
|
3
|
|
|
3
|
1
|
59
|
my $self = shift; |
47
|
3
|
|
50
|
|
|
19
|
my $param = shift || undef; |
48
|
3
|
|
|
|
|
14
|
$self->ellipsoid($param); |
49
|
|
|
|
|
|
|
} |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
=head2 ellipsoid |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
Method to set or retrieve the current ellipsoid object. The ellipsoid is a L object. |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
my $ellipsoid=$obj->ellipsoid; #Default is WGS84 |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
$obj->ellipsoid('Clarke 1866'); #Built in ellipsoids from Geo::Ellipsoids |
58
|
|
|
|
|
|
|
$obj->ellipsoid({a=>1}); #Custom Sphere 1 unit radius |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
=cut |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
sub ellipsoid { |
63
|
1012
|
|
|
1012
|
1
|
2693
|
my $self=shift; |
64
|
1012
|
100
|
|
|
|
2370
|
if (@_) { |
65
|
3
|
|
|
|
|
4
|
my $param=shift; |
66
|
3
|
|
|
|
|
19
|
$self->{'ellipsoid'}=Geo::Ellipsoids->new($param); |
67
|
|
|
|
|
|
|
} |
68
|
1012
|
|
|
|
|
2484
|
return $self->{'ellipsoid'}; |
69
|
|
|
|
|
|
|
} |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=head2 forward |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
This method is the user frontend to the mathematics. This interface will not change in future versions. |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
my ($lat2,$lon2,$baz) = $obj->forward($lat1,$lon1,$faz,$dist); |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
Note: Latitude and longitude units are signed decimal degrees. The distance units are based on the ellipsoid semi-major axis which is meters for WGS-84. The forward and backward azimuths units are signed degrees clockwise from North. |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=cut |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
sub forward { |
82
|
1007
|
|
|
1007
|
1
|
701974
|
my $self = shift; |
83
|
1007
|
|
|
|
|
1292
|
my $lat = shift; #degrees |
84
|
1007
|
|
|
|
|
1011
|
my $lon = shift; #degrees |
85
|
1007
|
|
|
|
|
990
|
my $heading = shift; #degrees |
86
|
1007
|
|
|
|
|
941
|
my $distance = shift; #meters (or the units of the semi-major axis) |
87
|
1007
|
|
|
|
|
2444
|
my ($lat2, $lon2, $baz)= $self->_dirct1(rad_deg($lat),rad_deg($lon),rad_deg($heading),$distance); |
88
|
1007
|
|
|
|
|
2814
|
return(deg_rad($lat2), deg_rad($lon2), deg_rad($baz)); |
89
|
|
|
|
|
|
|
} |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
sub _dirct1 { |
92
|
1007
|
|
|
1007
|
|
18094
|
my $self = shift; #provides A and F |
93
|
1007
|
|
|
|
|
1216
|
my $GLAT1 = shift; #radians |
94
|
1007
|
|
|
|
|
972
|
my $GLON1 = shift; #radians |
95
|
1007
|
|
|
|
|
938
|
my $FAZ = shift; #radians |
96
|
1007
|
|
|
|
|
1022
|
my $S = shift; #units of semi-major axis (default meters) |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
# SUBROUTINE DIRCT1(GLAT1,GLON1,GLAT2,GLON2,FAZ,BAZ,S) |
99
|
|
|
|
|
|
|
#C |
100
|
|
|
|
|
|
|
#C *** SOLUTION OF THE GEODETIC DIRECT PROBLEM AFTER T.VINCENTY |
101
|
|
|
|
|
|
|
#C *** MODIFIED RAINSFORD'S METHOD WITH HELMERT'S ELLIPTICAL TERMS |
102
|
|
|
|
|
|
|
#C *** EFFECTIVE IN ANY AZIMUTH AND AT ANY DISTANCE SHORT OF ANTIPODAL |
103
|
|
|
|
|
|
|
#C |
104
|
|
|
|
|
|
|
#C *** A IS THE SEMI-MAJOR AXIS OF THE REFERENCE ELLIPSOID |
105
|
|
|
|
|
|
|
#C *** F IS THE FLATTENING OF THE REFERENCE ELLIPSOID |
106
|
|
|
|
|
|
|
#C *** LATITUDES AND LONGITUDES IN RADIANS POSITIVE NORTH AND EAST |
107
|
|
|
|
|
|
|
#C *** AZIMUTHS IN RADIANS CLOCKWISE FROM NORTH |
108
|
|
|
|
|
|
|
#C *** GEODESIC DISTANCE S ASSUMED IN UNITS OF SEMI-MAJOR AXIS A |
109
|
|
|
|
|
|
|
#C |
110
|
|
|
|
|
|
|
#C *** PROGRAMMED FOR CDC-6600 BY LCDR L.PFEIFER NGS ROCKVILLE MD 20FEB75 |
111
|
|
|
|
|
|
|
#C *** MODIFIED FOR SYSTEM 360 BY JOHN G GERGEN NGS ROCKVILLE MD 750608 |
112
|
|
|
|
|
|
|
#C |
113
|
|
|
|
|
|
|
# IMPLICIT REAL*8 (A-H,O-Z) |
114
|
|
|
|
|
|
|
# COMMON/CONST/PI,RAD |
115
|
|
|
|
|
|
|
# COMMON/ELIPSOID/A,F |
116
|
1007
|
|
|
|
|
1833
|
my $ellipsoid=$self->ellipsoid; |
117
|
1007
|
|
|
|
|
2691
|
my $A=$ellipsoid->a; |
118
|
1007
|
|
|
|
|
5322
|
my $F=$ellipsoid->f; |
119
|
|
|
|
|
|
|
# DATA EPS/0.5D-13/ |
120
|
1007
|
|
|
|
|
6368
|
my $EPS=0.5E-13; |
121
|
|
|
|
|
|
|
# R=1.-F |
122
|
1007
|
|
|
|
|
1299
|
my $R=1.-$F; |
123
|
|
|
|
|
|
|
# TU=R*DSIN(GLAT1)/DCOS(GLAT1) |
124
|
1007
|
|
|
|
|
2080
|
my $TU=$R*sin($GLAT1)/cos($GLAT1); |
125
|
|
|
|
|
|
|
# SF=DSIN(FAZ) |
126
|
1007
|
|
|
|
|
1378
|
my $SF=sin($FAZ); |
127
|
|
|
|
|
|
|
# CF=DCOS(FAZ) |
128
|
1007
|
|
|
|
|
1170
|
my $CF=cos($FAZ); |
129
|
|
|
|
|
|
|
# BAZ=0. |
130
|
1007
|
|
|
|
|
989
|
my $BAZ=0.; |
131
|
|
|
|
|
|
|
# IF(CF.NE.0.) BAZ=DATAN2(TU,CF)*2. |
132
|
1007
|
50
|
|
|
|
3337
|
$BAZ=atan2($TU,$CF)*2. if ($CF != 0); |
133
|
|
|
|
|
|
|
# CU=1./DSQRT(TU*TU+1.) |
134
|
1007
|
|
|
|
|
1763
|
my $CU=1./sqrt($TU*$TU+1.); |
135
|
|
|
|
|
|
|
# SU=TU*CU |
136
|
1007
|
|
|
|
|
1173
|
my $SU=$TU*$CU; |
137
|
|
|
|
|
|
|
# SA=CU*SF |
138
|
1007
|
|
|
|
|
1103
|
my $SA=$CU*$SF; |
139
|
|
|
|
|
|
|
# C2A=-SA*SA+1. |
140
|
1007
|
|
|
|
|
1535
|
my $C2A=-$SA*$SA+1.; |
141
|
|
|
|
|
|
|
# X=DSQRT((1./R/R-1.)*C2A+1.)+1. |
142
|
1007
|
|
|
|
|
1924
|
my $X=sqrt((1./$R/$R-1.)*$C2A+1.)+1.; |
143
|
|
|
|
|
|
|
# X=(X-2.)/X |
144
|
1007
|
|
|
|
|
1504
|
$X=($X-2.)/$X; |
145
|
|
|
|
|
|
|
# C=1.-X |
146
|
1007
|
|
|
|
|
1253
|
my $C=1.-$X; |
147
|
|
|
|
|
|
|
# C=(X*X/4.+1)/C |
148
|
1007
|
|
|
|
|
1328
|
$C=($X*$X/4.+1)/$C; |
149
|
|
|
|
|
|
|
# D=(0.375D0*X*X-1.)*X |
150
|
1007
|
|
|
|
|
1525
|
my $D=(0.375*$X*$X-1.)*$X; |
151
|
|
|
|
|
|
|
# TU=S/R/A/C |
152
|
1007
|
|
|
|
|
1381
|
$TU=$S/$R/$A/$C; |
153
|
|
|
|
|
|
|
# Y=TU |
154
|
1007
|
|
|
|
|
1017
|
my $Y=$TU; |
155
|
|
|
|
|
|
|
# 100 SY=DSIN(Y) |
156
|
1007
|
|
|
|
|
1075
|
my ($SY, $CY, $CZ, $E); |
157
|
1007
|
|
|
|
|
956
|
do{ $SY=sin($Y); |
|
1020
|
|
|
|
|
1224
|
|
158
|
|
|
|
|
|
|
# CY=DCOS(Y) |
159
|
1020
|
|
|
|
|
988
|
$CY=cos($Y); |
160
|
|
|
|
|
|
|
# CZ=DCOS(BAZ+Y) |
161
|
1020
|
|
|
|
|
1471
|
$CZ=cos($BAZ+$Y); |
162
|
|
|
|
|
|
|
# E=CZ*CZ*2.-1. |
163
|
1020
|
|
|
|
|
1273
|
$E=$CZ*$CZ*2.-1.; |
164
|
|
|
|
|
|
|
# C=Y |
165
|
1020
|
|
|
|
|
1055
|
$C=$Y; |
166
|
|
|
|
|
|
|
# X=E*CY |
167
|
1020
|
|
|
|
|
1194
|
$X=$E*$CY; |
168
|
|
|
|
|
|
|
# Y=E+E-1. |
169
|
1020
|
|
|
|
|
1059
|
$Y=$E+$E-1.; |
170
|
|
|
|
|
|
|
# Y=(((SY*SY*4.-3.)*Y*CZ*D/6.+X)*D/4.-CZ)*SY*D+TU |
171
|
1020
|
|
|
|
|
4447
|
$Y=((($SY*$SY*4.-3.)*$Y*$CZ*$D/6.+$X)*$D/4.-$CZ)*$SY*$D+$TU; |
172
|
|
|
|
|
|
|
# IF(DABS(Y-C).GT.EPS)GO TO 100 |
173
|
|
|
|
|
|
|
} while (abs($Y-$C) > $EPS); |
174
|
|
|
|
|
|
|
# BAZ=CU*CY*CF-SU*SY |
175
|
1007
|
|
|
|
|
1449
|
$BAZ=$CU*$CY*$CF-$SU*$SY; |
176
|
|
|
|
|
|
|
# C=R*DSQRT(SA*SA+BAZ*BAZ) |
177
|
1007
|
|
|
|
|
1352
|
$C=$R*sqrt($SA*$SA+$BAZ*$BAZ); |
178
|
|
|
|
|
|
|
# D=SU*CY+CU*SY*CF |
179
|
1007
|
|
|
|
|
1356
|
$D=$SU*$CY+$CU*$SY*$CF; |
180
|
|
|
|
|
|
|
# GLAT2=DATAN2(D,C) |
181
|
1007
|
|
|
|
|
1657
|
my $GLAT2=atan2($D,$C); |
182
|
|
|
|
|
|
|
# C=CU*CY-SU*SY*CF |
183
|
1007
|
|
|
|
|
1337
|
$C=$CU*$CY-$SU*$SY*$CF; |
184
|
|
|
|
|
|
|
# X=DATAN2(SY*SF,C) |
185
|
1007
|
|
|
|
|
1155
|
$X=atan2($SY*$SF,$C); |
186
|
|
|
|
|
|
|
# C=((-3.*C2A+4.)*F+4.)*C2A*F/16. |
187
|
1007
|
|
|
|
|
1551
|
$C=((-3.*$C2A+4.)*$F+4.)*$C2A*$F/16.; |
188
|
|
|
|
|
|
|
# D=((E*CY*C+CZ)*SY*C+Y)*SA |
189
|
1007
|
|
|
|
|
1625
|
$D=(($E*$CY*$C+$CZ)*$SY*$C+$Y)*$SA; |
190
|
|
|
|
|
|
|
# GLON2=GLON1+X-(1.-C)*D*F |
191
|
1007
|
|
|
|
|
1668
|
my $GLON2=$GLON1+$X-(1.-$C)*$D*$F; |
192
|
|
|
|
|
|
|
# BAZ=DATAN2(SA,BAZ)+PI |
193
|
1007
|
|
|
|
|
2731
|
$BAZ=atan2($SA,$BAZ)+PI; |
194
|
|
|
|
|
|
|
# RETURN |
195
|
1007
|
|
|
|
|
4574
|
return $GLAT2, $GLON2, $BAZ; |
196
|
|
|
|
|
|
|
# END |
197
|
|
|
|
|
|
|
} |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
=head1 TODO |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
Add tests for more ellipsoids. |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=head1 BUGS |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
Please log on RT and email to the geo-perl email list as well as the author. |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
=head1 SUPPORT |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
DavisNetworks.com supports all Perl applications including this package. |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
=head1 LIMITS |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
No guarantees that Perl handles all of the double precision calculations in the same manner as Fortran. |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
=head1 AUTHOR |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
Michael R. Davis qw/perl michaelrdavis com/ |
218
|
|
|
|
|
|
|
CPAN ID: MRDVT |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
=head1 LICENSE |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
Copyright (c) 2011 Michael R. Davis (mrdvt92) |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
=head1 SEE ALSO |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
=head3 Similar Packages |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
L, L |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
=head2 Opposite Packages |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
L |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=head2 Building Blocks |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
L, L, L |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
=cut |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
1; |