diff options
| author | busstoptaktik <knudsen.thomas@gmail.com> | 2016-05-11 08:09:46 +0200 |
|---|---|---|
| committer | busstoptaktik <knudsen.thomas@gmail.com> | 2016-05-11 08:09:46 +0200 |
| commit | e8ed8e58e8f557fc6f9443a00af1e73a5956c759 (patch) | |
| tree | 2872dd2196c763ab8953d6d66937781a15842d88 /src | |
| parent | 9687e69b177933c2894adc842f6aa507fc70ca16 (diff) | |
| parent | 6cbf7e5f6c51b3b88e0d604124d688f79512496b (diff) | |
| download | PROJ-e8ed8e58e8f557fc6f9443a00af1e73a5956c759.tar.gz PROJ-e8ed8e58e8f557fc6f9443a00af1e73a5956c759.zip | |
Merge pull request #6 from kbevers/fix-projs-with-l
Converted files PJ_l*.c
Diffstat (limited to 'src')
52 files changed, 7914 insertions, 3513 deletions
diff --git a/src/PJ_aea.c b/src/PJ_aea.c index 72091132..5f7c6c92 100644 --- a/src/PJ_aea.c +++ b/src/PJ_aea.c @@ -353,76 +353,6 @@ source files ***********************************************************************/ -int pj_aeqd_selftest (void) {return 10000;} -int pj_alsk_selftest (void) {return 10000;} -int pj_eqc_selftest (void) {return 10000;} -int pj_eqdc_selftest (void) {return 10000;} int pj_etmerc_selftest (void) {return 10000;} -int pj_geocent_selftest (void) {return 10000;} -int pj_gs48_selftest (void) {return 10000;} -int pj_gs50_selftest (void) {return 10000;} - -int pj_labrd_selftest (void) {return 10000;} -int pj_laea_selftest (void) {return 10000;} -int pj_lagrng_selftest (void) {return 10000;} -int pj_larr_selftest (void) {return 10000;} -int pj_lask_selftest (void) {return 10000;} -int pj_latlon_selftest (void) {return 10000;} -int pj_latlong_selftest (void) {return 10000;} -int pj_lonlat_selftest (void) {return 10000;} -int pj_longlat_selftest (void) {return 10000;} -int pj_lcc_selftest (void) {return 10000;} -int pj_lcca_selftest (void) {return 10000;} - -int pj_lee_os_selftest (void) {return 10000;} -int pj_loxim_selftest (void) {return 10000;} -int pj_lsat_selftest (void) {return 10000;} - -int pj_mbt_fps_selftest (void) {return 10000;} -int pj_mbtfpp_selftest (void) {return 10000;} -int pj_mbtfpq_selftest (void) {return 10000;} -int pj_merc_selftest (void) {return 10000;} -int pj_mil_os_selftest (void) {return 10000;} -int pj_mill_selftest (void) {return 10000;} -int pj_misrsom_selftest (void) {return 10000;} -int pj_moll_selftest (void) {return 10000;} -int pj_natearth_selftest (void) {return 10000;} -int pj_natearth2_selftest (void) {return 10000;} -int pj_nell_selftest (void) {return 10000;} -int pj_nell_h_selftest (void) {return 10000;} -int pj_nicol_selftest (void) {return 10000;} -int pj_nsper_selftest (void) {return 10000;} -int pj_nzmg_selftest (void) {return 10000;} -int pj_ob_tran_selftest (void) {return 10000;} -int pj_ocea_selftest (void) {return 10000;} -int pj_oea_selftest (void) {return 10000;} -int pj_omerc_selftest (void) {return 10000;} -int pj_ortho_selftest (void) {return 10000;} -int pj_patterson_selftest (void) {return 10000;} -int pj_poly_selftest (void) {return 10000;} -int pj_putp2_selftest (void) {return 10000;} -int pj_putp3_selftest (void) {return 10000;} -int pj_putp3p_selftest (void) {return 10000;} -int pj_putp4p_selftest (void) {return 10000;} -int pj_putp5_selftest (void) {return 10000;} -int pj_putp5p_selftest (void) {return 10000;} -int pj_putp6_selftest (void) {return 10000;} -int pj_putp6p_selftest (void) {return 10000;} - -int pj_qsc_selftest (void) {return 10000;} -int pj_robin_selftest (void) {return 10000;} -int pj_rouss_selftest (void) {return 10000;} -int pj_rpoly_selftest (void) {return 10000;} -int pj_sch_selftest (void) {return 10000;} - -int pj_tpers_selftest (void) {return 10000;} - int pj_utm_selftest (void) {return 10000;} -int pj_vandg_selftest (void) {return 10000;} -int pj_vandg2_selftest (void) {return 10000;} -int pj_vandg3_selftest (void) {return 10000;} -int pj_vandg4_selftest (void) {return 10000;} -int pj_wag4_selftest (void) {return 10000;} -int pj_wag5_selftest (void) {return 10000;} -int pj_weren_selftest (void) {return 10000;} #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_eck5.c b/src/PJ_eck5.c index ccfcb88f..d7626939 100644 --- a/src/PJ_eck5.c +++ b/src/PJ_eck5.c @@ -56,7 +56,6 @@ int pj_eck5_selftest (void) { double tolerance_lp = 1e-10;
double tolerance_xy = 1e-7;
- char e_args[] = {"+proj=eck5 +ellps=GRS80 +lat_1=0.5 +lat_2=2"};
char s_args[] = {"+proj=eck5 +a=6400000 +lat_1=0.5 +lat_2=2"};
LP fwd_in[] = {
@@ -66,18 +65,11 @@ int pj_eck5_selftest (void) { {-2,-1}
};
- XY e_fwd_expect[] = {
- { 196358.31442683787, 98186.634363414545
},
- { 196358.31442683787, -98186.634363414545
},
- {-196358.31442683787, 98186.634363414545
},
- {-196358.31442683787, -98186.634363414545
},
- };
-
XY s_fwd_expect[] = {
- { 197031.39213406085, 98523.198847226551
},
- { 197031.39213406085, -98523.198847226551
},
- {-197031.39213406085, 98523.198847226551
},
- {-197031.39213406085, -98523.198847226551
},
+ { 197031.39213406085, 98523.198847226551},
+ { 197031.39213406085, -98523.198847226551},
+ {-197031.39213406085, 98523.198847226551},
+ {-197031.39213406085, -98523.198847226551},
};
XY inv_in[] = {
@@ -87,18 +79,11 @@ int pj_eck5_selftest (void) { {-200,-100}
};
- LP e_inv_expect[] = {
- {0.0020369371178927064, 0.0010184685588659013
},
- {0.0020369371178927064, -0.0010184685588659013
},
- {-0.0020369371178927064, 0.0010184685588659013
},
- {-0.0020369371178927064, -0.0010184685588659013
},
- };
-
LP s_inv_expect[] = {
- {0.002029978749734037, 0.001014989374787388
},
- {0.002029978749734037, -0.001014989374787388
},
- {-0.002029978749734037, 0.001014989374787388
},
- {-0.002029978749734037, -0.001014989374787388
},
+ {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);
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..a7308335 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) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + double cosphi, sinphi; + int secant; + + 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 864225f4..b8841c96 100644 --- a/src/PJ_fahey.c +++ b/src/PJ_fahey.c @@ -1,5 +1,5 @@ #define PJ_LIB__
-# include <projects.h>
+#include <projects.h>
PROJ_HEAD(fahey, "Fahey") "\n\tPcyl, Sph.";
@@ -58,7 +58,6 @@ int pj_fahey_selftest (void) { double tolerance_lp = 1e-10;
double tolerance_xy = 1e-7;
- char e_args[] = {"+proj=fahey +ellps=GRS80 +lat_1=0.5 +lat_2=2"};
char s_args[] = {"+proj=fahey +a=6400000 +lat_1=0.5 +lat_2=2"};
LP fwd_in[] = {
@@ -69,10 +68,10 @@ int pj_fahey_selftest (void) { };
XY s_fwd_expect[] = {
- { 182993.34464912376, 101603.19356988439
},
- { 182993.34464912376, -101603.19356988439
},
- {-182993.34464912376, 101603.19356988439
},
- {-182993.34464912376, -101603.19356988439
},
+ { 182993.34464912376, 101603.19356988439},
+ { 182993.34464912376, -101603.19356988439},
+ {-182993.34464912376, 101603.19356988439},
+ {-182993.34464912376, -101603.19356988439},
};
XY inv_in[] = {
@@ -83,10 +82,10 @@ int pj_fahey_selftest (void) { };
LP s_inv_expect[] = {
- {0.0021857886080359551, 0.00098424601668238403
},
- {0.0021857886080359551, -0.00098424601668238403
},
- {-0.0021857886080359551, 0.00098424601668238403
},
- {-0.0021857886080359551, -0.00098424601668238403
},
+ {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);
diff --git a/src/PJ_labrd.c b/src/PJ_labrd.c index 4cb39ec8..e7bca390 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) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + double Az, sinp, R, N, t; + + 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..02b281a4 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) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + double t; + + 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..9a018808 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) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + double phi1; + + 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 +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..28275089 100644 --- a/src/PJ_lcc.c +++ b/src/PJ_lcc.c @@ -1,105 +1,195 @@ -#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; } -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); + + +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; } -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; + +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); } -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) + +static void freeup (PJ *P) { + freeup_new (P); + return; +} + + +PJ *PROJECTION(lcc) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + double cosphi, sinphi; + int secant; + + 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; +} + + +#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); +} + + +#endif diff --git a/src/PJ_lcca.c b/src/PJ_lcca.c index 320d52db..eef46110 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) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + double s2p0, N0, R0, tan0; + + (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..2dcdb000 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) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; int land, path; double lam, alf, esc, ess; - 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) + 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); +} + + +#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 89913e50..dd894bbd 100644 --- a/src/PJ_merc.c +++ b/src/PJ_merc.c @@ -1,44 +1,44 @@ #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 - static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */ XY xy = {0.0,0.0}; - if (fabs(fabs(lp.phi) - HALFPI) <= EPS10) + 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; + xy.x = P->k0 * lp.lam; + xy.y = - P->k0 * log(pj_tsfn(lp.phi, sin(lp.phi), P->e)); + 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) + 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; + xy.x = P->k0 * lp.lam; + xy.y = P->k0 * log(tan(FORTPI + .5 * lp.phi)); + return xy; } 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) + 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; + 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; + lp.phi = HALFPI - 2. * atan(exp(-xy.y / P->k0)); + lp.lam = xy.x / P->k0; + return lp; } @@ -48,27 +48,87 @@ static void freeup(PJ *P) { /* Destructor */ PJ *PROJECTION(merc) { - double phits=0.0; - int is_phits; + 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( (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; - } + 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; - } + 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_misrsom.c b/src/PJ_misrsom.c index 19518a1d..fdafbd7c 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,22 +142,44 @@ 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) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + int path; double lam, alf, esc, ess; @@ -153,33 +187,97 @@ ENTRY0(misrsom) 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..551b83bd 100644 --- a/src/PJ_mod_ster.c +++ b/src/PJ_mod_ster.c @@ -1,211 +1,538 @@ /* 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; +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) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + static COMPLEX AB[] = { + {0.924500, 0.}, + {0., 0.}, + {0.019430, 0.} + }; + + 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) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + static COMPLEX AB[] = { + {0.721316, 0.}, + {0., 0.}, + {-0.0088162, -0.00617325} + }; + + 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) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + static COMPLEX /* 48 United States */ + AB[] = { + {0.98879, 0.}, + {0., 0.}, + {-0.050909, 0.}, + {0., 0.}, + {0.075528, 0.} + }; + + 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) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + 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} + }; + + 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) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + 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} + }; + + 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) { + double tolerance_lp = 1e-10; + double tolerance_xy = 1e-7; + + 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..2a0ea702 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) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + 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; + 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..76222fe9 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) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + 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 || !(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 +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..72414e7f 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) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + double phi_0=0.0, phi_1, phi_2, lam_1, lam_2, lonz, alpha; + + 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..50ab6b52 100644 --- a/src/PJ_oea.c +++ b/src/PJ_oea.c @@ -1,58 +1,142 @@ -#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; + + P->pfree = freeup; + P->descr = des_oea; + 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..3b25fb26 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), Q->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), Q->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) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + 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; + + 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, Q->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), Q->E), Q->B); + L = pow(pj_tsfn(phi2, sin(phi2), Q->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..29b7b34c 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,231 @@ * 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; +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( &(P->sph), pxyz[0], pxyz[1], pxyz[2], - temp, temp+1, temp+2) != 0) - I3_ERROR; + 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] = 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 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] += P->xyzoff[0]; - pxyz[1] += P->xyzoff[1]; - pxyz[2] += P->xyzoff[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( &(P->elp_0), pxyz[0], pxyz[1], pxyz[2], - temp, temp+1, temp+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]; + 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); + // 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; } -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( &(Q->elp_0), lpz.phi, lpz.lam, lpz.z, + temp, temp+1, temp+2 ) != 0 ) + F3_ERROR; + + //Adjust for offset + temp[0] -= Q->xyzoff[0]; + temp[1] -= Q->xyzoff[1]; + temp[2] -= Q->xyzoff[2]; - //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; + //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]; - //Adjust for offset - temp[0] -= P->xyzoff[0]; - temp[1] -= P->xyzoff[1]; - temp[2] -= P->xyzoff[2]; + //Convert to local lat,lon + pj_Convert_Geocentric_To_Geodetic( &(Q->sph), pxyz[0], pxyz[1], pxyz[2], + temp, temp+1, temp+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); + //Scale by radius + xyz.x = temp[1] * Q->rcurv / P->a; + xyz.y = temp[0] * Q->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] * P->rcurv / P->a; - xyz.y = temp[0] * P->rcurv / P->a; - xyz.z = temp[2]; + return xyz; +} + + +static void *freeup_new (PJ *P) { /* Destructor */ + if (0==P) + return 0; + if (0==P->opaque) + return pj_dealloc (P); -// 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); + pj_dealloc (P->opaque); + return pj_dealloc(P); +} - return (xyz); +static void freeup (PJ *P) { + freeup_new (P); + return; } -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]; +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]; - temp = P->a * sqrt(1.0 - P->es); + temp = P->a * sqrt(1.0 - P->es); - //Setup original geocentric system - if ( pj_Set_Geocentric_Parameters(&(P->elp_0), P->a, temp) != 0) - E_ERROR(-37); + //Setup original geocentric system + if ( pj_Set_Geocentric_Parameters(&(Q->elp_0), P->a, temp) != 0) + E_ERROR(-37); + clt = cos(Q->plat); + slt = sin(Q->plat); + clo = cos(Q->plon); + slo = sin(Q->plon); - clt = cos(P->plat); - slt = sin(P->plat); - clo = cos(P->plon); - slo = sin(P->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); - //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); - chdg = cos(P->phdg); - shdg = sin(P->phdg); - - P->rcurv = P->h0 + (reast*rnorth)/(reast * chdg * chdg + rnorth * shdg * shdg); + Q->rcurv = Q->h0 + (reast*rnorth)/(reast * chdg * chdg + rnorth * shdg * shdg); -// printf("North Radius: %f \n", rnorth); -// printf("East Radius: %f \n", reast); -// printf("Effective Radius: %f \n", P->rcurv); + // printf("North Radius: %f \n", rnorth); + // printf("East Radius: %f \n", reast); + // printf("Effective Radius: %f \n", Q->rcurv); - //Set up local sphere at the given peg point - if ( pj_Set_Geocentric_Parameters(&(P->sph), P->rcurv, P->rcurv) != 0) - E_ERROR(-37); + //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 - 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; + //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( &(P->elp_0), P->plat, P->plon, P->h0, - pxyz, pxyz+1, pxyz+2 ) != 0 ) - { - E_ERROR(-14) - } + if( pj_Convert_Geodetic_To_Geocentric( &(Q->elp_0), Q->plat, Q->plon, Q->h0, + pxyz, pxyz+1, pxyz+2 ) != 0 ) + { + E_ERROR(-14) + } - 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; -// printf("Offset: %f %f %f \n", P->xyzoff[0], P->xyzoff[1], P->xyzoff[2]); + 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; + // printf("Offset: %f %f %f \n", Q->xyzoff[0], Q->xyzoff[1], Q->xyzoff[2]); - P->fwd3d = forward3d; - P->inv3d = inverse3d; - return P; + + P->fwd3d = forward3d; + P->inv3d = inverse3d; + return P; } -ENTRY0(sch) - P->h0 = 0.0; - //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); - +PJ *PROJECTION(sch) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + Q->h0 = 0.0; + + //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 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; + //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); - //Completed reading in the projection parameters -// printf("PSA: Lat = %f Lon = %f Hdg = %f \n", P->plat, P->plon, P->phdg); + //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 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; + + //Completed reading in the projection parameters + //printf("PSA: Lat = %f Lon = %f Hdg = %f \n", Q->plat, Q->plon, Q->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_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_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_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/proj_rouss.c b/src/proj_rouss.c index f1c2f5a1..35cc73c7 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) { + struct pj_opaque *Q = pj_calloc (1, sizeof (struct pj_opaque)); + if (0==Q) + return freeup_new (P); + P->opaque = Q; + + double N0, es2, t, t2, R_R0_2, R_R0_4; + + 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 |
