diff options
127 files changed, 18886 insertions, 7919 deletions
diff --git a/.travis.yml b/.travis.yml index 7761baad..286d640c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -97,13 +97,15 @@ install: fi - make -j3 - make check + - ./src/proj -VC - mv src/.libs/*.gc* src script: - echo "done" after_success: - - coveralls --extension .c +# coveralls falsely reports .c-files in the build directories as having 100% coverage so we exclude them + - coveralls --extension .c --exclude build_autoconf --exclude build_cmake - echo "$TRAVIS_SECURE_ENV_VARS" - sh -c "./travis/build_docs.sh" - sh -c 'if test "$TRAVIS_SECURE_ENV_VARS" = "true" -a "$TRAVIS_BRANCH" = "master"; then echo "publish website"; ./travis/add_deploy_key.sh; ./travis/deploy_website.sh $TRAVIS_BUILD_DIR/docs/build /tmp; fi' diff --git a/man/man1/proj.1 b/man/man1/proj.1 index 645271c5..55d444c3 100644 --- a/man/man1/proj.1 +++ b/man/man1/proj.1 @@ -2,7 +2,7 @@ .nr LL 5.5i .ad b .hy 1 -.TH PROJ 1 "2000/03/21 Rel. 4.4" +.TH PROJ 1 "2000/03/21 Rel. 4.4" .SH NAME proj \- forward cartographic projection filter .br @@ -10,7 +10,7 @@ invproj \- inverse cartographic projection filter .SH SYNOPSIS .B proj [ -.B \-bceEfiIlmorsStTvVwW +.B \-bcCeEfiIlmorsStTvVwW [ .I args ] ] [ @@ -20,7 +20,7 @@ file[s] .br .B invproj [ -.B \-bceEfiIlmorsStTwW +.B \-bcCeEfiIlmorsStTwW [ .I args ] ] [ @@ -52,6 +52,11 @@ process and allows bypassing formatting operations. Selects binary input only (see .B \-b option). .TP +.BI \-C +Check. Invoke all built in self tests and report. +Get more verbose report by preceding with the +.B \-V option). +.TP .BI \-I alternate method to specify inverse projection. Redundant when used with @@ -106,7 +111,7 @@ that can be selected with .B +units or .B \-ld -list of datums that can be selected with +list of datums that can be selected with .B +datum. .TP .BI \-r @@ -278,7 +283,7 @@ projection coordinates within one datum. The \fIcs2cs\fR program operates similarly, but allows translation between any pair of definable coordinate systems, including support for datum translation. -.PP +.PP The \fIgeod\fR program provides the ability to compute geodesic (Great Circle) computations. .SH SEE ALSO diff --git a/src/Makefile.am b/src/Makefile.am index d59e3fde..b961c930 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -62,8 +62,9 @@ libproj_la_SOURCES = \ pj_factors.c pj_fwd.c pj_init.c pj_inv.c pj_fwd3d.c pj_inv3d.c\ pj_list.c pj_malloc.c pj_mlfn.c pj_msfn.c proj_mdist.c \ pj_open_lib.c pj_param.c pj_phi2.c pj_pr_list.c \ - pj_qsfn.c pj_strerrno.c pj_tsfn.c pj_units.c pj_ctx.c pj_log.c \ - pj_zpoly1.c rtodms.c vector1.c pj_release.c pj_gauss.c \ + pj_qsfn.c pj_generic_selftest.c pj_run_selftests.c pj_strerrno.c \ + pj_tsfn.c pj_units.c pj_ctx.c pj_log.c pj_zpoly1.c rtodms.c \ + vector1.c pj_release.c pj_gauss.c \ PJ_healpix.c PJ_natearth.c PJ_natearth2.c PJ_calcofi.c pj_fileapi.c \ \ pj_gc_reader.c pj_gridcatalog.c \ diff --git a/src/PJ_aea.c b/src/PJ_aea.c index 0881ba78..57898f3f 100644 --- a/src/PJ_aea.c +++ b/src/PJ_aea.c @@ -1,7 +1,9 @@ /****************************************************************************** * Project: PROJ.4 * Purpose: Implementation of the aea (Albers Equal Area) projection. - * Author: Gerald Evenden + * and the leac (Lambert Equal Area Conic) projection + * Author: Gerald Evenden (1995) + * Thomas Knudsen (2016) - revise/add regression tests * ****************************************************************************** * Copyright (c) 1995, Gerald Evenden @@ -25,35 +27,21 @@ * DEALINGS IN THE SOFTWARE. *****************************************************************************/ -#define PROJ_PARMS__ \ - double ec; \ - double n; \ - double c; \ - double dd; \ - double n2; \ - double rho0; \ - double rho; \ - double phi1; \ - double phi2; \ - double *en; \ - int ellips; - #define PJ_LIB__ #include <projects.h> # define EPS10 1.e-10 # define TOL7 1.e-7 -PROJ_HEAD(aea, "Albers Equal Area") - "\n\tConic Sph&Ell\n\tlat_1= lat_2="; -PROJ_HEAD(leac, "Lambert Equal Area Conic") - "\n\tConic, Sph&Ell\n\tlat_1= south"; +PROJ_HEAD(aea, "Albers Equal Area") "\n\tConic Sph&Ell\n\tlat_1= lat_2="; +PROJ_HEAD(leac, "Lambert Equal Area Conic") "\n\tConic, Sph&Ell\n\tlat_1= south"; + + /* determine latitude angle phi-1 */ # define N_ITER 15 # define EPSILON 1.0e-7 # define TOL 1.0e-10 - static double -phi1_(double qs, double Te, double Tone_es) { +static double phi1_(double qs, double Te, double Tone_es) { int i; double Phi, sinpi, cospi, con, com, dphi; @@ -73,86 +61,269 @@ phi1_(double qs, double Te, double Tone_es) { } while (fabs(dphi) > TOL && --i); return( i ? Phi : HUGE_VAL ); } -FORWARD(e_forward); /* ellipsoid & spheroid */ - if ((P->rho = P->c - (P->ellips ? P->n * pj_qsfn(sin(lp.phi), - P->e, P->one_es) : P->n2 * sin(lp.phi))) < 0.) F_ERROR - P->rho = P->dd * sqrt(P->rho); - xy.x = P->rho * sin( lp.lam *= P->n ); - xy.y = P->rho0 - P->rho * cos(lp.lam); - return (xy); + + +struct pj_opaque { + double ec; + double n; + double c; + double dd; + double n2; + double rho0; + double rho; + double phi1; + double phi2; + double *en; + int ellips; +}; + + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoid/spheroid, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + if ((Q->rho = Q->c - (Q->ellips ? Q->n * pj_qsfn(sin(lp.phi), + P->e, P->one_es) : Q->n2 * sin(lp.phi))) < 0.) F_ERROR + Q->rho = Q->dd * sqrt(Q->rho); + xy.x = Q->rho * sin( lp.lam *= Q->n ); + xy.y = Q->rho0 - Q->rho * cos(lp.lam); + return xy; } -INVERSE(e_inverse) /* ellipsoid & spheroid */; - if( (P->rho = hypot(xy.x, xy.y = P->rho0 - xy.y)) != 0.0 ) { - if (P->n < 0.) { - P->rho = -P->rho; + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoid/spheroid, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + if( (Q->rho = hypot(xy.x, xy.y = Q->rho0 - xy.y)) != 0.0 ) { + if (Q->n < 0.) { + Q->rho = -Q->rho; xy.x = -xy.x; xy.y = -xy.y; } - lp.phi = P->rho / P->dd; - if (P->ellips) { - lp.phi = (P->c - lp.phi * lp.phi) / P->n; - if (fabs(P->ec - fabs(lp.phi)) > TOL7) { + lp.phi = Q->rho / Q->dd; + if (Q->ellips) { + lp.phi = (Q->c - lp.phi * lp.phi) / Q->n; + if (fabs(Q->ec - fabs(lp.phi)) > TOL7) { if ((lp.phi = phi1_(lp.phi, P->e, P->one_es)) == HUGE_VAL) I_ERROR } else lp.phi = lp.phi < 0. ? -HALFPI : HALFPI; - } else if (fabs(lp.phi = (P->c - lp.phi * lp.phi) / P->n2) <= 1.) + } else if (fabs(lp.phi = (Q->c - lp.phi * lp.phi) / Q->n2) <= 1.) lp.phi = asin(lp.phi); else lp.phi = lp.phi < 0. ? -HALFPI : HALFPI; - lp.lam = atan2(xy.x, xy.y) / P->n; + lp.lam = atan2(xy.x, xy.y) / Q->n; } else { lp.lam = 0.; - lp.phi = P->n > 0. ? HALFPI : - HALFPI; + lp.phi = Q->n > 0. ? HALFPI : - HALFPI; } - return (lp); + return lp; } -FREEUP; if (P) { if (P->en) pj_dalloc(P->en); pj_dalloc(P); } } - static PJ * -setup(PJ *P) { + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque->en); + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +static PJ *setup(PJ *P) { double cosphi, sinphi; int secant; + struct pj_opaque *Q = P->opaque; - if (fabs(P->phi1 + P->phi2) < EPS10) E_ERROR(-21); - P->n = sinphi = sin(P->phi1); - cosphi = cos(P->phi1); - secant = fabs(P->phi1 - P->phi2) >= EPS10; - if( (P->ellips = (P->es > 0.))) { + P->inv = e_inverse; + P->fwd = e_forward; + + if (fabs(Q->phi1 + Q->phi2) < EPS10) E_ERROR(-21); + Q->n = sinphi = sin(Q->phi1); + cosphi = cos(Q->phi1); + secant = fabs(Q->phi1 - Q->phi2) >= EPS10; + if( (Q->ellips = (P->es > 0.))) { double ml1, m1; - if (!(P->en = pj_enfn(P->es))) E_ERROR_0; + if (!(Q->en = pj_enfn(P->es))) E_ERROR_0; m1 = pj_msfn(sinphi, cosphi, P->es); ml1 = pj_qsfn(sinphi, P->e, P->one_es); if (secant) { /* secant cone */ double ml2, m2; - sinphi = sin(P->phi2); - cosphi = cos(P->phi2); + sinphi = sin(Q->phi2); + cosphi = cos(Q->phi2); m2 = pj_msfn(sinphi, cosphi, P->es); ml2 = pj_qsfn(sinphi, P->e, P->one_es); - P->n = (m1 * m1 - m2 * m2) / (ml2 - ml1); + Q->n = (m1 * m1 - m2 * m2) / (ml2 - ml1); } - P->ec = 1. - .5 * P->one_es * log((1. - P->e) / + Q->ec = 1. - .5 * P->one_es * log((1. - P->e) / (1. + P->e)) / P->e; - P->c = m1 * m1 + P->n * ml1; - P->dd = 1. / P->n; - P->rho0 = P->dd * sqrt(P->c - P->n * pj_qsfn(sin(P->phi0), + Q->c = m1 * m1 + Q->n * ml1; + Q->dd = 1. / Q->n; + Q->rho0 = Q->dd * sqrt(Q->c - Q->n * pj_qsfn(sin(P->phi0), P->e, P->one_es)); } else { - if (secant) P->n = .5 * (P->n + sin(P->phi2)); - P->n2 = P->n + P->n; - P->c = cosphi * cosphi + P->n2 * sinphi; - P->dd = 1. / P->n; - P->rho0 = P->dd * sqrt(P->c - P->n2 * sin(P->phi0)); + if (secant) Q->n = .5 * (Q->n + sin(Q->phi2)); + Q->n2 = Q->n + Q->n; + Q->c = cosphi * cosphi + Q->n2 * sinphi; + Q->dd = 1. / Q->n; + Q->rho0 = Q->dd * sqrt(Q->c - Q->n2 * sin(P->phi0)); } - P->inv = e_inverse; P->fwd = e_forward; + return P; } -ENTRY1(aea,en) - P->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; - P->phi2 = pj_param(P->ctx, P->params, "rlat_2").f; -ENDENTRY(setup(P)) -ENTRY1(leac,en) - P->phi2 = pj_param(P->ctx, P->params, "rlat_1").f; - P->phi1 = pj_param(P->ctx, P->params, "bsouth").i ? - HALFPI: HALFPI; -ENDENTRY(setup(P)) + + +PJ *PROJECTION(aea) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; + Q->phi2 = pj_param(P->ctx, P->params, "rlat_2").f; + setup(P); + return P; +} + + +PJ *PROJECTION(leac) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->phi2 = pj_param(P->ctx, P->params, "rlat_1").f; + Q->phi1 = pj_param(P->ctx, P->params, "bsouth").i ? - HALFPI: HALFPI; + setup (P); + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_aea_selftest (void) {return 10000;} +#else + +int pj_aea_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=aea +ellps=GRS80 +lat_1=0 +lat_2=2"}; + char s_args[] = {"+proj=aea +a=6400000 +lat_1=0 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {222571.60875710563, 110653.32674302977}, + {222706.30650839131, -110484.26714439997}, + {-222571.60875710563, 110653.32674302977}, + {-222706.30650839131, -110484.26714439997}, + }; + + XY s_fwd_expect[] = { + {223334.08517088494, 111780.43188447191}, + {223470.15499168713, -111610.33943099028}, + {-223334.08517088494, 111780.43188447191}, + {-223470.15499168713, -111610.33943099028}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {0.0017966310597749514, 0.00090436885862202158}, + {0.0017966300767030448, -0.00090437009538581453}, + {-0.0017966310597749514, 0.00090436885862202158}, + {-0.0017966300767030448, -0.00090437009538581453}, + }; + + LP s_inv_expect[] = { + {0.0017904935979658752, 0.00089524594491375306}, + {0.0017904926216016812, -0.00089524716502493225}, + {-0.0017904935979658752, 0.00089524594491375306}, + {-0.0017904926216016812, -0.00089524716502493225}, + }; + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif + + + +#ifdef PJ_OMIT_SELFTEST +int pj_leac_selftest (void) {return 10000;} +#else + +int pj_leac_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=leac +ellps=GRS80 +lat_1=0 +lat_2=2"}; + char s_args[] = {"+proj=leac +a=6400000 +lat_1=0 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {220685.14054297868, 112983.50088939646}, + {224553.31227982609, -108128.63674487274}, + {-220685.14054297868, 112983.50088939646}, + {-224553.31227982609, -108128.63674487274}, + }; + + XY s_fwd_expect[] = { + {221432.86859285168, 114119.45452653214}, + {225331.72412711097, -109245.82943505641}, + {-221432.86859285168, 114119.45452653214}, + {-225331.72412711097, -109245.82943505641}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {0.0017966446840328458, 0.00090435171340223211}, + {0.0017966164523713021, -0.00090438724081843625}, + {-0.0017966446840328458, 0.00090435171340223211}, + {-0.0017966164523713021, -0.00090438724081843625}, + }; + + LP s_inv_expect[] = { + {0.0017905070979748127, 0.00089522906964877795}, + {0.001790479121519977, -0.00089526404022281043}, + {-0.0017905070979748127, 0.00089522906964877795}, + {-0.001790479121519977, -0.00089526404022281043}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} +#endif diff --git a/src/PJ_aeqd.c b/src/PJ_aeqd.c index 22a75ac8..560d5a91 100644 --- a/src/PJ_aeqd.c +++ b/src/PJ_aeqd.c @@ -25,236 +25,346 @@ * DEALINGS IN THE SOFTWARE. *****************************************************************************/ -#define PROJ_PARMS__ \ - double sinph0; \ - double cosph0; \ - double *en; \ - double M1; \ - double N1; \ - double Mp; \ - double He; \ - double G; \ - int mode; \ - struct geod_geodesic g; #define PJ_LIB__ -#include "geodesic.h" -#include <projects.h> +#include "geodesic.h" +#include <projects.h> + +struct pj_opaque { + double sinph0; + double cosph0; + double *en; + double M1; + double N1; + double Mp; + double He; + double G; + int mode; + struct geod_geodesic g; +}; PROJ_HEAD(aeqd, "Azimuthal Equidistant") "\n\tAzi, Sph&Ell\n\tlat_0 guam"; #define EPS10 1.e-10 #define TOL 1.e-14 -#define N_POLE 0 -#define S_POLE 1 -#define EQUIT 2 -#define OBLIQ 3 - -FORWARD(e_guam_fwd); /* Guam elliptical */ - double cosphi, sinphi, t; - - cosphi = cos(lp.phi); - sinphi = sin(lp.phi); - t = 1. / sqrt(1. - P->es * sinphi * sinphi); - xy.x = lp.lam * cosphi * t; - xy.y = pj_mlfn(lp.phi, sinphi, cosphi, P->en) - P->M1 + - .5 * lp.lam * lp.lam * cosphi * sinphi * t; - return (xy); +#define N_POLE 0 +#define S_POLE 1 +#define EQUIT 2 +#define OBLIQ 3 + + +static XY e_guam_fwd(LP lp, PJ *P) { /* Guam elliptical */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double cosphi, sinphi, t; + + cosphi = cos(lp.phi); + sinphi = sin(lp.phi); + t = 1. / sqrt(1. - P->es * sinphi * sinphi); + xy.x = lp.lam * cosphi * t; + xy.y = pj_mlfn(lp.phi, sinphi, cosphi, Q->en) - Q->M1 + + .5 * lp.lam * lp.lam * cosphi * sinphi * t; + + return xy; } -FORWARD(e_forward); /* elliptical */ - double coslam, cosphi, sinphi, rho; - double azi1, azi2, s12; - double lam1, phi1, lam2, phi2; - - coslam = cos(lp.lam); - cosphi = cos(lp.phi); - sinphi = sin(lp.phi); - switch (P->mode) { - case N_POLE: - coslam = - coslam; - case S_POLE: - xy.x = (rho = fabs(P->Mp - pj_mlfn(lp.phi, sinphi, cosphi, P->en))) * - sin(lp.lam); - xy.y = rho * coslam; - break; - case EQUIT: - case OBLIQ: - if (fabs(lp.lam) < EPS10 && fabs(lp.phi - P->phi0) < EPS10) { - xy.x = xy.y = 0.; - break; - } - - phi1 = P->phi0 / DEG_TO_RAD; lam1 = P->lam0 / DEG_TO_RAD; - phi2 = lp.phi / DEG_TO_RAD; lam2 = (lp.lam+P->lam0) / DEG_TO_RAD; - - geod_inverse(&P->g, phi1, lam1, phi2, lam2, &s12, &azi1, &azi2); - azi1 *= DEG_TO_RAD; - xy.x = s12 * sin(azi1) / P->a; - xy.y = s12 * cos(azi1) / P->a; - break; - } - return (xy); + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double coslam, cosphi, sinphi, rho; + double azi1, azi2, s12; + double lam1, phi1, lam2, phi2; + + coslam = cos(lp.lam); + cosphi = cos(lp.phi); + sinphi = sin(lp.phi); + switch (Q->mode) { + case N_POLE: + coslam = - coslam; + case S_POLE: + xy.x = (rho = fabs(Q->Mp - pj_mlfn(lp.phi, sinphi, cosphi, Q->en))) * + sin(lp.lam); + xy.y = rho * coslam; + break; + case EQUIT: + case OBLIQ: + if (fabs(lp.lam) < EPS10 && fabs(lp.phi - P->phi0) < EPS10) { + xy.x = xy.y = 0.; + break; + } + + phi1 = P->phi0 / DEG_TO_RAD; lam1 = P->lam0 / DEG_TO_RAD; + phi2 = lp.phi / DEG_TO_RAD; lam2 = (lp.lam+P->lam0) / DEG_TO_RAD; + + geod_inverse(&Q->g, phi1, lam1, phi2, lam2, &s12, &azi1, &azi2); + azi1 *= DEG_TO_RAD; + xy.x = s12 * sin(azi1) / P->a; + xy.y = s12 * cos(azi1) / P->a; + break; + } + return xy; } -FORWARD(s_forward); /* spherical */ - double coslam, cosphi, sinphi; - - sinphi = sin(lp.phi); - cosphi = cos(lp.phi); - coslam = cos(lp.lam); - switch (P->mode) { - case EQUIT: - xy.y = cosphi * coslam; - goto oblcon; - case OBLIQ: - xy.y = P->sinph0 * sinphi + P->cosph0 * cosphi * coslam; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double coslam, cosphi, sinphi; + + sinphi = sin(lp.phi); + cosphi = cos(lp.phi); + coslam = cos(lp.lam); + switch (Q->mode) { + case EQUIT: + xy.y = cosphi * coslam; + goto oblcon; + case OBLIQ: + xy.y = Q->sinph0 * sinphi + Q->cosph0 * cosphi * coslam; oblcon: - if (fabs(fabs(xy.y) - 1.) < TOL) - if (xy.y < 0.) - F_ERROR - else - xy.x = xy.y = 0.; - else { - xy.y = acos(xy.y); - xy.y /= sin(xy.y); - xy.x = xy.y * cosphi * sin(lp.lam); - xy.y *= (P->mode == EQUIT) ? sinphi : - P->cosph0 * sinphi - P->sinph0 * cosphi * coslam; - } - break; - case N_POLE: - lp.phi = -lp.phi; - coslam = -coslam; - case S_POLE: - if (fabs(lp.phi - HALFPI) < EPS10) F_ERROR; - xy.x = (xy.y = (HALFPI + lp.phi)) * sin(lp.lam); - xy.y *= coslam; - break; - } - return (xy); + if (fabs(fabs(xy.y) - 1.) < TOL) + if (xy.y < 0.) + F_ERROR + else + xy.x = xy.y = 0.; + else { + xy.y = acos(xy.y); + xy.y /= sin(xy.y); + xy.x = xy.y * cosphi * sin(lp.lam); + xy.y *= (Q->mode == EQUIT) ? sinphi : + Q->cosph0 * sinphi - Q->sinph0 * cosphi * coslam; + } + break; + case N_POLE: + lp.phi = -lp.phi; + coslam = -coslam; + case S_POLE: + if (fabs(lp.phi - HALFPI) < EPS10) F_ERROR; + xy.x = (xy.y = (HALFPI + lp.phi)) * sin(lp.lam); + xy.y *= coslam; + break; + } + return xy; } -INVERSE(e_guam_inv); /* Guam elliptical */ - double x2, t; - int i; - - x2 = 0.5 * xy.x * xy.x; - lp.phi = P->phi0; - for (i = 0; i < 3; ++i) { - t = P->e * sin(lp.phi); - lp.phi = pj_inv_mlfn(P->ctx, P->M1 + xy.y - - x2 * tan(lp.phi) * (t = sqrt(1. - t * t)), P->es, P->en); - } - lp.lam = xy.x * t / cos(lp.phi); - return (lp); + + +static LP e_guam_inv(XY xy, PJ *P) { /* Guam elliptical */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double x2, t; + int i; + + x2 = 0.5 * xy.x * xy.x; + lp.phi = P->phi0; + for (i = 0; i < 3; ++i) { + t = P->e * sin(lp.phi); + lp.phi = pj_inv_mlfn(P->ctx, Q->M1 + xy.y - + x2 * tan(lp.phi) * (t = sqrt(1. - t * t)), P->es, Q->en); + } + lp.lam = xy.x * t / cos(lp.phi); + return lp; } -INVERSE(e_inverse); /* elliptical */ - double c; - double azi1, azi2, s12, x2, y2, lat1, lon1, lat2, lon2; - - if ((c = hypot(xy.x, xy.y)) < EPS10) { - lp.phi = P->phi0; - lp.lam = 0.; - return (lp); - } - if (P->mode == OBLIQ || P->mode == EQUIT) { - - x2 = xy.x * P->a; - y2 = xy.y * P->a; - lat1 = P->phi0 / DEG_TO_RAD; - lon1 = P->lam0 / DEG_TO_RAD; - azi1 = atan2(x2, y2) / DEG_TO_RAD; - s12 = sqrt(x2 * x2 + y2 * y2); - geod_direct(&P->g, lat1, lon1, azi1, s12, &lat2, &lon2, &azi2); - lp.phi = lat2 * DEG_TO_RAD; - lp.lam = lon2 * DEG_TO_RAD; - lp.lam -= P->lam0; - } else { /* Polar */ - lp.phi = pj_inv_mlfn(P->ctx, P->mode == N_POLE ? P->Mp - c : P->Mp + c, - P->es, P->en); - lp.lam = atan2(xy.x, P->mode == N_POLE ? -xy.y : xy.y); - } - return (lp); + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double c; + double azi1, azi2, s12, x2, y2, lat1, lon1, lat2, lon2; + + if ((c = hypot(xy.x, xy.y)) < EPS10) { + lp.phi = P->phi0; + lp.lam = 0.; + return (lp); + } + if (Q->mode == OBLIQ || Q->mode == EQUIT) { + + x2 = xy.x * P->a; + y2 = xy.y * P->a; + lat1 = P->phi0 / DEG_TO_RAD; + lon1 = P->lam0 / DEG_TO_RAD; + azi1 = atan2(x2, y2) / DEG_TO_RAD; + s12 = sqrt(x2 * x2 + y2 * y2); + geod_direct(&Q->g, lat1, lon1, azi1, s12, &lat2, &lon2, &azi2); + lp.phi = lat2 * DEG_TO_RAD; + lp.lam = lon2 * DEG_TO_RAD; + lp.lam -= P->lam0; + } else { /* Polar */ + lp.phi = pj_inv_mlfn(P->ctx, Q->mode == N_POLE ? Q->Mp - c : Q->Mp + c, + P->es, Q->en); + lp.lam = atan2(xy.x, Q->mode == N_POLE ? -xy.y : xy.y); + } + return lp; } -INVERSE(s_inverse); /* spherical */ - double cosc, c_rh, sinc; - - if ((c_rh = hypot(xy.x, xy.y)) > PI) { - if (c_rh - EPS10 > PI) I_ERROR; - c_rh = PI; - } else if (c_rh < EPS10) { - lp.phi = P->phi0; - lp.lam = 0.; - return (lp); - } - if (P->mode == OBLIQ || P->mode == EQUIT) { - sinc = sin(c_rh); - cosc = cos(c_rh); - if (P->mode == EQUIT) { + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double cosc, c_rh, sinc; + + if ((c_rh = hypot(xy.x, xy.y)) > PI) { + if (c_rh - EPS10 > PI) I_ERROR; + c_rh = PI; + } else if (c_rh < EPS10) { + lp.phi = P->phi0; + lp.lam = 0.; + return (lp); + } + if (Q->mode == OBLIQ || Q->mode == EQUIT) { + sinc = sin(c_rh); + cosc = cos(c_rh); + if (Q->mode == EQUIT) { lp.phi = aasin(P->ctx, xy.y * sinc / c_rh); - xy.x *= sinc; - xy.y = cosc * c_rh; - } else { - lp.phi = aasin(P->ctx,cosc * P->sinph0 + xy.y * sinc * P->cosph0 / - c_rh); - xy.y = (cosc - P->sinph0 * sin(lp.phi)) * c_rh; - xy.x *= sinc * P->cosph0; - } - lp.lam = xy.y == 0. ? 0. : atan2(xy.x, xy.y); - } else if (P->mode == N_POLE) { - lp.phi = HALFPI - c_rh; - lp.lam = atan2(xy.x, -xy.y); - } else { - lp.phi = c_rh - HALFPI; - lp.lam = atan2(xy.x, xy.y); - } - return (lp); + xy.x *= sinc; + xy.y = cosc * c_rh; + } else { + lp.phi = aasin(P->ctx,cosc * Q->sinph0 + xy.y * sinc * Q->cosph0 / + c_rh); + xy.y = (cosc - Q->sinph0 * sin(lp.phi)) * c_rh; + xy.x *= sinc * Q->cosph0; + } + lp.lam = xy.y == 0. ? 0. : atan2(xy.x, xy.y); + } else if (Q->mode == N_POLE) { + lp.phi = HALFPI - c_rh; + lp.lam = atan2(xy.x, -xy.y); + } else { + lp.phi = c_rh - HALFPI; + lp.lam = atan2(xy.x, xy.y); + } + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + if (P->opaque->en) + pj_dealloc(P->opaque->en); + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; } -FREEUP; - if (P) { - if (P->en) - pj_dalloc(P->en); - pj_dalloc(P); - } + + +PJ *PROJECTION(aeqd) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + geod_init(&Q->g, P->a, P->es / (1 + sqrt(P->one_es))); + P->phi0 = pj_param(P->ctx, P->params, "rlat_0").f; + if (fabs(fabs(P->phi0) - HALFPI) < EPS10) { + Q->mode = P->phi0 < 0. ? S_POLE : N_POLE; + Q->sinph0 = P->phi0 < 0. ? -1. : 1.; + Q->cosph0 = 0.; + } else if (fabs(P->phi0) < EPS10) { + Q->mode = EQUIT; + Q->sinph0 = 0.; + Q->cosph0 = 1.; + } else { + Q->mode = OBLIQ; + Q->sinph0 = sin(P->phi0); + Q->cosph0 = cos(P->phi0); + } + if (! P->es) { + P->inv = s_inverse; + P->fwd = s_forward; + } else { + if (!(Q->en = pj_enfn(P->es))) E_ERROR_0; + if (pj_param(P->ctx, P->params, "bguam").i) { + Q->M1 = pj_mlfn(P->phi0, Q->sinph0, Q->cosph0, Q->en); + P->inv = e_guam_inv; + P->fwd = e_guam_fwd; + } else { + switch (Q->mode) { + case N_POLE: + Q->Mp = pj_mlfn(HALFPI, 1., 0., Q->en); + break; + case S_POLE: + Q->Mp = pj_mlfn(-HALFPI, -1., 0., Q->en); + break; + case EQUIT: + case OBLIQ: + P->inv = e_inverse; P->fwd = e_forward; + Q->N1 = 1. / sqrt(1. - P->es * Q->sinph0 * Q->sinph0); + Q->G = Q->sinph0 * (Q->He = P->e / sqrt(P->one_es)); + Q->He *= Q->cosph0; + break; + } + P->inv = e_inverse; + P->fwd = e_forward; + } + } + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_aeqd_selftest (void) {return 0;} +#else + +int pj_aeqd_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=aeqd +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + char s_args[] = {"+proj=aeqd +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 222616.522190051648, 110596.996549550197}, + { 222616.522190051648, -110596.996549550211}, + {-222616.522190051648, 110596.996549550197}, + {-222616.522190051648, -110596.996549550211}, + }; + + XY s_fwd_expect[] = { + { 223379.456047271, 111723.757570854126}, + { 223379.456047271, -111723.757570854126}, + {-223379.456047271, 111723.757570854126}, + {-223379.456047271, -111723.757570854126}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + { 0.00179663056838724787, 0.000904369476930248902}, + { 0.00179663056838724787, -0.000904369476930248469}, + {-0.00179663056838724787, 0.000904369476930248902}, + {-0.00179663056838724787, -0.000904369476930248469}, + }; + + LP s_inv_expect[] = { + { 0.00179049310992953335, 0.000895246554746200623}, + { 0.00179049310992953335, -0.000895246554746200623}, + {-0.00179049310992953335, 0.000895246554746200623}, + {-0.00179049310992953335, -0.000895246554746200623}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); } -ENTRY1(aeqd, en) - geod_init(&P->g, P->a, P->es / (1 + sqrt(P->one_es))); - P->phi0 = pj_param(P->ctx, P->params, "rlat_0").f; - if (fabs(fabs(P->phi0) - HALFPI) < EPS10) { - P->mode = P->phi0 < 0. ? S_POLE : N_POLE; - P->sinph0 = P->phi0 < 0. ? -1. : 1.; - P->cosph0 = 0.; - } else if (fabs(P->phi0) < EPS10) { - P->mode = EQUIT; - P->sinph0 = 0.; - P->cosph0 = 1.; - } else { - P->mode = OBLIQ; - P->sinph0 = sin(P->phi0); - P->cosph0 = cos(P->phi0); - } - if (! P->es) { - P->inv = s_inverse; P->fwd = s_forward; - } else { - if (!(P->en = pj_enfn(P->es))) E_ERROR_0; - if (pj_param(P->ctx, P->params, "bguam").i) { - P->M1 = pj_mlfn(P->phi0, P->sinph0, P->cosph0, P->en); - P->inv = e_guam_inv; P->fwd = e_guam_fwd; - } else { - switch (P->mode) { - case N_POLE: - P->Mp = pj_mlfn(HALFPI, 1., 0., P->en); - break; - case S_POLE: - P->Mp = pj_mlfn(-HALFPI, -1., 0., P->en); - break; - case EQUIT: - case OBLIQ: - P->inv = e_inverse; P->fwd = e_forward; - P->N1 = 1. / sqrt(1. - P->es * P->sinph0 * P->sinph0); - P->G = P->sinph0 * (P->He = P->e / sqrt(P->one_es)); - P->He *= P->cosph0; - break; - } - P->inv = e_inverse; P->fwd = e_forward; - } - } -ENDENTRY(P) + + +#endif diff --git a/src/PJ_airy.c b/src/PJ_airy.c index 73a51e5b..c1d31d6c 100644 --- a/src/PJ_airy.c +++ b/src/PJ_airy.c @@ -1,7 +1,8 @@ /****************************************************************************** * Project: PROJ.4 * Purpose: Implementation of the airy (Airy) projection. - * Author: Gerald Evenden + * Author: Gerald Evenden (1995) + * Thomas Knudsen (2016) - revise/add regression tests * ****************************************************************************** * Copyright (c) 1995, Gerald Evenden @@ -25,96 +26,161 @@ * DEALINGS IN THE SOFTWARE. *****************************************************************************/ -#define PROJ_PARMS__ \ - double p_halfpi; \ - double sinph0; \ - double cosph0; \ - double Cb; \ - int mode; \ - int no_cut; /* do not cut at hemisphere limit */ #define PJ_LIB__ #include <projects.h> PROJ_HEAD(airy, "Airy") "\n\tMisc Sph, no inv.\n\tno_cut lat_b="; + +struct pj_opaque { + double p_halfpi; + double sinph0; + double cosph0; + double Cb; + int mode; + int no_cut; /* do not cut at hemisphere limit */ +}; + + # define EPS 1.e-10 # define N_POLE 0 # define S_POLE 1 # define EQUIT 2 # define OBLIQ 3 -FORWARD(s_forward); /* spheroid */ + + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; double sinlam, coslam, cosphi, sinphi, t, s, Krho, cosz; sinlam = sin(lp.lam); coslam = cos(lp.lam); - switch (P->mode) { + switch (Q->mode) { case EQUIT: case OBLIQ: sinphi = sin(lp.phi); cosphi = cos(lp.phi); cosz = cosphi * coslam; - if (P->mode == OBLIQ) - cosz = P->sinph0 * sinphi + P->cosph0 * cosz; - if (!P->no_cut && cosz < -EPS) + if (Q->mode == OBLIQ) + cosz = Q->sinph0 * sinphi + Q->cosph0 * cosz; + if (!Q->no_cut && cosz < -EPS) F_ERROR; if (fabs(s = 1. - cosz) > EPS) { t = 0.5 * (1. + cosz); - Krho = -log(t)/s - P->Cb / t; + Krho = -log(t)/s - Q->Cb / t; } else - Krho = 0.5 - P->Cb; + Krho = 0.5 - Q->Cb; xy.x = Krho * cosphi * sinlam; - if (P->mode == OBLIQ) - xy.y = Krho * (P->cosph0 * sinphi - - P->sinph0 * cosphi * coslam); + if (Q->mode == OBLIQ) + xy.y = Krho * (Q->cosph0 * sinphi - + Q->sinph0 * cosphi * coslam); else xy.y = Krho * sinphi; break; case S_POLE: case N_POLE: - lp.phi = fabs(P->p_halfpi - lp.phi); - if (!P->no_cut && (lp.phi - EPS) > HALFPI) + lp.phi = fabs(Q->p_halfpi - lp.phi); + if (!Q->no_cut && (lp.phi - EPS) > HALFPI) F_ERROR; if ((lp.phi *= 0.5) > EPS) { t = tan(lp.phi); - Krho = -2.*(log(cos(lp.phi)) / t + t * P->Cb); + Krho = -2.*(log(cos(lp.phi)) / t + t * Q->Cb); xy.x = Krho * sinlam; xy.y = Krho * coslam; - if (P->mode == N_POLE) + if (Q->mode == N_POLE) xy.y = -xy.y; } else xy.x = xy.y = 0.; } - return (xy); + return xy; +} + + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(airy) + + +PJ *PROJECTION(airy) { double beta; - P->no_cut = pj_param(P->ctx, P->params, "bno_cut").i; + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + + P->opaque = Q; + + Q->no_cut = pj_param(P->ctx, P->params, "bno_cut").i; beta = 0.5 * (HALFPI - pj_param(P->ctx, P->params, "rlat_b").f); if (fabs(beta) < EPS) - P->Cb = -0.5; + Q->Cb = -0.5; else { - P->Cb = 1./tan(beta); - P->Cb *= P->Cb * log(cos(beta)); + Q->Cb = 1./tan(beta); + Q->Cb *= Q->Cb * log(cos(beta)); } + if (fabs(fabs(P->phi0) - HALFPI) < EPS) if (P->phi0 < 0.) { - P->p_halfpi = -HALFPI; - P->mode = S_POLE; + Q->p_halfpi = -HALFPI; + Q->mode = S_POLE; } else { - P->p_halfpi = HALFPI; - P->mode = N_POLE; + Q->p_halfpi = HALFPI; + Q->mode = N_POLE; } else { if (fabs(P->phi0) < EPS) - P->mode = EQUIT; + Q->mode = EQUIT; else { - P->mode = OBLIQ; - P->sinph0 = sin(P->phi0); - P->cosph0 = cos(P->phi0); + Q->mode = OBLIQ; + Q->sinph0 = sin(P->phi0); + Q->cosph0 = cos(P->phi0); } } P->fwd = s_forward; P->es = 0.; -ENDENTRY(P) + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_airy_selftest (void) {return 0;} +#else + +int pj_airy_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=airy +a=6400000 +lat_1=0 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 189109.88690862127, 94583.752387504152}, + { 189109.88690862127, -94583.752387504152}, + {-189109.88690862127, 94583.752387504152}, + {-189109.88690862127, -94583.752387504152}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 0, fwd_in, 0, s_fwd_expect, 0, 0, 0); +} + +#endif diff --git a/src/PJ_aitoff.c b/src/PJ_aitoff.c index d5350f2c..c9fe1c22 100644 --- a/src/PJ_aitoff.c +++ b/src/PJ_aitoff.c @@ -2,7 +2,9 @@ * Project: PROJ.4 * Purpose: Implementation of the aitoff (Aitoff) and wintri (Winkel Tripel) * projections. - * Author: Gerald Evenden + * Author: Gerald Evenden (1995) + * Drazen Tutic, Lovro Gradiser (2015) - add inverse + * Thomas Knudsen (2016) - revise/add regression tests * ****************************************************************************** * Copyright (c) 1995, Gerald Evenden @@ -26,12 +28,15 @@ * DEALINGS IN THE SOFTWARE. *****************************************************************************/ -#define PROJ_PARMS__ \ - double cosphi1; \ - int mode; #define PJ_LIB__ #include <projects.h> + +struct pj_opaque { + double cosphi1; + int mode; +}; + #ifndef M_PI # define M_PI 3.14159265358979323846 #endif @@ -42,7 +47,16 @@ PROJ_HEAD(aitoff, "Aitoff") "\n\tMisc Sph"; PROJ_HEAD(wintri, "Winkel Tripel") "\n\tMisc Sph\n\tlat_1"; + + +#if 0 FORWARD(s_forward); /* spheroid */ +#endif + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; double c, d; if((d = acos(cos(lp.phi) * cos(c = 0.5 * lp.lam)))) {/* basic Aitoff */ @@ -50,8 +64,8 @@ FORWARD(s_forward); /* spheroid */ xy.y *= d * sin(lp.phi); } else xy.x = xy.y = 0.; - if (P->mode) { /* Winkel Tripel */ - xy.x = (xy.x + lp.lam * P->cosphi1) * 0.5; + if (Q->mode) { /* Winkel Tripel */ + xy.x = (xy.x + lp.lam * Q->cosphi1) * 0.5; xy.y = (xy.y + lp.phi) * 0.5; } return (xy); @@ -60,13 +74,13 @@ FORWARD(s_forward); /* spheroid */ /*********************************************************************************** * * Inverse functions added by Drazen Tutic and Lovro Gradiser based on paper: -* -* I.Özbug Biklirici and Cengizhan Ipbüker. A General Algorithm for the Inverse +* +* I.Özbug Biklirici and Cengizhan Ipbüker. A General Algorithm for the Inverse * Transformation of Map Projections Using Jacobian Matrices. In Proceedings of the * Third International Symposium Mathematical & Computational Applications, * pages 175{182, Turkey, September 2002. * -* Expected accuracy is defined by EPSILON = 1e-12. Should be appropriate for +* Expected accuracy is defined by EPSILON = 1e-12. Should be appropriate for * most applications of Aitoff and Winkel Tripel projections. * * Longitudes of 180W and 180E can be mixed in solution obtained. @@ -78,19 +92,21 @@ FORWARD(s_forward); /* spheroid */ * ************************************************************************************/ -INVERSE(s_inverse); /* sphere */ - int iter, MAXITER = 10, round = 0, MAXROUND = 20; +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + int iter, MAXITER = 10, round = 0, MAXROUND = 20; double EPSILON = 1e-12, D, C, f1, f2, f1p, f1l, f2p, f2l, dp, dl, sl, sp, cp, cl, x, y; - if ((fabs(xy.x) < EPSILON) && (fabs(xy.y) < EPSILON )) { lp.phi = 0.; lp.lam = 0.; return (lp); } + if ((fabs(xy.x) < EPSILON) && (fabs(xy.y) < EPSILON )) { lp.phi = 0.; lp.lam = 0.; return lp; } /* intial values for Newton-Raphson method */ - lp.phi = xy.y; lp.lam = xy.x; + lp.phi = xy.y; lp.lam = xy.x; do { - iter = 0; - do { - sl = sin(lp.lam * 0.5); cl = cos(lp.lam * 0.5); - sp = sin(lp.phi); cp = cos(lp.phi); + iter = 0; + do { + sl = sin(lp.lam * 0.5); cl = cos(lp.lam * 0.5); + sp = sin(lp.phi); cp = cos(lp.phi); D = cp * cl; C = 1. - D * D; D = acos(D) / pow(C, 1.5); @@ -100,11 +116,11 @@ INVERSE(s_inverse); /* sphere */ f1l = cp * cp * sl * sl / C + D * cp * cl * sp * sp; f2p = sp * sp * cl / C + D * sl * sl * cp; f2l = 0.5 * (sp * cp * sl / C - D * sp * cp * cp * sl * cl); - if (P->mode) { /* Winkel Tripel */ - f1 = 0.5 * (f1 + lp.lam * P->cosphi1); + if (Q->mode) { /* Winkel Tripel */ + f1 = 0.5 * (f1 + lp.lam * Q->cosphi1); f2 = 0.5 * (f2 + lp.phi); f1p *= 0.5; - f1l = 0.5 * (f1l + P->cosphi1); + f1l = 0.5 * (f1l + Q->cosphi1); f2p = 0.5 * (f2p + 1.); f2l *= 0.5; } @@ -115,9 +131,9 @@ INVERSE(s_inverse); /* sphere */ while (dl < -M_PI) dl += M_PI; /* set to interval [-M_PI, M_PI] */ lp.phi -= dp; lp.lam -= dl; } while ((fabs(dp) > EPSILON || fabs(dl) > EPSILON) && (iter++ < MAXITER)); - if (lp.phi > M_PI_2) lp.phi -= 2.*(lp.phi-M_PI_2); /* correct if symmetrical solution for Aitoff */ - if (lp.phi < -M_PI_2) lp.phi -= 2.*(lp.phi+M_PI_2); /* correct if symmetrical solution for Aitoff */ - if ((fabs(fabs(lp.phi) - M_PI_2) < EPSILON) && (!P->mode)) lp.lam = 0.; /* if pole in Aitoff, return longitude of 0 */ + if (lp.phi > M_PI_2) lp.phi -= 2.*(lp.phi-M_PI_2); /* correct if symmetrical solution for Aitoff */ + if (lp.phi < -M_PI_2) lp.phi -= 2.*(lp.phi+M_PI_2); /* correct if symmetrical solution for Aitoff */ + if ((fabs(fabs(lp.phi) - M_PI_2) < EPSILON) && (!Q->mode)) lp.lam = 0.; /* if pole in Aitoff, return longitude of 0 */ /* calculate x,y coordinates with solution obtained */ if((D = acos(cos(lp.phi) * cos(C = 0.5 * lp.lam)))) {/* Aitoff */ @@ -125,8 +141,8 @@ INVERSE(s_inverse); /* sphere */ y *= D * sin(lp.phi); } else x = y = 0.; - if (P->mode) { /* Winkel Tripel */ - x = (x + lp.lam * P->cosphi1) * 0.5; + if (Q->mode) { /* Winkel Tripel */ + x = (x + lp.lam * Q->cosphi1) * 0.5; y = (y + lp.phi) * 0.5; } /* if too far from given values of x,y, repeat with better approximation of phi,lam */ @@ -134,27 +150,150 @@ INVERSE(s_inverse); /* sphere */ if (iter == MAXITER && round == MAXROUND) fprintf(stderr, "Warning: Accuracy of 1e-12 not reached. Last increments: dlat=%e and dlon=%e\n", dp, dl); - return (lp); + return lp; } -FREEUP; if (P) pj_dalloc(P); } - static PJ * -setup(PJ *P) { + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + +static PJ *setup(PJ *P) { P->inv = s_inverse; P->fwd = s_forward; P->es = 0.; return P; } -ENTRY0(aitoff) - P->mode = 0; -ENDENTRY(setup(P)) -ENTRY0(wintri) - P->mode = 1; - if (pj_param(P->ctx, P->params, "tlat_1").i) - { - if ((P->cosphi1 = cos(pj_param(P->ctx, P->params, "rlat_1").f)) == 0.) + + +PJ *PROJECTION(aitoff) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->mode = 0; + return setup(P); +} + + +PJ *PROJECTION(wintri) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->mode = 1; + if (pj_param(P->ctx, P->params, "tlat_1").i) { + if ((Q->cosphi1 = cos(pj_param(P->ctx, P->params, "rlat_1").f)) == 0.) E_ERROR(-22) - } + } else /* 50d28' or acos(2/pi) */ - P->cosphi1 = 0.636619772367581343; -ENDENTRY(setup(P)) + Q->cosphi1 = 0.636619772367581343; + return setup(P); +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_aitoff_selftest (void) {return 0;} +#else + +int pj_aitoff_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=aitoff +a=6400000 +lat_1=0 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + + XY s_fwd_expect[] = { + {223379.45881169615, 111706.74288385305}, + {223379.45881169615, -111706.74288385305}, + {-223379.45881169615, 111706.74288385305}, + {-223379.45881169615, -111706.74288385305}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + + LP s_inv_expect[] = { + {0.0017904931100388164, 0.00089524655491012516}, + {0.0017904931100388164, -0.00089524655491012516}, + {-0.0017904931100388164, 0.00089524655491012516}, + {-0.0017904931100388164, -0.00089524655491012516}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif + + + +#ifdef PJ_OMIT_SELFTEST +int pj_wintri_selftest (void) {return 0;} +#else + +int pj_wintri_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=wintri +a=6400000 +lat_1=0 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + {223390.80153348515, 111703.90750574505}, + {223390.80153348515, -111703.90750574505}, + {-223390.80153348515, 111703.90750574505}, + {-223390.80153348515, -111703.90750574505}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + {0.0017904931099113196, 0.00089524655490101819}, + {0.0017904931099113196, -0.00089524655490101819}, + {-0.0017904931099113196, 0.00089524655490101819}, + {-0.0017904931099113196, -0.00089524655490101819}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_august.c b/src/PJ_august.c index 496fc3c3..83463e9f 100644 --- a/src/PJ_august.c +++ b/src/PJ_august.c @@ -1,8 +1,13 @@ #define PJ_LIB__ #include <projects.h> + + PROJ_HEAD(august, "August Epicycloidal") "\n\tMisc Sph, no inv."; #define M 1.333333333333333 -FORWARD(s_forward); /* spheroid */ + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; double t, c1, c, x1, x12, y1, y12; (void) P; @@ -15,5 +20,52 @@ FORWARD(s_forward); /* spheroid */ xy.y = M * y1 * (3. + 3. * x12 - y12); return (xy); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(august) P->inv = 0; P->fwd = s_forward; P->es = 0.; ENDENTRY(P) + + + +static void *freeup_new (PJ *P) { /* Destructor */ + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(august) { + P->inv = 0; + P->fwd = s_forward; + P->es = 0.; + return P; +} + +#ifdef PJ_OMIT_SELFTEST +int pj_august_selftest (void) {return 0;} +#else + +int pj_august_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=august +a=6400000 +lat_1=0 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + {223404.97818097242, 111722.34028976287}, + {223404.97818097242, -111722.34028976287}, + {-223404.97818097242, 111722.34028976287}, + {-223404.97818097242, -111722.34028976287}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, 0, 0, 0); +} + + +#endif diff --git a/src/PJ_bacon.c b/src/PJ_bacon.c index dfc559b8..75afaeb0 100644 --- a/src/PJ_bacon.c +++ b/src/PJ_bacon.c @@ -1,19 +1,27 @@ # define HLFPI2 2.46740110027233965467 # define EPS 1e-10 -#define PROJ_PARMS__ \ - int bacn; \ - int ortl; #define PJ_LIB__ #include <projects.h> + + +struct pj_opaque { + int bacn; + int ortl; +}; + PROJ_HEAD(apian, "Apian Globular I") "\n\tMisc Sph, no inv."; PROJ_HEAD(ortel, "Ortelius Oval") "\n\tMisc Sph, no inv."; PROJ_HEAD(bacon, "Bacon Globular") "\n\tMisc Sph, no inv."; -FORWARD(s_forward); /* spheroid */ + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; double ax, f; - xy.y = P->bacn ? HALFPI * sin(lp.phi) : lp.phi; + xy.y = Q->bacn ? HALFPI * sin(lp.phi) : lp.phi; if ((ax = fabs(lp.lam)) >= EPS) { - if (P->ortl && ax >= HALFPI) + if (Q->ortl && ax >= HALFPI) xy.x = sqrt(HLFPI2 - lp.phi * lp.phi + EPS) + ax - HALFPI; else { f = 0.5 * (HLFPI2 / ax + ax); @@ -24,18 +32,148 @@ FORWARD(s_forward); /* spheroid */ xy.x = 0.; return (xy); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(bacon) - P->bacn = 1; - P->ortl = 0; - P->es = 0.; P->fwd = s_forward; -ENDENTRY(P) -ENTRY0(apian) - P->bacn = P->ortl = 0; - P->es = 0.; P->fwd = s_forward; -ENDENTRY(P) -ENTRY0(ortel) - P->bacn = 0; - P->ortl = 1; - P->es = 0.; P->fwd = s_forward; -ENDENTRY(P) + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(bacon) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->bacn = 1; + Q->ortl = 0; + P->es = 0.; + P->fwd = s_forward; + return P; +} + + +PJ *PROJECTION(apian) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->bacn = Q->ortl = 0; + P->es = 0.; + P->fwd = s_forward; + return P; +} + + +PJ *PROJECTION(ortel) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->bacn = 0; + Q->ortl = 1; + P->es = 0.; + P->fwd = s_forward; + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_bacon_selftest (void) {return 0;} +#else +int pj_bacon_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=bacon +a=6400000 +lat_1=0 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + {223334.13255596498, 175450.72592266591}, + {223334.13255596498, -175450.72592266591}, + {-223334.13255596498, 175450.72592266591}, + {-223334.13255596498, -175450.72592266591}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 0, fwd_in, 0, s_fwd_expect, 0, 0, 0); +} +#endif + + + + +#ifdef PJ_OMIT_SELFTEST +int pj_apian_selftest (void) {return 0;} +#else +int pj_apian_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=apian +a=6400000 +lat_1=0 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223374.57735525275, 111701.07212763709}, + { 223374.57735525275, -111701.07212763709}, + {-223374.57735525275, 111701.07212763709}, + {-223374.57735525275, -111701.07212763709}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 0, fwd_in, 0, s_fwd_expect, 0, 0, 0); +} +#endif + + + + +#ifdef PJ_OMIT_SELFTEST +int pj_ortel_selftest (void) {return 0;} +#else +int pj_ortel_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=ortel +a=6400000 +lat_1=0 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223374.57735525275, 111701.07212763709}, + { 223374.57735525275, -111701.07212763709}, + {-223374.57735525275, 111701.07212763709}, + {-223374.57735525275, -111701.07212763709}, + }; + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 0, fwd_in, 0, s_fwd_expect, 0, 0, 0); +} +#endif + diff --git a/src/PJ_bipc.c b/src/PJ_bipc.c index 471e7266..96082604 100644 --- a/src/PJ_bipc.c +++ b/src/PJ_bipc.c @@ -1,132 +1,224 @@ -#define PROJ_PARMS__ \ - int noskew; #define PJ_LIB__ -# include <projects.h> -PROJ_HEAD(bipc, "Bipolar conic of western hemisphere") - "\n\tConic Sph."; -# define EPS 1e-10 -# define EPS10 1e-10 +#include <projects.h> + +PROJ_HEAD(bipc, "Bipolar conic of western hemisphere") "\n\tConic Sph."; + +# define EPS 1e-10 +# define EPS10 1e-10 # define ONEEPS 1.000000001 -# define NITER 10 -# define lamB -.34894976726250681539 -# define n .63055844881274687180 -# define F 1.89724742567461030582 -# define Azab .81650043674686363166 -# define Azba 1.82261843856185925133 -# define T 1.27246578267089012270 -# define rhoc 1.20709121521568721927 -# define cAzc .69691523038678375519 -# define sAzc .71715351331143607555 -# define C45 .70710678118654752469 -# define S45 .70710678118654752410 -# define C20 .93969262078590838411 -# define S20 -.34202014332566873287 -# define R110 1.91986217719376253360 -# define R104 1.81514242207410275904 -FORWARD(s_forward); /* spheroid */ - double cphi, sphi, tphi, t, al, Az, z, Av, cdlam, sdlam, r; - int tag; - - cphi = cos(lp.phi); - sphi = sin(lp.phi); - cdlam = cos(sdlam = lamB - lp.lam); - sdlam = sin(sdlam); - if (fabs(fabs(lp.phi) - HALFPI) < EPS10) { - Az = lp.phi < 0. ? PI : 0.; - tphi = HUGE_VAL; - } else { - tphi = sphi / cphi; - Az = atan2(sdlam , C45 * (tphi - cdlam)); - } - if( (tag = (Az > Azba)) ) { - cdlam = cos(sdlam = lp.lam + R110); - sdlam = sin(sdlam); - z = S20 * sphi + C20 * cphi * cdlam; - if (fabs(z) > 1.) { - if (fabs(z) > ONEEPS) F_ERROR - else z = z < 0. ? -1. : 1.; - } else - z = acos(z); - if (tphi != HUGE_VAL) - Az = atan2(sdlam, (C20 * tphi - S20 * cdlam)); - Av = Azab; - xy.y = rhoc; - } else { - z = S45 * (sphi + cphi * cdlam); - if (fabs(z) > 1.) { - if (fabs(z) > ONEEPS) F_ERROR - else z = z < 0. ? -1. : 1.; - } else - z = acos(z); - Av = Azba; - xy.y = -rhoc; - } - if (z < 0.) F_ERROR; - r = F * (t = pow(tan(.5 * z), n)); - if ((al = .5 * (R104 - z)) < 0.) F_ERROR; - al = (t + pow(al, n)) / T; - if (fabs(al) > 1.) { - if (fabs(al) > ONEEPS) F_ERROR - else al = al < 0. ? -1. : 1.; - } else - al = acos(al); - if (fabs(t = n * (Av - Az)) < al) - r /= cos(al + (tag ? t : -t)); - xy.x = r * sin(t); - xy.y += (tag ? -r : r) * cos(t); - if (P->noskew) { - t = xy.x; - xy.x = -xy.x * cAzc - xy.y * sAzc; - xy.y = -xy.y * cAzc + t * sAzc; - } - return (xy); +# define NITER 10 +# define lamB -.34894976726250681539 +# define n .63055844881274687180 +# define F 1.89724742567461030582 +# define Azab .81650043674686363166 +# define Azba 1.82261843856185925133 +# define T 1.27246578267089012270 +# define rhoc 1.20709121521568721927 +# define cAzc .69691523038678375519 +# define sAzc .71715351331143607555 +# define C45 .70710678118654752469 +# define S45 .70710678118654752410 +# define C20 .93969262078590838411 +# define S20 -.34202014332566873287 +# define R110 1.91986217719376253360 +# define R104 1.81514242207410275904 + + +struct pj_opaque { + int noskew; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double cphi, sphi, tphi, t, al, Az, z, Av, cdlam, sdlam, r; + int tag; + + cphi = cos(lp.phi); + sphi = sin(lp.phi); + cdlam = cos(sdlam = lamB - lp.lam); + sdlam = sin(sdlam); + if (fabs(fabs(lp.phi) - HALFPI) < EPS10) { + Az = lp.phi < 0. ? PI : 0.; + tphi = HUGE_VAL; + } else { + tphi = sphi / cphi; + Az = atan2(sdlam , C45 * (tphi - cdlam)); + } + if( (tag = (Az > Azba)) ) { + cdlam = cos(sdlam = lp.lam + R110); + sdlam = sin(sdlam); + z = S20 * sphi + C20 * cphi * cdlam; + if (fabs(z) > 1.) { + if (fabs(z) > ONEEPS) F_ERROR + else z = z < 0. ? -1. : 1.; + } else + z = acos(z); + if (tphi != HUGE_VAL) + Az = atan2(sdlam, (C20 * tphi - S20 * cdlam)); + Av = Azab; + xy.y = rhoc; + } else { + z = S45 * (sphi + cphi * cdlam); + if (fabs(z) > 1.) { + if (fabs(z) > ONEEPS) F_ERROR + else z = z < 0. ? -1. : 1.; + } else + z = acos(z); + Av = Azba; + xy.y = -rhoc; + } + if (z < 0.) F_ERROR; + r = F * (t = pow(tan(.5 * z), n)); + if ((al = .5 * (R104 - z)) < 0.) F_ERROR; + al = (t + pow(al, n)) / T; + if (fabs(al) > 1.) { + if (fabs(al) > ONEEPS) F_ERROR + else al = al < 0. ? -1. : 1.; + } else + al = acos(al); + if (fabs(t = n * (Av - Az)) < al) + r /= cos(al + (tag ? t : -t)); + xy.x = r * sin(t); + xy.y += (tag ? -r : r) * cos(t); + if (Q->noskew) { + t = xy.x; + xy.x = -xy.x * cAzc - xy.y * sAzc; + xy.y = -xy.y * cAzc + t * sAzc; + } + return (xy); } -INVERSE(s_inverse); /* spheroid */ - double t, r, rp, rl, al, z, fAz, Az, s, c, Av; - int neg, i; - - if (P->noskew) { - t = xy.x; - xy.x = -xy.x * cAzc + xy.y * sAzc; - xy.y = -xy.y * cAzc - t * sAzc; - } - if( (neg = (xy.x < 0.)) ) { - xy.y = rhoc - xy.y; - s = S20; - c = C20; - Av = Azab; - } else { - xy.y += rhoc; - s = S45; - c = C45; - Av = Azba; - } - rl = rp = r = hypot(xy.x, xy.y); - fAz = fabs(Az = atan2(xy.x, xy.y)); - for (i = NITER; i ; --i) { - z = 2. * atan(pow(r / F,1 / n)); - al = acos((pow(tan(.5 * z), n) + - pow(tan(.5 * (R104 - z)), n)) / T); - if (fAz < al) - r = rp * cos(al + (neg ? Az : -Az)); - if (fabs(rl - r) < EPS) - break; - rl = r; - } - if (! i) I_ERROR; - Az = Av - Az / n; - lp.phi = asin(s * cos(z) + c * sin(z) * cos(Az)); - lp.lam = atan2(sin(Az), c / tan(z) - s * cos(Az)); - if (neg) - lp.lam -= R110; - else - lp.lam = lamB - lp.lam; - return (lp); + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double t, r, rp, rl, al, z, fAz, Az, s, c, Av; + int neg, i; + + if (Q->noskew) { + t = xy.x; + xy.x = -xy.x * cAzc + xy.y * sAzc; + xy.y = -xy.y * cAzc - t * sAzc; + } + if( (neg = (xy.x < 0.)) ) { + xy.y = rhoc - xy.y; + s = S20; + c = C20; + Av = Azab; + } else { + xy.y += rhoc; + s = S45; + c = C45; + Av = Azba; + } + rl = rp = r = hypot(xy.x, xy.y); + fAz = fabs(Az = atan2(xy.x, xy.y)); + for (i = NITER; i ; --i) { + z = 2. * atan(pow(r / F,1 / n)); + al = acos((pow(tan(.5 * z), n) + + pow(tan(.5 * (R104 - z)), n)) / T); + if (fAz < al) + r = rp * cos(al + (neg ? Az : -Az)); + if (fabs(rl - r) < EPS) + break; + rl = r; + } + if (! i) I_ERROR; + Az = Av - Az / n; + lp.phi = asin(s * cos(z) + c * sin(z) * cos(Az)); + lp.lam = atan2(sin(Az), c / tan(z) - s * cos(Az)); + if (neg) + lp.lam -= R110; + else + lp.lam = lamB - lp.lam; + return (lp); +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + pj_dealloc (P->opaque); + return pj_dealloc(P); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(bipc) - P->noskew = pj_param(P->ctx, P->params, "bns").i; - P->inv = s_inverse; - P->fwd = s_forward; - P->es = 0.; -ENDENTRY(P) + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(bipc) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->noskew = pj_param(P->ctx, P->params, "bns").i; + P->inv = s_inverse; + P->fwd = s_forward; + P->es = 0.; + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_bipc_selftest (void) {return 0;} +#else + +int pj_bipc_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=bipc +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + char s_args[] = {"+proj=bipc +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {2452160.2177257561, -14548450.759654747}, + {2447915.213725341, -14763427.21279873}, + {2021695.5229349085, -14540413.695283702}, + {2018090.5030046992, -14755620.651414108}, + }; + + XY s_fwd_expect[] = { + {2460565.7409749646, -14598319.9893308}, + {2456306.1859352002, -14814033.339502094}, + {2028625.4978190989, -14590255.375482792}, + {2025008.1205891429, -14806200.018759441}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {-73.038700284978702, 17.248118466239116}, + {-73.03730373933017, 17.249414978178777}, + {-73.03589317304332, 17.245536403008771}, + {-73.034496627213585, 17.246832895573739}, + }; + + LP s_inv_expect[] = { + {-73.038693104942126, 17.248116270440242}, + {-73.037301330021322, 17.24940835333777}, + {-73.035895582251086, 17.245543027866539}, + {-73.034503807150301, 17.246835091521532}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif diff --git a/src/PJ_boggs.c b/src/PJ_boggs.c index b22a25ff..68010e6a 100644 --- a/src/PJ_boggs.c +++ b/src/PJ_boggs.c @@ -8,7 +8,10 @@ PROJ_HEAD(boggs, "Boggs Eumorphic") "\n\tPCyl., no inv., Sph."; # define FXC2 1.11072 # define FYC 0.49931 # define FYC2 1.41421356237309504880 -FORWARD(s_forward); /* spheroid */ + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; double theta, th1, c; int i; (void) P; @@ -29,5 +32,47 @@ FORWARD(s_forward); /* spheroid */ xy.y = FYC * (lp.phi + FYC2 * sin(theta)); return (xy); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(boggs) P->es = 0.; P->fwd = s_forward; ENDENTRY(P) + + +static void *freeup_new (PJ *P) { /* Destructor */ + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(boggs) { + P->es = 0.; + P->fwd = s_forward; + return P; +} + +#ifdef PJ_OMIT_SELFTEST +int pj_boggs_selftest (void) {return 0;} +#else +int pj_boggs_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=boggs +a=6400000 +lat_1=0 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 211949.70080818201, 117720.99830541089}, + { 211949.70080818201, -117720.99830541089}, + {-211949.70080818201, 117720.99830541089}, + {-211949.70080818201, -117720.99830541089}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 0, fwd_in, 0, s_fwd_expect, 0, 0, 0); +} +#endif diff --git a/src/PJ_bonne.c b/src/PJ_bonne.c index 4b1e7866..13309f3f 100644 --- a/src/PJ_bonne.c +++ b/src/PJ_bonne.c @@ -1,51 +1,70 @@ -#define PROJ_PARMS__ \ - double phi1; \ - double cphi1; \ - double am1; \ - double m1; \ - double *en; #define PJ_LIB__ #include <projects.h> + PROJ_HEAD(bonne, "Bonne (Werner lat_1=90)") "\n\tConic Sph&Ell\n\tlat_1="; #define EPS10 1e-10 -FORWARD(e_forward); /* ellipsoid */ + +struct pj_opaque { + double phi1; + double cphi1; + double am1; + double m1; + double *en; +}; + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; double rh, E, c; - rh = P->am1 + P->m1 - pj_mlfn(lp.phi, E = sin(lp.phi), c = cos(lp.phi), P->en); + rh = Q->am1 + Q->m1 - pj_mlfn(lp.phi, E = sin(lp.phi), c = cos(lp.phi), Q->en); E = c * lp.lam / (rh * sqrt(1. - P->es * E * E)); xy.x = rh * sin(E); - xy.y = P->am1 - rh * cos(E); - return (xy); + xy.y = Q->am1 - rh * cos(E); + return xy; } -FORWARD(s_forward); /* spheroid */ + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; double E, rh; - rh = P->cphi1 + P->phi1 - lp.phi; + rh = Q->cphi1 + Q->phi1 - lp.phi; if (fabs(rh) > EPS10) { xy.x = rh * sin(E = lp.lam * cos(lp.phi) / rh); - xy.y = P->cphi1 - rh * cos(E); + xy.y = Q->cphi1 - rh * cos(E); } else xy.x = xy.y = 0.; - return (xy); + return xy; } -INVERSE(s_inverse); /* spheroid */ + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; double rh; - rh = hypot(xy.x, xy.y = P->cphi1 - xy.y); - lp.phi = P->cphi1 + P->phi1 - rh; + rh = hypot(xy.x, xy.y = Q->cphi1 - xy.y); + lp.phi = Q->cphi1 + Q->phi1 - rh; if (fabs(lp.phi) > HALFPI) I_ERROR; if (fabs(fabs(lp.phi) - HALFPI) <= EPS10) lp.lam = 0.; else lp.lam = rh * atan2(xy.x, xy.y) / cos(lp.phi); - return (lp); + return lp; } -INVERSE(e_inverse); /* ellipsoid */ + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; double s, rh; - rh = hypot(xy.x, xy.y = P->am1 - xy.y); - lp.phi = pj_inv_mlfn(P->ctx, P->am1 + P->m1 - rh, P->es, P->en); + rh = hypot(xy.x, xy.y = Q->am1 - xy.y); + lp.phi = pj_inv_mlfn(P->ctx, Q->am1 + Q->m1 - rh, P->es, Q->en); if ((s = fabs(lp.phi)) < HALFPI) { s = sin(lp.phi); lp.lam = rh * atan2(xy.x, xy.y) * @@ -53,33 +72,109 @@ INVERSE(e_inverse); /* ellipsoid */ } else if (fabs(s - HALFPI) <= EPS10) lp.lam = 0.; else I_ERROR; - return (lp); + return lp; } -FREEUP; - if (P) { - if (P->en) - pj_dalloc(P->en); - pj_dalloc(P); - } + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque->en); + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; } -ENTRY1(bonne, en) + + +PJ *PROJECTION(bonne) { double c; + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; - P->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; - if (fabs(P->phi1) < EPS10) E_ERROR(-23); + Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; + if (fabs(Q->phi1) < EPS10) E_ERROR(-23); if (P->es) { - P->en = pj_enfn(P->es); - P->m1 = pj_mlfn(P->phi1, P->am1 = sin(P->phi1), - c = cos(P->phi1), P->en); - P->am1 = c / (sqrt(1. - P->es * P->am1 * P->am1) * P->am1); + Q->en = pj_enfn(P->es); + Q->m1 = pj_mlfn(Q->phi1, Q->am1 = sin(Q->phi1), + c = cos(Q->phi1), Q->en); + Q->am1 = c / (sqrt(1. - P->es * Q->am1 * Q->am1) * Q->am1); P->inv = e_inverse; P->fwd = e_forward; } else { - if (fabs(P->phi1) + EPS10 >= HALFPI) - P->cphi1 = 0.; + if (fabs(Q->phi1) + EPS10 >= HALFPI) + Q->cphi1 = 0.; else - P->cphi1 = 1. / tan(P->phi1); + Q->cphi1 = 1. / tan(Q->phi1); P->inv = s_inverse; P->fwd = s_forward; } -ENDENTRY(P) + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_bonne_selftest (void) {return 0;} +#else +int pj_bonne_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=bonne +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + char s_args[] = {"+proj=bonne +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 222605.29609715697, 55321.139565494814}, + { 222605.29609923941, -165827.64779905154}, + {-222605.29609715697, 55321.139565494814}, + {-222605.29609923941, -165827.64779905154}, + }; + + XY s_fwd_expect[] = { + { 223368.11557252839, 55884.555246393575}, + { 223368.11557463196, -167517.59936969393}, + {-223368.11557252839, 55884.555246393575}, + {-223368.11557463196, -167517.59936969393}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + { 0.0017966987691132891, 0.50090436853737497}, + { 0.0017966982774478867, 0.4990956309655612}, + {-0.0017966987691132891, 0.50090436853737497}, + {-0.0017966982774478867, 0.4990956309655612}, + }; + + LP s_inv_expect[] = { + { 0.0017905615332457991, 0.50089524631087834}, + { 0.0017905610449335603, 0.49910475320072978}, + {-0.0017905615332457991, 0.50089524631087834}, + {-0.0017905610449335603, 0.49910475320072978}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif diff --git a/src/PJ_calcofi.c b/src/PJ_calcofi.c index 2d142938..661893f6 100644 --- a/src/PJ_calcofi.c +++ b/src/PJ_calcofi.c @@ -1,25 +1,27 @@ #define PJ_LIB__ #include <projects.h> + +PROJ_HEAD(calcofi, + "Cal Coop Ocean Fish Invest Lines/Stations") "\n\tCyl, Sph&Ell"; + #include <string.h> #include <stdio.h> #include <math.h> #include <proj_api.h> #include <errno.h> -/* Conversions for the California Cooperative Oceanic Fisheries Investigations +/* Conversions for the California Cooperative Oceanic Fisheries Investigations Line/Station coordinate system following the algorithm of: -Eber, L.E., and R.P. Hewitt. 1979. Conversion algorithms for the CALCOFI -station grid. California Cooperative Oceanic Fisheries Investigations Reports -20:135-137. (corrected for typographical errors). +Eber, L.E., and R.P. Hewitt. 1979. Conversion algorithms for the CALCOFI +station grid. California Cooperative Oceanic Fisheries Investigations Reports +20:135-137. (corrected for typographical errors). http://www.calcofi.org/publications/calcofireports/v20/Vol_20_Eber___Hewitt.pdf -They assume 1 unit of CalCOFI Line == 1/5 degree in longitude or -meridional units at reference point O, and similarly 1 unit of CalCOFI -Station == 1/15 of a degree at O. -By convention, CalCOFI Line/Station conversions use Clarke 1866 but we use +They assume 1 unit of CalCOFI Line == 1/5 degree in longitude or +meridional units at reference point O, and similarly 1 unit of CalCOFI +Station == 1/15 of a degree at O. +By convention, CalCOFI Line/Station conversions use Clarke 1866 but we use whatever ellipsoid is provided. */ -PROJ_HEAD(calcofi, - "Cal Coop Ocean Fish Invest Lines/Stations") "\n\tCyl, Sph&Ell"; #define EPS10 1.e-10 #define DEG_TO_LINE 5 @@ -31,14 +33,17 @@ PROJ_HEAD(calcofi, #define PT_O_LAMBDA -2.1144663887911301 /* lon -121.15 and */ #define PT_O_PHI 0.59602993955606354 /* lat 34.15 */ #define ROTATION_ANGLE 0.52359877559829882 /*CalCOFI angle of 30 deg in rad */ -FORWARD(e_forward); /* ellipsoid */ + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; double oy; /* pt O y value in Mercator */ double l1; /* l1 and l2 are distances calculated using trig that sum to the east/west distance between point O and point xy */ double l2; - double ry; /* r is the point on the same station as o (60) and the same + double ry; /* r is the point on the same station as o (60) and the same line as xy xy, r, o form a right triangle */ - /* if the user has specified +lon_0 or +k0 for some reason, + /* if the user has specified +lon_0 or +k0 for some reason, we're going to ignore it so that xy is consistent with point O */ lp.lam = lp.lam + P->lam0; if (fabs(fabs(lp.phi) - HALFPI) <= EPS10) F_ERROR; @@ -49,18 +54,21 @@ FORWARD(e_forward); /* ellipsoid */ l2 = -xy.x - l1 + PT_O_LAMBDA; ry = l2 * cos(ROTATION_ANGLE) * sin(ROTATION_ANGLE) + xy.y; ry = pj_phi2(P->ctx, exp(-ry), P->e); /*inverse Mercator*/ - xy.x = PT_O_LINE - RAD_TO_DEG * - (ry - PT_O_PHI) * DEG_TO_LINE / cos(ROTATION_ANGLE); - xy.y = PT_O_STATION + RAD_TO_DEG * + xy.x = PT_O_LINE - RAD_TO_DEG * + (ry - PT_O_PHI) * DEG_TO_LINE / cos(ROTATION_ANGLE); + xy.y = PT_O_STATION + RAD_TO_DEG * (ry - lp.phi) * DEG_TO_STATION / sin(ROTATION_ANGLE); /* set a = 1, x0 = 0, and y0 = 0 so that no further unit adjustments are done */ P->a = 1; P->x0 = 0; P->y0 = 0; - return (xy); + return xy; } -FORWARD(s_forward); /* spheroid */ + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; double oy; double l1; double l2; @@ -73,17 +81,20 @@ FORWARD(s_forward); /* spheroid */ l1 = (xy.y - oy) * tan(ROTATION_ANGLE); l2 = -xy.x - l1 + PT_O_LAMBDA; ry = l2 * cos(ROTATION_ANGLE) * sin(ROTATION_ANGLE) + xy.y; - ry = HALFPI - 2. * atan(exp(-ry)); - xy.x = PT_O_LINE - RAD_TO_DEG * - (ry - PT_O_PHI) * DEG_TO_LINE / cos(ROTATION_ANGLE); - xy.y = PT_O_STATION + RAD_TO_DEG * + ry = HALFPI - 2. * atan(exp(-ry)); + xy.x = PT_O_LINE - RAD_TO_DEG * + (ry - PT_O_PHI) * DEG_TO_LINE / cos(ROTATION_ANGLE); + xy.y = PT_O_STATION + RAD_TO_DEG * (ry - lp.phi) * DEG_TO_STATION / sin(ROTATION_ANGLE); P->a = 1; P->x0 = 0; P->y0 = 0; - return (xy); + return xy; } -INVERSE(e_inverse); /* ellipsoid */ + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; double ry; /* y value of point r */ double oymctr; /* Mercator-transformed y value of point O */ double rymctr; /* Mercator-transformed ry */ @@ -93,7 +104,7 @@ INVERSE(e_inverse); /* ellipsoid */ /* turn x and y back into Line/Station */ xy.x /= P->ra; xy.y /= P->ra; - ry = PT_O_PHI - LINE_TO_RAD * (xy.x - PT_O_LINE) * + ry = PT_O_PHI - LINE_TO_RAD * (xy.x - PT_O_LINE) * cos(ROTATION_ANGLE); lp.phi = ry - STATION_TO_RAD * (xy.y - PT_O_STATION) * sin(ROTATION_ANGLE); oymctr = -log(pj_tsfn(PT_O_PHI, sin(PT_O_PHI), P->e)); @@ -103,9 +114,12 @@ INVERSE(e_inverse); /* ellipsoid */ l2 = (rymctr - xymctr) / (cos(ROTATION_ANGLE) * sin(ROTATION_ANGLE)); lp.lam = PT_O_LAMBDA - (l1 + l2); P->over = 1; - return (lp); + return lp; } -INVERSE(s_inverse); /* spheroid */ + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; double ry; double oymctr; double rymctr; @@ -114,7 +128,7 @@ INVERSE(s_inverse); /* spheroid */ double l2; xy.x /= P->ra; xy.y /= P->ra; - ry = PT_O_PHI - LINE_TO_RAD * (xy.x - PT_O_LINE) * + ry = PT_O_PHI - LINE_TO_RAD * (xy.x - PT_O_LINE) * cos(ROTATION_ANGLE); lp.phi = ry - STATION_TO_RAD * (xy.y - PT_O_STATION) * sin(ROTATION_ANGLE); oymctr = log(tan(FORTPI + .5 * PT_O_PHI)); @@ -124,10 +138,23 @@ INVERSE(s_inverse); /* spheroid */ l2 = (rymctr - xymctr) / (cos(ROTATION_ANGLE) * sin(ROTATION_ANGLE)); lp.lam = PT_O_LAMBDA - (l1 + l2); P->over = 1; - return (lp); + return lp; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(calcofi) + + +static void *freeup_new (PJ *P) { /* Destructor */ + return pj_dealloc (P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(calcofi) { + P->opaque = 0; + if (P->es) { /* ellipsoid */ P->inv = e_inverse; P->fwd = e_forward; @@ -135,4 +162,65 @@ ENTRY0(calcofi) P->inv = s_inverse; P->fwd = s_forward; } -ENDENTRY(P) + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_calcofi_selftest (void) {return 0;} +#else + +int pj_calcofi_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=calcofi +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + char s_args[] = {"+proj=calcofi +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {508.44487214981905, -1171.7648604175156}, + {514.99916815188112, -1145.8219814677668}, + {500.68538412539851, -1131.4453779204598}, + {507.36971913666355, -1106.1782014834275}, + }; + + XY s_fwd_expect[] = { + {507.09050748781806, -1164.7273751978314}, + {513.68613637462886, -1138.9992682173072}, + {499.33626147591531, -1124.4351309968195}, + {506.0605703929898, -1099.3756650673038}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {-110.36330792469906, 12.032056975840137}, + {-98.455008863288782, 18.698723642506803}, + {-207.4470245036909, 81.314089278595247}, + {-62.486322854481287, 87.980755945261919}, + }; + + LP s_inv_expect[] = { + {-110.30519040955151, 12.032056975840137}, + {-98.322360950234085, 18.698723642506803}, + {-207.54490681381429, 81.314089278595247}, + {-62.576950371885275, 87.980755945261919}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif diff --git a/src/PJ_cass.c b/src/PJ_cass.c index 38fa9db5..28d35b91 100644 --- a/src/PJ_cass.c +++ b/src/PJ_cass.c @@ -1,79 +1,178 @@ -#define PROJ_PARMS__ \ - double m0; \ - double n; \ - double t; \ - double a1; \ - double c; \ - double r; \ - double dd; \ - double d2; \ - double a2; \ - double tn; \ - double *en; #define PJ_LIB__ -# include <projects.h> +# include <projects.h> PROJ_HEAD(cass, "Cassini") "\n\tCyl, Sph&Ell"; -# define EPS10 1e-10 -# define C1 .16666666666666666666 -# define C2 .00833333333333333333 -# define C3 .04166666666666666666 -# define C4 .33333333333333333333 -# define C5 .06666666666666666666 -FORWARD(e_forward); /* ellipsoid */ - xy.y = pj_mlfn(lp.phi, P->n = sin(lp.phi), P->c = cos(lp.phi), P->en); - P->n = 1./sqrt(1. - P->es * P->n * P->n); - P->tn = tan(lp.phi); P->t = P->tn * P->tn; - P->a1 = lp.lam * P->c; - P->c *= P->es * P->c / (1 - P->es); - P->a2 = P->a1 * P->a1; - xy.x = P->n * P->a1 * (1. - P->a2 * P->t * - (C1 - (8. - P->t + 8. * P->c) * P->a2 * C2)); - xy.y -= P->m0 - P->n * P->tn * P->a2 * - (.5 + (5. - P->t + 6. * P->c) * P->a2 * C3); - return (xy); + + +# define EPS10 1e-10 +# define C1 .16666666666666666666 +# define C2 .00833333333333333333 +# define C3 .04166666666666666666 +# define C4 .33333333333333333333 +# define C5 .06666666666666666666 + + +struct pj_opaque { + double *en; + double m0; +}; + + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + double n, t, a1, c, a2, tn; + XY xy = {0.0, 0.0}; + struct pj_opaque *Q = P->opaque; + + xy.y = pj_mlfn (lp.phi, n = sin (lp.phi), c = cos (lp.phi), Q->en); + + n = 1./sqrt(1. - P->es * n*n); + tn = tan(lp.phi); t = tn * tn; + a1 = lp.lam * c; + c *= P->es * c / (1 - P->es); + a2 = a1 * a1; + + xy.x = n * a1 * (1. - a2 * t * + (C1 - (8. - t + 8. * c) * a2 * C2)); + xy.y -= Q->m0 - n * tn * a2 * + (.5 + (5. - t + 6. * c) * a2 * C3); + + return xy; +} + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0, 0.0}; + xy.x = asin (cos (lp.phi) * sin (lp.lam)); + xy.y = atan2 (tan (lp.phi), cos (lp.lam)) - P->phi0; + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + double n, t, r, dd, d2, tn, ph1; + LP lp = {0.0, 0.0}; + struct pj_opaque *Q = P->opaque; + + ph1 = pj_inv_mlfn (P->ctx, Q->m0 + xy.y, P->es, Q->en); + tn = tan (ph1); t = tn*tn; + n = sin (ph1); + r = 1. / (1. - P->es * n * n); + n = sqrt (r); + r *= (1. - P->es) * n; + dd = xy.x / n; + d2 = dd * dd; + lp.phi = ph1 - (n * tn / r) * d2 * + (.5 - (1. + 3. * t) * d2 * C3); + lp.lam = dd * (1. + t * d2 * + (-C4 + (1. + 3. * t) * d2 * C5)) / cos (ph1); + return lp; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double dd; + lp.phi = asin(sin(dd = xy.y + P->phi0) * cos(xy.x)); + lp.lam = atan2(tan(xy.x), cos(dd)); + return lp; } -FORWARD(s_forward); /* spheroid */ - xy.x = asin(cos(lp.phi) * sin(lp.lam)); - xy.y = atan2(tan(lp.phi) , cos(lp.lam)) - P->phi0; - return (xy); + + +static void *freeup_new(PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc(P->opaque->en); + pj_dealloc(P->opaque); + return pj_dealloc(P); } -INVERSE(e_inverse); /* ellipsoid */ - double ph1; - - ph1 = pj_inv_mlfn(P->ctx, P->m0 + xy.y, P->es, P->en); - P->tn = tan(ph1); P->t = P->tn * P->tn; - P->n = sin(ph1); - P->r = 1. / (1. - P->es * P->n * P->n); - P->n = sqrt(P->r); - P->r *= (1. - P->es) * P->n; - P->dd = xy.x / P->n; - P->d2 = P->dd * P->dd; - lp.phi = ph1 - (P->n * P->tn / P->r) * P->d2 * - (.5 - (1. + 3. * P->t) * P->d2 * C3); - lp.lam = P->dd * (1. + P->t * P->d2 * - (-C4 + (1. + 3. * P->t) * P->d2 * C5)) / cos(ph1); - return (lp); + +static void freeup(PJ *P) { /* Destructor */ + freeup_new (P); + return; } -INVERSE(s_inverse); /* spheroid */ - lp.phi = asin(sin(P->dd = xy.y + P->phi0) * cos(xy.x)); - lp.lam = atan2(tan(xy.x), cos(P->dd)); - return (lp); + +PJ *PROJECTION(cass) { + + /* Spheroidal? */ + if (0==P->es) { + P->inv = s_inverse; + P->fwd = s_forward; + return P; + } + + /* otherwise it's ellipsoidal */ + P->opaque = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==P->opaque) + return freeup_new (P); + + P->opaque->en = pj_enfn (P->es); + if (0==P->opaque->en) + return freeup_new (P); + + P->opaque->m0 = pj_mlfn (P->phi0, sin (P->phi0), cos (P->phi0), P->opaque->en); + P->inv = e_inverse; + P->fwd = e_forward; + + return P; } -FREEUP; - if (P) { - if (P->en) - pj_dalloc(P->en); - pj_dalloc(P); - } + + +#ifdef PJ_OMIT_SELFTEST +int pj_cass_selftest (void) {return 0;} +#else + +int pj_cass_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=cass +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + char s_args[] = {"+proj=cass +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 222605.28577699114, 110642.22925399939}, + { 222605.28577699114, -110642.22925399939}, + {-222605.28577699114, 110642.22925399939}, + {-222605.28577699114, -110642.22925399939}, + }; + + XY s_fwd_expect[] = { + { 223368.10520348375, 111769.14504058579}, + { 223368.10520348375, -111769.14504058579}, + {-223368.10520348375, 111769.14504058579}, + {-223368.10520348375, -111769.14504058579}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + { 0.0017966305684613522, 0.00090436947663183841}, + { 0.0017966305684613522, -0.00090436947663183841}, + {-0.0017966305684613522, 0.00090436947663183841}, + {-0.0017966305684613522, -0.00090436947663183841}, + }; + + LP s_inv_expect[] = { + { 0.0017904931100023887, 0.00089524655445477922}, + { 0.0017904931100023887, -0.00089524655445477922}, + {-0.0017904931100023887, 0.00089524655445477922}, + {-0.0017904931100023887, -0.00089524655445477922}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); } -ENTRY1(cass, en) - if (P->es) { - if (!(P->en = pj_enfn(P->es))) E_ERROR_0; - P->m0 = pj_mlfn(P->phi0, sin(P->phi0), cos(P->phi0), P->en); - P->inv = e_inverse; - P->fwd = e_forward; - } else { - P->inv = s_inverse; - P->fwd = s_forward; - } -ENDENTRY(P) +#endif diff --git a/src/PJ_cc.c b/src/PJ_cc.c index 0f573929..475fbae4 100644 --- a/src/PJ_cc.c +++ b/src/PJ_cc.c @@ -1,20 +1,89 @@ -#define PROJ_PARMS__ \ - double ap; #define PJ_LIB__ #include <projects.h> + PROJ_HEAD(cc, "Central Cylindrical") "\n\tCyl, Sph"; #define EPS10 1.e-10 -FORWARD(s_forward); /* spheroid */ - if (fabs(fabs(lp.phi) - HALFPI) <= EPS10) F_ERROR; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + if (fabs (fabs(lp.phi) - HALFPI) <= EPS10) F_ERROR; xy.x = lp.lam; xy.y = tan(lp.phi); - return (xy); + return xy; } -INVERSE(s_inverse); /* spheroid */ + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; (void) P; lp.phi = atan(xy.y); lp.lam = xy.x; - return (lp); + return lp; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(cc) P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P) + + +static void *freeup_new (PJ *P) { /* Destructor */ + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(cc) { + P->es = 0.; + + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_cc_selftest (void) {return 0;} +#else + +int pj_cc_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=cc +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + {223402.14425527418, 111712.41554059254}, + {223402.14425527418, -111712.41554059254}, + {-223402.14425527418, 111712.41554059254}, + {-223402.14425527418, -111712.41554059254}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + {0.0017904931097838226, 0.00089524655481905597}, + {0.0017904931097838226, -0.00089524655481905597}, + {-0.0017904931097838226, 0.00089524655481905597}, + {-0.0017904931097838226, -0.00089524655481905597}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_cea.c b/src/PJ_cea.c index 44c0a887..e1bcb0af 100644 --- a/src/PJ_cea.c +++ b/src/PJ_cea.c @@ -1,63 +1,155 @@ -#define PROJ_PARMS__ \ - double qp; \ - double *apa; #define PJ_LIB__ -# include <projects.h> +#include <projects.h> + +struct pj_opaque { + double qp; + double *apa; +}; + PROJ_HEAD(cea, "Equal Area Cylindrical") "\n\tCyl, Sph&Ell\n\tlat_ts="; -# define EPS 1e-10 -FORWARD(e_forward); /* spheroid */ - xy.x = P->k0 * lp.lam; - xy.y = .5 * pj_qsfn(sin(lp.phi), P->e, P->one_es) / P->k0; - return (xy); -} -FORWARD(s_forward); /* spheroid */ - xy.x = P->k0 * lp.lam; - xy.y = sin(lp.phi) / P->k0; - return (xy); -} -INVERSE(e_inverse); /* spheroid */ - lp.phi = pj_authlat(asin( 2. * xy.y * P->k0 / P->qp), P->apa); - lp.lam = xy.x / P->k0; - return (lp); -} -INVERSE(s_inverse); /* spheroid */ - double t; - - if ((t = fabs(xy.y *= P->k0)) - EPS <= 1.) { - if (t >= 1.) - lp.phi = xy.y < 0. ? -HALFPI : HALFPI; - else - lp.phi = asin(xy.y); - lp.lam = xy.x / P->k0; - } else I_ERROR; - return (lp); -} -FREEUP; - if (P) { - if (P->apa) - pj_dalloc(P->apa); - pj_dalloc(P); - } -} -ENTRY1(cea, apa) - double t = 0.0; - - if (pj_param(P->ctx, P->params, "tlat_ts").i) { - P->k0 = cos(t = pj_param(P->ctx, P->params, "rlat_ts").f); - if (P->k0 < 0.) { - E_ERROR(-24); - } - } - if (P->es) { - t = sin(t); - P->k0 /= sqrt(1. - P->es * t * t); - P->e = sqrt(P->es); - if (!(P->apa = pj_authset(P->es))) E_ERROR_0; - P->qp = pj_qsfn(1., P->e, P->one_es); - P->inv = e_inverse; - P->fwd = e_forward; - } else { - P->inv = s_inverse; - P->fwd = s_forward; - } -ENDENTRY(P) +# define EPS 1e-10 + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + xy.x = P->k0 * lp.lam; + xy.y = 0.5 * pj_qsfn (sin (lp.phi), P->e, P->one_es) / P->k0; + return xy; +} + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + xy.x = P->k0 * lp.lam; + xy.y = sin(lp.phi) / P->k0; + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + lp.phi = pj_authlat(asin( 2. * xy.y * P->k0 / P->opaque->qp), P->opaque->apa); + lp.lam = xy.x / P->k0; + return lp; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double t; + + if ((t = fabs(xy.y *= P->k0)) - EPS <= 1.) { + if (t >= 1.) + lp.phi = xy.y < 0. ? -HALFPI : HALFPI; + else + lp.phi = asin(xy.y); + lp.lam = xy.x / P->k0; + } else I_ERROR; + return (lp); +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque->apa); + pj_dealloc (P->opaque); + return pj_dealloc (P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + +PJ *PROJECTION(cea) { + double t = 0.0; + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + + if (pj_param(P->ctx, P->params, "tlat_ts").i) { + P->k0 = cos(t = pj_param(P->ctx, P->params, "rlat_ts").f); + if (P->k0 < 0.) { + E_ERROR(-24); + } + } + if (P->es) { + t = sin(t); + P->k0 /= sqrt(1. - P->es * t * t); + P->e = sqrt(P->es); + if (!(Q->apa = pj_authset(P->es))) E_ERROR_0; + Q->qp = pj_qsfn(1., P->e, P->one_es); + P->inv = e_inverse; + P->fwd = e_forward; + } else { + P->inv = s_inverse; + P->fwd = s_forward; + } + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_cea_selftest (void) {return 0;} +#else + +int pj_cea_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=cea +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + char s_args[] = {"+proj=cea +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 222638.981586547132, 110568.812396267356}, + { 222638.981586547132, -110568.812396265886}, + {-222638.981586547132, 110568.812396267356}, + {-222638.981586547132, -110568.812396265886}, + }; + + XY s_fwd_expect[] = { + { 223402.144255274179, 111695.401198614476}, + { 223402.144255274179, -111695.401198614476}, + {-223402.144255274179, 111695.401198614476}, + {-223402.144255274179, -111695.401198614476}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + { 0.00179663056823904264, 0.000904369476105564289}, + { 0.00179663056823904264, -0.000904369476105564289}, + {-0.00179663056823904264, 0.000904369476105564289}, + {-0.00179663056823904264, -0.000904369476105564289}, + }; + + LP s_inv_expect[] = { + { 0.00179049310978382265, 0.000895246554928338998}, + { 0.00179049310978382265, -0.000895246554928338998}, + {-0.00179049310978382265, 0.000895246554928338998}, + {-0.00179049310978382265, -0.000895246554928338998}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + +#endif diff --git a/src/PJ_chamb.c b/src/PJ_chamb.c index 65f21129..68d99f26 100644 --- a/src/PJ_chamb.c +++ b/src/PJ_chamb.c @@ -1,112 +1,179 @@ -typedef struct { double r, Az; } VECT; -#define PROJ_PARMS__ \ - struct { /* control point data */ \ - double phi, lam; \ - double cosphi, sinphi; \ - VECT v; \ - XY p; \ - double Az; \ - } c[3]; \ - XY p; \ - double beta_0, beta_1, beta_2; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + +typedef struct { double r, Az; } VECT; +struct pj_opaque { + struct { /* control point data */ + double phi, lam; + double cosphi, sinphi; + VECT v; + XY p; + double Az; + } c[3]; + XY p; + double beta_0, beta_1, beta_2; +}; + PROJ_HEAD(chamb, "Chamberlin Trimetric") "\n\tMisc Sph, no inv." "\n\tlat_1= lon_1= lat_2= lon_2= lat_3= lon_3="; -#include <stdio.h> + +#include <stdio.h> #define THIRD 0.333333333333333333 #define TOL 1e-9 - static VECT /* distance and azimuth from point 1 to point 2 */ -vect(projCtx ctx, double dphi, double c1, double s1, double c2, double s2, double dlam) { - VECT v; - double cdl, dp, dl; - - cdl = cos(dlam); - if (fabs(dphi) > 1. || fabs(dlam) > 1.) - v.r = aacos(ctx, s1 * s2 + c1 * c2 * cdl); - else { /* more accurate for smaller distances */ - dp = sin(.5 * dphi); - dl = sin(.5 * dlam); - v.r = 2. * aasin(ctx,sqrt(dp * dp + c1 * c2 * dl * dl)); - } - if (fabs(v.r) > TOL) - v.Az = atan2(c2 * sin(dlam), c1 * s2 - s1 * c2 * cdl); - else - v.r = v.Az = 0.; - return v; + +/* distance and azimuth from point 1 to point 2 */ +static VECT vect(projCtx ctx, double dphi, double c1, double s1, double c2, double s2, double dlam) { + VECT v; + double cdl, dp, dl; + + cdl = cos(dlam); + if (fabs(dphi) > 1. || fabs(dlam) > 1.) + v.r = aacos(ctx, s1 * s2 + c1 * c2 * cdl); + else { /* more accurate for smaller distances */ + dp = sin(.5 * dphi); + dl = sin(.5 * dlam); + v.r = 2. * aasin(ctx,sqrt(dp * dp + c1 * c2 * dl * dl)); + } + if (fabs(v.r) > TOL) + v.Az = atan2(c2 * sin(dlam), c1 * s2 - s1 * c2 * cdl); + else + v.r = v.Az = 0.; + return v; +} + +/* law of cosines */ +static double lc(projCtx ctx, double b,double c,double a) { + return aacos(ctx, .5 * (b * b + c * c - a * a) / (b * c)); +} + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double sinphi, cosphi, a; + VECT v[3]; + int i, j; + + sinphi = sin(lp.phi); + cosphi = cos(lp.phi); + for (i = 0; i < 3; ++i) { /* dist/azimiths from control */ + v[i] = vect(P->ctx, lp.phi - Q->c[i].phi, Q->c[i].cosphi, Q->c[i].sinphi, + cosphi, sinphi, lp.lam - Q->c[i].lam); + if ( ! v[i].r) + break; + v[i].Az = adjlon(v[i].Az - Q->c[i].v.Az); + } + if (i < 3) /* current point at control point */ + xy = Q->c[i].p; + else { /* point mean of intersepts */ + xy = Q->p; + for (i = 0; i < 3; ++i) { + j = i == 2 ? 0 : i + 1; + a = lc(P->ctx,Q->c[i].v.r, v[i].r, v[j].r); + if (v[i].Az < 0.) + a = -a; + if (! i) { /* coord comp unique to each arc */ + xy.x += v[i].r * cos(a); + xy.y -= v[i].r * sin(a); + } else if (i == 1) { + a = Q->beta_1 - a; + xy.x -= v[i].r * cos(a); + xy.y -= v[i].r * sin(a); + } else { + a = Q->beta_2 - a; + xy.x += v[i].r * cos(a); + xy.y += v[i].r * sin(a); + } + } + xy.x *= THIRD; /* mean of arc intercepts */ + xy.y *= THIRD; + } + return xy; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); } - static double /* law of cosines */ -lc(projCtx ctx, double b,double c,double a) { - return aacos(ctx, .5 * (b * b + c * c - a * a) / (b * c)); + +static void freeup (PJ *P) { + freeup_new (P); + return; } -FORWARD(s_forward); /* spheroid */ - double sinphi, cosphi, a; - VECT v[3]; - int i, j; - - sinphi = sin(lp.phi); - cosphi = cos(lp.phi); - for (i = 0; i < 3; ++i) { /* dist/azimiths from control */ - v[i] = vect(P->ctx, lp.phi - P->c[i].phi, P->c[i].cosphi, P->c[i].sinphi, - cosphi, sinphi, lp.lam - P->c[i].lam); - if ( ! v[i].r) - break; - v[i].Az = adjlon(v[i].Az - P->c[i].v.Az); - } - if (i < 3) /* current point at control point */ - xy = P->c[i].p; - else { /* point mean of intersepts */ - xy = P->p; - for (i = 0; i < 3; ++i) { - j = i == 2 ? 0 : i + 1; - a = lc(P->ctx,P->c[i].v.r, v[i].r, v[j].r); - if (v[i].Az < 0.) - a = -a; - if (! i) { /* coord comp unique to each arc */ - xy.x += v[i].r * cos(a); - xy.y -= v[i].r * sin(a); - } else if (i == 1) { - a = P->beta_1 - a; - xy.x -= v[i].r * cos(a); - xy.y -= v[i].r * sin(a); - } else { - a = P->beta_2 - a; - xy.x += v[i].r * cos(a); - xy.y += v[i].r * sin(a); - } - } - xy.x *= THIRD; /* mean of arc intercepts */ - xy.y *= THIRD; - } - return xy; + + +PJ *PROJECTION(chamb) { + int i, j; + char line[10]; + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + + for (i = 0; i < 3; ++i) { /* get control point locations */ + (void)sprintf(line, "rlat_%d", i+1); + Q->c[i].phi = pj_param(P->ctx, P->params, line).f; + (void)sprintf(line, "rlon_%d", i+1); + Q->c[i].lam = pj_param(P->ctx, P->params, line).f; + Q->c[i].lam = adjlon(Q->c[i].lam - P->lam0); + Q->c[i].cosphi = cos(Q->c[i].phi); + Q->c[i].sinphi = sin(Q->c[i].phi); + } + for (i = 0; i < 3; ++i) { /* inter ctl pt. distances and azimuths */ + j = i == 2 ? 0 : i + 1; + Q->c[i].v = vect(P->ctx,Q->c[j].phi - Q->c[i].phi, Q->c[i].cosphi, Q->c[i].sinphi, + Q->c[j].cosphi, Q->c[j].sinphi, Q->c[j].lam - Q->c[i].lam); + if (! Q->c[i].v.r) E_ERROR(-25); + /* co-linearity problem ignored for now */ + } + Q->beta_0 = lc(P->ctx,Q->c[0].v.r, Q->c[2].v.r, Q->c[1].v.r); + Q->beta_1 = lc(P->ctx,Q->c[0].v.r, Q->c[1].v.r, Q->c[2].v.r); + Q->beta_2 = PI - Q->beta_0; + Q->p.y = 2. * (Q->c[0].p.y = Q->c[1].p.y = Q->c[2].v.r * sin(Q->beta_0)); + Q->c[2].p.y = 0.; + Q->c[0].p.x = - (Q->c[1].p.x = 0.5 * Q->c[0].v.r); + Q->p.x = Q->c[2].p.x = Q->c[0].p.x + Q->c[2].v.r * cos(Q->beta_0); + + P->es = 0.; + P->fwd = s_forward; + + return P; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(chamb) - int i, j; - char line[10]; - - for (i = 0; i < 3; ++i) { /* get control point locations */ - (void)sprintf(line, "rlat_%d", i+1); - P->c[i].phi = pj_param(P->ctx, P->params, line).f; - (void)sprintf(line, "rlon_%d", i+1); - P->c[i].lam = pj_param(P->ctx, P->params, line).f; - P->c[i].lam = adjlon(P->c[i].lam - P->lam0); - P->c[i].cosphi = cos(P->c[i].phi); - P->c[i].sinphi = sin(P->c[i].phi); - } - for (i = 0; i < 3; ++i) { /* inter ctl pt. distances and azimuths */ - j = i == 2 ? 0 : i + 1; - P->c[i].v = vect(P->ctx,P->c[j].phi - P->c[i].phi, P->c[i].cosphi, P->c[i].sinphi, - P->c[j].cosphi, P->c[j].sinphi, P->c[j].lam - P->c[i].lam); - if (! P->c[i].v.r) E_ERROR(-25); - /* co-linearity problem ignored for now */ - } - P->beta_0 = lc(P->ctx,P->c[0].v.r, P->c[2].v.r, P->c[1].v.r); - P->beta_1 = lc(P->ctx,P->c[0].v.r, P->c[1].v.r, P->c[2].v.r); - P->beta_2 = PI - P->beta_0; - P->p.y = 2. * (P->c[0].p.y = P->c[1].p.y = P->c[2].v.r * sin(P->beta_0)); - P->c[2].p.y = 0.; - P->c[0].p.x = - (P->c[1].p.x = 0.5 * P->c[0].v.r); - P->p.x = P->c[2].p.x = P->c[0].p.x + P->c[2].v.r * cos(P->beta_0); - P->es = 0.; P->fwd = s_forward; -ENDENTRY(P) + + +#ifdef PJ_OMIT_SELFTEST +int pj_chamb_selftest (void) {return 0;} +#else + +int pj_chamb_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=chamb +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + {-27864.7795868005815, -223364.324593274243}, + {-251312.283053493476, -223402.145526208304}, + {-27864.7856491046077, 223364.327328827145}, + {-251312.289116443484, 223402.142197287147}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, 0, 0, 0); +} + + +#endif diff --git a/src/PJ_collg.c b/src/PJ_collg.c index 871dfc97..80029a3a 100644 --- a/src/PJ_collg.c +++ b/src/PJ_collg.c @@ -1,10 +1,14 @@ #define PJ_LIB__ # include <projects.h> + PROJ_HEAD(collg, "Collignon") "\n\tPCyl, Sph."; #define FXC 1.12837916709551257390 #define FYC 1.77245385090551602729 #define ONEEPS 1.0000001 -FORWARD(s_forward); /* spheroid */ + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; (void) P; if ((xy.y = 1. - sin(lp.phi)) <= 0.) xy.y = 0.; @@ -14,7 +18,10 @@ FORWARD(s_forward); /* spheroid */ xy.y = FYC * (1. - xy.y); return (xy); } -INVERSE(s_inverse); /* spheroid */ + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; lp.phi = xy.y / FYC - 1.; if (fabs(lp.phi = 1. - lp.phi * lp.phi) < 1.) lp.phi = asin(lp.phi); @@ -26,5 +33,66 @@ INVERSE(s_inverse); /* spheroid */ lp.lam = xy.x / (FXC * sqrt(lp.lam)); return (lp); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(collg) P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P) + + +static void *freeup_new (PJ *P) { /* Destructor */ + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(collg) { + P->es = 0.0; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + +#ifdef PJ_OMIT_SELFTEST +int pj_collg_selftest (void) {return 0;} +#else + +int pj_collg_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=collg +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + {249872.921577929839, 99423.1747884602082}, + {254272.532301245432, -98559.3077607425657}, + {-249872.921577929839, 99423.1747884602082}, + {-254272.532301245432, -98559.3077607425657}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + {0.00158679719207879865, 0.00101017310941749921}, + {0.001586769215623956, -0.00101018201458258111}, + {-0.00158679719207879865, 0.00101017310941749921}, + {-0.001586769215623956, -0.00101018201458258111}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_comill.c b/src/PJ_comill.c index ad9914d4..6bccb264 100644 --- a/src/PJ_comill.c +++ b/src/PJ_comill.c @@ -1,13 +1,14 @@ /* -The Compact Miller projection was designed by Tom Patterson, US National -Park Service, in 2014. The polynomial equation was developed by Bojan -Savric and Bernhard Jenny, College of Earth, Ocean, and Atmospheric +The Compact Miller projection was designed by Tom Patterson, US National +Park Service, in 2014. The polynomial equation was developed by Bojan +Savric and Bernhard Jenny, College of Earth, Ocean, and Atmospheric Sciences, Oregon State University. Port to PROJ.4 by Bojan Savric, 4 April 2016 */ #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(comill, "Compact Miller") "\n\tCyl., Sph."; #define K1 0.9902 @@ -19,41 +20,112 @@ PROJ_HEAD(comill, "Compact Miller") "\n\tCyl., Sph."; #define EPS 1e-11 #define MAX_Y (0.6000207669862655 * PI) -FORWARD(s_forward); /* spheroid */ - double lat_sq; - lat_sq = lp.phi * lp.phi; - xy.x = lp.lam; - xy.y = lp.phi * (K1 + lat_sq * (K2 + K3 * lat_sq)); - return (xy); +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double lat_sq; + + (void) P; /* silence unused parameter warnings */ + + lat_sq = lp.phi * lp.phi; + xy.x = lp.lam; + xy.y = lp.phi * (K1 + lat_sq * (K2 + K3 * lat_sq)); + return xy; } -INVERSE(s_inverse); /* spheroid */ - double yc, tol, y2, y4, f, fder; + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double yc, tol, y2, f, fder; + + (void) P; /* silence unused parameter warnings */ /* make sure y is inside valid range */ - if (xy.y > MAX_Y) { - xy.y = MAX_Y; - } else if (xy.y < -MAX_Y) { - xy.y = -MAX_Y; - } + if (xy.y > MAX_Y) { + xy.y = MAX_Y; + } else if (xy.y < -MAX_Y) { + xy.y = -MAX_Y; + } /* latitude */ - yc = xy.y; + yc = xy.y; for (;;) { /* Newton-Raphson */ - y2 = yc * yc; - f = (yc * (K1 + y2 * (K2 + K3 * y2))) - xy.y; - fder = C1 + y2 * (C2 + C3 * y2); - yc -= tol = f / fder; - if (fabs(tol) < EPS) { - break; - } - } - lp.phi = yc; + y2 = yc * yc; + f = (yc * (K1 + y2 * (K2 + K3 * y2))) - xy.y; + fder = C1 + y2 * (C2 + C3 * y2); + yc -= tol = f / fder; + if (fabs(tol) < EPS) { + break; + } + } + lp.phi = yc; /* longitude */ - lp.lam = xy.x; + lp.lam = xy.x; + + return lp; +} + +static void *freeup_new (PJ *P) { /* Destructor */ + return pj_dealloc(P); +} - return (lp); +static void freeup (PJ *P) { + freeup_new (P); + return; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(comill) P->es = 0; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P) + + +PJ *PROJECTION(comill) { + P->es = 0; + + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_comill_selftest (void) {return 0;} +#else + +int pj_comill_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=comill +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + {223402.144255274179, 110611.859089458536}, + {223402.144255274179, -110611.859089458536}, + {-223402.144255274179, 110611.859089458536}, + {-223402.144255274179, -110611.859089458536}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + {0.00179049310978382265, 0.000904106801510605831}, + {0.00179049310978382265, -0.000904106801510605831}, + {-0.00179049310978382265, 0.000904106801510605831}, + {-0.00179049310978382265, -0.000904106801510605831}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_crast.c b/src/PJ_crast.c index 3f251ac6..4773ee7c 100644 --- a/src/PJ_crast.c +++ b/src/PJ_crast.c @@ -1,24 +1,94 @@ #define PJ_LIB__ -# include <projects.h> -PROJ_HEAD(crast, "Craster Parabolic (Putnins P4)") -"\n\tPCyl., Sph."; -#define XM 0.97720502380583984317 -#define RXM 1.02332670794648848847 -#define YM 3.06998012383946546542 -#define RYM 0.32573500793527994772 -#define THIRD 0.333333333333333333 -FORWARD(s_forward); /* spheroid */ - (void) P; - lp.phi *= THIRD; - xy.x = XM * lp.lam * (2. * cos(lp.phi + lp.phi) - 1.); - xy.y = YM * sin(lp.phi); - return (xy); +# include <projects.h> + +PROJ_HEAD(crast, "Craster Parabolic (Putnins P4)") "\n\tPCyl., Sph."; + +#define XM 0.97720502380583984317 +#define RXM 1.02332670794648848847 +#define YM 3.06998012383946546542 +#define RYM 0.32573500793527994772 +#define THIRD 0.333333333333333333 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + (void) P; + lp.phi *= THIRD; + xy.x = XM * lp.lam * (2. * cos(lp.phi + lp.phi) - 1.); + xy.y = YM * sin(lp.phi); + return xy; } -INVERSE(s_inverse); /* spheroid */ - (void) P; - lp.phi = 3. * asin(xy.y * RYM); - lp.lam = xy.x * RXM / (2. * cos((lp.phi + lp.phi) * THIRD) - 1); - return (lp); + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + (void) P; + lp.phi = 3. * asin(xy.y * RYM); + lp.lam = xy.x * RXM / (2. * cos((lp.phi + lp.phi) * THIRD) - 1); + return lp; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(crast) P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P) + + +static void *freeup_new (PJ *P) { /* Destructor */ + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(crast) { + P->es = 0.0; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + +#ifdef PJ_OMIT_SELFTEST +int pj_crast_selftest (void) {return 0;} +#else + +int pj_crast_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=crast +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + + XY s_fwd_expect[] = { + {218280.142056780722, 114306.045604279774}, + {218280.142056780722, -114306.045604279774}, + {-218280.142056780722, 114306.045604279774}, + {-218280.142056780722, -114306.045604279774}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + {0.00183225941982580187, 0.00087483943098902331}, + {0.00183225941982580187, -0.00087483943098902331}, + {-0.00183225941982580187, 0.00087483943098902331}, + {-0.00183225941982580187, -0.00087483943098902331}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_denoy.c b/src/PJ_denoy.c index b1a6fe80..10005a31 100644 --- a/src/PJ_denoy.c +++ b/src/PJ_denoy.c @@ -1,19 +1,69 @@ #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(denoy, "Denoyer Semi-Elliptical") "\n\tPCyl., no inv., Sph."; -#define C0 0.95 -#define C1 -.08333333333333333333 -#define C3 .00166666666666666666 -#define D1 0.9 -#define D5 0.03 -FORWARD(s_forward); /* spheroid */ - (void) P; - xy.y = lp.phi; - xy.x = lp.lam; - lp.lam = fabs(lp.lam); - xy.x *= cos((C0 + lp.lam * (C1 + lp.lam * lp.lam * C3)) * - (lp.phi * (D1 + D5 * lp.phi * lp.phi * lp.phi * lp.phi))); - return (xy); + +#define C0 0.95 +#define C1 -0.08333333333333333333 +#define C3 0.00166666666666666666 +#define D1 0.9 +#define D5 0.03 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0, 0.0}; + (void) P; + xy.y = lp.phi; + xy.x = lp.lam; + lp.lam = fabs(lp.lam); + xy.x *= cos((C0 + lp.lam * (C1 + lp.lam * lp.lam * C3)) * + (lp.phi * (D1 + D5 * lp.phi * lp.phi * lp.phi * lp.phi))); + return xy; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(denoy) P->es = 0.; P->fwd = s_forward; ENDENTRY(P) + + +static void *freeup_new (PJ *P) { /* Destructor */ + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(denoy) { + P->es = 0.0; + P->fwd = s_forward; + + return P; +} + +#ifdef PJ_OMIT_SELFTEST +int pj_denoy_selftest (void) {return 0;} +#else + +int pj_denoy_selftest (void) { + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=denoy +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223377.422876954137, 111701.07212763709}, + { 223377.422876954137, -111701.07212763709}, + {-223377.422876954137, 111701.07212763709}, + {-223377.422876954137, -111701.07212763709}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, 0, 4, 4, fwd_in, 0, s_fwd_expect, 0, 0, 0); +} + + +#endif diff --git a/src/PJ_eck1.c b/src/PJ_eck1.c index b0b43da5..da2c8685 100644 --- a/src/PJ_eck1.c +++ b/src/PJ_eck1.c @@ -1,21 +1,92 @@ -#define PJ_LIB__ -#include <projects.h> -PROJ_HEAD(eck1, "Eckert I") "\n\tPCyl., Sph."; -#define FC .92131773192356127802 -#define RP .31830988618379067154 -FORWARD(s_forward); /* spheroid */ - (void) P; - xy.x = FC * lp.lam * (1. - RP * fabs(lp.phi)); - xy.y = FC * lp.phi; - return (xy); -} -INVERSE(s_inverse); /* spheroid */ - (void) P; - lp.phi = xy.y / FC; - lp.lam = xy.x / (FC * (1. - RP * fabs(lp.phi))); - return (lp); -} -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(eck1) - P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; -ENDENTRY(P) +#define PJ_LIB__
+#include <projects.h>
+
+PROJ_HEAD(eck1, "Eckert I") "\n\tPCyl., Sph.";
+#define FC 0.92131773192356127802
+#define RP 0.31830988618379067154
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ (void) P;
+
+ xy.x = FC * lp.lam * (1. - RP * fabs(lp.phi));
+ xy.y = FC * lp.phi;
+
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ (void) P;
+
+ lp.phi = xy.y / FC;
+ lp.lam = xy.x / (FC * (1. - RP * fabs(lp.phi)));
+
+ return (lp);
+}
+
+
+static void *freeup_new (PJ *P) { /* Destructor */
+ return pj_dealloc(P);
+}
+
+
+static void freeup (PJ *P) {
+ freeup_new (P);
+ return;
+}
+
+
+PJ *PROJECTION(eck1) {
+ P->es = 0.0;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P
;
+}
+
+
+#ifdef PJ_OMIT_SELFTEST
+int pj_eck1_selftest (void) {return 0;}
+#else
+
+int pj_eck1_selftest (void) {
+ double tolerance_lp = 1e-10;
+ double tolerance_xy = 1e-7;
+
+ char s_args[] = {"+proj=eck1 +a=6400000 +lat_1=0.5 +lat_2=2"};
+
+ LP fwd_in[] = {
+ { 2, 1},
+ { 2,-1},
+ {-2, 1},
+ {-2,-1}
+ };
+ XY s_fwd_expect[] = {
+ { 204680.88820295094, 102912.17842606473},
+ { 204680.88820295094, -102912.17842606473},
+ {-204680.88820295094, 102912.17842606473},
+ {-204680.88820295094, -102912.17842606473},
+ };
+
+ XY inv_in[] = {
+ { 200, 100},
+ { 200,-100},
+ {-200, 100},
+ {-200,-100}
+ };
+
+ LP s_inv_expect[] = {
+ { 0.0019434150820034624, 0.00097170229538813102},
+ { 0.0019434150820034624, -0.00097170229538813102},
+ {-0.0019434150820034624, 0.00097170229538813102},
+ {-0.0019434150820034624, -0.00097170229538813102},
+ };
+
+ return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect);
+}
+
+
+#endif
diff --git a/src/PJ_eck2.c b/src/PJ_eck2.c index 08b65595..6d73b88a 100644 --- a/src/PJ_eck2.c +++ b/src/PJ_eck2.c @@ -1,29 +1,104 @@ #define PJ_LIB__ -# include <projects.h> +# include <projects.h> + PROJ_HEAD(eck2, "Eckert II") "\n\tPCyl. Sph."; -#define FXC 0.46065886596178063902 -#define FYC 1.44720250911653531871 -#define C13 0.33333333333333333333 -#define ONEEPS 1.0000001 -FORWARD(s_forward); /* spheroid */ - (void) P; - xy.x = FXC * lp.lam * (xy.y = sqrt(4. - 3. * sin(fabs(lp.phi)))); - xy.y = FYC * (2. - xy.y); - if ( lp.phi < 0.) xy.y = -xy.y; - return (xy); + +#define FXC 0.46065886596178063902 +#define FYC 1.44720250911653531871 +#define C13 0.33333333333333333333 +#define ONEEPS 1.0000001 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + (void) P; + + xy.x = FXC * lp.lam * (xy.y = sqrt(4. - 3. * sin(fabs(lp.phi)))); + xy.y = FYC * (2. - xy.y); + if ( lp.phi < 0.) xy.y = -xy.y; + + return (xy); } -INVERSE(s_inverse); /* spheroid */ - lp.lam = xy.x / (FXC * ( lp.phi = 2. - fabs(xy.y) / FYC) ); - lp.phi = (4. - lp.phi * lp.phi) * C13; - if (fabs(lp.phi) >= 1.) { - if (fabs(lp.phi) > ONEEPS) I_ERROR - else - lp.phi = lp.phi < 0. ? -HALFPI : HALFPI; - } else - lp.phi = asin(lp.phi); - if (xy.y < 0) - lp.phi = -lp.phi; - return (lp); + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + (void) P; + + lp.lam = xy.x / (FXC * ( lp.phi = 2. - fabs(xy.y) / FYC) ); + lp.phi = (4. - lp.phi * lp.phi) * C13; + if (fabs(lp.phi) >= 1.) { + if (fabs(lp.phi) > ONEEPS) I_ERROR + else + lp.phi = lp.phi < 0. ? -HALFPI : HALFPI; + } else + lp.phi = asin(lp.phi); + if (xy.y < 0) + lp.phi = -lp.phi; + return (lp); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(eck2); P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P) + + +static void *freeup_new (PJ *P) { /* Destructor */ + return pj_dealloc (P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(eck2) { + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_eck2_selftest (void) {return 0;} +#else + +int pj_eck2_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=eck2 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 204472.87090796008, 121633.73497524235}, + { 204472.87090796008, -121633.73497524235}, + {-204472.87090796008, 121633.73497524235}, + {-204472.87090796008, -121633.73497524235}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.0019434150820034624, 0.00082480429919795412}, + { 0.0019434150820034624, -0.00082480429919795412}, + {-0.0019434150820034624, 0.00082480429919795412}, + {-0.0019434150820034624, -0.00082480429919795412}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_eck3.c b/src/PJ_eck3.c index d7755f0c..3eb7f8f9 100644 --- a/src/PJ_eck3.c +++ b/src/PJ_eck3.c @@ -1,50 +1,295 @@ -#define PROJ_PARMS__ \ - double C_x, C_y, A, B; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(eck3, "Eckert III") "\n\tPCyl, Sph."; PROJ_HEAD(putp1, "Putnins P1") "\n\tPCyl, Sph."; PROJ_HEAD(wag6, "Wagner VI") "\n\tPCyl, Sph."; PROJ_HEAD(kav7, "Kavraisky VII") "\n\tPCyl, Sph."; -FORWARD(s_forward); /* spheroid */ - xy.y = P->C_y * lp.phi; - xy.x = P->C_x * lp.lam * (P->A + asqrt(1. - P->B * lp.phi * lp.phi)); - return (xy); -} -INVERSE(s_inverse); /* spheroid */ - lp.phi = xy.y / P->C_y; - lp.lam = xy.x / (P->C_x * (P->A + asqrt(1. - P->B * lp.phi * lp.phi))); - return (lp); -} -FREEUP; if (P) pj_dalloc(P); } - static PJ * -setup(PJ *P) { - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - return P; -} -ENTRY0(eck3) - P->C_x = .42223820031577120149; - P->C_y = .84447640063154240298; - P->A = 1.; - P->B = 0.4052847345693510857755; -ENDENTRY(setup(P)) -ENTRY0(kav7) - P->C_x = 0.2632401569273184856851; - P->C_x = 0.8660254037844; - P->C_y = 1.; - P->A = 0.; - P->B = 0.30396355092701331433; -ENDENTRY(setup(P)) -ENTRY0(wag6); - P->C_x = P->C_y = 0.94745; - P->A = 0.; - P->B = 0.30396355092701331433; -ENDENTRY(setup(P)) -ENTRY0(putp1); - P->C_x = 1.89490; - P->C_y = 0.94745; - P->A = -0.5; - P->B = 0.30396355092701331433; -ENDENTRY(setup(P)) + +struct pj_opaque { + double C_x, C_y, A, B; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + + xy.y = Q->C_y * lp.phi; + xy.x = Q->C_x * lp.lam * (Q->A + asqrt(1. - Q->B * lp.phi * lp.phi)); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + + lp.phi = xy.y / Q->C_y; + lp.lam = xy.x / (Q->C_x * (Q->A + asqrt(1. - Q->B * lp.phi * lp.phi))); + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +static PJ *setup(PJ *P) { + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + return P; +} + + +PJ *PROJECTION(eck3) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->C_x = 0.42223820031577120149; + Q->C_y = 0.84447640063154240298; + Q->A = 1.0; + Q->B = 0.4052847345693510857755; + + return setup(P); +} + + +PJ *PROJECTION(kav7) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + /* Defined twice in original code - Using 0.866..., + * but leaving the other one here as a safety measure. + * Q->C_x = 0.2632401569273184856851; */ + Q->C_x = 0.8660254037844; + Q->C_y = 1.; + Q->A = 0.; + Q->B = 0.30396355092701331433; + + return setup(P); +} + + +PJ *PROJECTION(wag6) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->C_x = Q->C_y = 0.94745; + Q->A = 0.0; + Q->B = 0.30396355092701331433; + + return setup(P); +} + + +PJ *PROJECTION(putp1) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->C_x = 1.89490; + Q->C_y = 0.94745; + Q->A = -0.5; + Q->B = 0.30396355092701331433; + + return setup(P); +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_eck3_selftest (void) {return 0;} +#else + +int pj_eck3_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=eck3 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 188652.01572153764, 94328.919337031271}, + { 188652.01572153764, -94328.919337031271}, + {-188652.01572153764, 94328.919337031271}, + {-188652.01572153764, -94328.919337031271}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.0021202405520236059, 0.0010601202759750307}, + { 0.0021202405520236059, -0.0010601202759750307}, + {-0.0021202405520236059, 0.0010601202759750307}, + {-0.0021202405520236059, -0.0010601202759750307}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif + +#ifdef PJ_OMIT_SELFTEST +int pj_kav7_selftest (void) {return 0;} +#else + +int pj_kav7_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=kav7 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 193462.9749437288, 111701.07212763709}, + { 193462.9749437288, -111701.07212763709}, + {-193462.9749437288, 111701.07212763709}, + {-193462.9749437288, -111701.07212763709} + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.0020674833579085268, 0.00089524655489191132}, + { 0.0020674833579085268, -0.00089524655489191132}, + {-0.0020674833579085268, 0.00089524655489191132}, + {-0.0020674833579085268, -0.00089524655489191132} + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif + +#ifdef PJ_OMIT_SELFTEST +int pj_wag6_selftest (void) {return 0;} +#else + +int pj_wag6_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=wag6 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 211652.56216440981, 105831.18078732977}, + { 211652.56216440981, -105831.18078732977}, + {-211652.56216440981, 105831.18078732977}, + {-211652.56216440981, -105831.18078732977} + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.0018898022163257513, 0.000944901108123818}, + { 0.0018898022163257513, -0.000944901108123818}, + {-0.0018898022163257513, 0.000944901108123818}, + {-0.0018898022163257513, -0.000944901108123818} + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif + + +#ifdef PJ_OMIT_SELFTEST +int pj_putp1_selftest (void) {return 0;} +#else + +int pj_putp1_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=putp1 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 211642.76275416015, 105831.18078732977}, + { 211642.76275416015, -105831.18078732977}, + {-211642.76275416015, 105831.18078732977}, + {-211642.76275416015, -105831.18078732977} + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.0018898022164038663, 0.000944901108123818}, + { 0.0018898022164038663, -0.000944901108123818}, + {-0.0018898022164038663, 0.000944901108123818}, + {-0.0018898022164038663, -0.000944901108123818} + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_eck4.c b/src/PJ_eck4.c index 0bedbbc9..8a56f019 100644 --- a/src/PJ_eck4.c +++ b/src/PJ_eck4.c @@ -1,45 +1,117 @@ -#define PJ_LIB__ -#include <projects.h> -PROJ_HEAD(eck4, "Eckert IV") "\n\tPCyl, Sph."; -#define C_x .42223820031577120149 -#define C_y 1.32650042817700232218 -#define RC_y .75386330736002178205 -#define C_p 3.57079632679489661922 -#define RC_p .28004957675577868795 -#define EPS 1e-7 -#define NITER 6 -FORWARD(s_forward); /* spheroid */ - double p, V, s, c; - int i; - (void) P; - - p = C_p * sin(lp.phi); - V = lp.phi * lp.phi; - lp.phi *= 0.895168 + V * ( 0.0218849 + V * 0.00826809 ); - for (i = NITER; i ; --i) { - c = cos(lp.phi); - s = sin(lp.phi); - lp.phi -= V = (lp.phi + s * (c + 2.) - p) / - (1. + c * (c + 2.) - s * s); - if (fabs(V) < EPS) - break; - } - if (!i) { - xy.x = C_x * lp.lam; - xy.y = lp.phi < 0. ? -C_y : C_y; - } else { - xy.x = C_x * lp.lam * (1. + cos(lp.phi)); - xy.y = C_y * sin(lp.phi); - } - return (xy); -} -INVERSE(s_inverse); /* spheroid */ - double c; - - lp.phi = aasin(P->ctx,xy.y / C_y); - lp.lam = xy.x / (C_x * (1. + (c = cos(lp.phi)))); - lp.phi = aasin(P->ctx,(lp.phi + sin(lp.phi) * (c + 2.)) / C_p); - return (lp); -} -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(eck4); P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P) +#define PJ_LIB__
+#include <projects.h>
+
+PROJ_HEAD(eck4, "Eckert IV") "\n\tPCyl, Sph.";
+
+#define C_x .42223820031577120149
+#define C_y 1.32650042817700232218
+#define RC_y .75386330736002178205
+#define C_p 3.57079632679489661922
+#define RC_p .28004957675577868795
+#define EPS 1e-7
+#define NITER 6
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ double p, V, s, c;
+ int i;
+ (void) P;
+
+ p = C_p * sin(lp.phi);
+ V = lp.phi * lp.phi;
+ lp.phi *= 0.895168 + V * ( 0.0218849 + V * 0.00826809 );
+ for (i = NITER; i ; --i) {
+ c = cos(lp.phi);
+ s = sin(lp.phi);
+ lp.phi -= V = (lp.phi + s * (c + 2.) - p) /
+ (1. + c * (c + 2.) - s * s);
+ if (fabs(V) < EPS)
+ break;
+ }
+ if (!i) {
+ xy.x = C_x * lp.lam;
+ xy.y = lp.phi < 0. ? -C_y : C_y;
+ } else {
+ xy.x = C_x * lp.lam * (1. + cos(lp.phi));
+ xy.y = C_y * sin(lp.phi);
+ }
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ double c;
+
+ lp.phi = aasin(P->ctx,xy.y / C_y);
+ lp.lam = xy.x / (C_x * (1. + (c = cos(lp.phi))));
+ lp.phi = aasin(P->ctx,(lp.phi + sin(lp.phi) * (c + 2.)) / C_p);
+ return lp;
+}
+
+
+static void *freeup_new (PJ *P) { /* Destructor */
+ if (0==P)
+ return 0;
+ return pj_dealloc(P);
+}
+
+static void freeup (PJ *P) {
+ freeup_new (P);
+ return;
+}
+
+
+PJ *PROJECTION(eck4) {
+ P->es = 0.0;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
+
+
+#ifdef PJ_OMIT_SELFTEST
+int pj_eck4_selftest (void) {return 0;}
+#else
+
+int pj_eck4_selftest (void) {
+ double tolerance_lp = 1e-10;
+ double tolerance_xy = 1e-7;
+
+ char s_args[] = {"+proj=eck4 +a=6400000 +lat_1=0.5 +lat_2=2"};
+
+ LP fwd_in[] = {
+ { 2, 1},
+ { 2,-1},
+ {-2, 1},
+ {-2,-1}
+ };
+
+ XY s_fwd_expect[] = {
+ { 188646.38935641639, 132268.54017406539},
+ { 188646.38935641639, -132268.54017406539},
+ {-188646.38935641639, 132268.54017406539},
+ {-188646.38935641639, -132268.54017406539},
+ };
+
+ XY inv_in[] = {
+ { 200, 100},
+ { 200,-100},
+ {-200, 100},
+ {-200,-100}
+ };
+
+ LP s_inv_expect[] = {
+ { 0.0021202405520236059, 0.00075601458836610643},
+ { 0.0021202405520236059, -0.00075601458836610643},
+ {-0.0021202405520236059, 0.00075601458836610643},
+ {-0.0021202405520236059, -0.00075601458836610643},
+ };
+
+ return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect);
+}
+
+
+#endif
diff --git a/src/PJ_eck5.c b/src/PJ_eck5.c index 029c18e0..d7626939 100644 --- a/src/PJ_eck5.c +++ b/src/PJ_eck5.c @@ -1,20 +1,93 @@ -#define PJ_LIB__ -# include <projects.h> -PROJ_HEAD(eck5, "Eckert V") "\n\tPCyl, Sph."; -#define XF 0.44101277172455148219 -#define RXF 2.26750802723822639137 -#define YF 0.88202554344910296438 -#define RYF 1.13375401361911319568 -FORWARD(s_forward); /* spheroid */ - (void) P; - xy.x = XF * (1. + cos(lp.phi)) * lp.lam; - xy.y = YF * lp.phi; - return (xy); -} -INVERSE(s_inverse); /* spheroid */ - (void) P; - lp.lam = RXF * xy.x / (1. + cos( lp.phi = RYF * xy.y)); - return (lp); -} -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(eck5); P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P) +#define PJ_LIB__
+#include <projects.h>
+
+PROJ_HEAD(eck5, "Eckert V") "\n\tPCyl, Sph.";
+
+#define XF 0.44101277172455148219
+#define RXF 2.26750802723822639137
+#define YF 0.88202554344910296438
+#define RYF 1.13375401361911319568
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ (void) P;
+ xy.x = XF * (1. + cos(lp.phi)) * lp.lam;
+ xy.y = YF * lp.phi;
+
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ (void) P;
+ lp.lam = RXF * xy.x / (1. + cos( lp.phi = RYF * xy.y));
+
+ return lp;
+}
+
+
+static void *freeup_new (PJ *P) { /* Destructor */
+ if (0==P)
+ return 0;
+ return pj_dealloc(P);
+}
+
+static void freeup (PJ *P) {
+ freeup_new (P);
+ return;
+}
+
+
+PJ *PROJECTION(eck5) {
+ P->es = 0.0;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
+
+#ifdef PJ_OMIT_SELFTEST
+int pj_eck5_selftest (void) {return 0;}
+#else
+
+int pj_eck5_selftest (void) {
+ double tolerance_lp = 1e-10;
+ double tolerance_xy = 1e-7;
+
+ char s_args[] = {"+proj=eck5 +a=6400000 +lat_1=0.5 +lat_2=2"};
+
+ LP fwd_in[] = {
+ { 2, 1},
+ { 2,-1},
+ {-2, 1},
+ {-2,-1}
+ };
+
+ XY s_fwd_expect[] = {
+ { 197031.39213406085, 98523.198847226551},
+ { 197031.39213406085, -98523.198847226551},
+ {-197031.39213406085, 98523.198847226551},
+ {-197031.39213406085, -98523.198847226551},
+ };
+
+ XY inv_in[] = {
+ { 200, 100},
+ { 200,-100},
+ {-200, 100},
+ {-200,-100}
+ };
+
+ LP s_inv_expect[] = {
+ {0.002029978749734037, 0.001014989374787388},
+ {0.002029978749734037, -0.001014989374787388},
+ {-0.002029978749734037, 0.001014989374787388},
+ {-0.002029978749734037, -0.001014989374787388},
+ };
+
+ return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect);
+}
+
+
+#endif
diff --git a/src/PJ_eqc.c b/src/PJ_eqc.c index c69a6baa..3fcb1007 100644 --- a/src/PJ_eqc.c +++ b/src/PJ_eqc.c @@ -1,23 +1,108 @@ -#define PROJ_PARMS__ \ - double rc; #define PJ_LIB__ -# include <projects.h> +#include <projects.h> + +struct pj_opaque { + double rc; +}; + PROJ_HEAD(eqc, "Equidistant Cylindrical (Plate Caree)") - "\n\tCyl, Sph\n\tlat_ts=[, lat_0=0]"; -FORWARD(s_forward); /* spheroid */ - xy.x = P->rc * lp.lam; - xy.y = lp.phi - P->phi0; - return (xy); + "\n\tCyl, Sph\n\tlat_ts=[, lat_0=0]"; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + + xy.x = Q->rc * lp.lam; + xy.y = lp.phi - P->phi0; + + return xy; } -INVERSE(s_inverse); /* spheroid */ - lp.lam = xy.x / P->rc; - lp.phi = xy.y + P->phi0; - return (lp); + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + + lp.lam = xy.x / Q->rc; + lp.phi = xy.y + P->phi0; + + return lp; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(eqc) - if ((P->rc = cos(pj_param(P->ctx, P->params, "rlat_ts").f)) <= 0.) E_ERROR(-24); - P->inv = s_inverse; - P->fwd = s_forward; - P->es = 0.; -ENDENTRY(P) + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(eqc) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + if ((Q->rc = cos(pj_param(P->ctx, P->params, "rlat_ts").f)) <= 0.) E_ERROR(-24); + P->inv = s_inverse; + P->fwd = s_forward; + P->es = 0.; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_eqc_selftest (void) {return 0;} +#else + +int pj_eqc_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=eqc +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223402.144255274179, 111701.07212763709}, + { 223402.144255274179, -111701.07212763709}, + {-223402.144255274179, 111701.07212763709}, + {-223402.144255274179, -111701.07212763709}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.00179049310978382265, 0.000895246554891911323}, + { 0.00179049310978382265, -0.000895246554891911323}, + {-0.00179049310978382265, 0.000895246554891911323}, + {-0.00179049310978382265, -0.000895246554891911323}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_eqdc.c b/src/PJ_eqdc.c index 01a0f03f..917131e1 100644 --- a/src/PJ_eqdc.c +++ b/src/PJ_eqdc.c @@ -1,85 +1,190 @@ -#define PROJ_PARMS__ \ - double phi1; \ - double phi2; \ - double n; \ - double rho; \ - double rho0; \ - double c; \ - double *en; \ - int ellips; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + +struct pj_opaque { + double phi1; + double phi2; + double n; + double rho; + double rho0; + double c; + double *en; + int ellips; +}; + PROJ_HEAD(eqdc, "Equidistant Conic") - "\n\tConic, Sph&Ell\n\tlat_1= lat_2="; -# define EPS10 1.e-10 -FORWARD(e_forward); /* sphere & ellipsoid */ - P->rho = P->c - (P->ellips ? pj_mlfn(lp.phi, sin(lp.phi), - cos(lp.phi), P->en) : lp.phi); - xy.x = P->rho * sin( lp.lam *= P->n ); - xy.y = P->rho0 - P->rho * cos(lp.lam); - return (xy); + "\n\tConic, Sph&Ell\n\tlat_1= lat_2="; +# define EPS10 1.e-10 + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + + Q->rho = Q->c - (Q->ellips ? pj_mlfn(lp.phi, sin(lp.phi), + cos(lp.phi), Q->en) : lp.phi); + xy.x = Q->rho * sin( lp.lam *= Q->n ); + xy.y = Q->rho0 - Q->rho * cos(lp.lam); + + return xy; } -INVERSE(e_inverse); /* sphere & ellipsoid */ - if ((P->rho = hypot(xy.x, xy.y = P->rho0 - xy.y)) != 0.0 ) { - if (P->n < 0.) { - P->rho = -P->rho; - xy.x = -xy.x; - xy.y = -xy.y; - } - lp.phi = P->c - P->rho; - if (P->ellips) - lp.phi = pj_inv_mlfn(P->ctx, lp.phi, P->es, P->en); - lp.lam = atan2(xy.x, xy.y) / P->n; - } else { - lp.lam = 0.; - lp.phi = P->n > 0. ? HALFPI : - HALFPI; - } - return (lp); + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + + if ((Q->rho = hypot(xy.x, xy.y = Q->rho0 - xy.y)) != 0.0 ) { + if (Q->n < 0.) { + Q->rho = -Q->rho; + xy.x = -xy.x; + xy.y = -xy.y; + } + lp.phi = Q->c - Q->rho; + if (Q->ellips) + lp.phi = pj_inv_mlfn(P->ctx, lp.phi, P->es, Q->en); + lp.lam = atan2(xy.x, xy.y) / Q->n; + } else { + lp.lam = 0.; + lp.phi = Q->n > 0. ? HALFPI : - HALFPI; + } + return lp; } -SPECIAL(fac) { - double sinphi, cosphi; - - sinphi = sin(lp.phi); - cosphi = cos(lp.phi); - fac->code |= IS_ANAL_HK; - fac->h = 1.; - fac->k = P->n * (P->c - (P->ellips ? pj_mlfn(lp.phi, sinphi, - cosphi, P->en) : lp.phi)) / pj_msfn(sinphi, cosphi, P->es); + + +static void special(LP lp, PJ *P, struct FACTORS *fac) { + struct pj_opaque *Q = P->opaque; + double sinphi, cosphi; + + sinphi = sin(lp.phi); + cosphi = cos(lp.phi); + fac->code |= IS_ANAL_HK; + fac->h = 1.; + fac->k = Q->n * (Q->c - (Q->ellips ? pj_mlfn(lp.phi, sinphi, + cosphi, Q->en) : lp.phi)) / pj_msfn(sinphi, cosphi, P->es); +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + if (P->opaque->en) + pj_dealloc (P->opaque->en); + + pj_dealloc (P->opaque); + return pj_dealloc(P); } -FREEUP; if (P) { if (P->en) pj_dalloc(P->en); pj_dalloc(P); } } -ENTRY1(eqdc, en) - double cosphi, sinphi; - int secant; - - P->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; - P->phi2 = pj_param(P->ctx, P->params, "rlat_2").f; - if (fabs(P->phi1 + P->phi2) < EPS10) E_ERROR(-21); - if (!(P->en = pj_enfn(P->es))) - E_ERROR_0; - P->n = sinphi = sin(P->phi1); - cosphi = cos(P->phi1); - secant = fabs(P->phi1 - P->phi2) >= EPS10; - if( (P->ellips = (P->es > 0.)) ) { - double ml1, m1; - - m1 = pj_msfn(sinphi, cosphi, P->es); - ml1 = pj_mlfn(P->phi1, sinphi, cosphi, P->en); - if (secant) { /* secant cone */ - sinphi = sin(P->phi2); - cosphi = cos(P->phi2); - P->n = (m1 - pj_msfn(sinphi, cosphi, P->es)) / - (pj_mlfn(P->phi2, sinphi, cosphi, P->en) - ml1); - } - P->c = ml1 + m1 / P->n; - P->rho0 = P->c - pj_mlfn(P->phi0, sin(P->phi0), - cos(P->phi0), P->en); - } else { - if (secant) - P->n = (cosphi - cos(P->phi2)) / (P->phi2 - P->phi1); - P->c = P->phi1 + cos(P->phi1) / P->n; - P->rho0 = P->c - P->phi0; - } - P->inv = e_inverse; - P->fwd = e_forward; - P->spc = fac; -ENDENTRY(P) + + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(eqdc) { + double cosphi, sinphi; + int secant; + + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; + Q->phi2 = pj_param(P->ctx, P->params, "rlat_2").f; + if (fabs(Q->phi1 + Q->phi2) < EPS10) E_ERROR(-21); + if (!(Q->en = pj_enfn(P->es))) + E_ERROR_0; + Q->n = sinphi = sin(Q->phi1); + cosphi = cos(Q->phi1); + secant = fabs(Q->phi1 - Q->phi2) >= EPS10; + if( (Q->ellips = (P->es > 0.)) ) { + double ml1, m1; + + m1 = pj_msfn(sinphi, cosphi, P->es); + ml1 = pj_mlfn(Q->phi1, sinphi, cosphi, Q->en); + if (secant) { /* secant cone */ + sinphi = sin(Q->phi2); + cosphi = cos(Q->phi2); + Q->n = (m1 - pj_msfn(sinphi, cosphi, P->es)) / + (pj_mlfn(Q->phi2, sinphi, cosphi, Q->en) - ml1); + } + Q->c = ml1 + m1 / Q->n; + Q->rho0 = Q->c - pj_mlfn(P->phi0, sin(P->phi0), + cos(P->phi0), Q->en); + } else { + if (secant) + Q->n = (cosphi - cos(Q->phi2)) / (Q->phi2 - Q->phi1); + Q->c = Q->phi1 + cos(Q->phi1) / Q->n; + Q->rho0 = Q->c - P->phi0; + } + P->inv = e_inverse; + P->fwd = e_forward; + P->spc = special; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_eqdc_selftest (void) {return 0;} +#else + +int pj_eqdc_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=eqdc +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + char s_args[] = {"+proj=eqdc +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 222588.440269285755, 110659.134907347048}, + { 222756.836702042434, -110489.578087220681}, + {-222588.440269285755, 110659.134907347048}, + {-222756.836702042434, -110489.578087220681}, + }; + + XY s_fwd_expect[] = { + { 223351.088175113517, 111786.108747173785}, + { 223521.200266735133, -111615.970741240744}, + {-223351.088175113517, 111786.108747173785}, + {-223521.200266735133, -111615.970741240744}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + { 0.00179635944879094839, 0.000904368858588402644}, + { 0.00179635822020772734, -0.000904370095529954975}, + {-0.00179635944879094839, 0.000904368858588402644}, + {-0.00179635822020772734, -0.000904370095529954975}, + }; + + LP s_inv_expect[] = { + { 0.0017902210900486641, 0.000895245944814909169}, + { 0.00179021986984890255, -0.000895247165333684842}, + {-0.0017902210900486641, 0.000895245944814909169}, + {-0.00179021986984890255, -0.000895247165333684842}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif diff --git a/src/PJ_fahey.c b/src/PJ_fahey.c index 007fc906..b8841c96 100644 --- a/src/PJ_fahey.c +++ b/src/PJ_fahey.c @@ -1,19 +1,95 @@ -#define PJ_LIB__ -# include <projects.h> -PROJ_HEAD(fahey, "Fahey") "\n\tPcyl, Sph."; -#define TOL 1e-6 -FORWARD(s_forward); /* spheroid */ - (void) P; - xy.y = 1.819152 * ( xy.x = tan(0.5 * lp.phi) ); - xy.x = 0.819152 * lp.lam * asqrt(1 - xy.x * xy.x); - return (xy); -} -INVERSE(s_inverse); /* spheroid */ - (void) P; - lp.phi = 2. * atan(xy.y /= 1.819152); - lp.lam = fabs(xy.y = 1. - xy.y * xy.y) < TOL ? 0. : - xy.x / (0.819152 * sqrt(xy.y)); - return (lp); -} -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(fahey) P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P) +#define PJ_LIB__
+#include <projects.h>
+
+PROJ_HEAD(fahey, "Fahey") "\n\tPcyl, Sph.";
+
+#define TOL 1e-6
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ (void) P;
+
+ xy.x = tan(0.5 * lp.phi);
+ xy.y = 1.819152 * xy.x;
+ xy.x = 0.819152 * lp.lam * asqrt(1 - xy.x * xy.x);
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ (void) P;
+
+ xy.y /= 1.819152;
+ lp.phi = 2. * atan(xy.y);
+ xy.y = 1. - xy.y * xy.y;
+ lp.lam = fabs(xy.y) < TOL ? 0. : xy.x / (0.819152 * sqrt(xy.y));
+ return lp;
+}
+
+
+static void *freeup_new (PJ *P) { /* Destructor */
+ if (0==P)
+ return 0;
+ return pj_dealloc(P);
+}
+
+
+static void freeup (PJ *P) {
+ freeup_new (P);
+ return;
+}
+
+
+PJ *PROJECTION(fahey) {
+ P->es = 0.;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
+
+#ifdef PJ_OMIT_SELFTEST
+int pj_fahey_selftest (void) {return 0;}
+#else
+
+int pj_fahey_selftest (void) {
+ double tolerance_lp = 1e-10;
+ double tolerance_xy = 1e-7;
+
+ char s_args[] = {"+proj=fahey +a=6400000 +lat_1=0.5 +lat_2=2"};
+
+ LP fwd_in[] = {
+ { 2, 1},
+ { 2,-1},
+ {-2, 1},
+ {-2,-1}
+ };
+
+ XY s_fwd_expect[] = {
+ { 182993.34464912376, 101603.19356988439},
+ { 182993.34464912376, -101603.19356988439},
+ {-182993.34464912376, 101603.19356988439},
+ {-182993.34464912376, -101603.19356988439},
+ };
+
+ XY inv_in[] = {
+ { 200, 100},
+ { 200,-100},
+ {-200, 100},
+ {-200,-100}
+ };
+
+ LP s_inv_expect[] = {
+ {0.0021857886080359551, 0.00098424601668238403},
+ {0.0021857886080359551, -0.00098424601668238403},
+ {-0.0021857886080359551, 0.00098424601668238403},
+ {-0.0021857886080359551, -0.00098424601668238403},
+ };
+
+ return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect);
+}
+
+
+#endif
diff --git a/src/PJ_fouc_s.c b/src/PJ_fouc_s.c index b84b3f82..4123497c 100644 --- a/src/PJ_fouc_s.c +++ b/src/PJ_fouc_s.c @@ -1,45 +1,126 @@ -#define PROJ_PARMS__ \ - double n, n1; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(fouc_s, "Foucaut Sinusoidal") "\n\tPCyl., Sph."; + #define MAX_ITER 10 #define LOOP_TOL 1e-7 -FORWARD(s_forward); /* spheroid */ - double t; - t = cos(lp.phi); - xy.x = lp.lam * t / (P->n + P->n1 * t); - xy.y = P->n * lp.phi + P->n1 * sin(lp.phi); - return (xy); +struct pj_opaque { + double n, n1; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double t; + + t = cos(lp.phi); + xy.x = lp.lam * t / (Q->n + Q->n1 * t); + xy.y = Q->n * lp.phi + Q->n1 * sin(lp.phi); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double V; + int i; + + if (Q->n) { + lp.phi = xy.y; + for (i = MAX_ITER; i ; --i) { + lp.phi -= V = (Q->n * lp.phi + Q->n1 * sin(lp.phi) - xy.y ) / + (Q->n + Q->n1 * cos(lp.phi)); + if (fabs(V) < LOOP_TOL) + break; + } + if (!i) + lp.phi = xy.y < 0. ? -HALFPI : HALFPI; + } else + lp.phi = aasin(P->ctx,xy.y); + V = cos(lp.phi); + lp.lam = xy.x * (Q->n + Q->n1 * V) / V; + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); } -INVERSE(s_inverse); /* spheroid */ - double V; - int i; - - if (P->n) { - lp.phi = xy.y; - for (i = MAX_ITER; i ; --i) { - lp.phi -= V = (P->n * lp.phi + P->n1 * sin(lp.phi) - xy.y ) / - (P->n + P->n1 * cos(lp.phi)); - if (fabs(V) < LOOP_TOL) - break; - } - if (!i) - lp.phi = xy.y < 0. ? -HALFPI : HALFPI; - } else - lp.phi = aasin(P->ctx,xy.y); - V = cos(lp.phi); - lp.lam = xy.x * (P->n + P->n1 * V) / V; - return (lp); + + +static void freeup (PJ *P) { + freeup_new (P); + return; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(fouc_s) - P->n = pj_param(P->ctx, P->params, "dn").f; - if (P->n < 0. || P->n > 1.) - E_ERROR(-99) - P->n1 = 1. - P->n; - P->es = 0; - P->inv = s_inverse; - P->fwd = s_forward; -ENDENTRY(P) + + +PJ *PROJECTION(fouc_s) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->n = pj_param(P->ctx, P->params, "dn").f; + if (Q->n < 0. || Q->n > 1.) + E_ERROR(-99) + Q->n1 = 1. - Q->n; + P->es = 0; + P->inv = s_inverse; + P->fwd = s_forward; + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_fouc_s_selftest (void) {return 0;} +#else + +int pj_fouc_s_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=fouc_s +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223402.14425527424, 111695.40119861449}, + { 223402.14425527424, -111695.40119861449}, + {-223402.14425527424, 111695.40119861449}, + {-223402.14425527424, -111695.40119861449}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.0017904931097838226, 0.000895246554928339}, + { 0.0017904931097838226, -0.000895246554928339}, + {-0.0017904931097838226, 0.000895246554928339}, + {-0.0017904931097838226, -0.000895246554928339}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_gall.c b/src/PJ_gall.c index 1acd7d09..b3d7cc6c 100644 --- a/src/PJ_gall.c +++ b/src/PJ_gall.c @@ -1,21 +1,100 @@ #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(gall, "Gall (Gall Stereographic)") "\n\tCyl, Sph"; -#define YF 1.70710678118654752440 -#define XF 0.70710678118654752440 -#define RYF 0.58578643762690495119 -#define RXF 1.41421356237309504880 -FORWARD(s_forward); /* spheroid */ - (void) P; - xy.x = XF * lp.lam; - xy.y = YF * tan(.5 * lp.phi); - return (xy); + +#define YF 1.70710678118654752440 +#define XF 0.70710678118654752440 +#define RYF 0.58578643762690495119 +#define RXF 1.41421356237309504880 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + (void) P; + + xy.x = XF * lp.lam; + xy.y = YF * tan(.5 * lp.phi); + + return xy; } -INVERSE(s_inverse); /* spheroid */ - (void) P; - lp.lam = RXF * xy.x; - lp.phi = 2. * atan(xy.y * RYF); - return (lp); + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + (void) P; + + lp.lam = RXF * xy.x; + lp.phi = 2. * atan(xy.y * RYF); + + return lp; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(gall) P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P) + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(gall) { + P->es = 0.0; + + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_gall_selftest (void) {return 0;} +#else + +int pj_gall_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=gall +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 157969.17113451968, 95345.249178385886}, + { 157969.17113451968, -95345.249178385886}, + {-157969.17113451968, 95345.249178385886}, + {-157969.17113451968, -95345.249178385886}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.0025321396391918614, 0.001048846580346495}, + { 0.0025321396391918614, -0.001048846580346495}, + {-0.0025321396391918614, 0.001048846580346495}, + {-0.0025321396391918614, -0.001048846580346495}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_geos.c b/src/PJ_geos.c index 09393adf..578608c9 100644 --- a/src/PJ_geos.c +++ b/src/PJ_geos.c @@ -3,8 +3,7 @@ ** ** Copyright (c) 2004 Gerald I. Evenden ** Copyright (c) 2012 Martin Raspaud -*/ -/* +** ** See also (section 4.4.3.2): ** http://www.eumetsat.int/en/area4/msg/news/us_doc/cgms_03_26.pdf ** @@ -27,164 +26,274 @@ ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#define PROJ_PARMS__ \ - double h; \ - double radius_p; \ - double radius_p2; \ - double radius_p_inv2; \ - double radius_g; \ - double radius_g_1; \ - double C; \ - char * sweep_axis; \ - int flip_axis; + #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + +struct pj_opaque { + double h; + double radius_p; + double radius_p2; + double radius_p_inv2; + double radius_g; + double radius_g_1; + double C; + char *sweep_axis; + int flip_axis; +}; PROJ_HEAD(geos, "Geostationary Satellite View") "\n\tAzi, Sph&Ell\n\th="; -FORWARD(s_forward); /* spheroid */ - double Vx, Vy, Vz, tmp; - -/* Calculation of the three components of the vector from satellite to -** position on earth surface (lon,lat).*/ - tmp = cos(lp.phi); - Vx = cos (lp.lam) * tmp; - Vy = sin (lp.lam) * tmp; - Vz = sin (lp.phi); -/* Check visibility.*/ - if (((P->radius_g - Vx) * Vx - Vy * Vy - Vz * Vz) < 0.) F_ERROR; -/* Calculation based on view angles from satellite.*/ - tmp = P->radius_g - Vx; - if(P->flip_axis) - { - xy.x = P->radius_g_1 * atan(Vy / hypot(Vz, tmp)); - xy.y = P->radius_g_1 * atan(Vz / tmp); - } - else - { - xy.x = P->radius_g_1 * atan(Vy / tmp); - xy.y = P->radius_g_1 * atan(Vz / hypot(Vy, tmp)); - } - return (xy); + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double Vx, Vy, Vz, tmp; + + /* Calculation of the three components of the vector from satellite to + ** position on earth surface (lon,lat).*/ + tmp = cos(lp.phi); + Vx = cos (lp.lam) * tmp; + Vy = sin (lp.lam) * tmp; + Vz = sin (lp.phi); + + /* Check visibility*/ + + + /* Calculation based on view angles from satellite.*/ + tmp = Q->radius_g - Vx; + + if(Q->flip_axis) { + xy.x = Q->radius_g_1 * atan(Vy / hypot(Vz, tmp)); + xy.y = Q->radius_g_1 * atan(Vz / tmp); + } else { + xy.x = Q->radius_g_1 * atan(Vy / tmp); + xy.y = Q->radius_g_1 * atan(Vz / hypot(Vy, tmp)); + } + + return xy; } -FORWARD(e_forward); /* ellipsoid */ - double r, Vx, Vy, Vz, tmp; - -/* Calculation of geocentric latitude. */ - lp.phi = atan (P->radius_p2 * tan (lp.phi)); -/* Calculation of the three components of the vector from satellite to -** position on earth surface (lon,lat).*/ - r = (P->radius_p) / hypot(P->radius_p * cos (lp.phi), sin (lp.phi)); - Vx = r * cos (lp.lam) * cos (lp.phi); - Vy = r * sin (lp.lam) * cos (lp.phi); - Vz = r * sin (lp.phi); -/* Check visibility. */ - if (((P->radius_g - Vx) * Vx - Vy * Vy - Vz * Vz * P->radius_p_inv2) < 0.) - F_ERROR; -/* Calculation based on view angles from satellite. */ - tmp = P->radius_g - Vx; - if(P->flip_axis) - { - xy.x = P->radius_g_1 * atan (Vy / hypot (Vz, tmp)); - xy.y = P->radius_g_1 * atan (Vz / tmp); - } - else - { - xy.x = P->radius_g_1 * atan (Vy / tmp); - xy.y = P->radius_g_1 * atan (Vz / hypot (Vy, tmp)); - } - return (xy); + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double r, Vx, Vy, Vz, tmp; + + /* Calculation of geocentric latitude. */ + lp.phi = atan (Q->radius_p2 * tan (lp.phi)); + + /* Calculation of the three components of the vector from satellite to + ** position on earth surface (lon,lat).*/ + r = (Q->radius_p) / hypot(Q->radius_p * cos (lp.phi), sin (lp.phi)); + Vx = r * cos (lp.lam) * cos (lp.phi); + Vy = r * sin (lp.lam) * cos (lp.phi); + Vz = r * sin (lp.phi); + + /* Check visibility. */ + if (((Q->radius_g - Vx) * Vx - Vy * Vy - Vz * Vz * Q->radius_p_inv2) < 0.) + F_ERROR; + + /* Calculation based on view angles from satellite. */ + tmp = Q->radius_g - Vx; + + if(Q->flip_axis) { + xy.x = Q->radius_g_1 * atan (Vy / hypot (Vz, tmp)); + xy.y = Q->radius_g_1 * atan (Vz / tmp); + } else { + xy.x = Q->radius_g_1 * atan (Vy / tmp); + xy.y = Q->radius_g_1 * atan (Vz / hypot (Vy, tmp)); + } + + return xy; } -INVERSE(s_inverse); /* spheroid */ - double Vx, Vy, Vz, a, b, det, k; - -/* Setting three components of vector from satellite to position.*/ - Vx = -1.0; - if(P->flip_axis) - { - Vz = tan (xy.y / (P->radius_g - 1.0)); - Vy = tan (xy.x / (P->radius_g - 1.0)) * sqrt (1.0 + Vz * Vz); - } - else - { - Vy = tan (xy.x / (P->radius_g - 1.0)); - Vz = tan (xy.y / (P->radius_g - 1.0)) * sqrt (1.0 + Vy * Vy); - } -/* Calculation of terms in cubic equation and determinant.*/ - a = Vy * Vy + Vz * Vz + Vx * Vx; - b = 2 * P->radius_g * Vx; - if ((det = (b * b) - 4 * a * P->C) < 0.) I_ERROR; -/* Calculation of three components of vector from satellite to position.*/ - k = (-b - sqrt(det)) / (2 * a); - Vx = P->radius_g + k * Vx; - Vy *= k; - Vz *= k; -/* Calculation of longitude and latitude.*/ - lp.lam = atan2 (Vy, Vx); - lp.phi = atan (Vz * cos (lp.lam) / Vx); - return (lp); + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double Vx, Vy, Vz, a, b, det, k; + + /* Setting three components of vector from satellite to position.*/ + Vx = -1.0; + if(Q->flip_axis) { + Vz = tan (xy.y / (Q->radius_g - 1.0)); + Vy = tan (xy.x / (Q->radius_g - 1.0)) * sqrt (1.0 + Vz * Vz); + } else { + Vy = tan (xy.x / (Q->radius_g - 1.0)); + Vz = tan (xy.y / (Q->radius_g - 1.0)) * sqrt (1.0 + Vy * Vy); + } + + /* Calculation of terms in cubic equation and determinant.*/ + a = Vy * Vy + Vz * Vz + Vx * Vx; + b = 2 * Q->radius_g * Vx; + if ((det = (b * b) - 4 * a * Q->C) < 0.) I_ERROR; + + /* Calculation of three components of vector from satellite to position.*/ + k = (-b - sqrt(det)) / (2 * a); + Vx = Q->radius_g + k * Vx; + Vy *= k; + Vz *= k; + + /* Calculation of longitude and latitude.*/ + lp.lam = atan2 (Vy, Vx); + lp.phi = atan (Vz * cos (lp.lam) / Vx); + + return lp; } -INVERSE(e_inverse); /* ellipsoid */ - double Vx, Vy, Vz, a, b, det, k; - -/* Setting three components of vector from satellite to position.*/ - Vx = -1.0; - if(P->flip_axis) - { - Vz = tan (xy.y / P->radius_g_1); - Vy = tan (xy.x / P->radius_g_1) * hypot(1.0, Vz); - } - else - { - Vy = tan (xy.x / P->radius_g_1); - Vz = tan (xy.y / P->radius_g_1) * hypot(1.0, Vy); - } -/* Calculation of terms in cubic equation and determinant.*/ - a = Vz / P->radius_p; - a = Vy * Vy + a * a + Vx * Vx; - b = 2 * P->radius_g * Vx; - if ((det = (b * b) - 4 * a * P->C) < 0.) I_ERROR; -/* Calculation of three components of vector from satellite to position.*/ - k = (-b - sqrt(det)) / (2. * a); - Vx = P->radius_g + k * Vx; - Vy *= k; - Vz *= k; -/* Calculation of longitude and latitude.*/ - lp.lam = atan2 (Vy, Vx); - lp.phi = atan (Vz * cos (lp.lam) / Vx); - lp.phi = atan (P->radius_p_inv2 * tan (lp.phi)); - return (lp); + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double Vx, Vy, Vz, a, b, det, k; + + /* Setting three components of vector from satellite to position.*/ + Vx = -1.0; + + if(Q->flip_axis) { + Vz = tan (xy.y / Q->radius_g_1); + Vy = tan (xy.x / Q->radius_g_1) * hypot(1.0, Vz); + } else { + Vy = tan (xy.x / Q->radius_g_1); + Vz = tan (xy.y / Q->radius_g_1) * hypot(1.0, Vy); + } + + /* Calculation of terms in cubic equation and determinant.*/ + a = Vz / Q->radius_p; + a = Vy * Vy + a * a + Vx * Vx; + b = 2 * Q->radius_g * Vx; + if ((det = (b * b) - 4 * a * Q->C) < 0.) I_ERROR; + + /* Calculation of three components of vector from satellite to position.*/ + k = (-b - sqrt(det)) / (2. * a); + Vx = Q->radius_g + k * Vx; + Vy *= k; + Vz *= k; + + /* Calculation of longitude and latitude.*/ + lp.lam = atan2 (Vy, Vx); + lp.phi = atan (Vz * cos (lp.lam) / Vx); + lp.phi = atan (Q->radius_p_inv2 * tan (lp.phi)); + + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); } -FREEUP; if (P) free(P); } -ENTRY0(geos) - if ((P->h = pj_param(P->ctx, P->params, "dh").f) <= 0.) E_ERROR(-30); - if (P->phi0) E_ERROR(-46); - P->sweep_axis = pj_param(P->ctx, P->params, "ssweep").s; - if (P->sweep_axis == NULL) - P->flip_axis = 0; + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(geos) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + if ((Q->h = pj_param(P->ctx, P->params, "dh").f) <= 0.) E_ERROR(-30); + + if (P->phi0) E_ERROR(-46); + + Q->sweep_axis = pj_param(P->ctx, P->params, "ssweep").s; + if (Q->sweep_axis == NULL) + Q->flip_axis = 0; + else { + if (Q->sweep_axis[1] != '\0' || + (Q->sweep_axis[0] != 'x' && + Q->sweep_axis[0] != 'y')) + E_ERROR(-49); + if (Q->sweep_axis[0] == 'x') + Q->flip_axis = 1; else - { - if (P->sweep_axis[1] != '\0' || - (P->sweep_axis[0] != 'x' && - P->sweep_axis[0] != 'y')) - E_ERROR(-49); - if (P->sweep_axis[0] == 'x') - P->flip_axis = 1; - else - P->flip_axis = 0; - } - P->radius_g_1 = P->h / P->a; - P->radius_g = 1. + P->radius_g_1; - P->C = P->radius_g * P->radius_g - 1.0; - if (P->es) { - P->radius_p = sqrt (P->one_es); - P->radius_p2 = P->one_es; - P->radius_p_inv2 = P->rone_es; - P->inv = e_inverse; - P->fwd = e_forward; - } else { - P->radius_p = P->radius_p2 = P->radius_p_inv2 = 1.0; - P->inv = s_inverse; - P->fwd = s_forward; - } -ENDENTRY(P) + Q->flip_axis = 0; + } + + Q->radius_g_1 = Q->h / P->a; + Q->radius_g = 1. + Q->radius_g_1; + Q->C = Q->radius_g * Q->radius_g - 1.0; + if (P->es) { + Q->radius_p = sqrt (P->one_es); + Q->radius_p2 = P->one_es; + Q->radius_p_inv2 = P->rone_es; + P->inv = e_inverse; + P->fwd = e_forward; + } else { + Q->radius_p = Q->radius_p2 = Q->radius_p_inv2 = 1.0; + P->inv = s_inverse; + P->fwd = s_forward; + } + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_geos_selftest (void) {return 0;} +#else + +int pj_geos_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=geos +ellps=GRS80 +lat_1=0.5 +lat_2=2 +h=35785831"}; + char s_args[] = {"+proj=geos +a=6400000 +lat_1=0.5 +lat_2=2 +h=35785831"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 222527.07036580026, 110551.30341332949}, + { 222527.07036580026, -110551.30341332949}, + {-222527.07036580026, 110551.30341332949}, + {-222527.07036580026, -110551.30341332949}, + }; + + XY s_fwd_expect[] = { + { 223289.45763579503, 111677.65745653701}, + { 223289.45763579503, -111677.65745653701}, + {-223289.45763579503, 111677.65745653701}, + {-223289.45763579503, -111677.65745653701}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + { 0.0017966305689715385, 0.00090436947723267452}, + { 0.0017966305689715385, -0.00090436947723267452}, + {-0.0017966305689715385, 0.00090436947723267452}, + {-0.0017966305689715385, -0.00090436947723267452}, + }; + + LP s_inv_expect[] = { + { 0.0017904931105078943, 0.00089524655504237148}, + { 0.0017904931105078943, -0.00089524655504237148}, + {-0.0017904931105078943, 0.00089524655504237148}, + {-0.0017904931105078943, -0.00089524655504237148}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif diff --git a/src/PJ_gins8.c b/src/PJ_gins8.c index b7f38ad9..3eae7efa 100644 --- a/src/PJ_gins8.c +++ b/src/PJ_gins8.c @@ -1,18 +1,75 @@ #define PJ_LIB__ -# include <projects.h> +#include <projects.h> + PROJ_HEAD(gins8, "Ginsburg VIII (TsNIIGAiK)") "\n\tPCyl, Sph., no inv."; -#define Cl 0.000952426 -#define Cp 0.162388 -#define C12 0.08333333333333333 -FORWARD(s_forward); /* spheroid */ - double t = lp.phi * lp.phi; - (void) P; - - xy.y = lp.phi * (1. + t * C12); - xy.x = lp.lam * (1. - Cp * t); - t = lp.lam * lp.lam; - xy.x *= (0.87 - Cl * t * t); - return (xy); + +#define Cl 0.000952426 +#define Cp 0.162388 +#define C12 0.08333333333333333 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double t = lp.phi * lp.phi; + (void) P; + + xy.y = lp.phi * (1. + t * C12); + xy.x = lp.lam * (1. - Cp * t); + t = lp.lam * lp.lam; + xy.x *= (0.87 - Cl * t * t); + + return xy; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(gins8) { + P->es = 0.0; + P->inv = 0; + P->fwd = s_forward; + + return P; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(gins8) P->es = 0.; P->inv = 0; P->fwd = s_forward; ENDENTRY(P) + + +#ifdef PJ_OMIT_SELFTEST +int pj_gins8_selftest (void) {return 0;} +#else + +int pj_gins8_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=gins8 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 194350.25093959007, 111703.90763533533}, + { 194350.25093959007, -111703.90763533533}, + {-194350.25093959007, 111703.90763533533}, + {-194350.25093959007, -111703.90763533533}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, 0, 0, 0); +} + + +#endif diff --git a/src/PJ_gn_sinu.c b/src/PJ_gn_sinu.c index bfd8bc2d..4b33b185 100644 --- a/src/PJ_gn_sinu.c +++ b/src/PJ_gn_sinu.c @@ -1,98 +1,373 @@ -#define PROJ_PARMS__ \ - double *en; \ - double m, n, C_x, C_y; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(gn_sinu, "General Sinusoidal Series") "\n\tPCyl, Sph.\n\tm= n="; PROJ_HEAD(sinu, "Sinusoidal (Sanson-Flamsteed)") "\n\tPCyl, Sph&Ell"; PROJ_HEAD(eck6, "Eckert VI") "\n\tPCyl, Sph."; PROJ_HEAD(mbtfps, "McBryde-Thomas Flat-Polar Sinusoidal") "\n\tPCyl, Sph."; -#define EPS10 1e-10 + +#define EPS10 1e-10 #define MAX_ITER 8 #define LOOP_TOL 1e-7 -/* Ellipsoidal Sinusoidal only */ -FORWARD(e_forward); /* ellipsoid */ - double s, c; - xy.y = pj_mlfn(lp.phi, s = sin(lp.phi), c = cos(lp.phi), P->en); - xy.x = lp.lam * c / sqrt(1. - P->es * s * s); - return (xy); +struct pj_opaque { + double *en; + double m, n, C_x, C_y; +}; + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + double s, c; + + xy.y = pj_mlfn(lp.phi, s = sin(lp.phi), c = cos(lp.phi), P->opaque->en); + xy.x = lp.lam * c / sqrt(1. - P->es * s * s); + return xy; } -INVERSE(e_inverse); /* ellipsoid */ - double s; - - if ((s = fabs(lp.phi = pj_inv_mlfn(P->ctx, xy.y, P->es, P->en))) < HALFPI) { - s = sin(lp.phi); - lp.lam = xy.x * sqrt(1. - P->es * s * s) / cos(lp.phi); - } else if ((s - EPS10) < HALFPI) - lp.lam = 0.; - else I_ERROR; - return (lp); + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + double s; + + if ((s = fabs(lp.phi = pj_inv_mlfn(P->ctx, xy.y, P->es, P->opaque->en))) < HALFPI) { + s = sin(lp.phi); + lp.lam = xy.x * sqrt(1. - P->es * s * s) / cos(lp.phi); + } else if ((s - EPS10) < HALFPI) { + lp.lam = 0.; + } else { + I_ERROR; + } + + return lp; } -/* General spherical sinusoidals */ -FORWARD(s_forward); /* sphere */ - if (!P->m) - lp.phi = P->n != 1. ? aasin(P->ctx,P->n * sin(lp.phi)): lp.phi; - else { - double k, V; - int i; - - k = P->n * sin(lp.phi); - for (i = MAX_ITER; i ; --i) { - lp.phi -= V = (P->m * lp.phi + sin(lp.phi) - k) / - (P->m + cos(lp.phi)); - if (fabs(V) < LOOP_TOL) - break; - } - if (!i) - F_ERROR - } - xy.x = P->C_x * lp.lam * (P->m + cos(lp.phi)); - xy.y = P->C_y * lp.phi; - return (xy); + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + + if (!Q->m) + lp.phi = Q->n != 1. ? aasin(P->ctx,Q->n * sin(lp.phi)): lp.phi; + else { + double k, V; + int i; + + k = Q->n * sin(lp.phi); + for (i = MAX_ITER; i ; --i) { + lp.phi -= V = (Q->m * lp.phi + sin(lp.phi) - k) / + (Q->m + cos(lp.phi)); + if (fabs(V) < LOOP_TOL) + break; + } + if (!i) + F_ERROR + } + xy.x = Q->C_x * lp.lam * (Q->m + cos(lp.phi)); + xy.y = Q->C_y * lp.phi; + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + + xy.y /= Q->C_y; + lp.phi = Q->m ? aasin(P->ctx,(Q->m * xy.y + sin(xy.y)) / Q->n) : + ( Q->n != 1. ? aasin(P->ctx,sin(xy.y) / Q->n) : xy.y ); + lp.lam = xy.x / (Q->C_x * (Q->m + cos(xy.y))); + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + if (P->opaque->en) + pj_dalloc(P->opaque->en); + + pj_dealloc (P->opaque); + return pj_dealloc(P); } -INVERSE(s_inverse); /* sphere */ - xy.y /= P->C_y; - lp.phi = P->m ? aasin(P->ctx,(P->m * xy.y + sin(xy.y)) / P->n) : - ( P->n != 1. ? aasin(P->ctx,sin(xy.y) / P->n) : xy.y ); - lp.lam = xy.x / (P->C_x * (P->m + cos(xy.y))); - return (lp); + + +static void freeup (PJ *P) { + freeup_new (P); + return; } -FREEUP; if (P) { if (P->en) pj_dalloc(P->en); pj_dalloc(P); } } - static void /* for spheres, only */ -setup(PJ *P) { - P->es = 0; - P->C_x = (P->C_y = sqrt((P->m + 1.) / P->n))/(P->m + 1.); - P->inv = s_inverse; - P->fwd = s_forward; + + +/* for spheres, only */ +static void setup(PJ *P) { + struct pj_opaque *Q = P->opaque; + P->es = 0; + P->inv = s_inverse; + P->fwd = s_forward; + + Q->C_x = (Q->C_y = sqrt((Q->m + 1.) / Q->n))/(Q->m + 1.); +} + + +PJ *PROJECTION(sinu) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + if (!(Q->en = pj_enfn(P->es))) + E_ERROR_0; + + if (P->es) { + P->inv = e_inverse; + P->fwd = e_forward; + } else { + Q->n = 1.; + Q->m = 0.; + setup(P); + } + return P; +} + + +PJ *PROJECTION(eck6) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->m = 1.; + Q->n = 2.570796326794896619231321691; + setup(P); + + return P; +} + + +PJ *PROJECTION(mbtfps) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->m = 0.5; + Q->n = 1.785398163397448309615660845; + setup(P); + + return P; +} + + +PJ *PROJECTION(gn_sinu) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + if (pj_param(P->ctx, P->params, "tn").i && pj_param(P->ctx, P->params, "tm").i) { + Q->n = pj_param(P->ctx, P->params, "dn").f; + Q->m = pj_param(P->ctx, P->params, "dm").f; + } else + E_ERROR(-99) + + setup(P); + + return P; } -ENTRY1(sinu, en) - if (!(P->en = pj_enfn(P->es))) - E_ERROR_0; - if (P->es) { - P->inv = e_inverse; - P->fwd = e_forward; - } else { - P->n = 1.; - P->m = 0.; - setup(P); - } -ENDENTRY(P) -ENTRY1(eck6, en) - P->m = 1.; - P->n = 2.570796326794896619231321691; - setup(P); -ENDENTRY(P) -ENTRY1(mbtfps, en) - P->m = 0.5; - P->n = 1.785398163397448309615660845; - setup(P); -ENDENTRY(P) -ENTRY1(gn_sinu, en) - if (pj_param(P->ctx, P->params, "tn").i && pj_param(P->ctx, P->params, "tm").i) { - P->n = pj_param(P->ctx, P->params, "dn").f; - P->m = pj_param(P->ctx, P->params, "dm").f; - } else - E_ERROR(-99) - setup(P); -ENDENTRY(P) + + +#ifdef PJ_OMIT_SELFTEST +int pj_sinu_selftest (void) {return 0;} +#else + +int pj_sinu_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=sinu +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + char s_args[] = {"+proj=sinu +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 222605.29953946592, 110574.38855415257}, + { 222605.29953946592, -110574.38855415257}, + {-222605.29953946592, 110574.38855415257}, + {-222605.29953946592, -110574.38855415257}, + }; + + XY s_fwd_expect[] = { + { 223368.11902663155, 111701.07212763709}, + { 223368.11902663155, -111701.07212763709}, + {-223368.11902663155, 111701.07212763709}, + {-223368.11902663155, -111701.07212763709}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + { 0.0017966305684613522, 0.00090436947707945409}, + { 0.0017966305684613522, -0.00090436947707945409}, + {-0.0017966305684613522, 0.00090436947707945409}, + {-0.0017966305684613522, -0.00090436947707945409}, + }; + + LP s_inv_expect[] = { + { 0.0017904931100023887, 0.00089524655489191132}, + { 0.0017904931100023887, -0.00089524655489191132}, + {-0.0017904931100023887, 0.00089524655489191132}, + {-0.0017904931100023887, -0.00089524655489191132}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif + +#ifdef PJ_OMIT_SELFTEST +int pj_eck6_selftest (void) {return 0;} +#else + +int pj_eck6_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=eck6 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 197021.60562899226, 126640.42073317352}, + { 197021.60562899226, -126640.42073317352}, + {-197021.60562899226, 126640.42073317352}, + {-197021.60562899226, -126640.42073317352}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.002029978749734037, 0.00078963032910382171}, + { 0.002029978749734037, -0.00078963032910382171}, + {-0.002029978749734037, 0.00078963032910382171}, + {-0.002029978749734037, -0.00078963032910382171}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif + +#ifdef PJ_OMIT_SELFTEST +int pj_mbtfps_selftest (void) {return 0;} +#else + +int pj_mbtfps_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=mbtfps +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 204740.11747857218, 121864.72971934026}, + { 204740.11747857218, -121864.72971934026}, + {-204740.11747857218, 121864.72971934026}, + {-204740.11747857218, -121864.72971934026}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.0019534152166442065, 0.00082057965689633387}, + { 0.0019534152166442065, -0.00082057965689633387}, + {-0.0019534152166442065, 0.00082057965689633387}, + {-0.0019534152166442065, -0.00082057965689633387}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif + + +#ifdef PJ_OMIT_SELFTEST +int pj_gn_sinu_selftest (void) {return 0;} +#else + +int pj_gn_sinu_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=gn_sinu +a=6400000 +lat_1=0.5 +lat_2=2 +m=1 +n=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223385.13250469571, 111698.23644718733}, + { 223385.13250469571, -111698.23644718733}, + {-223385.13250469571, 111698.23644718733}, + {-223385.13250469571, -111698.23644718733}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.0017904931098931057, 0.00089524655491012516}, + { 0.0017904931098931057, -0.00089524655491012516}, + {-0.0017904931098931057, 0.00089524655491012516}, + {-0.0017904931098931057, -0.00089524655491012516}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_gnom.c b/src/PJ_gnom.c index 11deb86c..151f718e 100644 --- a/src/PJ_gnom.c +++ b/src/PJ_gnom.c @@ -1,105 +1,192 @@ -#define PROJ_PARMS__ \ - double sinph0; \ - double cosph0; \ - int mode; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(gnom, "Gnomonic") "\n\tAzi, Sph."; -#define EPS10 1.e-10 -#define N_POLE 0 + +#define EPS10 1.e-10 +#define N_POLE 0 #define S_POLE 1 -#define EQUIT 2 -#define OBLIQ 3 -FORWARD(s_forward); /* spheroid */ - double coslam, cosphi, sinphi; - - sinphi = sin(lp.phi); - cosphi = cos(lp.phi); - coslam = cos(lp.lam); - switch (P->mode) { - case EQUIT: - xy.y = cosphi * coslam; - break; - case OBLIQ: - xy.y = P->sinph0 * sinphi + P->cosph0 * cosphi * coslam; - break; - case S_POLE: - xy.y = - sinphi; - break; - case N_POLE: - xy.y = sinphi; - break; - } - if (xy.y <= EPS10) F_ERROR; - xy.x = (xy.y = 1. / xy.y) * cosphi * sin(lp.lam); - switch (P->mode) { - case EQUIT: - xy.y *= sinphi; - break; - case OBLIQ: - xy.y *= P->cosph0 * sinphi - P->sinph0 * cosphi * coslam; - break; - case N_POLE: - coslam = - coslam; - case S_POLE: - xy.y *= cosphi * coslam; - break; - } - return (xy); +#define EQUIT 2 +#define OBLIQ 3 + +struct pj_opaque { + double sinph0; + double cosph0; + int mode; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double coslam, cosphi, sinphi; + + sinphi = sin(lp.phi); + cosphi = cos(lp.phi); + coslam = cos(lp.lam); + + switch (Q->mode) { + case EQUIT: + xy.y = cosphi * coslam; + break; + case OBLIQ: + xy.y = Q->sinph0 * sinphi + Q->cosph0 * cosphi * coslam; + break; + case S_POLE: + xy.y = - sinphi; + break; + case N_POLE: + xy.y = sinphi; + break; + } + + if (xy.y <= EPS10) F_ERROR; + + xy.x = (xy.y = 1. / xy.y) * cosphi * sin(lp.lam); + switch (Q->mode) { + case EQUIT: + xy.y *= sinphi; + break; + case OBLIQ: + xy.y *= Q->cosph0 * sinphi - Q->sinph0 * cosphi * coslam; + break; + case N_POLE: + coslam = - coslam; + case S_POLE: + xy.y *= cosphi * coslam; + break; + } + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double rh, cosz, sinz; + + rh = hypot(xy.x, xy.y); + sinz = sin(lp.phi = atan(rh)); + cosz = sqrt(1. - sinz * sinz); + + if (fabs(rh) <= EPS10) { + lp.phi = P->phi0; + lp.lam = 0.; + } else { + switch (Q->mode) { + case OBLIQ: + lp.phi = cosz * Q->sinph0 + xy.y * sinz * Q->cosph0 / rh; + if (fabs(lp.phi) >= 1.) + lp.phi = lp.phi > 0. ? HALFPI : - HALFPI; + else + lp.phi = asin(lp.phi); + xy.y = (cosz - Q->sinph0 * sin(lp.phi)) * rh; + xy.x *= sinz * Q->cosph0; + break; + case EQUIT: + lp.phi = xy.y * sinz / rh; + if (fabs(lp.phi) >= 1.) + lp.phi = lp.phi > 0. ? HALFPI : - HALFPI; + else + lp.phi = asin(lp.phi); + xy.y = cosz * rh; + xy.x *= sinz; + break; + case S_POLE: + lp.phi -= HALFPI; + break; + case N_POLE: + lp.phi = HALFPI - lp.phi; + xy.y = -xy.y; + break; + } + lp.lam = atan2(xy.x, xy.y); + } + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; } -INVERSE(s_inverse); /* spheroid */ - double rh, cosz, sinz; - - rh = hypot(xy.x, xy.y); - sinz = sin(lp.phi = atan(rh)); - cosz = sqrt(1. - sinz * sinz); - if (fabs(rh) <= EPS10) { - lp.phi = P->phi0; - lp.lam = 0.; - } else { - switch (P->mode) { - case OBLIQ: - lp.phi = cosz * P->sinph0 + xy.y * sinz * P->cosph0 / rh; - if (fabs(lp.phi) >= 1.) - lp.phi = lp.phi > 0. ? HALFPI : - HALFPI; - else - lp.phi = asin(lp.phi); - xy.y = (cosz - P->sinph0 * sin(lp.phi)) * rh; - xy.x *= sinz * P->cosph0; - break; - case EQUIT: - lp.phi = xy.y * sinz / rh; - if (fabs(lp.phi) >= 1.) - lp.phi = lp.phi > 0. ? HALFPI : - HALFPI; - else - lp.phi = asin(lp.phi); - xy.y = cosz * rh; - xy.x *= sinz; - break; - case S_POLE: - lp.phi -= HALFPI; - break; - case N_POLE: - lp.phi = HALFPI - lp.phi; - xy.y = -xy.y; - break; - } - lp.lam = atan2(xy.x, xy.y); - } - return (lp); + + +PJ *PROJECTION(gnom) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + if (fabs(fabs(P->phi0) - HALFPI) < EPS10) { + Q->mode = P->phi0 < 0. ? S_POLE : N_POLE; + } else if (fabs(P->phi0) < EPS10) { + Q->mode = EQUIT; + } else { + Q->mode = OBLIQ; + Q->sinph0 = sin(P->phi0); + Q->cosph0 = cos(P->phi0); + } + + P->inv = s_inverse; + P->fwd = s_forward; + P->es = 0.; + + return P; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(gnom) - if (fabs(fabs(P->phi0) - HALFPI) < EPS10) - P->mode = P->phi0 < 0. ? S_POLE : N_POLE; - else if (fabs(P->phi0) < EPS10) - P->mode = EQUIT; - else { - P->mode = OBLIQ; - P->sinph0 = sin(P->phi0); - P->cosph0 = cos(P->phi0); - } - P->inv = s_inverse; - P->fwd = s_forward; - P->es = 0.; -ENDENTRY(P) + + +#ifdef PJ_OMIT_SELFTEST +int pj_gnom_selftest (void) {return 0;} +#else + +int pj_gnom_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=gnom +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223492.92474718543, 111780.50920659291}, + { 223492.92474718543, -111780.50920659291}, + {-223492.92474718543, 111780.50920659291}, + {-223492.92474718543, -111780.50920659291}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.0017904931092009798, 0.00089524655438192376}, + { 0.0017904931092009798, -0.00089524655438192376}, + {-0.0017904931092009798, 0.00089524655438192376}, + {-0.0017904931092009798, -0.00089524655438192376}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_goode.c b/src/PJ_goode.c index 387557e6..48fc9ad5 100644 --- a/src/PJ_goode.c +++ b/src/PJ_goode.c @@ -1,49 +1,122 @@ -#define PROJ_PARMS__ \ - struct PJconsts *sinu; \ - struct PJconsts *moll; -#define PJ_LIB__ -#include <projects.h> -PROJ_HEAD(goode, "Goode Homolosine") "\n\tPCyl, Sph."; - C_NAMESPACE PJ -*pj_sinu(PJ *), *pj_moll(PJ *); -#define Y_COR 0.05280 -#define PHI_LIM .71093078197902358062 -FORWARD(s_forward); /* spheroid */ - if (fabs(lp.phi) <= PHI_LIM) - xy = P->sinu->fwd(lp, P->sinu); - else { - xy = P->moll->fwd(lp, P->moll); - xy.y -= lp.phi >= 0.0 ? Y_COR : -Y_COR; - } - return (xy); -} -INVERSE(s_inverse); /* spheroid */ - if (fabs(xy.y) <= PHI_LIM) - lp = P->sinu->inv(xy, P->sinu); - else { - xy.y += xy.y >= 0.0 ? Y_COR : -Y_COR; - lp = P->moll->inv(xy, P->moll); - } - return (lp); -} -FREEUP; - if (P) { - if (P->sinu) - (*(P->sinu->pfree))(P->sinu); - if (P->moll) - (*(P->moll->pfree))(P->moll); - pj_dalloc(P); - } -} -ENTRY2(goode, sinu, moll) - P->es = 0.; - if (!(P->sinu = pj_sinu(0)) || !(P->moll = pj_moll(0))) - E_ERROR_0; - P->sinu->es = 0.; - P->sinu->ctx = P->ctx; - P->moll->ctx = P->ctx; - if (!(P->sinu = pj_sinu(P->sinu)) || !(P->moll = pj_moll(P->moll))) - E_ERROR_0; - P->fwd = s_forward; - P->inv = s_inverse; -ENDENTRY(P) +#define PJ_LIB__
+#include <projects.h>
+
+PROJ_HEAD(goode, "Goode Homolosine") "\n\tPCyl, Sph.";
+
+#define Y_COR 0.05280
+#define PHI_LIM 0.71093078197902358062
+
+C_NAMESPACE PJ *pj_sinu(PJ *), *pj_moll(PJ *);
+
+struct pj_opaque {
+ PJ *sinu;
+ PJ *moll;
+};
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = P->opaque;
+
+ if (fabs(lp.phi) <= PHI_LIM)
+ xy = Q->sinu->fwd(lp, Q->sinu);
+ else {
+ xy = Q->moll->fwd(lp, Q->moll);
+ xy.y -= lp.phi >= 0.0 ? Y_COR : -Y_COR;
+ }
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = P->opaque;
+
+ if (fabs(xy.y) <= PHI_LIM)
+ lp = Q->sinu->inv(xy, Q->sinu);
+ else {
+ xy.y += xy.y >= 0.0 ? Y_COR : -Y_COR;
+ lp = Q->moll->inv(xy, Q->moll);
+ }
+ return lp;
+}
+
+
+static void *freeup_new (PJ *P) { /* Destructor */
+ if (0==P)
+ return 0;
+ if (0==P->opaque)
+ return pj_dealloc(P);
+ if (P->opaque->sinu)
+ pj_dealloc(P->opaque->sinu);
+ if (P->opaque->moll)
+ pj_dealloc(P->opaque->moll);
+ pj_dealloc (P->opaque);
+ return pj_dealloc(P);
+
+}
+
+
+static void freeup (PJ *P) {
+ freeup_new (P);
+ return;
+}
+
+
+PJ *PROJECTION(goode) {
+ struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque));
+ if (0==Q)
+ return freeup_new (P);
+ P->opaque = Q;
+
+ P->es = 0.;
+ if (!(Q->sinu = pj_sinu(0)) || !(Q->moll = pj_moll(0)))
+ E_ERROR_0;
+ Q->sinu->es = 0.;
+ Q->sinu->ctx = P->ctx;
+ Q->moll->ctx = P->ctx;
+ if (!(Q->sinu = pj_sinu(Q->sinu)) || !(Q->moll = pj_moll(Q->moll)))
+ E_ERROR_0;
+
+ P->fwd = s_forward;
+ P->inv = s_inverse;
+
+ return P;
+}
+
+
+#ifdef PJ_OMIT_SELFTEST
+int pj_goode_selftest (void) {return 0;}
+#else
+
+int pj_goode_selftest (void) {
+ double tolerance_lp = 1e-10;
+ double tolerance_xy = 1e-7;
+
+ char s_args[] = {"+proj=goode +a=6400000 +lat_1=0.5 +lat_2=2"};
+
+ LP fwd_in[] = {
+ { 2, 1},
+ { 2,-1},
+ {-2, 1},
+ {-2,-1}
+ };
+
+ XY s_fwd_expect[] = {
+ { 223368.11902663155, 111701.07212763709},
{ 223368.11902663155, -111701.07212763709},
{-223368.11902663155, 111701.07212763709},
{-223368.11902663155, -111701.07212763709},
};
+
+ XY inv_in[] = {
+ { 200, 100},
+ { 200,-100},
+ {-200, 100},
+ {-200,-100}
+ };
+
+ LP s_inv_expect[] = {
+ { 0.0017904931100023887, 0.00089524655489191132},
{ 0.0017904931100023887, -0.00089524655489191132},
{-0.0017904931100023887, 0.00089524655489191132},
{-0.0017904931100023887, -0.00089524655489191132},
};
+
+ return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect);
+}
+
+
+#endif
diff --git a/src/PJ_gstmerc.c b/src/PJ_gstmerc.c index bffe0b26..2720c945 100644 --- a/src/PJ_gstmerc.c +++ b/src/PJ_gstmerc.c @@ -1,48 +1,131 @@ -#define PROJ_PARMS__ \ - double lamc;\ - double phic;\ - double c;\ - double n1;\ - double n2;\ - double XS;\ - double YS; - #define PJ_LIB__ -# include <projects.h> +#include <projects.h> + PROJ_HEAD(gstmerc, "Gauss-Schreiber Transverse Mercator (aka Gauss-Laborde Reunion)") - "\n\tCyl, Sph&Ell\n\tlat_0= lon_0= k_0="; -FORWARD(s_forward); /* spheroid */ - double L, Ls, sinLs1, Ls1; - L= P->n1*lp.lam; - Ls= P->c+P->n1*log(pj_tsfn(-1.0*lp.phi,-1.0*sin(lp.phi),P->e)); - sinLs1= sin(L)/cosh(Ls); - Ls1= log(pj_tsfn(-1.0*asin(sinLs1),0.0,0.0)); - xy.x= (P->XS + P->n2*Ls1)*P->ra; - xy.y= (P->YS + P->n2*atan(sinh(Ls)/cos(L)))*P->ra; - /*fprintf(stderr,"fwd:\nL =%16.13f\nLs =%16.13f\nLs1 =%16.13f\nLP(%16.13f,%16.13f)=XY(%16.4f,%16.4f)\n",L,Ls,Ls1,lp.lam+P->lam0,lp.phi,(xy.x*P->a + P->x0)*P->to_meter,(xy.y*P->a + P->y0)*P->to_meter);*/ - return (xy); + "\n\tCyl, Sph&Ell\n\tlat_0= lon_0= k_0="; + +struct pj_opaque { + double lamc; + double phic; + double c; + double n1; + double n2; + double XS; + double YS; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double L, Ls, sinLs1, Ls1; + + L = Q->n1*lp.lam; + Ls = Q->c + Q->n1 * log(pj_tsfn(-1.0 * lp.phi, -1.0 * sin(lp.phi), P->e)); + sinLs1 = sin(L) / cosh(Ls); + Ls1 = log(pj_tsfn(-1.0 * asin(sinLs1), 0.0, 0.0)); + xy.x = (Q->XS + Q->n2*Ls1) * P->ra; + xy.y = (Q->YS + Q->n2*atan(sinh(Ls) / cos(L))) * P->ra; + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double L, LC, sinC; + + L = atan(sinh((xy.x * P->a - Q->XS) / Q->n2) / cos((xy.y * P->a - Q->YS) / Q->n2)); + sinC = sin((xy.y * P->a - Q->YS) / Q->n2) / cosh((xy.x * P->a - Q->XS) / Q->n2); + LC = log(pj_tsfn(-1.0 * asin(sinC), 0.0, 0.0)); + lp.lam = L / Q->n1; + lp.phi = -1.0 * pj_phi2(P->ctx, exp((LC - Q->c) / Q->n1), P->e); + + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); } -INVERSE(s_inverse); /* spheroid */ - double L, LC, sinC; - L= atan(sinh((xy.x*P->a - P->XS)/P->n2)/cos((xy.y*P->a - P->YS)/P->n2)); - sinC= sin((xy.y*P->a - P->YS)/P->n2)/cosh((xy.x*P->a - P->XS)/P->n2); - LC= log(pj_tsfn(-1.0*asin(sinC),0.0,0.0)); - lp.lam= L/P->n1; - lp.phi= -1.0*pj_phi2(P->ctx, exp((LC-P->c)/P->n1),P->e); - /*fprintf(stderr,"inv:\nL =%16.13f\nsinC =%16.13f\nLC =%16.13f\nXY(%16.4f,%16.4f)=LP(%16.13f,%16.13f)\n",L,sinC,LC,((xy.x/P->ra)+P->x0)/P->to_meter,((xy.y/P->ra)+P->y0)/P->to_meter,lp.lam+P->lam0,lp.phi);*/ - return (lp); + + +static void freeup (PJ *P) { + freeup_new (P); + return; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(gstmerc) - P->lamc= P->lam0; - P->n1= sqrt(1.0+P->es*pow(cos(P->phi0),4.0)/(1.0-P->es)); - P->phic= asin(sin(P->phi0)/P->n1); - P->c= log(pj_tsfn(-1.0*P->phic,0.0,0.0)) - -P->n1*log(pj_tsfn(-1.0*P->phi0,-1.0*sin(P->phi0),P->e)); - P->n2= P->k0*P->a*sqrt(1.0-P->es)/(1.0-P->es*sin(P->phi0)*sin(P->phi0)); - P->XS= 0;/* -P->x0 */ - P->YS= -1.0*P->n2*P->phic;/* -P->y0 */ - P->inv= s_inverse; - P->fwd= s_forward; - /*fprintf(stderr,"a (m) =%16.4f\ne =%16.13f\nl0(rad)=%16.13f\np0(rad)=%16.13f\nk0 =%16.4f\nX0 (m)=%16.4f\nY0 (m)=%16.4f\n\nlC(rad)=%16.13f\npC(rad)=%16.13f\nc =%16.13f\nn1 =%16.13f\nn2 (m) =%16.4f\nXS (m) =%16.4f\nYS (m) =%16.4f\n", P->a, P->e, P->lam0, P->phi0, P->k0, P->x0, P->y0, P->lamc, P->phic, P->c, P->n1, P->n2, P->XS +P->x0, P->YS + P->y0);*/ -ENDENTRY(P) + + +PJ *PROJECTION(gstmerc) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->lamc = P->lam0; + Q->n1 = sqrt(1.0 + P->es * pow(cos(P->phi0), 4.0) / (1.0 - P->es)); + Q->phic = asin(sin(P->phi0) / Q->n1); + Q->c = log(pj_tsfn(-1.0 * Q->phic, 0.0, 0.0)) + - Q->n1 * log(pj_tsfn(-1.0 * P->phi0, -1.0 * sin(P->phi0), P->e)); + Q->n2 = P->k0 * P->a * sqrt(1.0 - P->es) / (1.0 - P->es * sin(P->phi0) * sin(P->phi0)); + Q->XS = 0; + Q->YS = -1.0 * Q->n2 * Q->phic; + + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_gstmerc_selftest (void) {return 0;} +#else + +int pj_gstmerc_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=gstmerc +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + + XY s_fwd_expect[] = { + { 223413.46640632182, 111769.14504058557}, + { 223413.46640632182, -111769.14504058668}, + {-223413.46640632302, 111769.14504058557}, + {-223413.46640632302, -111769.14504058668}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.0017904931097109673, 0.0008952465544509083}, + { 0.0017904931097109673, -0.0008952465544509083}, + {-0.0017904931097109673, 0.0008952465544509083}, + {-0.0017904931097109673, -0.0008952465544509083}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_hammer.c b/src/PJ_hammer.c index 31e7a127..3118640b 100644 --- a/src/PJ_hammer.c +++ b/src/PJ_hammer.c @@ -1,43 +1,129 @@ -#define PROJ_PARMS__ \ - double w; \ - double m, rm; #define PJ_LIB__ -#define EPS 1.0e-10 -# include <projects.h> +#include <projects.h> + PROJ_HEAD(hammer, "Hammer & Eckert-Greifendorff") - "\n\tMisc Sph, \n\tW= M="; -FORWARD(s_forward); /* spheroid */ - double cosphi, d; - - d = sqrt(2./(1. + (cosphi = cos(lp.phi)) * cos(lp.lam *= P->w))); - xy.x = P->m * d * cosphi * sin(lp.lam); - xy.y = P->rm * d * sin(lp.phi); - return (xy); + "\n\tMisc Sph, \n\tW= M="; + +#define EPS 1.0e-10 + +struct pj_opaque { + double w; \ + double m, rm; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double cosphi, d; + + d = sqrt(2./(1. + (cosphi = cos(lp.phi)) * cos(lp.lam *= Q->w))); + xy.x = Q->m * d * cosphi * sin(lp.lam); + xy.y = Q->rm * d * sin(lp.phi); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double z; + + z = sqrt(1. - 0.25*Q->w*Q->w*xy.x*xy.x - 0.25*xy.y*xy.y); + if (fabs(2.*z*z-1.) < EPS) { + lp.lam = HUGE_VAL; + lp.phi = HUGE_VAL; + pj_errno = -14; + } else { + lp.lam = aatan2(Q->w * xy.x * z,2. * z * z - 1)/Q->w; + lp.phi = aasin(P->ctx,z * xy.y); + } + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; } -INVERSE(s_inverse); /* spheroid */ - double z; - z = sqrt(1. - 0.25*P->w*P->w*xy.x*xy.x - 0.25*xy.y*xy.y); - if (fabs(2.*z*z-1.) < EPS) { - lp.lam = HUGE_VAL; - lp.phi = HUGE_VAL; - pj_errno = -14; - } else { - lp.lam = aatan2(P->w * xy.x * z,2. * z * z - 1)/P->w; - lp.phi = aasin(P->ctx,z * xy.y); - } - return (lp); + + +PJ *PROJECTION(hammer) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + if (pj_param(P->ctx, P->params, "tW").i) { + if ((Q->w = fabs(pj_param(P->ctx, P->params, "dW").f)) <= 0.) E_ERROR(-27); + } else + Q->w = .5; + if (pj_param(P->ctx, P->params, "tM").i) { + if ((Q->m = fabs(pj_param(P->ctx, P->params, "dM").f)) <= 0.) E_ERROR(-27); + } else + Q->m = 1.; + + Q->rm = 1. / Q->m; + Q->m /= Q->w; + + P->es = 0.; + P->fwd = s_forward; + P->inv = s_inverse; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_hammer_selftest (void) {return 0;} +#else + +int pj_hammer_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=hammer +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223373.78870324057, 111703.90739776699}, + { 223373.78870324057, -111703.90739776699}, + {-223373.78870324057, 111703.90739776699}, + {-223373.78870324057, -111703.90739776699}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.001790493109965961, 0.00089524655487369749}, + { 0.001790493109965961, -0.00089524655487369749}, + {-0.001790493109965961, 0.00089524655487369749}, + {-0.001790493109965961, -0.00089524655487369749}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(hammer) - if (pj_param(P->ctx, P->params, "tW").i) { - if ((P->w = fabs(pj_param(P->ctx, P->params, "dW").f)) <= 0.) E_ERROR(-27); - } else - P->w = .5; - if (pj_param(P->ctx, P->params, "tM").i) { - if ((P->m = fabs(pj_param(P->ctx, P->params, "dM").f)) <= 0.) E_ERROR(-27); - } else - P->m = 1.; - P->rm = 1. / P->m; - P->m /= P->w; - P->es = 0.; P->fwd = s_forward; P->inv = s_inverse; -ENDENTRY(P) + + +#endif diff --git a/src/PJ_hatano.c b/src/PJ_hatano.c index 7516ba6e..ca97849b 100644 --- a/src/PJ_hatano.c +++ b/src/PJ_hatano.c @@ -1,51 +1,134 @@ -#define PJ_LIB__ -#include <projects.h> -PROJ_HEAD(hatano, "Hatano Asymmetrical Equal Area") "\n\tPCyl, Sph."; -#define NITER 20 -#define EPS 1e-7 -#define ONETOL 1.000001 -#define CN 2.67595 -#define CS 2.43763 -#define RCN 0.37369906014686373063 -#define RCS 0.41023453108141924738 -#define FYCN 1.75859 -#define FYCS 1.93052 -#define RYCN 0.56863737426006061674 -#define RYCS 0.51799515156538134803 -#define FXC 0.85 -#define RXC 1.17647058823529411764 -FORWARD(s_forward); /* spheroid */ - double th1, c; - int i; - (void) P; - - c = sin(lp.phi) * (lp.phi < 0. ? CS : CN); - for (i = NITER; i; --i) { - lp.phi -= th1 = (lp.phi + sin(lp.phi) - c) / (1. + cos(lp.phi)); - if (fabs(th1) < EPS) break; - } - xy.x = FXC * lp.lam * cos(lp.phi *= .5); - xy.y = sin(lp.phi) * (lp.phi < 0. ? FYCS : FYCN); - return (xy); -} -INVERSE(s_inverse); /* spheroid */ - double th; - - th = xy.y * ( xy.y < 0. ? RYCS : RYCN); - if (fabs(th) > 1.) - if (fabs(th) > ONETOL) I_ERROR - else th = th > 0. ? HALFPI : - HALFPI; - else - th = asin(th); - lp.lam = RXC * xy.x / cos(th); - th += th; - lp.phi = (th + sin(th)) * (xy.y < 0. ? RCS : RCN); - if (fabs(lp.phi) > 1.) - if (fabs(lp.phi) > ONETOL) I_ERROR - else lp.phi = lp.phi > 0. ? HALFPI : - HALFPI; - else - lp.phi = asin(lp.phi); - return (lp); -} -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(hatano) P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P) +#define PJ_LIB__
+#include <projects.h>
+
+PROJ_HEAD(hatano, "Hatano Asymmetrical Equal Area") "\n\tPCyl, Sph.";
+
+#define NITER 20
+#define EPS 1e-7
+#define ONETOL 1.000001
+#define CN 2.67595
+#define CS 2.43763
+#define RCN 0.37369906014686373063
+#define RCS 0.41023453108141924738
+#define FYCN 1.75859
+#define FYCS 1.93052
+#define RYCN 0.56863737426006061674
+#define RYCS 0.51799515156538134803
+#define FXC 0.85
+#define RXC 1.17647058823529411764
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ double th1, c;
+ int i;
+ (void) P;
+
+ c = sin(lp.phi) * (lp.phi < 0. ? CS : CN);
+ for (i = NITER; i; --i) {
+ lp.phi -= th1 = (lp.phi + sin(lp.phi) - c) / (1. + cos(lp.phi));
+ if (fabs(th1) < EPS) break;
+ }
+ xy.x = FXC * lp.lam * cos(lp.phi *= .5);
+ xy.y = sin(lp.phi) * (lp.phi < 0. ? FYCS : FYCN);
+
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ double th;
+
+ th = xy.y * ( xy.y < 0. ? RYCS : RYCN);
+ if (fabs(th) > 1.) {
+ if (fabs(th) > ONETOL) {
+ I_ERROR;
+ } else {
+ th = th > 0. ? HALFPI : - HALFPI;
+ }
+ } else {
+ th = asin(th);
+ }
+
+ lp.lam = RXC * xy.x / cos(th);
+ th += th;
+ lp.phi = (th + sin(th)) * (xy.y < 0. ? RCS : RCN);
+ if (fabs(lp.phi) > 1.) {
+ if (fabs(lp.phi) > ONETOL) {
+ I_ERROR;
+ } else {
+ lp.phi = lp.phi > 0. ? HALFPI : - HALFPI;
+ }
+ } else {
+ lp.phi = asin(lp.phi);
+ }
+
+ return (lp);
+}
+
+
+static void *freeup_new (PJ *P) { /* Destructor */
+ if (0==P)
+ return 0;
+
+ return pj_dealloc(P);
+}
+
+static void freeup (PJ *P) {
+ freeup_new (P);
+ return;
+}
+
+
+PJ *PROJECTION(hatano) {
+ P->es = 0.;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
+
+#ifdef PJ_OMIT_SELFTEST
+int pj_hatano_selftest (void) {return 0;}
+#else
+
+int pj_hatano_selftest (void) {
+ double tolerance_lp = 1e-10;
+ double tolerance_xy = 1e-7;
+
+ char s_args[] = {"+proj=hatano +a=6400000 +lat_1=0.5 +lat_2=2"};
+
+ LP fwd_in[] = {
+ { 2, 1},
+ { 2,-1},
+ {-2, 1},
+ {-2,-1}
+ };
+
+ XY s_fwd_expect[] = {
+ { 189878.87894652804, 131409.8024406255
},
+ { 189881.08195244463, -131409.14227607418
},
+ {-189878.87894652804, 131409.8024406255
},
+ {-189881.08195244463, -131409.14227607418
},
+ };
+
+ XY inv_in[] = {
+ { 200, 100},
+ { 200,-100},
+ {-200, 100},
+ {-200,-100}
+ };
+
+ LP s_inv_expect[] = {
+ { 0.0021064624821817597, 0.00076095689425791926
},
+ { 0.0021064624821676096, -0.00076095777439265377
},
+ {-0.0021064624821817597, 0.00076095689425791926
},
+ {-0.0021064624821676096, -0.00076095777439265377
},
+ };
+
+ return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect);
+}
+
+
+#endif
diff --git a/src/PJ_healpix.c b/src/PJ_healpix.c index 12d57ff2..f6fe624b 100644 --- a/src/PJ_healpix.c +++ b/src/PJ_healpix.c @@ -2,10 +2,10 @@ * Project: PROJ.4 * Purpose: Implementation of the HEALPix and rHEALPix projections. * For background see <http://code.scenzgrid.org/index.php/p/scenzgrid-py/source/tree/master/docs/rhealpix_dggs.pdf>. - * Authors: Alex Raichev (raichev@cs.auckland.ac.nz) + * Authors: Alex Raichev (raichev@cs.auckland.ac.nz) * Michael Speth (spethm@landcareresearch.co.nz) - * Notes: Raichev implemented these projections in Python and - * Speth translated them into C here. + * Notes: Raichev implemented these projections in Python and + * Speth translated them into C here. ****************************************************************************** * Copyright (c) 2001, Thomas Flemming, tf@ttqv.com * @@ -28,38 +28,46 @@ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. *****************************************************************************/ -# define PROJ_PARMS__ \ - int north_square; \ - int south_square; \ - double qp; \ - double *apa; -# define PJ_LIB__ -# include <projects.h> +# define PJ_LIB__ +# include <projects.h> + PROJ_HEAD(healpix, "HEALPix") "\n\tSph., Ellps."; PROJ_HEAD(rhealpix, "rHEALPix") "\n\tSph., Ellps.\n\tnorth_square= south_square="; -# include <stdio.h> + +# include <stdio.h> /* Matrix for counterclockwise rotation by pi/2: */ -# define R1 {{ 0,-1},{ 1, 0}} +# define R1 {{ 0,-1},{ 1, 0}} /* Matrix for counterclockwise rotation by pi: */ -# define R2 {{-1, 0},{ 0,-1}} +# define R2 {{-1, 0},{ 0,-1}} /* Matrix for counterclockwise rotation by 3*pi/2: */ -# define R3 {{ 0, 1},{-1, 0}} +# define R3 {{ 0, 1},{-1, 0}} /* Identity matrix */ # define IDENT {{1, 0},{0, 1}} /* IDENT, R1, R2, R3, R1 inverse, R2 inverse, R3 inverse:*/ # define ROT {IDENT, R1, R2, R3, R3, R2, R1} /* Fuzz to handle rounding errors: */ # define EPS 1e-15 + +struct pj_opaque { + int north_square; \ + int south_square; \ + double qp; \ + double *apa; +}; + typedef struct { int cn; /* An integer 0--3 indicating the position of the polar cap. */ double x, y; /* Coordinates of the pole point (point of most extreme latitude on the polar caps). */ enum Region {north, south, equatorial} region; } CapMap; + typedef struct { double x, y; } Point; + double rot[7][2][2] = ROT; + /** * Returns the sign of the double. * @param v the parameter whose sign is returned. @@ -68,31 +76,35 @@ double rot[7][2][2] = ROT; double pj_sign (double v) { return v > 0 ? 1 : (v < 0 ? -1 : 0); } + + /** * Return the index of the matrix in ROT. * @param index ranges from -3 to 3. */ static int get_rotate_index(int index) { switch(index) { - case 0: - return 0; - case 1: - return 1; - case 2: - return 2; - case 3: - return 3; - case -1: - return 4; - case -2: - return 5; - case -3: - return 6; + case 0: + return 0; + case 1: + return 1; + case 2: + return 2; + case 3: + return 3; + case -1: + return 4; + case -2: + return 5; + case -3: + return 6; } return 0; } + + /** - * Return 1 if point (testx, testy) lies in the interior of the polygon + * Return 1 if point (testx, testy) lies in the interior of the polygon * determined by the vertices in vert, and return 0 otherwise. * See http://paulbourke.net/geometry/polygonmesh/ for more details. * @param nvert the number of vertices in the polygon. @@ -103,6 +115,7 @@ static int pnpoly(int nvert, double vert[][2], double testx, double testy) { int counter = 0; double xinters; Point p1, p2; + /* Check for boundrary cases */ for (i = 0; i < nvert; i++) { if (testx == vert[i][0] && testy == vert[i][1]) { @@ -135,76 +148,84 @@ static int pnpoly(int nvert, double vert[][2], double testx, double testy) { } return c; } + + /** * Return 1 if (x, y) lies in (the interior or boundary of) the image of the - * HEALPix projection (in case proj=0) or in the image the rHEALPix projection + * HEALPix projection (in case proj=0) or in the image the rHEALPix projection * (in case proj=1), and return 0 otherwise. - * @param north_square the position of the north polar square (rHEALPix only) - * @param south_square the position of the south polar square (rHEALPix only) + * @param north_square the position of the north polar square (rHEALPix only) + * @param south_square the position of the south polar square (rHEALPix only) **/ int in_image(double x, double y, int proj, int north_square, int south_square) { if (proj == 0) { - double healpixVertsJit[][2] = { - {-1.0*PI- EPS, PI/4.0}, - {-3.0*PI/4.0, PI/2.0 + EPS}, - {-1.0*PI/2.0, PI/4.0 + EPS}, - {-1.0*PI/4.0, PI/2.0 + EPS}, - {0.0, PI/4.0 + EPS}, - {PI/4.0, PI/2.0 + EPS}, - {PI/2.0, PI/4.0 + EPS}, - {3.0*PI/4.0, PI/2.0 + EPS}, - {PI+ EPS, PI/4.0}, - {PI+ EPS, -1.0*PI/4.0}, - {3.0*PI/4.0, -1.0*PI/2.0 - EPS}, - {PI/2.0, -1.0*PI/4.0 - EPS}, - {PI/4.0, -1.0*PI/2.0 - EPS}, - {0.0, -1.0*PI/4.0 - EPS}, - {-1.0*PI/4.0, -1.0*PI/2.0 - EPS}, - {-1.0*PI/2.0, -1.0*PI/4.0 - EPS}, - {-3.0*PI/4.0, -1.0*PI/2.0 - EPS}, - {-1.0*PI - EPS, -1.0*PI/4.0} - }; - return pnpoly((int)sizeof(healpixVertsJit)/ - sizeof(healpixVertsJit[0]), healpixVertsJit, x, y); + double healpixVertsJit[][2] = { + {-1.0*PI- EPS, PI/4.0}, + {-3.0*PI/4.0, PI/2.0 + EPS}, + {-1.0*PI/2.0, PI/4.0 + EPS}, + {-1.0*PI/4.0, PI/2.0 + EPS}, + {0.0, PI/4.0 + EPS}, + {PI/4.0, PI/2.0 + EPS}, + {PI/2.0, PI/4.0 + EPS}, + {3.0*PI/4.0, PI/2.0 + EPS}, + {PI+ EPS, PI/4.0}, + {PI+ EPS, -1.0*PI/4.0}, + {3.0*PI/4.0, -1.0*PI/2.0 - EPS}, + {PI/2.0, -1.0*PI/4.0 - EPS}, + {PI/4.0, -1.0*PI/2.0 - EPS}, + {0.0, -1.0*PI/4.0 - EPS}, + {-1.0*PI/4.0, -1.0*PI/2.0 - EPS}, + {-1.0*PI/2.0, -1.0*PI/4.0 - EPS}, + {-3.0*PI/4.0, -1.0*PI/2.0 - EPS}, + {-1.0*PI - EPS, -1.0*PI/4.0} + }; + return pnpoly((int)sizeof(healpixVertsJit)/ + sizeof(healpixVertsJit[0]), healpixVertsJit, x, y); } else { - double rhealpixVertsJit[][2] = { - {-1.0*PI - EPS, PI/4.0 + EPS}, - {-1.0*PI + north_square*PI/2.0- EPS, PI/4.0 + EPS}, - {-1.0*PI + north_square*PI/2.0- EPS, 3*PI/4.0 + EPS}, - {-1.0*PI + (north_square + 1.0)*PI/2.0 + EPS, 3*PI/4.0 + EPS}, - {-1.0*PI + (north_square + 1.0)*PI/2.0 + EPS, PI/4.0 + EPS}, - {PI + EPS, PI/4.0 + EPS}, - {PI + EPS, -1.0*PI/4.0 - EPS}, - {-1.0*PI + (south_square + 1.0)*PI/2.0 + EPS, -1.0*PI/4.0 - EPS}, - {-1.0*PI + (south_square + 1.0)*PI/2.0 + EPS, -3.0*PI/4.0 - EPS}, - {-1.0*PI + south_square*PI/2.0 - EPS, -3.0*PI/4.0 - EPS}, - {-1.0*PI + south_square*PI/2.0 - EPS, -1.0*PI/4.0 - EPS}, - {-1.0*PI - EPS, -1.0*PI/4.0 - EPS}}; - return pnpoly((int)sizeof(rhealpixVertsJit)/ - sizeof(rhealpixVertsJit[0]), rhealpixVertsJit, x, y); + double rhealpixVertsJit[][2] = { + {-1.0*PI - EPS, PI/4.0 + EPS}, + {-1.0*PI + north_square*PI/2.0- EPS, PI/4.0 + EPS}, + {-1.0*PI + north_square*PI/2.0- EPS, 3*PI/4.0 + EPS}, + {-1.0*PI + (north_square + 1.0)*PI/2.0 + EPS, 3*PI/4.0 + EPS}, + {-1.0*PI + (north_square + 1.0)*PI/2.0 + EPS, PI/4.0 + EPS}, + {PI + EPS, PI/4.0 + EPS}, + {PI + EPS, -1.0*PI/4.0 - EPS}, + {-1.0*PI + (south_square + 1.0)*PI/2.0 + EPS, -1.0*PI/4.0 - EPS}, + {-1.0*PI + (south_square + 1.0)*PI/2.0 + EPS, -3.0*PI/4.0 - EPS}, + {-1.0*PI + south_square*PI/2.0 - EPS, -3.0*PI/4.0 - EPS}, + {-1.0*PI + south_square*PI/2.0 - EPS, -1.0*PI/4.0 - EPS}, + {-1.0*PI - EPS, -1.0*PI/4.0 - EPS}}; + return pnpoly((int)sizeof(rhealpixVertsJit)/ + sizeof(rhealpixVertsJit[0]), rhealpixVertsJit, x, y); } } + + /** * Return the authalic latitude of latitude alpha (if inverse=0) or - * return the approximate latitude of authalic latitude alpha (if inverse=1). - * P contains the relavent ellipsoid parameters. + * return the approximate latitude of authalic latitude alpha (if inverse=1). + * P contains the relavent ellipsoid parameters. **/ double auth_lat(PJ *P, double alpha, int inverse) { + struct pj_opaque *Q = P->opaque; if (inverse == 0) { /* Authalic latitude. */ double q = pj_qsfn(sin(alpha), P->e, 1.0 - P->es); - double qp = P->qp; - double ratio = q/qp; - if (fabsl(ratio) > 1) { - /* Rounding error. */ - ratio = pj_sign(ratio); - } - return asin(ratio); + double qp = Q->qp; + double ratio = q/qp; + + if (fabsl(ratio) > 1) { + /* Rounding error. */ + ratio = pj_sign(ratio); + } + return asin(ratio); } else { /* Approximation to inverse authalic latitude. */ - return pj_authlat(alpha, P->apa); + return pj_authlat(alpha, Q->apa); } } + + /** * Return the HEALPix projection of the longitude-latitude point lp on * the unit sphere. @@ -214,51 +235,57 @@ XY healpix_sphere(LP lp) { double phi = lp.phi; double phi0 = asin(2.0/3.0); XY xy; + /* equatorial region */ if ( fabsl(phi) <= phi0) { - xy.x = lam; - xy.y = 3.0*PI/8.0*sin(phi); + xy.x = lam; + xy.y = 3.0*PI/8.0*sin(phi); } else { - double lamc; - double sigma = sqrt(3.0*(1 - fabsl(sin(phi)))); - double cn = floor(2*lam / PI + 2); - if (cn >= 4) { - cn = 3; - } - lamc = -3*PI/4 + (PI/2)*cn; - xy.x = lamc + (lam - lamc)*sigma; - xy.y = pj_sign(phi)*PI/4*(2 - sigma); + double lamc; + double sigma = sqrt(3.0*(1 - fabsl(sin(phi)))); + double cn = floor(2*lam / PI + 2); + if (cn >= 4) { + cn = 3; + } + lamc = -3*PI/4 + (PI/2)*cn; + xy.x = lamc + (lam - lamc)*sigma; + xy.y = pj_sign(phi)*PI/4*(2 - sigma); } return xy; } + + /** - * Return the inverse of healpix_sphere(). + * Return the inverse of healpix_sphere(). **/ LP healpix_sphere_inverse(XY xy) { - LP lp; + LP lp; double x = xy.x; double y = xy.y; double y0 = PI/4.0; + /* Equatorial region. */ if (fabsl(y) <= y0) { - lp.lam = x; - lp.phi = asin(8.0*y/(3.0*PI)); + lp.lam = x; + lp.phi = asin(8.0*y/(3.0*PI)); } else if (fabsl(y) < PI/2.0) { - double cn = floor(2.0*x/PI + 2.0); + double cn = floor(2.0*x/PI + 2.0); double xc, tau; - if (cn >= 4) { - cn = 3; - } - xc = -3.0*PI/4.0 + (PI/2.0)*cn; - tau = 2.0 - 4.0*fabsl(y)/PI; - lp.lam = xc + (x - xc)/tau; - lp.phi = pj_sign(y)*asin(1.0 - pow(tau , 2.0)/3.0); + if (cn >= 4) { + cn = 3; + } + xc = -3.0*PI/4.0 + (PI/2.0)*cn; + tau = 2.0 - 4.0*fabsl(y)/PI; + lp.lam = xc + (x - xc)/tau; + lp.phi = pj_sign(y)*asin(1.0 - pow(tau , 2.0)/3.0); } else { - lp.lam = -1.0*PI; - lp.phi = pj_sign(y)*PI/2.0; + lp.lam = -1.0*PI; + lp.phi = pj_sign(y)*PI/2.0; } return (lp); } + + /** * Return the vector sum a + b, where a and b are 2-dimensional vectors. * @param ret holds a + b. @@ -266,21 +293,25 @@ LP healpix_sphere_inverse(XY xy) { static void vector_add(double a[2], double b[2], double *ret) { int i; for(i = 0; i < 2; i++) { - ret[i] = a[i] + b[i]; + ret[i] = a[i] + b[i]; } } + + /** * Return the vector difference a - b, where a and b are 2-dimensional vectors. * @param ret holds a - b. **/ static void vector_sub(double a[2], double b[2], double*ret) { int i; - for(i = 0; i < 2; i++) { - ret[i] = a[i] - b[i]; + for(i = 0; i < 2; i++) { + ret[i] = a[i] - b[i]; } } + + /** - * Return the 2 x 1 matrix product a*b, where a is a 2 x 2 matrix and + * Return the 2 x 1 matrix product a*b, where a is a 2 x 2 matrix and * b is a 2 x 1 matrix. * @param ret holds a*b. **/ @@ -288,105 +319,110 @@ static void dot_product(double a[2][2], double b[2], double *ret) { int i, j; int length = 2; for(i = 0; i < length; i++) { - ret[i] = 0; - for(j = 0; j < length; j++) { - ret[i] += a[i][j]*b[j]; - } + ret[i] = 0; + for(j = 0; j < length; j++) { + ret[i] += a[i][j]*b[j]; + } } } + + /** - * Return the number of the polar cap, the pole point coordinates, and + * Return the number of the polar cap, the pole point coordinates, and * the region that (x, y) lies in. - * If inverse=0, then assume (x,y) lies in the image of the HEALPix + * If inverse=0, then assume (x,y) lies in the image of the HEALPix * projection of the unit sphere. - * If inverse=1, then assume (x,y) lies in the image of the + * If inverse=1, then assume (x,y) lies in the image of the * (north_square, south_square)-rHEALPix projection of the unit sphere. **/ static CapMap get_cap(double x, double y, int north_square, int south_square, int inverse) { CapMap capmap; double c; + capmap.x = x; capmap.y = y; if (inverse == 0) { - if (y > PI/4.0) { - capmap.region = north; - c = PI/2.0; - } else if (y < -1*PI/4.0) { - capmap.region = south; - c = -1*PI/2.0; - } else { - capmap.region = equatorial; - capmap.cn = 0; - return capmap; - } - /* polar region */ - if (x < -1*PI/2.0) { - capmap.cn = 0; - capmap.x = (-1*3.0*PI/4.0); - capmap.y = c; - } else if (x >= -1*PI/2.0 && x < 0) { - capmap.cn = 1; - capmap.x = -1*PI/4.0; - capmap.y = c; - } else if (x >= 0 && x < PI/2.0) { - capmap.cn = 2; - capmap.x = PI/4.0; - capmap.y = c; - } else { - capmap.cn = 3; - capmap.x = 3.0*PI/4.0; - capmap.y = c; - } - return capmap; + if (y > PI/4.0) { + capmap.region = north; + c = PI/2.0; + } else if (y < -1*PI/4.0) { + capmap.region = south; + c = -1*PI/2.0; + } else { + capmap.region = equatorial; + capmap.cn = 0; + return capmap; + } + /* polar region */ + if (x < -1*PI/2.0) { + capmap.cn = 0; + capmap.x = (-1*3.0*PI/4.0); + capmap.y = c; + } else if (x >= -1*PI/2.0 && x < 0) { + capmap.cn = 1; + capmap.x = -1*PI/4.0; + capmap.y = c; + } else if (x >= 0 && x < PI/2.0) { + capmap.cn = 2; + capmap.x = PI/4.0; + capmap.y = c; + } else { + capmap.cn = 3; + capmap.x = 3.0*PI/4.0; + capmap.y = c; + } + return capmap; } else { - double eps; - if (y > PI/4.0) { - capmap.region = north; - capmap.x = (-3.0*PI/4.0 + north_square*PI/2.0); - capmap.y = PI/2.0; - x = x - north_square*PI/2.0; - } else if (y < -1*PI/4.0) { - capmap.region = south; - capmap.x = (-3.0*PI/4.0 + south_square*PI/2); - capmap.y = -1*PI/2.0; - x = x - south_square*PI/2.0; - } else { - capmap.region = equatorial; - capmap.cn = 0; - return capmap; - } - /* Polar Region, find the HEALPix polar cap number that - x, y moves to when rHEALPix polar square is disassembled. */ - eps = 1e-15; /* Kludge. Fuzz to avoid some rounding errors. */ - if (capmap.region == north) { - if (y >= -1*x - PI/4.0 - eps && y < x + 5.0*PI/4.0 - eps) { - capmap.cn = (north_square + 1) % 4; - } else if (y > -1*x -1*PI/4.0 + eps && y >= x + 5.0*PI/4.0 - eps) { - capmap.cn = (north_square + 2) % 4; - } else if (y <= -1*x -1*PI/4.0 + eps && y > x + 5.0*PI/4.0 + eps) { - capmap.cn = (north_square + 3) % 4; - } else { - capmap.cn = north_square; - } - } else if (capmap.region == south) { - if (y <= x + PI/4.0 + eps && y > -1*x - 5.0*PI/4 + eps) { - capmap.cn = (south_square + 1) % 4; - } else if (y < x + PI/4.0 - eps && y <= -1*x - 5.0*PI/4.0 + eps) { - capmap.cn = (south_square + 2) % 4; - } else if (y >= x + PI/4.0 - eps && y < -1*x - 5.0*PI/4.0 - eps) { - capmap.cn = (south_square + 3) % 4; - } else { - capmap.cn = south_square; - } - } - return capmap; + double eps; + if (y > PI/4.0) { + capmap.region = north; + capmap.x = (-3.0*PI/4.0 + north_square*PI/2.0); + capmap.y = PI/2.0; + x = x - north_square*PI/2.0; + } else if (y < -1*PI/4.0) { + capmap.region = south; + capmap.x = (-3.0*PI/4.0 + south_square*PI/2); + capmap.y = -1*PI/2.0; + x = x - south_square*PI/2.0; + } else { + capmap.region = equatorial; + capmap.cn = 0; + return capmap; + } + /* Polar Region, find the HEALPix polar cap number that + x, y moves to when rHEALPix polar square is disassembled. */ + eps = 1e-15; /* Kludge. Fuzz to avoid some rounding errors. */ + if (capmap.region == north) { + if (y >= -1*x - PI/4.0 - eps && y < x + 5.0*PI/4.0 - eps) { + capmap.cn = (north_square + 1) % 4; + } else if (y > -1*x -1*PI/4.0 + eps && y >= x + 5.0*PI/4.0 - eps) { + capmap.cn = (north_square + 2) % 4; + } else if (y <= -1*x -1*PI/4.0 + eps && y > x + 5.0*PI/4.0 + eps) { + capmap.cn = (north_square + 3) % 4; + } else { + capmap.cn = north_square; + } + } else if (capmap.region == south) { + if (y <= x + PI/4.0 + eps && y > -1*x - 5.0*PI/4 + eps) { + capmap.cn = (south_square + 1) % 4; + } else if (y < x + PI/4.0 - eps && y <= -1*x - 5.0*PI/4.0 + eps) { + capmap.cn = (south_square + 2) % 4; + } else if (y >= x + PI/4.0 - eps && y < -1*x - 5.0*PI/4.0 - eps) { + capmap.cn = (south_square + 3) % 4; + } else { + capmap.cn = south_square; + } + } + return capmap; } } + + /** - * Rearrange point (x, y) in the HEALPix projection by + * Rearrange point (x, y) in the HEALPix projection by * combining the polar caps into two polar squares. - * Put the north polar square in position north_square and + * Put the north polar square in position north_square and * the south polar square in position south_square. * If inverse=1, then uncombine the polar caps. * @param north_square integer between 0 and 3. @@ -400,173 +436,352 @@ static XY combine_caps(double x, double y, int north_square, int south_square, double vector[2]; double v_min_c[2]; double ret_dot[2]; + CapMap capmap = get_cap(x, y, north_square, south_square, inverse); if (capmap.region == equatorial) { - xy.x = capmap.x; - xy.y = capmap.y; - return xy; + xy.x = capmap.x; + xy.y = capmap.y; + return xy; } v[0] = x; v[1] = y; if (inverse == 0) { - /* Rotate (x, y) about its polar cap tip and then translate it to + /* Rotate (x, y) about its polar cap tip and then translate it to north_square or south_square. */ - int pole = 0; - double (*tmpRot)[2]; - double c[2] = {capmap.x, capmap.y}; - if (capmap.region == north) { - pole = north_square; - a[0] = (-3.0*PI/4.0 + pole*PI/2); - a[1] = (PI/2.0 + pole*0); - tmpRot = rot[get_rotate_index(capmap.cn - pole)]; - vector_sub(v, c, v_min_c); - dot_product(tmpRot, v_min_c, ret_dot); - vector_add(ret_dot, a, vector); - } else { - pole = south_square; - a[0] = (-3.0*PI/4.0 + pole*PI/2); - a[1] = (PI/-2.0 + pole*0); - tmpRot = rot[get_rotate_index(-1*(capmap.cn - pole))]; - vector_sub(v, c, v_min_c); - dot_product(tmpRot, v_min_c, ret_dot); - vector_add(ret_dot, a, vector); - } - xy.x = vector[0]; - xy.y = vector[1]; - return xy; + int pole = 0; + double (*tmpRot)[2]; + double c[2] = {capmap.x, capmap.y}; + if (capmap.region == north) { + pole = north_square; + a[0] = (-3.0*PI/4.0 + pole*PI/2); + a[1] = (PI/2.0 + pole*0); + tmpRot = rot[get_rotate_index(capmap.cn - pole)]; + vector_sub(v, c, v_min_c); + dot_product(tmpRot, v_min_c, ret_dot); + vector_add(ret_dot, a, vector); + } else { + pole = south_square; + a[0] = (-3.0*PI/4.0 + pole*PI/2); + a[1] = (PI/-2.0 + pole*0); + tmpRot = rot[get_rotate_index(-1*(capmap.cn - pole))]; + vector_sub(v, c, v_min_c); + dot_product(tmpRot, v_min_c, ret_dot); + vector_add(ret_dot, a, vector); + } + xy.x = vector[0]; + xy.y = vector[1]; + return xy; } else { /* Inverse function. Unrotate (x, y) and then translate it back. */ - int pole = 0; - double (*tmpRot)[2]; - double c[2] = {capmap.x, capmap.y}; - /* disassemble */ - if (capmap.region == north) { - pole = north_square; - a[0] = (-3.0*PI/4.0 + capmap.cn*PI/2); - a[1] = (PI/2.0 + capmap.cn*0); - tmpRot = rot[get_rotate_index(-1*(capmap.cn - pole))]; - vector_sub(v, c, v_min_c); - dot_product(tmpRot, v_min_c, ret_dot); - vector_add(ret_dot, a, vector); - } else { - pole = south_square; - a[0] = (-3.0*PI/4.0 + capmap.cn*PI/2); - a[1] = (PI/-2.0 + capmap.cn*0); - tmpRot = rot[get_rotate_index(capmap.cn - pole)]; - vector_sub(v, c, v_min_c); - dot_product(tmpRot, v_min_c, ret_dot); - vector_add(ret_dot, a, vector); - } - xy.x = vector[0]; - xy.y = vector[1]; - return xy; + int pole = 0; + double (*tmpRot)[2]; + double c[2] = {capmap.x, capmap.y}; + /* disassemble */ + if (capmap.region == north) { + pole = north_square; + a[0] = (-3.0*PI/4.0 + capmap.cn*PI/2); + a[1] = (PI/2.0 + capmap.cn*0); + tmpRot = rot[get_rotate_index(-1*(capmap.cn - pole))]; + vector_sub(v, c, v_min_c); + dot_product(tmpRot, v_min_c, ret_dot); + vector_add(ret_dot, a, vector); + } else { + pole = south_square; + a[0] = (-3.0*PI/4.0 + capmap.cn*PI/2); + a[1] = (PI/-2.0 + capmap.cn*0); + tmpRot = rot[get_rotate_index(capmap.cn - pole)]; + vector_sub(v, c, v_min_c); + dot_product(tmpRot, v_min_c, ret_dot); + vector_add(ret_dot, a, vector); + } + xy.x = vector[0]; + xy.y = vector[1]; + return xy; } } -FORWARD(s_healpix_forward); /* sphere */ + + +static XY s_healpix_forward(LP lp, PJ *P) { /* sphere */ (void) P; - (void) xy; return healpix_sphere(lp); } -FORWARD(e_healpix_forward); /* ellipsoid */ - (void) xy; + + +static XY e_healpix_forward(LP lp, PJ *P) { /* ellipsoid */ lp.phi = auth_lat(P, lp.phi, 0); return healpix_sphere(lp); } -INVERSE(s_healpix_inverse); /* sphere */ + + +static LP s_healpix_inverse(XY xy, PJ *P) { /* sphere */ + LP lp = {0.0,0.0}; + /* Check whether (x, y) lies in the HEALPix image */ if (in_image(xy.x, xy.y, 0, 0, 0) == 0) { - lp.lam = HUGE_VAL; - lp.phi = HUGE_VAL; - pj_ctx_set_errno(P->ctx, -15); - return lp; + lp.lam = HUGE_VAL; + lp.phi = HUGE_VAL; + pj_ctx_set_errno(P->ctx, -15); + return lp; } return healpix_sphere_inverse(xy); } -INVERSE(e_healpix_inverse); /* ellipsoid */ + + +static LP e_healpix_inverse(XY xy, PJ *P) { /* ellipsoid */ + LP lp = {0.0,0.0}; + /* Check whether (x, y) lies in the HEALPix image. */ if (in_image(xy.x, xy.y, 0, 0, 0) == 0) { - lp.lam = HUGE_VAL; - lp.phi = HUGE_VAL; - pj_ctx_set_errno(P->ctx, -15); - return lp; + lp.lam = HUGE_VAL; + lp.phi = HUGE_VAL; + pj_ctx_set_errno(P->ctx, -15); + return lp; } lp = healpix_sphere_inverse(xy); lp.phi = auth_lat(P, lp.phi, 1); - return (lp); + return lp; } -FORWARD(s_rhealpix_forward); /* sphere */ - xy = healpix_sphere(lp); - return combine_caps(xy.x, xy.y, P->north_square, P->south_square, 0); + + +static XY s_rhealpix_forward(LP lp, PJ *P) { /* sphere */ + struct pj_opaque *Q = P->opaque; + + XY xy = healpix_sphere(lp); + return combine_caps(xy.x, xy.y, Q->north_square, Q->south_square, 0); } -FORWARD(e_rhealpix_forward); /* ellipsoid */ + + +static XY e_rhealpix_forward(LP lp, PJ *P) { /* ellipsoid */ + struct pj_opaque *Q = P->opaque; + XY xy; lp.phi = auth_lat(P, lp.phi, 0); xy = healpix_sphere(lp); - return combine_caps(xy.x, xy.y, P->north_square, P->south_square, 0); + return combine_caps(xy.x, xy.y, Q->north_square, Q->south_square, 0); } -INVERSE(s_rhealpix_inverse); /* sphere */ + + +static LP s_rhealpix_inverse(XY xy, PJ *P) { /* sphere */ + struct pj_opaque *Q = P->opaque; + LP lp = {0.0,0.0}; + /* Check whether (x, y) lies in the rHEALPix image. */ - if (in_image(xy.x, xy.y, 1, P->north_square, P->south_square) == 0) { - lp.lam = HUGE_VAL; - lp.phi = HUGE_VAL; - pj_ctx_set_errno(P->ctx, -15); - return lp; + if (in_image(xy.x, xy.y, 1, Q->north_square, Q->south_square) == 0) { + lp.lam = HUGE_VAL; + lp.phi = HUGE_VAL; + pj_ctx_set_errno(P->ctx, -15); + return lp; } - xy = combine_caps(xy.x, xy.y, P->north_square, P->south_square, 1); + xy = combine_caps(xy.x, xy.y, Q->north_square, Q->south_square, 1); return healpix_sphere_inverse(xy); } -INVERSE(e_rhealpix_inverse); /* ellipsoid */ + + +static LP e_rhealpix_inverse(XY xy, PJ *P) { /* ellipsoid */ + struct pj_opaque *Q = P->opaque; + LP lp = {0.0,0.0}; + /* Check whether (x, y) lies in the rHEALPix image. */ - if (in_image(xy.x, xy.y, 1, P->north_square, P->south_square) == 0) { - lp.lam = HUGE_VAL; - lp.phi = HUGE_VAL; - pj_ctx_set_errno(P->ctx, -15); - return lp; + if (in_image(xy.x, xy.y, 1, Q->north_square, Q->south_square) == 0) { + lp.lam = HUGE_VAL; + lp.phi = HUGE_VAL; + pj_ctx_set_errno(P->ctx, -15); + return lp; } - xy = combine_caps(xy.x, xy.y, P->north_square, P->south_square, 1); + xy = combine_caps(xy.x, xy.y, Q->north_square, Q->south_square, 1); lp = healpix_sphere_inverse(xy); lp.phi = auth_lat(P, lp.phi, 1); return lp; } -FREEUP; - if (P) { - if (P->apa) - pj_dalloc(P->apa); - pj_dalloc(P); - } + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + if (P->opaque->apa) + pj_dealloc(P->opaque->apa); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; } -ENTRY1(healpix, apa) + + +PJ *PROJECTION(healpix) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + if (P->es) { - P->apa = pj_authset(P->es); /* For auth_lat(). */ - P->qp = pj_qsfn(1.0, P->e, P->one_es); /* For auth_lat(). */ - P->a = P->a*sqrt(0.5*P->qp); /* Set P->a to authalic radius. */ + Q->apa = pj_authset(P->es); /* For auth_lat(). */ + Q->qp = pj_qsfn(1.0, P->e, P->one_es); /* For auth_lat(). */ + P->a = P->a*sqrt(0.5*Q->qp); /* Set P->a to authalic radius. */ P->ra = 1.0/P->a; - P->fwd = e_healpix_forward; - P->inv = e_healpix_inverse; + P->fwd = e_healpix_forward; + P->inv = e_healpix_inverse; } else { - P->fwd = s_healpix_forward; - P->inv = s_healpix_inverse; + P->fwd = s_healpix_forward; + P->inv = s_healpix_inverse; } -ENDENTRY(P) -ENTRY1(rhealpix, apa) - P->north_square = pj_param(P->ctx, P->params,"inorth_square").i; - P->south_square = pj_param(P->ctx, P->params,"isouth_square").i; + + return P; +} + + +PJ *PROJECTION(rhealpix) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->north_square = pj_param(P->ctx, P->params,"inorth_square").i; + Q->south_square = pj_param(P->ctx, P->params,"isouth_square").i; + /* Check for valid north_square and south_square inputs. */ - if (P->north_square < 0 || P->north_square > 3) { - E_ERROR(-47); + if (Q->north_square < 0 || Q->north_square > 3) { + E_ERROR(-47); } - if (P->south_square < 0 || P->south_square > 3) { - E_ERROR(-47); + if (Q->south_square < 0 || Q->south_square > 3) { + E_ERROR(-47); } if (P->es) { - P->apa = pj_authset(P->es); /* For auth_lat(). */ - P->qp = pj_qsfn(1.0, P->e, P->one_es); /* For auth_lat(). */ - P->a = P->a*sqrt(0.5*P->qp); /* Set P->a to authalic radius. */ + Q->apa = pj_authset(P->es); /* For auth_lat(). */ + Q->qp = pj_qsfn(1.0, P->e, P->one_es); /* For auth_lat(). */ + P->a = P->a*sqrt(0.5*Q->qp); /* Set P->a to authalic radius. */ P->ra = 1.0/P->a; - P->fwd = e_rhealpix_forward; - P->inv = e_rhealpix_inverse; + P->fwd = e_rhealpix_forward; + P->inv = e_rhealpix_inverse; } else { - P->fwd = s_rhealpix_forward; - P->inv = s_rhealpix_inverse; + P->fwd = s_rhealpix_forward; + P->inv = s_rhealpix_inverse; } -ENDENTRY(P) + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_healpix_selftest (void) {return 0;} +#else + +int pj_healpix_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=healpix +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + char s_args[] = {"+proj=healpix +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 222390.10394923863, 130406.58866448226}, + { 222390.10394923863, -130406.58866448054}, + {-222390.10394923863, 130406.58866448226}, + {-222390.10394923863, -130406.58866448054}, + }; + + XY s_fwd_expect[] = { + { 223402.14425527418, 131588.04444199943}, + { 223402.14425527418, -131588.04444199943}, + {-223402.14425527418, 131588.04444199943}, + {-223402.14425527418, -131588.04444199943}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + { 0.0017986411845524453, 0.00076679453057823619}, + { 0.0017986411845524453, -0.00076679453057823619}, + {-0.0017986411845524453, 0.00076679453057823619}, + {-0.0017986411845524453, -0.00076679453057823619}, + }; + + LP s_inv_expect[] = { + { 0.0017904931097838226, 0.00075990887733981202}, + { 0.0017904931097838226, -0.00075990887733981202}, + {-0.0017904931097838226, 0.00075990887733981202}, + {-0.0017904931097838226, -0.00075990887733981202}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif + +#ifdef PJ_OMIT_SELFTEST +int pj_rhealpix_selftest (void) {return 0;} +#else + +int pj_rhealpix_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=rhealpix +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + char s_args[] = {"+proj=rhealpix +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 222390.10394923863, 130406.58866448226}, + { 222390.10394923863, -130406.58866448054}, + {-222390.10394923863, 130406.58866448226}, + {-222390.10394923863, -130406.58866448054}, + }; + + XY s_fwd_expect[] = { + { 223402.14425527418, 131588.04444199943}, + { 223402.14425527418, -131588.04444199943}, + {-223402.14425527418, 131588.04444199943}, + {-223402.14425527418, -131588.04444199943}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + { 0.0017986411845524453, 0.00076679453057823619}, + { 0.0017986411845524453, -0.00076679453057823619}, + {-0.0017986411845524453, 0.00076679453057823619}, + {-0.0017986411845524453, -0.00076679453057823619}, + }; + + LP s_inv_expect[] = { + { 0.0017904931097838226, 0.00075990887733981202}, + { 0.0017904931097838226, -0.00075990887733981202}, + {-0.0017904931097838226, 0.00075990887733981202}, + {-0.0017904931097838226, -0.00075990887733981202}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif diff --git a/src/PJ_igh.c b/src/PJ_igh.c index 4155c856..533c3aa0 100644 --- a/src/PJ_igh.c +++ b/src/PJ_igh.c @@ -1,13 +1,17 @@ -#define PROJ_PARMS__ \ - struct PJconsts* pj[12]; \ - double dy0; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(igh, "Interrupted Goode Homolosine") "\n\tPCyl, Sph."; - C_NAMESPACE PJ -*pj_sinu(PJ *), *pj_moll(PJ *); -static const double d4044118 = (40 + 44/60. + 11.8/3600.) * DEG_TO_RAD; // 40d 44' 11.8" [degrees] +C_NAMESPACE PJ *pj_sinu(PJ *), *pj_moll(PJ *); + +/* 40d 44' 11.8" [degrees] */ +/* +static const double d4044118 = (40 + 44/60. + 11.8/3600.) * DEG_TO_RAD; +has been replaced by this define, to eliminate portability issue: +Initializer element not computable at load time +*/ +#define d4044118 ((40 + 44/60. + 11.8/3600.) * DEG_TO_RAD) static const double d10 = 10 * DEG_TO_RAD; static const double d20 = 20 * DEG_TO_RAD; @@ -22,108 +26,134 @@ static const double d140 = 140 * DEG_TO_RAD; static const double d160 = 160 * DEG_TO_RAD; static const double d180 = 180 * DEG_TO_RAD; -static const double EPSLN = 1.e-10; // allow a little 'slack' on zone edge positions +static const double EPSLN = 1.e-10; /* allow a little 'slack' on zone edge positions */ -FORWARD(s_forward); /* spheroid */ - int z; - if (lp.phi >= d4044118) { // 1|2 - z = (lp.lam <= -d40 ? 1: 2); - } - else if (lp.phi >= 0) { // 3|4 - z = (lp.lam <= -d40 ? 3: 4); - } - else if (lp.phi >= -d4044118) { // 5|6|7|8 - if (lp.lam <= -d100) z = 5; // 5 - else if (lp.lam <= -d20) z = 6; // 6 - else if (lp.lam <= d80) z = 7; // 7 - else z = 8; // 8 - } - else { // 9|10|11|12 - if (lp.lam <= -d100) z = 9; // 9 - else if (lp.lam <= -d20) z = 10; // 10 - else if (lp.lam <= d80) z = 11; // 11 - else z = 12; // 12 - } +struct pj_opaque { + struct PJconsts* pj[12]; \ + double dy0; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + int z; + + if (lp.phi >= d4044118) { /* 1|2 */ + z = (lp.lam <= -d40 ? 1: 2); + } + else if (lp.phi >= 0) { /* 3|4 */ + z = (lp.lam <= -d40 ? 3: 4); + } + else if (lp.phi >= -d4044118) { /* 5|6|7|8 */ + if (lp.lam <= -d100) z = 5; /* 5 */ + else if (lp.lam <= -d20) z = 6; /* 6 */ + else if (lp.lam <= d80) z = 7; /* 7 */ + else z = 8; /* 8 */ + } + else { /* 9|10|11|12 */ + if (lp.lam <= -d100) z = 9; /* 9 */ + else if (lp.lam <= -d20) z = 10; /* 10 */ + else if (lp.lam <= d80) z = 11; /* 11 */ + else z = 12; /* 12 */ + } - lp.lam -= P->pj[z-1]->lam0; - xy = P->pj[z-1]->fwd(lp, P->pj[z-1]); - xy.x += P->pj[z-1]->x0; - xy.y += P->pj[z-1]->y0; + lp.lam -= Q->pj[z-1]->lam0; + xy = Q->pj[z-1]->fwd(lp, Q->pj[z-1]); + xy.x += Q->pj[z-1]->x0; + xy.y += Q->pj[z-1]->y0; - return (xy); + return xy; } -INVERSE(s_inverse); /* spheroid */ - const double y90 = P->dy0 + sqrt(2); // lt=90 corresponds to y=y0+sqrt(2) - - int z = 0; - if (xy.y > y90+EPSLN || xy.y < -y90+EPSLN) // 0 - z = 0; - else if (xy.y >= d4044118) // 1|2 - z = (xy.x <= -d40? 1: 2); - else if (xy.y >= 0) // 3|4 - z = (xy.x <= -d40? 3: 4); - else if (xy.y >= -d4044118) { // 5|6|7|8 - if (xy.x <= -d100) z = 5; // 5 - else if (xy.x <= -d20) z = 6; // 6 - else if (xy.x <= d80) z = 7; // 7 - else z = 8; // 8 - } - else { // 9|10|11|12 - if (xy.x <= -d100) z = 9; // 9 - else if (xy.x <= -d20) z = 10; // 10 - else if (xy.x <= d80) z = 11; // 11 - else z = 12; // 12 - } - if (z) - { - int ok = 0; - - xy.x -= P->pj[z-1]->x0; - xy.y -= P->pj[z-1]->y0; - lp = P->pj[z-1]->inv(xy, P->pj[z-1]); - lp.lam += P->pj[z-1]->lam0; - - switch (z) { - case 1: ok = (lp.lam >= -d180-EPSLN && lp.lam <= -d40+EPSLN) || - ((lp.lam >= -d40-EPSLN && lp.lam <= -d10+EPSLN) && - (lp.phi >= d60-EPSLN && lp.phi <= d90+EPSLN)); break; - case 2: ok = (lp.lam >= -d40-EPSLN && lp.lam <= d180+EPSLN) || - ((lp.lam >= -d180-EPSLN && lp.lam <= -d160+EPSLN) && - (lp.phi >= d50-EPSLN && lp.phi <= d90+EPSLN)) || - ((lp.lam >= -d50-EPSLN && lp.lam <= -d40+EPSLN) && - (lp.phi >= d60-EPSLN && lp.phi <= d90+EPSLN)); break; - case 3: ok = (lp.lam >= -d180-EPSLN && lp.lam <= -d40+EPSLN); break; - case 4: ok = (lp.lam >= -d40-EPSLN && lp.lam <= d180+EPSLN); break; - case 5: ok = (lp.lam >= -d180-EPSLN && lp.lam <= -d100+EPSLN); break; - case 6: ok = (lp.lam >= -d100-EPSLN && lp.lam <= -d20+EPSLN); break; - case 7: ok = (lp.lam >= -d20-EPSLN && lp.lam <= d80+EPSLN); break; - case 8: ok = (lp.lam >= d80-EPSLN && lp.lam <= d180+EPSLN); break; - case 9: ok = (lp.lam >= -d180-EPSLN && lp.lam <= -d100+EPSLN); break; - case 10: ok = (lp.lam >= -d100-EPSLN && lp.lam <= -d20+EPSLN); break; - case 11: ok = (lp.lam >= -d20-EPSLN && lp.lam <= d80+EPSLN); break; - case 12: ok = (lp.lam >= d80-EPSLN && lp.lam <= d180+EPSLN); break; - } - - z = (!ok? 0: z); // projectable? + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + const double y90 = Q->dy0 + sqrt(2); /* lt=90 corresponds to y=y0+sqrt(2) */ + + int z = 0; + if (xy.y > y90+EPSLN || xy.y < -y90+EPSLN) /* 0 */ + z = 0; + else if (xy.y >= d4044118) /* 1|2 */ + z = (xy.x <= -d40? 1: 2); + else if (xy.y >= 0) /* 3|4 */ + z = (xy.x <= -d40? 3: 4); + else if (xy.y >= -d4044118) { /* 5|6|7|8 */ + if (xy.x <= -d100) z = 5; /* 5 */ + else if (xy.x <= -d20) z = 6; /* 6 */ + else if (xy.x <= d80) z = 7; /* 7 */ + else z = 8; /* 8 */ + } + else { /* 9|10|11|12 */ + if (xy.x <= -d100) z = 9; /* 9 */ + else if (xy.x <= -d20) z = 10; /* 10 */ + else if (xy.x <= d80) z = 11; /* 11 */ + else z = 12; /* 12 */ + } + + if (z) { + int ok = 0; + + xy.x -= Q->pj[z-1]->x0; + xy.y -= Q->pj[z-1]->y0; + lp = Q->pj[z-1]->inv(xy, Q->pj[z-1]); + lp.lam += Q->pj[z-1]->lam0; + + switch (z) { + case 1: ok = (lp.lam >= -d180-EPSLN && lp.lam <= -d40+EPSLN) || + ((lp.lam >= -d40-EPSLN && lp.lam <= -d10+EPSLN) && + (lp.phi >= d60-EPSLN && lp.phi <= d90+EPSLN)); break; + case 2: ok = (lp.lam >= -d40-EPSLN && lp.lam <= d180+EPSLN) || + ((lp.lam >= -d180-EPSLN && lp.lam <= -d160+EPSLN) && + (lp.phi >= d50-EPSLN && lp.phi <= d90+EPSLN)) || + ((lp.lam >= -d50-EPSLN && lp.lam <= -d40+EPSLN) && + (lp.phi >= d60-EPSLN && lp.phi <= d90+EPSLN)); break; + case 3: ok = (lp.lam >= -d180-EPSLN && lp.lam <= -d40+EPSLN); break; + case 4: ok = (lp.lam >= -d40-EPSLN && lp.lam <= d180+EPSLN); break; + case 5: ok = (lp.lam >= -d180-EPSLN && lp.lam <= -d100+EPSLN); break; + case 6: ok = (lp.lam >= -d100-EPSLN && lp.lam <= -d20+EPSLN); break; + case 7: ok = (lp.lam >= -d20-EPSLN && lp.lam <= d80+EPSLN); break; + case 8: ok = (lp.lam >= d80-EPSLN && lp.lam <= d180+EPSLN); break; + case 9: ok = (lp.lam >= -d180-EPSLN && lp.lam <= -d100+EPSLN); break; + case 10: ok = (lp.lam >= -d100-EPSLN && lp.lam <= -d20+EPSLN); break; + case 11: ok = (lp.lam >= -d20-EPSLN && lp.lam <= d80+EPSLN); break; + case 12: ok = (lp.lam >= d80-EPSLN && lp.lam <= d180+EPSLN); break; } - // if (!z) pj_errno = -15; // invalid x or y - if (!z) lp.lam = HUGE_VAL; - if (!z) lp.phi = HUGE_VAL; - return (lp); + z = (!ok? 0: z); /* projectable? */ + } + + if (!z) lp.lam = HUGE_VAL; + if (!z) lp.phi = HUGE_VAL; + + return lp; } -FREEUP; - if (P) { - int i; - for (i = 0; i < 12; ++i) - { - if (P->pj[i]) - (*(P->pj[i]->pfree))(P->pj[i]); - } - pj_dalloc(P); - } + + +static void *freeup_new (PJ *P) { /* Destructor */ + int i; + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + for (i = 0; i < 12; ++i) { + if (P->opaque->pj[i]) + pj_dealloc(P->opaque->pj[i]); + } + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; } -ENTRY0(igh) + + + /* Zones: @@ -145,46 +175,96 @@ ENTRY0(igh) */ #define SETUP(n, proj, x_0, y_0, lon_0) \ - if (!(P->pj[n-1] = pj_##proj(0))) E_ERROR_0; \ - if (!(P->pj[n-1] = pj_##proj(P->pj[n-1]))) E_ERROR_0; \ - P->pj[n-1]->x0 = x_0; \ - P->pj[n-1]->y0 = y_0; \ - P->pj[n-1]->lam0 = lon_0; + if (!(Q->pj[n-1] = pj_##proj(0))) E_ERROR_0; \ + if (!(Q->pj[n-1] = pj_##proj(Q->pj[n-1]))) E_ERROR_0; \ + Q->pj[n-1]->x0 = x_0; \ + Q->pj[n-1]->y0 = y_0; \ + Q->pj[n-1]->lam0 = lon_0; + + +PJ *PROJECTION(igh) { + XY xy1, xy3; + LP lp = { 0, d4044118 }; + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; - LP lp = { 0, d4044118 }; - XY xy1; - XY xy3; - // sinusoidal zones - SETUP(3, sinu, -d100, 0, -d100); - SETUP(4, sinu, d30, 0, d30); - SETUP(5, sinu, -d160, 0, -d160); - SETUP(6, sinu, -d60, 0, -d60); - SETUP(7, sinu, d20, 0, d20); - SETUP(8, sinu, d140, 0, d140); + /* sinusoidal zones */ + SETUP(3, sinu, -d100, 0, -d100); + SETUP(4, sinu, d30, 0, d30); + SETUP(5, sinu, -d160, 0, -d160); + SETUP(6, sinu, -d60, 0, -d60); + SETUP(7, sinu, d20, 0, d20); + SETUP(8, sinu, d140, 0, d140); - // mollweide zones - SETUP(1, moll, -d100, 0, -d100); + /* mollweide zones */ + SETUP(1, moll, -d100, 0, -d100); - // y0 ? - xy1 = P->pj[0]->fwd(lp, P->pj[0]); // zone 1 - xy3 = P->pj[2]->fwd(lp, P->pj[2]); // zone 3 - // y0 + xy1.y = xy3.y for lt = 40d44'11.8" - P->dy0 = xy3.y - xy1.y; + /* y0 ? */ + xy1 = Q->pj[0]->fwd(lp, Q->pj[0]); /* zone 1 */ + xy3 = Q->pj[2]->fwd(lp, Q->pj[2]); /* zone 3 */ + /* y0 + xy1.y = xy3.y for lt = 40d44'11.8" */ + Q->dy0 = xy3.y - xy1.y; - P->pj[0]->y0 = P->dy0; + Q->pj[0]->y0 = Q->dy0; - // mollweide zones (cont'd) - SETUP( 2, moll, d30, P->dy0, d30); - SETUP( 9, moll, -d160, -P->dy0, -d160); - SETUP(10, moll, -d60, -P->dy0, -d60); - SETUP(11, moll, d20, -P->dy0, d20); - SETUP(12, moll, d140, -P->dy0, d140); + /* mollweide zones (cont'd) */ + SETUP( 2, moll, d30, Q->dy0, d30); + SETUP( 9, moll, -d160, -Q->dy0, -d160); + SETUP(10, moll, -d60, -Q->dy0, -d60); + SETUP(11, moll, d20, -Q->dy0, d20); + SETUP(12, moll, d140, -Q->dy0, d140); - P->inv = s_inverse; - P->fwd = s_forward; - P->es = 0.; -ENDENTRY(P) + P->inv = s_inverse; + P->fwd = s_forward; + P->es = 0.; + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_igh_selftest (void) {return 0;} +#else + +int pj_igh_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=igh +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223878.49745627123, 111701.07212763709}, + { 223708.37131305804, -111701.07212763709}, + {-222857.74059699223, 111701.07212763709}, + {-223027.86674020503, -111701.07212763709}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.001790489447892545, 0.00089524655489191132}, + { 0.0017904906685957927, -0.00089524655489191132}, + {-0.001790496772112032, 0.00089524655489191132}, + {-0.0017904955514087843, -0.00089524655489191132}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} +#endif diff --git a/src/PJ_imw_p.c b/src/PJ_imw_p.c index ae411116..a1675a90 100644 --- a/src/PJ_imw_p.c +++ b/src/PJ_imw_p.c @@ -1,151 +1,238 @@ -#define PROJ_PARMS__ \ - double P, Pp, Q, Qp, R_1, R_2, sphi_1, sphi_2, C2; \ - double phi_1, phi_2, lam_1; \ - double *en; \ - int mode; /* = 0, phi_1 and phi_2 != 0, = 1, phi_1 = 0, = -1 phi_2 = 0 */ #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(imw_p, "International Map of the World Polyconic") - "\n\tMod. Polyconic, Ell\n\tlat_1= and lat_2= [lon_1=]"; + "\n\tMod. Polyconic, Ell\n\tlat_1= and lat_2= [lon_1=]"; + #define TOL 1e-10 #define EPS 1e-10 - static int -phi12(PJ *P, double *del, double *sig) { - int err = 0; - - if (!pj_param(P->ctx, P->params, "tlat_1").i || - !pj_param(P->ctx, P->params, "tlat_2").i) { - err = -41; - } else { - P->phi_1 = pj_param(P->ctx, P->params, "rlat_1").f; - P->phi_2 = pj_param(P->ctx, P->params, "rlat_2").f; - *del = 0.5 * (P->phi_2 - P->phi_1); - *sig = 0.5 * (P->phi_2 + P->phi_1); - err = (fabs(*del) < EPS || fabs(*sig) < EPS) ? -42 : 0; - } - return err; + +struct pj_opaque { + double P, Pp, Q, Qp, R_1, R_2, sphi_1, sphi_2, C2; \ + double phi_1, phi_2, lam_1; \ + double *en; \ + int mode; /* = 0, phi_1 and phi_2 != 0, = 1, phi_1 = 0, = -1 phi_2 = 0 */ +}; + + +static int phi12(PJ *P, double *del, double *sig) { + struct pj_opaque *Q = P->opaque; + int err = 0; + + if (!pj_param(P->ctx, P->params, "tlat_1").i || + !pj_param(P->ctx, P->params, "tlat_2").i) { + err = -41; + } else { + Q->phi_1 = pj_param(P->ctx, P->params, "rlat_1").f; + Q->phi_2 = pj_param(P->ctx, P->params, "rlat_2").f; + *del = 0.5 * (Q->phi_2 - Q->phi_1); + *sig = 0.5 * (Q->phi_2 + Q->phi_1); + err = (fabs(*del) < EPS || fabs(*sig) < EPS) ? -42 : 0; + } + return err; +} + + +static XY loc_for(LP lp, PJ *P, double *yc) { + struct pj_opaque *Q = P->opaque; + XY xy; + + if (! lp.phi) { + xy.x = lp.lam; + xy.y = 0.; + } else { + double xa, ya, xb, yb, xc, D, B, m, sp, t, R, C; + + sp = sin(lp.phi); + m = pj_mlfn(lp.phi, sp, cos(lp.phi), Q->en); + xa = Q->Pp + Q->Qp * m; + ya = Q->P + Q->Q * m; + R = 1. / (tan(lp.phi) * sqrt(1. - P->es * sp * sp)); + C = sqrt(R * R - xa * xa); + if (lp.phi < 0.) C = - C; + C += ya - R; + if (Q->mode < 0) { + xb = lp.lam; + yb = Q->C2; + } else { + t = lp.lam * Q->sphi_2; + xb = Q->R_2 * sin(t); + yb = Q->C2 + Q->R_2 * (1. - cos(t)); + } + if (Q->mode > 0) { + xc = lp.lam; + *yc = 0.; + } else { + t = lp.lam * Q->sphi_1; + xc = Q->R_1 * sin(t); + *yc = Q->R_1 * (1. - cos(t)); + } + D = (xb - xc)/(yb - *yc); + B = xc + D * (C + R - *yc); + xy.x = D * sqrt(R * R * (1 + D * D) - B * B); + if (lp.phi > 0) + xy.x = - xy.x; + xy.x = (B + xy.x) / (1. + D * D); + xy.y = sqrt(R * R - xy.x * xy.x); + if (lp.phi > 0) + xy.y = - xy.y; + xy.y += C + R; + } + return xy; +} + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + double yc; + + xy = loc_for(lp, P, &yc); + return (xy); +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + XY t; + double yc; + + lp.phi = Q->phi_2; + lp.lam = xy.x / cos(lp.phi); + do { + t = loc_for(lp, P, &yc); + lp.phi = ((lp.phi - Q->phi_1) * (xy.y - yc) / (t.y - yc)) + Q->phi_1; + lp.lam = lp.lam * xy.x / t.x; + } while (fabs(t.x - xy.x) > TOL || fabs(t.y - xy.y) > TOL); + + return lp; +} + + +static void xy(PJ *P, double phi, double *x, double *y, double *sp, double *R) { + double F; + + *sp = sin(phi); + *R = 1./(tan(phi) * sqrt(1. - P->es * *sp * *sp )); + F = P->opaque->lam_1 * *sp; + *y = *R * (1 - cos(F)); + *x = *R * sin(F); } - static XY -loc_for(LP lp, PJ *P, double *yc) { - XY xy; - - if (! lp.phi) { - xy.x = lp.lam; - xy.y = 0.; - } else { - double xa, ya, xb, yb, xc, D, B, m, sp, t, R, C; - - sp = sin(lp.phi); - m = pj_mlfn(lp.phi, sp, cos(lp.phi), P->en); - xa = P->Pp + P->Qp * m; - ya = P->P + P->Q * m; - R = 1. / (tan(lp.phi) * sqrt(1. - P->es * sp * sp)); - C = sqrt(R * R - xa * xa); - if (lp.phi < 0.) C = - C; - C += ya - R; - if (P->mode < 0) { - xb = lp.lam; - yb = P->C2; - } else { - t = lp.lam * P->sphi_2; - xb = P->R_2 * sin(t); - yb = P->C2 + P->R_2 * (1. - cos(t)); - } - if (P->mode > 0) { - xc = lp.lam; - *yc = 0.; - } else { - t = lp.lam * P->sphi_1; - xc = P->R_1 * sin(t); - *yc = P->R_1 * (1. - cos(t)); - } - D = (xb - xc)/(yb - *yc); - B = xc + D * (C + R - *yc); - xy.x = D * sqrt(R * R * (1 + D * D) - B * B); - if (lp.phi > 0) - xy.x = - xy.x; - xy.x = (B + xy.x) / (1. + D * D); - xy.y = sqrt(R * R - xy.x * xy.x); - if (lp.phi > 0) - xy.y = - xy.y; - xy.y += C + R; - } - return (xy); + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); } -FORWARD(e_forward); /* ellipsoid */ - double yc; - xy = loc_for(lp, P, &yc); - return (xy); + +static void freeup (PJ *P) { + freeup_new (P); + return; } -INVERSE(e_inverse); /* ellipsoid */ - XY t; - double yc; - - lp.phi = P->phi_2; - lp.lam = xy.x / cos(lp.phi); - do { - t = loc_for(lp, P, &yc); - lp.phi = ((lp.phi - P->phi_1) * (xy.y - yc) / (t.y - yc)) + P->phi_1; - lp.lam = lp.lam * xy.x / t.x; - } while (fabs(t.x - xy.x) > TOL || fabs(t.y - xy.y) > TOL); - return (lp); + + +PJ *PROJECTION(imw_p) { + double del, sig, s, t, x1, x2, T2, y1, m1, m2, y2; + int i; + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + if (!(Q->en = pj_enfn(P->es))) E_ERROR_0; + if( (i = phi12(P, &del, &sig)) != 0) + E_ERROR(i); + if (Q->phi_2 < Q->phi_1) { /* make sure P->phi_1 most southerly */ + del = Q->phi_1; + Q->phi_1 = Q->phi_2; + Q->phi_2 = del; + } + if (pj_param(P->ctx, P->params, "tlon_1").i) + Q->lam_1 = pj_param(P->ctx, P->params, "rlon_1").f; + else { /* use predefined based upon latitude */ + sig = fabs(sig * RAD_TO_DEG); + if (sig <= 60) sig = 2.; + else if (sig <= 76) sig = 4.; + else sig = 8.; + Q->lam_1 = sig * DEG_TO_RAD; + } + Q->mode = 0; + if (Q->phi_1) xy(P, Q->phi_1, &x1, &y1, &Q->sphi_1, &Q->R_1); + else { + Q->mode = 1; + y1 = 0.; + x1 = Q->lam_1; + } + if (Q->phi_2) xy(P, Q->phi_2, &x2, &T2, &Q->sphi_2, &Q->R_2); + else { + Q->mode = -1; + T2 = 0.; + x2 = Q->lam_1; + } + m1 = pj_mlfn(Q->phi_1, Q->sphi_1, cos(Q->phi_1), Q->en); + m2 = pj_mlfn(Q->phi_2, Q->sphi_2, cos(Q->phi_2), Q->en); + t = m2 - m1; + s = x2 - x1; + y2 = sqrt(t * t - s * s) + y1; + Q->C2 = y2 - T2; + t = 1. / t; + Q->P = (m2 * y1 - m1 * y2) * t; + Q->Q = (y2 - y1) * t; + Q->Pp = (m2 * x1 - m1 * x2) * t; + Q->Qp = (x2 - x1) * t; + + P->fwd = e_forward; + P->inv = e_inverse; + + return P; } - static void -xy(PJ *P, double phi, double *x, double *y, double *sp, double *R) { - double F; - - *sp = sin(phi); - *R = 1./(tan(phi) * sqrt(1. - P->es * *sp * *sp )); - F = P->lam_1 * *sp; - *y = *R * (1 - cos(F)); - *x = *R * sin(F); + + +#ifdef PJ_OMIT_SELFTEST +int pj_imw_p_selftest (void) {return 0;} +#else + +int pj_imw_p_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=imw_p +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 222588.4411393762, 55321.128653809537}, + { 222756.90637768712, -165827.58428832365}, + {-222588.4411393762, 55321.128653809537}, + {-222756.90637768712, -165827.58428832365}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + { 0.0017966991379592214, 0.50090492361427374}, + { 0.0017966979081574697, 0.49909507588689922}, + {-0.0017966991379592214, 0.50090492361427374}, + {-0.0017966979081574697, 0.49909507588689922}, + }; + + return pj_generic_selftest (e_args, 0, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, 0, inv_in, e_inv_expect, 0); } -FREEUP; if (P) { if (P->en) pj_dalloc(P->en); pj_dalloc(P); } } -ENTRY1(imw_p, en) - double del, sig, s, t, x1, x2, T2, y1, m1, m2, y2; - int i; - - if (!(P->en = pj_enfn(P->es))) E_ERROR_0; - if( (i = phi12(P, &del, &sig)) != 0) - E_ERROR(i); - if (P->phi_2 < P->phi_1) { /* make sure P->phi_1 most southerly */ - del = P->phi_1; - P->phi_1 = P->phi_2; - P->phi_2 = del; - } - if (pj_param(P->ctx, P->params, "tlon_1").i) - P->lam_1 = pj_param(P->ctx, P->params, "rlon_1").f; - else { /* use predefined based upon latitude */ - sig = fabs(sig * RAD_TO_DEG); - if (sig <= 60) sig = 2.; - else if (sig <= 76) sig = 4.; - else sig = 8.; - P->lam_1 = sig * DEG_TO_RAD; - } - P->mode = 0; - if (P->phi_1) xy(P, P->phi_1, &x1, &y1, &P->sphi_1, &P->R_1); - else { - P->mode = 1; - y1 = 0.; - x1 = P->lam_1; - } - if (P->phi_2) xy(P, P->phi_2, &x2, &T2, &P->sphi_2, &P->R_2); - else { - P->mode = -1; - T2 = 0.; - x2 = P->lam_1; - } - m1 = pj_mlfn(P->phi_1, P->sphi_1, cos(P->phi_1), P->en); - m2 = pj_mlfn(P->phi_2, P->sphi_2, cos(P->phi_2), P->en); - t = m2 - m1; - s = x2 - x1; - y2 = sqrt(t * t - s * s) + y1; - P->C2 = y2 - T2; - t = 1. / t; - P->P = (m2 * y1 - m1 * y2) * t; - P->Q = (y2 - y1) * t; - P->Pp = (m2 * x1 - m1 * x2) * t; - P->Qp = (x2 - x1) * t; - P->fwd = e_forward; - P->inv = e_inverse; -ENDENTRY(P) + + +#endif diff --git a/src/PJ_isea.c b/src/PJ_isea.c index 7c09189c..ba9d3c62 100644 --- a/src/PJ_isea.c +++ b/src/PJ_isea.c @@ -1,1119 +1,1176 @@ -/* - * This code was entirely written by Nathan Wagner - * and is in the public domain. - */ - -#include <math.h> -#include <stdio.h> -#include <stdlib.h> -#include <float.h> - -#ifndef M_PI -# define M_PI 3.14159265358979323846 -#endif - -/* - * Proj 4 provides its own entry points into - * the code, so none of the library functions - * need to be global - */ -#define ISEA_STATIC static -#ifndef ISEA_STATIC -#define ISEA_STATIC -#endif - -struct hex { - int iso; - int x, y, z; -}; - -/* y *must* be positive down as the xy /iso conversion assumes this */ -ISEA_STATIC -int hex_xy(struct hex *h) { - if (!h->iso) return 1; - if (h->x >= 0) { - h->y = -h->y - (h->x+1)/2; - } else { - /* need to round toward -inf, not toward zero, so x-1 */ - h->y = -h->y - h->x/2; - } - h->iso = 0; - - return 1; -} - -ISEA_STATIC -int hex_iso(struct hex *h) { - if (h->iso) return 1; - - if (h->x >= 0) { - h->y = (-h->y - (h->x+1)/2); - } else { - /* need to round toward -inf, not toward zero, so x-1 */ - h->y = (-h->y - (h->x)/2); - } - - h->z = -h->x - h->y; - h->iso = 1; - return 1; -} - -ISEA_STATIC -int hexbin2(double width, double x, double y, - int *i, int *j) { - double z, rx, ry, rz; - double abs_dx, abs_dy, abs_dz; - int ix, iy, iz, s; - struct hex h; - - x = x / cos(30 * M_PI / 180.0); /* rotated X coord */ - y = y - x / 2.0; /* adjustment for rotated X */ - - /* adjust for actual hexwidth */ - x /= width; - y /= width; - - z = -x - y; - - ix = rx = floor(x + 0.5); - iy = ry = floor(y + 0.5); - iz = rz = floor(z + 0.5); - - s = ix + iy + iz; - - if (s) { - abs_dx = fabs(rx - x); - abs_dy = fabs(ry - y); - abs_dz = fabs(rz - z); - - if (abs_dx >= abs_dy && abs_dx >= abs_dz) { - ix -= s; - } else if (abs_dy >= abs_dx && abs_dy >= abs_dz) { - iy -= s; - } else { - iz -= s; - } - } - h.x = ix; - h.y = iy; - h.z = iz; - h.iso = 1; - - hex_xy(&h); - *i = h.x; - *j = h.y; - return ix * 100 + iy; -} -#ifndef ISEA_STATIC -#define ISEA_STATIC -#endif - -enum isea_poly { ISEA_NONE, ISEA_ICOSAHEDRON = 20 }; -enum isea_topology { ISEA_HEXAGON=6, ISEA_TRIANGLE=3, ISEA_DIAMOND=4 }; -enum isea_address_form { ISEA_GEO, ISEA_Q2DI, ISEA_SEQNUM, ISEA_INTERLEAVE, - ISEA_PLANE, ISEA_Q2DD, ISEA_PROJTRI, ISEA_VERTEX2DD, ISEA_HEX -}; - -struct isea_dgg { - int polyhedron; /* ignored, icosahedron */ - double o_lat, o_lon, o_az; /* orientation, radians */ - int pole; /* true if standard snyder */ - int topology; /* ignored, hexagon */ - int aperture; /* valid values depend on partitioning method */ - int resolution; - double radius; /* radius of the earth in meters, ignored 1.0 */ - int output; /* an isea_address_form */ - int triangle; /* triangle of last transformed point */ - int quad; /* quad of last transformed point */ - unsigned long serial; -}; - -struct isea_pt { - double x, y; -}; - -struct isea_geo { - double lon, lat; -}; - -struct isea_address { - int type; /* enum isea_address_form */ - int number; - double x,y; /* or i,j or lon,lat depending on type */ -}; - -/* ENDINC */ - -enum snyder_polyhedron { - SNYDER_POLY_HEXAGON, SNYDER_POLY_PENTAGON, - SNYDER_POLY_TETRAHEDRON, SNYDER_POLY_CUBE, - SNYDER_POLY_OCTAHEDRON, SNYDER_POLY_DODECAHEDRON, - SNYDER_POLY_ICOSAHEDRON -}; - -struct snyder_constants { - double g, G, theta, ea_w, ea_a, ea_b, g_w, g_a, g_b; -}; - -/* TODO put these in radians to avoid a later conversion */ -ISEA_STATIC -struct snyder_constants constants[] = { - {23.80018260, 62.15458023, 60.0, 3.75, 1.033, 0.968, 5.09, 1.195, 1.0}, - {20.07675127, 55.69063953, 54.0, 2.65, 1.030, 0.983, 3.59, 1.141, 1.027}, - {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, - {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, - {37.37736814, 36.0, 30.0, 17.27, 1.163, 0.860, 13.14, 1.584, 1.0}, -}; - -#define E 52.62263186 -#define F 10.81231696 - -#define DEG60 1.04719755119659774614 -#define DEG120 2.09439510239319549229 -#define DEG72 1.25663706143591729537 -#define DEG90 1.57079632679489661922 -#define DEG144 2.51327412287183459075 -#define DEG36 0.62831853071795864768 -#define DEG108 1.88495559215387594306 -#define DEG180 M_PI -/* sqrt(5)/M_PI */ -#define ISEA_SCALE 0.8301572857837594396028083 - -/* 26.565051177 degrees */ -#define V_LAT 0.46364760899944494524 - -#define RAD2DEG (180.0/M_PI) -#define DEG2RAD (M_PI/180.0) - -ISEA_STATIC -struct isea_geo vertex[] = { - {0.0, DEG90}, - {DEG180, V_LAT}, - {-DEG108, V_LAT}, - {-DEG36, V_LAT}, - {DEG36, V_LAT}, - {DEG108, V_LAT}, - {-DEG144, -V_LAT}, - {-DEG72, -V_LAT}, - {0.0, -V_LAT}, - {DEG72, -V_LAT}, - {DEG144, -V_LAT}, - {0.0, -DEG90} -}; - -/* TODO make an isea_pt array of the vertices as well */ - -static int tri_v1[] = {0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 2, 3, 4, 5, 1, 11, 11, 11, 11, 11}; - -/* 52.62263186 */ -#define E_RAD 0.91843818702186776133 - -/* 10.81231696 */ -#define F_RAD 0.18871053072122403508 - -/* triangle Centers */ -struct isea_geo icostriangles[] = { - {0.0, 0.0}, - {-DEG144, E_RAD}, - {-DEG72, E_RAD}, - {0.0, E_RAD}, - {DEG72, E_RAD}, - {DEG144, E_RAD}, - {-DEG144, F_RAD}, - {-DEG72, F_RAD}, - {0.0, F_RAD}, - {DEG72, F_RAD}, - {DEG144, F_RAD}, - {-DEG108, -F_RAD}, - {-DEG36, -F_RAD}, - {DEG36, -F_RAD}, - {DEG108, -F_RAD}, - {DEG180, -F_RAD}, - {-DEG108, -E_RAD}, - {-DEG36, -E_RAD}, - {DEG36, -E_RAD}, - {DEG108, -E_RAD}, - {DEG180, -E_RAD}, -}; - -static double -az_adjustment(int triangle) -{ - double adj; - - struct isea_geo v; - struct isea_geo c; - - v = vertex[tri_v1[triangle]]; - c = icostriangles[triangle]; - - /* TODO looks like the adjustment is always either 0 or 180 */ - /* at least if you pick your vertex carefully */ - adj = atan2(cos(v.lat) * sin(v.lon - c.lon), - cos(c.lat) * sin(v.lat) - - sin(c.lat) * cos(v.lat) * cos(v.lon - c.lon)); - return adj; -} - -/* R tan(g) sin(60) */ -#define TABLE_G 0.6615845383 - -/* H = 0.25 R tan g = */ -#define TABLE_H 0.1909830056 - -#define RPRIME 0.91038328153090290025 - -ISEA_STATIC -struct isea_pt -isea_triangle_xy(int triangle) -{ - struct isea_pt c; - double Rprime = 0.91038328153090290025; - - triangle = (triangle - 1) % 20; - - c.x = TABLE_G * ((triangle % 5) - 2) * 2.0; - if (triangle > 9) { - c.x += TABLE_G; - } - switch (triangle / 5) { - case 0: - c.y = 5.0 * TABLE_H; - break; - case 1: - c.y = TABLE_H; - break; - case 2: - c.y = -TABLE_H; - break; - case 3: - c.y = -5.0 * TABLE_H; - break; - default: - /* should be impossible */ - exit(EXIT_FAILURE); - }; - c.x *= Rprime; - c.y *= Rprime; - - return c; -} - -/* snyder eq 14 */ -static double -sph_azimuth(double f_lon, double f_lat, double t_lon, double t_lat) -{ - double az; - - az = atan2(cos(t_lat) * sin(t_lon - f_lon), - cos(f_lat) * sin(t_lat) - - sin(f_lat) * cos(t_lat) * cos(t_lon - f_lon) - ); - return az; -} - -/* coord needs to be in radians */ -ISEA_STATIC -int -isea_snyder_forward(struct isea_geo * ll, struct isea_pt * out) -{ - int i; - - /* - * spherical distance from center of polygon face to any of its - * vertexes on the globe - */ - double g; - - /* - * spherical angle between radius vector to center and adjacent edge - * of spherical polygon on the globe - */ - double G; - - /* - * plane angle between radius vector to center and adjacent edge of - * plane polygon - */ - double theta; - - /* additional variables from snyder */ - double q, Rprime, H, Ag, Azprime, Az, dprime, f, rho, - x, y; - - /* variables used to store intermediate results */ - double cot_theta, tan_g, az_offset; - - /* how many multiples of 60 degrees we adjust the azimuth */ - int Az_adjust_multiples; - - struct snyder_constants c; - - /* - * TODO by locality of reference, start by trying the same triangle - * as last time - */ - - /* TODO put these constants in as radians to begin with */ - c = constants[SNYDER_POLY_ICOSAHEDRON]; - theta = c.theta * DEG2RAD; - g = c.g * DEG2RAD; - G = c.G * DEG2RAD; - - for (i = 1; i <= 20; i++) { - double z; - struct isea_geo center; - - center = icostriangles[i]; - - /* step 1 */ - z = acos(sin(center.lat) * sin(ll->lat) - + cos(center.lat) * cos(ll->lat) * cos(ll->lon - center.lon)); - /* not on this triangle */ - if (z > g + 0.000005) { /* TODO DBL_EPSILON */ - continue; - } - - Az = sph_azimuth(center.lon, center.lat, ll->lon, ll->lat); - - /* step 2 */ - - /* This calculates "some" vertex coordinate */ - az_offset = az_adjustment(i); - - Az -= az_offset; - - /* TODO I don't know why we do this. It's not in snyder */ - /* maybe because we should have picked a better vertex */ - if (Az < 0.0) { - Az += 2.0 * M_PI; - } - /* - * adjust Az for the point to fall within the range of 0 to - * 2(90 - theta) or 60 degrees for the hexagon, by - * and therefore 120 degrees for the triangle - * of the icosahedron - * subtracting or adding multiples of 60 degrees to Az and - * recording the amount of adjustment - */ - - Az_adjust_multiples = 0; - while (Az < 0.0) { - Az += DEG120; - Az_adjust_multiples--; - } - while (Az > DEG120 + DBL_EPSILON) { - Az -= DEG120; - Az_adjust_multiples++; - } - - /* step 3 */ - cot_theta = 1.0 / tan(theta); - tan_g = tan(g); /* TODO this is a constant */ - - /* Calculate q from eq 9. */ - /* TODO cot_theta is cot(30) */ - q = atan2(tan_g, cos(Az) + sin(Az) * cot_theta); - - /* not in this triangle */ - if (z > q + 0.000005) { - continue; - } - /* step 4 */ - - /* Apply equations 5-8 and 10-12 in order */ - - /* eq 5 */ - /* Rprime = 0.9449322893 * R; */ - /* R' in the paper is for the truncated */ - Rprime = 0.91038328153090290025; - - /* eq 6 */ - H = acos(sin(Az) * sin(G) * cos(g) - cos(Az) * cos(G)); - - /* eq 7 */ - /* Ag = (Az + G + H - DEG180) * M_PI * R * R / DEG180; */ - Ag = Az + G + H - DEG180; - - /* eq 8 */ - Azprime = atan2(2.0 * Ag, Rprime * Rprime * tan_g * tan_g - 2.0 * Ag * cot_theta); - - /* eq 10 */ - /* cot(theta) = 1.73205080756887729355 */ - dprime = Rprime * tan_g / (cos(Azprime) + sin(Azprime) * cot_theta); - - /* eq 11 */ - f = dprime / (2.0 * Rprime * sin(q / 2.0)); - - /* eq 12 */ - rho = 2.0 * Rprime * f * sin(z / 2.0); - - /* - * add back the same 60 degree multiple adjustment from step - * 2 to Azprime - */ - - Azprime += DEG120 * Az_adjust_multiples; - - /* calculate rectangular coordinates */ - - x = rho * sin(Azprime); - y = rho * cos(Azprime); - - /* - * TODO - * translate coordinates to the origin for the particular - * hexagon on the flattened polyhedral map plot - */ - - out->x = x; - out->y = y; - - return i; - } - - /* - * should be impossible, this implies that the coordinate is not on - * any triangle - */ - - fprintf(stderr, "impossible transform: %f %f is not on any triangle\n", - ll->lon * RAD2DEG, ll->lat * RAD2DEG); - - exit(EXIT_FAILURE); - - /* not reached */ - return 0; /* supresses a warning */ -} - -/* - * return the new coordinates of any point in orginal coordinate system. - * Define a point (newNPold) in orginal coordinate system as the North Pole in - * new coordinate system, and the great circle connect the original and new - * North Pole as the lon0 longitude in new coordinate system, given any point - * in orginal coordinate system, this function return the new coordinates. - */ - -#define PRECISION 0.0000000000005 - -/* formula from Snyder, Map Projections: A working manual, p31 */ -/* - * old north pole at np in new coordinates - * could be simplified a bit with fewer intermediates - * - * TODO take a result pointer - */ -ISEA_STATIC -struct isea_geo -snyder_ctran(struct isea_geo * np, struct isea_geo * pt) -{ - struct isea_geo npt; - double alpha, phi, lambda, lambda0, beta, lambdap, phip; - double sin_phip; - double lp_b; /* lambda prime minus beta */ - double cos_p, sin_a; - - phi = pt->lat; - lambda = pt->lon; - alpha = np->lat; - beta = np->lon; - lambda0 = beta; - - cos_p = cos(phi); - sin_a = sin(alpha); - - /* mpawm 5-7 */ - sin_phip = sin_a * sin(phi) - cos(alpha) * cos_p * cos(lambda - lambda0); - - /* mpawm 5-8b */ - - /* use the two argument form so we end up in the right quadrant */ - lp_b = atan2(cos_p * sin(lambda - lambda0), - (sin_a * cos_p * cos(lambda - lambda0) + cos(alpha) * sin(phi))); - - lambdap = lp_b + beta; - - /* normalize longitude */ - /* TODO can we just do a modulus ? */ - lambdap = fmod(lambdap, 2 * M_PI); - while (lambdap > M_PI) - lambdap -= 2 * M_PI; - while (lambdap < -M_PI) - lambdap += 2 * M_PI; - - phip = asin(sin_phip); - - npt.lat = phip; - npt.lon = lambdap; - - return npt; -} - -ISEA_STATIC -struct isea_geo -isea_ctran(struct isea_geo * np, struct isea_geo * pt, double lon0) -{ - struct isea_geo npt; - - np->lon += M_PI; - npt = snyder_ctran(np, pt); - np->lon -= M_PI; - - npt.lon -= (M_PI - lon0 + np->lon); - - /* - * snyder is down tri 3, isea is along side of tri1 from vertex 0 to - * vertex 1 these are 180 degrees apart - */ - npt.lon += M_PI; - /* normalize longitude */ - npt.lon = fmod(npt.lon, 2 * M_PI); - while (npt.lon > M_PI) - npt.lon -= 2 * M_PI; - while (npt.lon < -M_PI) - npt.lon += 2 * M_PI; - - return npt; -} - -/* in radians */ -#define ISEA_STD_LAT 1.01722196792335072101 -#define ISEA_STD_LON .19634954084936207740 - -/* fuller's at 5.2454 west, 2.3009 N, adjacent at 7.46658 deg */ - -ISEA_STATIC -int -isea_grid_init(struct isea_dgg * g) -{ - if (!g) - return 0; - - g->polyhedron = 20; - g->o_lat = ISEA_STD_LAT; - g->o_lon = ISEA_STD_LON; - g->o_az = 0.0; - g->aperture = 4; - g->resolution = 6; - g->radius = 1.0; - g->topology = 6; - - return 1; -} - -ISEA_STATIC -int -isea_orient_isea(struct isea_dgg * g) -{ - if (!g) - return 0; - g->o_lat = ISEA_STD_LAT; - g->o_lon = ISEA_STD_LON; - g->o_az = 0.0; - return 1; -} - -ISEA_STATIC -int -isea_orient_pole(struct isea_dgg * g) -{ - if (!g) - return 0; - g->o_lat = M_PI / 2.0; - g->o_lon = 0.0; - g->o_az = 0; - return 1; -} - -ISEA_STATIC -int -isea_transform(struct isea_dgg * g, struct isea_geo * in, - struct isea_pt * out) -{ - struct isea_geo i, pole; - int tri; - - pole.lat = g->o_lat; - pole.lon = g->o_lon; - - i = isea_ctran(&pole, in, g->o_az); - - tri = isea_snyder_forward(&i, out); - out->x *= g->radius; - out->y *= g->radius; - g->triangle = tri; - - return tri; -} - -#define DOWNTRI(tri) (((tri - 1) / 5) % 2 == 1) - -ISEA_STATIC -void -isea_rotate(struct isea_pt * pt, double degrees) -{ - double rad; - - double x, y; - - rad = -degrees * M_PI / 180.0; - while (rad >= 2.0 * M_PI) rad -= 2.0 * M_PI; - while (rad <= -2.0 * M_PI) rad += 2.0 * M_PI; - - x = pt->x * cos(rad) + pt->y * sin(rad); - y = -pt->x * sin(rad) + pt->y * cos(rad); - - pt->x = x; - pt->y = y; -} - -ISEA_STATIC -int isea_tri_plane(int tri, struct isea_pt *pt, double radius) { - struct isea_pt tc; /* center of triangle */ - - if (DOWNTRI(tri)) { - isea_rotate(pt, 180.0); - } - tc = isea_triangle_xy(tri); - tc.x *= radius; - tc.y *= radius; - pt->x += tc.x; - pt->y += tc.y; - - return tri; -} - -/* convert projected triangle coords to quad xy coords, return quad number */ -ISEA_STATIC -int -isea_ptdd(int tri, struct isea_pt *pt) { - int downtri, quad; - - downtri = (((tri - 1) / 5) % 2 == 1); - quad = ((tri - 1) % 5) + ((tri - 1) / 10) * 5 + 1; - - isea_rotate(pt, downtri ? 240.0 : 60.0); - if (downtri) { - pt->x += 0.5; - /* pt->y += cos(30.0 * M_PI / 180.0); */ - pt->y += .86602540378443864672; - } - return quad; -} - -ISEA_STATIC -int -isea_dddi_ap3odd(struct isea_dgg *g, int quad, struct isea_pt *pt, struct isea_pt *di) -{ - struct isea_pt v; - double hexwidth; - double sidelength; /* in hexes */ - int d, i; - int maxcoord; - struct hex h; - - /* This is the number of hexes from apex to base of a triangle */ - sidelength = (pow(2.0, g->resolution) + 1.0) / 2.0; - - /* apex to base is cos(30deg) */ - hexwidth = cos(M_PI / 6.0) / sidelength; - - /* TODO I think sidelength is always x.5, so - * (int)sidelength * 2 + 1 might be just as good - */ - maxcoord = (int) (sidelength * 2.0 + 0.5); - - v = *pt; - hexbin2(hexwidth, v.x, v.y, &h.x, &h.y); - h.iso = 0; - hex_iso(&h); - - d = h.x - h.z; - i = h.x + h.y + h.y; - - /* - * you want to test for max coords for the next quad in the same - * "row" first to get the case where both are max - */ - if (quad <= 5) { - if (d == 0 && i == maxcoord) { - /* north pole */ - quad = 0; - d = 0; - i = 0; - } else if (i == maxcoord) { - /* upper right in next quad */ - quad += 1; - if (quad == 6) - quad = 1; - i = maxcoord - d; - d = 0; - } else if (d == maxcoord) { - /* lower right in quad to lower right */ - quad += 5; - d = 0; - } - } else if (quad >= 6) { - if (i == 0 && d == maxcoord) { - /* south pole */ - quad = 11; - d = 0; - i = 0; - } else if (d == maxcoord) { - /* lower right in next quad */ - quad += 1; - if (quad == 11) - quad = 6; - d = maxcoord - i; - i = 0; - } else if (i == maxcoord) { - /* upper right in quad to upper right */ - quad = (quad - 4) % 5; - i = 0; - } - } - - di->x = d; - di->y = i; - - g->quad = quad; - return quad; -} - -ISEA_STATIC -int -isea_dddi(struct isea_dgg *g, int quad, struct isea_pt *pt, struct isea_pt *di) { - struct isea_pt v; - double hexwidth; - int sidelength; /* in hexes */ - struct hex h; - - if (g->aperture == 3 && g->resolution % 2 != 0) { - return isea_dddi_ap3odd(g, quad, pt, di); - } - /* todo might want to do this as an iterated loop */ - if (g->aperture >0) { - sidelength = (int) (pow(g->aperture, g->resolution / 2.0) + 0.5); - } else { - sidelength = g->resolution; - } - - hexwidth = 1.0 / sidelength; - - v = *pt; - isea_rotate(&v, -30.0); - hexbin2(hexwidth, v.x, v.y, &h.x, &h.y); - h.iso = 0; - hex_iso(&h); - - /* we may actually be on another quad */ - if (quad <= 5) { - if (h.x == 0 && h.z == -sidelength) { - /* north pole */ - quad = 0; - h.z = 0; - h.y = 0; - h.x = 0; - } else if (h.z == -sidelength) { - quad = quad + 1; - if (quad == 6) - quad = 1; - h.y = sidelength - h.x; - h.z = h.x - sidelength; - h.x = 0; - } else if (h.x == sidelength) { - quad += 5; - h.y = -h.z; - h.x = 0; - } - } else if (quad >= 6) { - if (h.z == 0 && h.x == sidelength) { - /* south pole */ - quad = 11; - h.x = 0; - h.y = 0; - h.z = 0; - } else if (h.x == sidelength) { - quad = quad + 1; - if (quad == 11) - quad = 6; - h.x = h.y + sidelength; - h.y = 0; - h.z = -h.x; - } else if (h.y == -sidelength) { - quad -= 4; - h.y = 0; - h.z = -h.x; - } - } - di->x = h.x; - di->y = -h.z; - - g->quad = quad; - return quad; -} - -ISEA_STATIC -int isea_ptdi(struct isea_dgg *g, int tri, struct isea_pt *pt, - struct isea_pt *di) { - struct isea_pt v; - int quad; - - v = *pt; - quad = isea_ptdd(tri, &v); - quad = isea_dddi(g, quad, &v, di); - return quad; -} - -/* q2di to seqnum */ -ISEA_STATIC -int isea_disn(struct isea_dgg *g, int quad, struct isea_pt *di) { - int sidelength; - int sn, height; - int hexes; - - if (quad == 0) { - g->serial = 1; - return g->serial; - } - /* hexes in a quad */ - hexes = (int) (pow(g->aperture, g->resolution) + 0.5); - if (quad == 11) { - g->serial = 1 + 10 * hexes + 1; - return g->serial; - } - if (g->aperture == 3 && g->resolution % 2 == 1) { - height = (int) (pow(g->aperture, (g->resolution - 1) / 2.0)); - sn = ((int) di->x) * height; - sn += ((int) di->y) / height; - sn += (quad - 1) * hexes; - sn += 2; - } else { - sidelength = (int) (pow(g->aperture, g->resolution / 2.0) + 0.5); - sn = (quad - 1) * hexes + sidelength * di->x + di->y + 2; - } - - g->serial = sn; - return sn; -} - -/* TODO just encode the quad in the d or i coordinate - * quad is 0-11, which can be four bits. - * d' = d << 4 + q, d = d' >> 4, q = d' & 0xf - */ -/* convert a q2di to global hex coord */ -ISEA_STATIC -int isea_hex(struct isea_dgg *g, int tri, - struct isea_pt *pt, struct isea_pt *hex) { - struct isea_pt v; - int sidelength; - int d, i, x, y, quad; - - quad = isea_ptdi(g, tri, pt, &v); - - hex->x = ((int)v.x << 4) + quad; - hex->y = v.y; - - return 1; - - d = v.x; - i = v.y; - - /* Aperture 3 odd resolutions */ - if (g->aperture == 3 && g->resolution % 2 != 0) { - int offset = (int)(pow(3.0, g->resolution - 1) + 0.5); - - d += offset * ((g->quad-1) % 5); - i += offset * ((g->quad-1) % 5); - - if (quad == 0) { - d = 0; - i = offset; - } else if (quad == 11) { - d = 2 * offset; - i = 0; - } else if (quad > 5) { - d += offset; - } - - x = (2*d - i) /3; - y = (2*i - d) /3; - - hex->x = x + offset / 3; - hex->y = y + 2 * offset / 3; - return 1; - } - - /* aperture 3 even resolutions and aperture 4 */ - sidelength = (int) (pow(g->aperture, g->resolution / 2.0) + 0.5); - if (g->quad == 0) { - hex->x = 0; - hex->y = sidelength; - } else if (g->quad == 11) { - hex->x = sidelength * 2; - hex->y = 0; - } else { - hex->x = d + sidelength * ((g->quad-1) % 5); - if (g->quad > 5) hex->x += sidelength; - hex->y = i + sidelength * ((g->quad-1) % 5); - } - - return 1; -} - -ISEA_STATIC -struct isea_pt -isea_forward(struct isea_dgg *g, struct isea_geo *in) -{ - int tri; - struct isea_pt out, coord; - - tri = isea_transform(g, in, &out); - - if (g->output == ISEA_PLANE) { - isea_tri_plane(tri, &out, g->radius); - return out; - } - - /* convert to isea standard triangle size */ - out.x = out.x / g->radius * ISEA_SCALE; - out.y = out.y / g->radius * ISEA_SCALE; - out.x += 0.5; - out.y += 2.0 * .14433756729740644112; - - switch (g->output) { - case ISEA_PROJTRI: - /* nothing to do, already in projected triangle */ - break; - case ISEA_VERTEX2DD: - g->quad = isea_ptdd(tri, &out); - break; - case ISEA_Q2DD: - /* Same as above, we just don't print as much */ - g->quad = isea_ptdd(tri, &out); - break; - case ISEA_Q2DI: - g->quad = isea_ptdi(g, tri, &out, &coord); - return coord; - break; - case ISEA_SEQNUM: - isea_ptdi(g, tri, &out, &coord); - /* disn will set g->serial */ - isea_disn(g, g->quad, &coord); - return coord; - break; - case ISEA_HEX: - isea_hex(g, tri, &out, &coord); - return coord; - break; - } - - return out; -} -/* - * Proj 4 integration code follows - */ - -#define PROJ_PARMS__ \ - struct isea_dgg dgg; - -#define PJ_LIB__ -#include <projects.h> - -PROJ_HEAD(isea, "Icosahedral Snyder Equal Area") "\n\tSph"; - -FORWARD(s_forward); - struct isea_pt out; - struct isea_geo in; - - in.lon = lp.lam; - in.lat = lp.phi; - - out = isea_forward(&P->dgg, &in); - - xy.x = out.x; - xy.y = out.y; - - return xy; -} -FREEUP; if (P) pj_dalloc(P); } - -ENTRY0(isea) - char *opt; - - P->fwd = s_forward; - isea_grid_init(&P->dgg); - - P->dgg.output = ISEA_PLANE; -/* P->dgg.radius = P->a; / * otherwise defaults to 1 */ - /* calling library will scale, I think */ - - opt = pj_param(P->ctx,P->params, "sorient").s; - if (opt) { - if (!strcmp(opt, "isea")) { - isea_orient_isea(&P->dgg); - } else if (!strcmp(opt, "pole")) { - isea_orient_pole(&P->dgg); - } else { - E_ERROR(-34); - } - } - - if (pj_param(P->ctx,P->params, "tazi").i) { - P->dgg.o_az = pj_param(P->ctx,P->params, "razi").f; - } - - if (pj_param(P->ctx,P->params, "tlon_0").i) { - P->dgg.o_lon = pj_param(P->ctx,P->params, "rlon_0").f; - } - - if (pj_param(P->ctx,P->params, "tlat_0").i) { - P->dgg.o_lat = pj_param(P->ctx,P->params, "rlat_0").f; - } - - if (pj_param(P->ctx,P->params, "taperture").i) { - P->dgg.aperture = pj_param(P->ctx,P->params, "iaperture").i; - } - - if (pj_param(P->ctx,P->params, "tresolution").i) { - P->dgg.resolution = pj_param(P->ctx,P->params, "iresolution").i; - } - - opt = pj_param(P->ctx,P->params, "smode").s; - if (opt) { - if (!strcmp(opt, "plane")) { - P->dgg.output = ISEA_PLANE; - } else if (!strcmp(opt, "di")) { - P->dgg.output = ISEA_Q2DI; - } - else if (!strcmp(opt, "dd")) { - P->dgg.output = ISEA_Q2DD; - } - else if (!strcmp(opt, "hex")) { - P->dgg.output = ISEA_HEX; - } - else { - /* TODO verify error code. Possibly eliminate magic */ - E_ERROR(-34); - } - } - - if (pj_param(P->ctx,P->params, "trescale").i) { - P->dgg.radius = ISEA_SCALE; - } - - if (pj_param(P->ctx,P->params, "tresolution").i) { - P->dgg.resolution = pj_param(P->ctx,P->params, "iresolution").i; - } else { - P->dgg.resolution = 4; - } - - if (pj_param(P->ctx,P->params, "taperture").i) { - P->dgg.aperture = pj_param(P->ctx,P->params, "iaperture").i; - } else { - P->dgg.aperture = 3; - } - -ENDENTRY(P) +/*
+ * This code was entirely written by Nathan Wagner
+ * and is in the public domain.
+ */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <float.h>
+
+#ifndef M_PI
+# define M_PI 3.14159265358979323846
+#endif
+
+/*
+ * Proj 4 provides its own entry points into
+ * the code, so none of the library functions
+ * need to be global
+ */
+#define ISEA_STATIC static
+#ifndef ISEA_STATIC
+#define ISEA_STATIC
+#endif
+
+struct hex {
+ int iso;
+ int x, y, z;
+};
+
+/* y *must* be positive down as the xy /iso conversion assumes this */
+ISEA_STATIC
+int hex_xy(struct hex *h) {
+ if (!h->iso) return 1;
+ if (h->x >= 0) {
+ h->y = -h->y - (h->x+1)/2;
+ } else {
+ /* need to round toward -inf, not toward zero, so x-1 */
+ h->y = -h->y - h->x/2;
+ }
+ h->iso = 0;
+
+ return 1;
+}
+
+ISEA_STATIC
+int hex_iso(struct hex *h) {
+ if (h->iso) return 1;
+
+ if (h->x >= 0) {
+ h->y = (-h->y - (h->x+1)/2);
+ } else {
+ /* need to round toward -inf, not toward zero, so x-1 */
+ h->y = (-h->y - (h->x)/2);
+ }
+
+ h->z = -h->x - h->y;
+ h->iso = 1;
+ return 1;
+}
+
+ISEA_STATIC
+int hexbin2(double width, double x, double y,
+ int *i, int *j) {
+ double z, rx, ry, rz;
+ double abs_dx, abs_dy, abs_dz;
+ int ix, iy, iz, s;
+ struct hex h;
+
+ x = x / cos(30 * M_PI / 180.0); /* rotated X coord */
+ y = y - x / 2.0; /* adjustment for rotated X */
+
+ /* adjust for actual hexwidth */
+ x /= width;
+ y /= width;
+
+ z = -x - y;
+
+ ix = rx = floor(x + 0.5);
+ iy = ry = floor(y + 0.5);
+ iz = rz = floor(z + 0.5);
+
+ s = ix + iy + iz;
+
+ if (s) {
+ abs_dx = fabs(rx - x);
+ abs_dy = fabs(ry - y);
+ abs_dz = fabs(rz - z);
+
+ if (abs_dx >= abs_dy && abs_dx >= abs_dz) {
+ ix -= s;
+ } else if (abs_dy >= abs_dx && abs_dy >= abs_dz) {
+ iy -= s;
+ } else {
+ iz -= s;
+ }
+ }
+ h.x = ix;
+ h.y = iy;
+ h.z = iz;
+ h.iso = 1;
+
+ hex_xy(&h);
+ *i = h.x;
+ *j = h.y;
+ return ix * 100 + iy;
+}
+#ifndef ISEA_STATIC
+#define ISEA_STATIC
+#endif
+
+enum isea_poly { ISEA_NONE, ISEA_ICOSAHEDRON = 20 };
+enum isea_topology { ISEA_HEXAGON=6, ISEA_TRIANGLE=3, ISEA_DIAMOND=4 };
+enum isea_address_form { ISEA_GEO, ISEA_Q2DI, ISEA_SEQNUM, ISEA_INTERLEAVE,
+ ISEA_PLANE, ISEA_Q2DD, ISEA_PROJTRI, ISEA_VERTEX2DD, ISEA_HEX
+};
+
+struct isea_dgg {
+ int polyhedron; /* ignored, icosahedron */
+ double o_lat, o_lon, o_az; /* orientation, radians */
+ int pole; /* true if standard snyder */
+ int topology; /* ignored, hexagon */
+ int aperture; /* valid values depend on partitioning method */
+ int resolution;
+ double radius; /* radius of the earth in meters, ignored 1.0 */
+ int output; /* an isea_address_form */
+ int triangle; /* triangle of last transformed point */
+ int quad; /* quad of last transformed point */
+ unsigned long serial;
+};
+
+struct isea_pt {
+ double x, y;
+};
+
+struct isea_geo {
+ double lon, lat;
+};
+
+struct isea_address {
+ int type; /* enum isea_address_form */
+ int number;
+ double x,y; /* or i,j or lon,lat depending on type */
+};
+
+/* ENDINC */
+
+enum snyder_polyhedron {
+ SNYDER_POLY_HEXAGON, SNYDER_POLY_PENTAGON,
+ SNYDER_POLY_TETRAHEDRON, SNYDER_POLY_CUBE,
+ SNYDER_POLY_OCTAHEDRON, SNYDER_POLY_DODECAHEDRON,
+ SNYDER_POLY_ICOSAHEDRON
+};
+
+struct snyder_constants {
+ double g, G, theta, ea_w, ea_a, ea_b, g_w, g_a, g_b;
+};
+
+/* TODO put these in radians to avoid a later conversion */
+ISEA_STATIC
+struct snyder_constants constants[] = {
+ {23.80018260, 62.15458023, 60.0, 3.75, 1.033, 0.968, 5.09, 1.195, 1.0},
+ {20.07675127, 55.69063953, 54.0, 2.65, 1.030, 0.983, 3.59, 1.141, 1.027},
+ {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
+ {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
+ {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
+ {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
+ {37.37736814, 36.0, 30.0, 17.27, 1.163, 0.860, 13.14, 1.584, 1.0},
+};
+
+#define E 52.62263186
+#define F 10.81231696
+
+#define DEG60 1.04719755119659774614
+#define DEG120 2.09439510239319549229
+#define DEG72 1.25663706143591729537
+#define DEG90 1.57079632679489661922
+#define DEG144 2.51327412287183459075
+#define DEG36 0.62831853071795864768
+#define DEG108 1.88495559215387594306
+#define DEG180 M_PI
+/* sqrt(5)/M_PI */
+#define ISEA_SCALE 0.8301572857837594396028083
+
+/* 26.565051177 degrees */
+#define V_LAT 0.46364760899944494524
+
+#define RAD2DEG (180.0/M_PI)
+#define DEG2RAD (M_PI/180.0)
+
+ISEA_STATIC
+struct isea_geo vertex[] = {
+ {0.0, DEG90},
+ {DEG180, V_LAT},
+ {-DEG108, V_LAT},
+ {-DEG36, V_LAT},
+ {DEG36, V_LAT},
+ {DEG108, V_LAT},
+ {-DEG144, -V_LAT},
+ {-DEG72, -V_LAT},
+ {0.0, -V_LAT},
+ {DEG72, -V_LAT},
+ {DEG144, -V_LAT},
+ {0.0, -DEG90}
+};
+
+/* TODO make an isea_pt array of the vertices as well */
+
+static int tri_v1[] = {0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 2, 3, 4, 5, 1, 11, 11, 11, 11, 11};
+
+/* 52.62263186 */
+#define E_RAD 0.91843818702186776133
+
+/* 10.81231696 */
+#define F_RAD 0.18871053072122403508
+
+/* triangle Centers */
+struct isea_geo icostriangles[] = {
+ {0.0, 0.0},
+ {-DEG144, E_RAD},
+ {-DEG72, E_RAD},
+ {0.0, E_RAD},
+ {DEG72, E_RAD},
+ {DEG144, E_RAD},
+ {-DEG144, F_RAD},
+ {-DEG72, F_RAD},
+ {0.0, F_RAD},
+ {DEG72, F_RAD},
+ {DEG144, F_RAD},
+ {-DEG108, -F_RAD},
+ {-DEG36, -F_RAD},
+ {DEG36, -F_RAD},
+ {DEG108, -F_RAD},
+ {DEG180, -F_RAD},
+ {-DEG108, -E_RAD},
+ {-DEG36, -E_RAD},
+ {DEG36, -E_RAD},
+ {DEG108, -E_RAD},
+ {DEG180, -E_RAD},
+};
+
+static double
+az_adjustment(int triangle)
+{
+ double adj;
+
+ struct isea_geo v;
+ struct isea_geo c;
+
+ v = vertex[tri_v1[triangle]];
+ c = icostriangles[triangle];
+
+ /* TODO looks like the adjustment is always either 0 or 180 */
+ /* at least if you pick your vertex carefully */
+ adj = atan2(cos(v.lat) * sin(v.lon - c.lon),
+ cos(c.lat) * sin(v.lat)
+ - sin(c.lat) * cos(v.lat) * cos(v.lon - c.lon));
+ return adj;
+}
+
+/* R tan(g) sin(60) */
+#define TABLE_G 0.6615845383
+
+/* H = 0.25 R tan g = */
+#define TABLE_H 0.1909830056
+
+#define RPRIME 0.91038328153090290025
+
+ISEA_STATIC
+struct isea_pt
+isea_triangle_xy(int triangle)
+{
+ struct isea_pt c;
+ double Rprime = 0.91038328153090290025;
+
+ triangle = (triangle - 1) % 20;
+
+ c.x = TABLE_G * ((triangle % 5) - 2) * 2.0;
+ if (triangle > 9) {
+ c.x += TABLE_G;
+ }
+ switch (triangle / 5) {
+ case 0:
+ c.y = 5.0 * TABLE_H;
+ break;
+ case 1:
+ c.y = TABLE_H;
+ break;
+ case 2:
+ c.y = -TABLE_H;
+ break;
+ case 3:
+ c.y = -5.0 * TABLE_H;
+ break;
+ default:
+ /* should be impossible */
+ exit(EXIT_FAILURE);
+ };
+ c.x *= Rprime;
+ c.y *= Rprime;
+
+ return c;
+}
+
+/* snyder eq 14 */
+static double
+sph_azimuth(double f_lon, double f_lat, double t_lon, double t_lat)
+{
+ double az;
+
+ az = atan2(cos(t_lat) * sin(t_lon - f_lon),
+ cos(f_lat) * sin(t_lat)
+ - sin(f_lat) * cos(t_lat) * cos(t_lon - f_lon)
+ );
+ return az;
+}
+
+/* coord needs to be in radians */
+ISEA_STATIC
+int
+isea_snyder_forward(struct isea_geo * ll, struct isea_pt * out)
+{
+ int i;
+
+ /*
+ * spherical distance from center of polygon face to any of its
+ * vertexes on the globe
+ */
+ double g;
+
+ /*
+ * spherical angle between radius vector to center and adjacent edge
+ * of spherical polygon on the globe
+ */
+ double G;
+
+ /*
+ * plane angle between radius vector to center and adjacent edge of
+ * plane polygon
+ */
+ double theta;
+
+ /* additional variables from snyder */
+ double q, Rprime, H, Ag, Azprime, Az, dprime, f, rho,
+ x, y;
+
+ /* variables used to store intermediate results */
+ double cot_theta, tan_g, az_offset;
+
+ /* how many multiples of 60 degrees we adjust the azimuth */
+ int Az_adjust_multiples;
+
+ struct snyder_constants c;
+
+ /*
+ * TODO by locality of reference, start by trying the same triangle
+ * as last time
+ */
+
+ /* TODO put these constants in as radians to begin with */
+ c = constants[SNYDER_POLY_ICOSAHEDRON];
+ theta = c.theta * DEG2RAD;
+ g = c.g * DEG2RAD;
+ G = c.G * DEG2RAD;
+
+ for (i = 1; i <= 20; i++) {
+ double z;
+ struct isea_geo center;
+
+ center = icostriangles[i];
+
+ /* step 1 */
+ z = acos(sin(center.lat) * sin(ll->lat)
+ + cos(center.lat) * cos(ll->lat) * cos(ll->lon - center.lon));
+ /* not on this triangle */
+ if (z > g + 0.000005) { /* TODO DBL_EPSILON */
+ continue;
+ }
+
+ Az = sph_azimuth(center.lon, center.lat, ll->lon, ll->lat);
+
+ /* step 2 */
+
+ /* This calculates "some" vertex coordinate */
+ az_offset = az_adjustment(i);
+
+ Az -= az_offset;
+
+ /* TODO I don't know why we do this. It's not in snyder */
+ /* maybe because we should have picked a better vertex */
+ if (Az < 0.0) {
+ Az += 2.0 * M_PI;
+ }
+ /*
+ * adjust Az for the point to fall within the range of 0 to
+ * 2(90 - theta) or 60 degrees for the hexagon, by
+ * and therefore 120 degrees for the triangle
+ * of the icosahedron
+ * subtracting or adding multiples of 60 degrees to Az and
+ * recording the amount of adjustment
+ */
+
+ Az_adjust_multiples = 0;
+ while (Az < 0.0) {
+ Az += DEG120;
+ Az_adjust_multiples--;
+ }
+ while (Az > DEG120 + DBL_EPSILON) {
+ Az -= DEG120;
+ Az_adjust_multiples++;
+ }
+
+ /* step 3 */
+ cot_theta = 1.0 / tan(theta);
+ tan_g = tan(g); /* TODO this is a constant */
+
+ /* Calculate q from eq 9. */
+ /* TODO cot_theta is cot(30) */
+ q = atan2(tan_g, cos(Az) + sin(Az) * cot_theta);
+
+ /* not in this triangle */
+ if (z > q + 0.000005) {
+ continue;
+ }
+ /* step 4 */
+
+ /* Apply equations 5-8 and 10-12 in order */
+
+ /* eq 5 */
+ /* Rprime = 0.9449322893 * R; */
+ /* R' in the paper is for the truncated */
+ Rprime = 0.91038328153090290025;
+
+ /* eq 6 */
+ H = acos(sin(Az) * sin(G) * cos(g) - cos(Az) * cos(G));
+
+ /* eq 7 */
+ /* Ag = (Az + G + H - DEG180) * M_PI * R * R / DEG180; */
+ Ag = Az + G + H - DEG180;
+
+ /* eq 8 */
+ Azprime = atan2(2.0 * Ag, Rprime * Rprime * tan_g * tan_g - 2.0 * Ag * cot_theta);
+
+ /* eq 10 */
+ /* cot(theta) = 1.73205080756887729355 */
+ dprime = Rprime * tan_g / (cos(Azprime) + sin(Azprime) * cot_theta);
+
+ /* eq 11 */
+ f = dprime / (2.0 * Rprime * sin(q / 2.0));
+
+ /* eq 12 */
+ rho = 2.0 * Rprime * f * sin(z / 2.0);
+
+ /*
+ * add back the same 60 degree multiple adjustment from step
+ * 2 to Azprime
+ */
+
+ Azprime += DEG120 * Az_adjust_multiples;
+
+ /* calculate rectangular coordinates */
+
+ x = rho * sin(Azprime);
+ y = rho * cos(Azprime);
+
+ /*
+ * TODO
+ * translate coordinates to the origin for the particular
+ * hexagon on the flattened polyhedral map plot
+ */
+
+ out->x = x;
+ out->y = y;
+
+ return i;
+ }
+
+ /*
+ * should be impossible, this implies that the coordinate is not on
+ * any triangle
+ */
+
+ fprintf(stderr, "impossible transform: %f %f is not on any triangle\n",
+ ll->lon * RAD2DEG, ll->lat * RAD2DEG);
+
+ exit(EXIT_FAILURE);
+
+ /* not reached */
+ return 0; /* supresses a warning */
+}
+
+/*
+ * return the new coordinates of any point in orginal coordinate system.
+ * Define a point (newNPold) in orginal coordinate system as the North Pole in
+ * new coordinate system, and the great circle connect the original and new
+ * North Pole as the lon0 longitude in new coordinate system, given any point
+ * in orginal coordinate system, this function return the new coordinates.
+ */
+
+#define PRECISION 0.0000000000005
+
+/* formula from Snyder, Map Projections: A working manual, p31 */
+/*
+ * old north pole at np in new coordinates
+ * could be simplified a bit with fewer intermediates
+ *
+ * TODO take a result pointer
+ */
+ISEA_STATIC
+struct isea_geo
+snyder_ctran(struct isea_geo * np, struct isea_geo * pt)
+{
+ struct isea_geo npt;
+ double alpha, phi, lambda, lambda0, beta, lambdap, phip;
+ double sin_phip;
+ double lp_b; /* lambda prime minus beta */
+ double cos_p, sin_a;
+
+ phi = pt->lat;
+ lambda = pt->lon;
+ alpha = np->lat;
+ beta = np->lon;
+ lambda0 = beta;
+
+ cos_p = cos(phi);
+ sin_a = sin(alpha);
+
+ /* mpawm 5-7 */
+ sin_phip = sin_a * sin(phi) - cos(alpha) * cos_p * cos(lambda - lambda0);
+
+ /* mpawm 5-8b */
+
+ /* use the two argument form so we end up in the right quadrant */
+ lp_b = atan2(cos_p * sin(lambda - lambda0),
+ (sin_a * cos_p * cos(lambda - lambda0) + cos(alpha) * sin(phi)));
+
+ lambdap = lp_b + beta;
+
+ /* normalize longitude */
+ /* TODO can we just do a modulus ? */
+ lambdap = fmod(lambdap, 2 * M_PI);
+ while (lambdap > M_PI)
+ lambdap -= 2 * M_PI;
+ while (lambdap < -M_PI)
+ lambdap += 2 * M_PI;
+
+ phip = asin(sin_phip);
+
+ npt.lat = phip;
+ npt.lon = lambdap;
+
+ return npt;
+}
+
+ISEA_STATIC
+struct isea_geo
+isea_ctran(struct isea_geo * np, struct isea_geo * pt, double lon0)
+{
+ struct isea_geo npt;
+
+ np->lon += M_PI;
+ npt = snyder_ctran(np, pt);
+ np->lon -= M_PI;
+
+ npt.lon -= (M_PI - lon0 + np->lon);
+
+ /*
+ * snyder is down tri 3, isea is along side of tri1 from vertex 0 to
+ * vertex 1 these are 180 degrees apart
+ */
+ npt.lon += M_PI;
+ /* normalize longitude */
+ npt.lon = fmod(npt.lon, 2 * M_PI);
+ while (npt.lon > M_PI)
+ npt.lon -= 2 * M_PI;
+ while (npt.lon < -M_PI)
+ npt.lon += 2 * M_PI;
+
+ return npt;
+}
+
+/* in radians */
+#define ISEA_STD_LAT 1.01722196792335072101
+#define ISEA_STD_LON .19634954084936207740
+
+/* fuller's at 5.2454 west, 2.3009 N, adjacent at 7.46658 deg */
+
+ISEA_STATIC
+int
+isea_grid_init(struct isea_dgg * g)
+{
+ if (!g)
+ return 0;
+
+ g->polyhedron = 20;
+ g->o_lat = ISEA_STD_LAT;
+ g->o_lon = ISEA_STD_LON;
+ g->o_az = 0.0;
+ g->aperture = 4;
+ g->resolution = 6;
+ g->radius = 1.0;
+ g->topology = 6;
+
+ return 1;
+}
+
+ISEA_STATIC
+int
+isea_orient_isea(struct isea_dgg * g)
+{
+ if (!g)
+ return 0;
+ g->o_lat = ISEA_STD_LAT;
+ g->o_lon = ISEA_STD_LON;
+ g->o_az = 0.0;
+ return 1;
+}
+
+ISEA_STATIC
+int
+isea_orient_pole(struct isea_dgg * g)
+{
+ if (!g)
+ return 0;
+ g->o_lat = M_PI / 2.0;
+ g->o_lon = 0.0;
+ g->o_az = 0;
+ return 1;
+}
+
+ISEA_STATIC
+int
+isea_transform(struct isea_dgg * g, struct isea_geo * in,
+ struct isea_pt * out)
+{
+ struct isea_geo i, pole;
+ int tri;
+
+ pole.lat = g->o_lat;
+ pole.lon = g->o_lon;
+
+ i = isea_ctran(&pole, in, g->o_az);
+
+ tri = isea_snyder_forward(&i, out);
+ out->x *= g->radius;
+ out->y *= g->radius;
+ g->triangle = tri;
+
+ return tri;
+}
+
+#define DOWNTRI(tri) (((tri - 1) / 5) % 2 == 1)
+
+ISEA_STATIC
+void
+isea_rotate(struct isea_pt * pt, double degrees)
+{
+ double rad;
+
+ double x, y;
+
+ rad = -degrees * M_PI / 180.0;
+ while (rad >= 2.0 * M_PI) rad -= 2.0 * M_PI;
+ while (rad <= -2.0 * M_PI) rad += 2.0 * M_PI;
+
+ x = pt->x * cos(rad) + pt->y * sin(rad);
+ y = -pt->x * sin(rad) + pt->y * cos(rad);
+
+ pt->x = x;
+ pt->y = y;
+}
+
+ISEA_STATIC
+int isea_tri_plane(int tri, struct isea_pt *pt, double radius) {
+ struct isea_pt tc; /* center of triangle */
+
+ if (DOWNTRI(tri)) {
+ isea_rotate(pt, 180.0);
+ }
+ tc = isea_triangle_xy(tri);
+ tc.x *= radius;
+ tc.y *= radius;
+ pt->x += tc.x;
+ pt->y += tc.y;
+
+ return tri;
+}
+
+/* convert projected triangle coords to quad xy coords, return quad number */
+ISEA_STATIC
+int
+isea_ptdd(int tri, struct isea_pt *pt) {
+ int downtri, quad;
+
+ downtri = (((tri - 1) / 5) % 2 == 1);
+ quad = ((tri - 1) % 5) + ((tri - 1) / 10) * 5 + 1;
+
+ isea_rotate(pt, downtri ? 240.0 : 60.0);
+ if (downtri) {
+ pt->x += 0.5;
+ /* pt->y += cos(30.0 * M_PI / 180.0); */
+ pt->y += .86602540378443864672;
+ }
+ return quad;
+}
+
+ISEA_STATIC
+int
+isea_dddi_ap3odd(struct isea_dgg *g, int quad, struct isea_pt *pt, struct isea_pt *di)
+{
+ struct isea_pt v;
+ double hexwidth;
+ double sidelength; /* in hexes */
+ int d, i;
+ int maxcoord;
+ struct hex h;
+
+ /* This is the number of hexes from apex to base of a triangle */
+ sidelength = (pow(2.0, g->resolution) + 1.0) / 2.0;
+
+ /* apex to base is cos(30deg) */
+ hexwidth = cos(M_PI / 6.0) / sidelength;
+
+ /* TODO I think sidelength is always x.5, so
+ * (int)sidelength * 2 + 1 might be just as good
+ */
+ maxcoord = (int) (sidelength * 2.0 + 0.5);
+
+ v = *pt;
+ hexbin2(hexwidth, v.x, v.y, &h.x, &h.y);
+ h.iso = 0;
+ hex_iso(&h);
+
+ d = h.x - h.z;
+ i = h.x + h.y + h.y;
+
+ /*
+ * you want to test for max coords for the next quad in the same
+ * "row" first to get the case where both are max
+ */
+ if (quad <= 5) {
+ if (d == 0 && i == maxcoord) {
+ /* north pole */
+ quad = 0;
+ d = 0;
+ i = 0;
+ } else if (i == maxcoord) {
+ /* upper right in next quad */
+ quad += 1;
+ if (quad == 6)
+ quad = 1;
+ i = maxcoord - d;
+ d = 0;
+ } else if (d == maxcoord) {
+ /* lower right in quad to lower right */
+ quad += 5;
+ d = 0;
+ }
+ } else if (quad >= 6) {
+ if (i == 0 && d == maxcoord) {
+ /* south pole */
+ quad = 11;
+ d = 0;
+ i = 0;
+ } else if (d == maxcoord) {
+ /* lower right in next quad */
+ quad += 1;
+ if (quad == 11)
+ quad = 6;
+ d = maxcoord - i;
+ i = 0;
+ } else if (i == maxcoord) {
+ /* upper right in quad to upper right */
+ quad = (quad - 4) % 5;
+ i = 0;
+ }
+ }
+
+ di->x = d;
+ di->y = i;
+
+ g->quad = quad;
+ return quad;
+}
+
+ISEA_STATIC
+int
+isea_dddi(struct isea_dgg *g, int quad, struct isea_pt *pt, struct isea_pt *di) {
+ struct isea_pt v;
+ double hexwidth;
+ int sidelength; /* in hexes */
+ struct hex h;
+
+ if (g->aperture == 3 && g->resolution % 2 != 0) {
+ return isea_dddi_ap3odd(g, quad, pt, di);
+ }
+ /* todo might want to do this as an iterated loop */
+ if (g->aperture >0) {
+ sidelength = (int) (pow(g->aperture, g->resolution / 2.0) + 0.5);
+ } else {
+ sidelength = g->resolution;
+ }
+
+ hexwidth = 1.0 / sidelength;
+
+ v = *pt;
+ isea_rotate(&v, -30.0);
+ hexbin2(hexwidth, v.x, v.y, &h.x, &h.y);
+ h.iso = 0;
+ hex_iso(&h);
+
+ /* we may actually be on another quad */
+ if (quad <= 5) {
+ if (h.x == 0 && h.z == -sidelength) {
+ /* north pole */
+ quad = 0;
+ h.z = 0;
+ h.y = 0;
+ h.x = 0;
+ } else if (h.z == -sidelength) {
+ quad = quad + 1;
+ if (quad == 6)
+ quad = 1;
+ h.y = sidelength - h.x;
+ h.z = h.x - sidelength;
+ h.x = 0;
+ } else if (h.x == sidelength) {
+ quad += 5;
+ h.y = -h.z;
+ h.x = 0;
+ }
+ } else if (quad >= 6) {
+ if (h.z == 0 && h.x == sidelength) {
+ /* south pole */
+ quad = 11;
+ h.x = 0;
+ h.y = 0;
+ h.z = 0;
+ } else if (h.x == sidelength) {
+ quad = quad + 1;
+ if (quad == 11)
+ quad = 6;
+ h.x = h.y + sidelength;
+ h.y = 0;
+ h.z = -h.x;
+ } else if (h.y == -sidelength) {
+ quad -= 4;
+ h.y = 0;
+ h.z = -h.x;
+ }
+ }
+ di->x = h.x;
+ di->y = -h.z;
+
+ g->quad = quad;
+ return quad;
+}
+
+ISEA_STATIC
+int isea_ptdi(struct isea_dgg *g, int tri, struct isea_pt *pt,
+ struct isea_pt *di) {
+ struct isea_pt v;
+ int quad;
+
+ v = *pt;
+ quad = isea_ptdd(tri, &v);
+ quad = isea_dddi(g, quad, &v, di);
+ return quad;
+}
+
+/* q2di to seqnum */
+ISEA_STATIC
+int isea_disn(struct isea_dgg *g, int quad, struct isea_pt *di) {
+ int sidelength;
+ int sn, height;
+ int hexes;
+
+ if (quad == 0) {
+ g->serial = 1;
+ return g->serial;
+ }
+ /* hexes in a quad */
+ hexes = (int) (pow(g->aperture, g->resolution) + 0.5);
+ if (quad == 11) {
+ g->serial = 1 + 10 * hexes + 1;
+ return g->serial;
+ }
+ if (g->aperture == 3 && g->resolution % 2 == 1) {
+ height = (int) (pow(g->aperture, (g->resolution - 1) / 2.0));
+ sn = ((int) di->x) * height;
+ sn += ((int) di->y) / height;
+ sn += (quad - 1) * hexes;
+ sn += 2;
+ } else {
+ sidelength = (int) (pow(g->aperture, g->resolution / 2.0) + 0.5);
+ sn = (quad - 1) * hexes + sidelength * di->x + di->y + 2;
+ }
+
+ g->serial = sn;
+ return sn;
+}
+
+/* TODO just encode the quad in the d or i coordinate
+ * quad is 0-11, which can be four bits.
+ * d' = d << 4 + q, d = d' >> 4, q = d' & 0xf
+ */
+/* convert a q2di to global hex coord */
+ISEA_STATIC
+int isea_hex(struct isea_dgg *g, int tri,
+ struct isea_pt *pt, struct isea_pt *hex) {
+ struct isea_pt v;
+ int sidelength;
+ int d, i, x, y, quad;
+
+ quad = isea_ptdi(g, tri, pt, &v);
+
+ hex->x = ((int)v.x << 4) + quad;
+ hex->y = v.y;
+
+ return 1;
+
+ d = v.x;
+ i = v.y;
+
+ /* Aperture 3 odd resolutions */
+ if (g->aperture == 3 && g->resolution % 2 != 0) {
+ int offset = (int)(pow(3.0, g->resolution - 1) + 0.5);
+
+ d += offset * ((g->quad-1) % 5);
+ i += offset * ((g->quad-1) % 5);
+
+ if (quad == 0) {
+ d = 0;
+ i = offset;
+ } else if (quad == 11) {
+ d = 2 * offset;
+ i = 0;
+ } else if (quad > 5) {
+ d += offset;
+ }
+
+ x = (2*d - i) /3;
+ y = (2*i - d) /3;
+
+ hex->x = x + offset / 3;
+ hex->y = y + 2 * offset / 3;
+ return 1;
+ }
+
+ /* aperture 3 even resolutions and aperture 4 */
+ sidelength = (int) (pow(g->aperture, g->resolution / 2.0) + 0.5);
+ if (g->quad == 0) {
+ hex->x = 0;
+ hex->y = sidelength;
+ } else if (g->quad == 11) {
+ hex->x = sidelength * 2;
+ hex->y = 0;
+ } else {
+ hex->x = d + sidelength * ((g->quad-1) % 5);
+ if (g->quad > 5) hex->x += sidelength;
+ hex->y = i + sidelength * ((g->quad-1) % 5);
+ }
+
+ return 1;
+}
+
+ISEA_STATIC
+struct isea_pt
+isea_forward(struct isea_dgg *g, struct isea_geo *in)
+{
+ int tri;
+ struct isea_pt out, coord;
+
+ tri = isea_transform(g, in, &out);
+
+ if (g->output == ISEA_PLANE) {
+ isea_tri_plane(tri, &out, g->radius);
+ return out;
+ }
+
+ /* convert to isea standard triangle size */
+ out.x = out.x / g->radius * ISEA_SCALE;
+ out.y = out.y / g->radius * ISEA_SCALE;
+ out.x += 0.5;
+ out.y += 2.0 * .14433756729740644112;
+
+ switch (g->output) {
+ case ISEA_PROJTRI:
+ /* nothing to do, already in projected triangle */
+ break;
+ case ISEA_VERTEX2DD:
+ g->quad = isea_ptdd(tri, &out);
+ break;
+ case ISEA_Q2DD:
+ /* Same as above, we just don't print as much */
+ g->quad = isea_ptdd(tri, &out);
+ break;
+ case ISEA_Q2DI:
+ g->quad = isea_ptdi(g, tri, &out, &coord);
+ return coord;
+ break;
+ case ISEA_SEQNUM:
+ isea_ptdi(g, tri, &out, &coord);
+ /* disn will set g->serial */
+ isea_disn(g, g->quad, &coord);
+ return coord;
+ break;
+ case ISEA_HEX:
+ isea_hex(g, tri, &out, &coord);
+ return coord;
+ break;
+ }
+
+ return out;
+}
+/*
+ * Proj 4 integration code follows
+ */
+
+#define PJ_LIB__
+#include <projects.h>
+
+PROJ_HEAD(isea, "Icosahedral Snyder Equal Area") "\n\tSph";
+
+struct pj_opaque {
+ struct isea_dgg dgg;
+};
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = P->opaque;
+ struct isea_pt out;
+ struct isea_geo in;
+
+ in.lon = lp.lam;
+ in.lat = lp.phi;
+
+ out = isea_forward(&Q->dgg, &in);
+
+ xy.x = out.x;
+ xy.y = out.y;
+
+ return xy;
+}
+
+
+static void *freeup_new (PJ *P) { /* Destructor */
+ if (0==P)
+ return 0;
+ if (0==P->opaque)
+ return pj_dealloc (P);
+
+ pj_dealloc (P->opaque);
+ return pj_dealloc(P);
+}
+
+static void freeup (PJ *P) {
+ freeup_new (P);
+ return;
+}
+
+
+PJ *PROJECTION(isea) {
+ char *opt;
+ struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque));
+ if (0==Q)
+ return freeup_new (P);
+ P->opaque = Q;
+
+
+ P->fwd = s_forward;
+ isea_grid_init(&Q->dgg);
+
+ Q->dgg.output = ISEA_PLANE;
+/* P->dgg.radius = P->a; / * otherwise defaults to 1 */
+ /* calling library will scale, I think */
+
+ opt = pj_param(P->ctx,P->params, "sorient").s;
+ if (opt) {
+ if (!strcmp(opt, "isea")) {
+ isea_orient_isea(&Q->dgg);
+ } else if (!strcmp(opt, "pole")) {
+ isea_orient_pole(&Q->dgg);
+ } else {
+ E_ERROR(-34);
+ }
+ }
+
+ if (pj_param(P->ctx,P->params, "tazi").i) {
+ Q->dgg.o_az = pj_param(P->ctx,P->params, "razi").f;
+ }
+
+ if (pj_param(P->ctx,P->params, "tlon_0").i) {
+ Q->dgg.o_lon = pj_param(P->ctx,P->params, "rlon_0").f;
+ }
+
+ if (pj_param(P->ctx,P->params, "tlat_0").i) {
+ Q->dgg.o_lat = pj_param(P->ctx,P->params, "rlat_0").f;
+ }
+
+ if (pj_param(P->ctx,P->params, "taperture").i) {
+ Q->dgg.aperture = pj_param(P->ctx,P->params, "iaperture").i;
+ }
+
+ if (pj_param(P->ctx,P->params, "tresolution").i) {
+ Q->dgg.resolution = pj_param(P->ctx,P->params, "iresolution").i;
+ }
+
+ opt = pj_param(P->ctx,P->params, "smode").s;
+ if (opt) {
+ if (!strcmp(opt, "plane")) {
+ Q->dgg.output = ISEA_PLANE;
+ } else if (!strcmp(opt, "di")) {
+ Q->dgg.output = ISEA_Q2DI;
+ }
+ else if (!strcmp(opt, "dd")) {
+ Q->dgg.output = ISEA_Q2DD;
+ }
+ else if (!strcmp(opt, "hex")) {
+ Q->dgg.output = ISEA_HEX;
+ }
+ else {
+ /* TODO verify error code. Possibly eliminate magic */
+ E_ERROR(-34);
+ }
+ }
+
+ if (pj_param(P->ctx,P->params, "trescale").i) {
+ Q->dgg.radius = ISEA_SCALE;
+ }
+
+ if (pj_param(P->ctx,P->params, "tresolution").i) {
+ Q->dgg.resolution = pj_param(P->ctx,P->params, "iresolution").i;
+ } else {
+ Q->dgg.resolution = 4;
+ }
+
+ if (pj_param(P->ctx,P->params, "taperture").i) {
+ Q->dgg.aperture = pj_param(P->ctx,P->params, "iaperture").i;
+ } else {
+ Q->dgg.aperture = 3;
+ }
+
+ return P;
+}
+
+
+#ifdef PJ_OMIT_SELFTEST
+int pj_isea_selftest (void) {return 0;}
+#else
+
+int pj_isea_selftest (void) {
+ double tolerance_lp = 1e-10;
+ double tolerance_xy = 1e-7;
+
+ char s_args[] = {"+proj=isea +a=6400000 +lat_1=0.5 +lat_2=2"};
+
+ LP fwd_in[] = {
+ { 2, 1},
+ { 2,-1},
+ {-2, 1},
+ {-2,-1}
+ };
+
+ XY s_fwd_expect[] = {
+ {-1097074.9480224741, 3442909.3090371834},
+ {-1097074.9482647954, 3233611.7285857084},
+ {-1575486.3536415542, 3442168.3420281881},
+ {-1575486.353880283, 3234352.6955947056},
+ };
+
+ return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, 0, 0, 0);
+}
+
+
+#endif
diff --git a/src/PJ_krovak.c b/src/PJ_krovak.c index dd250134..4a9a4e53 100644 --- a/src/PJ_krovak.c +++ b/src/PJ_krovak.c @@ -29,220 +29,282 @@ * SOFTWARE. *****************************************************************************/ -#define PROJ_PARMS__ \ - double C_x; #define PJ_LIB__ - #include <projects.h> #include <string.h> #include <stdio.h> PROJ_HEAD(krovak, "Krovak") "\n\tPCyl., Ellps."; +struct pj_opaque { + double C_x; +}; + /** NOTES: According to EPSG the full Krovak projection method should have the following parameters. Within PROJ.4 the azimuth, and pseudo - standard parallel are hardcoded in the algorithm and can't be + standard parallel are hardcoded in the algorithm and can't be altered from outside. The others all have defaults to match the common usage with Krovak projection. lat_0 = latitude of centre of the projection - + lon_0 = longitude of centre of the projection - + ** = azimuth (true) of the centre line passing through the centre of the projection ** = latitude of pseudo standard parallel - + k = scale factor on the pseudo standard parallel - + x_0 = False Easting of the centre of the projection at the apex of the cone - + y_0 = False Northing of the centre of the projection at the apex of the cone **/ +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; -FORWARD(e_forward); /* ellipsoid */ -/* calculate xy from lat/lon */ + /* calculate xy from lat/lon */ -/* Constants, identical to inverse transform function */ - double s45, s90, e2, e, alfa, uq, u0, g, k, k1, n0, ro0, ad, a, s0, n; - double gfi, u, fi0, deltav, s, d, eps, ro; + /* Constants, identical to inverse transform function */ + double s45, s90, e2, e, alfa, uq, u0, g, k, k1, n0, ro0, ad, a, s0, n; + double gfi, u, fi0, deltav, s, d, eps, ro; - s45 = 0.785398163397448; /* 45deg */ - s90 = 2 * s45; - fi0 = P->phi0; /* Latitude of projection centre 49deg 30' */ + s45 = 0.785398163397448; /* 45deg */ + s90 = 2 * s45; + fi0 = P->phi0; /* Latitude of projection centre 49deg 30' */ - /* Ellipsoid is used as Parameter in for.c and inv.c, therefore a must + /* Ellipsoid is used as Parameter in for.c and inv.c, therefore a must be set to 1 here. Ellipsoid Bessel 1841 a = 6377397.155m 1/f = 299.1528128, e2=0.006674372230614; - */ - a = 1; /* 6377397.155; */ - /* e2 = P->es;*/ /* 0.006674372230614; */ - e2 = 0.006674372230614; - e = sqrt(e2); - - alfa = sqrt(1. + (e2 * pow(cos(fi0), 4)) / (1. - e2)); + */ + a = 1; /* 6377397.155; */ + /* e2 = P->es;*/ /* 0.006674372230614; */ + e2 = 0.006674372230614; + e = sqrt(e2); - uq = 1.04216856380474; /* DU(2, 59, 42, 42.69689) */ - u0 = asin(sin(fi0) / alfa); - g = pow( (1. + e * sin(fi0)) / (1. - e * sin(fi0)) , alfa * e / 2. ); + alfa = sqrt(1. + (e2 * pow(cos(fi0), 4)) / (1. - e2)); - k = tan( u0 / 2. + s45) / pow (tan(fi0 / 2. + s45) , alfa) * g; + uq = 1.04216856380474; /* DU(2, 59, 42, 42.69689) */ + u0 = asin(sin(fi0) / alfa); + g = pow( (1. + e * sin(fi0)) / (1. - e * sin(fi0)) , alfa * e / 2. ); - k1 = P->k0; - n0 = a * sqrt(1. - e2) / (1. - e2 * pow(sin(fi0), 2)); - s0 = 1.37008346281555; /* Latitude of pseudo standard parallel 78deg 30'00" N */ - n = sin(s0); - ro0 = k1 * n0 / tan(s0); - ad = s90 - uq; + k = tan( u0 / 2. + s45) / pow (tan(fi0 / 2. + s45) , alfa) * g; -/* Transformation */ + k1 = P->k0; + n0 = a * sqrt(1. - e2) / (1. - e2 * pow(sin(fi0), 2)); + s0 = 1.37008346281555; /* Latitude of pseudo standard parallel 78deg 30'00" N */ + n = sin(s0); + ro0 = k1 * n0 / tan(s0); + ad = s90 - uq; - gfi =pow ( ((1. + e * sin(lp.phi)) / - (1. - e * sin(lp.phi))) , (alfa * e / 2.)); + /* Transformation */ + gfi = pow ( ((1. + e * sin(lp.phi)) / + (1. - e * sin(lp.phi))) , (alfa * e / 2.)); - u= 2. * (atan(k * pow( tan(lp.phi / 2. + s45), alfa) / gfi)-s45); + u = 2. * (atan(k * pow( tan(lp.phi / 2. + s45), alfa) / gfi)-s45); - deltav = - lp.lam * alfa; + deltav = - lp.lam * alfa; - s = asin(cos(ad) * sin(u) + sin(ad) * cos(u) * cos(deltav)); - d = asin(cos(u) * sin(deltav) / cos(s)); - eps = n * d; - ro = ro0 * pow(tan(s0 / 2. + s45) , n) / pow(tan(s / 2. + s45) , n) ; + s = asin(cos(ad) * sin(u) + sin(ad) * cos(u) * cos(deltav)); + d = asin(cos(u) * sin(deltav) / cos(s)); + eps = n * d; + ro = ro0 * pow(tan(s0 / 2. + s45) , n) / pow(tan(s / 2. + s45) , n); /* x and y are reverted! */ - xy.y = ro * cos(eps) / a; - xy.x = ro * sin(eps) / a; + xy.y = ro * cos(eps) / a; + xy.x = ro * sin(eps) / a; - if( !pj_param(P->ctx, P->params, "tczech").i ) - { - xy.y *= -1.0; - xy.x *= -1.0; - } + if( !pj_param(P->ctx, P->params, "tczech").i ) { + xy.y *= -1.0; + xy.x *= -1.0; + } - return (xy); + return xy; } +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; -INVERSE(e_inverse); /* ellipsoid */ - /* calculate lat/lon from xy */ + /* calculate lat/lon from xy */ -/* Constants, identisch wie in der Umkehrfunktion */ - double s45, s90, fi0, e2, e, alfa, uq, u0, g, k, k1, n0, ro0, ad, a, s0, n; - double u, deltav, s, d, eps, ro, fi1, xy0; - int ok; + /* Constants, identisch wie in der Umkehrfunktion */ + double s45, s90, fi0, e2, e, alfa, uq, u0, g, k, k1, n0, ro0, ad, a, s0, n; + double u, deltav, s, d, eps, ro, fi1, xy0; + int ok; - s45 = 0.785398163397448; /* 45deg */ - s90 = 2 * s45; - fi0 = P->phi0; /* Latitude of projection centre 49deg 30' */ + s45 = 0.785398163397448; /* 45deg */ + s90 = 2 * s45; + fi0 = P->phi0; /* Latitude of projection centre 49deg 30' */ - /* Ellipsoid is used as Parameter in for.c and inv.c, therefore a must + /* Ellipsoid is used as Parameter in for.c and inv.c, therefore a must be set to 1 here. Ellipsoid Bessel 1841 a = 6377397.155m 1/f = 299.1528128, e2=0.006674372230614; */ - a = 1; /* 6377397.155; */ - /* e2 = P->es; */ /* 0.006674372230614; */ - e2 = 0.006674372230614; - e = sqrt(e2); + a = 1; /* 6377397.155; */ + /* e2 = P->es; */ /* 0.006674372230614; */ + e2 = 0.006674372230614; + e = sqrt(e2); - alfa = sqrt(1. + (e2 * pow(cos(fi0), 4)) / (1. - e2)); - uq = 1.04216856380474; /* DU(2, 59, 42, 42.69689) */ - u0 = asin(sin(fi0) / alfa); - g = pow( (1. + e * sin(fi0)) / (1. - e * sin(fi0)) , alfa * e / 2. ); + alfa = sqrt(1. + (e2 * pow(cos(fi0), 4)) / (1. - e2)); + uq = 1.04216856380474; /* DU(2, 59, 42, 42.69689) */ + u0 = asin(sin(fi0) / alfa); + g = pow( (1. + e * sin(fi0)) / (1. - e * sin(fi0)) , alfa * e / 2. ); - k = tan( u0 / 2. + s45) / pow (tan(fi0 / 2. + s45) , alfa) * g; + k = tan( u0 / 2. + s45) / pow (tan(fi0 / 2. + s45) , alfa) * g; - k1 = P->k0; - n0 = a * sqrt(1. - e2) / (1. - e2 * pow(sin(fi0), 2)); - s0 = 1.37008346281555; /* Latitude of pseudo standard parallel 78deg 30'00" N */ - n = sin(s0); - ro0 = k1 * n0 / tan(s0); - ad = s90 - uq; + k1 = P->k0; + n0 = a * sqrt(1. - e2) / (1. - e2 * pow(sin(fi0), 2)); + s0 = 1.37008346281555; /* Latitude of pseudo standard parallel 78deg 30'00" N */ + n = sin(s0); + ro0 = k1 * n0 / tan(s0); + ad = s90 - uq; -/* Transformation */ + /* Transformation */ /* revert y, x*/ - xy0=xy.x; - xy.x=xy.y; - xy.y=xy0; + xy0 = xy.x; + xy.x = xy.y; + xy.y = xy0; - if( !pj_param(P->ctx, P->params, "tczech").i ) - { - xy.x *= -1.0; - xy.y *= -1.0; - } + if( !pj_param(P->ctx, P->params, "tczech").i ) { + xy.x *= -1.0; + xy.y *= -1.0; + } - ro = sqrt(xy.x * xy.x + xy.y * xy.y); - eps = atan2(xy.y, xy.x); - d = eps / sin(s0); - s = 2. * (atan( pow(ro0 / ro, 1. / n) * tan(s0 / 2. + s45)) - s45); + ro = sqrt(xy.x * xy.x + xy.y * xy.y); + eps = atan2(xy.y, xy.x); + d = eps / sin(s0); + s = 2. * (atan( pow(ro0 / ro, 1. / n) * tan(s0 / 2. + s45)) - s45); - u = asin(cos(ad) * sin(s) - sin(ad) * cos(s) * cos(d)); - deltav = asin(cos(s) * sin(d) / cos(u)); + u = asin(cos(ad) * sin(s) - sin(ad) * cos(s) * cos(d)); + deltav = asin(cos(s) * sin(d) / cos(u)); - lp.lam = P->lam0 - deltav / alfa; + lp.lam = P->lam0 - deltav / alfa; -/* ITERATION FOR lp.phi */ - fi1 = u; + /* ITERATION FOR lp.phi */ + fi1 = u; - ok = 0; - do - { - lp.phi = 2. * ( atan( pow( k, -1. / alfa) * - pow( tan(u / 2. + s45) , 1. / alfa) * - pow( (1. + e * sin(fi1)) / (1. - e * sin(fi1)) , e / 2.) - ) - s45); + ok = 0; + do { + lp.phi = 2. * ( atan( pow( k, -1. / alfa) * + pow( tan(u / 2. + s45) , 1. / alfa) * + pow( (1. + e * sin(fi1)) / (1. - e * sin(fi1)) , e / 2.) + ) - s45); - if (fabs(fi1 - lp.phi) < 0.000000000000001) ok=1; - fi1 = lp.phi; - - } - while (ok==0); + if (fabs(fi1 - lp.phi) < 0.000000000000001) ok=1; + fi1 = lp.phi; + } while (ok==0); lp.lam -= P->lam0; - return (lp); + return lp; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(krovak) - double ts; - /* read some Parameters, - * here Latitude Truescale */ +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); - ts = pj_param(P->ctx, P->params, "rlat_ts").f; - P->C_x = ts; - - /* we want Bessel as fixed ellipsoid */ - P->a = 6377397.155; - P->e = sqrt(P->es = 0.006674372230614); + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(krovak) { + double ts; + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + /* read some Parameters, + * here Latitude Truescale */ + + ts = pj_param(P->ctx, P->params, "rlat_ts").f; + Q->C_x = ts; + + /* we want Bessel as fixed ellipsoid */ + P->a = 6377397.155; + P->e = sqrt(P->es = 0.006674372230614); /* if latitude of projection center is not set, use 49d30'N */ - if (!pj_param(P->ctx, P->params, "tlat_0").i) - P->phi0 = 0.863937979737193; + if (!pj_param(P->ctx, P->params, "tlat_0").i) + P->phi0 = 0.863937979737193; /* if center long is not set use 42d30'E of Ferro - 17d40' for Ferro */ /* that will correspond to using longitudes relative to greenwich */ /* as input and output, instead of lat/long relative to Ferro */ - if (!pj_param(P->ctx, P->params, "tlon_0").i) + if (!pj_param(P->ctx, P->params, "tlon_0").i) P->lam0 = 0.7417649320975901 - 0.308341501185665; /* if scale not set default to 0.9999 */ - if (!pj_param(P->ctx, P->params, "tk").i) + if (!pj_param(P->ctx, P->params, "tk").i) P->k0 = 0.9999; - /* always the same */ - P->inv = e_inverse; - P->fwd = e_forward; + /* always the same */ + P->inv = e_inverse; + P->fwd = e_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_krovak_selftest (void) {return 0;} +#else + +int pj_krovak_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=krovak +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {-3196535.2325636409, -6617878.8675514441}, + {-3260035.4405521089, -6898873.6148780314}, + {-3756305.3288691747, -6478142.5615715114}, + {-3831703.6585019818, -6759107.1701553948}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {24.836218918719162, 59.758403933233858}, + {24.836315484509566, 59.756888425730189}, + {24.830447747947495, 59.758403933233858}, + {24.830351182157091, 59.756888425730189}, + }; + + return pj_generic_selftest (e_args, 0, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, 0, inv_in, e_inv_expect, 0); +} -ENDENTRY(P) +#endif diff --git a/src/PJ_labrd.c b/src/PJ_labrd.c index 4cb39ec8..4c435731 100644 --- a/src/PJ_labrd.c +++ b/src/PJ_labrd.c @@ -1,109 +1,186 @@ -#define PROJ_PARMS__ \ - double Az, kRg, p0s, A, C, Ca, Cb, Cc, Cd; \ - int rot; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(labrd, "Laborde") "\n\tCyl, Sph\n\tSpecial for Madagascar"; -#define EPS 1.e-10 -FORWARD(e_forward); - double V1, V2, ps, sinps, cosps, sinps2, cosps2, I1, I2, I3, I4, I5, I6, - x2, y2, t; - - V1 = P->A * log( tan(FORTPI + .5 * lp.phi) ); - t = P->e * sin(lp.phi); - V2 = .5 * P->e * P->A * log ((1. + t)/(1. - t)); - ps = 2. * (atan(exp(V1 - V2 + P->C)) - FORTPI); - I1 = ps - P->p0s; - cosps = cos(ps); cosps2 = cosps * cosps; - sinps = sin(ps); sinps2 = sinps * sinps; - I4 = P->A * cosps; - I2 = .5 * P->A * I4 * sinps; - I3 = I2 * P->A * P->A * (5. * cosps2 - sinps2) / 12.; - I6 = I4 * P->A * P->A; - I5 = I6 * (cosps2 - sinps2) / 6.; - I6 *= P->A * P->A * - (5. * cosps2 * cosps2 + sinps2 * (sinps2 - 18. * cosps2)) / 120.; - t = lp.lam * lp.lam; - xy.x = P->kRg * lp.lam * (I4 + t * (I5 + t * I6)); - xy.y = P->kRg * (I1 + t * (I2 + t * I3)); - x2 = xy.x * xy.x; - y2 = xy.y * xy.y; - V1 = 3. * xy.x * y2 - xy.x * x2; - V2 = xy.y * y2 - 3. * x2 * xy.y; - xy.x += P->Ca * V1 + P->Cb * V2; - xy.y += P->Ca * V2 - P->Cb * V1; - return (xy); +#define EPS 1.e-10 + +struct pj_opaque { + double kRg, p0s, A, C, Ca, Cb, Cc, Cd; \ + int rot; +}; + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double V1, V2, ps, sinps, cosps, sinps2, cosps2; + double I1, I2, I3, I4, I5, I6, x2, y2, t; + + V1 = Q->A * log( tan(FORTPI + .5 * lp.phi) ); + t = P->e * sin(lp.phi); + V2 = .5 * P->e * Q->A * log ((1. + t)/(1. - t)); + ps = 2. * (atan(exp(V1 - V2 + Q->C)) - FORTPI); + I1 = ps - Q->p0s; + cosps = cos(ps); cosps2 = cosps * cosps; + sinps = sin(ps); sinps2 = sinps * sinps; + I4 = Q->A * cosps; + I2 = .5 * Q->A * I4 * sinps; + I3 = I2 * Q->A * Q->A * (5. * cosps2 - sinps2) / 12.; + I6 = I4 * Q->A * Q->A; + I5 = I6 * (cosps2 - sinps2) / 6.; + I6 *= Q->A * Q->A * + (5. * cosps2 * cosps2 + sinps2 * (sinps2 - 18. * cosps2)) / 120.; + t = lp.lam * lp.lam; + xy.x = Q->kRg * lp.lam * (I4 + t * (I5 + t * I6)); + xy.y = Q->kRg * (I1 + t * (I2 + t * I3)); + x2 = xy.x * xy.x; + y2 = xy.y * xy.y; + V1 = 3. * xy.x * y2 - xy.x * x2; + V2 = xy.y * y2 - 3. * x2 * xy.y; + xy.x += Q->Ca * V1 + Q->Cb * V2; + xy.y += Q->Ca * V2 - Q->Cb * V1; + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double x2, y2, V1, V2, V3, V4, t, t2, ps, pe, tpe, s; + double I7, I8, I9, I10, I11, d, Re; + int i; + + x2 = xy.x * xy.x; + y2 = xy.y * xy.y; + V1 = 3. * xy.x * y2 - xy.x * x2; + V2 = xy.y * y2 - 3. * x2 * xy.y; + V3 = xy.x * (5. * y2 * y2 + x2 * (-10. * y2 + x2 )); + V4 = xy.y * (5. * x2 * x2 + y2 * (-10. * x2 + y2 )); + xy.x += - Q->Ca * V1 - Q->Cb * V2 + Q->Cc * V3 + Q->Cd * V4; + xy.y += Q->Cb * V1 - Q->Ca * V2 - Q->Cd * V3 + Q->Cc * V4; + ps = Q->p0s + xy.y / Q->kRg; + pe = ps + P->phi0 - Q->p0s; + for ( i = 20; i; --i) { + V1 = Q->A * log(tan(FORTPI + .5 * pe)); + tpe = P->e * sin(pe); + V2 = .5 * P->e * Q->A * log((1. + tpe)/(1. - tpe)); + t = ps - 2. * (atan(exp(V1 - V2 + Q->C)) - FORTPI); + pe += t; + if (fabs(t) < EPS) + break; + } + + t = P->e * sin(pe); + t = 1. - t * t; + Re = P->one_es / ( t * sqrt(t) ); + t = tan(ps); + t2 = t * t; + s = Q->kRg * Q->kRg; + d = Re * P->k0 * Q->kRg; + I7 = t / (2. * d); + I8 = t * (5. + 3. * t2) / (24. * d * s); + d = cos(ps) * Q->kRg * Q->A; + I9 = 1. / d; + d *= s; + I10 = (1. + 2. * t2) / (6. * d); + I11 = (5. + t2 * (28. + 24. * t2)) / (120. * d * s); + x2 = xy.x * xy.x; + lp.phi = pe + x2 * (-I7 + I8 * x2); + lp.lam = xy.x * (I9 + x2 * (-I10 + x2 * I11)); + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; } -INVERSE(e_inverse); /* ellipsoid & spheroid */ - double x2, y2, V1, V2, V3, V4, t, t2, ps, pe, tpe, s, - I7, I8, I9, I10, I11, d, Re; - int i; - - x2 = xy.x * xy.x; - y2 = xy.y * xy.y; - V1 = 3. * xy.x * y2 - xy.x * x2; - V2 = xy.y * y2 - 3. * x2 * xy.y; - V3 = xy.x * (5. * y2 * y2 + x2 * (-10. * y2 + x2 )); - V4 = xy.y * (5. * x2 * x2 + y2 * (-10. * x2 + y2 )); - xy.x += - P->Ca * V1 - P->Cb * V2 + P->Cc * V3 + P->Cd * V4; - xy.y += P->Cb * V1 - P->Ca * V2 - P->Cd * V3 + P->Cc * V4; - ps = P->p0s + xy.y / P->kRg; - pe = ps + P->phi0 - P->p0s; - for ( i = 20; i; --i) { - V1 = P->A * log(tan(FORTPI + .5 * pe)); - tpe = P->e * sin(pe); - V2 = .5 * P->e * P->A * log((1. + tpe)/(1. - tpe)); - t = ps - 2. * (atan(exp(V1 - V2 + P->C)) - FORTPI); - pe += t; - if (fabs(t) < EPS) - break; - } -/* - if (!i) { - } else { - } -*/ - t = P->e * sin(pe); - t = 1. - t * t; - Re = P->one_es / ( t * sqrt(t) ); - t = tan(ps); - t2 = t * t; - s = P->kRg * P->kRg; - d = Re * P->k0 * P->kRg; - I7 = t / (2. * d); - I8 = t * (5. + 3. * t2) / (24. * d * s); - d = cos(ps) * P->kRg * P->A; - I9 = 1. / d; - d *= s; - I10 = (1. + 2. * t2) / (6. * d); - I11 = (5. + t2 * (28. + 24. * t2)) / (120. * d * s); - x2 = xy.x * xy.x; - lp.phi = pe + x2 * (-I7 + I8 * x2); - lp.lam = xy.x * (I9 + x2 * (-I10 + x2 * I11)); - return (lp); + + +PJ *PROJECTION(labrd) { + double Az, sinp, R, N, t; + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->rot = pj_param(P->ctx, P->params, "bno_rot").i == 0; + Az = pj_param(P->ctx, P->params, "razi").f; + sinp = sin(P->phi0); + t = 1. - P->es * sinp * sinp; + N = 1. / sqrt(t); + R = P->one_es * N / t; + Q->kRg = P->k0 * sqrt( N * R ); + Q->p0s = atan( sqrt(R / N) * tan(P->phi0) ); + Q->A = sinp / sin(Q->p0s); + t = P->e * sinp; + Q->C = .5 * P->e * Q->A * log((1. + t)/(1. - t)) + + - Q->A * log( tan(FORTPI + .5 * P->phi0)) + + log( tan(FORTPI + .5 * Q->p0s)); + t = Az + Az; + Q->Ca = (1. - cos(t)) * ( Q->Cb = 1. / (12. * Q->kRg * Q->kRg) ); + Q->Cb *= sin(t); + Q->Cc = 3. * (Q->Ca * Q->Ca - Q->Cb * Q->Cb); + Q->Cd = 6. * Q->Ca * Q->Cb; + + P->inv = e_inverse; + P->fwd = e_forward; + + return P; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(labrd) - double Az, sinp, R, N, t; - - P->rot = pj_param(P->ctx, P->params, "bno_rot").i == 0; - Az = pj_param(P->ctx, P->params, "razi").f; - sinp = sin(P->phi0); - t = 1. - P->es * sinp * sinp; - N = 1. / sqrt(t); - R = P->one_es * N / t; - P->kRg = P->k0 * sqrt( N * R ); - P->p0s = atan( sqrt(R / N) * tan(P->phi0) ); - P->A = sinp / sin(P->p0s); - t = P->e * sinp; - P->C = .5 * P->e * P->A * log((1. + t)/(1. - t)) + - - P->A * log( tan(FORTPI + .5 * P->phi0)) - + log( tan(FORTPI + .5 * P->p0s)); - t = Az + Az; - P->Ca = (1. - cos(t)) * ( P->Cb = 1. / (12. * P->kRg * P->kRg) ); - P->Cb *= sin(t); - P->Cc = 3. * (P->Ca * P->Ca - P->Cb * P->Cb); - P->Cd = 6. * P->Ca * P->Cb; - P->inv = e_inverse; - P->fwd = e_forward; -ENDENTRY(P) + + +#ifdef PJ_OMIT_SELFTEST +int pj_labrd_selftest (void) {return 0;} +#else + +int pj_labrd_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=labrd +ellps=GRS80 +lon_0=0.5 +lat_0=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 166973.166090228391, -110536.912730266107}, + { 166973.168287157256, -331761.993650884193}, + {-278345.500519976194, -110469.032642031714}, + {-278345.504185269645, -331829.870790275279}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {0.501797719349373672, 2.00090435742047923}, + {0.501797717380853658, 1.99909564058898681}, + {0.498202280650626328, 2.00090435742047923}, + {0.498202282619146342, 1.99909564058898681}, + }; + + return pj_generic_selftest (e_args, 0, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, 0, inv_in, e_inv_expect, 0); +} + + +#endif diff --git a/src/PJ_laea.c b/src/PJ_laea.c index 445e39c3..8393ebd0 100644 --- a/src/PJ_laea.c +++ b/src/PJ_laea.c @@ -1,233 +1,338 @@ -#define PROJ_PARMS__ \ - double sinb1; \ - double cosb1; \ - double xmf; \ - double ymf; \ - double mmf; \ - double qp; \ - double dd; \ - double rq; \ - double *apa; \ - int mode; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(laea, "Lambert Azimuthal Equal Area") "\n\tAzi, Sph&Ell"; -#define sinph0 P->sinb1 -#define cosph0 P->cosb1 -#define EPS10 1.e-10 -#define NITER 20 -#define CONV 1.e-10 -#define N_POLE 0 -#define S_POLE 1 -#define EQUIT 2 -#define OBLIQ 3 -FORWARD(e_forward); /* ellipsoid */ - double coslam, sinlam, sinphi, q, sinb=0.0, cosb=0.0, b=0.0; - - coslam = cos(lp.lam); - sinlam = sin(lp.lam); - sinphi = sin(lp.phi); - q = pj_qsfn(sinphi, P->e, P->one_es); - if (P->mode == OBLIQ || P->mode == EQUIT) { - sinb = q / P->qp; - cosb = sqrt(1. - sinb * sinb); - } - switch (P->mode) { - case OBLIQ: - b = 1. + P->sinb1 * sinb + P->cosb1 * cosb * coslam; - break; - case EQUIT: - b = 1. + cosb * coslam; - break; - case N_POLE: - b = HALFPI + lp.phi; - q = P->qp - q; - break; - case S_POLE: - b = lp.phi - HALFPI; - q = P->qp + q; - break; - } - if (fabs(b) < EPS10) F_ERROR; - switch (P->mode) { - case OBLIQ: - xy.y = P->ymf * ( b = sqrt(2. / b) ) - * (P->cosb1 * sinb - P->sinb1 * cosb * coslam); - goto eqcon; - break; - case EQUIT: - xy.y = (b = sqrt(2. / (1. + cosb * coslam))) * sinb * P->ymf; + +struct pj_opaque { + double sinb1; + double cosb1; + double xmf; + double ymf; + double mmf; + double qp; + double dd; + double rq; + double *apa; + int mode; +}; + +#define EPS10 1.e-10 +#define NITER 20 +#define CONV 1.e-10 +#define N_POLE 0 +#define S_POLE 1 +#define EQUIT 2 +#define OBLIQ 3 + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double coslam, sinlam, sinphi, q, sinb=0.0, cosb=0.0, b=0.0; + + coslam = cos(lp.lam); + sinlam = sin(lp.lam); + sinphi = sin(lp.phi); + q = pj_qsfn(sinphi, P->e, P->one_es); + + if (Q->mode == OBLIQ || Q->mode == EQUIT) { + sinb = q / Q->qp; + cosb = sqrt(1. - sinb * sinb); + } + + switch (Q->mode) { + case OBLIQ: + b = 1. + Q->sinb1 * sinb + Q->cosb1 * cosb * coslam; + break; + case EQUIT: + b = 1. + cosb * coslam; + break; + case N_POLE: + b = HALFPI + lp.phi; + q = Q->qp - q; + break; + case S_POLE: + b = lp.phi - HALFPI; + q = Q->qp + q; + break; + } + if (fabs(b) < EPS10) F_ERROR; + + switch (Q->mode) { + case OBLIQ: + b = sqrt(2. / b); + xy.y = Q->ymf * b * (Q->cosb1 * sinb - Q->sinb1 * cosb * coslam); + goto eqcon; + break; + case EQUIT: + b = sqrt(2. / (1. + cosb * coslam)); + xy.y = b * sinb * Q->ymf; eqcon: - xy.x = P->xmf * b * cosb * sinlam; - break; - case N_POLE: - case S_POLE: - if (q >= 0.) { - xy.x = (b = sqrt(q)) * sinlam; - xy.y = coslam * (P->mode == S_POLE ? b : -b); - } else - xy.x = xy.y = 0.; - break; - } - return (xy); + xy.x = Q->xmf * b * cosb * sinlam; + break; + case N_POLE: + case S_POLE: + if (q >= 0.) { + b = sqrt(q); + xy.x = b * sinlam; + xy.y = coslam * (Q->mode == S_POLE ? b : -b); + } else + xy.x = xy.y = 0.; + break; + } + return xy; } -FORWARD(s_forward); /* spheroid */ - double coslam, cosphi, sinphi; - - sinphi = sin(lp.phi); - cosphi = cos(lp.phi); - coslam = cos(lp.lam); - switch (P->mode) { - case EQUIT: - xy.y = 1. + cosphi * coslam; - goto oblcon; - case OBLIQ: - xy.y = 1. + sinph0 * sinphi + cosph0 * cosphi * coslam; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double coslam, cosphi, sinphi; + + sinphi = sin(lp.phi); + cosphi = cos(lp.phi); + coslam = cos(lp.lam); + switch (Q->mode) { + case EQUIT: + xy.y = 1. + cosphi * coslam; + goto oblcon; + case OBLIQ: + xy.y = 1. + Q->sinb1 * sinphi + Q->cosb1 * cosphi * coslam; oblcon: - if (xy.y <= EPS10) F_ERROR; - xy.x = (xy.y = sqrt(2. / xy.y)) * cosphi * sin(lp.lam); - xy.y *= P->mode == EQUIT ? sinphi : - cosph0 * sinphi - sinph0 * cosphi * coslam; - break; - case N_POLE: - coslam = -coslam; - case S_POLE: - if (fabs(lp.phi + P->phi0) < EPS10) F_ERROR; - xy.y = FORTPI - lp.phi * .5; - xy.y = 2. * (P->mode == S_POLE ? cos(xy.y) : sin(xy.y)); - xy.x = xy.y * sin(lp.lam); - xy.y *= coslam; - break; - } - return (xy); + if (xy.y <= EPS10) F_ERROR; + xy.y = sqrt(2. / xy.y); + xy.x = xy.y * cosphi * sin(lp.lam); + xy.y *= Q->mode == EQUIT ? sinphi : + Q->cosb1 * sinphi - Q->sinb1 * cosphi * coslam; + break; + case N_POLE: + coslam = -coslam; + case S_POLE: + if (fabs(lp.phi + P->phi0) < EPS10) F_ERROR; + xy.y = FORTPI - lp.phi * .5; + xy.y = 2. * (Q->mode == S_POLE ? cos(xy.y) : sin(xy.y)); + xy.x = xy.y * sin(lp.lam); + xy.y *= coslam; + break; + } + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double cCe, sCe, q, rho, ab=0.0; + + switch (Q->mode) { + case EQUIT: + case OBLIQ: + xy.x /= Q->dd; + xy.y *= Q->dd; + rho = hypot(xy.x, xy.y); + if (rho < EPS10) { + lp.lam = 0.; + lp.phi = P->phi0; + return lp; + } + sCe = 2. * asin(.5 * rho / Q->rq); + cCe = cos(sCe); + sCe = sin(sCe); + xy.x *= sCe; + if (Q->mode == OBLIQ) { + ab = cCe * Q->sinb1 + xy.y * sCe * Q->cosb1 / rho; + xy.y = rho * Q->cosb1 * cCe - xy.y * Q->sinb1 * sCe; + } else { + ab = xy.y * sCe / rho; + xy.y = rho * cCe; + } + break; + case N_POLE: + xy.y = -xy.y; + case S_POLE: + q = (xy.x * xy.x + xy.y * xy.y); + if (!q) { + lp.lam = 0.; + lp.phi = P->phi0; + return (lp); + } + ab = 1. - q / Q->qp; + if (Q->mode == S_POLE) + ab = - ab; + break; + } + lp.lam = atan2(xy.x, xy.y); + lp.phi = pj_authlat(asin(ab), Q->apa); + return lp; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double cosz=0.0, rh, sinz=0.0; + + rh = hypot(xy.x, xy.y); + if ((lp.phi = rh * .5 ) > 1.) I_ERROR; + lp.phi = 2. * asin(lp.phi); + if (Q->mode == OBLIQ || Q->mode == EQUIT) { + sinz = sin(lp.phi); + cosz = cos(lp.phi); + } + switch (Q->mode) { + case EQUIT: + lp.phi = fabs(rh) <= EPS10 ? 0. : asin(xy.y * sinz / rh); + xy.x *= sinz; + xy.y = cosz * rh; + break; + case OBLIQ: + lp.phi = fabs(rh) <= EPS10 ? P->phi0 : + asin(cosz * Q->sinb1 + xy.y * sinz * Q->cosb1 / rh); + xy.x *= sinz * Q->cosb1; + xy.y = (cosz - sin(lp.phi) * Q->sinb1) * rh; + break; + case N_POLE: + xy.y = -xy.y; + lp.phi = HALFPI - lp.phi; + break; + case S_POLE: + lp.phi -= HALFPI; + break; + } + lp.lam = (xy.y == 0. && (Q->mode == EQUIT || Q->mode == OBLIQ)) ? + 0. : atan2(xy.x, xy.y); + return (lp); +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque->apa); + pj_dealloc (P->opaque); + return pj_dealloc(P); } -INVERSE(e_inverse); /* ellipsoid */ - double cCe, sCe, q, rho, ab=0.0; - - switch (P->mode) { - case EQUIT: - case OBLIQ: - if ((rho = hypot(xy.x /= P->dd, xy.y *= P->dd)) < EPS10) { - lp.lam = 0.; - lp.phi = P->phi0; - return (lp); - } - cCe = cos(sCe = 2. * asin(.5 * rho / P->rq)); - xy.x *= (sCe = sin(sCe)); - if (P->mode == OBLIQ) { - ab = cCe * P->sinb1 + xy.y * sCe * P->cosb1 / rho; - xy.y = rho * P->cosb1 * cCe - xy.y * P->sinb1 * sCe; - } else { - ab = xy.y * sCe / rho; - xy.y = rho * cCe; - } - break; - case N_POLE: - xy.y = -xy.y; - case S_POLE: - if (!(q = (xy.x * xy.x + xy.y * xy.y)) ) { - lp.lam = 0.; - lp.phi = P->phi0; - return (lp); - } - /* - q = P->qp - q; - */ - ab = 1. - q / P->qp; - if (P->mode == S_POLE) - ab = - ab; - break; - } - lp.lam = atan2(xy.x, xy.y); - lp.phi = pj_authlat(asin(ab), P->apa); - return (lp); + +static void freeup (PJ *P) { + freeup_new (P); + return; } -INVERSE(s_inverse); /* spheroid */ - double cosz=0.0, rh, sinz=0.0; - - rh = hypot(xy.x, xy.y); - if ((lp.phi = rh * .5 ) > 1.) I_ERROR; - lp.phi = 2. * asin(lp.phi); - if (P->mode == OBLIQ || P->mode == EQUIT) { - sinz = sin(lp.phi); - cosz = cos(lp.phi); - } - switch (P->mode) { - case EQUIT: - lp.phi = fabs(rh) <= EPS10 ? 0. : asin(xy.y * sinz / rh); - xy.x *= sinz; - xy.y = cosz * rh; - break; - case OBLIQ: - lp.phi = fabs(rh) <= EPS10 ? P->phi0 : - asin(cosz * sinph0 + xy.y * sinz * cosph0 / rh); - xy.x *= sinz * cosph0; - xy.y = (cosz - sin(lp.phi) * sinph0) * rh; - break; - case N_POLE: - xy.y = -xy.y; - lp.phi = HALFPI - lp.phi; - break; - case S_POLE: - lp.phi -= HALFPI; - break; - } - lp.lam = (xy.y == 0. && (P->mode == EQUIT || P->mode == OBLIQ)) ? - 0. : atan2(xy.x, xy.y); - return (lp); + + +PJ *PROJECTION(laea) { + double t; + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + t = fabs(P->phi0); + if (fabs(t - HALFPI) < EPS10) + Q->mode = P->phi0 < 0. ? S_POLE : N_POLE; + else if (fabs(t) < EPS10) + Q->mode = EQUIT; + else + Q->mode = OBLIQ; + if (P->es) { + double sinphi; + + P->e = sqrt(P->es); + Q->qp = pj_qsfn(1., P->e, P->one_es); + Q->mmf = .5 / (1. - P->es); + Q->apa = pj_authset(P->es); + switch (Q->mode) { + case N_POLE: + case S_POLE: + Q->dd = 1.; + break; + case EQUIT: + Q->dd = 1. / (Q->rq = sqrt(.5 * Q->qp)); + Q->xmf = 1.; + Q->ymf = .5 * Q->qp; + break; + case OBLIQ: + Q->rq = sqrt(.5 * Q->qp); + sinphi = sin(P->phi0); + Q->sinb1 = pj_qsfn(sinphi, P->e, P->one_es) / Q->qp; + Q->cosb1 = sqrt(1. - Q->sinb1 * Q->sinb1); + Q->dd = cos(P->phi0) / (sqrt(1. - P->es * sinphi * sinphi) * + Q->rq * Q->cosb1); + Q->ymf = (Q->xmf = Q->rq) / Q->dd; + Q->xmf *= Q->dd; + break; + } + P->inv = e_inverse; + P->fwd = e_forward; + } else { + if (Q->mode == OBLIQ) { + Q->sinb1 = sin(P->phi0); + Q->cosb1 = cos(P->phi0); + } + P->inv = s_inverse; + P->fwd = s_forward; + } + + return P; } -FREEUP; - if (P) { - if (P->apa) - pj_dalloc(P->apa); - pj_dalloc(P); - } + + +#ifdef PJ_OMIT_SELFTEST +int pj_laea_selftest (void) {return 0;} +#else + +int pj_laea_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=laea +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + char s_args[] = {"+proj=laea +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 222602.471450095181, 110589.82722441027}, + { 222602.471450095181, -110589.827224408786}, + {-222602.471450095181, 110589.82722441027}, + {-222602.471450095181, -110589.827224408786}, + }; + + XY s_fwd_expect[] = { + { 223365.281370124663, 111716.668072915665}, + { 223365.281370124663, -111716.668072915665}, + {-223365.281370124663, 111716.668072915665}, + {-223365.281370124663, -111716.668072915665}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + { 0.00179663056847900867, 0.000904369475966495845}, + { 0.00179663056847900867, -0.000904369475966495845}, + {-0.00179663056847900867, 0.000904369475966495845}, + {-0.00179663056847900867, -0.000904369475966495845}, + }; + + LP s_inv_expect[] = { + { 0.00179049311002060264, 0.000895246554791735271}, + { 0.00179049311002060264, -0.000895246554791735271}, + {-0.00179049311002060264, 0.000895246554791735271}, + {-0.00179049311002060264, -0.000895246554791735271}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); } -ENTRY1(laea,apa) - double t; - - if (fabs((t = fabs(P->phi0)) - HALFPI) < EPS10) - P->mode = P->phi0 < 0. ? S_POLE : N_POLE; - else if (fabs(t) < EPS10) - P->mode = EQUIT; - else - P->mode = OBLIQ; - if (P->es) { - double sinphi; - - P->e = sqrt(P->es); - P->qp = pj_qsfn(1., P->e, P->one_es); - P->mmf = .5 / (1. - P->es); - P->apa = pj_authset(P->es); - switch (P->mode) { - case N_POLE: - case S_POLE: - P->dd = 1.; - break; - case EQUIT: - P->dd = 1. / (P->rq = sqrt(.5 * P->qp)); - P->xmf = 1.; - P->ymf = .5 * P->qp; - break; - case OBLIQ: - P->rq = sqrt(.5 * P->qp); - sinphi = sin(P->phi0); - P->sinb1 = pj_qsfn(sinphi, P->e, P->one_es) / P->qp; - P->cosb1 = sqrt(1. - P->sinb1 * P->sinb1); - P->dd = cos(P->phi0) / (sqrt(1. - P->es * sinphi * sinphi) * - P->rq * P->cosb1); - P->ymf = (P->xmf = P->rq) / P->dd; - P->xmf *= P->dd; - break; - } - P->inv = e_inverse; - P->fwd = e_forward; - } else { - if (P->mode == OBLIQ) { - sinph0 = sin(P->phi0); - cosph0 = cos(P->phi0); - } - P->inv = s_inverse; - P->fwd = s_forward; - } -ENDENTRY(P) + + +#endif diff --git a/src/PJ_lagrng.c b/src/PJ_lagrng.c index 8a13b3da..776894d7 100644 --- a/src/PJ_lagrng.c +++ b/src/PJ_lagrng.c @@ -1,35 +1,103 @@ -#define PROJ_PARMS__ \ - double hrw; \ - double rw; \ - double a1; -#define TOL 1e-10 #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(lagrng, "Lagrange") "\n\tMisc Sph, no inv.\n\tW="; -FORWARD(s_forward); /* spheroid */ - double v, c; - - if (fabs(fabs(lp.phi) - HALFPI) < TOL) { - xy.x = 0; - xy.y = lp.phi < 0 ? -2. : 2.; - } else { - lp.phi = sin(lp.phi); - v = P->a1 * pow((1. + lp.phi)/(1. - lp.phi), P->hrw); - if ((c = 0.5 * (v + 1./v) + cos(lp.lam *= P->rw)) < TOL) - F_ERROR; - xy.x = 2. * sin(lp.lam) / c; - xy.y = (v - 1./v) / c; - } - return (xy); + +#define TOL 1e-10 + +struct pj_opaque { + double a1; + double hrw; + double rw; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double v, c; + + if (fabs(fabs(lp.phi) - HALFPI) < TOL) { + xy.x = 0; + xy.y = lp.phi < 0 ? -2. : 2.; + } else { + lp.phi = sin(lp.phi); + v = Q->a1 * pow((1. + lp.phi)/(1. - lp.phi), Q->hrw); + if ((c = 0.5 * (v + 1./v) + cos(lp.lam *= Q->rw)) < TOL) + F_ERROR; + xy.x = 2. * sin(lp.lam) / c; + xy.y = (v - 1./v) / c; + } + return xy; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(lagrng) { + double phi1; + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->rw = pj_param(P->ctx, P->params, "dW").f; + if (Q->rw <= 0) E_ERROR(-27); + + Q->rw = 1. / Q->rw; + Q->hrw = 0.5 * Q->rw; + phi1 = sin(pj_param(P->ctx, P->params, "rlat_1").f); + if (fabs(fabs(phi1) - 1.) < TOL) E_ERROR(-22); + + Q->a1 = pow((1. - phi1)/(1. + phi1), Q->hrw); + + P->es = 0.; + P->fwd = s_forward; + + return P; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(lagrng) - double phi1; - - if ((P->rw = pj_param(P->ctx, P->params, "dW").f) <= 0) E_ERROR(-27); - P->hrw = 0.5 * (P->rw = 1. / P->rw); - phi1 = pj_param(P->ctx, P->params, "rlat_1").f; - if (fabs(fabs(phi1 = sin(phi1)) - 1.) < TOL) E_ERROR(-22); - P->a1 = pow((1. - phi1)/(1. + phi1), P->hrw); - P->es = 0.; P->fwd = s_forward; -ENDENTRY(P) + + +#ifdef PJ_OMIT_SELFTEST +int pj_lagrng_selftest (void) {return 0;} +#else + +int pj_lagrng_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=lagrng +a=6400000 +W=2 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 111703.37591722561, 27929.8319080333386}, + { 111699.122088816002, -83784.1780133577704}, + {-111703.37591722561, 27929.8319080333386}, + {-111699.122088816002, -83784.1780133577704}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, 0, 0, 0); +} + + +#endif diff --git a/src/PJ_larr.c b/src/PJ_larr.c index 118bef00..f55289b1 100644 --- a/src/PJ_larr.c +++ b/src/PJ_larr.c @@ -1,13 +1,70 @@ -#define PROJ_PARMS__ #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(larr, "Larrivee") "\n\tMisc Sph, no inv."; + #define SIXTH .16666666666666666 -FORWARD(s_forward); /* sphere */ - (void) P; - xy.x = 0.5 * lp.lam * (1. + sqrt(cos(lp.phi))); - xy.y = lp.phi / (cos(0.5 * lp.phi) * cos(SIXTH * lp.lam)); - return (xy); -} -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(larr) P->fwd = s_forward; P->inv = 0; P->es = 0.; ENDENTRY(P) + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + (void) P; + + xy.x = 0.5 * lp.lam * (1. + sqrt(cos(lp.phi))); + xy.y = lp.phi / (cos(0.5 * lp.phi) * cos(SIXTH * lp.lam)); + return xy; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(larr) { + + P->es = 0; + P->fwd = s_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_larr_selftest (void) {return 0;} +#else + +int pj_larr_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=larr +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + {223393.637624200899, 111707.215961255497}, + {223393.637624200899, -111707.215961255497}, + {-223393.637624200899, 111707.215961255497}, + {-223393.637624200899, -111707.215961255497}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, 0, 0, 0); +} + + +#endif diff --git a/src/PJ_lask.c b/src/PJ_lask.c index 797b580d..38e7045c 100644 --- a/src/PJ_lask.c +++ b/src/PJ_lask.c @@ -1,27 +1,81 @@ -#define PROJ_PARMS__ #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(lask, "Laskowski") "\n\tMisc Sph, no inv."; -#define a10 0.975534 -#define a12 -0.119161 -#define a32 -0.0143059 -#define a14 -0.0547009 -#define b01 1.00384 -#define b21 0.0802894 -#define b03 0.0998909 -#define b41 0.000199025 -#define b23 -0.0285500 -#define b05 -0.0491032 -FORWARD(s_forward); /* sphere */ - double l2, p2; - (void) P; - - l2 = lp.lam * lp.lam; - p2 = lp.phi * lp.phi; - xy.x = lp.lam * (a10 + p2 * (a12 + l2 * a32 + p2 * a14)); - xy.y = lp.phi * (b01 + l2 * (b21 + p2 * b23 + l2 * b41) + - p2 * (b03 + p2 * b05)); - return (xy); + +#define a10 0.975534 +#define a12 -0.119161 +#define a32 -0.0143059 +#define a14 -0.0547009 +#define b01 1.00384 +#define b21 0.0802894 +#define b03 0.0998909 +#define b41 0.000199025 +#define b23 -0.0285500 +#define b05 -0.0491032 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double l2, p2; + (void) P; + + l2 = lp.lam * lp.lam; + p2 = lp.phi * lp.phi; + xy.x = lp.lam * (a10 + p2 * (a12 + l2 * a32 + p2 * a14)); + xy.y = lp.phi * (b01 + l2 * (b21 + p2 * b23 + l2 * b41) + + p2 * (b03 + p2 * b05)); + return xy; +} + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(lask) { + + P->fwd = s_forward; + P->es = 0.; + + return P; +} + +#ifdef PJ_OMIT_SELFTEST +int pj_lask_selftest (void) {return 0;} +#else + +int pj_lask_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=lask +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 217928.275907355128, 112144.32922014239}, + { 217928.275907355128, -112144.32922014239}, + {-217928.275907355128, 112144.32922014239}, + {-217928.275907355128, -112144.32922014239}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, 0, 0, 0); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(lask) P->fwd = s_forward; P->inv = 0; P->es = 0.; ENDENTRY(P) + + +#endif diff --git a/src/PJ_lcc.c b/src/PJ_lcc.c index 9d3494bf..e7b74be4 100644 --- a/src/PJ_lcc.c +++ b/src/PJ_lcc.c @@ -1,105 +1,196 @@ -#define PROJ_PARMS__ \ - double phi1; \ - double phi2; \ - double n; \ - double rho0; \ - double c; \ - int ellips; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(lcc, "Lambert Conformal Conic") - "\n\tConic, Sph&Ell\n\tlat_1= and lat_2= or lat_0"; -# define EPS10 1.e-10 -FORWARD(e_forward); /* ellipsoid & spheroid */ - double rho; - if (fabs(fabs(lp.phi) - HALFPI) < EPS10) { - if ((lp.phi * P->n) <= 0.) F_ERROR; - rho = 0.; - } - else - rho = P->c * (P->ellips ? pow(pj_tsfn(lp.phi, sin(lp.phi), - P->e), P->n) : pow(tan(FORTPI + .5 * lp.phi), -P->n)); - xy.x = P->k0 * (rho * sin( lp.lam *= P->n ) ); - xy.y = P->k0 * (P->rho0 - rho * cos(lp.lam) ); - return (xy); + "\n\tConic, Sph&Ell\n\tlat_1= and lat_2= or lat_0"; + +# define EPS10 1.e-10 + +struct pj_opaque { + double phi1; + double phi2; + double n; + double rho0; + double c; + int ellips; +}; + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double rho; + + if (fabs(fabs(lp.phi) - HALFPI) < EPS10) { + if ((lp.phi * Q->n) <= 0.) F_ERROR; + rho = 0.; + } else { + rho = Q->c * (Q->ellips ? pow(pj_tsfn(lp.phi, sin(lp.phi), + P->e), Q->n) : pow(tan(FORTPI + .5 * lp.phi), -Q->n)); + } + lp.lam *= Q->n; + xy.x = P->k0 * (rho * sin( lp.lam) ); + xy.y = P->k0 * (Q->rho0 - rho * cos(lp.lam) ); + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double rho; + + xy.x /= P->k0; + xy.y /= P->k0; + + xy.y = Q->rho0 - xy.y; + rho = hypot(xy.x, xy.y); + if (rho != 0.0) { + if (Q->n < 0.) { + rho = -rho; + xy.x = -xy.x; + xy.y = -xy.y; + } + if (Q->ellips) { + lp.phi = pj_phi2(P->ctx, pow(rho / Q->c, 1./Q->n), P->e); + if (lp.phi == HUGE_VAL) + I_ERROR; + } else + lp.phi = 2. * atan(pow(Q->c / rho, 1./Q->n)) - HALFPI; + lp.lam = atan2(xy.x, xy.y) / Q->n; + } else { + lp.lam = 0.; + lp.phi = Q->n > 0. ? HALFPI : - HALFPI; + } + return lp; +} + +static void special(LP lp, PJ *P, struct FACTORS *fac) { + struct pj_opaque *Q = P->opaque; + double rho; + if (fabs(fabs(lp.phi) - HALFPI) < EPS10) { + if ((lp.phi * Q->n) <= 0.) return; + rho = 0.; + } else + rho = Q->c * (Q->ellips ? pow(pj_tsfn(lp.phi, sin(lp.phi), + P->e), Q->n) : pow(tan(FORTPI + .5 * lp.phi), -Q->n)); + fac->code |= IS_ANAL_HK + IS_ANAL_CONV; + fac->k = fac->h = P->k0 * Q->n * rho / + pj_msfn(sin(lp.phi), cos(lp.phi), P->es); + fac->conv = - Q->n * lp.lam; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; } -INVERSE(e_inverse); /* ellipsoid & spheroid */ - double rho; - xy.x /= P->k0; - xy.y /= P->k0; - if( (rho = hypot(xy.x, xy.y = P->rho0 - xy.y)) != 0.0) { - if (P->n < 0.) { - rho = -rho; - xy.x = -xy.x; - xy.y = -xy.y; - } - if (P->ellips) { - if ((lp.phi = pj_phi2(P->ctx, pow(rho / P->c, 1./P->n), P->e)) - == HUGE_VAL) - I_ERROR; - } else - lp.phi = 2. * atan(pow(P->c / rho, 1./P->n)) - HALFPI; - lp.lam = atan2(xy.x, xy.y) / P->n; - } else { - lp.lam = 0.; - lp.phi = P->n > 0. ? HALFPI : - HALFPI; - } - return (lp); + + +PJ *PROJECTION(lcc) { + double cosphi, sinphi; + int secant; + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + + Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; + if (pj_param(P->ctx, P->params, "tlat_2").i) + Q->phi2 = pj_param(P->ctx, P->params, "rlat_2").f; + else { + Q->phi2 = Q->phi1; + if (!pj_param(P->ctx, P->params, "tlat_0").i) + P->phi0 = Q->phi1; + } + if (fabs(Q->phi1 + Q->phi2) < EPS10) E_ERROR(-21); + Q->n = sinphi = sin(Q->phi1); + cosphi = cos(Q->phi1); + secant = fabs(Q->phi1 - Q->phi2) >= EPS10; + if( (Q->ellips = (P->es != 0.)) ) { + double ml1, m1; + + P->e = sqrt(P->es); + m1 = pj_msfn(sinphi, cosphi, P->es); + ml1 = pj_tsfn(Q->phi1, sinphi, P->e); + if (secant) { /* secant cone */ + sinphi = sin(Q->phi2); + Q->n = log(m1 / pj_msfn(sinphi, cos(Q->phi2), P->es)); + Q->n /= log(ml1 / pj_tsfn(Q->phi2, sinphi, P->e)); + } + Q->c = (Q->rho0 = m1 * pow(ml1, -Q->n) / Q->n); + Q->rho0 *= (fabs(fabs(P->phi0) - HALFPI) < EPS10) ? 0. : + pow(pj_tsfn(P->phi0, sin(P->phi0), P->e), Q->n); + } else { + if (secant) + Q->n = log(cosphi / cos(Q->phi2)) / + log(tan(FORTPI + .5 * Q->phi2) / + tan(FORTPI + .5 * Q->phi1)); + Q->c = cosphi * pow(tan(FORTPI + .5 * Q->phi1), Q->n) / Q->n; + Q->rho0 = (fabs(fabs(P->phi0) - HALFPI) < EPS10) ? 0. : + Q->c * pow(tan(FORTPI + .5 * P->phi0), -Q->n); + } + + P->inv = e_inverse; + P->fwd = e_forward; + P->spc = special; + + return P; } -SPECIAL(fac) { - double rho; - if (fabs(fabs(lp.phi) - HALFPI) < EPS10) { - if ((lp.phi * P->n) <= 0.) return; - rho = 0.; - } else - rho = P->c * (P->ellips ? pow(pj_tsfn(lp.phi, sin(lp.phi), - P->e), P->n) : pow(tan(FORTPI + .5 * lp.phi), -P->n)); - fac->code |= IS_ANAL_HK + IS_ANAL_CONV; - fac->k = fac->h = P->k0 * P->n * rho / - pj_msfn(sin(lp.phi), cos(lp.phi), P->es); - fac->conv = - P->n * lp.lam; + + +#ifdef PJ_OMIT_SELFTEST +int pj_lcc_selftest (void) {return 0;} +#else + +int pj_lcc_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=lcc +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 222588.439735968423, 110660.533870799671}, + { 222756.879700278747, -110532.797660827026}, + {-222588.439735968423, 110660.533870799671}, + {-222756.879700278747, -110532.797660827026}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + { 0.00179635940600536667, 0.000904232207322381741}, + { 0.00179635817735249777, -0.000904233135128348995}, + {-0.00179635940600536667, 0.000904232207322381741}, + {-0.00179635817735249777, -0.000904233135128348995}, + }; + + return pj_generic_selftest (e_args, 0, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, 0, inv_in, e_inv_expect, 0); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(lcc) - double cosphi, sinphi; - int secant; - - P->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; - if (pj_param(P->ctx, P->params, "tlat_2").i) - P->phi2 = pj_param(P->ctx, P->params, "rlat_2").f; - else { - P->phi2 = P->phi1; - if (!pj_param(P->ctx, P->params, "tlat_0").i) - P->phi0 = P->phi1; - } - if (fabs(P->phi1 + P->phi2) < EPS10) E_ERROR(-21); - P->n = sinphi = sin(P->phi1); - cosphi = cos(P->phi1); - secant = fabs(P->phi1 - P->phi2) >= EPS10; - if( (P->ellips = (P->es != 0.)) ) { - double ml1, m1; - - P->e = sqrt(P->es); - m1 = pj_msfn(sinphi, cosphi, P->es); - ml1 = pj_tsfn(P->phi1, sinphi, P->e); - if (secant) { /* secant cone */ - P->n = log(m1 / - pj_msfn(sinphi = sin(P->phi2), cos(P->phi2), P->es)); - P->n /= log(ml1 / pj_tsfn(P->phi2, sinphi, P->e)); - } - P->c = (P->rho0 = m1 * pow(ml1, -P->n) / P->n); - P->rho0 *= (fabs(fabs(P->phi0) - HALFPI) < EPS10) ? 0. : - pow(pj_tsfn(P->phi0, sin(P->phi0), P->e), P->n); - } else { - if (secant) - P->n = log(cosphi / cos(P->phi2)) / - log(tan(FORTPI + .5 * P->phi2) / - tan(FORTPI + .5 * P->phi1)); - P->c = cosphi * pow(tan(FORTPI + .5 * P->phi1), P->n) / P->n; - P->rho0 = (fabs(fabs(P->phi0) - HALFPI) < EPS10) ? 0. : - P->c * pow(tan(FORTPI + .5 * P->phi0), -P->n); - } - P->inv = e_inverse; - P->fwd = e_forward; - P->spc = fac; -ENDENTRY(P) + + +#endif diff --git a/src/PJ_lcca.c b/src/PJ_lcca.c index 320d52db..50c89978 100644 --- a/src/PJ_lcca.c +++ b/src/PJ_lcca.c @@ -1,70 +1,156 @@ -/* PROJ.4 Cartographic Projection System +/* PROJ.4 Cartographic Projection System */ -#define MAX_ITER 10 -#define DEL_TOL 1e-12 -#define PROJ_PARMS__ \ - double *en; \ - double r0, l, M0; \ - double C; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> PROJ_HEAD(lcca, "Lambert Conformal Conic Alternative") - "\n\tConic, Sph&Ell\n\tlat_0="; + "\n\tConic, Sph&Ell\n\tlat_0="; + +#define MAX_ITER 10 +#define DEL_TOL 1e-12 + +struct pj_opaque { + double *en; + double r0, l, M0; + double C; +}; + + +static double fS(double S, double C) { /* func to compute dr */ + + return S * ( 1. + S * S * C); +} + + +static double fSp(double S, double C) { /* deriv of fs */ + + return 1. + 3.* S * S * C; +} + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double S, r, dr; + + S = pj_mlfn(lp.phi, sin(lp.phi), cos(lp.phi), Q->en) - Q->M0; + dr = fS(S, Q->C); + r = Q->r0 - dr; + xy.x = P->k0 * (r * sin( lp.lam *= Q->l ) ); + xy.y = P->k0 * (Q->r0 - r * cos(lp.lam) ); + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double theta, dr, S, dif; + int i; + + xy.x /= P->k0; + xy.y /= P->k0; + theta = atan2(xy.x , Q->r0 - xy.y); + dr = xy.y - xy.x * tan(0.5 * theta); + lp.lam = theta / Q->l; + S = dr; + for (i = MAX_ITER; i ; --i) { + S -= (dif = (fS(S, Q->C) - dr) / fSp(S, Q->C)); + if (fabs(dif) < DEL_TOL) break; + } + if (!i) I_ERROR + lp.phi = pj_inv_mlfn(P->ctx, S + Q->M0, P->es, Q->en); - static double /* func to compute dr */ -fS(double S, double C) { - return(S * ( 1. + S * S * C)); + return lp; } - static double /* deriv of fs */ -fSp(double S, double C) { - return(1. + 3.* S * S * C); + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque->en); + pj_dealloc (P->opaque); + return pj_dealloc(P); } -FORWARD(e_forward); /* ellipsoid */ - double S, r, dr; - - S = pj_mlfn(lp.phi, sin(lp.phi), cos(lp.phi), P->en) - P->M0; - dr = fS(S, P->C); - r = P->r0 - dr; - xy.x = P->k0 * (r * sin( lp.lam *= P->l ) ); - xy.y = P->k0 * (P->r0 - r * cos(lp.lam) ); - return (xy); + +static void freeup (PJ *P) { + freeup_new (P); + return; } -INVERSE(e_inverse); /* ellipsoid & spheroid */ - double theta, dr, S, dif; - int i; - - xy.x /= P->k0; - xy.y /= P->k0; - theta = atan2(xy.x , P->r0 - xy.y); - dr = xy.y - xy.x * tan(0.5 * theta); - lp.lam = theta / P->l; - S = dr; - for (i = MAX_ITER; i ; --i) { - S -= (dif = (fS(S, P->C) - dr) / fSp(S, P->C)); - if (fabs(dif) < DEL_TOL) break; - } - if (!i) I_ERROR - lp.phi = pj_inv_mlfn(P->ctx, S + P->M0, P->es, P->en); - return (lp); + + +PJ *PROJECTION(lcca) { + double s2p0, N0, R0, tan0; + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + (Q->en = pj_enfn(P->es)); + if (!Q->en) E_ERROR_0; + if (!pj_param(P->ctx, P->params, "tlat_0").i) E_ERROR(50); + if (P->phi0 == 0.) E_ERROR(51); + Q->l = sin(P->phi0); + Q->M0 = pj_mlfn(P->phi0, Q->l, cos(P->phi0), Q->en); + s2p0 = Q->l * Q->l; + R0 = 1. / (1. - P->es * s2p0); + N0 = sqrt(R0); + R0 *= P->one_es * N0; + tan0 = tan(P->phi0); + Q->r0 = N0 / tan0; + Q->C = 1. / (6. * R0 * N0); + + P->inv = e_inverse; + P->fwd = e_forward; + + return P; } -FREEUP; if (P) { if (P->en) pj_dalloc(P->en); pj_dalloc(P); } } -ENTRY0(lcca) - double s2p0, N0, R0, tan0; - - if (!(P->en = pj_enfn(P->es))) E_ERROR_0; - if (!pj_param(P->ctx, P->params, "tlat_0").i) E_ERROR(50); - if (P->phi0 == 0.) E_ERROR(51); - P->l = sin(P->phi0); - P->M0 = pj_mlfn(P->phi0, P->l, cos(P->phi0), P->en); - s2p0 = P->l * P->l; - R0 = 1. / (1. - P->es * s2p0); - N0 = sqrt(R0); - R0 *= P->one_es * N0; - tan0 = tan(P->phi0); - P->r0 = N0 / tan0; - P->C = 1. / (6. * R0 * N0); - P->inv = e_inverse; - P->fwd = e_forward; -ENDENTRY(P) + + +#ifdef PJ_OMIT_SELFTEST +int pj_lcca_selftest (void) {return 0;} +#else + +int pj_lcca_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=lcca +ellps=GRS80 +lat_0=1 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 222605.285770237417, 67.8060072715846616}, + { 222740.037637936533, -221125.539829601563}, + {-222605.285770237417, 67.8060072715846616}, + {-222740.037637936533, -221125.539829601563}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + { 0.00179690290525662526, 1.00090436621350798}, + { 0.00179690192174008037, 0.999095632791497268}, + {-0.00179690290525662526, 1.00090436621350798}, + {-0.00179690192174008037, 0.999095632791497268}, + }; + + return pj_generic_selftest (e_args, 0, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, 0, inv_in, e_inv_expect, 0); +} + + +#endif diff --git a/src/PJ_loxim.c b/src/PJ_loxim.c index 595423f7..9ddfd51c 100644 --- a/src/PJ_loxim.c +++ b/src/PJ_loxim.c @@ -1,41 +1,130 @@ -#define PROJ_PARMS__ \ - double phi1; \ - double cosphi1; \ - double tanphi1; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(loxim, "Loximuthal") "\n\tPCyl Sph"; -#define EPS 1e-8 -FORWARD(s_forward); /* spheroid */ - xy.y = lp.phi - P->phi1; - if (fabs(xy.y) < EPS) - xy.x = lp.lam * P->cosphi1; - else { - xy.x = FORTPI + 0.5 * lp.phi; - if (fabs(xy.x) < EPS || fabs(fabs(xy.x) - HALFPI) < EPS) - xy.x = 0.; - else - xy.x = lp.lam * xy.y / log( tan(xy.x) / P->tanphi1 ); - } - return (xy); + +#define EPS 1e-8 + +struct pj_opaque { + double phi1; + double cosphi1; + double tanphi1; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + + xy.y = lp.phi - Q->phi1; + if (fabs(xy.y) < EPS) + xy.x = lp.lam * Q->cosphi1; + else { + xy.x = FORTPI + 0.5 * lp.phi; + if (fabs(xy.x) < EPS || fabs(fabs(xy.x) - HALFPI) < EPS) + xy.x = 0.; + else + xy.x = lp.lam * xy.y / log( tan(xy.x) / Q->tanphi1 ); + } + return xy; } -INVERSE(s_inverse); /* spheroid */ - lp.phi = xy.y + P->phi1; - if (fabs(xy.y) < EPS) - lp.lam = xy.x / P->cosphi1; - else - if (fabs( lp.lam = FORTPI + 0.5 * lp.phi ) < EPS || - fabs(fabs(lp.lam) - HALFPI) < EPS) - lp.lam = 0.; - else - lp.lam = xy.x * log( tan(lp.lam) / P->tanphi1 ) / xy.y ; - return (lp); + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + + lp.phi = xy.y + Q->phi1; + if (fabs(xy.y) < EPS) { + lp.lam = xy.x / Q->cosphi1; + } else { + lp.lam = FORTPI + 0.5 * lp.phi; + if (fabs(lp.lam) < EPS || fabs(fabs(lp.lam) - HALFPI) < EPS) + lp.lam = 0.; + else + lp.lam = xy.x * log( tan(lp.lam) / Q->tanphi1 ) / xy.y ; + } + return lp; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(loxim); - P->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; - if ((P->cosphi1 = cos(P->phi1)) < EPS) E_ERROR(-22); - P->tanphi1 = tan(FORTPI + 0.5 * P->phi1); - P->inv = s_inverse; P->fwd = s_forward; - P->es = 0.; -ENDENTRY(P) + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(loxim) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f; + Q->cosphi1 = cos(Q->phi1); + if (Q->cosphi1 < EPS) + E_ERROR(-22); + + Q->tanphi1 = tan(FORTPI + 0.5 * Q->phi1); + + P->inv = s_inverse; + P->fwd = s_forward; + P->es = 0.; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_loxim_selftest (void) {return 0;} +#else + +int pj_loxim_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=loxim +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223382.295791338867, 55850.5360638185448}, + { 223393.637462243292, -167551.608191455656}, + {-223382.295791338867, 55850.5360638185448}, + {-223393.637462243292, -167551.608191455656}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.00179056141104335601, 0.500895246554891926}, + { 0.00179056116683692576, 0.499104753445108074}, + {-0.00179056141104335601, 0.500895246554891926}, + {-0.00179056116683692576, 0.499104753445108074}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_lsat.c b/src/PJ_lsat.c index d11a5c14..fa96407b 100644 --- a/src/PJ_lsat.c +++ b/src/PJ_lsat.c @@ -1,171 +1,258 @@ /* based upon Snyder and Linck, USGS-NMD */ -#define PROJ_PARMS__ \ - double a2, a4, b, c1, c3; \ - double q, t, u, w, p22, sa, ca, xj, rlm, rlm2; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(lsat, "Space oblique for LANDSAT") - "\n\tCyl, Sph&Ell\n\tlsat= path="; + "\n\tCyl, Sph&Ell\n\tlsat= path="; + #define TOL 1e-7 #define PI_HALFPI 4.71238898038468985766 #define TWOPI_HALFPI 7.85398163397448309610 - static void -seraz0(double lam, double mult, PJ *P) { - double sdsq, h, s, fc, sd, sq, d__1; + +struct pj_opaque { + double a2, a4, b, c1, c3; + double q, t, u, w, p22, sa, ca, xj, rlm, rlm2; +}; + +static void seraz0(double lam, double mult, PJ *P) { + struct pj_opaque *Q = P->opaque; + double sdsq, h, s, fc, sd, sq, d__1 = 0; lam *= DEG_TO_RAD; sd = sin(lam); sdsq = sd * sd; - s = P->p22 * P->sa * cos(lam) * sqrt((1. + P->t * sdsq) / (( - 1. + P->w * sdsq) * (1. + P->q * sdsq))); - d__1 = 1. + P->q * sdsq; - h = sqrt((1. + P->q * sdsq) / (1. + P->w * sdsq)) * ((1. + - P->w * sdsq) / (d__1 * d__1) - P->p22 * P->ca); - sq = sqrt(P->xj * P->xj + s * s); - P->b += fc = mult * (h * P->xj - s * s) / sq; - P->a2 += fc * cos(lam + lam); - P->a4 += fc * cos(lam * 4.); - fc = mult * s * (h + P->xj) / sq; - P->c1 += fc * cos(lam); - P->c3 += fc * cos(lam * 3.); + s = Q->p22 * Q->sa * cos(lam) * sqrt((1. + Q->t * sdsq) + / ((1. + Q->w * sdsq) * (1. + Q->q * sdsq))); + + h = sqrt((1. + Q->q * sdsq) / (1. + Q->w * sdsq)) * ((1. + Q->w * sdsq) + / (d__1 * d__1) - Q->p22 * Q->ca); + + sq = sqrt(Q->xj * Q->xj + s * s); + fc = mult * (h * Q->xj - s * s) / sq; + Q->b += fc; + Q->a2 += fc * cos(lam + lam); + Q->a4 += fc * cos(lam * 4.); + fc = mult * s * (h + Q->xj) / sq; + Q->c1 += fc * cos(lam); + Q->c3 += fc * cos(lam * 3.); } -FORWARD(e_forward); /* ellipsoid */ + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; int l, nn; - double lamt, xlam, sdsq, c, d, s, lamdp, phidp, lampp, tanph, - lamtp, cl, sd, sp, fac, sav, tanphi; - - if (lp.phi > HALFPI) - lp.phi = HALFPI; - else if (lp.phi < -HALFPI) - lp.phi = -HALFPI; - lampp = lp.phi >= 0. ? HALFPI : PI_HALFPI; - tanphi = tan(lp.phi); - for (nn = 0;;) { - sav = lampp; - lamtp = lp.lam + P->p22 * lampp; - cl = cos(lamtp); - if (fabs(cl) < TOL) - lamtp -= TOL; - fac = lampp - sin(lampp) * (cl < 0. ? -HALFPI : HALFPI); - for (l = 50; l; --l) { - lamt = lp.lam + P->p22 * sav; - if (fabs(c = cos(lamt)) < TOL) - lamt -= TOL; - xlam = (P->one_es * tanphi * P->sa + sin(lamt) * P->ca) / c; - lamdp = atan(xlam) + fac; - if (fabs(fabs(sav) - fabs(lamdp)) < TOL) - break; - sav = lamdp; - } - if (!l || ++nn >= 3 || (lamdp > P->rlm && lamdp < P->rlm2)) - break; - if (lamdp <= P->rlm) - lampp = TWOPI_HALFPI; - else if (lamdp >= P->rlm2) - lampp = HALFPI; - } - if (l) { - sp = sin(lp.phi); - phidp = aasin(P->ctx,(P->one_es * P->ca * sp - P->sa * cos(lp.phi) * - sin(lamt)) / sqrt(1. - P->es * sp * sp)); - tanph = log(tan(FORTPI + .5 * phidp)); - sd = sin(lamdp); - sdsq = sd * sd; - s = P->p22 * P->sa * cos(lamdp) * sqrt((1. + P->t * sdsq) - / ((1. + P->w * sdsq) * (1. + P->q * sdsq))); - d = sqrt(P->xj * P->xj + s * s); - xy.x = P->b * lamdp + P->a2 * sin(2. * lamdp) + P->a4 * - sin(lamdp * 4.) - tanph * s / d; - xy.y = P->c1 * sd + P->c3 * sin(lamdp * 3.) + tanph * P->xj / d; - } else - xy.x = xy.y = HUGE_VAL; - return xy; + double lamt, xlam, sdsq, c, d, s, lamdp, phidp, lampp, tanph; + double lamtp, cl, sd, sp, fac, sav, tanphi; + + if (lp.phi > HALFPI) + lp.phi = HALFPI; + else if (lp.phi < -HALFPI) + lp.phi = -HALFPI; + + lampp = lp.phi >= 0. ? HALFPI : PI_HALFPI; + tanphi = tan(lp.phi); + for (nn = 0;;) { + sav = lampp; + lamtp = lp.lam + Q->p22 * lampp; + cl = cos(lamtp); + if (fabs(cl) < TOL) + lamtp -= TOL; + fac = lampp - sin(lampp) * (cl < 0. ? -HALFPI : HALFPI); + for (l = 50; l; --l) { + lamt = lp.lam + Q->p22 * sav; + c = cos(lamt); + if (fabs(c) < TOL) + lamt -= TOL; + xlam = (P->one_es * tanphi * Q->sa + sin(lamt) * Q->ca) / c; + lamdp = atan(xlam) + fac; + if (fabs(fabs(sav) - fabs(lamdp)) < TOL) + break; + sav = lamdp; + } + if (!l || ++nn >= 3 || (lamdp > Q->rlm && lamdp < Q->rlm2)) + break; + if (lamdp <= Q->rlm) + lampp = TWOPI_HALFPI; + else if (lamdp >= Q->rlm2) + lampp = HALFPI; + } + if (l) { + sp = sin(lp.phi); + phidp = aasin(P->ctx,(P->one_es * Q->ca * sp - Q->sa * cos(lp.phi) * + sin(lamt)) / sqrt(1. - P->es * sp * sp)); + tanph = log(tan(FORTPI + .5 * phidp)); + sd = sin(lamdp); + sdsq = sd * sd; + s = Q->p22 * Q->sa * cos(lamdp) * sqrt((1. + Q->t * sdsq) + / ((1. + Q->w * sdsq) * (1. + Q->q * sdsq))); + d = sqrt(Q->xj * Q->xj + s * s); + xy.x = Q->b * lamdp + Q->a2 * sin(2. * lamdp) + Q->a4 * + sin(lamdp * 4.) - tanph * s / d; + xy.y = Q->c1 * sd + Q->c3 * sin(lamdp * 3.) + tanph * Q->xj / d; + } else + xy.x = xy.y = HUGE_VAL; + return xy; } -INVERSE(e_inverse); /* ellipsoid */ + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; int nn; double lamt, sdsq, s, lamdp, phidp, sppsq, dd, sd, sl, fac, scl, sav, spp; - lamdp = xy.x / P->b; - nn = 50; - do { - sav = lamdp; - sd = sin(lamdp); - sdsq = sd * sd; - s = P->p22 * P->sa * cos(lamdp) * sqrt((1. + P->t * sdsq) - / ((1. + P->w * sdsq) * (1. + P->q * sdsq))); - lamdp = xy.x + xy.y * s / P->xj - P->a2 * sin( - 2. * lamdp) - P->a4 * sin(lamdp * 4.) - s / P->xj * ( - P->c1 * sin(lamdp) + P->c3 * sin(lamdp * 3.)); - lamdp /= P->b; - } while (fabs(lamdp - sav) >= TOL && --nn); - sl = sin(lamdp); - fac = exp(sqrt(1. + s * s / P->xj / P->xj) * (xy.y - - P->c1 * sl - P->c3 * sin(lamdp * 3.))); - phidp = 2. * (atan(fac) - FORTPI); - dd = sl * sl; - if (fabs(cos(lamdp)) < TOL) - lamdp -= TOL; - spp = sin(phidp); - sppsq = spp * spp; - lamt = atan(((1. - sppsq * P->rone_es) * tan(lamdp) * - P->ca - spp * P->sa * sqrt((1. + P->q * dd) * ( - 1. - sppsq) - sppsq * P->u) / cos(lamdp)) / (1. - sppsq - * (1. + P->u))); - sl = lamt >= 0. ? 1. : -1.; - scl = cos(lamdp) >= 0. ? 1. : -1; - lamt -= HALFPI * (1. - scl) * sl; - lp.lam = lamt - P->p22 * lamdp; - if (fabs(P->sa) < TOL) - lp.phi = aasin(P->ctx,spp / sqrt(P->one_es * P->one_es + P->es * sppsq)); - else - lp.phi = atan((tan(lamdp) * cos(lamt) - P->ca * sin(lamt)) / - (P->one_es * P->sa)); - return lp; + lamdp = xy.x / Q->b; + nn = 50; + do { + sav = lamdp; + sd = sin(lamdp); + sdsq = sd * sd; + s = Q->p22 * Q->sa * cos(lamdp) * sqrt((1. + Q->t * sdsq) + / ((1. + Q->w * sdsq) * (1. + Q->q * sdsq))); + lamdp = xy.x + xy.y * s / Q->xj - Q->a2 * sin( + 2. * lamdp) - Q->a4 * sin(lamdp * 4.) - s / Q->xj * ( + Q->c1 * sin(lamdp) + Q->c3 * sin(lamdp * 3.)); + lamdp /= Q->b; + } while (fabs(lamdp - sav) >= TOL && --nn); + sl = sin(lamdp); + fac = exp(sqrt(1. + s * s / Q->xj / Q->xj) * (xy.y - + Q->c1 * sl - Q->c3 * sin(lamdp * 3.))); + phidp = 2. * (atan(fac) - FORTPI); + dd = sl * sl; + if (fabs(cos(lamdp)) < TOL) + lamdp -= TOL; + spp = sin(phidp); + sppsq = spp * spp; + lamt = atan(((1. - sppsq * P->rone_es) * tan(lamdp) * + Q->ca - spp * Q->sa * sqrt((1. + Q->q * dd) * ( + 1. - sppsq) - sppsq * Q->u) / cos(lamdp)) / (1. - sppsq + * (1. + Q->u))); + sl = lamt >= 0. ? 1. : -1.; + scl = cos(lamdp) >= 0. ? 1. : -1; + lamt -= HALFPI * (1. - scl) * sl; + lp.lam = lamt - Q->p22 * lamdp; + if (fabs(Q->sa) < TOL) + lp.phi = aasin(P->ctx,spp / sqrt(P->one_es * P->one_es + P->es * sppsq)); + else + lp.phi = atan((tan(lamdp) * cos(lamt) - Q->ca * sin(lamt)) / + (P->one_es * Q->sa)); + return lp; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(lsat) + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(lsat) { int land, path; double lam, alf, esc, ess; + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + land = pj_param(P->ctx, P->params, "ilsat").i; + if (land <= 0 || land > 5) E_ERROR(-28); + path = pj_param(P->ctx, P->params, "ipath").i; + if (path <= 0 || path > (land <= 3 ? 251 : 233)) E_ERROR(-29); + if (land <= 3) { + P->lam0 = DEG_TO_RAD * 128.87 - TWOPI / 251. * path; + Q->p22 = 103.2669323; + alf = DEG_TO_RAD * 99.092; + } else { + P->lam0 = DEG_TO_RAD * 129.3 - TWOPI / 233. * path; + Q->p22 = 98.8841202; + alf = DEG_TO_RAD * 98.2; + } + Q->p22 /= 1440.; + Q->sa = sin(alf); + Q->ca = cos(alf); + if (fabs(Q->ca) < 1e-9) + Q->ca = 1e-9; + esc = P->es * Q->ca * Q->ca; + ess = P->es * Q->sa * Q->sa; + Q->w = (1. - esc) * P->rone_es; + Q->w = Q->w * Q->w - 1.; + Q->q = ess * P->rone_es; + Q->t = ess * (2. - P->es) * P->rone_es * P->rone_es; + Q->u = esc * P->rone_es; + Q->xj = P->one_es * P->one_es * P->one_es; + Q->rlm = PI * (1. / 248. + .5161290322580645); + Q->rlm2 = Q->rlm + TWOPI; + Q->a2 = Q->a4 = Q->b = Q->c1 = Q->c3 = 0.; + seraz0(0., 1., P); + for (lam = 9.; lam <= 81.0001; lam += 18.) + seraz0(lam, 4., P); + for (lam = 18; lam <= 72.0001; lam += 18.) + seraz0(lam, 2., P); + seraz0(90., 1., P); + Q->a2 /= 30.; + Q->a4 /= 60.; + Q->b /= 30.; + Q->c1 /= 15.; + Q->c3 /= 45.; + + P->inv = e_inverse; + P->fwd = e_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_lsat_selftest (void) {return 0;} +#else + +int pj_lsat_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=lsat +ellps=GRS80 +lat_1=0.5 +lat_2=2 +lsat=1 +path=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {18241950.01455855, 9998256.83982293494}, + {18746856.2533194572, 10215761.669925211}, + {18565503.6836331636, 9085039.14672705345}, + {19019696.9020289108, 9247763.0394328218}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {126.000423834530011, 0.00172378224025701425}, + {126.002213738256714, 0.00188015467480917966}, + {126.000734468914601, -0.00188015467480917966}, + {126.002524372641304, -0.00172378224025701425}, + }; + + return pj_generic_selftest (e_args, 0, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, 0, inv_in, e_inv_expect, 0); +} + - land = pj_param(P->ctx, P->params, "ilsat").i; - if (land <= 0 || land > 5) E_ERROR(-28); - path = pj_param(P->ctx, P->params, "ipath").i; - if (path <= 0 || path > (land <= 3 ? 251 : 233)) E_ERROR(-29); - if (land <= 3) { - P->lam0 = DEG_TO_RAD * 128.87 - TWOPI / 251. * path; - P->p22 = 103.2669323; - alf = DEG_TO_RAD * 99.092; - } else { - P->lam0 = DEG_TO_RAD * 129.3 - TWOPI / 233. * path; - P->p22 = 98.8841202; - alf = DEG_TO_RAD * 98.2; - } - P->p22 /= 1440.; - P->sa = sin(alf); - P->ca = cos(alf); - if (fabs(P->ca) < 1e-9) - P->ca = 1e-9; - esc = P->es * P->ca * P->ca; - ess = P->es * P->sa * P->sa; - P->w = (1. - esc) * P->rone_es; - P->w = P->w * P->w - 1.; - P->q = ess * P->rone_es; - P->t = ess * (2. - P->es) * P->rone_es * P->rone_es; - P->u = esc * P->rone_es; - P->xj = P->one_es * P->one_es * P->one_es; - P->rlm = PI * (1. / 248. + .5161290322580645); - P->rlm2 = P->rlm + TWOPI; - P->a2 = P->a4 = P->b = P->c1 = P->c3 = 0.; - seraz0(0., 1., P); - for (lam = 9.; lam <= 81.0001; lam += 18.) - seraz0(lam, 4., P); - for (lam = 18; lam <= 72.0001; lam += 18.) - seraz0(lam, 2., P); - seraz0(90., 1., P); - P->a2 /= 30.; - P->a4 /= 60.; - P->b /= 30.; - P->c1 /= 15.; - P->c3 /= 45.; - P->inv = e_inverse; P->fwd = e_forward; -ENDENTRY(P) +#endif diff --git a/src/PJ_mbt_fps.c b/src/PJ_mbt_fps.c index 3cd5e674..daa1555c 100644 --- a/src/PJ_mbt_fps.c +++ b/src/PJ_mbt_fps.c @@ -1,39 +1,112 @@ #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(mbt_fps, "McBryde-Thomas Flat-Pole Sine (No. 2)") "\n\tCyl., Sph."; -#define MAX_ITER 10 -#define LOOP_TOL 1e-7 + +#define MAX_ITER 10 +#define LOOP_TOL 1e-7 #define C1 0.45503 #define C2 1.36509 #define C3 1.41546 #define C_x 0.22248 #define C_y 1.44492 #define C1_2 0.33333333333333333333333333 -FORWARD(s_forward); /* spheroid */ - double k, V, t; - int i; - (void) P; - - k = C3 * sin(lp.phi); - for (i = MAX_ITER; i ; --i) { - t = lp.phi / C2; - lp.phi -= V = (C1 * sin(t) + sin(lp.phi) - k) / - (C1_2 * cos(t) + cos(lp.phi)); - if (fabs(V) < LOOP_TOL) - break; - } - t = lp.phi / C2; - xy.x = C_x * lp.lam * (1. + 3. * cos(lp.phi)/cos(t) ); - xy.y = C_y * sin(t); - return (xy); + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double k, V, t; + int i; + (void) P; + + k = C3 * sin(lp.phi); + for (i = MAX_ITER; i ; --i) { + t = lp.phi / C2; + lp.phi -= V = (C1 * sin(t) + sin(lp.phi) - k) / + (C1_2 * cos(t) + cos(lp.phi)); + if (fabs(V) < LOOP_TOL) + break; + } + t = lp.phi / C2; + xy.x = C_x * lp.lam * (1. + 3. * cos(lp.phi)/cos(t) ); + xy.y = C_y * sin(t); + return xy; } -INVERSE(s_inverse); /* spheroid */ - double t; - lp.phi = C2 * (t = aasin(P->ctx,xy.y / C_y)); - lp.lam = xy.x / (C_x * (1. + 3. * cos(lp.phi)/cos(t))); - lp.phi = aasin(P->ctx,(C1 * sin(t) + sin(lp.phi)) / C3); - return (lp); + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double t; + + lp.phi = C2 * (t = aasin(P->ctx,xy.y / C_y)); + lp.lam = xy.x / (C_x * (1. + 3. * cos(lp.phi)/cos(t))); + lp.phi = aasin(P->ctx,(C1 * sin(t) + sin(lp.phi)) / C3); + return (lp); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(mbt_fps) P->es = 0; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P) + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(mbt_fps) { + + P->es = 0; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + +#ifdef PJ_OMIT_SELFTEST +int pj_mbt_fps_selftest (void) {return 0;} +#else + +int pj_mbt_fps_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=mbt_fps +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 198798.176129849948, 125512.017254530627}, + { 198798.176129849948, -125512.017254530627}, + {-198798.176129849948, 125512.017254530627}, + {-198798.176129849948, -125512.017254530627}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.00201197086238270742, 0.000796711850174446003}, + { 0.00201197086238270742, -0.000796711850174446003}, + {-0.00201197086238270742, 0.000796711850174446003}, + {-0.00201197086238270742, -0.000796711850174446003}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_mbtfpp.c b/src/PJ_mbtfpp.c index 433d3c8f..688150fe 100644 --- a/src/PJ_mbtfpp.c +++ b/src/PJ_mbtfpp.c @@ -1,33 +1,114 @@ #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(mbtfpp, "McBride-Thomas Flat-Polar Parabolic") "\n\tCyl., Sph."; -#define CS .95257934441568037152 -#define FXC .92582009977255146156 -#define FYC 3.40168025708304504493 -#define C23 .66666666666666666666 -#define C13 .33333333333333333333 -#define ONEEPS 1.0000001 -FORWARD(s_forward); /* spheroid */ - (void) P; - lp.phi = asin(CS * sin(lp.phi)); - xy.x = FXC * lp.lam * (2. * cos(C23 * lp.phi) - 1.); - xy.y = FYC * sin(C13 * lp.phi); - return (xy); + +#define CS .95257934441568037152 +#define FXC .92582009977255146156 +#define FYC 3.40168025708304504493 +#define C23 .66666666666666666666 +#define C13 .33333333333333333333 +#define ONEEPS 1.0000001 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + (void) P; + + lp.phi = asin(CS * sin(lp.phi)); + xy.x = FXC * lp.lam * (2. * cos(C23 * lp.phi) - 1.); + xy.y = FYC * sin(C13 * lp.phi); + return xy; } -INVERSE(s_inverse); /* spheroid */ - lp.phi = xy.y / FYC; - if (fabs(lp.phi) >= 1.) { - if (fabs(lp.phi) > ONEEPS) I_ERROR - else lp.phi = (lp.phi < 0.) ? -HALFPI : HALFPI; - } else - lp.phi = asin(lp.phi); - lp.lam = xy.x / ( FXC * (2. * cos(C23 * (lp.phi *= 3.)) - 1.) ); - if (fabs(lp.phi = sin(lp.phi) / CS) >= 1.) { - if (fabs(lp.phi) > ONEEPS) I_ERROR - else lp.phi = (lp.phi < 0.) ? -HALFPI : HALFPI; - } else - lp.phi = asin(lp.phi); - return (lp); + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + + lp.phi = xy.y / FYC; + if (fabs(lp.phi) >= 1.) { + if (fabs(lp.phi) > ONEEPS) + I_ERROR + else + lp.phi = (lp.phi < 0.) ? -HALFPI : HALFPI; + } else + lp.phi = asin(lp.phi); + + lp.lam = xy.x / ( FXC * (2. * cos(C23 * (lp.phi *= 3.)) - 1.) ); + if (fabs(lp.phi = sin(lp.phi) / CS) >= 1.) { + if (fabs(lp.phi) > ONEEPS) + I_ERROR + else + lp.phi = (lp.phi < 0.) ? -HALFPI : HALFPI; + } else + lp.phi = asin(lp.phi); + + return lp; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(mbtfpp) P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P) + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(mbtfpp) { + + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + +#ifdef PJ_OMIT_SELFTEST +int pj_mbtfpp_selftest (void) {return 0;} +#else + +int pj_mbtfpp_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=mbtfpp +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + {206804.786929820373, 120649.762565792524}, + {206804.786929820373, -120649.762565792524}, + {-206804.786929820373, 120649.762565792524}, + {-206804.786929820373, -120649.762565792524}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + {0.00193395359462902698, 0.00082883725477665357}, + {0.00193395359462902698, -0.00082883725477665357}, + {-0.00193395359462902698, 0.00082883725477665357}, + {-0.00193395359462902698, -0.00082883725477665357}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_mbtfpq.c b/src/PJ_mbtfpq.c index 0d343fad..d382f45a 100644 --- a/src/PJ_mbtfpq.c +++ b/src/PJ_mbtfpq.c @@ -1,48 +1,121 @@ #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(mbtfpq, "McBryde-Thomas Flat-Polar Quartic") "\n\tCyl., Sph."; -#define NITER 20 -#define EPS 1e-7 + +#define NITER 20 +#define EPS 1e-7 #define ONETOL 1.000001 -#define C 1.70710678118654752440 -#define RC 0.58578643762690495119 -#define FYC 1.87475828462269495505 -#define RYC 0.53340209679417701685 -#define FXC 0.31245971410378249250 -#define RXC 3.20041258076506210122 -FORWARD(s_forward); /* spheroid */ - double th1, c; - int i; - (void) P; - - c = C * sin(lp.phi); - for (i = NITER; i; --i) { - lp.phi -= th1 = (sin(.5*lp.phi) + sin(lp.phi) - c) / - (.5*cos(.5*lp.phi) + cos(lp.phi)); - if (fabs(th1) < EPS) break; - } - xy.x = FXC * lp.lam * (1.0 + 2. * cos(lp.phi)/cos(0.5 * lp.phi)); - xy.y = FYC * sin(0.5 * lp.phi); - return (xy); +#define C 1.70710678118654752440 +#define RC 0.58578643762690495119 +#define FYC 1.87475828462269495505 +#define RYC 0.53340209679417701685 +#define FXC 0.31245971410378249250 +#define RXC 3.20041258076506210122 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double th1, c; + int i; + (void) P; + + c = C * sin(lp.phi); + for (i = NITER; i; --i) { + lp.phi -= th1 = (sin(.5*lp.phi) + sin(lp.phi) - c) / + (.5*cos(.5*lp.phi) + cos(lp.phi)); + if (fabs(th1) < EPS) break; + } + xy.x = FXC * lp.lam * (1.0 + 2. * cos(lp.phi)/cos(0.5 * lp.phi)); + xy.y = FYC * sin(0.5 * lp.phi); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double t; + + lp.phi = RYC * xy.y; + if (fabs(lp.phi) > 1.) { + if (fabs(lp.phi) > ONETOL) I_ERROR + else if (lp.phi < 0.) { t = -1.; lp.phi = -PI; } + else { t = 1.; lp.phi = PI; } + } else + lp.phi = 2. * asin(t = lp.phi); + lp.lam = RXC * xy.x / (1. + 2. * cos(lp.phi)/cos(0.5 * lp.phi)); + lp.phi = RC * (t + sin(lp.phi)); + if (fabs(lp.phi) > 1.) + if (fabs(lp.phi) > ONETOL) I_ERROR + else lp.phi = lp.phi < 0. ? -HALFPI : HALFPI; + else + lp.phi = asin(lp.phi); + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; } -INVERSE(s_inverse); /* spheroid */ - double t; - - lp.phi = RYC * xy.y; - if (fabs(lp.phi) > 1.) { - if (fabs(lp.phi) > ONETOL) I_ERROR - else if (lp.phi < 0.) { t = -1.; lp.phi = -PI; } - else { t = 1.; lp.phi = PI; } - } else - lp.phi = 2. * asin(t = lp.phi); - lp.lam = RXC * xy.x / (1. + 2. * cos(lp.phi)/cos(0.5 * lp.phi)); - lp.phi = RC * (t + sin(lp.phi)); - if (fabs(lp.phi) > 1.) - if (fabs(lp.phi) > ONETOL) I_ERROR - else lp.phi = lp.phi < 0. ? -HALFPI : HALFPI; - else - lp.phi = asin(lp.phi); - return (lp); + + +PJ *PROJECTION(mbtfpq) { + + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(mbtfpq) P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P) + +#ifdef PJ_OMIT_SELFTEST +int pj_mbtfpq_selftest (void) {return 0;} +#else + +int pj_mbtfpq_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=mbtfpq +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 209391.854738393013, 119161.040199054827}, + { 209391.854738393013, -119161.040199054827}, + {-209391.854738393013, 119161.040199054827}, + {-209391.854738393013, -119161.040199054827}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.00191010555824111571, 0.000839185447792341723}, + { 0.00191010555824111571, -0.000839185447792341723}, + {-0.00191010555824111571, 0.000839185447792341723}, + {-0.00191010555824111571, -0.000839185447792341723}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_merc.c b/src/PJ_merc.c index 4b991c1c..dd894bbd 100644 --- a/src/PJ_merc.c +++ b/src/PJ_merc.c @@ -1,47 +1,134 @@ #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(merc, "Mercator") "\n\tCyl, Sph&Ell\n\tlat_ts="; + #define EPS10 1.e-10 -FORWARD(e_forward); /* ellipsoid */ - if (fabs(fabs(lp.phi) - HALFPI) <= EPS10) F_ERROR; - xy.x = P->k0 * lp.lam; - xy.y = - P->k0 * log(pj_tsfn(lp.phi, sin(lp.phi), P->e)); - return (xy); + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + if (fabs(fabs(lp.phi) - HALFPI) <= EPS10) + F_ERROR; + xy.x = P->k0 * lp.lam; + xy.y = - P->k0 * log(pj_tsfn(lp.phi, sin(lp.phi), P->e)); + return xy; } -FORWARD(s_forward); /* spheroid */ - if (fabs(fabs(lp.phi) - HALFPI) <= EPS10) F_ERROR; - xy.x = P->k0 * lp.lam; - xy.y = P->k0 * log(tan(FORTPI + .5 * lp.phi)); - return (xy); + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + if (fabs(fabs(lp.phi) - HALFPI) <= EPS10) + F_ERROR; + xy.x = P->k0 * lp.lam; + xy.y = P->k0 * log(tan(FORTPI + .5 * lp.phi)); + return xy; } -INVERSE(e_inverse); /* ellipsoid */ - if ((lp.phi = pj_phi2(P->ctx, exp(- xy.y / P->k0), P->e)) == HUGE_VAL) I_ERROR; - lp.lam = xy.x / P->k0; - return (lp); + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + if ((lp.phi = pj_phi2(P->ctx, exp(- xy.y / P->k0), P->e)) == HUGE_VAL) + I_ERROR; + lp.lam = xy.x / P->k0; + return lp; } -INVERSE(s_inverse); /* spheroid */ - lp.phi = HALFPI - 2. * atan(exp(-xy.y / P->k0)); - lp.lam = xy.x / P->k0; - return (lp); + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + lp.phi = HALFPI - 2. * atan(exp(-xy.y / P->k0)); + lp.lam = xy.x / P->k0; + return lp; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(merc) - double phits=0.0; - int is_phits; - - if( (is_phits = pj_param(P->ctx, P->params, "tlat_ts").i) ) { - phits = fabs(pj_param(P->ctx, P->params, "rlat_ts").f); - if (phits >= HALFPI) E_ERROR(-24); - } - if (P->es) { /* ellipsoid */ - if (is_phits) - P->k0 = pj_msfn(sin(phits), cos(phits), P->es); - P->inv = e_inverse; - P->fwd = e_forward; - } else { /* sphere */ - if (is_phits) - P->k0 = cos(phits); - P->inv = s_inverse; - P->fwd = s_forward; - } -ENDENTRY(P) + + +static void freeup(PJ *P) { /* Destructor */ + pj_dealloc(P); +} + + +PJ *PROJECTION(merc) { + double phits=0.0; + int is_phits; + + if( (is_phits = pj_param(P->ctx, P->params, "tlat_ts").i) ) { + phits = fabs(pj_param(P->ctx, P->params, "rlat_ts").f); + if (phits >= HALFPI) E_ERROR(-24); + } + + if (P->es) { /* ellipsoid */ + if (is_phits) + P->k0 = pj_msfn(sin(phits), cos(phits), P->es); + P->inv = e_inverse; + P->fwd = e_forward; + } + + else { /* sphere */ + if (is_phits) + P->k0 = cos(phits); + P->inv = s_inverse; + P->fwd = s_forward; + } + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_merc_selftest (void) {return 0;} +#else + +int pj_merc_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=merc +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + char s_args[] = {"+proj=merc +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 222638.981586547132, 110579.965218249708}, + { 222638.981586547132, -110579.965218249112}, + {-222638.981586547132, 110579.965218249708}, + {-222638.981586547132, -110579.965218249112}, + }; + + XY s_fwd_expect[] = { + { 223402.144255274179, 111706.743574944077}, + { 223402.144255274179, -111706.743574944485}, + {-223402.144255274179, 111706.743574944077}, + {-223402.144255274179, -111706.743574944485}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + { 0.00179663056823904264, 0.00090436947522799056}, + { 0.00179663056823904264, -0.00090436947522799056}, + {-0.00179663056823904264, 0.00090436947522799056}, + {-0.00179663056823904264, -0.00090436947522799056}, + }; + + LP s_inv_expect[] = { + { 0.00179049310978382265, 0.000895246554845297135}, + { 0.00179049310978382265, -0.000895246554858019272}, + {-0.00179049310978382265, 0.000895246554845297135}, + {-0.00179049310978382265, -0.000895246554858019272}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif diff --git a/src/PJ_mill.c b/src/PJ_mill.c index c4cee7f5..2c7fa266 100644 --- a/src/PJ_mill.c +++ b/src/PJ_mill.c @@ -1,17 +1,92 @@ #define PJ_LIB__ -# include <projects.h> +#include <projects.h> + PROJ_HEAD(mill, "Miller Cylindrical") "\n\tCyl, Sph"; -FORWARD(s_forward); /* spheroid */ - (void) P; - xy.x = lp.lam; - xy.y = log(tan(FORTPI + lp.phi * .4)) * 1.25; - return (xy); + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + (void) P; + + xy.x = lp.lam; + xy.y = log(tan(FORTPI + lp.phi * .4)) * 1.25; + + return (xy); } -INVERSE(s_inverse); /* spheroid */ - (void) P; - lp.lam = xy.x; - lp.phi = 2.5 * (atan(exp(.8 * xy.y)) - FORTPI); - return (lp); + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + (void) P; + + lp.lam = xy.x; + lp.phi = 2.5 * (atan(exp(.8 * xy.y)) - FORTPI); + + return (lp); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(mill) P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P) + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(mill) { + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_mill_selftest (void) {return 0;} +#else + +int pj_mill_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=mill +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223402.144255274179, 111704.701754393827}, + { 223402.144255274179, -111704.701754396243}, + {-223402.144255274179, 111704.701754393827}, + {-223402.144255274179, -111704.701754396243}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.00179049310978382265, 0.000895246554873922024}, + { 0.00179049310978382265, -0.000895246554873922024}, + {-0.00179049310978382265, 0.000895246554873922024}, + {-0.00179049310978382265, -0.000895246554873922024}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_minimal.c b/src/PJ_minimal.c new file mode 100644 index 00000000..1108c4fa --- /dev/null +++ b/src/PJ_minimal.c @@ -0,0 +1,204 @@ +/*********************************************************************** + + A minimal example of a new proj.4 projection implementation + + ...and a verbose justification for some highly intrusive code + surgery + +************************************************************************ + +**The brief version:** + +In an attempt to make proj.4 code slightly more secure and much easier +to read and maintain, I'm trying to eliminate a few unfortunate design +decisions from the early days of proj.4 + +The work will be *very* intrusive, especially in the PJ_xxx segment of +the code tree, but great care has been taken to design a process that +can be implemented stepwise and localized, one projection at a time, +then finalized with a relatively small and concentrated work package. + +**The (very) long version:** + +Gerald I. Evenden's original design for the proj.4 projection system +is a beautiful example of software architecture, where a very limited +set of policy rules leads to a well defined hierarchical structure and +a high degree of both encapsulation and internal interoperability. + +In the proj.4 code, the policy rules are *enforced* by a system of +preprocessor macros for building the scaffolding for implementation +of a new projection. + +While this system of macros undeniably possesses the property of both +reducing repetitive code and enforcing policy, unfortunately it also +possesses two much less desirable properties: + +First, while enforcing policy, it also *hides* policy: The "beauty in +simplicity" of Gerald's design is hidden behind layers of macros, +whose architectural clarity do not match that of proj.4 in general. + +Second (and related), the macros make the source code look like +something only vaguely related to C, making it hard to read (an effect +that gets amplified to the tune of syntax highlighters getting confused +by the macros). + +While the policy rule enforcement macros can be eliminated in relatively +non-intrusive ways, a more fundamental flaw in the proj.4 use of macros +is found in the PJ_xxx.c files implementing the individual projections: +The use of internal redefinition of PJ, the fundamental proj data object, +through the use of the PROJ_PARMS__ macro, makes the sizeof (PJ) +fundamentally unknown to the calling pj_init function. + +This leads to code that is probably not in full conformance with the +C standard. + +It is also a memory management catastrophe waiting to happen. + +But first and foremost, it leads to some very clumsy initialization code, +where pj_init (the constructor function), needs to start the constsruction +process by asking the PJ_xxx function to do the memory allocation (because +pj_init does not know the size of the PROJ_PARMS-mangled PJ object being +instantiated). + +Then, after doing some initialization work, pj_init returns control to +PJ_xxx, asking it to finalize the initialization with the projection +specific parameters specified by the PROJ_PARMS__ macro. + +Behind the scenes, hidden by two layers of macros, what happens is even +worse, as a lot of the initialization code is duplicated in every PJ_xxx +file, rather than being centralized in the pj_init function. + +**Solution procedure:** + +Evidently, the way to eliminate this clumsyness will be to introduce an +opaque object, that is managed by tne individual PJ_xxx projection code, +and represented as a simple void-pointer in the PJ object. + +This can be done one projection code file at a time, working through the +code base as time permits (it will take at least a month). + +When a PJ_xxx file is on the surgical bench, it will also have its +ENTRYA/ENTRY0/ENTRY1/ENTRY2/ENDENTRY/etc. etc. macro-guts torn out and +replaced by the PROJECTION macro (introduced in projects.h). + +This leads to code that looks a lot more like real C, and hence is much +less confusing to both syntax higlighters and humans. It also leads +to code that, after all projections have been processed, with a final +sweep over the code base can be brought into the style of the code in +PJ_minimal.c + +In my humble opinion the result wil be a code base that is not only easier +to maintain, but also more welcoming to new contributors. + +And if proj is to expand its strong basis in projections into the fields +of geodetic transformations and general geometric geodesy, we will need +to be able to attract quite a few expert geodesist contributors. + +And since expert geodesists are not necessarily expert coders, a welcoming +code base is a real asset (to put the icing on the cake of the already +welcoming user- and developer community). + +Note that the entire process does not touch the algorithmic/mathematical +parts of the code at all - it is actuallly an attempt to make this part +stand out more clearly. + +--- + +The attached material is an attempt to show what happens if we remove +the layers of macros, and introduce a more centralized approach to +memory allocation and initialization. + +Please note, however, that the level of cantralization achieved here +is not yet fully supported by the proj.4 infrastructure: It is an +example, intended to show what can be achieved through a smooth, +gradual and safe refactoring of the existing layered macro system. + +In my humble opinion, this version makes the beauty of Gerald's design +much more evident than the current layered-macro-version. + +Thomas Knudsen, thokn@sdfe.dk, 2016-03-31 + +***********************************************************************/ + +#define PJ_LIB__ +#include <projects.h> +#include <assert.h> +PROJ_HEAD(minimal, "Minimal example (brief description goes here)"); + + +/* Projection specific elements for the PJ object */ +struct pj_opaque { + double a; + int b; +}; + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + /* Actual ellipsoidal forward code goes here */ + xy.y = lp.lam + P->es; + xy.x = lp.phi + 42; + return xy; +} + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + /* Actual spheroidal forward code goes here */ + xy.y = lp.lam + P->es; + xy.x = lp.phi + 42; + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + /* Actual ellipsoidal forward code goes here */ + lp.lam = xy.x - P->es; + lp.phi = xy.y - P->opaque->b; + return lp; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + /* Actual spheroidal forward code goes here */ + lp.lam = xy.x - P->es; + lp.phi = xy.y - P->opaque->b; + return lp; +} + + +static void freeup(PJ *P) { /* Destructor */ + if (P==0) + return; + /* Projection specific deallocation goes here */ + pj_dealloc (P->opaque); + pj_dealloc (P); + return; +} + + +PJ *pj_projection_specific_setup_minimal (PJ *P) { + pj_prepare (P, des_minimal, freeup, sizeof (struct pj_opaque)); + if (0==P->opaque) { + freeup (P); + return 0; + } + + P->opaque->a = 42.42; + P->opaque->b = 42; + + /* Spheroidal? */ + if (0==P->es) { + P->fwd = s_forward; + P->inv = s_inverse; + return P; + } + + /* Otherwise it's ellipsoidal */ + P->fwd = e_forward; + P->inv = e_inverse; + + return P; +} diff --git a/src/PJ_misrsom.c b/src/PJ_misrsom.c index 19518a1d..c164b1c7 100644 --- a/src/PJ_misrsom.c +++ b/src/PJ_misrsom.c @@ -12,49 +12,57 @@ * * and the following code change: * - * P->rlm = PI * (1. / 248. + .5161290322580645); + * Q->rlm = PI * (1. / 248. + .5161290322580645); * * changed to: * - * P->rlm = 0 + * Q->rlm = 0 * *****************************************************************************/ /* based upon Snyder and Linck, USGS-NMD */ -#define PROJ_PARMS__ \ - double a2, a4, b, c1, c3; \ - double q, t, u, w, p22, sa, ca, xj, rlm, rlm2; #define PJ_LIB__ #include <projects.h> + PROJ_HEAD(misrsom, "Space oblique for MISR") "\n\tCyl, Sph&Ell\n\tpath="; + #define TOL 1e-7 #define PI_HALFPI 4.71238898038468985766 #define TWOPI_HALFPI 7.85398163397448309610 -static void -seraz0(double lam, double mult, PJ *P) { +struct pj_opaque { + double a2, a4, b, c1, c3; + double q, t, u, w, p22, sa, ca, xj, rlm, rlm2; +}; + +static void seraz0(double lam, double mult, PJ *P) { + struct pj_opaque *Q = P->opaque; double sdsq, h, s, fc, sd, sq, d__1; lam *= DEG_TO_RAD; sd = sin(lam); sdsq = sd * sd; - s = P->p22 * P->sa * cos(lam) * sqrt((1. + P->t * sdsq) / (( - 1. + P->w * sdsq) * (1. + P->q * sdsq))); - d__1 = 1. + P->q * sdsq; - h = sqrt((1. + P->q * sdsq) / (1. + P->w * sdsq)) * ((1. + - P->w * sdsq) / (d__1 * d__1) - P->p22 * P->ca); - sq = sqrt(P->xj * P->xj + s * s); - P->b += fc = mult * (h * P->xj - s * s) / sq; - P->a2 += fc * cos(lam + lam); - P->a4 += fc * cos(lam * 4.); - fc = mult * s * (h + P->xj) / sq; - P->c1 += fc * cos(lam); - P->c3 += fc * cos(lam * 3.); + s = Q->p22 * Q->sa * cos(lam) * sqrt((1. + Q->t * sdsq) / (( + 1. + Q->w * sdsq) * (1. + Q->q * sdsq))); + d__1 = 1. + Q->q * sdsq; + h = sqrt((1. + Q->q * sdsq) / (1. + Q->w * sdsq)) * ((1. + + Q->w * sdsq) / (d__1 * d__1) - Q->p22 * Q->ca); + sq = sqrt(Q->xj * Q->xj + s * s); + Q->b += fc = mult * (h * Q->xj - s * s) / sq; + Q->a2 += fc * cos(lam + lam); + Q->a4 += fc * cos(lam * 4.); + fc = mult * s * (h + Q->xj) / sq; + Q->c1 += fc * cos(lam); + Q->c3 += fc * cos(lam * 3.); } -FORWARD(e_forward); /* ellipsoid */ + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; int l, nn; - double lamt, xlam, sdsq, c, d, s, lamdp, phidp, lampp, tanph, - lamtp, cl, sd, sp, fac, sav, tanphi; + double lamt, xlam, sdsq, c, d, s, lamdp, phidp, lampp, tanph; + double lamtp, cl, sd, sp, fac, sav, tanphi; if (lp.phi > HALFPI) lp.phi = HALFPI; @@ -64,65 +72,69 @@ FORWARD(e_forward); /* ellipsoid */ tanphi = tan(lp.phi); for (nn = 0;;) { sav = lampp; - lamtp = lp.lam + P->p22 * lampp; + lamtp = lp.lam + Q->p22 * lampp; cl = cos(lamtp); if (fabs(cl) < TOL) lamtp -= TOL; fac = lampp - sin(lampp) * (cl < 0. ? -HALFPI : HALFPI); for (l = 50; l; --l) { - lamt = lp.lam + P->p22 * sav; + lamt = lp.lam + Q->p22 * sav; if (fabs(c = cos(lamt)) < TOL) lamt -= TOL; - xlam = (P->one_es * tanphi * P->sa + sin(lamt) * P->ca) / c; + xlam = (P->one_es * tanphi * Q->sa + sin(lamt) * Q->ca) / c; lamdp = atan(xlam) + fac; if (fabs(fabs(sav) - fabs(lamdp)) < TOL) break; sav = lamdp; } - if (!l || ++nn >= 3 || (lamdp > P->rlm && lamdp < P->rlm2)) + if (!l || ++nn >= 3 || (lamdp > Q->rlm && lamdp < Q->rlm2)) break; - if (lamdp <= P->rlm) + if (lamdp <= Q->rlm) lampp = TWOPI_HALFPI; - else if (lamdp >= P->rlm2) + else if (lamdp >= Q->rlm2) lampp = HALFPI; } if (l) { sp = sin(lp.phi); - phidp = aasin(P->ctx,(P->one_es * P->ca * sp - P->sa * cos(lp.phi) * + phidp = aasin(P->ctx,(P->one_es * Q->ca * sp - Q->sa * cos(lp.phi) * sin(lamt)) / sqrt(1. - P->es * sp * sp)); tanph = log(tan(FORTPI + .5 * phidp)); sd = sin(lamdp); sdsq = sd * sd; - s = P->p22 * P->sa * cos(lamdp) * sqrt((1. + P->t * sdsq) - / ((1. + P->w * sdsq) * (1. + P->q * sdsq))); - d = sqrt(P->xj * P->xj + s * s); - xy.x = P->b * lamdp + P->a2 * sin(2. * lamdp) + P->a4 * + s = Q->p22 * Q->sa * cos(lamdp) * sqrt((1. + Q->t * sdsq) + / ((1. + Q->w * sdsq) * (1. + Q->q * sdsq))); + d = sqrt(Q->xj * Q->xj + s * s); + xy.x = Q->b * lamdp + Q->a2 * sin(2. * lamdp) + Q->a4 * sin(lamdp * 4.) - tanph * s / d; - xy.y = P->c1 * sd + P->c3 * sin(lamdp * 3.) + tanph * P->xj / d; + xy.y = Q->c1 * sd + Q->c3 * sin(lamdp * 3.) + tanph * Q->xj / d; } else xy.x = xy.y = HUGE_VAL; return xy; } -INVERSE(e_inverse); /* ellipsoid */ + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; int nn; double lamt, sdsq, s, lamdp, phidp, sppsq, dd, sd, sl, fac, scl, sav, spp; - lamdp = xy.x / P->b; + lamdp = xy.x / Q->b; nn = 50; do { - sav = lamdp; - sd = sin(lamdp); - sdsq = sd * sd; - s = P->p22 * P->sa * cos(lamdp) * sqrt((1. + P->t * sdsq) - / ((1. + P->w * sdsq) * (1. + P->q * sdsq))); - lamdp = xy.x + xy.y * s / P->xj - P->a2 * sin( - 2. * lamdp) - P->a4 * sin(lamdp * 4.) - s / P->xj * ( - P->c1 * sin(lamdp) + P->c3 * sin(lamdp * 3.)); - lamdp /= P->b; + sav = lamdp; + sd = sin(lamdp); + sdsq = sd * sd; + s = Q->p22 * Q->sa * cos(lamdp) * sqrt((1. + Q->t * sdsq) + / ((1. + Q->w * sdsq) * (1. + Q->q * sdsq))); + lamdp = xy.x + xy.y * s / Q->xj - Q->a2 * sin( + 2. * lamdp) - Q->a4 * sin(lamdp * 4.) - s / Q->xj * ( + Q->c1 * sin(lamdp) + Q->c3 * sin(lamdp * 3.)); + lamdp /= Q->b; } while (fabs(lamdp - sav) >= TOL && --nn); sl = sin(lamdp); - fac = exp(sqrt(1. + s * s / P->xj / P->xj) * (xy.y - - P->c1 * sl - P->c3 * sin(lamdp * 3.))); + fac = exp(sqrt(1. + s * s / Q->xj / Q->xj) * (xy.y - + Q->c1 * sl - Q->c3 * sin(lamdp * 3.))); phidp = 2. * (atan(fac) - FORTPI); dd = sl * sl; if (fabs(cos(lamdp)) < TOL) @@ -130,56 +142,142 @@ INVERSE(e_inverse); /* ellipsoid */ spp = sin(phidp); sppsq = spp * spp; lamt = atan(((1. - sppsq * P->rone_es) * tan(lamdp) * - P->ca - spp * P->sa * sqrt((1. + P->q * dd) * ( - 1. - sppsq) - sppsq * P->u) / cos(lamdp)) / (1. - sppsq - * (1. + P->u))); + Q->ca - spp * Q->sa * sqrt((1. + Q->q * dd) * ( + 1. - sppsq) - sppsq * Q->u) / cos(lamdp)) / (1. - sppsq + * (1. + Q->u))); sl = lamt >= 0. ? 1. : -1.; scl = cos(lamdp) >= 0. ? 1. : -1; lamt -= HALFPI * (1. - scl) * sl; - lp.lam = lamt - P->p22 * lamdp; - if (fabs(P->sa) < TOL) + lp.lam = lamt - Q->p22 * lamdp; + if (fabs(Q->sa) < TOL) lp.phi = aasin(P->ctx,spp / sqrt(P->one_es * P->one_es + P->es * sppsq)); else - lp.phi = atan((tan(lamdp) * cos(lamt) - P->ca * sin(lamt)) / - (P->one_es * P->sa)); + lp.phi = atan((tan(lamdp) * cos(lamt) - Q->ca * sin(lamt)) / + (P->one_es * Q->sa)); return lp; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(misrsom) + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(misrsom) { int path; double lam, alf, esc, ess; + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + path = pj_param(P->ctx, P->params, "ipath").i; if (path <= 0 || path > 233) E_ERROR(-29); P->lam0 = DEG_TO_RAD * 129.3056 - TWOPI / 233. * path; alf = 98.30382 * DEG_TO_RAD; - P->p22 = 98.88 / 1440.0; - - P->sa = sin(alf); - P->ca = cos(alf); - if (fabs(P->ca) < 1e-9) - P->ca = 1e-9; - esc = P->es * P->ca * P->ca; - ess = P->es * P->sa * P->sa; - P->w = (1. - esc) * P->rone_es; - P->w = P->w * P->w - 1.; - P->q = ess * P->rone_es; - P->t = ess * (2. - P->es) * P->rone_es * P->rone_es; - P->u = esc * P->rone_es; - P->xj = P->one_es * P->one_es * P->one_es; - P->rlm = 0; - P->rlm2 = P->rlm + TWOPI; - P->a2 = P->a4 = P->b = P->c1 = P->c3 = 0.; + Q->p22 = 98.88 / 1440.0; + + Q->sa = sin(alf); + Q->ca = cos(alf); + if (fabs(Q->ca) < 1e-9) + Q->ca = 1e-9; + esc = P->es * Q->ca * Q->ca; + ess = P->es * Q->sa * Q->sa; + Q->w = (1. - esc) * P->rone_es; + Q->w = Q->w * Q->w - 1.; + Q->q = ess * P->rone_es; + Q->t = ess * (2. - P->es) * P->rone_es * P->rone_es; + Q->u = esc * P->rone_es; + Q->xj = P->one_es * P->one_es * P->one_es; + Q->rlm = 0; + Q->rlm2 = Q->rlm + TWOPI; + Q->a2 = Q->a4 = Q->b = Q->c1 = Q->c3 = 0.; seraz0(0., 1., P); for (lam = 9.; lam <= 81.0001; lam += 18.) seraz0(lam, 4., P); for (lam = 18; lam <= 72.0001; lam += 18.) seraz0(lam, 2., P); seraz0(90., 1., P); - P->a2 /= 30.; - P->a4 /= 60.; - P->b /= 30.; - P->c1 /= 15.; - P->c3 /= 45.; - P->inv = e_inverse; P->fwd = e_forward; -ENDENTRY(P) + Q->a2 /= 30.; + Q->a4 /= 60.; + Q->b /= 30.; + Q->c1 /= 15.; + Q->c3 /= 45.; + + P->inv = e_inverse; + P->fwd = e_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_misrsom_selftest (void) {return 0;} +#else + +int pj_misrsom_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=misrsom +ellps=GRS80 +lat_1=0.5 +lat_2=2 +path=1"}; + char s_args[] = {"+proj=misrsom +a=6400000 +lat_1=0.5 +lat_2=2 +path=1"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {18556630.3683698252, 9533394.6753112711}, + {19041866.0067297369, 9707182.17532352544}, + {18816810.1301847994, 8647669.64980295487}, + {19252610.7845367305, 8778164.08580140397}, + }; + + XY s_fwd_expect[] = { + {18641249.2791703865, 9563342.53233416565}, + {19130982.4615812786, 9739539.59350463562}, + {18903483.5150115378, 8675064.50061797537}, + {19343388.3998006098, 8807471.90406848863}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {127.759503987730625, 0.00173515039622462014}, + {127.761295471077958, 0.00187196632421706517}, + {127.759775773557251, -0.00187196632421891525}, + {127.76156725690457, -0.00173515039622462014}, + }; + + LP s_inv_expect[] = { + {127.75950514818588, 0.00171623111593511971}, + {127.761290323778738, 0.00185412132880796244}, + {127.759780920856471, -0.00185412132880796244}, + {127.761566096449329, -0.00171623111593511971}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif diff --git a/src/PJ_mod_ster.c b/src/PJ_mod_ster.c index 185e95e3..4c217141 100644 --- a/src/PJ_mod_ster.c +++ b/src/PJ_mod_ster.c @@ -1,211 +1,540 @@ /* based upon Snyder and Linck, USGS-NMD */ -#define PROJ_PARMS__ \ - COMPLEX *zcoeff; \ - double cchio, schio; \ - int n; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(mil_os, "Miller Oblated Stereographic") "\n\tAzi(mod)"; PROJ_HEAD(lee_os, "Lee Oblated Stereographic") "\n\tAzi(mod)"; PROJ_HEAD(gs48, "Mod. Stererographics of 48 U.S.") "\n\tAzi(mod)"; PROJ_HEAD(alsk, "Mod. Stererographics of Alaska") "\n\tAzi(mod)"; PROJ_HEAD(gs50, "Mod. Stererographics of 50 U.S.") "\n\tAzi(mod)"; -#define EPSLN 1e-10 - -FORWARD(e_forward); /* ellipsoid */ - double sinlon, coslon, esphi, chi, schi, cchi, s; - COMPLEX p; - - sinlon = sin(lp.lam); - coslon = cos(lp.lam); - esphi = P->e * sin(lp.phi); - chi = 2. * atan(tan((HALFPI + lp.phi) * .5) * - pow((1. - esphi) / (1. + esphi), P->e * .5)) - HALFPI; - schi = sin(chi); - cchi = cos(chi); - s = 2. / (1. + P->schio * schi + P->cchio * cchi * coslon); - p.r = s * cchi * sinlon; - p.i = s * (P->cchio * schi - P->schio * cchi * coslon); - p = pj_zpoly1(p, P->zcoeff, P->n); - xy.x = p.r; - xy.y = p.i; - return xy; + +#define EPSLN 1e-12 + +struct pj_opaque { + COMPLEX *zcoeff; \ + double cchio, schio; \ + int n; +}; + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double sinlon, coslon, esphi, chi, schi, cchi, s; + COMPLEX p; + + sinlon = sin(lp.lam); + coslon = cos(lp.lam); + esphi = P->e * sin(lp.phi); + chi = 2. * atan(tan((HALFPI + lp.phi) * .5) * + pow((1. - esphi) / (1. + esphi), P->e * .5)) - HALFPI; + schi = sin(chi); + cchi = cos(chi); + s = 2. / (1. + Q->schio * schi + Q->cchio * cchi * coslon); + p.r = s * cchi * sinlon; + p.i = s * (Q->cchio * schi - Q->schio * cchi * coslon); + p = pj_zpoly1(p, Q->zcoeff, Q->n); + xy.x = p.r; + xy.y = p.i; + + return xy; } -INVERSE(e_inverse); /* ellipsoid */ - int nn; - COMPLEX p, fxy, fpxy, dp; - double den, rh, z, sinz, cosz, chi, phi, dphi, esphi; - - p.r = xy.x; - p.i = xy.y; - for (nn = 20; nn ;--nn) { - fxy = pj_zpolyd1(p, P->zcoeff, P->n, &fpxy); - fxy.r -= xy.x; - fxy.i -= xy.y; - den = fpxy.r * fpxy.r + fpxy.i * fpxy.i; - dp.r = -(fxy.r * fpxy.r + fxy.i * fpxy.i) / den; - dp.i = -(fxy.i * fpxy.r - fxy.r * fpxy.i) / den; - p.r += dp.r; - p.i += dp.i; - if ((fabs(dp.r) + fabs(dp.i)) <= EPSLN) - break; - } - if (nn) { - rh = hypot(p.r, p.i); - z = 2. * atan(.5 * rh); - sinz = sin(z); - cosz = cos(z); - lp.lam = P->lam0; - if (fabs(rh) <= EPSLN) { - lp.phi = P->phi0; - return lp; - } - chi = aasin(P->ctx, cosz * P->schio + p.i * sinz * P->cchio / rh); - phi = chi; - for (nn = 20; nn ;--nn) { - esphi = P->e * sin(phi); - dphi = 2. * atan(tan((HALFPI + chi) * .5) * - pow((1. + esphi) / (1. - esphi), P->e * .5)) - HALFPI - phi; - phi += dphi; - if (fabs(dphi) <= EPSLN) - break; - } - } - if (nn) { - lp.phi = phi; - lp.lam = atan2(p.r * sinz, rh * P->cchio * cosz - p.i * - P->schio * sinz); + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + int nn; + COMPLEX p, fxy, fpxy, dp; + double den, rh, z, sinz, cosz, chi, phi, dphi, esphi; + + p.r = xy.x; + p.i = xy.y; + for (nn = 20; nn ;--nn) { + fxy = pj_zpolyd1(p, Q->zcoeff, Q->n, &fpxy); + fxy.r -= xy.x; + fxy.i -= xy.y; + den = fpxy.r * fpxy.r + fpxy.i * fpxy.i; + dp.r = -(fxy.r * fpxy.r + fxy.i * fpxy.i) / den; + dp.i = -(fxy.i * fpxy.r - fxy.r * fpxy.i) / den; + p.r += dp.r; + p.i += dp.i; + if ((fabs(dp.r) + fabs(dp.i)) <= EPSLN) + break; + } + if (nn) { + rh = hypot(p.r, p.i); + z = 2. * atan(.5 * rh); + sinz = sin(z); + cosz = cos(z); + lp.lam = P->lam0; + if (fabs(rh) <= EPSLN) { + lp.phi = P->phi0; + return lp; + } + chi = aasin(P->ctx, cosz * Q->schio + p.i * sinz * Q->cchio / rh); + phi = chi; + for (nn = 20; nn ;--nn) { + esphi = P->e * sin(phi); + dphi = 2. * atan(tan((HALFPI + chi) * .5) * + pow((1. + esphi) / (1. - esphi), P->e * .5)) - HALFPI - phi; + phi += dphi; + if (fabs(dphi) <= EPSLN) + break; + } + } + if (nn) { + lp.phi = phi; + lp.lam = atan2(p.r * sinz, rh * Q->cchio * cosz - p.i * + Q->schio * sinz); } else - lp.lam = lp.phi = HUGE_VAL; - return lp; + lp.lam = lp.phi = HUGE_VAL; + return lp; } -FREEUP; if (P) pj_dalloc(P); } - static PJ * -setup(PJ *P) { /* general initialization */ - double esphi, chio; - - if (P->es) { - esphi = P->e * sin(P->phi0); - chio = 2. * atan(tan((HALFPI + P->phi0) * .5) * - pow((1. - esphi) / (1. + esphi), P->e * .5)) - HALFPI; - } else - chio = P->phi0; - P->schio = sin(chio); - P->cchio = cos(chio); - P->inv = e_inverse; P->fwd = e_forward; - return P; + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); } -ENTRY0(mil_os) - static COMPLEX /* Miller Oblated Stereographic */ -AB[] = { - {0.924500, 0.}, - {0., 0.}, - {0.019430, 0.} -}; - P->n = 2; - P->lam0 = DEG_TO_RAD * 20.; - P->phi0 = DEG_TO_RAD * 18.; - P->zcoeff = AB; - P->es = 0.; -ENDENTRY(setup(P)) -ENTRY0(lee_os) - static COMPLEX /* Lee Oblated Stereographic */ -AB[] = { - {0.721316, 0.}, - {0., 0.}, - {-0.0088162, -0.00617325} -}; +static void freeup (PJ *P) { + freeup_new (P); + return; +} - P->n = 2; - P->lam0 = DEG_TO_RAD * -165.; - P->phi0 = DEG_TO_RAD * -10.; - P->zcoeff = AB; - P->es = 0.; -ENDENTRY(setup(P)) -ENTRY0(gs48) - static COMPLEX /* 48 United States */ -AB[] = { - {0.98879, 0.}, - {0., 0.}, - {-0.050909, 0.}, - {0., 0.}, - {0.075528, 0.} -}; +static PJ *setup(PJ *P) { /* general initialization */ + struct pj_opaque *Q = P->opaque; + double esphi, chio; - P->n = 4; - P->lam0 = DEG_TO_RAD * -96.; - P->phi0 = DEG_TO_RAD * -39.; - P->zcoeff = AB; - P->es = 0.; - P->a = 6370997.; -ENDENTRY(setup(P)) -ENTRY0(alsk) - static COMPLEX -ABe[] = { /* Alaska ellipsoid */ - {.9945303, 0.}, - {.0052083, -.0027404}, - {.0072721, .0048181}, - {-.0151089, -.1932526}, - {.0642675, -.1381226}, - {.3582802, -.2884586}}, -ABs[] = { /* Alaska sphere */ - {.9972523, 0.}, - {.0052513, -.0041175}, - {.0074606, .0048125}, - {-.0153783, -.1968253}, - {.0636871, -.1408027}, - {.3660976, -.2937382} -}; + if (P->es) { + esphi = P->e * sin(P->phi0); + chio = 2. * atan(tan((HALFPI + P->phi0) * .5) * + pow((1. - esphi) / (1. + esphi), P->e * .5)) - HALFPI; + } else + chio = P->phi0; + Q->schio = sin(chio); + Q->cchio = cos(chio); + P->inv = e_inverse; + P->fwd = e_forward; + + return P; +} + + +/* Miller Oblated Stereographic */ +PJ *PROJECTION(mil_os) { + static COMPLEX AB[] = { + {0.924500, 0.}, + {0., 0.}, + {0.019430, 0.} + }; + + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->n = 2; + P->lam0 = DEG_TO_RAD * 20.; + P->phi0 = DEG_TO_RAD * 18.; + Q->zcoeff = AB; + P->es = 0.; + + return setup(P); +} + + +/* Lee Oblated Stereographic */ +PJ *PROJECTION(lee_os) { + static COMPLEX AB[] = { + {0.721316, 0.}, + {0., 0.}, + {-0.0088162, -0.00617325} + }; + + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->n = 2; + P->lam0 = DEG_TO_RAD * -165.; + P->phi0 = DEG_TO_RAD * -10.; + Q->zcoeff = AB; + P->es = 0.; + + return setup(P); +} + + +PJ *PROJECTION(gs48) { + static COMPLEX /* 48 United States */ + AB[] = { + {0.98879, 0.}, + {0., 0.}, + {-0.050909, 0.}, + {0., 0.}, + {0.075528, 0.} + }; + + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->n = 4; + P->lam0 = DEG_TO_RAD * -96.; + P->phi0 = DEG_TO_RAD * -39.; + Q->zcoeff = AB; + P->es = 0.; + P->a = 6370997.; + + return setup(P); +} + + +PJ *PROJECTION(alsk) { + static COMPLEX ABe[] = { /* Alaska ellipsoid */ + { .9945303, 0.}, + { .0052083, -.0027404}, + { .0072721, .0048181}, + {-.0151089, -.1932526}, + { .0642675, -.1381226}, + { .3582802, -.2884586}, + }; + + static COMPLEX ABs[] = { /* Alaska sphere */ + { .9972523, 0.}, + { .0052513, -.0041175}, + { .0074606, .0048125}, + {-.0153783, -.1968253}, + { .0636871, -.1408027}, + { .3660976, -.2937382} + }; + + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->n = 5; + P->lam0 = DEG_TO_RAD * -152.; + P->phi0 = DEG_TO_RAD * 64.; + if (P->es) { /* fixed ellipsoid/sphere */ + Q->zcoeff = ABe; + P->a = 6378206.4; + P->e = sqrt(P->es = 0.00676866); + } else { + Q->zcoeff = ABs; + P->a = 6370997.; + } + + return setup(P); +} + + +PJ *PROJECTION(gs50) { + static COMPLEX ABe[] = { /* GS50 ellipsoid */ + { .9827497, 0.}, + { .0210669, .0053804}, + {-.1031415, -.0571664}, + {-.0323337, -.0322847}, + { .0502303, .1211983}, + { .0251805, .0895678}, + {-.0012315, -.1416121}, + { .0072202, -.1317091}, + {-.0194029, .0759677}, + {-.0210072, .0834037} + }; + + static COMPLEX ABs[] = { /* GS50 sphere */ + { .9842990, 0.}, + { .0211642, .0037608}, + {-.1036018, -.0575102}, + {-.0329095, -.0320119}, + { .0499471, .1223335}, + { .0260460, .0899805}, + { .0007388, -.1435792}, + { .0075848, -.1334108}, + {-.0216473, .0776645}, + {-.0225161, .0853673} + }; + + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->n = 9; + P->lam0 = DEG_TO_RAD * -120.; + P->phi0 = DEG_TO_RAD * 45.; + if (P->es) { /* fixed ellipsoid/sphere */ + Q->zcoeff = ABe; + P->a = 6378206.4; + P->e = sqrt(P->es = 0.00676866); + } else { + Q->zcoeff = ABs; + P->a = 6370997.; + } + + return setup(P); +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_mil_os_selftest (void) {return 0;} +#else + +int pj_mil_os_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=mil_os +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + {-1908527.94959420455, -1726237.4730614475}, + {-1916673.02291848511, -1943133.88812552323}, + {-2344429.41208962305, -1706258.05121891224}, + {-2354637.83553299867, -1926468.60513541684}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + {20.0020363939492398, 18.0009683469140498}, + {20.0020363715837419, 17.999031631815086}, + {19.9979636060507602, 18.0009683469140498}, + {19.9979636284162581, 17.999031631815086}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + +#endif + + +#ifdef PJ_OMIT_SELFTEST +int pj_lee_os_selftest (void) {return 0;} +#else + +int pj_lee_os_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=lee_os +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + {-25564478.9526050538, 154490848.8286255}, + { 30115393.9385746419, 125193997.439701974}, + {-31039340.5921660066, 57678685.0448915437}, + {-3088419.93942357088, 58150091.0991110131}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + {-164.997479457813824, -9.99875886103541411}, + {-164.997479438558884, -10.0012411200022751}, + {-165.002520542186289, -9.99875886103545142}, + {-165.002520561440946, -10.0012411200022999}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + +#endif + + +#ifdef PJ_OMIT_SELFTEST +int pj_gs48_selftest (void) {return 0;} +#else + +int pj_gs48_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=gs48 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + {36625944.1923860237, -10443454.370062707}, + {32392147.7449533679, -12580705.358382076}, + {31394918.3206626177, -3352580.92795714363}, + {28553241.7702435851, -5673083.97367164213}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + {-95.997669975773789, -38.9990945807843801}, + {-95.9976699161415326, -39.0009053728726585}, + {-96.0023300242262252, -38.9990945807843801}, + {-96.0023300838584817, -39.0009053728726585}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + +#endif + + +#ifdef PJ_OMIT_SELFTEST +int pj_alsk_selftest (void) {return 0;} +#else + +int pj_alsk_selftest (void) { + + /* The standard test points are way outside the definition area bounds, hence we relax tolerances */ + double tolerance_lp = 1e-7; + double tolerance_xy = 1e-4; + + char e_args[] = {"+proj=alsk +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + char s_args[] = {"+proj=alsk +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {1642864080.02732754, -1139302009.02887797}, + {1991193850.2052319, -1518851027.13339996}, + { 896601956.44416213, -1580143099.4169271}, + {1018553822.30535674, -2028874099.61853552}, + }; + + XY s_fwd_expect[] = { + {1651105846.06753755, -1132233885.29928017}, + {2004822703.03845358, -1512578241.93150067}, + { 907039096.196665168, -1578565868.60405397}, + {1033243931.08174837, -2030485879.68075895}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {-151.99590140340095, 64.0008994430213534}, + {-151.995901665843263, 63.9991004415502687}, + {-152.004098597614217, 64.0008994436154524}, + {-152.004098335171818, 63.9991004409561413}, + }; + + LP s_inv_expect[] = { + {-151.995904192970613, 64.0008976554005926}, + {-151.995904455051942, 63.9991022295943921}, + {-152.004095808043218, 64.0008976559930716}, + {-152.004095545961775, 63.9991022290018208}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + +#endif + + +#ifdef PJ_OMIT_SELFTEST +int pj_gs50_selftest (void) {return 0;} +#else + +int pj_gs50_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=gs50 +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + char s_args[] = {"+proj=gs50 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {29729016747.3571701, -3918216218.3354063}, + {36735969814.5893631, -14677374776.9809761}, + {14161556831.8443203, -11410194658.0201168}, + {14213883663.1140423, -19065362387.8745575}, + }; + + XY s_fwd_expect[] = { + {30210185088.2270584, -3211636634.93554688}, + {37664745793.3519745, -13981519124.6516781}, + {14657726636.8231983, -11238369787.8965397}, + {14923510723.7682972, -19062616309.6883698}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {-119.997423271634844, 45.0009140704890811}, + {-119.997423354194112, 44.9990858708950014}, + {-120.00257673100937, 45.0009140729899428}, + {-120.002576648448965, 44.9990858683939408}, + }; + + LP s_inv_expect[] = { + {-119.997427429220934, 45.0009094967551704}, + {-119.997427511518453, 44.9990904451617482}, + {-120.002572573413815, 45.0009094992385954}, + {-120.00257249111516, 44.9990904426780602}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} - P->n = 5; - P->lam0 = DEG_TO_RAD * -152.; - P->phi0 = DEG_TO_RAD * 64.; - if (P->es) { /* fixed ellipsoid/sphere */ - P->zcoeff = ABe; - P->a = 6378206.4; - P->e = sqrt(P->es = 0.00676866); - } else { - P->zcoeff = ABs; - P->a = 6370997.; - } -ENDENTRY(setup(P)) -ENTRY0(gs50) - static COMPLEX -ABe[] = { /* GS50 ellipsoid */ - {.9827497, 0.}, - {.0210669, .0053804}, - {-.1031415, -.0571664}, - {-.0323337, -.0322847}, - {.0502303, .1211983}, - {.0251805, .0895678}, - {-.0012315, -.1416121}, - {.0072202, -.1317091}, - {-.0194029, .0759677}, - {-.0210072, .0834037} -}, -ABs[] = { /* GS50 sphere */ - {.9842990, 0.}, - {.0211642, .0037608}, - {-.1036018, -.0575102}, - {-.0329095, -.0320119}, - {.0499471, .1223335}, - {.0260460, .0899805}, - {.0007388, -.1435792}, - {.0075848, -.1334108}, - {-.0216473, .0776645}, - {-.0225161, .0853673} -}; - P->n = 9; - P->lam0 = DEG_TO_RAD * -120.; - P->phi0 = DEG_TO_RAD * 45.; - if (P->es) { /* fixed ellipsoid/sphere */ - P->zcoeff = ABe; - P->a = 6378206.4; - P->e = sqrt(P->es = 0.00676866); - } else { - P->zcoeff = ABs; - P->a = 6370997.; - } -ENDENTRY(setup(P)) +#endif diff --git a/src/PJ_moll.c b/src/PJ_moll.c index cf9369dc..31829b68 100644 --- a/src/PJ_moll.c +++ b/src/PJ_moll.c @@ -1,64 +1,259 @@ -#define PROJ_PARMS__ \ - double C_x, C_y, C_p; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(moll, "Mollweide") "\n\tPCyl., Sph."; PROJ_HEAD(wag4, "Wagner IV") "\n\tPCyl., Sph."; PROJ_HEAD(wag5, "Wagner V") "\n\tPCyl., Sph."; -#define MAX_ITER 10 -#define LOOP_TOL 1e-7 -FORWARD(s_forward); /* spheroid */ - double k, V; - int i; - - k = P->C_p * sin(lp.phi); - for (i = MAX_ITER; i ; --i) { - lp.phi -= V = (lp.phi + sin(lp.phi) - k) / - (1. + cos(lp.phi)); - if (fabs(V) < LOOP_TOL) - break; - } - if (!i) - lp.phi = (lp.phi < 0.) ? -HALFPI : HALFPI; - else - lp.phi *= 0.5; - xy.x = P->C_x * lp.lam * cos(lp.phi); - xy.y = P->C_y * sin(lp.phi); - return (xy); + +#define MAX_ITER 10 +#define LOOP_TOL 1e-7 + +struct pj_opaque { + double C_x, C_y, C_p; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double k, V; + int i; + + k = Q->C_p * sin(lp.phi); + for (i = MAX_ITER; i ; --i) { + lp.phi -= V = (lp.phi + sin(lp.phi) - k) / + (1. + cos(lp.phi)); + if (fabs(V) < LOOP_TOL) + break; + } + if (!i) + lp.phi = (lp.phi < 0.) ? -HALFPI : HALFPI; + else + lp.phi *= 0.5; + xy.x = Q->C_x * lp.lam * cos(lp.phi); + xy.y = Q->C_y * sin(lp.phi); + return xy; } -INVERSE(s_inverse); /* spheroid */ - lp.phi = aasin(P->ctx, xy.y / P->C_y); - lp.lam = xy.x / (P->C_x * cos(lp.phi)); + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + lp.phi = aasin(P->ctx, xy.y / Q->C_y); + lp.lam = xy.x / (Q->C_x * cos(lp.phi)); if (fabs(lp.lam) < PI) { lp.phi += lp.phi; - lp.phi = aasin(P->ctx, (lp.phi + sin(lp.phi)) / P->C_p); + lp.phi = aasin(P->ctx, (lp.phi + sin(lp.phi)) / Q->C_p); } else { lp.lam = lp.phi = HUGE_VAL; } - return (lp); + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +static PJ * setup(PJ *P, double p) { + struct pj_opaque *Q = P->opaque; + double r, sp, p2 = p + p; + + P->es = 0; + sp = sin(p); + r = sqrt(TWOPI * sp / (p2 + sin(p2))); + + Q->C_x = 2. * r / PI; + Q->C_y = r / sp; + Q->C_p = p2 + sin(p2); + + P->inv = s_inverse; + P->fwd = s_forward; + return P; +} + + +PJ *PROJECTION(moll) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + return setup(P, HALFPI); +} + + +PJ *PROJECTION(wag4) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + return setup(P, PI/3.); +} + +PJ *PROJECTION(wag5) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + P->es = 0; + Q->C_x = 0.90977; + Q->C_y = 1.65014; + Q->C_p = 3.00896; + + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_moll_selftest (void) {return 0;} +#else + +int pj_moll_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=moll +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + {201113.698641813244, 124066.283433859542}, + {201113.698641813244, -124066.283433859542}, + {-201113.698641813244, 124066.283433859542}, + {-201113.698641813244, -124066.283433859542}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + {0.00198873782220854774, 0.000806005080362811612}, + {0.00198873782220854774, -0.000806005080362811612}, + {-0.00198873782220854774, 0.000806005080362811612}, + {-0.00198873782220854774, -0.000806005080362811612}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); } -FREEUP; if (P) pj_dalloc(P); } - static PJ * -setup(PJ *P, double p) { - double r, sp, p2 = p + p; - - P->es = 0; - sp = sin(p); - r = sqrt(TWOPI * sp / (p2 + sin(p2))); - P->C_x = 2. * r / PI; - P->C_y = r / sp; - P->C_p = p2 + sin(p2); - P->inv = s_inverse; - P->fwd = s_forward; - return P; + +#endif + + +#ifdef PJ_OMIT_SELFTEST +int pj_wag4_selftest (void) {return 0;} +#else + +int pj_wag4_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=wag4 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 192801.218662384286, 129416.216394802992}, + { 192801.218662384286, -129416.216394802992}, + {-192801.218662384286, 129416.216394802992}, + {-192801.218662384286, -129416.216394802992}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.00207450259783523421, 0.000772682950537716476}, + { 0.00207450259783523421, -0.000772682950537716476}, + {-0.00207450259783523421, 0.000772682950537716476}, + {-0.00207450259783523421, -0.000772682950537716476}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); } -ENTRY0(moll) ENDENTRY(setup(P, HALFPI)) -ENTRY0(wag4) ENDENTRY(setup(P, PI/3.)) -ENTRY0(wag5) - P->es = 0; - P->C_x = 0.90977; - P->C_y = 1.65014; - P->C_p = 3.00896; - P->inv = s_inverse; - P->fwd = s_forward; -ENDENTRY(P) + +#endif + +#ifdef PJ_OMIT_SELFTEST +int pj_wag5_selftest (void) {return 0;} +#else + +int pj_wag5_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=wag5 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + + XY s_fwd_expect[] = { + { 203227.05192532466, 138651.631442713202}, + { 203227.05192532466, -138651.631442713202}, + {-203227.05192532466, 138651.631442713202}, + {-203227.05192532466, -138651.631442713202}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + + + + LP s_inv_expect[] = { + { 0.00196807227086416396, 0.00072121615041701424}, + { 0.00196807227086416396, -0.00072121615041701424}, + {-0.00196807227086416396, 0.00072121615041701424}, + {-0.00196807227086416396, -0.00072121615041701424}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_natearth.c b/src/PJ_natearth.c index 0f283415..290f8efe 100644 --- a/src/PJ_natearth.c +++ b/src/PJ_natearth.c @@ -12,10 +12,11 @@ where they meet the horizontal pole line. This improvement is by intention and designed in collaboration with Tom Patterson. Port to PROJ.4 by Bernhard Jenny, 6 June 2011 */ - #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(natearth, "Natural Earth") "\n\tPCyl., Sph."; + #define A0 0.8707 #define A1 -0.131979 #define A2 -0.013791 @@ -34,46 +35,116 @@ PROJ_HEAD(natearth, "Natural Earth") "\n\tPCyl., Sph."; #define EPS 1e-11 #define MAX_Y (0.8707 * 0.52 * PI) -FORWARD(s_forward); /* spheroid */ - double phi2, phi4; - (void) P; - phi2 = lp.phi * lp.phi; - phi4 = phi2 * phi2; - xy.x = lp.lam * (A0 + phi2 * (A1 + phi2 * (A2 + phi4 * phi2 * (A3 + phi2 * A4)))); - xy.y = lp.phi * (B0 + phi2 * (B1 + phi4 * (B2 + B3 * phi2 + B4 * phi4))); - return (xy); +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double phi2, phi4; + (void) P; + + phi2 = lp.phi * lp.phi; + phi4 = phi2 * phi2; + xy.x = lp.lam * (A0 + phi2 * (A1 + phi2 * (A2 + phi4 * phi2 * (A3 + phi2 * A4)))); + xy.y = lp.phi * (B0 + phi2 * (B1 + phi4 * (B2 + B3 * phi2 + B4 * phi4))); + return xy; } -INVERSE(s_inverse); /* spheroid */ - double yc, tol, y2, y4, f, fder; - (void) P; - - /* make sure y is inside valid range */ - if (xy.y > MAX_Y) { - xy.y = MAX_Y; - } else if (xy.y < -MAX_Y) { - xy.y = -MAX_Y; - } - - /* latitude */ - yc = xy.y; + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double yc, tol, y2, y4, f, fder; + (void) P; + + /* make sure y is inside valid range */ + if (xy.y > MAX_Y) { + xy.y = MAX_Y; + } else if (xy.y < -MAX_Y) { + xy.y = -MAX_Y; + } + + /* latitude */ + yc = xy.y; for (;;) { /* Newton-Raphson */ - y2 = yc * yc; - y4 = y2 * y2; - f = (yc * (B0 + y2 * (B1 + y4 * (B2 + B3 * y2 + B4 * y4)))) - xy.y; - fder = C0 + y2 * (C1 + y4 * (C2 + C3 * y2 + C4 * y4)); - yc -= tol = f / fder; - if (fabs(tol) < EPS) { - break; - } - } - lp.phi = yc; - - /* longitude */ - y2 = yc * yc; - lp.lam = xy.x / (A0 + y2 * (A1 + y2 * (A2 + y2 * y2 * y2 * (A3 + y2 * A4)))); - - return (lp); + y2 = yc * yc; + y4 = y2 * y2; + f = (yc * (B0 + y2 * (B1 + y4 * (B2 + B3 * y2 + B4 * y4)))) - xy.y; + fder = C0 + y2 * (C1 + y4 * (C2 + C3 * y2 + C4 * y4)); + yc -= tol = f / fder; + if (fabs(tol) < EPS) { + break; + } + } + lp.phi = yc; + + /* longitude */ + y2 = yc * yc; + lp.lam = xy.x / (A0 + y2 * (A1 + y2 * (A2 + y2 * y2 * y2 * (A3 + y2 * A4)))); + + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(natearth) { + P->es = 0; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + +#ifdef PJ_OMIT_SELFTEST +int pj_natearth_selftest (void) {return 0;} +#else + +int pj_natearth_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=natearth +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 194507.265257889288, 112508.737358294515}, + { 194507.265257889288, -112508.737358294515}, + {-194507.265257889288, 112508.737358294515}, + {-194507.265257889288, -112508.737358294515}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.00205638349586440223, 0.000888823913291242177}, + { 0.00205638349586440223, -0.000888823913291242177}, + {-0.00205638349586440223, 0.000888823913291242177}, + {-0.00205638349586440223, -0.000888823913291242177}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(natearth) P->es = 0; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P) + + +#endif diff --git a/src/PJ_natearth2.c b/src/PJ_natearth2.c index c3776a8b..5a57fb14 100644 --- a/src/PJ_natearth2.c +++ b/src/PJ_natearth2.c @@ -1,13 +1,13 @@ /* -The Natural Earth II projection was designed by Tom Patterson, US National +The Natural Earth II projection was designed by Tom Patterson, US National Park Service, in 2012, using Flex Projector. The polynomial equation was -developed by Bojan Savric and Bernhard Jenny, College of Earth, Ocean, +developed by Bojan Savric and Bernhard Jenny, College of Earth, Ocean, and Atmospheric Sciences, Oregon State University. Port to PROJ.4 by Bojan Savric, 4 April 2016 */ - #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(natearth2, "Natural Earth 2") "\n\tPCyl., Sph."; #define A0 0.84719 @@ -27,49 +27,121 @@ PROJ_HEAD(natearth2, "Natural Earth 2") "\n\tPCyl., Sph."; #define EPS 1e-11 #define MAX_Y (0.84719 * 0.535117535153096 * PI) -FORWARD(s_forward); /* spheroid */ - double phi2, phi4, phi6; - phi2 = lp.phi * lp.phi; - phi4 = phi2 * phi2; - phi6 = phi2 * phi4; +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double phi2, phi4, phi6; + (void) P; - xy.x = lp.lam * (A0 + A1 * phi2 + phi6 * phi6 * (A2 + A3 * phi2 + A4 * phi4 + A5 * phi6)); - xy.y = lp.phi * (B0 + phi4 * phi4 * (B1 + B2 * phi2 + B3 * phi4)); - return (xy); + phi2 = lp.phi * lp.phi; + phi4 = phi2 * phi2; + phi6 = phi2 * phi4; + + xy.x = lp.lam * (A0 + A1 * phi2 + phi6 * phi6 * (A2 + A3 * phi2 + A4 * phi4 + A5 * phi6)); + xy.y = lp.phi * (B0 + phi4 * phi4 * (B1 + B2 * phi2 + B3 * phi4)); + return xy; } -INVERSE(s_inverse); /* spheroid */ - double yc, tol, y2, y4, y6, f, fder; + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double yc, tol, y2, y4, y6, f, fder; + (void) P; /* make sure y is inside valid range */ - if (xy.y > MAX_Y) { - xy.y = MAX_Y; - } else if (xy.y < -MAX_Y) { - xy.y = -MAX_Y; - } + if (xy.y > MAX_Y) { + xy.y = MAX_Y; + } else if (xy.y < -MAX_Y) { + xy.y = -MAX_Y; + } /* latitude */ - yc = xy.y; + yc = xy.y; for (;;) { /* Newton-Raphson */ - y2 = yc * yc; - y4 = y2 * y2; - f = (yc * (B0 + y4 * y4 * (B1 + B2 * y2 + B3 * y4))) - xy.y; - fder = C0 + y4 * y4 * (C1 + C2 * y2 + C3 * y4); - yc -= tol = f / fder; - if (fabs(tol) < EPS) { - break; - } - } - lp.phi = yc; + y2 = yc * yc; + y4 = y2 * y2; + f = (yc * (B0 + y4 * y4 * (B1 + B2 * y2 + B3 * y4))) - xy.y; + fder = C0 + y4 * y4 * (C1 + C2 * y2 + C3 * y4); + yc -= tol = f / fder; + if (fabs(tol) < EPS) { + break; + } + } + lp.phi = yc; /* longitude */ - y2 = yc * yc; - y4 = y2 * y2; - y6 = y2 * y4; + y2 = yc * yc; + y4 = y2 * y2; + y6 = y2 * y4; + + lp.lam = xy.x / (A0 + A1 * y2 + y6 * y6 * (A2 + A3 * y2 + A4 * y4 + A5 * y6)); + + return lp; +} + - lp.lam = xy.x / (A0 + A1 * y2 + y6 * y6 * (A2 + A3 * y2 + A4 * y4 + A5 * y6)); +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; - return (lp); + return pj_dealloc(P); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(natearth2) P->es = 0; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P) + + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(natearth2) { + P->es = 0; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + +#ifdef PJ_OMIT_SELFTEST +int pj_natearth2_selftest (void) {return 0;} +#else + +int pj_natearth2_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=natearth2 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 189255.172934730799, 113022.495810907014}, + { 189255.172934730799, -113022.495810907014}, + {-189255.172934730799, 113022.495810907014}, + {-189255.172934730799, -113022.495810907014}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.00211344929691056112, 0.000884779612080993237}, + { 0.00211344929691056112, -0.000884779612080993237}, + {-0.00211344929691056112, 0.000884779612080993237}, + {-0.00211344929691056112, -0.000884779612080993237}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_nell.c b/src/PJ_nell.c index 77983de8..b69b9997 100644 --- a/src/PJ_nell.c +++ b/src/PJ_nell.c @@ -1,30 +1,106 @@ #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(nell, "Nell") "\n\tPCyl., Sph."; -#define MAX_ITER 10 -#define LOOP_TOL 1e-7 -FORWARD(s_forward); /* spheroid */ - double k, V; - int i; - (void) P; - - k = 2. * sin(lp.phi); - V = lp.phi * lp.phi; - lp.phi *= 1.00371 + V * (-0.0935382 + V * -0.011412); - for (i = MAX_ITER; i ; --i) { - lp.phi -= V = (lp.phi + sin(lp.phi) - k) / - (1. + cos(lp.phi)); - if (fabs(V) < LOOP_TOL) - break; - } - xy.x = 0.5 * lp.lam * (1. + cos(lp.phi)); - xy.y = lp.phi; - return (xy); + +#define MAX_ITER 10 +#define LOOP_TOL 1e-7 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double k, V; + int i; + (void) P; + + k = 2. * sin(lp.phi); + V = lp.phi * lp.phi; + lp.phi *= 1.00371 + V * (-0.0935382 + V * -0.011412); + for (i = MAX_ITER; i ; --i) { + lp.phi -= V = (lp.phi + sin(lp.phi) - k) / + (1. + cos(lp.phi)); + if (fabs(V) < LOOP_TOL) + break; + } + xy.x = 0.5 * lp.lam * (1. + cos(lp.phi)); + xy.y = lp.phi; + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + lp.lam = 2. * xy.x / (1. + cos(xy.y)); + lp.phi = aasin(P->ctx,0.5 * (xy.y + sin(xy.y))); + + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(nell) { + + P->es = 0; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; } -INVERSE(s_inverse); /* spheroid */ - lp.lam = 2. * xy.x / (1. + cos(xy.y)); - lp.phi = aasin(P->ctx,0.5 * (xy.y + sin(xy.y))); - return (lp); + +#ifdef PJ_OMIT_SELFTEST +int pj_nell_selftest (void) {return 0;} +#else + +int pj_nell_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=nell +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223385.132504695706, 111698.23644718733}, + { 223385.132504695706, -111698.23644718733}, + {-223385.132504695706, 111698.23644718733}, + {-223385.132504695706, -111698.23644718733}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.00179049310989310567, 0.000895246554910125161}, + { 0.00179049310989310567, -0.000895246554910125161}, + {-0.00179049310989310567, 0.000895246554910125161}, + {-0.00179049310989310567, -0.000895246554910125161}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(nell) P->es = 0; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P) + + +#endif diff --git a/src/PJ_nell_h.c b/src/PJ_nell_h.c index 22061ea9..1e863945 100644 --- a/src/PJ_nell_h.c +++ b/src/PJ_nell_h.c @@ -1,32 +1,109 @@ #define PJ_LIB__ -# include <projects.h> +#include <projects.h> + PROJ_HEAD(nell_h, "Nell-Hammer") "\n\tPCyl., Sph."; + #define NITER 9 #define EPS 1e-7 -FORWARD(s_forward); /* spheroid */ - (void) P; - xy.x = 0.5 * lp.lam * (1. + cos(lp.phi)); - xy.y = 2.0 * (lp.phi - tan(0.5 *lp.phi)); - return (xy); + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + (void) P; + + xy.x = 0.5 * lp.lam * (1. + cos(lp.phi)); + xy.y = 2.0 * (lp.phi - tan(0.5 *lp.phi)); + + return xy; } -INVERSE(s_inverse); /* spheroid */ - double V, c, p; - int i; - (void) P; - - p = 0.5 * xy.y; - for (i = NITER; i ; --i) { - c = cos(0.5 * lp.phi); - lp.phi -= V = (lp.phi - tan(lp.phi/2) - p)/(1. - 0.5/(c*c)); - if (fabs(V) < EPS) - break; - } - if (!i) { - lp.phi = p < 0. ? -HALFPI : HALFPI; - lp.lam = 2. * xy.x; - } else - lp.lam = 2. * xy.x / (1. + cos(lp.phi)); - return (lp); + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double V, c, p; + int i; + (void) P; + + p = 0.5 * xy.y; + for (i = NITER; i ; --i) { + c = cos(0.5 * lp.phi); + lp.phi -= V = (lp.phi - tan(lp.phi/2) - p)/(1. - 0.5/(c*c)); + if (fabs(V) < EPS) + break; + } + if (!i) { + lp.phi = p < 0. ? -HALFPI : HALFPI; + lp.lam = 2. * xy.x; + } else + lp.lam = 2. * xy.x / (1. + cos(lp.phi)); + + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(nell_h) P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P) + + +PJ *PROJECTION(nell_h) { + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_nell_h_selftest (void) {return 0;} +#else + +int pj_nell_h_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=nell_h +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223385.131640952837, 111698.236533561678}, + { 223385.131640952837, -111698.236533561678}, + {-223385.131640952837, 111698.236533561678}, + {-223385.131640952837, -111698.236533561678}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.00179049310989310567, 0.000895246554910125378}, + { 0.00179049310989310567, -0.000895246554910125378}, + {-0.00179049310989310567, 0.000895246554910125378}, + {-0.00179049310989310567, -0.000895246554910125378}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_nocol.c b/src/PJ_nocol.c index 2b005e1d..ad96ff31 100644 --- a/src/PJ_nocol.c +++ b/src/PJ_nocol.c @@ -1,40 +1,96 @@ #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(nicol, "Nicolosi Globular") "\n\tMisc Sph, no inv."; -#define EPS 1e-10 -FORWARD(s_forward); /* spheroid */ - (void) P; - - if (fabs(lp.lam) < EPS) { - xy.x = 0; - xy.y = lp.phi; - } else if (fabs(lp.phi) < EPS) { - xy.x = lp.lam; - xy.y = 0.; - } else if (fabs(fabs(lp.lam) - HALFPI) < EPS) { - xy.x = lp.lam * cos(lp.phi); - xy.y = HALFPI * sin(lp.phi); - } else if (fabs(fabs(lp.phi) - HALFPI) < EPS) { - xy.x = 0; - xy.y = lp.phi; - } else { - double tb, c, d, m, n, r2, sp; - - tb = HALFPI / lp.lam - lp.lam / HALFPI; - c = lp.phi / HALFPI; - d = (1 - c * c)/((sp = sin(lp.phi)) - c); - r2 = tb / d; - r2 *= r2; - m = (tb * sp / d - 0.5 * tb)/(1. + r2); - n = (sp / r2 + 0.5 * d)/(1. + 1./r2); - xy.x = cos(lp.phi); - xy.x = sqrt(m * m + xy.x * xy.x / (1. + r2)); - xy.x = HALFPI * ( m + (lp.lam < 0. ? -xy.x : xy.x)); - xy.y = sqrt(n * n - (sp * sp / r2 + d * sp - 1.) / - (1. + 1./r2)); - xy.y = HALFPI * ( n + (lp.phi < 0. ? xy.y : -xy.y )); - } - return (xy); + +#define EPS 1e-10 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + (void) P; + + if (fabs(lp.lam) < EPS) { + xy.x = 0; + xy.y = lp.phi; + } else if (fabs(lp.phi) < EPS) { + xy.x = lp.lam; + xy.y = 0.; + } else if (fabs(fabs(lp.lam) - HALFPI) < EPS) { + xy.x = lp.lam * cos(lp.phi); + xy.y = HALFPI * sin(lp.phi); + } else if (fabs(fabs(lp.phi) - HALFPI) < EPS) { + xy.x = 0; + xy.y = lp.phi; + } else { + double tb, c, d, m, n, r2, sp; + + tb = HALFPI / lp.lam - lp.lam / HALFPI; + c = lp.phi / HALFPI; + d = (1 - c * c)/((sp = sin(lp.phi)) - c); + r2 = tb / d; + r2 *= r2; + m = (tb * sp / d - 0.5 * tb)/(1. + r2); + n = (sp / r2 + 0.5 * d)/(1. + 1./r2); + xy.x = cos(lp.phi); + xy.x = sqrt(m * m + xy.x * xy.x / (1. + r2)); + xy.x = HALFPI * ( m + (lp.lam < 0. ? -xy.x : xy.x)); + xy.y = sqrt(n * n - (sp * sp / r2 + d * sp - 1.) / + (1. + 1./r2)); + xy.y = HALFPI * ( n + (lp.phi < 0. ? xy.y : -xy.y )); + } + return (xy); +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + + return pj_dealloc(P); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(nicol) P->es = 0.; P->fwd = s_forward; ENDENTRY(P) + + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(nicol) { + P->es = 0.; + P->fwd = s_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_nicol_selftest (void) {return 0;} +#else + +int pj_nicol_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=nicol +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223374.561814139714, 111732.553988545071}, + { 223374.561814139714, -111732.553988545071}, + {-223374.561814139714, 111732.553988545071}, + {-223374.561814139714, -111732.553988545071}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, 0, 0, 0); +} + + +#endif diff --git a/src/PJ_nsper.c b/src/PJ_nsper.c index 0f355b93..7e3fcda8 100644 --- a/src/PJ_nsper.c +++ b/src/PJ_nsper.c @@ -1,149 +1,287 @@ -#define PROJ_PARMS__ \ - double height; \ - double sinph0; \ - double cosph0; \ - double p; \ - double rp; \ - double pn1; \ - double pfact; \ - double h; \ - double cg; \ - double sg; \ - double sw; \ - double cw; \ - int mode; \ - int tilt; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + +struct pj_opaque { + double height; + double sinph0; + double cosph0; + double p; + double rp; + double pn1; + double pfact; + double h; + double cg; + double sg; + double sw; + double cw; + int mode; + int tilt; +}; + PROJ_HEAD(nsper, "Near-sided perspective") "\n\tAzi, Sph\n\th="; PROJ_HEAD(tpers, "Tilted perspective") "\n\tAzi, Sph\n\ttilt= azi= h="; + # define EPS10 1.e-10 -# define N_POLE 0 +# define N_POLE 0 # define S_POLE 1 -# define EQUIT 2 -# define OBLIQ 3 -FORWARD(s_forward); /* spheroid */ - double coslam, cosphi, sinphi; - - sinphi = sin(lp.phi); - cosphi = cos(lp.phi); - coslam = cos(lp.lam); - switch (P->mode) { - case OBLIQ: - xy.y = P->sinph0 * sinphi + P->cosph0 * cosphi * coslam; - break; - case EQUIT: - xy.y = cosphi * coslam; - break; - case S_POLE: - xy.y = - sinphi; - break; - case N_POLE: - xy.y = sinphi; - break; - } - if (xy.y < P->rp) F_ERROR; - xy.y = P->pn1 / (P->p - xy.y); - xy.x = xy.y * cosphi * sin(lp.lam); - switch (P->mode) { - case OBLIQ: - xy.y *= (P->cosph0 * sinphi - - P->sinph0 * cosphi * coslam); - break; - case EQUIT: - xy.y *= sinphi; - break; - case N_POLE: - coslam = - coslam; - case S_POLE: - xy.y *= cosphi * coslam; - break; - } - if (P->tilt) { - double yt, ba; - - yt = xy.y * P->cg + xy.x * P->sg; - ba = 1. / (yt * P->sw * P->h + P->cw); - xy.x = (xy.x * P->cg - xy.y * P->sg) * P->cw * ba; - xy.y = yt * ba; - } - return (xy); +# define EQUIT 2 +# define OBLIQ 3 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double coslam, cosphi, sinphi; + + sinphi = sin(lp.phi); + cosphi = cos(lp.phi); + coslam = cos(lp.lam); + switch (Q->mode) { + case OBLIQ: + xy.y = Q->sinph0 * sinphi + Q->cosph0 * cosphi * coslam; + break; + case EQUIT: + xy.y = cosphi * coslam; + break; + case S_POLE: + xy.y = - sinphi; + break; + case N_POLE: + xy.y = sinphi; + break; + } + if (xy.y < Q->rp) F_ERROR; + xy.y = Q->pn1 / (Q->p - xy.y); + xy.x = xy.y * cosphi * sin(lp.lam); + switch (Q->mode) { + case OBLIQ: + xy.y *= (Q->cosph0 * sinphi - + Q->sinph0 * cosphi * coslam); + break; + case EQUIT: + xy.y *= sinphi; + break; + case N_POLE: + coslam = - coslam; + case S_POLE: + xy.y *= cosphi * coslam; + break; + } + if (Q->tilt) { + double yt, ba; + + yt = xy.y * Q->cg + xy.x * Q->sg; + ba = 1. / (yt * Q->sw * Q->h + Q->cw); + xy.x = (xy.x * Q->cg - xy.y * Q->sg) * Q->cw * ba; + xy.y = yt * ba; + } + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double rh, cosz, sinz; + + if (Q->tilt) { + double bm, bq, yt; + + yt = 1./(Q->pn1 - xy.y * Q->sw); + bm = Q->pn1 * xy.x * yt; + bq = Q->pn1 * xy.y * Q->cw * yt; + xy.x = bm * Q->cg + bq * Q->sg; + xy.y = bq * Q->cg - bm * Q->sg; + } + rh = hypot(xy.x, xy.y); + if ((sinz = 1. - rh * rh * Q->pfact) < 0.) I_ERROR; + sinz = (Q->p - sqrt(sinz)) / (Q->pn1 / rh + rh / Q->pn1); + cosz = sqrt(1. - sinz * sinz); + if (fabs(rh) <= EPS10) { + lp.lam = 0.; + lp.phi = P->phi0; + } else { + switch (Q->mode) { + case OBLIQ: + lp.phi = asin(cosz * Q->sinph0 + xy.y * sinz * Q->cosph0 / rh); + xy.y = (cosz - Q->sinph0 * sin(lp.phi)) * rh; + xy.x *= sinz * Q->cosph0; + break; + case EQUIT: + lp.phi = asin(xy.y * sinz / rh); + xy.y = cosz * rh; + xy.x *= sinz; + break; + case N_POLE: + lp.phi = asin(cosz); + xy.y = -xy.y; + break; + case S_POLE: + lp.phi = - asin(cosz); + break; + } + lp.lam = atan2(xy.x, xy.y); + } + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; } -INVERSE(s_inverse); /* spheroid */ - double rh, cosz, sinz; - - if (P->tilt) { - double bm, bq, yt; - - yt = 1./(P->pn1 - xy.y * P->sw); - bm = P->pn1 * xy.x * yt; - bq = P->pn1 * xy.y * P->cw * yt; - xy.x = bm * P->cg + bq * P->sg; - xy.y = bq * P->cg - bm * P->sg; - } - rh = hypot(xy.x, xy.y); - if ((sinz = 1. - rh * rh * P->pfact) < 0.) I_ERROR; - sinz = (P->p - sqrt(sinz)) / (P->pn1 / rh + rh / P->pn1); - cosz = sqrt(1. - sinz * sinz); - if (fabs(rh) <= EPS10) { - lp.lam = 0.; - lp.phi = P->phi0; - } else { - switch (P->mode) { - case OBLIQ: - lp.phi = asin(cosz * P->sinph0 + xy.y * sinz * P->cosph0 / rh); - xy.y = (cosz - P->sinph0 * sin(lp.phi)) * rh; - xy.x *= sinz * P->cosph0; - break; - case EQUIT: - lp.phi = asin(xy.y * sinz / rh); - xy.y = cosz * rh; - xy.x *= sinz; - break; - case N_POLE: - lp.phi = asin(cosz); - xy.y = -xy.y; - break; - case S_POLE: - lp.phi = - asin(cosz); - break; - } - lp.lam = atan2(xy.x, xy.y); - } - return (lp); + + +static PJ *setup(PJ *P) { + struct pj_opaque *Q = P->opaque; + + if ((Q->height = pj_param(P->ctx, P->params, "dh").f) <= 0.) E_ERROR(-30); + if (fabs(fabs(P->phi0) - HALFPI) < EPS10) + Q->mode = P->phi0 < 0. ? S_POLE : N_POLE; + else if (fabs(P->phi0) < EPS10) + Q->mode = EQUIT; + else { + Q->mode = OBLIQ; + Q->sinph0 = sin(P->phi0); + Q->cosph0 = cos(P->phi0); + } + Q->pn1 = Q->height / P->a; /* normalize by radius */ + Q->p = 1. + Q->pn1; + Q->rp = 1. / Q->p; + Q->h = 1. / Q->pn1; + Q->pfact = (Q->p + 1.) * Q->h; + P->inv = s_inverse; + P->fwd = s_forward; + P->es = 0.; + return P; } -FREEUP; if (P) pj_dalloc(P); } - static PJ * -setup(PJ *P) { - if ((P->height = pj_param(P->ctx, P->params, "dh").f) <= 0.) E_ERROR(-30); - if (fabs(fabs(P->phi0) - HALFPI) < EPS10) - P->mode = P->phi0 < 0. ? S_POLE : N_POLE; - else if (fabs(P->phi0) < EPS10) - P->mode = EQUIT; - else { - P->mode = OBLIQ; - P->sinph0 = sin(P->phi0); - P->cosph0 = cos(P->phi0); - } - P->pn1 = P->height / P->a; /* normalize by radius */ - P->p = 1. + P->pn1; - P->rp = 1. / P->p; - P->h = 1. / P->pn1; - P->pfact = (P->p + 1.) * P->h; - P->inv = s_inverse; - P->fwd = s_forward; - P->es = 0.; - return P; + + +PJ *PROJECTION(nsper) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->tilt = 0; + + return setup(P); } -ENTRY0(nsper) - P->tilt = 0; -ENDENTRY(setup(P)) -ENTRY0(tpers) - double omega, gamma; - - omega = pj_param(P->ctx, P->params, "dtilt").f * DEG_TO_RAD; - gamma = pj_param(P->ctx, P->params, "dazi").f * DEG_TO_RAD; - P->tilt = 1; - P->cg = cos(gamma); P->sg = sin(gamma); - P->cw = cos(omega); P->sw = sin(omega); -ENDENTRY(setup(P)) + + +PJ *PROJECTION(tpers) { + double omega, gamma; + + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + omega = pj_param(P->ctx, P->params, "dtilt").f * DEG_TO_RAD; + gamma = pj_param(P->ctx, P->params, "dazi").f * DEG_TO_RAD; + Q->tilt = 1; + Q->cg = cos(gamma); Q->sg = sin(gamma); + Q->cw = cos(omega); Q->sw = sin(omega); + + return setup(P); +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_nsper_selftest (void) {return 0;} +#else + +int pj_nsper_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=nsper +a=6400000 +h=1000000"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 222239.816114099842, 111153.763991924759}, + { 222239.816114099842, -111153.763991924759}, + {-222239.816114099842, 111153.763991924759}, + {-222239.816114099842, -111153.763991924759}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.00179049311728792437, 0.000895246558425396135}, + { 0.00179049311728792437, -0.000895246558425396135}, + {-0.00179049311728792437, 0.000895246558425396135}, + {-0.00179049311728792437, -0.000895246558425396135}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif + + +#ifdef PJ_OMIT_SELFTEST +int pj_tpers_selftest (void) {return 0;} +#else + +int pj_tpers_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=tpers +a=6400000 +h=1000000 +azi=20"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 170820.288955531199, 180460.865555804776}, + { 246853.941538942483, -28439.8780357754222}, + {-246853.941538942483, 28439.8780357754222}, + {-170820.288955531199, -180460.865555804776} + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.00198870552603137678, 0.000228871872278689991}, + { 0.00137632081376749859, -0.00145364129728205432}, + {-0.00137632081376749859, 0.00145364129728205432}, + {-0.00198870552603137678, -0.000228871872278689991}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_nzmg.c b/src/PJ_nzmg.c index 60df0041..8dcb2634 100644 --- a/src/PJ_nzmg.c +++ b/src/PJ_nzmg.c @@ -25,79 +25,153 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * DEALINGS IN THE SOFTWARE. *****************************************************************************/ - #define PJ_LIB__ -#include <projects.h> +#include <projects.h> PROJ_HEAD(nzmg, "New Zealand Map Grid") "\n\tfixed Earth"; #define EPSLN 1e-10 #define SEC5_TO_RAD 0.4848136811095359935899141023 #define RAD_TO_SEC5 2.062648062470963551564733573 - static COMPLEX -bf[] = { - {.7557853228, 0.0}, - {.249204646, .003371507}, - {-.001541739, .041058560}, - {-.10162907, .01727609}, - {-.26623489, -.36249218}, - {-.6870983, -1.1651967} }; - static double -tphi[] = { 1.5627014243, .5185406398, -.03333098, -.1052906, -.0368594, - .007317, .01220, .00394, -.0013 }, -tpsi[] = { .6399175073, -.1358797613, .063294409, -.02526853, .0117879, - -.0055161, .0026906, -.001333, .00067, -.00034 }; + +static COMPLEX bf[] = { + { .7557853228, 0.0}, + { .249204646, 0.003371507}, + {-.001541739, 0.041058560}, + {-.10162907, 0.01727609}, + {-.26623489, -0.36249218}, + {-.6870983, -1.1651967} }; + +static double tphi[] = { 1.5627014243, .5185406398, -.03333098, + -.1052906, -.0368594, .007317, + .01220, .00394, -.0013 }; + +static double tpsi[] = { .6399175073, -.1358797613, .063294409, -.02526853, .0117879, + -.0055161, .0026906, -.001333, .00067, -.00034 }; + #define Nbf 5 #define Ntpsi 9 #define Ntphi 8 -FORWARD(e_forward); /* ellipsoid */ - COMPLEX p; - double *C; - int i; - - lp.phi = (lp.phi - P->phi0) * RAD_TO_SEC5; - for (p.r = *(C = tpsi + (i = Ntpsi)); i ; --i) - p.r = *--C + lp.phi * p.r; - p.r *= lp.phi; - p.i = lp.lam; - p = pj_zpoly1(p, bf, Nbf); - xy.x = p.i; - xy.y = p.r; - return xy; + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + COMPLEX p; + double *C; + int i; + + lp.phi = (lp.phi - P->phi0) * RAD_TO_SEC5; + for (p.r = *(C = tpsi + (i = Ntpsi)); i ; --i) + p.r = *--C + lp.phi * p.r; + p.r *= lp.phi; + p.i = lp.lam; + p = pj_zpoly1(p, bf, Nbf); + xy.x = p.i; + xy.y = p.r; + + return xy; } -INVERSE(e_inverse); /* ellipsoid */ - int nn, i; - COMPLEX p, f, fp, dp; - double den, *C; - - p.r = xy.y; - p.i = xy.x; - for (nn = 20; nn ;--nn) { - f = pj_zpolyd1(p, bf, Nbf, &fp); - f.r -= xy.y; - f.i -= xy.x; - den = fp.r * fp.r + fp.i * fp.i; - p.r += dp.r = -(f.r * fp.r + f.i * fp.i) / den; - p.i += dp.i = -(f.i * fp.r - f.r * fp.i) / den; - if ((fabs(dp.r) + fabs(dp.i)) <= EPSLN) - break; - } - if (nn) { - lp.lam = p.i; - for (lp.phi = *(C = tphi + (i = Ntphi)); i ; --i) - lp.phi = *--C + p.r * lp.phi; - lp.phi = P->phi0 + p.r * lp.phi * SEC5_TO_RAD; - } else - lp.lam = lp.phi = HUGE_VAL; - return lp; + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + int nn, i; + COMPLEX p, f, fp, dp; + double den, *C; + + p.r = xy.y; + p.i = xy.x; + for (nn = 20; nn ;--nn) { + f = pj_zpolyd1(p, bf, Nbf, &fp); + f.r -= xy.y; + f.i -= xy.x; + den = fp.r * fp.r + fp.i * fp.i; + p.r += dp.r = -(f.r * fp.r + f.i * fp.i) / den; + p.i += dp.i = -(f.i * fp.r - f.r * fp.i) / den; + if ((fabs(dp.r) + fabs(dp.i)) <= EPSLN) + break; + } + if (nn) { + lp.lam = p.i; + for (lp.phi = *(C = tphi + (i = Ntphi)); i ; --i) + lp.phi = *--C + p.r * lp.phi; + lp.phi = P->phi0 + p.r * lp.phi * SEC5_TO_RAD; + } else + lp.lam = lp.phi = HUGE_VAL; + + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(nzmg) - /* force to International major axis */ - P->ra = 1. / (P->a = 6378388.0); - P->lam0 = DEG_TO_RAD * 173.; - P->phi0 = DEG_TO_RAD * -41.; - P->x0 = 2510000.; - P->y0 = 6023150.; - P->inv = e_inverse; P->fwd = e_forward; -ENDENTRY(P) + + +PJ *PROJECTION(nzmg) { + /* force to International major axis */ + P->ra = 1. / (P->a = 6378388.0); + P->lam0 = DEG_TO_RAD * 173.; + P->phi0 = DEG_TO_RAD * -41.; + P->x0 = 2510000.; + P->y0 = 6023150.; + + P->inv = e_inverse; + P->fwd = e_forward; + + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_nzmg_selftest (void) {return 0;} +#else + +int pj_nzmg_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=nzmg +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {3352675144.74742508, -7043205391.10024357}, + {3691989502.77930641, -6729069415.33210468}, + {4099000768.45323849, -7863208779.66724873}, + {4466166927.36997604, -7502531736.62860489}, + }; + + XY inv_in[] = { + { 200000, 100000}, + { 200000,-100000}, + {-200000, 100000}, + {-200000,-100000} + }; + + LP e_inv_expect[] = { + {175.48208682711271, -69.4226921826331846}, + {175.756819472543611, -69.5335710883796168}, + {134.605119233460016, -61.4599957106629091}, + {134.333684315954827, -61.6215536756024349}, + }; + + return pj_generic_selftest (e_args, 0, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, 0, inv_in, e_inv_expect, 0); +} + + +#endif diff --git a/src/PJ_ob_tran.c b/src/PJ_ob_tran.c index 4ddba9a4..e726b3b2 100644 --- a/src/PJ_ob_tran.c +++ b/src/PJ_ob_tran.c @@ -1,145 +1,225 @@ -#define PROJ_PARMS__ \ - struct PJconsts *link; \ - double lamp; \ - double cphip, sphip; #define PJ_LIB__ #include <projects.h> #include <string.h> + +struct pj_opaque { + struct PJconsts *link; + double lamp; + double cphip, sphip; +}; + PROJ_HEAD(ob_tran, "General Oblique Transformation") "\n\tMisc Sph" "\n\to_proj= plus parameters for projection" "\n\to_lat_p= o_lon_p= (new pole) or" "\n\to_alpha= o_lon_c= o_lat_c= or" "\n\to_lon_1= o_lat_1= o_lon_2= o_lat_2="; + #define TOL 1e-10 -FORWARD(o_forward); /* spheroid */ - double coslam, sinphi, cosphi; - - (void) xy; - - coslam = cos(lp.lam); - sinphi = sin(lp.phi); - cosphi = cos(lp.phi); - lp.lam = adjlon(aatan2(cosphi * sin(lp.lam), P->sphip * cosphi * coslam + - P->cphip * sinphi) + P->lamp); - lp.phi = aasin(P->ctx,P->sphip * sinphi - P->cphip * cosphi * coslam); - return (P->link->fwd(lp, P->link)); + + +static XY o_forward(LP lp, PJ *P) { /* spheroid */ + struct pj_opaque *Q = P->opaque; + double coslam, sinphi, cosphi; + + coslam = cos(lp.lam); + sinphi = sin(lp.phi); + cosphi = cos(lp.phi); + lp.lam = adjlon(aatan2(cosphi * sin(lp.lam), Q->sphip * cosphi * coslam + + Q->cphip * sinphi) + Q->lamp); + lp.phi = aasin(P->ctx,Q->sphip * sinphi - Q->cphip * cosphi * coslam); + + return Q->link->fwd(lp, Q->link); +} + + +static XY t_forward(LP lp, PJ *P) { /* spheroid */ + struct pj_opaque *Q = P->opaque; + double cosphi, coslam; + + cosphi = cos(lp.phi); + coslam = cos(lp.lam); + lp.lam = adjlon(aatan2(cosphi * sin(lp.lam), sin(lp.phi)) + Q->lamp); + lp.phi = aasin(P->ctx, - cosphi * coslam); + + return Q->link->fwd(lp, Q->link); } -FORWARD(t_forward); /* spheroid */ - double cosphi, coslam; - (void) xy; - cosphi = cos(lp.phi); - coslam = cos(lp.lam); - lp.lam = adjlon(aatan2(cosphi * sin(lp.lam), sin(lp.phi)) + P->lamp); - lp.phi = aasin(P->ctx, - cosphi * coslam); - return (P->link->fwd(lp, P->link)); +static LP o_inverse(XY xy, PJ *P) { /* spheroid */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double coslam, sinphi, cosphi; + + lp = Q->link->inv(xy, Q->link); + if (lp.lam != HUGE_VAL) { + coslam = cos(lp.lam -= Q->lamp); + sinphi = sin(lp.phi); + cosphi = cos(lp.phi); + lp.phi = aasin(P->ctx,Q->sphip * sinphi + Q->cphip * cosphi * coslam); + lp.lam = aatan2(cosphi * sin(lp.lam), Q->sphip * cosphi * coslam - + Q->cphip * sinphi); + } + return lp; } -INVERSE(o_inverse); /* spheroid */ - double coslam, sinphi, cosphi; - - lp = P->link->inv(xy, P->link); - if (lp.lam != HUGE_VAL) { - coslam = cos(lp.lam -= P->lamp); - sinphi = sin(lp.phi); - cosphi = cos(lp.phi); - lp.phi = aasin(P->ctx,P->sphip * sinphi + P->cphip * cosphi * coslam); - lp.lam = aatan2(cosphi * sin(lp.lam), P->sphip * cosphi * coslam - - P->cphip * sinphi); - } - return (lp); + + +static LP t_inverse(XY xy, PJ *P) { /* spheroid */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double cosphi, t; + + lp = Q->link->inv(xy, Q->link); + if (lp.lam != HUGE_VAL) { + cosphi = cos(lp.phi); + t = lp.lam - Q->lamp; + lp.lam = aatan2(cosphi * sin(t), - sin(lp.phi)); + lp.phi = aasin(P->ctx,cosphi * cos(t)); + } + return lp; } -INVERSE(t_inverse); /* spheroid */ - double cosphi, t; - - lp = P->link->inv(xy, P->link); - if (lp.lam != HUGE_VAL) { - cosphi = cos(lp.phi); - t = lp.lam - P->lamp; - lp.lam = aatan2(cosphi * sin(t), - sin(lp.phi)); - lp.phi = aasin(P->ctx,cosphi * cos(t)); - } - return (lp); + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + if (P->opaque->link) + return pj_dealloc (P->opaque->link); + + pj_dealloc (P->opaque); + return pj_dealloc(P); } -FREEUP; - if (P) { - if (P->link) - (*(P->link->pfree))(P->link); - pj_dalloc(P); - } + + +static void freeup (PJ *P) { + freeup_new (P); + return; } -ENTRY1(ob_tran, link) - int i; - double phip; - char *name, *s; - - /* get name of projection to be translated */ - if (!(name = pj_param(P->ctx, P->params, "so_proj").s)) E_ERROR(-26); - for (i = 0; (s = pj_list[i].id) && strcmp(name, s) ; ++i) ; - if (!s || !(P->link = (*pj_list[i].proj)(0))) E_ERROR(-37); - /* copy existing header into new */ - P->es = 0.; /* force to spherical */ - P->link->params = P->params; - P->link->ctx = P->ctx; - P->link->over = P->over; - P->link->geoc = P->geoc; - P->link->a = P->a; - P->link->es = P->es; - P->link->ra = P->ra; - P->link->lam0 = P->lam0; - P->link->phi0 = P->phi0; - P->link->x0 = P->x0; - P->link->y0 = P->y0; - P->link->k0 = P->k0; - /* force spherical earth */ - P->link->one_es = P->link->rone_es = 1.; - P->link->es = P->link->e = 0.; - if (!(P->link = pj_list[i].proj(P->link))) { - freeup(P); - return 0; - } - if (pj_param(P->ctx, P->params, "to_alpha").i) { - double lamc, phic, alpha; - - lamc = pj_param(P->ctx, P->params, "ro_lon_c").f; - phic = pj_param(P->ctx, P->params, "ro_lat_c").f; - alpha = pj_param(P->ctx, P->params, "ro_alpha").f; + + +PJ *PROJECTION(ob_tran) { + int i; + double phip; + char *name, *s; + + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + /* get name of projection to be translated */ + if (!(name = pj_param(P->ctx, P->params, "so_proj").s)) E_ERROR(-26); + for (i = 0; (s = pj_list[i].id) && strcmp(name, s) ; ++i) ; + if (!s || !(Q->link = (*pj_list[i].proj)(0))) E_ERROR(-37); + /* copy existing header into new */ + P->es = 0.; /* force to spherical */ + Q->link->params = P->params; + Q->link->ctx = P->ctx; + Q->link->over = P->over; + Q->link->geoc = P->geoc; + Q->link->a = P->a; + Q->link->es = P->es; + Q->link->ra = P->ra; + Q->link->lam0 = P->lam0; + Q->link->phi0 = P->phi0; + Q->link->x0 = P->x0; + Q->link->y0 = P->y0; + Q->link->k0 = P->k0; + /* force spherical earth */ + Q->link->one_es = Q->link->rone_es = 1.; + Q->link->es = Q->link->e = 0.; + if (!(Q->link = pj_list[i].proj(Q->link))) { + return freeup_new(P); + } + if (pj_param(P->ctx, P->params, "to_alpha").i) { + double lamc, phic, alpha; + + lamc = pj_param(P->ctx, P->params, "ro_lon_c").f; + phic = pj_param(P->ctx, P->params, "ro_lat_c").f; + alpha = pj_param(P->ctx, P->params, "ro_alpha").f; /* - if (fabs(phic) <= TOL || - fabs(fabs(phic) - HALFPI) <= TOL || - fabs(fabs(alpha) - HALFPI) <= TOL) + if (fabs(phic) <= TOL || + fabs(fabs(phic) - HALFPI) <= TOL || + fabs(fabs(alpha) - HALFPI) <= TOL) */ - if (fabs(fabs(phic) - HALFPI) <= TOL) - E_ERROR(-32); - P->lamp = lamc + aatan2(-cos(alpha), -sin(alpha) * sin(phic)); - phip = aasin(P->ctx,cos(phic) * sin(alpha)); - } else if (pj_param(P->ctx, P->params, "to_lat_p").i) { /* specified new pole */ - P->lamp = pj_param(P->ctx, P->params, "ro_lon_p").f; - phip = pj_param(P->ctx, P->params, "ro_lat_p").f; - } else { /* specified new "equator" points */ - double lam1, lam2, phi1, phi2, con; - - lam1 = pj_param(P->ctx, P->params, "ro_lon_1").f; - phi1 = pj_param(P->ctx, P->params, "ro_lat_1").f; - lam2 = pj_param(P->ctx, P->params, "ro_lon_2").f; - phi2 = pj_param(P->ctx, P->params, "ro_lat_2").f; - if (fabs(phi1 - phi2) <= TOL || - (con = fabs(phi1)) <= TOL || - fabs(con - HALFPI) <= TOL || - fabs(fabs(phi2) - HALFPI) <= TOL) E_ERROR(-33); - P->lamp = atan2(cos(phi1) * sin(phi2) * cos(lam1) - - sin(phi1) * cos(phi2) * cos(lam2), - sin(phi1) * cos(phi2) * sin(lam2) - - cos(phi1) * sin(phi2) * sin(lam1)); - phip = atan(-cos(P->lamp - lam1) / tan(phi1)); - } - if (fabs(phip) > TOL) { /* oblique */ - P->cphip = cos(phip); - P->sphip = sin(phip); - P->fwd = o_forward; - P->inv = P->link->inv ? o_inverse : 0; - } else { /* transverse */ - P->fwd = t_forward; - P->inv = P->link->inv ? t_inverse : 0; - } -ENDENTRY(P) + if (fabs(fabs(phic) - HALFPI) <= TOL) + E_ERROR(-32); + Q->lamp = lamc + aatan2(-cos(alpha), -sin(alpha) * sin(phic)); + phip = aasin(P->ctx,cos(phic) * sin(alpha)); + } else if (pj_param(P->ctx, P->params, "to_lat_p").i) { /* specified new pole */ + Q->lamp = pj_param(P->ctx, P->params, "ro_lon_p").f; + phip = pj_param(P->ctx, P->params, "ro_lat_p").f; + } else { /* specified new "equator" points */ + double lam1, lam2, phi1, phi2, con; + + lam1 = pj_param(P->ctx, P->params, "ro_lon_1").f; + phi1 = pj_param(P->ctx, P->params, "ro_lat_1").f; + lam2 = pj_param(P->ctx, P->params, "ro_lon_2").f; + phi2 = pj_param(P->ctx, P->params, "ro_lat_2").f; + if (fabs(phi1 - phi2) <= TOL || + (con = fabs(phi1)) <= TOL || + fabs(con - HALFPI) <= TOL || + fabs(fabs(phi2) - HALFPI) <= TOL) E_ERROR(-33); + Q->lamp = atan2(cos(phi1) * sin(phi2) * cos(lam1) - + sin(phi1) * cos(phi2) * cos(lam2), + sin(phi1) * cos(phi2) * sin(lam2) - + cos(phi1) * sin(phi2) * sin(lam1)); + phip = atan(-cos(Q->lamp - lam1) / tan(phi1)); + } + if (fabs(phip) > TOL) { /* oblique */ + Q->cphip = cos(phip); + Q->sphip = sin(phip); + P->fwd = o_forward; + P->inv = Q->link->inv ? o_inverse : 0; + } else { /* transverse */ + P->fwd = t_forward; + P->inv = Q->link->inv ? t_inverse : 0; + } + + return P; +} + +#ifdef PJ_OMIT_SELFTEST +int pj_ob_tran_selftest (void) {return 0;} +#else + +int pj_ob_tran_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=ob_tran +a=6400000 +o_proj=latlon +o_lon_p=20 +o_lat_p=20 +lon_0=180"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + {-2.6856872138416592, 1.2374302350496296}, + {-2.6954069748943286, 1.2026833954513816}, + {-2.8993663925401947, 1.2374302350496296}, + {-2.8896466314875244, 1.2026833954513816}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 121.5518748407577, -2.5361001573966084}, + { 63.261184340201858, 17.585319578673531}, + {-141.10073322351622, 26.091712304855108}, + {-65.862385598848391, 51.830295078417215}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + +#endif diff --git a/src/PJ_ocea.c b/src/PJ_ocea.c index b268e1b8..817f0ce6 100644 --- a/src/PJ_ocea.c +++ b/src/PJ_ocea.c @@ -1,76 +1,153 @@ -#define PROJ_PARMS__ \ - double rok; \ - double rtk; \ - double sinphi; \ - double cosphi; \ - double singam; \ - double cosgam; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(ocea, "Oblique Cylindrical Equal Area") "\n\tCyl, Sph" - "lonc= alpha= or\n\tlat_1= lat_2= lon_1= lon_2="; -FORWARD(s_forward); /* spheroid */ - double t; - - xy.y = sin(lp.lam); -/* - xy.x = atan2((tan(lp.phi) * P->cosphi + P->sinphi * xy.y) , cos(lp.lam)); -*/ - t = cos(lp.lam); - xy.x = atan((tan(lp.phi) * P->cosphi + P->sinphi * xy.y) / t); - if (t < 0.) - xy.x += PI; - xy.x *= P->rtk; - xy.y = P->rok * (P->sinphi * sin(lp.phi) - P->cosphi * cos(lp.phi) * xy.y); - return (xy); + "lonc= alpha= or\n\tlat_1= lat_2= lon_1= lon_2="; + +struct pj_opaque { + double rok; + double rtk; + double sinphi; + double cosphi; + double singam; + double cosgam; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double t; + + xy.y = sin(lp.lam); + t = cos(lp.lam); + xy.x = atan((tan(lp.phi) * Q->cosphi + Q->sinphi * xy.y) / t); + if (t < 0.) + xy.x += PI; + xy.x *= Q->rtk; + xy.y = Q->rok * (Q->sinphi * sin(lp.phi) - Q->cosphi * cos(lp.phi) * xy.y); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double t, s; + + xy.y /= Q->rok; + xy.x /= Q->rtk; + t = sqrt(1. - xy.y * xy.y); + lp.phi = asin(xy.y * Q->sinphi + t * Q->cosphi * (s = sin(xy.x))); + lp.lam = atan2(t * Q->sinphi * s - xy.y * Q->cosphi, + t * cos(xy.x)); + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(ocea) { + double phi_0=0.0, phi_1, phi_2, lam_1, lam_2, lonz, alpha; + + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->rok = P->a / P->k0; + Q->rtk = P->a * P->k0; + /*If the keyword "alpha" is found in the sentence then use 1point+1azimuth*/ + if ( pj_param(P->ctx, P->params, "talpha").i) { + /*Define Pole of oblique transformation from 1 point & 1 azimuth*/ + alpha = pj_param(P->ctx, P->params, "ralpha").f; + lonz = pj_param(P->ctx, P->params, "rlonc").f; + /*Equation 9-8 page 80 (http://pubs.usgs.gov/pp/1395/report.pdf)*/ + Q->singam = atan(-cos(alpha)/(-sin(phi_0) * sin(alpha))) + lonz; + /*Equation 9-7 page 80 (http://pubs.usgs.gov/pp/1395/report.pdf)*/ + Q->sinphi = asin(cos(phi_0) * sin(alpha)); + /*If the keyword "alpha" is NOT found in the sentence then use 2points*/ + } else { + /*Define Pole of oblique transformation from 2 points*/ + phi_1 = pj_param(P->ctx, P->params, "rlat_1").f; + phi_2 = pj_param(P->ctx, P->params, "rlat_2").f; + lam_1 = pj_param(P->ctx, P->params, "rlon_1").f; + lam_2 = pj_param(P->ctx, P->params, "rlon_2").f; + /*Equation 9-1 page 80 (http://pubs.usgs.gov/pp/1395/report.pdf)*/ + Q->singam = atan2(cos(phi_1) * sin(phi_2) * cos(lam_1) - + sin(phi_1) * cos(phi_2) * cos(lam_2), + sin(phi_1) * cos(phi_2) * sin(lam_2) - + cos(phi_1) * sin(phi_2) * sin(lam_1) ); + /*Equation 9-2 page 80 (http://pubs.usgs.gov/pp/1395/report.pdf)*/ + Q->sinphi = atan(-cos(Q->singam - lam_1) / tan(phi_1)); + } + P->lam0 = Q->singam + HALFPI; + Q->cosphi = cos(Q->sinphi); + Q->sinphi = sin(Q->sinphi); + Q->cosgam = cos(Q->singam); + Q->singam = sin(Q->singam); + P->inv = s_inverse; + P->fwd = s_forward; + P->es = 0.; + + return P; } -INVERSE(s_inverse); /* spheroid */ - double t, s; - - xy.y /= P->rok; - xy.x /= P->rtk; - t = sqrt(1. - xy.y * xy.y); - lp.phi = asin(xy.y * P->sinphi + t * P->cosphi * (s = sin(xy.x))); - lp.lam = atan2(t * P->sinphi * s - xy.y * P->cosphi, - t * cos(xy.x)); - return (lp); + + +#ifdef PJ_OMIT_SELFTEST +int pj_ocea_selftest (void) {return 0;} +#else + +int pj_ocea_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=ocea +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + {127964312562778.156, 1429265667691.05786}, + {129394957619297.641, 1429265667691.06812}, + {127964312562778.188, -1429265667691.0498}, + {129394957619297.688, -1429265667691.03955}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 179.999999999860108, 2.79764548403721305e-10}, + {-179.999999999860108, 2.7976454840372327e-10}, + { 179.999999999860108, -2.7976454840372327e-10}, + {-179.999999999860108, -2.79764548403721305e-10}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(ocea) - double phi_0=0.0, phi_1, phi_2, lam_1, lam_2, lonz, alpha; - - P->rok = P->a / P->k0; - P->rtk = P->a * P->k0; - /*If the keyword "alpha" is found in the sentence then use 1point+1azimuth*/ - if ( pj_param(P->ctx, P->params, "talpha").i) { - /*Define Pole of oblique transformation from 1 point & 1 azimuth*/ - alpha = pj_param(P->ctx, P->params, "ralpha").f; - lonz = pj_param(P->ctx, P->params, "rlonc").f; - /*Equation 9-8 page 80 (http://pubs.usgs.gov/pp/1395/report.pdf)*/ - P->singam = atan(-cos(alpha)/(-sin(phi_0) * sin(alpha))) + lonz; - /*Equation 9-7 page 80 (http://pubs.usgs.gov/pp/1395/report.pdf)*/ - P->sinphi = asin(cos(phi_0) * sin(alpha)); - /*If the keyword "alpha" is NOT found in the sentence then use 2points*/ - } else { - /*Define Pole of oblique transformation from 2 points*/ - phi_1 = pj_param(P->ctx, P->params, "rlat_1").f; - phi_2 = pj_param(P->ctx, P->params, "rlat_2").f; - lam_1 = pj_param(P->ctx, P->params, "rlon_1").f; - lam_2 = pj_param(P->ctx, P->params, "rlon_2").f; - /*Equation 9-1 page 80 (http://pubs.usgs.gov/pp/1395/report.pdf)*/ - P->singam = atan2(cos(phi_1) * sin(phi_2) * cos(lam_1) - - sin(phi_1) * cos(phi_2) * cos(lam_2), - sin(phi_1) * cos(phi_2) * sin(lam_2) - - cos(phi_1) * sin(phi_2) * sin(lam_1) ); - /*Equation 9-2 page 80 (http://pubs.usgs.gov/pp/1395/report.pdf)*/ - P->sinphi = atan(-cos(P->singam - lam_1) / tan(phi_1)); - } - P->lam0 = P->singam + HALFPI; - P->cosphi = cos(P->sinphi); - P->sinphi = sin(P->sinphi); - P->cosgam = cos(P->singam); - P->singam = sin(P->singam); - P->inv = s_inverse; - P->fwd = s_forward; - P->es = 0.; -ENDENTRY(P) + + +#endif diff --git a/src/PJ_oea.c b/src/PJ_oea.c index b84a7ea6..f29dcfd0 100644 --- a/src/PJ_oea.c +++ b/src/PJ_oea.c @@ -1,58 +1,140 @@ -#define PROJ_PARMS__ \ - double theta; \ - double m, n; \ - double two_r_m, two_r_n, rm, rn, hm, hn; \ - double cp0, sp0; #define PJ_LIB__ #include <projects.h> + PROJ_HEAD(oea, "Oblated Equal Area") "\n\tMisc Sph\n\tn= m= theta="; -FORWARD(s_forward); /* sphere */ + +struct pj_opaque { + double theta; + double m, n; + double two_r_m, two_r_n, rm, rn, hm, hn; + double cp0, sp0; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; double Az, M, N, cp, sp, cl, shz; cp = cos(lp.phi); sp = sin(lp.phi); cl = cos(lp.lam); - Az = aatan2(cp * sin(lp.lam), P->cp0 * sp - P->sp0 * cp * cl) + P->theta; - shz = sin(0.5 * aacos(P->ctx, P->sp0 * sp + P->cp0 * cp * cl)); + Az = aatan2(cp * sin(lp.lam), Q->cp0 * sp - Q->sp0 * cp * cl) + Q->theta; + shz = sin(0.5 * aacos(P->ctx, Q->sp0 * sp + Q->cp0 * cp * cl)); M = aasin(P->ctx, shz * sin(Az)); - N = aasin(P->ctx, shz * cos(Az) * cos(M) / cos(M * P->two_r_m)); - xy.y = P->n * sin(N * P->two_r_n); - xy.x = P->m * sin(M * P->two_r_m) * cos(N) / cos(N * P->two_r_n); - return (xy); + N = aasin(P->ctx, shz * cos(Az) * cos(M) / cos(M * Q->two_r_m)); + xy.y = Q->n * sin(N * Q->two_r_n); + xy.x = Q->m * sin(M * Q->two_r_m) * cos(N) / cos(N * Q->two_r_n); + + return xy; } -INVERSE(s_inverse); /* sphere */ + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; double N, M, xp, yp, z, Az, cz, sz, cAz; - N = P->hn * aasin(P->ctx,xy.y * P->rn); - M = P->hm * aasin(P->ctx,xy.x * P->rm * cos(N * P->two_r_n) / cos(N)); + N = Q->hn * aasin(P->ctx,xy.y * Q->rn); + M = Q->hm * aasin(P->ctx,xy.x * Q->rm * cos(N * Q->two_r_n) / cos(N)); xp = 2. * sin(M); - yp = 2. * sin(N) * cos(M * P->two_r_m) / cos(M); - cAz = cos(Az = aatan2(xp, yp) - P->theta); + yp = 2. * sin(N) * cos(M * Q->two_r_m) / cos(M); + cAz = cos(Az = aatan2(xp, yp) - Q->theta); z = 2. * aasin(P->ctx, 0.5 * hypot(xp, yp)); sz = sin(z); cz = cos(z); - lp.phi = aasin(P->ctx, P->sp0 * cz + P->cp0 * sz * cAz); + lp.phi = aasin(P->ctx, Q->sp0 * cz + Q->cp0 * sz * cAz); lp.lam = aatan2(sz * sin(Az), - P->cp0 * cz - P->sp0 * sz * cAz); - return (lp); + Q->cp0 * cz - Q->sp0 * sz * cAz); + + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(oea) - if (((P->n = pj_param(P->ctx, P->params, "dn").f) <= 0.) || - ((P->m = pj_param(P->ctx, P->params, "dm").f) <= 0.)) + + +PJ *PROJECTION(oea) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + if (((Q->n = pj_param(P->ctx, P->params, "dn").f) <= 0.) || + ((Q->m = pj_param(P->ctx, P->params, "dm").f) <= 0.)) E_ERROR(-39) else { - P->theta = pj_param(P->ctx, P->params, "rtheta").f; - P->sp0 = sin(P->phi0); - P->cp0 = cos(P->phi0); - P->rn = 1./ P->n; - P->rm = 1./ P->m; - P->two_r_n = 2. * P->rn; - P->two_r_m = 2. * P->rm; - P->hm = 0.5 * P->m; - P->hn = 0.5 * P->n; + Q->theta = pj_param(P->ctx, P->params, "rtheta").f; + Q->sp0 = sin(P->phi0); + Q->cp0 = cos(P->phi0); + Q->rn = 1./ Q->n; + Q->rm = 1./ Q->m; + Q->two_r_n = 2. * Q->rn; + Q->two_r_m = 2. * Q->rm; + Q->hm = 0.5 * Q->m; + Q->hn = 0.5 * Q->n; P->fwd = s_forward; P->inv = s_inverse; P->es = 0.; } -ENDENTRY(P) + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_oea_selftest (void) {return 0;} +#else + +int pj_oea_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=oea +a=6400000 +lat_1=0.5 +lat_2=2 +n=1 +m=2 +theta=3"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 228926.872097864107, 99870.4884300760023}, + { 217242.584036940476, -123247.885607474513}, + {-217242.584036940476, 123247.885607474556}, + {-228926.872097864078, -99870.4884300760168}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.0017411857167771369, 0.000987726819566195693}, + { 0.00183489288577854998, -0.000800312481495174641}, + {-0.00183489288577854954, 0.000800312481495174966}, + {-0.00174118571677713712, -0.000987726819566195043}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_omerc.c b/src/PJ_omerc.c index 8a9fa0db..2fe41426 100644 --- a/src/PJ_omerc.c +++ b/src/PJ_omerc.c @@ -21,93 +21,125 @@ ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#define PROJ_PARMS__ \ - double A, B, E, AB, ArB, BrA, rB, singam, cosgam, sinrot, cosrot; \ - double v_pole_n, v_pole_s, u_0; \ - int no_rot; #define PJ_LIB__ #include <projects.h> PROJ_HEAD(omerc, "Oblique Mercator") - "\n\tCyl, Sph&Ell no_rot\n\t" -"alpha= [gamma=] [no_off] lonc= or\n\t lon_1= lat_1= lon_2= lat_2="; -#define TOL 1.e-7 -#define EPS 1.e-10 - -FORWARD(e_forward); /* ellipsoid */ - double Q, S, T, U, V, temp, u, v; - - if (fabs(fabs(lp.phi) - HALFPI) > EPS) { - Q = P->E / pow(pj_tsfn(lp.phi, sin(lp.phi), P->e), P->B); - temp = 1. / Q; - S = .5 * (Q - temp); - T = .5 * (Q + temp); - V = sin(P->B * lp.lam); - U = (S * P->singam - V * P->cosgam) / T; - if (fabs(fabs(U) - 1.0) < EPS) - F_ERROR; - v = 0.5 * P->ArB * log((1. - U)/(1. + U)); - temp = cos(P->B * lp.lam); + "\n\tCyl, Sph&Ell no_rot\n\t" + "alpha= [gamma=] [no_off] lonc= or\n\t lon_1= lat_1= lon_2= lat_2="; + +struct pj_opaque { + double A, B, E, AB, ArB, BrA, rB, singam, cosgam, sinrot, cosrot; + double v_pole_n, v_pole_s, u_0; + int no_rot; +}; + +#define TOL 1.e-7 +#define EPS 1.e-10 + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double S, T, U, V, W, temp, u, v; + + if (fabs(fabs(lp.phi) - HALFPI) > EPS) { + W = Q->E / pow(pj_tsfn(lp.phi, sin(lp.phi), P->e), Q->B); + temp = 1. / W; + S = .5 * (W - temp); + T = .5 * (W + temp); + V = sin(Q->B * lp.lam); + U = (S * Q->singam - V * Q->cosgam) / T; + if (fabs(fabs(U) - 1.0) < EPS) + F_ERROR; + v = 0.5 * Q->ArB * log((1. - U)/(1. + U)); + temp = cos(Q->B * lp.lam); if(fabs(temp) < TOL) { - u = P->A * lp.lam; + u = Q->A * lp.lam; } else { - u = P->ArB * atan2((S * P->cosgam + V * P->singam), temp); + u = Q->ArB * atan2((S * Q->cosgam + V * Q->singam), temp); } - } else { - v = lp.phi > 0 ? P->v_pole_n : P->v_pole_s; - u = P->ArB * lp.phi; - } - if (P->no_rot) { - xy.x = u; - xy.y = v; - } else { - u -= P->u_0; - xy.x = v * P->cosrot + u * P->sinrot; - xy.y = u * P->cosrot - v * P->sinrot; - } - return (xy); + } else { + v = lp.phi > 0 ? Q->v_pole_n : Q->v_pole_s; + u = Q->ArB * lp.phi; + } + if (Q->no_rot) { + xy.x = u; + xy.y = v; + } else { + u -= Q->u_0; + xy.x = v * Q->cosrot + u * Q->sinrot; + xy.y = u * Q->cosrot - v * Q->sinrot; + } + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double u, v, Qp, Sp, Tp, Vp, Up; + + if (Q->no_rot) { + v = xy.y; + u = xy.x; + } else { + v = xy.x * Q->cosrot - xy.y * Q->sinrot; + u = xy.y * Q->cosrot + xy.x * Q->sinrot + Q->u_0; + } + Qp = exp(- Q->BrA * v); + Sp = .5 * (Qp - 1. / Qp); + Tp = .5 * (Qp + 1. / Qp); + Vp = sin(Q->BrA * u); + Up = (Vp * Q->cosgam + Sp * Q->singam) / Tp; + if (fabs(fabs(Up) - 1.) < EPS) { + lp.lam = 0.; + lp.phi = Up < 0. ? -HALFPI : HALFPI; + } else { + lp.phi = Q->E / sqrt((1. + Up) / (1. - Up)); + if ((lp.phi = pj_phi2(P->ctx, pow(lp.phi, 1. / Q->B), P->e)) == HUGE_VAL) + I_ERROR; + lp.lam = - Q->rB * atan2((Sp * Q->cosgam - + Vp * Q->singam), cos(Q->BrA * u)); + } + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); } -INVERSE(e_inverse); /* ellipsoid */ - double u, v, Qp, Sp, Tp, Vp, Up; - - if (P->no_rot) { - v = xy.y; - u = xy.x; - } else { - v = xy.x * P->cosrot - xy.y * P->sinrot; - u = xy.y * P->cosrot + xy.x * P->sinrot + P->u_0; - } - Qp = exp(- P->BrA * v); - Sp = .5 * (Qp - 1. / Qp); - Tp = .5 * (Qp + 1. / Qp); - Vp = sin(P->BrA * u); - Up = (Vp * P->cosgam + Sp * P->singam) / Tp; - if (fabs(fabs(Up) - 1.) < EPS) { - lp.lam = 0.; - lp.phi = Up < 0. ? -HALFPI : HALFPI; - } else { - lp.phi = P->E / sqrt((1. + Up) / (1. - Up)); - if ((lp.phi = pj_phi2(P->ctx, pow(lp.phi, 1. / P->B), P->e)) == HUGE_VAL) - I_ERROR; - lp.lam = - P->rB * atan2((Sp * P->cosgam - - Vp * P->singam), cos(P->BrA * u)); - } - return (lp); + +static void freeup (PJ *P) { + freeup_new (P); + return; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(omerc) - double con, com, cosph0, D, F, H, L, sinph0, p, J, gamma=0, - gamma0, lamc=0, lam1=0, lam2=0, phi1=0, phi2=0, alpha_c=0; - int alp, gam, no_off = 0; - P->no_rot = pj_param(P->ctx, P->params, "tno_rot").i; + +PJ *PROJECTION(omerc) { + double con, com, cosph0, D, F, H, L, sinph0, p, J, gamma=0, + gamma0, lamc=0, lam1=0, lam2=0, phi1=0, phi2=0, alpha_c=0; + int alp, gam, no_off = 0; + + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->no_rot = pj_param(P->ctx, P->params, "tno_rot").i; if ((alp = pj_param(P->ctx, P->params, "talpha").i) != 0) - alpha_c = pj_param(P->ctx, P->params, "ralpha").f; + alpha_c = pj_param(P->ctx, P->params, "ralpha").f; if ((gam = pj_param(P->ctx, P->params, "tgamma").i) != 0) - gamma = pj_param(P->ctx, P->params, "rgamma").f; - if (alp || gam) { - lamc = pj_param(P->ctx, P->params, "rlonc").f; - no_off = + gamma = pj_param(P->ctx, P->params, "rgamma").f; + if (alp || gam) { + lamc = pj_param(P->ctx, P->params, "rlonc").f; + no_off = /* For libproj4 compatability */ pj_param(P->ctx, P->params, "tno_off").i /* for backward compatibility */ @@ -118,86 +150,133 @@ ENTRY0(omerc) pj_param(P->ctx, P->params, "sno_uoff"); pj_param(P->ctx, P->params, "sno_off"); } - } else { - lam1 = pj_param(P->ctx, P->params, "rlon_1").f; - phi1 = pj_param(P->ctx, P->params, "rlat_1").f; - lam2 = pj_param(P->ctx, P->params, "rlon_2").f; - phi2 = pj_param(P->ctx, P->params, "rlat_2").f; - if (fabs(phi1 - phi2) <= TOL || - (con = fabs(phi1)) <= TOL || - fabs(con - HALFPI) <= TOL || - fabs(fabs(P->phi0) - HALFPI) <= TOL || - fabs(fabs(phi2) - HALFPI) <= TOL) E_ERROR(-33); - } - com = sqrt(P->one_es); - if (fabs(P->phi0) > EPS) { - sinph0 = sin(P->phi0); - cosph0 = cos(P->phi0); - con = 1. - P->es * sinph0 * sinph0; - P->B = cosph0 * cosph0; - P->B = sqrt(1. + P->es * P->B * P->B / P->one_es); - P->A = P->B * P->k0 * com / con; - D = P->B * com / (cosph0 * sqrt(con)); - if ((F = D * D - 1.) <= 0.) - F = 0.; - else { - F = sqrt(F); - if (P->phi0 < 0.) - F = -F; - } - P->E = F += D; - P->E *= pow(pj_tsfn(P->phi0, sinph0, P->e), P->B); - } else { - P->B = 1. / com; - P->A = P->k0; - P->E = D = F = 1.; - } - if (alp || gam) { - if (alp) { - gamma0 = asin(sin(alpha_c) / D); - if (!gam) - gamma = alpha_c; - } else - alpha_c = asin(D*sin(gamma0 = gamma)); - if ((con = fabs(alpha_c)) <= TOL || - fabs(con - PI) <= TOL || - fabs(fabs(P->phi0) - HALFPI) <= TOL) - E_ERROR(-32); - P->lam0 = lamc - asin(.5 * (F - 1. / F) * - tan(gamma0)) / P->B; - } else { - H = pow(pj_tsfn(phi1, sin(phi1), P->e), P->B); - L = pow(pj_tsfn(phi2, sin(phi2), P->e), P->B); - F = P->E / H; - p = (L - H) / (L + H); - J = P->E * P->E; - J = (J - L * H) / (J + L * H); - if ((con = lam1 - lam2) < -PI) - lam2 -= TWOPI; - else if (con > PI) - lam2 += TWOPI; - P->lam0 = adjlon(.5 * (lam1 + lam2) - atan( - J * tan(.5 * P->B * (lam1 - lam2)) / p) / P->B); - gamma0 = atan(2. * sin(P->B * adjlon(lam1 - P->lam0)) / - (F - 1. / F)); - gamma = alpha_c = asin(D * sin(gamma0)); - } - P->singam = sin(gamma0); - P->cosgam = cos(gamma0); - P->sinrot = sin(gamma); - P->cosrot = cos(gamma); - P->BrA = 1. / (P->ArB = P->A * (P->rB = 1. / P->B)); - P->AB = P->A * P->B; - if (no_off) - P->u_0 = 0; - else { - P->u_0 = fabs(P->ArB * atan2(sqrt(D * D - 1.), cos(alpha_c))); - if (P->phi0 < 0.) - P->u_0 = - P->u_0; - } - F = 0.5 * gamma0; - P->v_pole_n = P->ArB * log(tan(FORTPI - F)); - P->v_pole_s = P->ArB * log(tan(FORTPI + F)); - P->inv = e_inverse; - P->fwd = e_forward; -ENDENTRY(P) + } else { + lam1 = pj_param(P->ctx, P->params, "rlon_1").f; + phi1 = pj_param(P->ctx, P->params, "rlat_1").f; + lam2 = pj_param(P->ctx, P->params, "rlon_2").f; + phi2 = pj_param(P->ctx, P->params, "rlat_2").f; + if (fabs(phi1 - phi2) <= TOL || + (con = fabs(phi1)) <= TOL || + fabs(con - HALFPI) <= TOL || + fabs(fabs(P->phi0) - HALFPI) <= TOL || + fabs(fabs(phi2) - HALFPI) <= TOL) E_ERROR(-33); + } + com = sqrt(P->one_es); + if (fabs(P->phi0) > EPS) { + sinph0 = sin(P->phi0); + cosph0 = cos(P->phi0); + con = 1. - P->es * sinph0 * sinph0; + Q->B = cosph0 * cosph0; + Q->B = sqrt(1. + P->es * Q->B * Q->B / P->one_es); + Q->A = Q->B * P->k0 * com / con; + D = Q->B * com / (cosph0 * sqrt(con)); + if ((F = D * D - 1.) <= 0.) + F = 0.; + else { + F = sqrt(F); + if (P->phi0 < 0.) + F = -F; + } + Q->E = F += D; + Q->E *= pow(pj_tsfn(P->phi0, sinph0, P->e), Q->B); + } else { + Q->B = 1. / com; + Q->A = P->k0; + Q->E = D = F = 1.; + } + if (alp || gam) { + if (alp) { + gamma0 = asin(sin(alpha_c) / D); + if (!gam) + gamma = alpha_c; + } else + alpha_c = asin(D*sin(gamma0 = gamma)); + if ((con = fabs(alpha_c)) <= TOL || + fabs(con - PI) <= TOL || + fabs(fabs(P->phi0) - HALFPI) <= TOL) + E_ERROR(-32); + P->lam0 = lamc - asin(.5 * (F - 1. / F) * + tan(gamma0)) / Q->B; + } else { + H = pow(pj_tsfn(phi1, sin(phi1), P->e), Q->B); + L = pow(pj_tsfn(phi2, sin(phi2), P->e), Q->B); + F = Q->E / H; + p = (L - H) / (L + H); + J = Q->E * Q->E; + J = (J - L * H) / (J + L * H); + if ((con = lam1 - lam2) < -PI) + lam2 -= TWOPI; + else if (con > PI) + lam2 += TWOPI; + P->lam0 = adjlon(.5 * (lam1 + lam2) - atan( + J * tan(.5 * Q->B * (lam1 - lam2)) / p) / Q->B); + gamma0 = atan(2. * sin(Q->B * adjlon(lam1 - P->lam0)) / + (F - 1. / F)); + gamma = alpha_c = asin(D * sin(gamma0)); + } + Q->singam = sin(gamma0); + Q->cosgam = cos(gamma0); + Q->sinrot = sin(gamma); + Q->cosrot = cos(gamma); + Q->BrA = 1. / (Q->ArB = Q->A * (Q->rB = 1. / Q->B)); + Q->AB = Q->A * Q->B; + if (no_off) + Q->u_0 = 0; + else { + Q->u_0 = fabs(Q->ArB * atan2(sqrt(D * D - 1.), cos(alpha_c))); + if (P->phi0 < 0.) + Q->u_0 = - Q->u_0; + } + F = 0.5 * gamma0; + Q->v_pole_n = Q->ArB * log(tan(FORTPI - F)); + Q->v_pole_s = Q->ArB * log(tan(FORTPI + F)); + P->inv = e_inverse; + P->fwd = e_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_omerc_selftest (void) {return 0;} +#else + +int pj_omerc_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=omerc +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 222650.796885261341, 110642.229314983808}, + { 222650.796885261341, -110642.229314983808}, + {-222650.796885261545, 110642.229314983808}, + {-222650.796885261545, -110642.229314983808}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + { 0.00179663056816996357, 0.000904369474808157338}, + { 0.00179663056816996357, -0.000904369474820879583}, + {-0.0017966305681604536, 0.000904369474808157338}, + {-0.0017966305681604536, -0.000904369474820879583}, + }; + + return pj_generic_selftest (e_args, 0, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, 0, inv_in, e_inv_expect, 0); +} + + +#endif diff --git a/src/PJ_ortho.c b/src/PJ_ortho.c index 202b0176..14aeacc3 100644 --- a/src/PJ_ortho.c +++ b/src/PJ_ortho.c @@ -1,29 +1,37 @@ -#define PROJ_PARMS__ \ - double sinph0; \ - double cosph0; \ - int mode; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(ortho, "Orthographic") "\n\tAzi, Sph."; + +struct pj_opaque { + double sinph0; + double cosph0; + int mode; +}; + #define EPS10 1.e-10 #define N_POLE 0 #define S_POLE 1 #define EQUIT 2 #define OBLIQ 3 -FORWARD(s_forward); /* spheroid */ + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; double coslam, cosphi, sinphi; cosphi = cos(lp.phi); coslam = cos(lp.lam); - switch (P->mode) { + switch (Q->mode) { case EQUIT: if (cosphi * coslam < - EPS10) F_ERROR; xy.y = sin(lp.phi); break; case OBLIQ: - if (P->sinph0 * (sinphi = sin(lp.phi)) + - P->cosph0 * cosphi * coslam < - EPS10) F_ERROR; - xy.y = P->cosph0 * sinphi - P->sinph0 * cosphi * coslam; + if (Q->sinph0 * (sinphi = sin(lp.phi)) + + Q->cosph0 * cosphi * coslam < - EPS10) F_ERROR; + xy.y = Q->cosph0 * sinphi - Q->sinph0 * cosphi * coslam; break; case N_POLE: coslam = - coslam; @@ -33,10 +41,13 @@ FORWARD(s_forward); /* spheroid */ break; } xy.x = cosphi * sin(lp.lam); - return (xy); + return xy; } -INVERSE(s_inverse); /* spheroid */ + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; double rh, cosc, sinc; if ((sinc = (rh = hypot(xy.x, xy.y))) > 1.) { @@ -48,7 +59,7 @@ INVERSE(s_inverse); /* spheroid */ lp.phi = P->phi0; lp.lam = 0.0; } else { - switch (P->mode) { + switch (Q->mode) { case N_POLE: xy.y = -xy.y; lp.phi = acos(sinc); @@ -62,9 +73,9 @@ INVERSE(s_inverse); /* spheroid */ xy.y = cosc * rh; goto sinchk; case OBLIQ: - lp.phi = cosc * P->sinph0 + xy.y * sinc * P->cosph0 /rh; - xy.y = (cosc - P->sinph0 * lp.phi) * rh; - xy.x *= sinc * P->cosph0; + lp.phi = cosc * Q->sinph0 + xy.y * sinc * Q->cosph0 /rh; + xy.y = (cosc - Q->sinph0 * lp.phi) * rh; + xy.x *= sinc * Q->cosph0; sinchk: if (fabs(lp.phi) >= 1.) lp.phi = lp.phi < 0. ? -HALFPI : HALFPI; @@ -72,24 +83,92 @@ INVERSE(s_inverse); /* spheroid */ lp.phi = asin(lp.phi); break; } - lp.lam = (xy.y == 0. && (P->mode == OBLIQ || P->mode == EQUIT)) + lp.lam = (xy.y == 0. && (Q->mode == OBLIQ || Q->mode == EQUIT)) ? (xy.x == 0. ? 0. : xy.x < 0. ? -HALFPI : HALFPI) : atan2(xy.x, xy.y); } - return (lp); + return lp; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(ortho) + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(ortho) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + if (fabs(fabs(P->phi0) - HALFPI) <= EPS10) - P->mode = P->phi0 < 0. ? S_POLE : N_POLE; + Q->mode = P->phi0 < 0. ? S_POLE : N_POLE; else if (fabs(P->phi0) > EPS10) { - P->mode = OBLIQ; - P->sinph0 = sin(P->phi0); - P->cosph0 = cos(P->phi0); + Q->mode = OBLIQ; + Q->sinph0 = sin(P->phi0); + Q->cosph0 = cos(P->phi0); } else - P->mode = EQUIT; + Q->mode = EQUIT; P->inv = s_inverse; P->fwd = s_forward; P->es = 0.; -ENDENTRY(P) + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_ortho_selftest (void) {return 0;} +#else + +int pj_ortho_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=ortho +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223322.76057672748, 111695.401198614476}, + { 223322.76057672748, -111695.401198614476}, + {-223322.76057672748, 111695.401198614476}, + {-223322.76057672748, -111695.401198614476}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.0017904931102938101, 0.000895246554928338998}, + { 0.0017904931102938101, -0.000895246554928338998}, + {-0.0017904931102938101, 0.000895246554928338998}, + {-0.0017904931102938101, -0.000895246554928338998}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_patterson.c b/src/PJ_patterson.c index 6fa10c28..f3a18423 100644 --- a/src/PJ_patterson.c +++ b/src/PJ_patterson.c @@ -1,92 +1,167 @@ -/*
- * Copyright (c) 2014 Bojan Savric
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * The Patterson Cylindrical projection was designed by Tom Patterson, US National
- * Park Service, in 2014, using Flex Projector. The polynomial equations for the
- * projection were developed by Bojan Savric, Oregon State University, in
- * collaboration with Tom Patterson and Bernhard Jenny, Oregon State University.
- *
- * Java reference algorithm implemented by Bojan Savric in Java Map Projection
- * Library (a Java port of PROJ.4) in the file PattersonProjection.java.
- *
- * References:
- * Java Map Projection Library
- * https://github.com/OSUCartography/JMapProjLib
- *
- * Patterson Cylindrical Projection
- * http://shadedrelief.com/patterson/
- *
- * Patterson, T., Savric, B., and Jenny, B. (2015). Cartographic Perspectives
- * (No.78). Describes the projection design and characteristics, and
- * developing the equations. doi:10.14714/CP78.1270
- * http://dx.doi.org/10.14714/CP78.1270
- *
- * Port to PROJ.4 by Micah Cochran, 26 March 2016
- */
-
-#define PJ_LIB__
-# include <projects.h>
-PROJ_HEAD(patterson, "Patterson Cylindrical") "\n\tCyl.";
-#define K1 1.0148
-#define K2 0.23185
-#define K3 -0.14499
-#define K4 0.02406
-#define C1 K1
-#define C2 (5.0 * K2)
-#define C3 (7.0 * K3)
-#define C4 (9.0 * K4)
-#define EPS11 1.0e-11
-#define MAX_Y 1.790857183
-
-FORWARD(s_forward); /* spheroid */
- double phi2;
- (void) P;
- phi2 = lp.phi * lp.phi;
- xy.x = lp.lam;
- xy.y = lp.phi * (K1 + phi2 * phi2 * (K2 + phi2 * (K3 + K4 * phi2)));
- return (xy);
-}
-INVERSE(s_inverse); /* spheroid */
- double yc, tol, y2, f, fder;
- (void) P;
- yc = xy.y;
-
- /* make sure y is inside valid range */
- if (xy.y > MAX_Y) {
- xy.y = MAX_Y;
- } else if (xy.y < -MAX_Y) {
- xy.y = -MAX_Y;
- }
-
- for (;;) { /* Newton-Raphson */
- y2 = yc * yc;
- f = (yc * (K1 + y2 * y2 * (K2 + y2 * (K3 + K4 * y2)))) - xy.y;
- fder = C1 + y2 * y2 * (C2 + y2 * (C3 + C4 * y2));
- yc -= tol = f / fder;
- if (fabs(tol) < EPS11) {
- break;
- }
- }
- lp.phi = yc;
-
- /* longitude */
- lp.lam = xy.x;
-
- return (lp);
-}
-FREEUP; if (P) pj_dalloc(P); }
-ENTRY0(patterson) P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P)
+/* + * Copyright (c) 2014 Bojan Savric + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * The Patterson Cylindrical projection was designed by Tom Patterson, US National + * Park Service, in 2014, using Flex Projector. The polynomial equations for the + * projection were developed by Bojan Savric, Oregon State University, in + * collaboration with Tom Patterson and Bernhard Jenny, Oregon State University. + * + * Java reference algorithm implemented by Bojan Savric in Java Map Projection + * Library (a Java port of PROJ.4) in the file PattersonProjection.java. + * + * References: + * Java Map Projection Library + * https://github.com/OSUCartography/JMapProjLib + * + * Patterson Cylindrical Projection + * http://shadedrelief.com/patterson/ + * + * Patterson, T., Savric, B., and Jenny, B. (2015). Cartographic Perspectives + * (No.78). Describes the projection design and characteristics, and + * developing the equations. doi:10.14714/CP78.1270 + * http://dx.doi.org/10.14714/CP78.1270 + * + * Port to PROJ.4 by Micah Cochran, 26 March 2016 + */ + +#define PJ_LIB__ +#include <projects.h> + +PROJ_HEAD(patterson, "Patterson Cylindrical") "\n\tCyl."; + +#define K1 1.0148 +#define K2 0.23185 +#define K3 -0.14499 +#define K4 0.02406 +#define C1 K1 +#define C2 (5.0 * K2) +#define C3 (7.0 * K3) +#define C4 (9.0 * K4) +#define EPS11 1.0e-11 +#define MAX_Y 1.790857183 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double phi2; + (void) P; + + phi2 = lp.phi * lp.phi; + xy.x = lp.lam; + xy.y = lp.phi * (K1 + phi2 * phi2 * (K2 + phi2 * (K3 + K4 * phi2))); + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double yc, tol, y2, f, fder; + (void) P; + + yc = xy.y; + + /* make sure y is inside valid range */ + if (xy.y > MAX_Y) { + xy.y = MAX_Y; + } else if (xy.y < -MAX_Y) { + xy.y = -MAX_Y; + } + + for (;;) { /* Newton-Raphson */ + y2 = yc * yc; + f = (yc * (K1 + y2 * y2 * (K2 + y2 * (K3 + K4 * y2)))) - xy.y; + fder = C1 + y2 * y2 * (C2 + y2 * (C3 + C4 * y2)); + yc -= tol = f / fder; + if (fabs(tol) < EPS11) { + break; + } + } + lp.phi = yc; + + /* longitude */ + lp.lam = xy.x; + + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(patterson) { + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_patterson_selftest (void) {return 0;} +#else + +int pj_patterson_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=patterson +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + {223402.144255274179, 113354.250397779804}, + {223402.144255274179, -113354.250397779804}, + {-223402.144255274179, 113354.250397779804}, + {-223402.144255274179, -113354.250397779804}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + {0.00179049310978382265, 0.000882190140807953657}, + {0.00179049310978382265, -0.000882190140807953657}, + {-0.00179049310978382265, 0.000882190140807953657}, + {-0.00179049310978382265, -0.000882190140807953657}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_poly.c b/src/PJ_poly.c index ef5b8905..50c0ee5f 100644 --- a/src/PJ_poly.c +++ b/src/PJ_poly.c @@ -1,42 +1,67 @@ -#define PROJ_PARMS__ \ - double ml0; \ - double *en; #define PJ_LIB__ #include <projects.h> + PROJ_HEAD(poly, "Polyconic (American)") "\n\tConic, Sph&Ell"; + +struct pj_opaque { + double ml0; \ + double *en; +}; + #define TOL 1e-10 #define CONV 1e-10 #define N_ITER 10 #define I_ITER 20 #define ITOL 1.e-12 -FORWARD(e_forward); /* ellipsoid */ + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; double ms, sp, cp; - if (fabs(lp.phi) <= TOL) { xy.x = lp.lam; xy.y = -P->ml0; } - else { + if (fabs(lp.phi) <= TOL) { + xy.x = lp.lam; + xy.y = -Q->ml0; + } else { sp = sin(lp.phi); ms = fabs(cp = cos(lp.phi)) > TOL ? pj_msfn(sp, cp, P->es) / sp : 0.; xy.x = ms * sin(lp.lam *= sp); - xy.y = (pj_mlfn(lp.phi, sp, cp, P->en) - P->ml0) + ms * (1. - cos(lp.lam)); + xy.y = (pj_mlfn(lp.phi, sp, cp, Q->en) - Q->ml0) + ms * (1. - cos(lp.lam)); } - return (xy); + + return xy; } -FORWARD(s_forward); /* spheroid */ + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; double cot, E; - if (fabs(lp.phi) <= TOL) { xy.x = lp.lam; xy.y = P->ml0; } - else { + if (fabs(lp.phi) <= TOL) { + xy.x = lp.lam; + xy.y = Q->ml0; + } else { cot = 1. / tan(lp.phi); xy.x = sin(E = lp.lam * sin(lp.phi)) * cot; xy.y = lp.phi - P->phi0 + cot * (1. - cos(E)); } - return (xy); + + return xy; } -INVERSE(e_inverse); /* ellipsoid */ - xy.y += P->ml0; - if (fabs(xy.y) <= TOL) { lp.lam = xy.x; lp.phi = 0.; } - else { + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + + xy.y += Q->ml0; + if (fabs(xy.y) <= TOL) { + lp.lam = xy.x; + lp.phi = 0.; + } else { double r, c, sp, cp, s2ph, ml, mlb, mlp, dPhi; int i; @@ -47,7 +72,7 @@ INVERSE(e_inverse); /* ellipsoid */ if (fabs(cp) < ITOL) I_ERROR; c = sp * (mlp = sqrt(1. - P->es * sp * sp)) / cp; - ml = pj_mlfn(lp.phi, sp, cp, P->en); + ml = pj_mlfn(lp.phi, sp, cp, Q->en); mlb = ml * ml + r; mlp = P->one_es / (mlp * mlp * mlp); lp.phi += ( dPhi = @@ -62,14 +87,20 @@ INVERSE(e_inverse); /* ellipsoid */ c = sin(lp.phi); lp.lam = asin(xy.x * tan(lp.phi) * sqrt(1. - P->es * c * c)) / sin(lp.phi); } - return (lp); + + return lp; } -INVERSE(s_inverse); /* spheroid */ + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; double B, dphi, tp; int i; - if (fabs(xy.y = P->phi0 + xy.y) <= TOL) { lp.lam = xy.x; lp.phi = 0.; } - else { + if (fabs(xy.y = P->phi0 + xy.y) <= TOL) { + lp.lam = xy.x; + lp.phi = 0.; + } else { lp.phi = xy.y; B = xy.x * xy.x + xy.y * xy.y; i = N_ITER; @@ -82,18 +113,105 @@ INVERSE(s_inverse); /* spheroid */ if (! i) I_ERROR; lp.lam = asin(xy.x * tan(lp.phi)) / sin(lp.phi); } - return (lp); + + return lp; } -FREEUP; if (P) { if (P->en) pj_dalloc(P->en); pj_dalloc(P); } } -ENTRY1(poly, en) + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + if (P->opaque->en) + pj_dealloc (P->opaque->en); + pj_dealloc (P->opaque); + + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(poly) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + if (P->es) { - if (!(P->en = pj_enfn(P->es))) E_ERROR_0; - P->ml0 = pj_mlfn(P->phi0, sin(P->phi0), cos(P->phi0), P->en); + if (!(Q->en = pj_enfn(P->es))) E_ERROR_0; + Q->ml0 = pj_mlfn(P->phi0, sin(P->phi0), cos(P->phi0), Q->en); P->inv = e_inverse; P->fwd = e_forward; } else { - P->ml0 = -P->phi0; + Q->ml0 = -P->phi0; P->inv = s_inverse; P->fwd = s_forward; } -ENDENTRY(P) + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_poly_selftest (void) {return 0;} +#else + +int pj_poly_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=poly +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + char s_args[] = {"+proj=poly +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 222605.285770237475, 110642.194561440483}, + { 222605.285770237475, -110642.194561440483}, + {-222605.285770237475, 110642.194561440483}, + {-222605.285770237475, -110642.194561440483}, + }; + + XY s_fwd_expect[] = { + { 223368.105210218986, 111769.110491224754}, + { 223368.105210218986, -111769.110491224754}, + {-223368.105210218986, 111769.110491224754}, + {-223368.105210218986, -111769.110491224754}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + { 0.00179663056846135222, 0.000904369476631838518}, + { 0.00179663056846135222, -0.000904369476631838518}, + {-0.00179663056846135222, 0.000904369476631838518}, + {-0.00179663056846135222, -0.000904369476631838518}, + }; + + LP s_inv_expect[] = { + { 0.0017904931100023887, 0.000895246554454779222}, + { 0.0017904931100023887, -0.000895246554454779222}, + {-0.0017904931100023887, 0.000895246554454779222}, + {-0.0017904931100023887, -0.000895246554454779222}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif diff --git a/src/PJ_putp2.c b/src/PJ_putp2.c index d6fcaea0..4c5a417a 100644 --- a/src/PJ_putp2.c +++ b/src/PJ_putp2.c @@ -1,13 +1,18 @@ #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(putp2, "Putnins P2") "\n\tPCyl., Sph."; + #define C_x 1.89490 #define C_y 1.71848 #define C_p 0.6141848493043784 #define EPS 1e-10 #define NITER 10 #define PI_DIV_3 1.0471975511965977 -FORWARD(s_forward); /* spheroid */ + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; double p, c, s, V; int i; (void) P; @@ -27,15 +32,84 @@ FORWARD(s_forward); /* spheroid */ lp.phi = lp.phi < 0 ? - PI_DIV_3 : PI_DIV_3; xy.x = C_x * lp.lam * (cos(lp.phi) - 0.5); xy.y = C_y * sin(lp.phi); - return (xy); + + return xy; } -INVERSE(s_inverse); /* spheroid */ + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; double c; lp.phi = aasin(P->ctx,xy.y / C_y); lp.lam = xy.x / (C_x * ((c = cos(lp.phi)) - 0.5)); lp.phi = aasin(P->ctx,(lp.phi + sin(lp.phi) * (c - 1.)) / C_p); - return (lp); + + return lp; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(putp2) P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P) + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(putp2) { + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + +#ifdef PJ_OMIT_SELFTEST +int pj_putp2_selftest (void) {return 0;} +#else + +int pj_putp2_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=putp2 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 211638.039634339279, 117895.033043379764}, + { 211638.039634339279, -117895.033043379764}, + {-211638.039634339279, 117895.033043379764}, + {-211638.039634339279, -117895.033043379764}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.00188980221640386672, 0.000848201580276863377}, + { 0.00188980221640386672, -0.000848201580276863377}, + {-0.00188980221640386672, 0.000848201580276863377}, + {-0.00188980221640386672, -0.000848201580276863377}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_putp3.c b/src/PJ_putp3.c index 298ce55d..26fc0a90 100644 --- a/src/PJ_putp3.c +++ b/src/PJ_putp3.c @@ -1,26 +1,171 @@ -#define PROJ_PARMS__ \ - double A; #define PJ_LIB__ -# include <projects.h> +#include <projects.h> + +struct pj_opaque { + double A; +}; + PROJ_HEAD(putp3, "Putnins P3") "\n\tPCyl., Sph."; PROJ_HEAD(putp3p, "Putnins P3'") "\n\tPCyl., Sph."; -#define C 0.79788456 -#define RPISQ 0.1013211836 -FORWARD(s_forward); /* spheroid */ - xy.x = C * lp.lam * (1. - P->A * lp.phi * lp.phi); - xy.y = C * lp.phi; - return (xy); -} -INVERSE(s_inverse); /* spheroid */ - lp.phi = xy.y / C; - lp.lam = xy.x / (C * (1. - P->A * lp.phi * lp.phi)); - return (lp); -} -FREEUP; if (P) pj_dalloc(P); } - static PJ * -setup(PJ *P) { - P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; - return P; -} -ENTRY0(putp3) P->A = 4. * RPISQ; ENDENTRY(setup(P)) -ENTRY0(putp3p) P->A = 2. * RPISQ; ENDENTRY(setup(P)) + +#define C 0.79788456 +#define RPISQ 0.1013211836 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + + xy.x = C * lp.lam * (1. - P->opaque->A * lp.phi * lp.phi); + xy.y = C * lp.phi; + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + + lp.phi = xy.y / C; + lp.lam = xy.x / (C * (1. - P->opaque->A * lp.phi * lp.phi)); + + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(putp3) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->A = 4. * RPISQ; + + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + +PJ *PROJECTION(putp3p) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->A = 2. * RPISQ; + + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_putp3_selftest (void) {return 0;} +#else + +int pj_putp3_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=putp3 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 178227.115507793525, 89124.5607860879827}, + { 178227.115507793525, -89124.5607860879827}, + {-178227.115507793525, 89124.5607860879827}, + {-178227.115507793525, -89124.5607860879827}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.00224405032986489889, 0.00112202516475805899}, + { 0.00224405032986489889, -0.00112202516475805899}, + {-0.00224405032986489889, 0.00112202516475805899}, + {-0.00224405032986489889, -0.00112202516475805899}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif + + +#ifdef PJ_OMIT_SELFTEST +int pj_putp3p_selftest (void) {return 0;} +#else + +int pj_putp3p_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=putp3p +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 178238.118539984745, 89124.5607860879827}, + { 178238.118539984745, -89124.5607860879827}, + {-178238.118539984745, 89124.5607860879827}, + {-178238.118539984745, -89124.5607860879827}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.00224405032969050844, 0.00112202516475805899}, + { 0.00224405032969050844, -0.00112202516475805899}, + {-0.00224405032969050844, 0.00112202516475805899}, + {-0.00224405032969050844, -0.00112202516475805899}, + + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_putp4p.c b/src/PJ_putp4p.c index 32036bbe..9264d896 100644 --- a/src/PJ_putp4p.c +++ b/src/PJ_putp4p.c @@ -1,29 +1,177 @@ -#define PROJ_PARMS__ \ - double C_x, C_y; #define PJ_LIB__ -# include <projects.h> +#include <projects.h> + +struct pj_opaque { + double C_x, C_y; +}; + PROJ_HEAD(putp4p, "Putnins P4'") "\n\tPCyl., Sph."; PROJ_HEAD(weren, "Werenskiold I") "\n\tPCyl., Sph."; -FORWARD(s_forward); /* spheroid */ - lp.phi = aasin(P->ctx,0.883883476 * sin(lp.phi)); - xy.x = P->C_x * lp.lam * cos(lp.phi); - xy.x /= cos(lp.phi *= 0.333333333333333); - xy.y = P->C_y * sin(lp.phi); - return (xy); -} -INVERSE(s_inverse); /* spheroid */ - lp.phi = aasin(P->ctx,xy.y / P->C_y); - lp.lam = xy.x * cos(lp.phi) / P->C_x; - lp.phi *= 3.; - lp.lam /= cos(lp.phi); - lp.phi = aasin(P->ctx,1.13137085 * sin(lp.phi)); - return (lp); -} -FREEUP; if (P) pj_dalloc(P); } - static PJ * -setup(PJ *P) { - P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; - return P; -} -ENTRY0(putp4p) P->C_x = 0.874038744; P->C_y = 3.883251825; ENDENTRY(setup(P)) -ENTRY0(weren) P->C_x = 1.; P->C_y = 4.442882938; ENDENTRY(setup(P)) + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + + lp.phi = aasin(P->ctx,0.883883476 * sin(lp.phi)); + xy.x = Q->C_x * lp.lam * cos(lp.phi); + xy.x /= cos(lp.phi *= 0.333333333333333); + xy.y = Q->C_y * sin(lp.phi); + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + + lp.phi = aasin(P->ctx,xy.y / Q->C_y); + lp.lam = xy.x * cos(lp.phi) / Q->C_x; + lp.phi *= 3.; + lp.lam /= cos(lp.phi); + lp.phi = aasin(P->ctx,1.13137085 * sin(lp.phi)); + + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(putp4p) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->C_x = 0.874038744; + Q->C_y = 3.883251825; + + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + + +PJ *PROJECTION(weren) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->C_x = 1.; + Q->C_y = 4.442882938; + + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_putp4p_selftest (void) {return 0;} +#else + +int pj_putp4p_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=putp4p +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 195241.47734938623, 127796.782307926231}, + { 195241.47734938623, -127796.782307926231}, + {-195241.47734938623, 127796.782307926231}, + {-195241.47734938623, -127796.782307926231}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.00204852830860296001, 0.000782480174932193733}, + { 0.00204852830860296001, -0.000782480174932193733}, + {-0.00204852830860296001, 0.000782480174932193733}, + {-0.00204852830860296001, -0.000782480174932193733}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif + + +#ifdef PJ_OMIT_SELFTEST +int pj_weren_selftest (void) {return 0;} +#else + +int pj_weren_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=weren +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223378.515757633519, 146214.093042288267}, + { 223378.515757633519, -146214.093042288267}, + {-223378.515757633519, 146214.093042288267}, + {-223378.515757633519, -146214.093042288267}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.00179049310987240413, 0.000683917989676492265}, + { 0.00179049310987240413, -0.000683917989676492265}, + {-0.00179049310987240413, 0.000683917989676492265}, + {-0.00179049310987240413, -0.000683917989676492265}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_putp5.c b/src/PJ_putp5.c index 8d3c59a0..d4e59ee8 100644 --- a/src/PJ_putp5.c +++ b/src/PJ_putp5.c @@ -1,26 +1,172 @@ -#define PROJ_PARMS__ \ - double A, B; #define PJ_LIB__ -# include <projects.h> +#include <projects.h> + +struct pj_opaque { + double A, B; +}; + PROJ_HEAD(putp5, "Putnins P5") "\n\tPCyl., Sph."; PROJ_HEAD(putp5p, "Putnins P5'") "\n\tPCyl., Sph."; -#define C 1.01346 -#define D 1.2158542 -FORWARD(s_forward); /* spheroid */ - xy.x = C * lp.lam * (P->A - P->B * sqrt(1. + D * lp.phi * lp.phi)); - xy.y = C * lp.phi; - return (xy); -} -INVERSE(s_inverse); /* spheroid */ - lp.phi = xy.y / C; - lp.lam = xy.x / (C * (P->A - P->B * sqrt(1. + D * lp.phi * lp.phi))); - return (lp); -} -FREEUP; if (P) pj_dalloc(P); } - static PJ * -setup(PJ *P) { - P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; - return P; -} -ENTRY0(putp5) P->A = 2.; P->B = 1.; ENDENTRY(setup(P)) -ENTRY0(putp5p) P->A = 1.5; P->B = 0.5; ENDENTRY(setup(P)) + +#define C 1.01346 +#define D 1.2158542 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + + xy.x = C * lp.lam * (Q->A - Q->B * sqrt(1. + D * lp.phi * lp.phi)); + xy.y = C * lp.phi; + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + + lp.phi = xy.y / C; + lp.lam = xy.x / (C * (Q->A - Q->B * sqrt(1. + D * lp.phi * lp.phi))); + + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(putp5) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->A = 2.; + Q->B = 1.; + + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + + +PJ *PROJECTION(putp5p) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->A = 1.5; + Q->B = 0.5; + + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + +#ifdef PJ_OMIT_SELFTEST +int pj_putp5_selftest (void) {return 0;} +#else + +int pj_putp5_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=putp5 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 226367.21338056153, 113204.56855847509}, + { 226367.21338056153, -113204.56855847509}, + {-226367.21338056153, 113204.56855847509}, + {-226367.21338056153, -113204.56855847509}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.00176671315102969553, 0.000883356575387199546}, + { 0.00176671315102969553, -0.000883356575387199546}, + {-0.00176671315102969553, 0.000883356575387199546}, + {-0.00176671315102969553, -0.000883356575387199546}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + +#endif + + +#ifdef PJ_OMIT_SELFTEST +int pj_putp5p_selftest (void) {return 0;} +#else + +int pj_putp5p_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=putp5p +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 226388.175248755841, 113204.56855847509}, + { 226388.175248755841, -113204.56855847509}, + {-226388.175248755841, 113204.56855847509}, + {-226388.175248755841, -113204.56855847509}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.00176671315090204742, 0.000883356575387199546}, + { 0.00176671315090204742, -0.000883356575387199546}, + {-0.00176671315090204742, 0.000883356575387199546}, + {-0.00176671315090204742, -0.000883356575387199546}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + +#endif diff --git a/src/PJ_putp6.c b/src/PJ_putp6.c index 5909dd4c..7c860cdb 100644 --- a/src/PJ_putp6.c +++ b/src/PJ_putp6.c @@ -1,59 +1,196 @@ -#define PROJ_PARMS__ \ - double C_x, C_y, A, B, D; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + +struct pj_opaque { + double C_x, C_y, A, B, D; +}; + PROJ_HEAD(putp6, "Putnins P6") "\n\tPCyl., Sph."; PROJ_HEAD(putp6p, "Putnins P6'") "\n\tPCyl., Sph."; -#define EPS 1e-10 -#define NITER 10 + +#define EPS 1e-10 +#define NITER 10 #define CON_POLE 1.732050807568877 -FORWARD(s_forward); /* spheroid */ - double p, r, V; - int i; - - p = P->B * sin(lp.phi); - lp.phi *= 1.10265779; - for (i = NITER; i ; --i) { - r = sqrt(1. + lp.phi * lp.phi); - lp.phi -= V = ( (P->A - r) * lp.phi - log(lp.phi + r) - p ) / - (P->A - 2. * r); - if (fabs(V) < EPS) - break; - } - if (!i) - lp.phi = p < 0. ? -CON_POLE : CON_POLE; - xy.x = P->C_x * lp.lam * (P->D - sqrt(1. + lp.phi * lp.phi)); - xy.y = P->C_y * lp.phi; - return (xy); -} -INVERSE(s_inverse); /* spheroid */ - double r; - - lp.phi = xy.y / P->C_y; - r = sqrt(1. + lp.phi * lp.phi); - lp.lam = xy.x / (P->C_x * (P->D - r)); - lp.phi = aasin( P->ctx, ( (P->A - r) * lp.phi - log(lp.phi + r) ) / P->B); - return (lp); -} -FREEUP; if (P) pj_dalloc(P); } - static PJ * -setup(PJ *P) { - P->es = 0.; - P->inv = s_inverse; - P->fwd = s_forward; - return P; -} -ENTRY0(putp6) - P->C_x = 1.01346; - P->C_y = 0.91910; - P->A = 4.; - P->B = 2.1471437182129378784; - P->D = 2.; -ENDENTRY(setup(P)) -ENTRY0(putp6p) - P->C_x = 0.44329; - P->C_y = 0.80404; - P->A = 6.; - P->B = 5.61125; - P->D = 3.; -ENDENTRY(setup(P)) + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double p, r, V; + int i; + + p = Q->B * sin(lp.phi); + lp.phi *= 1.10265779; + for (i = NITER; i ; --i) { + r = sqrt(1. + lp.phi * lp.phi); + lp.phi -= V = ( (Q->A - r) * lp.phi - log(lp.phi + r) - p ) / + (Q->A - 2. * r); + if (fabs(V) < EPS) + break; + } + if (!i) + lp.phi = p < 0. ? -CON_POLE : CON_POLE; + xy.x = Q->C_x * lp.lam * (Q->D - sqrt(1. + lp.phi * lp.phi)); + xy.y = Q->C_y * lp.phi; + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double r; + + lp.phi = xy.y / Q->C_y; + r = sqrt(1. + lp.phi * lp.phi); + lp.lam = xy.x / (Q->C_x * (Q->D - r)); + lp.phi = aasin( P->ctx, ( (Q->A - r) * lp.phi - log(lp.phi + r) ) / Q->B); + + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(putp6) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->C_x = 1.01346; + Q->C_y = 0.91910; + Q->A = 4.; + Q->B = 2.1471437182129378784; + Q->D = 2.; + + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + + +PJ *PROJECTION(putp6p) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->C_x = 0.44329; + Q->C_y = 0.80404; + Q->A = 6.; + Q->B = 5.61125; + Q->D = 3.; + + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_putp6_selftest (void) {return 0;} +#else + +int pj_putp6_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=putp6 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 226369.395133402577, 110218.523796520662}, + { 226369.395133402577, -110218.523796520749}, + {-226369.395133402577, 110218.523796520662}, + {-226369.395133402577, -110218.523796520749}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.00176671315102969921, 0.000907295534210503544}, + { 0.00176671315102969921, -0.000907295534205924308}, + {-0.00176671315102969921, 0.000907295534210503544}, + {-0.00176671315102969921, -0.000907295534205924308}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + +#endif + + +#ifdef PJ_OMIT_SELFTEST +int pj_putp6p_selftest (void) {return 0;} +#else + +int pj_putp6p_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=putp6p +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 198034.195132195076, 125989.475461323193}, + { 198034.195132195076, -125989.475461323193}, + {-198034.195132195076, 125989.475461323193}, + {-198034.195132195076, -125989.475461323193}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.00201955053120177067, 0.000793716441164738612}, + { 0.00201955053120177067, -0.000793716441164738612}, + {-0.00201955053120177067, 0.000793716441164738612}, + {-0.00201955053120177067, -0.000793716441164738612}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + +#endif diff --git a/src/PJ_qsc.c b/src/PJ_qsc.c index 12cb9d63..b9e9eb0d 100644 --- a/src/PJ_qsc.c +++ b/src/PJ_qsc.c @@ -28,7 +28,7 @@ * Other projection centers will not work! * * In the projection code below, each cube face is handled differently. - * See the computation of the face parameter in the ENTRY0(qsc) function + * See the computation of the face parameter in the PROJECTION(qsc) function * and the handling of different face values (FACE_*) in the forward and * inverse projections. * @@ -38,15 +38,18 @@ * three areas of a cube face are handled by rotation of AREA_0. */ -#define PROJ_PARMS__ \ - int face; \ - double a_squared; \ - double b; \ - double one_minus_f; \ - double one_minus_f_squared; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + +struct pj_opaque { + int face; + double a_squared; + double b; + double one_minus_f; + double one_minus_f_squared; +}; PROJ_HEAD(qsc, "Quadrilateralized Spherical Cube") "\n\tAzi, Sph."; + #define EPS10 1.e-10 /* The six cube faces. */ @@ -66,316 +69,403 @@ PROJ_HEAD(qsc, "Quadrilateralized Spherical Cube") "\n\tAzi, Sph."; /* Helper function for forward projection: compute the theta angle * and determine the area number. */ -static double -qsc_fwd_equat_face_theta(double phi, double y, double x, int *area) { - double theta; - if (phi < EPS10) { +static double qsc_fwd_equat_face_theta(double phi, double y, double x, int *area) { + double theta; + if (phi < EPS10) { + *area = AREA_0; + theta = 0.0; + } else { + theta = atan2(y, x); + if (fabs(theta) <= FORTPI) { *area = AREA_0; - theta = 0.0; + } else if (theta > FORTPI && theta <= HALFPI + FORTPI) { + *area = AREA_1; + theta -= HALFPI; + } else if (theta > HALFPI + FORTPI || theta <= -(HALFPI + FORTPI)) { + *area = AREA_2; + theta = (theta >= 0.0 ? theta - PI : theta + PI); } else { - theta = atan2(y, x); - if (fabs(theta) <= FORTPI) { - *area = AREA_0; - } else if (theta > FORTPI && theta <= HALFPI + FORTPI) { - *area = AREA_1; - theta -= HALFPI; - } else if (theta > HALFPI + FORTPI || theta <= -(HALFPI + FORTPI)) { - *area = AREA_2; - theta = (theta >= 0.0 ? theta - PI : theta + PI); - } else { - *area = AREA_3; - theta += HALFPI; - } + *area = AREA_3; + theta += HALFPI; } - return (theta); + } + return theta; } /* Helper function: shift the longitude. */ -static double -qsc_shift_lon_origin(double lon, double offset) { - double slon = lon + offset; - if (slon < -PI) { - slon += TWOPI; - } else if (slon > +PI) { - slon -= TWOPI; - } - return slon; +static double qsc_shift_lon_origin(double lon, double offset) { + double slon = lon + offset; + if (slon < -PI) { + slon += TWOPI; + } else if (slon > +PI) { + slon -= TWOPI; + } + return slon; } -/* Forward projection, ellipsoid */ -FORWARD(e_forward); - double lat, lon; - double theta, phi; - double t, mu; /* nu; */ - int area; - - /* Convert the geodetic latitude to a geocentric latitude. - * This corresponds to the shift from the ellipsoid to the sphere - * described in [LK12]. */ - if (P->es) { - lat = atan(P->one_minus_f_squared * tan(lp.phi)); - } else { - lat = lp.phi; - } - /* Convert the input lat, lon into theta, phi as used by QSC. - * This depends on the cube face and the area on it. - * For the top and bottom face, we can compute theta and phi - * directly from phi, lam. For the other faces, we must use - * unit sphere cartesian coordinates as an intermediate step. */ - lon = lp.lam; - if (P->face == FACE_TOP) { - phi = HALFPI - lat; - if (lon >= FORTPI && lon <= HALFPI + FORTPI) { - area = AREA_0; - theta = lon - HALFPI; - } else if (lon > HALFPI + FORTPI || lon <= -(HALFPI + FORTPI)) { - area = AREA_1; - theta = (lon > 0.0 ? lon - PI : lon + PI); - } else if (lon > -(HALFPI + FORTPI) && lon <= -FORTPI) { - area = AREA_2; - theta = lon + HALFPI; - } else { - area = AREA_3; - theta = lon; - } - } else if (P->face == FACE_BOTTOM) { - phi = HALFPI + lat; - if (lon >= FORTPI && lon <= HALFPI + FORTPI) { - area = AREA_0; - theta = -lon + HALFPI; - } else if (lon < FORTPI && lon >= -FORTPI) { - area = AREA_1; - theta = -lon; - } else if (lon < -FORTPI && lon >= -(HALFPI + FORTPI)) { - area = AREA_2; - theta = -lon - HALFPI; - } else { - area = AREA_3; - theta = (lon > 0.0 ? -lon + PI : -lon - PI); - } - } else { - double q, r, s; - double sinlat, coslat; - double sinlon, coslon; - - if (P->face == FACE_RIGHT) { - lon = qsc_shift_lon_origin(lon, +HALFPI); - } else if (P->face == FACE_BACK) { - lon = qsc_shift_lon_origin(lon, +PI); - } else if (P->face == FACE_LEFT) { - lon = qsc_shift_lon_origin(lon, -HALFPI); - } - sinlat = sin(lat); - coslat = cos(lat); - sinlon = sin(lon); - coslon = cos(lon); - q = coslat * coslon; - r = coslat * sinlon; - s = sinlat; - - if (P->face == FACE_FRONT) { - phi = acos(q); - theta = qsc_fwd_equat_face_theta(phi, s, r, &area); - } else if (P->face == FACE_RIGHT) { - phi = acos(r); - theta = qsc_fwd_equat_face_theta(phi, s, -q, &area); - } else if (P->face == FACE_BACK) { - phi = acos(-q); - theta = qsc_fwd_equat_face_theta(phi, s, -r, &area); - } else if (P->face == FACE_LEFT) { - phi = acos(-r); - theta = qsc_fwd_equat_face_theta(phi, s, q, &area); - } else { - /* Impossible */ - phi = theta = 0.0; - area = AREA_0; - } - } +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double lat, lon; + double theta, phi; + double t, mu; /* nu; */ + int area; - /* Compute mu and nu for the area of definition. - * For mu, see Eq. (3-21) in [OL76], but note the typos: - * compare with Eq. (3-14). For nu, see Eq. (3-38). */ - mu = atan((12.0 / PI) * (theta + acos(sin(theta) * cos(FORTPI)) - HALFPI)); - t = sqrt((1.0 - cos(phi)) / (cos(mu) * cos(mu)) / (1.0 - cos(atan(1.0 / cos(theta))))); - /* nu = atan(t); We don't really need nu, just t, see below. */ + /* Convert the geodetic latitude to a geocentric latitude. + * This corresponds to the shift from the ellipsoid to the sphere + * described in [LK12]. */ + if (P->es) { + lat = atan(Q->one_minus_f_squared * tan(lp.phi)); + } else { + lat = lp.phi; + } - /* Apply the result to the real area. */ - if (area == AREA_1) { - mu += HALFPI; - } else if (area == AREA_2) { - mu += PI; - } else if (area == AREA_3) { - mu += HALFPI + PI; + /* Convert the input lat, lon into theta, phi as used by QSC. + * This depends on the cube face and the area on it. + * For the top and bottom face, we can compute theta and phi + * directly from phi, lam. For the other faces, we must use + * unit sphere cartesian coordinates as an intermediate step. */ + lon = lp.lam; + if (Q->face == FACE_TOP) { + phi = HALFPI - lat; + if (lon >= FORTPI && lon <= HALFPI + FORTPI) { + area = AREA_0; + theta = lon - HALFPI; + } else if (lon > HALFPI + FORTPI || lon <= -(HALFPI + FORTPI)) { + area = AREA_1; + theta = (lon > 0.0 ? lon - PI : lon + PI); + } else if (lon > -(HALFPI + FORTPI) && lon <= -FORTPI) { + area = AREA_2; + theta = lon + HALFPI; + } else { + area = AREA_3; + theta = lon; } - - /* Now compute x, y from mu and nu */ - /* t = tan(nu); */ - xy.x = t * cos(mu); - xy.y = t * sin(mu); - return (xy); -} - -/* Inverse projection, ellipsoid */ -INVERSE(e_inverse); - double mu, nu, cosmu, tannu; - double tantheta, theta, cosphi, phi; - double t; - int area; - - /* Convert the input x, y to the mu and nu angles as used by QSC. - * This depends on the area of the cube face. */ - nu = atan(sqrt(xy.x * xy.x + xy.y * xy.y)); - mu = atan2(xy.y, xy.x); - if (xy.x >= 0.0 && xy.x >= fabs(xy.y)) { + } else if (Q->face == FACE_BOTTOM) { + phi = HALFPI + lat; + if (lon >= FORTPI && lon <= HALFPI + FORTPI) { area = AREA_0; - } else if (xy.y >= 0.0 && xy.y >= fabs(xy.x)) { + theta = -lon + HALFPI; + } else if (lon < FORTPI && lon >= -FORTPI) { area = AREA_1; - mu -= HALFPI; - } else if (xy.x < 0.0 && -xy.x >= fabs(xy.y)) { + theta = -lon; + } else if (lon < -FORTPI && lon >= -(HALFPI + FORTPI)) { area = AREA_2; - mu = (mu < 0.0 ? mu + PI : mu - PI); + theta = -lon - HALFPI; } else { area = AREA_3; - mu += HALFPI; + theta = (lon > 0.0 ? -lon + PI : -lon - PI); } + } else { + double q, r, s; + double sinlat, coslat; + double sinlon, coslon; - /* Compute phi and theta for the area of definition. - * The inverse projection is not described in the original paper, but some - * good hints can be found here (as of 2011-12-14): - * http://fits.gsfc.nasa.gov/fitsbits/saf.93/saf.9302 - * (search for "Message-Id: <9302181759.AA25477 at fits.cv.nrao.edu>") */ - t = (PI / 12.0) * tan(mu); - tantheta = sin(t) / (cos(t) - (1.0 / sqrt(2.0))); - theta = atan(tantheta); - cosmu = cos(mu); - tannu = tan(nu); - cosphi = 1.0 - cosmu * cosmu * tannu * tannu * (1.0 - cos(atan(1.0 / cos(theta)))); - if (cosphi < -1.0) { - cosphi = -1.0; - } else if (cosphi > +1.0) { - cosphi = +1.0; + if (Q->face == FACE_RIGHT) { + lon = qsc_shift_lon_origin(lon, +HALFPI); + } else if (Q->face == FACE_BACK) { + lon = qsc_shift_lon_origin(lon, +PI); + } else if (Q->face == FACE_LEFT) { + lon = qsc_shift_lon_origin(lon, -HALFPI); } + sinlat = sin(lat); + coslat = cos(lat); + sinlon = sin(lon); + coslon = cos(lon); + q = coslat * coslon; + r = coslat * sinlon; + s = sinlat; - /* Apply the result to the real area on the cube face. - * For the top and bottom face, we can compute phi and lam directly. - * For the other faces, we must use unit sphere cartesian coordinates - * as an intermediate step. */ - if (P->face == FACE_TOP) { - phi = acos(cosphi); - lp.phi = HALFPI - phi; - if (area == AREA_0) { - lp.lam = theta + HALFPI; - } else if (area == AREA_1) { - lp.lam = (theta < 0.0 ? theta + PI : theta - PI); - } else if (area == AREA_2) { - lp.lam = theta - HALFPI; - } else /* area == AREA_3 */ { - lp.lam = theta; - } - } else if (P->face == FACE_BOTTOM) { - phi = acos(cosphi); - lp.phi = phi - HALFPI; - if (area == AREA_0) { - lp.lam = -theta + HALFPI; - } else if (area == AREA_1) { - lp.lam = -theta; - } else if (area == AREA_2) { - lp.lam = -theta - HALFPI; - } else /* area == AREA_3 */ { - lp.lam = (theta < 0.0 ? -theta - PI : -theta + PI); - } + if (Q->face == FACE_FRONT) { + phi = acos(q); + theta = qsc_fwd_equat_face_theta(phi, s, r, &area); + } else if (Q->face == FACE_RIGHT) { + phi = acos(r); + theta = qsc_fwd_equat_face_theta(phi, s, -q, &area); + } else if (Q->face == FACE_BACK) { + phi = acos(-q); + theta = qsc_fwd_equat_face_theta(phi, s, -r, &area); + } else if (Q->face == FACE_LEFT) { + phi = acos(-r); + theta = qsc_fwd_equat_face_theta(phi, s, q, &area); } else { - /* Compute phi and lam via cartesian unit sphere coordinates. */ - double q, r, s, t; - q = cosphi; - t = q * q; - if (t >= 1.0) { - s = 0.0; - } else { - s = sqrt(1.0 - t) * sin(theta); - } - t += s * s; - if (t >= 1.0) { - r = 0.0; - } else { - r = sqrt(1.0 - t); - } - /* Rotate q,r,s into the correct area. */ - if (area == AREA_1) { - t = r; - r = -s; - s = t; - } else if (area == AREA_2) { - r = -r; - s = -s; - } else if (area == AREA_3) { - t = r; - r = s; - s = -t; - } - /* Rotate q,r,s into the correct cube face. */ - if (P->face == FACE_RIGHT) { - t = q; - q = -r; - r = t; - } else if (P->face == FACE_BACK) { - q = -q; - r = -r; - } else if (P->face == FACE_LEFT) { - t = q; - q = r; - r = -t; - } - /* Now compute phi and lam from the unit sphere coordinates. */ - lp.phi = acos(-s) - HALFPI; - lp.lam = atan2(r, q); - if (P->face == FACE_RIGHT) { - lp.lam = qsc_shift_lon_origin(lp.lam, -HALFPI); - } else if (P->face == FACE_BACK) { - lp.lam = qsc_shift_lon_origin(lp.lam, -PI); - } else if (P->face == FACE_LEFT) { - lp.lam = qsc_shift_lon_origin(lp.lam, +HALFPI); - } + /* Impossible */ + phi = theta = 0.0; + area = AREA_0; } + } - /* Apply the shift from the sphere to the ellipsoid as described - * in [LK12]. */ - if (P->es) { - int invert_sign; - double tanphi, xa; - invert_sign = (lp.phi < 0.0 ? 1 : 0); - tanphi = tan(lp.phi); - xa = P->b / sqrt(tanphi * tanphi + P->one_minus_f_squared); - lp.phi = atan(sqrt(P->a * P->a - xa * xa) / (P->one_minus_f * xa)); - if (invert_sign) { - lp.phi = -lp.phi; - } - } - return (lp); + /* Compute mu and nu for the area of definition. + * For mu, see Eq. (3-21) in [OL76], but note the typos: + * compare with Eq. (3-14). For nu, see Eq. (3-38). */ + mu = atan((12.0 / PI) * (theta + acos(sin(theta) * cos(FORTPI)) - HALFPI)); + t = sqrt((1.0 - cos(phi)) / (cos(mu) * cos(mu)) / (1.0 - cos(atan(1.0 / cos(theta))))); + /* nu = atan(t); We don't really need nu, just t, see below. */ + + /* Apply the result to the real area. */ + if (area == AREA_1) { + mu += HALFPI; + } else if (area == AREA_2) { + mu += PI; + } else if (area == AREA_3) { + mu += HALFPI + PI; + } + + /* Now compute x, y from mu and nu */ + /* t = tan(nu); */ + xy.x = t * cos(mu); + xy.y = t * sin(mu); + return xy; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(qsc) - P->inv = e_inverse; - P->fwd = e_forward; - /* Determine the cube face from the center of projection. */ - if (P->phi0 >= HALFPI - FORTPI / 2.0) { - P->face = FACE_TOP; - } else if (P->phi0 <= -(HALFPI - FORTPI / 2.0)) { - P->face = FACE_BOTTOM; - } else if (fabs(P->lam0) <= FORTPI) { - P->face = FACE_FRONT; - } else if (fabs(P->lam0) <= HALFPI + FORTPI) { - P->face = (P->lam0 > 0.0 ? FACE_RIGHT : FACE_LEFT); + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double mu, nu, cosmu, tannu; + double tantheta, theta, cosphi, phi; + double t; + int area; + + /* Convert the input x, y to the mu and nu angles as used by QSC. + * This depends on the area of the cube face. */ + nu = atan(sqrt(xy.x * xy.x + xy.y * xy.y)); + mu = atan2(xy.y, xy.x); + if (xy.x >= 0.0 && xy.x >= fabs(xy.y)) { + area = AREA_0; + } else if (xy.y >= 0.0 && xy.y >= fabs(xy.x)) { + area = AREA_1; + mu -= HALFPI; + } else if (xy.x < 0.0 && -xy.x >= fabs(xy.y)) { + area = AREA_2; + mu = (mu < 0.0 ? mu + PI : mu - PI); + } else { + area = AREA_3; + mu += HALFPI; + } + + /* Compute phi and theta for the area of definition. + * The inverse projection is not described in the original paper, but some + * good hints can be found here (as of 2011-12-14): + * http://fits.gsfc.nasa.gov/fitsbits/saf.93/saf.9302 + * (search for "Message-Id: <9302181759.AA25477 at fits.cv.nrao.edu>") */ + t = (PI / 12.0) * tan(mu); + tantheta = sin(t) / (cos(t) - (1.0 / sqrt(2.0))); + theta = atan(tantheta); + cosmu = cos(mu); + tannu = tan(nu); + cosphi = 1.0 - cosmu * cosmu * tannu * tannu * (1.0 - cos(atan(1.0 / cos(theta)))); + if (cosphi < -1.0) { + cosphi = -1.0; + } else if (cosphi > +1.0) { + cosphi = +1.0; + } + + /* Apply the result to the real area on the cube face. + * For the top and bottom face, we can compute phi and lam directly. + * For the other faces, we must use unit sphere cartesian coordinates + * as an intermediate step. */ + if (Q->face == FACE_TOP) { + phi = acos(cosphi); + lp.phi = HALFPI - phi; + if (area == AREA_0) { + lp.lam = theta + HALFPI; + } else if (area == AREA_1) { + lp.lam = (theta < 0.0 ? theta + PI : theta - PI); + } else if (area == AREA_2) { + lp.lam = theta - HALFPI; + } else /* area == AREA_3 */ { + lp.lam = theta; + } + } else if (Q->face == FACE_BOTTOM) { + phi = acos(cosphi); + lp.phi = phi - HALFPI; + if (area == AREA_0) { + lp.lam = -theta + HALFPI; + } else if (area == AREA_1) { + lp.lam = -theta; + } else if (area == AREA_2) { + lp.lam = -theta - HALFPI; + } else /* area == AREA_3 */ { + lp.lam = (theta < 0.0 ? -theta - PI : -theta + PI); + } + } else { + /* Compute phi and lam via cartesian unit sphere coordinates. */ + double q, r, s, t; + q = cosphi; + t = q * q; + if (t >= 1.0) { + s = 0.0; } else { - P->face = FACE_BACK; + s = sqrt(1.0 - t) * sin(theta); } - /* Fill in useful values for the ellipsoid <-> sphere shift - * described in [LK12]. */ - if (P->es) { - P->a_squared = P->a * P->a; - P->b = P->a * sqrt(1.0 - P->es); - P->one_minus_f = 1.0 - (P->a - P->b) / P->a; - P->one_minus_f_squared = P->one_minus_f * P->one_minus_f; + t += s * s; + if (t >= 1.0) { + r = 0.0; + } else { + r = sqrt(1.0 - t); } -ENDENTRY(P) + /* Rotate q,r,s into the correct area. */ + if (area == AREA_1) { + t = r; + r = -s; + s = t; + } else if (area == AREA_2) { + r = -r; + s = -s; + } else if (area == AREA_3) { + t = r; + r = s; + s = -t; + } + /* Rotate q,r,s into the correct cube face. */ + if (Q->face == FACE_RIGHT) { + t = q; + q = -r; + r = t; + } else if (Q->face == FACE_BACK) { + q = -q; + r = -r; + } else if (Q->face == FACE_LEFT) { + t = q; + q = r; + r = -t; + } + /* Now compute phi and lam from the unit sphere coordinates. */ + lp.phi = acos(-s) - HALFPI; + lp.lam = atan2(r, q); + if (Q->face == FACE_RIGHT) { + lp.lam = qsc_shift_lon_origin(lp.lam, -HALFPI); + } else if (Q->face == FACE_BACK) { + lp.lam = qsc_shift_lon_origin(lp.lam, -PI); + } else if (Q->face == FACE_LEFT) { + lp.lam = qsc_shift_lon_origin(lp.lam, +HALFPI); + } + } + + /* Apply the shift from the sphere to the ellipsoid as described + * in [LK12]. */ + if (P->es) { + int invert_sign; + double tanphi, xa; + invert_sign = (lp.phi < 0.0 ? 1 : 0); + tanphi = tan(lp.phi); + xa = Q->b / sqrt(tanphi * tanphi + Q->one_minus_f_squared); + lp.phi = atan(sqrt(P->a * P->a - xa * xa) / (Q->one_minus_f * xa)); + if (invert_sign) { + lp.phi = -lp.phi; + } + } + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(qsc) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + P->inv = e_inverse; + P->fwd = e_forward; + /* Determine the cube face from the center of projection. */ + if (P->phi0 >= HALFPI - FORTPI / 2.0) { + Q->face = FACE_TOP; + } else if (P->phi0 <= -(HALFPI - FORTPI / 2.0)) { + Q->face = FACE_BOTTOM; + } else if (fabs(P->lam0) <= FORTPI) { + Q->face = FACE_FRONT; + } else if (fabs(P->lam0) <= HALFPI + FORTPI) { + Q->face = (P->lam0 > 0.0 ? FACE_RIGHT : FACE_LEFT); + } else { + Q->face = FACE_BACK; + } + /* Fill in useful values for the ellipsoid <-> sphere shift + * described in [LK12]. */ + if (P->es) { + Q->a_squared = P->a * P->a; + Q->b = P->a * sqrt(1.0 - P->es); + Q->one_minus_f = 1.0 - (P->a - Q->b) / P->a; + Q->one_minus_f_squared = Q->one_minus_f * Q->one_minus_f; + } + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_qsc_selftest (void) {return 0;} +#else + +int pj_qsc_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=qsc +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + char s_args[] = {"+proj=qsc +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 304638.450843852363, 164123.870923793991}, + { 304638.450843852363, -164123.870923793991}, + {-304638.450843852363, 164123.870923793962}, + {-304638.450843852421, -164123.870923793904}, + }; + + XY s_fwd_expect[] = { + { 305863.792402890511, 165827.722754715243}, + { 305863.792402890511, -165827.722754715243}, + {-305863.792402890511, 165827.722754715243}, + {-305863.792402890569, -165827.722754715156}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + { 0.00132134098471627126, 0.000610652900922527926}, + { 0.00132134098471627126, -0.000610652900922527926}, + {-0.00132134098471627126, 0.000610652900922527926}, + {-0.00132134098471627126, -0.000610652900922527926}, + }; + + LP s_inv_expect[] = { + { 0.00131682718763827234, 0.000604493198178676161}, + { 0.00131682718763827234, -0.000604493198178676161}, + {-0.00131682718763827234, 0.000604493198178676161}, + {-0.00131682718763827234, -0.000604493198178676161}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif diff --git a/src/PJ_robin.c b/src/PJ_robin.c index e8572ae4..5fabd9d0 100644 --- a/src/PJ_robin.c +++ b/src/PJ_robin.c @@ -1,10 +1,12 @@ #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(robin, "Robinson") "\n\tPCyl., Sph."; + #define V(C,z) (C.c0 + z * (C.c1 + z * (C.c2 + z * C.c3))) #define DV(C,z) (C.c1 + z * (C.c2 + C.c2 + z * 3. * C.c3)) -/* +/* note: following terms based upon 5 deg. intervals in degrees. Some background on these coefficients is available at: @@ -14,7 +16,7 @@ http://trac.osgeo.org/proj/ticket/113 */ struct COEFS { - float c0, c1, c2, c3; + float c0, c1, c2, c3; }; static const struct COEFS X[] = { @@ -38,6 +40,7 @@ static const struct COEFS X[] = { {0.5722, -0.00906601, 0.000182, 6.24051e-06}, {0.5322, -0.00677797, 0.000275608, 6.24051e-06} }; + static const struct COEFS Y[] = { {-5.20417e-18, 0.0124, 1.21431e-18, -8.45284e-11}, {0.062, 0.0124, -1.26793e-09, 4.22642e-10}, @@ -59,61 +62,135 @@ static const struct COEFS Y[] = { {0.9761, 0.00616527, -0.000256, -4.2106e-06}, {1, 0.00328947, -0.000319159, -4.2106e-06} }; -#define FXC 0.8487 -#define FYC 1.3523 -#define C1 11.45915590261646417544 -#define RC1 0.08726646259971647884 -#define NODES 18 -#define ONEEPS 1.000001 -#define EPS 1e-8 -FORWARD(s_forward); /* spheroid */ - int i; - double dphi; - (void) P; - - i = floor((dphi = fabs(lp.phi)) * C1); - if (i >= NODES) i = NODES - 1; - dphi = RAD_TO_DEG * (dphi - RC1 * i); - xy.x = V(X[i], dphi) * FXC * lp.lam; - xy.y = V(Y[i], dphi) * FYC; - if (lp.phi < 0.) xy.y = -xy.y; - return (xy); + +#define FXC 0.8487 +#define FYC 1.3523 +#define C1 11.45915590261646417544 +#define RC1 0.08726646259971647884 +#define NODES 18 +#define ONEEPS 1.000001 +#define EPS 1e-8 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + int i; + double dphi; + (void) P; + + i = floor((dphi = fabs(lp.phi)) * C1); + if (i >= NODES) i = NODES - 1; + dphi = RAD_TO_DEG * (dphi - RC1 * i); + xy.x = V(X[i], dphi) * FXC * lp.lam; + xy.y = V(Y[i], dphi) * FYC; + if (lp.phi < 0.) xy.y = -xy.y; + + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + int i; + double t, t1; + struct COEFS T; + + lp.lam = xy.x / FXC; + lp.phi = fabs(xy.y / FYC); + if (lp.phi >= 1.) { /* simple pathologic cases */ + if (lp.phi > ONEEPS) I_ERROR + else { + lp.phi = xy.y < 0. ? -HALFPI : HALFPI; + lp.lam /= X[NODES].c0; + } + } else { /* general problem */ + /* in Y space, reduce to table interval */ + for (i = floor(lp.phi * NODES);;) { + if (Y[i].c0 > lp.phi) --i; + else if (Y[i+1].c0 <= lp.phi) ++i; + else break; + } + T = Y[i]; + /* first guess, linear interp */ + t = 5. * (lp.phi - T.c0)/(Y[i+1].c0 - T.c0); + /* make into root */ + T.c0 -= lp.phi; + for (;;) { /* Newton-Raphson reduction */ + t -= t1 = V(T,t) / DV(T,t); + if (fabs(t1) < EPS) + break; + } + lp.phi = (5 * i + t) * DEG_TO_RAD; + if (xy.y < 0.) lp.phi = -lp.phi; + lp.lam /= V(X[i], t); + } + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; } -INVERSE(s_inverse); /* spheroid */ - int i; - double t, t1; - struct COEFS T; - - lp.lam = xy.x / FXC; - lp.phi = fabs(xy.y / FYC); - if (lp.phi >= 1.) { /* simple pathologic cases */ - if (lp.phi > ONEEPS) I_ERROR - else { - lp.phi = xy.y < 0. ? -HALFPI : HALFPI; - lp.lam /= X[NODES].c0; - } - } else { /* general problem */ - /* in Y space, reduce to table interval */ - for (i = floor(lp.phi * NODES);;) { - if (Y[i].c0 > lp.phi) --i; - else if (Y[i+1].c0 <= lp.phi) ++i; - else break; - } - T = Y[i]; - /* first guess, linear interp */ - t = 5. * (lp.phi - T.c0)/(Y[i+1].c0 - T.c0); - /* make into root */ - T.c0 -= lp.phi; - for (;;) { /* Newton-Raphson reduction */ - t -= t1 = V(T,t) / DV(T,t); - if (fabs(t1) < EPS) - break; - } - lp.phi = (5 * i + t) * DEG_TO_RAD; - if (xy.y < 0.) lp.phi = -lp.phi; - lp.lam /= V(X[i], t); - } - return (lp); + + +PJ *PROJECTION(robin) { + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(robin) P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P) + + +#ifdef PJ_OMIT_SELFTEST +int pj_robin_selftest (void) {return 0;} +#else + +int pj_robin_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=robin +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 189588.423282507836, 107318.530350702888}, + { 189588.423282507836, -107318.530350702888}, + {-189588.423282507836, 107318.530350702888}, + {-189588.423282507836, -107318.530350702888}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.002109689065506131, 0.000931805533547745983}, + { 0.002109689065506131, -0.000931805533547745983}, + {-0.002109689065506131, 0.000931805533547745983}, + {-0.002109689065506131, -0.000931805533547745983}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_rpoly.c b/src/PJ_rpoly.c index 7a7670ed..ad240917 100644 --- a/src/PJ_rpoly.c +++ b/src/PJ_rpoly.c @@ -1,35 +1,99 @@ -#define PROJ_PARMS__ \ - double phi1; \ - double fxa; \ - double fxb; \ - int mode; -#define EPS 1e-9 #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + +struct pj_opaque { + double phi1; + double fxa; + double fxb; + int mode; +}; + PROJ_HEAD(rpoly, "Rectangular Polyconic") - "\n\tConic, Sph., no inv.\n\tlat_ts="; -FORWARD(s_forward); /* spheroid */ - double fa; - - if (P->mode) - fa = tan(lp.lam * P->fxb) * P->fxa; - else - fa = 0.5 * lp.lam; - if (fabs(lp.phi) < EPS) { - xy.x = fa + fa; - xy.y = - P->phi0; - } else { - xy.y = 1. / tan(lp.phi); - xy.x = sin(fa = 2. * atan(fa * sin(lp.phi))) * xy.y; - xy.y = lp.phi - P->phi0 + (1. - cos(fa)) * xy.y; - } - return (xy); + "\n\tConic, Sph., no inv.\n\tlat_ts="; + +#define EPS 1e-9 + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double fa; + + if (Q->mode) + fa = tan(lp.lam * Q->fxb) * Q->fxa; + else + fa = 0.5 * lp.lam; + if (fabs(lp.phi) < EPS) { + xy.x = fa + fa; + xy.y = - P->phi0; + } else { + xy.y = 1. / tan(lp.phi); + xy.x = sin(fa = 2. * atan(fa * sin(lp.phi))) * xy.y; + xy.y = lp.phi - P->phi0 + (1. - cos(fa)) * xy.y; + } + return xy; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(rpoly) - if ((P->mode = (P->phi1 = fabs(pj_param(P->ctx, P->params, "rlat_ts").f)) > EPS)) { - P->fxb = 0.5 * sin(P->phi1); - P->fxa = 0.5 / P->fxb; - } - P->es = 0.; P->fwd = s_forward; -ENDENTRY(P) + + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(rpoly) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + if ((Q->mode = (Q->phi1 = fabs(pj_param(P->ctx, P->params, "rlat_ts").f)) > EPS)) { + Q->fxb = 0.5 * sin(Q->phi1); + Q->fxa = 0.5 / Q->fxb; + } + P->es = 0.; + P->fwd = s_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_rpoly_selftest (void) {return 0;} +#else + +int pj_rpoly_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=rpoly +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223368.09830201423, 111769.110486991223}, + { 223368.09830201423, -111769.110486991223}, + {-223368.09830201423, 111769.110486991223}, + {-223368.09830201423, -111769.110486991223}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, 0, 0, 0); +} + + +#endif diff --git a/src/PJ_sch.c b/src/PJ_sch.c index 9a0120fe..048224ec 100644 --- a/src/PJ_sch.c +++ b/src/PJ_sch.c @@ -3,8 +3,8 @@ * * Project: SCH Coordinate system * Purpose: Implementation of SCH Coordinate system - * References : - * 1. Hensley. Scott. SCH Coordinates and various transformations. June 15, 2000. + * References : + * 1. Hensley. Scott. SCH Coordinates and various transformations. June 15, 2000. * 2. Buckley, Sean Monroe. Radar interferometry measurement of land subsidence. 2000.. * PhD Thesis. UT Austin. (Appendix) * 3. Hensley, Scott, Elaine Chapin, and T. Michel. "Improved processing of AIRSAR @@ -15,8 +15,8 @@ * Copyright (c) 2015 California Institute of Technology. * Government sponsorship acknowledged. * - * NOTE: The SCH coordinate system is a sensor aligned coordinate system - * developed at JPL for radar mapping missions. Details pertaining to the + * NOTE: The SCH coordinate system is a sensor aligned coordinate system + * developed at JPL for radar mapping missions. Details pertaining to the * coordinate system have been release in the public domain (see references above). * This code is an independent implementation of the SCH coordinate system * that conforms to the PROJ.4 conventions and uses the details presented in these @@ -32,200 +32,237 @@ * DEALINGS IN THE SOFTWARE. ****************************************************************************/ +#define PJ_LIB__ +#include <projects.h> #include "geocent.h" -#define PROJ_PARMS__ \ - double plat; /*Peg Latitude */ \ - double plon; /*Peg Longitude*/ \ - double phdg; /*Peg heading */ \ - double h0; /*Average altitude */\ - double transMat[9]; \ - double xyzoff[3]; \ - double rcurv; \ - GeocentricInfo sph; \ - GeocentricInfo elp_0; - -#define PJ_LIB__ -#include <projects.h> +struct pj_opaque { + double plat; /*Peg Latitude */ + double plon; /*Peg Longitude*/ + double phdg; /*Peg heading */ + double h0; /*Average altitude */ + double transMat[9]; + double xyzoff[3]; + double rcurv; + GeocentricInfo sph; + GeocentricInfo elp_0; +}; PROJ_HEAD(sch, "Spherical Cross-track Height") "\n\tMisc\n\tplat_0 = ,plon_0 = , phdg_0 = ,[h_0 = ]"; -INVERSE3D(inverse3d); - double temp[3]; - double pxyz[3]; - - //Local lat,lon using radius - pxyz[0] = xyz.y * P->a / P->rcurv; - pxyz[1] = xyz.x * P->a / P->rcurv; - pxyz[2] = xyz.z; - - - if( pj_Convert_Geodetic_To_Geocentric( &(P->sph), pxyz[0], pxyz[1], pxyz[2], - temp, temp+1, temp+2) != 0) - I3_ERROR; - - //Apply rotation - pxyz[0] = P->transMat[0] * temp[0] + P->transMat[1] * temp[1] + P->transMat[2] * temp[2]; - pxyz[1] = P->transMat[3] * temp[0] + P->transMat[4] * temp[1] + P->transMat[5] * temp[2]; - pxyz[2] = P->transMat[6] * temp[0] + P->transMat[7] * temp[1] + P->transMat[8] * temp[2]; - - //Apply offset - pxyz[0] += P->xyzoff[0]; - pxyz[1] += P->xyzoff[1]; - pxyz[2] += P->xyzoff[2]; - - //Convert geocentric coordinates to lat lon - pj_Convert_Geocentric_To_Geodetic( &(P->elp_0), pxyz[0], pxyz[1], pxyz[2], - temp, temp+1, temp+2); - - - lpz.lam = temp[1] ; - lpz.phi = temp[0] ; - lpz.z = temp[2]; - -// printf("INVERSE: \n"); -// printf("XYZ: %f %f %f \n", xyz.x, xyz.y, xyz.z); -// printf("LPZ: %f %f %f \n", lpz.lam, lpz.phi, lpz.z); - return (lpz); +static LPZ inverse3d(XYZ xyz, PJ *P) { + LPZ lpz = {0.0, 0.0, 0.0}; + struct pj_opaque *Q = P->opaque; + double temp[3]; + double pxyz[3]; + + /* Local lat,lon using radius */ + pxyz[0] = xyz.y * P->a / Q->rcurv; + pxyz[1] = xyz.x * P->a / Q->rcurv; + pxyz[2] = xyz.z; + + if( pj_Convert_Geodetic_To_Geocentric( &(Q->sph), pxyz[0], pxyz[1], pxyz[2], + temp, temp+1, temp+2) != 0) + I3_ERROR; + + /* Apply rotation */ + pxyz[0] = Q->transMat[0] * temp[0] + Q->transMat[1] * temp[1] + Q->transMat[2] * temp[2]; + pxyz[1] = Q->transMat[3] * temp[0] + Q->transMat[4] * temp[1] + Q->transMat[5] * temp[2]; + pxyz[2] = Q->transMat[6] * temp[0] + Q->transMat[7] * temp[1] + Q->transMat[8] * temp[2]; + + /* Apply offset */ + pxyz[0] += Q->xyzoff[0]; + pxyz[1] += Q->xyzoff[1]; + pxyz[2] += Q->xyzoff[2]; + + /* Convert geocentric coordinates to lat lon */ + pj_Convert_Geocentric_To_Geodetic( &(Q->elp_0), pxyz[0], pxyz[1], pxyz[2], + temp, temp+1, temp+2); + + + lpz.lam = temp[1] ; + lpz.phi = temp[0] ; + lpz.z = temp[2]; + +#if 0 + printf("INVERSE: \n"); + printf("XYZ: %f %f %f \n", xyz.x, xyz.y, xyz.z); + printf("LPZ: %f %f %f \n", lpz.lam, lpz.phi, lpz.z); +#endif + return lpz; } -FORWARD3D(forward3d); - double temp[3]; - double pxyz[3]; +static XYZ forward3d(LPZ lpz, PJ *P) { + XYZ xyz = {0.0, 0.0, 0.0}; + struct pj_opaque *Q = P->opaque; + double temp[3]; + double pxyz[3]; - //Convert lat lon to geocentric coordinates - if( pj_Convert_Geodetic_To_Geocentric( &(P->elp_0), lpz.phi, lpz.lam, lpz.z, - temp, temp+1, temp+2 ) != 0 ) - F3_ERROR; + /* Convert lat lon to geocentric coordinates */ + if( pj_Convert_Geodetic_To_Geocentric( &(Q->elp_0), lpz.phi, lpz.lam, lpz.z, + temp, temp+1, temp+2 ) != 0 ) + F3_ERROR; - //Adjust for offset - temp[0] -= P->xyzoff[0]; - temp[1] -= P->xyzoff[1]; - temp[2] -= P->xyzoff[2]; + /* Adjust for offset */ + temp[0] -= Q->xyzoff[0]; + temp[1] -= Q->xyzoff[1]; + temp[2] -= Q->xyzoff[2]; - - //Apply rotation - pxyz[0] = P->transMat[0] * temp[0] + P->transMat[3] * temp[1] + P->transMat[6] * temp[2]; - pxyz[1] = P->transMat[1] * temp[0] + P->transMat[4] * temp[1] + P->transMat[7] * temp[2]; - pxyz[2] = P->transMat[2] * temp[0] + P->transMat[5] * temp[1] + P->transMat[8] * temp[2]; - //Convert to local lat,lon - pj_Convert_Geocentric_To_Geodetic( &(P->sph), pxyz[0], pxyz[1], pxyz[2], - temp, temp+1, temp+2); + /* Apply rotation */ + pxyz[0] = Q->transMat[0] * temp[0] + Q->transMat[3] * temp[1] + Q->transMat[6] * temp[2]; + pxyz[1] = Q->transMat[1] * temp[0] + Q->transMat[4] * temp[1] + Q->transMat[7] * temp[2]; + pxyz[2] = Q->transMat[2] * temp[0] + Q->transMat[5] * temp[1] + Q->transMat[8] * temp[2]; + /* Convert to local lat,lon */ + pj_Convert_Geocentric_To_Geodetic( &(Q->sph), pxyz[0], pxyz[1], pxyz[2], + temp, temp+1, temp+2); - //Scale by radius - xyz.x = temp[1] * P->rcurv / P->a; - xyz.y = temp[0] * P->rcurv / P->a; - xyz.z = temp[2]; -// printf("FORWARD: \n"); -// printf("LPZ: %f %f %f \n", lpz.lam, lpz.phi, lpz.z); -// printf("XYZ: %f %f %f \n", xyz.x, xyz.y, xyz.z); + /* Scale by radius */ + xyz.x = temp[1] * Q->rcurv / P->a; + xyz.y = temp[0] * Q->rcurv / P->a; + xyz.z = temp[2]; - return (xyz); +#if 0 + printf("FORWARD: \n"); + printf("LPZ: %f %f %f \n", lpz.lam, lpz.phi, lpz.z); + printf("XYZ: %f %f %f \n", xyz.x, xyz.y, xyz.z); +#endif + return xyz; } -FREEUP; if (P) pj_dalloc(P); } - static PJ * -setup(PJ *P) { /* general initialization */ - double reast, rnorth; - double chdg, shdg; - double clt, slt; - double clo, slo; - double temp; - double pxyz[3]; - temp = P->a * sqrt(1.0 - P->es); +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); - //Setup original geocentric system - if ( pj_Set_Geocentric_Parameters(&(P->elp_0), P->a, temp) != 0) - E_ERROR(-37); - - - clt = cos(P->plat); - slt = sin(P->plat); - clo = cos(P->plon); - slo = sin(P->plon); + pj_dealloc (P->opaque); + return pj_dealloc(P); +} - //Estimate the radius of curvature for given peg - temp = sqrt(1.0 - (P->es) * slt * slt); - reast = (P->a)/temp; - rnorth = (P->a) * (1.0 - (P->es))/pow(temp,3); +static void freeup (PJ *P) { + freeup_new (P); + return; +} - chdg = cos(P->phdg); - shdg = sin(P->phdg); - - P->rcurv = P->h0 + (reast*rnorth)/(reast * chdg * chdg + rnorth * shdg * shdg); +static PJ *setup(PJ *P) { /* general initialization */ + struct pj_opaque *Q = P->opaque; + double reast, rnorth; + double chdg, shdg; + double clt, slt; + double clo, slo; + double temp; + double pxyz[3]; -// printf("North Radius: %f \n", rnorth); -// printf("East Radius: %f \n", reast); -// printf("Effective Radius: %f \n", P->rcurv); + temp = P->a * sqrt(1.0 - P->es); - //Set up local sphere at the given peg point - if ( pj_Set_Geocentric_Parameters(&(P->sph), P->rcurv, P->rcurv) != 0) + /* Setup original geocentric system */ + if ( pj_Set_Geocentric_Parameters(&(Q->elp_0), P->a, temp) != 0) E_ERROR(-37); - //Set up the transformation matrices - P->transMat[0] = clt * clo; - P->transMat[1] = -shdg*slo - slt*clo * chdg; - P->transMat[2] = slo*chdg - slt*clo*shdg; - P->transMat[3] = clt*slo; - P->transMat[4] = clo*shdg - slt*slo*chdg; - P->transMat[5] = -clo*chdg - slt*slo*shdg; - P->transMat[6] = slt; - P->transMat[7] = clt*chdg; - P->transMat[8] = clt*shdg; + clt = cos(Q->plat); + slt = sin(Q->plat); + clo = cos(Q->plon); + slo = sin(Q->plon); + + /* Estimate the radius of curvature for given peg */ + temp = sqrt(1.0 - (P->es) * slt * slt); + reast = (P->a)/temp; + rnorth = (P->a) * (1.0 - (P->es))/pow(temp,3); + + chdg = cos(Q->phdg); + shdg = sin(Q->phdg); + + Q->rcurv = Q->h0 + (reast*rnorth)/(reast * chdg * chdg + rnorth * shdg * shdg); + +#if 0 + printf("North Radius: %f \n", rnorth); + printf("East Radius: %f \n", reast); + printf("Effective Radius: %f \n", Q->rcurv); +#endif + + /* Set up local sphere at the given peg point */ + if ( pj_Set_Geocentric_Parameters(&(Q->sph), Q->rcurv, Q->rcurv) != 0) + E_ERROR(-37); + + /* Set up the transformation matrices */ + Q->transMat[0] = clt * clo; + Q->transMat[1] = -shdg*slo - slt*clo * chdg; + Q->transMat[2] = slo*chdg - slt*clo*shdg; + Q->transMat[3] = clt*slo; + Q->transMat[4] = clo*shdg - slt*slo*chdg; + Q->transMat[5] = -clo*chdg - slt*slo*shdg; + Q->transMat[6] = slt; + Q->transMat[7] = clt*chdg; + Q->transMat[8] = clt*shdg; + + + if( pj_Convert_Geodetic_To_Geocentric( &(Q->elp_0), Q->plat, Q->plon, Q->h0, + pxyz, pxyz+1, pxyz+2 ) != 0 ) + { + E_ERROR(-14) + } + + + Q->xyzoff[0] = pxyz[0] - (Q->rcurv) * clt * clo; + Q->xyzoff[1] = pxyz[1] - (Q->rcurv) * clt * slo; + Q->xyzoff[2] = pxyz[2] - (Q->rcurv) * slt; + +#if 0 + printf("Offset: %f %f %f \n", Q->xyzoff[0], Q->xyzoff[1], Q->xyzoff[2]); +#endif + + P->fwd3d = forward3d; + P->inv3d = inverse3d; + return P; +} - - if( pj_Convert_Geodetic_To_Geocentric( &(P->elp_0), P->plat, P->plon, P->h0, - pxyz, pxyz+1, pxyz+2 ) != 0 ) - { - E_ERROR(-14) - } +PJ *PROJECTION(sch) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; - P->xyzoff[0] = pxyz[0] - (P->rcurv) * clt * clo; - P->xyzoff[1] = pxyz[1] - (P->rcurv) * clt * slo; - P->xyzoff[2] = pxyz[2] - (P->rcurv) * slt; + Q->h0 = 0.0; -// printf("Offset: %f %f %f \n", P->xyzoff[0], P->xyzoff[1], P->xyzoff[2]); + /* Check if peg latitude was defined */ + if (pj_param(P->ctx, P->params, "tplat_0").i) + Q->plat = pj_param(P->ctx, P->params, "rplat_0").f; + else + E_ERROR(-37); + /* Check if peg longitude was defined */ + if (pj_param(P->ctx, P->params, "tplon_0").i) + Q->plon = pj_param(P->ctx, P->params, "rplon_0").f; + else + E_ERROR(-37); - P->fwd3d = forward3d; - P->inv3d = inverse3d; - return P; -} -ENTRY0(sch) - P->h0 = 0.0; + /* Check if peg latitude is defined */ + if (pj_param(P->ctx, P->params, "tphdg_0").i) + Q->phdg = pj_param(P->ctx, P->params, "rphdg_0").f; + else + E_ERROR(-37); - //Check if peg latitude was defined - if (pj_param(P->ctx, P->params, "tplat_0").i) - P->plat = pj_param(P->ctx, P->params, "rplat_0").f; - else - E_ERROR(-37); - //Check if peg longitude was defined - if (pj_param(P->ctx, P->params, "tplon_0").i) - P->plon = pj_param(P->ctx, P->params, "rplon_0").f; - else - E_ERROR(-37); - - //Check if peg latitude is defined - if (pj_param(P->ctx, P->params, "tphdg_0").i) - P->phdg = pj_param(P->ctx, P->params, "rphdg_0").f; - else - E_ERROR(-37); - + /* Check if average height was defined - If so read it in */ + if (pj_param(P->ctx, P->params, "th_0").i) + Q->h0 = pj_param(P->ctx, P->params, "dh_0").f; - //Check if average height was defined - //If so read it in - if (pj_param(P->ctx, P->params, "th_0").i) - P->h0 = pj_param(P->ctx, P->params, "dh_0").f; + /* Completed reading in the projection parameters */ +#if 0 + printf("PSA: Lat = %f Lon = %f Hdg = %f \n", Q->plat, Q->plon, Q->phdg); +#endif - //Completed reading in the projection parameters -// printf("PSA: Lat = %f Lon = %f Hdg = %f \n", P->plat, P->plon, P->phdg); + return setup(P); +} -ENDENTRY(setup(P)) +/* Skipping sef-test since the test system is not capable of handling + * 3D coordinate systems for the time being. Relying on tests in ../nad/ + */ +int pj_sch_selftest (void) {return 0;} diff --git a/src/PJ_sconics.c b/src/PJ_sconics.c index 8c17533d..2bf2213b 100644 --- a/src/PJ_sconics.c +++ b/src/PJ_sconics.c @@ -1,12 +1,17 @@ -#define PROJ_PARMS__ \ - double n; \ - double rho_c; \ - double rho_0; \ - double sig; \ - double c1, c2; \ - int type; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + + +struct pj_opaque { + double n; + double rho_c; + double rho_0; + double sig; + double c1, c2; + int type; +}; + + #define EULER 0 #define MURD1 1 #define MURD2 2 @@ -14,141 +19,677 @@ #define PCONIC 4 #define TISSOT 5 #define VITK1 6 -#define EPS10 1.e-10 +#define EPS10 1.e-10 #define EPS 1e-10 #define LINE2 "\n\tConic, Sph\n\tlat_1= and lat_2=" -PROJ_HEAD(tissot, "Tissot") - LINE2; -PROJ_HEAD(murd1, "Murdoch I") - LINE2; -PROJ_HEAD(murd2, "Murdoch II") - LINE2; -PROJ_HEAD(murd3, "Murdoch III") - LINE2; -PROJ_HEAD(euler, "Euler") - LINE2; -PROJ_HEAD(pconic, "Perspective Conic") - LINE2; -PROJ_HEAD(vitk1, "Vitkovsky I") - LINE2; + +PROJ_HEAD(euler, "Euler") LINE2; +PROJ_HEAD(murd1, "Murdoch I") LINE2; +PROJ_HEAD(murd2, "Murdoch II") LINE2; +PROJ_HEAD(murd3, "Murdoch III") LINE2; +PROJ_HEAD(pconic, "Perspective Conic") LINE2; +PROJ_HEAD(tissot, "Tissot") LINE2; +PROJ_HEAD(vitk1, "Vitkovsky I") LINE2; + + + /* get common factors for simple conics */ - static int -phi12(PJ *P, double *del) { - double p1, p2; - int err = 0; - - if (!pj_param(P->ctx, P->params, "tlat_1").i || - !pj_param(P->ctx, P->params, "tlat_2").i) { - err = -41; - } else { - p1 = pj_param(P->ctx, P->params, "rlat_1").f; - p2 = pj_param(P->ctx, P->params, "rlat_2").f; - *del = 0.5 * (p2 - p1); - P->sig = 0.5 * (p2 + p1); - err = (fabs(*del) < EPS || fabs(P->sig) < EPS) ? -42 : 0; - *del = *del; - } - return err; -} -FORWARD(s_forward); /* spheroid */ - double rho; - - switch (P->type) { - case MURD2: - rho = P->rho_c + tan(P->sig - lp.phi); - break; - case PCONIC: - rho = P->c2 * (P->c1 - tan(lp.phi - P->sig)); - break; - default: - rho = P->rho_c - lp.phi; - break; - } - xy.x = rho * sin( lp.lam *= P->n ); - xy.y = P->rho_0 - rho * cos(lp.lam); - return (xy); -} -INVERSE(s_inverse); /* ellipsoid & spheroid */ - double rho; - - rho = hypot(xy.x, xy.y = P->rho_0 - xy.y); - if (P->n < 0.) { - rho = - rho; - xy.x = - xy.x; - xy.y = - xy.y; - } - lp.lam = atan2(xy.x, xy.y) / P->n; - switch (P->type) { - case PCONIC: - lp.phi = atan(P->c1 - rho / P->c2) + P->sig; - break; - case MURD2: - lp.phi = P->sig - atan(rho - P->rho_c); - break; - default: - lp.phi = P->rho_c - rho; - } - return (lp); -} -FREEUP; if (P) pj_dalloc(P); } - static PJ * -setup(PJ *P) { - double del, cs; - int i; - - if( (i = phi12(P, &del)) ) - E_ERROR(i); - switch (P->type) { - case TISSOT: - P->n = sin(P->sig); - cs = cos(del); - P->rho_c = P->n / cs + cs / P->n; - P->rho_0 = sqrt((P->rho_c - 2 * sin(P->phi0))/P->n); - break; - case MURD1: - P->rho_c = sin(del)/(del * tan(P->sig)) + P->sig; - P->rho_0 = P->rho_c - P->phi0; - P->n = sin(P->sig); - break; - case MURD2: - P->rho_c = (cs = sqrt(cos(del))) / tan(P->sig); - P->rho_0 = P->rho_c + tan(P->sig - P->phi0); - P->n = sin(P->sig) * cs; - break; - case MURD3: - P->rho_c = del / (tan(P->sig) * tan(del)) + P->sig; - P->rho_0 = P->rho_c - P->phi0; - P->n = sin(P->sig) * sin(del) * tan(del) / (del * del); - break; - case EULER: - P->n = sin(P->sig) * sin(del) / del; - del *= 0.5; - P->rho_c = del / (tan(del) * tan(P->sig)) + P->sig; - P->rho_0 = P->rho_c - P->phi0; - break; - case PCONIC: - P->n = sin(P->sig); - P->c2 = cos(del); - P->c1 = 1./tan(P->sig); - if (fabs(del = P->phi0 - P->sig) - EPS10 >= HALFPI) - E_ERROR(-43); - P->rho_0 = P->c2 * (P->c1 - tan(del)); - break; - case VITK1: - P->n = (cs = tan(del)) * sin(P->sig) / del; - P->rho_c = del / (cs * tan(P->sig)) + P->sig; - P->rho_0 = P->rho_c - P->phi0; - break; - } - P->inv = s_inverse; - P->fwd = s_forward; - P->es = 0; - return (P); -} -ENTRY0(euler) P->type = EULER; ENDENTRY(setup(P)) -ENTRY0(tissot) P->type = TISSOT; ENDENTRY(setup(P)) -ENTRY0(murd1) P->type = MURD1; ENDENTRY(setup(P)) -ENTRY0(murd2) P->type = MURD2; ENDENTRY(setup(P)) -ENTRY0(murd3) P->type = MURD3; ENDENTRY(setup(P)) -ENTRY0(pconic) P->type = PCONIC; ENDENTRY(setup(P)) -ENTRY0(vitk1) P->type = VITK1; ENDENTRY(setup(P)) +static int phi12(PJ *P, double *del) { + double p1, p2; + int err = 0; + + if (!pj_param(P->ctx, P->params, "tlat_1").i || + !pj_param(P->ctx, P->params, "tlat_2").i) { + err = -41; + } else { + p1 = pj_param(P->ctx, P->params, "rlat_1").f; + p2 = pj_param(P->ctx, P->params, "rlat_2").f; + *del = 0.5 * (p2 - p1); + P->opaque->sig = 0.5 * (p2 + p1); + err = (fabs(*del) < EPS || fabs(P->opaque->sig) < EPS) ? -42 : 0; + *del = *del; + } + return err; +} + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0, 0.0}; + struct pj_opaque *Q = P->opaque; + double rho; + + switch (Q->type) { + case MURD2: + rho = Q->rho_c + tan (Q->sig - lp.phi); + break; + case PCONIC: + rho = Q->c2 * (Q->c1 - tan (lp.phi - Q->sig)); + break; + default: + rho = Q->rho_c - lp.phi; + break; + } + + xy.x = rho * sin ( lp.lam *= Q->n ); + xy.y = Q->rho_0 - rho * cos (lp.lam); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, (and ellipsoidal?) inverse */ + LP lp = {0.0, 0.0}; + struct pj_opaque *Q = P->opaque; + double rho; + + rho = hypot (xy.x, xy.y = Q->rho_0 - xy.y); + if (Q->n < 0.) { + rho = - rho; + xy.x = - xy.x; + xy.y = - xy.y; + } + + lp.lam = atan2 (xy.x, xy.y) / Q->n; + + switch (Q->type) { + case PCONIC: + lp.phi = atan (Q->c1 - rho / Q->c2) + Q->sig; + break; + case MURD2: + lp.phi = Q->sig - atan(rho - Q->rho_c); + break; + default: + lp.phi = Q->rho_c - rho; + } + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +static PJ *setup(PJ *P, int type) { + double del, cs; + int i; + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + Q->type = type; + + i = phi12 (P, &del); + if(i) + E_ERROR(i); + switch (Q->type) { + + case TISSOT: + Q->n = sin (Q->sig); + cs = cos (del); + Q->rho_c = Q->n / cs + cs / Q->n; + Q->rho_0 = sqrt ((Q->rho_c - 2 * sin (P->phi0)) / Q->n); + break; + + case MURD1: + Q->rho_c = sin(del)/(del * tan(Q->sig)) + Q->sig; + Q->rho_0 = Q->rho_c - P->phi0; + Q->n = sin(Q->sig); + break; + + case MURD2: + Q->rho_c = (cs = sqrt (cos (del))) / tan (Q->sig); + Q->rho_0 = Q->rho_c + tan (Q->sig - P->phi0); + Q->n = sin (Q->sig) * cs; + break; + + case MURD3: + Q->rho_c = del / (tan(Q->sig) * tan(del)) + Q->sig; + Q->rho_0 = Q->rho_c - P->phi0; + Q->n = sin (Q->sig) * sin (del) * tan (del) / (del * del); + break; + + case EULER: + Q->n = sin (Q->sig) * sin (del) / del; + del *= 0.5; + Q->rho_c = del / (tan (del) * tan (Q->sig)) + Q->sig; + Q->rho_0 = Q->rho_c - P->phi0; + break; + + case PCONIC: + Q->n = sin (Q->sig); + Q->c2 = cos (del); + Q->c1 = 1./tan (Q->sig); + if (fabs (del = P->phi0 - Q->sig) - EPS10 >= HALFPI) + E_ERROR(-43); + Q->rho_0 = Q->c2 * (Q->c1 - tan (del)); + break; + + case VITK1: + Q->n = (cs = tan (del)) * sin (Q->sig) / del; + Q->rho_c = del / (cs * tan (Q->sig)) + Q->sig; + Q->rho_0 = Q->rho_c - P->phi0; + break; + } + + P->inv = s_inverse; + P->fwd = s_forward; + P->es = 0; + return (P); +} + + +PJ *PROJECTION(euler) { + return setup(P, EULER); +} + + +PJ *PROJECTION(tissot) { + return setup(P, TISSOT); +} + + +PJ *PROJECTION(murd1) { + return setup(P, MURD1); +} + + +PJ *PROJECTION(murd2) { + return setup(P, MURD2); +} + + +PJ *PROJECTION(murd3) { + return setup(P, MURD3); +} + + +PJ *PROJECTION(pconic) { + return setup(P, PCONIC); +} + + +PJ *PROJECTION(vitk1) { + return setup(P, VITK1); +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_euler_selftest (void) {return 0;} +#else + +int pj_euler_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=euler +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5"}; + char s_args[] = {"+proj=euler +a=6400000 +lat_1=0.5 +lat_2=2 +n=0.5"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {222597.63465910763, 111404.24054991946}, + {222767.16563187627, -111234.6764910177}, + {-222597.63465910763, 111404.24054991946}, + {-222767.16563187627, -111234.6764910177}, + }; + + XY s_fwd_expect[] = { + {223360.65559869423, 111786.11238979101}, + {223530.76769031584, -111615.96709862351}, + {-223360.65559869423, 111786.11238979101}, + {-223530.76769031584, -111615.96709862351}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {0.0017962807023075235, 0.0008983146697688839}, + {0.0017962794738334226, -0.00089831589842987965}, + {-0.0017962807023075235, 0.0008983146697688839}, + {-0.0017962794738334226, -0.00089831589842987965}, + }; + + LP s_inv_expect[] = { + {0.0017901444369360026, 0.00089524594522202015}, + {0.001790143216840731, -0.00089524716533368484}, + {-0.0017901444369360026, 0.00089524594522202015}, + {-0.001790143216840731, -0.00089524716533368484}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif + + + + + + +#ifdef PJ_OMIT_SELFTEST +int pj_murd1_selftest (void) {return 0;} +#else + +int pj_murd1_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=murd1 +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5"}; + char s_args[] = {"+proj=murd1 +a=6400000 +lat_1=0.5 +lat_2=2 +n=0.5"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {222600.81347355421, 111404.24418054636}, + {222770.3492878644, -111234.6728566746}, + {-222600.81347355421, 111404.24418054636}, + {-222770.3492878644, -111234.6728566746}, + }; + + XY s_fwd_expect[] = { + {223363.84530949194, 111786.11603286299}, + {223533.96225925098, -111615.96345182261}, + {-223363.84530949194, 111786.11603286299}, + {-223533.96225925098, -111615.96345182261}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {0.0017962550410516366, 0.0008983146697688839}, + {0.0017962538125775522, -0.00089831589842987965}, + {-0.0017962550410516366, 0.0008983146697688839}, + {-0.0017962538125775522, -0.00089831589842987965}, + }; + + LP s_inv_expect[] = { + {0.0017901188633413715, 0.00089524594522202015}, + {0.0017901176432461162, -0.00089524716492657387}, + {-0.0017901188633413715, 0.00089524594522202015}, + {-0.0017901176432461162, -0.00089524716492657387}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif + + + + + + + + + + + + + + +#ifdef PJ_OMIT_SELFTEST +int pj_murd2_selftest (void) {return 0;} +#else + +int pj_murd2_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=murd2 +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5"}; + char s_args[] = {"+proj=murd2 +a=6400000 +lat_1=0.5 +lat_2=2 +n=0.5"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {222588.09975123021, 111426.14002741246}, + {222757.72626701824, -111341.43131750476}, + {-222588.09975123021, 111426.14002741246}, + {-222757.72626701824, -111341.43131750476}, + }; + + XY s_fwd_expect[] = { + {223351.08800702673, 111808.08693438848}, + {223521.2959691704, -111723.08785967289}, + {-223351.08800702673, 111808.08693438848}, + {-223521.2959691704, -111723.08785967289}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {0.0017963574947305447, 0.00089788747830845382}, + {0.0017963562661689487, -0.00089788809264252983}, + {-0.0017963574947305447, 0.00089788747830845382}, + {-0.0017963562661689487, -0.00089788809264252983}, + }; + + LP s_inv_expect[] = { + {0.0017902209670287586, 0.00089482021163422854}, + {0.0017902197468465887, -0.00089482082161134206}, + {-0.0017902209670287586, 0.00089482021163422854}, + {-0.0017902197468465887, -0.00089482082161134206}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif + + + + + + + + + + +#ifdef PJ_OMIT_SELFTEST +int pj_murd3_selftest (void) {return 0;} +#else + +int pj_murd3_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=murd3 +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5"}; + char s_args[] = {"+proj=murd3 +a=6400000 +lat_1=0.5 +lat_2=2 +n=0.5"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {222600.81407757697, 111404.24660137216}, + {222770.35473389886, -111234.67043217793}, + {-222600.81407757697, 111404.24660137216}, + {-222770.35473389886, -111234.67043217793}, + }; + + XY s_fwd_expect[] = { + {223363.84591558515, 111786.11846198692}, + {223533.96772395336, -111615.96101901523}, + {-223363.84591558515, 111786.11846198692}, + {-223533.96772395336, -111615.96101901523}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {0.0017962550166583809, 0.0008983146697688839}, + {0.0017962537881492445, -0.00089831589842987965}, + {-0.0017962550166583809, 0.0008983146697688839}, + {-0.0017962537881492445, -0.00089831589842987965}, + }; + + LP s_inv_expect[] = { + {0.0017901188390313859, 0.00089524594522202015}, + {0.0017901176189013177, -0.00089524716533368484}, + {-0.0017901188390313859, 0.00089524594522202015}, + {-0.0017901176189013177, -0.00089524716533368484}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif + + + + + + + + + + + +#ifdef PJ_OMIT_SELFTEST +int pj_pconic_selftest (void) {return 0;} +#else + +int pj_pconic_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=pconic +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5"}; + char s_args[] = {"+proj=pconic +a=6400000 +lat_1=0.5 +lat_2=2 +n=0.5"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {222588.09884161691, 111416.60477006658}, + {222757.71809109033, -111331.88153107995}, + {-222588.09884161691, 111416.60477006658}, + {-222757.71809109033, -111331.88153107995}, + }; + + XY s_fwd_expect[] = { + {223351.08709429545, 111798.5189920546}, + {223521.28776521701, -111713.50533845725}, + {-223351.08709429545, 111798.5189920546}, + {-223521.28776521701, -111713.50533845725}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {0.0017963575313784969, 0.0008979644089172499}, + {0.0017963563027642206, -0.00089796502355327969}, + {-0.0017963575313784969, 0.0008979644089172499}, + {-0.0017963563027642206, -0.00089796502355327969}, + }; + + LP s_inv_expect[] = { + {0.0017902210035514285, 0.0008948968793741558}, + {0.0017902197833169374, -0.00089489748965381963}, + {-0.0017902210035514285, 0.0008948968793741558}, + {-0.0017902197833169374, -0.00089489748965381963}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif + + + + + + + + + + + + + + + +#ifdef PJ_OMIT_SELFTEST +int pj_tissot_selftest (void) {return 0;} +#else + +int pj_tissot_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=tissot +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5"}; + char s_args[] = {"+proj=tissot +a=6400000 +lat_1=0.5 +lat_2=2 +n=0.5"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {222641.07869963095, 54347.828487281469}, + {222810.61451394114, -168291.08854993948}, + {-222641.07869963095, 54347.828487281469}, + {-222810.61451394114, -168291.08854993948}, + }; + + XY s_fwd_expect[] = { + {223404.24855684943, 54534.122161157939}, + {223574.36550660848, -168867.95732352766}, + {-223404.24855684943, 54534.122161157939}, + {-223574.36550660848, -168867.95732352766}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {0.0017962807107425871, 0.51344495513064536}, + {0.0017962794822333915, 0.51164832456244658}, + {-0.0017962807107425871, 0.51344495513064536}, + {-0.0017962794822333915, 0.51164832456244658}, + }; + + LP s_inv_expect[] = { + {0.0017901444453421915, 0.51344188640609856}, + {0.001790143225212064, 0.51165139329554277}, + {-0.0017901444453421915, 0.51344188640609856}, + {-0.001790143225212064, 0.51165139329554277}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif + + + + + + + + +#ifdef PJ_OMIT_SELFTEST +int pj_vitk1_selftest (void) {return 0;} +#else + +int pj_vitk1_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=vitk1 +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5"}; + char s_args[] = {"+proj=vitk1 +a=6400000 +lat_1=0.5 +lat_2=2 +n=0.5"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {222607.17121145778, 111404.25144243463}, + {222776.71670959776, -111234.66558744459}, + {-222607.17121145778, 111404.25144243463}, + {-222776.71670959776, -111234.66558744459}, + }; + + XY s_fwd_expect[] = { + {223370.22484047143, 111786.12331964359}, + {223540.3515072545, -111615.9561576751}, + {-223370.22484047143, 111786.12331964359}, + {-223540.3515072545, -111615.9561576751}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {0.0017962037198570686, 0.0008983146697688839}, + {0.0017962024913830157, -0.00089831589842987965}, + {-0.0017962037198570686, 0.0008983146697688839}, + {-0.0017962024913830157, -0.00089831589842987965}, + }; + + LP s_inv_expect[] = { + {0.0017900677174648159, 0.00089524594522202015}, + {0.0017900664973695916, -0.00089524716533368484}, + {-0.0017900677174648159, 0.00089524594522202015}, + {-0.0017900664973695916, -0.00089524716533368484}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif diff --git a/src/PJ_somerc.c b/src/PJ_somerc.c index f7d1f119..e7659876 100644 --- a/src/PJ_somerc.c +++ b/src/PJ_somerc.c @@ -1,66 +1,165 @@ -#define PROJ_PARMS__ \ - double K, c, hlf_e, kR, cosp0, sinp0; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(somerc, "Swiss. Obl. Mercator") "\n\tCyl, Ell\n\tFor CH1903"; -#define EPS 1.e-10 + +struct pj_opaque { + double K, c, hlf_e, kR, cosp0, sinp0; +}; + +#define EPS 1.e-10 #define NITER 6 -FORWARD(e_forward); - double phip, lamp, phipp, lampp, sp, cp; - - sp = P->e * sin(lp.phi); - phip = 2.* atan( exp( P->c * ( - log(tan(FORTPI + 0.5 * lp.phi)) - P->hlf_e * log((1. + sp)/(1. - sp))) - + P->K)) - HALFPI; - lamp = P->c * lp.lam; - cp = cos(phip); - phipp = aasin(P->ctx,P->cosp0 * sin(phip) - P->sinp0 * cp * cos(lamp)); - lampp = aasin(P->ctx,cp * sin(lamp) / cos(phipp)); - xy.x = P->kR * lampp; - xy.y = P->kR * log(tan(FORTPI + 0.5 * phipp)); - return (xy); + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0, 0.0}; + double phip, lamp, phipp, lampp, sp, cp; + struct pj_opaque *Q = P->opaque; + + sp = P->e * sin (lp.phi); + phip = 2.* atan ( exp ( Q->c * ( + log (tan (FORTPI + 0.5 * lp.phi)) - Q->hlf_e * log ((1. + sp)/(1. - sp))) + + Q->K)) - HALFPI; + lamp = Q->c * lp.lam; + cp = cos(phip); + phipp = aasin (P->ctx, Q->cosp0 * sin (phip) - Q->sinp0 * cp * cos (lamp)); + lampp = aasin (P->ctx, cp * sin (lamp) / cos (phipp)); + xy.x = Q->kR * lampp; + xy.y = Q->kR * log (tan (FORTPI + 0.5 * phipp)); + return xy; } -INVERSE(e_inverse); /* ellipsoid & spheroid */ - double phip, lamp, phipp, lampp, cp, esp, con, delp; - int i; - - phipp = 2. * (atan(exp(xy.y / P->kR)) - FORTPI); - lampp = xy.x / P->kR; - cp = cos(phipp); - phip = aasin(P->ctx,P->cosp0 * sin(phipp) + P->sinp0 * cp * cos(lampp)); - lamp = aasin(P->ctx,cp * sin(lampp) / cos(phip)); - con = (P->K - log(tan(FORTPI + 0.5 * phip)))/P->c; - for (i = NITER; i ; --i) { - esp = P->e * sin(phip); - delp = (con + log(tan(FORTPI + 0.5 * phip)) - P->hlf_e * - log((1. + esp)/(1. - esp)) ) * - (1. - esp * esp) * cos(phip) * P->rone_es; - phip -= delp; - if (fabs(delp) < EPS) - break; - } - if (i) { - lp.phi = phip; - lp.lam = lamp / P->c; - } else - I_ERROR - return (lp); + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double phip, lamp, phipp, lampp, cp, esp, con, delp; + int i; + + phipp = 2. * (atan (exp (xy.y / Q->kR)) - FORTPI); + lampp = xy.x / Q->kR; + cp = cos (phipp); + phip = aasin (P->ctx, Q->cosp0 * sin (phipp) + Q->sinp0 * cp * cos (lampp)); + lamp = aasin (P->ctx, cp * sin (lampp) / cos (phip)); + con = (Q->K - log (tan (FORTPI + 0.5 * phip)))/Q->c; + for (i = NITER; i ; --i) { + esp = P->e * sin(phip); + delp = (con + log(tan(FORTPI + 0.5 * phip)) - Q->hlf_e * + log((1. + esp)/(1. - esp)) ) * + (1. - esp * esp) * cos(phip) * P->rone_es; + phip -= delp; + if (fabs(delp) < EPS) + break; + } + if (i) { + lp.phi = phip; + lp.lam = lamp / Q->c; + } else + I_ERROR + return (lp); } + + +#if 0 FREEUP; if (P) pj_dalloc(P); } -ENTRY0(somerc) - double cp, phip0, sp; - - P->hlf_e = 0.5 * P->e; - cp = cos(P->phi0); - cp *= cp; - P->c = sqrt(1 + P->es * cp * cp * P->rone_es); - sp = sin(P->phi0); - P->cosp0 = cos( phip0 = aasin(P->ctx, P->sinp0 = sp / P->c) ); - sp *= P->e; - P->K = log(tan(FORTPI + 0.5 * phip0)) - P->c * ( - log(tan(FORTPI + 0.5 * P->phi0)) - P->hlf_e * - log((1. + sp) / (1. - sp))); - P->kR = P->k0 * sqrt(P->one_es) / (1. - sp * sp); - P->inv = e_inverse; - P->fwd = e_forward; -ENDENTRY(P) +#endif + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(somerc) { + double cp, phip0, sp; + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + + Q->hlf_e = 0.5 * P->e; + cp = cos (P->phi0); + cp *= cp; + Q->c = sqrt (1 + P->es * cp * cp * P->rone_es); + sp = sin (P->phi0); + Q->cosp0 = cos( phip0 = aasin (P->ctx, Q->sinp0 = sp / Q->c) ); + sp *= P->e; + Q->K = log (tan (FORTPI + 0.5 * phip0)) - Q->c * ( + log (tan (FORTPI + 0.5 * P->phi0)) - Q->hlf_e * + log ((1. + sp) / (1. - sp))); + Q->kR = P->k0 * sqrt(P->one_es) / (1. - sp * sp); + P->inv = e_inverse; + P->fwd = e_forward; + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_somerc_selftest (void) {return 0;} +#else + +int pj_somerc_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=somerc +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5"}; + char s_args[] = {"+proj=somerc +a=6400000 +lat_1=0.5 +lat_2=2 +n=0.5"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {222638.98158654713, 110579.96521824898}, + {222638.98158654713, -110579.96521825089}, + {-222638.98158654713, 110579.96521824898}, + {-222638.98158654713, -110579.96521825089}, + }; + + XY s_fwd_expect[] = { + {223402.14425527418, 111706.74357494408}, + {223402.14425527418, -111706.74357494518}, + {-223402.14425527418, 111706.74357494408}, + {-223402.14425527418, -111706.74357494518}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {0.0017966305682390426, 0.00090436947704129484}, + {0.0017966305682390426, -0.00090436947704377105}, + {-0.0017966305682390426, 0.00090436947704129484}, + {-0.0017966305682390426, -0.00090436947704377105}, + }; + + LP s_inv_expect[] = { + {0.0017904931097838226, 0.00089524655485801927}, + {0.0017904931097838226, -0.00089524655484529714}, + {-0.0017904931097838226, 0.00089524655485801927}, + {-0.0017904931097838226, -0.00089524655484529714}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif diff --git a/src/PJ_stere.c b/src/PJ_stere.c index a0524627..fbca195f 100644 --- a/src/PJ_stere.c +++ b/src/PJ_stere.c @@ -1,239 +1,409 @@ -#define PROJ_PARMS__ \ - double phits; \ - double sinX1; \ - double cosX1; \ - double akm1; \ - int mode; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(stere, "Stereographic") "\n\tAzi, Sph&Ell\n\tlat_ts="; PROJ_HEAD(ups, "Universal Polar Stereographic") "\n\tAzi, Sph&Ell\n\tsouth"; -#define sinph0 P->sinX1 -#define cosph0 P->cosX1 -#define EPS10 1.e-10 -#define TOL 1.e-8 -#define NITER 8 -#define CONV 1.e-10 -#define S_POLE 0 -#define N_POLE 1 -#define OBLIQ 2 -#define EQUIT 3 - static double -ssfn_(double phit, double sinphi, double eccen) { - sinphi *= eccen; - return (tan (.5 * (HALFPI + phit)) * - pow((1. - sinphi) / (1. + sinphi), .5 * eccen)); + + +struct pj_opaque { + double phits; + double sinX1; + double cosX1; + double akm1; + int mode; +}; + +#define sinph0 P->opaque->sinX1 +#define cosph0 P->opaque->cosX1 +#define EPS10 1.e-10 +#define TOL 1.e-8 +#define NITER 8 +#define CONV 1.e-10 +#define S_POLE 0 +#define N_POLE 1 +#define OBLIQ 2 +#define EQUIT 3 + +static double ssfn_ (double phit, double sinphi, double eccen) { + sinphi *= eccen; + return (tan (.5 * (HALFPI + phit)) * + pow ((1. - sinphi) / (1. + sinphi), .5 * eccen)); } -FORWARD(e_forward); /* ellipsoid */ - double coslam, sinlam, sinX=0.0, cosX=0.0, X, A, sinphi; - - coslam = cos(lp.lam); - sinlam = sin(lp.lam); - sinphi = sin(lp.phi); - if (P->mode == OBLIQ || P->mode == EQUIT) { - sinX = sin(X = 2. * atan(ssfn_(lp.phi, sinphi, P->e)) - HALFPI); - cosX = cos(X); - } - switch (P->mode) { - case OBLIQ: - A = P->akm1 / (P->cosX1 * (1. + P->sinX1 * sinX + - P->cosX1 * cosX * coslam)); - xy.y = A * (P->cosX1 * sinX - P->sinX1 * cosX * coslam); - goto xmul; - case EQUIT: - A = 2. * P->akm1 / (1. + cosX * coslam); - xy.y = A * sinX; + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double coslam, sinlam, sinX = 0.0, cosX = 0.0, X, A, sinphi; + + coslam = cos (lp.lam); + sinlam = sin (lp.lam); + sinphi = sin (lp.phi); + if (Q->mode == OBLIQ || Q->mode == EQUIT) { + sinX = sin (X = 2. * atan(ssfn_(lp.phi, sinphi, P->e)) - HALFPI); + cosX = cos (X); + } + + switch (Q->mode) { + case OBLIQ: + A = Q->akm1 / (Q->cosX1 * (1. + Q->sinX1 * sinX + + Q->cosX1 * cosX * coslam)); + xy.y = A * (Q->cosX1 * sinX - Q->sinX1 * cosX * coslam); + goto xmul; /* but why not just xy.x = A * cosX; break; ? */ + + case EQUIT: + A = 2. * Q->akm1 / (1. + cosX * coslam); + xy.y = A * sinX; xmul: - xy.x = A * cosX; - break; - case S_POLE: - lp.phi = -lp.phi; - coslam = - coslam; - sinphi = -sinphi; - case N_POLE: - xy.x = P->akm1 * pj_tsfn(lp.phi, sinphi, P->e); - xy.y = - xy.x * coslam; - break; - } - xy.x = xy.x * sinlam; - return (xy); + xy.x = A * cosX; + break; + + case S_POLE: + lp.phi = -lp.phi; + coslam = - coslam; + sinphi = -sinphi; + case N_POLE: + xy.x = Q->akm1 * pj_tsfn (lp.phi, sinphi, P->e); + xy.y = - xy.x * coslam; + break; + } + + xy.x = xy.x * sinlam; + return xy; } -FORWARD(s_forward); /* spheroid */ - double sinphi, cosphi, coslam, sinlam; - - sinphi = sin(lp.phi); - cosphi = cos(lp.phi); - coslam = cos(lp.lam); - sinlam = sin(lp.lam); - switch (P->mode) { - case EQUIT: - xy.y = 1. + cosphi * coslam; - goto oblcon; - case OBLIQ: - xy.y = 1. + sinph0 * sinphi + cosph0 * cosphi * coslam; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double sinphi, cosphi, coslam, sinlam; + + sinphi = sin(lp.phi); + cosphi = cos(lp.phi); + coslam = cos(lp.lam); + sinlam = sin(lp.lam); + + switch (Q->mode) { + case EQUIT: + xy.y = 1. + cosphi * coslam; + goto oblcon; + case OBLIQ: + xy.y = 1. + sinph0 * sinphi + cosph0 * cosphi * coslam; oblcon: - if (xy.y <= EPS10) F_ERROR; - xy.x = (xy.y = P->akm1 / xy.y) * cosphi * sinlam; - xy.y *= (P->mode == EQUIT) ? sinphi : - cosph0 * sinphi - sinph0 * cosphi * coslam; - break; - case N_POLE: - coslam = - coslam; - lp.phi = - lp.phi; - case S_POLE: - if (fabs(lp.phi - HALFPI) < TOL) F_ERROR; - xy.x = sinlam * ( xy.y = P->akm1 * tan(FORTPI + .5 * lp.phi) ); - xy.y *= coslam; - break; - } - return (xy); + if (xy.y <= EPS10) F_ERROR; + xy.x = (xy.y = Q->akm1 / xy.y) * cosphi * sinlam; + xy.y *= (Q->mode == EQUIT) ? sinphi : + cosph0 * sinphi - sinph0 * cosphi * coslam; + break; + case N_POLE: + coslam = - coslam; + lp.phi = - lp.phi; + case S_POLE: + if (fabs (lp.phi - HALFPI) < TOL) F_ERROR; + xy.x = sinlam * ( xy.y = Q->akm1 * tan (FORTPI + .5 * lp.phi) ); + xy.y *= coslam; + break; + } + return xy; } -INVERSE(e_inverse); /* ellipsoid */ - double cosphi, sinphi, tp=0.0, phi_l=0.0, rho, halfe=0.0, halfpi=0.0; - int i; - - rho = hypot(xy.x, xy.y); - switch (P->mode) { - case OBLIQ: - case EQUIT: - cosphi = cos( tp = 2. * atan2(rho * P->cosX1 , P->akm1) ); - sinphi = sin(tp); - if( rho == 0.0 ) - phi_l = asin(cosphi * P->sinX1); + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double cosphi, sinphi, tp=0.0, phi_l=0.0, rho, halfe=0.0, halfpi=0.0; + int i; + + rho = hypot (xy.x, xy.y); + + switch (Q->mode) { + case OBLIQ: + case EQUIT: + cosphi = cos ( tp = 2. * atan2 (rho * Q->cosX1 , Q->akm1) ); + sinphi = sin (tp); + if ( rho == 0.0 ) + phi_l = asin (cosphi * Q->sinX1); else - phi_l = asin(cosphi * P->sinX1 + (xy.y * sinphi * P->cosX1 / rho)); - - tp = tan(.5 * (HALFPI + phi_l)); - xy.x *= sinphi; - xy.y = rho * P->cosX1 * cosphi - xy.y * P->sinX1* sinphi; - halfpi = HALFPI; - halfe = .5 * P->e; - break; - case N_POLE: - xy.y = -xy.y; - case S_POLE: - phi_l = HALFPI - 2. * atan(tp = - rho / P->akm1); - halfpi = -HALFPI; - halfe = -.5 * P->e; - break; - } - for (i = NITER; i--; phi_l = lp.phi) { - sinphi = P->e * sin(phi_l); - lp.phi = 2. * atan(tp * pow((1.+sinphi)/(1.-sinphi), - halfe)) - halfpi; - if (fabs(phi_l - lp.phi) < CONV) { - if (P->mode == S_POLE) - lp.phi = -lp.phi; - lp.lam = (xy.x == 0. && xy.y == 0.) ? 0. : atan2(xy.x, xy.y); - return (lp); - } - } - I_ERROR; + phi_l = asin (cosphi * Q->sinX1 + (xy.y * sinphi * Q->cosX1 / rho)); + + tp = tan (.5 * (HALFPI + phi_l)); + xy.x *= sinphi; + xy.y = rho * Q->cosX1 * cosphi - xy.y * Q->sinX1* sinphi; + halfpi = HALFPI; + halfe = .5 * P->e; + break; + case N_POLE: + xy.y = -xy.y; + case S_POLE: + phi_l = HALFPI - 2. * atan (tp = - rho / Q->akm1); + halfpi = -HALFPI; + halfe = -.5 * P->e; + break; + } + + for (i = NITER; i--; phi_l = lp.phi) { + sinphi = P->e * sin(phi_l); + lp.phi = 2. * atan (tp * pow ((1.+sinphi)/(1.-sinphi), halfe)) - halfpi; + if (fabs (phi_l - lp.phi) < CONV) { + if (Q->mode == S_POLE) + lp.phi = -lp.phi; + lp.lam = (xy.x == 0. && xy.y == 0.) ? 0. : atan2 (xy.x, xy.y); + return lp; + } + } + I_ERROR; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double c, rh, sinc, cosc; + + sinc = sin (c = 2. * atan ((rh = hypot (xy.x, xy.y)) / Q->akm1)); + cosc = cos (c); + lp.lam = 0.; + + switch (Q->mode) { + case EQUIT: + if (fabs (rh) <= EPS10) + lp.phi = 0.; + else + lp.phi = asin (xy.y * sinc / rh); + if (cosc != 0. || xy.x != 0.) + lp.lam = atan2 (xy.x * sinc, cosc * rh); + break; + case OBLIQ: + if (fabs (rh) <= EPS10) + lp.phi = P->phi0; + else + lp.phi = asin (cosc * sinph0 + xy.y * sinc * cosph0 / rh); + if ((c = cosc - sinph0 * sin (lp.phi)) != 0. || xy.x != 0.) + lp.lam = atan2 (xy.x * sinc * cosph0, c * rh); + break; + case N_POLE: + xy.y = -xy.y; + case S_POLE: + if (fabs (rh) <= EPS10) + lp.phi = P->phi0; + else + lp.phi = asin (Q->mode == S_POLE ? - cosc : cosc); + lp.lam = (xy.x == 0. && xy.y == 0.) ? 0. : atan2 (xy.x, xy.y); + break; + } + return lp; } -INVERSE(s_inverse); /* spheroid */ - double c, rh, sinc, cosc; - - sinc = sin(c = 2. * atan((rh = hypot(xy.x, xy.y)) / P->akm1)); - cosc = cos(c); - lp.lam = 0.; - switch (P->mode) { - case EQUIT: - if (fabs(rh) <= EPS10) - lp.phi = 0.; - else - lp.phi = asin(xy.y * sinc / rh); - if (cosc != 0. || xy.x != 0.) - lp.lam = atan2(xy.x * sinc, cosc * rh); - break; - case OBLIQ: - if (fabs(rh) <= EPS10) - lp.phi = P->phi0; - else - lp.phi = asin(cosc * sinph0 + xy.y * sinc * cosph0 / rh); - if ((c = cosc - sinph0 * sin(lp.phi)) != 0. || xy.x != 0.) - lp.lam = atan2(xy.x * sinc * cosph0, c * rh); - break; - case N_POLE: - xy.y = -xy.y; - case S_POLE: - if (fabs(rh) <= EPS10) - lp.phi = P->phi0; - else - lp.phi = asin(P->mode == S_POLE ? - cosc : cosc); - lp.lam = (xy.x == 0. && xy.y == 0.) ? 0. : atan2(xy.x, xy.y); - break; - } - return (lp); + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +static PJ *setup(PJ *P) { /* general initialization */ + double t; + struct pj_opaque *Q = P->opaque; + + if (fabs ((t = fabs (P->phi0)) - HALFPI) < EPS10) + Q->mode = P->phi0 < 0. ? S_POLE : N_POLE; + else + Q->mode = t > EPS10 ? OBLIQ : EQUIT; + Q->phits = fabs (Q->phits); + + if (P->es) { + double X; + + switch (Q->mode) { + case N_POLE: + case S_POLE: + if (fabs (Q->phits - HALFPI) < EPS10) + Q->akm1 = 2. * P->k0 / + sqrt (pow (1+P->e,1+P->e) * pow (1-P->e,1-P->e)); + else { + Q->akm1 = cos (Q->phits) / + pj_tsfn (Q->phits, t = sin (Q->phits), P->e); + t *= P->e; + Q->akm1 /= sqrt(1. - t * t); + } + break; + case EQUIT: + case OBLIQ: + t = sin (P->phi0); + X = 2. * atan (ssfn_(P->phi0, t, P->e)) - HALFPI; + t *= P->e; + Q->akm1 = 2. * P->k0 * cos (P->phi0) / sqrt(1. - t * t); + Q->sinX1 = sin (X); + Q->cosX1 = cos (X); + break; + } + P->inv = e_inverse; + P->fwd = e_forward; + } else { + switch (Q->mode) { + case OBLIQ: + sinph0 = sin (P->phi0); + cosph0 = cos (P->phi0); + case EQUIT: + Q->akm1 = 2. * P->k0; + break; + case S_POLE: + case N_POLE: + Q->akm1 = fabs (Q->phits - HALFPI) >= EPS10 ? + cos (Q->phits) / tan (FORTPI - .5 * Q->phits) : + 2. * P->k0 ; + break; + } + + P->inv = s_inverse; + P->fwd = s_forward; + } + return P; } -FREEUP; if (P) pj_dalloc(P); } - static PJ * -setup(PJ *P) { /* general initialization */ - double t; - - if (fabs((t = fabs(P->phi0)) - HALFPI) < EPS10) - P->mode = P->phi0 < 0. ? S_POLE : N_POLE; - else - P->mode = t > EPS10 ? OBLIQ : EQUIT; - P->phits = fabs(P->phits); - if (P->es) { - double X; - - switch (P->mode) { - case N_POLE: - case S_POLE: - if (fabs(P->phits - HALFPI) < EPS10) - P->akm1 = 2. * P->k0 / - sqrt(pow(1+P->e,1+P->e)*pow(1-P->e,1-P->e)); - else { - P->akm1 = cos(P->phits) / - pj_tsfn(P->phits, t = sin(P->phits), P->e); - t *= P->e; - P->akm1 /= sqrt(1. - t * t); - } - break; - case EQUIT: - case OBLIQ: - t = sin(P->phi0); - X = 2. * atan(ssfn_(P->phi0, t, P->e)) - HALFPI; - t *= P->e; - P->akm1 = 2. * P->k0 * cos(P->phi0) / sqrt(1. - t * t); - P->sinX1 = sin(X); - P->cosX1 = cos(X); - break; - } - P->inv = e_inverse; - P->fwd = e_forward; - } else { - switch (P->mode) { - case OBLIQ: - sinph0 = sin(P->phi0); - cosph0 = cos(P->phi0); - case EQUIT: - P->akm1 = 2. * P->k0; - break; - case S_POLE: - case N_POLE: - P->akm1 = fabs(P->phits - HALFPI) >= EPS10 ? - cos(P->phits) / tan(FORTPI - .5 * P->phits) : - 2. * P->k0 ; - break; - } - P->inv = s_inverse; - P->fwd = s_forward; - } - return P; + + +PJ *PROJECTION(stere) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->phits = pj_param (P->ctx, P->params, "tlat_ts").i ? + pj_param (P->ctx, P->params, "rlat_ts").f : HALFPI; + + return setup(P); } -ENTRY0(stere) - P->phits = pj_param(P->ctx, P->params, "tlat_ts").i ? - pj_param(P->ctx, P->params, "rlat_ts").f : HALFPI; -ENDENTRY(setup(P)) -ENTRY0(ups) + + +PJ *PROJECTION(ups) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + /* International Ellipsoid */ P->phi0 = pj_param(P->ctx, P->params, "bsouth").i ? - HALFPI: HALFPI; if (!P->es) E_ERROR(-34); P->k0 = .994; P->x0 = 2000000.; P->y0 = 2000000.; - P->phits = HALFPI; + Q->phits = HALFPI; P->lam0 = 0.; -ENDENTRY(setup(P)) + + return setup(P); +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_stere_selftest (void) {return 0;} +#else + +int pj_stere_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=stere +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5"}; + char s_args[] = {"+proj=stere +a=6400000 +lat_1=0.5 +lat_2=2 +n=0.5"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 445289.70910023432, 221221.76694834774}, + { 445289.70910023432, -221221.76694835056}, + {-445289.70910023432, 221221.76694834774}, + {-445289.70910023432, -221221.76694835056}, + }; + + XY s_fwd_expect[] = { + { 223407.81025950745, 111737.938996443}, + { 223407.81025950745, -111737.938996443}, + {-223407.81025950745, 111737.938996443}, + {-223407.81025950745, -111737.938996443}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + { 0.0017966305682022392, 0.00090436947502443507}, + { 0.0017966305682022392, -0.00090436947502443507}, + {-0.0017966305682022392, 0.00090436947502443507}, + {-0.0017966305682022392, -0.00090436947502443507}, + }; + + LP s_inv_expect[] = { + { 0.001790493109747395, 0.00089524655465513144}, + { 0.001790493109747395, -0.00089524655465513144}, + {-0.001790493109747395, 0.00089524655465513144}, + {-0.001790493109747395, -0.00089524655465513144}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif + + + + + +#ifdef PJ_OMIT_SELFTEST +int pj_ups_selftest (void) {return 0;} +#else + +int pj_ups_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=ups +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {2433455.5634384668, -10412543.301512826}, + {2448749.1185681992, -10850493.419804076}, + {1566544.4365615332, -10412543.301512826}, + {1551250.8814318008, -10850493.419804076}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {-44.998567498074834, 64.9182362867341}, + {-44.995702709112308, 64.917020250675748}, + {-45.004297076028529, 64.915804280954518}, + {-45.001432287066002, 64.914588377560719}, + }; + + return pj_generic_selftest (e_args, 0, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, 0, inv_in, e_inv_expect, 0); +} + + +#endif diff --git a/src/PJ_sterea.c b/src/PJ_sterea.c index fff2e952..93799011 100644 --- a/src/PJ_sterea.c +++ b/src/PJ_sterea.c @@ -23,61 +23,156 @@ ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#define PROJ_PARMS__ \ - double phic0; \ - double cosc0, sinc0; \ - double R2; \ - void *en; - #define PJ_LIB__ -#include <projects.h> - -PROJ_HEAD(sterea, "Oblique Stereographic Alternative") - "\n\tAzimuthal, Sph&Ell"; -# define DEL_TOL 1.e-14 -# define MAX_ITER 10 - -FORWARD(e_forward); /* ellipsoid */ - double cosc, sinc, cosl, k; - - lp = pj_gauss(P->ctx, lp, P->en); - sinc = sin(lp.phi); - cosc = cos(lp.phi); - cosl = cos(lp.lam); - k = P->k0 * P->R2 / (1. + P->sinc0 * sinc + P->cosc0 * cosc * cosl); - xy.x = k * cosc * sin(lp.lam); - xy.y = k * (P->cosc0 * sinc - P->sinc0 * cosc * cosl); - return (xy); +#include <projects.h> + + +struct pj_opaque { + double phic0; + double cosc0, sinc0; + double R2; + void *en; +}; + + +PROJ_HEAD(sterea, "Oblique Stereographic Alternative") "\n\tAzimuthal, Sph&Ell"; +# define DEL_TOL 1.e-14 +# define MAX_ITER 10 + + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double cosc, sinc, cosl, k; + + lp = pj_gauss(P->ctx, lp, Q->en); + sinc = sin(lp.phi); + cosc = cos(lp.phi); + cosl = cos(lp.lam); + k = P->k0 * Q->R2 / (1. + Q->sinc0 * sinc + Q->cosc0 * cosc * cosl); + xy.x = k * cosc * sin(lp.lam); + xy.y = k * (Q->cosc0 * sinc - Q->sinc0 * cosc * cosl); + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double rho, c, sinc, cosc; + + xy.x /= P->k0; + xy.y /= P->k0; + if ( (rho = hypot (xy.x, xy.y)) ) { + c = 2. * atan2 (rho, Q->R2); + sinc = sin (c); + cosc = cos (c); + lp.phi = asin (cosc * Q->sinc0 + xy.y * sinc * Q->cosc0 / rho); + lp.lam = atan2 (xy.x * sinc, rho * Q->cosc0 * cosc - xy.y * Q->sinc0 * sinc); + } else { + lp.phi = Q->phic0; + lp.lam = 0.; + } + return pj_inv_gauss(P->ctx, lp, Q->en); +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque->en); + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; } -INVERSE(e_inverse); /* ellipsoid */ - double rho, c, sinc, cosc; - - xy.x /= P->k0; - xy.y /= P->k0; - if((rho = hypot(xy.x, xy.y))) { - c = 2. * atan2(rho, P->R2); - sinc = sin(c); - cosc = cos(c); - lp.phi = asin(cosc * P->sinc0 + xy.y * sinc * P->cosc0 / rho); - lp.lam = atan2(xy.x * sinc, rho * P->cosc0 * cosc - - xy.y * P->sinc0 * sinc); - } else { - lp.phi = P->phic0; - lp.lam = 0.; - } - return(pj_inv_gauss(P->ctx, lp, P->en)); + + +PJ *PROJECTION(sterea) { + double R; + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->en = pj_gauss_ini(P->e, P->phi0, &(Q->phic0), &R); + if (0==Q->en) + return freeup_new (P); + + Q->sinc0 = sin (Q->phic0); + Q->cosc0 = cos (Q->phic0); + Q->R2 = 2. * R; + + P->inv = e_inverse; + P->fwd = e_forward; + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_sterea_selftest (void) {return 0;} +#else + +int pj_sterea_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=sterea +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5"}; + char s_args[] = {"+proj=sterea +a=6400000 +lat_1=0.5 +lat_2=2 +n=0.5"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 222644.89410919772, 110611.09187173686}, + { 222644.89410919772, -110611.09187173827}, + {-222644.89410919772, 110611.09187173686}, + {-222644.89410919772, -110611.09187173827}, + }; + + XY s_fwd_expect[] = { + { 223407.81025950745, 111737.93899644315}, + { 223407.81025950745, -111737.93899644315}, + {-223407.81025950745, 111737.93899644315}, + {-223407.81025950745, -111737.93899644315}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + { 0.0017966305682019911, 0.00090436947683099009}, + { 0.0017966305682019911, -0.00090436947684371233}, + {-0.0017966305682019911, 0.00090436947683099009}, + {-0.0017966305682019911, -0.00090436947684371233}, + }; + + LP s_inv_expect[] = { + { 0.001790493109747395, 0.00089524655465446378}, + { 0.001790493109747395, -0.00089524655465446378}, + {-0.001790493109747395, 0.00089524655465446378}, + {-0.001790493109747395, -0.00089524655465446378}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); } -FREEUP; if (P) { if (P->en) free(P->en); free(P); } } -ENTRYA(sterea) - - P->en=0; -ENTRYX - double R; - - if (!(P->en = pj_gauss_ini(P->e, P->phi0, &(P->phic0), &R))) E_ERROR_0; - P->sinc0 = sin(P->phic0); - P->cosc0 = cos(P->phic0); - P->R2 = 2. * R; - P->inv = e_inverse; - P->fwd = e_forward; -ENDENTRY(P) + + +#endif diff --git a/src/PJ_sts.c b/src/PJ_sts.c index c35d5cfc..347a7303 100644 --- a/src/PJ_sts.c +++ b/src/PJ_sts.c @@ -1,54 +1,351 @@ -#define PROJ_PARMS__ \ - double C_x, C_y, C_p; \ - int tan_mode; #define PJ_LIB__ # include <projects.h> -PROJ_HEAD(kav5, "Kavraisky V") "\n\tPCyl., Sph."; -PROJ_HEAD(qua_aut, "Quartic Authalic") "\n\tPCyl., Sph."; -PROJ_HEAD(mbt_s, "McBryde-Thomas Flat-Polar Sine (No. 1)") "\n\tPCyl., Sph."; -PROJ_HEAD(fouc, "Foucaut") "\n\tPCyl., Sph."; -FORWARD(s_forward); /* spheroid */ + +PROJ_HEAD(kav5, "Kavraisky V") "\n\tPCyl., Sph."; +PROJ_HEAD(qua_aut, "Quartic Authalic") "\n\tPCyl., Sph."; +PROJ_HEAD(fouc, "Foucaut") "\n\tPCyl., Sph."; +PROJ_HEAD(mbt_s, "McBryde-Thomas Flat-Polar Sine (No. 1)") "\n\tPCyl., Sph."; + + +struct pj_opaque { + double C_x, C_y, C_p; \ + int tan_mode; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; double c; - xy.x = P->C_x * lp.lam * cos(lp.phi); - xy.y = P->C_y; - lp.phi *= P->C_p; + xy.x = Q->C_x * lp.lam * cos(lp.phi); + xy.y = Q->C_y; + lp.phi *= Q->C_p; c = cos(lp.phi); - if (P->tan_mode) { + if (Q->tan_mode) { xy.x *= c * c; - xy.y *= tan(lp.phi); + xy.y *= tan (lp.phi); } else { xy.x /= c; - xy.y *= sin(lp.phi); + xy.y *= sin (lp.phi); } - return (xy); + return xy; } -INVERSE(s_inverse); /* spheroid */ + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; double c; - - xy.y /= P->C_y; - c = cos(lp.phi = P->tan_mode ? atan(xy.y) : aasin(P->ctx,xy.y)); - lp.phi /= P->C_p; - lp.lam = xy.x / (P->C_x * cos(lp.phi)); - if (P->tan_mode) + + xy.y /= Q->C_y; + c = cos (lp.phi = Q->tan_mode ? atan (xy.y) : aasin (P->ctx, xy.y)); + lp.phi /= Q->C_p; + lp.lam = xy.x / (Q->C_x * cos(lp.phi)); + if (Q->tan_mode) lp.lam /= c * c; else lp.lam *= c; - return (lp); + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; } -FREEUP; if (P) pj_dalloc(P); } - static PJ * -setup(PJ *P, double p, double q, int mode) { - P->es = 0.; + + +static PJ *setup(PJ *P, double p, double q, int mode) { + P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; - P->C_x = q / p; - P->C_y = p; - P->C_p = 1/ q; - P->tan_mode = mode; + P->opaque->C_x = q / p; + P->opaque->C_y = p; + P->opaque->C_p = 1/ q; + P->opaque->tan_mode = mode; return P; } -ENTRY0(kav5) ENDENTRY(setup(P, 1.50488, 1.35439, 0)) -ENTRY0(qua_aut) ENDENTRY(setup(P, 2., 2., 0)) -ENTRY0(mbt_s) ENDENTRY(setup(P, 1.48875, 1.36509, 0)) -ENTRY0(fouc) ENDENTRY(setup(P, 2., 2., 1)) + + + + + +PJ *PROJECTION(fouc) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + return setup(P, 2., 2., 1); +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_fouc_selftest (void) {return 0;} +#else +int pj_fouc_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=fouc +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5"}; + char s_args[] = {"+proj=fouc +a=6400000 +lat_1=0.5 +lat_2=2 +n=0.5"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {222588.12067589167, 111322.31670069379}, + {222588.12067589167, -111322.31670069379}, + {-222588.12067589167, 111322.31670069379}, + {-222588.12067589167, -111322.31670069379}, + }; + + XY s_fwd_expect[] = { + {223351.10900341379, 111703.9077217125}, + {223351.10900341379, -111703.9077217125}, + {-223351.10900341379, 111703.9077217125}, + {-223351.10900341379, -111703.9077217125}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {0.0017966305685702751, 0.00089831528410111959}, + {0.0017966305685702751, -0.00089831528410111959}, + {-0.0017966305685702751, 0.00089831528410111959}, + {-0.0017966305685702751, -0.00089831528410111959}, + }; + + LP s_inv_expect[] = { + {0.0017904931101116717, 0.00089524655487369749}, + {0.0017904931101116717, -0.00089524655487369749}, + {-0.0017904931101116717, 0.00089524655487369749}, + {-0.0017904931101116717, -0.00089524655487369749}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} +#endif + + + + + + +PJ *PROJECTION(kav5) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + return setup(P, 1.50488, 1.35439, 0); +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_kav5_selftest (void) {return 0;} +#else +int pj_kav5_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=kav5 +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5"}; + char s_args[] = {"+proj=kav5 +a=6400000 +lat_1=0.5 +lat_2=2 +n=0.5"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {200360.90530882866, 123685.08247699818}, + {200360.90530882866, -123685.08247699818}, + {-200360.90530882866, 123685.08247699818}, + {-200360.90530882866, -123685.08247699818}, + }; + + XY s_fwd_expect[] = { + {201047.7031108776, 124109.05062917093}, + {201047.7031108776, -124109.05062917093}, + {-201047.7031108776, 124109.05062917093}, + {-201047.7031108776, -124109.05062917093}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {0.0019962591348533314, 0.00080848256185253912}, + {0.0019962591348533314, -0.00080848256185253912}, + {-0.0019962591348533314, 0.00080848256185253912}, + {-0.0019962591348533314, -0.00080848256185253912}, + }; + + LP s_inv_expect[] = { + {0.0019894397264987643, 0.00080572070962591153}, + {0.0019894397264987643, -0.00080572070962591153}, + {-0.0019894397264987643, 0.00080572070962591153}, + {-0.0019894397264987643, -0.00080572070962591153}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} +#endif + + + + + +PJ *PROJECTION(qua_aut) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + return setup(P, 2., 2., 0); +} + +#ifdef PJ_OMIT_SELFTEST +int pj_qua_aut_selftest (void) {return 0;} +#else +int pj_qua_aut_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=qua_aut +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5"}; + char s_args[] = {"+proj=qua_aut +a=6400000 +lat_1=0.5 +lat_2=2 +n=0.5"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {222613.54903309655, 111318.07788798446}, + {222613.54903309655, -111318.07788798446}, + {-222613.54903309655, 111318.07788798446}, + {-222613.54903309655, -111318.07788798446}, + }; + + XY s_fwd_expect[] = { + {223376.62452402918, 111699.65437918637}, + {223376.62452402918, -111699.65437918637}, + {-223376.62452402918, 111699.65437918637}, + {-223376.62452402918, -111699.65437918637}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {0.0017966305684046586, 0.00089831528412872229}, + {0.0017966305684046586, -0.00089831528412872229}, + {-0.0017966305684046586, 0.00089831528412872229}, + {-0.0017966305684046586, -0.00089831528412872229}, + }; + + LP s_inv_expect[] = { + {0.0017904931099477471, 0.00089524655490101819}, + {0.0017904931099477471, -0.00089524655490101819}, + {-0.0017904931099477471, 0.00089524655490101819}, + {-0.0017904931099477471, -0.00089524655490101819}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} +#endif + + + + + +PJ *PROJECTION(mbt_s) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + return setup(P, 1.48875, 1.36509, 0); +} + +#ifdef PJ_OMIT_SELFTEST +int pj_mbt_s_selftest (void) {return 0;} +#else +int pj_mbt_s_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=mbt_s +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5"}; + char s_args[] = {"+proj=mbt_s +a=6400000 +lat_1=0.5 +lat_2=2 +n=0.5"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {204131.51785027285, 121400.33022550763}, + {204131.51785027285, -121400.33022550763}, + {-204131.51785027285, 121400.33022550763}, + {-204131.51785027285, -121400.33022550763}, + }; + + XY s_fwd_expect[] = { + {204831.24057099217, 121816.46669603503}, + {204831.24057099217, -121816.46669603503}, + {-204831.24057099217, 121816.46669603503}, + {-204831.24057099217, -121816.46669603503}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {0.0019593827209883237, 0.00082369854658027549}, + {0.0019593827209883237, -0.00082369854658027549}, + {-0.0019593827209883237, 0.00082369854658027549}, + {-0.0019593827209883237, -0.00082369854658027549}, + }; + + LP s_inv_expect[] = { + {0.0019526892859206603, 0.00082088471512331508}, + {0.0019526892859206603, -0.00082088471512331508}, + {-0.0019526892859206603, 0.00082088471512331508}, + {-0.0019526892859206603, -0.00082088471512331508}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} +#endif diff --git a/src/PJ_tcc.c b/src/PJ_tcc.c index 00cdb556..457924c0 100644 --- a/src/PJ_tcc.c +++ b/src/PJ_tcc.c @@ -1,17 +1,65 @@ -#define PROJ_PARMS__ \ - double ap; -#define EPS10 1.e-10 #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(tcc, "Transverse Central Cylindrical") "\n\tCyl, Sph, no inv."; -FORWARD(s_forward); /* spheroid */ - double b, bt; - - b = cos(lp.phi) * sin(lp.lam); - if ((bt = 1. - b * b) < EPS10) F_ERROR; - xy.x = b / sqrt(bt); - xy.y = atan2(tan(lp.phi) , cos(lp.lam)); - return (xy); + +#define EPS10 1.e-10 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0, 0.0}; + double b, bt; + + b = cos (lp.phi) * sin (lp.lam); + if ((bt = 1. - b * b) < EPS10) F_ERROR; + xy.x = b / sqrt(bt); + xy.y = atan2 (tan (lp.phi) , cos (lp.lam)); + return xy; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(tcc) { + P->es = 0.; + P->fwd = s_forward; + P->inv = 0; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_tcc_selftest (void) {return 0;} +#else +int pj_tcc_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=tcc +a=6400000 +lat_1=0.5 +lat_2=2 +n=0.5"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + {223458.84419245756, 111769.14504058579}, + {223458.84419245756, -111769.14504058579}, + {-223458.84419245756, 111769.14504058579}, + {-223458.84419245756, -111769.14504058579}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, 0, 0, 0); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(tcc) P->es = 0.; P->fwd = s_forward; ENDENTRY(P) +#endif diff --git a/src/PJ_tcea.c b/src/PJ_tcea.c index 3626fa17..7b469d0e 100644 --- a/src/PJ_tcea.c +++ b/src/PJ_tcea.c @@ -1,27 +1,85 @@ -#define PROJ_PARMS__ \ - double rk0; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(tcea, "Transverse Cylindrical Equal Area") "\n\tCyl, Sph"; -FORWARD(s_forward); /* spheroid */ - xy.x = P->rk0 * cos(lp.phi) * sin(lp.lam); - xy.y = P->k0 * (atan2(tan(lp.phi), cos(lp.lam)) - P->phi0); - return (xy); + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + xy.x = cos (lp.phi) * sin (lp.lam) / P->k0; + xy.y = P->k0 * (atan2 (tan (lp.phi), cos (lp.lam)) - P->phi0); + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0, 0.0}; + double t; + + xy.y = xy.y / P->k0 + P->phi0; + xy.x *= P->k0; + t = sqrt (1. - xy.x * xy.x); + lp.phi = asin (t * sin (xy.y)); + lp.lam = atan2 (xy.x, t * cos (xy.y)); + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; } -INVERSE(s_inverse); /* spheroid */ - double t; - - xy.y = xy.y * P->rk0 + P->phi0; - xy.x *= P->k0; - t = sqrt(1. - xy.x * xy.x); - lp.phi = asin(t * sin(xy.y)); - lp.lam = atan2(xy.x, t * cos(xy.y)); - return (lp); + + +PJ *PROJECTION(tcea) { + P->inv = s_inverse; + P->fwd = s_forward; + P->es = 0.; + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_tcea_selftest (void) {return 0;} +#else +int pj_tcea_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=tcea +a=6400000 +lat_1=0.5 +lat_2=2 +n=0.5"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223322.76057672748, 111769.14504058579}, + { 223322.76057672748, -111769.14504058579}, + {-223322.76057672748, 111769.14504058579}, + {-223322.76057672748, -111769.14504058579}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.0017904931102938101, 0.00089524655445477922}, + { 0.0017904931102938101, -0.00089524655445477922}, + {-0.0017904931102938101, 0.00089524655445477922}, + {-0.0017904931102938101, -0.00089524655445477922}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(tcea) - P->rk0 = 1 / P->k0; - P->inv = s_inverse; - P->fwd = s_forward; - P->es = 0.; -ENDENTRY(P) +#endif diff --git a/src/PJ_tmerc.c b/src/PJ_tmerc.c index 70e8e72f..35203261 100644 --- a/src/PJ_tmerc.c +++ b/src/PJ_tmerc.c @@ -1,13 +1,16 @@ -#define PROJ_PARMS__ \ - double esp; \ - double ml0; \ - double *en; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(tmerc, "Transverse Mercator") "\n\tCyl, Sph&Ell"; -#define EPS10 1.e-10 -#define aks0 P->esp -#define aks5 P->ml0 + + +struct pj_opaque { + double esp; \ + double ml0; \ + double *en; +}; + +#define EPS10 1.e-10 #define FC1 1. #define FC2 .5 #define FC3 .16666666666666666666 @@ -16,138 +19,234 @@ PROJ_HEAD(tmerc, "Transverse Mercator") "\n\tCyl, Sph&Ell"; #define FC6 .03333333333333333333 #define FC7 .02380952380952380952 #define FC8 .01785714285714285714 -FORWARD(e_forward); /* ellipse */ - double al, als, n, cosphi, sinphi, t; - - /* - * Fail if our longitude is more than 90 degrees from the - * central meridian since the results are essentially garbage. - * Is error -20 really an appropriate return value? - * - * http://trac.osgeo.org/proj/ticket/5 - */ - if( lp.lam < -HALFPI || lp.lam > HALFPI ) - { - xy.x = HUGE_VAL; - xy.y = HUGE_VAL; - pj_ctx_set_errno( P->ctx, -14 ); - return xy; - } - - sinphi = sin(lp.phi); cosphi = cos(lp.phi); - t = fabs(cosphi) > 1e-10 ? sinphi/cosphi : 0.; - t *= t; - al = cosphi * lp.lam; - als = al * al; - al /= sqrt(1. - P->es * sinphi * sinphi); - n = P->esp * cosphi * cosphi; - xy.x = P->k0 * al * (FC1 + - FC3 * als * (1. - t + n + - FC5 * als * (5. + t * (t - 18.) + n * (14. - 58. * t) - + FC7 * als * (61. + t * ( t * (179. - t) - 479. ) ) - ))); - xy.y = P->k0 * (pj_mlfn(lp.phi, sinphi, cosphi, P->en) - P->ml0 + - sinphi * al * lp.lam * FC2 * ( 1. + - FC4 * als * (5. - t + n * (9. + 4. * n) + - FC6 * als * (61. + t * (t - 58.) + n * (270. - 330 * t) - + FC8 * als * (1385. + t * ( t * (543. - t) - 3111.) ) - )))); - return (xy); + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0, 0.0}; + struct pj_opaque *Q = P->opaque; + double al, als, n, cosphi, sinphi, t; + + /* + * Fail if our longitude is more than 90 degrees from the + * central meridian since the results are essentially garbage. + * Is error -20 really an appropriate return value? + * + * http://trac.osgeo.org/proj/ticket/5 + */ + if( lp.lam < -HALFPI || lp.lam > HALFPI ) { + xy.x = HUGE_VAL; + xy.y = HUGE_VAL; + pj_ctx_set_errno( P->ctx, -14 ); + return xy; + } + + sinphi = sin (lp.phi); + cosphi = cos (lp.phi); + t = fabs (cosphi) > 1e-10 ? sinphi/cosphi : 0.; + t *= t; + al = cosphi * lp.lam; + als = al * al; + al /= sqrt (1. - P->es * sinphi * sinphi); + n = Q->esp * cosphi * cosphi; + xy.x = P->k0 * al * (FC1 + + FC3 * als * (1. - t + n + + FC5 * als * (5. + t * (t - 18.) + n * (14. - 58. * t) + + FC7 * als * (61. + t * ( t * (179. - t) - 479. ) ) + ))); + xy.y = P->k0 * (pj_mlfn(lp.phi, sinphi, cosphi, Q->en) - Q->ml0 + + sinphi * al * lp.lam * FC2 * ( 1. + + FC4 * als * (5. - t + n * (9. + 4. * n) + + FC6 * als * (61. + t * (t - 58.) + n * (270. - 330 * t) + + FC8 * als * (1385. + t * ( t * (543. - t) - 3111.) ) + )))); + return (xy); +} + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double b, cosphi; + + /* + * Fail if our longitude is more than 90 degrees from the + * central meridian since the results are essentially garbage. + * Is error -20 really an appropriate return value? + * + * http://trac.osgeo.org/proj/ticket/5 + */ + if( lp.lam < -HALFPI || lp.lam > HALFPI ) { + xy.x = HUGE_VAL; + xy.y = HUGE_VAL; + pj_ctx_set_errno( P->ctx, -14 ); + return xy; + } + + cosphi = cos(lp.phi); + b = cosphi * sin (lp.lam); + if (fabs (fabs (b) - 1.) <= EPS10) + F_ERROR; + + xy.x = P->opaque->ml0 * log ((1. + b) / (1. - b)); + xy.y = cosphi * cos (lp.lam) / sqrt (1. - b * b); + + b = fabs ( xy.y ); + if (b >= 1.) { + if ((b - 1.) > EPS10) + F_ERROR + else xy.y = 0.; + } else + xy.y = acos (xy.y); + + if (lp.phi < 0.) + xy.y = -xy.y; + xy.y = P->opaque->esp * (xy.y - P->phi0); + return xy; +} + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double n, con, cosphi, d, ds, sinphi, t; + + lp.phi = pj_inv_mlfn(P->ctx, Q->ml0 + xy.y / P->k0, P->es, Q->en); + if (fabs(lp.phi) >= HALFPI) { + lp.phi = xy.y < 0. ? -HALFPI : HALFPI; + lp.lam = 0.; + } else { + sinphi = sin(lp.phi); + cosphi = cos(lp.phi); + t = fabs (cosphi) > 1e-10 ? sinphi/cosphi : 0.; + n = Q->esp * cosphi * cosphi; + d = xy.x * sqrt (con = 1. - P->es * sinphi * sinphi) / P->k0; + con *= t; + t *= t; + ds = d * d; + lp.phi -= (con * ds / (1.-P->es)) * FC2 * (1. - + ds * FC4 * (5. + t * (3. - 9. * n) + n * (1. - 4 * n) - + ds * FC6 * (61. + t * (90. - 252. * n + + 45. * t) + 46. * n + - ds * FC8 * (1385. + t * (3633. + t * (4095. + 1574. * t)) ) + ))); + lp.lam = d*(FC1 - + ds*FC3*( 1. + 2.*t + n - + ds*FC5*(5. + t*(28. + 24.*t + 8.*n) + 6.*n + - ds * FC7 * (61. + t * (662. + t * (1320. + 720. * t)) ) + ))) / cosphi; + } + return lp; } -FORWARD(s_forward); /* sphere */ - double b, cosphi; - - /* - * Fail if our longitude is more than 90 degrees from the - * central meridian since the results are essentially garbage. - * Is error -20 really an appropriate return value? - * - * http://trac.osgeo.org/proj/ticket/5 - */ - if( lp.lam < -HALFPI || lp.lam > HALFPI ) - { - xy.x = HUGE_VAL; - xy.y = HUGE_VAL; - pj_ctx_set_errno( P->ctx, -14 ); - return xy; - } - - b = (cosphi = cos(lp.phi)) * sin(lp.lam); - if (fabs(fabs(b) - 1.) <= EPS10) F_ERROR; - xy.x = aks5 * log((1. + b) / (1. - b)); - if ((b = fabs( xy.y = cosphi * cos(lp.lam) / sqrt(1. - b * b) )) >= 1.) { - if ((b - 1.) > EPS10) F_ERROR - else xy.y = 0.; - } else - xy.y = acos(xy.y); - if (lp.phi < 0.) xy.y = -xy.y; - xy.y = aks0 * (xy.y - P->phi0); - return (xy); + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0, 0.0}; + double h, g; + + h = exp(xy.x / P->opaque->esp); + g = .5 * (h - 1. / h); + h = cos (P->phi0 + xy.y / P->opaque->esp); + lp.phi = asin(sqrt((1. - h * h) / (1. + g * g))); + if (xy.y < 0.) lp.phi = -lp.phi; + lp.lam = (g || h) ? atan2 (g, h) : 0.; + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + pj_dealloc (P->opaque->en); + pj_dealloc (P->opaque); + return pj_dealloc(P); } -INVERSE(e_inverse); /* ellipsoid */ - double n, con, cosphi, d, ds, sinphi, t; - - lp.phi = pj_inv_mlfn(P->ctx, P->ml0 + xy.y / P->k0, P->es, P->en); - if (fabs(lp.phi) >= HALFPI) { - lp.phi = xy.y < 0. ? -HALFPI : HALFPI; - lp.lam = 0.; - } else { - sinphi = sin(lp.phi); - cosphi = cos(lp.phi); - t = fabs(cosphi) > 1e-10 ? sinphi/cosphi : 0.; - n = P->esp * cosphi * cosphi; - d = xy.x * sqrt(con = 1. - P->es * sinphi * sinphi) / P->k0; - con *= t; - t *= t; - ds = d * d; - lp.phi -= (con * ds / (1.-P->es)) * FC2 * (1. - - ds * FC4 * (5. + t * (3. - 9. * n) + n * (1. - 4 * n) - - ds * FC6 * (61. + t * (90. - 252. * n + - 45. * t) + 46. * n - - ds * FC8 * (1385. + t * (3633. + t * (4095. + 1574. * t)) ) - ))); - lp.lam = d*(FC1 - - ds*FC3*( 1. + 2.*t + n - - ds*FC5*(5. + t*(28. + 24.*t + 8.*n) + 6.*n - - ds * FC7 * (61. + t * (662. + t * (1320. + 720. * t)) ) - ))) / cosphi; - } - return (lp); + +static void freeup (PJ *P) { + freeup_new (P); + return; } -INVERSE(s_inverse); /* sphere */ - double h, g; - - h = exp(xy.x / aks0); - g = .5 * (h - 1. / h); - h = cos(P->phi0 + xy.y / aks0); - lp.phi = asin(sqrt((1. - h * h) / (1. + g * g))); - if (xy.y < 0.) lp.phi = -lp.phi; - lp.lam = (g || h) ? atan2(g, h) : 0.; - return (lp); + +static PJ *setup(PJ *P) { /* general initialization */ + struct pj_opaque *Q = P->opaque; + if (P->es) { + if (!(Q->en = pj_enfn(P->es))) + E_ERROR_0; + Q->ml0 = pj_mlfn(P->phi0, sin(P->phi0), cos(P->phi0), Q->en); + Q->esp = P->es / (1. - P->es); + P->inv = e_inverse; + P->fwd = e_forward; + } else { + Q->esp = P->k0; + Q->ml0 = .5 * Q->esp; + P->inv = s_inverse; + P->fwd = s_forward; + } + return P; } -FREEUP; - if (P) { - if (P->en) - pj_dalloc(P->en); - pj_dalloc(P); - } + + +PJ *PROJECTION(tmerc) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + return setup(P); } - static PJ * -setup(PJ *P) { /* general initialization */ - if (P->es) { - if (!(P->en = pj_enfn(P->es))) - E_ERROR_0; - P->ml0 = pj_mlfn(P->phi0, sin(P->phi0), cos(P->phi0), P->en); - P->esp = P->es / (1. - P->es); - P->inv = e_inverse; - P->fwd = e_forward; - } else { - aks0 = P->k0; - aks5 = .5 * aks0; - P->inv = s_inverse; - P->fwd = s_forward; - } - return P; + + +#ifdef PJ_OMIT_SELFTEST +int pj_tmerc_selftest (void) {return 0;} +#else +int pj_tmerc_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=tmerc +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5"}; + char s_args[] = {"+proj=tmerc +a=6400000 +lat_1=0.5 +lat_2=2 +n=0.5"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 222650.79679577847, 110642.22941192707}, + { 222650.79679577847, -110642.22941192707}, + {-222650.79679577847, 110642.22941192707}, + {-222650.79679577847, -110642.22941192707}, + }; + + XY s_fwd_expect[] = { + { 223413.46640632232, 111769.14504059685}, + { 223413.46640632232, -111769.14504059685}, + {-223413.46640632208, 111769.14504059685}, + {-223413.46640632208, -111769.14504059685}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + { 0.0017966305681649396, 0.00090436947663183841}, + { 0.0017966305681649396, -0.00090436947663183841}, + {-0.0017966305681649396, 0.00090436947663183841}, + {-0.0017966305681649396, -0.00090436947663183841}, + }; + + LP s_inv_expect[] = { + { 0.0017904931097048034, 0.00089524670602767842}, + { 0.0017904931097048034, -0.00089524670602767842}, + {-0.001790493109714345, 0.00089524670602767842}, + {-0.001790493109714345, -0.00089524670602767842}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); } -ENTRY1(tmerc, en) -ENDENTRY(setup(P)) +#endif diff --git a/src/PJ_tpeqd.c b/src/PJ_tpeqd.c index 4ab5cf4e..182f23a5 100644 --- a/src/PJ_tpeqd.c +++ b/src/PJ_tpeqd.c @@ -1,76 +1,177 @@ -#define PROJ_PARMS__ \ - double cp1, sp1, cp2, sp2, ccs, cs, sc, r2z0, z02, dlam2; \ - double hz0, thz0, rhshz0, ca, sa, lp, lamc; #define PJ_LIB__ #include <projects.h> + + PROJ_HEAD(tpeqd, "Two Point Equidistant") "\n\tMisc Sph\n\tlat_1= lon_1= lat_2= lon_2="; -FORWARD(s_forward); /* sphere */ + +struct pj_opaque { + double cp1, sp1, cp2, sp2, ccs, cs, sc, r2z0, z02, dlam2; \ + double hz0, thz0, rhshz0, ca, sa, lp, lamc; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0, 0.0}; + struct pj_opaque *Q = P->opaque; double t, z1, z2, dl1, dl2, sp, cp; sp = sin(lp.phi); cp = cos(lp.phi); - z1 = aacos(P->ctx,P->sp1 * sp + P->cp1 * cp * cos(dl1 = lp.lam + P->dlam2)); - z2 = aacos(P->ctx,P->sp2 * sp + P->cp2 * cp * cos(dl2 = lp.lam - P->dlam2)); + z1 = aacos(P->ctx, Q->sp1 * sp + Q->cp1 * cp * cos (dl1 = lp.lam + Q->dlam2)); + z2 = aacos(P->ctx, Q->sp2 * sp + Q->cp2 * cp * cos (dl2 = lp.lam - Q->dlam2)); z1 *= z1; z2 *= z2; - xy.x = P->r2z0 * (t = z1 - z2); - t = P->z02 - t; - xy.y = P->r2z0 * asqrt(4. * P->z02 * z2 - t * t); - if ((P->ccs * sp - cp * (P->cs * sin(dl1) - P->sc * sin(dl2))) < 0.) + + xy.x = Q->r2z0 * (t = z1 - z2); + t = Q->z02 - t; + xy.y = Q->r2z0 * asqrt (4. * Q->z02 * z2 - t * t); + if ((Q->ccs * sp - cp * (Q->cs * sin(dl1) - Q->sc * sin(dl2))) < 0.) xy.y = -xy.y; return xy; } -INVERSE(s_inverse); /* sphere */ + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; double cz1, cz2, s, d, cp, sp; - cz1 = cos(hypot(xy.y, xy.x + P->hz0)); - cz2 = cos(hypot(xy.y, xy.x - P->hz0)); + cz1 = cos (hypot(xy.y, xy.x + Q->hz0)); + cz2 = cos (hypot(xy.y, xy.x - Q->hz0)); s = cz1 + cz2; d = cz1 - cz2; - lp.lam = - atan2(d, (s * P->thz0)); - lp.phi = aacos(P->ctx,hypot(P->thz0 * s, d) * P->rhshz0); + lp.lam = - atan2(d, (s * Q->thz0)); + lp.phi = aacos(P->ctx, hypot (Q->thz0 * s, d) * Q->rhshz0); if ( xy.y < 0. ) lp.phi = - lp.phi; /* lam--phi now in system relative to P1--P2 base equator */ - sp = sin(lp.phi); - cp = cos(lp.phi); - lp.phi = aasin(P->ctx,P->sa * sp + P->ca * cp * (s = cos(lp.lam -= P->lp))); - lp.lam = atan2(cp * sin(lp.lam), P->sa * cp * s - P->ca * sp) + P->lamc; + sp = sin (lp.phi); + cp = cos (lp.phi); + lp.phi = aasin (P->ctx, Q->sa * sp + Q->ca * cp * (s = cos(lp.lam -= Q->lp))); + lp.lam = atan2 (cp * sin(lp.lam), Q->sa * cp * s - Q->ca * sp) + Q->lamc; return lp; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(tpeqd) + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(tpeqd) { double lam_1, lam_2, phi_1, phi_2, A12, pp; + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + /* get control point locations */ phi_1 = pj_param(P->ctx, P->params, "rlat_1").f; lam_1 = pj_param(P->ctx, P->params, "rlon_1").f; phi_2 = pj_param(P->ctx, P->params, "rlat_2").f; lam_2 = pj_param(P->ctx, P->params, "rlon_2").f; - if (phi_1 == phi_2 && lam_1 == lam_2) E_ERROR(-25); - P->lam0 = adjlon(0.5 * (lam_1 + lam_2)); - P->dlam2 = adjlon(lam_2 - lam_1); - P->cp1 = cos(phi_1); - P->cp2 = cos(phi_2); - P->sp1 = sin(phi_1); - P->sp2 = sin(phi_2); - P->cs = P->cp1 * P->sp2; - P->sc = P->sp1 * P->cp2; - P->ccs = P->cp1 * P->cp2 * sin(P->dlam2); - P->z02 = aacos(P->ctx,P->sp1 * P->sp2 + P->cp1 * P->cp2 * cos(P->dlam2)); - P->hz0 = .5 * P->z02; - A12 = atan2(P->cp2 * sin(P->dlam2), - P->cp1 * P->sp2 - P->sp1 * P->cp2 * cos(P->dlam2)); - P->ca = cos(pp = aasin(P->ctx,P->cp1 * sin(A12))); - P->sa = sin(pp); - P->lp = adjlon(atan2(P->cp1 * cos(A12), P->sp1) - P->hz0); - P->dlam2 *= .5; - P->lamc = HALFPI - atan2(sin(A12) * P->sp1, cos(A12)) - P->dlam2; - P->thz0 = tan(P->hz0); - P->rhshz0 = .5 / sin(P->hz0); - P->r2z0 = 0.5 / P->z02; - P->z02 *= P->z02; - P->inv = s_inverse; P->fwd = s_forward; + + if (phi_1 == phi_2 && lam_1 == lam_2) + E_ERROR(-25); + P->lam0 = adjlon (0.5 * (lam_1 + lam_2)); + Q->dlam2 = adjlon (lam_2 - lam_1); + + Q->cp1 = cos (phi_1); + Q->cp2 = cos (phi_2); + Q->sp1 = sin (phi_1); + Q->sp2 = sin (phi_2); + Q->cs = Q->cp1 * Q->sp2; + Q->sc = Q->sp1 * Q->cp2; + Q->ccs = Q->cp1 * Q->cp2 * sin(Q->dlam2); + Q->z02 = aacos(P->ctx, Q->sp1 * Q->sp2 + Q->cp1 * Q->cp2 * cos (Q->dlam2)); + Q->hz0 = .5 * Q->z02; + A12 = atan2(Q->cp2 * sin (Q->dlam2), + Q->cp1 * Q->sp2 - Q->sp1 * Q->cp2 * cos (Q->dlam2)); + Q->ca = cos(pp = aasin(P->ctx, Q->cp1 * sin(A12))); + Q->sa = sin(pp); + Q->lp = adjlon ( atan2 (Q->cp1 * cos(A12), Q->sp1) - Q->hz0); + Q->dlam2 *= .5; + Q->lamc = HALFPI - atan2(sin(A12) * Q->sp1, cos(A12)) - Q->dlam2; + Q->thz0 = tan (Q->hz0); + Q->rhshz0 = .5 / sin (Q->hz0); + Q->r2z0 = 0.5 / Q->z02; + Q->z02 *= Q->z02; + + P->inv = s_inverse; + P->fwd = s_forward; P->es = 0.; -ENDENTRY(P) + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_tpeqd_selftest (void) {return 0;} +#else + +int pj_tpeqd_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=tpeqd +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5"}; + char s_args[] = {"+proj=tpeqd +a=6400000 +lat_1=0.5 +lat_2=2 +n=0.5"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {-27750.758831679042, -222599.40369177726}, + {-250434.93702403645, -222655.93819326628}, + {-27750.758831679042, 222599.40369177726}, + {-250434.93702403645, 222655.93819326628}, + }; + + XY s_fwd_expect[] = { + {-27845.882978485075, -223362.43069526015}, + {-251293.37876465076, -223419.15898590829}, + {-27845.882978485075, 223362.43069526015}, + {-251293.37876465076, 223419.15898590829}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {-0.00089855554821257374, 1.2517966304145272}, + {0.0008985555481998515, 1.2517966304145272}, + {-0.00089855431859741167, 1.2482033692781642}, + {0.00089855431859741167, 1.2482033692781642}, + }; + + LP s_inv_expect[] = { + {-0.00089548606640108474, 1.2517904929571837}, + {0.0008954860663883625, 1.2517904929571837}, + {-0.000895484845182587, 1.248209506737604}, + {0.00089548484516986475, 1.248209506737604}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, s_fwd_expect, inv_in, e_inv_expect, s_inv_expect); +} + + +#endif diff --git a/src/PJ_urm5.c b/src/PJ_urm5.c index 9159df65..7a759b07 100644 --- a/src/PJ_urm5.c +++ b/src/PJ_urm5.c @@ -1,28 +1,81 @@ -#define PROJ_PARMS__ \ - double m, rmn, q3, n; #define PJ_LIB__ -# include <projects.h> +#include <projects.h> + PROJ_HEAD(urm5, "Urmaev V") "\n\tPCyl., Sph., no inv.\n\tn= q= alpha="; -FORWARD(s_forward); /* spheroid */ - double t; - - t = lp.phi = aasin(P->ctx,P->n * sin(lp.phi)); - xy.x = P->m * lp.lam * cos(lp.phi); - t *= t; - xy.y = lp.phi * (1. + t * P->q3) * P->rmn; - return xy; + +struct pj_opaque { + double m, rmn, q3, n; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0, 0.0}; + struct pj_opaque *Q = P->opaque; + double t; + + t = lp.phi = aasin (P->ctx, Q->n * sin (lp.phi)); + xy.x = Q->m * lp.lam * cos (lp.phi); + t *= t; + xy.y = lp.phi * (1. + t * Q->q3) * Q->rmn; + return xy; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(urm5) { + double alpha, t; + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->n = pj_param(P->ctx, P->params, "dn").f; + Q->q3 = pj_param(P->ctx, P->params, "dq").f / 3.; + alpha = pj_param(P->ctx, P->params, "ralpha").f; + t = Q->n * sin (alpha); + Q->m = cos (alpha) / sqrt (1. - t * t); + Q->rmn = 1. / (Q->m * Q->n); + + P->es = 0.; + P->inv = 0; + P->fwd = s_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_urm5_selftest (void) {return 0;} +#else +int pj_urm5_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=urm5 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223393.6384339639, 111696.81878511712}, + { 223393.6384339639, -111696.81878511712}, + {-223393.6384339639, 111696.81878511712}, + {-223393.6384339639, -111696.81878511712}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, 0, 0, 0); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(urm5) - double alpha, t; - - P->n = pj_param(P->ctx, P->params, "dn").f; - P->q3 = pj_param(P->ctx, P->params, "dq").f / 3.; - alpha = pj_param(P->ctx, P->params, "ralpha").f; - t = P->n * sin(alpha); - P->m = cos(alpha) / sqrt(1. - t * t); - P->rmn = 1. / (P->m * P->n); - P->es = 0.; - P->inv = 0; - P->fwd = s_forward; -ENDENTRY(P) +#endif diff --git a/src/PJ_urmfps.c b/src/PJ_urmfps.c index 5c5918ae..2322aa04 100644 --- a/src/PJ_urmfps.c +++ b/src/PJ_urmfps.c @@ -1,40 +1,166 @@ -#define PROJ_PARMS__ \ - double n, C_y; #define PJ_LIB__ #include <projects.h> + PROJ_HEAD(urmfps, "Urmaev Flat-Polar Sinusoidal") "\n\tPCyl, Sph.\n\tn="; PROJ_HEAD(wag1, "Wagner I (Kavraisky VI)") "\n\tPCyl, Sph."; + +struct pj_opaque { + double n, C_y; +}; + #define C_x 0.8773826753 #define Cy 1.139753528477 -FORWARD(s_forward); /* sphere */ - lp.phi = aasin(P->ctx,P->n * sin(lp.phi)); - xy.x = C_x * lp.lam * cos(lp.phi); - xy.y = P->C_y * lp.phi; - return (xy); -} -INVERSE(s_inverse); /* sphere */ - xy.y /= P->C_y; - lp.phi = aasin(P->ctx,sin(xy.y) / P->n); - lp.lam = xy.x / (C_x * cos(xy.y)); - return (lp); -} -FREEUP; if (P) pj_dalloc(P); } - static PJ * -setup(PJ *P) { - P->C_y = Cy / P->n; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0, 0.0}; + lp.phi = aasin (P->ctx,P->opaque->n * sin (lp.phi)); + xy.x = C_x * lp.lam * cos (lp.phi); + xy.y = P->opaque->C_y * lp.phi; + return xy; +} + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0, 0.0}; + xy.y /= P->opaque->C_y; + lp.phi = aasin(P->ctx, sin (xy.y) / P->opaque->n); + lp.lam = xy.x / (C_x * cos (xy.y)); + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + +static PJ *setup(PJ *P) { + P->opaque->C_y = Cy / P->opaque->n; P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; return P; } -ENTRY0(urmfps) + + +PJ *PROJECTION(urmfps) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + if (pj_param(P->ctx, P->params, "tn").i) { - P->n = pj_param(P->ctx, P->params, "dn").f; - if (P->n <= 0. || P->n > 1.) + P->opaque->n = pj_param(P->ctx, P->params, "dn").f; + if (P->opaque->n <= 0. || P->opaque->n > 1.) E_ERROR(-40) } else E_ERROR(-40) -ENDENTRY(setup(P)) -ENTRY0(wag1) - P->n = 0.8660254037844386467637231707; -ENDENTRY(setup(P)) + + return setup(P); +} + + +PJ *PROJECTION(wag1) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + P->opaque->n = 0.8660254037844386467637231707; + return setup(P); +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_urmfps_selftest (void) {return 0;} +#else +int pj_urmfps_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=urmfps +a=6400000 +lat_1=0.5 +lat_2=2 +n=0.5"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 196001.70813419219, 127306.84332999329}, + { 196001.70813419219, -127306.84332999329}, + {-196001.70813419219, 127306.84332999329}, + {-196001.70813419219, -127306.84332999329}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.002040720839642371, 0.00078547381740438178}, + { 0.002040720839642371, -0.00078547381740438178}, + {-0.002040720839642371, 0.00078547381740438178}, + {-0.002040720839642371, -0.00078547381740438178}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} +#endif + + +#ifdef PJ_OMIT_SELFTEST +int pj_wag1_selftest (void) {return 0;} +#else +int pj_wag1_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=wag1 +a=6400000 +lat_1=0.5 +lat_2=2 +n=0.5"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 195986.78156115755, 127310.07506065986}, + { 195986.78156115755, -127310.07506065986}, + {-195986.78156115755, 127310.07506065986}, + {-195986.78156115755, -127310.07506065986}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.002040720839738254, 0.00078547381739207999}, + { 0.002040720839738254, -0.00078547381739207999}, + {-0.002040720839738254, 0.00078547381739207999}, + {-0.002040720839738254, -0.00078547381739207999}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} +#endif + diff --git a/src/PJ_vandg.c b/src/PJ_vandg.c index 784674b7..5ce058fd 100644 --- a/src/PJ_vandg.c +++ b/src/PJ_vandg.c @@ -1,78 +1,156 @@ #define PJ_LIB__ -# include <projects.h> +#include <projects.h> + PROJ_HEAD(vandg, "van der Grinten (I)") "\n\tMisc Sph"; -# define TOL 1.e-10 -# define THIRD .33333333333333333333 -# define TWO_THRD .66666666666666666666 -# define C2_27 .07407407407407407407 -# define PI4_3 4.18879020478639098458 -# define PISQ 9.86960440108935861869 -# define TPISQ 19.73920880217871723738 -# define HPISQ 4.93480220054467930934 -FORWARD(s_forward); /* spheroid */ - double al, al2, g, g2, p2; - - p2 = fabs(lp.phi / HALFPI); - if ((p2 - TOL) > 1.) F_ERROR; - if (p2 > 1.) - p2 = 1.; - if (fabs(lp.phi) <= TOL) { - xy.x = lp.lam; - xy.y = 0.; - } else if (fabs(lp.lam) <= TOL || fabs(p2 - 1.) < TOL) { - xy.x = 0.; - xy.y = PI * tan(.5 * asin(p2)); - if (lp.phi < 0.) xy.y = -xy.y; - } else { - al = .5 * fabs(PI / lp.lam - lp.lam / PI); - al2 = al * al; - g = sqrt(1. - p2 * p2); - g = g / (p2 + g - 1.); - g2 = g * g; - p2 = g * (2. / p2 - 1.); - p2 = p2 * p2; - xy.x = g - p2; g = p2 + al2; - xy.x = PI * (al * xy.x + sqrt(al2 * xy.x * xy.x - g * (g2 - p2))) / g; - if (lp.lam < 0.) xy.x = -xy.x; - xy.y = fabs(xy.x / PI); - xy.y = 1. - xy.y * (xy.y + 2. * al); - if (xy.y < -TOL) F_ERROR; - if (xy.y < 0.) xy.y = 0.; - else xy.y = sqrt(xy.y) * (lp.phi < 0. ? -PI : PI); - } - return (xy); + +# define TOL 1.e-10 +# define THIRD .33333333333333333333 +# define TWO_THRD .66666666666666666666 +# define C2_27 .07407407407407407407 +# define PI4_3 4.18879020478639098458 +# define PISQ 9.86960440108935861869 +# define TPISQ 19.73920880217871723738 +# define HPISQ 4.93480220054467930934 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + double al, al2, g, g2, p2; + + p2 = fabs(lp.phi / HALFPI); + if ((p2 - TOL) > 1.) F_ERROR; + if (p2 > 1.) + p2 = 1.; + if (fabs(lp.phi) <= TOL) { + xy.x = lp.lam; + xy.y = 0.; + } else if (fabs(lp.lam) <= TOL || fabs(p2 - 1.) < TOL) { + xy.x = 0.; + xy.y = PI * tan(.5 * asin(p2)); + if (lp.phi < 0.) xy.y = -xy.y; + } else { + al = .5 * fabs(PI / lp.lam - lp.lam / PI); + al2 = al * al; + g = sqrt(1. - p2 * p2); + g = g / (p2 + g - 1.); + g2 = g * g; + p2 = g * (2. / p2 - 1.); + p2 = p2 * p2; + xy.x = g - p2; g = p2 + al2; + xy.x = PI * (al * xy.x + sqrt(al2 * xy.x * xy.x - g * (g2 - p2))) / g; + if (lp.lam < 0.) xy.x = -xy.x; + xy.y = fabs(xy.x / PI); + xy.y = 1. - xy.y * (xy.y + 2. * al); + if (xy.y < -TOL) F_ERROR; + if (xy.y < 0.) + xy.y = 0.; + else + xy.y = sqrt(xy.y) * (lp.phi < 0. ? -PI : PI); + } + + return xy; } -INVERSE(s_inverse); /* spheroid */ - double t, c0, c1, c2, c3, al, r2, r, m, d, ay, x2, y2; - - x2 = xy.x * xy.x; - if ((ay = fabs(xy.y)) < TOL) { - lp.phi = 0.; - t = x2 * x2 + TPISQ * (x2 + HPISQ); - lp.lam = fabs(xy.x) <= TOL ? 0. : - .5 * (x2 - PISQ + sqrt(t)) / xy.x; - return (lp); - } - y2 = xy.y * xy.y; - r = x2 + y2; r2 = r * r; - c1 = - PI * ay * (r + PISQ); - c3 = r2 + TWOPI * (ay * r + PI * (y2 + PI * (ay + HALFPI))); - c2 = c1 + PISQ * (r - 3. * y2); - c0 = PI * ay; - c2 /= c3; - al = c1 / c3 - THIRD * c2 * c2; - m = 2. * sqrt(-THIRD * al); - d = C2_27 * c2 * c2 * c2 + (c0 * c0 - THIRD * c2 * c1) / c3; - if (((t = fabs(d = 3. * d / (al * m))) - TOL) <= 1.) { - d = t > 1. ? (d > 0. ? 0. : PI) : acos(d); - lp.phi = PI * (m * cos(d * THIRD + PI4_3) - THIRD * c2); - if (xy.y < 0.) lp.phi = -lp.phi; - t = r2 + TPISQ * (x2 - y2 + HPISQ); - lp.lam = fabs(xy.x) <= TOL ? 0. : - .5 * (r - PISQ + (t <= 0. ? 0. : sqrt(t))) / xy.x; - } else - I_ERROR; - return (lp); + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; + double t, c0, c1, c2, c3, al, r2, r, m, d, ay, x2, y2; + + x2 = xy.x * xy.x; + if ((ay = fabs(xy.y)) < TOL) { + lp.phi = 0.; + t = x2 * x2 + TPISQ * (x2 + HPISQ); + lp.lam = fabs(xy.x) <= TOL ? 0. : + .5 * (x2 - PISQ + sqrt(t)) / xy.x; + return (lp); + } + y2 = xy.y * xy.y; + r = x2 + y2; r2 = r * r; + c1 = - PI * ay * (r + PISQ); + c3 = r2 + TWOPI * (ay * r + PI * (y2 + PI * (ay + HALFPI))); + c2 = c1 + PISQ * (r - 3. * y2); + c0 = PI * ay; + c2 /= c3; + al = c1 / c3 - THIRD * c2 * c2; + m = 2. * sqrt(-THIRD * al); + d = C2_27 * c2 * c2 * c2 + (c0 * c0 - THIRD * c2 * c1) / c3; + if (((t = fabs(d = 3. * d / (al * m))) - TOL) <= 1.) { + d = t > 1. ? (d > 0. ? 0. : PI) : acos(d); + lp.phi = PI * (m * cos(d * THIRD + PI4_3) - THIRD * c2); + if (xy.y < 0.) lp.phi = -lp.phi; + t = r2 + TPISQ * (x2 - y2 + HPISQ); + lp.lam = fabs(xy.x) <= TOL ? 0. : + .5 * (r - PISQ + (t <= 0. ? 0. : sqrt(t))) / xy.x; + } else + I_ERROR; + + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(vandg) P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P) + + +PJ *PROJECTION(vandg) { + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_vandg_selftest (void) {return 0;} +#else + +int pj_vandg_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=vandg +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223395.24954340671, 111704.59663367498}, + { 223395.24954340671, -111704.59663367498}, + {-223395.24954340671, 111704.59663367498}, + {-223395.24954340671, -111704.59663367498}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.001790493715929761, 0.00089524655486993867}, + { 0.001790493715929761, -0.00089524655486993867}, + {-0.001790493715929761, 0.00089524655486993867}, + {-0.001790493715929761, -0.00089524655486993867}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_vandg2.c b/src/PJ_vandg2.c index 7bfa8f4e..f8850cad 100644 --- a/src/PJ_vandg2.c +++ b/src/PJ_vandg2.c @@ -1,45 +1,149 @@ -# define TOL 1e-10 -# define TWORPI 0.63661977236758134308 -#define PROJ_PARMS__ \ - int vdg3; #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + +struct pj_opaque { + int vdg3; +}; + PROJ_HEAD(vandg2, "van der Grinten II") "\n\tMisc Sph, no inv."; PROJ_HEAD(vandg3, "van der Grinten III") "\n\tMisc Sph, no inv."; -FORWARD(s_forward); /* spheroid */ - double x1, at, bt, ct; - - bt = fabs(TWORPI * lp.phi); - if ((ct = 1. - bt * bt) < 0.) - ct = 0.; - else - ct = sqrt(ct); - if (fabs(lp.lam) < TOL) { - xy.x = 0.; - xy.y = PI * (lp.phi < 0. ? -bt : bt) / (1. + ct); - } else { - at = 0.5 * fabs(PI / lp.lam - lp.lam / PI); - if (P->vdg3) { - x1 = bt / (1. + ct); - xy.x = PI * (sqrt(at * at + 1. - x1 * x1) - at); - xy.y = PI * x1; - } else { - x1 = (ct * sqrt(1. + at * at) - at * ct * ct) / - (1. + at * at * bt * bt); - xy.x = PI * x1; - xy.y = PI * sqrt(1. - x1 * (x1 + 2. * at) + TOL); - } - if ( lp.lam < 0.) xy.x = -xy.x; - if ( lp.phi < 0.) xy.y = -xy.y; - } - return (xy); + +#define TOL 1e-10 +#define TWORPI 0.63661977236758134308 + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double x1, at, bt, ct; + + bt = fabs(TWORPI * lp.phi); + if ((ct = 1. - bt * bt) < 0.) + ct = 0.; + else + ct = sqrt(ct); + if (fabs(lp.lam) < TOL) { + xy.x = 0.; + xy.y = PI * (lp.phi < 0. ? -bt : bt) / (1. + ct); + } else { + at = 0.5 * fabs(PI / lp.lam - lp.lam / PI); + if (Q->vdg3) { + x1 = bt / (1. + ct); + xy.x = PI * (sqrt(at * at + 1. - x1 * x1) - at); + xy.y = PI * x1; + } else { + x1 = (ct * sqrt(1. + at * at) - at * ct * ct) / + (1. + at * at * bt * bt); + xy.x = PI * x1; + xy.y = PI * sqrt(1. - x1 * (x1 + 2. * at) + TOL); + } + if ( lp.lam < 0.) xy.x = -xy.x; + if ( lp.phi < 0.) xy.y = -xy.y; + } + + return xy; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(vandg2) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->vdg3 = 0; + P->fwd = s_forward; + + return P; +} + +PJ *PROJECTION(vandg3) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->vdg3 = 1; + P->es = 0.; + P->fwd = s_forward; + + return P; } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(vandg2) - P->vdg3 = 0; - P->inv = 0; P->fwd = s_forward; -ENDENTRY(P) -ENTRY0(vandg3) - P->vdg3 = 1; - P->es = 0.; P->fwd = s_forward; -ENDENTRY(P) + + +#ifdef PJ_OMIT_SELFTEST +int pj_vandg2_selftest (void) {return 0;} +#else + +int pj_vandg2_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=vandg2 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223395.24785043663, 111718.49103722633}, + { 223395.24785043663, -111718.49103722633}, + {-223395.24785043663, 111718.49103722633}, + {-223395.24785043663, -111718.49103722633}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, 0, 0, 0); +} + + +#endif + +#ifdef PJ_OMIT_SELFTEST +int pj_vandg3_selftest (void) {return 0;} +#else + +int pj_vandg3_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=vandg3 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223395.24955283134, 111704.51990442065}, + { 223395.24955283134, -111704.51990442065}, + {-223395.24955283134, 111704.51990442065}, + {-223395.24955283134, -111704.51990442065}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, 0, 0, 0); +} + + +#endif diff --git a/src/PJ_vandg4.c b/src/PJ_vandg4.c index 0229042b..7a7ffefb 100644 --- a/src/PJ_vandg4.c +++ b/src/PJ_vandg4.c @@ -1,42 +1,98 @@ -#define PJ_LIB__ -#include <projects.h> -PROJ_HEAD(vandg4, "van der Grinten IV") "\n\tMisc Sph, no inv."; -#define TOL 1e-10 -#define TWORPI 0.63661977236758134308 -FORWARD(s_forward); /* spheroid */ - double x1, t, bt, ct, ft, bt2, ct2, dt, dt2; - (void) P; - - if (fabs(lp.phi) < TOL) { - xy.x = lp.lam; - xy.y = 0.; - } else if (fabs(lp.lam) < TOL || fabs(fabs(lp.phi) - HALFPI) < TOL) { - xy.x = 0.; - xy.y = lp.phi; - } else { - bt = fabs(TWORPI * lp.phi); - bt2 = bt * bt; - ct = 0.5 * (bt * (8. - bt * (2. + bt2)) - 5.) - / (bt2 * (bt - 1.)); - ct2 = ct * ct; - dt = TWORPI * lp.lam; - dt = dt + 1. / dt; - dt = sqrt(dt * dt - 4.); - if ((fabs(lp.lam) - HALFPI) < 0.) dt = -dt; - dt2 = dt * dt; - x1 = bt + ct; x1 *= x1; - t = bt + 3.*ct; - ft = x1 * (bt2 + ct2 * dt2 - 1.) + (1.-bt2) * ( - bt2 * (t * t + 4. * ct2) + - ct2 * (12. * bt * ct + 4. * ct2) ); - x1 = (dt*(x1 + ct2 - 1.) + 2.*sqrt(ft)) / - (4.* x1 + dt2); - xy.x = HALFPI * x1; - xy.y = HALFPI * sqrt(1. + dt * fabs(x1) - x1 * x1); - if (lp.lam < 0.) xy.x = -xy.x; - if (lp.phi < 0.) xy.y = -xy.y; - } - return (xy); -} -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(vandg4) P->es = 0.; P->fwd = s_forward; ENDENTRY(P) +#define PJ_LIB__
+#include <projects.h>
+
+PROJ_HEAD(vandg4, "van der Grinten IV") "\n\tMisc Sph, no inv.";
+
+#define TOL 1e-10
+#define TWORPI 0.63661977236758134308
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ double x1, t, bt, ct, ft, bt2, ct2, dt, dt2;
+ (void) P;
+
+ if (fabs(lp.phi) < TOL) {
+ xy.x = lp.lam;
+ xy.y = 0.;
+ } else if (fabs(lp.lam) < TOL || fabs(fabs(lp.phi) - HALFPI) < TOL) {
+ xy.x = 0.;
+ xy.y = lp.phi;
+ } else {
+ bt = fabs(TWORPI * lp.phi);
+ bt2 = bt * bt;
+ ct = 0.5 * (bt * (8. - bt * (2. + bt2)) - 5.)
+ / (bt2 * (bt - 1.));
+ ct2 = ct * ct;
+ dt = TWORPI * lp.lam;
+ dt = dt + 1. / dt;
+ dt = sqrt(dt * dt - 4.);
+ if ((fabs(lp.lam) - HALFPI) < 0.) dt = -dt;
+ dt2 = dt * dt;
+ x1 = bt + ct; x1 *= x1;
+ t = bt + 3.*ct;
+ ft = x1 * (bt2 + ct2 * dt2 - 1.) + (1.-bt2) * (
+ bt2 * (t * t + 4. * ct2) +
+ ct2 * (12. * bt * ct + 4. * ct2) );
+ x1 = (dt*(x1 + ct2 - 1.) + 2.*sqrt(ft)) /
+ (4.* x1 + dt2);
+ xy.x = HALFPI * x1;
+ xy.y = HALFPI * sqrt(1. + dt * fabs(x1) - x1 * x1);
+ if (lp.lam < 0.) xy.x = -xy.x;
+ if (lp.phi < 0.) xy.y = -xy.y;
+ }
+ return xy;
+}
+
+
+static void *freeup_new (PJ *P) { /* Destructor */
+ if (0==P)
+ return 0;
+
+ return pj_dealloc(P);
+}
+
+
+static void freeup (PJ *P) {
+ freeup_new (P);
+ return;
+}
+
+
+PJ *PROJECTION(vandg4) {
+ P->es = 0.;
+ P->fwd = s_forward;
+
+ return P;
+}
+
+
+#ifdef PJ_OMIT_SELFTEST
+int pj_vandg4_selftest (void) {return 0;}
+#else
+
+int pj_vandg4_selftest (void) {
+ double tolerance_lp = 1e-10;
+ double tolerance_xy = 1e-7;
+
+ char s_args[] = {"+proj=vandg4 +a=6400000 +lat_1=0.5 +lat_2=2"};
+
+ LP fwd_in[] = {
+ { 2, 1},
+ { 2,-1},
+ {-2, 1},
+ {-2,-1}
+ };
+
+ XY s_fwd_expect[] = {
+ { 223374.57729435508, 111701.19548415358
},
+ { 223374.57729435508, -111701.19548415358
},
+ {-223374.57729435508, 111701.19548415358
},
+ {-223374.57729435508, -111701.19548415358
},
+ };
+
+ return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, 0, 0, 0);
+}
+
+
+#endif
diff --git a/src/PJ_wag2.c b/src/PJ_wag2.c index f4942bb2..b70dcd4f 100644 --- a/src/PJ_wag2.c +++ b/src/PJ_wag2.c @@ -5,17 +5,84 @@ PROJ_HEAD(wag2, "Wagner II") "\n\tPCyl., Sph."; #define C_y 1.38725 #define C_p1 0.88022 #define C_p2 0.88550 -FORWARD(s_forward); /* spheroid */ - lp.phi = aasin(P->ctx,C_p1 * sin(C_p2 * lp.phi)); - xy.x = C_x * lp.lam * cos(lp.phi); + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + lp.phi = aasin (P->ctx,C_p1 * sin (C_p2 * lp.phi)); + xy.x = C_x * lp.lam * cos (lp.phi); xy.y = C_y * lp.phi; return (xy); } -INVERSE(s_inverse); /* spheroid */ + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; lp.phi = xy.y / C_y; lp.lam = xy.x / (C_x * cos(lp.phi)); - lp.phi = aasin(P->ctx,sin(lp.phi) / C_p1) / C_p2; + lp.phi = aasin (P->ctx,sin(lp.phi) / C_p1) / C_p2; return (lp); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(wag2) P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; ENDENTRY(P) + + +static void *freeup_new (PJ *P) { /* Destructor */ + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(wag2) { + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_wag2_selftest (void) {return 0;} +#else + +int pj_wag2_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=wag2 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 206589.88809996162, 120778.04035754716}, + { 206589.88809996162, -120778.04035754716}, + {-206589.88809996162, 120778.04035754716}, + {-206589.88809996162, -120778.04035754716}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.0019360240367390709, 0.00082795765763814082}, + { 0.0019360240367390709, -0.00082795765763814082}, + {-0.0019360240367390709, 0.00082795765763814082}, + {-0.0019360240367390709, -0.00082795765763814082}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_wag3.c b/src/PJ_wag3.c index 482e389c..0539f4a1 100644 --- a/src/PJ_wag3.c +++ b/src/PJ_wag3.c @@ -1,24 +1,101 @@ -#define PROJ_PARMS__ \ - double C_x; #define PJ_LIB__ # include <projects.h> PROJ_HEAD(wag3, "Wagner III") "\n\tPCyl., Sph.\n\tlat_ts="; #define TWOTHIRD 0.6666666666666666666667 -FORWARD(s_forward); /* spheroid */ - xy.x = P->C_x * lp.lam * cos(TWOTHIRD * lp.phi); + +struct pj_opaque { + double C_x; +}; + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + xy.x = P->opaque->C_x * lp.lam * cos(TWOTHIRD * lp.phi); xy.y = lp.phi; - return (xy); + return xy; } + + +#if 0 INVERSE(s_inverse); /* spheroid */ +#endif + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; lp.phi = xy.y; - lp.lam = xy.x / (P->C_x * cos(TWOTHIRD * lp.phi)); - return (lp); + lp.lam = xy.x / (P->opaque->C_x * cos(TWOTHIRD * lp.phi)); + return lp; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + return pj_dealloc(P); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(wag3) + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(wag3) { double ts; + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + ts = pj_param (P->ctx, P->params, "rlat_ts").f; + P->opaque->C_x = cos (ts) / cos (2.*ts/3.); + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_wag3_selftest (void) {return 0;} +#else + +int pj_wag3_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=wag3 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + {223387.02171816575, 111701.07212763709}, + {223387.02171816575, -111701.07212763709}, + {-223387.02171816575, 111701.07212763709}, + {-223387.02171816575, -111701.07212763709}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + {0.001790493109880963, 0.00089524655489191132}, + {0.001790493109880963, -0.00089524655489191132}, + {-0.001790493109880963, 0.00089524655489191132}, + {-0.001790493109880963, -0.00089524655489191132}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + - ts = pj_param(P->ctx, P->params, "rlat_ts").f; - P->C_x = cos(ts) / cos(2.*ts/3.); - P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; -ENDENTRY(P) +#endif diff --git a/src/PJ_wag7.c b/src/PJ_wag7.c index db29ffb1..847566d8 100644 --- a/src/PJ_wag7.c +++ b/src/PJ_wag7.c @@ -1,15 +1,67 @@ #define PJ_LIB__ #include <projects.h> + PROJ_HEAD(wag7, "Wagner VII") "\n\tMisc Sph, no inv."; -FORWARD(s_forward); /* sphere */ + + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0, 0.0}; double theta, ct, D; - (void) P; - theta = asin(xy.y = 0.90630778703664996 * sin(lp.phi)); - xy.x = 2.66723 * (ct = cos(theta)) * sin(lp.lam /= 3.); - xy.y *= 1.24104 * (D = 1/(sqrt(0.5 * (1 + ct * cos(lp.lam))))); + (void) P; /* Shut up compiler warnnings about unused P */ + + theta = asin (xy.y = 0.90630778703664996 * sin(lp.phi)); + xy.x = 2.66723 * (ct = cos (theta)) * sin (lp.lam /= 3.); + xy.y *= 1.24104 * (D = 1/(sqrt (0.5 * (1 + ct * cos (lp.lam))))); xy.x *= D; return (xy); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(wag7) P->fwd = s_forward; P->inv = 0; P->es = 0.; ENDENTRY(P) + + +static void *freeup_new (PJ *P) { /* Destructor */ + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + + +PJ *PROJECTION(wag7) { + P->fwd = s_forward; + P->inv = 0; + P->es = 0.; + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_wag7_selftest (void) {return 0;} +#else + +int pj_wag7_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=wag7 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 198601.87695731167, 125637.0457141714}, + { 198601.87695731167, -125637.0457141714}, + {-198601.87695731167, 125637.0457141714}, + {-198601.87695731167, -125637.0457141714}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, 0, 0, 0); +} +#endif diff --git a/src/PJ_wink1.c b/src/PJ_wink1.c index dfb455e0..f9d127c0 100644 --- a/src/PJ_wink1.c +++ b/src/PJ_wink1.c @@ -1,20 +1,100 @@ -#define PROJ_PARMS__ \ - double cosphi1; #define PJ_LIB__ -# include <projects.h> +#include <projects.h> PROJ_HEAD(wink1, "Winkel I") "\n\tPCyl., Sph.\n\tlat_ts="; -FORWARD(s_forward); /* spheroid */ - xy.x = .5 * lp.lam * (P->cosphi1 + cos(lp.phi)); + + +struct pj_opaque { + double cosphi1; +}; + + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0,0.0}; + xy.x = .5 * lp.lam * (P->opaque->cosphi1 + cos(lp.phi)); xy.y = lp.phi; return (xy); } -INVERSE(s_inverse); /* spheroid */ + + +static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */ + LP lp = {0.0,0.0}; lp.phi = xy.y; - lp.lam = 2. * xy.x / (P->cosphi1 + cos(lp.phi)); + lp.lam = 2. * xy.x / (P->opaque->cosphi1 + cos(lp.phi)); return (lp); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(wink1) - P->cosphi1 = cos(pj_param(P->ctx, P->params, "rlat_ts").f); - P->es = 0.; P->inv = s_inverse; P->fwd = s_forward; -ENDENTRY(P) + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(wink1) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + P->opaque->cosphi1 = cos (pj_param(P->ctx, P->params, "rlat_ts").f); + P->es = 0.; + P->inv = s_inverse; + P->fwd = s_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_wink1_selftest (void) {return 0;} +#else + +int pj_wink1_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=wink1 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223385.13164095284, 111701.07212763709}, + { 223385.13164095284, -111701.07212763709}, + {-223385.13164095284, 111701.07212763709}, + {-223385.13164095284, -111701.07212763709}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP s_inv_expect[] = { + { 0.0017904931098931057, 0.00089524655489191132}, + { 0.0017904931098931057, -0.00089524655489191132}, + {-0.0017904931098931057, 0.00089524655489191132}, + {-0.0017904931098931057, -0.00089524655489191132}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, inv_in, 0, s_inv_expect); +} + + +#endif diff --git a/src/PJ_wink2.c b/src/PJ_wink2.c index 388c790a..f31ba5a5 100644 --- a/src/PJ_wink2.c +++ b/src/PJ_wink2.c @@ -1,34 +1,93 @@ -#define PROJ_PARMS__ \ - double cosphi1; #define PJ_LIB__ # include <projects.h> + PROJ_HEAD(wink2, "Winkel II") "\n\tPCyl., Sph., no inv.\n\tlat_1="; + +struct pj_opaque { double cosphi1; }; + #define MAX_ITER 10 #define LOOP_TOL 1e-7 #define TWO_D_PI 0.636619772367581343 -FORWARD(s_forward); /* spheroid */ + + +static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */ + XY xy = {0.0, 0.0}; double k, V; int i; xy.y = lp.phi * TWO_D_PI; - k = PI * sin(lp.phi); + k = PI * sin (lp.phi); lp.phi *= 1.8; for (i = MAX_ITER; i ; --i) { - lp.phi -= V = (lp.phi + sin(lp.phi) - k) / - (1. + cos(lp.phi)); - if (fabs(V) < LOOP_TOL) + lp.phi -= V = (lp.phi + sin (lp.phi) - k) / + (1. + cos (lp.phi)); + if (fabs (V) < LOOP_TOL) break; } if (!i) lp.phi = (lp.phi < 0.) ? -HALFPI : HALFPI; else lp.phi *= 0.5; - xy.x = 0.5 * lp.lam * (cos(lp.phi) + P->cosphi1); - xy.y = FORTPI * (sin(lp.phi) + xy.y); - return (xy); + xy.x = 0.5 * lp.lam * (cos (lp.phi) + P->opaque->cosphi1); + xy.y = FORTPI * (sin (lp.phi) + xy.y); + return xy; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(wink2) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + P->opaque->cosphi1 = cos(pj_param(P->ctx, P->params, "rlat_1").f); + P->es = 0.; + P->inv = 0; + P->fwd = s_forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_wink2_selftest (void) {return 0;} +#else + +int pj_wink2_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char s_args[] = {"+proj=wink2 +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY s_fwd_expect[] = { + { 223387.39643378611, 124752.03279744535}, + { 223387.39643378611, -124752.03279744535}, + {-223387.39643378611, 124752.03279744535}, + {-223387.39643378611, -124752.03279744535}, + }; + + return pj_generic_selftest (0, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, 0, s_fwd_expect, 0, 0, 0); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(wink2) - P->cosphi1 = cos(pj_param(P->ctx, P->params, "rlat_1").f); - P->es = 0.; P->inv = 0; P->fwd = s_forward; -ENDENTRY(P) +#endif diff --git a/src/lib_proj.cmake b/src/lib_proj.cmake index ed14e99e..7b8ec961 100644 --- a/src/lib_proj.cmake +++ b/src/lib_proj.cmake @@ -174,6 +174,7 @@ SET(SRC_LIBPROJ_CORE pj_fwd3d.c pj_gauss.c pj_gc_reader.c + pj_generic_selftest.c pj_geocent.c pj_gridcatalog.c pj_gridinfo.c @@ -197,6 +198,7 @@ SET(SRC_LIBPROJ_CORE pj_pr_list.c pj_qsfn.c pj_release.c + pj_run_selftests.c pj_strerrno.c pj_transform.c pj_tsfn.c diff --git a/src/makefile.vc b/src/makefile.vc index 6989519d..2efad1f0 100644 --- a/src/makefile.vc +++ b/src/makefile.vc @@ -14,19 +14,19 @@ conic = \ PJ_rpoly.obj PJ_sconics.obj PJ_lcca.obj cylinder = \ - PJ_cass.obj PJ_cc.obj PJ_cea.obj PJ_comill.obj PJ_eqc.obj \ + PJ_cass.obj PJ_cc.obj PJ_cea.obj PJ_eqc.obj \ PJ_gall.obj PJ_labrd.obj PJ_lsat.obj PJ_misrsom.obj PJ_merc.obj \ PJ_mill.obj PJ_ocea.obj PJ_omerc.obj PJ_patterson.obj PJ_somerc.obj \ PJ_tcc.obj PJ_tcea.obj PJ_tmerc.obj PJ_geos.obj \ - PJ_gstmerc.obj proj_etmerc.obj + PJ_gstmerc.obj proj_etmerc.obj PJ_comill.obj misc = \ PJ_airy.obj PJ_aitoff.obj PJ_august.obj PJ_bacon.obj \ PJ_chamb.obj PJ_hammer.obj PJ_lagrng.obj PJ_larr.obj \ PJ_lask.obj PJ_nocol.obj PJ_ob_tran.obj PJ_oea.obj \ PJ_sch.obj PJ_tpeqd.obj PJ_vandg.obj PJ_vandg2.obj \ - PJ_vandg4.obj PJ_wag7.obj pj_latlong.obj PJ_krovak.obj \ - pj_geocent.obj PJ_healpix.obj PJ_natearth.obj PJ_natearth2.obj PJ_qsc.obj + PJ_vandg4.obj PJ_wag7.obj pj_latlong.obj PJ_krovak.obj \ + pj_geocent.obj PJ_healpix.obj PJ_qsc.obj pseudo = \ PJ_boggs.obj PJ_collg.obj PJ_crast.obj PJ_denoy.obj \ @@ -38,14 +38,14 @@ pseudo = \ PJ_putp4p.obj PJ_putp5.obj PJ_putp6.obj PJ_robin.obj \ PJ_sts.obj PJ_urm5.obj PJ_urmfps.obj PJ_wag2.obj \ PJ_wag3.obj PJ_wink1.obj PJ_wink2.obj PJ_isea.obj \ - PJ_calcofi.obj + PJ_calcofi.obj PJ_natearth.obj PJ_natearth2.obj support = \ aasincos.obj adjlon.obj bch2bps.obj bchgen.obj pj_gauss.obj \ biveval.obj dmstor.obj mk_cheby.obj pj_auth.obj \ pj_deriv.obj pj_ell_set.obj pj_ellps.obj pj_errno.obj \ pj_factors.obj pj_fwd.obj pj_init.obj pj_inv.obj \ - pj_fwd3d.obj pj_inv3d.obj \ + pj_fwd3d.obj pj_inv3d.obj \ pj_list.obj pj_malloc.obj pj_mlfn.obj pj_msfn.obj \ pj_open_lib.obj pj_param.obj pj_phi2.obj pj_pr_list.obj \ pj_qsfn.obj pj_strerrno.obj pj_tsfn.obj pj_units.obj \ @@ -56,7 +56,7 @@ support = \ pj_utils.obj pj_gridlist.obj pj_gridinfo.obj \ proj_mdist.obj pj_mutex.obj pj_initcache.obj \ pj_ctx.obj pj_fileapi.obj pj_log.obj pj_apply_vgridshift.obj \ - pj_strtod.obj + pj_strtod.obj pj_run_selftests.obj pj_generic_selftest.obj geodesic = geodesic.obj LIBOBJ = $(support) $(pseudo) $(azimuthal) $(conic) $(cylinder) $(misc) \ @@ -146,4 +146,4 @@ install: all copy proj_api.h $(INSTDIR)\include copy projects.h $(INSTDIR)\include copy geodesic.h $(INSTDIR)\include - + diff --git a/src/pj_fwd.c b/src/pj_fwd.c index 1cd002b5..d4948d99 100644 --- a/src/pj_fwd.c +++ b/src/pj_fwd.c @@ -25,7 +25,7 @@ pj_fwd(LP lp, PJ *P) { if (!P->over) lp.lam = adjlon(lp.lam); /* adjust del longitude */ - //Check for NULL pointer + /* Check for NULL pointer */ if (P->fwd != NULL) { xy = (*P->fwd)(lp, P); /* project */ diff --git a/src/pj_fwd3d.c b/src/pj_fwd3d.c index 834746d4..9da8a482 100644 --- a/src/pj_fwd3d.c +++ b/src/pj_fwd3d.c @@ -19,13 +19,13 @@ pj_fwd3d(LPZ lpz, PJ *P) { if (fabs(t) <= EPS) lpz.phi = lpz.phi < 0. ? -HALFPI : HALFPI; - else if (P->geoc) //Maybe redundant and never used. + else if (P->geoc) /* Maybe redundant and never used. */ lpz.phi = atan(P->rone_es * tan(lpz.phi)); lpz.lam -= P->lam0; /* compute del lp.lam */ if (!P->over) lpz.lam = adjlon(lpz.lam); /* adjust del longitude */ - //Check for NULL pointer + /* Check for NULL pointer */ if (P->fwd3d != NULL) { xyz = (*P->fwd3d)(lpz, P); /* project */ @@ -35,7 +35,7 @@ pj_fwd3d(LPZ lpz, PJ *P) { else { xyz.x = P->fr_meter * (P->a * xyz.x + P->x0); xyz.y = P->fr_meter * (P->a * xyz.y + P->y0); - //z is not scaled since this handled by vto_meter outside + /* z is not scaled since this handled by vto_meter outside */ } } else diff --git a/src/pj_generic_selftest.c b/src/pj_generic_selftest.c new file mode 100644 index 00000000..88b5c308 --- /dev/null +++ b/src/pj_generic_selftest.c @@ -0,0 +1,197 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Generic regression test for PROJ.4 projection algorithms. + * Author: Thomas Knudsen + * + ****************************************************************************** + * Copyright (c) 2016, Thomas Knudsen + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + + +#include <stdio.h> +#define PJ_LIB__ +#include <projects.h> + + +static int deviates_xy (XY expected, XY got, double tolerance); +static int deviates_lp (LP expected, LP got, double tolerance); +static XY pj_fwd_deg (LP in, PJ *P); + + +/**********************************************************************/ +int pj_generic_selftest ( +/**********************************************************************/ + char *e_args, + char *s_args, + double tolerance_xy, + double tolerance_lp, + int n_fwd, + int n_inv, + LP *fwd_in, + XY *e_fwd_expect, + XY *s_fwd_expect, + XY *inv_in, + LP *e_inv_expect, + LP *s_inv_expect +) { +/*********************************************************************** + +Generic regression test for PROJ.4 projection algorithms, testing both +ellipsoidal ("e_") and spheroidal ("s_") versions of the projection +algorithms in both forward ("_fwd_") and inverse ("_inv_") mode. + +Compares the "known good" results in <e_fwd_expect> and <s_fwd_expect> +with the actual results obtained by transforming the forward input data +set in <fwd_in> with pj_fwd() using setup arguments <e_args> and +<s_args>, respectively. + +Then + +Compares the "known good" results in <e_inv_expect> and <s_inv_expect> +with the actual results obtained by transforming the inverse input data +set in <inv_in> with pj_inv() using setup arguments <e_args> and +<s_args>, respectively. + +Any of the pointers passed may be set to 0, indicating "don't test this +part". + +Returns 0 if all data agree to within the accuracy specified in +<tolerance_xy> and <tolerance_lp>. Non-zero otherwise. + +***********************************************************************/ + int i; + + PJ *P; + + if (e_args) { + P = pj_init_plus(e_args); + if (0==P) + return 2; + + /* Test forward ellipsoidal */ + if (e_fwd_expect) { + for (i = 0; i < n_fwd; i++) + if (deviates_xy (e_fwd_expect[i], pj_fwd_deg ( fwd_in[i], P ), tolerance_xy)) + break; + if ( i != n_fwd ) + return 100 + i; + } + + /* Test inverse ellipsoidal */ + if (e_inv_expect) { + for (i = 0; i < n_inv; i++) + if (deviates_lp (e_inv_expect[i], pj_inv ( inv_in[i], P ), tolerance_lp)) + break; + if ( i != n_inv ) + return 200 + i; + } + + pj_free (P); + } + + + if (s_args) { + P = pj_init_plus(s_args); + if (0==P) + return 3; + + /* Test forward spherical */ + if (s_fwd_expect) { + for (i = 0; i < n_fwd; i++) + if (deviates_xy (s_fwd_expect[i], pj_fwd_deg ( fwd_in[i], P ), tolerance_xy)) + break; + if ( i != n_fwd ) + return 300 + i; + } + + /* Test inverse spherical */ + if (s_inv_expect) { + for (i = 0; i < n_inv; i++) + if (deviates_lp (s_inv_expect[i], pj_inv ( inv_in[i], P ), tolerance_lp)) + break; + if ( i != n_inv ) + return 400 + i; + } + + pj_free (P); + } + + return 0; +} + + + +/**********************************************************************/ +static int deviates_xy (XY expected, XY got, double tolerance) { +/*********************************************************************** + + Determine whether two XYs deviate by more than <tolerance>. + + The test material ("expected" values) may contain coordinates that + are indeterminate. For those cases, we test the other coordinate + only by forcing expected and actual ("got") coordinates to 0. + +***********************************************************************/ + if (HUGE_VAL== expected.x) + return 0; + if (HUGE_VAL== expected.y) + return 0; + if (hypot ( expected.x - got.x, expected.y - got.y ) > tolerance) + return 1; + return 0; +} + + +/**********************************************************************/ +static int deviates_lp (LP expected, LP got, double tolerance) { +/*********************************************************************** + + Determine whether two LPs deviate by more than <tolerance>. + + This one is slightly tricky, since the <expected> LP is + supposed to be represented as degrees (since it was at some + time written down by a real human), whereas the <got> LP is + represented in radians (since it is supposed to be the result + output from pj_inv) + +***********************************************************************/ + if (HUGE_VAL== expected.lam) + return 0; + if (HUGE_VAL== expected.phi) + return 0; + if (hypot ( DEG_TO_RAD * expected.lam - got.lam, DEG_TO_RAD * expected.phi - got.phi ) > tolerance) + return 1; + return 0; +} + + +/**********************************************************************/ +static XY pj_fwd_deg (LP in, PJ *P) { +/*********************************************************************** + + Wrapper for pj_fwd, accepting input in degrees. + +***********************************************************************/ + LP in_rad; + in_rad.lam = DEG_TO_RAD * in.lam; + in_rad.phi = DEG_TO_RAD * in.phi; + return pj_fwd (in_rad, P); +} diff --git a/src/pj_geocent.c b/src/pj_geocent.c index 6b2f3026..fa01b01f 100644 --- a/src/pj_geocent.c +++ b/src/pj_geocent.c @@ -32,24 +32,88 @@ PROJ_HEAD(geocent, "Geocentric") "\n\t"; -FORWARD(forward); - (void) P; - xy.x = lp.lam; - xy.y = lp.phi; - return xy; +static XY forward(LP lp, PJ *P) { + XY xy = {0.0,0.0}; + (void) P; + xy.x = lp.lam; + xy.y = lp.phi; + return xy; } -INVERSE(inverse); - (void) P; - lp.phi = xy.y; - lp.lam = xy.x; - return lp; + +static LP inverse(XY xy, PJ *P) { + LP lp = {0.0,0.0}; + (void) P; + lp.phi = xy.y; + lp.lam = xy.x; + return lp; +} + + +static void *freeup_new (PJ *P) { + if (0==P) + return 0; + + return pj_dealloc(P); } -FREEUP; if (P) pj_dalloc(P); } -ENTRY0(geocent) - P->is_geocent = 1; +static void freeup (PJ *P) { + freeup_new (P); + return; +} + +PJ *PROJECTION(geocent) { + P->is_geocent = 1; P->x0 = 0.0; P->y0 = 0.0; - P->inv = inverse; P->fwd = forward; -ENDENTRY(P) + P->inv = inverse; + P->fwd = forward; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_geocent_selftest (void) {return 0;} +#else + +int pj_geocent_selftest (void) { + + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=geocent +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + char s_args[] = {"+proj=geocent +a=6400000 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 222638.98158654713, 111319.49079327357}, + { 222638.98158654713, -111319.49079327357}, + {-222638.98158654713, 111319.49079327357}, + {-222638.98158654713, -111319.49079327357}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + { 0.0017966305682390426, 0.00089831528411952132}, + { 0.0017966305682390426, -0.00089831528411952132}, + {-0.0017966305682390426, 0.00089831528411952132}, + {-0.0017966305682390426, -0.00089831528411952132}, + }; + + return pj_generic_selftest (e_args, s_args, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, 0, inv_in, e_inv_expect, 0); +} + +#endif diff --git a/src/pj_init.c b/src/pj_init.c index 13d469da..79b64bb3 100644 --- a/src/pj_init.c +++ b/src/pj_init.c @@ -110,7 +110,7 @@ get_opt(projCtx ctx, paralist **start, PAFile fid, char *name, paralist *next, *sword = 't'; /* loop till we find our target keyword */ - while (*next_char) + while (*next_char) { next_char = fill_buffer(state, next_char); @@ -119,9 +119,9 @@ get_opt(projCtx ctx, paralist **start, PAFile fid, char *name, paralist *next, next_char++; next_char = fill_buffer(state, next_char); - + /* for comments, skip past end of line. */ - if( *next_char == '#' ) + if( *next_char == '#' ) { while( *next_char && *next_char != '\n' ) next_char++; @@ -131,11 +131,11 @@ get_opt(projCtx ctx, paralist **start, PAFile fid, char *name, paralist *next, next_char++; if (*next_char == '\r') next_char++; - - } + + } /* Is this our target? */ - else if( *next_char == '<' ) + else if( *next_char == '<' ) { /* terminate processing target on the next block definition */ if (in_target) @@ -143,7 +143,7 @@ get_opt(projCtx ctx, paralist **start, PAFile fid, char *name, paralist *next, next_char++; if (strncmp(name, next_char, len) == 0 - && next_char[len] == '>') + && next_char[len] == '>') { /* skip past target word */ next_char += len + 1; @@ -151,14 +151,14 @@ get_opt(projCtx ctx, paralist **start, PAFile fid, char *name, paralist *next, if(found_def) *found_def = 1; } - else + else { /* skip past end of line */ while( *next_char && *next_char != '\n' ) next_char++; } } - else if (in_target) + else if (in_target) { const char *start_of_word = next_char; int word_len = 0; @@ -183,27 +183,27 @@ get_opt(projCtx ctx, paralist **start, PAFile fid, char *name, paralist *next, if (!pj_param(ctx, *start, sword).i) { /* don't default ellipse if datum, ellps or any earth model information is set. */ - if( strncmp(sword+1,"ellps=",6) != 0 - || (!pj_param(ctx, *start, "tdatum").i - && !pj_param(ctx, *start, "tellps").i - && !pj_param(ctx, *start, "ta").i - && !pj_param(ctx, *start, "tb").i - && !pj_param(ctx, *start, "trf").i + if( strncmp(sword+1,"ellps=",6) != 0 + || (!pj_param(ctx, *start, "tdatum").i + && !pj_param(ctx, *start, "tellps").i + && !pj_param(ctx, *start, "ta").i + && !pj_param(ctx, *start, "tb").i + && !pj_param(ctx, *start, "trf").i && !pj_param(ctx, *start, "tf").i) ) { next = next->next = pj_mkparam(sword+1); } } - + } - else + else { /* skip past word */ while( *next_char && !isspace(*next_char) ) next_char++; - + } - } + } if (errno == 25) errno = 0; @@ -229,7 +229,7 @@ get_defaults(projCtx ctx, paralist **start, paralist *next, char *name) { if (errno) errno = 0; /* don't care if can't open file */ ctx->last_errno = 0; - + return next; } @@ -245,11 +245,11 @@ get_init(projCtx ctx, paralist **start, paralist *next, char *name, const paralist *orig_next = next; (void)strncpy(fname, name, MAX_PATH_FILENAME + ID_TAG_MAX + 1); - - /* - ** Search for file/key pair in cache + + /* + ** Search for file/key pair in cache */ - + init_items = pj_search_initcache( name ); if( init_items != NULL ) { @@ -275,8 +275,8 @@ get_init(projCtx ctx, paralist **start, paralist *next, char *name, if (errno == 25) errno = 0; /* unknown problem with some sys errno<-25 */ - /* - ** If we seem to have gotten a result, insert it into the + /* + ** If we seem to have gotten a result, insert it into the ** init file cache. */ if( next != NULL && next != orig_next ) @@ -308,7 +308,7 @@ pj_init_plus_ctx( projCtx ctx, const char *definition ) char *defn_copy; int argc = 0, i, blank_count = 0; PJ *result = NULL; - + /* make a copy that we can manipulate */ defn_copy = (char *) pj_malloc( strlen(definition)+1 ); strcpy( defn_copy, definition ); @@ -328,13 +328,13 @@ pj_init_plus_ctx( projCtx ctx, const char *definition ) defn_copy[i - blank_count] = '\0'; blank_count = 0; } - + if( argc+1 == MAX_ARG ) { pj_ctx_set_errno( ctx, -44 ); goto bum_call; } - + argv[argc++] = defn_copy + i + 1; } break; @@ -455,7 +455,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { PIN->rone_es = 1./PIN->one_es; /* Now that we have ellipse information check for WGS84 datum */ - if( PIN->datum_type == PJD_3PARAM + if( PIN->datum_type == PJD_3PARAM && PIN->datum_params[0] == 0.0 && PIN->datum_params[1] == 0.0 && PIN->datum_params[2] == 0.0 @@ -464,7 +464,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { { PIN->datum_type = PJD_WGS84; } - + /* set PIN->geoc coordinate system */ PIN->geoc = (PIN->es && pj_param(ctx, start, "bgeoc").i); @@ -532,7 +532,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { /* set units */ s = 0; - if ((name = pj_param(ctx, start, "sunits").s) != NULL) { + if ((name = pj_param(ctx, start, "sunits").s) != NULL) { for (i = 0; (s = pj_units[i].id) && strcmp(name, s) ; ++i) ; if (!s) { pj_ctx_set_errno( ctx, -7 ); goto bum_call; } s = pj_units[i].to_meter; @@ -547,7 +547,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { /* set vertical units */ s = 0; - if ((name = pj_param(ctx, start, "svunits").s) != NULL) { + if ((name = pj_param(ctx, start, "svunits").s) != NULL) { for (i = 0; (s = pj_units[i].id) && strcmp(name, s) ; ++i) ; if (!s) { pj_ctx_set_errno( ctx, -7 ); goto bum_call; } s = pj_units[i].to_meter; @@ -564,7 +564,7 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { /* prime meridian */ s = 0; - if ((name = pj_param(ctx, start, "spm").s) != NULL) { + if ((name = pj_param(ctx, start, "spm").s) != NULL) { const char *value = NULL; char *next_str = NULL; @@ -576,8 +576,8 @@ pj_init_ctx(projCtx ctx, int argc, char **argv) { break; } } - - if( value == NULL + + if( value == NULL && (dmstor_ctx(ctx,name,&next_str) != 0.0 || *name == '0') && *next_str == '\0' ) value = name; @@ -639,3 +639,27 @@ pj_free(PJ *P) { P->pfree(P); } } + + + + + + + + +/************************************************************************/ +/* pj_prepare() */ +/* */ +/* Helper function for the PJ_xxxx functions providing the */ +/* projection specific setup for each projection type. */ +/* */ +/* Currently not used, but placed here as part of the material */ +/* Demonstrating the idea for a future PJ_xxx architecture */ +/* (cf. pj_minimal.c) */ +/* */ +/************************************************************************/ +void pj_prepare (PJ *P, const char *description, void (*freeup)(struct PJconsts *), size_t sizeof_struct_opaque) { + P->descr = description; + P->pfree = freeup; + P->opaque = pj_calloc (1, sizeof_struct_opaque); +} diff --git a/src/pj_inv.c b/src/pj_inv.c index d77b4e56..711a0005 100644 --- a/src/pj_inv.c +++ b/src/pj_inv.c @@ -20,7 +20,7 @@ pj_inv(XY xy, PJ *P) { xy.x = (xy.x * P->to_meter - P->x0) * P->ra; /* descale and de-offset */ xy.y = (xy.y * P->to_meter - P->y0) * P->ra; - //Check for NULL pointer + /* Check for NULL pointer */ if (P->inv != NULL) { lp = (*P->inv)(xy, P); /* inverse project */ diff --git a/src/pj_inv3d.c b/src/pj_inv3d.c index de35e776..d06ed080 100644 --- a/src/pj_inv3d.c +++ b/src/pj_inv3d.c @@ -19,9 +19,9 @@ pj_inv3d(XYZ xyz, PJ *P) { xyz.x = (xyz.x * P->to_meter - P->x0) * P->ra; /* descale and de-offset */ xyz.y = (xyz.y * P->to_meter - P->y0) * P->ra; - //z is not scaled since that is handled by vto_meter before we get here - - //Check for NULL pointer + /* z is not scaled since that is handled by vto_meter before we get here */ + + /* Check for NULL pointer */ if (P->inv3d != NULL) { lpz = (*P->inv3d)(xyz, P); /* inverse project */ @@ -32,7 +32,7 @@ pj_inv3d(XYZ xyz, PJ *P) { if (!P->over) lpz.lam = adjlon(lpz.lam); /* adjust longitude to CM */ - //This maybe redundant and never user + /* This may be redundant and never used */ if (P->geoc && fabs(fabs(lpz.phi)-HALFPI) > EPS) lpz.phi = atan(P->one_es * tan(lpz.phi)); } diff --git a/src/pj_latlong.c b/src/pj_latlong.c index b7a771a8..761eadc5 100644 --- a/src/pj_latlong.c +++ b/src/pj_latlong.c @@ -29,50 +29,94 @@ /* very loosely based upon DMA code by Bradford W. Drew */ #define PJ_LIB__ -#include <projects.h> +#include <projects.h> + PROJ_HEAD(lonlat, "Lat/long (Geodetic)") "\n\t"; PROJ_HEAD(latlon, "Lat/long (Geodetic alias)") "\n\t"; PROJ_HEAD(latlong, "Lat/long (Geodetic alias)") "\n\t"; PROJ_HEAD(longlat, "Lat/long (Geodetic alias)") "\n\t"; -FORWARD(forward); - xy.x = lp.lam / P->a; - xy.y = lp.phi / P->a; - return xy; + static XY forward(LP lp, PJ *P) { + XY xy = {0.0,0.0}; + xy.x = lp.lam / P->a; + xy.y = lp.phi / P->a; + return xy; +} + + +static LP inverse(XY xy, PJ *P) { + LP lp = {0.0,0.0}; + lp.phi = xy.y * P->a; + lp.lam = xy.x * P->a; + return lp; +} + + +static void *freeup_new (PJ *P) { + if (0==P) + return 0; + + return pj_dealloc(P); } -INVERSE(inverse); - lp.phi = xy.y * P->a; - lp.lam = xy.x * P->a; - return lp; +static void freeup (PJ *P) { + freeup_new (P); + return; } -FREEUP; if (P) pj_dalloc(P); } - -ENTRY0(latlong) - P->is_latlong = 1; - P->x0 = 0.0; - P->y0 = 0.0; - P->inv = inverse; P->fwd = forward; -ENDENTRY(P) - -ENTRY0(longlat) - P->is_latlong = 1; - P->x0 = 0.0; - P->y0 = 0.0; - P->inv = inverse; P->fwd = forward; -ENDENTRY(P) - -ENTRY0(latlon) - P->is_latlong = 1; - P->x0 = 0.0; - P->y0 = 0.0; - P->inv = inverse; P->fwd = forward; -ENDENTRY(P) - -ENTRY0(lonlat) - P->is_latlong = 1; - P->x0 = 0.0; - P->y0 = 0.0; - P->inv = inverse; P->fwd = forward; -ENDENTRY(P) + + +PJ *PROJECTION(latlong) { + P->is_latlong = 1; + P->x0 = 0.0; + P->y0 = 0.0; + P->inv = inverse; + P->fwd = forward; + + return P; +} + + +PJ *PROJECTION(longlat) { + P->is_latlong = 1; + P->x0 = 0.0; + P->y0 = 0.0; + P->inv = inverse; + P->fwd = forward; + + return P; +} + + +PJ *PROJECTION(latlon) { + P->is_latlong = 1; + P->x0 = 0.0; + P->y0 = 0.0; + P->inv = inverse; + P->fwd = forward; + + return P; +} + + +PJ *PROJECTION(lonlat) { + P->is_latlong = 1; + P->x0 = 0.0; + P->y0 = 0.0; + P->inv = inverse; P->fwd = forward; + + return P; +} + + +/* Bogus self-test functions. Self-tests can't be implemented the usual way for + * these "projections" since they can't be used directly from proj. + * We still need them though, as all projections are automatically added to + * the list of self-test functions. + * + * The code should be covered by the tests in nad/. + * */ +int pj_latlong_selftest (void) {return 0;} +int pj_longlat_selftest (void) {return 0;} +int pj_latlon_selftest (void) {return 0;} +int pj_lonlat_selftest (void) {return 0;} diff --git a/src/pj_list.c b/src/pj_list.c index bb56ade9..a8f171ab 100644 --- a/src/pj_list.c +++ b/src/pj_list.c @@ -1,11 +1,56 @@ /* Projection System: default list of projections ** Use local definition of PJ_LIST_H for subset. */ + #define USE_PJ_LIST_H 1 #include "projects.h" -struct PJ_LIST *pj_get_list_ref() -{ + +#define PASTE(a,b) a##b + +/* Generate prototypes for projection functions */ +#define PROJ_HEAD(id, name) struct PJconsts *pj_##id(struct PJconsts*); +#include "pj_list.h" +#undef PROJ_HEAD + +/* Generate prototypes for projection selftest functions */ +#define PROJ_HEAD(id, name) int PASTE(pj_##id, _selftest) (void); +#include "pj_list.h" +#undef PROJ_HEAD + + +/* Generate extern declarations for description strings */ +#define PROJ_HEAD(id, name) extern char * const pj_s_##id; +#include "pj_list.h" +#undef PROJ_HEAD + + +/* Generate the null-terminated list of projection functions with associated mnemonics and descriptions */ +#define PROJ_HEAD(id, name) {#id, pj_##id, &pj_s_##id}, +struct PJ_LIST pj_list[] = { +#include "pj_list.h" + {0, 0, 0}, + }; +#undef PROJ_HEAD + + +/* Generate the null-terminated list of projection selftest functions with associated mnemonics */ +#define PROJ_HEAD(id, name) {#id, PASTE(pj_##id, _selftest)}, +struct PJ_SELFTEST_LIST pj_selftest_list[] = { +#include "pj_list.h" + {0, 0}, + }; +#undef PROJ_HEAD +#undef PASTE + + +struct PJ_LIST *pj_get_list_ref (void) { return pj_list; } + + +struct PJ_SELFTEST_LIST *pj_get_selftest_list_ref (void) { + return pj_selftest_list; +} + diff --git a/src/pj_malloc.c b/src/pj_malloc.c index 80443a2b..aab69e99 100644 --- a/src/pj_malloc.c +++ b/src/pj_malloc.c @@ -9,19 +9,64 @@ pj_malloc(size_t size) { /* / Currently, pj_malloc is a hack to solve an errno problem. -/ The problem is described in more details at -/ https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=86420. -/ It seems, that pj_init and similar functions incorrectly -/ (under debian/glibs-2.3.2) assume that pj_malloc resets +/ The problem is described in more details at +/ https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=86420. +/ It seems, that pj_init and similar functions incorrectly +/ (under debian/glibs-2.3.2) assume that pj_malloc resets / errno after success. pj_malloc tries to mimic this. */ int old_errno = errno; - void *res = malloc(size); + void *res = malloc(size); if ( res && !old_errno ) - errno = 0; + errno = 0; return res; } void pj_dalloc(void *ptr) { free(ptr); } + + +/**********************************************************************/ +void *pj_calloc (size_t n, size_t size) { +/*********************************************************************** + +pj_calloc is the pj-equivalent of calloc(). + +It allocates space for an array of <n> elements of size <size>. +The array is initialized to zeros. + +***********************************************************************/ + void *res = pj_malloc (n*size); + if (0==res) + return 0; + memset (res, 0, n*size); + return res; +} + + +/**********************************************************************/ +void *pj_dealloc (void *ptr) { +/*********************************************************************** + +pj_dealloc supports the common use case of "clean up and return a null +pointer" to signal an error in a multi level allocation: + + struct foo { int bar; int *baz; }; + + struct foo *p = pj_calloc (1, sizeof (struct foo)); + if (0==p) + return 0; + + p->baz = pj_calloc (10, sizeof(int)); + if (0==p->baz) + return pj_dealloc (p); // clean up + signal error by 0-return + + return p; // success + +***********************************************************************/ + if (0==ptr) + return 0; + pj_dalloc (ptr); + return 0; +} diff --git a/src/pj_mutex.c b/src/pj_mutex.c index dc484c69..2f6533be 100644 --- a/src/pj_mutex.c +++ b/src/pj_mutex.c @@ -29,7 +29,7 @@ /* projects.h and windows.h conflict - avoid this! */ #if defined(MUTEX_pthread) && !defined(_XOPEN_SOURCE) -// For pthread_mutexattr_settype +/* For pthread_mutexattr_settype */ #define _XOPEN_SOURCE 500 #endif @@ -86,7 +86,7 @@ void pj_cleanup_lock() { } -#endif // def MUTEX_stub +#endif /* def MUTEX_stub */ /************************************************************************/ /* ==================================================================== */ @@ -154,7 +154,7 @@ void pj_cleanup_lock() { } -#endif // def MUTEX_pthread +#endif /* def MUTEX_pthread */ /************************************************************************/ /* ==================================================================== */ @@ -219,4 +219,4 @@ void pj_cleanup_lock() } } -#endif // def MUTEX_win32 +#endif /* def MUTEX_win32 */ diff --git a/src/pj_run_selftests.c b/src/pj_run_selftests.c new file mode 100644 index 00000000..cede4961 --- /dev/null +++ b/src/pj_run_selftests.c @@ -0,0 +1,78 @@ +/****************************************************************************** + * Project: PROJ.4 + * Purpose: Generic regression test for PROJ.4 projection algorithms. + * Author: Thomas Knudsen + * + ****************************************************************************** + * Copyright (c) 2016, Thomas Knudsen + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + *****************************************************************************/ + +#include <stdio.h> +#define PJ_LIB__ +#include <projects.h> + + +static void run_one_test (const char *mnemonic, int (testfunc)(void), int verbosity, int *n_ok, int *n_ko, int *n_stubs) { + int ret = testfunc (); + switch (ret) { + case 0: (*n_ok)++; break; + case 10000: (*n_stubs)++; break; + default: (*n_ko)++; + } + + if (verbosity) { + if (ret==10000) + printf ("Testing: %10s - [stub]\n", mnemonic); + else + printf ("Testing: %10s - return code: %d\n", mnemonic, ret); + } + return; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_run_selftests (int verbosity) { + printf ("This version of libproj is not configured for internal regression tests.\n"); + return 0; +} +#else + + +int pj_run_selftests (int verbosity) { + int n_ok = 0, n_ko = 0, n_stubs = 0, i = 0; + + struct PJ_SELFTEST_LIST *tests = pj_get_selftest_list_ref (); + + if (0==tests) + printf ("This version of libproj is not configured for internal regression tests.\n"); + + if (verbosity) + printf ("Running internal regression tests\n"); + + for (i = 0; tests[i].testfunc != 0; i++) + run_one_test (tests[i].id, tests[i].testfunc, verbosity, &n_ok, &n_ko, &n_stubs); + + if (0==verbosity) + printf ("Internal regression tests done. "); + printf ("[Stubs: %d] Total: %d. Failure: %d. Success: %d\n", n_stubs, n_ok+n_ko, n_ko, n_ok); + return n_ko; +} +#endif diff --git a/src/pj_transform.c b/src/pj_transform.c index 32f14955..ce7b96a3 100644 --- a/src/pj_transform.c +++ b/src/pj_transform.c @@ -1,6 +1,6 @@ /****************************************************************************** * Project: PROJ.4 - * Purpose: Perform overall coordinate system to coordinate system + * Purpose: Perform overall coordinate system to coordinate system * transformations (pj_transform() function) including reprojection * and datum shifting. * Author: Frank Warmerdam, warmerdam@pobox.com @@ -33,7 +33,7 @@ #include "geocent.h" static int pj_adjust_axis( projCtx ctx, const char *axis, int denormalize_flag, - long point_count, int point_offset, + long point_count, int point_offset, double *x, double *y, double *z ); #ifndef SRS_WGS84_SEMIMAJOR @@ -52,23 +52,23 @@ static int pj_adjust_axis( projCtx ctx, const char *axis, int denormalize_flag, #define Rz_BF (defn->datum_params[5]) #define M_BF (defn->datum_params[6]) -/* -** This table is intended to indicate for any given error code in +/* +** This table is intended to indicate for any given error code in ** the range 0 to -44, whether that error will occur for all locations (ie. ** it is a problem with the coordinate system as a whole) in which case the ** value would be 0, or if the problem is with the point being transformed -** in which case the value is 1. +** in which case the value is 1. ** ** At some point we might want to move this array in with the error message -** list or something, but while experimenting with it this should be fine. +** list or something, but while experimenting with it this should be fine. */ static const int transient_error[50] = { /* 0 1 2 3 4 5 6 7 8 9 */ - /* 0 to 9 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - /* 10 to 19 */ 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, - /* 20 to 29 */ 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, - /* 30 to 39 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 0 to 9 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 10 to 19 */ 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, + /* 20 to 29 */ 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, + /* 30 to 39 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 40 to 49 */ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 }; /************************************************************************/ @@ -101,7 +101,7 @@ int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, { int err; - err = pj_adjust_axis( srcdefn->ctx, srcdefn->axis, + err = pj_adjust_axis( srcdefn->ctx, srcdefn->axis, 0, point_count, point_offset, x, y, z ); if( err != 0 ) return err; @@ -140,7 +140,7 @@ int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, } err = pj_geocentric_to_geodetic( srcdefn->a_orig, srcdefn->es_orig, - point_count, point_offset, + point_count, point_offset, x, y, z ); if( err != 0 ) return err; @@ -153,19 +153,19 @@ int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, else if( !srcdefn->is_latlong ) { - //Check first if projection is invertible. + /* Check first if projection is invertible. */ if( (srcdefn->inv3d == NULL) && (srcdefn->inv == NULL)) { pj_ctx_set_errno( pj_get_ctx(srcdefn), -17 ); - pj_log( pj_get_ctx(srcdefn), PJ_LOG_ERROR, + pj_log( pj_get_ctx(srcdefn), PJ_LOG_ERROR, "pj_transform(): source projection not invertable" ); return -17; } - //If invertible - First try inv3d if defined + /* If invertible - First try inv3d if defined */ if (srcdefn->inv3d != NULL) { - //Three dimensions must be defined + /* Three dimensions must be defined */ if ( z == NULL) { pj_ctx_set_errno( pj_get_ctx(srcdefn), PJD_ERR_GEOCENTRIC); @@ -187,9 +187,9 @@ int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, geodetic_loc = pj_inv3d(projected_loc, srcdefn); if( srcdefn->ctx->last_errno != 0 ) { - if( (srcdefn->ctx->last_errno != 33 /*EDOM*/ + if( (srcdefn->ctx->last_errno != 33 /*EDOM*/ && srcdefn->ctx->last_errno != 34 /*ERANGE*/ ) - && (srcdefn->ctx->last_errno > 0 + && (srcdefn->ctx->last_errno > 0 || srcdefn->ctx->last_errno < -44 || point_count == 1 || transient_error[-srcdefn->ctx->last_errno] == 0 ) ) return srcdefn->ctx->last_errno; @@ -210,7 +210,7 @@ int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, } else { - //Fallback to the original PROJ.4 API 2d inversion- inv + /* Fallback to the original PROJ.4 API 2d inversion - inv */ for( i = 0; i < point_count; i++ ) { XY projected_loc; @@ -225,9 +225,9 @@ int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, geodetic_loc = pj_inv( projected_loc, srcdefn ); if( srcdefn->ctx->last_errno != 0 ) { - if( (srcdefn->ctx->last_errno != 33 /*EDOM*/ + if( (srcdefn->ctx->last_errno != 33 /*EDOM*/ && srcdefn->ctx->last_errno != 34 /*ERANGE*/ ) - && (srcdefn->ctx->last_errno > 0 + && (srcdefn->ctx->last_errno > 0 || srcdefn->ctx->last_errno < -44 || point_count == 1 || transient_error[-srcdefn->ctx->last_errno] == 0 ) ) return srcdefn->ctx->last_errno; @@ -262,17 +262,17 @@ int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, /* -------------------------------------------------------------------- */ if( srcdefn->has_geoid_vgrids && z != NULL ) { - if( pj_apply_vgridshift( srcdefn, "sgeoidgrids", - &(srcdefn->vgridlist_geoid), + if( pj_apply_vgridshift( srcdefn, "sgeoidgrids", + &(srcdefn->vgridlist_geoid), &(srcdefn->vgridlist_geoid_count), 0, point_count, point_offset, x, y, z ) != 0 ) return pj_ctx_get_errno(srcdefn->ctx); } - + /* -------------------------------------------------------------------- */ /* Convert datums if needed, and possible. */ /* -------------------------------------------------------------------- */ - if( pj_datum_transform( srcdefn, dstdefn, point_count, point_offset, + if( pj_datum_transform( srcdefn, dstdefn, point_count, point_offset, x, y, z ) != 0 ) { if( srcdefn->ctx->last_errno != 0 ) @@ -287,13 +287,13 @@ int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, /* -------------------------------------------------------------------- */ if( dstdefn->has_geoid_vgrids && z != NULL ) { - if( pj_apply_vgridshift( dstdefn, "sgeoidgrids", - &(dstdefn->vgridlist_geoid), + if( pj_apply_vgridshift( dstdefn, "sgeoidgrids", + &(dstdefn->vgridlist_geoid), &(dstdefn->vgridlist_geoid_count), 1, point_count, point_offset, x, y, z ) != 0 ) return dstdefn->ctx->last_errno; } - + /* -------------------------------------------------------------------- */ /* But if they are staying lat long, adjust for the prime */ /* meridian if there is one in effect. */ @@ -348,7 +348,7 @@ int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, { XYZ projected_loc; LPZ geodetic_loc; - + geodetic_loc.u = x[point_offset*i]; geodetic_loc.v = y[point_offset*i]; geodetic_loc.w = z[point_offset*i]; @@ -359,9 +359,9 @@ int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, projected_loc = pj_fwd3d( geodetic_loc, dstdefn); if( dstdefn->ctx->last_errno != 0 ) { - if( (dstdefn->ctx->last_errno != 33 /*EDOM*/ + if( (dstdefn->ctx->last_errno != 33 /*EDOM*/ && dstdefn->ctx->last_errno != 34 /*ERANGE*/ ) - && (dstdefn->ctx->last_errno > 0 + && (dstdefn->ctx->last_errno > 0 || dstdefn->ctx->last_errno < -44 || point_count == 1 || transient_error[-dstdefn->ctx->last_errno] == 0 ) ) return dstdefn->ctx->last_errno; @@ -371,7 +371,7 @@ int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, projected_loc.v = HUGE_VAL; projected_loc.w = HUGE_VAL; } - } + } x[point_offset*i] = projected_loc.u; y[point_offset*i] = projected_loc.v; @@ -395,9 +395,9 @@ int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, projected_loc = pj_fwd( geodetic_loc, dstdefn ); if( dstdefn->ctx->last_errno != 0 ) { - if( (dstdefn->ctx->last_errno != 33 /*EDOM*/ + if( (dstdefn->ctx->last_errno != 33 /*EDOM*/ && dstdefn->ctx->last_errno != 34 /*ERANGE*/ ) - && (dstdefn->ctx->last_errno > 0 + && (dstdefn->ctx->last_errno > 0 || dstdefn->ctx->last_errno < -44 || point_count == 1 || transient_error[-dstdefn->ctx->last_errno] == 0 ) ) return dstdefn->ctx->last_errno; @@ -406,7 +406,7 @@ int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, projected_loc.u = HUGE_VAL; projected_loc.v = HUGE_VAL; } - } + } x[point_offset*i] = projected_loc.u; y[point_offset*i] = projected_loc.v; @@ -449,7 +449,7 @@ int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, { int err; - err = pj_adjust_axis( dstdefn->ctx, dstdefn->axis, + err = pj_adjust_axis( dstdefn->ctx, dstdefn->axis, 1, point_count, point_offset, x, y, z ); if( err != 0 ) return err; @@ -462,7 +462,7 @@ int pj_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, /* pj_geodetic_to_geocentric() */ /************************************************************************/ -int pj_geodetic_to_geocentric( double a, double es, +int pj_geodetic_to_geocentric( double a, double es, long point_count, int point_offset, double *x, double *y, double *z ) @@ -489,7 +489,7 @@ int pj_geodetic_to_geocentric( double a, double es, if( x[io] == HUGE_VAL ) continue; - if( pj_Convert_Geodetic_To_Geocentric( &gi, y[io], x[io], z[io], + if( pj_Convert_Geodetic_To_Geocentric( &gi, y[io], x[io], z[io], x+io, y+io, z+io ) != 0 ) { ret_errno = -14; @@ -505,7 +505,7 @@ int pj_geodetic_to_geocentric( double a, double es, /* pj_geodetic_to_geocentric() */ /************************************************************************/ -int pj_geocentric_to_geodetic( double a, double es, +int pj_geocentric_to_geodetic( double a, double es, long point_count, int point_offset, double *x, double *y, double *z ) @@ -531,7 +531,7 @@ int pj_geocentric_to_geodetic( double a, double es, if( x[io] == HUGE_VAL ) continue; - pj_Convert_Geocentric_To_Geodetic( &gi, x[io], y[io], z[io], + pj_Convert_Geocentric_To_Geodetic( &gi, x[io], y[io], z[io], y+io, x+io, z+io ); } @@ -552,7 +552,7 @@ int pj_compare_datums( PJ *srcdefn, PJ *dstdefn ) { return 0; } - else if( srcdefn->a_orig != dstdefn->a_orig + else if( srcdefn->a_orig != dstdefn->a_orig || ABS(srcdefn->es_orig - dstdefn->es_orig) > 0.000000000050 ) { /* the tolerence for es is to ensure that GRS80 and WGS84 are @@ -588,7 +588,7 @@ int pj_compare_datums( PJ *srcdefn, PJ *dstdefn ) /* pj_geocentic_to_wgs84() */ /************************************************************************/ -int pj_geocentric_to_wgs84( PJ *defn, +int pj_geocentric_to_wgs84( PJ *defn, long point_count, int point_offset, double *x, double *y, double *z ) @@ -600,7 +600,7 @@ int pj_geocentric_to_wgs84( PJ *defn, for( i = 0; i < point_count; i++ ) { long io = i * point_offset; - + if( x[io] == HUGE_VAL ) continue; @@ -636,7 +636,7 @@ int pj_geocentric_to_wgs84( PJ *defn, /* pj_geocentic_from_wgs84() */ /************************************************************************/ -int pj_geocentric_from_wgs84( PJ *defn, +int pj_geocentric_from_wgs84( PJ *defn, long point_count, int point_offset, double *x, double *y, double *z ) @@ -651,7 +651,7 @@ int pj_geocentric_from_wgs84( PJ *defn, if( x[io] == HUGE_VAL ) continue; - + x[io] = x[io] - Dx_BF; y[io] = y[io] - Dy_BF; z[io] = z[io] - Dz_BF; @@ -688,7 +688,7 @@ int pj_geocentric_from_wgs84( PJ *defn, /* coordinates in radians in the destination datum. */ /************************************************************************/ -int pj_datum_transform( PJ *srcdefn, PJ *dstdefn, +int pj_datum_transform( PJ *srcdefn, PJ *dstdefn, long point_count, int point_offset, double *x, double *y, double *z ) @@ -754,15 +754,15 @@ int pj_datum_transform( PJ *srcdefn, PJ *dstdefn, /* Do we need to go through geocentric coordinates? */ /* ==================================================================== */ if( src_es != dst_es || src_a != dst_a - || srcdefn->datum_type == PJD_3PARAM + || srcdefn->datum_type == PJD_3PARAM || srcdefn->datum_type == PJD_7PARAM - || dstdefn->datum_type == PJD_3PARAM + || dstdefn->datum_type == PJD_3PARAM || dstdefn->datum_type == PJD_7PARAM) { /* -------------------------------------------------------------------- */ /* Convert to geocentric coordinates. */ /* -------------------------------------------------------------------- */ - srcdefn->ctx->last_errno = + srcdefn->ctx->last_errno = pj_geodetic_to_geocentric( src_a, src_es, point_count, point_offset, x, y, z ); CHECK_RETURN(srcdefn); @@ -770,14 +770,14 @@ int pj_datum_transform( PJ *srcdefn, PJ *dstdefn, /* -------------------------------------------------------------------- */ /* Convert between datums. */ /* -------------------------------------------------------------------- */ - if( srcdefn->datum_type == PJD_3PARAM + if( srcdefn->datum_type == PJD_3PARAM || srcdefn->datum_type == PJD_7PARAM ) { pj_geocentric_to_wgs84( srcdefn, point_count, point_offset,x,y,z); CHECK_RETURN(srcdefn); } - if( dstdefn->datum_type == PJD_3PARAM + if( dstdefn->datum_type == PJD_3PARAM || dstdefn->datum_type == PJD_7PARAM ) { pj_geocentric_from_wgs84( dstdefn, point_count,point_offset,x,y,z); @@ -787,7 +787,7 @@ int pj_datum_transform( PJ *srcdefn, PJ *dstdefn, /* -------------------------------------------------------------------- */ /* Convert back to geodetic coordinates. */ /* -------------------------------------------------------------------- */ - dstdefn->ctx->last_errno = + dstdefn->ctx->last_errno = pj_geocentric_to_geodetic( dst_a, dst_es, point_count, point_offset, x, y, z ); CHECK_RETURN(dstdefn); @@ -814,9 +814,9 @@ int pj_datum_transform( PJ *srcdefn, PJ *dstdefn, /* Normalize or de-normalized the x/y/z axes. The normal form */ /* is "enu" (easting, northing, up). */ /************************************************************************/ -static int pj_adjust_axis( projCtx ctx, +static int pj_adjust_axis( projCtx ctx, const char *axis, int denormalize_flag, - long point_count, int point_offset, + long point_count, int point_offset, double *x, double *y, double *z ) { @@ -831,7 +831,7 @@ static int pj_adjust_axis( projCtx ctx, y_in = y[point_offset*i]; if( z ) z_in = z[point_offset*i]; - + for( i_axis = 0; i_axis < 3; i_axis++ ) { double value; @@ -842,7 +842,7 @@ static int pj_adjust_axis( projCtx ctx, value = y_in; else value = z_in; - + switch( axis[i_axis] ) { case 'e': @@ -873,7 +873,7 @@ static int pj_adjust_axis( projCtx ctx, y_in = y[point_offset*i]; if( z ) z_in = z[point_offset*i]; - + for( i_axis = 0; i_axis < 3; i_axis++ ) { double *target; @@ -887,7 +887,7 @@ static int pj_adjust_axis( projCtx ctx, target = y; else target = z; - + switch( axis[i_axis] ) { case 'e': @@ -909,7 +909,6 @@ static int pj_adjust_axis( projCtx ctx, } /* i_axis */ } /* i (point) */ } - + return 0; } - diff --git a/src/pj_utils.c b/src/pj_utils.c index f11081fc..203c637c 100644 --- a/src/pj_utils.c +++ b/src/pj_utils.c @@ -78,27 +78,27 @@ PJ *pj_latlong_from_proj( PJ *pj_in ) if( pj_param(pj_in->ctx, pj_in->params, "tdatum").i ) { got_datum = TRUE; - sprintf( defn+strlen(defn), " +datum=%s", + sprintf( defn+strlen(defn), " +datum=%s", pj_param(pj_in->ctx, pj_in->params,"sdatum").s ); } else if( pj_param(pj_in->ctx, pj_in->params, "tellps").i ) { - sprintf( defn+strlen(defn), " +ellps=%s", + sprintf( defn+strlen(defn), " +ellps=%s", pj_param(pj_in->ctx, pj_in->params,"sellps").s ); } else if( pj_param(pj_in->ctx,pj_in->params, "ta").i ) { - sprintf( defn+strlen(defn), " +a=%s", + sprintf( defn+strlen(defn), " +a=%s", pj_param(pj_in->ctx,pj_in->params,"sa").s ); - + if( pj_param(pj_in->ctx,pj_in->params, "tb").i ) - sprintf( defn+strlen(defn), " +b=%s", + sprintf( defn+strlen(defn), " +b=%s", pj_param(pj_in->ctx,pj_in->params,"sb").s ); else if( pj_param(pj_in->ctx,pj_in->params, "tes").i ) - sprintf( defn+strlen(defn), " +es=%s", + sprintf( defn+strlen(defn), " +es=%s", pj_param(pj_in->ctx,pj_in->params,"ses").s ); else if( pj_param(pj_in->ctx,pj_in->params, "tf").i ) - sprintf( defn+strlen(defn), " +f=%s", + sprintf( defn+strlen(defn), " +f=%s", pj_param(pj_in->ctx,pj_in->params,"sf").s ); else { @@ -121,17 +121,17 @@ PJ *pj_latlong_from_proj( PJ *pj_in ) if( !got_datum ) { if( pj_param(pj_in->ctx,pj_in->params, "ttowgs84").i ) - sprintf( defn+strlen(defn), " +towgs84=%s", + sprintf( defn+strlen(defn), " +towgs84=%s", pj_param(pj_in->ctx,pj_in->params,"stowgs84").s ); if( pj_param(pj_in->ctx,pj_in->params, "tnadgrids").i ) - sprintf( defn+strlen(defn), " +nadgrids=%s", + sprintf( defn+strlen(defn), " +nadgrids=%s", pj_param(pj_in->ctx,pj_in->params,"snadgrids").s ); } /* copy over some other information related to ellipsoid */ if( pj_param(pj_in->ctx,pj_in->params, "tR").i ) - sprintf( defn+strlen(defn), " +R=%s", + sprintf( defn+strlen(defn), " +R=%s", pj_param(pj_in->ctx,pj_in->params,"sR").s ); if( pj_param(pj_in->ctx,pj_in->params, "tR_A").i ) @@ -144,16 +144,16 @@ PJ *pj_latlong_from_proj( PJ *pj_in ) sprintf( defn+strlen(defn), " +R_a" ); if( pj_param(pj_in->ctx,pj_in->params, "tR_lat_a").i ) - sprintf( defn+strlen(defn), " +R_lat_a=%s", + sprintf( defn+strlen(defn), " +R_lat_a=%s", pj_param(pj_in->ctx,pj_in->params,"sR_lat_a").s ); if( pj_param(pj_in->ctx,pj_in->params, "tR_lat_g").i ) - sprintf( defn+strlen(defn), " +R_lat_g=%s", + sprintf( defn+strlen(defn), " +R_lat_g=%s", pj_param(pj_in->ctx,pj_in->params,"sR_lat_g").s ); /* copy over prime meridian */ if( pj_param(pj_in->ctx,pj_in->params, "tpm").i ) - sprintf( defn+strlen(defn), " +pm=%s", + sprintf( defn+strlen(defn), " +pm=%s", pj_param(pj_in->ctx,pj_in->params,"spm").s ); return pj_init_plus_ctx( pj_in->ctx, defn ); @@ -172,7 +172,7 @@ void pj_get_spheroid_defn(projPJ defn, double *major_axis, double *eccentricity_ { if ( major_axis ) *major_axis = defn->a; - + if ( eccentricity_squared ) *eccentricity_squared = defn->es; -}; +} @@ -42,7 +42,7 @@ postscale = 0; *oform = (char *)0, /* output format for x-y or decimal degrees */ *oterr = "*\t*", /* output line for unprojectable input */ *usage = -"%s\nusage: %s [ -beEfiIlormsStTvVwW [args] ] [ +opts[=arg] ] [ files ]\n"; +"%s\nusage: %s [ -bCeEfiIlormsStTvVwW [args] ] [ +opts[=arg] ] [ files ]\n"; static struct FACTORS facs; static double @@ -158,7 +158,7 @@ vprocess(FILE *fid) { if (!oform) oform = "%.3f"; if (bin_in || bin_out) - emess(1,"binary I/O not available in -V option"); + emess(1,"binary I/O not available in -V option"); for (;;) { ++emess_dat.File_line; if (!(s = fgets(line, MAX_LINE, fid))) @@ -267,6 +267,10 @@ int main(int argc, char **argv) { case 'b': /* binary I/O */ bin_in = bin_out = 1; continue; + case 'C': /* Check - run internal regression tests */ + pj_run_selftests (very_verby); + return 0; + continue; case 'v': /* monitor dump of initialization */ mon = 1; continue; @@ -300,8 +304,8 @@ int main(int argc, char **argv) { char *str; for (lp = pj_get_list_ref() ; lp->id ; ++lp) { - if( strcmp(lp->id,"latlong") == 0 - || strcmp(lp->id,"longlat") == 0 + if( strcmp(lp->id,"latlong") == 0 + || strcmp(lp->id,"longlat") == 0 || strcmp(lp->id,"geocent") == 0 ) continue; @@ -353,7 +357,7 @@ int main(int argc, char **argv) { continue; /* artificial */ case 'e': /* error line alternative */ if (--argc <= 0) - noargument: + noargument: emess(1,"missing argument for -%c",*arg); oterr = *++argv; continue; @@ -364,7 +368,7 @@ int main(int argc, char **argv) { case 'm': /* cartesian multiplier */ if (--argc <= 0) goto noargument; postscale = 1; - if (!strncmp("1/",*++argv,2) || + if (!strncmp("1/",*++argv,2) || !strncmp("1:",*argv,2)) { if((fscale = atof((*argv)+2)) == 0.) goto badscale; diff --git a/src/proj.def b/src/proj.def index c7708cc5..1f228a6c 100644 --- a/src/proj.def +++ b/src/proj.def @@ -1,90 +1,91 @@ -VERSION 1.2 +VERSION 1.2 EXPORTS - pj_init @1 - pj_fwd @2 - pj_inv @3 - pj_free @4 - pj_transform @5 - pj_geocentric_to_geodetic @6 - pj_geodetic_to_geocentric @7 - pj_deallocate_grids @8 - pj_init_plus @9 - pj_latlong_from_proj @10 - pj_is_latlong @11 - pj_get_errno_ref @12 - pj_set_finder @13 - pj_strerrno @14 - pj_errno @15 - pj_get_def @16 - pj_dalloc @17 - pj_is_geocent @18 - pj_get_release @19 - pj_malloc @20 - pj_pr_list @21 - pj_compare_datums @22 - pj_apply_gridshift @23 - pj_datum_transform @24 - pj_set_searchpath @25 - dmstor @26 - pj_get_ellps_ref @27 - pj_get_datums_ref @28 - pj_get_units_ref @29 - pj_get_list_ref @30 - pj_get_prime_meridians_ref @31 - rtodms @32 - set_rtodms @33 - pj_factors @34 - mk_cheby @35 - adjlon @36 - pj_param @37 - pj_ell_set @38 - pj_mkparam @39 - pj_init_ctx @40 - pj_init_plus_ctx @41 - pj_get_default_ctx @42 - pj_get_ctx @43 - pj_set_ctx @44 - pj_ctx_alloc @45 - pj_ctx_free @46 - pj_ctx_get_errno @47 - pj_ctx_set_errno @48 - pj_ctx_set_debug @49 - pj_ctx_set_logger @50 - pj_ctx_set_app_data @51 - pj_ctx_get_app_data @52 - pj_log @53 - pj_clear_initcache @54 - geod_init @55 - geod_lineinit @56 - geod_genposition @57 - geod_position @58 - geod_gendirect @59 - geod_direct @60 - geod_geninverse @61 - geod_inverse @62 - geod_polygonarea @63 - pj_get_spheroid_defn @64 - pj_get_default_fileapi @65 - pj_ctx_set_fileapi @66 - pj_ctx_get_fileapi @67 - pj_ctx_fopen @68 - pj_ctx_fread @69 - pj_ctx_ftell @70 - pj_ctx_fclose @71 - pj_open_lib @72 - pj_atof @73 - pj_strtod @74 - pj_fwd3d @75 - pj_inv3d @76 - geod_gensetdistance @77 - geod_setdistance @78 - geod_gendirectline @79 - geod_directline @80 - geod_inverseline @81 - geod_polygon_init @82 - geod_polygon_addedge @83 - geod_polygon_addpoint @84 - geod_polygon_compute @85 - geod_polygon_testedge @86 - geod_polygon_testpoint @87 - geod_polygon_clear @88 + pj_init @1 + pj_fwd @2 + pj_inv @3 + pj_free @4 + pj_transform @5 + pj_geocentric_to_geodetic @6 + pj_geodetic_to_geocentric @7 + pj_deallocate_grids @8 + pj_init_plus @9 + pj_latlong_from_proj @10 + pj_is_latlong @11 + pj_get_errno_ref @12 + pj_set_finder @13 + pj_strerrno @14 + pj_errno @15 + pj_get_def @16 + pj_dalloc @17 + pj_is_geocent @18 + pj_get_release @19 + pj_malloc @20 + pj_pr_list @21 + pj_compare_datums @22 + pj_apply_gridshift @23 + pj_datum_transform @24 + pj_set_searchpath @25 + dmstor @26 + pj_get_ellps_ref @27 + pj_get_datums_ref @28 + pj_get_units_ref @29 + pj_get_list_ref @30 + pj_get_prime_meridians_ref @31 + rtodms @32 + set_rtodms @33 + pj_factors @34 + mk_cheby @35 + adjlon @36 + pj_param @37 + pj_ell_set @38 + pj_mkparam @39 + pj_init_ctx @40 + pj_init_plus_ctx @41 + pj_get_default_ctx @42 + pj_get_ctx @43 + pj_set_ctx @44 + pj_ctx_alloc @45 + pj_ctx_free @46 + pj_ctx_get_errno @47 + pj_ctx_set_errno @48 + pj_ctx_set_debug @49 + pj_ctx_set_logger @50 + pj_ctx_set_app_data @51 + pj_ctx_get_app_data @52 + pj_log @53 + pj_clear_initcache @54 + geod_init @55 + geod_lineinit @56 + geod_genposition @57 + geod_position @58 + geod_gendirect @59 + geod_direct @60 + geod_geninverse @61 + geod_inverse @62 + geod_polygonarea @63 + pj_get_spheroid_defn @64 + pj_get_default_fileapi @65 + pj_ctx_set_fileapi @66 + pj_ctx_get_fileapi @67 + pj_ctx_fopen @68 + pj_ctx_fread @69 + pj_ctx_ftell @70 + pj_ctx_fclose @71 + pj_open_lib @72 + pj_atof @73 + pj_strtod @74 + pj_fwd3d @75 + pj_inv3d @76 + geod_gensetdistance @77 + geod_setdistance @78 + geod_gendirectline @79 + geod_directline @80 + geod_inverseline @81 + geod_polygon_init @82 + geod_polygon_addedge @83 + geod_polygon_addpoint @84 + geod_polygon_compute @85 + geod_polygon_testedge @86 + geod_polygon_testpoint @87 + geod_polygon_clear @88 + pj_run_selftests @89 diff --git a/src/proj_api.h b/src/proj_api.h index b482c0f5..24a6f053 100644 --- a/src/proj_api.h +++ b/src/proj_api.h @@ -130,6 +130,8 @@ char *pj_get_def(projPJ, int); projPJ pj_latlong_from_proj( projPJ ); void *pj_malloc(size_t); void pj_dalloc(void *); +void *pj_calloc (size_t n, size_t size); +void *pj_dealloc (void *ptr); char *pj_strerrno(int); int *pj_get_errno_ref(void); const char *pj_get_release(void); @@ -166,6 +168,9 @@ char *pj_ctx_fgets(projCtx ctx, char *line, int size, PAFile file); PAFile pj_open_lib(projCtx, const char *, const char *); +int pj_run_selftests (int verbosity); + + #define PJ_LOG_NONE 0 #define PJ_LOG_ERROR 1 #define PJ_LOG_DEBUG_MAJOR 2 diff --git a/src/proj_etmerc.c b/src/proj_etmerc.c index 3ef0903f..a88a0837 100644 --- a/src/proj_etmerc.c +++ b/src/proj_etmerc.c @@ -35,20 +35,24 @@ * * Modified and used here by permission of Reference Networks * Division, Kort og Matrikelstyrelsen (KMS), Copenhagen, Denmark + * */ -#define PROJ_PARMS__ \ + +#define PROJ_LIB__ +#define PJ_LIB__ + +#include <projects.h> + + +struct pj_opaque { double Qn; /* Merid. quad., scaled to the projection */ \ double Zb; /* Radius vector in polar coord. systems */ \ double cgb[6]; /* Constants for Gauss -> Geo lat */ \ double cbg[6]; /* Constants for Geo lat -> Gauss */ \ double utg[6]; /* Constants for transv. merc. -> geo */ \ double gtu[6]; /* Constants for geo -> transv. merc. */ - -#define PROJ_LIB__ -#define PJ_LIB__ - -#include <projects.h> +}; PROJ_HEAD(etmerc, "Extended Transverse Mercator") "\n\tCyl, Sph\n\tlat_ts=(0)\nlat_0=(0)"; @@ -57,11 +61,11 @@ PROJ_HEAD(utm, "Universal Transverse Mercator (UTM)") #define PROJ_ETMERC_ORDER 6 + #ifdef _GNU_SOURCE inline #endif - static double -log1py(double x) { /* Compute log(1+x) accurately */ +static double log1py(double x) { /* Compute log(1+x) accurately */ volatile double y = 1 + x, z = y - 1; @@ -72,21 +76,21 @@ log1py(double x) { /* Compute log(1+x) accurately */ return z == 0 ? x : x * log(y) / z; } + #ifdef _GNU_SOURCE inline #endif - static double -asinhy(double x) { /* Compute asinh(x) accurately */ +static double asinhy(double x) { /* Compute asinh(x) accurately */ double y = fabs(x); /* Enforce odd parity */ y = log1py(y * (1 + y/(hypot(1.0, y) + 1))); return x < 0 ? -y : y; } + #ifdef _GNU_SOURCE inline #endif - static double -gatg(double *p1, int len_p1, double B) { +static double gatg(double *p1, int len_p1, double B) { double *p; double h = 0, h1, h2 = 0, cos_2B; @@ -96,11 +100,11 @@ gatg(double *p1, int len_p1, double B) { return (B + h*sin(2*B)); } +/* Complex Clenshaw summation */ #ifdef _GNU_SOURCE inline #endif - static double -clenS(double *a, int size, double arg_r, double arg_i, double *R, double *I) { +static double clenS(double *a, int size, double arg_r, double arg_i, double *R, double *I) { double *p, r, i, hr, hr1, hr2, hi, hi1, hi2; double sin_arg_r, cos_arg_r, sinh_arg_i, cosh_arg_i; @@ -116,6 +120,7 @@ clenS(double *a, int size, double arg_r, double arg_i, double *R, double *I) { cosh_arg_i = cosh(arg_i); r = 2*cos_arg_r*cosh_arg_i; i = -2*sin_arg_r*sinh_arg_i; + /* summation loop */ for (hi1 = hr1 = hi = 0, hr = *--p; a - p;) { hr2 = hr1; @@ -125,100 +130,132 @@ clenS(double *a, int size, double arg_r, double arg_i, double *R, double *I) { hr = -hr2 + r*hr1 - i*hi1 + *--p; hi = -hi2 + i*hr1 + r*hi1; } + r = sin_arg_r*cosh_arg_i; i = cos_arg_r*sinh_arg_i; *R = r*hr - i*hi; *I = r*hi + i*hr; - return(*R); + return *R; } - static double -clens(double *a, int size, double arg_r) { + + +/* Real Clenshaw summation */ +static double clens(double *a, int size, double arg_r) { double *p, r, hr, hr1, hr2, cos_arg_r; p = a + size; cos_arg_r = cos(arg_r); r = 2*cos_arg_r; + /* summation loop */ for (hr1 = 0, hr = *--p; a - p;) { hr2 = hr1; hr1 = hr; hr = -hr2 + r*hr1 + *--p; } - return(sin(arg_r)*hr); + return sin (arg_r)*hr; } -FORWARD(e_forward); /* ellipsoid */ + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; double sin_Cn, cos_Cn, cos_Ce, sin_Ce, dCn, dCe; double Cn = lp.phi, Ce = lp.lam; /* ell. LAT, LNG -> Gaussian LAT, LNG */ - Cn = gatg(P->cbg, PROJ_ETMERC_ORDER, Cn); + Cn = gatg (Q->cbg, PROJ_ETMERC_ORDER, Cn); /* Gaussian LAT, LNG -> compl. sph. LAT */ #ifdef _GNU_SOURCE - sincos(Cn, &sin_Cn, &cos_Cn); - sincos(Ce, &sin_Ce, &cos_Ce); + sincos (Cn, &sin_Cn, &cos_Cn); + sincos (Ce, &sin_Ce, &cos_Ce); #else - sin_Cn = sin(Cn); - cos_Cn = cos(Cn); - sin_Ce = sin(Ce); - cos_Ce = cos(Ce); + sin_Cn = sin (Cn); + cos_Cn = cos (Cn); + sin_Ce = sin (Ce); + cos_Ce = cos (Ce); #endif - Cn = atan2(sin_Cn, cos_Ce*cos_Cn); - Ce = atan2(sin_Ce*cos_Cn, hypot(sin_Cn, cos_Cn*cos_Ce)); + Cn = atan2 (sin_Cn, cos_Ce*cos_Cn); + Ce = atan2 (sin_Ce*cos_Cn, hypot (sin_Cn, cos_Cn*cos_Ce)); + /* compl. sph. N, E -> ell. norm. N, E */ - Ce = asinhy(tan(Ce)); /* Replaces: Ce = log(tan(FORTPI + Ce*0.5)); */ - Cn += clenS(P->gtu, PROJ_ETMERC_ORDER, 2*Cn, 2*Ce, &dCn, &dCe); + Ce = asinhy ( tan (Ce) ); /* Replaces: Ce = log(tan(FORTPI + Ce*0.5)); */ + Cn += clenS (Q->gtu, PROJ_ETMERC_ORDER, 2*Cn, 2*Ce, &dCn, &dCe); Ce += dCe; - if (fabs(Ce) <= 2.623395162778) { - xy.y = P->Qn * Cn + P->Zb; /* Northing */ - xy.x = P->Qn * Ce; /* Easting */ + if (fabs (Ce) <= 2.623395162778) { + xy.y = Q->Qn * Cn + Q->Zb; /* Northing */ + xy.x = Q->Qn * Ce; /* Easting */ } else xy.x = xy.y = HUGE_VAL; - return (xy); + return xy; } -INVERSE(e_inverse); /* ellipsoid */ + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; double sin_Cn, cos_Cn, cos_Ce, sin_Ce, dCn, dCe; double Cn = xy.y, Ce = xy.x; /* normalize N, E */ - Cn = (Cn - P->Zb)/P->Qn; - Ce = Ce/P->Qn; + Cn = (Cn - Q->Zb)/Q->Qn; + Ce = Ce/Q->Qn; + if (fabs(Ce) <= 2.623395162778) { /* 150 degrees */ - /* norm. N, E -> compl. sph. LAT, LNG */ - Cn += clenS(P->utg, PROJ_ETMERC_ORDER, 2*Cn, 2*Ce, &dCn, &dCe); + /* norm. N, E -> compl. sph. LAT, LNG */ + Cn += clenS(Q->utg, PROJ_ETMERC_ORDER, 2*Cn, 2*Ce, &dCn, &dCe); Ce += dCe; - Ce = atan(sinh(Ce)); /* Replaces: Ce = 2*(atan(exp(Ce)) - FORTPI); */ + Ce = atan (sinh (Ce)); /* Replaces: Ce = 2*(atan(exp(Ce)) - FORTPI); */ /* compl. sph. LAT -> Gaussian LAT, LNG */ #ifdef _GNU_SOURCE - sincos(Cn, &sin_Cn, &cos_Cn); - sincos(Ce, &sin_Ce, &cos_Ce); + sincos (Cn, &sin_Cn, &cos_Cn); + sincos (Ce, &sin_Ce, &cos_Ce); #else - sin_Cn = sin(Cn); - cos_Cn = cos(Cn); - sin_Ce = sin(Ce); - cos_Ce = cos(Ce); + sin_Cn = sin (Cn); + cos_Cn = cos (Cn); + sin_Ce = sin (Ce); + cos_Ce = cos (Ce); #endif - Ce = atan2(sin_Ce, cos_Ce*cos_Cn); - Cn = atan2(sin_Cn*cos_Ce, hypot(sin_Ce, cos_Ce*cos_Cn)); + Ce = atan2 (sin_Ce, cos_Ce*cos_Cn); + Cn = atan2 (sin_Cn*cos_Ce, hypot (sin_Ce, cos_Ce*cos_Cn)); /* Gaussian LAT, LNG -> ell. LAT, LNG */ - lp.phi = gatg(P->cgb, PROJ_ETMERC_ORDER, Cn); + lp.phi = gatg (Q->cgb, PROJ_ETMERC_ORDER, Cn); lp.lam = Ce; } else lp.phi = lp.lam = HUGE_VAL; - return (lp); + return lp; } -FREEUP; if (P) free(P); } - static PJ * -setup(PJ *P) { /* general initialization */ + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + +static PJ *setup(PJ *P) { /* general initialization */ double f, n, np, Z; + struct pj_opaque *Q = P->opaque; + + if (P->es <= 0) + E_ERROR(-34); + + /* flattening */ + f = P->es / (1 + sqrt (1 - P->es)); /* Replaces: f = 1 - sqrt(1-P->es); */ - if (P->es <= 0) E_ERROR(-34); - f = P->es / (1 + sqrt(1 - P->es)); /* Replaces: f = 1 - sqrt(1-P->es); */ /* third flattening */ np = n = f/(2 - f); @@ -226,93 +263,217 @@ setup(PJ *P) { /* general initialization */ /* cgb := Gaussian -> Geodetic, KW p190 - 191 (61) - (62) */ /* cbg := Geodetic -> Gaussian, KW p186 - 187 (51) - (52) */ /* PROJ_ETMERC_ORDER = 6th degree : Engsager and Poder: ICC2007 */ - P->cgb[0] = n*( 2 + n*(-2/3.0 + n*(-2 + n*(116/45.0 + n*(26/45.0 + + + Q->cgb[0] = n*( 2 + n*(-2/3.0 + n*(-2 + n*(116/45.0 + n*(26/45.0 + n*(-2854/675.0 )))))); - P->cbg[0] = n*(-2 + n*( 2/3.0 + n*( 4/3.0 + n*(-82/45.0 + n*(32/45.0 + + Q->cbg[0] = n*(-2 + n*( 2/3.0 + n*( 4/3.0 + n*(-82/45.0 + n*(32/45.0 + n*( 4642/4725.0)))))); np *= n; - P->cgb[1] = np*(7/3.0 + n*( -8/5.0 + n*(-227/45.0 + n*(2704/315.0 + + Q->cgb[1] = np*(7/3.0 + n*( -8/5.0 + n*(-227/45.0 + n*(2704/315.0 + n*( 2323/945.0))))); - P->cbg[1] = np*(5/3.0 + n*(-16/15.0 + n*( -13/9.0 + n*( 904/315.0 + + Q->cbg[1] = np*(5/3.0 + n*(-16/15.0 + n*( -13/9.0 + n*( 904/315.0 + n*(-1522/945.0))))); np *= n; /* n^5 coeff corrected from 1262/105 -> -1262/105 */ - P->cgb[2] = np*( 56/15.0 + n*(-136/35.0 + n*(-1262/105.0 + + Q->cgb[2] = np*( 56/15.0 + n*(-136/35.0 + n*(-1262/105.0 + n*( 73814/2835.0)))); - P->cbg[2] = np*(-26/15.0 + n*( 34/21.0 + n*( 8/5.0 + + Q->cbg[2] = np*(-26/15.0 + n*( 34/21.0 + n*( 8/5.0 + n*(-12686/2835.0)))); np *= n; /* n^5 coeff corrected from 322/35 -> 332/35 */ - P->cgb[3] = np*(4279/630.0 + n*(-332/35.0 + n*(-399572/14175.0))); - P->cbg[3] = np*(1237/630.0 + n*( -12/5.0 + n*( -24832/14175.0))); + Q->cgb[3] = np*(4279/630.0 + n*(-332/35.0 + n*(-399572/14175.0))); + Q->cbg[3] = np*(1237/630.0 + n*( -12/5.0 + n*( -24832/14175.0))); np *= n; - P->cgb[4] = np*(4174/315.0 + n*(-144838/6237.0 )); - P->cbg[4] = np*(-734/315.0 + n*( 109598/31185.0)); + Q->cgb[4] = np*(4174/315.0 + n*(-144838/6237.0 )); + Q->cbg[4] = np*(-734/315.0 + n*( 109598/31185.0)); np *= n; - P->cgb[5] = np*(601676/22275.0 ); - P->cbg[5] = np*(444337/155925.0); + Q->cgb[5] = np*(601676/22275.0 ); + Q->cbg[5] = np*(444337/155925.0); /* Constants of the projections */ /* Transverse Mercator (UTM, ITM, etc) */ np = n*n; /* Norm. mer. quad, K&W p.50 (96), p.19 (38b), p.5 (2) */ - P->Qn = P->k0/(1 + n) * (1 + np*(1/4.0 + np*(1/64.0 + np/256.0))); + Q->Qn = P->k0/(1 + n) * (1 + np*(1/4.0 + np*(1/64.0 + np/256.0))); /* coef of trig series */ /* utg := ell. N, E -> sph. N, E, KW p194 (65) */ /* gtu := sph. N, E -> ell. N, E, KW p196 (69) */ - P->utg[0] = n*(-0.5 + n*( 2/3.0 + n*(-37/96.0 + n*( 1/360.0 + + Q->utg[0] = n*(-0.5 + n*( 2/3.0 + n*(-37/96.0 + n*( 1/360.0 + n*( 81/512.0 + n*(-96199/604800.0)))))); - P->gtu[0] = n*( 0.5 + n*(-2/3.0 + n*( 5/16.0 + n*(41/180.0 + + Q->gtu[0] = n*( 0.5 + n*(-2/3.0 + n*( 5/16.0 + n*(41/180.0 + n*(-127/288.0 + n*( 7891/37800.0 )))))); - P->utg[1] = np*(-1/48.0 + n*(-1/15.0 + n*(437/1440.0 + n*(-46/105.0 + + Q->utg[1] = np*(-1/48.0 + n*(-1/15.0 + n*(437/1440.0 + n*(-46/105.0 + n*( 1118711/3870720.0))))); - P->gtu[1] = np*(13/48.0 + n*(-3/5.0 + n*(557/1440.0 + n*(281/630.0 + + Q->gtu[1] = np*(13/48.0 + n*(-3/5.0 + n*(557/1440.0 + n*(281/630.0 + n*(-1983433/1935360.0))))); np *= n; - P->utg[2] = np*(-17/480.0 + n*( 37/840.0 + n*( 209/4480.0 + + Q->utg[2] = np*(-17/480.0 + n*( 37/840.0 + n*( 209/4480.0 + n*( -5569/90720.0 )))); - P->gtu[2] = np*( 61/240.0 + n*(-103/140.0 + n*(15061/26880.0 + + Q->gtu[2] = np*( 61/240.0 + n*(-103/140.0 + n*(15061/26880.0 + n*(167603/181440.0)))); np *= n; - P->utg[3] = np*(-4397/161280.0 + n*( 11/504.0 + n*( 830251/7257600.0))); - P->gtu[3] = np*(49561/161280.0 + n*(-179/168.0 + n*(6601661/7257600.0))); + Q->utg[3] = np*(-4397/161280.0 + n*( 11/504.0 + n*( 830251/7257600.0))); + Q->gtu[3] = np*(49561/161280.0 + n*(-179/168.0 + n*(6601661/7257600.0))); np *= n; - P->utg[4] = np*(-4583/161280.0 + n*( 108847/3991680.0)); - P->gtu[4] = np*(34729/80640.0 + n*(-3418889/1995840.0)); + Q->utg[4] = np*(-4583/161280.0 + n*( 108847/3991680.0)); + Q->gtu[4] = np*(34729/80640.0 + n*(-3418889/1995840.0)); np *= n; - P->utg[5] = np*(-20648693/638668800.0); - P->gtu[5] = np*(212378941/319334400.0); + Q->utg[5] = np*(-20648693/638668800.0); + Q->gtu[5] = np*(212378941/319334400.0); + /* Gaussian latitude value of the origin latitude */ - Z = gatg(P->cbg, PROJ_ETMERC_ORDER, P->phi0); + Z = gatg (Q->cbg, PROJ_ETMERC_ORDER, P->phi0); + /* Origin northing minus true northing at the origin latitude */ /* i.e. true northing = N - P->Zb */ - P->Zb = - P->Qn*(Z + clens(P->gtu, PROJ_ETMERC_ORDER, 2*Z)); + Q->Zb = - Q->Qn*(Z + clens(Q->gtu, PROJ_ETMERC_ORDER, 2*Z)); P->inv = e_inverse; P->fwd = e_forward; return P; } -ENTRY0(etmerc) -ENDENTRY(setup(P)) + + +PJ *PROJECTION(etmerc) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + return setup (P); +} + + + + + + + +#ifdef PJ_OMIT_SELFTEST +int pj_etmerc_selftest (void) {return 0;} +#else + +int pj_etmerc_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=etmerc +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5 +zone=30"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {222650.79679758562, 110642.22941193319}, + {222650.79679758562, -110642.22941193319}, + {-222650.79679758562, 110642.22941193319}, + {-222650.79679758562, -110642.22941193319}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {0.0017966305681649398, 0.00090436947663183873}, + {0.0017966305681649398, -0.00090436947663183873}, + {-0.0017966305681649398, 0.00090436947663183873}, + {-0.0017966305681649398, -0.00090436947663183873}, + }; + + return pj_generic_selftest (e_args, 0, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, 0, inv_in, e_inv_expect, 0); +} +#endif + + + + + + + + + + + /* utm uses etmerc for the underlying projection */ -ENTRY0(utm) + + +PJ *PROJECTION(utm) { int zone; + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; - if (!P->es) E_ERROR(-34); - P->y0 = pj_param(P->ctx, P->params, "bsouth").i ? 10000000. : 0.; + if (!P->es) + E_ERROR(-34); + P->y0 = pj_param (P->ctx, P->params, "bsouth").i ? 10000000. : 0.; P->x0 = 500000.; - if (pj_param(P->ctx, P->params, "tzone").i) /* zone input ? */ + if (pj_param (P->ctx, P->params, "tzone").i) /* zone input ? */ if ((zone = pj_param(P->ctx, P->params, "izone").i) > 0 && zone <= 60) --zone; else E_ERROR(-35) else /* nearest central meridian input */ - if ((zone = (int)(floor((adjlon(P->lam0) + PI) * 30. / PI))) < 0) + if ((zone = (int)(floor ((adjlon (P->lam0) + PI) * 30. / PI))) < 0) zone = 0; else if (zone >= 60) zone = 59; P->lam0 = (zone + .5) * PI / 30. - PI; P->k0 = 0.9996; P->phi0 = 0.; -ENDENTRY(setup(P)) + + return setup (P); +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_utm_selftest (void) {return 0;} +#else + +int pj_utm_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=utm +ellps=GRS80 +lat_1=0.5 +lat_2=2 +n=0.5 +zone=30"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + {1057002.4054912981, 110955.14117594929}, + {1057002.4054912981, -110955.14117594929}, + {611263.81227890507, 110547.10569680421}, + {611263.81227890507, -110547.10569680421}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + {-7.4869520833902357, 0.00090193980983462605}, + {-7.4869520833902357, -0.00090193980983462605}, + {-7.4905356820622613, 0.00090193535121489081}, + {-7.4905356820622613, -0.00090193535121489081}, + }; + + return pj_generic_selftest (e_args, 0, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, 0, inv_in, e_inv_expect, 0); +} +#endif + + + diff --git a/src/proj_rouss.c b/src/proj_rouss.c index f1c2f5a1..fc9918be 100644 --- a/src/proj_rouss.c +++ b/src/proj_rouss.c @@ -23,98 +23,176 @@ ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE ** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -#define PROJ_PARMS__ \ - double s0; \ - double A1, A2, A3, A4, A5, A6; \ - double B1, B2, B3, B4, B5, B6, B7, B8; \ - double C1, C2, C3, C4, C5, C6, C7, C8; \ - double D1, D2, D3, D4, D5, D6, D7, D8, D9, D10, D11; \ - void *en; #define PJ_LIB__ -# include <projects.h> +#include <projects.h> + +struct pj_opaque { + double s0; + double A1, A2, A3, A4, A5, A6; + double B1, B2, B3, B4, B5, B6, B7, B8; + double C1, C2, C3, C4, C5, C6, C7, C8; + double D1, D2, D3, D4, D5, D6, D7, D8, D9, D10, D11; + void *en; +}; PROJ_HEAD(rouss, "Roussilhe Stereographic") "\n\tAzi., Ellps."; -FORWARD(e_forward); /* ellipsoid */ - double s, al, cp, sp, al2, s2; - - cp = cos(lp.phi); - sp = sin(lp.phi); - s = proj_mdist(lp.phi, sp, cp, P->en) - P->s0; - s2 = s * s; - al = lp.lam * cp / sqrt(1. - P->es * sp * sp); - al2 = al * al; - xy.x = P->k0 * al*(1.+s2*(P->A1+s2*P->A4)-al2*(P->A2+s*P->A3+s2*P->A5 - +al2*P->A6)); - xy.y = P->k0 * (al2*(P->B1+al2*P->B4)+ - s*(1.+al2*(P->B3-al2*P->B6)+s2*(P->B2+s2*P->B8)+ - s*al2*(P->B5+s*P->B7))); - return (xy); + + +static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ + XY xy = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double s, al, cp, sp, al2, s2; + + cp = cos(lp.phi); + sp = sin(lp.phi); + s = proj_mdist(lp.phi, sp, cp, Q->en) - Q->s0; + s2 = s * s; + al = lp.lam * cp / sqrt(1. - P->es * sp * sp); + al2 = al * al; + xy.x = P->k0 * al*(1.+s2*(Q->A1+s2*Q->A4)-al2*(Q->A2+s*Q->A3+s2*Q->A5 + +al2*Q->A6)); + xy.y = P->k0 * (al2*(Q->B1+al2*Q->B4)+ + s*(1.+al2*(Q->B3-al2*Q->B6)+s2*(Q->B2+s2*Q->B8)+ + s*al2*(Q->B5+s*Q->B7))); + + return xy; } -INVERSE(e_inverse); /* ellipsoid */ - double s, al, x = xy.x / P->k0, y = xy.y / P->k0, x2, y2;; - - x2 = x * x; - y2 = y * y; - al = x*(1.-P->C1*y2+x2*(P->C2+P->C3*y-P->C4*x2+P->C5*y2-P->C7*x2*y) - +y2*(P->C6*y2-P->C8*x2*y)); - s = P->s0 + y*(1.+y2*(-P->D2+P->D8*y2))+ - x2*(-P->D1+y*(-P->D3+y*(-P->D5+y*(-P->D7+y*P->D11)))+ - x2*(P->D4+y*(P->D6+y*P->D10)-x2*P->D9)); - lp.phi=proj_inv_mdist(P->ctx, s, P->en); - s = sin(lp.phi); - lp.lam=al * sqrt(1. - P->es * s * s)/cos(lp.phi); - return (lp); + + +static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */ + LP lp = {0.0,0.0}; + struct pj_opaque *Q = P->opaque; + double s, al, x = xy.x / P->k0, y = xy.y / P->k0, x2, y2;; + + x2 = x * x; + y2 = y * y; + al = x*(1.-Q->C1*y2+x2*(Q->C2+Q->C3*y-Q->C4*x2+Q->C5*y2-Q->C7*x2*y) + +y2*(Q->C6*y2-Q->C8*x2*y)); + s = Q->s0 + y*(1.+y2*(-Q->D2+Q->D8*y2))+ + x2*(-Q->D1+y*(-Q->D3+y*(-Q->D5+y*(-Q->D7+y*Q->D11)))+ + x2*(Q->D4+y*(Q->D6+y*Q->D10)-x2*Q->D9)); + lp.phi=proj_inv_mdist(P->ctx, s, Q->en); + s = sin(lp.phi); + lp.lam=al * sqrt(1. - P->es * s * s)/cos(lp.phi); + + return lp; } -FREEUP; - if (P) { - if (P->en) - free(P->en); - free(P); - } + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); + + if (P->opaque->en) + pj_dealloc (P->opaque->en); + pj_dealloc (P->opaque); + return pj_dealloc(P); +} + +static void freeup (PJ *P) { + freeup_new (P); + return; } -ENTRY1(rouss, en) - double N0, es2, t, t2, R_R0_2, R_R0_4; - - if (!((P->en = proj_mdist_ini(P->es)))) - E_ERROR_0; - es2 = sin(P->phi0); - P->s0 = proj_mdist(P->phi0, es2, cos(P->phi0), P->en); - t = 1. - (es2 = P->es * es2 * es2); - N0 = 1./sqrt(t); - R_R0_2 = t * t / P->one_es; - R_R0_4 = R_R0_2 * R_R0_2; - t = tan(P->phi0); - t2 = t * t; - P->C1 = P->A1 = R_R0_2 / 4.; - P->C2 = P->A2 = R_R0_2 * (2 * t2 - 1. - 2. * es2) / 12.; - P->A3 = R_R0_2 * t * (1. + 4. * t2)/ ( 12. * N0); - P->A4 = R_R0_4 / 24.; - P->A5 = R_R0_4 * ( -1. + t2 * (11. + 12. * t2))/24.; - P->A6 = R_R0_4 * ( -2. + t2 * (11. - 2. * t2))/240.; - P->B1 = t / (2. * N0); - P->B2 = R_R0_2 / 12.; - P->B3 = R_R0_2 * (1. + 2. * t2 - 2. * es2)/4.; - P->B4 = R_R0_2 * t * (2. - t2)/(24. * N0); - P->B5 = R_R0_2 * t * (5. + 4.* t2)/(8. * N0); - P->B6 = R_R0_4 * (-2. + t2 * (-5. + 6. * t2))/48.; - P->B7 = R_R0_4 * (5. + t2 * (19. + 12. * t2))/24.; - P->B8 = R_R0_4 / 120.; - P->C3 = R_R0_2 * t * (1. + t2)/(3. * N0); - P->C4 = R_R0_4 * (-3. + t2 * (34. + 22. * t2))/240.; - P->C5 = R_R0_4 * (4. + t2 * (13. + 12. * t2))/24.; - P->C6 = R_R0_4 / 16.; - P->C7 = R_R0_4 * t * (11. + t2 * (33. + t2 * 16.))/(48. * N0); - P->C8 = R_R0_4 * t * (1. + t2 * 4.)/(36. * N0); - P->D1 = t / (2. * N0); - P->D2 = R_R0_2 / 12.; - P->D3 = R_R0_2 * (2 * t2 + 1. - 2. * es2) / 4.; - P->D4 = R_R0_2 * t * (1. + t2)/(8. * N0); - P->D5 = R_R0_2 * t * (1. + t2 * 2.)/(4. * N0); - P->D6 = R_R0_4 * (1. + t2 * (6. + t2 * 6.))/16.; - P->D7 = R_R0_4 * t2 * (3. + t2 * 4.)/8.; - P->D8 = R_R0_4 / 80.; - P->D9 = R_R0_4 * t * (-21. + t2 * (178. - t2 * 26.))/720.; - P->D10 = R_R0_4 * t * (29. + t2 * (86. + t2 * 48.))/(96. * N0); - P->D11 = R_R0_4 * t * (37. + t2 * 44.)/(96. * N0); - P->fwd = e_forward; - P->inv = e_inverse; - ENDENTRY(P) + + +PJ *PROJECTION(rouss) { + double N0, es2, t, t2, R_R0_2, R_R0_4; + + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + if (!((Q->en = proj_mdist_ini(P->es)))) + E_ERROR_0; + es2 = sin(P->phi0); + Q->s0 = proj_mdist(P->phi0, es2, cos(P->phi0), Q->en); + t = 1. - (es2 = P->es * es2 * es2); + N0 = 1./sqrt(t); + R_R0_2 = t * t / P->one_es; + R_R0_4 = R_R0_2 * R_R0_2; + t = tan(P->phi0); + t2 = t * t; + Q->C1 = Q->A1 = R_R0_2 / 4.; + Q->C2 = Q->A2 = R_R0_2 * (2 * t2 - 1. - 2. * es2) / 12.; + Q->A3 = R_R0_2 * t * (1. + 4. * t2)/ ( 12. * N0); + Q->A4 = R_R0_4 / 24.; + Q->A5 = R_R0_4 * ( -1. + t2 * (11. + 12. * t2))/24.; + Q->A6 = R_R0_4 * ( -2. + t2 * (11. - 2. * t2))/240.; + Q->B1 = t / (2. * N0); + Q->B2 = R_R0_2 / 12.; + Q->B3 = R_R0_2 * (1. + 2. * t2 - 2. * es2)/4.; + Q->B4 = R_R0_2 * t * (2. - t2)/(24. * N0); + Q->B5 = R_R0_2 * t * (5. + 4.* t2)/(8. * N0); + Q->B6 = R_R0_4 * (-2. + t2 * (-5. + 6. * t2))/48.; + Q->B7 = R_R0_4 * (5. + t2 * (19. + 12. * t2))/24.; + Q->B8 = R_R0_4 / 120.; + Q->C3 = R_R0_2 * t * (1. + t2)/(3. * N0); + Q->C4 = R_R0_4 * (-3. + t2 * (34. + 22. * t2))/240.; + Q->C5 = R_R0_4 * (4. + t2 * (13. + 12. * t2))/24.; + Q->C6 = R_R0_4 / 16.; + Q->C7 = R_R0_4 * t * (11. + t2 * (33. + t2 * 16.))/(48. * N0); + Q->C8 = R_R0_4 * t * (1. + t2 * 4.)/(36. * N0); + Q->D1 = t / (2. * N0); + Q->D2 = R_R0_2 / 12.; + Q->D3 = R_R0_2 * (2 * t2 + 1. - 2. * es2) / 4.; + Q->D4 = R_R0_2 * t * (1. + t2)/(8. * N0); + Q->D5 = R_R0_2 * t * (1. + t2 * 2.)/(4. * N0); + Q->D6 = R_R0_4 * (1. + t2 * (6. + t2 * 6.))/16.; + Q->D7 = R_R0_4 * t2 * (3. + t2 * 4.)/8.; + Q->D8 = R_R0_4 / 80.; + Q->D9 = R_R0_4 * t * (-21. + t2 * (178. - t2 * 26.))/720.; + Q->D10 = R_R0_4 * t * (29. + t2 * (86. + t2 * 48.))/(96. * N0); + Q->D11 = R_R0_4 * t * (37. + t2 * 44.)/(96. * N0); + + P->fwd = e_forward; + P->inv = e_inverse; + + return P; +} + + +#ifdef PJ_OMIT_SELFTEST +int pj_rouss_selftest (void) {return 0;} +#else + +int pj_rouss_selftest (void) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + char e_args[] = {"+proj=rouss +ellps=GRS80 +lat_1=0.5 +lat_2=2"}; + + LP fwd_in[] = { + { 2, 1}, + { 2,-1}, + {-2, 1}, + {-2,-1} + }; + + XY e_fwd_expect[] = { + { 222644.89413161727, 110611.09186837047}, + { 222644.89413161727, -110611.09186837047}, + {-222644.89413161727, 110611.09186837047}, + {-222644.89413161727, -110611.09186837047}, + }; + + XY inv_in[] = { + { 200, 100}, + { 200,-100}, + {-200, 100}, + {-200,-100} + }; + + LP e_inv_expect[] = { + { 0.0017966305682019911, 0.00090436947683699559}, + { 0.0017966305682019911, -0.00090436947683699559}, + {-0.0017966305682019911, 0.00090436947683699559}, + {-0.0017966305682019911, -0.00090436947683699559}, + }; + + return pj_generic_selftest (e_args, 0, tolerance_xy, tolerance_lp, 4, 4, fwd_in, e_fwd_expect, 0, inv_in, e_inv_expect, 0); +} + + +#endif diff --git a/src/projects.h b/src/projects.h index 411a3403..2957f848 100644 --- a/src/projects.h +++ b/src/projects.h @@ -48,7 +48,7 @@ #define C_NAMESPACE extern "C" #define C_NAMESPACE_VAR extern "C" extern "C" { -#else +#else #define C_NAMESPACE extern #define C_NAMESPACE_VAR #endif @@ -132,8 +132,8 @@ typedef struct { /* datum_type values */ #define PJD_UNKNOWN 0 -#define PJD_3PARAM 1 -#define PJD_7PARAM 2 +#define PJD_3PARAM 1 +#define PJD_7PARAM 2 #define PJD_GRIDSHIFT 3 #define PJD_WGS84 4 /* WGS84 (or anything considered equivelent) */ @@ -143,7 +143,7 @@ typedef struct { #define PJD_ERR_GRID_AREA -48 #define PJD_ERR_CATALOG -49 -#define USE_PROJUV +#define USE_PROJUV typedef struct { double u, v; } projUV; typedef struct { double r, i; } COMPLEX; @@ -163,12 +163,19 @@ typedef struct { double lam, phi, z; } LPZ; typedef union { double f; int i; char *s; } PROJVALUE; struct PJconsts; - + struct PJ_LIST { char *id; /* projection keyword */ struct PJconsts *(*proj)(struct PJconsts*);/* projection entry point */ char * const *descr; /* description text */ }; + +/* Merging this into the PJ_LIST infrastructure is tempting, but may imply ABI breakage. Perhaps at next major version? */ +struct PJ_SELFTEST_LIST { + char *id; /* projection keyword */ + int (* testfunc)(void); /* projection entry point */ +}; + struct PJ_ELLPS { char *id; /* ellipse keyword name */ char *major; /* a= value */ @@ -197,14 +204,14 @@ typedef struct { double ll_long; /* lower left corner coordinates (radians) */ double ll_lat; double ur_long; /* upper right corner coordinates (radians) */ - double ur_lat; + double ur_lat; } PJ_Region; struct DERIVS { double x_l, x_p; /* derivatives of x for lambda-phi */ double y_l, y_p; /* derivatives of y for lambda-phi */ }; - + struct FACTORS { struct DERIVS der; double h, k; /* meridinal, parallel scales */ @@ -226,12 +233,18 @@ typedef struct ARG_list { /* base projection data structure */ +#ifdef PJ_LIB__ + /* we need this forward declaration in order to be able to add a + pointer to struct opaque to the typedef struct PJconsts below */ + struct pj_opaque; +#endif + typedef struct PJconsts { projCtx_t *ctx; XY (*fwd)(LP, struct PJconsts *); LP (*inv)(XY, struct PJconsts *); XYZ (*fwd3d)(LPZ, struct PJconsts *); - LPZ (*inv3d)(XYZ, struct PJconsts *); + LPZ (*inv3d)(XYZ, struct PJconsts *); void (*spc)(LP, struct PJconsts *, struct FACTORS *); void (*pfree)(struct PJconsts *); const char *descr; @@ -253,7 +266,7 @@ typedef struct PJconsts { x0, y0, /* easting and northing */ k0, /* general scaling factor */ to_meter, fr_meter; /* cartesian scaling */ - + int datum_type; /* PJD_UNKNOWN/3PARAM/7PARAM/GRIDSHIFT/WGS84 */ double datum_params[7]; struct _pj_gi **gridlist; @@ -272,9 +285,9 @@ typedef struct PJconsts { /* New Datum Shift Grid Catalogs */ char *catalog_name; struct _PJ_GridCatalog *catalog; - + double datum_date; - + struct _pj_gi *last_before_grid; PJ_Region last_before_region; double last_before_date; @@ -283,6 +296,10 @@ typedef struct PJconsts { PJ_Region last_after_region; double last_after_date; +#ifdef PJ_LIB__ + struct pj_opaque *opaque; +#endif + #ifdef PROJ_PARMS__ PROJ_PARMS__ #endif /* end of optional extensions */ @@ -291,24 +308,16 @@ PROJ_PARMS__ /* public API */ #include "proj_api.h" + /* Generate pj_list external or make list from include file */ + #ifndef USE_PJ_LIST_H extern struct PJ_LIST pj_list[]; -#else -#define PROJ_HEAD(id, name) \ - struct PJconsts *pj_##id(struct PJconsts*); extern char * const pj_s_##id; - -#include "pj_list.h" -#undef PROJ_HEAD -#define PROJ_HEAD(id, name) {#id, pj_##id, &pj_s_##id}, - struct PJ_LIST -pj_list[] = { -#include "pj_list.h" - {0, 0, 0}, - }; -#undef PROJ_HEAD +extern struct PJ_SELFTEST_LIST pj_selftest_list[]; #endif + + #ifndef PJ_ELLPS__ extern struct PJ_ELLPS pj_ellps[]; #endif @@ -350,7 +359,45 @@ extern struct PJ_PRIME_MERIDIANS pj_prime_meridians[]; #define INVERSE3D(name) static LPZ name(XYZ xyz, PJ *P) {LPZ lpz = {0.0, 0.0, 0.0} #define FREEUP static void freeup(PJ *P) { #define SPECIAL(name) static void name(LP lp, PJ *P, struct FACTORS *fac) +#define ELLIPSOIDAL(P) ((P->es==0)? (FALSE): (TRUE)) + +/* cleaned up alternative to most of the "repetitive projection code" macros */ +#define PROJECTION(name) \ +pj_projection_specific_setup_##name (PJ *P); \ +C_NAMESPACE_VAR const char * const pj_s_##name = des_##name; \ +C_NAMESPACE PJ *pj_##name (PJ *P) { \ + if (P) \ + return pj_projection_specific_setup_##name (P); \ + P = (PJ*) pj_calloc (1, sizeof(PJ)); \ + if (0==P) \ + return 0; \ + P->pfree = freeup; \ + P->descr = des_##name; \ + return P; \ +} \ +PJ *pj_projection_specific_setup_##name (PJ *P) + #endif + + +int pj_generic_selftest ( + char *e_args, + char *s_args, + double tolerance_xy, + double tolerance_lp, + int n_fwd, + int n_inv, + LP *fwd_in, + XY *e_fwd_expect, + XY *s_fwd_expect, + XY *inv_in, + LP *e_inv_expect, + LP *s_inv_expect +); + + + + #define MAX_TAB_ID 80 typedef struct { float lam, phi; } FLP; typedef struct { int lam, phi; } ILP; @@ -366,8 +413,8 @@ struct CTABLE { typedef struct _pj_gi { char *gridname; /* identifying name of grid, eg "conus" or ntv2_0.gsb */ char *filename; /* full path to filename */ - - const char *format; /* format of this grid, ie "ctable", "ntv1", + + const char *format; /* format of this grid, ie "ctable", "ntv1", "ntv2" or "missing". */ int grid_offset; /* offset in file, for delayed loading */ @@ -414,6 +461,7 @@ int pj_ell_set(projCtx ctx, paralist *, double *, double *); int pj_datum_set(projCtx,paralist *, PJ *); int pj_prime_meridian_set(paralist *, PJ *); int pj_angular_units_set(paralist *, PJ *); +void pj_prepare (PJ *P, const char *description, void (*freeup)(struct PJconsts *), size_t sizeof_struct_opaque); paralist *pj_clone_paralist( const paralist* ); paralist*pj_search_initcache( const char *filekey ); @@ -439,7 +487,7 @@ struct PW_COEF {/* row coefficient structure */ int m; /* number of c coefficients (=0 for none) */ double *c; /* power coefficients */ }; - + /* Approximation structures and procedures */ typedef struct { /* Chebyshev or Power series structure */ projUV a, b; /* power series range for evaluation */ @@ -457,6 +505,7 @@ void **vector2(int, int, int); void freev2(void **v, int nrows); int bchgen(projUV, projUV, int, int, projUV **, projUV(*)(projUV)); int bch2bps(projUV, projUV, projUV **, int, int); + /* nadcon related protos */ LP nad_intr(LP, struct CTABLE *); LP nad_cvt(LP, int, struct CTABLE *); @@ -470,15 +519,15 @@ void nad_free(struct CTABLE *); /* higher level handling of datum grid shift files */ int pj_apply_vgridshift( PJ *defn, const char *listname, - PJ_GRIDINFO ***gridlist_p, + PJ_GRIDINFO ***gridlist_p, int *gridlist_count_p, - int inverse, + int inverse, long point_count, int point_offset, double *x, double *y, double *z ); -int pj_apply_gridshift_2( PJ *defn, int inverse, +int pj_apply_gridshift_2( PJ *defn, int inverse, long point_count, int point_offset, double *x, double *y, double *z ); -int pj_apply_gridshift_3( projCtx ctx, +int pj_apply_gridshift_3( projCtx ctx, PJ_GRIDINFO **gridlist, int gridlist_count, int inverse, long point_count, int point_offset, double *x, double *y, double *z ); @@ -493,15 +542,15 @@ void pj_gridinfo_free( projCtx, PJ_GRIDINFO * ); PJ_GridCatalog *pj_gc_findcatalog( projCtx, const char * ); PJ_GridCatalog *pj_gc_readcatalog( projCtx, const char * ); void pj_gc_unloadall( projCtx ); -int pj_gc_apply_gridshift( PJ *defn, int inverse, +int pj_gc_apply_gridshift( PJ *defn, int inverse, long point_count, int point_offset, double *x, double *y, double *z ); -int pj_gc_apply_gridshift( PJ *defn, int inverse, +int pj_gc_apply_gridshift( PJ *defn, int inverse, long point_count, int point_offset, double *x, double *y, double *z ); -PJ_GRIDINFO *pj_gc_findgrid( projCtx ctx, - PJ_GridCatalog *catalog, int after, +PJ_GRIDINFO *pj_gc_findgrid( projCtx ctx, + PJ_GridCatalog *catalog, int after, LP location, double date, PJ_Region *optional_region, double *grid_date ); @@ -521,6 +570,7 @@ struct PJ_ELLPS *pj_get_ellps_ref( void ); struct PJ_DATUMS *pj_get_datums_ref( void ); struct PJ_UNITS *pj_get_units_ref( void ); struct PJ_LIST *pj_get_list_ref( void ); +struct PJ_SELFTEST_LIST *pj_get_selftest_list_ref ( void ); struct PJ_PRIME_MERIDIANS *pj_get_prime_meridians_ref( void ); double pj_atof( const char* nptr ); diff --git a/src/test228.c b/src/test228.c index cdfc8650..a8e55c4c 100644 --- a/src/test228.c +++ b/src/test228.c @@ -1,4 +1,6 @@ #include <proj_api.h> +#include <stdio.h> /* for printf declaration */ + #ifdef _WIN32 @@ -33,7 +35,7 @@ void* thread_main(void* unused) p_OSGB36_proj=pj_init_plus_ctx(p_proj_ctxt, "+proj=longlat +ellps=airy +datum=OSGB36 +nadgrids=OSTN02_NTv2.gsb " "+no_defs"); - + while(run) { double x, y; @@ -73,4 +75,4 @@ int main(int argc, char* argv[]) return 0; } -#endif /* _WIN32 */
\ No newline at end of file +#endif /* _WIN32 */ |
