line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# Geo::Ellipsoid |
2
|
|
|
|
|
|
|
# |
3
|
|
|
|
|
|
|
# This package implements an Ellipsoid class to perform latitude |
4
|
|
|
|
|
|
|
# and longitude calculations on the surface of an ellipsoid. |
5
|
|
|
|
|
|
|
# |
6
|
|
|
|
|
|
|
# This is a Perl conversion of existing Fortran code (see |
7
|
|
|
|
|
|
|
# ACKNOWLEDGEMENTS) and the author of this class makes no |
8
|
|
|
|
|
|
|
# claims of originality. Nor can he even vouch for the |
9
|
|
|
|
|
|
|
# results of the calculations, although they do seem to |
10
|
|
|
|
|
|
|
# work for him and have been tested against other methods. |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
package Geo::Ellipsoid; |
13
|
|
|
|
|
|
|
|
14
|
23
|
|
|
23
|
|
1680184
|
use warnings; |
|
23
|
|
|
|
|
246
|
|
|
23
|
|
|
|
|
760
|
|
15
|
23
|
|
|
23
|
|
125
|
use strict; |
|
23
|
|
|
|
|
53
|
|
|
23
|
|
|
|
|
459
|
|
16
|
23
|
|
|
23
|
|
475
|
use 5.006_00; |
|
23
|
|
|
|
|
99
|
|
17
|
|
|
|
|
|
|
|
18
|
23
|
|
|
23
|
|
153
|
use Scalar::Util 'looks_like_number'; |
|
23
|
|
|
|
|
53
|
|
|
23
|
|
|
|
|
1403
|
|
19
|
23
|
|
|
23
|
|
13227
|
use Math::Trig 1.23; |
|
23
|
|
|
|
|
372834
|
|
|
23
|
|
|
|
|
3203
|
|
20
|
23
|
|
|
23
|
|
207
|
use Carp; |
|
23
|
|
|
|
|
46
|
|
|
23
|
|
|
|
|
111403
|
|
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=pod |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=head1 NAME |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
Geo::Ellipsoid - longitude and latitude calculations using an ellipsoid model |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
=head1 VERSION |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
Version 1.15. |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
=cut |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
our $VERSION = '1.15'; |
35
|
|
|
|
|
|
|
our $DEBUG = 0; |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
=pod |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=head1 SYNOPSIS |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
use Geo::Ellipsoid; |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
my $geo = Geo::Ellipsoid->new(ellipsoid => 'NAD27', |
44
|
|
|
|
|
|
|
angle_unit => 'degrees'); |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
my @origin = ( 37.619002, -122.374843 ); # SFO |
47
|
|
|
|
|
|
|
my @dest = ( 33.942536, -118.408074 ); # LAX |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# range and bearing from one location to another |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
my ( $range, $bearing ) = $geo->to( @origin, @dest ); |
52
|
|
|
|
|
|
|
$range = $geo->range( @origin, @dest ); |
53
|
|
|
|
|
|
|
$bearing = $geo->bearing( @origin, @dest ); |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
# destination given start point, range, and bearing |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
my ( $lat, $lon ) = $geo->at( @origin, 2000, 45.0 ); |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# approximate displacement given one location and a destination |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
my ( $x, $y ) = $geo->displacement( @origin, @dest ); |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
# approximate location given one location and displacement |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
my @pos = $geo->location( $lat, $lon, $x, $y ); |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
=head1 ABSTRACT |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
Accurate latitude/longitude calculations. |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=head1 DESCRIPTION |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
Geo::Ellipsoid performs geometrical calculations on the surface of |
74
|
|
|
|
|
|
|
an ellipsoid. An ellipsoid is a three-dimension object formed from |
75
|
|
|
|
|
|
|
the rotation of an ellipse about one of its axes. The approximate |
76
|
|
|
|
|
|
|
shape of the earth is an ellipsoid, so Geo::Ellipsoid can accurately |
77
|
|
|
|
|
|
|
calculate distance and bearing between two widely-separated locations |
78
|
|
|
|
|
|
|
on the earth's surface. |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
The shape of an ellipsoid is defined by the lengths of its |
81
|
|
|
|
|
|
|
semi-major and semi-minor axes. The shape may also be specified by |
82
|
|
|
|
|
|
|
the flattening ratio C as: |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
f = ( semi-major - semi-minor ) / semi-major |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
which, since f is a small number, is normally given as the reciprocal |
87
|
|
|
|
|
|
|
of the flattening C<1/f>. |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
The shape of the earth has been surveyed and estimated differently |
90
|
|
|
|
|
|
|
at different times over the years. The two most common sets of values |
91
|
|
|
|
|
|
|
used to describe the size and shape of the earth in the United States |
92
|
|
|
|
|
|
|
are 'NAD27', dating from 1927, and 'WGS84', from 1984. United States |
93
|
|
|
|
|
|
|
Geological Survey topographical maps, for example, use one or the |
94
|
|
|
|
|
|
|
other of these values, and commonly-available Global Positioning |
95
|
|
|
|
|
|
|
System (GPS) units can be set to use one or the other. |
96
|
|
|
|
|
|
|
See L<"DEFINED ELLIPSOIDS"> below for the ellipsoid survey values |
97
|
|
|
|
|
|
|
that may be selected for use by Geo::Ellipsoid. |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=cut |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
# class data and constants |
102
|
|
|
|
|
|
|
our $degrees_per_radian = 180/pi; |
103
|
|
|
|
|
|
|
our $eps = 1.0e-23; |
104
|
|
|
|
|
|
|
our $max_loop_count = 20; |
105
|
|
|
|
|
|
|
our $twopi = 2 * pi; |
106
|
|
|
|
|
|
|
our $halfpi = pi/2; |
107
|
|
|
|
|
|
|
our %defaults = ( |
108
|
|
|
|
|
|
|
ellipsoid => 'WGS84', |
109
|
|
|
|
|
|
|
angle_unit => 'radians', |
110
|
|
|
|
|
|
|
distance_unit => 'meter', |
111
|
|
|
|
|
|
|
longitude_symmetric => 0, |
112
|
|
|
|
|
|
|
latitude_symmetric => 1, # allows use of _normalize_output |
113
|
|
|
|
|
|
|
bearing_symmetric => 0, |
114
|
|
|
|
|
|
|
); |
115
|
|
|
|
|
|
|
our %distance = ( |
116
|
|
|
|
|
|
|
'foot' => 0.3048, |
117
|
|
|
|
|
|
|
'kilometer' => 1_000, |
118
|
|
|
|
|
|
|
'meter' => 1.0, |
119
|
|
|
|
|
|
|
'mile' => 1_609.344, |
120
|
|
|
|
|
|
|
'nm' => 1_852, |
121
|
|
|
|
|
|
|
); |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
# set of ellipsoids that can be used. |
124
|
|
|
|
|
|
|
# values are |
125
|
|
|
|
|
|
|
# 1) a = semi-major (equatorial) radius of Ellipsoid |
126
|
|
|
|
|
|
|
# 2) 1/f = reciprocal of flattening (f), the ratio of the semi-minor |
127
|
|
|
|
|
|
|
# (polar) radius to the semi-major (equatorial) axis, or |
128
|
|
|
|
|
|
|
# polar radius = equatorial radius * ( 1 - f ) |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
our %ellipsoids = ( |
131
|
|
|
|
|
|
|
'AIRY' => [ 6377563.396, 299.3249646 ], |
132
|
|
|
|
|
|
|
'AIRY-MODIFIED' => [ 6377340.189, 299.3249646 ], |
133
|
|
|
|
|
|
|
'AUSTRALIAN' => [ 6378160.0, 298.25 ], |
134
|
|
|
|
|
|
|
'BESSEL-1841' => [ 6377397.155, 299.1528128 ], |
135
|
|
|
|
|
|
|
'CLARKE-1880' => [ 6378249.145, 293.465 ], |
136
|
|
|
|
|
|
|
'EVEREST-1830' => [ 6377276.345, 300.8017 ], |
137
|
|
|
|
|
|
|
'EVEREST-MODIFIED' => [ 6377304.063, 300.8017 ], |
138
|
|
|
|
|
|
|
'FISHER-1960' => [ 6378166.0, 298.3 ], |
139
|
|
|
|
|
|
|
'FISHER-1968' => [ 6378150.0, 298.3 ], |
140
|
|
|
|
|
|
|
'GRS80' => [ 6378137.0, 298.25722210088 ], |
141
|
|
|
|
|
|
|
'HOUGH-1956' => [ 6378270.0, 297.0 ], |
142
|
|
|
|
|
|
|
'HAYFORD' => [ 6378388.0, 297.0 ], |
143
|
|
|
|
|
|
|
'IAU76' => [ 6378140.0, 298.257 ], |
144
|
|
|
|
|
|
|
'KRASSOVSKY-1938' => [ 6378245.0, 298.3 ], |
145
|
|
|
|
|
|
|
'NAD27' => [ 6378206.4, 294.9786982138 ], |
146
|
|
|
|
|
|
|
'NWL-9D' => [ 6378145.0, 298.25 ], |
147
|
|
|
|
|
|
|
'SOUTHAMERICAN-1969' => [ 6378160.0, 298.25 ], |
148
|
|
|
|
|
|
|
'SOVIET-1985' => [ 6378136.0, 298.257 ], |
149
|
|
|
|
|
|
|
'WGS72' => [ 6378135.0, 298.26 ], |
150
|
|
|
|
|
|
|
'WGS84' => [ 6378137.0, 298.257223563 ], |
151
|
|
|
|
|
|
|
); |
152
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
=pod |
154
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
=head1 CONSTRUCTOR |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
=over |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=item new |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
The new() constructor may be called with a hash list to set the value of the |
162
|
|
|
|
|
|
|
ellipsoid to be used, the value of the units to be used for angles and |
163
|
|
|
|
|
|
|
distances, and whether or not the output range of longitudes and bearing |
164
|
|
|
|
|
|
|
angles should be symmetric around zero or always greater than zero. |
165
|
|
|
|
|
|
|
The initial default constructor is equivalent to the following: |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
my $geo = Geo::Ellipsoid->new( |
168
|
|
|
|
|
|
|
ellipsoid => 'WGS84', |
169
|
|
|
|
|
|
|
angle_unit => 'radians' , |
170
|
|
|
|
|
|
|
distance_unit => 'meter', |
171
|
|
|
|
|
|
|
longitude_symmetric => 0, |
172
|
|
|
|
|
|
|
bearing_symmetric => 0, |
173
|
|
|
|
|
|
|
); |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
The constructor arguments may be of any case and, with the exception of |
176
|
|
|
|
|
|
|
the ellipsoid value, abbreviated to their first three characters. |
177
|
|
|
|
|
|
|
Thus, ( UNI => 'DEG', DIS => 'FEE', Lon => 1, ell => 'NAD27', bEA => 0 ) |
178
|
|
|
|
|
|
|
is valid. |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=cut |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
sub new |
183
|
|
|
|
|
|
|
{ |
184
|
231
|
|
|
231
|
1
|
183878
|
my( $class, %args ) = @_; |
185
|
231
|
|
|
|
|
1152
|
my $self = {%defaults}; |
186
|
231
|
50
|
|
|
|
699
|
print "new: @_\n" if $DEBUG; |
187
|
231
|
|
|
|
|
780
|
while (my ($key, $val) = each %args) { |
188
|
148
|
100
|
|
|
|
770
|
if( $key =~ /^ell/i ) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
189
|
105
|
|
|
|
|
457
|
$self->{ellipsoid} = uc $val; |
190
|
|
|
|
|
|
|
}elsif( $key =~ /^(uni|ang)/i ) { |
191
|
21
|
|
|
|
|
94
|
$self->{angle_unit} = $val; |
192
|
|
|
|
|
|
|
}elsif( $key =~ /^dis/i ) { |
193
|
12
|
|
|
|
|
45
|
$self->{distance_unit} = $val; |
194
|
|
|
|
|
|
|
}elsif( $key =~ /^lon/i ) { |
195
|
6
|
|
|
|
|
29
|
$self->{longitude_symmetric} = $val; |
196
|
|
|
|
|
|
|
}elsif( $key =~ /^bea/i ) { |
197
|
4
|
|
|
|
|
34
|
$self->{bearing_symmetric} = $val; |
198
|
|
|
|
|
|
|
}else{ |
199
|
0
|
|
|
|
|
0
|
carp("Unknown argument: $key => $val"); |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
} |
202
|
231
|
|
|
|
|
470
|
bless $self, $class; |
203
|
231
|
|
|
|
|
922
|
$self->set_ellipsoid($self->{ellipsoid}); |
204
|
231
|
|
|
|
|
671
|
$self->set_units($self->{angle_unit}); |
205
|
231
|
|
|
|
|
558
|
$self->set_distance_unit($self->{distance_unit}); |
206
|
231
|
|
|
|
|
626
|
$self->set_longitude_symmetric($self->{longitude_symmetric}); |
207
|
231
|
|
|
|
|
658
|
$self->set_bearing_symmetric($self->{bearing_symmetric}); |
208
|
231
|
50
|
|
|
|
461
|
print |
209
|
|
|
|
|
|
|
"Ellipsoid(angle_unit=>$self->{angle_unit},distance_unit=>" . |
210
|
|
|
|
|
|
|
"$self->{distance_unit},ellipsoid=>$self->{ellipsoid}," . |
211
|
|
|
|
|
|
|
"longitude_symmetric=>$self->{longitude_symmetric},bearing_symmetric=>$self->{bearing_symmetric})\n" if $DEBUG; |
212
|
231
|
|
|
|
|
705
|
return $self; |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
=pod |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=back |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
=head1 CLASS METHODS |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
=over |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
=item get_ellipsoids |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
Returns a list with the names all known ellipsoids. |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=back |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
=cut |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
sub get_ellipsoids { |
232
|
1
|
|
|
1
|
1
|
695
|
sort keys %ellipsoids; |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
=pod |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
=head1 INSTANCE METHODS |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
=head2 Setters |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
=over |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
=item set_angle_unit |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
=item set_units |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
Set the angle unit used by the Geo::Ellipsoid object. The unit may |
248
|
|
|
|
|
|
|
also be set in the constructor of the object. The allowable values are |
249
|
|
|
|
|
|
|
'degrees' or 'radians'. The default is 'radians'. The unit value is |
250
|
|
|
|
|
|
|
not case sensitive and may be abbreviated to 3 letters. The unit of |
251
|
|
|
|
|
|
|
angle apply to both input and output latitude, longitude, and bearing |
252
|
|
|
|
|
|
|
values. |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
$geo->set_angle_unit('degrees'); |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
=cut |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
sub set_angle_unit |
259
|
|
|
|
|
|
|
{ |
260
|
280
|
|
|
280
|
1
|
1114
|
my $self = shift; |
261
|
280
|
|
|
|
|
390
|
my $unit = shift; |
262
|
280
|
100
|
|
|
|
1150
|
if( $unit =~ /deg/i ) { |
|
|
50
|
|
|
|
|
|
263
|
62
|
|
|
|
|
107
|
$unit = 'degrees'; |
264
|
|
|
|
|
|
|
}elsif( $unit =~ /rad/i ) { |
265
|
218
|
|
|
|
|
356
|
$unit = 'radians'; |
266
|
|
|
|
|
|
|
}else{ |
267
|
0
|
0
|
|
|
|
0
|
croak("Invalid unit specifier '$unit' - please use either " . |
268
|
|
|
|
|
|
|
"degrees or radians (the default)") unless $unit =~ /rad/i; |
269
|
|
|
|
|
|
|
} |
270
|
280
|
|
|
|
|
498
|
$self->{angle_unit} = $unit; |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
*set_units = \&set_angle_unit; |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
=pod |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
=item set_distance_unit |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
Set the distance unit used by the Geo::Ellipsoid object. The unit of distance |
280
|
|
|
|
|
|
|
may also be set in the constructor of the object. The recognized values are |
281
|
|
|
|
|
|
|
'meter', 'kilometer', 'mile', 'nm' (nautical mile), or 'foot'. The default is |
282
|
|
|
|
|
|
|
'meter'. The value is not case sensitive and may be abbreviated to 3 letters. |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
$geo->set_distance_unit('kilometer'); |
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
For any other unit of distance not recognized by this method, pass a numerical |
287
|
|
|
|
|
|
|
argument representing the length of the distance unit in meters. For example, |
288
|
|
|
|
|
|
|
to use units of furlongs, call |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
$geo->set_distance_unit(201.168); |
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
The distance conversion factors used by this module are as follows: |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
Unit Units per meter |
295
|
|
|
|
|
|
|
-------- --------------- |
296
|
|
|
|
|
|
|
foot 0.3048 |
297
|
|
|
|
|
|
|
kilometer 1000.0 |
298
|
|
|
|
|
|
|
mile 1609.344 |
299
|
|
|
|
|
|
|
nm 1852.0 |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
=cut |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
sub set_distance_unit |
304
|
|
|
|
|
|
|
{ |
305
|
239
|
|
|
239
|
1
|
350
|
my $self = shift; |
306
|
239
|
|
|
|
|
685
|
my $unit = shift; |
307
|
239
|
50
|
|
|
|
469
|
print "distance unit = $unit\n" if $DEBUG; |
308
|
|
|
|
|
|
|
|
309
|
239
|
|
|
|
|
367
|
my $conversion = 0; |
310
|
|
|
|
|
|
|
|
311
|
239
|
50
|
|
|
|
413
|
if( defined $unit ) { |
312
|
|
|
|
|
|
|
|
313
|
239
|
|
|
|
|
369
|
my( $key, $val ); |
314
|
239
|
|
|
|
|
748
|
while( ($key,$val) = each %distance ) { |
315
|
816
|
|
|
|
|
1734
|
my $re = substr($key,0,3); |
316
|
816
|
50
|
|
|
|
1397
|
print "trying ($key,$re,$val)\n" if $DEBUG; |
317
|
816
|
100
|
|
|
|
6680
|
if( $unit =~ /^$re/i ) { |
318
|
239
|
|
|
|
|
483
|
$self->{distance_unit} = $unit; |
319
|
239
|
|
|
|
|
351
|
$conversion = $val; |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
# finish iterating to reset 'each' function call |
322
|
239
|
|
|
|
|
939
|
while( each %distance ) {} |
323
|
239
|
|
|
|
|
479
|
last; |
324
|
|
|
|
|
|
|
} |
325
|
|
|
|
|
|
|
} |
326
|
|
|
|
|
|
|
|
327
|
239
|
50
|
|
|
|
660
|
if( $conversion == 0 ) { |
328
|
0
|
0
|
|
|
|
0
|
if( looks_like_number($unit) ) { |
329
|
0
|
|
|
|
|
0
|
$conversion = $unit; |
330
|
|
|
|
|
|
|
}else{ |
331
|
0
|
|
|
|
|
0
|
carp("Unknown argument to set_distance_unit: $unit\nAssuming meters"); |
332
|
0
|
|
|
|
|
0
|
$conversion = 1.0; |
333
|
|
|
|
|
|
|
} |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
}else{ |
336
|
0
|
|
|
|
|
0
|
carp("Missing or undefined argument to set_distance_unit: ". |
337
|
|
|
|
|
|
|
"$unit\nAssuming meters"); |
338
|
0
|
|
|
|
|
0
|
$conversion = 1.0; |
339
|
|
|
|
|
|
|
} |
340
|
239
|
|
|
|
|
512
|
$self->{conversion} = $conversion; |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
|
343
|
|
|
|
|
|
|
=pod |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
=item set_ellipsoid |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
Set the ellipsoid to be used by the Geo::Ellipsoid object. See |
348
|
|
|
|
|
|
|
L<"DEFINED ELLIPSOIDS"> below for the allowable values. The value |
349
|
|
|
|
|
|
|
may also be set by the constructor. The default value is 'WGS84'. |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
$geo->set_ellipsoid('NAD27'); |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
=cut |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
sub set_ellipsoid |
356
|
|
|
|
|
|
|
{ |
357
|
314
|
|
|
314
|
1
|
660
|
my $self = shift; |
358
|
314
|
|
33
|
|
|
875
|
my $ellipsoid = uc shift || $defaults{ellipsoid}; |
359
|
314
|
50
|
|
|
|
623
|
print " set ellipsoid to $ellipsoid\n" if $DEBUG; |
360
|
314
|
50
|
|
|
|
733
|
unless( exists $ellipsoids{$ellipsoid} ) { |
361
|
0
|
|
|
|
|
0
|
croak("Ellipsoid $ellipsoid does not exist - please use " . |
362
|
|
|
|
|
|
|
"set_custom_ellipsoid to use an ellipsoid not in valid set"); |
363
|
|
|
|
|
|
|
} |
364
|
314
|
|
|
|
|
553
|
$self->{ellipsoid} = $ellipsoid; |
365
|
314
|
|
|
|
|
456
|
my( $major, $recip ) = @{$ellipsoids{$ellipsoid}}; |
|
314
|
|
|
|
|
832
|
|
366
|
314
|
|
|
|
|
615
|
$self->{equatorial} = $major; |
367
|
314
|
100
|
|
|
|
725
|
if( $recip == 0 ) { |
368
|
1
|
|
|
|
|
188
|
carp("Infinite flattening specified by ellipsoid -- assuming a sphere"); |
369
|
1
|
|
|
|
|
123
|
$self->{polar} = $self->{equatorial}; |
370
|
1
|
|
|
|
|
4
|
$self->{flattening} = 0; |
371
|
1
|
|
|
|
|
4
|
$self->{eccentricity} = 0; |
372
|
|
|
|
|
|
|
}else{ |
373
|
313
|
|
|
|
|
935
|
$self->{flattening} = ( 1.0 / $ellipsoids{$ellipsoid}[1]); |
374
|
313
|
|
|
|
|
714
|
$self->{polar} = $self->{equatorial} * ( 1.0 - $self->{flattening} ); |
375
|
|
|
|
|
|
|
$self->{eccentricity} = sqrt( 2.0 * $self->{flattening} - |
376
|
313
|
|
|
|
|
781
|
( $self->{flattening} * $self->{flattening} ) ); |
377
|
|
|
|
|
|
|
} |
378
|
|
|
|
|
|
|
} |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
=pod |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
=item set_custom_ellipsoid |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
Sets the ellipsoid parameters to the specified semi-major axis (given in |
385
|
|
|
|
|
|
|
meters) and reciprocal flattening. A zero value for the reciprocal flattening |
386
|
|
|
|
|
|
|
will result in a sphere for the ellipsoid, and a warning message will be |
387
|
|
|
|
|
|
|
issued. |
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
$geo->set_custom_ellipsoid( 'sphere', 6378137, 0 ); |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
=cut |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
sub set_custom_ellipsoid |
394
|
|
|
|
|
|
|
{ |
395
|
3
|
|
|
3
|
1
|
19
|
my $self = shift; |
396
|
3
|
|
|
|
|
9
|
my( $name, $major, $recip ) = @_; |
397
|
3
|
|
|
|
|
7
|
$name = uc $name; |
398
|
3
|
50
|
|
|
|
10
|
$recip = 0 unless defined $recip; |
399
|
3
|
50
|
|
|
|
9
|
if( $major ) { |
400
|
3
|
|
|
|
|
11
|
$ellipsoids{$name} = [ $major, $recip ]; |
401
|
|
|
|
|
|
|
}else{ |
402
|
0
|
|
|
|
|
0
|
croak("set_custom_ellipsoid called without semi-major radius parameter"); |
403
|
|
|
|
|
|
|
} |
404
|
3
|
|
|
|
|
8
|
$self->set_ellipsoid($name); |
405
|
|
|
|
|
|
|
} |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
=pod |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
=item set_longitude_symmetric |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
If called with no argument or a true argument, sets the range of output values |
412
|
|
|
|
|
|
|
for longitude to be symmetric around zero, i.e., in the range [-pi,+pi) |
413
|
|
|
|
|
|
|
radians, [-180,180) degrees etc. depending on the angle unit. |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
If called with a false or undefined argument, sets the output angle range to be |
416
|
|
|
|
|
|
|
non-negative, i.e., [0,2*pi) radians, [0, 360) degrees etc. depending on the |
417
|
|
|
|
|
|
|
angle unit. |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
$geo->set_longitude_symmetric(1); |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
=cut |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
sub set_longitude_symmetric |
424
|
|
|
|
|
|
|
{ |
425
|
235
|
|
|
235
|
1
|
454
|
my( $self, $sym ) = @_; |
426
|
|
|
|
|
|
|
# see if argument passed |
427
|
235
|
50
|
|
|
|
529
|
if( $#_ > 0 ) { |
428
|
|
|
|
|
|
|
# yes -- use value passed |
429
|
235
|
|
|
|
|
396
|
$self->{longitude_symmetric} = $sym; |
430
|
|
|
|
|
|
|
}else{ |
431
|
|
|
|
|
|
|
# no -- set to true |
432
|
0
|
|
|
|
|
0
|
$self->{longitude_symmetric} = 1; |
433
|
|
|
|
|
|
|
} |
434
|
|
|
|
|
|
|
} |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
=pod |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
=item set_bearing_symmetric |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
If called with no argument or a true argument, sets the range of output values |
441
|
|
|
|
|
|
|
for bearing to be symmetric around zero, i.e., in the range [-pi,+pi) radians, |
442
|
|
|
|
|
|
|
[-180,180) degrees etc. depending on the angle unit. |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
If called with a false or undefined argument, sets the output angle range to be |
445
|
|
|
|
|
|
|
non-negative, i.e., [0,2*pi) radians, [0,360) degrees etc. depending on the |
446
|
|
|
|
|
|
|
angle unit. |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
$geo->set_bearing_symmetric(1); |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
=cut |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
sub set_bearing_symmetric |
453
|
|
|
|
|
|
|
{ |
454
|
235
|
|
|
235
|
1
|
403
|
my( $self, $sym ) = @_; |
455
|
|
|
|
|
|
|
# see if argument passed |
456
|
235
|
50
|
|
|
|
457
|
if( $#_ > 0 ) { |
457
|
|
|
|
|
|
|
# yes -- use value passed |
458
|
235
|
|
|
|
|
363
|
$self->{bearing_symmetric} = $sym; |
459
|
|
|
|
|
|
|
}else{ |
460
|
|
|
|
|
|
|
# no -- set to true |
461
|
0
|
|
|
|
|
0
|
$self->{bearing_symmetric} = 1; |
462
|
|
|
|
|
|
|
} |
463
|
|
|
|
|
|
|
} |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
=pod |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
=item set_defaults |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
Sets the defaults for the new() constructor method. Call with key, value pairs |
470
|
|
|
|
|
|
|
similar to new. |
471
|
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
Geo::Ellipsoid->set_defaults( |
473
|
|
|
|
|
|
|
ellipsoid => 'GRS80', |
474
|
|
|
|
|
|
|
angle_unit => 'degrees', |
475
|
|
|
|
|
|
|
distance_unit => 'kilometer', |
476
|
|
|
|
|
|
|
longitude_symmetric => 1, |
477
|
|
|
|
|
|
|
bearing_symmetric => 0 |
478
|
|
|
|
|
|
|
); |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
Keys and string values (except for the ellipsoid identifier) may be shortened |
481
|
|
|
|
|
|
|
to their first three letters and are case-insensitive: |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
Geo::Ellipsoid->set_defaults( |
484
|
|
|
|
|
|
|
uni => 'deg', |
485
|
|
|
|
|
|
|
ell => 'GRS80', |
486
|
|
|
|
|
|
|
dis => 'kil', |
487
|
|
|
|
|
|
|
lon => 1, |
488
|
|
|
|
|
|
|
bea => 0 |
489
|
|
|
|
|
|
|
); |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
=cut |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
sub set_defaults |
494
|
|
|
|
|
|
|
{ |
495
|
21
|
|
|
21
|
1
|
57109
|
my $self = shift; |
496
|
21
|
|
|
|
|
70
|
my %args = @_; |
497
|
21
|
|
|
|
|
81
|
while (my ($key, $val) = each %args) { |
498
|
45
|
100
|
|
|
|
210
|
if( $key =~ /^ell/i ) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
499
|
21
|
|
|
|
|
86
|
$defaults{ellipsoid} = uc $val; |
500
|
|
|
|
|
|
|
}elsif( $key =~ /^(uni|ang)/i ) { |
501
|
21
|
|
|
|
|
76
|
$defaults{angle_unit} = $val; |
502
|
|
|
|
|
|
|
}elsif( $key =~ /^dis/i ) { |
503
|
1
|
|
|
|
|
3
|
$defaults{distance_unit} = $val; |
504
|
|
|
|
|
|
|
}elsif( $key =~ /^lon/i ) { |
505
|
1
|
|
|
|
|
5
|
$defaults{longitude_symmetric} = $val; |
506
|
|
|
|
|
|
|
}elsif( $key =~ /^bea/i ) { |
507
|
1
|
|
|
|
|
4
|
$defaults{bearing_symmetric} = $val; |
508
|
|
|
|
|
|
|
}else{ |
509
|
0
|
|
|
|
|
0
|
croak("Geo::Ellipsoid::set_defaults called with invalid key: $key"); |
510
|
|
|
|
|
|
|
} |
511
|
|
|
|
|
|
|
} |
512
|
21
|
50
|
|
|
|
90
|
print "Defaults set to ($defaults{ellipsoid},$defaults{angle_unit}\n" |
513
|
|
|
|
|
|
|
if $DEBUG; |
514
|
|
|
|
|
|
|
} |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
=pod |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
=back |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
=head2 Getters |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
=over |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
=item get_ellipsoid |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
Returns the name of the ellipsoid. |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
=cut |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
sub get_ellipsoid { |
531
|
40
|
|
|
40
|
1
|
21067
|
my $self = shift; |
532
|
40
|
|
|
|
|
136
|
return $self -> {ellipsoid}; |
533
|
|
|
|
|
|
|
} |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
=pod |
536
|
|
|
|
|
|
|
|
537
|
|
|
|
|
|
|
=item get_equatorial_radius |
538
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
Returns the equatorial radius in meters. |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
=cut |
542
|
|
|
|
|
|
|
|
543
|
|
|
|
|
|
|
sub get_equatorial_radius { |
544
|
20
|
|
|
20
|
1
|
63
|
my $self = shift; |
545
|
20
|
|
|
|
|
37
|
return $self -> {equatorial}; |
546
|
|
|
|
|
|
|
} |
547
|
|
|
|
|
|
|
|
548
|
|
|
|
|
|
|
=pod |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
=item get_polar_radius |
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
Returns the polar radius in meters. |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
=cut |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
sub get_polar_radius { |
557
|
20
|
|
|
20
|
1
|
62
|
my $self = shift; |
558
|
20
|
|
|
|
|
35
|
return $self -> {polar}; |
559
|
|
|
|
|
|
|
} |
560
|
|
|
|
|
|
|
|
561
|
|
|
|
|
|
|
=pod |
562
|
|
|
|
|
|
|
|
563
|
|
|
|
|
|
|
=item get_geocentric_radius ANGLE |
564
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
Returns the geocentric radius in meters, given a geocentric latitude. The |
566
|
|
|
|
|
|
|
geocentric latitude is the angle between the equatorial plane and the radius |
567
|
|
|
|
|
|
|
from centre to the point on the surface. |
568
|
|
|
|
|
|
|
|
569
|
|
|
|
|
|
|
=cut |
570
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
sub get_geocentric_radius { |
572
|
10
|
|
|
10
|
1
|
3414
|
my $self = shift; |
573
|
10
|
|
|
|
|
16
|
my $angle = shift; |
574
|
10
|
|
|
|
|
15
|
my $angle_unit = $self->{angle_unit}; |
575
|
10
|
50
|
|
|
|
32
|
$angle /= $degrees_per_radian if $angle_unit eq 'degrees'; |
576
|
|
|
|
|
|
|
|
577
|
10
|
|
|
|
|
18
|
my $a = $self -> {equatorial}; |
578
|
10
|
|
|
|
|
14
|
my $b = $self -> {polar}; |
579
|
|
|
|
|
|
|
|
580
|
10
|
|
|
|
|
22
|
my $sa = sin $angle; |
581
|
10
|
|
|
|
|
37
|
my $ca = cos $angle; |
582
|
|
|
|
|
|
|
|
583
|
10
|
|
|
|
|
29
|
return $a * $b / _hypot($a * $sa, $b * $ca); |
584
|
|
|
|
|
|
|
} |
585
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
=pod |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
=item get_flattening |
589
|
|
|
|
|
|
|
|
590
|
|
|
|
|
|
|
Returns the flattening. |
591
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
=cut |
593
|
|
|
|
|
|
|
|
594
|
|
|
|
|
|
|
sub get_flattening { |
595
|
20
|
|
|
20
|
1
|
66
|
my $self = shift; |
596
|
20
|
|
|
|
|
39
|
return $self -> {flattening}; |
597
|
|
|
|
|
|
|
} |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
=pod |
600
|
|
|
|
|
|
|
|
601
|
|
|
|
|
|
|
=item get_eccentricity |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
Returns the eccentricity. |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
=cut |
606
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
sub get_eccentricity { |
608
|
20
|
|
|
20
|
1
|
62
|
my $self = shift; |
609
|
20
|
|
|
|
|
39
|
return $self -> {eccentricity}; |
610
|
|
|
|
|
|
|
} |
611
|
|
|
|
|
|
|
|
612
|
|
|
|
|
|
|
=pod |
613
|
|
|
|
|
|
|
|
614
|
|
|
|
|
|
|
=item get_longitude_symmetric |
615
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
Returns true if the longitude is symmetric around zero, and false otherwise. |
617
|
|
|
|
|
|
|
|
618
|
|
|
|
|
|
|
=cut |
619
|
|
|
|
|
|
|
|
620
|
|
|
|
|
|
|
sub get_longitude_symmetric { |
621
|
6
|
|
|
6
|
1
|
19
|
my $self = shift; |
622
|
6
|
|
|
|
|
24
|
return $self -> {longitude_symmetric}; |
623
|
|
|
|
|
|
|
} |
624
|
|
|
|
|
|
|
|
625
|
|
|
|
|
|
|
=pod |
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
=item get_bearing_symmetric |
628
|
|
|
|
|
|
|
|
629
|
|
|
|
|
|
|
Returns true if the bearing is symmetric around zero, and false otherwise. |
630
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
=cut |
632
|
|
|
|
|
|
|
|
633
|
|
|
|
|
|
|
sub get_bearing_symmetric { |
634
|
6
|
|
|
6
|
1
|
17
|
my $self = shift; |
635
|
6
|
|
|
|
|
33
|
return $self -> {bearing_symmetric}; |
636
|
|
|
|
|
|
|
} |
637
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
=pod |
639
|
|
|
|
|
|
|
|
640
|
|
|
|
|
|
|
=item get_angle_unit |
641
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
Returns the angle unit, i.e., the unit for latitude, longitude, and bearing. |
643
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
=cut |
645
|
|
|
|
|
|
|
|
646
|
|
|
|
|
|
|
sub get_angle_unit { |
647
|
32
|
|
|
32
|
1
|
16526
|
my $self = shift; |
648
|
32
|
|
|
|
|
102
|
return $self -> {angle_unit}; |
649
|
|
|
|
|
|
|
} |
650
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
=pod |
652
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
=item get_distance_unit |
654
|
|
|
|
|
|
|
|
655
|
|
|
|
|
|
|
Returns the distance unit. |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
=cut |
658
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
sub get_distance_unit { |
660
|
14
|
|
|
14
|
1
|
47
|
my $self = shift; |
661
|
14
|
|
|
|
|
58
|
return $self -> {distance_unit}; |
662
|
|
|
|
|
|
|
} |
663
|
|
|
|
|
|
|
|
664
|
|
|
|
|
|
|
=pod |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
=back |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
=head2 Calculations |
669
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
=over |
671
|
|
|
|
|
|
|
|
672
|
|
|
|
|
|
|
=item scales |
673
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
Returns a list consisting of the distance unit per angle of latitude |
675
|
|
|
|
|
|
|
and longitude (degrees or radians) at the specified latitude. |
676
|
|
|
|
|
|
|
These values may be used for fast approximations of distance |
677
|
|
|
|
|
|
|
calculations in the vicinity of some location. |
678
|
|
|
|
|
|
|
|
679
|
|
|
|
|
|
|
( $lat_scale, $lon_scale ) = $geo->scales($lat0); |
680
|
|
|
|
|
|
|
$x = $lon_scale * ($lon - $lon0); |
681
|
|
|
|
|
|
|
$y = $lat_scale * ($lat - $lat0); |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
=cut |
684
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
sub scales |
686
|
|
|
|
|
|
|
{ |
687
|
90
|
|
|
90
|
1
|
53287
|
my $self = shift; |
688
|
90
|
|
|
|
|
174
|
my $units = $self->{angle_unit}; |
689
|
90
|
|
|
|
|
124
|
my $lat = $_[0]; |
690
|
90
|
50
|
|
|
|
199
|
if( defined $lat ) { |
691
|
90
|
50
|
|
|
|
280
|
$lat /= $degrees_per_radian if( $units eq 'degrees' ); |
692
|
|
|
|
|
|
|
}else{ |
693
|
0
|
|
|
|
|
0
|
carp("scales() method requires latitude argument; assuming 0"); |
694
|
0
|
|
|
|
|
0
|
$lat = 0; |
695
|
|
|
|
|
|
|
} |
696
|
|
|
|
|
|
|
|
697
|
90
|
|
|
|
|
137
|
my $aa = $self->{equatorial}; |
698
|
90
|
|
|
|
|
127
|
my $bb = $self->{polar}; |
699
|
90
|
|
|
|
|
225
|
my $d1 = $aa * cos($lat); |
700
|
90
|
|
|
|
|
152
|
my $d2 = $bb * sin($lat); |
701
|
90
|
|
|
|
|
158
|
my $d3 = $d1*$d1 + $d2*$d2; |
702
|
90
|
|
|
|
|
122
|
my $d4 = sqrt($d3); |
703
|
90
|
|
|
|
|
109
|
my $n1 = $aa * $bb; |
704
|
90
|
|
|
|
|
175
|
my $latscl = ( $n1 * $n1 ) / ( $d3 * $d4 * $self->{conversion} ); |
705
|
90
|
|
|
|
|
141
|
my $lonscl = ( $aa * $d1 ) / ( $d4 * $self->{conversion} ); |
706
|
|
|
|
|
|
|
|
707
|
90
|
50
|
|
|
|
171
|
if( $DEBUG ) { |
708
|
0
|
|
|
|
|
0
|
print "lat=$lat, aa=$aa, bb=$bb\nd1=$d1, d2=$d2, d3=$d3, d4=$d4\n"; |
709
|
0
|
|
|
|
|
0
|
print "latscl=$latscl, lonscl=$lonscl\n"; |
710
|
|
|
|
|
|
|
} |
711
|
|
|
|
|
|
|
|
712
|
90
|
50
|
|
|
|
184
|
if( $self->{angle_unit} eq 'degrees' ) { |
713
|
90
|
|
|
|
|
118
|
$latscl /= $degrees_per_radian; |
714
|
90
|
|
|
|
|
119
|
$lonscl /= $degrees_per_radian; |
715
|
|
|
|
|
|
|
} |
716
|
90
|
|
|
|
|
327
|
return ( $latscl, $lonscl ); |
717
|
|
|
|
|
|
|
} |
718
|
|
|
|
|
|
|
|
719
|
|
|
|
|
|
|
=pod |
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
=item range |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
Returns the range in distance units between two specified locations given |
724
|
|
|
|
|
|
|
as latitude, longitude pairs. |
725
|
|
|
|
|
|
|
|
726
|
|
|
|
|
|
|
my $dist = $geo->range( $lat1, $lon1, $lat2, $lon2 ); |
727
|
|
|
|
|
|
|
my $dist = $geo->range( @origin, @destination ); |
728
|
|
|
|
|
|
|
|
729
|
|
|
|
|
|
|
=cut |
730
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
sub range |
732
|
|
|
|
|
|
|
{ |
733
|
1220
|
|
|
1220
|
1
|
375726
|
my $self = shift; |
734
|
1220
|
|
|
|
|
2448
|
my @args = _normalize_input($self->{angle_unit},@_); |
735
|
1220
|
|
|
|
|
9671
|
my($range,$bearing) = $self->_inverse(@args); |
736
|
1220
|
50
|
|
|
|
2236
|
print "inverse(@_[1..4]) returns($range,$bearing)\n" if $DEBUG; |
737
|
1220
|
|
|
|
|
3030
|
return $range; |
738
|
|
|
|
|
|
|
} |
739
|
|
|
|
|
|
|
|
740
|
|
|
|
|
|
|
=pod |
741
|
|
|
|
|
|
|
|
742
|
|
|
|
|
|
|
=item bearing |
743
|
|
|
|
|
|
|
|
744
|
|
|
|
|
|
|
Returns the bearing in degrees or radians from the first location to |
745
|
|
|
|
|
|
|
the second. Zero bearing is true north. |
746
|
|
|
|
|
|
|
|
747
|
|
|
|
|
|
|
my $bearing = $geo->bearing( $lat1, $lon1, $lat2, $lon2 ); |
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
=cut |
750
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
sub bearing |
752
|
|
|
|
|
|
|
{ |
753
|
2432
|
|
|
2432
|
1
|
803983
|
my $self = shift; |
754
|
2432
|
|
|
|
|
4671
|
my $units = $self->{angle_unit}; |
755
|
2432
|
|
|
|
|
5401
|
my @args = _normalize_input($units,@_); |
756
|
2432
|
|
|
|
|
8059
|
my($range,$bearing) = $self->_inverse(@args); |
757
|
2432
|
50
|
|
|
|
4506
|
print "inverse(@args) returns($range,$bearing)\n" if $DEBUG; |
758
|
2432
|
|
|
|
|
3377
|
my $t = $bearing; |
759
|
2432
|
|
|
|
|
5659
|
$self->_normalize_output('bearing_symmetric',$bearing); |
760
|
2432
|
50
|
|
|
|
6970
|
print "_normalize_output($t) returns($bearing)\n" if $DEBUG; |
761
|
2432
|
|
|
|
|
6448
|
return $bearing; |
762
|
|
|
|
|
|
|
} |
763
|
|
|
|
|
|
|
|
764
|
|
|
|
|
|
|
=pod |
765
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
=item at |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
Returns the list (latitude,longitude) in degrees or radians that is a |
769
|
|
|
|
|
|
|
specified range and bearing from a given location. |
770
|
|
|
|
|
|
|
|
771
|
|
|
|
|
|
|
my( $lat2, $lon2 ) = $geo->at( $lat1, $lon1, $range, $bearing ); |
772
|
|
|
|
|
|
|
|
773
|
|
|
|
|
|
|
=cut |
774
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
sub at |
776
|
|
|
|
|
|
|
{ |
777
|
2408
|
|
|
2408
|
1
|
968163
|
my $self = shift; |
778
|
2408
|
|
|
|
|
4690
|
my $units = $self->{angle_unit}; |
779
|
2408
|
|
|
|
|
6119
|
my( $lat, $lon, $az ) = _normalize_input($units,@_[0,1,3]); |
780
|
2408
|
|
|
|
|
6593
|
my $r = $_[2]; |
781
|
2408
|
50
|
|
|
|
5292
|
print "at($lat,$lon,$r,$az)\n" if $DEBUG; |
782
|
2408
|
|
|
|
|
5138
|
my( $lat2, $lon2 ) = $self->_forward($lat,$lon,$r,$az); |
783
|
2408
|
50
|
|
|
|
4883
|
print "_forward returns ($lat2,$lon2)\n" if $DEBUG; |
784
|
2408
|
|
|
|
|
5947
|
$self->_normalize_output('longitude_symmetric',$lon2); |
785
|
2408
|
|
|
|
|
7684
|
$self->_normalize_output('latitude_symmetric',$lat2); |
786
|
2408
|
|
|
|
|
9570
|
return ( $lat2, $lon2 ); |
787
|
|
|
|
|
|
|
} |
788
|
|
|
|
|
|
|
|
789
|
|
|
|
|
|
|
=pod |
790
|
|
|
|
|
|
|
|
791
|
|
|
|
|
|
|
=item to |
792
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
In list context, returns (range, bearing) between two specified locations. |
794
|
|
|
|
|
|
|
In scalar context, returns just the range. |
795
|
|
|
|
|
|
|
|
796
|
|
|
|
|
|
|
my( $dist, $theta ) = $geo->to( $lat1, $lon1, $lat2, $lon2 ); |
797
|
|
|
|
|
|
|
my $dist = $geo->to( $lat1, $lon1, $lat2, $lon2 ); |
798
|
|
|
|
|
|
|
|
799
|
|
|
|
|
|
|
=cut |
800
|
|
|
|
|
|
|
|
801
|
|
|
|
|
|
|
sub to |
802
|
|
|
|
|
|
|
{ |
803
|
252
|
|
|
252
|
1
|
148746
|
my $self = shift; |
804
|
252
|
|
|
|
|
465
|
my $units = $self->{angle_unit}; |
805
|
252
|
|
|
|
|
555
|
my @args = _normalize_input($units,@_); |
806
|
252
|
50
|
|
|
|
2134
|
print "to($units,@args)\n" if $DEBUG; |
807
|
252
|
|
|
|
|
547
|
my($range,$bearing) = $self->_inverse(@args); |
808
|
252
|
50
|
|
|
|
595
|
print "to: inverse(@args) returns($range,$bearing)\n" if $DEBUG; |
809
|
|
|
|
|
|
|
#$bearing *= $degrees_per_radian if $units eq 'degrees'; |
810
|
252
|
|
|
|
|
697
|
$self->_normalize_output('bearing_symmetric',$bearing); |
811
|
252
|
50
|
|
|
|
2163
|
if( wantarray() ) { |
812
|
252
|
|
|
|
|
1099
|
return ( $range, $bearing ); |
813
|
|
|
|
|
|
|
}else{ |
814
|
0
|
|
|
|
|
0
|
return $range; |
815
|
|
|
|
|
|
|
} |
816
|
|
|
|
|
|
|
} |
817
|
|
|
|
|
|
|
|
818
|
|
|
|
|
|
|
=pod |
819
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
=item displacement |
821
|
|
|
|
|
|
|
|
822
|
|
|
|
|
|
|
Returns the (x,y) displacement in distance units between the two specified |
823
|
|
|
|
|
|
|
locations. |
824
|
|
|
|
|
|
|
|
825
|
|
|
|
|
|
|
my( $x, $y ) = $geo->displacement( $lat1, $lon1, $lat2, $lon2 ); |
826
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
NOTE: The x and y displacements are only approximations and only valid |
828
|
|
|
|
|
|
|
between two locations that are fairly near to each other. Beyond 10 kilometers |
829
|
|
|
|
|
|
|
or more, the concept of X and Y on a curved surface loses its meaning. |
830
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
=cut |
832
|
|
|
|
|
|
|
|
833
|
|
|
|
|
|
|
sub displacement |
834
|
|
|
|
|
|
|
{ |
835
|
100
|
|
|
100
|
1
|
59880
|
my $self = shift; |
836
|
100
|
50
|
|
|
|
250
|
print "displacement(",join(',',@_),"\n" if $DEBUG; |
837
|
100
|
|
|
|
|
281
|
my @args = _normalize_input($self->{angle_unit},@_); |
838
|
100
|
50
|
|
|
|
799
|
print "call _inverse(@args)\n" if $DEBUG; |
839
|
100
|
|
|
|
|
227
|
my( $range, $bearing ) = $self->_inverse(@args); |
840
|
100
|
50
|
|
|
|
175
|
print "disp: _inverse(@args) returns ($range,$bearing)\n" if $DEBUG; |
841
|
100
|
|
|
|
|
167
|
my $x = $range * sin($bearing); |
842
|
100
|
|
|
|
|
162
|
my $y = $range * cos($bearing); |
843
|
100
|
|
|
|
|
348
|
return ($x,$y); |
844
|
|
|
|
|
|
|
} |
845
|
|
|
|
|
|
|
|
846
|
|
|
|
|
|
|
=pod |
847
|
|
|
|
|
|
|
|
848
|
|
|
|
|
|
|
=item location |
849
|
|
|
|
|
|
|
|
850
|
|
|
|
|
|
|
Returns the list (latitude,longitude) of a location at a given (x,y) |
851
|
|
|
|
|
|
|
displacement from a given location. |
852
|
|
|
|
|
|
|
|
853
|
|
|
|
|
|
|
my @loc = $geo->location( $lat, $lon, $x, $y ); |
854
|
|
|
|
|
|
|
|
855
|
|
|
|
|
|
|
=cut |
856
|
|
|
|
|
|
|
|
857
|
|
|
|
|
|
|
sub location |
858
|
|
|
|
|
|
|
{ |
859
|
200
|
|
|
200
|
1
|
119820
|
my $self = shift; |
860
|
200
|
|
|
|
|
374
|
my $units = $self->{angle_unit}; |
861
|
200
|
|
|
|
|
351
|
my($lat,$lon,$x,$y) = @_; |
862
|
200
|
|
|
|
|
383
|
my $range = sqrt( $x*$x+ $y*$y ); |
863
|
200
|
|
|
|
|
442
|
my $bearing = atan2($x,$y); |
864
|
200
|
50
|
|
|
|
516
|
$bearing *= $degrees_per_radian if $units eq 'degrees'; |
865
|
200
|
50
|
|
|
|
406
|
print "location($lat,$lon,$x,$y,$range,$bearing)\n" if $DEBUG; |
866
|
200
|
|
|
|
|
418
|
return $self->at($lat,$lon,$range,$bearing); |
867
|
|
|
|
|
|
|
} |
868
|
|
|
|
|
|
|
|
869
|
|
|
|
|
|
|
######################################################################## |
870
|
|
|
|
|
|
|
# |
871
|
|
|
|
|
|
|
# internal functions |
872
|
|
|
|
|
|
|
# |
873
|
|
|
|
|
|
|
# inverse |
874
|
|
|
|
|
|
|
# |
875
|
|
|
|
|
|
|
# Calculate the displacement from origin to destination. |
876
|
|
|
|
|
|
|
# The input to this subroutine is |
877
|
|
|
|
|
|
|
# ( latitude-1, longitude-1, latitude-2, longitude-2 ) in radians. |
878
|
|
|
|
|
|
|
# |
879
|
|
|
|
|
|
|
# Return the results as the list (range,bearing) with range in the |
880
|
|
|
|
|
|
|
# current specified distance unit and bearing in radians. |
881
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
sub _inverse() |
883
|
|
|
|
|
|
|
{ |
884
|
3996
|
|
|
3996
|
|
5814
|
my $self = shift; |
885
|
3996
|
|
|
|
|
6872
|
my( $lat1, $lon1, $lat2, $lon2 ) = (@_); |
886
|
3996
|
50
|
|
|
|
8013
|
print "_inverse($lat1,$lon1,$lat2,$lon2)\n" if $DEBUG; |
887
|
|
|
|
|
|
|
|
888
|
3996
|
|
|
|
|
6158
|
my $a = $self->{equatorial}; |
889
|
3996
|
|
|
|
|
6084
|
my $f = $self->{flattening}; |
890
|
|
|
|
|
|
|
|
891
|
3996
|
|
|
|
|
5670
|
my $r = 1.0 - $f; |
892
|
3996
|
|
|
|
|
8890
|
my $tu1 = $r * sin($lat1) / cos($lat1); |
893
|
3996
|
|
|
|
|
6997
|
my $tu2 = $r * sin($lat2) / cos($lat2); |
894
|
3996
|
|
|
|
|
6690
|
my $cu1 = 1.0 / ( sqrt(($tu1*$tu1) + 1.0) ); |
895
|
3996
|
|
|
|
|
5313
|
my $su1 = $cu1 * $tu1; |
896
|
3996
|
|
|
|
|
5778
|
my $cu2 = 1.0 / ( sqrt( ($tu2*$tu2) + 1.0 )); |
897
|
3996
|
|
|
|
|
5496
|
my $s = $cu1 * $cu2; |
898
|
3996
|
|
|
|
|
5496
|
my $baz = $s * $tu2; |
899
|
3996
|
|
|
|
|
5351
|
my $faz = $baz * $tu1; |
900
|
3996
|
|
|
|
|
5730
|
my $dlon = $lon2 - $lon1; |
901
|
|
|
|
|
|
|
|
902
|
3996
|
50
|
|
|
|
6379
|
if( $DEBUG ) { |
903
|
0
|
|
|
|
|
0
|
printf "lat1=%.8f, lon1=%.8f\n", $lat1, $lon1; |
904
|
0
|
|
|
|
|
0
|
printf "lat2=%.8f, lon2=%.8f\n", $lat2, $lon2; |
905
|
0
|
|
|
|
|
0
|
printf "r=%.8f, tu1=%.8f, tu2=%.8f\n", $r, $tu1, $tu2; |
906
|
0
|
|
|
|
|
0
|
printf "faz=%.8f, dlon=%.8f\n", $faz, $dlon; |
907
|
|
|
|
|
|
|
} |
908
|
|
|
|
|
|
|
|
909
|
3996
|
|
|
|
|
5157
|
my $x = $dlon; |
910
|
3996
|
|
|
|
|
5024
|
my $cnt = 0; |
911
|
3996
|
50
|
|
|
|
6453
|
print "enter loop:\n" if $DEBUG; |
912
|
3996
|
|
|
|
|
5970
|
my( $c2a, $c, $cx, $cy, $cz, $d, $del, $e, $sx, $sy, $y ); |
913
|
3996
|
|
100
|
|
|
5075
|
do { |
914
|
22431
|
50
|
|
|
|
35574
|
printf " x=%.8f\n", $x if $DEBUG; |
915
|
22431
|
|
|
|
|
29932
|
$sx = sin($x); |
916
|
22431
|
|
|
|
|
29404
|
$cx = cos($x); |
917
|
22431
|
|
|
|
|
26944
|
$tu1 = $cu2*$sx; |
918
|
22431
|
|
|
|
|
29241
|
$tu2 = $baz - ($su1*$cu2*$cx); |
919
|
|
|
|
|
|
|
|
920
|
22431
|
50
|
|
|
|
33840
|
printf " sx=%.8f, cx=%.8f, tu1=%.8f, tu2=%.8f\n", |
921
|
|
|
|
|
|
|
$sx, $cx, $tu1, $tu2 if $DEBUG; |
922
|
|
|
|
|
|
|
|
923
|
22431
|
|
|
|
|
30472
|
$sy = sqrt( $tu1*$tu1 + $tu2*$tu2 ); |
924
|
22431
|
|
|
|
|
28442
|
$cy = $s*$cx + $faz; |
925
|
22431
|
|
|
|
|
30785
|
$y = atan2($sy,$cy); |
926
|
22431
|
|
|
|
|
26269
|
my $sa; |
927
|
22431
|
100
|
|
|
|
34312
|
if( $sy == 0.0 ) { |
928
|
72
|
|
|
|
|
100
|
$sa = 1.0; |
929
|
|
|
|
|
|
|
}else{ |
930
|
22359
|
|
|
|
|
28993
|
$sa = ($s*$sx) / $sy; |
931
|
|
|
|
|
|
|
} |
932
|
|
|
|
|
|
|
|
933
|
22431
|
50
|
|
|
|
34265
|
printf " sy=%.8f, cy=%.8f, y=%.8f, sa=%.8f\n", $sy, $cy, $y, $sa |
934
|
|
|
|
|
|
|
if $DEBUG; |
935
|
|
|
|
|
|
|
|
936
|
22431
|
|
|
|
|
28200
|
$c2a = 1.0 - ($sa*$sa); |
937
|
22431
|
|
|
|
|
26949
|
$cz = $faz + $faz; |
938
|
22431
|
100
|
|
|
|
35408
|
if( $c2a > 0.0 ) { |
939
|
21687
|
|
|
|
|
28632
|
$cz = ((-$cz)/$c2a) + $cy; |
940
|
|
|
|
|
|
|
} |
941
|
22431
|
|
|
|
|
29091
|
$e = ( 2.0 * $cz * $cz ) - 1.0; |
942
|
22431
|
|
|
|
|
32194
|
$c = ( ((( (-3.0 * $c2a) + 4.0)*$f) + 4.0) * $c2a * $f )/16.0; |
943
|
22431
|
|
|
|
|
29251
|
$d = $x; |
944
|
22431
|
|
|
|
|
31631
|
$x = ( ($e * $cy * $c + $cz) * $sy * $c + $y) * $sa; |
945
|
22431
|
|
|
|
|
30493
|
$x = ( 1.0 - $c ) * $x * $f + $dlon; |
946
|
22431
|
|
|
|
|
26817
|
$del = $d - $x; |
947
|
|
|
|
|
|
|
|
948
|
22431
|
50
|
|
|
|
70606
|
if( $DEBUG ) { |
949
|
0
|
|
|
|
|
0
|
printf " c2a=%.8f, cz=%.8f\n", $c2a, $cz; |
950
|
0
|
|
|
|
|
0
|
printf " e=%.8f, d=%.8f\n", $e, $d; |
951
|
0
|
|
|
|
|
0
|
printf " (d-x)=%.8g\n", $del; |
952
|
|
|
|
|
|
|
} |
953
|
|
|
|
|
|
|
|
954
|
|
|
|
|
|
|
}while( (abs($del) > $eps) && ( ++$cnt <= $max_loop_count ) ); |
955
|
|
|
|
|
|
|
|
956
|
3996
|
|
|
|
|
5998
|
$faz = atan2($tu1,$tu2); |
957
|
3996
|
|
|
|
|
7135
|
$baz = atan2($cu1*$sx,($baz*$cx - $su1*$cu2)) + pi; |
958
|
3996
|
|
|
|
|
6413
|
$x = sqrt( ((1.0/($r*$r)) -1.0 ) * $c2a+1.0 ) + 1.0; |
959
|
3996
|
|
|
|
|
5406
|
$x = ($x-2.0)/$x; |
960
|
3996
|
|
|
|
|
5089
|
$c = 1.0 - $x; |
961
|
3996
|
|
|
|
|
5843
|
$c = (($x*$x)/4.0 + 1.0)/$c; |
962
|
3996
|
|
|
|
|
5938
|
$d = ((0.375*$x*$x) - 1.0)*$x; |
963
|
3996
|
|
|
|
|
5011
|
$x = $e*$cy; |
964
|
|
|
|
|
|
|
|
965
|
3996
|
50
|
|
|
|
6775
|
if( $DEBUG ) { |
966
|
0
|
|
|
|
|
0
|
printf "e=%.8f, cy=%.8f, x=%.8f\n", $e, $cy, $x; |
967
|
0
|
|
|
|
|
0
|
printf "sy=%.8f, c=%.8f, d=%.8f\n", $sy, $c, $d; |
968
|
0
|
|
|
|
|
0
|
printf "cz=%.8f, a=%.8f, r=%.8f\n", $cz, $a, $r; |
969
|
|
|
|
|
|
|
} |
970
|
|
|
|
|
|
|
|
971
|
3996
|
|
|
|
|
5452
|
$s = 1.0 - $e - $e; |
972
|
3996
|
|
|
|
|
8152
|
$s = (((((((( $sy * $sy * 4.0 ) - 3.0) * $s * $cz * $d/6.0) - $x) * |
973
|
|
|
|
|
|
|
$d /4.0) + $cz) * $sy * $d) + $y ) * $c * $a * $r; |
974
|
|
|
|
|
|
|
|
975
|
3996
|
50
|
|
|
|
6399
|
printf "s=%.8f\n", $s if $DEBUG; |
976
|
|
|
|
|
|
|
|
977
|
|
|
|
|
|
|
# return result |
978
|
3996
|
|
|
|
|
8832
|
my @disp = ( ($s/$self->{conversion}), $faz ); |
979
|
3996
|
50
|
|
|
|
6538
|
print "disp = (@disp)\n" if $DEBUG; |
980
|
3996
|
|
|
|
|
10334
|
return @disp; |
981
|
|
|
|
|
|
|
} |
982
|
|
|
|
|
|
|
|
983
|
|
|
|
|
|
|
# _forward |
984
|
|
|
|
|
|
|
# |
985
|
|
|
|
|
|
|
# Calculate the location (latitue,longitude) of a point |
986
|
|
|
|
|
|
|
# given a starting point and a displacement from that |
987
|
|
|
|
|
|
|
# point as (range,bearing) |
988
|
|
|
|
|
|
|
# |
989
|
|
|
|
|
|
|
sub _forward |
990
|
|
|
|
|
|
|
{ |
991
|
2400
|
|
|
2400
|
|
3390
|
my $self = shift; |
992
|
2400
|
|
|
|
|
4265
|
my( $lat1, $lon1, $range, $bearing ) = @_; |
993
|
|
|
|
|
|
|
|
994
|
2400
|
50
|
|
|
|
4130
|
if( $DEBUG ) { |
995
|
0
|
|
|
|
|
0
|
printf "_forward(lat1=%.8f,lon1=%.8f,range=%.8f,bearing=%.8f)\n", |
996
|
|
|
|
|
|
|
$lat1, $lon1, $range, $bearing; |
997
|
|
|
|
|
|
|
} |
998
|
|
|
|
|
|
|
|
999
|
2400
|
|
|
|
|
3117
|
my $eps = 0.5e-13; |
1000
|
|
|
|
|
|
|
|
1001
|
2400
|
|
|
|
|
3573
|
my $a = $self->{equatorial}; |
1002
|
2400
|
|
|
|
|
3596
|
my $f = $self->{flattening}; |
1003
|
2400
|
|
|
|
|
3525
|
my $r = 1.0 - $f; |
1004
|
|
|
|
|
|
|
|
1005
|
2400
|
|
|
|
|
6301
|
my $tu = $r * sin($lat1) / cos($lat1); |
1006
|
2400
|
|
|
|
|
3242
|
my $faz = $bearing; |
1007
|
2400
|
|
|
|
|
3692
|
my $s = $self->{conversion} * $range; |
1008
|
2400
|
|
|
|
|
3847
|
my $sf = sin($faz); |
1009
|
2400
|
|
|
|
|
3467
|
my $cf = cos($faz); |
1010
|
|
|
|
|
|
|
|
1011
|
2400
|
|
|
|
|
3082
|
my $baz = 0.0; |
1012
|
2400
|
50
|
|
|
|
5788
|
$baz = 2.0 * atan2($tu,$cf) if( $cf != 0.0 ); |
1013
|
|
|
|
|
|
|
|
1014
|
2400
|
|
|
|
|
4292
|
my $cu = 1.0 / sqrt(1.0 + $tu*$tu); |
1015
|
2400
|
|
|
|
|
3286
|
my $su = $tu * $cu; |
1016
|
2400
|
|
|
|
|
3108
|
my $sa = $cu * $sf; |
1017
|
2400
|
|
|
|
|
3342
|
my $c2a = 1.0 - ($sa*$sa); |
1018
|
2400
|
|
|
|
|
4208
|
my $x = 1.0 + sqrt( (((1.0/($r*$r)) - 1.0 )*$c2a) +1.0); |
1019
|
2400
|
|
|
|
|
3533
|
$x = ($x-2.0)/$x; |
1020
|
2400
|
|
|
|
|
3156
|
my $c = 1.0 - $x; |
1021
|
2400
|
|
|
|
|
3785
|
$c = ((($x*$x)/4.0) + 1.0)/$c; |
1022
|
2400
|
|
|
|
|
3744
|
my $d = $x * ((0.375*$x*$x)-1.0); |
1023
|
2400
|
|
|
|
|
3668
|
$tu = (($s/$r)/$a)/$c; |
1024
|
2400
|
|
|
|
|
3188
|
my $y = $tu; |
1025
|
|
|
|
|
|
|
|
1026
|
2400
|
50
|
|
|
|
4242
|
if( $DEBUG ) { |
1027
|
0
|
|
|
|
|
0
|
printf "r=%.8f, tu=%.8f, faz=%.8f\n", $r, $tu, $faz; |
1028
|
0
|
|
|
|
|
0
|
printf "baz=%.8f, sf=%.8f, cf=%.8f\n", $baz, $sf, $cf; |
1029
|
0
|
|
|
|
|
0
|
printf "cu=%.8f, su=%.8f, sa=%.8f\n", $cu, $su, $sa; |
1030
|
0
|
|
|
|
|
0
|
printf "x=%.8f, c=%.8f, y=%.8f\n", $x, $c, $y; |
1031
|
|
|
|
|
|
|
} |
1032
|
|
|
|
|
|
|
|
1033
|
2400
|
|
|
|
|
3505
|
my( $cy, $cz, $e, $sy ); |
1034
|
2400
|
|
|
|
|
3025
|
do { |
1035
|
8676
|
|
|
|
|
10763
|
$sy = sin($y); |
1036
|
8676
|
|
|
|
|
10891
|
$cy = cos($y); |
1037
|
8676
|
|
|
|
|
11672
|
$cz = cos($baz+$y); |
1038
|
8676
|
|
|
|
|
12220
|
$e = (2.0*$cz*$cz)-1.0; |
1039
|
8676
|
|
|
|
|
10792
|
$c = $y; |
1040
|
8676
|
|
|
|
|
10510
|
$x = $e * $cy; |
1041
|
8676
|
|
|
|
|
10737
|
$y = (2.0 * $e) - 1.0; |
1042
|
8676
|
|
|
|
|
20840
|
$y = ((((((((($sy*$sy*4.0)-3.0)*$y*$cz*$d)/6.0)+$x)*$d)/4.0)-$cz)*$sy*$d) + |
1043
|
|
|
|
|
|
|
$tu; |
1044
|
|
|
|
|
|
|
} while( abs($y-$c) > $eps ); |
1045
|
|
|
|
|
|
|
|
1046
|
2400
|
|
|
|
|
3549
|
$baz = ($cu*$cy*$cf) - ($su*$sy); |
1047
|
2400
|
|
|
|
|
3571
|
$c = $r*sqrt(($sa*$sa) + ($baz*$baz)); |
1048
|
2400
|
|
|
|
|
3303
|
$d = $su*$cy + $cu*$sy*$cf; |
1049
|
2400
|
|
|
|
|
3849
|
my $lat2 = atan2($d,$c); |
1050
|
2400
|
|
|
|
|
3386
|
$c = $cu*$cy - $su*$sy*$cf; |
1051
|
2400
|
|
|
|
|
3650
|
$x = atan2($sy*$sf,$c); |
1052
|
2400
|
|
|
|
|
3989
|
$c = (((((-3.0*$c2a)+4.0)*$f)+4.0)*$c2a*$f)/16.0; |
1053
|
2400
|
|
|
|
|
3849
|
$d = (((($e*$cy*$c) + $cz)*$sy*$c)+$y)*$sa; |
1054
|
2400
|
|
|
|
|
3524
|
my $lon2 = $lon1 + $x - (1.0-$c)*$d*$f; |
1055
|
|
|
|
|
|
|
#$baz = atan2($sa,$baz) + pi; |
1056
|
|
|
|
|
|
|
|
1057
|
|
|
|
|
|
|
# return result |
1058
|
2400
|
|
|
|
|
5835
|
return ($lat2,$lon2); |
1059
|
|
|
|
|
|
|
|
1060
|
|
|
|
|
|
|
} |
1061
|
|
|
|
|
|
|
|
1062
|
|
|
|
|
|
|
# _normalize_input |
1063
|
|
|
|
|
|
|
# |
1064
|
|
|
|
|
|
|
# Normalize a set of input angle values by converting to radians if given |
1065
|
|
|
|
|
|
|
# in degrees. We don't add/subtract multiples of two pi, because the |
1066
|
|
|
|
|
|
|
# trigonometric functions do this more accuractely. |
1067
|
|
|
|
|
|
|
# |
1068
|
|
|
|
|
|
|
sub _normalize_input |
1069
|
|
|
|
|
|
|
{ |
1070
|
6412
|
|
|
6412
|
|
9749
|
my $units = shift; |
1071
|
6412
|
|
|
|
|
12284
|
my @args = @_; |
1072
|
|
|
|
|
|
|
return map { |
1073
|
6412
|
100
|
|
|
|
12838
|
$units eq 'degrees' ? deg2rad($_) : $_ |
|
23240
|
|
|
|
|
90130
|
|
1074
|
|
|
|
|
|
|
} @args; |
1075
|
|
|
|
|
|
|
} |
1076
|
|
|
|
|
|
|
|
1077
|
|
|
|
|
|
|
# _normalize_output |
1078
|
|
|
|
|
|
|
# |
1079
|
|
|
|
|
|
|
# Normalize a set of output angle values by converting to |
1080
|
|
|
|
|
|
|
# degrees if needed and by converting to the range [-pi,+pi) or |
1081
|
|
|
|
|
|
|
# [0,2pi) as needed. |
1082
|
|
|
|
|
|
|
# |
1083
|
|
|
|
|
|
|
sub _normalize_output |
1084
|
|
|
|
|
|
|
{ |
1085
|
7500
|
|
|
7500
|
|
9925
|
my $self = shift; |
1086
|
7500
|
|
|
|
|
10478
|
my $elem = shift; # '(bearing|latitude|longitude)_symmetric' |
1087
|
|
|
|
|
|
|
# adjust remaining input values by reference |
1088
|
7500
|
|
|
|
|
13366
|
for ( @_ ) { |
1089
|
7500
|
100
|
|
|
|
14259
|
if( $self->{$elem} ) { |
1090
|
|
|
|
|
|
|
# normalize to range [-pi,pi) |
1091
|
4824
|
|
|
|
|
9399
|
while( $_ < -(pi) ) { $_ += $twopi } |
|
0
|
|
|
|
|
0
|
|
1092
|
4824
|
|
|
|
|
8771
|
while( $_ >= pi ) { $_ -= $twopi } |
|
98
|
|
|
|
|
168
|
|
1093
|
|
|
|
|
|
|
}else{ |
1094
|
|
|
|
|
|
|
# normalize to range [0,2*pi) |
1095
|
2676
|
|
|
|
|
5498
|
while( $_ < 0 ) { $_ += $twopi } |
|
1193
|
|
|
|
|
2429
|
|
1096
|
2676
|
|
|
|
|
5205
|
while( $_ >= $twopi ) { $_ -= $twopi } |
|
0
|
|
|
|
|
0
|
|
1097
|
|
|
|
|
|
|
} |
1098
|
7500
|
100
|
|
|
|
16716
|
$_ = rad2deg($_) if $self->{angle_unit} eq 'degrees'; |
1099
|
|
|
|
|
|
|
} |
1100
|
|
|
|
|
|
|
} |
1101
|
|
|
|
|
|
|
|
1102
|
|
|
|
|
|
|
# _max |
1103
|
|
|
|
|
|
|
# |
1104
|
|
|
|
|
|
|
# Return the maximum of the two input arguments. |
1105
|
|
|
|
|
|
|
|
1106
|
|
|
|
|
|
|
sub _max { |
1107
|
10
|
100
|
|
10
|
|
37
|
$_[0] > $_[1] ? $_[0] : $_[1]; |
1108
|
|
|
|
|
|
|
} |
1109
|
|
|
|
|
|
|
|
1110
|
|
|
|
|
|
|
# _min |
1111
|
|
|
|
|
|
|
# |
1112
|
|
|
|
|
|
|
# Return the minimum of the two input arguments. |
1113
|
|
|
|
|
|
|
|
1114
|
|
|
|
|
|
|
sub _min { |
1115
|
10
|
100
|
|
10
|
|
25
|
$_[0] < $_[1] ? $_[0] : $_[1]; |
1116
|
|
|
|
|
|
|
} |
1117
|
|
|
|
|
|
|
|
1118
|
|
|
|
|
|
|
# _hypot |
1119
|
|
|
|
|
|
|
# |
1120
|
|
|
|
|
|
|
# Returns the length of the hypotenuse of a right-angle triangle given the |
1121
|
|
|
|
|
|
|
# length of the two catheti (the two other sides). The result is computed in a |
1122
|
|
|
|
|
|
|
# way that avoids problems that occur when squaring very large or very small |
1123
|
|
|
|
|
|
|
# numbers. |
1124
|
|
|
|
|
|
|
|
1125
|
|
|
|
|
|
|
sub _hypot { |
1126
|
10
|
|
|
10
|
|
18
|
my $x = abs($_[0]); |
1127
|
10
|
|
|
|
|
13
|
my $y = abs($_[1]); |
1128
|
10
|
|
|
|
|
31
|
my $z = _max($x, $y); |
1129
|
10
|
|
|
|
|
21
|
my $r = _min($x, $y) / $z; |
1130
|
10
|
|
|
|
|
51
|
return $z * sqrt(1 + $r * $r); |
1131
|
|
|
|
|
|
|
} |
1132
|
|
|
|
|
|
|
|
1133
|
|
|
|
|
|
|
=pod |
1134
|
|
|
|
|
|
|
|
1135
|
|
|
|
|
|
|
=back |
1136
|
|
|
|
|
|
|
|
1137
|
|
|
|
|
|
|
=head1 DEFINED ELLIPSOIDS |
1138
|
|
|
|
|
|
|
|
1139
|
|
|
|
|
|
|
The following ellipsoids are defined in Geo::Ellipsoid, with the |
1140
|
|
|
|
|
|
|
semi-major axis in meters and the reciprocal flattening as shown. |
1141
|
|
|
|
|
|
|
The default ellipsoid is WGS84. |
1142
|
|
|
|
|
|
|
|
1143
|
|
|
|
|
|
|
Ellipsoid Semi-Major Axis (m.) 1/Flattening |
1144
|
|
|
|
|
|
|
--------- ------------------- --------------- |
1145
|
|
|
|
|
|
|
AIRY 6377563.396 299.3249646 |
1146
|
|
|
|
|
|
|
AIRY-MODIFIED 6377340.189 299.3249646 |
1147
|
|
|
|
|
|
|
AUSTRALIAN 6378160.0 298.25 |
1148
|
|
|
|
|
|
|
BESSEL-1841 6377397.155 299.1528128 |
1149
|
|
|
|
|
|
|
CLARKE-1880 6378249.145 293.465 |
1150
|
|
|
|
|
|
|
EVEREST-1830 6377276.345 290.8017 |
1151
|
|
|
|
|
|
|
EVEREST-MODIFIED 6377304.063 290.8017 |
1152
|
|
|
|
|
|
|
FISHER-1960 6378166.0 298.3 |
1153
|
|
|
|
|
|
|
FISHER-1968 6378150.0 298.3 |
1154
|
|
|
|
|
|
|
GRS80 6378137.0 298.25722210088 |
1155
|
|
|
|
|
|
|
HOUGH-1956 6378270.0 297.0 |
1156
|
|
|
|
|
|
|
HAYFORD 6378388.0 297.0 |
1157
|
|
|
|
|
|
|
IAU76 6378140.0 298.257 |
1158
|
|
|
|
|
|
|
KRASSOVSKY-1938 6378245.0 298.3 |
1159
|
|
|
|
|
|
|
NAD27 6378206.4 294.9786982138 |
1160
|
|
|
|
|
|
|
NWL-9D 6378145.0 298.25 |
1161
|
|
|
|
|
|
|
SOUTHAMERICAN-1969 6378160.0 298.25 |
1162
|
|
|
|
|
|
|
SOVIET-1985 6378136.0 298.257 |
1163
|
|
|
|
|
|
|
WGS72 6378135.0 298.26 |
1164
|
|
|
|
|
|
|
WGS84 6378137.0 298.257223563 |
1165
|
|
|
|
|
|
|
|
1166
|
|
|
|
|
|
|
=head1 LIMITATIONS |
1167
|
|
|
|
|
|
|
|
1168
|
|
|
|
|
|
|
The methods should not be used on points which are too near the poles |
1169
|
|
|
|
|
|
|
(above or below 89 degrees), and should not be used on points which |
1170
|
|
|
|
|
|
|
are antipodal, i.e., exactly on opposite sides of the ellipsoid. The |
1171
|
|
|
|
|
|
|
methods will not return valid results in these cases. |
1172
|
|
|
|
|
|
|
|
1173
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
1174
|
|
|
|
|
|
|
|
1175
|
|
|
|
|
|
|
The conversion algorithms used here are Perl translations of Fortran |
1176
|
|
|
|
|
|
|
routines written by LCDR S NGS Rockville MD that implement |
1177
|
|
|
|
|
|
|
S Modified Rainsford's method with Helmert's elliptical |
1178
|
|
|
|
|
|
|
terms as published in "Direct and Inverse Solutions of Ellipsoid on |
1179
|
|
|
|
|
|
|
the Ellipsoid with Application of Nested Equations", S |
1180
|
|
|
|
|
|
|
Survey Review, April 1975. |
1181
|
|
|
|
|
|
|
|
1182
|
|
|
|
|
|
|
The Fortran source code files inverse.for and forward.for |
1183
|
|
|
|
|
|
|
may be obtained from |
1184
|
|
|
|
|
|
|
|
1185
|
|
|
|
|
|
|
ftp://ftp.ngs.noaa.gov/pub/pcsoft/for_inv.3d/source/ |
1186
|
|
|
|
|
|
|
|
1187
|
|
|
|
|
|
|
=head1 AUTHOR |
1188
|
|
|
|
|
|
|
|
1189
|
|
|
|
|
|
|
Peter John Acklam, C<< >> (current maintainer) |
1190
|
|
|
|
|
|
|
|
1191
|
|
|
|
|
|
|
Jim Gibson, C<< >> (original author) |
1192
|
|
|
|
|
|
|
|
1193
|
|
|
|
|
|
|
=head1 BUGS |
1194
|
|
|
|
|
|
|
|
1195
|
|
|
|
|
|
|
See LIMITATIONS, above. |
1196
|
|
|
|
|
|
|
|
1197
|
|
|
|
|
|
|
There are currently no known bugs. |
1198
|
|
|
|
|
|
|
|
1199
|
|
|
|
|
|
|
Please report any bugs or feature requests via |
1200
|
|
|
|
|
|
|
L. |
1201
|
|
|
|
|
|
|
|
1202
|
|
|
|
|
|
|
Old bug reports and feature requests can be found at |
1203
|
|
|
|
|
|
|
L. |
1204
|
|
|
|
|
|
|
|
1205
|
|
|
|
|
|
|
=head1 SUPPORT |
1206
|
|
|
|
|
|
|
|
1207
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
1208
|
|
|
|
|
|
|
|
1209
|
|
|
|
|
|
|
perldoc Geo::Ellipsoid |
1210
|
|
|
|
|
|
|
|
1211
|
|
|
|
|
|
|
You can also look for information at: |
1212
|
|
|
|
|
|
|
|
1213
|
|
|
|
|
|
|
=over 4 |
1214
|
|
|
|
|
|
|
|
1215
|
|
|
|
|
|
|
=item * |
1216
|
|
|
|
|
|
|
|
1217
|
|
|
|
|
|
|
GitHub |
1218
|
|
|
|
|
|
|
|
1219
|
|
|
|
|
|
|
GitHub is a code hosting platform for version control and collaboration. |
1220
|
|
|
|
|
|
|
|
1221
|
|
|
|
|
|
|
L |
1222
|
|
|
|
|
|
|
|
1223
|
|
|
|
|
|
|
=item * |
1224
|
|
|
|
|
|
|
|
1225
|
|
|
|
|
|
|
MetaCPAN |
1226
|
|
|
|
|
|
|
|
1227
|
|
|
|
|
|
|
A modern, open-source CPAN search engine, useful to view POD in HTML format. |
1228
|
|
|
|
|
|
|
|
1229
|
|
|
|
|
|
|
L |
1230
|
|
|
|
|
|
|
|
1231
|
|
|
|
|
|
|
=item * |
1232
|
|
|
|
|
|
|
|
1233
|
|
|
|
|
|
|
CPANTS |
1234
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
The CPANTS is a website that analyzes the Kwalitee (code metrics) of a |
1236
|
|
|
|
|
|
|
distribution. |
1237
|
|
|
|
|
|
|
|
1238
|
|
|
|
|
|
|
L |
1239
|
|
|
|
|
|
|
|
1240
|
|
|
|
|
|
|
=item * |
1241
|
|
|
|
|
|
|
|
1242
|
|
|
|
|
|
|
CPAN Testers Reports |
1243
|
|
|
|
|
|
|
|
1244
|
|
|
|
|
|
|
The CPAN Testers is a network of smoke testers who run automated tests on |
1245
|
|
|
|
|
|
|
uploaded CPAN distributions. |
1246
|
|
|
|
|
|
|
|
1247
|
|
|
|
|
|
|
L |
1248
|
|
|
|
|
|
|
|
1249
|
|
|
|
|
|
|
=item * |
1250
|
|
|
|
|
|
|
|
1251
|
|
|
|
|
|
|
CPAN Testers Matrix |
1252
|
|
|
|
|
|
|
|
1253
|
|
|
|
|
|
|
The CPAN Testers Matrix displays smoke test results for this distribution for |
1254
|
|
|
|
|
|
|
various combinations of Perl version and operating systems. |
1255
|
|
|
|
|
|
|
|
1256
|
|
|
|
|
|
|
L |
1257
|
|
|
|
|
|
|
|
1258
|
|
|
|
|
|
|
=back |
1259
|
|
|
|
|
|
|
|
1260
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
1261
|
|
|
|
|
|
|
|
1262
|
|
|
|
|
|
|
Copyright 2016-2021 Peter John Acklam, current maintainer. |
1263
|
|
|
|
|
|
|
|
1264
|
|
|
|
|
|
|
Copyright 2005-2008 Jim Gibson, all rights reserved. |
1265
|
|
|
|
|
|
|
|
1266
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
1267
|
|
|
|
|
|
|
under the same terms as Perl itself. |
1268
|
|
|
|
|
|
|
|
1269
|
|
|
|
|
|
|
=head1 SEE ALSO |
1270
|
|
|
|
|
|
|
|
1271
|
|
|
|
|
|
|
Geo::Distance, Geo::Ellipsoids |
1272
|
|
|
|
|
|
|
|
1273
|
|
|
|
|
|
|
=cut |
1274
|
|
|
|
|
|
|
|
1275
|
|
|
|
|
|
|
1; # End of Geo::Ellipsoid |