1 |
function [range,A12,A21]=dist(lat,long,argu1,argu2); |
2 |
% DIST Computes distance and bearing between points on the earth |
3 |
% using various reference spheroids. |
4 |
% |
5 |
% [RANGE,AF,AR]=DIST(LAT,LONG) computes the ranges RANGE between |
6 |
% points specified in the LAT and LONG vectors (decimal degrees with |
7 |
% positive indicating north/east). Forward and reverse bearings |
8 |
% (degrees) are returned in AF, AR. |
9 |
% |
10 |
% [RANGE,GLAT,GLONG]=DIST(LAT,LONG,N) computes N-point geodesics |
11 |
% between successive points. Each successive geodesic occupies |
12 |
% it's own row (N>=2) |
13 |
% |
14 |
% [..]=DIST(...,'ellipsoid') uses the specified ellipsoid |
15 |
% to get distances and bearing. Available ellipsoids are: |
16 |
% |
17 |
% 'clarke66' Clarke 1866 |
18 |
% 'iau73' IAU 1973 |
19 |
% 'wgs84' WGS 1984 |
20 |
% 'sphere' Sphere of radius 6371.0 km |
21 |
% |
22 |
% The default is 'wgs84'. |
23 |
% |
24 |
% Ellipsoid formulas are recommended for distance d<2000 km, |
25 |
% but can be used for longer distances. |
26 |
|
27 |
%Notes: RP (WHOI) 3/Dec/91 |
28 |
% Mostly copied from BDC "dist.f" routine (copied from ....?), but |
29 |
% then wildly modified to bring it in line with Matlab vectorization. |
30 |
% |
31 |
% RP (WHOI) 6/Dec/91 |
32 |
% Feeping Creaturism! - added geodesic computations. This turned |
33 |
% out to be pretty hairy since there were a lot of branch problems |
34 |
% with asin, atan when computing geodesics subtending > 90 degrees |
35 |
% that were ignored in the original code! |
36 |
% RP (WHOI) 15/Jan/91 |
37 |
% Fixed some bothersome special cases, like when computing geodesics |
38 |
% and N=2, or LAT=0... |
39 |
|
40 |
%C GIVEN THE LATITUDES AND LONGITUDES (IN DEG.) IT ASSUMES THE IAU SPHERO |
41 |
%C DEFINED IN THE NOTES ON PAGE 523 OF THE EXPLANATORY SUPPLEMENT TO THE |
42 |
%C AMERICAN EPHEMERIS. |
43 |
%C |
44 |
%C THIS PROGRAM COMPUTES THE DISTANCE ALONG THE NORMAL |
45 |
%C SECTION (IN M.) OF A SPECIFIED REFERENCE SPHEROID GIVEN |
46 |
%C THE GEODETIC LATITUDES AND LONGITUDES OF THE END POINTS |
47 |
%C *** IN DECIMAL DEGREES *** |
48 |
%C |
49 |
%C IT USES ROBBIN'S FORMULA, AS GIVEN BY BOMFORD, GEODESY, |
50 |
%C FOURTH EDITION, P. 122. CORRECT TO ONE PART IN 10**8 |
51 |
%C AT 1600 KM. ERRORS OF 20 M AT 5000 KM. |
52 |
%C |
53 |
%C CHECK: SMITHSONIAN METEOROLOGICAL TABLES, PP. 483 AND 484, |
54 |
%C GIVES LENGTHS OF ONE DEGREE OF LATITUDE AND LONGITUDE |
55 |
%C AS A FUNCTION OF LATITUDE. (SO DOES THE EPHEMERIS ABOVE) |
56 |
%C |
57 |
%C PETER WORCESTER, AS TOLD TO BRUCE CORNUELLE...1983 MAY 27 |
58 |
%C |
59 |
|
60 |
spheroid='wgs84'; |
61 |
geodes=0; |
62 |
if (nargin >= 3), |
63 |
if (isstr(argu1)), |
64 |
spheroid=argu1; |
65 |
else |
66 |
geodes=1; |
67 |
Ngeodes=argu1; |
68 |
if (Ngeodes <2), error('Must have at least 2 points in a goedesic!');end; |
69 |
if (nargin==4), spheroid=argu2; end; |
70 |
end; |
71 |
end; |
72 |
|
73 |
if (spheroid(1:3)=='sph'), |
74 |
A = 6371000.0; |
75 |
B = A; |
76 |
E = sqrt(A*A-B*B)/A; |
77 |
EPS= E*E/(1-E*E); |
78 |
elseif (spheroid(1:3)=='cla'), |
79 |
A = 6378206.4E0; |
80 |
B = 6356583.8E0; |
81 |
E= sqrt(A*A-B*B)/A; |
82 |
EPS = E*E/(1.-E*E); |
83 |
elseif(spheroid(1:3)=='iau'), |
84 |
A = 6378160.e0; |
85 |
B = 6356774.516E0; |
86 |
E = sqrt(A*A-B*B)/A; |
87 |
EPS = E*E/(1.-E*E); |
88 |
elseif(spheroid(1:3)=='wgs'), |
89 |
|
90 |
%c on 9/11/88, Peter Worcester gave me the constants for the |
91 |
%c WGS84 spheroid, and he gave A (semi-major axis), F = (A-B)/A |
92 |
%c (flattening) (where B is the semi-minor axis), and E is the |
93 |
%c eccentricity, E = ( (A**2 - B**2)**.5 )/ A |
94 |
%c the numbers from peter are: A=6378137.; 1/F = 298.257223563 |
95 |
%c E = 0.081819191 |
96 |
A = 6378137.; |
97 |
E = 0.081819191; |
98 |
EPS= E*E/(1.-E*E); |
99 |
|
100 |
B = sqrt(A^2-(E*A)^2); % added by D Menemenlis, 4 nov 97 |
101 |
|
102 |
else |
103 |
error('dist: Unknown spheroid specified!'); |
104 |
end; |
105 |
|
106 |
|
107 |
NN=max(size(lat)); |
108 |
if (NN ~= max(size(long))), |
109 |
error('dist: Lat, Long vectors of different sizes!'); |
110 |
end |
111 |
|
112 |
if (NN==size(lat)), rowvec=0; % It is easier if things are column vectors, |
113 |
else rowvec=1; end; % but we have to fix things before returning! |
114 |
|
115 |
lat=lat(:)*pi/180; % convert to radians |
116 |
long=long(:)*pi/180; |
117 |
|
118 |
lat(lat==0)=eps*ones(sum(lat==0),1); % Fixes some nasty 0/0 cases in the |
119 |
% geodesics stuff |
120 |
|
121 |
PHI1=lat(1:NN-1); % endpoints of each segment |
122 |
XLAM1=long(1:NN-1); |
123 |
PHI2=lat(2:NN); |
124 |
XLAM2=long(2:NN); |
125 |
|
126 |
% wiggle lines of constant lat to prevent numerical probs. |
127 |
if (any(PHI1==PHI2)), |
128 |
for ii=1:NN-1, |
129 |
if (PHI1(ii)==PHI2(ii)), PHI2(ii)=PHI2(ii)+ 1e-14; end; |
130 |
end; |
131 |
end; |
132 |
% wiggle lines of constant long to prevent numerical probs. |
133 |
if (any(XLAM1==XLAM2)), |
134 |
for ii=1:NN-1, |
135 |
if (XLAM1(ii)==XLAM2(ii)), XLAM2(ii)=XLAM2(ii)+ 1e-14; end; |
136 |
end; |
137 |
end; |
138 |
|
139 |
|
140 |
|
141 |
%C COMPUTE THE RADIUS OF CURVATURE IN THE PRIME VERTICAL FOR |
142 |
%C EACH POINT |
143 |
|
144 |
xnu=A./sqrt(1.0-(E*sin(lat)).^2); |
145 |
xnu1=xnu(1:NN-1); |
146 |
xnu2=xnu(2:NN); |
147 |
|
148 |
%C*** COMPUTE THE AZIMUTHS. A12 (A21) IS THE AZIMUTH AT POINT 1 (2) |
149 |
%C OF THE NORMAL SECTION CONTAINING THE POINT 2 (1) |
150 |
|
151 |
TPSI2=(1.-E*E)*tan(PHI2) + E*E*xnu1.*sin(PHI1)./(xnu2.*cos(PHI2)); |
152 |
PSI2=atan(TPSI2); |
153 |
|
154 |
%C*** SOME FORM OF ANGLE DIFFERENCE COMPUTED HERE?? |
155 |
|
156 |
DPHI2=PHI2-PSI2; |
157 |
DLAM=XLAM2-XLAM1; |
158 |
CTA12=(cos(PHI1).*TPSI2 - sin(PHI1).*cos(DLAM))./sin(DLAM); |
159 |
A12=atan((1.)./CTA12); |
160 |
CTA21P=(sin(PSI2).*cos(DLAM) - cos(PSI2).*tan(PHI1))./sin(DLAM); |
161 |
A21P=atan((1.)./CTA21P); |
162 |
|
163 |
%C GET THE QUADRANT RIGHT |
164 |
DLAM2=(abs(DLAM)<pi).*DLAM + (DLAM>=pi).*(-2*pi+DLAM) + ... |
165 |
(DLAM<=-pi).*(2*pi+DLAM); |
166 |
A12=A12+(A12<-pi)*2*pi-(A12>=pi)*2*pi; |
167 |
A12=A12+pi*sign(-A12).*( sign(A12) ~= sign(DLAM2) ); |
168 |
A21P=A21P+(A21P<-pi)*2*pi-(A21P>=pi)*2*pi; |
169 |
A21P=A21P+pi*sign(-A21P).*( sign(A21P) ~= sign(-DLAM2) ); |
170 |
%%A12*180/pi |
171 |
%%A21P*180/pi |
172 |
|
173 |
|
174 |
SSIG=sin(DLAM).*cos(PSI2)./sin(A12); |
175 |
% At this point we are OK if the angle < 90...but otherwise |
176 |
% we get the wrong branch of asin! |
177 |
% This fudge will correct every case on a sphere, and *almost* |
178 |
% every case on an ellipsoid (wrong hnadling will be when |
179 |
% angle is almost exactly 90 degrees) |
180 |
dd2=[cos(long).*cos(lat) sin(long).*cos(lat) sin(lat)]; |
181 |
dd2=sum((diff(dd2).*diff(dd2))')'; |
182 |
if ( any(abs(dd2-2) < 2*((B-A)/A))^2 ), |
183 |
disp('dist: Warning...point(s) too close to 90 degrees apart'); |
184 |
end; |
185 |
bigbrnch=dd2>2; |
186 |
|
187 |
SIG=asin(SSIG).*(bigbrnch==0) + (pi-asin(SSIG)).*bigbrnch; |
188 |
|
189 |
SSIGC=-sin(DLAM).*cos(PHI1)./sin(A21P); |
190 |
SIGC=asin(SSIGC); |
191 |
A21 = A21P - DPHI2.*sin(A21P).*tan(SIG/2.0); |
192 |
|
193 |
%C COMPUTE RANGE |
194 |
|
195 |
G2=EPS*(sin(PHI1)).^2; |
196 |
G=sqrt(G2); |
197 |
H2=EPS*(cos(PHI1).*cos(A12)).^2; |
198 |
H=sqrt(H2); |
199 |
TERM1=-SIG.*SIG.*H2.*(1.0-H2)/6.0; |
200 |
TERM2=(SIG.^3).*G.*H.*(1.0-2.0*H2)/8.0; |
201 |
TERM3=(SIG.^4).*(H2.*(4.0-7.0*H2)-3.0*G2.*(1.0-7.0*H2))/120.0; |
202 |
TERM4=-(SIG.^5).*G.*H/48.0; |
203 |
|
204 |
range=xnu1.*SIG.*(1.0+TERM1+TERM2+TERM3+TERM4); |
205 |
|
206 |
|
207 |
if (geodes), |
208 |
|
209 |
%c now calculate the locations along the ray path. (for extra accuracy, could |
210 |
%c do it from start to halfway, then from end for the rest, switching from A12 |
211 |
%c to A21... |
212 |
%c started to use Rudoe's formula, page 117 in Bomford...(1980, fourth edition) |
213 |
%c but then went to Clarke's best formula (pg 118) |
214 |
|
215 |
%RP I am doing this twice because this formula doesn't work when we go |
216 |
%past 90 degrees! |
217 |
Ngd1=round(Ngeodes/2); |
218 |
|
219 |
% First time...away from point 1 |
220 |
if (Ngd1>1), |
221 |
wns=ones(1,Ngd1); |
222 |
CP1CA12 = (cos(PHI1).*cos(A12)).^2; |
223 |
R2PRM = -EPS.*CP1CA12; |
224 |
R3PRM = 3.0*EPS.*(1.0-R2PRM).*cos(PHI1).*sin(PHI1).*cos(A12); |
225 |
C1 = R2PRM.*(1.0+R2PRM)/6.0*wns; |
226 |
C2 = R3PRM.*(1.0+3.0*R2PRM)/24.0*wns; |
227 |
R2PRM=R2PRM*wns; |
228 |
R3PRM=R3PRM*wns; |
229 |
|
230 |
%c now have to loop over positions |
231 |
RLRAT = (range./xnu1)*([0:Ngd1-1]/(Ngeodes-1)); |
232 |
|
233 |
THETA = RLRAT.*(1 - (RLRAT.^2).*(C1 - C2.*RLRAT)); |
234 |
C3 = 1.0 - (R2PRM.*(THETA.^2))/2.0 - (R3PRM.*(THETA.^3))/6.0; |
235 |
DSINPSI =(sin(PHI1)*wns).*cos(THETA) + ... |
236 |
((cos(PHI1).*cos(A12))*wns).*sin(THETA); |
237 |
%try to identify the branch...got to other branch if range> 1/4 circle |
238 |
PSI = asin(DSINPSI); |
239 |
|
240 |
DCOSPSI = cos(PSI); |
241 |
DSINDLA = (sin(A12)*wns).*sin(THETA)./DCOSPSI; |
242 |
DTANPHI=(1.0+EPS)*(1.0 - (E^2)*C3.*(sin(PHI1)*wns)./DSINPSI).*tan(PSI); |
243 |
%C compute output latitude (phi) and long (xla) in radians |
244 |
%c I believe these are absolute, and don't need source coords added |
245 |
PHI = atan(DTANPHI); |
246 |
% fix branch cut stuff - |
247 |
otherbrcnh= sign(DLAM2*wns) ~= sign([sign(DLAM2) diff(DSINDLA')'] ); |
248 |
XLA = XLAM1*wns + asin(DSINDLA).*(otherbrcnh==0) + ... |
249 |
(pi-asin(DSINDLA)).*(otherbrcnh); |
250 |
else |
251 |
PHI=PHI1; |
252 |
XLA=XLAM1; |
253 |
end; |
254 |
|
255 |
% Now we do the same thing, but in the reverse direction from the receiver! |
256 |
if (Ngeodes-Ngd1>1), |
257 |
wns=ones(1,Ngeodes-Ngd1); |
258 |
CP2CA21 = (cos(PHI2).*cos(A21)).^2; |
259 |
R2PRM = -EPS.*CP2CA21; |
260 |
R3PRM = 3.0*EPS.*(1.0-R2PRM).*cos(PHI2).*sin(PHI2).*cos(A21); |
261 |
C1 = R2PRM.*(1.0+R2PRM)/6.0*wns; |
262 |
C2 = R3PRM.*(1.0+3.0*R2PRM)/24.0*wns; |
263 |
R2PRM=R2PRM*wns; |
264 |
R3PRM=R3PRM*wns; |
265 |
|
266 |
%c now have to loop over positions |
267 |
RLRAT = (range./xnu2)*([0:Ngeodes-Ngd1-1]/(Ngeodes-1)); |
268 |
|
269 |
THETA = RLRAT.*(1 - (RLRAT.^2).*(C1 - C2.*RLRAT)); |
270 |
C3 = 1.0 - (R2PRM.*(THETA.^2))/2.0 - (R3PRM.*(THETA.^3))/6.0; |
271 |
DSINPSI =(sin(PHI2)*wns).*cos(THETA) + ... |
272 |
((cos(PHI2).*cos(A21))*wns).*sin(THETA); |
273 |
%try to identify the branch...got to other branch if range> 1/4 circle |
274 |
PSI = asin(DSINPSI); |
275 |
|
276 |
DCOSPSI = cos(PSI); |
277 |
DSINDLA = (sin(A21)*wns).*sin(THETA)./DCOSPSI; |
278 |
DTANPHI=(1.0+EPS)*(1.0 - (E^2)*C3.*(sin(PHI2)*wns)./DSINPSI).*tan(PSI); |
279 |
%C compute output latitude (phi) and long (xla) in radians |
280 |
%c I believe these are absolute, and don't need source coords added |
281 |
PHI = [PHI fliplr(atan(DTANPHI))]; |
282 |
% fix branch cut stuff |
283 |
otherbrcnh= sign(-DLAM2*wns) ~= sign( [sign(-DLAM2) diff(DSINDLA')'] ); |
284 |
XLA = [XLA fliplr(XLAM2*wns + asin(DSINDLA).*(otherbrcnh==0) + ... |
285 |
(pi-asin(DSINDLA)).*(otherbrcnh))]; |
286 |
else |
287 |
PHI = [PHI PHI2]; |
288 |
XLA = [XLA XLAM2]; |
289 |
end; |
290 |
|
291 |
%c convert to degrees |
292 |
A12 = PHI*180/pi; |
293 |
A21 = XLA*180/pi; |
294 |
range=range*([0:Ngeodes-1]/(Ngeodes-1)); |
295 |
|
296 |
|
297 |
else |
298 |
|
299 |
%C*** CONVERT TO DECIMAL DEGREES |
300 |
A12=A12*180/pi; |
301 |
A21=A21*180/pi; |
302 |
if (rowvec), |
303 |
range=range'; |
304 |
A12=A12'; |
305 |
A21=A21'; |
306 |
end; |
307 |
end; |
308 |
|
309 |
|
310 |
|
311 |
|