aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog2
-rw-r--r--src/geodesic.c86
-rw-r--r--src/geodesic.h41
3 files changed, 87 insertions, 42 deletions
diff --git a/ChangeLog b/ChangeLog
index 0c9353dc..d78679e4 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,8 @@
* Preparing 4.9.0 beta release.
+ * src/geodesic.{c,h}: sync relative to GeographicLib 1.31. (#216)
+
* src/pj_fileapi.c, etc: Implement a virtual file api accessable
through the context for init file and grid shift file access.
diff --git a/src/geodesic.c b/src/geodesic.c
index c5d481ee..957f2144 100644
--- a/src/geodesic.c
+++ b/src/geodesic.c
@@ -195,6 +195,8 @@ static real InverseStart(const struct geod_geodesic* g,
real* psalp1, real* pcalp1,
/* Only updated if return val >= 0 */
real* psalp2, real* pcalp2,
+ /* Only updated for short lines */
+ real* pdnm,
/* Scratch areas of the right size */
real C1a[], real C2a[]);
static real Lambda12(const struct geod_geodesic* g,
@@ -218,6 +220,8 @@ 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 void accini(real s[]);
+static void accadd(real s[], real y);
/** @endcond */
@@ -234,8 +238,18 @@ void geod_init(struct geod_geodesic* g, real a, real f) {
(g->e2 == 0 ? 1 :
(g->e2 > 0 ? atanhx(sqrt(g->e2)) : atan(sqrt(-g->e2))) /
sqrt(fabs(g->e2))))/2; /* authalic radius squared */
- /* The sig12 threshold for "really short" */
- g->etol2 = 0.01 * tol2 / maxx((real)(0.1), sqrt(fabs(g->e2)));
+ /* The sig12 threshold for "really short". Using the auxiliary sphere
+ * solution with dnm computed at (bet1 + bet2) / 2, the relative error in the
+ * azimuth consistency check is sig12^2 * abs(f) * min(1, 1-f/2) / 2. (Error
+ * measured for 1/100 < b/a < 100 and abs(f) >= 1/1000. For a given f and
+ * sig12, the max error occurs for lines near the pole. If the old rule for
+ * computing dnm = (dn1 + dn2)/2 is used, then the error increases by a
+ * factor of 2.) Setting this equal to epsilon gives sig12 = etol2. Here
+ * 0.1 is a safety factor (error decreased by 100) and max(0.001, abs(f))
+ * stops etol2 getting too large in the nearly spherical case. */
+ 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);
@@ -716,14 +730,14 @@ real geod_geninverse(const struct geod_geodesic* g,
* meridian and geodesic is neither meridional or equatorial. */
/* Figure a starting point for Newton's method */
+ real dnm = 0;
sig12 = InverseStart(g, sbet1, cbet1, dn1, sbet2, cbet2, dn2,
lam12,
- &salp1, &calp1, &salp2, &calp2,
+ &salp1, &calp1, &salp2, &calp2, &dnm,
C1a, C2a);
if (sig12 >= 0) {
- /* Short lines (InverseStart sets salp2, calp2) */
- real dnm = (dn1 + dn2) / 2;
+ /* Short lines (InverseStart sets salp2, calp2, dnm) */
s12x = sig12 * g->b * dnm;
m12x = sq(dnm) * g->b * sin(sig12 / dnm);
if (outmask & GEOD_GEODESICSCALE)
@@ -1046,9 +1060,11 @@ real InverseStart(const struct geod_geodesic* g,
real* psalp1, real* pcalp1,
/* Only updated if return val >= 0 */
real* psalp2, real* pcalp2,
+ /* Only updated for short lines */
+ real* pdnm,
/* Scratch areas of the right size */
real C1a[], real C2a[]) {
- real salp1 = 0, calp1 = 0, salp2 = 0, calp2 = 0;
+ real salp1 = 0, calp1 = 0, salp2 = 0, calp2 = 0, dnm = 0;
/* Return a starting point for Newton's method in salp1 and calp1 (function
* value is -1). If Newton's method doesn't need to be used, return also
@@ -1076,11 +1092,17 @@ real InverseStart(const struct geod_geodesic* g,
real sbet12a = sbet2 * cbet1 + cbet2 * sbet1;
#endif
boolx shortline = cbet12 >= 0 && sbet12 < (real)(0.5) &&
- lam12 <= pi / 6;
- real
- omg12 = !shortline ? lam12 : lam12 / (g->f1 * (dn1 + dn2) / 2),
- somg12 = sin(omg12), comg12 = cos(omg12);
- real ssig12, csig12;
+ cbet2 * lam12 < (real)(0.5);
+ real omg12 = lam12, somg12, comg12, ssig12, csig12;
+ if (shortline) {
+ real sbetm2 = sq(sbet1 + sbet2);
+ /* sin((bet1+bet2)/2)^2
+ * = (sbet1 + sbet2)^2 / ((sbet1 + sbet2)^2 + (cbet1 + cbet2)^2) */
+ sbetm2 /= sbetm2 + sq(cbet1 + cbet2);
+ dnm = sqrt(1 + g->ep2 * sbetm2);
+ omg12 /= g->f1 * dnm;
+ }
+ somg12 = sin(omg12); comg12 = cos(omg12);
salp1 = cbet2 * somg12;
calp1 = comg12 >= 0 ?
@@ -1093,7 +1115,8 @@ real InverseStart(const struct geod_geodesic* g,
if (shortline && ssig12 < g->etol2) {
/* really short lines */
salp2 = cbet1 * somg12;
- calp2 = sbet12 - cbet1 * sbet2 * sq(somg12) / (1 + comg12);
+ calp2 = sbet12 - cbet1 * sbet2 *
+ (comg12 >= 0 ? sq(somg12) / (1 + comg12) : 1 - comg12);
SinCosNorm(&salp2, &calp2);
/* Set return value */
sig12 = atan2(ssig12, csig12);
@@ -1200,6 +1223,8 @@ real InverseStart(const struct geod_geodesic* g,
*psalp1 = salp1;
*pcalp1 = calp1;
+ if (shortline)
+ *pdnm = dnm;
if (sig12 >= 0) {
*psalp2 = salp2;
*pcalp2 = calp2;
@@ -1492,28 +1517,45 @@ int transit(real lon1, real lon2) {
(lon2 < 0 && lon1 >= 0 && lon12 < 0 ? -1 : 0);
}
+void accini(real s[]) {
+ /* Initialize an accumulator; this is an array with two elements. */
+ s[0] = s[1] = 0;
+}
+
+void accadd(real s[], real y) {
+ /* Add y to an accumulator. */
+ 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;
+}
+
/** @endcond */
void geod_polygonarea(const struct geod_geodesic* g,
real lats[], real lons[], int n,
real* pA, real* pP) {
int i, crossings = 0;
- real area0 = 4 * pi * g->c2, A = 0, P = 0;
+ real area0 = 4 * pi * g->c2, A[2], P[2];
+ accini(A); accini(P);
for (i = 0; i < n; ++i) {
real s12, S12;
geod_geninverse(g, lats[i], lons[i], lats[(i + 1) % n], lons[(i + 1) % n],
&s12, 0, 0, 0, 0, 0, &S12);
- P += s12;
- A -= S12; /* The minus sign is due to the counter-clockwise convention */
+ accadd(P, s12);
+ /* The minus sign is due to the counter-clockwise convention */
+ accadd(A, -S12);
crossings += transit(lons[i], lons[(i + 1) % n]);
}
if (crossings & 1)
- A += (A < 0 ? 1 : -1) * area0/2;
+ accadd(A, (A[0] < 0 ? 1 : -1) * area0/2);
/* Put area in (-area0/2, area0/2] */
- if (A > area0/2)
- A -= area0;
- else if (A <= -area0/2)
- A += area0;
- if (pA) *pA = A;
- if (pP) *pP = P;
+ if (A[0] > area0/2)
+ accadd(A, -area0);
+ else if (A[0] <= -area0/2)
+ accadd(A, +area0);
+ if (pA) *pA = A[0];
+ if (pP) *pP = P[0];
}
diff --git a/src/geodesic.h b/src/geodesic.h
index 2c49d0dc..87d087a5 100644
--- a/src/geodesic.h
+++ b/src/geodesic.h
@@ -116,7 +116,7 @@
* http://geographiclib.sourceforge.net/
*
* This library was distributed with
- * <a href="../index.html">GeographicLib</a> 1.30.
+ * <a href="../index.html">GeographicLib</a> 1.31.
**********************************************************************/
#if !defined(GEODESIC_H)
@@ -127,7 +127,8 @@ extern "C" {
#endif
/**
- * The struct containing information about the ellipsoid.
+ * The struct containing information about the ellipsoid. This must be
+ * initialized by geod_init() before use.
**********************************************************************/
struct geod_geodesic {
double a; /**< the equatorial radius */
@@ -139,7 +140,8 @@ extern "C" {
};
/**
- * The struct containing information about a single geodesic.
+ * The struct containing information about a single geodesic. This must be
+ * initialized by geod_lineinit() before use.
**********************************************************************/
struct geod_geodesicline {
double lat1; /**< the starting latitude */
@@ -182,17 +184,17 @@ extern "C" {
* should be in the range [&minus;90&deg;, 90&deg;]; \e azi1 should be in the
* range [&minus;540&deg;, 540&deg;).
*
- * The geod_mask values are
+ * The geod_mask values are [see geod_mask()]:
* - \e caps |= GEOD_LATITUDE for the latitude \e lat2; this is
- * added automatically
- * - \e caps |= GEOD_LONGITUDE for the latitude \e lon2
+ * added automatically,
+ * - \e caps |= GEOD_LONGITUDE for the latitude \e lon2,
* - \e caps |= GEOD_AZIMUTH for the latitude \e azi2; this is
- * added automatically
- * - \e caps |= GEOD_DISTANCE for the distance \e s12
- * - \e caps |= GEOD_REDUCEDLENGTH for the reduced length \e m12
+ * added automatically,
+ * - \e caps |= GEOD_DISTANCE for the distance \e s12,
+ * - \e caps |= GEOD_REDUCEDLENGTH for the reduced length \e m12,
* - \e caps |= GEOD_GEODESICSCALE for the geodesic scales \e M12
- * and \e M21
- * - \e caps |= GEOD_AREA for the area \e S12
+ * and \e M21,
+ * - \e caps |= GEOD_AREA for the area \e S12,
* - \e caps |= GEOD_DISTANCE_IN permits the length of the
* geodesic to be given in terms of \e s12; without this capability the
* length can only be specified in terms of arc length.
@@ -226,11 +228,11 @@ extern "C" {
* need some quantities computed.
*
* If either point is at a pole, the azimuth is defined by keeping the
- * longitude fixed and writing \e lat = &plusmn;(90&deg; &minus; &epsilon;)
- * and taking the limit &epsilon; &rarr; 0+. An arc length greater that
- * 180&deg; signifies a geodesic which is not a shortest path. (For a
- * prolate ellipsoid, an additional condition is necessary for a shortest
- * path: the longitudinal extent must not exceed of 180&deg;.)
+ * longitude fixed, writing \e lat = &plusmn;(90&deg; &minus; &epsilon;), and
+ * taking the limit &epsilon; &rarr; 0+. An arc length greater that 180&deg;
+ * signifies a geodesic which is not a shortest path. (For a prolate
+ * ellipsoid, an additional condition is necessary for a shortest path: the
+ * longitudinal extent must not exceed of 180&deg;.)
*
* Example, determine the point 10000 km NE of JFK:
@code
@@ -266,8 +268,8 @@ extern "C" {
* not need some quantities computed.
*
* If either point is at a pole, the azimuth is defined by keeping the
- * longitude fixed and writing \e lat = &plusmn;(90&deg; &minus; &epsilon;)
- * and taking the limit &epsilon; &rarr; 0+.
+ * longitude fixed, writing \e lat = &plusmn;(90&deg; &minus; &epsilon;), and
+ * taking the limit &epsilon; &rarr; 0+.
*
* The solution to the inverse problem is found using Newton's method. If
* this fails to converge (this is very unlikely in geodetic applications
@@ -436,7 +438,6 @@ extern "C" {
* @param[out] pS12 pointer to the area under the geodesic
* (meters<sup>2</sup>); requires that \e l was initialized with \e caps |=
* GEOD_AREA.
-
* @return \e a12 arc length of between point 1 and point 2 (degrees).
*
* \e l must have been initialized with a call to geod_lineinit() with \e
@@ -514,7 +515,7 @@ extern "C" {
* mask values for geod_geodesicline capabilities.
**********************************************************************/
enum geod_mask {
- GEOD_NONE = 0U,
+ GEOD_NONE = 0U, /**< Calculate nothing */
GEOD_LATITUDE = 1U<<7 | 0U, /**< Calculate latitude */
GEOD_LONGITUDE = 1U<<8 | 1U<<3, /**< Calculate longitude */
GEOD_AZIMUTH = 1U<<9 | 0U, /**< Calculate azimuth */