function [lon2,lat2,a21] = m_fdist(lon1,lat1,a12,s,spheroid)
% M_FDIST - On an ellipsoidal earth, compute location of
%         a point at a given bearing/distance, all using a vectorized
%         version of Vincenty's algorithm.
%
% [lon2,lat2,a21] = m_fdist(lon1,lat1,a12,s,spheroid)
%
% lat1 = GEODETIC latitude of first point (degrees)
% lon1 = longitude of first point (degrees)
% a12 = azimuth in degrees from first point to second point (forward)
% s = distance in meters  
% spheroid = (Optional) spheroid, defaults to 'wgs84'
%
% lat2, lon2 = second point (degrees)
% a21 = azimuth in degrees from second point to first point (backward)
%       (Azimuths are in degrees clockwise from north.)
%
% Inputs can be all the same size, or a mix of scalars and matrices.
% If a mixture, inputs are expanded as required.
%
%  Original algorithm source:
%  T. Vincenty, "Direct and Inverse Solutions of Geodesics on the Ellipsoid
%  with Application of Nested Equations", Survey Review, vol. 23, no. 176,
%  April 1975, pp 88-93.
%  Available at: http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf
%
% See M_IDIST, M_GEODESIC

% Notes: (1) lat1,lon1,a12,s can be any (identical) size/shape. Outputs
%            will have the same size and shape.
%        (2) Vincenty describes his distance algorithm as precise to within
%            0.01 millimeters, subject to the ellipsoidal model.
%        (3) code written by heavily modifying M. Kleder's 'vdist' in line
%            with original Vincenty paper. m_fdist and m_idist are inverses to within
%            1e-10 degrees.
%       (4) No warranties; use at your own risk.

% R. Pawlowicz (rich@ocgy.ubc.ca) 9/Jan/2005
%
% This software is provided "as is" without warranty of any kind. But
% it's mine, so you can't sell it.


pi180=pi/180;

MAP_ELLIP = struct ( 'normal', [1.0, 0], ...
    'sphere', [6370997.0, 0], ...
    'grs80' , [6378137.0, 1/298.257], ...
    'grs67' , [6378160.0, 1/247.247], ...
    'wgs84' , [6378137.0, 1/298.257223563], ... 
    'wgs72' , [6378135.0, 1/298.260], ...
    'wgs66' , [6378145.0, 1/298.250], ...
    'wgs60' , [6378165.0, 1/298.300], ...
    'clrk66', [6378206.4, 1/294.980], ...
    'clrk80', [6378249.1, 1/293.466], ...
    'intl24', [6378388.0, 1/297.000], ...
    'intl67', [6378157.5, 1/298.250]);

if nargin<5,
  spheroid='wgs84';
end;
ellip=getfield(MAP_ELLIP,spheroid); 
if length(ellip)~=2,
 disp(MAP_ELLIP);
 error('Spheroid not chosen from above list');
end;

% Do the equivalent of meshgrid-type calls to make all
% matrix sizes match. To do this we can have no more than
% two different numbers in any particular dimension, and one
% of these has to be a '1'.
allsize=[size(lon1);size(lat1);size(a12);size(s)];
i1=ones(1,size(allsize,2));
for k=1:size(allsize,2),
 rs=unique(allsize(:,k));
 if length(rs)==2 & rs(1)==1,
   j1=i1;j1(k)=rs(2);
   if allsize(1,k)==1,lon1=repmat(lon1,j1); end;
   if allsize(2,k)==1,lat1=repmat(lat1,j1); end;
   if allsize(3,k)==1,a12=repmat(a12,j1); end;
   if allsize(4,k)==1,s=repmat(s,j1); end;
 elseif length(rs)>2,
  error('incompatible array sizes!');  
 end;
end;

% reshape inputs
keepsize = size(lat1);
lat1=lat1(:);
lon1=lon1(:);
a12=a12(:);
s=s(:);
% Input check:
if any(abs(lat1)>90  )
    error('Input latitudes must be between -90 and 90 degrees, inclusive.')
end


 % correct for errors at exact poles by adjusting 0.6 millimeters:
kidx = abs(90-abs(lat1)) < 1e-10;
if any(kidx);
    lat1(kidx) = sign(lat1(kidx))*(90-(1e-10));
end

% Algorithm begins here

a=ellip(1);
b=a*(1-ellip(2));
f = (a-b)/a;
U1 = atan((1-f)*tan(lat1*pi180));
 
a12=a12*pi180;
sigma1=atan2( tan(U1),cos(a12));
alpha = asin( cos(U1).*sin(a12) );

u2 = cos(alpha).^2.*(a^2-b^2)/b^2;

A = 1+u2./16384.*(4096+u2.*(-768+u2.*(320-175.*u2)));
B = u2./1024.*(256+u2.*(-128+u2.*(74-47.*u2)));

sigmainit=s./(b*A);
sigma=sigmainit;
sigmaold=sigmainit;

itercount = 0;
k = logical(1+0*lat1);
deltasigma =0*lat1;
sigmam2=0*lat1;
cos2sigmam=0*lat1;
while any(k)  % force at least one execution
    itercount = itercount+1;
    if itercount > 50
        warning('More than 50 iterations');
        break
    end
    sigmam2(k)=2*sigma1(k)+sigma(k);
    cos2sigmam(k)=cos(sigmam2(k));
    deltasigma(k) = B(k).*sin(sigma(k)).*(cos2sigmam(k)+B(k)./4.*(cos(sigma(k)).*(-1+ ...
                   2.*cos2sigmam(k).^2)-B(k)./6.*cos2sigmam(k).*(-3+ ...
		   4.*sin(sigma(k)).^2).*(-3+4*cos2sigmam(k).^2)));
    sigmaold(k)=sigma(k);		 
    sigma(k)=sigmainit(k)+deltasigma(k);
    k = abs(sigma-sigmaold) > 1e-12;
end


numer=sin(sigma).*sin(a12);
denom=cos(U1).*cos(sigma) - sin(U1).*sin(sigma).*cos(a12);
lambda=atan2(numer,denom);
C = f/16*cos(alpha).^2.*(4+f*(4-3*cos(alpha).^2));
L=lambda-(1-C).*f.*sin(alpha).*(sigma+C.*sin(sigma).*(cos2sigmam+ ...
                           C.*cos(sigma).*(-1+2.*cos2sigmam.^2)));

lon2=mod(reshape((lon1+L/pi180),keepsize),360);

if nargout>1,
  numer=sin(U1).*cos(sigma)+cos(U1).*sin(sigma).*cos(a12);
  denom=(1-f)*sqrt(sin(alpha).^2 +  ...
                 (sin(U1).*sin(sigma) - cos(U1).*cos(sigma).*cos(a12)).^2 );
  lat2=reshape(atan2(numer,denom)/pi180,keepsize);
end;

if nargout>2,
  a21=mod(reshape( atan2( -sin(alpha),  ...
                       sin(U1).*sin(sigma)-cos(U1).*cos(sigma).*cos(a12) )/pi180, ...
                keepsize),360);
end;


