diff options
Diffstat (limited to 'src/geodesic.c')
| -rw-r--r-- | src/geodesic.c | 275 |
1 files changed, 242 insertions, 33 deletions
diff --git a/src/geodesic.c b/src/geodesic.c index 957f2144..bd9fc960 100644 --- a/src/geodesic.c +++ b/src/geodesic.c @@ -1,8 +1,12 @@ /** * \file geodesic.c * \brief Implementation of the geodesic routines in C + * + * For the full documentation see geodesic.h. **********************************************************************/ +/** @cond SKIP */ + /* * This is a C implementation of the geodesic algorithms described in * @@ -20,8 +24,6 @@ */ #include "geodesic.h" - -/** @cond SKIP */ #include <math.h> #define GEOGRAPHICLIB_GEODESIC_ORDER 6 @@ -221,9 +223,10 @@ 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 acccopy(const real s[], real t[]); static void accadd(real s[], real y); - -/** @endcond */ +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(); @@ -925,16 +928,12 @@ real geod_geninverse(const struct geod_geodesic* g, } void geod_inverse(const struct geod_geodesic* g, - double lat1, double lon1, double lat2, double lon2, - double* ps12, double* pazi1, double* pazi2) { + 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); } -/** @cond SKIP */ - -real SinCosSeries(boolx sinp, - real sinx, real cosx, - const real c[], int n) { +real SinCosSeries(boolx sinp, real sinx, real cosx, const real c[], int n) { /* Evaluate * y = sinp ? sum(c[i] * sin( 2*i * x), i, 1, n) : * sum(c[i] * cos((2*i+1) * x), i, 0, n-1) @@ -1522,6 +1521,11 @@ void accini(real s[]) { s[0] = s[1] = 0; } +void acccopy(const real s[], real t[]) { + /* Copy an accumulator; t = s. */ + t[0] = s[0]; t[1] = s[1]; +} + void accadd(real s[], real y) { /* Add y to an accumulator. */ real u, z = sumx(y, s[1], &u); @@ -1532,30 +1536,235 @@ void accadd(real s[], real y) { s[1] = s[1] + u; } -/** @endcond */ +real accsum(const real s[], real y) { + /* Return accumulator + y (but don't add to accumulator). */ + real t[2]; + acccopy(s, t); + accadd(t, y); + return t[0]; +} -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[2], P[2]; - accini(A); accini(P); - for (i = 0; i < n; ++i) { +void accneg(real s[]) { + /* Negate an accumulator. */ + s[0] = -s[0]; s[1] = -s[1]; +} + +void geod_polygon_init(struct geod_polygon* p, boolx polylinep) { + p->lat0 = p->lon0 = p->lat = p->lon = NaN; + p->polyline = (polylinep != 0); + 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; + 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) { /* Do nothing is num is zero */ + real lat, lon, S12; + geod_gendirect(g, p->lat, p->lon, azi, FALSE, 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 += transit(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); + /* area is with the clockwise sense. If !reverse convert to + * counter-clockwise convention. */ + if (!reverse) + accneg(t); + /* If sign put area in (-area0/2, area0/2], else put area in [0, area0) */ + 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; - geod_geninverse(g, lats[i], lons[i], lats[(i + 1) % n], lons[(i + 1) % n], - &s12, 0, 0, 0, 0, 0, &S12); - accadd(P, s12); - /* The minus sign is due to the counter-clockwise convention */ - accadd(A, -S12); - crossings += transit(lons[i], lons[(i + 1) % n]); + 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) - accadd(A, (A[0] < 0 ? 1 : -1) * area0/2); - /* Put area in (-area0/2, area0/2] */ - 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]; + tempsum += (tempsum < 0 ? 1 : -1) * area0/2; + /* area is with the clockwise sense. If !reverse convert to + * counter-clockwise convention. */ + if (!reverse) + tempsum *= -1; + /* If sign put area in (-area0/2, area0/2], else put area in [0, area0) */ + 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) { /* we don't have a starting point! */ + 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, FALSE, s, + &lat, &lon, 0, + 0, 0, 0, 0, &S12); + tempsum += S12; + crossings += transit(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; + /* area is with the clockwise sense. If !reverse convert to + * counter-clockwise convention. */ + if (!reverse) + tempsum *= -1; + /* If sign put area in (-area0/2, area0/2], else put area in [0, area0) */ + 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); +} + +/** @endcond */ |
