#include "geodesic.h"
#include <math.h>
#if !defined(HAVE_C99_MATH)
#define HAVE_C99_MATH 0
#endif
#define GEOGRAPHICLIB_GEODESIC_ORDER 6
#define nA1 GEOGRAPHICLIB_GEODESIC_ORDER
#define nC1 GEOGRAPHICLIB_GEODESIC_ORDER
#define nC1p GEOGRAPHICLIB_GEODESIC_ORDER
#define nA2 GEOGRAPHICLIB_GEODESIC_ORDER
#define nC2 GEOGRAPHICLIB_GEODESIC_ORDER
#define nA3 GEOGRAPHICLIB_GEODESIC_ORDER
#define nA3x nA3
#define nC3 GEOGRAPHICLIB_GEODESIC_ORDER
#define nC3x ((nC3 * (nC3 - 1)) / 2)
#define nC4 GEOGRAPHICLIB_GEODESIC_ORDER
#define nC4x ((nC4 * (nC4 + 1)) / 2)
#define nC (GEOGRAPHICLIB_GEODESIC_ORDER + 1)
typedef double real;
typedef int boolx;
static unsigned init = 0;
static const int FALSE = 0;
static const int TRUE = 1;
static unsigned digits, maxit1, maxit2;
static real epsilon, realmin, pi, degree, NaN,
tiny, tol0, tol1, tol2, tolb, xthresh;
static void Init() {
if (!init) {
#if defined(__DBL_MANT_DIG__)
digits = __DBL_MANT_DIG__;
#else
digits = 53;
#endif
#if defined(__DBL_EPSILON__)
epsilon = __DBL_EPSILON__;
#else
epsilon = pow(0.5, digits - 1);
#endif
#if defined(__DBL_MIN__)
realmin = __DBL_MIN__;
#else
realmin = pow(0.5, 1022);
#endif
#if defined(M_PI)
pi = M_PI;
#else
pi = atan2(0.0, -1.0);
#endif
maxit1 = 20;
maxit2 = maxit1 + digits + 10;
tiny = sqrt(realmin);
tol0 = epsilon;
tol1 = 200 * tol0;
tol2 = sqrt(tol0);
tolb = tol0 * tol2;
xthresh = 1000 * tol2;
degree = pi/180;
#if defined(NAN)
NaN = NAN;
#else
{
real minus1 = -1;
NaN = sqrt(minus1);
}
#endif
init = 1;
}
}
enum captype {
CAP_NONE = 0U,
CAP_C1 = 1U<<0,
CAP_C1p = 1U<<1,
CAP_C2 = 1U<<2,
CAP_C3 = 1U<<3,
CAP_C4 = 1U<<4,
CAP_ALL = 0x1FU,
OUT_ALL = 0x7F80U
};
static real sq(real x) { return x * x; }
#if HAVE_C99_MATH
#define atanhx atanh
#define copysignx copysign
#define hypotx hypot
#define cbrtx cbrt
#else
static real log1px(real x) {
volatile real
y = 1 + x,
z = y - 1;
return z == 0 ? x : x * log(y) / z;
}
static real atanhx(real x) {
real y = fabs(x);
y = log1px(2 * y/(1 - y))/2;
return x < 0 ? -y : y;
}
static real copysignx(real x, real y) {
return fabs(x) * (y < 0 || (y == 0 && 1/y < 0) ? -1 : 1);
}
static real hypotx(real x, real y)
{ return sqrt(x * x + y * y); }
static real cbrtx(real x) {
real y = pow(fabs(x), 1/(real)(3));
return x < 0 ? -y : y;
}
#endif
static real sumx(real u, real v, real* t) {
volatile real s = u + v;
volatile real up = s - v;
volatile real vpp = s - up;
up -= u;
vpp -= v;
if (t) *t = -(up + vpp);
return s;
}
static real polyval(int N, const real p[], real x) {
real y = N < 0 ? 0 : *p++;
while (--N >= 0) y = y * x + *p++;
return y;
}
static real minx(real a, real b)
{ return (b < a) ? b : a; }
static real maxx(real a, real b)
{ return (a < b) ? b : a; }
static void swapx(real* x, real* y)
{ real t = *x; *x = *y; *y = t; }
static void norm2(real* sinx, real* cosx) {
real r = hypotx(*sinx, *cosx);
*sinx /= r;
*cosx /= r;
}
static real AngNormalize(real x) {
#if HAVE_C99_MATH
x = remainder(x, (real)(360));
return x != -180 ? x : 180;
#else
real y = fmod(x, (real)(360));
#if defined(_MSC_VER) && _MSC_VER < 1900
if (x == 0) y = x;
#endif
return y <= -180 ? y + 360 : (y <= 180 ? y : y - 360);
#endif
}
static real LatFix(real x)
{ return fabs(x) > 90 ? NaN : x; }
static real AngDiff(real x, real y, real* e) {
real t, d = AngNormalize(sumx(AngNormalize(-x), AngNormalize(y), &t));
return sumx(d == 180 && t > 0 ? -180 : d, t, e);
}
static real AngRound(real x) {
const real z = 1/(real)(16);
volatile real y;
if (x == 0) return 0;
y = fabs(x);
y = y < z ? z - (z - y) : y;
return x < 0 ? -y : y;
}
static void sincosdx(real x, real* sinx, real* cosx) {
real r, s, c; int q;
#if HAVE_C99_MATH && !defined(__GNUC__)
r = remquo(x, (real)(90), &q);
#else
r = fmod(x, (real)(360));
q = (int)(floor(r / 90 + (real)(0.5)));
r -= 90 * q;
#endif
r *= degree;
s = sin(r); c = cos(r);
#if defined(_MSC_VER) && _MSC_VER < 1900
if (x == 0) s = x;
#endif
switch ((unsigned)q & 3U) {
case 0U: *sinx = s; *cosx = c; break;
case 1U: *sinx = c; *cosx = -s; break;
case 2U: *sinx = -s; *cosx = -c; break;
default: *sinx = -c; *cosx = s; break;
}
if (x != 0) { *sinx += (real)(0); *cosx += (real)(0); }
}
static real atan2dx(real y, real x) {
int q = 0; real ang;
if (fabs(y) > fabs(x)) { swapx(&x, &y); q = 2; }
if (x < 0) { x = -x; ++q; }
ang = atan2(y, x) / degree;
switch (q) {
case 1: ang = (y >= 0 ? 180 : -180) - ang; break;
case 2: ang = 90 - ang; break;
case 3: ang = -90 + ang; break;
}
return ang;
}
static void A3coeff(struct geod_geodesic* g);
static void C3coeff(struct geod_geodesic* g);
static void C4coeff(struct geod_geodesic* g);
static real SinCosSeries(boolx sinp,
real sinx, real cosx,
const real c[], int n);
static void Lengths(const struct geod_geodesic* g,
real eps, real sig12,
real ssig1, real csig1, real dn1,
real ssig2, real csig2, real dn2,
real cbet1, real cbet2,
real* ps12b, real* pm12b, real* pm0,
real* pM12, real* pM21,
real Ca[]);
static real Astroid(real x, real y);
static real InverseStart(const struct geod_geodesic* g,
real sbet1, real cbet1, real dn1,
real sbet2, real cbet2, real dn2,
real lam12, real slam12, real clam12,
real* psalp1, real* pcalp1,
real* psalp2, real* pcalp2,
real* pdnm,
real Ca[]);
static real Lambda12(const struct geod_geodesic* g,
real sbet1, real cbet1, real dn1,
real sbet2, real cbet2, real dn2,
real salp1, real calp1,
real slam120, real clam120,
real* psalp2, real* pcalp2,
real* psig12,
real* pssig1, real* pcsig1,
real* pssig2, real* pcsig2,
real* peps,
real* pgomg12,
boolx diffp, real* pdlam12,
real Ca[]);
static real A3f(const struct geod_geodesic* g, real eps);
static void C3f(const struct geod_geodesic* g, real eps, real c[]);
static void C4f(const struct geod_geodesic* g, real eps, real c[]);
static real A1m1f(real eps);
static void C1f(real eps, real c[]);
static void C1pf(real eps, real c[]);
static real A2m1f(real eps);
static void C2f(real eps, real c[]);
static int transit(real lon1, real lon2);
static int transitdirect(real lon1, real lon2);
static void accini(real s[]);
static void acccopy(const real s[], real t[]);
static void accadd(real s[], real y);
static real accsum(const real s[], real y);
static void accneg(real s[]);
void geod_init(struct geod_geodesic* g, real a, real f) {
if (!init) Init();
g->a = a;
g->f = f;
g->f1 = 1 - g->f;
g->e2 = g->f * (2 - g->f);
g->ep2 = g->e2 / sq(g->f1);
g->n = g->f / ( 2 - g->f);
g->b = g->a * g->f1;
g->c2 = (sq(g->a) + sq(g->b) *
(g->e2 == 0 ? 1 :
(g->e2 > 0 ? atanhx(sqrt(g->e2)) : atan(sqrt(-g->e2))) /
sqrt(fabs(g->e2))))/2;
g->etol2 = 0.1 * tol2 /
sqrt( maxx((real)(0.001), fabs(g->f)) * minx((real)(1), 1 - g->f/2) / 2 );
A3coeff(g);
C3coeff(g);
C4coeff(g);
}
static void geod_lineinit_int(struct geod_geodesicline* l,
const struct geod_geodesic* g,
real lat1, real lon1,
real azi1, real salp1, real calp1,
unsigned caps) {
real cbet1, sbet1, eps;
l->a = g->a;
l->f = g->f;
l->b = g->b;
l->c2 = g->c2;
l->f1 = g->f1;
l->caps = (caps ? caps : GEOD_DISTANCE_IN | GEOD_LONGITUDE) |
GEOD_LATITUDE | GEOD_AZIMUTH | GEOD_LONG_UNROLL;
l->lat1 = LatFix(lat1);
l->lon1 = lon1;
l->azi1 = azi1;
l->salp1 = salp1;
l->calp1 = calp1;
sincosdx(AngRound(l->lat1), &sbet1, &cbet1); sbet1 *= l->f1;
norm2(&sbet1, &cbet1); cbet1 = maxx(tiny, cbet1);
l->dn1 = sqrt(1 + g->ep2 * sq(sbet1));
l->salp0 = l->salp1 * cbet1;
l->calp0 = hypotx(l->calp1, l->salp1 * sbet1);
l->ssig1 = sbet1; l->somg1 = l->salp0 * sbet1;
l->csig1 = l->comg1 = sbet1 != 0 || l->calp1 != 0 ? cbet1 * l->calp1 : 1;
norm2(&l->ssig1, &l->csig1);
l->k2 = sq(l->calp0) * g->ep2;
eps = l->k2 / (2 * (1 + sqrt(1 + l->k2)) + l->k2);
if (l->caps & CAP_C1) {
real s, c;
l->A1m1 = A1m1f(eps);
C1f(eps, l->C1a);
l->B11 = SinCosSeries(TRUE, l->ssig1, l->csig1, l->C1a, nC1);
s = sin(l->B11); c = cos(l->B11);
l->stau1 = l->ssig1 * c + l->csig1 * s;
l->ctau1 = l->csig1 * c - l->ssig1 * s;
}
if (l->caps & CAP_C1p)
C1pf(eps, l->C1pa);
if (l->caps & CAP_C2) {
l->A2m1 = A2m1f(eps);
C2f(eps, l->C2a);
l->B21 = SinCosSeries(TRUE, l->ssig1, l->csig1, l->C2a, nC2);
}
if (l->caps & CAP_C3) {
C3f(g, eps, l->C3a);
l->A3c = -l->f * l->salp0 * A3f(g, eps);
l->B31 = SinCosSeries(TRUE, l->ssig1, l->csig1, l->C3a, nC3-1);
}
if (l->caps & CAP_C4) {
C4f(g, eps, l->C4a);
l->A4 = sq(l->a) * l->calp0 * l->salp0 * g->e2;
l->B41 = SinCosSeries(FALSE, l->ssig1, l->csig1, l->C4a, nC4);
}
l->a13 = l->s13 = NaN;
}
void geod_lineinit(struct geod_geodesicline* l,
const struct geod_geodesic* g,
real lat1, real lon1, real azi1, unsigned caps) {
real salp1, calp1;
azi1 = AngNormalize(azi1);
sincosdx(AngRound(azi1), &salp1, &calp1);
geod_lineinit_int(l, g, lat1, lon1, azi1, salp1, calp1, caps);
}
void geod_gendirectline(struct geod_geodesicline* l,
const struct geod_geodesic* g,
real lat1, real lon1, real azi1,
unsigned flags, real a12_s12,
unsigned caps) {
geod_lineinit(l, g, lat1, lon1, azi1, caps);
geod_gensetdistance(l, flags, a12_s12);
}
void geod_directline(struct geod_geodesicline* l,
const struct geod_geodesic* g,
real lat1, real lon1, real azi1,
real s12, unsigned caps) {
geod_gendirectline(l, g, lat1, lon1, azi1, GEOD_NOFLAGS, s12, caps);
}
real geod_genposition(const struct geod_geodesicline* l,
unsigned flags, real s12_a12,
real* plat2, real* plon2, real* pazi2,
real* ps12, real* pm12,
real* pM12, real* pM21,
real* pS12) {
real lat2 = 0, lon2 = 0, azi2 = 0, s12 = 0,
m12 = 0, M12 = 0, M21 = 0, S12 = 0;
real sig12, ssig12, csig12, B12 = 0, AB1 = 0;
real omg12, lam12, lon12;
real ssig2, csig2, sbet2, cbet2, somg2, comg2, salp2, calp2, dn2;
unsigned outmask =
(plat2 ? GEOD_LATITUDE : 0U) |
(plon2 ? GEOD_LONGITUDE : 0U) |
(pazi2 ? GEOD_AZIMUTH : 0U) |
(ps12 ? GEOD_DISTANCE : 0U) |
(pm12 ? GEOD_REDUCEDLENGTH : 0U) |
(pM12 || pM21 ? GEOD_GEODESICSCALE : 0U) |
(pS12 ? GEOD_AREA : 0U);
outmask &= l->caps & OUT_ALL;
if (!( TRUE &&
(flags & GEOD_ARCMODE || (l->caps & (GEOD_DISTANCE_IN & OUT_ALL))) ))
return NaN;
if (flags & GEOD_ARCMODE) {
sig12 = s12_a12 * degree;
sincosdx(s12_a12, &ssig12, &csig12);
} else {
real
tau12 = s12_a12 / (l->b * (1 + l->A1m1)),
s = sin(tau12),
c = cos(tau12);
B12 = - SinCosSeries(TRUE,
l->stau1 * c + l->ctau1 * s,
l->ctau1 * c - l->stau1 * s,
l->C1pa, nC1p);
sig12 = tau12 - (B12 - l->B11);
ssig12 = sin(sig12); csig12 = cos(sig12);
if (fabs(l->f) > 0.01) {
real serr;
ssig2 = l->ssig1 * csig12 + l->csig1 * ssig12;
csig2 = l->csig1 * csig12 - l->ssig1 * ssig12;
B12 = SinCosSeries(TRUE, ssig2, csig2, l->C1a, nC1);
serr = (1 + l->A1m1) * (sig12 + (B12 - l->B11)) - s12_a12 / l->b;
sig12 = sig12 - serr / sqrt(1 + l->k2 * sq(ssig2));
ssig12 = sin(sig12); csig12 = cos(sig12);
}
}
ssig2 = l->ssig1 * csig12 + l->csig1 * ssig12;
csig2 = l->csig1 * csig12 - l->ssig1 * ssig12;
dn2 = sqrt(1 + l->k2 * sq(ssig2));
if (outmask & (GEOD_DISTANCE | GEOD_REDUCEDLENGTH | GEOD_GEODESICSCALE)) {
if (flags & GEOD_ARCMODE || fabs(l->f) > 0.01)
B12 = SinCosSeries(TRUE, ssig2, csig2, l->C1a, nC1);
AB1 = (1 + l->A1m1) * (B12 - l->B11);
}
sbet2 = l->calp0 * ssig2;
cbet2 = hypotx(l->salp0, l->calp0 * csig2);
if (cbet2 == 0)
cbet2 = csig2 = tiny;
salp2 = l->salp0; calp2 = l->calp0 * csig2;
if (outmask & GEOD_DISTANCE)
s12 = flags & GEOD_ARCMODE ?
l->b * ((1 + l->A1m1) * sig12 + AB1) :
s12_a12;
if (outmask & GEOD_LONGITUDE) {
real E = copysignx(1, l->salp0);
somg2 = l->salp0 * ssig2; comg2 = csig2;
omg12 = flags & GEOD_LONG_UNROLL
? E * (sig12
- (atan2( ssig2, csig2) - atan2( l->ssig1, l->csig1))
+ (atan2(E * somg2, comg2) - atan2(E * l->somg1, l->comg1)))
: atan2(somg2 * l->comg1 - comg2 * l->somg1,
comg2 * l->comg1 + somg2 * l->somg1);
lam12 = omg12 + l->A3c *
( sig12 + (SinCosSeries(TRUE, ssig2, csig2, l->C3a, nC3-1)
- l->B31));
lon12 = lam12 / degree;
lon2 = flags & GEOD_LONG_UNROLL ? l->lon1 + lon12 :
AngNormalize(AngNormalize(l->lon1) + AngNormalize(lon12));
}
if (outmask & GEOD_LATITUDE)
lat2 = atan2dx(sbet2, l->f1 * cbet2);
if (outmask & GEOD_AZIMUTH)
azi2 = atan2dx(salp2, calp2);
if (outmask & (GEOD_REDUCEDLENGTH | GEOD_GEODESICSCALE)) {
real
B22 = SinCosSeries(TRUE, ssig2, csig2, l->C2a, nC2),
AB2 = (1 + l->A2m1) * (B22 - l->B21),
J12 = (l->A1m1 - l->A2m1) * sig12 + (AB1 - AB2);
if (outmask & GEOD_REDUCEDLENGTH)
m12 = l->b * ((dn2 * (l->csig1 * ssig2) - l->dn1 * (l->ssig1 * csig2))
- l->csig1 * csig2 * J12);
if (outmask & GEOD_GEODESICSCALE) {
real t = l->k2 * (ssig2 - l->ssig1) * (ssig2 + l->ssig1) /
(l->dn1 + dn2);
M12 = csig12 + (t * ssig2 - csig2 * J12) * l->ssig1 / l->dn1;
M21 = csig12 - (t * l->ssig1 - l->csig1 * J12) * ssig2 / dn2;
}
}
if (outmask & GEOD_AREA) {
real
B42 = SinCosSeries(FALSE, ssig2, csig2, l->C4a, nC4);
real salp12, calp12;
if (l->calp0 == 0 || l->salp0 == 0) {
salp12 = salp2 * l->calp1 - calp2 * l->salp1;
calp12 = calp2 * l->calp1 + salp2 * l->salp1;
} else {
salp12 = l->calp0 * l->salp0 *
(csig12 <= 0 ? l->csig1 * (1 - csig12) + ssig12 * l->ssig1 :
ssig12 * (l->csig1 * ssig12 / (1 + csig12) + l->ssig1));
calp12 = sq(l->salp0) + sq(l->calp0) * l->csig1 * csig2;
}
S12 = l->c2 * atan2(salp12, calp12) + l->A4 * (B42 - l->B41);
}
if (outmask & GEOD_LATITUDE)
*plat2 = lat2;
if (outmask & GEOD_LONGITUDE)
*plon2 = lon2;
if (outmask & GEOD_AZIMUTH)
*pazi2 = azi2;
if (outmask & GEOD_DISTANCE)
*ps12 = s12;
if (outmask & GEOD_REDUCEDLENGTH)
*pm12 = m12;
if (outmask & GEOD_GEODESICSCALE) {
if (pM12) *pM12 = M12;
if (pM21) *pM21 = M21;
}
if (outmask & GEOD_AREA)
*pS12 = S12;
return flags & GEOD_ARCMODE ? s12_a12 : sig12 / degree;
}
void geod_setdistance(struct geod_geodesicline* l, real s13) {
l->s13 = s13;
l->a13 = geod_genposition(l, GEOD_NOFLAGS, l->s13, 0, 0, 0, 0, 0, 0, 0, 0);
}
static void geod_setarc(struct geod_geodesicline* l, real a13) {
l->a13 = a13; l->s13 = NaN;
geod_genposition(l, GEOD_ARCMODE, l->a13, 0, 0, 0, &l->s13, 0, 0, 0, 0);
}
void geod_gensetdistance(struct geod_geodesicline* l,
unsigned flags, real s13_a13) {
flags & GEOD_ARCMODE ?
geod_setarc(l, s13_a13) :
geod_setdistance(l, s13_a13);
}
void geod_position(const struct geod_geodesicline* l, real s12,
real* plat2, real* plon2, real* pazi2) {
geod_genposition(l, FALSE, s12, plat2, plon2, pazi2, 0, 0, 0, 0, 0);
}
real geod_gendirect(const struct geod_geodesic* g,
real lat1, real lon1, real azi1,
unsigned flags, real s12_a12,
real* plat2, real* plon2, real* pazi2,
real* ps12, real* pm12, real* pM12, real* pM21,
real* pS12) {
struct geod_geodesicline l;
unsigned outmask =
(plat2 ? GEOD_LATITUDE : 0U) |
(plon2 ? GEOD_LONGITUDE : 0U) |
(pazi2 ? GEOD_AZIMUTH : 0U) |
(ps12 ? GEOD_DISTANCE : 0U) |
(pm12 ? GEOD_REDUCEDLENGTH : 0U) |
(pM12 || pM21 ? GEOD_GEODESICSCALE : 0U) |
(pS12 ? GEOD_AREA : 0U);
geod_lineinit(&l, g, lat1, lon1, azi1,
outmask |
(flags & GEOD_ARCMODE ? GEOD_NONE : GEOD_DISTANCE_IN));
return geod_genposition(&l, flags, s12_a12,
plat2, plon2, pazi2, ps12, pm12, pM12, pM21, pS12);
}
void geod_direct(const struct geod_geodesic* g,
real lat1, real lon1, real azi1,
real s12,
real* plat2, real* plon2, real* pazi2) {
geod_gendirect(g, lat1, lon1, azi1, GEOD_NOFLAGS, s12, plat2, plon2, pazi2,
0, 0, 0, 0, 0);
}
static real geod_geninverse_int(const struct geod_geodesic* g,
real lat1, real lon1, real lat2, real lon2,
real* ps12,
real* psalp1, real* pcalp1,
real* psalp2, real* pcalp2,
real* pm12, real* pM12, real* pM21,
real* pS12) {
real s12 = 0, m12 = 0, M12 = 0, M21 = 0, S12 = 0;
real lon12, lon12s;
int latsign, lonsign, swapp;
real sbet1, cbet1, sbet2, cbet2, s12x = 0, m12x = 0;
real dn1, dn2, lam12, slam12, clam12;
real a12 = 0, sig12, calp1 = 0, salp1 = 0, calp2 = 0, salp2 = 0;
real Ca[nC];
boolx meridian;
real omg12 = 0, somg12 = 2, comg12 = 0;
unsigned outmask =
(ps12 ? GEOD_DISTANCE : 0U) |
(pm12 ? GEOD_REDUCEDLENGTH : 0U) |
(pM12 || pM21 ? GEOD_GEODESICSCALE : 0U) |
(pS12 ? GEOD_AREA : 0U);
outmask &= OUT_ALL;
lon12 = AngDiff(lon1, lon2, &lon12s);
lonsign = lon12 >= 0 ? 1 : -1;
lon12 = lonsign * AngRound(lon12);
lon12s = AngRound((180 - lon12) - lonsign * lon12s);
lam12 = lon12 * degree;
if (lon12 > 90) {
sincosdx(lon12s, &slam12, &clam12);
clam12 = -clam12;
} else
sincosdx(lon12, &slam12, &clam12);
lat1 = AngRound(LatFix(lat1));
lat2 = AngRound(LatFix(lat2));
swapp = fabs(lat1) < fabs(lat2) ? -1 : 1;
if (swapp < 0) {
lonsign *= -1;
swapx(&lat1, &lat2);
}
latsign = lat1 < 0 ? 1 : -1;
lat1 *= latsign;
lat2 *= latsign;
sincosdx(lat1, &sbet1, &cbet1); sbet1 *= g->f1;
norm2(&sbet1, &cbet1); cbet1 = maxx(tiny, cbet1);
sincosdx(lat2, &sbet2, &cbet2); sbet2 *= g->f1;
norm2(&sbet2, &cbet2); cbet2 = maxx(tiny, cbet2);
if (cbet1 < -sbet1) {
if (cbet2 == cbet1)
sbet2 = sbet2 < 0 ? sbet1 : -sbet1;
} else {
if (fabs(sbet2) == -sbet1)
cbet2 = cbet1;
}
dn1 = sqrt(1 + g->ep2 * sq(sbet1));
dn2 = sqrt(1 + g->ep2 * sq(sbet2));
meridian = lat1 == -90 || slam12 == 0;
if (meridian) {
real ssig1, csig1, ssig2, csig2;
calp1 = clam12; salp1 = slam12;
calp2 = 1; salp2 = 0;
ssig1 = sbet1; csig1 = calp1 * cbet1;
ssig2 = sbet2; csig2 = calp2 * cbet2;
sig12 = atan2(maxx((real)(0), csig1 * ssig2 - ssig1 * csig2),
csig1 * csig2 + ssig1 * ssig2);
Lengths(g, g->n, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2,
cbet1, cbet2, &s12x, &m12x, 0,
outmask & GEOD_GEODESICSCALE ? &M12 : 0,
outmask & GEOD_GEODESICSCALE ? &M21 : 0,
Ca);
if (sig12 < 1 || m12x >= 0) {
if (sig12 < 3 * tiny)
sig12 = m12x = s12x = 0;
m12x *= g->b;
s12x *= g->b;
a12 = sig12 / degree;
} else
meridian = FALSE;
}
if (!meridian &&
sbet1 == 0 &&
(g->f <= 0 || lon12s >= g->f * 180)) {
calp1 = calp2 = 0; salp1 = salp2 = 1;
s12x = g->a * lam12;
sig12 = omg12 = lam12 / g->f1;
m12x = g->b * sin(sig12);
if (outmask & GEOD_GEODESICSCALE)
M12 = M21 = cos(sig12);
a12 = lon12 / g->f1;
} else if (!meridian) {
real dnm = 0;
sig12 = InverseStart(g, sbet1, cbet1, dn1, sbet2, cbet2, dn2,
lam12, slam12, clam12,
&salp1, &calp1, &salp2, &calp2, &dnm,
Ca);
if (sig12 >= 0) {
s12x = sig12 * g->b * dnm;
m12x = sq(dnm) * g->b * sin(sig12 / dnm);
if (outmask & GEOD_GEODESICSCALE)
M12 = M21 = cos(sig12 / dnm);
a12 = sig12 / degree;
omg12 = lam12 / (g->f1 * dnm);
} else {
real ssig1 = 0, csig1 = 0, ssig2 = 0, csig2 = 0, eps = 0, domg12 = 0;
unsigned numit = 0;
real salp1a = tiny, calp1a = 1, salp1b = tiny, calp1b = -1;
boolx tripn, tripb;
for (tripn = FALSE, tripb = FALSE; numit < maxit2; ++numit) {
real dv = 0,
v = Lambda12(g, sbet1, cbet1, dn1, sbet2, cbet2, dn2, salp1, calp1,
slam12, clam12,
&salp2, &calp2, &sig12, &ssig1, &csig1, &ssig2, &csig2,
&eps, &domg12, numit < maxit1, &dv, Ca);
if (tripb || !(fabs(v) >= (tripn ? 8 : 1) * tol0)) break;
if (v > 0 && (numit > maxit1 || calp1/salp1 > calp1b/salp1b))
{ salp1b = salp1; calp1b = calp1; }
else if (v < 0 && (numit > maxit1 || calp1/salp1 < calp1a/salp1a))
{ salp1a = salp1; calp1a = calp1; }
if (numit < maxit1 && dv > 0) {
real
dalp1 = -v/dv;
real
sdalp1 = sin(dalp1), cdalp1 = cos(dalp1),
nsalp1 = salp1 * cdalp1 + calp1 * sdalp1;
if (nsalp1 > 0 && fabs(dalp1) < pi) {
calp1 = calp1 * cdalp1 - salp1 * sdalp1;
salp1 = nsalp1;
norm2(&salp1, &calp1);
tripn = fabs(v) <= 16 * tol0;
continue;
}
}
salp1 = (salp1a + salp1b)/2;
calp1 = (calp1a + calp1b)/2;
norm2(&salp1, &calp1);
tripn = FALSE;
tripb = (fabs(salp1a - salp1) + (calp1a - calp1) < tolb ||
fabs(salp1 - salp1b) + (calp1 - calp1b) < tolb);
}
Lengths(g, eps, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2,
cbet1, cbet2, &s12x, &m12x, 0,
outmask & GEOD_GEODESICSCALE ? &M12 : 0,
outmask & GEOD_GEODESICSCALE ? &M21 : 0, Ca);
m12x *= g->b;
s12x *= g->b;
a12 = sig12 / degree;
if (outmask & GEOD_AREA) {
real sdomg12 = sin(domg12), cdomg12 = cos(domg12);
somg12 = slam12 * cdomg12 - clam12 * sdomg12;
comg12 = clam12 * cdomg12 + slam12 * sdomg12;
}
}
}
if (outmask & GEOD_DISTANCE)
s12 = 0 + s12x;
if (outmask & GEOD_REDUCEDLENGTH)
m12 = 0 + m12x;
if (outmask & GEOD_AREA) {
real
salp0 = salp1 * cbet1,
calp0 = hypotx(calp1, salp1 * sbet1);
real alp12;
if (calp0 != 0 && salp0 != 0) {
real
ssig1 = sbet1, csig1 = calp1 * cbet1,
ssig2 = sbet2, csig2 = calp2 * cbet2,
k2 = sq(calp0) * g->ep2,
eps = k2 / (2 * (1 + sqrt(1 + k2)) + k2),
A4 = sq(g->a) * calp0 * salp0 * g->e2;
real B41, B42;
norm2(&ssig1, &csig1);
norm2(&ssig2, &csig2);
C4f(g, eps, Ca);
B41 = SinCosSeries(FALSE, ssig1, csig1, Ca, nC4);
B42 = SinCosSeries(FALSE, ssig2, csig2, Ca, nC4);
S12 = A4 * (B42 - B41);
} else
S12 = 0;
if (!meridian && somg12 > 1) {
somg12 = sin(omg12); comg12 = cos(omg12);
}
if (!meridian &&
comg12 > -(real)(0.7071) &&
sbet2 - sbet1 < (real)(1.75)) {
real
domg12 = 1 + comg12, dbet1 = 1 + cbet1, dbet2 = 1 + cbet2;
alp12 = 2 * atan2( somg12 * ( sbet1 * dbet2 + sbet2 * dbet1 ),
domg12 * ( sbet1 * sbet2 + dbet1 * dbet2 ) );
} else {
real
salp12 = salp2 * calp1 - calp2 * salp1,
calp12 = calp2 * calp1 + salp2 * salp1;
if (salp12 == 0 && calp12 < 0) {
salp12 = tiny * calp1;
calp12 = -1;
}
alp12 = atan2(salp12, calp12);
}
S12 += g->c2 * alp12;
S12 *= swapp * lonsign * latsign;
S12 += 0;
}
if (swapp < 0) {
swapx(&salp1, &salp2);
swapx(&calp1, &calp2);
if (outmask & GEOD_GEODESICSCALE)
swapx(&M12, &M21);
}
salp1 *= swapp * lonsign; calp1 *= swapp * latsign;
salp2 *= swapp * lonsign; calp2 *= swapp * latsign;
if (psalp1) *psalp1 = salp1;
if (pcalp1) *pcalp1 = calp1;
if (psalp2) *psalp2 = salp2;
if (pcalp2) *pcalp2 = calp2;
if (outmask & GEOD_DISTANCE)
*ps12 = s12;
if (outmask & GEOD_REDUCEDLENGTH)
*pm12 = m12;
if (outmask & GEOD_GEODESICSCALE) {
if (pM12) *pM12 = M12;
if (pM21) *pM21 = M21;
}
if (outmask & GEOD_AREA)
*pS12 = S12;
return a12;
}
real geod_geninverse(const struct geod_geodesic* g,
real lat1, real lon1, real lat2, real lon2,
real* ps12, real* pazi1, real* pazi2,
real* pm12, real* pM12, real* pM21, real* pS12) {
real salp1, calp1, salp2, calp2,
a12 = geod_geninverse_int(g, lat1, lon1, lat2, lon2, ps12,
&salp1, &calp1, &salp2, &calp2,
pm12, pM12, pM21, pS12);
if (pazi1) *pazi1 = atan2dx(salp1, calp1);
if (pazi2) *pazi2 = atan2dx(salp2, calp2);
return a12;
}
void geod_inverseline(struct geod_geodesicline* l,
const struct geod_geodesic* g,
real lat1, real lon1, real lat2, real lon2,
unsigned caps) {
real salp1, calp1,
a12 = geod_geninverse_int(g, lat1, lon1, lat2, lon2, 0,
&salp1, &calp1, 0, 0,
0, 0, 0, 0),
azi1 = atan2dx(salp1, calp1);
caps = caps ? caps : GEOD_DISTANCE_IN | GEOD_LONGITUDE;
if (caps & (OUT_ALL & GEOD_DISTANCE_IN)) caps |= GEOD_DISTANCE;
geod_lineinit_int(l, g, lat1, lon1, azi1, salp1, calp1, caps);
geod_setarc(l, a12);
}
void geod_inverse(const struct geod_geodesic* g,
real lat1, real lon1, real lat2, real lon2,
real* ps12, real* pazi1, real* pazi2) {
geod_geninverse(g, lat1, lon1, lat2, lon2, ps12, pazi1, pazi2, 0, 0, 0, 0);
}
real SinCosSeries(boolx sinp, real sinx, real cosx, const real c[], int n) {
real ar, y0, y1;
c += (n + sinp);
ar = 2 * (cosx - sinx) * (cosx + sinx);
y0 = n & 1 ? *--c : 0; y1 = 0;
n /= 2;
while (n--) {
y1 = ar * y0 - y1 + *--c;
y0 = ar * y1 - y0 + *--c;
}
return sinp
? 2 * sinx * cosx * y0
: cosx * (y0 - y1);
}
void Lengths(const struct geod_geodesic* g,
real eps, real sig12,
real ssig1, real csig1, real dn1,
real ssig2, real csig2, real dn2,
real cbet1, real cbet2,
real* ps12b, real* pm12b, real* pm0,
real* pM12, real* pM21,
real Ca[]) {
real m0 = 0, J12 = 0, A1 = 0, A2 = 0;
real Cb[nC];
boolx redlp = pm12b || pm0 || pM12 || pM21;
if (ps12b || redlp) {
A1 = A1m1f(eps);
C1f(eps, Ca);
if (redlp) {
A2 = A2m1f(eps);
C2f(eps, Cb);
m0 = A1 - A2;
A2 = 1 + A2;
}
A1 = 1 + A1;
}
if (ps12b) {
real B1 = SinCosSeries(TRUE, ssig2, csig2, Ca, nC1) -
SinCosSeries(TRUE, ssig1, csig1, Ca, nC1);
*ps12b = A1 * (sig12 + B1);
if (redlp) {
real B2 = SinCosSeries(TRUE, ssig2, csig2, Cb, nC2) -
SinCosSeries(TRUE, ssig1, csig1, Cb, nC2);
J12 = m0 * sig12 + (A1 * B1 - A2 * B2);
}
} else if (redlp) {
int l;
for (l = 1; l <= nC2; ++l)
Cb[l] = A1 * Ca[l] - A2 * Cb[l];
J12 = m0 * sig12 + (SinCosSeries(TRUE, ssig2, csig2, Cb, nC2) -
SinCosSeries(TRUE, ssig1, csig1, Cb, nC2));
}
if (pm0) *pm0 = m0;
if (pm12b)
*pm12b = dn2 * (csig1 * ssig2) - dn1 * (ssig1 * csig2) -
csig1 * csig2 * J12;
if (pM12 || pM21) {
real csig12 = csig1 * csig2 + ssig1 * ssig2;
real t = g->ep2 * (cbet1 - cbet2) * (cbet1 + cbet2) / (dn1 + dn2);
if (pM12)
*pM12 = csig12 + (t * ssig2 - csig2 * J12) * ssig1 / dn1;
if (pM21)
*pM21 = csig12 - (t * ssig1 - csig1 * J12) * ssig2 / dn2;
}
}
real Astroid(real x, real y) {
real k;
real
p = sq(x),
q = sq(y),
r = (p + q - 1) / 6;
if ( !(q == 0 && r <= 0) ) {
real
S = p * q / 4,
r2 = sq(r),
r3 = r * r2,
disc = S * (S + 2 * r3);
real u = r;
real v, uv, w;
if (disc >= 0) {
real T3 = S + r3, T;
T3 += T3 < 0 ? -sqrt(disc) : sqrt(disc);
T = cbrtx(T3);
u += T + (T != 0 ? r2 / T : 0);
} else {
real ang = atan2(sqrt(-disc), -(S + r3));
u += 2 * r * cos(ang / 3);
}
v = sqrt(sq(u) + q);
uv = u < 0 ? q / (v - u) : u + v;
w = (uv - q) / (2 * v);
k = uv / (sqrt(uv + sq(w)) + w);
} else {
k = 0;
}
return k;
}
real InverseStart(const struct geod_geodesic* g,
real sbet1, real cbet1, real dn1,
real sbet2, real cbet2, real dn2,
real lam12, real slam12, real clam12,
real* psalp1, real* pcalp1,
real* psalp2, real* pcalp2,
real* pdnm,
real Ca[]) {
real salp1 = 0, calp1 = 0, salp2 = 0, calp2 = 0, dnm = 0;
real
sig12 = -1,
sbet12 = sbet2 * cbet1 - cbet2 * sbet1,
cbet12 = cbet2 * cbet1 + sbet2 * sbet1;
real sbet12a;
boolx shortline = cbet12 >= 0 && sbet12 < (real)(0.5) &&
cbet2 * lam12 < (real)(0.5);
real somg12, comg12, ssig12, csig12;
#if defined(__GNUC__) && __GNUC__ == 4 && \
(__GNUC_MINOR__ < 6 || defined(__MINGW32__))
{
volatile real xx1 = sbet2 * cbet1;
volatile real xx2 = cbet2 * sbet1;
sbet12a = xx1 + xx2;
}
#else
sbet12a = sbet2 * cbet1 + cbet2 * sbet1;
#endif
if (shortline) {
real sbetm2 = sq(sbet1 + sbet2), omg12;
sbetm2 /= sbetm2 + sq(cbet1 + cbet2);
dnm = sqrt(1 + g->ep2 * sbetm2);
omg12 = lam12 / (g->f1 * dnm);
somg12 = sin(omg12); comg12 = cos(omg12);
} else {
somg12 = slam12; comg12 = clam12;
}
salp1 = cbet2 * somg12;
calp1 = comg12 >= 0 ?
sbet12 + cbet2 * sbet1 * sq(somg12) / (1 + comg12) :
sbet12a - cbet2 * sbet1 * sq(somg12) / (1 - comg12);
ssig12 = hypotx(salp1, calp1);
csig12 = sbet1 * sbet2 + cbet1 * cbet2 * comg12;
if (shortline && ssig12 < g->etol2) {
salp2 = cbet1 * somg12;
calp2 = sbet12 - cbet1 * sbet2 *
(comg12 >= 0 ? sq(somg12) / (1 + comg12) : 1 - comg12);
norm2(&salp2, &calp2);
sig12 = atan2(ssig12, csig12);
} else if (fabs(g->n) > (real)(0.1) ||
csig12 >= 0 ||
ssig12 >= 6 * fabs(g->n) * pi * sq(cbet1)) {
} else {
real y, lamscale, betscale;
volatile real x;
real lam12x = atan2(-slam12, -clam12);
if (g->f >= 0) {
{
real
k2 = sq(sbet1) * g->ep2,
eps = k2 / (2 * (1 + sqrt(1 + k2)) + k2);
lamscale = g->f * cbet1 * A3f(g, eps) * pi;
}
betscale = lamscale * cbet1;
x = lam12x / lamscale;
y = sbet12a / betscale;
} else {
real
cbet12a = cbet2 * cbet1 - sbet2 * sbet1,
bet12a = atan2(sbet12a, cbet12a);
real m12b, m0;
Lengths(g, g->n, pi + bet12a,
sbet1, -cbet1, dn1, sbet2, cbet2, dn2,
cbet1, cbet2, 0, &m12b, &m0, 0, 0, Ca);
x = -1 + m12b / (cbet1 * cbet2 * m0 * pi);
betscale = x < -(real)(0.01) ? sbet12a / x :
-g->f * sq(cbet1) * pi;
lamscale = betscale / cbet1;
y = lam12x / lamscale;
}
if (y > -tol1 && x > -1 - xthresh) {
if (g->f >= 0) {
salp1 = minx((real)(1), -(real)(x)); calp1 = - sqrt(1 - sq(salp1));
} else {
calp1 = maxx((real)(x > -tol1 ? 0 : -1), (real)(x));
salp1 = sqrt(1 - sq(calp1));
}
} else {
real k = Astroid(x, y);
real
omg12a = lamscale * ( g->f >= 0 ? -x * k/(1 + k) : -y * (1 + k)/k );
somg12 = sin(omg12a); comg12 = -cos(omg12a);
salp1 = cbet2 * somg12;
calp1 = sbet12a - cbet2 * sbet1 * sq(somg12) / (1 - comg12);
}
}
if (!(salp1 <= 0))
norm2(&salp1, &calp1);
else {
salp1 = 1; calp1 = 0;
}
*psalp1 = salp1;
*pcalp1 = calp1;
if (shortline)
*pdnm = dnm;
if (sig12 >= 0) {
*psalp2 = salp2;
*pcalp2 = calp2;
}
return sig12;
}
real Lambda12(const struct geod_geodesic* g,
real sbet1, real cbet1, real dn1,
real sbet2, real cbet2, real dn2,
real salp1, real calp1,
real slam120, real clam120,
real* psalp2, real* pcalp2,
real* psig12,
real* pssig1, real* pcsig1,
real* pssig2, real* pcsig2,
real* peps,
real* pdomg12,
boolx diffp, real* pdlam12,
real Ca[]) {
real salp2 = 0, calp2 = 0, sig12 = 0,
ssig1 = 0, csig1 = 0, ssig2 = 0, csig2 = 0, eps = 0,
domg12 = 0, dlam12 = 0;
real salp0, calp0;
real somg1, comg1, somg2, comg2, somg12, comg12, lam12;
real B312, eta, k2;
if (sbet1 == 0 && calp1 == 0)
calp1 = -tiny;
salp0 = salp1 * cbet1;
calp0 = hypotx(calp1, salp1 * sbet1);
ssig1 = sbet1; somg1 = salp0 * sbet1;
csig1 = comg1 = calp1 * cbet1;
norm2(&ssig1, &csig1);
salp2 = cbet2 != cbet1 ? salp0 / cbet2 : salp1;
calp2 = cbet2 != cbet1 || fabs(sbet2) != -sbet1 ?
sqrt(sq(calp1 * cbet1) +
(cbet1 < -sbet1 ?
(cbet2 - cbet1) * (cbet1 + cbet2) :
(sbet1 - sbet2) * (sbet1 + sbet2))) / cbet2 :
fabs(calp1);
ssig2 = sbet2; somg2 = salp0 * sbet2;
csig2 = comg2 = calp2 * cbet2;
norm2(&ssig2, &csig2);
sig12 = atan2(maxx((real)(0), csig1 * ssig2 - ssig1 * csig2),
csig1 * csig2 + ssig1 * ssig2);
somg12 = maxx((real)(0), comg1 * somg2 - somg1 * comg2);
comg12 = comg1 * comg2 + somg1 * somg2;
eta = atan2(somg12 * clam120 - comg12 * slam120,
comg12 * clam120 + somg12 * slam120);
k2 = sq(calp0) * g->ep2;
eps = k2 / (2 * (1 + sqrt(1 + k2)) + k2);
C3f(g, eps, Ca);
B312 = (SinCosSeries(TRUE, ssig2, csig2, Ca, nC3-1) -
SinCosSeries(TRUE, ssig1, csig1, Ca, nC3-1));
domg12 = -g->f * A3f(g, eps) * salp0 * (sig12 + B312);
lam12 = eta + domg12;
if (diffp) {
if (calp2 == 0)
dlam12 = - 2 * g->f1 * dn1 / sbet1;
else {
Lengths(g, eps, sig12, ssig1, csig1, dn1, ssig2, csig2, dn2,
cbet1, cbet2, 0, &dlam12, 0, 0, 0, Ca);
dlam12 *= g->f1 / (calp2 * cbet2);
}
}
*psalp2 = salp2;
*pcalp2 = calp2;
*psig12 = sig12;
*pssig1 = ssig1;
*pcsig1 = csig1;
*pssig2 = ssig2;
*pcsig2 = csig2;
*peps = eps;
*pdomg12 = domg12;
if (diffp)
*pdlam12 = dlam12;
return lam12;
}
real A3f(const struct geod_geodesic* g, real eps) {
return polyval(nA3 - 1, g->A3x, eps);
}
void C3f(const struct geod_geodesic* g, real eps, real c[]) {
real mult = 1;
int o = 0, l;
for (l = 1; l < nC3; ++l) {
int m = nC3 - l - 1;
mult *= eps;
c[l] = mult * polyval(m, g->C3x + o, eps);
o += m + 1;
}
}
void C4f(const struct geod_geodesic* g, real eps, real c[]) {
real mult = 1;
int o = 0, l;
for (l = 0; l < nC4; ++l) {
int m = nC4 - l - 1;
c[l] = mult * polyval(m, g->C4x + o, eps);
o += m + 1;
mult *= eps;
}
}
real A1m1f(real eps) {
static const real coeff[] = {
1, 4, 64, 0, 256,
};
int m = nA1/2;
real t = polyval(m, coeff, sq(eps)) / coeff[m + 1];
return (t + eps) / (1 - eps);
}
void C1f(real eps, real c[]) {
static const real coeff[] = {
-1, 6, -16, 32,
-9, 64, -128, 2048,
9, -16, 768,
3, -5, 512,
-7, 1280,
-7, 2048,
};
real
eps2 = sq(eps),
d = eps;
int o = 0, l;
for (l = 1; l <= nC1; ++l) {
int m = (nC1 - l) / 2;
c[l] = d * polyval(m, coeff + o, eps2) / coeff[o + m + 1];
o += m + 2;
d *= eps;
}
}
void C1pf(real eps, real c[]) {
static const real coeff[] = {
205, -432, 768, 1536,
4005, -4736, 3840, 12288,
-225, 116, 384,
-7173, 2695, 7680,
3467, 7680,
38081, 61440,
};
real
eps2 = sq(eps),
d = eps;
int o = 0, l;
for (l = 1; l <= nC1p; ++l) {
int m = (nC1p - l) / 2;
c[l] = d * polyval(m, coeff + o, eps2) / coeff[o + m + 1];
o += m + 2;
d *= eps;
}
}
real A2m1f(real eps) {
static const real coeff[] = {
-11, -28, -192, 0, 256,
};
int m = nA2/2;
real t = polyval(m, coeff, sq(eps)) / coeff[m + 1];
return (t - eps) / (1 + eps);
}
void C2f(real eps, real c[]) {
static const real coeff[] = {
1, 2, 16, 32,
35, 64, 384, 2048,
15, 80, 768,
7, 35, 512,
63, 1280,
77, 2048,
};
real
eps2 = sq(eps),
d = eps;
int o = 0, l;
for (l = 1; l <= nC2; ++l) {
int m = (nC2 - l) / 2;
c[l] = d * polyval(m, coeff + o, eps2) / coeff[o + m + 1];
o += m + 2;
d *= eps;
}
}
void A3coeff(struct geod_geodesic* g) {
static const real coeff[] = {
-3, 128,
-2, -3, 64,
-1, -3, -1, 16,
3, -1, -2, 8,
1, -1, 2,
1, 1,
};
int o = 0, k = 0, j;
for (j = nA3 - 1; j >= 0; --j) {
int m = nA3 - j - 1 < j ? nA3 - j - 1 : j;
g->A3x[k++] = polyval(m, coeff + o, g->n) / coeff[o + m + 1];
o += m + 2;
}
}
void C3coeff(struct geod_geodesic* g) {
static const real coeff[] = {
3, 128,
2, 5, 128,
-1, 3, 3, 64,
-1, 0, 1, 8,
-1, 1, 4,
5, 256,
1, 3, 128,
-3, -2, 3, 64,
1, -3, 2, 32,
7, 512,
-10, 9, 384,
5, -9, 5, 192,
7, 512,
-14, 7, 512,
21, 2560,
};
int o = 0, k = 0, l, j;
for (l = 1; l < nC3; ++l) {
for (j = nC3 - 1; j >= l; --j) {
int m = nC3 - j - 1 < j ? nC3 - j - 1 : j;
g->C3x[k++] = polyval(m, coeff + o, g->n) / coeff[o + m + 1];
o += m + 2;
}
}
}
void C4coeff(struct geod_geodesic* g) {
static const real coeff[] = {
97, 15015,
1088, 156, 45045,
-224, -4784, 1573, 45045,
-10656, 14144, -4576, -858, 45045,
64, 624, -4576, 6864, -3003, 15015,
100, 208, 572, 3432, -12012, 30030, 45045,
1, 9009,
-2944, 468, 135135,
5792, 1040, -1287, 135135,
5952, -11648, 9152, -2574, 135135,
-64, -624, 4576, -6864, 3003, 135135,
8, 10725,
1856, -936, 225225,
-8448, 4992, -1144, 225225,
-1440, 4160, -4576, 1716, 225225,
-136, 63063,
1024, -208, 105105,
3584, -3328, 1144, 315315,
-128, 135135,
-2560, 832, 405405,
128, 99099,
};
int o = 0, k = 0, l, j;
for (l = 0; l < nC4; ++l) {
for (j = nC4 - 1; j >= l; --j) {
int m = nC4 - j - 1;
g->C4x[k++] = polyval(m, coeff + o, g->n) / coeff[o + m + 1];
o += m + 2;
}
}
}
int transit(real lon1, real lon2) {
real lon12;
lon1 = AngNormalize(lon1);
lon2 = AngNormalize(lon2);
lon12 = AngDiff(lon1, lon2, 0);
return lon1 <= 0 && lon2 > 0 && lon12 > 0 ? 1 :
(lon2 <= 0 && lon1 > 0 && lon12 < 0 ? -1 : 0);
}
int transitdirect(real lon1, real lon2) {
#if HAVE_C99_MATH
lon1 = remainder(lon1, (real)(720));
lon2 = remainder(lon2, (real)(720));
return ( (lon2 <= 0 && lon2 > -360 ? 1 : 0) -
(lon1 <= 0 && lon1 > -360 ? 1 : 0) );
#else
lon1 = fmod(lon1, (real)(720));
lon2 = fmod(lon2, (real)(720));
return ( ((lon2 <= 0 && lon2 > -360) || lon2 > 360 ? 1 : 0) -
((lon1 <= 0 && lon1 > -360) || lon1 > 360 ? 1 : 0) );
#endif
}
void accini(real s[]) {
s[0] = s[1] = 0;
}
void acccopy(const real s[], real t[]) {
t[0] = s[0]; t[1] = s[1];
}
void accadd(real s[], real y) {
real u, z = sumx(y, s[1], &u);
s[0] = sumx(z, s[0], &s[1]);
if (s[0] == 0)
s[0] = u;
else
s[1] = s[1] + u;
}
real accsum(const real s[], real y) {
real t[2];
acccopy(s, t);
accadd(t, y);
return t[0];
}
void accneg(real s[]) {
s[0] = -s[0]; s[1] = -s[1];
}
void geod_polygon_init(struct geod_polygon* p, boolx polylinep) {
p->polyline = (polylinep != 0);
geod_polygon_clear(p);
}
void geod_polygon_clear(struct geod_polygon* p) {
p->lat0 = p->lon0 = p->lat = p->lon = NaN;
accini(p->P);
accini(p->A);
p->num = p->crossings = 0;
}
void geod_polygon_addpoint(const struct geod_geodesic* g,
struct geod_polygon* p,
real lat, real lon) {
lon = AngNormalize(lon);
if (p->num == 0) {
p->lat0 = p->lat = lat;
p->lon0 = p->lon = lon;
} else {
real s12, S12 = 0;
geod_geninverse(g, p->lat, p->lon, lat, lon,
&s12, 0, 0, 0, 0, 0, p->polyline ? 0 : &S12);
accadd(p->P, s12);
if (!p->polyline) {
accadd(p->A, S12);
p->crossings += transit(p->lon, lon);
}
p->lat = lat; p->lon = lon;
}
++p->num;
}
void geod_polygon_addedge(const struct geod_geodesic* g,
struct geod_polygon* p,
real azi, real s) {
if (p->num) {
real lat, lon, S12 = 0;
geod_gendirect(g, p->lat, p->lon, azi, GEOD_LONG_UNROLL, s,
&lat, &lon, 0,
0, 0, 0, 0, p->polyline ? 0 : &S12);
accadd(p->P, s);
if (!p->polyline) {
accadd(p->A, S12);
p->crossings += transitdirect(p->lon, lon);
}
p->lat = lat; p->lon = lon;
++p->num;
}
}
unsigned geod_polygon_compute(const struct geod_geodesic* g,
const struct geod_polygon* p,
boolx reverse, boolx sign,
real* pA, real* pP) {
real s12, S12, t[2], area0;
int crossings;
if (p->num < 2) {
if (pP) *pP = 0;
if (!p->polyline && pA) *pA = 0;
return p->num;
}
if (p->polyline) {
if (pP) *pP = p->P[0];
return p->num;
}
geod_geninverse(g, p->lat, p->lon, p->lat0, p->lon0,
&s12, 0, 0, 0, 0, 0, &S12);
if (pP) *pP = accsum(p->P, s12);
acccopy(p->A, t);
accadd(t, S12);
crossings = p->crossings + transit(p->lon, p->lon0);
area0 = 4 * pi * g->c2;
if (crossings & 1)
accadd(t, (t[0] < 0 ? 1 : -1) * area0/2);
if (!reverse)
accneg(t);
if (sign) {
if (t[0] > area0/2)
accadd(t, -area0);
else if (t[0] <= -area0/2)
accadd(t, +area0);
} else {
if (t[0] >= area0)
accadd(t, -area0);
else if (t[0] < 0)
accadd(t, +area0);
}
if (pA) *pA = 0 + t[0];
return p->num;
}
unsigned geod_polygon_testpoint(const struct geod_geodesic* g,
const struct geod_polygon* p,
real lat, real lon,
boolx reverse, boolx sign,
real* pA, real* pP) {
real perimeter, tempsum, area0;
int crossings, i;
unsigned num = p->num + 1;
if (num == 1) {
if (pP) *pP = 0;
if (!p->polyline && pA) *pA = 0;
return num;
}
perimeter = p->P[0];
tempsum = p->polyline ? 0 : p->A[0];
crossings = p->crossings;
for (i = 0; i < (p->polyline ? 1 : 2); ++i) {
real s12, S12 = 0;
geod_geninverse(g,
i == 0 ? p->lat : lat, i == 0 ? p->lon : lon,
i != 0 ? p->lat0 : lat, i != 0 ? p->lon0 : lon,
&s12, 0, 0, 0, 0, 0, p->polyline ? 0 : &S12);
perimeter += s12;
if (!p->polyline) {
tempsum += S12;
crossings += transit(i == 0 ? p->lon : lon,
i != 0 ? p->lon0 : lon);
}
}
if (pP) *pP = perimeter;
if (p->polyline)
return num;
area0 = 4 * pi * g->c2;
if (crossings & 1)
tempsum += (tempsum < 0 ? 1 : -1) * area0/2;
if (!reverse)
tempsum *= -1;
if (sign) {
if (tempsum > area0/2)
tempsum -= area0;
else if (tempsum <= -area0/2)
tempsum += area0;
} else {
if (tempsum >= area0)
tempsum -= area0;
else if (tempsum < 0)
tempsum += area0;
}
if (pA) *pA = 0 + tempsum;
return num;
}
unsigned geod_polygon_testedge(const struct geod_geodesic* g,
const struct geod_polygon* p,
real azi, real s,
boolx reverse, boolx sign,
real* pA, real* pP) {
real perimeter, tempsum, area0;
int crossings;
unsigned num = p->num + 1;
if (num == 1) {
if (pP) *pP = NaN;
if (!p->polyline && pA) *pA = NaN;
return 0;
}
perimeter = p->P[0] + s;
if (p->polyline) {
if (pP) *pP = perimeter;
return num;
}
tempsum = p->A[0];
crossings = p->crossings;
{
real lat, lon, s12, S12;
geod_gendirect(g, p->lat, p->lon, azi, GEOD_LONG_UNROLL, s,
&lat, &lon, 0,
0, 0, 0, 0, &S12);
tempsum += S12;
crossings += transitdirect(p->lon, lon);
geod_geninverse(g, lat, lon, p->lat0, p->lon0,
&s12, 0, 0, 0, 0, 0, &S12);
perimeter += s12;
tempsum += S12;
crossings += transit(lon, p->lon0);
}
area0 = 4 * pi * g->c2;
if (crossings & 1)
tempsum += (tempsum < 0 ? 1 : -1) * area0/2;
if (!reverse)
tempsum *= -1;
if (sign) {
if (tempsum > area0/2)
tempsum -= area0;
else if (tempsum <= -area0/2)
tempsum += area0;
} else {
if (tempsum >= area0)
tempsum -= area0;
else if (tempsum < 0)
tempsum += area0;
}
if (pP) *pP = perimeter;
if (pA) *pA = 0 + tempsum;
return num;
}
void geod_polygonarea(const struct geod_geodesic* g,
real lats[], real lons[], int n,
real* pA, real* pP) {
int i;
struct geod_polygon p;
geod_polygon_init(&p, FALSE);
for (i = 0; i < n; ++i)
geod_polygon_addpoint(g, &p, lats[i], lons[i]);
geod_polygon_compute(g, &p, FALSE, TRUE, pA, pP);
}