## Copyright (C) 2018-2022 Philip Nienhuis
##
## This program is free software; you can redistribute it and/or modify it
## under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or (at
## your option) any later version.
##
## This program is distributed in the hope that it will be useful, but
## WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSEll. See the GNU
## General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; see the file COPYING. If not, see
## .
## -*- texinfo -*-
## @deftypefn {Function File} {@var{r} =} rcurve (@var{spheroid}, @var{lat})
## @deftypefnx {Function File} {@var{r} =} rcurve (@var{type}, @var{spheroid}, @var{lat})
## @deftypefnx {Function File} {@var{r} =} rcurve (@dots{}, @var{angleUnit})
## Return the length of a curve based on its type: meridian, parallel, or
## transverse.
##
## Optional input argument @var{type} is one of "meridian", "parallel", or
## "transverse; default (when left empty or skipped) is "parallel".
## @var{spheroid} is the spheroid of choice (default: "wgs84"). @var{lat}
## is the latitude at which the curve length should be computed and can be
## a numeric scalar, vector or matrix. Output argument @var{r} will have the
## same size and dimension(s) as @var{lat}.
##
## Optional input argument @var{angleUnit} can be either "radians" or "degrees"
## (= default); just "r" or "d" will do. All character input is
## case-insensitive.
##
## Examples:
##
## @example
## r = rcurve ("parallel", "wgs84", 45)
## => r =
## 4.5176e+06
## Note: this is in meters
## @end example
##
## @example
## r = rcurve ("", 45)
## => r =
## 4.5176e+06
## @end example
##
## @example
## r = rcurve ("", "", 45)
## => r =
## 4.5176e+06
## @end example
##
## @example
## r = rcurve ("", "", pi/4, "radians")
## => r =
## 4.5176e+06
## @end example
##
## @example
## r = rcurve ("meridian", "wgs84", 45)
## => r =
## 6.3674e+06
## @end example
##
## @example
## r = rcurve ("transverse", "wgs84", 45)
## => r =
## 6.3888e+06
## @end example
##
## Also can use structures as inputs:
## @example
## r = rcurve("", referenceEllipsoid ("venus"), 45)
## => r =
## 4.2793e+06
## @end example
## @end deftypefn
## Function supplied by anonymous contributor, see:
## https://savannah.gnu.org/patch/index.php?9658
function r = rcurve (varargin)
if (nargin < 2 || nargin > 4)
print_usage ();
elseif (nargin == 2)
## Neither type nor angleUnit specified
type = "parallel";
angleUnit = "degrees";
spheroid = varargin{1};
lat = varargin{2};
ip = 1;
elseif (nargin >= 3)
if (isnumeric (varargin{2}) && isreal (varargin{2}))
## arg{1} = spheroid, type skipped
type = "parallel";
ip = 1;
elseif (isnumeric (varargin{3}) && isreal (varargin{3}))
## arg{1} = type, no angleunit given
angleUnit = "degrees";
ip = 0;
else
error ("rcurve: real numeric input expected for Lat");
endif
type = varargin{ip + 1};
spheroid = varargin{ip + 2};
lat = varargin{ip + 3};
endif
if (nargin == 4)
if (ischar (varargin{4}))
angleUnit = varargin{4};
else
error ("rcurve: 'degrees' or 'radians' expected for angleUnits");
endif
endif
if isempty (type)
type = "parallel";
endif
if (isnumeric (spheroid))
spheroid = num2str (spheroid);
endif
E = sph_chk (spheroid);
if (! ischar (angleUnit) || ! ismember (lower (angleUnit(1)), {"d", "r"}))
error ("rcurve: angleUnit should be one of 'degrees' or 'radians'")
endif
if (strncmpi (lower (angleUnit), "r", 1) == 1)
c_l = cos (lat);
s_l = sin (lat);
else
c_l = cosd (lat);
s_l = sind (lat);
endif
## Insight From: Algorithms for Global Positioning pg 370-372
e2 = E.Eccentricity ^ 2;
R = E.SemimajorAxis;
e_p = e2 / (1 - e2);
N = (R * sqrt ( 1 + e_p) ./ (sqrt (1 + e_p * c_l .^ 2)));
switch type
case {"meridian"}
w = sqrt (1 - e2 .* s_l .^ 2);
r = R * (1 - e2 ) ./ (w .^ 3);
case {"parallel"}
r = N .* c_l;
case {"transverse"}
r = N;
otherwise
error ("rcurve: type should be one of 'meridian', 'parallel', or 'transverse'")
endswitch
endfunction
%!test
%! assert (rcurve ("", 45), 4517590.87885, 10e-6)
%% Row vector
%!test
%! assert (rcurve ("", [45; 20]), [4517590.87885; 5995836.38390], 10e-6)
%% Column vector
%!test
%! assert (rcurve ("", [45, 20]), [4517590.87885, 5995836.38390], 10e-6)
%% Matrix
%!test
%! assert (rcurve ("", [60 45; 35 20]), [3197104.58692, 4517590.87885; 5230426.84020, 5995836.38390], 10e-6)
%!test
%! assert (rcurve ("", "", 45), 4517590.87885, 10e-6)
%!test
%! assert (rcurve ("transverse", "", 45), 6388838.29012, 10e-6)
%!test
%! assert (rcurve ("meridian", "", 45), 6367381.81562, 10e-6)
%!error rcurve ("","", 45, "km")
%!error rcurve ("", "", "A")
%!error rcurve ("", "", 45i)
%!error rcurve ('All', "", 45)