aboutsummaryrefslogtreecommitdiff
path: root/src/projections
diff options
context:
space:
mode:
authorKristian Evers <kristianevers@gmail.com>2018-12-26 10:53:01 +0100
committerGitHub <noreply@github.com>2018-12-26 10:53:01 +0100
commit81ec8c0b58d66333fccd3703dab62a11132a0331 (patch)
tree41b4675d169f849516649700ab4f50d4cffa4c02 /src/projections
parent355d681ed88019e97742344bd642c2fd97e700a1 (diff)
parent80dad6ef2bed4a83008db06986dc168918d48476 (diff)
downloadPROJ-81ec8c0b58d66333fccd3703dab62a11132a0331.tar.gz
PROJ-81ec8c0b58d66333fccd3703dab62a11132a0331.zip
Merge pull request #1203 from rouault/cpp_conversion
Conversion of most C files to C++
Diffstat (limited to 'src/projections')
-rw-r--r--src/projections/aea.cpp224
-rw-r--r--src/projections/aeqd.cpp327
-rw-r--r--src/projections/airy.cpp155
-rw-r--r--src/projections/aitoff.cpp201
-rw-r--r--src/projections/august.cpp34
-rw-r--r--src/projections/bacon.cpp81
-rw-r--r--src/projections/bertin1953.cpp96
-rw-r--r--src/projections/bipc.cpp176
-rw-r--r--src/projections/boggs.cpp43
-rw-r--r--src/projections/bonne.cpp136
-rw-r--r--src/projections/calcofi.cpp163
-rw-r--r--src/projections/cass.cpp123
-rw-r--r--src/projections/cc.cpp41
-rw-r--r--src/projections/ccon.cpp109
-rw-r--r--src/projections/cea.cpp103
-rw-r--r--src/projections/chamb.cpp141
-rw-r--r--src/projections/collg.cpp53
-rw-r--r--src/projections/comill.cpp84
-rw-r--r--src/projections/crast.cpp40
-rw-r--r--src/projections/denoy.cpp32
-rw-r--r--src/projections/eck1.cpp41
-rw-r--r--src/projections/eck2.cpp56
-rw-r--r--src/projections/eck3.cpp112
-rw-r--r--src/projections/eck4.cpp63
-rw-r--r--src/projections/eck5.cpp40
-rw-r--r--src/projections/eqc.cpp54
-rw-r--r--src/projections/eqdc.cpp122
-rw-r--r--src/projections/eqearth.cpp164
-rw-r--r--src/projections/etmerc.cpp362
-rw-r--r--src/projections/fahey.cpp41
-rw-r--r--src/projections/fouc_s.cpp72
-rw-r--r--src/projections/gall.cpp44
-rw-r--r--src/projections/geos.cpp238
-rw-r--r--src/projections/gins8.cpp33
-rw-r--r--src/projections/gn_sinu.cpp189
-rw-r--r--src/projections/gnom.cpp147
-rw-r--r--src/projections/goode.cpp84
-rw-r--r--src/projections/gstmerc.cpp74
-rw-r--r--src/projections/hammer.cpp77
-rw-r--r--src/projections/hatano.cpp83
-rw-r--r--src/projections/healpix.cpp674
-rw-r--r--src/projections/igh.cpp227
-rw-r--r--src/projections/imw_p.cpp217
-rw-r--r--src/projections/isea.cpp1098
-rw-r--r--src/projections/krovak.cpp222
-rw-r--r--src/projections/labrd.cpp132
-rw-r--r--src/projections/laea.cpp300
-rw-r--r--src/projections/lagrng.cpp98
-rw-r--r--src/projections/larr.cpp28
-rw-r--r--src/projections/lask.cpp39
-rw-r--r--src/projections/latlong.cpp124
-rw-r--r--src/projections/lcc.cpp130
-rw-r--r--src/projections/lcca.cpp164
-rw-r--r--src/projections/loxim.cpp77
-rw-r--r--src/projections/lsat.cpp212
-rw-r--r--src/projections/mbt_fps.cpp57
-rw-r--r--src/projections/mbtfpp.cpp65
-rw-r--r--src/projections/mbtfpq.cpp74
-rw-r--r--src/projections/merc.cpp101
-rw-r--r--src/projections/mill.cpp37
-rw-r--r--src/projections/misrsom.cpp219
-rw-r--r--src/projections/mod_ster.cpp282
-rw-r--r--src/projections/moll.cpp112
-rw-r--r--src/projections/natearth.cpp100
-rw-r--r--src/projections/natearth2.cpp97
-rw-r--r--src/projections/nell.cpp51
-rw-r--r--src/projections/nell_h.cpp53
-rw-r--r--src/projections/nicol.cpp54
-rw-r--r--src/projections/nsper.cpp202
-rw-r--r--src/projections/nzmg.cpp123
-rw-r--r--src/projections/ob_tran.cpp245
-rw-r--r--src/projections/ocea.cpp102
-rw-r--r--src/projections/oea.cpp87
-rw-r--r--src/projections/omerc.cpp229
-rw-r--r--src/projections/ortho.cpp143
-rw-r--r--src/projections/patterson.cpp117
-rw-r--r--src/projections/poly.cpp171
-rw-r--r--src/projections/putp2.cpp61
-rw-r--r--src/projections/putp3.cpp67
-rw-r--r--src/projections/putp4p.cpp76
-rw-r--r--src/projections/putp5.cpp75
-rw-r--r--src/projections/putp6.cpp97
-rw-r--r--src/projections/qsc.cpp408
-rw-r--r--src/projections/robin.cpp161
-rw-r--r--src/projections/rouss.cpp158
-rw-r--r--src/projections/rpoly.cpp58
-rw-r--r--src/projections/sch.cpp232
-rw-r--r--src/projections/sconics.cpp220
-rw-r--r--src/projections/somerc.cpp94
-rw-r--r--src/projections/stere.cpp320
-rw-r--r--src/projections/sterea.cpp117
-rw-r--r--src/projections/sts.cpp109
-rw-r--r--src/projections/tcc.cpp34
-rw-r--r--src/projections/tcea.cpp36
-rw-r--r--src/projections/times.cpp79
-rw-r--r--src/projections/tmerc.cpp210
-rw-r--r--src/projections/tobmerc.cpp51
-rw-r--r--src/projections/tpeqd.cpp109
-rw-r--r--src/projections/urm5.cpp56
-rw-r--r--src/projections/urmfps.cpp76
-rw-r--r--src/projections/vandg.cpp106
-rw-r--r--src/projections/vandg2.cpp76
-rw-r--r--src/projections/vandg4.cpp55
-rw-r--r--src/projections/wag2.cpp38
-rw-r--r--src/projections/wag3.cpp50
-rw-r--r--src/projections/wag7.cpp30
-rw-r--r--src/projections/wink1.cpp46
-rw-r--r--src/projections/wink2.cpp56
108 files changed, 14282 insertions, 0 deletions
diff --git a/src/projections/aea.cpp b/src/projections/aea.cpp
new file mode 100644
index 00000000..c4a4a72a
--- /dev/null
+++ b/src/projections/aea.cpp
@@ -0,0 +1,224 @@
+/******************************************************************************
+ * Project: PROJ.4
+ * Purpose: Implementation of the aea (Albers Equal Area) projection.
+ * and the leac (Lambert Equal Area Conic) projection
+ * Author: Gerald Evenden (1995)
+ * Thomas Knudsen (2016) - revise/add regression tests
+ *
+ ******************************************************************************
+ * Copyright (c) 1995, Gerald Evenden
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************/
+
+#define PJ_LIB__
+#include "proj.h"
+#include <errno.h>
+#include "projects.h"
+#include "proj_math.h"
+
+
+# define EPS10 1.e-10
+# define TOL7 1.e-7
+
+PROJ_HEAD(aea, "Albers Equal Area") "\n\tConic Sph&Ell\n\tlat_1= lat_2=";
+PROJ_HEAD(leac, "Lambert Equal Area Conic") "\n\tConic, Sph&Ell\n\tlat_1= south";
+
+
+/* determine latitude angle phi-1 */
+# define N_ITER 15
+# define EPSILON 1.0e-7
+# define TOL 1.0e-10
+static double phi1_(double qs, double Te, double Tone_es) {
+ int i;
+ double Phi, sinpi, cospi, con, com, dphi;
+
+ Phi = asin (.5 * qs);
+ if (Te < EPSILON)
+ return( Phi );
+ i = N_ITER;
+ do {
+ sinpi = sin (Phi);
+ cospi = cos (Phi);
+ con = Te * sinpi;
+ com = 1. - con * con;
+ dphi = .5 * com * com / cospi * (qs / Tone_es -
+ sinpi / com + .5 / Te * log ((1. - con) /
+ (1. + con)));
+ Phi += dphi;
+ } while (fabs(dphi) > TOL && --i);
+ return( i ? Phi : HUGE_VAL );
+}
+
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double ec;
+ double n;
+ double c;
+ double dd;
+ double n2;
+ double rho0;
+ double rho;
+ double phi1;
+ double phi2;
+ double *en;
+ int ellips;
+};
+} // anonymous namespace
+
+
+static PJ *destructor (PJ *P, int errlev) { /* Destructor */
+ if (nullptr==P)
+ return nullptr;
+
+ if (nullptr==P->opaque)
+ return pj_default_destructor (P, errlev);
+
+ pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->en);
+ return pj_default_destructor (P, errlev);
+}
+
+
+
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoid/spheroid, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ Q->rho = Q->c - (Q->ellips ? Q->n * pj_qsfn(sin(lp.phi), P->e, P->one_es) : Q->n2 * sin(lp.phi));;
+ if (Q->rho < 0.) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ Q->rho = Q->dd * sqrt(Q->rho);
+ xy.x = Q->rho * sin( lp.lam *= Q->n );
+ xy.y = Q->rho0 - Q->rho * cos(lp.lam);
+ return xy;
+}
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoid/spheroid, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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->rho / Q->dd;
+ if (Q->ellips) {
+ lp.phi = (Q->c - lp.phi * lp.phi) / Q->n;
+ if (fabs(Q->ec - fabs(lp.phi)) > TOL7) {
+ if ((lp.phi = phi1_(lp.phi, P->e, P->one_es)) == HUGE_VAL) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+ } else
+ lp.phi = lp.phi < 0. ? -M_HALFPI : M_HALFPI;
+ } else if (fabs(lp.phi = (Q->c - lp.phi * lp.phi) / Q->n2) <= 1.)
+ lp.phi = asin(lp.phi);
+ else
+ lp.phi = lp.phi < 0. ? -M_HALFPI : M_HALFPI;
+ lp.lam = atan2(xy.x, xy.y) / Q->n;
+ } else {
+ lp.lam = 0.;
+ lp.phi = Q->n > 0. ? M_HALFPI : - M_HALFPI;
+ }
+ return lp;
+}
+
+
+
+static PJ *setup(PJ *P) {
+ double cosphi, sinphi;
+ int secant;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+
+ P->inv = e_inverse;
+ P->fwd = e_forward;
+
+ if (fabs(Q->phi1 + Q->phi2) < EPS10)
+ return destructor(P, PJD_ERR_CONIC_LAT_EQUAL);
+ Q->n = sinphi = sin(Q->phi1);
+ cosphi = cos(Q->phi1);
+ secant = fabs(Q->phi1 - Q->phi2) >= EPS10;
+ if( (Q->ellips = (P->es > 0.))) {
+ double ml1, m1;
+
+ if (!(Q->en = pj_enfn(P->es)))
+ return destructor(P, 0);
+ m1 = pj_msfn(sinphi, cosphi, P->es);
+ ml1 = pj_qsfn(sinphi, P->e, P->one_es);
+ if (secant) { /* secant cone */
+ double ml2, m2;
+
+ sinphi = sin(Q->phi2);
+ cosphi = cos(Q->phi2);
+ m2 = pj_msfn(sinphi, cosphi, P->es);
+ ml2 = pj_qsfn(sinphi, P->e, P->one_es);
+ if (ml2 == ml1)
+ return destructor(P, 0);
+
+ Q->n = (m1 * m1 - m2 * m2) / (ml2 - ml1);
+ }
+ Q->ec = 1. - .5 * P->one_es * log((1. - P->e) /
+ (1. + P->e)) / P->e;
+ Q->c = m1 * m1 + Q->n * ml1;
+ Q->dd = 1. / Q->n;
+ Q->rho0 = Q->dd * sqrt(Q->c - Q->n * pj_qsfn(sin(P->phi0),
+ P->e, P->one_es));
+ } else {
+ if (secant) Q->n = .5 * (Q->n + sin(Q->phi2));
+ Q->n2 = Q->n + Q->n;
+ Q->c = cosphi * cosphi + Q->n2 * sinphi;
+ Q->dd = 1. / Q->n;
+ Q->rho0 = Q->dd * sqrt(Q->c - Q->n2 * sin(P->phi0));
+ }
+
+ return P;
+}
+
+
+PJ *PROJECTION(aea) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+ P->destructor = destructor;
+
+ Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f;
+ Q->phi2 = pj_param(P->ctx, P->params, "rlat_2").f;
+ return setup(P);
+}
+
+
+PJ *PROJECTION(leac) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+ P->destructor = destructor;
+
+ Q->phi2 = pj_param(P->ctx, P->params, "rlat_1").f;
+ Q->phi1 = pj_param(P->ctx, P->params, "bsouth").i ? - M_HALFPI: M_HALFPI;
+ return setup(P);
+}
+
diff --git a/src/projections/aeqd.cpp b/src/projections/aeqd.cpp
new file mode 100644
index 00000000..1a350d90
--- /dev/null
+++ b/src/projections/aeqd.cpp
@@ -0,0 +1,327 @@
+/******************************************************************************
+ * Project: PROJ.4
+ * Purpose: Implementation of the aeqd (Azimuthal Equidistant) projection.
+ * Author: Gerald Evenden
+ *
+ ******************************************************************************
+ * Copyright (c) 1995, Gerald Evenden
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************/
+
+#define PJ_LIB__
+#include "geodesic.h"
+#include "proj.h"
+#include <errno.h>
+#include "projects.h"
+#include "proj_math.h"
+
+namespace { // anonymous namespace
+enum Mode {
+ N_POLE = 0,
+ S_POLE = 1,
+ EQUIT = 2,
+ OBLIQ = 3
+};
+} // anonymous namespace
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double sinph0;
+ double cosph0;
+ double *en;
+ double M1;
+ double N1;
+ double Mp;
+ double He;
+ double G;
+ enum Mode mode;
+ struct geod_geodesic g;
+};
+} // anonymous namespace
+
+PROJ_HEAD(aeqd, "Azimuthal Equidistant") "\n\tAzi, Sph&Ell\n\tlat_0 guam";
+
+#define EPS10 1.e-10
+#define TOL 1.e-14
+
+
+static PJ *destructor (PJ *P, int errlev) { /* Destructor */
+ if (nullptr==P)
+ return nullptr;
+
+ if (nullptr==P->opaque)
+ return pj_default_destructor (P, errlev);
+
+ pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->en);
+ return pj_default_destructor (P, errlev);
+}
+
+
+
+static XY e_guam_fwd(LP lp, PJ *P) { /* Guam elliptical */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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;
+}
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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;
+ /*-fallthrough*/
+ 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;
+}
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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.) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ 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;
+ /*-fallthrough*/
+ case S_POLE:
+ if (fabs(lp.phi - M_HALFPI) < EPS10) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ xy.x = (xy.y = (M_HALFPI + lp.phi)) * sin(lp.lam);
+ xy.y *= coslam;
+ break;
+ }
+ return xy;
+}
+
+
+static LP e_guam_inv(XY xy, PJ *P) { /* Guam elliptical */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double x2, t = 0.0;
+ 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;
+}
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double cosc, c_rh, sinc;
+
+ if ((c_rh = hypot(xy.x, xy.y)) > M_PI) {
+ if (c_rh - EPS10 > M_PI) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+ c_rh = M_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 * 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 = M_HALFPI - c_rh;
+ lp.lam = atan2(xy.x, -xy.y);
+ } else {
+ lp.phi = c_rh - M_HALFPI;
+ lp.lam = atan2(xy.x, xy.y);
+ }
+ return lp;
+}
+
+
+PJ *PROJECTION(aeqd) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+ P->destructor = destructor;
+
+ geod_init(&Q->g, P->a, P->es / (1 + sqrt(P->one_es)));
+
+ if (fabs(fabs(P->phi0) - M_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 == 0.0) {
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ } else {
+ if (!(Q->en = pj_enfn(P->es)))
+ return pj_default_destructor (P, 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(M_HALFPI, 1., 0., Q->en);
+ break;
+ case S_POLE:
+ Q->Mp = pj_mlfn(-M_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;
+}
+
+
diff --git a/src/projections/airy.cpp b/src/projections/airy.cpp
new file mode 100644
index 00000000..0eb5efd7
--- /dev/null
+++ b/src/projections/airy.cpp
@@ -0,0 +1,155 @@
+/******************************************************************************
+ * Project: PROJ.4
+ * Purpose: Implementation of the airy (Airy) projection.
+ * Author: Gerald Evenden (1995)
+ * Thomas Knudsen (2016) - revise/add regression tests
+ *
+ ******************************************************************************
+ * Copyright (c) 1995, Gerald Evenden
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************/
+
+#define PJ_LIB__
+#include "proj.h"
+#include <errno.h>
+#include "projects.h"
+
+PROJ_HEAD(airy, "Airy") "\n\tMisc Sph, no inv\n\tno_cut lat_b=";
+
+
+namespace { // anonymous namespace
+enum Mode {
+ N_POLE = 0,
+ S_POLE = 1,
+ EQUIT = 2,
+ OBLIQ = 3
+};
+} // anonymous namespace
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double p_halfpi;
+ double sinph0;
+ double cosph0;
+ double Cb;
+ enum Mode mode;
+ int no_cut; /* do not cut at hemisphere limit */
+};
+} // anonymous namespace
+
+
+# define EPS 1.e-10
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double sinlam, coslam, cosphi, sinphi, t, s, Krho, cosz;
+
+ sinlam = sin(lp.lam);
+ coslam = cos(lp.lam);
+ switch (Q->mode) {
+ case EQUIT:
+ case OBLIQ:
+ sinphi = sin(lp.phi);
+ cosphi = cos(lp.phi);
+ cosz = cosphi * coslam;
+ if (Q->mode == OBLIQ)
+ cosz = Q->sinph0 * sinphi + Q->cosph0 * cosz;
+ if (!Q->no_cut && cosz < -EPS) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ if (fabs(s = 1. - cosz) > EPS) {
+ t = 0.5 * (1. + cosz);
+ Krho = -log(t)/s - Q->Cb / t;
+ } else
+ Krho = 0.5 - Q->Cb;
+ xy.x = Krho * cosphi * sinlam;
+ if (Q->mode == OBLIQ)
+ xy.y = Krho * (Q->cosph0 * sinphi -
+ Q->sinph0 * cosphi * coslam);
+ else
+ xy.y = Krho * sinphi;
+ break;
+ case S_POLE:
+ case N_POLE:
+ lp.phi = fabs(Q->p_halfpi - lp.phi);
+ if (!Q->no_cut && (lp.phi - EPS) > M_HALFPI) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ if ((lp.phi *= 0.5) > EPS) {
+ t = tan(lp.phi);
+ Krho = -2.*(log(cos(lp.phi)) / t + t * Q->Cb);
+ xy.x = Krho * sinlam;
+ xy.y = Krho * coslam;
+ if (Q->mode == N_POLE)
+ xy.y = -xy.y;
+ } else
+ xy.x = xy.y = 0.;
+ }
+ return xy;
+}
+
+
+
+
+PJ *PROJECTION(airy) {
+ double beta;
+
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+
+ P->opaque = Q;
+
+ Q->no_cut = pj_param(P->ctx, P->params, "bno_cut").i;
+ beta = 0.5 * (M_HALFPI - pj_param(P->ctx, P->params, "rlat_b").f);
+ if (fabs(beta) < EPS)
+ Q->Cb = -0.5;
+ else {
+ Q->Cb = 1./tan(beta);
+ Q->Cb *= Q->Cb * log(cos(beta));
+ }
+
+ if (fabs(fabs(P->phi0) - M_HALFPI) < EPS)
+ if (P->phi0 < 0.) {
+ Q->p_halfpi = -M_HALFPI;
+ Q->mode = S_POLE;
+ } else {
+ Q->p_halfpi = M_HALFPI;
+ Q->mode = N_POLE;
+ }
+ else {
+ if (fabs(P->phi0) < EPS)
+ Q->mode = EQUIT;
+ else {
+ Q->mode = OBLIQ;
+ Q->sinph0 = sin(P->phi0);
+ Q->cosph0 = cos(P->phi0);
+ }
+ }
+ P->fwd = s_forward;
+ P->es = 0.;
+ return P;
+}
+
+
diff --git a/src/projections/aitoff.cpp b/src/projections/aitoff.cpp
new file mode 100644
index 00000000..effd2c29
--- /dev/null
+++ b/src/projections/aitoff.cpp
@@ -0,0 +1,201 @@
+/******************************************************************************
+ * Project: PROJ.4
+ * Purpose: Implementation of the aitoff (Aitoff) and wintri (Winkel Tripel)
+ * projections.
+ * Author: Gerald Evenden (1995)
+ * Drazen Tutic, Lovro Gradiser (2015) - add inverse
+ * Thomas Knudsen (2016) - revise/add regression tests
+ *
+ ******************************************************************************
+ * Copyright (c) 1995, Gerald Evenden
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************/
+
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+
+namespace { // anonymous namespace
+enum Mode {
+ AITOFF = 0,
+ WINKEL_TRIPEL = 1
+};
+} // anonymous namespace
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double cosphi1;
+ enum Mode mode;
+};
+} // anonymous namespace
+
+
+PROJ_HEAD(aitoff, "Aitoff") "\n\tMisc Sph";
+PROJ_HEAD(wintri, "Winkel Tripel") "\n\tMisc Sph\n\tlat_1";
+
+
+
+#if 0
+FORWARD(s_forward); /* spheroid */
+#endif
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double c, d;
+
+ if((d = acos(cos(lp.phi) * cos(c = 0.5 * lp.lam))) != 0.0) {/* basic Aitoff */
+ xy.x = 2. * d * cos(lp.phi) * sin(c) * (xy.y = 1. / sin(d));
+ xy.y *= d * sin(lp.phi);
+ } else
+ xy.x = xy.y = 0.;
+ if (Q->mode == WINKEL_TRIPEL) {
+ xy.x = (xy.x + lp.lam * Q->cosphi1) * 0.5;
+ xy.y = (xy.y + lp.phi) * 0.5;
+ }
+ return (xy);
+}
+
+/***********************************************************************************
+*
+* Inverse functions added by Drazen Tutic and Lovro Gradiser based on paper:
+*
+* I.Özbug Biklirici and Cengizhan Ipbüker. A General Algorithm for the Inverse
+* Transformation of Map Projections Using Jacobian Matrices. In Proceedings of the
+* Third International Symposium Mathematical & Computational Applications,
+* pages 175{182, Turkey, September 2002.
+*
+* Expected accuracy is defined by EPSILON = 1e-12. Should be appropriate for
+* most applications of Aitoff and Winkel Tripel projections.
+*
+* Longitudes of 180W and 180E can be mixed in solution obtained.
+*
+* Inverse for Aitoff projection in poles is undefined, longitude value of 0 is assumed.
+*
+* Contact : dtutic@geof.hr
+* Date: 2015-02-16
+*
+************************************************************************************/
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ int iter, MAXITER = 10, round = 0, MAXROUND = 20;
+ double EPSILON = 1e-12, D, C, f1, f2, f1p, f1l, f2p, f2l, dp, dl, sl, sp, cp, cl, x, y;
+
+ if ((fabs(xy.x) < EPSILON) && (fabs(xy.y) < EPSILON )) { lp.phi = 0.; lp.lam = 0.; return lp; }
+
+ /* initial values for Newton-Raphson method */
+ lp.phi = xy.y; lp.lam = xy.x;
+ do {
+ iter = 0;
+ do {
+ sl = sin(lp.lam * 0.5); cl = cos(lp.lam * 0.5);
+ sp = sin(lp.phi); cp = cos(lp.phi);
+ D = cp * cl;
+ C = 1. - D * D;
+ D = acos(D) / pow(C, 1.5);
+ f1 = 2. * D * C * cp * sl;
+ f2 = D * C * sp;
+ f1p = 2.* (sl * cl * sp * cp / C - D * sp * sl);
+ f1l = cp * cp * sl * sl / C + D * cp * cl * sp * sp;
+ f2p = sp * sp * cl / C + D * sl * sl * cp;
+ f2l = 0.5 * (sp * cp * sl / C - D * sp * cp * cp * sl * cl);
+ if (Q->mode == WINKEL_TRIPEL) {
+ f1 = 0.5 * (f1 + lp.lam * Q->cosphi1);
+ f2 = 0.5 * (f2 + lp.phi);
+ f1p *= 0.5;
+ f1l = 0.5 * (f1l + Q->cosphi1);
+ f2p = 0.5 * (f2p + 1.);
+ f2l *= 0.5;
+ }
+ f1 -= xy.x; f2 -= xy.y;
+ dl = (f2 * f1p - f1 * f2p) / (dp = f1p * f2l - f2p * f1l);
+ dp = (f1 * f2l - f2 * f1l) / dp;
+ dl = fmod(dl, M_PI); /* set to interval [-M_PI, M_PI] */
+ lp.phi -= dp; lp.lam -= dl;
+ } while ((fabs(dp) > EPSILON || fabs(dl) > EPSILON) && (iter++ < MAXITER));
+ if (lp.phi > M_PI_2) lp.phi -= 2.*(lp.phi-M_PI_2); /* correct if symmetrical solution for Aitoff */
+ if (lp.phi < -M_PI_2) lp.phi -= 2.*(lp.phi+M_PI_2); /* correct if symmetrical solution for Aitoff */
+ if ((fabs(fabs(lp.phi) - M_PI_2) < EPSILON) && (Q->mode == AITOFF)) lp.lam = 0.; /* if pole in Aitoff, return longitude of 0 */
+
+ /* calculate x,y coordinates with solution obtained */
+ if((D = acos(cos(lp.phi) * cos(C = 0.5 * lp.lam))) != 0.0) {/* Aitoff */
+ x = 2. * D * cos(lp.phi) * sin(C) * (y = 1. / sin(D));
+ y *= D * sin(lp.phi);
+ } else
+ x = y = 0.;
+ if (Q->mode == WINKEL_TRIPEL) {
+ x = (x + lp.lam * Q->cosphi1) * 0.5;
+ y = (y + lp.phi) * 0.5;
+ }
+ /* if too far from given values of x,y, repeat with better approximation of phi,lam */
+ } while (((fabs(xy.x-x) > EPSILON) || (fabs(xy.y-y) > EPSILON)) && (round++ < MAXROUND));
+
+ if (iter == MAXITER && round == MAXROUND)
+ {
+ pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT );
+ /* fprintf(stderr, "Warning: Accuracy of 1e-12 not reached. Last increments: dlat=%e and dlon=%e\n", dp, dl); */
+ }
+
+ return lp;
+}
+
+
+static PJ *setup(PJ *P) {
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ P->es = 0.;
+ return P;
+}
+
+
+PJ *PROJECTION(aitoff) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor(P, ENOMEM);
+ P->opaque = Q;
+
+ Q->mode = AITOFF;
+ return setup(P);
+}
+
+
+PJ *PROJECTION(wintri) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor(P, ENOMEM);
+ P->opaque = Q;
+
+ Q->mode = WINKEL_TRIPEL;
+ if (pj_param(P->ctx, P->params, "tlat_1").i) {
+ if ((Q->cosphi1 = cos(pj_param(P->ctx, P->params, "rlat_1").f)) == 0.)
+ return pj_default_destructor (P, PJD_ERR_LAT_LARGER_THAN_90);
+ }
+ else /* 50d28' or acos(2/pi) */
+ Q->cosphi1 = 0.636619772367581343;
+ return setup(P);
+}
diff --git a/src/projections/august.cpp b/src/projections/august.cpp
new file mode 100644
index 00000000..b5a21ef7
--- /dev/null
+++ b/src/projections/august.cpp
@@ -0,0 +1,34 @@
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(august, "August Epicycloidal") "\n\tMisc Sph, no inv";
+#define M 1.333333333333333
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ double t, c1, c, x1, x12, y1, y12;
+ (void) P;
+
+ t = tan(.5 * lp.phi);
+ c1 = sqrt(1. - t * t);
+ c = 1. + c1 * cos(lp.lam *= .5);
+ x1 = sin(lp.lam) * c1 / c;
+ y1 = t / c;
+ xy.x = M * x1 * (3. + (x12 = x1 * x1) - 3. * (y12 = y1 * y1));
+ xy.y = M * y1 * (3. + 3. * x12 - y12);
+ return (xy);
+}
+
+
+
+
+PJ *PROJECTION(august) {
+ P->inv = nullptr;
+ P->fwd = s_forward;
+ P->es = 0.;
+ return P;
+}
diff --git a/src/projections/bacon.cpp b/src/projections/bacon.cpp
new file mode 100644
index 00000000..6c6350fe
--- /dev/null
+++ b/src/projections/bacon.cpp
@@ -0,0 +1,81 @@
+# define HLFPI2 2.46740110027233965467 /* (pi/2)^2 */
+# define EPS 1e-10
+#define PJ_LIB__
+#include <errno.h>
+#include <math.h>
+
+#include "projects.h"
+
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ int bacn;
+ int ortl;
+};
+} // anonymous namespace
+
+PROJ_HEAD(apian, "Apian Globular I") "\n\tMisc Sph, no inv";
+PROJ_HEAD(ortel, "Ortelius Oval") "\n\tMisc Sph, no inv";
+PROJ_HEAD(bacon, "Bacon Globular") "\n\tMisc Sph, no inv";
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double ax, f;
+
+ xy.y = Q->bacn ? M_HALFPI * sin(lp.phi) : lp.phi;
+ if ((ax = fabs(lp.lam)) >= EPS) {
+ if (Q->ortl && ax >= M_HALFPI)
+ xy.x = sqrt(HLFPI2 - lp.phi * lp.phi + EPS) + ax - M_HALFPI;
+ else {
+ f = 0.5 * (HLFPI2 / ax + ax);
+ xy.x = ax - f + sqrt(f * f - xy.y * xy.y);
+ }
+ if (lp.lam < 0.) xy.x = - xy.x;
+ } else
+ xy.x = 0.;
+ return (xy);
+}
+
+
+
+PJ *PROJECTION(bacon) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->bacn = 1;
+ Q->ortl = 0;
+ P->es = 0.;
+ P->fwd = s_forward;
+ return P;
+}
+
+
+PJ *PROJECTION(apian) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->bacn = Q->ortl = 0;
+ P->es = 0.;
+ P->fwd = s_forward;
+ return P;
+}
+
+
+PJ *PROJECTION(ortel) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->bacn = 0;
+ Q->ortl = 1;
+ P->es = 0.;
+ P->fwd = s_forward;
+ return P;
+}
diff --git a/src/projections/bertin1953.cpp b/src/projections/bertin1953.cpp
new file mode 100644
index 00000000..2203d6f1
--- /dev/null
+++ b/src/projections/bertin1953.cpp
@@ -0,0 +1,96 @@
+/*
+ Created by Jacques Bertin in 1953, this projection was the go-to choice
+ of the French cartographic school when they wished to represent phenomena
+ on a global scale.
+
+ Formula designed by Philippe Rivière, 2017.
+ https://visionscarto.net/bertin-projection-1953
+
+ Port to PROJ by Philippe Rivière, 21 September 2018
+*/
+
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj_internal.h"
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(bertin1953, "Bertin 1953")
+ "\n\tMisc Sph no inv.";
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double cos_delta_phi, sin_delta_phi, cos_delta_gamma, sin_delta_gamma, deltaLambda;
+};
+} // anonymous namespace
+
+
+static XY s_forward (LP lp, PJ *P) {
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+
+ double fu = 1.4, k = 12., w = 1.68, d;
+
+ /* Rotate */
+ double cosphi, x, y, z, z0;
+ lp.lam += PJ_TORAD(-16.5);
+ cosphi = cos(lp.phi);
+ x = cos(lp.lam) * cosphi;
+ y = sin(lp.lam) * cosphi;
+ z = sin(lp.phi);
+ z0 = z * Q->cos_delta_phi + x * Q->sin_delta_phi;
+ lp.lam = atan2(y * Q->cos_delta_gamma - z0 * Q->sin_delta_gamma,
+ x * Q->cos_delta_phi - z * Q->sin_delta_phi);
+ z0 = z0 * Q->cos_delta_gamma + y * Q->sin_delta_gamma;
+ lp.phi = asin(z0);
+
+ lp.lam = adjlon(lp.lam);
+
+ /* Adjust pre-projection */
+ if (lp.lam + lp.phi < -fu) {
+ d = (lp.lam - lp.phi + 1.6) * (lp.lam + lp.phi + fu) / 8.;
+ lp.lam += d;
+ lp.phi -= 0.8 * d * sin(lp.phi + M_PI / 2.);
+ }
+
+ /* Project with Hammer (1.68,2) */
+ cosphi = cos(lp.phi);
+ d = sqrt(2./(1. + cosphi * cos(lp.lam / 2.)));
+ xy.x = w * d * cosphi * sin(lp.lam / 2.);
+ xy.y = d * sin(lp.phi);
+
+ /* Adjust post-projection */
+ d = (1. - cos(lp.lam * lp.phi)) / k;
+ if (xy.y < 0.) {
+ xy.x *= 1. + d;
+ }
+ if (xy.y > 0.) {
+ xy.x *= 1. + d / 1.5 * xy.x * xy.x;
+ }
+
+ return xy;
+}
+
+
+PJ *PROJECTION(bertin1953) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ P->lam0 = 0;
+ P->phi0 = PJ_TORAD(-42.);
+
+ Q->cos_delta_phi = cos(P->phi0);
+ Q->sin_delta_phi = sin(P->phi0);
+ Q->cos_delta_gamma = 1.;
+ Q->sin_delta_gamma = 0.;
+
+ P->es = 0.;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/bipc.cpp b/src/projections/bipc.cpp
new file mode 100644
index 00000000..19a6bbe1
--- /dev/null
+++ b/src/projections/bipc.cpp
@@ -0,0 +1,176 @@
+#define PJ_LIB__
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+#include "proj_math.h"
+
+PROJ_HEAD(bipc, "Bipolar conic of western hemisphere") "\n\tConic Sph";
+
+# define EPS 1e-10
+# define EPS10 1e-10
+# define ONEEPS 1.000000001
+# define NITER 10
+# define lamB -.34894976726250681539
+# define n .63055844881274687180
+# define F 1.89724742567461030582
+# define Azab .81650043674686363166
+# define Azba 1.82261843856185925133
+# define T 1.27246578267089012270
+# define rhoc 1.20709121521568721927
+# define cAzc .69691523038678375519
+# define sAzc .71715351331143607555
+# define C45 .70710678118654752469
+# define S45 .70710678118654752410
+# define C20 .93969262078590838411
+# define S20 -.34202014332566873287
+# define R110 1.91986217719376253360
+# define R104 1.81514242207410275904
+
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ int noskew;
+};
+} // anonymous namespace
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double cphi, sphi, tphi, t, al, Az, z, Av, cdlam, sdlam, r;
+ int tag;
+
+ cphi = cos(lp.phi);
+ sphi = sin(lp.phi);
+ cdlam = cos(sdlam = lamB - lp.lam);
+ sdlam = sin(sdlam);
+ if (fabs(fabs(lp.phi) - M_HALFPI) < EPS10) {
+ Az = lp.phi < 0. ? M_PI : 0.;
+ tphi = HUGE_VAL;
+ } else {
+ tphi = sphi / cphi;
+ Az = atan2(sdlam , C45 * (tphi - cdlam));
+ }
+ if( (tag = (Az > Azba)) ) {
+ cdlam = cos(sdlam = lp.lam + R110);
+ sdlam = sin(sdlam);
+ z = S20 * sphi + C20 * cphi * cdlam;
+ if (fabs(z) > 1.) {
+ if (fabs(z) > ONEEPS) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ else z = z < 0. ? -1. : 1.;
+ } else
+ z = acos(z);
+ if (tphi != HUGE_VAL)
+ Az = atan2(sdlam, (C20 * tphi - S20 * cdlam));
+ Av = Azab;
+ xy.y = rhoc;
+ } else {
+ z = S45 * (sphi + cphi * cdlam);
+ if (fabs(z) > 1.) {
+ if (fabs(z) > ONEEPS) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ else z = z < 0. ? -1. : 1.;
+ } else
+ z = acos(z);
+ Av = Azba;
+ xy.y = -rhoc;
+ }
+ if (z < 0.) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ r = F * (t = pow(tan(.5 * z), n));
+ if ((al = .5 * (R104 - z)) < 0.) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ al = (t + pow(al, n)) / T;
+ if (fabs(al) > 1.) {
+ if (fabs(al) > ONEEPS) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ else al = al < 0. ? -1. : 1.;
+ } else
+ al = acos(al);
+ if (fabs(t = n * (Av - Az)) < al)
+ r /= cos(al + (tag ? t : -t));
+ xy.x = r * sin(t);
+ xy.y += (tag ? -r : r) * cos(t);
+ if (Q->noskew) {
+ t = xy.x;
+ xy.x = -xy.x * cAzc - xy.y * sAzc;
+ xy.y = -xy.y * cAzc + t * sAzc;
+ }
+ return (xy);
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double t, r, rp, rl, al, z = 0.0, fAz, Az, s, c, Av;
+ int neg, i;
+
+ if (Q->noskew) {
+ t = xy.x;
+ xy.x = -xy.x * cAzc + xy.y * sAzc;
+ xy.y = -xy.y * cAzc - t * sAzc;
+ }
+ if( (neg = (xy.x < 0.)) ) {
+ xy.y = rhoc - xy.y;
+ s = S20;
+ c = C20;
+ Av = Azab;
+ } else {
+ xy.y += rhoc;
+ s = S45;
+ c = C45;
+ Av = Azba;
+ }
+ rl = rp = r = hypot(xy.x, xy.y);
+ fAz = fabs(Az = atan2(xy.x, xy.y));
+ for (i = NITER; i ; --i) {
+ z = 2. * atan(pow(r / F,1 / n));
+ al = acos((pow(tan(.5 * z), n) +
+ pow(tan(.5 * (R104 - z)), n)) / T);
+ if (fAz < al)
+ r = rp * cos(al + (neg ? Az : -Az));
+ if (fabs(rl - r) < EPS)
+ break;
+ rl = r;
+ }
+ if (! i) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+ Az = Av - Az / n;
+ lp.phi = asin(s * cos(z) + c * sin(z) * cos(Az));
+ lp.lam = atan2(sin(Az), c / tan(z) - s * cos(Az));
+ if (neg)
+ lp.lam -= R110;
+ else
+ lp.lam = lamB - lp.lam;
+ return (lp);
+}
+
+
+PJ *PROJECTION(bipc) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->noskew = pj_param(P->ctx, P->params, "bns").i;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ P->es = 0.;
+ return P;
+}
diff --git a/src/projections/boggs.cpp b/src/projections/boggs.cpp
new file mode 100644
index 00000000..119357c0
--- /dev/null
+++ b/src/projections/boggs.cpp
@@ -0,0 +1,43 @@
+#define PJ_LIB__
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(boggs, "Boggs Eumorphic") "\n\tPCyl, no inv, Sph";
+# define NITER 20
+# define EPS 1e-7
+# define FXC 2.00276
+# define FXC2 1.11072
+# define FYC 0.49931
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ double theta, th1, c;
+ int i;
+ (void) P;
+
+ theta = lp.phi;
+ if (fabs(fabs(lp.phi) - M_HALFPI) < EPS)
+ xy.x = 0.;
+ else {
+ c = sin(theta) * M_PI;
+ for (i = NITER; i; --i) {
+ theta -= th1 = (theta + sin(theta) - c) /
+ (1. + cos(theta));
+ if (fabs(th1) < EPS) break;
+ }
+ theta *= 0.5;
+ xy.x = FXC * lp.lam / (1. / cos(lp.phi) + FXC2 / cos(theta));
+ }
+ xy.y = FYC * (lp.phi + M_SQRT2 * sin(theta));
+ return (xy);
+}
+
+
+
+PJ *PROJECTION(boggs) {
+ P->es = 0.;
+ P->fwd = s_forward;
+ return P;
+}
diff --git a/src/projections/bonne.cpp b/src/projections/bonne.cpp
new file mode 100644
index 00000000..385c1c4b
--- /dev/null
+++ b/src/projections/bonne.cpp
@@ -0,0 +1,136 @@
+#define PJ_LIB__
+#include <errno.h>
+#include "proj.h"
+#include "projects.h"
+#include "proj_math.h"
+
+
+PROJ_HEAD(bonne, "Bonne (Werner lat_1=90)")
+ "\n\tConic Sph&Ell\n\tlat_1=";
+#define EPS10 1e-10
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double phi1;
+ double cphi1;
+ double am1;
+ double m1;
+ double *en;
+};
+} // anonymous namespace
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double rh, E, c;
+
+ rh = Q->am1 + Q->m1 - pj_mlfn(lp.phi, E = sin(lp.phi), c = cos(lp.phi), Q->en);
+ E = c * lp.lam / (rh * sqrt(1. - P->es * E * E));
+ xy.x = rh * sin(E);
+ xy.y = Q->am1 - rh * cos(E);
+ return xy;
+}
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double E, rh;
+
+ rh = Q->cphi1 + Q->phi1 - lp.phi;
+ if (fabs(rh) > EPS10) {
+ xy.x = rh * sin(E = lp.lam * cos(lp.phi) / rh);
+ xy.y = Q->cphi1 - rh * cos(E);
+ } else
+ xy.x = xy.y = 0.;
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double rh;
+
+ rh = hypot(xy.x, xy.y = Q->cphi1 - xy.y);
+ lp.phi = Q->cphi1 + Q->phi1 - rh;
+ if (fabs(lp.phi) > M_HALFPI) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+ if (fabs(fabs(lp.phi) - M_HALFPI) <= EPS10)
+ lp.lam = 0.;
+ else
+ lp.lam = rh * atan2(xy.x, xy.y) / 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 = static_cast<struct pj_opaque*>(P->opaque);
+ double s, rh;
+
+ rh = hypot(xy.x, xy.y = Q->am1 - xy.y);
+ lp.phi = pj_inv_mlfn(P->ctx, Q->am1 + Q->m1 - rh, P->es, Q->en);
+ if ((s = fabs(lp.phi)) < M_HALFPI) {
+ s = sin(lp.phi);
+ lp.lam = rh * atan2(xy.x, xy.y) *
+ sqrt(1. - P->es * s * s) / cos(lp.phi);
+ } else if (fabs(s - M_HALFPI) <= EPS10)
+ lp.lam = 0.;
+ else {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+ return lp;
+}
+
+
+
+static PJ *destructor (PJ *P, int errlev) { /* Destructor */
+ if (nullptr==P)
+ return nullptr;
+
+ if (nullptr==P->opaque)
+ return pj_default_destructor (P, errlev);
+
+ pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->en);
+ return pj_default_destructor (P, errlev);
+}
+
+
+PJ *PROJECTION(bonne) {
+ double c;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+ P->destructor = destructor;
+
+ Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f;
+ if (fabs(Q->phi1) < EPS10)
+ return destructor (P, PJD_ERR_LAT1_IS_ZERO);
+
+ if (P->es != 0.0) {
+ Q->en = pj_enfn(P->es);
+ if (nullptr==Q->en)
+ return destructor(P, ENOMEM);
+ Q->m1 = pj_mlfn(Q->phi1, Q->am1 = sin(Q->phi1),
+ c = cos(Q->phi1), Q->en);
+ Q->am1 = c / (sqrt(1. - P->es * Q->am1 * Q->am1) * Q->am1);
+ P->inv = e_inverse;
+ P->fwd = e_forward;
+ } else {
+ if (fabs(Q->phi1) + EPS10 >= M_HALFPI)
+ Q->cphi1 = 0.;
+ else
+ Q->cphi1 = 1. / tan(Q->phi1);
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ }
+ return P;
+}
+
+
diff --git a/src/projections/calcofi.cpp b/src/projections/calcofi.cpp
new file mode 100644
index 00000000..e81e4d2a
--- /dev/null
+++ b/src/projections/calcofi.cpp
@@ -0,0 +1,163 @@
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+#include "proj_api.h"
+
+PROJ_HEAD(calcofi,
+ "Cal Coop Ocean Fish Invest Lines/Stations") "\n\tCyl, Sph&Ell";
+
+
+/* Conversions for the California Cooperative Oceanic Fisheries Investigations
+Line/Station coordinate system following the algorithm of:
+Eber, L.E., and R.P. Hewitt. 1979. Conversion algorithms for the CalCOFI
+station grid. California Cooperative Oceanic Fisheries Investigations Reports
+20:135-137. (corrected for typographical errors).
+http://www.calcofi.org/publications/calcofireports/v20/Vol_20_Eber___Hewitt.pdf
+They assume 1 unit of CalCOFI Line == 1/5 degree in longitude or
+meridional units at reference point O, and similarly 1 unit of CalCOFI
+Station == 1/15 of a degree at O.
+By convention, CalCOFI Line/Station conversions use Clarke 1866 but we use
+whatever ellipsoid is provided. */
+
+
+#define EPS10 1.e-10
+#define DEG_TO_LINE 5
+#define DEG_TO_STATION 15
+#define LINE_TO_RAD 0.0034906585039886592
+#define STATION_TO_RAD 0.0011635528346628863
+#define PT_O_LINE 80 /* reference point O is at line 80, */
+#define PT_O_STATION 60 /* station 60, */
+#define PT_O_LAMBDA -2.1144663887911301 /* lon -121.15 and */
+#define PT_O_PHI 0.59602993955606354 /* lat 34.15 */
+#define ROTATION_ANGLE 0.52359877559829882 /*CalCOFI angle of 30 deg in rad */
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ XY xy = {0.0,0.0};
+ double oy; /* pt O y value in Mercator */
+ double l1; /* l1 and l2 are distances calculated using trig that sum
+ to the east/west distance between point O and point xy */
+ double l2;
+ double ry; /* r is the point on the same station as o (60) and the same
+ line as xy xy, r, o form a right triangle */
+
+ if (fabs(fabs(lp.phi) - M_HALFPI) <= EPS10) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+
+ xy.x = lp.lam;
+ xy.y = -log(pj_tsfn(lp.phi, sin(lp.phi), P->e)); /* Mercator transform xy*/
+ oy = -log(pj_tsfn(PT_O_PHI, sin(PT_O_PHI), P->e));
+ l1 = (xy.y - oy) * tan(ROTATION_ANGLE);
+ l2 = -xy.x - l1 + PT_O_LAMBDA;
+ ry = l2 * cos(ROTATION_ANGLE) * sin(ROTATION_ANGLE) + xy.y;
+ ry = pj_phi2(P->ctx, exp(-ry), P->e); /*inverse Mercator*/
+ xy.x = PT_O_LINE - RAD_TO_DEG *
+ (ry - PT_O_PHI) * DEG_TO_LINE / cos(ROTATION_ANGLE);
+ xy.y = PT_O_STATION + RAD_TO_DEG *
+ (ry - lp.phi) * DEG_TO_STATION / sin(ROTATION_ANGLE);
+ /* set a = 1, x0 = 0, and y0 = 0 so that no further unit adjustments
+ are done */
+
+ return xy;
+}
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ double oy;
+ double l1;
+ double l2;
+ double ry;
+ if (fabs(fabs(lp.phi) - M_HALFPI) <= EPS10) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ xy.x = lp.lam;
+ xy.y = log(tan(M_FORTPI + .5 * lp.phi));
+ oy = log(tan(M_FORTPI + .5 * PT_O_PHI));
+ l1 = (xy.y - oy) * tan(ROTATION_ANGLE);
+ l2 = -xy.x - l1 + PT_O_LAMBDA;
+ ry = l2 * cos(ROTATION_ANGLE) * sin(ROTATION_ANGLE) + xy.y;
+ ry = M_HALFPI - 2. * atan(exp(-ry));
+ xy.x = PT_O_LINE - RAD_TO_DEG *
+ (ry - PT_O_PHI) * DEG_TO_LINE / cos(ROTATION_ANGLE);
+ xy.y = PT_O_STATION + RAD_TO_DEG *
+ (ry - lp.phi) * DEG_TO_STATION / sin(ROTATION_ANGLE);
+
+ return xy;
+}
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */
+ LP lp = {0.0,0.0};
+ double ry; /* y value of point r */
+ double oymctr; /* Mercator-transformed y value of point O */
+ double rymctr; /* Mercator-transformed ry */
+ double xymctr; /* Mercator-transformed xy.y */
+ double l1;
+ double l2;
+
+ ry = PT_O_PHI - LINE_TO_RAD * (xy.x - PT_O_LINE) *
+ cos(ROTATION_ANGLE);
+ lp.phi = ry - STATION_TO_RAD * (xy.y - PT_O_STATION) * sin(ROTATION_ANGLE);
+ oymctr = -log(pj_tsfn(PT_O_PHI, sin(PT_O_PHI), P->e));
+ rymctr = -log(pj_tsfn(ry, sin(ry), P->e));
+ xymctr = -log(pj_tsfn(lp.phi, sin(lp.phi), P->e));
+ l1 = (xymctr - oymctr) * tan(ROTATION_ANGLE);
+ l2 = (rymctr - xymctr) / (cos(ROTATION_ANGLE) * sin(ROTATION_ANGLE));
+ lp.lam = PT_O_LAMBDA - (l1 + l2);
+
+ return lp;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ double ry;
+ double oymctr;
+ double rymctr;
+ double xymctr;
+ double l1;
+ double l2;
+ (void) P;
+
+ ry = PT_O_PHI - LINE_TO_RAD * (xy.x - PT_O_LINE) *
+ cos(ROTATION_ANGLE);
+ lp.phi = ry - STATION_TO_RAD * (xy.y - PT_O_STATION) * sin(ROTATION_ANGLE);
+ oymctr = log(tan(M_FORTPI + .5 * PT_O_PHI));
+ rymctr = log(tan(M_FORTPI + .5 * ry));
+ xymctr = log(tan(M_FORTPI + .5 * lp.phi));
+ l1 = (xymctr - oymctr) * tan(ROTATION_ANGLE);
+ l2 = (rymctr - xymctr) / (cos(ROTATION_ANGLE) * sin(ROTATION_ANGLE));
+ lp.lam = PT_O_LAMBDA - (l1 + l2);
+
+ return lp;
+}
+
+
+PJ *PROJECTION(calcofi) {
+ P->opaque = nullptr;
+
+ /* if the user has specified +lon_0 or +k0 for some reason,
+ we're going to ignore it so that xy is consistent with point O */
+ P->lam0 = 0;
+ P->ra = 1;
+ P->a = 1;
+ P->x0 = 0;
+ P->y0 = 0;
+ P->over = 1;
+
+ if (P->es != 0.0) { /* ellipsoid */
+ P->inv = e_inverse;
+ P->fwd = e_forward;
+ } else { /* sphere */
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ }
+ return P;
+}
diff --git a/src/projections/cass.cpp b/src/projections/cass.cpp
new file mode 100644
index 00000000..c831558c
--- /dev/null
+++ b/src/projections/cass.cpp
@@ -0,0 +1,123 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(cass, "Cassini") "\n\tCyl, Sph&Ell";
+
+
+# define C1 .16666666666666666666
+# define C2 .00833333333333333333
+# define C3 .04166666666666666666
+# define C4 .33333333333333333333
+# define C5 .06666666666666666666
+
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double *en;
+ double m0;
+};
+} // anonymous namespace
+
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ double n, t, a1, c, a2, tn;
+ XY xy = {0.0, 0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+
+ xy.y = pj_mlfn (lp.phi, n = sin (lp.phi), c = cos (lp.phi), Q->en);
+
+ n = 1./sqrt(1. - P->es * n*n);
+ tn = tan(lp.phi); t = tn * tn;
+ a1 = lp.lam * c;
+ c *= P->es * c / (1 - P->es);
+ a2 = a1 * a1;
+
+ xy.x = n * a1 * (1. - a2 * t *
+ (C1 - (8. - t + 8. * c) * a2 * C2));
+ xy.y -= Q->m0 - n * tn * a2 *
+ (.5 + (5. - t + 6. * c) * a2 * C3);
+
+ return xy;
+}
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0, 0.0};
+ xy.x = asin (cos (lp.phi) * sin (lp.lam));
+ xy.y = atan2 (tan (lp.phi), cos (lp.lam)) - P->phi0;
+ return xy;
+}
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */
+ double n, t, r, dd, d2, tn, ph1;
+ LP lp = {0.0, 0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+
+ ph1 = pj_inv_mlfn (P->ctx, Q->m0 + xy.y, P->es, Q->en);
+ tn = tan (ph1); t = tn*tn;
+ n = sin (ph1);
+ r = 1. / (1. - P->es * n * n);
+ n = sqrt (r);
+ r *= (1. - P->es) * n;
+ dd = xy.x / n;
+ d2 = dd * dd;
+ lp.phi = ph1 - (n * tn / r) * d2 *
+ (.5 - (1. + 3. * t) * d2 * C3);
+ lp.lam = dd * (1. + t * d2 *
+ (-C4 + (1. + 3. * t) * d2 * C5)) / cos (ph1);
+ return lp;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ double dd;
+ lp.phi = asin(sin(dd = xy.y + P->phi0) * cos(xy.x));
+ lp.lam = atan2(tan(xy.x), cos(dd));
+ return lp;
+}
+
+static PJ *destructor (PJ *P, int errlev) { /* Destructor */
+ if (nullptr==P)
+ return nullptr;
+
+ if (nullptr==P->opaque)
+ return pj_default_destructor (P, errlev);
+
+ pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->en);
+ return pj_default_destructor (P, errlev);
+}
+
+
+
+PJ *PROJECTION(cass) {
+
+ /* Spheroidal? */
+ if (0==P->es) {
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ return P;
+ }
+
+ /* otherwise it's ellipsoidal */
+ P->opaque = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==P->opaque)
+ return pj_default_destructor (P, ENOMEM);
+ P->destructor = destructor;
+
+ static_cast<struct pj_opaque*>(P->opaque)->en = pj_enfn (P->es);
+ if (nullptr==static_cast<struct pj_opaque*>(P->opaque)->en)
+ return pj_default_destructor (P, ENOMEM);
+
+ static_cast<struct pj_opaque*>(P->opaque)->m0 = pj_mlfn (P->phi0, sin (P->phi0), cos (P->phi0), static_cast<struct pj_opaque*>(P->opaque)->en);
+ P->inv = e_inverse;
+ P->fwd = e_forward;
+
+ return P;
+}
diff --git a/src/projections/cc.cpp b/src/projections/cc.cpp
new file mode 100644
index 00000000..152e6e4a
--- /dev/null
+++ b/src/projections/cc.cpp
@@ -0,0 +1,41 @@
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(cc, "Central Cylindrical") "\n\tCyl, Sph";
+#define EPS10 1.e-10
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ if (fabs (fabs(lp.phi) - M_HALFPI) <= EPS10) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ xy.x = lp.lam;
+ xy.y = tan(lp.phi);
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ (void) P;
+ lp.phi = atan(xy.y);
+ lp.lam = xy.x;
+ return lp;
+}
+
+
+
+PJ *PROJECTION(cc) {
+ P->es = 0.;
+
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/ccon.cpp b/src/projections/ccon.cpp
new file mode 100644
index 00000000..4f7dedb4
--- /dev/null
+++ b/src/projections/ccon.cpp
@@ -0,0 +1,109 @@
+/******************************************************************************
+ * Copyright (c) 2017, Lukasz Komsta
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************/
+
+#define PJ_LIB__
+#include <errno.h>
+#include "proj.h"
+#include "projects.h"
+#include "proj_math.h"
+
+#define EPS10 1e-10
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double phi1;
+ double ctgphi1;
+ double sinphi1;
+ double cosphi1;
+ double *en;
+};
+} // anonymous namespace
+
+PROJ_HEAD(ccon, "Central Conic")
+ "\n\tCentral Conic, Sph\n\tlat_1=";
+
+
+
+static XY forward (LP lp, PJ *P) {
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double r;
+
+ r = Q->ctgphi1 - tan(lp.phi - Q->phi1);
+ xy.x = r * sin(lp.lam * Q->sinphi1);
+ xy.y = Q->ctgphi1 - r * cos(lp.lam * Q->sinphi1);
+
+ return xy;
+}
+
+
+static LP inverse (XY xy, PJ *P) {
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+
+ xy.y = Q->ctgphi1 - xy.y;
+ lp.phi = Q->phi1 - atan(hypot(xy.x,xy.y) - Q->ctgphi1);
+ lp.lam = atan2(xy.x,xy.y)/Q->sinphi1;
+
+ return lp;
+}
+
+
+static PJ *destructor (PJ *P, int errlev) {
+ if (nullptr==P)
+ return nullptr;
+
+ if (nullptr==P->opaque)
+ return pj_default_destructor (P, errlev);
+
+ pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->en);
+ return pj_default_destructor (P, errlev);
+}
+
+
+PJ *PROJECTION(ccon) {
+
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+ P->destructor = destructor;
+
+ Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f;
+ if (fabs(Q->phi1) < EPS10)
+ return destructor (P, PJD_ERR_LAT1_IS_ZERO);
+
+ if (!(Q->en = pj_enfn(P->es)))
+ return destructor(P, ENOMEM);
+
+ Q->sinphi1 = sin(Q->phi1);
+ Q->cosphi1 = cos(Q->phi1);
+ Q->ctgphi1 = Q->cosphi1/Q->sinphi1;
+
+
+ P->inv = inverse;
+ P->fwd = forward;
+
+ return P;
+}
+
+
diff --git a/src/projections/cea.cpp b/src/projections/cea.cpp
new file mode 100644
index 00000000..f8275b62
--- /dev/null
+++ b/src/projections/cea.cpp
@@ -0,0 +1,103 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double qp;
+ double *apa;
+};
+} // anonymous namespace
+
+PROJ_HEAD(cea, "Equal Area Cylindrical") "\n\tCyl, Sph&Ell\n\tlat_ts=";
+# define EPS 1e-10
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ XY xy = {0.0,0.0};
+ xy.x = P->k0 * lp.lam;
+ xy.y = 0.5 * pj_qsfn (sin (lp.phi), P->e, P->one_es) / P->k0;
+ return xy;
+}
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ xy.x = P->k0 * lp.lam;
+ xy.y = sin(lp.phi) / P->k0;
+ return xy;
+}
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */
+ LP lp = {0.0,0.0};
+ lp.phi = pj_authlat(asin( 2. * xy.y * P->k0 / static_cast<struct pj_opaque*>(P->opaque)->qp), static_cast<struct pj_opaque*>(P->opaque)->apa);
+ lp.lam = xy.x / P->k0;
+ return lp;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ double t;
+
+ if ((t = fabs(xy.y *= P->k0)) - EPS <= 1.) {
+ if (t >= 1.)
+ lp.phi = xy.y < 0. ? -M_HALFPI : M_HALFPI;
+ else
+ lp.phi = asin(xy.y);
+ lp.lam = xy.x / P->k0;
+ } else {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+ return (lp);
+}
+
+static PJ *destructor (PJ *P, int errlev) { /* Destructor */
+ if (nullptr==P)
+ return nullptr;
+
+ if (nullptr==P->opaque)
+ return pj_default_destructor (P, errlev);
+
+ pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->apa);
+ return pj_default_destructor (P, errlev);
+}
+
+
+PJ *PROJECTION(cea) {
+ double t = 0.0;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+ P->destructor = destructor;
+
+
+ if (pj_param(P->ctx, P->params, "tlat_ts").i) {
+ P->k0 = cos(t = pj_param(P->ctx, P->params, "rlat_ts").f);
+ if (P->k0 < 0.)
+ return pj_default_destructor (P, PJD_ERR_LAT_TS_LARGER_THAN_90);
+ }
+ if (P->es != 0.0) {
+ t = sin(t);
+ P->k0 /= sqrt(1. - P->es * t * t);
+ P->e = sqrt(P->es);
+ if (!(Q->apa = pj_authset(P->es)))
+ return pj_default_destructor(P, ENOMEM);
+
+ Q->qp = pj_qsfn(1., P->e, P->one_es);
+ P->inv = e_inverse;
+ P->fwd = e_forward;
+ } else {
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ }
+
+ return P;
+}
diff --git a/src/projections/chamb.cpp b/src/projections/chamb.cpp
new file mode 100644
index 00000000..a490e817
--- /dev/null
+++ b/src/projections/chamb.cpp
@@ -0,0 +1,141 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+typedef struct { double r, Az; } VECT;
+namespace { // anonymous namespace
+struct pj_opaque {
+ struct { /* control point data */
+ double phi, lam;
+ double cosphi, sinphi;
+ VECT v;
+ XY p;
+ double Az;
+ } c[3];
+ XY p;
+ double beta_0, beta_1, beta_2;
+};
+} // anonymous namespace
+
+PROJ_HEAD(chamb, "Chamberlin Trimetric") "\n\tMisc Sph, no inv"
+"\n\tlat_1= lon_1= lat_2= lon_2= lat_3= lon_3=";
+
+#include <stdio.h>
+#define THIRD 0.333333333333333333
+#define TOL 1e-9
+
+/* distance and azimuth from point 1 to point 2 */
+static VECT vect(projCtx ctx, double dphi, double c1, double s1, double c2, double s2, double dlam) {
+ VECT v;
+ double cdl, dp, dl;
+
+ cdl = cos(dlam);
+ if (fabs(dphi) > 1. || fabs(dlam) > 1.)
+ v.r = aacos(ctx, s1 * s2 + c1 * c2 * cdl);
+ else { /* more accurate for smaller distances */
+ dp = sin(.5 * dphi);
+ dl = sin(.5 * dlam);
+ v.r = 2. * aasin(ctx,sqrt(dp * dp + c1 * c2 * dl * dl));
+ }
+ if (fabs(v.r) > TOL)
+ v.Az = atan2(c2 * sin(dlam), c1 * s2 - s1 * c2 * cdl);
+ else
+ v.r = v.Az = 0.;
+ return v;
+}
+
+/* law of cosines */
+static double lc(projCtx ctx, double b,double c,double a) {
+ return aacos(ctx, .5 * (b * b + c * c - a * a) / (b * c));
+}
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double sinphi, cosphi, a;
+ VECT v[3];
+ int i, j;
+
+ sinphi = sin(lp.phi);
+ cosphi = cos(lp.phi);
+ for (i = 0; i < 3; ++i) { /* dist/azimiths from control */
+ v[i] = vect(P->ctx, lp.phi - Q->c[i].phi, Q->c[i].cosphi, Q->c[i].sinphi,
+ cosphi, sinphi, lp.lam - Q->c[i].lam);
+ if (v[i].r == 0.0)
+ break;
+ v[i].Az = adjlon(v[i].Az - Q->c[i].v.Az);
+ }
+ if (i < 3) /* current point at control point */
+ xy = Q->c[i].p;
+ else { /* point mean of intersepts */
+ xy = Q->p;
+ for (i = 0; i < 3; ++i) {
+ j = i == 2 ? 0 : i + 1;
+ a = lc(P->ctx,Q->c[i].v.r, v[i].r, v[j].r);
+ if (v[i].Az < 0.)
+ a = -a;
+ if (! i) { /* coord comp unique to each arc */
+ xy.x += v[i].r * cos(a);
+ xy.y -= v[i].r * sin(a);
+ } else if (i == 1) {
+ a = Q->beta_1 - a;
+ xy.x -= v[i].r * cos(a);
+ xy.y -= v[i].r * sin(a);
+ } else {
+ a = Q->beta_2 - a;
+ xy.x += v[i].r * cos(a);
+ xy.y += v[i].r * sin(a);
+ }
+ }
+ xy.x *= THIRD; /* mean of arc intercepts */
+ xy.y *= THIRD;
+ }
+ return xy;
+}
+
+
+
+PJ *PROJECTION(chamb) {
+ int i, j;
+ char line[10];
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+
+ for (i = 0; i < 3; ++i) { /* get control point locations */
+ (void)sprintf(line, "rlat_%d", i+1);
+ Q->c[i].phi = pj_param(P->ctx, P->params, line).f;
+ (void)sprintf(line, "rlon_%d", i+1);
+ Q->c[i].lam = pj_param(P->ctx, P->params, line).f;
+ Q->c[i].lam = adjlon(Q->c[i].lam - P->lam0);
+ Q->c[i].cosphi = cos(Q->c[i].phi);
+ Q->c[i].sinphi = sin(Q->c[i].phi);
+ }
+ for (i = 0; i < 3; ++i) { /* inter ctl pt. distances and azimuths */
+ j = i == 2 ? 0 : i + 1;
+ Q->c[i].v = vect(P->ctx,Q->c[j].phi - Q->c[i].phi, Q->c[i].cosphi, Q->c[i].sinphi,
+ Q->c[j].cosphi, Q->c[j].sinphi, Q->c[j].lam - Q->c[i].lam);
+ if (Q->c[i].v.r == 0.0)
+ return pj_default_destructor (P, PJD_ERR_CONTROL_POINT_NO_DIST);
+ /* co-linearity problem ignored for now */
+ }
+ Q->beta_0 = lc(P->ctx,Q->c[0].v.r, Q->c[2].v.r, Q->c[1].v.r);
+ Q->beta_1 = lc(P->ctx,Q->c[0].v.r, Q->c[1].v.r, Q->c[2].v.r);
+ Q->beta_2 = M_PI - Q->beta_0;
+ Q->p.y = 2. * (Q->c[0].p.y = Q->c[1].p.y = Q->c[2].v.r * sin(Q->beta_0));
+ Q->c[2].p.y = 0.;
+ Q->c[0].p.x = - (Q->c[1].p.x = 0.5 * Q->c[0].v.r);
+ Q->p.x = Q->c[2].p.x = Q->c[0].p.x + Q->c[2].v.r * cos(Q->beta_0);
+
+ P->es = 0.;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/collg.cpp b/src/projections/collg.cpp
new file mode 100644
index 00000000..7904de29
--- /dev/null
+++ b/src/projections/collg.cpp
@@ -0,0 +1,53 @@
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(collg, "Collignon") "\n\tPCyl, Sph";
+#define FXC 1.12837916709551257390
+#define FYC 1.77245385090551602729
+#define ONEEPS 1.0000001
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ (void) P;
+ if ((xy.y = 1. - sin(lp.phi)) <= 0.)
+ xy.y = 0.;
+ else
+ xy.y = sqrt(xy.y);
+ xy.x = FXC * lp.lam * xy.y;
+ xy.y = FYC * (1. - xy.y);
+ return (xy);
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ lp.phi = xy.y / FYC - 1.;
+ if (fabs(lp.phi = 1. - lp.phi * lp.phi) < 1.)
+ lp.phi = asin(lp.phi);
+ else if (fabs(lp.phi) > ONEEPS) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ } else {
+ lp.phi = lp.phi < 0. ? -M_HALFPI : M_HALFPI;
+ }
+
+ if ((lp.lam = 1. - sin(lp.phi)) <= 0.)
+ lp.lam = 0.;
+ else
+ lp.lam = xy.x / (FXC * sqrt(lp.lam));
+ return (lp);
+}
+
+
+PJ *PROJECTION(collg) {
+ P->es = 0.0;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/comill.cpp b/src/projections/comill.cpp
new file mode 100644
index 00000000..b6e0192e
--- /dev/null
+++ b/src/projections/comill.cpp
@@ -0,0 +1,84 @@
+/*
+The Compact Miller projection was designed by Tom Patterson, US National
+Park Service, in 2014. The polynomial equation was developed by Bojan
+Savric and Bernhard Jenny, College of Earth, Ocean, and Atmospheric
+Sciences, Oregon State University.
+Port to PROJ.4 by Bojan Savric, 4 April 2016
+*/
+
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(comill, "Compact Miller") "\n\tCyl, Sph";
+
+#define K1 0.9902
+#define K2 0.1604
+#define K3 -0.03054
+#define C1 K1
+#define C2 (3 * K2)
+#define C3 (5 * K3)
+#define EPS 1e-11
+#define MAX_Y (0.6000207669862655 * M_PI)
+/* Not sure at all of the appropriate number for MAX_ITER... */
+#define MAX_ITER 100
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ double lat_sq;
+
+ (void) P; /* silence unused parameter warnings */
+
+ lat_sq = lp.phi * lp.phi;
+ xy.x = lp.lam;
+ xy.y = lp.phi * (K1 + lat_sq * (K2 + K3 * lat_sq));
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ double yc, tol, y2, f, fder;
+ int i;
+
+ (void) P; /* silence unused parameter warnings */
+
+ /* make sure y is inside valid range */
+ if (xy.y > MAX_Y) {
+ xy.y = MAX_Y;
+ } else if (xy.y < -MAX_Y) {
+ xy.y = -MAX_Y;
+ }
+
+ /* latitude */
+ yc = xy.y;
+ for (i = MAX_ITER; i ; --i) { /* Newton-Raphson */
+ y2 = yc * yc;
+ f = (yc * (K1 + y2 * (K2 + K3 * y2))) - xy.y;
+ fder = C1 + y2 * (C2 + C3 * y2);
+ yc -= tol = f / fder;
+ if (fabs(tol) < EPS) {
+ break;
+ }
+ }
+ if( i == 0 )
+ pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT );
+ lp.phi = yc;
+
+ /* longitude */
+ lp.lam = xy.x;
+
+ return lp;
+}
+
+
+PJ *PROJECTION(comill) {
+ P->es = 0;
+
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/crast.cpp b/src/projections/crast.cpp
new file mode 100644
index 00000000..4e4dee8b
--- /dev/null
+++ b/src/projections/crast.cpp
@@ -0,0 +1,40 @@
+#define PJ_LIB__
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(crast, "Craster Parabolic (Putnins P4)") "\n\tPCyl, Sph";
+
+#define XM 0.97720502380583984317
+#define RXM 1.02332670794648848847
+#define YM 3.06998012383946546542
+#define RYM 0.32573500793527994772
+#define THIRD 0.333333333333333333
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ (void) P;
+ lp.phi *= THIRD;
+ xy.x = XM * lp.lam * (2. * cos(lp.phi + lp.phi) - 1.);
+ xy.y = YM * sin(lp.phi);
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ (void) P;
+ lp.phi = 3. * asin(xy.y * RYM);
+ lp.lam = xy.x * RXM / (2. * cos((lp.phi + lp.phi) * THIRD) - 1);
+ return lp;
+}
+
+
+PJ *PROJECTION(crast) {
+ P->es = 0.0;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/denoy.cpp b/src/projections/denoy.cpp
new file mode 100644
index 00000000..5c337c45
--- /dev/null
+++ b/src/projections/denoy.cpp
@@ -0,0 +1,32 @@
+#define PJ_LIB__
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(denoy, "Denoyer Semi-Elliptical") "\n\tPCyl, no inv, Sph";
+
+#define C0 0.95
+#define C1 -0.08333333333333333333
+#define C3 0.00166666666666666666
+#define D1 0.9
+#define D5 0.03
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0, 0.0};
+ (void) P;
+ xy.y = lp.phi;
+ xy.x = lp.lam;
+ lp.lam = fabs(lp.lam);
+ xy.x *= cos((C0 + lp.lam * (C1 + lp.lam * lp.lam * C3)) *
+ (lp.phi * (D1 + D5 * lp.phi * lp.phi * lp.phi * lp.phi)));
+ return xy;
+}
+
+
+PJ *PROJECTION(denoy) {
+ P->es = 0.0;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/eck1.cpp b/src/projections/eck1.cpp
new file mode 100644
index 00000000..88a7430c
--- /dev/null
+++ b/src/projections/eck1.cpp
@@ -0,0 +1,41 @@
+#define PJ_LIB__
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(eck1, "Eckert I") "\n\tPCyl, Sph";
+#define FC 0.92131773192356127802
+#define RP 0.31830988618379067154
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ (void) P;
+
+ xy.x = FC * lp.lam * (1. - RP * fabs(lp.phi));
+ xy.y = FC * lp.phi;
+
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ (void) P;
+
+ lp.phi = xy.y / FC;
+ lp.lam = xy.x / (FC * (1. - RP * fabs(lp.phi)));
+
+ return (lp);
+}
+
+
+
+PJ *PROJECTION(eck1) {
+ P->es = 0.0;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P
+;
+}
diff --git a/src/projections/eck2.cpp b/src/projections/eck2.cpp
new file mode 100644
index 00000000..f76ab4ec
--- /dev/null
+++ b/src/projections/eck2.cpp
@@ -0,0 +1,56 @@
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(eck2, "Eckert II") "\n\tPCyl, Sph";
+
+#define FXC 0.46065886596178063902
+#define FYC 1.44720250911653531871
+#define C13 0.33333333333333333333
+#define ONEEPS 1.0000001
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ (void) P;
+
+ xy.x = FXC * lp.lam * (xy.y = sqrt(4. - 3. * sin(fabs(lp.phi))));
+ xy.y = FYC * (2. - xy.y);
+ if ( lp.phi < 0.) xy.y = -xy.y;
+
+ return (xy);
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ (void) P;
+
+ lp.lam = xy.x / (FXC * ( lp.phi = 2. - fabs(xy.y) / FYC) );
+ lp.phi = (4. - lp.phi * lp.phi) * C13;
+ if (fabs(lp.phi) >= 1.) {
+ if (fabs(lp.phi) > ONEEPS) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ } else {
+ lp.phi = lp.phi < 0. ? -M_HALFPI : M_HALFPI;
+ }
+ } else
+ lp.phi = asin(lp.phi);
+ if (xy.y < 0)
+ lp.phi = -lp.phi;
+ return (lp);
+}
+
+
+
+PJ *PROJECTION(eck2) {
+ P->es = 0.;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/eck3.cpp b/src/projections/eck3.cpp
new file mode 100644
index 00000000..90376631
--- /dev/null
+++ b/src/projections/eck3.cpp
@@ -0,0 +1,112 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(eck3, "Eckert III") "\n\tPCyl, Sph";
+PROJ_HEAD(putp1, "Putnins P1") "\n\tPCyl, Sph";
+PROJ_HEAD(wag6, "Wagner VI") "\n\tPCyl, Sph";
+PROJ_HEAD(kav7, "Kavraisky VII") "\n\tPCyl, Sph";
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double C_x, C_y, A, B;
+};
+} // anonymous namespace
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+
+ xy.y = Q->C_y * lp.phi;
+ xy.x = Q->C_x * lp.lam * (Q->A + asqrt(1. - Q->B * lp.phi * lp.phi));
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double denominator;
+
+ lp.phi = xy.y / Q->C_y;
+ denominator = (Q->C_x * (Q->A + asqrt(1. - Q->B * lp.phi * lp.phi)));
+ if ( denominator == 0.0)
+ lp.lam = HUGE_VAL;
+ else
+ lp.lam = xy.x / denominator;
+ return lp;
+}
+
+
+static PJ *setup(PJ *P) {
+ P->es = 0.;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ return P;
+}
+
+
+PJ *PROJECTION(eck3) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->C_x = 0.42223820031577120149;
+ Q->C_y = 0.84447640063154240298;
+ Q->A = 1.0;
+ Q->B = 0.4052847345693510857755;
+
+ return setup(P);
+}
+
+
+PJ *PROJECTION(kav7) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ /* Defined twice in original code - Using 0.866...,
+ * but leaving the other one here as a safety measure.
+ * Q->C_x = 0.2632401569273184856851; */
+ Q->C_x = 0.8660254037844;
+ Q->C_y = 1.;
+ Q->A = 0.;
+ Q->B = 0.30396355092701331433;
+
+ return setup(P);
+}
+
+
+PJ *PROJECTION(wag6) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->C_x = Q->C_y = 0.94745;
+ Q->A = 0.0;
+ Q->B = 0.30396355092701331433;
+
+ return setup(P);
+}
+
+
+PJ *PROJECTION(putp1) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->C_x = 1.89490;
+ Q->C_y = 0.94745;
+ Q->A = -0.5;
+ Q->B = 0.30396355092701331433;
+
+ return setup(P);
+}
diff --git a/src/projections/eck4.cpp b/src/projections/eck4.cpp
new file mode 100644
index 00000000..4fa4c21f
--- /dev/null
+++ b/src/projections/eck4.cpp
@@ -0,0 +1,63 @@
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(eck4, "Eckert IV") "\n\tPCyl, Sph";
+
+#define C_x .42223820031577120149
+#define C_y 1.32650042817700232218
+#define RC_y .75386330736002178205
+#define C_p 3.57079632679489661922
+#define RC_p .28004957675577868795
+#define EPS 1e-7
+#define NITER 6
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ double p, V, s, c;
+ int i;
+ (void) P;
+
+ p = C_p * sin(lp.phi);
+ V = lp.phi * lp.phi;
+ lp.phi *= 0.895168 + V * ( 0.0218849 + V * 0.00826809 );
+ for (i = NITER; i ; --i) {
+ c = cos(lp.phi);
+ s = sin(lp.phi);
+ lp.phi -= V = (lp.phi + s * (c + 2.) - p) /
+ (1. + c * (c + 2.) - s * s);
+ if (fabs(V) < EPS)
+ break;
+ }
+ if (!i) {
+ xy.x = C_x * lp.lam;
+ xy.y = lp.phi < 0. ? -C_y : C_y;
+ } else {
+ xy.x = C_x * lp.lam * (1. + cos(lp.phi));
+ xy.y = C_y * sin(lp.phi);
+ }
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ double c;
+
+ lp.phi = aasin(P->ctx,xy.y * RC_y);
+ lp.lam = xy.x / (C_x * (1. + (c = cos(lp.phi))));
+ lp.phi = aasin(P->ctx,(lp.phi + sin(lp.phi) * (c + 2.)) * RC_p);
+ return lp;
+}
+
+
+PJ *PROJECTION(eck4) {
+ P->es = 0.0;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/eck5.cpp b/src/projections/eck5.cpp
new file mode 100644
index 00000000..f9f28460
--- /dev/null
+++ b/src/projections/eck5.cpp
@@ -0,0 +1,40 @@
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(eck5, "Eckert V") "\n\tPCyl, Sph";
+
+#define XF 0.44101277172455148219
+#define RXF 2.26750802723822639137
+#define YF 0.88202554344910296438
+#define RYF 1.13375401361911319568
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ (void) P;
+ xy.x = XF * (1. + cos(lp.phi)) * lp.lam;
+ xy.y = YF * lp.phi;
+
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ (void) P;
+ lp.lam = RXF * xy.x / (1. + cos( lp.phi = RYF * xy.y));
+
+ return lp;
+}
+
+
+PJ *PROJECTION(eck5) {
+ P->es = 0.0;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/eqc.cpp b/src/projections/eqc.cpp
new file mode 100644
index 00000000..3fdb6dc0
--- /dev/null
+++ b/src/projections/eqc.cpp
@@ -0,0 +1,54 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double rc;
+};
+} // anonymous namespace
+
+PROJ_HEAD(eqc, "Equidistant Cylindrical (Plate Carree)")
+ "\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 = static_cast<struct pj_opaque*>(P->opaque);
+
+ xy.x = Q->rc * lp.lam;
+ xy.y = lp.phi - P->phi0;
+
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+
+ lp.lam = xy.x / Q->rc;
+ lp.phi = xy.y + P->phi0;
+
+ return lp;
+}
+
+
+PJ *PROJECTION(eqc) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ if ((Q->rc = cos(pj_param(P->ctx, P->params, "rlat_ts").f)) <= 0.)
+ return pj_default_destructor (P, PJD_ERR_LAT_TS_LARGER_THAN_90);
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ P->es = 0.;
+
+ return P;
+}
diff --git a/src/projections/eqdc.cpp b/src/projections/eqdc.cpp
new file mode 100644
index 00000000..0831fca4
--- /dev/null
+++ b/src/projections/eqdc.cpp
@@ -0,0 +1,122 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+#include "proj_math.h"
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double phi1;
+ double phi2;
+ double n;
+ double rho;
+ double rho0;
+ double c;
+ double *en;
+ int ellips;
+};
+} // anonymous namespace
+
+PROJ_HEAD(eqdc, "Equidistant Conic")
+ "\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 = static_cast<struct pj_opaque*>(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;
+}
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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. ? M_HALFPI : -M_HALFPI;
+ }
+ return lp;
+}
+
+
+static PJ *destructor (PJ *P, int errlev) { /* Destructor */
+ if (nullptr==P)
+ return nullptr;
+
+ if (nullptr==P->opaque)
+ return pj_default_destructor (P, errlev);
+
+ pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->en);
+ return pj_default_destructor (P, errlev);
+}
+
+
+PJ *PROJECTION(eqdc) {
+ double cosphi, sinphi;
+ int secant;
+
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+ P->destructor = destructor;
+
+ 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)
+ return pj_default_destructor (P, PJD_ERR_CONIC_LAT_EQUAL);
+
+ if (!(Q->en = pj_enfn(P->es)))
+ return pj_default_destructor(P, ENOMEM);
+
+ 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;
+
+ return P;
+}
diff --git a/src/projections/eqearth.cpp b/src/projections/eqearth.cpp
new file mode 100644
index 00000000..e5c1f974
--- /dev/null
+++ b/src/projections/eqearth.cpp
@@ -0,0 +1,164 @@
+/*
+Equal Earth is a projection inspired by the Robinson projection, but unlike
+the Robinson projection retains the relative size of areas. The projection
+was designed in 2018 by Bojan Savric, Tom Patterson and Bernhard Jenny.
+
+Publication:
+Bojan Savric, Tom Patterson & Bernhard Jenny (2018). The Equal Earth map
+projection, International Journal of Geographical Information Science,
+DOI: 10.1080/13658816.2018.1504949
+
+Port to PROJ by Juernjakob Dugge, 16 August 2018
+Added ellipsoidal equations by Bojan Savric, 22 August 2018
+*/
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(eqearth, "Equal Earth") "\n\tPCyl, Sph&Ell";
+
+/* A1..A4, polynomial coefficients */
+#define A1 1.340264
+#define A2 -0.081106
+#define A3 0.000893
+#define A4 0.003796
+#define M (sqrt(3) / 2.0)
+
+#define MAX_Y 1.3173627591574 /* 90° latitude on a sphere with radius 1 */
+#define EPS 1e-11
+#define MAX_ITER 12
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double qp;
+ double rqda;
+ double *apa;
+};
+} // anonymous namespace
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal/spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double sbeta;
+ double psi, psi2, psi6;
+
+ /* Spheroidal case, using sine latitude */
+ sbeta = sin(lp.phi);
+
+ /* In the ellipsoidal case, we convert sbeta to sine of authalic latitude */
+ if (P->es != 0.0) {
+ sbeta = pj_qsfn(sbeta, P->e, 1.0 - P->es) / Q->qp;
+
+ /* Rounding error. */
+ if (fabs(sbeta) > 1)
+ sbeta = sbeta > 0 ? 1 : -1;
+ }
+
+ /* Equal Earth projection */
+ psi = asin(M * sbeta);
+ psi2 = psi * psi;
+ psi6 = psi2 * psi2 * psi2;
+
+ xy.x = lp.lam * cos(psi) / (M * (A1 + 3 * A2 * psi2 + psi6 * (7 * A3 + 9 * A4 * psi2)));
+ xy.y = psi * (A1 + A2 * psi2 + psi6 * (A3 + A4 * psi2));
+
+ /* Adjusting x and y for authalic radius */
+ xy.x *= Q->rqda;
+ xy.y *= Q->rqda;
+
+ return xy;
+}
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal/spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double yc, y2, y6;
+ int i;
+
+ /* Adjusting x and y for authalic radius */
+ xy.x /= Q->rqda;
+ xy.y /= Q->rqda;
+
+ /* 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;
+
+ yc = xy.y;
+
+ /* Newton-Raphson */
+ for (i = MAX_ITER; i ; --i) {
+ double f, fder, tol;
+
+ y2 = yc * yc;
+ y6 = y2 * y2 * y2;
+
+ f = yc * (A1 + A2 * y2 + y6 * (A3 + A4 * y2)) - xy.y;
+ fder = A1 + 3 * A2 * y2 + y6 * (7 * A3 + 9 * A4 * y2);
+
+ tol = f / fder;
+ yc -= tol;
+
+ if (fabs(tol) < EPS)
+ break;
+ }
+
+ if( i == 0 ) {
+ pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT );
+ return lp;
+ }
+
+ /* Longitude */
+ y2 = yc * yc;
+ y6 = y2 * y2 * y2;
+
+ lp.lam = M * xy.x * (A1 + 3 * A2 * y2 + y6 * (7 * A3 + 9 * A4 * y2)) / cos(yc);
+
+ /* Latitude (for spheroidal case, this is latitude */
+ lp.phi = asin(sin(yc) / M);
+
+ /* Ellipsoidal case, converting auth. latitude */
+ if (P->es != 0.0)
+ lp.phi = pj_authlat(lp.phi, Q->apa);
+
+ return lp;
+}
+
+static PJ *destructor (PJ *P, int errlev) { /* Destructor */
+ if (nullptr==P)
+ return nullptr;
+
+ if (nullptr==P->opaque)
+ return pj_default_destructor (P, errlev);
+
+ pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->apa);
+ return pj_default_destructor (P, errlev);
+}
+
+
+PJ *PROJECTION(eqearth) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+ P->destructor = destructor;
+ P->fwd = e_forward;
+ P->inv = e_inverse;
+ Q->rqda = 1.0;
+
+ /* Ellipsoidal case */
+ if (P->es != 0.0) {
+ Q->apa = pj_authset(P->es); /* For auth_lat(). */
+ if (nullptr == Q->apa)
+ return destructor(P, ENOMEM);
+ Q->qp = pj_qsfn(1.0, P->e, P->one_es); /* For auth_lat(). */
+ Q->rqda = sqrt(0.5*Q->qp); /* Authalic radius divided by major axis */
+ }
+
+ return P;
+}
diff --git a/src/projections/etmerc.cpp b/src/projections/etmerc.cpp
new file mode 100644
index 00000000..05f86f37
--- /dev/null
+++ b/src/projections/etmerc.cpp
@@ -0,0 +1,362 @@
+/*
+** libproj -- library of cartographic projections
+**
+** Copyright (c) 2008 Gerald I. Evenden
+*/
+
+/*
+** Permission is hereby granted, free of charge, to any person obtaining
+** a copy of this software and associated documentation files (the
+** "Software"), to deal in the Software without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Software, and to
+** permit persons to whom the Software is furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be
+** included in all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+/* The code in this file is largly based upon procedures:
+ *
+ * Written by: Knud Poder and Karsten Engsager
+ *
+ * Based on math from: R.Koenig and K.H. Weise, "Mathematische
+ * Grundlagen der hoeheren Geodaesie und Kartographie,
+ * Springer-Verlag, Berlin/Goettingen" Heidelberg, 1951.
+ *
+ * Modified and used here by permission of Reference Networks
+ * Division, Kort og Matrikelstyrelsen (KMS), Copenhagen, Denmark
+ *
+*/
+
+#define PJ_LIB__
+
+#include <errno.h>
+
+#include "proj.h"
+#include "projects.h"
+#include "proj_math.h"
+
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double Qn; /* Merid. quad., scaled to the projection */ \
+ double Zb; /* Radius vector in polar coord. systems */ \
+ double cgb[6]; /* Constants for Gauss -> Geo lat */ \
+ double cbg[6]; /* Constants for Geo lat -> Gauss */ \
+ double utg[6]; /* Constants for transv. merc. -> geo */ \
+ double gtu[6]; /* Constants for geo -> transv. merc. */
+};
+} // anonymous namespace
+
+PROJ_HEAD(etmerc, "Extended Transverse Mercator")
+ "\n\tCyl, Sph\n\tlat_ts=(0)\nlat_0=(0)";
+PROJ_HEAD(utm, "Universal Transverse Mercator (UTM)")
+ "\n\tCyl, Sph\n\tzone= south";
+
+#define PROJ_ETMERC_ORDER 6
+
+#ifdef _GNU_SOURCE
+ inline
+#endif
+static double gatg(double *p1, int len_p1, double B) {
+ double *p;
+ double h = 0, h1, h2 = 0, cos_2B;
+
+ cos_2B = 2*cos(2*B);
+ p = p1 + len_p1;
+ h1 = *--p;
+ while (p - p1) {
+ h = -h2 + cos_2B*h1 + *--p;
+ h2 = h1;
+ h1 = h;
+ }
+ return (B + h*sin(2*B));
+}
+
+/* Complex Clenshaw summation */
+#ifdef _GNU_SOURCE
+ inline
+#endif
+static double clenS(double *a, int size, double arg_r, double arg_i, double *R, double *I) {
+ double *p, r, i, hr, hr1, hr2, hi, hi1, hi2;
+ double sin_arg_r, cos_arg_r, sinh_arg_i, cosh_arg_i;
+
+ /* arguments */
+ p = a + size;
+#ifdef _GNU_SOURCE
+ sincos(arg_r, &sin_arg_r, &cos_arg_r);
+#else
+ sin_arg_r = sin(arg_r);
+ cos_arg_r = cos(arg_r);
+#endif
+ sinh_arg_i = sinh(arg_i);
+ cosh_arg_i = cosh(arg_i);
+ r = 2*cos_arg_r*cosh_arg_i;
+ i = -2*sin_arg_r*sinh_arg_i;
+
+ /* summation loop */
+ hi1 = hr1 = hi = 0;
+ hr = *--p;
+ for (; a - p;) {
+ hr2 = hr1;
+ hi2 = hi1;
+ hr1 = hr;
+ hi1 = hi;
+ hr = -hr2 + r*hr1 - i*hi1 + *--p;
+ hi = -hi2 + i*hr1 + r*hi1;
+ }
+
+ r = sin_arg_r*cosh_arg_i;
+ i = cos_arg_r*sinh_arg_i;
+ *R = r*hr - i*hi;
+ *I = r*hi + i*hr;
+ return *R;
+}
+
+
+/* Real Clenshaw summation */
+static double clens(double *a, int size, double arg_r) {
+ double *p, r, hr, hr1, hr2, cos_arg_r;
+
+ p = a + size;
+ cos_arg_r = cos(arg_r);
+ r = 2*cos_arg_r;
+
+ /* summation loop */
+ hr1 = 0;
+ hr = *--p;
+ for (; a - p;) {
+ hr2 = hr1;
+ hr1 = hr;
+ hr = -hr2 + r*hr1 + *--p;
+ }
+ return sin (arg_r)*hr;
+}
+
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double sin_Cn, cos_Cn, cos_Ce, sin_Ce, dCn, dCe;
+ double Cn = lp.phi, Ce = lp.lam;
+
+ /* ell. LAT, LNG -> Gaussian LAT, LNG */
+ Cn = gatg (Q->cbg, PROJ_ETMERC_ORDER, Cn);
+ /* Gaussian LAT, LNG -> compl. sph. LAT */
+#ifdef _GNU_SOURCE
+ sincos (Cn, &sin_Cn, &cos_Cn);
+ sincos (Ce, &sin_Ce, &cos_Ce);
+#else
+ sin_Cn = sin (Cn);
+ cos_Cn = cos (Cn);
+ sin_Ce = sin (Ce);
+ cos_Ce = cos (Ce);
+#endif
+
+ Cn = atan2 (sin_Cn, cos_Ce*cos_Cn);
+ Ce = atan2 (sin_Ce*cos_Cn, hypot (sin_Cn, cos_Cn*cos_Ce));
+
+ /* compl. sph. N, E -> ell. norm. N, E */
+ Ce = asinh ( tan (Ce) ); /* Replaces: Ce = log(tan(FORTPI + Ce*0.5)); */
+ Cn += clenS (Q->gtu, PROJ_ETMERC_ORDER, 2*Cn, 2*Ce, &dCn, &dCe);
+ Ce += dCe;
+ if (fabs (Ce) <= 2.623395162778) {
+ xy.y = Q->Qn * Cn + Q->Zb; /* Northing */
+ xy.x = Q->Qn * Ce; /* Easting */
+ } else
+ xy.x = xy.y = HUGE_VAL;
+ return xy;
+}
+
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double sin_Cn, cos_Cn, cos_Ce, sin_Ce, dCn, dCe;
+ double Cn = xy.y, Ce = xy.x;
+
+ /* normalize N, E */
+ Cn = (Cn - Q->Zb)/Q->Qn;
+ Ce = Ce/Q->Qn;
+
+ if (fabs(Ce) <= 2.623395162778) { /* 150 degrees */
+ /* norm. N, E -> compl. sph. LAT, LNG */
+ Cn += clenS(Q->utg, PROJ_ETMERC_ORDER, 2*Cn, 2*Ce, &dCn, &dCe);
+ Ce += dCe;
+ Ce = atan (sinh (Ce)); /* Replaces: Ce = 2*(atan(exp(Ce)) - FORTPI); */
+ /* compl. sph. LAT -> Gaussian LAT, LNG */
+#ifdef _GNU_SOURCE
+ sincos (Cn, &sin_Cn, &cos_Cn);
+ sincos (Ce, &sin_Ce, &cos_Ce);
+#else
+ sin_Cn = sin (Cn);
+ cos_Cn = cos (Cn);
+ sin_Ce = sin (Ce);
+ cos_Ce = cos (Ce);
+#endif
+ Ce = atan2 (sin_Ce, cos_Ce*cos_Cn);
+ Cn = atan2 (sin_Cn*cos_Ce, hypot (sin_Ce, cos_Ce*cos_Cn));
+ /* Gaussian LAT, LNG -> ell. LAT, LNG */
+ lp.phi = gatg (Q->cgb, PROJ_ETMERC_ORDER, Cn);
+ lp.lam = Ce;
+ }
+ else
+ lp.phi = lp.lam = HUGE_VAL;
+ return lp;
+}
+
+
+static PJ *setup(PJ *P) { /* general initialization */
+ double f, n, np, Z;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+
+ if (P->es <= 0) {
+ return pj_default_destructor(P, PJD_ERR_ELLIPSOID_USE_REQUIRED);
+ }
+
+ /* flattening */
+ f = P->es / (1 + sqrt (1 - P->es)); /* Replaces: f = 1 - sqrt(1-P->es); */
+
+ /* third flattening */
+ np = n = f/(2 - f);
+
+ /* COEF. OF TRIG SERIES GEO <-> GAUSS */
+ /* cgb := Gaussian -> Geodetic, KW p190 - 191 (61) - (62) */
+ /* cbg := Geodetic -> Gaussian, KW p186 - 187 (51) - (52) */
+ /* PROJ_ETMERC_ORDER = 6th degree : Engsager and Poder: ICC2007 */
+
+ Q->cgb[0] = n*( 2 + n*(-2/3.0 + n*(-2 + n*(116/45.0 + n*(26/45.0 +
+ n*(-2854/675.0 ))))));
+ Q->cbg[0] = n*(-2 + n*( 2/3.0 + n*( 4/3.0 + n*(-82/45.0 + n*(32/45.0 +
+ n*( 4642/4725.0))))));
+ np *= n;
+ Q->cgb[1] = np*(7/3.0 + n*( -8/5.0 + n*(-227/45.0 + n*(2704/315.0 +
+ n*( 2323/945.0)))));
+ Q->cbg[1] = np*(5/3.0 + n*(-16/15.0 + n*( -13/9.0 + n*( 904/315.0 +
+ n*(-1522/945.0)))));
+ np *= n;
+ /* n^5 coeff corrected from 1262/105 -> -1262/105 */
+ Q->cgb[2] = np*( 56/15.0 + n*(-136/35.0 + n*(-1262/105.0 +
+ n*( 73814/2835.0))));
+ Q->cbg[2] = np*(-26/15.0 + n*( 34/21.0 + n*( 8/5.0 +
+ n*(-12686/2835.0))));
+ np *= n;
+ /* n^5 coeff corrected from 322/35 -> 332/35 */
+ Q->cgb[3] = np*(4279/630.0 + n*(-332/35.0 + n*(-399572/14175.0)));
+ Q->cbg[3] = np*(1237/630.0 + n*( -12/5.0 + n*( -24832/14175.0)));
+ np *= n;
+ Q->cgb[4] = np*(4174/315.0 + n*(-144838/6237.0 ));
+ Q->cbg[4] = np*(-734/315.0 + n*( 109598/31185.0));
+ np *= n;
+ Q->cgb[5] = np*(601676/22275.0 );
+ Q->cbg[5] = np*(444337/155925.0);
+
+ /* Constants of the projections */
+ /* Transverse Mercator (UTM, ITM, etc) */
+ np = n*n;
+ /* Norm. mer. quad, K&W p.50 (96), p.19 (38b), p.5 (2) */
+ Q->Qn = P->k0/(1 + n) * (1 + np*(1/4.0 + np*(1/64.0 + np/256.0)));
+ /* coef of trig series */
+ /* utg := ell. N, E -> sph. N, E, KW p194 (65) */
+ /* gtu := sph. N, E -> ell. N, E, KW p196 (69) */
+ Q->utg[0] = n*(-0.5 + n*( 2/3.0 + n*(-37/96.0 + n*( 1/360.0 +
+ n*( 81/512.0 + n*(-96199/604800.0))))));
+ Q->gtu[0] = n*( 0.5 + n*(-2/3.0 + n*( 5/16.0 + n*(41/180.0 +
+ n*(-127/288.0 + n*( 7891/37800.0 ))))));
+ Q->utg[1] = np*(-1/48.0 + n*(-1/15.0 + n*(437/1440.0 + n*(-46/105.0 +
+ n*( 1118711/3870720.0)))));
+ Q->gtu[1] = np*(13/48.0 + n*(-3/5.0 + n*(557/1440.0 + n*(281/630.0 +
+ n*(-1983433/1935360.0)))));
+ np *= n;
+ Q->utg[2] = np*(-17/480.0 + n*( 37/840.0 + n*( 209/4480.0 +
+ n*( -5569/90720.0 ))));
+ Q->gtu[2] = np*( 61/240.0 + n*(-103/140.0 + n*(15061/26880.0 +
+ n*(167603/181440.0))));
+ np *= n;
+ Q->utg[3] = np*(-4397/161280.0 + n*( 11/504.0 + n*( 830251/7257600.0)));
+ Q->gtu[3] = np*(49561/161280.0 + n*(-179/168.0 + n*(6601661/7257600.0)));
+ np *= n;
+ Q->utg[4] = np*(-4583/161280.0 + n*( 108847/3991680.0));
+ Q->gtu[4] = np*(34729/80640.0 + n*(-3418889/1995840.0));
+ np *= n;
+ Q->utg[5] = np*(-20648693/638668800.0);
+ Q->gtu[5] = np*(212378941/319334400.0);
+
+ /* Gaussian latitude value of the origin latitude */
+ Z = gatg (Q->cbg, PROJ_ETMERC_ORDER, P->phi0);
+
+ /* Origin northing minus true northing at the origin latitude */
+ /* i.e. true northing = N - P->Zb */
+ Q->Zb = - Q->Qn*(Z + clens(Q->gtu, PROJ_ETMERC_ORDER, 2*Z));
+ P->inv = e_inverse;
+ P->fwd = e_forward;
+ return P;
+}
+
+
+
+PJ *PROJECTION(etmerc) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+ return setup (P);
+}
+
+
+
+/* utm uses etmerc for the underlying projection */
+
+
+PJ *PROJECTION(utm) {
+ long zone;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ if (P->es == 0.0) {
+ proj_errno_set(P, PJD_ERR_ELLIPSOID_USE_REQUIRED);
+ return pj_default_destructor(P, ENOMEM);
+ }
+ if (P->lam0 < -1000.0 || P->lam0 > 1000.0) {
+ return pj_default_destructor(P, PJD_ERR_INVALID_UTM_ZONE);
+ }
+
+ P->y0 = pj_param (P->ctx, P->params, "bsouth").i ? 10000000. : 0.;
+ P->x0 = 500000.;
+ if (pj_param (P->ctx, P->params, "tzone").i) /* zone input ? */
+ {
+ zone = pj_param(P->ctx, P->params, "izone").i;
+ if (zone > 0 && zone <= 60)
+ --zone;
+ else {
+ return pj_default_destructor(P, PJD_ERR_INVALID_UTM_ZONE);
+ }
+ }
+ else /* nearest central meridian input */
+ {
+ zone = lround((floor ((adjlon (P->lam0) + M_PI) * 30. / M_PI)));
+ if (zone < 0)
+ zone = 0;
+ else if (zone >= 60)
+ zone = 59;
+ }
+ P->lam0 = (zone + .5) * M_PI / 30. - M_PI;
+ P->k0 = 0.9996;
+ P->phi0 = 0.;
+
+ return setup (P);
+}
diff --git a/src/projections/fahey.cpp b/src/projections/fahey.cpp
new file mode 100644
index 00000000..85e0ab69
--- /dev/null
+++ b/src/projections/fahey.cpp
@@ -0,0 +1,41 @@
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(fahey, "Fahey") "\n\tPcyl, Sph";
+
+#define TOL 1e-6
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ (void) P;
+
+ xy.x = tan(0.5 * lp.phi);
+ xy.y = 1.819152 * xy.x;
+ xy.x = 0.819152 * lp.lam * asqrt(1 - xy.x * xy.x);
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ (void) P;
+
+ xy.y /= 1.819152;
+ lp.phi = 2. * atan(xy.y);
+ xy.y = 1. - xy.y * xy.y;
+ lp.lam = fabs(xy.y) < TOL ? 0. : xy.x / (0.819152 * sqrt(xy.y));
+ return lp;
+}
+
+
+PJ *PROJECTION(fahey) {
+ P->es = 0.;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/fouc_s.cpp b/src/projections/fouc_s.cpp
new file mode 100644
index 00000000..c5e711de
--- /dev/null
+++ b/src/projections/fouc_s.cpp
@@ -0,0 +1,72 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(fouc_s, "Foucaut Sinusoidal") "\n\tPCyl, Sph";
+
+#define MAX_ITER 10
+#define LOOP_TOL 1e-7
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double n, n1;
+};
+} // anonymous namespace
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double t;
+
+ t = cos(lp.phi);
+ xy.x = lp.lam * t / (Q->n + Q->n1 * t);
+ xy.y = Q->n * lp.phi + Q->n1 * sin(lp.phi);
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double V;
+ int i;
+
+ if (Q->n != 0.0) {
+ lp.phi = xy.y;
+ for (i = MAX_ITER; i ; --i) {
+ lp.phi -= V = (Q->n * lp.phi + Q->n1 * sin(lp.phi) - xy.y ) /
+ (Q->n + Q->n1 * cos(lp.phi));
+ if (fabs(V) < LOOP_TOL)
+ break;
+ }
+ if (!i)
+ lp.phi = xy.y < 0. ? -M_HALFPI : M_HALFPI;
+ } else
+ lp.phi = aasin(P->ctx,xy.y);
+ V = cos(lp.phi);
+ lp.lam = xy.x * (Q->n + Q->n1 * V) / V;
+ return lp;
+}
+
+
+PJ *PROJECTION(fouc_s) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->n = pj_param(P->ctx, P->params, "dn").f;
+ if (Q->n < 0. || Q->n > 1.)
+ return pj_default_destructor (P, PJD_ERR_N_OUT_OF_RANGE);
+
+ Q->n1 = 1. - Q->n;
+ P->es = 0;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ return P;
+}
diff --git a/src/projections/gall.cpp b/src/projections/gall.cpp
new file mode 100644
index 00000000..a8697482
--- /dev/null
+++ b/src/projections/gall.cpp
@@ -0,0 +1,44 @@
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(gall, "Gall (Gall Stereographic)") "\n\tCyl, Sph";
+
+#define YF 1.70710678118654752440
+#define XF 0.70710678118654752440
+#define RYF 0.58578643762690495119
+#define RXF 1.41421356237309504880
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ (void) P;
+
+ xy.x = XF * lp.lam;
+ xy.y = YF * tan(.5 * lp.phi);
+
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ (void) P;
+
+ lp.lam = RXF * xy.x;
+ lp.phi = 2. * atan(xy.y * RYF);
+
+ return lp;
+}
+
+
+PJ *PROJECTION(gall) {
+ P->es = 0.0;
+
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/geos.cpp b/src/projections/geos.cpp
new file mode 100644
index 00000000..90fb01ab
--- /dev/null
+++ b/src/projections/geos.cpp
@@ -0,0 +1,238 @@
+/*
+** libproj -- library of cartographic projections
+**
+** Copyright (c) 2004 Gerald I. Evenden
+** Copyright (c) 2012 Martin Raspaud
+**
+** See also (section 4.4.3.2):
+** http://www.eumetsat.int/en/area4/msg/news/us_doc/cgms_03_26.pdf
+**
+** Permission is hereby granted, free of charge, to any person obtaining
+** a copy of this software and associated documentation files (the
+** "Software"), to deal in the Software without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Software, and to
+** permit persons to whom the Software is furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be
+** included in all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#define PJ_LIB__
+#include <errno.h>
+#include <math.h>
+#include <stddef.h>
+
+#include "proj.h"
+#include "projects.h"
+#include "proj_math.h"
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double h;
+ double radius_p;
+ double radius_p2;
+ double radius_p_inv2;
+ double radius_g;
+ double radius_g_1;
+ double C;
+ int flip_axis;
+};
+} // anonymous namespace
+
+PROJ_HEAD(geos, "Geostationary Satellite View") "\n\tAzi, Sph&Ell\n\th=";
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double Vx, Vy, Vz, tmp;
+
+ /* Calculation of the three components of the vector from satellite to
+ ** position on earth surface (lon,lat).*/
+ tmp = cos(lp.phi);
+ Vx = cos (lp.lam) * tmp;
+ Vy = sin (lp.lam) * tmp;
+ Vz = sin (lp.phi);
+
+ /* Check visibility*/
+
+
+ /* Calculation based on view angles from satellite.*/
+ tmp = Q->radius_g - Vx;
+
+ if(Q->flip_axis) {
+ xy.x = Q->radius_g_1 * atan(Vy / hypot(Vz, tmp));
+ xy.y = Q->radius_g_1 * atan(Vz / tmp);
+ } else {
+ xy.x = Q->radius_g_1 * atan(Vy / tmp);
+ xy.y = Q->radius_g_1 * atan(Vz / hypot(Vy, tmp));
+ }
+
+ return xy;
+}
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double r, Vx, Vy, Vz, tmp;
+
+ /* Calculation of geocentric latitude. */
+ lp.phi = atan (Q->radius_p2 * tan (lp.phi));
+
+ /* Calculation of the three components of the vector from satellite to
+ ** position on earth surface (lon,lat).*/
+ r = (Q->radius_p) / hypot(Q->radius_p * cos (lp.phi), sin (lp.phi));
+ Vx = r * cos (lp.lam) * cos (lp.phi);
+ Vy = r * sin (lp.lam) * cos (lp.phi);
+ Vz = r * sin (lp.phi);
+
+ /* Check visibility. */
+ if (((Q->radius_g - Vx) * Vx - Vy * Vy - Vz * Vz * Q->radius_p_inv2) < 0.) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+
+ /* Calculation based on view angles from satellite. */
+ tmp = Q->radius_g - Vx;
+
+ if(Q->flip_axis) {
+ xy.x = Q->radius_g_1 * atan (Vy / hypot (Vz, tmp));
+ xy.y = Q->radius_g_1 * atan (Vz / tmp);
+ } else {
+ xy.x = Q->radius_g_1 * atan (Vy / tmp);
+ xy.y = Q->radius_g_1 * atan (Vz / hypot (Vy, tmp));
+ }
+
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double Vx, Vy, Vz, a, b, det, k;
+
+ /* Setting three components of vector from satellite to position.*/
+ Vx = -1.0;
+ if(Q->flip_axis) {
+ Vz = tan (xy.y / (Q->radius_g - 1.0));
+ Vy = tan (xy.x / (Q->radius_g - 1.0)) * sqrt (1.0 + Vz * Vz);
+ } else {
+ Vy = tan (xy.x / (Q->radius_g - 1.0));
+ Vz = tan (xy.y / (Q->radius_g - 1.0)) * sqrt (1.0 + Vy * Vy);
+ }
+
+ /* Calculation of terms in cubic equation and determinant.*/
+ a = Vy * Vy + Vz * Vz + Vx * Vx;
+ b = 2 * Q->radius_g * Vx;
+ if ((det = (b * b) - 4 * a * Q->C) < 0.) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+
+ /* Calculation of three components of vector from satellite to position.*/
+ k = (-b - sqrt(det)) / (2 * a);
+ Vx = Q->radius_g + k * Vx;
+ Vy *= k;
+ Vz *= k;
+
+ /* Calculation of longitude and latitude.*/
+ lp.lam = atan2 (Vy, Vx);
+ lp.phi = atan (Vz * cos (lp.lam) / Vx);
+
+ return lp;
+}
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double Vx, Vy, Vz, a, b, det, k;
+
+ /* Setting three components of vector from satellite to position.*/
+ Vx = -1.0;
+
+ if(Q->flip_axis) {
+ Vz = tan (xy.y / Q->radius_g_1);
+ Vy = tan (xy.x / Q->radius_g_1) * hypot(1.0, Vz);
+ } else {
+ Vy = tan (xy.x / Q->radius_g_1);
+ Vz = tan (xy.y / Q->radius_g_1) * hypot(1.0, Vy);
+ }
+
+ /* Calculation of terms in cubic equation and determinant.*/
+ a = Vz / Q->radius_p;
+ a = Vy * Vy + a * a + Vx * Vx;
+ b = 2 * Q->radius_g * Vx;
+ if ((det = (b * b) - 4 * a * Q->C) < 0.) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+
+ /* Calculation of three components of vector from satellite to position.*/
+ k = (-b - sqrt(det)) / (2. * a);
+ Vx = Q->radius_g + k * Vx;
+ Vy *= k;
+ Vz *= k;
+
+ /* Calculation of longitude and latitude.*/
+ lp.lam = atan2 (Vy, Vx);
+ lp.phi = atan (Vz * cos (lp.lam) / Vx);
+ lp.phi = atan (Q->radius_p_inv2 * tan (lp.phi));
+
+ return lp;
+}
+
+
+PJ *PROJECTION(geos) {
+ char *sweep_axis;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ if ((Q->h = pj_param(P->ctx, P->params, "dh").f) <= 0.)
+ return pj_default_destructor (P, PJD_ERR_H_LESS_THAN_ZERO);
+
+ sweep_axis = pj_param(P->ctx, P->params, "ssweep").s;
+ if (sweep_axis == nullptr)
+ Q->flip_axis = 0;
+ else {
+ if ((sweep_axis[0] != 'x' && sweep_axis[0] != 'y') ||
+ sweep_axis[1] != '\0')
+ return pj_default_destructor (P, PJD_ERR_INVALID_SWEEP_AXIS);
+
+ if (sweep_axis[0] == 'x')
+ Q->flip_axis = 1;
+ else
+ Q->flip_axis = 0;
+ }
+
+ Q->radius_g_1 = Q->h / P->a;
+ Q->radius_g = 1. + Q->radius_g_1;
+ Q->C = Q->radius_g * Q->radius_g - 1.0;
+ if (P->es != 0.0) {
+ Q->radius_p = sqrt (P->one_es);
+ Q->radius_p2 = P->one_es;
+ Q->radius_p_inv2 = P->rone_es;
+ P->inv = e_inverse;
+ P->fwd = e_forward;
+ } else {
+ Q->radius_p = Q->radius_p2 = Q->radius_p_inv2 = 1.0;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ }
+
+ return P;
+}
diff --git a/src/projections/gins8.cpp b/src/projections/gins8.cpp
new file mode 100644
index 00000000..cc422437
--- /dev/null
+++ b/src/projections/gins8.cpp
@@ -0,0 +1,33 @@
+#define PJ_LIB__
+#include "projects.h"
+
+PROJ_HEAD(gins8, "Ginsburg VIII (TsNIIGAiK)") "\n\tPCyl, Sph, no inv";
+
+#define Cl 0.000952426
+#define Cp 0.162388
+#define C12 0.08333333333333333
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ double t = lp.phi * lp.phi;
+ (void) P;
+
+ xy.y = lp.phi * (1. + t * C12);
+ xy.x = lp.lam * (1. - Cp * t);
+ t = lp.lam * lp.lam;
+ xy.x *= (0.87 - Cl * t * t);
+
+ return xy;
+}
+
+
+PJ *PROJECTION(gins8) {
+ P->es = 0.0;
+ P->inv = nullptr;
+ P->fwd = s_forward;
+
+ return P;
+}
+
+
diff --git a/src/projections/gn_sinu.cpp b/src/projections/gn_sinu.cpp
new file mode 100644
index 00000000..530de229
--- /dev/null
+++ b/src/projections/gn_sinu.cpp
@@ -0,0 +1,189 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(gn_sinu, "General Sinusoidal Series") "\n\tPCyl, Sph\n\tm= n=";
+PROJ_HEAD(sinu, "Sinusoidal (Sanson-Flamsteed)") "\n\tPCyl, Sph&Ell";
+PROJ_HEAD(eck6, "Eckert VI") "\n\tPCyl, Sph";
+PROJ_HEAD(mbtfps, "McBryde-Thomas Flat-Polar Sinusoidal") "\n\tPCyl, Sph";
+
+#define EPS10 1e-10
+#define MAX_ITER 8
+#define LOOP_TOL 1e-7
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double *en;
+ double m, n, C_x, C_y;
+};
+} // anonymous namespace
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ XY xy = {0.0,0.0};
+ double s, c;
+
+ xy.y = pj_mlfn(lp.phi, s = sin(lp.phi), c = cos(lp.phi), static_cast<struct pj_opaque*>(P->opaque)->en);
+ xy.x = lp.lam * c / sqrt(1. - P->es * s * s);
+ return xy;
+}
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */
+ LP lp = {0.0,0.0};
+ double s;
+
+ if ((s = fabs(lp.phi = pj_inv_mlfn(P->ctx, xy.y, P->es, static_cast<struct pj_opaque*>(P->opaque)->en))) < M_HALFPI) {
+ s = sin(lp.phi);
+ lp.lam = xy.x * sqrt(1. - P->es * s * s) / cos(lp.phi);
+ } else if ((s - EPS10) < M_HALFPI) {
+ lp.lam = 0.;
+ } else {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ }
+
+ return lp;
+}
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+
+ if (Q->m == 0.0)
+ lp.phi = Q->n != 1. ? aasin(P->ctx,Q->n * sin(lp.phi)): lp.phi;
+ else {
+ double k, V;
+ int i;
+
+ k = Q->n * sin(lp.phi);
+ for (i = MAX_ITER; i ; --i) {
+ lp.phi -= V = (Q->m * lp.phi + sin(lp.phi) - k) /
+ (Q->m + cos(lp.phi));
+ if (fabs(V) < LOOP_TOL)
+ break;
+ }
+ if (!i) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+
+ }
+ xy.x = Q->C_x * lp.lam * (Q->m + cos(lp.phi));
+ xy.y = Q->C_y * lp.phi;
+
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+
+ xy.y /= Q->C_y;
+ lp.phi = (Q->m != 0.0) ? aasin(P->ctx,(Q->m * xy.y + sin(xy.y)) / Q->n) :
+ ( Q->n != 1. ? aasin(P->ctx,sin(xy.y) / Q->n) : xy.y );
+ lp.lam = xy.x / (Q->C_x * (Q->m + cos(xy.y)));
+ return lp;
+}
+
+
+static PJ *destructor (PJ *P, int errlev) { /* Destructor */
+ if (nullptr==P)
+ return nullptr;
+
+ if (nullptr==P->opaque)
+ return pj_default_destructor (P, errlev);
+
+ pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->en);
+ return pj_default_destructor (P, errlev);
+}
+
+
+
+/* for spheres, only */
+static void setup(PJ *P) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ P->es = 0;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ Q->C_x = (Q->C_y = sqrt((Q->m + 1.) / Q->n))/(Q->m + 1.);
+}
+
+
+PJ *PROJECTION(sinu) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+ P->destructor = destructor;
+
+ if (!(Q->en = pj_enfn(P->es)))
+ return pj_default_destructor (P, ENOMEM);
+
+ if (P->es != 0.0) {
+ P->inv = e_inverse;
+ P->fwd = e_forward;
+ } else {
+ Q->n = 1.;
+ Q->m = 0.;
+ setup(P);
+ }
+ return P;
+}
+
+
+PJ *PROJECTION(eck6) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+ P->destructor = destructor;
+
+ Q->m = 1.;
+ Q->n = 2.570796326794896619231321691;
+ setup(P);
+
+ return P;
+}
+
+
+PJ *PROJECTION(mbtfps) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+ P->destructor = destructor;
+
+ Q->m = 0.5;
+ Q->n = 1.785398163397448309615660845;
+ setup(P);
+
+ return P;
+}
+
+
+PJ *PROJECTION(gn_sinu) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+ P->destructor = destructor;
+
+ if (pj_param(P->ctx, P->params, "tn").i && pj_param(P->ctx, P->params, "tm").i) {
+ Q->n = pj_param(P->ctx, P->params, "dn").f;
+ Q->m = pj_param(P->ctx, P->params, "dm").f;
+ if (Q->n <= 0 || Q->m < 0)
+ return destructor (P, PJD_ERR_INVALID_M_OR_N);
+ } else
+ return destructor (P, PJD_ERR_INVALID_M_OR_N);
+
+ setup(P);
+
+ return P;
+}
diff --git a/src/projections/gnom.cpp b/src/projections/gnom.cpp
new file mode 100644
index 00000000..a4b5e35d
--- /dev/null
+++ b/src/projections/gnom.cpp
@@ -0,0 +1,147 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+#include "proj_math.h"
+
+PROJ_HEAD(gnom, "Gnomonic") "\n\tAzi, Sph";
+
+#define EPS10 1.e-10
+
+namespace { // anonymous namespace
+enum Mode {
+ N_POLE = 0,
+ S_POLE = 1,
+ EQUIT = 2,
+ OBLIQ = 3
+};
+} // anonymous namespace
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double sinph0;
+ double cosph0;
+ enum Mode mode;
+};
+} // anonymous namespace
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double coslam, cosphi, sinphi;
+
+ sinphi = sin(lp.phi);
+ cosphi = cos(lp.phi);
+ coslam = cos(lp.lam);
+
+ switch (Q->mode) {
+ case EQUIT:
+ xy.y = cosphi * coslam;
+ break;
+ case OBLIQ:
+ xy.y = Q->sinph0 * sinphi + Q->cosph0 * cosphi * coslam;
+ break;
+ case S_POLE:
+ xy.y = - sinphi;
+ break;
+ case N_POLE:
+ xy.y = sinphi;
+ break;
+ }
+
+ if (xy.y <= EPS10) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+
+ xy.x = (xy.y = 1. / xy.y) * cosphi * sin(lp.lam);
+ switch (Q->mode) {
+ case EQUIT:
+ xy.y *= sinphi;
+ break;
+ case OBLIQ:
+ xy.y *= Q->cosph0 * sinphi - Q->sinph0 * cosphi * coslam;
+ break;
+ case N_POLE:
+ coslam = - coslam;
+ /*-fallthrough*/
+ case S_POLE:
+ xy.y *= cosphi * coslam;
+ break;
+ }
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double rh, cosz, sinz;
+
+ rh = hypot(xy.x, xy.y);
+ sinz = sin(lp.phi = atan(rh));
+ cosz = sqrt(1. - sinz * sinz);
+
+ if (fabs(rh) <= EPS10) {
+ lp.phi = P->phi0;
+ lp.lam = 0.;
+ } else {
+ switch (Q->mode) {
+ case OBLIQ:
+ lp.phi = cosz * Q->sinph0 + xy.y * sinz * Q->cosph0 / rh;
+ if (fabs(lp.phi) >= 1.)
+ lp.phi = lp.phi > 0. ? M_HALFPI : - M_HALFPI;
+ else
+ lp.phi = asin(lp.phi);
+ xy.y = (cosz - Q->sinph0 * sin(lp.phi)) * rh;
+ xy.x *= sinz * Q->cosph0;
+ break;
+ case EQUIT:
+ lp.phi = xy.y * sinz / rh;
+ if (fabs(lp.phi) >= 1.)
+ lp.phi = lp.phi > 0. ? M_HALFPI : - M_HALFPI;
+ else
+ lp.phi = asin(lp.phi);
+ xy.y = cosz * rh;
+ xy.x *= sinz;
+ break;
+ case S_POLE:
+ lp.phi -= M_HALFPI;
+ break;
+ case N_POLE:
+ lp.phi = M_HALFPI - lp.phi;
+ xy.y = -xy.y;
+ break;
+ }
+ lp.lam = atan2(xy.x, xy.y);
+ }
+ return lp;
+}
+
+
+PJ *PROJECTION(gnom) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ if (fabs(fabs(P->phi0) - M_HALFPI) < EPS10) {
+ Q->mode = P->phi0 < 0. ? S_POLE : N_POLE;
+ } else if (fabs(P->phi0) < EPS10) {
+ Q->mode = EQUIT;
+ } else {
+ Q->mode = OBLIQ;
+ Q->sinph0 = sin(P->phi0);
+ Q->cosph0 = cos(P->phi0);
+ }
+
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ P->es = 0.;
+
+ return P;
+}
diff --git a/src/projections/goode.cpp b/src/projections/goode.cpp
new file mode 100644
index 00000000..c79d125e
--- /dev/null
+++ b/src/projections/goode.cpp
@@ -0,0 +1,84 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(goode, "Goode Homolosine") "\n\tPCyl, Sph";
+
+#define Y_COR 0.05280
+#define PHI_LIM 0.71093078197902358062
+
+C_NAMESPACE PJ *pj_sinu(PJ *), *pj_moll(PJ *);
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ PJ *sinu;
+ PJ *moll;
+};
+} // anonymous namespace
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+
+ if (fabs(lp.phi) <= PHI_LIM)
+ xy = Q->sinu->fwd(lp, Q->sinu);
+ else {
+ xy = Q->moll->fwd(lp, Q->moll);
+ xy.y -= lp.phi >= 0.0 ? Y_COR : -Y_COR;
+ }
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+
+ if (fabs(xy.y) <= PHI_LIM)
+ lp = Q->sinu->inv(xy, Q->sinu);
+ else {
+ xy.y += xy.y >= 0.0 ? Y_COR : -Y_COR;
+ lp = Q->moll->inv(xy, Q->moll);
+ }
+ return lp;
+}
+
+
+static PJ *destructor (PJ *P, int errlev) { /* Destructor */
+ if (nullptr==P)
+ return nullptr;
+ if (nullptr==P->opaque)
+ return pj_default_destructor (P, errlev);
+ pj_free (static_cast<struct pj_opaque*>(P->opaque)->sinu);
+ pj_free (static_cast<struct pj_opaque*>(P->opaque)->moll);
+ return pj_default_destructor (P, errlev);
+}
+
+
+
+PJ *PROJECTION(goode) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+ P->destructor = destructor;
+
+ P->es = 0.;
+ if (!(Q->sinu = pj_sinu(nullptr)) || !(Q->moll = pj_moll(nullptr)))
+ return destructor (P, ENOMEM);
+ Q->sinu->es = 0.;
+ Q->sinu->ctx = P->ctx;
+ Q->moll->ctx = P->ctx;
+ if (!(Q->sinu = pj_sinu(Q->sinu)) || !(Q->moll = pj_moll(Q->moll)))
+ return destructor (P, ENOMEM);
+
+ P->fwd = s_forward;
+ P->inv = s_inverse;
+
+ return P;
+}
diff --git a/src/projections/gstmerc.cpp b/src/projections/gstmerc.cpp
new file mode 100644
index 00000000..9b819bac
--- /dev/null
+++ b/src/projections/gstmerc.cpp
@@ -0,0 +1,74 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(gstmerc, "Gauss-Schreiber Transverse Mercator (aka Gauss-Laborde Reunion)")
+ "\n\tCyl, Sph&Ell\n\tlat_0= lon_0= k_0=";
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double lamc;
+ double phic;
+ double c;
+ double n1;
+ double n2;
+ double XS;
+ double YS;
+};
+} // anonymous namespace
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double L, Ls, sinLs1, Ls1;
+
+ L = Q->n1*lp.lam;
+ Ls = Q->c + Q->n1 * log(pj_tsfn(-1.0 * lp.phi, -1.0 * sin(lp.phi), P->e));
+ sinLs1 = sin(L) / cosh(Ls);
+ Ls1 = log(pj_tsfn(-1.0 * asin(sinLs1), 0.0, 0.0));
+ xy.x = (Q->XS + Q->n2*Ls1) * P->ra;
+ xy.y = (Q->YS + Q->n2*atan(sinh(Ls) / cos(L))) * P->ra;
+
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double L, LC, sinC;
+
+ L = atan(sinh((xy.x * P->a - Q->XS) / Q->n2) / cos((xy.y * P->a - Q->YS) / Q->n2));
+ sinC = sin((xy.y * P->a - Q->YS) / Q->n2) / cosh((xy.x * P->a - Q->XS) / Q->n2);
+ LC = log(pj_tsfn(-1.0 * asin(sinC), 0.0, 0.0));
+ lp.lam = L / Q->n1;
+ lp.phi = -1.0 * pj_phi2(P->ctx, exp((LC - Q->c) / Q->n1), P->e);
+
+ return lp;
+}
+
+
+PJ *PROJECTION(gstmerc) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->lamc = P->lam0;
+ Q->n1 = sqrt(1.0 + P->es * pow(cos(P->phi0), 4.0) / (1.0 - P->es));
+ Q->phic = asin(sin(P->phi0) / Q->n1);
+ Q->c = log(pj_tsfn(-1.0 * Q->phic, 0.0, 0.0))
+ - Q->n1 * log(pj_tsfn(-1.0 * P->phi0, -1.0 * sin(P->phi0), P->e));
+ Q->n2 = P->k0 * P->a * sqrt(1.0 - P->es) / (1.0 - P->es * sin(P->phi0) * sin(P->phi0));
+ Q->XS = 0;
+ Q->YS = -1.0 * Q->n2 * Q->phic;
+
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/hammer.cpp b/src/projections/hammer.cpp
new file mode 100644
index 00000000..d4caa656
--- /dev/null
+++ b/src/projections/hammer.cpp
@@ -0,0 +1,77 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(hammer, "Hammer & Eckert-Greifendorff")
+ "\n\tMisc Sph, \n\tW= M=";
+
+#define EPS 1.0e-10
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double w;
+ double m, rm;
+};
+} // anonymous namespace
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double cosphi, d;
+
+ d = sqrt(2./(1. + (cosphi = cos(lp.phi)) * cos(lp.lam *= Q->w)));
+ xy.x = Q->m * d * cosphi * sin(lp.lam);
+ xy.y = Q->rm * d * sin(lp.phi);
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double z;
+
+ z = sqrt(1. - 0.25*Q->w*Q->w*xy.x*xy.x - 0.25*xy.y*xy.y);
+ if (fabs(2.*z*z-1.) < EPS) {
+ lp.lam = HUGE_VAL;
+ lp.phi = HUGE_VAL;
+ proj_errno_set(P, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT);
+ } else {
+ lp.lam = aatan2(Q->w * xy.x * z,2. * z * z - 1)/Q->w;
+ lp.phi = aasin(P->ctx,z * xy.y);
+ }
+ return lp;
+}
+
+
+PJ *PROJECTION(hammer) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ if (pj_param(P->ctx, P->params, "tW").i) {
+ if ((Q->w = fabs(pj_param(P->ctx, P->params, "dW").f)) <= 0.)
+ return pj_default_destructor (P, PJD_ERR_W_OR_M_ZERO_OR_LESS);
+ } else
+ Q->w = .5;
+ if (pj_param(P->ctx, P->params, "tM").i) {
+ if ((Q->m = fabs(pj_param(P->ctx, P->params, "dM").f)) <= 0.)
+ return pj_default_destructor (P, PJD_ERR_W_OR_M_ZERO_OR_LESS);
+ } else
+ Q->m = 1.;
+
+ Q->rm = 1. / Q->m;
+ Q->m /= Q->w;
+
+ P->es = 0.;
+ P->fwd = s_forward;
+ P->inv = s_inverse;
+
+ return P;
+}
diff --git a/src/projections/hatano.cpp b/src/projections/hatano.cpp
new file mode 100644
index 00000000..019671cc
--- /dev/null
+++ b/src/projections/hatano.cpp
@@ -0,0 +1,83 @@
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(hatano, "Hatano Asymmetrical Equal Area") "\n\tPCyl, Sph";
+
+#define NITER 20
+#define EPS 1e-7
+#define ONETOL 1.000001
+#define CN 2.67595
+#define CS 2.43763
+#define RCN 0.37369906014686373063
+#define RCS 0.41023453108141924738
+#define FYCN 1.75859
+#define FYCS 1.93052
+#define RYCN 0.56863737426006061674
+#define RYCS 0.51799515156538134803
+#define FXC 0.85
+#define RXC 1.17647058823529411764
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ double th1, c;
+ int i;
+ (void) P;
+
+ c = sin(lp.phi) * (lp.phi < 0. ? CS : CN);
+ for (i = NITER; i; --i) {
+ lp.phi -= th1 = (lp.phi + sin(lp.phi) - c) / (1. + cos(lp.phi));
+ if (fabs(th1) < EPS) break;
+ }
+ xy.x = FXC * lp.lam * cos(lp.phi *= .5);
+ xy.y = sin(lp.phi) * (lp.phi < 0. ? FYCS : FYCN);
+
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ double th;
+
+ th = xy.y * ( xy.y < 0. ? RYCS : RYCN);
+ if (fabs(th) > 1.) {
+ if (fabs(th) > ONETOL) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ } else {
+ th = th > 0. ? M_HALFPI : - M_HALFPI;
+ }
+ } else {
+ th = asin(th);
+ }
+
+ lp.lam = RXC * xy.x / cos(th);
+ th += th;
+ lp.phi = (th + sin(th)) * (xy.y < 0. ? RCS : RCN);
+ if (fabs(lp.phi) > 1.) {
+ if (fabs(lp.phi) > ONETOL) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ } else {
+ lp.phi = lp.phi > 0. ? M_HALFPI : - M_HALFPI;
+ }
+ } else {
+ lp.phi = asin(lp.phi);
+ }
+
+ return (lp);
+}
+
+
+PJ *PROJECTION(hatano) {
+ P->es = 0.;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/healpix.cpp b/src/projections/healpix.cpp
new file mode 100644
index 00000000..7f0b3e83
--- /dev/null
+++ b/src/projections/healpix.cpp
@@ -0,0 +1,674 @@
+/******************************************************************************
+ * Project: PROJ.4
+ * Purpose: Implementation of the HEALPix and rHEALPix projections.
+ * For background see <http://code.scenzgrid.org/index.php/p/scenzgrid-py/source/tree/master/docs/rhealpix_dggs.pdf>.
+ * Authors: Alex Raichev (raichev@cs.auckland.ac.nz)
+ * Michael Speth (spethm@landcareresearch.co.nz)
+ * Notes: Raichev implemented these projections in Python and
+ * Speth translated them into C here.
+ ******************************************************************************
+ * Copyright (c) 2001, Thomas Flemming, tf@ttqv.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substcounteral portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *****************************************************************************/
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj_internal.h"
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(healpix, "HEALPix") "\n\tSph&Ell";
+PROJ_HEAD(rhealpix, "rHEALPix") "\n\tSph&Ell\n\tnorth_square= south_square=";
+
+/* Matrix for counterclockwise rotation by pi/2: */
+# define R1 {{ 0,-1},{ 1, 0}}
+/* Matrix for counterclockwise rotation by pi: */
+# define R2 {{-1, 0},{ 0,-1}}
+/* Matrix for counterclockwise rotation by 3*pi/2: */
+# define R3 {{ 0, 1},{-1, 0}}
+/* Identity matrix */
+# define IDENT {{1, 0},{0, 1}}
+/* IDENT, R1, R2, R3, R1 inverse, R2 inverse, R3 inverse:*/
+# define ROT {IDENT, R1, R2, R3, R3, R2, R1}
+/* Fuzz to handle rounding errors: */
+# define EPS 1e-15
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ int north_square;
+ int south_square;
+ double qp;
+ double *apa;
+};
+} // anonymous namespace
+
+typedef struct {
+ int cn; /* An integer 0--3 indicating the position of the polar cap. */
+ double x, y; /* Coordinates of the pole point (point of most extreme latitude on the polar caps). */
+ enum Region {north, south, equatorial} region;
+} CapMap;
+
+static const double rot[7][2][2] = ROT;
+
+/**
+ * Returns the sign of the double.
+ * @param v the parameter whose sign is returned.
+ * @return 1 for positive number, -1 for negative, and 0 for zero.
+ **/
+static double sign (double v) {
+ return v > 0 ? 1 : (v < 0 ? -1 : 0);
+}
+
+
+/**
+ * Return the index of the matrix in ROT.
+ * @param index ranges from -3 to 3.
+ */
+static int get_rotate_index(int index) {
+ switch(index) {
+ case 0:
+ return 0;
+ case 1:
+ return 1;
+ case 2:
+ return 2;
+ case 3:
+ return 3;
+ case -1:
+ return 4;
+ case -2:
+ return 5;
+ case -3:
+ return 6;
+ }
+ return 0;
+}
+
+
+/**
+ * Return 1 if point (testx, testy) lies in the interior of the polygon
+ * determined by the vertices in vert, and return 0 otherwise.
+ * See http://paulbourke.net/geometry/polygonmesh/ for more details.
+ * @param nvert the number of vertices in the polygon.
+ * @param vert the (x, y)-coordinates of the polygon's vertices
+ **/
+static int pnpoly(int nvert, double vert[][2], double testx, double testy) {
+ int i;
+ int counter = 0;
+ double xinters;
+ XY p1, p2;
+
+ /* Check for boundrary cases */
+ for (i = 0; i < nvert; i++) {
+ if (testx == vert[i][0] && testy == vert[i][1]) {
+ return 1;
+ }
+ }
+
+ p1.x = vert[0][0];
+ p1.y = vert[0][1];
+
+ for (i = 1; i < nvert; i++) {
+ p2.x = vert[i % nvert][0];
+ p2.y = vert[i % nvert][1];
+ if (testy > MIN(p1.y, p2.y) &&
+ testy <= MAX(p1.y, p2.y) &&
+ testx <= MAX(p1.x, p2.x) &&
+ p1.y != p2.y)
+ {
+ xinters = (testy-p1.y)*(p2.x-p1.x)/(p2.y-p1.y)+p1.x;
+ if (p1.x == p2.x || testx <= xinters)
+ counter++;
+ }
+ p1 = p2;
+ }
+
+ if (counter % 2 == 0) {
+ return 0;
+ } else {
+ return 1;
+ }
+}
+
+
+/**
+ * Return 1 if (x, y) lies in (the interior or boundary of) the image of the
+ * HEALPix projection (in case proj=0) or in the image the rHEALPix projection
+ * (in case proj=1), and return 0 otherwise.
+ * @param north_square the position of the north polar square (rHEALPix only)
+ * @param south_square the position of the south polar square (rHEALPix only)
+ **/
+static int in_image(double x, double y, int proj, int north_square,
+ int south_square) {
+ if (proj == 0) {
+ double healpixVertsJit[][2] = {
+ {-M_PI - EPS, M_FORTPI},
+ {-3*M_FORTPI, M_HALFPI + EPS},
+ {-M_HALFPI, M_FORTPI + EPS},
+ {-M_FORTPI, M_HALFPI + EPS},
+ {0.0, M_FORTPI + EPS},
+ {M_FORTPI, M_HALFPI + EPS},
+ {M_HALFPI, M_FORTPI + EPS},
+ {3*M_FORTPI, M_HALFPI + EPS},
+ {M_PI + EPS, M_FORTPI},
+ {M_PI + EPS, -M_FORTPI},
+ {3*M_FORTPI, -M_HALFPI - EPS},
+ {M_HALFPI, -M_FORTPI - EPS},
+ {M_FORTPI, -M_HALFPI - EPS},
+ {0.0, -M_FORTPI - EPS},
+ {-M_FORTPI, -M_HALFPI - EPS},
+ {-M_HALFPI, -M_FORTPI - EPS},
+ {-3*M_FORTPI, -M_HALFPI - EPS},
+ {-M_PI - EPS, -M_FORTPI}
+ };
+ return pnpoly((int)sizeof(healpixVertsJit)/
+ sizeof(healpixVertsJit[0]), healpixVertsJit, x, y);
+ } else {
+ /**
+ * Assigning each element by index to avoid warnings such as
+ * 'initializer element is not computable at load time'.
+ * Before C99 this was not allowed and to keep as portable as
+ * possible we do it the C89 way here.
+ * We need to assign the array this way because the input is
+ * dynamic (north_square and south_square vars are unknown at
+ * compile time).
+ **/
+ double rhealpixVertsJit[12][2];
+ rhealpixVertsJit[0][0] = -M_PI - EPS;
+ rhealpixVertsJit[0][1] = M_FORTPI + EPS;
+ rhealpixVertsJit[1][0] = -M_PI + north_square*M_HALFPI- EPS;
+ rhealpixVertsJit[1][1] = M_FORTPI + EPS;
+ rhealpixVertsJit[2][0] = -M_PI + north_square*M_HALFPI- EPS;
+ rhealpixVertsJit[2][1] = 3*M_FORTPI + EPS;
+ rhealpixVertsJit[3][0] = -M_PI + (north_square + 1.0)*M_HALFPI + EPS;
+ rhealpixVertsJit[3][1] = 3*M_FORTPI + EPS;
+ rhealpixVertsJit[4][0] = -M_PI + (north_square + 1.0)*M_HALFPI + EPS;
+ rhealpixVertsJit[4][1] = M_FORTPI + EPS;
+ rhealpixVertsJit[5][0] = M_PI + EPS;
+ rhealpixVertsJit[5][1] = M_FORTPI + EPS;
+ rhealpixVertsJit[6][0] = M_PI + EPS;
+ rhealpixVertsJit[6][1] = -M_FORTPI - EPS;
+ rhealpixVertsJit[7][0] = -M_PI + (south_square + 1.0)*M_HALFPI + EPS;
+ rhealpixVertsJit[7][1] = -M_FORTPI - EPS;
+ rhealpixVertsJit[8][0] = -M_PI + (south_square + 1.0)*M_HALFPI + EPS;
+ rhealpixVertsJit[8][1] = -3*M_FORTPI - EPS;
+ rhealpixVertsJit[9][0] = -M_PI + south_square*M_HALFPI - EPS;
+ rhealpixVertsJit[9][1] = -3*M_FORTPI - EPS;
+ rhealpixVertsJit[10][0] = -M_PI + south_square*M_HALFPI - EPS;
+ rhealpixVertsJit[10][1] = -M_FORTPI - EPS;
+ rhealpixVertsJit[11][0] = -M_PI - EPS;
+ rhealpixVertsJit[11][1] = -M_FORTPI - EPS;
+
+ return pnpoly((int)sizeof(rhealpixVertsJit)/
+ sizeof(rhealpixVertsJit[0]), rhealpixVertsJit, x, y);
+ }
+}
+
+
+/**
+ * Return the authalic latitude of latitude alpha (if inverse=0) or
+ * return the approximate latitude of authalic latitude alpha (if inverse=1).
+ * P contains the relevant ellipsoid parameters.
+ **/
+static double auth_lat(PJ *P, double alpha, int inverse) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ if (inverse == 0) {
+ /* Authalic latitude. */
+ double q = pj_qsfn(sin(alpha), P->e, 1.0 - P->es);
+ double qp = Q->qp;
+ double ratio = q/qp;
+
+ if (fabs(ratio) > 1) {
+ /* Rounding error. */
+ ratio = sign(ratio);
+ }
+ return asin(ratio);
+ } else {
+ /* Approximation to inverse authalic latitude. */
+ return pj_authlat(alpha, Q->apa);
+ }
+}
+
+
+/**
+ * Return the HEALPix projection of the longitude-latitude point lp on
+ * the unit sphere.
+**/
+static XY healpix_sphere(LP lp) {
+ double lam = lp.lam;
+ double phi = lp.phi;
+ double phi0 = asin(2.0/3.0);
+ XY xy;
+
+ /* equatorial region */
+ if ( fabs(phi) <= phi0) {
+ xy.x = lam;
+ xy.y = 3*M_PI/8*sin(phi);
+ } else {
+ double lamc;
+ double sigma = sqrt(3*(1 - fabs(sin(phi))));
+ double cn = floor(2*lam / M_PI + 2);
+ if (cn >= 4) {
+ cn = 3;
+ }
+ lamc = -3*M_FORTPI + M_HALFPI*cn;
+ xy.x = lamc + (lam - lamc)*sigma;
+ xy.y = sign(phi)*M_FORTPI*(2 - sigma);
+ }
+ return xy;
+}
+
+
+/**
+ * Return the inverse of healpix_sphere().
+**/
+static LP healpix_sphere_inverse(XY xy) {
+ LP lp;
+ double x = xy.x;
+ double y = xy.y;
+ double y0 = M_FORTPI;
+
+ /* Equatorial region. */
+ if (fabs(y) <= y0) {
+ lp.lam = x;
+ lp.phi = asin(8*y/(3*M_PI));
+ } else if (fabs(y) < M_HALFPI) {
+ double cn = floor(2*x/M_PI + 2);
+ double xc, tau;
+ if (cn >= 4) {
+ cn = 3;
+ }
+ xc = -3*M_FORTPI + M_HALFPI*cn;
+ tau = 2.0 - 4*fabs(y)/M_PI;
+ lp.lam = xc + (x - xc)/tau;
+ lp.phi = sign(y)*asin(1.0 - pow(tau, 2)/3.0);
+ } else {
+ lp.lam = -M_PI;
+ lp.phi = sign(y)*M_HALFPI;
+ }
+ return (lp);
+}
+
+
+/**
+ * Return the vector sum a + b, where a and b are 2-dimensional vectors.
+ * @param ret holds a + b.
+ **/
+static void vector_add(const double a[2], const double b[2], double *ret) {
+ int i;
+ for(i = 0; i < 2; i++) {
+ ret[i] = a[i] + b[i];
+ }
+}
+
+
+/**
+ * Return the vector difference a - b, where a and b are 2-dimensional vectors.
+ * @param ret holds a - b.
+ **/
+static void vector_sub(const double a[2], const double b[2], double*ret) {
+ int i;
+ for(i = 0; i < 2; i++) {
+ ret[i] = a[i] - b[i];
+ }
+}
+
+
+/**
+ * Return the 2 x 1 matrix product a*b, where a is a 2 x 2 matrix and
+ * b is a 2 x 1 matrix.
+ * @param ret holds a*b.
+ **/
+static void dot_product(const double a[2][2], const double b[2], double *ret) {
+ int i, j;
+ int length = 2;
+ for(i = 0; i < length; i++) {
+ ret[i] = 0;
+ for(j = 0; j < length; j++) {
+ ret[i] += a[i][j]*b[j];
+ }
+ }
+}
+
+
+/**
+ * Return the number of the polar cap, the pole point coordinates, and
+ * the region that (x, y) lies in.
+ * If inverse=0, then assume (x,y) lies in the image of the HEALPix
+ * projection of the unit sphere.
+ * If inverse=1, then assume (x,y) lies in the image of the
+ * (north_square, south_square)-rHEALPix projection of the unit sphere.
+ **/
+static CapMap get_cap(double x, double y, int north_square, int south_square,
+ int inverse) {
+ CapMap capmap;
+ double c;
+
+ capmap.x = x;
+ capmap.y = y;
+ if (inverse == 0) {
+ if (y > M_FORTPI) {
+ capmap.region = CapMap::north;
+ c = M_HALFPI;
+ } else if (y < -M_FORTPI) {
+ capmap.region = CapMap::south;
+ c = -M_HALFPI;
+ } else {
+ capmap.region = CapMap::equatorial;
+ capmap.cn = 0;
+ return capmap;
+ }
+ /* polar region */
+ if (x < -M_HALFPI) {
+ capmap.cn = 0;
+ capmap.x = (-3*M_FORTPI);
+ capmap.y = c;
+ } else if (x >= -M_HALFPI && x < 0) {
+ capmap.cn = 1;
+ capmap.x = -M_FORTPI;
+ capmap.y = c;
+ } else if (x >= 0 && x < M_HALFPI) {
+ capmap.cn = 2;
+ capmap.x = M_FORTPI;
+ capmap.y = c;
+ } else {
+ capmap.cn = 3;
+ capmap.x = 3*M_FORTPI;
+ capmap.y = c;
+ }
+ } else {
+ if (y > M_FORTPI) {
+ capmap.region = CapMap::north;
+ capmap.x = -3*M_FORTPI + north_square*M_HALFPI;
+ capmap.y = M_HALFPI;
+ x = x - north_square*M_HALFPI;
+ } else if (y < -M_FORTPI) {
+ capmap.region = CapMap::south;
+ capmap.x = -3*M_FORTPI + south_square*M_HALFPI;
+ capmap.y = -M_HALFPI;
+ x = x - south_square*M_HALFPI;
+ } else {
+ capmap.region = CapMap::equatorial;
+ capmap.cn = 0;
+ return capmap;
+ }
+ /* Polar Region, find the HEALPix polar cap number that
+ x, y moves to when rHEALPix polar square is disassembled. */
+ if (capmap.region == CapMap::north) {
+ if (y >= -x - M_FORTPI - EPS && y < x + 5*M_FORTPI - EPS) {
+ capmap.cn = (north_square + 1) % 4;
+ } else if (y > -x -M_FORTPI + EPS && y >= x + 5*M_FORTPI - EPS) {
+ capmap.cn = (north_square + 2) % 4;
+ } else if (y <= -x -M_FORTPI + EPS && y > x + 5*M_FORTPI + EPS) {
+ capmap.cn = (north_square + 3) % 4;
+ } else {
+ capmap.cn = north_square;
+ }
+ } else if (capmap.region == CapMap::south) {
+ if (y <= x + M_FORTPI + EPS && y > -x - 5*M_FORTPI + EPS) {
+ capmap.cn = (south_square + 1) % 4;
+ } else if (y < x + M_FORTPI - EPS && y <= -x - 5*M_FORTPI + EPS) {
+ capmap.cn = (south_square + 2) % 4;
+ } else if (y >= x + M_FORTPI - EPS && y < -x - 5*M_FORTPI - EPS) {
+ capmap.cn = (south_square + 3) % 4;
+ } else {
+ capmap.cn = south_square;
+ }
+ }
+ }
+ return capmap;
+}
+
+
+/**
+ * Rearrange point (x, y) in the HEALPix projection by
+ * combining the polar caps into two polar squares.
+ * Put the north polar square in position north_square and
+ * the south polar square in position south_square.
+ * If inverse=1, then uncombine the polar caps.
+ * @param north_square integer between 0 and 3.
+ * @param south_square integer between 0 and 3.
+ **/
+static XY combine_caps(double x, double y, int north_square, int south_square,
+ int inverse) {
+ XY xy;
+ double v[2];
+ double c[2];
+ double vector[2];
+ double v_min_c[2];
+ double ret_dot[2];
+ const double (*tmpRot)[2];
+ int pole = 0;
+
+ CapMap capmap = get_cap(x, y, north_square, south_square, inverse);
+ if (capmap.region == CapMap::equatorial) {
+ xy.x = capmap.x;
+ xy.y = capmap.y;
+ return xy;
+ }
+
+ v[0] = x; v[1] = y;
+ c[0] = capmap.x; c[1] = capmap.y;
+
+ if (inverse == 0) {
+ /* Rotate (x, y) about its polar cap tip and then translate it to
+ north_square or south_square. */
+
+ if (capmap.region == CapMap::north) {
+ pole = north_square;
+ tmpRot = rot[get_rotate_index(capmap.cn - pole)];
+ } else {
+ pole = south_square;
+ tmpRot = rot[get_rotate_index(-1*(capmap.cn - pole))];
+ }
+ } else {
+ /* Inverse function.
+ Unrotate (x, y) and then translate it back. */
+
+ /* disassemble */
+ if (capmap.region == CapMap::north) {
+ pole = north_square;
+ tmpRot = rot[get_rotate_index(-1*(capmap.cn - pole))];
+ } else {
+ pole = south_square;
+ tmpRot = rot[get_rotate_index(capmap.cn - pole)];
+ }
+ }
+
+ vector_sub(v, c, v_min_c);
+ dot_product(tmpRot, v_min_c, ret_dot);
+ {
+ double a[2];
+ /* Workaround cppcheck git issue */
+ double* pa = a;
+ pa[0] = -3*M_FORTPI + ((inverse == 0) ? pole : capmap.cn) *M_HALFPI;
+ pa[1] = ((capmap.region == CapMap::north) ? 1 : -1) *M_HALFPI;
+ vector_add(ret_dot, a, vector);
+ }
+
+ xy.x = vector[0];
+ xy.y = vector[1];
+ return xy;
+}
+
+
+static XY s_healpix_forward(LP lp, PJ *P) { /* sphere */
+ (void) P;
+ return healpix_sphere(lp);
+}
+
+
+static XY e_healpix_forward(LP lp, PJ *P) { /* ellipsoid */
+ lp.phi = auth_lat(P, lp.phi, 0);
+ return healpix_sphere(lp);
+}
+
+
+static LP s_healpix_inverse(XY xy, PJ *P) { /* sphere */
+ /* Check whether (x, y) lies in the HEALPix image */
+ if (in_image(xy.x, xy.y, 0, 0, 0) == 0) {
+ LP lp;
+ lp.lam = HUGE_VAL;
+ lp.phi = HUGE_VAL;
+ pj_ctx_set_errno(P->ctx, PJD_ERR_INVALID_X_OR_Y);
+ return lp;
+ }
+ return healpix_sphere_inverse(xy);
+}
+
+
+static LP e_healpix_inverse(XY xy, PJ *P) { /* ellipsoid */
+ LP lp = {0.0,0.0};
+
+ /* Check whether (x, y) lies in the HEALPix image. */
+ if (in_image(xy.x, xy.y, 0, 0, 0) == 0) {
+ lp.lam = HUGE_VAL;
+ lp.phi = HUGE_VAL;
+ pj_ctx_set_errno(P->ctx, PJD_ERR_INVALID_X_OR_Y);
+ return lp;
+ }
+ lp = healpix_sphere_inverse(xy);
+ lp.phi = auth_lat(P, lp.phi, 1);
+ return lp;
+}
+
+
+static XY s_rhealpix_forward(LP lp, PJ *P) { /* sphere */
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+
+ XY xy = healpix_sphere(lp);
+ return combine_caps(xy.x, xy.y, Q->north_square, Q->south_square, 0);
+}
+
+
+static XY e_rhealpix_forward(LP lp, PJ *P) { /* ellipsoid */
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ XY xy;
+ lp.phi = auth_lat(P, lp.phi, 0);
+ xy = healpix_sphere(lp);
+ return combine_caps(xy.x, xy.y, Q->north_square, Q->south_square, 0);
+}
+
+
+static LP s_rhealpix_inverse(XY xy, PJ *P) { /* sphere */
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+
+ /* Check whether (x, y) lies in the rHEALPix image. */
+ if (in_image(xy.x, xy.y, 1, Q->north_square, Q->south_square) == 0) {
+ LP lp;
+ lp.lam = HUGE_VAL;
+ lp.phi = HUGE_VAL;
+ pj_ctx_set_errno(P->ctx, PJD_ERR_INVALID_X_OR_Y);
+ return lp;
+ }
+ xy = combine_caps(xy.x, xy.y, Q->north_square, Q->south_square, 1);
+ return healpix_sphere_inverse(xy);
+}
+
+
+static LP e_rhealpix_inverse(XY xy, PJ *P) { /* ellipsoid */
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ LP lp = {0.0,0.0};
+
+ /* Check whether (x, y) lies in the rHEALPix image. */
+ if (in_image(xy.x, xy.y, 1, Q->north_square, Q->south_square) == 0) {
+ lp.lam = HUGE_VAL;
+ lp.phi = HUGE_VAL;
+ pj_ctx_set_errno(P->ctx, PJD_ERR_INVALID_X_OR_Y);
+ return lp;
+ }
+ xy = combine_caps(xy.x, xy.y, Q->north_square, Q->south_square, 1);
+ lp = healpix_sphere_inverse(xy);
+ lp.phi = auth_lat(P, lp.phi, 1);
+ return lp;
+}
+
+
+static PJ *destructor (PJ *P, int errlev) { /* Destructor */
+ if (nullptr==P)
+ return nullptr;
+
+ if (nullptr==P->opaque)
+ return pj_default_destructor (P, errlev);
+
+ pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->apa);
+ return pj_default_destructor (P, errlev);
+}
+
+
+PJ *PROJECTION(healpix) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+ P->destructor = destructor;
+
+ if (P->es != 0.0) {
+ Q->apa = pj_authset(P->es); /* For auth_lat(). */
+ if (nullptr==Q->apa)
+ return destructor(P, ENOMEM);
+ Q->qp = pj_qsfn(1.0, P->e, P->one_es); /* For auth_lat(). */
+ P->a = P->a*sqrt(0.5*Q->qp); /* Set P->a to authalic radius. */
+ pj_calc_ellipsoid_params (P, P->a, P->es); /* Ensure we have a consistent parameter set */
+ P->fwd = e_healpix_forward;
+ P->inv = e_healpix_inverse;
+ } else {
+ P->fwd = s_healpix_forward;
+ P->inv = s_healpix_inverse;
+ }
+
+ return P;
+}
+
+
+PJ *PROJECTION(rhealpix) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+ P->destructor = destructor;
+
+ Q->north_square = pj_param(P->ctx, P->params,"inorth_square").i;
+ Q->south_square = pj_param(P->ctx, P->params,"isouth_square").i;
+
+ /* Check for valid north_square and south_square inputs. */
+ if (Q->north_square < 0 || Q->north_square > 3)
+ return destructor (P, PJD_ERR_AXIS);
+ if (Q->south_square < 0 || Q->south_square > 3)
+ return destructor (P, PJD_ERR_AXIS);
+ if (P->es != 0.0) {
+ Q->apa = pj_authset(P->es); /* For auth_lat(). */
+ if (nullptr==Q->apa)
+ return destructor(P, ENOMEM);
+ Q->qp = pj_qsfn(1.0, P->e, P->one_es); /* For auth_lat(). */
+ P->a = P->a*sqrt(0.5*Q->qp); /* Set P->a to authalic radius. */
+ P->ra = 1.0/P->a;
+ P->fwd = e_rhealpix_forward;
+ P->inv = e_rhealpix_inverse;
+ } else {
+ P->fwd = s_rhealpix_forward;
+ P->inv = s_rhealpix_inverse;
+ }
+
+ return P;
+}
diff --git a/src/projections/igh.cpp b/src/projections/igh.cpp
new file mode 100644
index 00000000..e3576861
--- /dev/null
+++ b/src/projections/igh.cpp
@@ -0,0 +1,227 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(igh, "Interrupted Goode Homolosine") "\n\tPCyl, Sph";
+
+C_NAMESPACE PJ *pj_sinu(PJ *), *pj_moll(PJ *);
+
+/* 40d 44' 11.8" [degrees] */
+/*
+static const double d4044118 = (40 + 44/60. + 11.8/3600.) * DEG_TO_RAD;
+has been replaced by this define, to eliminate portability issue:
+Initializer element not computable at load time
+*/
+#define d4044118 ((40 + 44/60. + 11.8/3600.) * DEG_TO_RAD)
+
+static const double d10 = 10 * DEG_TO_RAD;
+static const double d20 = 20 * DEG_TO_RAD;
+static const double d30 = 30 * DEG_TO_RAD;
+static const double d40 = 40 * DEG_TO_RAD;
+static const double d50 = 50 * DEG_TO_RAD;
+static const double d60 = 60 * DEG_TO_RAD;
+static const double d80 = 80 * DEG_TO_RAD;
+static const double d90 = 90 * DEG_TO_RAD;
+static const double d100 = 100 * DEG_TO_RAD;
+static const double d140 = 140 * DEG_TO_RAD;
+static const double d160 = 160 * DEG_TO_RAD;
+static const double d180 = 180 * DEG_TO_RAD;
+
+static const double EPSLN = 1.e-10; /* allow a little 'slack' on zone edge positions */
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ struct PJconsts* pj[12]; \
+ double dy0;
+};
+} // anonymous namespace
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ int z;
+
+ if (lp.phi >= d4044118) { /* 1|2 */
+ z = (lp.lam <= -d40 ? 1: 2);
+ }
+ else if (lp.phi >= 0) { /* 3|4 */
+ z = (lp.lam <= -d40 ? 3: 4);
+ }
+ else if (lp.phi >= -d4044118) { /* 5|6|7|8 */
+ if (lp.lam <= -d100) z = 5; /* 5 */
+ else if (lp.lam <= -d20) z = 6; /* 6 */
+ else if (lp.lam <= d80) z = 7; /* 7 */
+ else z = 8; /* 8 */
+ }
+ else { /* 9|10|11|12 */
+ if (lp.lam <= -d100) z = 9; /* 9 */
+ else if (lp.lam <= -d20) z = 10; /* 10 */
+ else if (lp.lam <= d80) z = 11; /* 11 */
+ else z = 12; /* 12 */
+ }
+
+ lp.lam -= Q->pj[z-1]->lam0;
+ xy = Q->pj[z-1]->fwd(lp, Q->pj[z-1]);
+ xy.x += Q->pj[z-1]->x0;
+ xy.y += Q->pj[z-1]->y0;
+
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ const double y90 = Q->dy0 + sqrt(2); /* lt=90 corresponds to y=y0+sqrt(2) */
+
+ int z = 0;
+ if (xy.y > y90+EPSLN || xy.y < -y90+EPSLN) /* 0 */
+ z = 0;
+ else if (xy.y >= d4044118) /* 1|2 */
+ z = (xy.x <= -d40? 1: 2);
+ else if (xy.y >= 0) /* 3|4 */
+ z = (xy.x <= -d40? 3: 4);
+ else if (xy.y >= -d4044118) { /* 5|6|7|8 */
+ if (xy.x <= -d100) z = 5; /* 5 */
+ else if (xy.x <= -d20) z = 6; /* 6 */
+ else if (xy.x <= d80) z = 7; /* 7 */
+ else z = 8; /* 8 */
+ }
+ else { /* 9|10|11|12 */
+ if (xy.x <= -d100) z = 9; /* 9 */
+ else if (xy.x <= -d20) z = 10; /* 10 */
+ else if (xy.x <= d80) z = 11; /* 11 */
+ else z = 12; /* 12 */
+ }
+
+ if (z) {
+ int ok = 0;
+
+ xy.x -= Q->pj[z-1]->x0;
+ xy.y -= Q->pj[z-1]->y0;
+ lp = Q->pj[z-1]->inv(xy, Q->pj[z-1]);
+ lp.lam += Q->pj[z-1]->lam0;
+
+ switch (z) {
+ case 1: ok = (lp.lam >= -d180-EPSLN && lp.lam <= -d40+EPSLN) ||
+ ((lp.lam >= -d40-EPSLN && lp.lam <= -d10+EPSLN) &&
+ (lp.phi >= d60-EPSLN && lp.phi <= d90+EPSLN)); break;
+ case 2: ok = (lp.lam >= -d40-EPSLN && lp.lam <= d180+EPSLN) ||
+ ((lp.lam >= -d180-EPSLN && lp.lam <= -d160+EPSLN) &&
+ (lp.phi >= d50-EPSLN && lp.phi <= d90+EPSLN)) ||
+ ((lp.lam >= -d50-EPSLN && lp.lam <= -d40+EPSLN) &&
+ (lp.phi >= d60-EPSLN && lp.phi <= d90+EPSLN)); break;
+ case 3: ok = (lp.lam >= -d180-EPSLN && lp.lam <= -d40+EPSLN); break;
+ case 4: ok = (lp.lam >= -d40-EPSLN && lp.lam <= d180+EPSLN); break;
+ case 5: ok = (lp.lam >= -d180-EPSLN && lp.lam <= -d100+EPSLN); break;
+ case 6: ok = (lp.lam >= -d100-EPSLN && lp.lam <= -d20+EPSLN); break;
+ case 7: ok = (lp.lam >= -d20-EPSLN && lp.lam <= d80+EPSLN); break;
+ case 8: ok = (lp.lam >= d80-EPSLN && lp.lam <= d180+EPSLN); break;
+ case 9: ok = (lp.lam >= -d180-EPSLN && lp.lam <= -d100+EPSLN); break;
+ case 10: ok = (lp.lam >= -d100-EPSLN && lp.lam <= -d20+EPSLN); break;
+ case 11: ok = (lp.lam >= -d20-EPSLN && lp.lam <= d80+EPSLN); break;
+ case 12: ok = (lp.lam >= d80-EPSLN && lp.lam <= d180+EPSLN); break;
+ }
+ z = (!ok? 0: z); /* projectable? */
+ }
+
+ if (!z) lp.lam = HUGE_VAL;
+ if (!z) lp.phi = HUGE_VAL;
+
+ return lp;
+}
+
+
+static PJ *destructor (PJ *P, int errlev) {
+ int i;
+ if (nullptr==P)
+ return nullptr;
+
+ if (nullptr==P->opaque)
+ return pj_default_destructor (P, errlev);
+
+ for (i = 0; i < 12; ++i) {
+ if (static_cast<struct pj_opaque*>(P->opaque)->pj[i])
+ static_cast<struct pj_opaque*>(P->opaque)->pj[i]->destructor(static_cast<struct pj_opaque*>(P->opaque)->pj[i], errlev);
+ }
+
+ return pj_default_destructor(P, errlev);
+}
+
+
+
+/*
+ Zones:
+
+ -180 -40 180
+ +--------------+-------------------------+ Zones 1,2,9,10,11 & 12:
+ |1 |2 | Mollweide projection
+ | | |
+ +--------------+-------------------------+ Zones 3,4,5,6,7 & 8:
+ |3 |4 | Sinusoidal projection
+ | | |
+ 0 +-------+------+-+-----------+-----------+
+ |5 |6 |7 |8 |
+ | | | | |
+ +-------+--------+-----------+-----------+
+ |9 |10 |11 |12 |
+ | | | | |
+ +-------+--------+-----------+-----------+
+ -180 -100 -20 80 180
+*/
+
+#define SETUP(n, proj, x_0, y_0, lon_0) \
+ if (!(Q->pj[n-1] = pj_##proj(nullptr))) return destructor(P, ENOMEM); \
+ if (!(Q->pj[n-1] = pj_##proj(Q->pj[n-1]))) return destructor(P, ENOMEM); \
+ Q->pj[n-1]->ctx = P->ctx; \
+ Q->pj[n-1]->x0 = x_0; \
+ Q->pj[n-1]->y0 = y_0; \
+ Q->pj[n-1]->lam0 = lon_0;
+
+
+PJ *PROJECTION(igh) {
+ XY xy1, xy3;
+ LP lp = { 0, d4044118 };
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+
+ /* sinusoidal zones */
+ SETUP(3, sinu, -d100, 0, -d100);
+ SETUP(4, sinu, d30, 0, d30);
+ SETUP(5, sinu, -d160, 0, -d160);
+ SETUP(6, sinu, -d60, 0, -d60);
+ SETUP(7, sinu, d20, 0, d20);
+ SETUP(8, sinu, d140, 0, d140);
+
+ /* mollweide zones */
+ SETUP(1, moll, -d100, 0, -d100);
+
+ /* y0 ? */
+ xy1 = Q->pj[0]->fwd(lp, Q->pj[0]); /* zone 1 */
+ xy3 = Q->pj[2]->fwd(lp, Q->pj[2]); /* zone 3 */
+ /* y0 + xy1.y = xy3.y for lt = 40d44'11.8" */
+ Q->dy0 = xy3.y - xy1.y;
+
+ Q->pj[0]->y0 = Q->dy0;
+
+ /* mollweide zones (cont'd) */
+ SETUP( 2, moll, d30, Q->dy0, d30);
+ SETUP( 9, moll, -d160, -Q->dy0, -d160);
+ SETUP(10, moll, -d60, -Q->dy0, -d60);
+ SETUP(11, moll, d20, -Q->dy0, d20);
+ SETUP(12, moll, d140, -Q->dy0, d140);
+
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ P->destructor = destructor;
+ P->es = 0.;
+
+ return P;
+}
diff --git a/src/projections/imw_p.cpp b/src/projections/imw_p.cpp
new file mode 100644
index 00000000..012c5caa
--- /dev/null
+++ b/src/projections/imw_p.cpp
@@ -0,0 +1,217 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(imw_p, "International Map of the World Polyconic")
+ "\n\tMod. Polyconic, Ell\n\tlat_1= and lat_2= [lon_1=]";
+
+#define TOL 1e-10
+#define EPS 1e-10
+
+namespace { // anonymous namespace
+enum Mode {
+ NONE_IS_ZERO = 0, /* phi_1 and phi_2 != 0 */
+ PHI_1_IS_ZERO = 1, /* phi_1 = 0 */
+ PHI_2_IS_ZERO = -1 /* phi_2 = 0 */
+};
+} // anonymous namespace
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double P, Pp, Q, Qp, R_1, R_2, sphi_1, sphi_2, C2;
+ double phi_1, phi_2, lam_1;
+ double *en;
+ enum Mode mode;
+};
+} // anonymous namespace
+
+
+static int phi12(PJ *P, double *del, double *sig) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ int err = 0;
+
+ if (!pj_param(P->ctx, P->params, "tlat_1").i ||
+ !pj_param(P->ctx, P->params, "tlat_2").i) {
+ err = PJD_ERR_LAT_1_2_UNSPECIFIED;
+ } else {
+ Q->phi_1 = pj_param(P->ctx, P->params, "rlat_1").f;
+ Q->phi_2 = pj_param(P->ctx, P->params, "rlat_2").f;
+ *del = 0.5 * (Q->phi_2 - Q->phi_1);
+ *sig = 0.5 * (Q->phi_2 + Q->phi_1);
+ err = (fabs(*del) < EPS || fabs(*sig) < EPS) ? PJD_ERR_ABS_LAT1_EQ_ABS_LAT2 : 0;
+ }
+ return err;
+}
+
+
+static XY loc_for(LP lp, PJ *P, double *yc) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ XY xy;
+
+ if (lp.phi == 0.0) {
+ xy.x = lp.lam;
+ xy.y = 0.;
+ } else {
+ double xa, ya, xb, yb, xc, D, B, m, sp, t, R, C;
+
+ sp = sin(lp.phi);
+ m = pj_mlfn(lp.phi, sp, cos(lp.phi), Q->en);
+ xa = Q->Pp + Q->Qp * m;
+ ya = Q->P + Q->Q * m;
+ R = 1. / (tan(lp.phi) * sqrt(1. - P->es * sp * sp));
+ C = sqrt(R * R - xa * xa);
+ if (lp.phi < 0.) C = - C;
+ C += ya - R;
+ if (Q->mode == PHI_2_IS_ZERO) {
+ xb = lp.lam;
+ yb = Q->C2;
+ } else {
+ t = lp.lam * Q->sphi_2;
+ xb = Q->R_2 * sin(t);
+ yb = Q->C2 + Q->R_2 * (1. - cos(t));
+ }
+ if (Q->mode == PHI_1_IS_ZERO) {
+ xc = lp.lam;
+ *yc = 0.;
+ } else {
+ t = lp.lam * Q->sphi_1;
+ xc = Q->R_1 * sin(t);
+ *yc = Q->R_1 * (1. - cos(t));
+ }
+ D = (xb - xc)/(yb - *yc);
+ B = xc + D * (C + R - *yc);
+ xy.x = D * sqrt(R * R * (1 + D * D) - B * B);
+ if (lp.phi > 0)
+ xy.x = - xy.x;
+ xy.x = (B + xy.x) / (1. + D * D);
+ xy.y = sqrt(R * R - xy.x * xy.x);
+ if (lp.phi > 0)
+ xy.y = - xy.y;
+ xy.y += C + R;
+ }
+ return xy;
+}
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ double yc;
+ XY xy = loc_for(lp, P, &yc);
+ return (xy);
+}
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ XY t;
+ double yc = 0.0;
+ int i = 0;
+ const int N_MAX_ITER = 1000; /* Arbitrarily chosen number... */
+
+ lp.phi = Q->phi_2;
+ lp.lam = xy.x / cos(lp.phi);
+ do {
+ t = loc_for(lp, P, &yc);
+ lp.phi = ((lp.phi - Q->phi_1) * (xy.y - yc) / (t.y - yc)) + Q->phi_1;
+ lp.lam = lp.lam * xy.x / t.x;
+ i ++;
+ } while (i < N_MAX_ITER &&
+ (fabs(t.x - xy.x) > TOL || fabs(t.y - xy.y) > TOL));
+
+ if( i == N_MAX_ITER )
+ {
+ lp.lam = lp.phi = HUGE_VAL;
+ }
+
+ return lp;
+}
+
+
+static void xy(PJ *P, double phi, double *x, double *y, double *sp, double *R) {
+ double F;
+
+ *sp = sin(phi);
+ *R = 1./(tan(phi) * sqrt(1. - P->es * *sp * *sp ));
+ F = static_cast<struct pj_opaque*>(P->opaque)->lam_1 * *sp;
+ *y = *R * (1 - cos(F));
+ *x = *R * sin(F);
+}
+
+
+static PJ *destructor (PJ *P, int errlev) {
+ if (nullptr==P)
+ return nullptr;
+
+ if (nullptr==P->opaque)
+ return pj_default_destructor (P, errlev);
+
+ if( static_cast<struct pj_opaque*>(P->opaque)->en )
+ pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->en);
+
+ return pj_default_destructor(P, errlev);
+}
+
+
+PJ *PROJECTION(imw_p) {
+ double del, sig, s, t, x1, x2, T2, y1, m1, m2, y2;
+ int err;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ if (!(Q->en = pj_enfn(P->es))) return pj_default_destructor (P, ENOMEM);
+ if( (err = phi12(P, &del, &sig)) != 0) {
+ return destructor(P, err);
+ }
+ if (Q->phi_2 < Q->phi_1) { /* make sure P->phi_1 most southerly */
+ del = Q->phi_1;
+ Q->phi_1 = Q->phi_2;
+ Q->phi_2 = del;
+ }
+ if (pj_param(P->ctx, P->params, "tlon_1").i)
+ Q->lam_1 = pj_param(P->ctx, P->params, "rlon_1").f;
+ else { /* use predefined based upon latitude */
+ sig = fabs(sig * RAD_TO_DEG);
+ if (sig <= 60) sig = 2.;
+ else if (sig <= 76) sig = 4.;
+ else sig = 8.;
+ Q->lam_1 = sig * DEG_TO_RAD;
+ }
+ Q->mode = NONE_IS_ZERO;
+ if (Q->phi_1 != 0.0)
+ xy(P, Q->phi_1, &x1, &y1, &Q->sphi_1, &Q->R_1);
+ else {
+ Q->mode = PHI_1_IS_ZERO;
+ y1 = 0.;
+ x1 = Q->lam_1;
+ }
+ if (Q->phi_2 != 0.0)
+ xy(P, Q->phi_2, &x2, &T2, &Q->sphi_2, &Q->R_2);
+ else {
+ Q->mode = PHI_2_IS_ZERO;
+ T2 = 0.;
+ x2 = Q->lam_1;
+ }
+ m1 = pj_mlfn(Q->phi_1, Q->sphi_1, cos(Q->phi_1), Q->en);
+ m2 = pj_mlfn(Q->phi_2, Q->sphi_2, cos(Q->phi_2), Q->en);
+ t = m2 - m1;
+ s = x2 - x1;
+ y2 = sqrt(t * t - s * s) + y1;
+ Q->C2 = y2 - T2;
+ t = 1. / t;
+ Q->P = (m2 * y1 - m1 * y2) * t;
+ Q->Q = (y2 - y1) * t;
+ Q->Pp = (m2 * x1 - m1 * x2) * t;
+ Q->Qp = (x2 - x1) * t;
+
+ P->fwd = e_forward;
+ P->inv = e_inverse;
+ P->destructor = destructor;
+
+ return P;
+}
diff --git a/src/projections/isea.cpp b/src/projections/isea.cpp
new file mode 100644
index 00000000..522e6813
--- /dev/null
+++ b/src/projections/isea.cpp
@@ -0,0 +1,1098 @@
+/*
+ * This code was entirely written by Nathan Wagner
+ * and is in the public domain.
+ */
+
+#include <errno.h>
+#include <math.h>
+#include <float.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define PJ_LIB__
+#include "proj_internal.h"
+#include "proj_math.h"
+#include "proj.h"
+#include "projects.h"
+
+#define DEG36 0.62831853071795864768
+#define DEG72 1.25663706143591729537
+#define DEG90 M_PI_2
+#define DEG108 1.88495559215387594306
+#define DEG120 2.09439510239319549229
+#define DEG144 2.51327412287183459075
+#define DEG180 M_PI
+
+/* sqrt(5)/M_PI */
+#define ISEA_SCALE 0.8301572857837594396028083
+
+/* 26.565051177 degrees */
+#define V_LAT 0.46364760899944494524
+
+/* 52.62263186 */
+#define E_RAD 0.91843818702186776133
+
+/* 10.81231696 */
+#define F_RAD 0.18871053072122403508
+
+/* R tan(g) sin(60) */
+#define TABLE_G 0.6615845383
+
+/* H = 0.25 R tan g = */
+#define TABLE_H 0.1909830056
+
+/* in radians */
+#define ISEA_STD_LAT 1.01722196792335072101
+#define ISEA_STD_LON .19634954084936207740
+
+namespace { // anonymous namespace
+struct hex {
+ int iso;
+ long x, y, z;
+};
+} // anonymous namespace
+
+/* y *must* be positive down as the xy /iso conversion assumes this */
+static void hex_xy(struct hex *h) {
+ if (!h->iso) return;
+ if (h->x >= 0) {
+ h->y = -h->y - (h->x+1)/2;
+ } else {
+ /* need to round toward -inf, not toward zero, so x-1 */
+ h->y = -h->y - h->x/2;
+ }
+ h->iso = 0;
+}
+
+static void hex_iso(struct hex *h) {
+ if (h->iso) return;
+
+ if (h->x >= 0) {
+ h->y = (-h->y - (h->x+1)/2);
+ } else {
+ /* need to round toward -inf, not toward zero, so x-1 */
+ h->y = (-h->y - (h->x)/2);
+ }
+
+ h->z = -h->x - h->y;
+ h->iso = 1;
+}
+
+static void hexbin2(double width, double x, double y, long *i, long *j) {
+ double z, rx, ry, rz;
+ double abs_dx, abs_dy, abs_dz;
+ long ix, iy, iz, s;
+ struct hex h;
+
+ x = x / cos(30 * M_PI / 180.0); /* rotated X coord */
+ y = y - x / 2.0; /* adjustment for rotated X */
+
+ /* adjust for actual hexwidth */
+ x /= width;
+ y /= width;
+
+ z = -x - y;
+
+ rx = floor(x + 0.5);
+ ix = lround(rx);
+ ry = floor(y + 0.5);
+ iy = lround(ry);
+ rz = floor(z + 0.5);
+ iz = lround(rz);
+
+ s = ix + iy + iz;
+
+ if (s) {
+ abs_dx = fabs(rx - x);
+ abs_dy = fabs(ry - y);
+ abs_dz = fabs(rz - z);
+
+ if (abs_dx >= abs_dy && abs_dx >= abs_dz) {
+ ix -= s;
+ } else if (abs_dy >= abs_dx && abs_dy >= abs_dz) {
+ iy -= s;
+ } else {
+ iz -= s;
+ }
+ }
+ h.x = ix;
+ h.y = iy;
+ h.z = iz;
+ h.iso = 1;
+
+ hex_xy(&h);
+ *i = h.x;
+ *j = h.y;
+}
+
+namespace { // anonymous namespace
+enum isea_poly { ISEA_NONE, ISEA_ICOSAHEDRON = 20 };
+enum isea_topology { ISEA_HEXAGON=6, ISEA_TRIANGLE=3, ISEA_DIAMOND=4 };
+enum isea_address_form { ISEA_GEO, ISEA_Q2DI, ISEA_SEQNUM, ISEA_INTERLEAVE,
+ ISEA_PLANE, ISEA_Q2DD, ISEA_PROJTRI, ISEA_VERTEX2DD, ISEA_HEX
+};
+} // anonymous namespace
+
+namespace { // anonymous namespace
+struct isea_dgg {
+ int polyhedron; /* ignored, icosahedron */
+ double o_lat, o_lon, o_az; /* orientation, radians */
+ int pole; /* true if standard snyder */
+ int topology; /* ignored, hexagon */
+ int aperture; /* valid values depend on partitioning method */
+ int resolution;
+ double radius; /* radius of the earth in meters, ignored 1.0 */
+ int output; /* an isea_address_form */
+ int triangle; /* triangle of last transformed point */
+ int quad; /* quad of last transformed point */
+ unsigned long serial;
+};
+} // anonymous namespace
+
+namespace { // anonymous namespace
+struct isea_pt {
+ double x, y;
+};
+} // anonymous namespace
+
+namespace { // anonymous namespace
+struct isea_geo {
+ double lon, lat;
+};
+} // anonymous namespace
+
+/* ENDINC */
+
+namespace { // anonymous namespace
+enum snyder_polyhedron {
+ SNYDER_POLY_HEXAGON, SNYDER_POLY_PENTAGON,
+ SNYDER_POLY_TETRAHEDRON, SNYDER_POLY_CUBE,
+ SNYDER_POLY_OCTAHEDRON, SNYDER_POLY_DODECAHEDRON,
+ SNYDER_POLY_ICOSAHEDRON
+};
+} // anonymous namespace
+
+namespace { // anonymous namespace
+struct snyder_constants {
+ double g, G, theta;
+ /* cppcheck-suppress unusedStructMember */
+ double ea_w, ea_a, ea_b, g_w, g_a, g_b;
+};
+} // anonymous namespace
+
+/* TODO put these in radians to avoid a later conversion */
+static const struct snyder_constants constants[] = {
+ {23.80018260, 62.15458023, 60.0, 3.75, 1.033, 0.968, 5.09, 1.195, 1.0},
+ {20.07675127, 55.69063953, 54.0, 2.65, 1.030, 0.983, 3.59, 1.141, 1.027},
+ {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
+ {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
+ {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
+ {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0},
+ {37.37736814, 36.0, 30.0, 17.27, 1.163, 0.860, 13.14, 1.584, 1.0},
+};
+
+static struct isea_geo vertex[] = {
+ {0.0, DEG90},
+ {DEG180, V_LAT},
+ {-DEG108, V_LAT},
+ {-DEG36, V_LAT},
+ {DEG36, V_LAT},
+ {DEG108, V_LAT},
+ {-DEG144, -V_LAT},
+ {-DEG72, -V_LAT},
+ {0.0, -V_LAT},
+ {DEG72, -V_LAT},
+ {DEG144, -V_LAT},
+ {0.0, -DEG90}
+};
+
+/* TODO make an isea_pt array of the vertices as well */
+
+static int tri_v1[] = {0, 0, 0, 0, 0, 0, 6, 7, 8, 9, 10, 2, 3, 4, 5, 1, 11, 11, 11, 11, 11};
+
+/* triangle Centers */
+static const struct isea_geo icostriangles[] = {
+ {0.0, 0.0},
+ {-DEG144, E_RAD},
+ {-DEG72, E_RAD},
+ {0.0, E_RAD},
+ {DEG72, E_RAD},
+ {DEG144, E_RAD},
+ {-DEG144, F_RAD},
+ {-DEG72, F_RAD},
+ {0.0, F_RAD},
+ {DEG72, F_RAD},
+ {DEG144, F_RAD},
+ {-DEG108, -F_RAD},
+ {-DEG36, -F_RAD},
+ {DEG36, -F_RAD},
+ {DEG108, -F_RAD},
+ {DEG180, -F_RAD},
+ {-DEG108, -E_RAD},
+ {-DEG36, -E_RAD},
+ {DEG36, -E_RAD},
+ {DEG108, -E_RAD},
+ {DEG180, -E_RAD},
+};
+
+static double az_adjustment(int triangle)
+{
+ double adj;
+
+ struct isea_geo v;
+ struct isea_geo c;
+
+ v = vertex[tri_v1[triangle]];
+ c = icostriangles[triangle];
+
+ /* TODO looks like the adjustment is always either 0 or 180 */
+ /* at least if you pick your vertex carefully */
+ adj = atan2(cos(v.lat) * sin(v.lon - c.lon),
+ cos(c.lat) * sin(v.lat)
+ - sin(c.lat) * cos(v.lat) * cos(v.lon - c.lon));
+ return adj;
+}
+
+static struct isea_pt isea_triangle_xy(int triangle)
+{
+ struct isea_pt c;
+ const double Rprime = 0.91038328153090290025;
+
+ triangle = (triangle - 1) % 20;
+
+ c.x = TABLE_G * ((triangle % 5) - 2) * 2.0;
+ if (triangle > 9) {
+ c.x += TABLE_G;
+ }
+ switch (triangle / 5) {
+ case 0:
+ c.y = 5.0 * TABLE_H;
+ break;
+ case 1:
+ c.y = TABLE_H;
+ break;
+ case 2:
+ c.y = -TABLE_H;
+ break;
+ case 3:
+ c.y = -5.0 * TABLE_H;
+ break;
+ default:
+ /* should be impossible */
+ exit(EXIT_FAILURE);
+ };
+ c.x *= Rprime;
+ c.y *= Rprime;
+
+ return c;
+}
+
+/* snyder eq 14 */
+static double sph_azimuth(double f_lon, double f_lat,
+ double t_lon, double t_lat)
+{
+ double az;
+
+ az = atan2(cos(t_lat) * sin(t_lon - f_lon),
+ cos(f_lat) * sin(t_lat)
+ - sin(f_lat) * cos(t_lat) * cos(t_lon - f_lon)
+ );
+ return az;
+}
+
+#ifdef _MSC_VER
+#pragma warning( push )
+/* disable unreachable code warning for return 0 */
+#pragma warning( disable : 4702 )
+#endif
+
+/* coord needs to be in radians */
+static int isea_snyder_forward(struct isea_geo * ll, struct isea_pt * out)
+{
+ int i;
+
+ /*
+ * spherical distance from center of polygon face to any of its
+ * vertexes on the globe
+ */
+ double g;
+
+ /*
+ * spherical angle between radius vector to center and adjacent edge
+ * of spherical polygon on the globe
+ */
+ double G;
+
+ /*
+ * plane angle between radius vector to center and adjacent edge of
+ * plane polygon
+ */
+ double theta;
+
+ /* additional variables from snyder */
+ double q, Rprime, H, Ag, Azprime, Az, dprime, f, rho,
+ x, y;
+
+ /* variables used to store intermediate results */
+ double cot_theta, tan_g, az_offset;
+
+ /* how many multiples of 60 degrees we adjust the azimuth */
+ int Az_adjust_multiples;
+
+ struct snyder_constants c;
+
+ /*
+ * TODO by locality of reference, start by trying the same triangle
+ * as last time
+ */
+
+ /* TODO put these constants in as radians to begin with */
+ c = constants[SNYDER_POLY_ICOSAHEDRON];
+ theta = PJ_TORAD(c.theta);
+ g = PJ_TORAD(c.g);
+ G = PJ_TORAD(c.G);
+
+ for (i = 1; i <= 20; i++) {
+ double z;
+ struct isea_geo center;
+
+ center = icostriangles[i];
+
+ /* step 1 */
+ z = acos(sin(center.lat) * sin(ll->lat)
+ + cos(center.lat) * cos(ll->lat) * cos(ll->lon - center.lon));
+ /* not on this triangle */
+ if (z > g + 0.000005) { /* TODO DBL_EPSILON */
+ continue;
+ }
+
+ Az = sph_azimuth(center.lon, center.lat, ll->lon, ll->lat);
+
+ /* step 2 */
+
+ /* This calculates "some" vertex coordinate */
+ az_offset = az_adjustment(i);
+
+ Az -= az_offset;
+
+ /* TODO I don't know why we do this. It's not in snyder */
+ /* maybe because we should have picked a better vertex */
+ if (Az < 0.0) {
+ Az += 2.0 * M_PI;
+ }
+ /*
+ * adjust Az for the point to fall within the range of 0 to
+ * 2(90 - theta) or 60 degrees for the hexagon, by
+ * and therefore 120 degrees for the triangle
+ * of the icosahedron
+ * subtracting or adding multiples of 60 degrees to Az and
+ * recording the amount of adjustment
+ */
+
+ Az_adjust_multiples = 0;
+ while (Az < 0.0) {
+ Az += DEG120;
+ Az_adjust_multiples--;
+ }
+ while (Az > DEG120 + DBL_EPSILON) {
+ Az -= DEG120;
+ Az_adjust_multiples++;
+ }
+
+ /* step 3 */
+ cot_theta = 1.0 / tan(theta);
+ tan_g = tan(g); /* TODO this is a constant */
+
+ /* Calculate q from eq 9. */
+ /* TODO cot_theta is cot(30) */
+ q = atan2(tan_g, cos(Az) + sin(Az) * cot_theta);
+
+ /* not in this triangle */
+ if (z > q + 0.000005) {
+ continue;
+ }
+ /* step 4 */
+
+ /* Apply equations 5-8 and 10-12 in order */
+
+ /* eq 5 */
+ /* Rprime = 0.9449322893 * R; */
+ /* R' in the paper is for the truncated */
+ Rprime = 0.91038328153090290025;
+
+ /* eq 6 */
+ H = acos(sin(Az) * sin(G) * cos(g) - cos(Az) * cos(G));
+
+ /* eq 7 */
+ /* Ag = (Az + G + H - DEG180) * M_PI * R * R / DEG180; */
+ Ag = Az + G + H - DEG180;
+
+ /* eq 8 */
+ Azprime = atan2(2.0 * Ag, Rprime * Rprime * tan_g * tan_g - 2.0 * Ag * cot_theta);
+
+ /* eq 10 */
+ /* cot(theta) = 1.73205080756887729355 */
+ dprime = Rprime * tan_g / (cos(Azprime) + sin(Azprime) * cot_theta);
+
+ /* eq 11 */
+ f = dprime / (2.0 * Rprime * sin(q / 2.0));
+
+ /* eq 12 */
+ rho = 2.0 * Rprime * f * sin(z / 2.0);
+
+ /*
+ * add back the same 60 degree multiple adjustment from step
+ * 2 to Azprime
+ */
+
+ Azprime += DEG120 * Az_adjust_multiples;
+
+ /* calculate rectangular coordinates */
+
+ x = rho * sin(Azprime);
+ y = rho * cos(Azprime);
+
+ /*
+ * TODO
+ * translate coordinates to the origin for the particular
+ * hexagon on the flattened polyhedral map plot
+ */
+
+ out->x = x;
+ out->y = y;
+
+ return i;
+ }
+
+ /*
+ * should be impossible, this implies that the coordinate is not on
+ * any triangle
+ */
+
+ fprintf(stderr, "impossible transform: %f %f is not on any triangle\n",
+ PJ_TODEG(ll->lon), PJ_TODEG(ll->lat));
+
+ exit(EXIT_FAILURE);
+
+ /* not reached */
+ return 0; /* suppresses a warning */
+}
+
+#ifdef _MSC_VER
+#pragma warning( pop )
+#endif
+
+/*
+ * return the new coordinates of any point in original coordinate system.
+ * Define a point (newNPold) in original coordinate system as the North Pole in
+ * new coordinate system, and the great circle connect the original and new
+ * North Pole as the lon0 longitude in new coordinate system, given any point
+ * in original coordinate system, this function return the new coordinates.
+ */
+
+/* formula from Snyder, Map Projections: A working manual, p31 */
+/*
+ * old north pole at np in new coordinates
+ * could be simplified a bit with fewer intermediates
+ *
+ * TODO take a result pointer
+ */
+static struct isea_geo snyder_ctran(struct isea_geo * np, struct isea_geo * pt)
+{
+ struct isea_geo npt;
+ double alpha, phi, lambda, lambda0, beta, lambdap, phip;
+ double sin_phip;
+ double lp_b; /* lambda prime minus beta */
+ double cos_p, sin_a;
+
+ phi = pt->lat;
+ lambda = pt->lon;
+ alpha = np->lat;
+ beta = np->lon;
+ lambda0 = beta;
+
+ cos_p = cos(phi);
+ sin_a = sin(alpha);
+
+ /* mpawm 5-7 */
+ sin_phip = sin_a * sin(phi) - cos(alpha) * cos_p * cos(lambda - lambda0);
+
+ /* mpawm 5-8b */
+
+ /* use the two argument form so we end up in the right quadrant */
+ lp_b = atan2(cos_p * sin(lambda - lambda0),
+ (sin_a * cos_p * cos(lambda - lambda0) + cos(alpha) * sin(phi)));
+
+ lambdap = lp_b + beta;
+
+ /* normalize longitude */
+ /* TODO can we just do a modulus ? */
+ lambdap = fmod(lambdap, 2 * M_PI);
+ while (lambdap > M_PI)
+ lambdap -= 2 * M_PI;
+ while (lambdap < -M_PI)
+ lambdap += 2 * M_PI;
+
+ phip = asin(sin_phip);
+
+ npt.lat = phip;
+ npt.lon = lambdap;
+
+ return npt;
+}
+
+static struct isea_geo isea_ctran(struct isea_geo * np, struct isea_geo * pt,
+ double lon0)
+{
+ struct isea_geo npt;
+
+ np->lon += M_PI;
+ npt = snyder_ctran(np, pt);
+ np->lon -= M_PI;
+
+ npt.lon -= (M_PI - lon0 + np->lon);
+
+ /*
+ * snyder is down tri 3, isea is along side of tri1 from vertex 0 to
+ * vertex 1 these are 180 degrees apart
+ */
+ npt.lon += M_PI;
+ /* normalize longitude */
+ npt.lon = fmod(npt.lon, 2 * M_PI);
+ while (npt.lon > M_PI)
+ npt.lon -= 2 * M_PI;
+ while (npt.lon < -M_PI)
+ npt.lon += 2 * M_PI;
+
+ return npt;
+}
+
+/* fuller's at 5.2454 west, 2.3009 N, adjacent at 7.46658 deg */
+
+static int isea_grid_init(struct isea_dgg * g)
+{
+ if (!g)
+ return 0;
+
+ g->polyhedron = 20;
+ g->o_lat = ISEA_STD_LAT;
+ g->o_lon = ISEA_STD_LON;
+ g->o_az = 0.0;
+ g->aperture = 4;
+ g->resolution = 6;
+ g->radius = 1.0;
+ g->topology = 6;
+
+ return 1;
+}
+
+static void isea_orient_isea(struct isea_dgg * g)
+{
+ if (!g)
+ return;
+ g->o_lat = ISEA_STD_LAT;
+ g->o_lon = ISEA_STD_LON;
+ g->o_az = 0.0;
+}
+
+static void isea_orient_pole(struct isea_dgg * g)
+{
+ if (!g)
+ return;
+ g->o_lat = M_PI / 2.0;
+ g->o_lon = 0.0;
+ g->o_az = 0;
+}
+
+static int isea_transform(struct isea_dgg * g, struct isea_geo * in,
+ struct isea_pt * out)
+{
+ struct isea_geo i, pole;
+ int tri;
+
+ pole.lat = g->o_lat;
+ pole.lon = g->o_lon;
+
+ i = isea_ctran(&pole, in, g->o_az);
+
+ tri = isea_snyder_forward(&i, out);
+ out->x *= g->radius;
+ out->y *= g->radius;
+ g->triangle = tri;
+
+ return tri;
+}
+
+#define DOWNTRI(tri) (((tri - 1) / 5) % 2 == 1)
+
+static void isea_rotate(struct isea_pt * pt, double degrees)
+{
+ double rad;
+
+ double x, y;
+
+ rad = -degrees * M_PI / 180.0;
+ while (rad >= 2.0 * M_PI) rad -= 2.0 * M_PI;
+ while (rad <= -2.0 * M_PI) rad += 2.0 * M_PI;
+
+ x = pt->x * cos(rad) + pt->y * sin(rad);
+ y = -pt->x * sin(rad) + pt->y * cos(rad);
+
+ pt->x = x;
+ pt->y = y;
+}
+
+static int isea_tri_plane(int tri, struct isea_pt *pt, double radius) {
+ struct isea_pt tc; /* center of triangle */
+
+ if (DOWNTRI(tri)) {
+ isea_rotate(pt, 180.0);
+ }
+ tc = isea_triangle_xy(tri);
+ tc.x *= radius;
+ tc.y *= radius;
+ pt->x += tc.x;
+ pt->y += tc.y;
+
+ return tri;
+}
+
+/* convert projected triangle coords to quad xy coords, return quad number */
+static int isea_ptdd(int tri, struct isea_pt *pt) {
+ int downtri, quad;
+
+ downtri = (((tri - 1) / 5) % 2 == 1);
+ quad = ((tri - 1) % 5) + ((tri - 1) / 10) * 5 + 1;
+
+ isea_rotate(pt, downtri ? 240.0 : 60.0);
+ if (downtri) {
+ pt->x += 0.5;
+ /* pt->y += cos(30.0 * M_PI / 180.0); */
+ pt->y += .86602540378443864672;
+ }
+ return quad;
+}
+
+static int isea_dddi_ap3odd(struct isea_dgg *g, int quad, struct isea_pt *pt,
+ struct isea_pt *di)
+{
+ struct isea_pt v;
+ double hexwidth;
+ double sidelength; /* in hexes */
+ long d, i;
+ long maxcoord;
+ struct hex h;
+
+ /* This is the number of hexes from apex to base of a triangle */
+ sidelength = (pow(2.0, g->resolution) + 1.0) / 2.0;
+
+ /* apex to base is cos(30deg) */
+ hexwidth = cos(M_PI / 6.0) / sidelength;
+
+ /* TODO I think sidelength is always x.5, so
+ * (int)sidelength * 2 + 1 might be just as good
+ */
+ maxcoord = lround((sidelength * 2.0));
+
+ v = *pt;
+ hexbin2(hexwidth, v.x, v.y, &h.x, &h.y);
+ h.iso = 0;
+ hex_iso(&h);
+
+ d = h.x - h.z;
+ i = h.x + h.y + h.y;
+
+ /*
+ * you want to test for max coords for the next quad in the same
+ * "row" first to get the case where both are max
+ */
+ if (quad <= 5) {
+ if (d == 0 && i == maxcoord) {
+ /* north pole */
+ quad = 0;
+ d = 0;
+ i = 0;
+ } else if (i == maxcoord) {
+ /* upper right in next quad */
+ quad += 1;
+ if (quad == 6)
+ quad = 1;
+ i = maxcoord - d;
+ d = 0;
+ } else if (d == maxcoord) {
+ /* lower right in quad to lower right */
+ quad += 5;
+ d = 0;
+ }
+ } else if (quad >= 6) {
+ if (i == 0 && d == maxcoord) {
+ /* south pole */
+ quad = 11;
+ d = 0;
+ i = 0;
+ } else if (d == maxcoord) {
+ /* lower right in next quad */
+ quad += 1;
+ if (quad == 11)
+ quad = 6;
+ d = maxcoord - i;
+ i = 0;
+ } else if (i == maxcoord) {
+ /* upper right in quad to upper right */
+ quad = (quad - 4) % 5;
+ i = 0;
+ }
+ }
+
+ di->x = d;
+ di->y = i;
+
+ g->quad = quad;
+ return quad;
+}
+
+static int isea_dddi(struct isea_dgg *g, int quad, struct isea_pt *pt,
+ struct isea_pt *di) {
+ struct isea_pt v;
+ double hexwidth;
+ long sidelength; /* in hexes */
+ struct hex h;
+
+ if (g->aperture == 3 && g->resolution % 2 != 0) {
+ return isea_dddi_ap3odd(g, quad, pt, di);
+ }
+ /* todo might want to do this as an iterated loop */
+ if (g->aperture >0) {
+ sidelength = lround(pow(g->aperture, g->resolution / 2.0));
+ } else {
+ sidelength = g->resolution;
+ }
+
+ hexwidth = 1.0 / sidelength;
+
+ v = *pt;
+ isea_rotate(&v, -30.0);
+ hexbin2(hexwidth, v.x, v.y, &h.x, &h.y);
+ h.iso = 0;
+ hex_iso(&h);
+
+ /* we may actually be on another quad */
+ if (quad <= 5) {
+ if (h.x == 0 && h.z == -sidelength) {
+ /* north pole */
+ quad = 0;
+ h.z = 0;
+ h.y = 0;
+ h.x = 0;
+ } else if (h.z == -sidelength) {
+ quad = quad + 1;
+ if (quad == 6)
+ quad = 1;
+ h.y = sidelength - h.x;
+ h.z = h.x - sidelength;
+ h.x = 0;
+ } else if (h.x == sidelength) {
+ quad += 5;
+ h.y = -h.z;
+ h.x = 0;
+ }
+ } else if (quad >= 6) {
+ if (h.z == 0 && h.x == sidelength) {
+ /* south pole */
+ quad = 11;
+ h.x = 0;
+ h.y = 0;
+ h.z = 0;
+ } else if (h.x == sidelength) {
+ quad = quad + 1;
+ if (quad == 11)
+ quad = 6;
+ h.x = h.y + sidelength;
+ h.y = 0;
+ h.z = -h.x;
+ } else if (h.y == -sidelength) {
+ quad -= 4;
+ h.y = 0;
+ h.z = -h.x;
+ }
+ }
+ di->x = h.x;
+ di->y = -h.z;
+
+ g->quad = quad;
+ return quad;
+}
+
+static int isea_ptdi(struct isea_dgg *g, int tri, struct isea_pt *pt,
+ struct isea_pt *di) {
+ struct isea_pt v;
+ int quad;
+
+ v = *pt;
+ quad = isea_ptdd(tri, &v);
+ quad = isea_dddi(g, quad, &v, di);
+ return quad;
+}
+
+/* q2di to seqnum */
+
+static long isea_disn(struct isea_dgg *g, int quad, struct isea_pt *di) {
+ long sidelength;
+ long sn, height;
+ long hexes;
+
+ if (quad == 0) {
+ g->serial = 1;
+ return g->serial;
+ }
+ /* hexes in a quad */
+ hexes = lround(pow(g->aperture, g->resolution));
+ if (quad == 11) {
+ g->serial = 1 + 10 * hexes + 1;
+ return g->serial;
+ }
+ if (g->aperture == 3 && g->resolution % 2 == 1) {
+ height = lround(floor((pow(g->aperture, (g->resolution - 1) / 2.0))));
+ sn = ((long)di->x) * height;
+ sn += ((long)di->y) / height;
+ sn += (quad - 1) * hexes;
+ sn += 2;
+ } else {
+ sidelength = lround((pow(g->aperture, g->resolution / 2.0)));
+ sn = lround(floor(((quad - 1) * hexes + sidelength * di->x + di->y + 2)));
+ }
+
+ g->serial = sn;
+ return sn;
+}
+
+/* TODO just encode the quad in the d or i coordinate
+ * quad is 0-11, which can be four bits.
+ * d' = d << 4 + q, d = d' >> 4, q = d' & 0xf
+ */
+/* convert a q2di to global hex coord */
+static int isea_hex(struct isea_dgg *g, int tri,
+ struct isea_pt *pt, struct isea_pt *hex) {
+ struct isea_pt v;
+#ifdef FIXME
+ long sidelength;
+ long d, i, x, y;
+#endif
+ int quad;
+
+ quad = isea_ptdi(g, tri, pt, &v);
+
+ hex->x = ((int)v.x << 4) + quad;
+ hex->y = v.y;
+
+ return 1;
+#ifdef FIXME
+ d = lround(floor(v.x));
+ i = lround(floor(v.y));
+
+ /* Aperture 3 odd resolutions */
+ if (g->aperture == 3 && g->resolution % 2 != 0) {
+ long offset = lround((pow(3.0, g->resolution - 1) + 0.5));
+
+ d += offset * ((g->quad-1) % 5);
+ i += offset * ((g->quad-1) % 5);
+
+ if (quad == 0) {
+ d = 0;
+ i = offset;
+ } else if (quad == 11) {
+ d = 2 * offset;
+ i = 0;
+ } else if (quad > 5) {
+ d += offset;
+ }
+
+ x = (2*d - i) /3;
+ y = (2*i - d) /3;
+
+ hex->x = x + offset / 3;
+ hex->y = y + 2 * offset / 3;
+ return 1;
+ }
+
+ /* aperture 3 even resolutions and aperture 4 */
+ sidelength = lround((pow(g->aperture, g->resolution / 2.0)));
+ if (g->quad == 0) {
+ hex->x = 0;
+ hex->y = sidelength;
+ } else if (g->quad == 11) {
+ hex->x = sidelength * 2;
+ hex->y = 0;
+ } else {
+ hex->x = d + sidelength * ((g->quad-1) % 5);
+ if (g->quad > 5) hex->x += sidelength;
+ hex->y = i + sidelength * ((g->quad-1) % 5);
+ }
+
+ return 1;
+#endif
+}
+
+static struct isea_pt isea_forward(struct isea_dgg *g, struct isea_geo *in)
+{
+ int tri;
+ struct isea_pt out, coord;
+
+ tri = isea_transform(g, in, &out);
+
+ if (g->output == ISEA_PLANE) {
+ isea_tri_plane(tri, &out, g->radius);
+ return out;
+ }
+
+ /* convert to isea standard triangle size */
+ out.x = out.x / g->radius * ISEA_SCALE;
+ out.y = out.y / g->radius * ISEA_SCALE;
+ out.x += 0.5;
+ out.y += 2.0 * .14433756729740644112;
+
+ switch (g->output) {
+ case ISEA_PROJTRI:
+ /* nothing to do, already in projected triangle */
+ break;
+ case ISEA_VERTEX2DD:
+ g->quad = isea_ptdd(tri, &out);
+ break;
+ case ISEA_Q2DD:
+ /* Same as above, we just don't print as much */
+ g->quad = isea_ptdd(tri, &out);
+ break;
+ case ISEA_Q2DI:
+ g->quad = isea_ptdi(g, tri, &out, &coord);
+ return coord;
+ break;
+ case ISEA_SEQNUM:
+ isea_ptdi(g, tri, &out, &coord);
+ /* disn will set g->serial */
+ isea_disn(g, g->quad, &coord);
+ return coord;
+ break;
+ case ISEA_HEX:
+ isea_hex(g, tri, &out, &coord);
+ return coord;
+ break;
+ }
+
+ return out;
+}
+
+/*
+ * Proj 4 integration code follows
+ */
+
+PROJ_HEAD(isea, "Icosahedral Snyder Equal Area") "\n\tSph";
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ struct isea_dgg dgg;
+};
+} // anonymous namespace
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ struct isea_pt out;
+ struct isea_geo in;
+
+ in.lon = lp.lam;
+ in.lat = lp.phi;
+
+ out = isea_forward(&Q->dgg, &in);
+
+ xy.x = out.x;
+ xy.y = out.y;
+
+ return xy;
+}
+
+
+PJ *PROJECTION(isea) {
+ char *opt;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+
+ P->fwd = s_forward;
+ isea_grid_init(&Q->dgg);
+
+ Q->dgg.output = ISEA_PLANE;
+/* P->dgg.radius = P->a; / * otherwise defaults to 1 */
+ /* calling library will scale, I think */
+
+ opt = pj_param(P->ctx,P->params, "sorient").s;
+ if (opt) {
+ if (!strcmp(opt, "isea")) {
+ isea_orient_isea(&Q->dgg);
+ } else if (!strcmp(opt, "pole")) {
+ isea_orient_pole(&Q->dgg);
+ } else {
+ return pj_default_destructor(P, PJD_ERR_ELLIPSOID_USE_REQUIRED);
+ }
+ }
+
+ if (pj_param(P->ctx,P->params, "tazi").i) {
+ Q->dgg.o_az = pj_param(P->ctx,P->params, "razi").f;
+ }
+
+ if (pj_param(P->ctx,P->params, "tlon_0").i) {
+ Q->dgg.o_lon = pj_param(P->ctx,P->params, "rlon_0").f;
+ }
+
+ if (pj_param(P->ctx,P->params, "tlat_0").i) {
+ Q->dgg.o_lat = pj_param(P->ctx,P->params, "rlat_0").f;
+ }
+
+ if (pj_param(P->ctx,P->params, "taperture").i) {
+ Q->dgg.aperture = pj_param(P->ctx,P->params, "iaperture").i;
+ }
+
+ if (pj_param(P->ctx,P->params, "tresolution").i) {
+ Q->dgg.resolution = pj_param(P->ctx,P->params, "iresolution").i;
+ }
+
+ opt = pj_param(P->ctx,P->params, "smode").s;
+ if (opt) {
+ if (!strcmp(opt, "plane")) {
+ Q->dgg.output = ISEA_PLANE;
+ } else if (!strcmp(opt, "di")) {
+ Q->dgg.output = ISEA_Q2DI;
+ }
+ else if (!strcmp(opt, "dd")) {
+ Q->dgg.output = ISEA_Q2DD;
+ }
+ else if (!strcmp(opt, "hex")) {
+ Q->dgg.output = ISEA_HEX;
+ }
+ else {
+ /* TODO verify error code. Possibly eliminate magic */
+ return pj_default_destructor(P, PJD_ERR_ELLIPSOID_USE_REQUIRED);
+ }
+ }
+
+ if (pj_param(P->ctx,P->params, "trescale").i) {
+ Q->dgg.radius = ISEA_SCALE;
+ }
+
+ if (pj_param(P->ctx,P->params, "tresolution").i) {
+ Q->dgg.resolution = pj_param(P->ctx,P->params, "iresolution").i;
+ } else {
+ Q->dgg.resolution = 4;
+ }
+
+ if (pj_param(P->ctx,P->params, "taperture").i) {
+ Q->dgg.aperture = pj_param(P->ctx,P->params, "iaperture").i;
+ } else {
+ Q->dgg.aperture = 3;
+ }
+
+ return P;
+}
diff --git a/src/projections/krovak.cpp b/src/projections/krovak.cpp
new file mode 100644
index 00000000..9ecffb89
--- /dev/null
+++ b/src/projections/krovak.cpp
@@ -0,0 +1,222 @@
+ /*
+ * Project: PROJ
+ * Purpose: Implementation of the krovak (Krovak) projection.
+ * Definition: http://www.ihsenergy.com/epsg/guid7.html#1.4.3
+ * Author: Thomas Flemming, tf@ttqv.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2001, Thomas Flemming, tf@ttqv.com
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ ******************************************************************************
+ * A description of the (forward) projection is found in:
+ *
+ * Bohuslav Veverka,
+ *
+ * KROVAK’S PROJECTION AND ITS USE FOR THE
+ * CZECH REPUBLIC AND THE SLOVAK REPUBLIC,
+ *
+ * 50 years of the Research Institute of
+ * and the Slovak Republic Geodesy, Topography and Cartography
+ *
+ * which can be found via the Wayback Machine:
+ *
+ * https://web.archive.org/web/20150216143806/https://www.vugtk.cz/odis/sborniky/sb2005/Sbornik_50_let_VUGTK/Part_1-Scientific_Contribution/16-Veverka.pdf
+ *
+ * Further info, including the inverse projection, is given by EPSG:
+ *
+ * Guidance Note 7 part 2
+ * Coordinate Conversions and Transformations including Formulas
+ *
+ * http://www.iogp.org/pubs/373-07-2.pdf
+ *
+ * Variable names in this file mostly follows what is used in the
+ * paper by Veverka.
+ *
+ * According to EPSG the full Krovak projection method should have
+ * the following parameters. Within PROJ the azimuth, and pseudo
+ * standard parallel are hardcoded in the algorithm and can't be
+ * altered from outside. The others all have defaults to match the
+ * common usage with Krovak projection.
+ *
+ * lat_0 = latitude of centre of the projection
+ *
+ * lon_0 = longitude of centre of the projection
+ *
+ * ** = azimuth (true) of the centre line passing through the
+ * centre of the projection
+ *
+ * ** = latitude of pseudo standard parallel
+ *
+ * k = scale factor on the pseudo standard parallel
+ *
+ * x_0 = False Easting of the centre of the projection at the
+ * apex of the cone
+ *
+ * y_0 = False Northing of the centre of the projection at
+ * the apex of the cone
+ *
+ *****************************************************************************/
+
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(krovak, "Krovak") "\n\tPCyl, Ell";
+
+#define EPS 1e-15
+#define UQ 1.04216856380474 /* DU(2, 59, 42, 42.69689) */
+#define S0 1.37008346281555 /* Latitude of pseudo standard parallel 78deg 30'00" N */
+/* Not sure at all of the appropriate number for MAX_ITER... */
+#define MAX_ITER 100
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double alpha;
+ double k;
+ double n;
+ double rho0;
+ double ad;
+ int czech;
+};
+} // anonymous namespace
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ XY xy = {0.0,0.0};
+
+ double gfi, u, deltav, s, d, eps, rho;
+
+ gfi = pow ( (1. + P->e * sin(lp.phi)) / (1. - P->e * sin(lp.phi)), Q->alpha * P->e / 2.);
+
+ u = 2. * (atan(Q->k * pow( tan(lp.phi / 2. + M_PI_4), Q->alpha) / gfi)-M_PI_4);
+ deltav = -lp.lam * Q->alpha;
+
+ s = asin(cos(Q->ad) * sin(u) + sin(Q->ad) * cos(u) * cos(deltav));
+ d = asin(cos(u) * sin(deltav) / cos(s));
+
+ eps = Q->n * d;
+ rho = Q->rho0 * pow(tan(S0 / 2. + M_PI_4) , Q->n) / pow(tan(s / 2. + M_PI_4) , Q->n);
+
+ xy.y = rho * cos(eps);
+ xy.x = rho * sin(eps);
+
+ xy.y *= Q->czech;
+ xy.x *= Q->czech;
+
+ return xy;
+}
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ LP lp = {0.0,0.0};
+
+ double u, deltav, s, d, eps, rho, fi1, xy0;
+ int i;
+
+ xy0 = xy.x;
+ xy.x = xy.y;
+ xy.y = xy0;
+
+ xy.x *= Q->czech;
+ xy.y *= Q->czech;
+
+ rho = sqrt(xy.x * xy.x + xy.y * xy.y);
+ eps = atan2(xy.y, xy.x);
+
+ d = eps / sin(S0);
+ s = 2. * (atan( pow(Q->rho0 / rho, 1. / Q->n) * tan(S0 / 2. + M_PI_4)) - M_PI_4);
+
+ u = asin(cos(Q->ad) * sin(s) - sin(Q->ad) * cos(s) * cos(d));
+ deltav = asin(cos(s) * sin(d) / cos(u));
+
+ lp.lam = P->lam0 - deltav / Q->alpha;
+
+ /* ITERATION FOR lp.phi */
+ fi1 = u;
+
+ for (i = MAX_ITER; i ; --i) {
+ lp.phi = 2. * ( atan( pow( Q->k, -1. / Q->alpha) *
+ pow( tan(u / 2. + M_PI_4) , 1. / Q->alpha) *
+ pow( (1. + P->e * sin(fi1)) / (1. - P->e * sin(fi1)) , P->e / 2.)
+ ) - M_PI_4);
+
+ if (fabs(fi1 - lp.phi) < EPS)
+ break;
+ fi1 = lp.phi;
+ }
+ if( i == 0 )
+ pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT );
+
+ lp.lam -= P->lam0;
+
+ return lp;
+}
+
+
+PJ *PROJECTION(krovak) {
+ double u0, n0, g;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ /* we want Bessel as fixed ellipsoid */
+ P->a = 6377397.155;
+ P->e = sqrt(P->es = 0.006674372230614);
+
+ /* if latitude of projection center is not set, use 49d30'N */
+ if (!pj_param(P->ctx, P->params, "tlat_0").i)
+ P->phi0 = 0.863937979737193;
+
+ /* if center long is not set use 42d30'E of Ferro - 17d40' for Ferro */
+ /* that will correspond to using longitudes relative to greenwich */
+ /* as input and output, instead of lat/long relative to Ferro */
+ if (!pj_param(P->ctx, P->params, "tlon_0").i)
+ P->lam0 = 0.7417649320975901 - 0.308341501185665;
+
+ /* if scale not set default to 0.9999 */
+ if (!pj_param(P->ctx, P->params, "tk").i && !pj_param(P->ctx, P->params, "tk_0").i)
+ P->k0 = 0.9999;
+
+ Q->czech = 1;
+ if( !pj_param(P->ctx, P->params, "tczech").i )
+ Q->czech = -1;
+
+ /* Set up shared parameters between forward and inverse */
+ Q->alpha = sqrt(1. + (P->es * pow(cos(P->phi0), 4)) / (1. - P->es));
+ u0 = asin(sin(P->phi0) / Q->alpha);
+ g = pow( (1. + P->e * sin(P->phi0)) / (1. - P->e * sin(P->phi0)) , Q->alpha * P->e / 2. );
+ Q->k = tan( u0 / 2. + M_PI_4) / pow (tan(P->phi0 / 2. + M_PI_4) , Q->alpha) * g;
+ n0 = sqrt(1. - P->es) / (1. - P->es * pow(sin(P->phi0), 2));
+ Q->n = sin(S0);
+ Q->rho0 = P->k0 * n0 / tan(S0);
+ Q->ad = M_PI_2 - UQ;
+
+ P->inv = e_inverse;
+ P->fwd = e_forward;
+
+ return P;
+}
diff --git a/src/projections/labrd.cpp b/src/projections/labrd.cpp
new file mode 100644
index 00000000..d3930243
--- /dev/null
+++ b/src/projections/labrd.cpp
@@ -0,0 +1,132 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(labrd, "Laborde") "\n\tCyl, Sph\n\tSpecial for Madagascar";
+#define EPS 1.e-10
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double kRg, p0s, A, C, Ca, Cb, Cc, Cd;
+};
+} // anonymous namespace
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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(M_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)) - M_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 = static_cast<struct pj_opaque*>(P->opaque);
+ /* t = 0.0 optimization is to avoid a false positive cppcheck warning */
+ /* (cppcheck git beaf29c15867984aa3c2a15cf15bd7576ccde2b3). Might no */
+ /* longer be necessary with later versions. */
+ double x2, y2, V1, V2, V3, V4, t = 0.0, 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(M_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)) - M_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;
+}
+
+
+PJ *PROJECTION(labrd) {
+ double Az, sinp, R, N, t;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ 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(M_FORTPI + .5 * P->phi0))
+ + log( tan(M_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;
+}
diff --git a/src/projections/laea.cpp b/src/projections/laea.cpp
new file mode 100644
index 00000000..dd02c75a
--- /dev/null
+++ b/src/projections/laea.cpp
@@ -0,0 +1,300 @@
+#define PJ_LIB__
+#include <errno.h>
+#include "proj.h"
+#include "projects.h"
+#include "proj_math.h"
+
+PROJ_HEAD(laea, "Lambert Azimuthal Equal Area") "\n\tAzi, Sph&Ell";
+
+namespace { // anonymous namespace
+enum Mode {
+ N_POLE = 0,
+ S_POLE = 1,
+ EQUIT = 2,
+ OBLIQ = 3
+};
+} // anonymous namespace
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double sinb1;
+ double cosb1;
+ double xmf;
+ double ymf;
+ double mmf;
+ double qp;
+ double dd;
+ double rq;
+ double *apa;
+ enum Mode mode;
+};
+} // anonymous namespace
+
+#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 = static_cast<struct pj_opaque*>(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 = M_HALFPI + lp.phi;
+ q = Q->qp - q;
+ break;
+ case S_POLE:
+ b = lp.phi - M_HALFPI;
+ q = Q->qp + q;
+ break;
+ }
+ if (fabs(b) < EPS10) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+
+ 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 = 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;
+}
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ 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;
+ /*-fallthrough*/
+ case S_POLE:
+ if (fabs(lp.phi + P->phi0) < EPS10) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ xy.y = M_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 = static_cast<struct pj_opaque*>(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;
+ /*-fallthrough*/
+ case S_POLE:
+ q = (xy.x * xy.x + xy.y * xy.y);
+ if (q == 0.0) {
+ 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 = static_cast<struct pj_opaque*>(P->opaque);
+ double cosz=0.0, rh, sinz=0.0;
+
+ rh = hypot(xy.x, xy.y);
+ if ((lp.phi = rh * .5 ) > 1.) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+ 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 = M_HALFPI - lp.phi;
+ break;
+ case S_POLE:
+ lp.phi -= M_HALFPI;
+ break;
+ }
+ lp.lam = (xy.y == 0. && (Q->mode == EQUIT || Q->mode == OBLIQ)) ?
+ 0. : atan2(xy.x, xy.y);
+ return (lp);
+}
+
+
+static PJ *destructor (PJ *P, int errlev) {
+ if (nullptr==P)
+ return nullptr;
+
+ if (nullptr==P->opaque)
+ return pj_default_destructor (P, errlev);
+
+ pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->apa);
+
+ return pj_default_destructor(P, errlev);
+}
+
+
+PJ *PROJECTION(laea) {
+ double t;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+ P->destructor = destructor;
+
+ t = fabs(P->phi0);
+ if (fabs(t - M_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 != 0.0) {
+ 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);
+ if (nullptr==Q->apa)
+ return destructor(P, ENOMEM);
+ 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;
+}
+
diff --git a/src/projections/lagrng.cpp b/src/projections/lagrng.cpp
new file mode 100644
index 00000000..8c0150aa
--- /dev/null
+++ b/src/projections/lagrng.cpp
@@ -0,0 +1,98 @@
+#define PJ_LIB__
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(lagrng, "Lagrange") "\n\tMisc Sph\n\tW=";
+
+#define TOL 1e-10
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double a1;
+ double a2;
+ double hrw;
+ double hw;
+ double rw;
+ double w;
+};
+} // anonymous namespace
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double v, c;
+
+ if (fabs(fabs(lp.phi) - M_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);
+ lp.lam *= Q->rw;
+ c = 0.5 * (v + 1./v) + cos(lp.lam);
+ if (c < TOL) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ xy.x = 2. * sin(lp.lam) / c;
+ xy.y = (v - 1./v) / c;
+ }
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double c, x2, y2p, y2m;
+
+ if (fabs(fabs(xy.y) - 2.) < TOL) {
+ lp.phi = xy.y < 0 ? -M_HALFPI : M_HALFPI;
+ lp.lam = 0;
+ } else {
+ x2 = xy.x * xy.x;
+ y2p = 2. + xy.y;
+ y2m = 2. - xy.y;
+ c = y2p * y2m - x2;
+ if (fabs(c) < TOL) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+ lp.phi = 2. * atan(pow((y2p * y2p + x2) / (Q->a2 * (y2m * y2m + x2)), Q->hw)) - M_HALFPI;
+ lp.lam = Q->w * atan2(4. * xy.x, c);
+ }
+ return lp;
+}
+
+
+PJ *PROJECTION(lagrng) {
+ double phi1;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->w = pj_param(P->ctx, P->params, "dW").f;
+ if (Q->w <= 0)
+ return pj_default_destructor(P, PJD_ERR_W_OR_M_ZERO_OR_LESS);
+ Q->hw = 0.5 * Q->w;
+ Q->rw = 1. / Q->w;
+ Q->hrw = 0.5 * Q->rw;
+ phi1 = sin(pj_param(P->ctx, P->params, "rlat_1").f);
+ if (fabs(fabs(phi1) - 1.) < TOL)
+ return pj_default_destructor(P, PJD_ERR_LAT_LARGER_THAN_90);
+
+ Q->a1 = pow((1. - phi1)/(1. + phi1), Q->hrw);
+ Q->a2 = Q->a1 * Q->a1;
+
+ P->es = 0.;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
+
diff --git a/src/projections/larr.cpp b/src/projections/larr.cpp
new file mode 100644
index 00000000..e4d5d240
--- /dev/null
+++ b/src/projections/larr.cpp
@@ -0,0 +1,28 @@
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(larr, "Larrivee") "\n\tMisc Sph, no inv";
+
+#define SIXTH .16666666666666666
+
+
+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;
+}
+
+
+PJ *PROJECTION(larr) {
+
+ P->es = 0;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/lask.cpp b/src/projections/lask.cpp
new file mode 100644
index 00000000..46f23edb
--- /dev/null
+++ b/src/projections/lask.cpp
@@ -0,0 +1,39 @@
+#define PJ_LIB__
+#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
+
+
+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;
+}
+
+
+PJ *PROJECTION(lask) {
+
+ P->fwd = s_forward;
+ P->es = 0.;
+
+ return P;
+}
+
diff --git a/src/projections/latlong.cpp b/src/projections/latlong.cpp
new file mode 100644
index 00000000..1331d59a
--- /dev/null
+++ b/src/projections/latlong.cpp
@@ -0,0 +1,124 @@
+/******************************************************************************
+ * Project: PROJ.4
+ * Purpose: Stub projection implementation for lat/long coordinates. We
+ * don't actually change the coordinates, but we want proj=latlong
+ * to act sort of like a projection.
+ * Author: Frank Warmerdam, warmerdam@pobox.com
+ *
+ ******************************************************************************
+ * Copyright (c) 2000, Frank Warmerdam
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************/
+
+/* very loosely based upon DMA code by Bradford W. Drew */
+#define PJ_LIB__
+#include "proj_internal.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";
+
+
+ static XY latlong_forward(LP lp, PJ *P) {
+ XY xy = {0.0,0.0};
+ (void) P;
+ xy.x = lp.lam;
+ xy.y = lp.phi;
+ return xy;
+}
+
+
+static LP latlong_inverse(XY xy, PJ *P) {
+ LP lp = {0.0,0.0};
+ (void) P;
+ lp.phi = xy.y;
+ lp.lam = xy.x;
+ return lp;
+}
+
+
+ static XYZ latlong_forward_3d (LPZ lpz, PJ *P) {
+ XYZ xyz = {0,0,0};
+ (void) P;
+ xyz.x = lpz.lam;
+ xyz.y = lpz.phi;
+ xyz.z = lpz.z;
+ return xyz;
+}
+
+
+static LPZ latlong_inverse_3d (XYZ xyz, PJ *P) {
+ LPZ lpz = {0,0,0};
+ (void) P;
+ lpz.lam = xyz.x;
+ lpz.phi = xyz.y;
+ lpz.z = xyz.z;
+ return lpz;
+}
+
+static PJ_COORD latlong_forward_4d (PJ_COORD obs, PJ *P) {
+ (void) P;
+ return obs;
+}
+
+
+static PJ_COORD latlong_inverse_4d (PJ_COORD obs, PJ *P) {
+ (void) P;
+ return obs;
+}
+
+
+
+static PJ *latlong_setup (PJ *P) {
+ P->is_latlong = 1;
+ P->x0 = 0;
+ P->y0 = 0;
+ P->inv = latlong_inverse;
+ P->fwd = latlong_forward;
+ P->inv3d = latlong_inverse_3d;
+ P->fwd3d = latlong_forward_3d;
+ P->inv4d = latlong_inverse_4d;
+ P->fwd4d = latlong_forward_4d;
+ P->left = PJ_IO_UNITS_ANGULAR;
+ P->right = PJ_IO_UNITS_ANGULAR;
+ return P;
+}
+
+PJ *PROJECTION(latlong) {
+ return latlong_setup (P);
+}
+
+
+PJ *PROJECTION(longlat) {
+ return latlong_setup (P);
+}
+
+
+PJ *PROJECTION(latlon) {
+ return latlong_setup (P);
+}
+
+
+PJ *PROJECTION(lonlat) {
+ return latlong_setup (P);
+}
+
diff --git a/src/projections/lcc.cpp b/src/projections/lcc.cpp
new file mode 100644
index 00000000..7d6e3f57
--- /dev/null
+++ b/src/projections/lcc.cpp
@@ -0,0 +1,130 @@
+#define PJ_LIB__
+#include <errno.h>
+#include "proj.h"
+#include "projects.h"
+#include "proj_math.h"
+
+PROJ_HEAD(lcc, "Lambert Conformal Conic")
+ "\n\tConic, Sph&Ell\n\tlat_1= and lat_2= or lat_0, k_0=";
+
+#define EPS10 1.e-10
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double phi1;
+ double phi2;
+ double n;
+ double rho0;
+ double c;
+};
+} // anonymous namespace
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ XY xy = {0., 0.};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double rho;
+
+ if (fabs(fabs(lp.phi) - M_HALFPI) < EPS10) {
+ if ((lp.phi * Q->n) <= 0.) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ rho = 0.;
+ } else {
+ rho = Q->c * (P->es != 0. ?
+ pow(pj_tsfn(lp.phi, sin(lp.phi), P->e), Q->n) :
+ pow(tan(M_FORTPI + .5 * lp.phi), -Q->n));
+ }
+ lp.lam *= Q->n;
+ xy.x = P->k0 * (rho * sin(lp.lam));
+ xy.y = P->k0 * (Q->rho0 - rho * cos(lp.lam));
+ return xy;
+}
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */
+ LP lp = {0., 0.};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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.) {
+ if (Q->n < 0.) {
+ rho = -rho;
+ xy.x = -xy.x;
+ xy.y = -xy.y;
+ }
+ if (P->es != 0.) {
+ lp.phi = pj_phi2(P->ctx, pow(rho / Q->c, 1./Q->n), P->e);
+ if (lp.phi == HUGE_VAL) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+
+ } else
+ lp.phi = 2. * atan(pow(Q->c / rho, 1./Q->n)) - M_HALFPI;
+ lp.lam = atan2(xy.x, xy.y) / Q->n;
+ } else {
+ lp.lam = 0.;
+ lp.phi = Q->n > 0. ? M_HALFPI : -M_HALFPI;
+ }
+ return lp;
+}
+
+
+PJ *PROJECTION(lcc) {
+ double cosphi, sinphi;
+ int secant;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc(1, sizeof (struct pj_opaque)));
+
+ if (nullptr == Q)
+ return pj_default_destructor(P, ENOMEM);
+ P->opaque = Q;
+
+ Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f;
+ if (pj_param(P->ctx, P->params, "tlat_2").i)
+ Q->phi2 = pj_param(P->ctx, P->params, "rlat_2").f;
+ else {
+ Q->phi2 = Q->phi1;
+ if (!pj_param(P->ctx, P->params, "tlat_0").i)
+ P->phi0 = Q->phi1;
+ }
+ if (fabs(Q->phi1 + Q->phi2) < EPS10)
+ return pj_default_destructor(P, PJD_ERR_CONIC_LAT_EQUAL);
+
+ Q->n = sinphi = sin(Q->phi1);
+ cosphi = cos(Q->phi1);
+ secant = fabs(Q->phi1 - Q->phi2) >= EPS10;
+ if (P->es != 0.) {
+ double ml1, m1;
+
+ 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) - M_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(M_FORTPI + .5 * Q->phi2) /
+ tan(M_FORTPI + .5 * Q->phi1));
+ Q->c = cosphi * pow(tan(M_FORTPI + .5 * Q->phi1), Q->n) / Q->n;
+ Q->rho0 = (fabs(fabs(P->phi0) - M_HALFPI) < EPS10) ? 0. :
+ Q->c * pow(tan(M_FORTPI + .5 * P->phi0), -Q->n);
+ }
+
+ P->inv = e_inverse;
+ P->fwd = e_forward;
+
+ return P;
+}
diff --git a/src/projections/lcca.cpp b/src/projections/lcca.cpp
new file mode 100644
index 00000000..70b5dff9
--- /dev/null
+++ b/src/projections/lcca.cpp
@@ -0,0 +1,164 @@
+/*****************************************************************************
+
+ Lambert Conformal Conic Alternative
+ -----------------------------------
+
+ This is Gerald Evenden's 2003 implementation of an alternative
+ "almost" LCC, which has been in use historically, but which
+ should NOT be used for new projects - i.e: use this implementation
+ if you need interoperability with old data represented in this
+ projection, but not in any other case.
+
+ The code was originally discussed on the PROJ.4 mailing list in
+ a thread archived over at
+
+ http://lists.maptools.org/pipermail/proj/2003-March/000644.html
+
+ It was discussed again in the thread starting at
+
+ http://lists.maptools.org/pipermail/proj/2017-October/007828.html
+ and continuing at
+ http://lists.maptools.org/pipermail/proj/2017-November/007831.html
+
+ which prompted Clifford J. Mugnier to add these clarifying notes:
+
+ The French Army Truncated Cubic Lambert (partially conformal) Conic
+ projection is the Legal system for the projection in France between
+ the late 1800s and 1948 when the French Legislature changed the law
+ to recognize the fully conformal version.
+
+ It was (might still be in one or two North African prior French
+ Colonies) used in North Africa in Algeria, Tunisia, & Morocco, as
+ well as in Syria during the Levant.
+
+ Last time I have seen it used was about 30+ years ago in
+ Algeria when it was used to define Lease Block boundaries for
+ Petroleum Exploration & Production.
+
+ (signed)
+
+ Clifford J. Mugnier, c.p., c.m.s.
+ Chief of Geodesy
+ LSU Center for GeoInformatics
+ Dept. of Civil Engineering
+ LOUISIANA STATE UNIVERSITY
+
+*****************************************************************************/
+
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(lcca, "Lambert Conformal Conic Alternative")
+ "\n\tConic, Sph&Ell\n\tlat_0=";
+
+#define MAX_ITER 10
+#define DEL_TOL 1e-12
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double *en;
+ double r0, l, M0;
+ double C;
+};
+} // anonymous namespace
+
+
+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 = static_cast<struct pj_opaque*>(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 = static_cast<struct pj_opaque*>(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) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+ lp.phi = pj_inv_mlfn(P->ctx, S + Q->M0, P->es, Q->en);
+
+ return lp;
+}
+
+
+static PJ *destructor (PJ *P, int errlev) {
+ if (nullptr==P)
+ return nullptr;
+
+ if (nullptr==P->opaque)
+ return pj_default_destructor (P, errlev);
+
+ pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->en);
+ return pj_default_destructor (P, errlev);
+}
+
+
+PJ *PROJECTION(lcca) {
+ double s2p0, N0, R0, tan0;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ (Q->en = pj_enfn(P->es));
+ if (!Q->en)
+ return pj_default_destructor (P, ENOMEM);
+
+ if (P->phi0 == 0.) {
+ return destructor(P, PJD_ERR_LAT_0_IS_ZERO);
+ }
+ 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;
+ P->destructor = destructor;
+
+ return P;
+}
diff --git a/src/projections/loxim.cpp b/src/projections/loxim.cpp
new file mode 100644
index 00000000..f68e844a
--- /dev/null
+++ b/src/projections/loxim.cpp
@@ -0,0 +1,77 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(loxim, "Loximuthal") "\n\tPCyl Sph";
+
+#define EPS 1e-8
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double phi1;
+ double cosphi1;
+ double tanphi1;
+};
+} // anonymous namespace
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+
+ xy.y = lp.phi - Q->phi1;
+ if (fabs(xy.y) < EPS)
+ xy.x = lp.lam * Q->cosphi1;
+ else {
+ xy.x = M_FORTPI + 0.5 * lp.phi;
+ if (fabs(xy.x) < EPS || fabs(fabs(xy.x) - M_HALFPI) < EPS)
+ xy.x = 0.;
+ else
+ xy.x = lp.lam * xy.y / log( tan(xy.x) / Q->tanphi1 );
+ }
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+
+ lp.phi = xy.y + Q->phi1;
+ if (fabs(xy.y) < EPS) {
+ lp.lam = xy.x / Q->cosphi1;
+ } else {
+ lp.lam = M_FORTPI + 0.5 * lp.phi;
+ if (fabs(lp.lam) < EPS || fabs(fabs(lp.lam) - M_HALFPI) < EPS)
+ lp.lam = 0.;
+ else
+ lp.lam = xy.x * log( tan(lp.lam) / Q->tanphi1 ) / xy.y ;
+ }
+ return lp;
+}
+
+
+PJ *PROJECTION(loxim) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->phi1 = pj_param(P->ctx, P->params, "rlat_1").f;
+ Q->cosphi1 = cos(Q->phi1);
+ if (Q->cosphi1 < EPS)
+ return pj_default_destructor(P, PJD_ERR_LAT_LARGER_THAN_90);
+
+
+ Q->tanphi1 = tan(M_FORTPI + 0.5 * Q->phi1);
+
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ P->es = 0.;
+
+ return P;
+}
diff --git a/src/projections/lsat.cpp b/src/projections/lsat.cpp
new file mode 100644
index 00000000..a0eca1bd
--- /dev/null
+++ b/src/projections/lsat.cpp
@@ -0,0 +1,212 @@
+/* based upon Snyder and Linck, USGS-NMD */
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(lsat, "Space oblique for LANDSAT")
+ "\n\tCyl, Sph&Ell\n\tlsat= path=";
+
+#define TOL 1e-7
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double a2, a4, b, c1, c3;
+ double q, t, u, w, p22, sa, ca, xj, rlm, rlm2;
+};
+} // anonymous namespace
+
+static void seraz0(double lam, double mult, PJ *P) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double sdsq, h, s, fc, sd, sq, d__1 = 0;
+
+ lam *= DEG_TO_RAD;
+ sd = sin(lam);
+ sdsq = sd * sd;
+ 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);
+ 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.);
+}
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ int l, nn;
+ double lamt = 0.0, xlam, sdsq, c, d, s, lamdp = 0.0, phidp, lampp, tanph;
+ double lamtp, cl, sd, sp, sav, tanphi;
+
+ if (lp.phi > M_HALFPI)
+ lp.phi = M_HALFPI;
+ else if (lp.phi < -M_HALFPI)
+ lp.phi = -M_HALFPI;
+
+ if (lp.phi >= 0. )
+ lampp = M_HALFPI;
+ else
+ lampp = M_PI_HALFPI;
+ tanphi = tan(lp.phi);
+ for (nn = 0;;) {
+ double fac;
+ sav = lampp;
+ lamtp = lp.lam + Q->p22 * lampp;
+ cl = cos(lamtp);
+ if( cl < 0 )
+ fac = lampp + sin(lampp) * M_HALFPI;
+ else
+ fac = lampp - sin(lampp) * M_HALFPI;
+ for (l = 50; l >= 0; --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 = M_TWOPI_HALFPI;
+ else if (lamdp >= Q->rlm2)
+ lampp = M_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(M_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;
+}
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ int nn;
+ double lamt, sdsq, s, lamdp, phidp, sppsq, dd, sd, sl, fac, scl, sav, spp;
+
+ 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) - M_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 -= M_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;
+}
+
+
+PJ *PROJECTION(lsat) {
+ int land, path;
+ double lam, alf, esc, ess;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor(P, ENOMEM);
+ P->opaque = Q;
+
+ land = pj_param(P->ctx, P->params, "ilsat").i;
+ if (land <= 0 || land > 5)
+ return pj_default_destructor(P, PJD_ERR_LSAT_NOT_IN_RANGE);
+
+ path = pj_param(P->ctx, P->params, "ipath").i;
+ if (path <= 0 || path > (land <= 3 ? 251 : 233))
+ return pj_default_destructor(P, PJD_ERR_PATH_NOT_IN_RANGE);
+
+ if (land <= 3) {
+ P->lam0 = DEG_TO_RAD * 128.87 - M_TWOPI / 251. * path;
+ Q->p22 = 103.2669323;
+ alf = DEG_TO_RAD * 99.092;
+ } else {
+ P->lam0 = DEG_TO_RAD * 129.3 - M_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 = M_PI * (1. / 248. + .5161290322580645);
+ Q->rlm2 = Q->rlm + M_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;
+}
diff --git a/src/projections/mbt_fps.cpp b/src/projections/mbt_fps.cpp
new file mode 100644
index 00000000..66ed9458
--- /dev/null
+++ b/src/projections/mbt_fps.cpp
@@ -0,0 +1,57 @@
+#define PJ_LIB__
+
+#include <math.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 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
+
+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;
+}
+
+
+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);
+}
+
+
+PJ *PROJECTION(mbt_fps) {
+
+ P->es = 0;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/mbtfpp.cpp b/src/projections/mbtfpp.cpp
new file mode 100644
index 00000000..276a43eb
--- /dev/null
+++ b/src/projections/mbtfpp.cpp
@@ -0,0 +1,65 @@
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "proj.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
+
+
+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;
+}
+
+
+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) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ } else {
+ lp.phi = (lp.phi < 0.) ? -M_HALFPI : M_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) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ } else {
+ lp.phi = (lp.phi < 0.) ? -M_HALFPI : M_HALFPI;
+ }
+ } else
+ lp.phi = asin(lp.phi);
+
+ return lp;
+}
+
+
+PJ *PROJECTION(mbtfpp) {
+
+ P->es = 0.;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/mbtfpq.cpp b/src/projections/mbtfpq.cpp
new file mode 100644
index 00000000..b7c0eb16
--- /dev/null
+++ b/src/projections/mbtfpq.cpp
@@ -0,0 +1,74 @@
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(mbtfpq, "McBryde-Thomas Flat-Polar Quartic") "\n\tCyl, Sph";
+
+#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
+
+
+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) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+ else if (lp.phi < 0.) { t = -1.; lp.phi = -M_PI; }
+ else { t = 1.; lp.phi = M_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) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+ else lp.phi = lp.phi < 0. ? -M_HALFPI : M_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;
+}
diff --git a/src/projections/merc.cpp b/src/projections/merc.cpp
new file mode 100644
index 00000000..1998234e
--- /dev/null
+++ b/src/projections/merc.cpp
@@ -0,0 +1,101 @@
+#define PJ_LIB__
+
+#include <float.h>
+#include <math.h>
+
+#include "proj_internal.h"
+#include "proj.h"
+#include "proj_math.h"
+#include "projects.h"
+
+PROJ_HEAD(merc, "Mercator") "\n\tCyl, Sph&Ell\n\tlat_ts=";
+PROJ_HEAD(webmerc, "Web Mercator / Pseudo Mercator") "\n\tCyl, Ell\n\t";
+
+#define EPS10 1.e-10
+static double logtanpfpim1(double x) { /* log(tan(x/2 + M_FORTPI)) */
+ if (fabs(x) <= DBL_EPSILON) {
+ /* tan(M_FORTPI + .5 * x) can be approximated by 1.0 + x */
+ return log1p(x);
+ }
+ return log(tan(M_FORTPI + .5 * x));
+}
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ XY xy = {0.0,0.0};
+ if (fabs(fabs(lp.phi) - M_HALFPI) <= EPS10) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ 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) - M_HALFPI) <= EPS10) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+}
+ xy.x = P->k0 * lp.lam;
+ xy.y = P->k0 * logtanpfpim1(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) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ 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 = atan(sinh(xy.y / P->k0));
+ lp.lam = xy.x / P->k0;
+ return lp;
+}
+
+
+PJ *PROJECTION(merc) {
+ double phits=0.0;
+ int is_phits;
+
+ if( (is_phits = pj_param(P->ctx, P->params, "tlat_ts").i) ) {
+ phits = fabs(pj_param(P->ctx, P->params, "rlat_ts").f);
+ if (phits >= M_HALFPI)
+ return pj_default_destructor(P, PJD_ERR_LAT_TS_LARGER_THAN_90);
+ }
+
+ if (P->es != 0.0) { /* ellipsoid */
+ if (is_phits)
+ P->k0 = pj_msfn(sin(phits), cos(phits), P->es);
+ P->inv = e_inverse;
+ P->fwd = e_forward;
+ }
+
+ else { /* sphere */
+ if (is_phits)
+ P->k0 = cos(phits);
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ }
+
+ return P;
+}
+
+PJ *PROJECTION(webmerc) {
+
+ /* Overriding k_0 with fixed parameter */
+ P->k0 = 1.0;
+
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ return P;
+}
diff --git a/src/projections/mill.cpp b/src/projections/mill.cpp
new file mode 100644
index 00000000..3ea9636f
--- /dev/null
+++ b/src/projections/mill.cpp
@@ -0,0 +1,37 @@
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(mill, "Miller Cylindrical") "\n\tCyl, Sph";
+
+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(M_FORTPI + lp.phi * .4)) * 1.25;
+
+ return (xy);
+}
+
+
+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)) - M_FORTPI);
+
+ return (lp);
+}
+
+
+PJ *PROJECTION(mill) {
+ P->es = 0.;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/misrsom.cpp b/src/projections/misrsom.cpp
new file mode 100644
index 00000000..c84b96e3
--- /dev/null
+++ b/src/projections/misrsom.cpp
@@ -0,0 +1,219 @@
+/******************************************************************************
+ * This implements Space Oblique Mercator (SOM) projection, used by the
+ * Multi-angle Imaging SpectroRadiometer (MISR) products, from the NASA EOS Terra
+ * platform.
+ *
+ * The code is identical to that of Landsat SOM (PJ_lsat.c) with the following
+ * parameter changes:
+ *
+ * inclination angle = 98.30382 degrees
+ * period of revolution = 98.88 minutes
+ * ascending longitude = 129.3056 degrees - (360 / 233) * path_number
+ *
+ * and the following code change:
+ *
+ * Q->rlm = PI * (1. / 248. + .5161290322580645);
+ *
+ * changed to:
+ *
+ * Q->rlm = 0
+ *
+ *****************************************************************************/
+/* based upon Snyder and Linck, USGS-NMD */
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(misrsom, "Space oblique for MISR")
+ "\n\tCyl, Sph&Ell\n\tpath=";
+
+#define TOL 1e-7
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double a2, a4, b, c1, c3;
+ double q, t, u, w, p22, sa, ca, xj, rlm, rlm2;
+};
+} // anonymous namespace
+
+static void seraz0(double lam, double mult, PJ *P) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double sdsq, h, s, fc, sd, sq, d__1;
+
+ lam *= DEG_TO_RAD;
+ sd = sin(lam);
+ sdsq = sd * sd;
+ 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.);
+}
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ int l, nn;
+ double lamt = 0.0, xlam, sdsq, c, d, s, lamdp = 0.0, phidp, lampp, tanph;
+ double lamtp, cl, sd, sp, sav, tanphi;
+
+ if (lp.phi > M_HALFPI)
+ lp.phi = M_HALFPI;
+ else if (lp.phi < -M_HALFPI)
+ lp.phi = -M_HALFPI;
+ if (lp.phi >= 0. )
+ lampp = M_HALFPI;
+ else
+ lampp = M_PI_HALFPI;
+ tanphi = tan(lp.phi);
+ for (nn = 0;;) {
+ double fac;
+ sav = lampp;
+ lamtp = lp.lam + Q->p22 * lampp;
+ cl = cos(lamtp);
+ if( cl < 0 )
+ fac = lampp + sin(lampp) * M_HALFPI;
+ else
+ fac = lampp - sin(lampp) * M_HALFPI;
+ for (l = 50; l; --l) {
+ lamt = lp.lam + Q->p22 * sav;
+ if (fabs(c = cos(lamt)) < 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 = M_TWOPI_HALFPI;
+ else if (lamdp >= Q->rlm2)
+ lampp = M_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(M_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;
+}
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ int nn;
+ double lamt, sdsq, s, lamdp, phidp, sppsq, dd, sd, sl, fac, scl, sav, spp;
+
+ 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) - M_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 -= M_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;
+}
+
+
+PJ *PROJECTION(misrsom) {
+ int path;
+ double lam, alf, esc, ess;
+
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ path = pj_param(P->ctx, P->params, "ipath").i;
+ if (path <= 0 || path > 233)
+ return pj_default_destructor(P, PJD_ERR_PATH_NOT_IN_RANGE);
+
+ P->lam0 = DEG_TO_RAD * 129.3056 - M_TWOPI / 233. * path;
+ alf = 98.30382 * DEG_TO_RAD;
+ 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 + M_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;
+}
diff --git a/src/projections/mod_ster.cpp b/src/projections/mod_ster.cpp
new file mode 100644
index 00000000..7c4f363b
--- /dev/null
+++ b/src/projections/mod_ster.cpp
@@ -0,0 +1,282 @@
+/* based upon Snyder and Linck, USGS-NMD */
+#define PJ_LIB__
+#include <errno.h>
+#include "projects.h"
+#include "proj_math.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. Stereographic of 48 U.S.") "\n\tAzi(mod)";
+PROJ_HEAD(alsk, "Mod. Stereographic of Alaska") "\n\tAzi(mod)";
+PROJ_HEAD(gs50, "Mod. Stereographic of 50 U.S.") "\n\tAzi(mod)";
+
+#define EPSLN 1e-12
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ const COMPLEX *zcoeff; \
+ double cchio, schio; \
+ int n;
+};
+} // anonymous namespace
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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((M_HALFPI + lp.phi) * .5) *
+ pow((1. - esphi) / (1. + esphi), P->e * .5)) - M_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;
+}
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ int nn;
+ COMPLEX p, fxy, fpxy, dp;
+ double den, rh = 0.0, z, sinz = 0.0, cosz = 0.0, chi, phi = 0.0, 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) {
+ /* if we end up here input coordinates were (0,0).
+ * pj_inv() adds P->lam0 to lp.lam, this way we are
+ * sure to get the correct offset */
+ lp.lam = 0.0;
+ 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) {
+ double dphi;
+ esphi = P->e * sin(phi);
+ dphi = 2. * atan(tan((M_HALFPI + chi) * .5) *
+ pow((1. + esphi) / (1. - esphi), P->e * .5)) - M_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;
+}
+
+
+static PJ *setup(PJ *P) { /* general initialization */
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double esphi, chio;
+
+ if (P->es != 0.0) {
+ esphi = P->e * sin(P->phi0);
+ chio = 2. * atan(tan((M_HALFPI + P->phi0) * .5) *
+ pow((1. - esphi) / (1. + esphi), P->e * .5)) - M_HALFPI;
+ } else
+ chio = P->phi0;
+ Q->schio = sin(chio);
+ Q->cchio = cos(chio);
+ P->inv = e_inverse;
+ P->fwd = e_forward;
+
+ return P;
+}
+
+
+/* Miller Oblated Stereographic */
+PJ *PROJECTION(mil_os) {
+ static const COMPLEX AB[] = {
+ {0.924500, 0.},
+ {0., 0.},
+ {0.019430, 0.}
+ };
+
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->n = 2;
+ P->lam0 = DEG_TO_RAD * 20.;
+ P->phi0 = DEG_TO_RAD * 18.;
+ Q->zcoeff = AB;
+ P->es = 0.;
+
+ return setup(P);
+}
+
+
+/* Lee Oblated Stereographic */
+PJ *PROJECTION(lee_os) {
+ static const COMPLEX AB[] = {
+ {0.721316, 0.},
+ {0., 0.},
+ {-0.0088162, -0.00617325}
+ };
+
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->n = 2;
+ P->lam0 = DEG_TO_RAD * -165.;
+ P->phi0 = DEG_TO_RAD * -10.;
+ Q->zcoeff = AB;
+ P->es = 0.;
+
+ return setup(P);
+}
+
+
+PJ *PROJECTION(gs48) {
+ static const COMPLEX /* 48 United States */
+ AB[] = {
+ {0.98879, 0.},
+ {0., 0.},
+ {-0.050909, 0.},
+ {0., 0.},
+ {0.075528, 0.}
+ };
+
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->n = 4;
+ P->lam0 = DEG_TO_RAD * -96.;
+ P->phi0 = DEG_TO_RAD * 39.;
+ Q->zcoeff = AB;
+ P->es = 0.;
+ P->a = 6370997.;
+
+ return setup(P);
+}
+
+
+PJ *PROJECTION(alsk) {
+ static const COMPLEX ABe[] = { /* Alaska ellipsoid */
+ { .9945303, 0.},
+ { .0052083, -.0027404},
+ { .0072721, .0048181},
+ {-.0151089, -.1932526},
+ { .0642675, -.1381226},
+ { .3582802, -.2884586},
+ };
+
+ static const COMPLEX ABs[] = { /* Alaska sphere */
+ { .9972523, 0.},
+ { .0052513, -.0041175},
+ { .0074606, .0048125},
+ {-.0153783, -.1968253},
+ { .0636871, -.1408027},
+ { .3660976, -.2937382}
+ };
+
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->n = 5;
+ P->lam0 = DEG_TO_RAD * -152.;
+ P->phi0 = DEG_TO_RAD * 64.;
+ if (P->es != 0.0) { /* fixed ellipsoid/sphere */
+ Q->zcoeff = ABe;
+ P->a = 6378206.4;
+ P->e = sqrt(P->es = 0.00676866);
+ } else {
+ Q->zcoeff = ABs;
+ P->a = 6370997.;
+ }
+
+ return setup(P);
+}
+
+
+PJ *PROJECTION(gs50) {
+ static const 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 const COMPLEX ABs[] = { /* GS50 sphere */
+ { .9842990, 0.},
+ { .0211642, .0037608},
+ {-.1036018, -.0575102},
+ {-.0329095, -.0320119},
+ { .0499471, .1223335},
+ { .0260460, .0899805},
+ { .0007388, -.1435792},
+ { .0075848, -.1334108},
+ {-.0216473, .0776645},
+ {-.0225161, .0853673}
+ };
+
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->n = 9;
+ P->lam0 = DEG_TO_RAD * -120.;
+ P->phi0 = DEG_TO_RAD * 45.;
+ if (P->es != 0.0) { /* 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);
+}
+
diff --git a/src/projections/moll.cpp b/src/projections/moll.cpp
new file mode 100644
index 00000000..c877a1bb
--- /dev/null
+++ b/src/projections/moll.cpp
@@ -0,0 +1,112 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.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
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double C_x, C_y, C_p;
+};
+} // anonymous namespace
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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.) ? -M_HALFPI : M_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;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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) < M_PI) {
+ lp.phi += lp.phi;
+ lp.phi = aasin(P->ctx, (lp.phi + sin(lp.phi)) / Q->C_p);
+ } else {
+ lp.lam = lp.phi = HUGE_VAL;
+ }
+ return lp;
+}
+
+
+static PJ * setup(PJ *P, double p) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double r, sp, p2 = p + p;
+
+ P->es = 0;
+ sp = sin(p);
+ r = sqrt(M_TWOPI * sp / (p2 + sin(p2)));
+
+ Q->C_x = 2. * r / M_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 = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ return setup(P, M_HALFPI);
+}
+
+
+PJ *PROJECTION(wag4) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ return setup(P, M_PI/3.);
+}
+
+PJ *PROJECTION(wag5) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ 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;
+}
diff --git a/src/projections/natearth.cpp b/src/projections/natearth.cpp
new file mode 100644
index 00000000..27a6b137
--- /dev/null
+++ b/src/projections/natearth.cpp
@@ -0,0 +1,100 @@
+/*
+The Natural Earth projection was designed by Tom Patterson, US National Park
+Service, in 2007, using Flex Projector. The shape of the original projection
+was defined at every 5 degrees and piece-wise cubic spline interpolation was
+used to compute the complete graticule.
+The code here uses polynomial functions instead of cubic splines and
+is therefore much simpler to program. The polynomial approximation was
+developed by Bojan Savric, in collaboration with Tom Patterson and Bernhard
+Jenny, Institute of Cartography, ETH Zurich. It slightly deviates from
+Patterson's original projection by adding additional curvature to meridians
+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 <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(natearth, "Natural Earth") "\n\tPCyl, Sph";
+
+#define A0 0.8707
+#define A1 -0.131979
+#define A2 -0.013791
+#define A3 0.003971
+#define A4 -0.001529
+#define B0 1.007226
+#define B1 0.015085
+#define B2 -0.044475
+#define B3 0.028874
+#define B4 -0.005916
+#define C0 B0
+#define C1 (3 * B1)
+#define C2 (7 * B2)
+#define C3 (9 * B3)
+#define C4 (11 * B4)
+#define EPS 1e-11
+#define MAX_Y (0.8707 * 0.52 * M_PI)
+/* Not sure at all of the appropriate number for MAX_ITER... */
+#define MAX_ITER 100
+
+
+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;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ double yc, tol, y2, y4, f, fder;
+ int i;
+ (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 (i = MAX_ITER; i ; --i) { /* 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;
+ }
+ }
+ if( i == 0 )
+ pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT );
+ lp.phi = yc;
+
+ /* longitude */
+ y2 = yc * yc;
+ lp.lam = xy.x / (A0 + y2 * (A1 + y2 * (A2 + y2 * y2 * y2 * (A3 + y2 * A4))));
+
+ return lp;
+}
+
+
+PJ *PROJECTION(natearth) {
+ P->es = 0;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/natearth2.cpp b/src/projections/natearth2.cpp
new file mode 100644
index 00000000..f6aba671
--- /dev/null
+++ b/src/projections/natearth2.cpp
@@ -0,0 +1,97 @@
+/*
+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,
+and Atmospheric Sciences, Oregon State University.
+Port to PROJ.4 by Bojan Savric, 4 April 2016
+*/
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(natearth2, "Natural Earth 2") "\n\tPCyl, Sph";
+
+#define A0 0.84719
+#define A1 -0.13063
+#define A2 -0.04515
+#define A3 0.05494
+#define A4 -0.02326
+#define A5 0.00331
+#define B0 1.01183
+#define B1 -0.02625
+#define B2 0.01926
+#define B3 -0.00396
+#define C0 B0
+#define C1 (9 * B1)
+#define C2 (11 * B2)
+#define C3 (13 * B3)
+#define EPS 1e-11
+#define MAX_Y (0.84719 * 0.535117535153096 * M_PI)
+/* Not sure at all of the appropriate number for MAX_ITER... */
+#define MAX_ITER 100
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ double phi2, phi4, phi6;
+ (void) P;
+
+ 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;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ double yc, tol, y2, y4, y6, f, fder;
+ int i;
+ (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 (i = MAX_ITER; i ; --i) { /* 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;
+ }
+ }
+ if( i == 0 )
+ pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT );
+ lp.phi = yc;
+
+ /* longitude */
+ 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;
+}
+
+
+PJ *PROJECTION(natearth2) {
+ P->es = 0;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/nell.cpp b/src/projections/nell.cpp
new file mode 100644
index 00000000..2a7ea32c
--- /dev/null
+++ b/src/projections/nell.cpp
@@ -0,0 +1,51 @@
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(nell, "Nell") "\n\tPCyl, Sph";
+
+#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;
+}
+
+
+PJ *PROJECTION(nell) {
+
+ P->es = 0;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/nell_h.cpp b/src/projections/nell_h.cpp
new file mode 100644
index 00000000..28c3ace7
--- /dev/null
+++ b/src/projections/nell_h.cpp
@@ -0,0 +1,53 @@
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(nell_h, "Nell-Hammer") "\n\tPCyl, Sph";
+
+#define NITER 9
+#define EPS 1e-7
+
+
+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;
+}
+
+
+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. ? -M_HALFPI : M_HALFPI;
+ lp.lam = 2. * xy.x;
+ } else
+ lp.lam = 2. * xy.x / (1. + cos(lp.phi));
+
+ return lp;
+}
+
+
+PJ *PROJECTION(nell_h) {
+ P->es = 0.;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/nicol.cpp b/src/projections/nicol.cpp
new file mode 100644
index 00000000..541d08b2
--- /dev/null
+++ b/src/projections/nicol.cpp
@@ -0,0 +1,54 @@
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(nicol, "Nicolosi Globular") "\n\tMisc Sph, no inv";
+
+#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) - M_HALFPI) < EPS) {
+ xy.x = lp.lam * cos(lp.phi);
+ xy.y = M_HALFPI * sin(lp.phi);
+ } else if (fabs(fabs(lp.phi) - M_HALFPI) < EPS) {
+ xy.x = 0;
+ xy.y = lp.phi;
+ } else {
+ double tb, c, d, m, n, r2, sp;
+
+ tb = M_HALFPI / lp.lam - lp.lam / M_HALFPI;
+ c = lp.phi / M_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 = M_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 = M_HALFPI * ( n + (lp.phi < 0. ? xy.y : -xy.y ));
+ }
+ return (xy);
+}
+
+
+PJ *PROJECTION(nicol) {
+ P->es = 0.;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/nsper.cpp b/src/projections/nsper.cpp
new file mode 100644
index 00000000..f93010f8
--- /dev/null
+++ b/src/projections/nsper.cpp
@@ -0,0 +1,202 @@
+#define PJ_LIB__
+#include <errno.h>
+#include "proj.h"
+#include "projects.h"
+#include "proj_math.h"
+
+namespace { // anonymous namespace
+enum Mode {
+ N_POLE = 0,
+ S_POLE = 1,
+ EQUIT = 2,
+ OBLIQ = 3
+};
+} // anonymous namespace
+
+namespace { // anonymous namespace
+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;
+ enum Mode mode;
+ int tilt;
+};
+} // anonymous namespace
+
+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
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ 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;
+ /*-fallthrough*/
+ 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 = static_cast<struct pj_opaque*>(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.) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+ 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 PJ *setup(PJ *P) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+
+ if ((Q->height = pj_param(P->ctx, P->params, "dh").f) <= 0.)
+ return pj_default_destructor(P, PJD_ERR_H_LESS_THAN_ZERO);
+
+ if (fabs(fabs(P->phi0) - M_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;
+}
+
+
+PJ *PROJECTION(nsper) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->tilt = 0;
+
+ return setup(P);
+}
+
+
+PJ *PROJECTION(tpers) {
+ double omega, gamma;
+
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ omega = pj_param(P->ctx, P->params, "rtilt").f;
+ gamma = pj_param(P->ctx, P->params, "razi").f;
+ Q->tilt = 1;
+ Q->cg = cos(gamma); Q->sg = sin(gamma);
+ Q->cw = cos(omega); Q->sw = sin(omega);
+
+ return setup(P);
+}
+
diff --git a/src/projections/nzmg.cpp b/src/projections/nzmg.cpp
new file mode 100644
index 00000000..bf0862fb
--- /dev/null
+++ b/src/projections/nzmg.cpp
@@ -0,0 +1,123 @@
+/******************************************************************************
+ * Project: PROJ.4
+ * Purpose: Implementation of the nzmg (New Zealand Map Grid) projection.
+ * Very loosely based upon DMA code by Bradford W. Drew
+ * Author: Gerald Evenden
+ *
+ ******************************************************************************
+ * Copyright (c) 1995, Gerald Evenden
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************/
+#define PJ_LIB__
+
+#include <math.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 const COMPLEX bf[] = {
+ { .7557853228, 0.0},
+ { .249204646, 0.003371507},
+ {-.001541739, 0.041058560},
+ {-.10162907, 0.01727609},
+ {-.26623489, -0.36249218},
+ {-.6870983, -1.1651967} };
+
+static const double tphi[] = { 1.5627014243, .5185406398, -.03333098,
+ -.1052906, -.0368594, .007317,
+ .01220, .00394, -.0013 };
+
+static const double tpsi[] = { .6399175073, -.1358797613, .063294409, -.02526853, .0117879,
+ -.0055161, .0026906, -.001333, .00067, -.00034 };
+
+#define Nbf 5
+#define Ntpsi 9
+#define Ntphi 8
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ XY xy = {0.0,0.0};
+ COMPLEX p;
+ const 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 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;
+ const double *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;
+}
+
+
+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;
+}
diff --git a/src/projections/ob_tran.cpp b/src/projections/ob_tran.cpp
new file mode 100644
index 00000000..d34059a9
--- /dev/null
+++ b/src/projections/ob_tran.cpp
@@ -0,0 +1,245 @@
+#define PJ_LIB__
+#include <errno.h>
+#include <math.h>
+#include <stddef.h>
+#include <string.h>
+
+#include "proj.h"
+#include "projects.h"
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ struct PJconsts *link;
+ double lamp;
+ double cphip, sphip;
+};
+} // anonymous namespace
+
+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
+
+
+static XY o_forward(LP lp, PJ *P) { /* spheroid */
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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 = static_cast<struct pj_opaque*>(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);
+}
+
+
+static LP o_inverse(XY xy, PJ *P) { /* spheroid */
+
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double coslam, sinphi, cosphi;
+
+ LP 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;
+}
+
+
+static LP t_inverse(XY xy, PJ *P) { /* spheroid */
+
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double cosphi, t;
+
+ LP 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;
+}
+
+
+static PJ *destructor(PJ *P, int errlev) {
+ if (nullptr==P)
+ return nullptr;
+ if (nullptr==P->opaque)
+ return pj_default_destructor (P, errlev);
+
+ if (static_cast<struct pj_opaque*>(P->opaque)->link)
+ static_cast<struct pj_opaque*>(P->opaque)->link->destructor (static_cast<struct pj_opaque*>(P->opaque)->link, errlev);
+
+ return pj_default_destructor(P, errlev);
+}
+
+
+
+
+/***********************************************************************
+
+These functions are modified versions of the functions "argc_params"
+and "argv_params" from PJ_pipeline.c
+
+Basically, they do the somewhat backwards stunt of turning the paralist
+representation of the +args back into the original +argv, +argc
+representation accepted by pj_init_ctx().
+
+This, however, also begs the question of whether we really need the
+paralist linked list representation, or if we could do with a simpler
+null-terminated argv style array? This would simplfy some code, and
+keep memory allocations more localized.
+
+***********************************************************************/
+
+typedef struct {int argc; char **argv;} ARGS;
+
+/* count the number of args in the linked list <params> */
+static size_t paralist_params_argc (paralist *params) {
+ size_t argc = 0;
+ for (; params != nullptr; params = params->next)
+ argc++;
+ return argc;
+}
+
+
+/* turn paralist into argc/argv style argument list */
+static ARGS ob_tran_target_params (paralist *params) {
+ int i = 0;
+ ARGS args = {0, nullptr};
+ size_t argc = paralist_params_argc (params);
+ if (argc < 2)
+ return args;
+
+ /* all args except the proj_ob_tran */
+ args.argv = static_cast<char**>(pj_calloc (argc - 1, sizeof (char *)));
+ if (nullptr==args.argv)
+ return args;
+
+ /* Copy all args *except* the proj=ob_tran arg to the argv array */
+ for (i = 0; params != nullptr; params = params->next) {
+ if (0==strcmp (params->param, "proj=ob_tran"))
+ continue;
+ args.argv[i++] = params->param;
+ }
+ args.argc = i;
+
+ /* Then convert the o_proj=xxx element to proj=xxx */
+ for (i = 0; i < args.argc; i++) {
+ if (0!=strncmp (args.argv[i], "o_proj=", 7))
+ continue;
+ args.argv[i] += 2;
+ break;
+ }
+
+ return args;
+}
+
+
+
+PJ *PROJECTION(ob_tran) {
+ double phip;
+ char *name;
+ ARGS args;
+ PJ *R; /* projection to rotate */
+
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return destructor(P, ENOMEM);
+
+ P->opaque = Q;
+ P->destructor = destructor;
+
+ /* get name of projection to be translated */
+ if (!(name = pj_param(P->ctx, P->params, "so_proj").s))
+ return destructor(P, PJD_ERR_NO_ROTATION_PROJ);
+
+ /* avoid endless recursion */
+ if( strcmp(name, "ob_tran") == 0 )
+ return destructor(P, PJD_ERR_FAILED_TO_FIND_PROJ);
+
+ /* Create the target projection object to rotate */
+ args = ob_tran_target_params (P->params);
+ R = pj_init_ctx (pj_get_ctx(P), args.argc, args.argv);
+ pj_dealloc (args.argv);
+
+ if (nullptr==R)
+ return destructor (P, PJD_ERR_UNKNOWN_PROJECTION_ID);
+ Q->link = R;
+
+ 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(fabs(phic) - M_HALFPI) <= TOL)
+ return destructor(P, PJD_ERR_LAT_0_OR_ALPHA_EQ_90);
+
+ 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 - M_HALFPI) <= TOL || fabs(fabs(phi2) - M_HALFPI) <= TOL)
+ return destructor(P, PJD_ERR_LAT_1_OR_2_ZERO_OR_90);
+
+ 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 = Q->link->fwd ? o_forward : nullptr;
+ P->inv = Q->link->inv ? o_inverse : nullptr;
+ } else { /* transverse */
+ P->fwd = Q->link->fwd ? t_forward : nullptr;
+ P->inv = Q->link->inv ? t_inverse : nullptr;
+ }
+
+ /* Support some rather speculative test cases, where the rotated projection */
+ /* is actually latlong. We do not want scaling in that case... */
+ if (Q->link->right==PJ_IO_UNITS_ANGULAR)
+ P->right = PJ_IO_UNITS_PROJECTED;
+
+
+ return P;
+}
diff --git a/src/projections/ocea.cpp b/src/projections/ocea.cpp
new file mode 100644
index 00000000..0576ace7
--- /dev/null
+++ b/src/projections/ocea.cpp
@@ -0,0 +1,102 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.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=";
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double rok;
+ double rtk;
+ double sinphi;
+ double cosphi;
+ double singam;
+ double cosgam;
+};
+} // anonymous namespace
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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 += M_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 = static_cast<struct pj_opaque*>(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;
+}
+
+
+PJ *PROJECTION(ocea) {
+ double phi_0=0.0, phi_1, phi_2, lam_1, lam_2, lonz, alpha;
+
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->rok = 1. / P->k0;
+ Q->rtk = 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) );
+
+ /* take care of P->lam0 wrap-around when +lam_1=-90*/
+ if (lam_1 == -M_HALFPI)
+ Q->singam = -Q->singam;
+
+ /*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 + M_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;
+}
diff --git a/src/projections/oea.cpp b/src/projections/oea.cpp
new file mode 100644
index 00000000..0c401b2f
--- /dev/null
+++ b/src/projections/oea.cpp
@@ -0,0 +1,87 @@
+#define PJ_LIB__
+#include <errno.h>
+#include "proj.h"
+#include "projects.h"
+#include "proj_math.h"
+
+PROJ_HEAD(oea, "Oblated Equal Area") "\n\tMisc Sph\n\tn= m= theta=";
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double theta;
+ double m, n;
+ double two_r_m, two_r_n, rm, rn, hm, hn;
+ double cp0, sp0;
+};
+} // anonymous namespace
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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), 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 * 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;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double N, M, xp, yp, z, Az, cz, sz, cAz;
+
+ 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 * 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, Q->sp0 * cz + Q->cp0 * sz * cAz);
+ lp.lam = aatan2(sz * sin(Az),
+ Q->cp0 * cz - Q->sp0 * sz * cAz);
+
+ return lp;
+}
+
+
+
+
+PJ *PROJECTION(oea) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ if (((Q->n = pj_param(P->ctx, P->params, "dn").f) <= 0.) ||
+ ((Q->m = pj_param(P->ctx, P->params, "dm").f) <= 0.)) {
+ return pj_default_destructor(P, PJD_ERR_INVALID_M_OR_N);
+ } else {
+ 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.;
+ }
+
+ return P;
+}
+
diff --git a/src/projections/omerc.cpp b/src/projections/omerc.cpp
new file mode 100644
index 00000000..ead07128
--- /dev/null
+++ b/src/projections/omerc.cpp
@@ -0,0 +1,229 @@
+/*
+** Copyright (c) 2003, 2006 Gerald I. Evenden
+*/
+/*
+** Permission is hereby granted, free of charge, to any person obtaining
+** a copy of this software and associated documentation files (the
+** "Software"), to deal in the Software without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Software, and to
+** permit persons to whom the Software is furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be
+** included in all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#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=";
+
+namespace { // anonymous namespace
+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;
+};
+} // anonymous namespace
+
+#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 = static_cast<struct pj_opaque*>(P->opaque);
+ double S, T, U, V, W, temp, u, v;
+
+ if (fabs(fabs(lp.phi) - M_HALFPI) > EPS) {
+ W = Q->E / pow(pj_tsfn(lp.phi, sin(lp.phi), P->e), Q->B);
+ temp = 1. / W;
+ S = .5 * (W - temp);
+ T = .5 * (W + temp);
+ V = sin(Q->B * lp.lam);
+ U = (S * Q->singam - V * Q->cosgam) / T;
+ if (fabs(fabs(U) - 1.0) < EPS) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ v = 0.5 * Q->ArB * log((1. - U)/(1. + U));
+ temp = cos(Q->B * lp.lam);
+ if(fabs(temp) < TOL) {
+ u = Q->A * lp.lam;
+ } else {
+ u = Q->ArB * atan2((S * Q->cosgam + V * Q->singam), temp);
+ }
+ } 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 = static_cast<struct pj_opaque*>(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. ? -M_HALFPI : M_HALFPI;
+ } else {
+ lp.phi = Q->E / sqrt((1. + Up) / (1. - Up));
+ if ((lp.phi = pj_phi2(P->ctx, pow(lp.phi, 1. / Q->B), P->e)) == HUGE_VAL) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+ lp.lam = - Q->rB * atan2((Sp * Q->cosgam -
+ Vp * Q->singam), cos(Q->BrA * u));
+ }
+ return lp;
+}
+
+
+PJ *PROJECTION(omerc) {
+ double con, com, cosph0, D, F, H, L, sinph0, p, J, gamma=0,
+ gamma0, lamc=0, lam1=0, lam2=0, phi1=0, phi2=0, alpha_c=0;
+ int alp, gam, no_off = 0;
+
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->no_rot = pj_param(P->ctx, P->params, "bno_rot").i;
+ if ((alp = pj_param(P->ctx, P->params, "talpha").i) != 0)
+ 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 =
+ /* For libproj4 compatibility */
+ pj_param(P->ctx, P->params, "tno_off").i
+ /* for backward compatibility */
+ || pj_param(P->ctx, P->params, "tno_uoff").i;
+ if( no_off )
+ {
+ /* Mark the parameter as used, so that the pj_get_def() return them */
+ 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 - M_HALFPI) <= TOL ||
+ fabs(fabs(P->phi0) - M_HALFPI) <= TOL ||
+ fabs(fabs(phi2) - M_HALFPI) <= TOL)
+ return pj_default_destructor(P, PJD_ERR_LAT_0_OR_ALPHA_EQ_90);
+ }
+ com = sqrt(P->one_es);
+ if (fabs(P->phi0) > EPS) {
+ sinph0 = sin(P->phi0);
+ cosph0 = cos(P->phi0);
+ con = 1. - P->es * sinph0 * sinph0;
+ Q->B = cosph0 * cosph0;
+ Q->B = sqrt(1. + P->es * Q->B * Q->B / P->one_es);
+ Q->A = Q->B * P->k0 * com / con;
+ D = Q->B * com / (cosph0 * sqrt(con));
+ if ((F = D * D - 1.) <= 0.)
+ F = 0.;
+ else {
+ F = sqrt(F);
+ if (P->phi0 < 0.)
+ F = -F;
+ }
+ Q->E = F += D;
+ Q->E *= pow(pj_tsfn(P->phi0, sinph0, P->e), Q->B);
+ } else {
+ Q->B = 1. / com;
+ Q->A = P->k0;
+ Q->E = D = F = 1.;
+ }
+ if (alp || gam) {
+ if (alp) {
+ gamma0 = aasin(P->ctx, sin(alpha_c) / D);
+ if (!gam)
+ gamma = alpha_c;
+ } else
+ alpha_c = aasin(P->ctx, D*sin(gamma0 = gamma));
+ P->lam0 = lamc - aasin(P->ctx, .5 * (F - 1. / F) *
+ tan(gamma0)) / Q->B;
+ } else {
+ H = pow(pj_tsfn(phi1, sin(phi1), P->e), Q->B);
+ L = pow(pj_tsfn(phi2, sin(phi2), P->e), Q->B);
+ F = Q->E / H;
+ p = (L - H) / (L + H);
+ J = Q->E * Q->E;
+ J = (J - L * H) / (J + L * H);
+ if ((con = lam1 - lam2) < -M_PI)
+ lam2 -= M_TWOPI;
+ else if (con > M_PI)
+ lam2 += M_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 = aasin(P->ctx, 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 * atan(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(M_FORTPI - F));
+ Q->v_pole_s = Q->ArB * log(tan(M_FORTPI + F));
+ P->inv = e_inverse;
+ P->fwd = e_forward;
+
+ return P;
+}
diff --git a/src/projections/ortho.cpp b/src/projections/ortho.cpp
new file mode 100644
index 00000000..6ea55248
--- /dev/null
+++ b/src/projections/ortho.cpp
@@ -0,0 +1,143 @@
+#define PJ_LIB__
+#include <errno.h>
+#include "proj.h"
+#include "proj_internal.h"
+#include "proj_math.h"
+#include "projects.h"
+
+PROJ_HEAD(ortho, "Orthographic") "\n\tAzi, Sph";
+
+namespace { // anonymous namespace
+enum Mode {
+ N_POLE = 0,
+ S_POLE = 1,
+ EQUIT = 2,
+ OBLIQ = 3
+};
+} // anonymous namespace
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double sinph0;
+ double cosph0;
+ enum Mode mode;
+};
+} // anonymous namespace
+
+#define EPS10 1.e-10
+
+static XY forward_error(PJ *P, LP lp, XY xy) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ proj_log_trace(P, "Coordinate (%.3f, %.3f) is on the unprojected hemisphere",
+ proj_todeg(lp.lam), proj_todeg(lp.phi));
+ return xy;
+}
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double coslam, cosphi, sinphi;
+
+ xy.x = HUGE_VAL; xy.y = HUGE_VAL;
+
+ cosphi = cos(lp.phi);
+ coslam = cos(lp.lam);
+ switch (Q->mode) {
+ case EQUIT:
+ if (cosphi * coslam < - EPS10)
+ return forward_error(P, lp, xy);
+ xy.y = sin(lp.phi);
+ break;
+ case OBLIQ:
+ if (Q->sinph0 * (sinphi = sin(lp.phi)) + Q->cosph0 * cosphi * coslam < - EPS10)
+ return forward_error(P, lp, xy);
+ xy.y = Q->cosph0 * sinphi - Q->sinph0 * cosphi * coslam;
+ break;
+ case N_POLE:
+ coslam = - coslam;
+ /*-fallthrough*/
+ case S_POLE:
+ if (fabs(lp.phi - P->phi0) - EPS10 > M_HALFPI)
+ return forward_error(P, lp, xy);
+ xy.y = cosphi * coslam;
+ break;
+ }
+ xy.x = cosphi * sin(lp.lam);
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double rh, cosc, sinc;
+
+ lp.lam = HUGE_VAL; lp.phi = HUGE_VAL;
+
+ if ((sinc = (rh = hypot(xy.x, xy.y))) > 1.) {
+ if ((sinc - 1.) > EPS10) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ proj_log_trace(P, "Point (%.3f, %.3f) is outside the projection boundary");
+ return lp;
+ }
+ sinc = 1.;
+ }
+ cosc = sqrt(1. - sinc * sinc); /* in this range OK */
+ if (fabs(rh) <= EPS10) {
+ lp.phi = P->phi0;
+ lp.lam = 0.0;
+ } else {
+ switch (Q->mode) {
+ case N_POLE:
+ xy.y = -xy.y;
+ lp.phi = acos(sinc);
+ break;
+ case S_POLE:
+ lp.phi = - acos(sinc);
+ break;
+ case EQUIT:
+ lp.phi = xy.y * sinc / rh;
+ xy.x *= sinc;
+ xy.y = cosc * rh;
+ goto sinchk;
+ case OBLIQ:
+ 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. ? -M_HALFPI : M_HALFPI;
+ else
+ lp.phi = asin(lp.phi);
+ break;
+ }
+ lp.lam = (xy.y == 0. && (Q->mode == OBLIQ || Q->mode == EQUIT))
+ ? (xy.x == 0. ? 0. : xy.x < 0. ? -M_HALFPI : M_HALFPI)
+ : atan2(xy.x, xy.y);
+ }
+ return lp;
+}
+
+
+
+PJ *PROJECTION(ortho) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor(P, ENOMEM);
+ P->opaque = Q;
+
+ if (fabs(fabs(P->phi0) - M_HALFPI) <= EPS10)
+ Q->mode = P->phi0 < 0. ? S_POLE : N_POLE;
+ else if (fabs(P->phi0) > EPS10) {
+ Q->mode = OBLIQ;
+ Q->sinph0 = sin(P->phi0);
+ Q->cosph0 = cos(P->phi0);
+ } else
+ Q->mode = EQUIT;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ P->es = 0.;
+
+ return P;
+}
+
diff --git a/src/projections/patterson.cpp b/src/projections/patterson.cpp
new file mode 100644
index 00000000..0d19414e
--- /dev/null
+++ b/src/projections/patterson.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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
+ * https://doi.org/10.14714/CP78.1270
+ *
+ * Port to PROJ.4 by Micah Cochran, 26 March 2016
+ */
+
+#define PJ_LIB__
+
+#include <math.h>
+
+#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
+/* Not sure at all of the appropriate number for MAX_ITER... */
+#define MAX_ITER 100
+
+
+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;
+ int i;
+ (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 (i = MAX_ITER; i ; --i) { /* 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;
+ }
+ }
+ if( i == 0 )
+ pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT );
+ lp.phi = yc;
+
+ /* longitude */
+ lp.lam = xy.x;
+
+ return lp;
+}
+
+
+PJ *PROJECTION(patterson) {
+ P->es = 0.;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/poly.cpp b/src/projections/poly.cpp
new file mode 100644
index 00000000..a970fdb1
--- /dev/null
+++ b/src/projections/poly.cpp
@@ -0,0 +1,171 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(poly, "Polyconic (American)")
+ "\n\tConic, Sph&Ell";
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double ml0; \
+ double *en;
+};
+} // anonymous namespace
+
+#define TOL 1e-10
+#define CONV 1e-10
+#define N_ITER 10
+#define I_ITER 20
+#define ITOL 1.e-12
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double ms, sp, cp;
+
+ 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, Q->en) - Q->ml0) + ms * (1. - cos(lp.lam));
+ }
+
+ return xy;
+}
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double cot, E;
+
+ 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;
+}
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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;
+
+ r = xy.y * xy.y + xy.x * xy.x;
+ lp.phi = xy.y;
+ for (i = I_ITER; i ; --i) {
+ sp = sin(lp.phi);
+ s2ph = sp * ( cp = cos(lp.phi));
+ if (fabs(cp) < ITOL) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+ c = sp * (mlp = sqrt(1. - P->es * sp * sp)) / cp;
+ ml = pj_mlfn(lp.phi, sp, cp, Q->en);
+ mlb = ml * ml + r;
+ mlp = P->one_es / (mlp * mlp * mlp);
+ lp.phi += ( dPhi =
+ ( ml + ml + c * mlb - 2. * xy.y * (c * ml + 1.) ) / (
+ P->es * s2ph * (mlb - 2. * xy.y * ml) / c +
+ 2.* (xy.y - ml) * (c * mlp - 1. / s2ph) - mlp - mlp ));
+ if (fabs(dPhi) <= ITOL)
+ break;
+ }
+ if (!i) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+ c = sin(lp.phi);
+ lp.lam = asin(xy.x * tan(lp.phi) * sqrt(1. - P->es * c * c)) / sin(lp.phi);
+ }
+
+ return lp;
+}
+
+
+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 {
+ lp.phi = xy.y;
+ B = xy.x * xy.x + xy.y * xy.y;
+ i = N_ITER;
+ do {
+ tp = tan(lp.phi);
+ lp.phi -= (dphi = (xy.y * (lp.phi * tp + 1.) - lp.phi -
+ .5 * ( lp.phi * lp.phi + B) * tp) /
+ ((lp.phi - xy.y) / tp - 1.));
+ } while (fabs(dphi) > CONV && --i);
+ if (! i) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+ lp.lam = asin(xy.x * tan(lp.phi)) / sin(lp.phi);
+ }
+
+ return lp;
+}
+
+
+static PJ *destructor(PJ *P, int errlev) {
+ if (nullptr==P)
+ return nullptr;
+
+ if (nullptr==P->opaque)
+ return pj_default_destructor (P, errlev);
+
+ if (static_cast<struct pj_opaque*>(P->opaque)->en)
+ pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->en);
+
+ return pj_default_destructor(P, errlev);
+}
+
+
+PJ *PROJECTION(poly) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+
+ P->opaque = Q;
+ P->destructor = destructor;
+
+ if (P->es != 0.0) {
+ if (!(Q->en = pj_enfn(P->es)))
+ return pj_default_destructor (P, ENOMEM);
+ Q->ml0 = pj_mlfn(P->phi0, sin(P->phi0), cos(P->phi0), Q->en);
+ P->inv = e_inverse;
+ P->fwd = e_forward;
+ } else {
+ Q->ml0 = -P->phi0;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ }
+
+ return P;
+}
diff --git a/src/projections/putp2.cpp b/src/projections/putp2.cpp
new file mode 100644
index 00000000..d7a847c8
--- /dev/null
+++ b/src/projections/putp2.cpp
@@ -0,0 +1,61 @@
+#define PJ_LIB__
+
+#include <math.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
+
+
+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;
+
+ p = C_p * sin(lp.phi);
+ s = lp.phi * lp.phi;
+ lp.phi *= 0.615709 + s * ( 0.00909953 + s * 0.0046292 );
+ for (i = NITER; i ; --i) {
+ c = cos(lp.phi);
+ s = sin(lp.phi);
+ lp.phi -= V = (lp.phi + s * (c - 1.) - p) /
+ (1. + c * (c - 1.) - s * s);
+ if (fabs(V) < EPS)
+ break;
+ }
+ if (!i)
+ 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;
+}
+
+
+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;
+}
+
+
+PJ *PROJECTION(putp2) {
+ P->es = 0.;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/putp3.cpp b/src/projections/putp3.cpp
new file mode 100644
index 00000000..98bb2ff0
--- /dev/null
+++ b/src/projections/putp3.cpp
@@ -0,0 +1,67 @@
+#define PJ_LIB__
+#include <errno.h>
+#include "projects.h"
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double A;
+};
+} // anonymous namespace
+
+PROJ_HEAD(putp3, "Putnins P3") "\n\tPCyl, Sph";
+PROJ_HEAD(putp3p, "Putnins P3'") "\n\tPCyl, Sph";
+
+#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. - static_cast<struct pj_opaque*>(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. - static_cast<struct pj_opaque*>(P->opaque)->A * lp.phi * lp.phi));
+
+ return lp;
+}
+
+
+PJ *PROJECTION(putp3) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ 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 = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->A = 2. * RPISQ;
+
+ P->es = 0.;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
+
diff --git a/src/projections/putp4p.cpp b/src/projections/putp4p.cpp
new file mode 100644
index 00000000..608fc76e
--- /dev/null
+++ b/src/projections/putp4p.cpp
@@ -0,0 +1,76 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "projects.h"
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double C_x, C_y;
+};
+} // anonymous namespace
+
+PROJ_HEAD(putp4p, "Putnins P4'") "\n\tPCyl, Sph";
+PROJ_HEAD(weren, "Werenskiold I") "\n\tPCyl, Sph";
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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 = static_cast<struct pj_opaque*>(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;
+}
+
+
+PJ *PROJECTION(putp4p) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ 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 = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ 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;
+}
diff --git a/src/projections/putp5.cpp b/src/projections/putp5.cpp
new file mode 100644
index 00000000..79e2ad15
--- /dev/null
+++ b/src/projections/putp5.cpp
@@ -0,0 +1,75 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "projects.h"
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double A, B;
+};
+} // anonymous namespace
+
+PROJ_HEAD(putp5, "Putnins P5") "\n\tPCyl, Sph";
+PROJ_HEAD(putp5p, "Putnins P5'") "\n\tPCyl, Sph";
+
+#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 = static_cast<struct pj_opaque*>(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 = static_cast<struct pj_opaque*>(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;
+}
+
+
+
+PJ *PROJECTION(putp5) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ 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 = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->A = 1.5;
+ Q->B = 0.5;
+
+ P->es = 0.;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/putp6.cpp b/src/projections/putp6.cpp
new file mode 100644
index 00000000..1186b18b
--- /dev/null
+++ b/src/projections/putp6.cpp
@@ -0,0 +1,97 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "projects.h"
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double C_x, C_y, A, B, D;
+};
+} // anonymous namespace
+
+PROJ_HEAD(putp6, "Putnins P6") "\n\tPCyl, Sph";
+PROJ_HEAD(putp6p, "Putnins P6'") "\n\tPCyl, Sph";
+
+#define EPS 1e-10
+#define NITER 10
+#define CON_POLE 1.732050807568877
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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 = static_cast<struct pj_opaque*>(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;
+}
+
+
+PJ *PROJECTION(putp6) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor(P, ENOMEM);
+ 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 = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor(P, ENOMEM);
+ 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;
+}
diff --git a/src/projections/qsc.cpp b/src/projections/qsc.cpp
new file mode 100644
index 00000000..b50a7c95
--- /dev/null
+++ b/src/projections/qsc.cpp
@@ -0,0 +1,408 @@
+/*
+ * This implements the Quadrilateralized Spherical Cube (QSC) projection.
+ *
+ * Copyright (c) 2011, 2012 Martin Lambers <marlam@marlam.de>
+ *
+ * The QSC projection was introduced in:
+ * [OL76]
+ * E.M. O'Neill and R.E. Laubscher, "Extended Studies of a Quadrilateralized
+ * Spherical Cube Earth Data Base", Naval Environmental Prediction Research
+ * Facility Tech. Report NEPRF 3-76 (CSC), May 1976.
+ *
+ * The preceding shift from an ellipsoid to a sphere, which allows to apply
+ * this projection to ellipsoids as used in the Ellipsoidal Cube Map model,
+ * is described in
+ * [LK12]
+ * M. Lambers and A. Kolb, "Ellipsoidal Cube Maps for Accurate Rendering of
+ * Planetary-Scale Terrain Data", Proc. Pacific Graphics (Short Papers), Sep.
+ * 2012
+ *
+ * You have to choose one of the following projection centers,
+ * corresponding to the centers of the six cube faces:
+ * phi0 = 0.0, lam0 = 0.0 ("front" face)
+ * phi0 = 0.0, lam0 = 90.0 ("right" face)
+ * phi0 = 0.0, lam0 = 180.0 ("back" face)
+ * phi0 = 0.0, lam0 = -90.0 ("left" face)
+ * phi0 = 90.0 ("top" face)
+ * phi0 = -90.0 ("bottom" face)
+ * 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 PROJECTION(qsc) function
+ * and the handling of different face values (FACE_*) in the forward and
+ * inverse projections.
+ *
+ * Furthermore, the projection is originally only defined for theta angles
+ * between (-1/4 * PI) and (+1/4 * PI) on the current cube face. This area
+ * of definition is named AREA_0 in the projection code below. The other
+ * three areas of a cube face are handled by rotation of AREA_0.
+ */
+
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "projects.h"
+
+/* The six cube faces. */
+namespace { // anonymous namespace
+enum Face {
+ FACE_FRONT = 0,
+ FACE_RIGHT = 1,
+ FACE_BACK = 2,
+ FACE_LEFT = 3,
+ FACE_TOP = 4,
+ FACE_BOTTOM = 5
+};
+} // anonymous namespace
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ enum Face face;
+ double a_squared;
+ double b;
+ double one_minus_f;
+ double one_minus_f_squared;
+};
+} // anonymous namespace
+PROJ_HEAD(qsc, "Quadrilateralized Spherical Cube") "\n\tAzi, Sph";
+
+#define EPS10 1.e-10
+
+/* The four areas on a cube face. AREA_0 is the area of definition,
+ * the other three areas are counted counterclockwise. */
+namespace { // anonymous namespace
+enum Area {
+ AREA_0 = 0,
+ AREA_1 = 1,
+ AREA_2 = 2,
+ AREA_3 = 3
+};
+} // anonymous namespace
+
+/* 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, enum Area *area) {
+ double theta;
+ if (phi < EPS10) {
+ *area = AREA_0;
+ theta = 0.0;
+ } else {
+ theta = atan2(y, x);
+ if (fabs(theta) <= M_FORTPI) {
+ *area = AREA_0;
+ } else if (theta > M_FORTPI && theta <= M_HALFPI + M_FORTPI) {
+ *area = AREA_1;
+ theta -= M_HALFPI;
+ } else if (theta > M_HALFPI + M_FORTPI || theta <= -(M_HALFPI + M_FORTPI)) {
+ *area = AREA_2;
+ theta = (theta >= 0.0 ? theta - M_PI : theta + M_PI);
+ } else {
+ *area = AREA_3;
+ theta += M_HALFPI;
+ }
+ }
+ return theta;
+}
+
+/* Helper function: shift the longitude. */
+static double qsc_shift_lon_origin(double lon, double offset) {
+ double slon = lon + offset;
+ if (slon < -M_PI) {
+ slon += M_TWOPI;
+ } else if (slon > +M_PI) {
+ slon -= M_TWOPI;
+ }
+ return slon;
+}
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double lat, lon;
+ double theta, phi;
+ double t, mu; /* nu; */
+ enum Area 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 != 0.0) {
+ lat = atan(Q->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 (Q->face == FACE_TOP) {
+ phi = M_HALFPI - lat;
+ if (lon >= M_FORTPI && lon <= M_HALFPI + M_FORTPI) {
+ area = AREA_0;
+ theta = lon - M_HALFPI;
+ } else if (lon > M_HALFPI + M_FORTPI || lon <= -(M_HALFPI + M_FORTPI)) {
+ area = AREA_1;
+ theta = (lon > 0.0 ? lon - M_PI : lon + M_PI);
+ } else if (lon > -(M_HALFPI + M_FORTPI) && lon <= -M_FORTPI) {
+ area = AREA_2;
+ theta = lon + M_HALFPI;
+ } else {
+ area = AREA_3;
+ theta = lon;
+ }
+ } else if (Q->face == FACE_BOTTOM) {
+ phi = M_HALFPI + lat;
+ if (lon >= M_FORTPI && lon <= M_HALFPI + M_FORTPI) {
+ area = AREA_0;
+ theta = -lon + M_HALFPI;
+ } else if (lon < M_FORTPI && lon >= -M_FORTPI) {
+ area = AREA_1;
+ theta = -lon;
+ } else if (lon < -M_FORTPI && lon >= -(M_HALFPI + M_FORTPI)) {
+ area = AREA_2;
+ theta = -lon - M_HALFPI;
+ } else {
+ area = AREA_3;
+ theta = (lon > 0.0 ? -lon + M_PI : -lon - M_PI);
+ }
+ } else {
+ double q, r, s;
+ double sinlat, coslat;
+ double sinlon, coslon;
+
+ if (Q->face == FACE_RIGHT) {
+ lon = qsc_shift_lon_origin(lon, +M_HALFPI);
+ } else if (Q->face == FACE_BACK) {
+ lon = qsc_shift_lon_origin(lon, +M_PI);
+ } else if (Q->face == FACE_LEFT) {
+ lon = qsc_shift_lon_origin(lon, -M_HALFPI);
+ }
+ sinlat = sin(lat);
+ coslat = cos(lat);
+ sinlon = sin(lon);
+ coslon = cos(lon);
+ q = coslat * coslon;
+ r = coslat * sinlon;
+ s = sinlat;
+
+ 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 {
+ /* Impossible */
+ phi = theta = 0.0;
+ area = AREA_0;
+ }
+ }
+
+ /* 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 / M_PI) * (theta + acos(sin(theta) * cos(M_FORTPI)) - M_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 += M_HALFPI;
+ } else if (area == AREA_2) {
+ mu += M_PI;
+ } else if (area == AREA_3) {
+ mu += M_PI_HALFPI;
+ }
+
+ /* Now compute x, y from mu and nu */
+ /* t = tan(nu); */
+ xy.x = t * cos(mu);
+ xy.y = t * sin(mu);
+ return xy;
+}
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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 -= M_HALFPI;
+ } else if (xy.x < 0.0 && -xy.x >= fabs(xy.y)) {
+ area = AREA_2;
+ mu = (mu < 0.0 ? mu + M_PI : mu - M_PI);
+ } else {
+ area = AREA_3;
+ mu += M_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 = (M_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 = M_HALFPI - phi;
+ if (area == AREA_0) {
+ lp.lam = theta + M_HALFPI;
+ } else if (area == AREA_1) {
+ lp.lam = (theta < 0.0 ? theta + M_PI : theta - M_PI);
+ } else if (area == AREA_2) {
+ lp.lam = theta - M_HALFPI;
+ } else /* area == AREA_3 */ {
+ lp.lam = theta;
+ }
+ } else if (Q->face == FACE_BOTTOM) {
+ phi = acos(cosphi);
+ lp.phi = phi - M_HALFPI;
+ if (area == AREA_0) {
+ lp.lam = -theta + M_HALFPI;
+ } else if (area == AREA_1) {
+ lp.lam = -theta;
+ } else if (area == AREA_2) {
+ lp.lam = -theta - M_HALFPI;
+ } else /* area == AREA_3 */ {
+ lp.lam = (theta < 0.0 ? -theta - M_PI : -theta + M_PI);
+ }
+ } else {
+ /* Compute phi and lam via cartesian unit sphere coordinates. */
+ double q, r, s;
+ 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 (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) - M_HALFPI;
+ lp.lam = atan2(r, q);
+ if (Q->face == FACE_RIGHT) {
+ lp.lam = qsc_shift_lon_origin(lp.lam, -M_HALFPI);
+ } else if (Q->face == FACE_BACK) {
+ lp.lam = qsc_shift_lon_origin(lp.lam, -M_PI);
+ } else if (Q->face == FACE_LEFT) {
+ lp.lam = qsc_shift_lon_origin(lp.lam, +M_HALFPI);
+ }
+ }
+
+ /* Apply the shift from the sphere to the ellipsoid as described
+ * in [LK12]. */
+ if (P->es != 0.0) {
+ 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;
+}
+
+
+PJ *PROJECTION(qsc) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ P->inv = e_inverse;
+ P->fwd = e_forward;
+ /* Determine the cube face from the center of projection. */
+ if (P->phi0 >= M_HALFPI - M_FORTPI / 2.0) {
+ Q->face = FACE_TOP;
+ } else if (P->phi0 <= -(M_HALFPI - M_FORTPI / 2.0)) {
+ Q->face = FACE_BOTTOM;
+ } else if (fabs(P->lam0) <= M_FORTPI) {
+ Q->face = FACE_FRONT;
+ } else if (fabs(P->lam0) <= M_HALFPI + M_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 != 0.0) {
+ 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;
+}
diff --git a/src/projections/robin.cpp b/src/projections/robin.cpp
new file mode 100644
index 00000000..987977ae
--- /dev/null
+++ b/src/projections/robin.cpp
@@ -0,0 +1,161 @@
+#define PJ_LIB__
+#include "proj_math.h"
+#include "proj_internal.h"
+#include "proj.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:
+
+http://article.gmane.org/gmane.comp.gis.proj-4.devel/6039
+http://trac.osgeo.org/proj/ticket/113
+*/
+
+namespace { // anonymous namespace
+struct COEFS {
+ float c0, c1, c2, c3;
+};
+} // anonymous namespace
+
+static const struct COEFS X[] = {
+ {1.0f, 2.2199e-17f, -7.15515e-05f, 3.1103e-06f},
+ {0.9986f, -0.000482243f, -2.4897e-05f, -1.3309e-06f},
+ {0.9954f, -0.00083103f, -4.48605e-05f, -9.86701e-07f},
+ {0.99f, -0.00135364f, -5.9661e-05f, 3.6777e-06f},
+ {0.9822f, -0.00167442f, -4.49547e-06f, -5.72411e-06f},
+ {0.973f, -0.00214868f, -9.03571e-05f, 1.8736e-08f},
+ {0.96f, -0.00305085f, -9.00761e-05f, 1.64917e-06f},
+ {0.9427f, -0.00382792f, -6.53386e-05f, -2.6154e-06f},
+ {0.9216f, -0.00467746f, -0.00010457f, 4.81243e-06f},
+ {0.8962f, -0.00536223f, -3.23831e-05f, -5.43432e-06f},
+ {0.8679f, -0.00609363f, -0.000113898f, 3.32484e-06f},
+ {0.835f, -0.00698325f, -6.40253e-05f, 9.34959e-07f},
+ {0.7986f, -0.00755338f, -5.00009e-05f, 9.35324e-07f},
+ {0.7597f, -0.00798324f, -3.5971e-05f, -2.27626e-06f},
+ {0.7186f, -0.00851367f, -7.01149e-05f, -8.6303e-06f},
+ {0.6732f, -0.00986209f, -0.000199569f, 1.91974e-05f},
+ {0.6213f, -0.010418f, 8.83923e-05f, 6.24051e-06f},
+ {0.5722f, -0.00906601f, 0.000182f, 6.24051e-06f},
+ {0.5322f, -0.00677797f, 0.000275608f, 6.24051e-06f}
+};
+
+static const struct COEFS Y[] = {
+ {-5.20417e-18f, 0.0124f, 1.21431e-18f, -8.45284e-11f},
+ {0.062f, 0.0124f, -1.26793e-09f, 4.22642e-10f},
+ {0.124f, 0.0124f, 5.07171e-09f, -1.60604e-09f},
+ {0.186f, 0.0123999f, -1.90189e-08f, 6.00152e-09f},
+ {0.248f, 0.0124002f, 7.10039e-08f, -2.24e-08f},
+ {0.31f, 0.0123992f, -2.64997e-07f, 8.35986e-08f},
+ {0.372f, 0.0124029f, 9.88983e-07f, -3.11994e-07f},
+ {0.434f, 0.0123893f, -3.69093e-06f, -4.35621e-07f},
+ {0.4958f, 0.0123198f, -1.02252e-05f, -3.45523e-07f},
+ {0.5571f, 0.0121916f, -1.54081e-05f, -5.82288e-07f},
+ {0.6176f, 0.0119938f, -2.41424e-05f, -5.25327e-07f},
+ {0.6769f, 0.011713f, -3.20223e-05f, -5.16405e-07f},
+ {0.7346f, 0.0113541f, -3.97684e-05f, -6.09052e-07f},
+ {0.7903f, 0.0109107f, -4.89042e-05f, -1.04739e-06f},
+ {0.8435f, 0.0103431f, -6.4615e-05f, -1.40374e-09f},
+ {0.8936f, 0.00969686f, -6.4636e-05f, -8.547e-06f},
+ {0.9394f, 0.00840947f, -0.000192841f, -4.2106e-06f},
+ {0.9761f, 0.00616527f, -0.000256f, -4.2106e-06f},
+ {1.0f, 0.00328947f, -0.000319159f, -4.2106e-06f}
+};
+
+#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
+/* Not sure at all of the appropriate number for MAX_ITER... */
+#define MAX_ITER 100
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ long i;
+ double dphi;
+ (void) P;
+
+ dphi = fabs(lp.phi);
+ i = isnan(lp.phi) ? -1 : lround(floor(dphi * C1));
+ if( i < 0 ){
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ 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};
+ long i;
+ double t, t1;
+ struct COEFS T;
+ int iters;
+
+ lp.lam = xy.x / FXC;
+ lp.phi = fabs(xy.y / FYC);
+ if (lp.phi >= 1.) { /* simple pathologic cases */
+ if (lp.phi > ONEEPS) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+ else {
+ lp.phi = xy.y < 0. ? -M_HALFPI : M_HALFPI;
+ lp.lam /= X[NODES].c0;
+ }
+ } else { /* general problem */
+ /* in Y space, reduce to table interval */
+ i = isnan(lp.phi) ? -1 : lround(floor(lp.phi * NODES));
+ if( i < 0 || i >= NODES ) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+ for (;;) {
+ 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 = (float)(T.c0 - lp.phi);
+ for (iters = MAX_ITER; iters ; --iters) { /* Newton-Raphson */
+ t -= t1 = V(T,t) / DV(T,t);
+ if (fabs(t1) < EPS)
+ break;
+ }
+ if( iters == 0 )
+ pj_ctx_set_errno( P->ctx, PJD_ERR_NON_CONVERGENT );
+ 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;
+}
+
+
diff --git a/src/projections/rouss.cpp b/src/projections/rouss.cpp
new file mode 100644
index 00000000..3b4428bc
--- /dev/null
+++ b/src/projections/rouss.cpp
@@ -0,0 +1,158 @@
+/*
+** libproj -- library of cartographic projections
+**
+** Copyright (c) 2003, 2006 Gerald I. Evenden
+*/
+/*
+** Permission is hereby granted, free of charge, to any person obtaining
+** a copy of this software and associated documentation files (the
+** "Software"), to deal in the Software without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Software, and to
+** permit persons to whom the Software is furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be
+** included in all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+namespace { // anonymous namespace
+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;
+};
+} // anonymous namespace
+PROJ_HEAD(rouss, "Roussilhe Stereographic") "\n\tAzi, Ell";
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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;
+}
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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;
+}
+
+
+static PJ *destructor (PJ *P, int errlev) {
+ if (nullptr==P)
+ return nullptr;
+
+ if (nullptr==P->opaque)
+ return pj_default_destructor (P, errlev);
+
+ if (static_cast<struct pj_opaque*>(P->opaque)->en)
+ pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->en);
+
+ return pj_default_destructor (P, ENOMEM);
+}
+
+
+PJ *PROJECTION(rouss) {
+ double N0, es2, t, t2, R_R0_2, R_R0_4;
+
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor(P, ENOMEM);
+ P->opaque = Q;
+
+ if (!((Q->en = proj_mdist_ini(P->es))))
+ return pj_default_destructor (P, ENOMEM);
+
+ 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;
+ P->destructor = destructor;
+
+ return P;
+}
diff --git a/src/projections/rpoly.cpp b/src/projections/rpoly.cpp
new file mode 100644
index 00000000..a34f6171
--- /dev/null
+++ b/src/projections/rpoly.cpp
@@ -0,0 +1,58 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "projects.h"
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double phi1;
+ double fxa;
+ double fxb;
+ int mode;
+};
+} // anonymous namespace
+
+PROJ_HEAD(rpoly, "Rectangular Polyconic")
+ "\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 = static_cast<struct pj_opaque*>(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;
+}
+
+
+
+PJ *PROJECTION(rpoly) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor(P, ENOMEM);
+ 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;
+}
diff --git a/src/projections/sch.cpp b/src/projections/sch.cpp
new file mode 100644
index 00000000..5a2f944b
--- /dev/null
+++ b/src/projections/sch.cpp
@@ -0,0 +1,232 @@
+/******************************************************************************
+ * Project: SCH Coordinate system
+ * Purpose: Implementation of SCH Coordinate system
+ * 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
+ * data based on the GeoSAR processor." Airsar earth science and applications
+ * workshop, March. 2002. (http://airsar.jpl.nasa.gov/documents/workshop2002/papers/T3.pdf)
+ *
+ * Author: Piyush Agram (piyush.agram@jpl.nasa.gov)
+ * 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
+ * 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
+ * publicly released documents. All credit for the development of the coordinate
+ * system and its use should be directed towards the original developers at JPL.
+ ******************************************************************************
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ ****************************************************************************/
+
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+#include "geocent.h"
+
+namespace { // anonymous namespace
+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;
+};
+} // anonymous namespace
+
+PROJ_HEAD(sch, "Spherical Cross-track Height") "\n\tMisc\n\tplat_0= plon_0= phdg_0= [h_0=]";
+
+static LPZ inverse3d(XYZ xyz, PJ *P) {
+ LPZ lpz = {0.0, 0.0, 0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double temp[3];
+ double pxyz[3];
+
+ /* Local lat,lon using radius */
+ pxyz[0] = xyz.y * P->a / Q->rcurv;
+ pxyz[1] = xyz.x * P->a / Q->rcurv;
+ pxyz[2] = xyz.z;
+
+ if( pj_Convert_Geodetic_To_Geocentric( &(Q->sph), pxyz[0], pxyz[1], pxyz[2], temp, temp+1, temp+2) != 0) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lpz;
+ }
+
+ /* Apply rotation */
+ pxyz[0] = Q->transMat[0] * temp[0] + Q->transMat[1] * temp[1] + Q->transMat[2] * temp[2];
+ pxyz[1] = Q->transMat[3] * temp[0] + Q->transMat[4] * temp[1] + Q->transMat[5] * temp[2];
+ pxyz[2] = Q->transMat[6] * temp[0] + Q->transMat[7] * temp[1] + Q->transMat[8] * temp[2];
+
+ /* Apply offset */
+ pxyz[0] += Q->xyzoff[0];
+ pxyz[1] += Q->xyzoff[1];
+ pxyz[2] += Q->xyzoff[2];
+
+ /* Convert geocentric coordinates to lat lon */
+ pj_Convert_Geocentric_To_Geodetic( &(Q->elp_0), pxyz[0], pxyz[1], pxyz[2],
+ temp, temp+1, temp+2);
+
+
+ lpz.lam = temp[1] ;
+ lpz.phi = temp[0] ;
+ lpz.z = temp[2];
+
+ return lpz;
+}
+
+static XYZ forward3d(LPZ lpz, PJ *P) {
+ XYZ xyz = {0.0, 0.0, 0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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 ) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xyz;
+ }
+
+
+ /* Adjust for offset */
+ temp[0] -= Q->xyzoff[0];
+ temp[1] -= Q->xyzoff[1];
+ temp[2] -= Q->xyzoff[2];
+
+
+ /* Apply rotation */
+ pxyz[0] = Q->transMat[0] * temp[0] + Q->transMat[3] * temp[1] + Q->transMat[6] * temp[2];
+ pxyz[1] = Q->transMat[1] * temp[0] + Q->transMat[4] * temp[1] + Q->transMat[7] * temp[2];
+ pxyz[2] = Q->transMat[2] * temp[0] + Q->transMat[5] * temp[1] + Q->transMat[8] * temp[2];
+
+ /* Convert to local lat,lon */
+ pj_Convert_Geocentric_To_Geodetic( &(Q->sph), pxyz[0], pxyz[1], pxyz[2],
+ temp, temp+1, temp+2);
+
+
+ /* Scale by radius */
+ xyz.x = temp[1] * Q->rcurv / P->a;
+ xyz.y = temp[0] * Q->rcurv / P->a;
+ xyz.z = temp[2];
+
+ return xyz;
+}
+
+
+static PJ *setup(PJ *P) { /* general initialization */
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(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);
+
+ /* Setup original geocentric system */
+ if ( pj_Set_Geocentric_Parameters(&(Q->elp_0), P->a, temp) != 0)
+ return pj_default_destructor(P, PJD_ERR_FAILED_TO_FIND_PROJ);
+
+ clt = cos(Q->plat);
+ slt = sin(Q->plat);
+ clo = cos(Q->plon);
+ slo = sin(Q->plon);
+
+ /* Estimate the radius of curvature for given peg */
+ temp = sqrt(1.0 - (P->es) * slt * slt);
+ reast = (P->a)/temp;
+ rnorth = (P->a) * (1.0 - (P->es))/pow(temp,3);
+
+ chdg = cos(Q->phdg);
+ shdg = sin(Q->phdg);
+
+ Q->rcurv = Q->h0 + (reast*rnorth)/(reast * chdg * chdg + rnorth * shdg * shdg);
+
+ /* Set up local sphere at the given peg point */
+ if ( pj_Set_Geocentric_Parameters(&(Q->sph), Q->rcurv, Q->rcurv) != 0)
+ return pj_default_destructor(P, PJD_ERR_FAILED_TO_FIND_PROJ);
+
+ /* Set up the transformation matrices */
+ Q->transMat[0] = clt * clo;
+ Q->transMat[1] = -shdg*slo - slt*clo * chdg;
+ Q->transMat[2] = slo*chdg - slt*clo*shdg;
+ Q->transMat[3] = clt*slo;
+ Q->transMat[4] = clo*shdg - slt*slo*chdg;
+ Q->transMat[5] = -clo*chdg - slt*slo*shdg;
+ Q->transMat[6] = slt;
+ Q->transMat[7] = clt*chdg;
+ Q->transMat[8] = clt*shdg;
+
+
+ if( pj_Convert_Geodetic_To_Geocentric( &(Q->elp_0), Q->plat, Q->plon, Q->h0,
+ pxyz, pxyz+1, pxyz+2 ) != 0 )
+ return pj_default_destructor(P, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT);
+
+
+ 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;
+
+ P->fwd3d = forward3d;
+ P->inv3d = inverse3d;
+ return P;
+}
+
+
+PJ *PROJECTION(sch) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor(P, ENOMEM);
+ 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 {
+ return pj_default_destructor(P, PJD_ERR_FAILED_TO_FIND_PROJ);
+ }
+
+ /* 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 {
+ return pj_default_destructor(P, PJD_ERR_FAILED_TO_FIND_PROJ);
+ }
+
+ /* 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 {
+ return pj_default_destructor(P, PJD_ERR_FAILED_TO_FIND_PROJ);
+ }
+
+
+ /* 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;
+
+
+ return setup(P);
+}
diff --git a/src/projections/sconics.cpp b/src/projections/sconics.cpp
new file mode 100644
index 00000000..1d19a13d
--- /dev/null
+++ b/src/projections/sconics.cpp
@@ -0,0 +1,220 @@
+#define PJ_LIB__
+#include <errno.h>
+#include "proj.h"
+#include "projects.h"
+#include "proj_math.h"
+
+
+namespace { // anonymous namespace
+enum Type {
+ EULER = 0,
+ MURD1 = 1,
+ MURD2 = 2,
+ MURD3 = 3,
+ PCONIC = 4,
+ TISSOT = 5,
+ VITK1 = 6
+};
+} // anonymous namespace
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double n;
+ double rho_c;
+ double rho_0;
+ double sig;
+ double c1, c2;
+ enum Type type;
+};
+} // anonymous namespace
+
+
+#define EPS10 1.e-10
+#define EPS 1e-10
+#define LINE2 "\n\tConic, Sph\n\tlat_1= and lat_2="
+
+PROJ_HEAD(euler, "Euler") LINE2;
+PROJ_HEAD(murd1, "Murdoch I") LINE2;
+PROJ_HEAD(murd2, "Murdoch II") LINE2;
+PROJ_HEAD(murd3, "Murdoch III") LINE2;
+PROJ_HEAD(pconic, "Perspective Conic") LINE2;
+PROJ_HEAD(tissot, "Tissot") LINE2;
+PROJ_HEAD(vitk1, "Vitkovsky I") LINE2;
+
+
+
+/* get common factors for simple conics */
+static int phi12(PJ *P, double *del) {
+ double p1, p2;
+ int err = 0;
+
+ if (!pj_param(P->ctx, P->params, "tlat_1").i ||
+ !pj_param(P->ctx, P->params, "tlat_2").i) {
+ err = -41;
+ } else {
+ p1 = pj_param(P->ctx, P->params, "rlat_1").f;
+ p2 = pj_param(P->ctx, P->params, "rlat_2").f;
+ *del = 0.5 * (p2 - p1);
+ static_cast<struct pj_opaque*>(P->opaque)->sig = 0.5 * (p2 + p1);
+ err = (fabs(*del) < EPS || fabs(static_cast<struct pj_opaque*>(P->opaque)->sig) < EPS) ? PJD_ERR_ABS_LAT1_EQ_ABS_LAT2 : 0;
+ }
+ return err;
+}
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0, 0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double rho;
+
+ switch (Q->type) {
+ case MURD2:
+ rho = Q->rho_c + tan (Q->sig - lp.phi);
+ break;
+ case PCONIC:
+ rho = Q->c2 * (Q->c1 - tan (lp.phi - Q->sig));
+ break;
+ default:
+ rho = Q->rho_c - lp.phi;
+ break;
+ }
+
+ xy.x = rho * sin ( lp.lam *= Q->n );
+ xy.y = Q->rho_0 - rho * cos (lp.lam);
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, (and ellipsoidal?) inverse */
+ LP lp = {0.0, 0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double rho;
+
+ rho = hypot (xy.x, xy.y = Q->rho_0 - xy.y);
+ if (Q->n < 0.) {
+ rho = - rho;
+ xy.x = - xy.x;
+ xy.y = - xy.y;
+ }
+
+ lp.lam = atan2 (xy.x, xy.y) / Q->n;
+
+ switch (Q->type) {
+ case PCONIC:
+ lp.phi = atan (Q->c1 - rho / Q->c2) + Q->sig;
+ break;
+ case MURD2:
+ lp.phi = Q->sig - atan(rho - Q->rho_c);
+ break;
+ default:
+ lp.phi = Q->rho_c - rho;
+ }
+ return lp;
+}
+
+
+static PJ *setup(PJ *P, enum Type type) {
+ double del, cs;
+ int err;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+ Q->type = type;
+
+ err = phi12 (P, &del);
+ if(err)
+ return pj_default_destructor (P, err);
+
+ switch (Q->type) {
+
+ case TISSOT:
+ Q->n = sin (Q->sig);
+ cs = cos (del);
+ Q->rho_c = Q->n / cs + cs / Q->n;
+ Q->rho_0 = sqrt ((Q->rho_c - 2 * sin (P->phi0)) / Q->n);
+ break;
+
+ case MURD1:
+ Q->rho_c = sin(del)/(del * tan(Q->sig)) + Q->sig;
+ Q->rho_0 = Q->rho_c - P->phi0;
+ Q->n = sin(Q->sig);
+ break;
+
+ case MURD2:
+ Q->rho_c = (cs = sqrt (cos (del))) / tan (Q->sig);
+ Q->rho_0 = Q->rho_c + tan (Q->sig - P->phi0);
+ Q->n = sin (Q->sig) * cs;
+ break;
+
+ case MURD3:
+ Q->rho_c = del / (tan(Q->sig) * tan(del)) + Q->sig;
+ Q->rho_0 = Q->rho_c - P->phi0;
+ Q->n = sin (Q->sig) * sin (del) * tan (del) / (del * del);
+ break;
+
+ case EULER:
+ Q->n = sin (Q->sig) * sin (del) / del;
+ del *= 0.5;
+ Q->rho_c = del / (tan (del) * tan (Q->sig)) + Q->sig;
+ Q->rho_0 = Q->rho_c - P->phi0;
+ break;
+
+ case PCONIC:
+ Q->n = sin (Q->sig);
+ Q->c2 = cos (del);
+ Q->c1 = 1./tan (Q->sig);
+ if (fabs (del = P->phi0 - Q->sig) - EPS10 >= M_HALFPI)
+ return pj_default_destructor(P, PJD_ERR_LAT_0_HALF_PI_FROM_MEAN);
+
+ Q->rho_0 = Q->c2 * (Q->c1 - tan (del));
+ break;
+
+ case VITK1:
+ Q->n = (cs = tan (del)) * sin (Q->sig) / del;
+ Q->rho_c = del / (cs * tan (Q->sig)) + Q->sig;
+ Q->rho_0 = Q->rho_c - P->phi0;
+ break;
+ }
+
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ P->es = 0;
+ return (P);
+}
+
+
+PJ *PROJECTION(euler) {
+ return setup(P, EULER);
+}
+
+
+PJ *PROJECTION(tissot) {
+ return setup(P, TISSOT);
+}
+
+
+PJ *PROJECTION(murd1) {
+ return setup(P, MURD1);
+}
+
+
+PJ *PROJECTION(murd2) {
+ return setup(P, MURD2);
+}
+
+
+PJ *PROJECTION(murd3) {
+ return setup(P, MURD3);
+}
+
+
+PJ *PROJECTION(pconic) {
+ return setup(P, PCONIC);
+}
+
+
+PJ *PROJECTION(vitk1) {
+ return setup(P, VITK1);
+}
+
diff --git a/src/projections/somerc.cpp b/src/projections/somerc.cpp
new file mode 100644
index 00000000..15d2e765
--- /dev/null
+++ b/src/projections/somerc.cpp
@@ -0,0 +1,94 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(somerc, "Swiss. Obl. Mercator") "\n\tCyl, Ell\n\tFor CH1903";
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double K, c, hlf_e, kR, cosp0, sinp0;
+};
+} // anonymous namespace
+
+#define EPS 1.e-10
+#define NITER 6
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ XY xy = {0.0, 0.0};
+ double phip, lamp, phipp, lampp, sp, cp;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+
+ sp = P->e * sin (lp.phi);
+ phip = 2.* atan ( exp ( Q->c * (
+ log (tan (M_FORTPI + 0.5 * lp.phi)) - Q->hlf_e * log ((1. + sp)/(1. - sp)))
+ + Q->K)) - M_HALFPI;
+ lamp = Q->c * lp.lam;
+ cp = cos(phip);
+ phipp = aasin (P->ctx, Q->cosp0 * sin (phip) - Q->sinp0 * cp * cos (lamp));
+ lampp = aasin (P->ctx, cp * sin (lamp) / cos (phipp));
+ xy.x = Q->kR * lampp;
+ xy.y = Q->kR * log (tan (M_FORTPI + 0.5 * phipp));
+ return xy;
+}
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double phip, lamp, phipp, lampp, cp, esp, con, delp;
+ int i;
+
+ phipp = 2. * (atan (exp (xy.y / Q->kR)) - M_FORTPI);
+ lampp = xy.x / Q->kR;
+ cp = cos (phipp);
+ phip = aasin (P->ctx, Q->cosp0 * sin (phipp) + Q->sinp0 * cp * cos (lampp));
+ lamp = aasin (P->ctx, cp * sin (lampp) / cos (phip));
+ con = (Q->K - log (tan (M_FORTPI + 0.5 * phip)))/Q->c;
+ for (i = NITER; i ; --i) {
+ esp = P->e * sin(phip);
+ delp = (con + log(tan(M_FORTPI + 0.5 * phip)) - Q->hlf_e *
+ log((1. + esp)/(1. - esp)) ) *
+ (1. - esp * esp) * cos(phip) * P->rone_es;
+ phip -= delp;
+ if (fabs(delp) < EPS)
+ break;
+ }
+ if (i) {
+ lp.phi = phip;
+ lp.lam = lamp / Q->c;
+ } else {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+ return (lp);
+}
+
+
+PJ *PROJECTION(somerc) {
+ double cp, phip0, sp;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+
+ Q->hlf_e = 0.5 * P->e;
+ cp = cos (P->phi0);
+ cp *= cp;
+ Q->c = sqrt (1 + P->es * cp * cp * P->rone_es);
+ sp = sin (P->phi0);
+ Q->cosp0 = cos( phip0 = aasin (P->ctx, Q->sinp0 = sp / Q->c) );
+ sp *= P->e;
+ Q->K = log (tan (M_FORTPI + 0.5 * phip0)) - Q->c * (
+ log (tan (M_FORTPI + 0.5 * P->phi0)) - Q->hlf_e *
+ log ((1. + sp) / (1. - sp)));
+ Q->kR = P->k0 * sqrt(P->one_es) / (1. - sp * sp);
+ P->inv = e_inverse;
+ P->fwd = e_forward;
+ return P;
+}
diff --git a/src/projections/stere.cpp b/src/projections/stere.cpp
new file mode 100644
index 00000000..1502b2a6
--- /dev/null
+++ b/src/projections/stere.cpp
@@ -0,0 +1,320 @@
+#define PJ_LIB__
+#include <errno.h>
+#include "proj.h"
+#include "projects.h"
+#include "proj_math.h"
+
+PROJ_HEAD(stere, "Stereographic") "\n\tAzi, Sph&Ell\n\tlat_ts=";
+PROJ_HEAD(ups, "Universal Polar Stereographic") "\n\tAzi, Sph&Ell\n\tsouth";
+
+
+namespace { // anonymous namespace
+enum Mode {
+ S_POLE = 0,
+ N_POLE = 1,
+ OBLIQ = 2,
+ EQUIT = 3
+};
+} // anonymous namespace
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double phits;
+ double sinX1;
+ double cosX1;
+ double akm1;
+ enum Mode mode;
+};
+} // anonymous namespace
+
+#define sinph0 static_cast<struct pj_opaque*>(P->opaque)->sinX1
+#define cosph0 static_cast<struct pj_opaque*>(P->opaque)->cosX1
+#define EPS10 1.e-10
+#define TOL 1.e-8
+#define NITER 8
+#define CONV 1.e-10
+
+static double ssfn_ (double phit, double sinphi, double eccen) {
+ sinphi *= eccen;
+ return (tan (.5 * (M_HALFPI + phit)) *
+ pow ((1. - sinphi) / (1. + sinphi), .5 * eccen));
+}
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double coslam, sinlam, sinX = 0.0, cosX = 0.0, X, A = 0.0, sinphi;
+
+ coslam = cos (lp.lam);
+ sinlam = sin (lp.lam);
+ sinphi = sin (lp.phi);
+ if (Q->mode == OBLIQ || Q->mode == EQUIT) {
+ sinX = sin (X = 2. * atan(ssfn_(lp.phi, sinphi, P->e)) - M_HALFPI);
+ cosX = cos (X);
+ }
+
+ switch (Q->mode) {
+ case OBLIQ:
+ A = Q->akm1 / (Q->cosX1 * (1. + Q->sinX1 * sinX +
+ Q->cosX1 * cosX * coslam));
+ xy.y = A * (Q->cosX1 * sinX - Q->sinX1 * cosX * coslam);
+ goto xmul; /* but why not just xy.x = A * cosX; break; ? */
+
+ case EQUIT:
+ /* avoid zero division */
+ if (1. + cosX * coslam == 0.0) {
+ xy.y = HUGE_VAL;
+ } else {
+ A = Q->akm1 / (1. + cosX * coslam);
+ xy.y = A * sinX;
+ }
+xmul:
+ xy.x = A * cosX;
+ break;
+
+ case S_POLE:
+ lp.phi = -lp.phi;
+ coslam = - coslam;
+ sinphi = -sinphi;
+ /*-fallthrough*/
+ case N_POLE:
+ xy.x = Q->akm1 * pj_tsfn (lp.phi, sinphi, P->e);
+ xy.y = - xy.x * coslam;
+ break;
+ }
+
+ xy.x = xy.x * sinlam;
+ return xy;
+}
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double sinphi, cosphi, coslam, sinlam;
+
+ sinphi = sin(lp.phi);
+ cosphi = cos(lp.phi);
+ coslam = cos(lp.lam);
+ sinlam = sin(lp.lam);
+
+ switch (Q->mode) {
+ case EQUIT:
+ xy.y = 1. + cosphi * coslam;
+ goto oblcon;
+ case OBLIQ:
+ xy.y = 1. + sinph0 * sinphi + cosph0 * cosphi * coslam;
+oblcon:
+ if (xy.y <= EPS10) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ xy.x = (xy.y = Q->akm1 / xy.y) * cosphi * sinlam;
+ xy.y *= (Q->mode == EQUIT) ? sinphi :
+ cosph0 * sinphi - sinph0 * cosphi * coslam;
+ break;
+ case N_POLE:
+ coslam = - coslam;
+ lp.phi = - lp.phi;
+ /*-fallthrough*/
+ case S_POLE:
+ if (fabs (lp.phi - M_HALFPI) < TOL) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ xy.x = sinlam * ( xy.y = Q->akm1 * tan (M_FORTPI + .5 * lp.phi) );
+ 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 = static_cast<struct pj_opaque*>(P->opaque);
+ double cosphi, sinphi, tp=0.0, phi_l=0.0, rho, halfe=0.0, halfpi=0.0;
+ int i;
+
+ rho = hypot (xy.x, xy.y);
+
+ switch (Q->mode) {
+ case OBLIQ:
+ case EQUIT:
+ cosphi = cos ( tp = 2. * atan2 (rho * Q->cosX1 , Q->akm1) );
+ sinphi = sin (tp);
+ if ( rho == 0.0 )
+ phi_l = asin (cosphi * Q->sinX1);
+ else
+ phi_l = asin (cosphi * Q->sinX1 + (xy.y * sinphi * Q->cosX1 / rho));
+
+ tp = tan (.5 * (M_HALFPI + phi_l));
+ xy.x *= sinphi;
+ xy.y = rho * Q->cosX1 * cosphi - xy.y * Q->sinX1* sinphi;
+ halfpi = M_HALFPI;
+ halfe = .5 * P->e;
+ break;
+ case N_POLE:
+ xy.y = -xy.y;
+ /*-fallthrough*/
+ case S_POLE:
+ phi_l = M_HALFPI - 2. * atan (tp = - rho / Q->akm1);
+ halfpi = -M_HALFPI;
+ halfe = -.5 * P->e;
+ break;
+ }
+
+ for (i = NITER; i--; phi_l = lp.phi) {
+ sinphi = P->e * sin(phi_l);
+ lp.phi = 2. * atan (tp * pow ((1.+sinphi)/(1.-sinphi), halfe)) - halfpi;
+ if (fabs (phi_l - lp.phi) < CONV) {
+ if (Q->mode == S_POLE)
+ lp.phi = -lp.phi;
+ lp.lam = (xy.x == 0. && xy.y == 0.) ? 0. : atan2 (xy.x, xy.y);
+ return lp;
+ }
+ }
+
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double c, rh, sinc, cosc;
+
+ sinc = sin (c = 2. * atan ((rh = hypot (xy.x, xy.y)) / Q->akm1));
+ cosc = cos (c);
+ lp.lam = 0.;
+
+ switch (Q->mode) {
+ case EQUIT:
+ if (fabs (rh) <= EPS10)
+ lp.phi = 0.;
+ else
+ lp.phi = asin (xy.y * sinc / rh);
+ if (cosc != 0. || xy.x != 0.)
+ lp.lam = atan2 (xy.x * sinc, cosc * rh);
+ break;
+ case OBLIQ:
+ if (fabs (rh) <= EPS10)
+ lp.phi = P->phi0;
+ else
+ lp.phi = asin (cosc * sinph0 + xy.y * sinc * cosph0 / rh);
+ if ((c = cosc - sinph0 * sin (lp.phi)) != 0. || xy.x != 0.)
+ lp.lam = atan2 (xy.x * sinc * cosph0, c * rh);
+ break;
+ case N_POLE:
+ xy.y = -xy.y;
+ /*-fallthrough*/
+ case S_POLE:
+ if (fabs (rh) <= EPS10)
+ lp.phi = P->phi0;
+ else
+ lp.phi = asin (Q->mode == S_POLE ? - cosc : cosc);
+ lp.lam = (xy.x == 0. && xy.y == 0.) ? 0. : atan2 (xy.x, xy.y);
+ break;
+ }
+ return lp;
+}
+
+
+static PJ *setup(PJ *P) { /* general initialization */
+ double t;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+
+ if (fabs ((t = fabs (P->phi0)) - M_HALFPI) < EPS10)
+ Q->mode = P->phi0 < 0. ? S_POLE : N_POLE;
+ else
+ Q->mode = t > EPS10 ? OBLIQ : EQUIT;
+ Q->phits = fabs (Q->phits);
+
+ if (P->es != 0.0) {
+ double X;
+
+ switch (Q->mode) {
+ case N_POLE:
+ case S_POLE:
+ if (fabs (Q->phits - M_HALFPI) < EPS10)
+ Q->akm1 = 2. * P->k0 /
+ sqrt (pow (1+P->e,1+P->e) * pow (1-P->e,1-P->e));
+ else {
+ Q->akm1 = cos (Q->phits) /
+ pj_tsfn (Q->phits, t = sin (Q->phits), P->e);
+ t *= P->e;
+ Q->akm1 /= sqrt(1. - t * t);
+ }
+ break;
+ case EQUIT:
+ case OBLIQ:
+ t = sin (P->phi0);
+ X = 2. * atan (ssfn_(P->phi0, t, P->e)) - M_HALFPI;
+ t *= P->e;
+ Q->akm1 = 2. * P->k0 * cos (P->phi0) / sqrt(1. - t * t);
+ Q->sinX1 = sin (X);
+ Q->cosX1 = cos (X);
+ break;
+ }
+ P->inv = e_inverse;
+ P->fwd = e_forward;
+ } else {
+ switch (Q->mode) {
+ case OBLIQ:
+ sinph0 = sin (P->phi0);
+ cosph0 = cos (P->phi0);
+ /*-fallthrough*/
+ case EQUIT:
+ Q->akm1 = 2. * P->k0;
+ break;
+ case S_POLE:
+ case N_POLE:
+ Q->akm1 = fabs (Q->phits - M_HALFPI) >= EPS10 ?
+ cos (Q->phits) / tan (M_FORTPI - .5 * Q->phits) :
+ 2. * P->k0 ;
+ break;
+ }
+
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ }
+ return P;
+}
+
+
+PJ *PROJECTION(stere) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->phits = pj_param (P->ctx, P->params, "tlat_ts").i ?
+ pj_param (P->ctx, P->params, "rlat_ts").f : M_HALFPI;
+
+ return setup(P);
+}
+
+
+PJ *PROJECTION(ups) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ /* International Ellipsoid */
+ P->phi0 = pj_param(P->ctx, P->params, "bsouth").i ? - M_HALFPI: M_HALFPI;
+ if (P->es == 0.0) {
+ proj_errno_set(P, PJD_ERR_ELLIPSOID_USE_REQUIRED);
+ return pj_default_destructor (P, ENOMEM);
+ }
+ P->k0 = .994;
+ P->x0 = 2000000.;
+ P->y0 = 2000000.;
+ Q->phits = M_HALFPI;
+ P->lam0 = 0.;
+
+ return setup(P);
+}
+
diff --git a/src/projections/sterea.cpp b/src/projections/sterea.cpp
new file mode 100644
index 00000000..bb498068
--- /dev/null
+++ b/src/projections/sterea.cpp
@@ -0,0 +1,117 @@
+/*
+** libproj -- library of cartographic projections
+**
+** Copyright (c) 2003 Gerald I. Evenden
+*/
+/*
+** Permission is hereby granted, free of charge, to any person obtaining
+** a copy of this software and associated documentation files (the
+** "Software"), to deal in the Software without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Software, and to
+** permit persons to whom the Software is furnished to do so, subject to
+** the following conditions:
+**
+** The above copyright notice and this permission notice shall be
+** included in all copies or substantial portions of the Software.
+**
+** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+#define PJ_LIB__
+#include <errno.h>
+#include "projects.h"
+#include "proj_math.h"
+
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double phic0;
+ double cosc0, sinc0;
+ double R2;
+ void *en;
+};
+} // anonymous namespace
+
+
+PROJ_HEAD(sterea, "Oblique Stereographic Alternative") "\n\tAzimuthal, Sph&Ell";
+
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double cosc, sinc, cosl, k;
+
+ lp = pj_gauss(P->ctx, lp, Q->en);
+ sinc = sin(lp.phi);
+ cosc = cos(lp.phi);
+ cosl = cos(lp.lam);
+ k = P->k0 * Q->R2 / (1. + Q->sinc0 * sinc + Q->cosc0 * cosc * cosl);
+ xy.x = k * cosc * sin(lp.lam);
+ xy.y = k * (Q->cosc0 * sinc - Q->sinc0 * cosc * cosl);
+ return xy;
+}
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double rho, c, sinc, cosc;
+
+ xy.x /= P->k0;
+ xy.y /= P->k0;
+ if ( (rho = hypot (xy.x, xy.y)) != 0.0 ) {
+ c = 2. * atan2 (rho, Q->R2);
+ sinc = sin (c);
+ cosc = cos (c);
+ lp.phi = asin (cosc * Q->sinc0 + xy.y * sinc * Q->cosc0 / rho);
+ lp.lam = atan2 (xy.x * sinc, rho * Q->cosc0 * cosc - xy.y * Q->sinc0 * sinc);
+ } else {
+ lp.phi = Q->phic0;
+ lp.lam = 0.;
+ }
+ return pj_inv_gauss(P->ctx, lp, Q->en);
+}
+
+
+static PJ *destructor (PJ *P, int errlev) {
+ if (nullptr==P)
+ return nullptr;
+
+ if (nullptr==P->opaque)
+ return pj_default_destructor (P, errlev);
+
+ pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->en);
+ return pj_default_destructor (P, errlev);
+}
+
+
+PJ *PROJECTION(sterea) {
+ double R;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->en = pj_gauss_ini(P->e, P->phi0, &(Q->phic0), &R);
+ if (nullptr==Q->en)
+ return pj_default_destructor (P, ENOMEM);
+
+ Q->sinc0 = sin (Q->phic0);
+ Q->cosc0 = cos (Q->phic0);
+ Q->R2 = 2. * R;
+
+ P->inv = e_inverse;
+ P->fwd = e_forward;
+ P->destructor = destructor;
+
+ return P;
+}
+
diff --git a/src/projections/sts.cpp b/src/projections/sts.cpp
new file mode 100644
index 00000000..9f889611
--- /dev/null
+++ b/src/projections/sts.cpp
@@ -0,0 +1,109 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(kav5, "Kavraisky V") "\n\tPCyl, Sph";
+PROJ_HEAD(qua_aut, "Quartic Authalic") "\n\tPCyl, Sph";
+PROJ_HEAD(fouc, "Foucaut") "\n\tPCyl, Sph";
+PROJ_HEAD(mbt_s, "McBryde-Thomas Flat-Polar Sine (No. 1)") "\n\tPCyl, Sph";
+
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double C_x, C_y, C_p;
+ int tan_mode;
+};
+} // anonymous namespace
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double c;
+
+ xy.x = Q->C_x * lp.lam * cos(lp.phi);
+ xy.y = Q->C_y;
+ lp.phi *= Q->C_p;
+ c = cos(lp.phi);
+ if (Q->tan_mode) {
+ xy.x *= c * c;
+ xy.y *= tan (lp.phi);
+ } else {
+ xy.x /= c;
+ xy.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 = static_cast<struct pj_opaque*>(P->opaque);
+ double c;
+
+ xy.y /= Q->C_y;
+ c = cos (lp.phi = Q->tan_mode ? atan (xy.y) : aasin (P->ctx, xy.y));
+ lp.phi /= Q->C_p;
+ lp.lam = xy.x / (Q->C_x * cos(lp.phi));
+ if (Q->tan_mode)
+ lp.lam /= c * c;
+ else
+ lp.lam *= c;
+ return lp;
+}
+
+
+static PJ *setup(PJ *P, double p, double q, int mode) {
+ P->es = 0.;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ static_cast<struct pj_opaque*>(P->opaque)->C_x = q / p;
+ static_cast<struct pj_opaque*>(P->opaque)->C_y = p;
+ static_cast<struct pj_opaque*>(P->opaque)->C_p = 1/ q;
+ static_cast<struct pj_opaque*>(P->opaque)->tan_mode = mode;
+ return P;
+}
+
+
+
+PJ *PROJECTION(fouc) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor(P, ENOMEM);
+ P->opaque = Q;
+ return setup(P, 2., 2., 1);
+}
+
+
+
+PJ *PROJECTION(kav5) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor(P, ENOMEM);
+ P->opaque = Q;
+
+ return setup(P, 1.50488, 1.35439, 0);
+}
+
+
+
+PJ *PROJECTION(qua_aut) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor(P, ENOMEM);
+ P->opaque = Q;
+ return setup(P, 2., 2., 0);
+}
+
+
+
+PJ *PROJECTION(mbt_s) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor(P, ENOMEM);
+ P->opaque = Q;
+ return setup(P, 1.48875, 1.36509, 0);
+}
diff --git a/src/projections/tcc.cpp b/src/projections/tcc.cpp
new file mode 100644
index 00000000..64fdc182
--- /dev/null
+++ b/src/projections/tcc.cpp
@@ -0,0 +1,34 @@
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(tcc, "Transverse Central Cylindrical") "\n\tCyl, Sph, no inv";
+
+#define EPS10 1.e-10
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0, 0.0};
+ double b, bt;
+
+ b = cos (lp.phi) * sin (lp.lam);
+ if ((bt = 1. - b * b) < EPS10) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ xy.x = b / sqrt(bt);
+ xy.y = atan2 (tan (lp.phi) , cos (lp.lam));
+ return xy;
+}
+
+
+PJ *PROJECTION(tcc) {
+ P->es = 0.;
+ P->fwd = s_forward;
+ P->inv = nullptr;
+
+ return P;
+}
diff --git a/src/projections/tcea.cpp b/src/projections/tcea.cpp
new file mode 100644
index 00000000..d30f3df0
--- /dev/null
+++ b/src/projections/tcea.cpp
@@ -0,0 +1,36 @@
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(tcea, "Transverse Cylindrical Equal Area") "\n\tCyl, Sph";
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ xy.x = cos (lp.phi) * sin (lp.lam) / P->k0;
+ xy.y = P->k0 * (atan2 (tan (lp.phi), cos (lp.lam)) - P->phi0);
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0, 0.0};
+ double t;
+
+ xy.y = xy.y / P->k0 + P->phi0;
+ xy.x *= P->k0;
+ t = sqrt (1. - xy.x * xy.x);
+ lp.phi = asin (t * sin (xy.y));
+ lp.lam = atan2 (xy.x, t * cos (xy.y));
+ return lp;
+}
+
+
+PJ *PROJECTION(tcea) {
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ P->es = 0.;
+ return P;
+}
diff --git a/src/projections/times.cpp b/src/projections/times.cpp
new file mode 100644
index 00000000..e8b4499f
--- /dev/null
+++ b/src/projections/times.cpp
@@ -0,0 +1,79 @@
+/******************************************************************************
+ * Project: PROJ.4
+ * Purpose: Implementation of the Times projection.
+ * Author: Kristian Evers <kristianevers@gmail.com>
+ *
+ ******************************************************************************
+ * Copyright (c) 2016, Kristian Evers
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ *****************************************************************************
+ * Based on describtion of the Times Projection in
+ *
+ * Flattening the Earth, Snyder, J.P., 1993, p.213-214.
+ *****************************************************************************/
+
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(times, "Times") "\n\tCyl, Sph";
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ double T, S, S2;
+ XY xy = {0.0,0.0};
+ (void) P;
+
+ T = tan(lp.phi/2.0);
+ S = sin(M_FORTPI * T);
+ S2 = S*S;
+
+ xy.x = lp.lam * (0.74482 - 0.34588*S2);
+ xy.y = 1.70711 * T;
+
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ double T, S, S2;
+ LP lp = {0.0,0.0};
+ (void) P;
+
+ T = xy.y / 1.70711;
+ S = sin(M_FORTPI * T);
+ S2 = S*S;
+
+ lp.lam = xy.x / (0.74482 - 0.34588 * S2);
+ lp.phi = 2 * atan(T);
+
+ return lp;
+}
+
+
+PJ *PROJECTION(times) {
+ P->es = 0.0;
+
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/tmerc.cpp b/src/projections/tmerc.cpp
new file mode 100644
index 00000000..5a2dacbd
--- /dev/null
+++ b/src/projections/tmerc.cpp
@@ -0,0 +1,210 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(tmerc, "Transverse Mercator") "\n\tCyl, Sph&Ell";
+
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double esp;
+ double ml0;
+ double *en;
+};
+} // anonymous namespace
+
+#define EPS10 1.e-10
+#define FC1 1.
+#define FC2 .5
+#define FC3 .16666666666666666666
+#define FC4 .08333333333333333333
+#define FC5 .05
+#define FC6 .03333333333333333333
+#define FC7 .02380952380952380952
+#define FC8 .01785714285714285714
+
+
+static XY e_forward (LP lp, PJ *P) { /* Ellipsoidal, forward */
+ XY xy = {0.0, 0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double al, als, n, cosphi, sinphi, t;
+
+ /*
+ * Fail if our longitude is more than 90 degrees from the
+ * central meridian since the results are essentially garbage.
+ * Is error -20 really an appropriate return value?
+ *
+ * http://trac.osgeo.org/proj/ticket/5
+ */
+ if( lp.lam < -M_HALFPI || lp.lam > M_HALFPI ) {
+ xy.x = HUGE_VAL;
+ xy.y = HUGE_VAL;
+ pj_ctx_set_errno( P->ctx, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT );
+ return xy;
+ }
+
+ sinphi = sin (lp.phi);
+ cosphi = cos (lp.phi);
+ t = fabs (cosphi) > 1e-10 ? sinphi/cosphi : 0.;
+ t *= t;
+ al = cosphi * lp.lam;
+ als = al * al;
+ al /= sqrt (1. - P->es * sinphi * sinphi);
+ n = Q->esp * cosphi * cosphi;
+ xy.x = P->k0 * al * (FC1 +
+ FC3 * als * (1. - t + n +
+ FC5 * als * (5. + t * (t - 18.) + n * (14. - 58. * t)
+ + FC7 * als * (61. + t * ( t * (179. - t) - 479. ) )
+ )));
+ xy.y = P->k0 * (pj_mlfn(lp.phi, sinphi, cosphi, Q->en) - Q->ml0 +
+ sinphi * al * lp.lam * FC2 * ( 1. +
+ FC4 * als * (5. - t + n * (9. + 4. * n) +
+ FC6 * als * (61. + t * (t - 58.) + n * (270. - 330 * t)
+ + FC8 * als * (1385. + t * ( t * (543. - t) - 3111.) )
+ ))));
+ return (xy);
+}
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ double b, cosphi;
+
+ /*
+ * Fail if our longitude is more than 90 degrees from the
+ * central meridian since the results are essentially garbage.
+ * Is error -20 really an appropriate return value?
+ *
+ * http://trac.osgeo.org/proj/ticket/5
+ */
+ if( lp.lam < -M_HALFPI || lp.lam > M_HALFPI ) {
+ xy.x = HUGE_VAL;
+ xy.y = HUGE_VAL;
+ pj_ctx_set_errno( P->ctx, PJD_ERR_LAT_OR_LON_EXCEED_LIMIT );
+ return xy;
+ }
+
+ cosphi = cos(lp.phi);
+ b = cosphi * sin (lp.lam);
+ if (fabs (fabs (b) - 1.) <= EPS10) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+
+ xy.x = static_cast<struct pj_opaque*>(P->opaque)->ml0 * log ((1. + b) / (1. - b));
+ xy.y = cosphi * cos (lp.lam) / sqrt (1. - b * b);
+
+ b = fabs ( xy.y );
+ if (b >= 1.) {
+ if ((b - 1.) > EPS10) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ else xy.y = 0.;
+ } else
+ xy.y = acos (xy.y);
+
+ if (lp.phi < 0.)
+ xy.y = -xy.y;
+ xy.y = static_cast<struct pj_opaque*>(P->opaque)->esp * (xy.y - P->phi0);
+ return xy;
+}
+
+
+static LP e_inverse (XY xy, PJ *P) { /* Ellipsoidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double n, con, cosphi, d, ds, sinphi, t;
+
+ lp.phi = pj_inv_mlfn(P->ctx, Q->ml0 + xy.y / P->k0, P->es, Q->en);
+ if (fabs(lp.phi) >= M_HALFPI) {
+ lp.phi = xy.y < 0. ? -M_HALFPI : M_HALFPI;
+ lp.lam = 0.;
+ } else {
+ sinphi = sin(lp.phi);
+ cosphi = cos(lp.phi);
+ t = fabs (cosphi) > 1e-10 ? sinphi/cosphi : 0.;
+ n = Q->esp * cosphi * cosphi;
+ d = xy.x * sqrt (con = 1. - P->es * sinphi * sinphi) / P->k0;
+ con *= t;
+ t *= t;
+ ds = d * d;
+ lp.phi -= (con * ds / (1.-P->es)) * FC2 * (1. -
+ ds * FC4 * (5. + t * (3. - 9. * n) + n * (1. - 4 * n) -
+ ds * FC6 * (61. + t * (90. - 252. * n +
+ 45. * t) + 46. * n
+ - ds * FC8 * (1385. + t * (3633. + t * (4095. + 1575. * t)) )
+ )));
+ lp.lam = d*(FC1 -
+ ds*FC3*( 1. + 2.*t + n -
+ ds*FC5*(5. + t*(28. + 24.*t + 8.*n) + 6.*n
+ - ds * FC7 * (61. + t * (662. + t * (1320. + 720. * t)) )
+ ))) / cosphi;
+ }
+ return lp;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0, 0.0};
+ double h, g;
+
+ h = exp(xy.x / static_cast<struct pj_opaque*>(P->opaque)->esp);
+ g = .5 * (h - 1. / h);
+ h = cos (P->phi0 + xy.y / static_cast<struct pj_opaque*>(P->opaque)->esp);
+ lp.phi = asin(sqrt((1. - h * h) / (1. + g * g)));
+
+ /* Make sure that phi is on the correct hemisphere when false northing is used */
+ if (xy.y < 0. && -lp.phi+P->phi0 < 0.0) lp.phi = -lp.phi;
+
+ lp.lam = (g != 0.0 || h != 0.0) ? atan2 (g, h) : 0.;
+ return lp;
+}
+
+
+static PJ *destructor(PJ *P, int errlev) { /* Destructor */
+ if (nullptr==P)
+ return nullptr;
+
+ if (nullptr==P->opaque)
+ return pj_default_destructor(P, errlev);
+
+ pj_dealloc (static_cast<struct pj_opaque*>(P->opaque)->en);
+ return pj_default_destructor(P, errlev);
+}
+
+
+static PJ *setup(PJ *P) { /* general initialization */
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ if (P->es != 0.0) {
+ if (!(Q->en = pj_enfn(P->es)))
+ return pj_default_destructor(P, ENOMEM);
+
+ Q->ml0 = pj_mlfn(P->phi0, sin(P->phi0), cos(P->phi0), Q->en);
+ Q->esp = P->es / (1. - P->es);
+ P->inv = e_inverse;
+ P->fwd = e_forward;
+ } else {
+ Q->esp = P->k0;
+ Q->ml0 = .5 * Q->esp;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ }
+ return P;
+}
+
+
+PJ *PROJECTION(tmerc) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+
+ P->opaque = Q;
+ P->destructor = destructor;
+
+ return setup(P);
+}
diff --git a/src/projections/tobmerc.cpp b/src/projections/tobmerc.cpp
new file mode 100644
index 00000000..9c939f0b
--- /dev/null
+++ b/src/projections/tobmerc.cpp
@@ -0,0 +1,51 @@
+#define PJ_LIB__
+
+#include <float.h>
+#include <math.h>
+
+#include "proj_internal.h"
+#include "proj.h"
+#include "proj_math.h"
+#include "projects.h"
+
+PROJ_HEAD(tobmerc, "Tobler-Mercator") "\n\tCyl, Sph";
+
+#define EPS10 1.e-10
+static double logtanpfpim1(double x) { /* log(tan(x/2 + M_FORTPI)) */
+ if (fabs(x) <= DBL_EPSILON) {
+ /* tan(M_FORTPI + .5 * x) can be approximated by 1.0 + x */
+ return log1p(x);
+ }
+ return log(tan(M_FORTPI + .5 * x));
+}
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0, 0.0};
+ double cosphi;
+
+ if (fabs(fabs(lp.phi) - M_HALFPI) <= EPS10) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+
+ cosphi = cos(lp.phi);
+ xy.x = P->k0 * lp.lam * cosphi * cosphi;
+ xy.y = P->k0 * logtanpfpim1(lp.phi);
+ return xy;
+}
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0, 0.0};
+ double cosphi;
+
+ lp.phi = atan(sinh(xy.y / P->k0));
+ cosphi = cos(lp.phi);
+ lp.lam = xy.x / P->k0 / (cosphi * cosphi);
+ return lp;
+}
+
+PJ *PROJECTION(tobmerc) {
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ return P;
+}
diff --git a/src/projections/tpeqd.cpp b/src/projections/tpeqd.cpp
new file mode 100644
index 00000000..2720327a
--- /dev/null
+++ b/src/projections/tpeqd.cpp
@@ -0,0 +1,109 @@
+#define PJ_LIB__
+#include <errno.h>
+#include "proj.h"
+#include "proj_math.h"
+#include "projects.h"
+
+
+PROJ_HEAD(tpeqd, "Two Point Equidistant")
+ "\n\tMisc Sph\n\tlat_1= lon_1= lat_2= lon_2=";
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double cp1, sp1, cp2, sp2, ccs, cs, sc, r2z0, z02, dlam2;
+ double hz0, thz0, rhshz0, ca, sa, lp, lamc;
+};
+} // anonymous namespace
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0, 0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double t, z1, z2, dl1, dl2, sp, cp;
+
+ sp = sin(lp.phi);
+ cp = cos(lp.phi);
+ z1 = aacos(P->ctx, Q->sp1 * sp + Q->cp1 * cp * cos (dl1 = lp.lam + Q->dlam2));
+ z2 = aacos(P->ctx, Q->sp2 * sp + Q->cp2 * cp * cos (dl2 = lp.lam - Q->dlam2));
+ z1 *= z1;
+ z2 *= z2;
+
+ xy.x = Q->r2z0 * (t = z1 - z2);
+ t = Q->z02 - t;
+ xy.y = Q->r2z0 * asqrt (4. * Q->z02 * z2 - t * t);
+ if ((Q->ccs * sp - cp * (Q->cs * sin(dl1) - Q->sc * sin(dl2))) < 0.)
+ xy.y = -xy.y;
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double cz1, cz2, s, d, cp, sp;
+
+ cz1 = cos (hypot(xy.y, xy.x + Q->hz0));
+ cz2 = cos (hypot(xy.y, xy.x - Q->hz0));
+ s = cz1 + cz2;
+ d = cz1 - cz2;
+ lp.lam = - atan2(d, (s * Q->thz0));
+ lp.phi = aacos(P->ctx, hypot (Q->thz0 * s, d) * Q->rhshz0);
+ if ( xy.y < 0. )
+ lp.phi = - lp.phi;
+ /* lam--phi now in system relative to P1--P2 base equator */
+ sp = sin (lp.phi);
+ cp = cos (lp.phi);
+ lp.phi = aasin (P->ctx, Q->sa * sp + Q->ca * cp * (s = cos(lp.lam -= Q->lp)));
+ lp.lam = atan2 (cp * sin(lp.lam), Q->sa * cp * s - Q->ca * sp) + Q->lamc;
+ return lp;
+}
+
+
+PJ *PROJECTION(tpeqd) {
+ double lam_1, lam_2, phi_1, phi_2, A12, pp;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor(P, ENOMEM);
+ P->opaque = Q;
+
+
+ /* get control point locations */
+ phi_1 = pj_param(P->ctx, P->params, "rlat_1").f;
+ lam_1 = pj_param(P->ctx, P->params, "rlon_1").f;
+ phi_2 = pj_param(P->ctx, P->params, "rlat_2").f;
+ lam_2 = pj_param(P->ctx, P->params, "rlon_2").f;
+
+ if (phi_1 == phi_2 && lam_1 == lam_2)
+ return pj_default_destructor(P, PJD_ERR_CONTROL_POINT_NO_DIST);
+
+ P->lam0 = adjlon (0.5 * (lam_1 + lam_2));
+ Q->dlam2 = adjlon (lam_2 - lam_1);
+
+ Q->cp1 = cos (phi_1);
+ Q->cp2 = cos (phi_2);
+ Q->sp1 = sin (phi_1);
+ Q->sp2 = sin (phi_2);
+ Q->cs = Q->cp1 * Q->sp2;
+ Q->sc = Q->sp1 * Q->cp2;
+ Q->ccs = Q->cp1 * Q->cp2 * sin(Q->dlam2);
+ Q->z02 = aacos(P->ctx, Q->sp1 * Q->sp2 + Q->cp1 * Q->cp2 * cos (Q->dlam2));
+ Q->hz0 = .5 * Q->z02;
+ A12 = atan2(Q->cp2 * sin (Q->dlam2),
+ Q->cp1 * Q->sp2 - Q->sp1 * Q->cp2 * cos (Q->dlam2));
+ Q->ca = cos(pp = aasin(P->ctx, Q->cp1 * sin(A12)));
+ Q->sa = sin(pp);
+ Q->lp = adjlon ( atan2 (Q->cp1 * cos(A12), Q->sp1) - Q->hz0);
+ Q->dlam2 *= .5;
+ Q->lamc = M_HALFPI - atan2(sin(A12) * Q->sp1, cos(A12)) - Q->dlam2;
+ Q->thz0 = tan (Q->hz0);
+ Q->rhshz0 = .5 / sin (Q->hz0);
+ Q->r2z0 = 0.5 / Q->z02;
+ Q->z02 *= Q->z02;
+
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ P->es = 0.;
+
+ return P;
+}
+
diff --git a/src/projections/urm5.cpp b/src/projections/urm5.cpp
new file mode 100644
index 00000000..0e3c7e3c
--- /dev/null
+++ b/src/projections/urm5.cpp
@@ -0,0 +1,56 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(urm5, "Urmaev V") "\n\tPCyl, Sph, no inv\n\tn= q= alpha=";
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double m, rmn, q3, n;
+};
+} // anonymous namespace
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0, 0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double t;
+
+ t = lp.phi = aasin (P->ctx, Q->n * sin (lp.phi));
+ xy.x = Q->m * lp.lam * cos (lp.phi);
+ t *= t;
+ xy.y = lp.phi * (1. + t * Q->q3) * Q->rmn;
+ return xy;
+}
+
+
+PJ *PROJECTION(urm5) {
+ double alpha, t;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor(P, ENOMEM);
+ P->opaque = Q;
+
+ if (pj_param(P->ctx, P->params, "tn").i) {
+ Q->n = pj_param(P->ctx, P->params, "dn").f;
+ if (Q->n <= 0. || Q->n > 1.)
+ return pj_default_destructor(P, PJD_ERR_N_OUT_OF_RANGE);
+ } else {
+ return pj_default_destructor(P, PJD_ERR_N_OUT_OF_RANGE);
+ }
+ Q->q3 = pj_param(P->ctx, P->params, "dq").f / 3.;
+ alpha = pj_param(P->ctx, P->params, "ralpha").f;
+ t = Q->n * sin (alpha);
+ Q->m = cos (alpha) / sqrt (1. - t * t);
+ Q->rmn = 1. / (Q->m * Q->n);
+
+ P->es = 0.;
+ P->inv = nullptr;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/urmfps.cpp b/src/projections/urmfps.cpp
new file mode 100644
index 00000000..7103222a
--- /dev/null
+++ b/src/projections/urmfps.cpp
@@ -0,0 +1,76 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(urmfps, "Urmaev Flat-Polar Sinusoidal") "\n\tPCyl, Sph\n\tn=";
+PROJ_HEAD(wag1, "Wagner I (Kavraisky VI)") "\n\tPCyl, Sph";
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double n, C_y;
+};
+} // anonymous namespace
+
+#define C_x 0.8773826753
+#define Cy 1.139753528477
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0, 0.0};
+ lp.phi = aasin (P->ctx,static_cast<struct pj_opaque*>(P->opaque)->n * sin (lp.phi));
+ xy.x = C_x * lp.lam * cos (lp.phi);
+ xy.y = static_cast<struct pj_opaque*>(P->opaque)->C_y * lp.phi;
+ return xy;
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0, 0.0};
+ xy.y /= static_cast<struct pj_opaque*>(P->opaque)->C_y;
+ lp.phi = aasin(P->ctx, sin (xy.y) / static_cast<struct pj_opaque*>(P->opaque)->n);
+ lp.lam = xy.x / (C_x * cos (xy.y));
+ return lp;
+}
+
+
+static PJ *setup(PJ *P) {
+ static_cast<struct pj_opaque*>(P->opaque)->C_y = Cy / static_cast<struct pj_opaque*>(P->opaque)->n;
+ P->es = 0.;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ return P;
+}
+
+
+PJ *PROJECTION(urmfps) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor(P, ENOMEM);
+
+ P->opaque = Q;
+
+ if (pj_param(P->ctx, P->params, "tn").i) {
+ static_cast<struct pj_opaque*>(P->opaque)->n = pj_param(P->ctx, P->params, "dn").f;
+ if (static_cast<struct pj_opaque*>(P->opaque)->n <= 0. || static_cast<struct pj_opaque*>(P->opaque)->n > 1.)
+ return pj_default_destructor(P, PJD_ERR_N_OUT_OF_RANGE);
+ } else {
+ return pj_default_destructor(P, PJD_ERR_N_OUT_OF_RANGE);
+ }
+
+ return setup(P);
+}
+
+
+PJ *PROJECTION(wag1) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor(P, ENOMEM);
+ P->opaque = Q;
+
+ static_cast<struct pj_opaque*>(P->opaque)->n = 0.8660254037844386467637231707;
+ return setup(P);
+}
diff --git a/src/projections/vandg.cpp b/src/projections/vandg.cpp
new file mode 100644
index 00000000..d148e210
--- /dev/null
+++ b/src/projections/vandg.cpp
@@ -0,0 +1,106 @@
+#define PJ_LIB__
+#include "proj.h"
+#include "projects.h"
+
+PROJ_HEAD(vandg, "van der Grinten (I)") "\n\tMisc Sph";
+
+# define TOL 1.e-10
+# define THIRD .33333333333333333333
+# 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 / M_HALFPI);
+ if ((p2 - TOL) > 1.) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ 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 = M_PI * tan(.5 * asin(p2));
+ if (lp.phi < 0.) xy.y = -xy.y;
+ } else {
+ al = .5 * fabs(M_PI / lp.lam - lp.lam / M_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 = M_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 / M_PI);
+ xy.y = 1. - xy.y * (xy.y + 2. * al);
+ if (xy.y < -TOL) {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return xy;
+ }
+ if (xy.y < 0.)
+ xy.y = 0.;
+ else
+ xy.y = sqrt(xy.y) * (lp.phi < 0. ? -M_PI : M_PI);
+ }
+
+ return xy;
+}
+
+
+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 = - M_PI * ay * (r + PISQ);
+ c3 = r2 + M_TWOPI * (ay * r + M_PI * (y2 + M_PI * (ay + M_HALFPI)));
+ c2 = c1 + PISQ * (r - 3. * y2);
+ c0 = M_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. : M_PI) : acos(d);
+ lp.phi = M_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 {
+ proj_errno_set(P, PJD_ERR_TOLERANCE_CONDITION);
+ return lp;
+ }
+
+ return lp;
+}
+
+
+PJ *PROJECTION(vandg) {
+ P->es = 0.;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
+
diff --git a/src/projections/vandg2.cpp b/src/projections/vandg2.cpp
new file mode 100644
index 00000000..61d50044
--- /dev/null
+++ b/src/projections/vandg2.cpp
@@ -0,0 +1,76 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "projects.h"
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ int vdg3;
+};
+} // anonymous namespace
+
+PROJ_HEAD(vandg2, "van der Grinten II") "\n\tMisc Sph, no inv";
+PROJ_HEAD(vandg3, "van der Grinten III") "\n\tMisc Sph, no inv";
+
+#define TOL 1e-10
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(P->opaque);
+ double x1, at, bt, ct;
+
+ bt = fabs(M_TWO_D_PI * lp.phi);
+ if ((ct = 1. - bt * bt) < 0.)
+ ct = 0.;
+ else
+ ct = sqrt(ct);
+ if (fabs(lp.lam) < TOL) {
+ xy.x = 0.;
+ xy.y = M_PI * (lp.phi < 0. ? -bt : bt) / (1. + ct);
+ } else {
+ at = 0.5 * fabs(M_PI / lp.lam - lp.lam / M_PI);
+ if (Q->vdg3) {
+ x1 = bt / (1. + ct);
+ xy.x = M_PI * (sqrt(at * at + 1. - x1 * x1) - at);
+ xy.y = M_PI * x1;
+ } else {
+ x1 = (ct * sqrt(1. + at * at) - at * ct * ct) /
+ (1. + at * at * bt * bt);
+ xy.x = M_PI * x1;
+ xy.y = M_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;
+}
+
+
+PJ *PROJECTION(vandg2) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->vdg3 = 0;
+ P->fwd = s_forward;
+
+ return P;
+}
+
+PJ *PROJECTION(vandg3) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor (P, ENOMEM);
+ P->opaque = Q;
+
+ Q->vdg3 = 1;
+ P->es = 0.;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/vandg4.cpp b/src/projections/vandg4.cpp
new file mode 100644
index 00000000..d9a53c87
--- /dev/null
+++ b/src/projections/vandg4.cpp
@@ -0,0 +1,55 @@
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(vandg4, "van der Grinten IV") "\n\tMisc Sph, no inv";
+
+#define TOL 1e-10
+
+
+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) - M_HALFPI) < TOL) {
+ xy.x = 0.;
+ xy.y = lp.phi;
+ } else {
+ bt = fabs(M_TWO_D_PI * lp.phi);
+ bt2 = bt * bt;
+ ct = 0.5 * (bt * (8. - bt * (2. + bt2)) - 5.)
+ / (bt2 * (bt - 1.));
+ ct2 = ct * ct;
+ dt = M_TWO_D_PI * lp.lam;
+ dt = dt + 1. / dt;
+ dt = sqrt(dt * dt - 4.);
+ if ((fabs(lp.lam) - M_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 = M_HALFPI * x1;
+ xy.y = M_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;
+}
+
+
+PJ *PROJECTION(vandg4) {
+ P->es = 0.;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/wag2.cpp b/src/projections/wag2.cpp
new file mode 100644
index 00000000..1bee737a
--- /dev/null
+++ b/src/projections/wag2.cpp
@@ -0,0 +1,38 @@
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(wag2, "Wagner II") "\n\tPCyl, Sph";
+
+#define C_x 0.92483
+#define C_y 1.38725
+#define C_p1 0.88022
+#define C_p2 0.88550
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ lp.phi = aasin (P->ctx,C_p1 * sin (C_p2 * lp.phi));
+ xy.x = C_x * lp.lam * cos (lp.phi);
+ xy.y = C_y * lp.phi;
+ return (xy);
+}
+
+
+static LP s_inverse (XY xy, PJ *P) { /* Spheroidal, inverse */
+ LP lp = {0.0,0.0};
+ lp.phi = xy.y / C_y;
+ lp.lam = xy.x / (C_x * cos(lp.phi));
+ lp.phi = aasin (P->ctx,sin(lp.phi) / C_p1) / C_p2;
+ return (lp);
+}
+
+
+PJ *PROJECTION(wag2) {
+ P->es = 0.;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+ return P;
+}
diff --git a/src/projections/wag3.cpp b/src/projections/wag3.cpp
new file mode 100644
index 00000000..bb1b4d49
--- /dev/null
+++ b/src/projections/wag3.cpp
@@ -0,0 +1,50 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(wag3, "Wagner III") "\n\tPCyl, Sph\n\tlat_ts=";
+
+#define TWOTHIRD 0.6666666666666666666667
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double C_x;
+};
+} // anonymous namespace
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ xy.x = static_cast<struct pj_opaque*>(P->opaque)->C_x * lp.lam * cos(TWOTHIRD * 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.phi = xy.y;
+ lp.lam = xy.x / (static_cast<struct pj_opaque*>(P->opaque)->C_x * cos(TWOTHIRD * lp.phi));
+ return lp;
+}
+
+
+PJ *PROJECTION(wag3) {
+ double ts;
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor(P, ENOMEM);
+
+ P->opaque = Q;
+
+ ts = pj_param (P->ctx, P->params, "rlat_ts").f;
+ static_cast<struct pj_opaque*>(P->opaque)->C_x = cos (ts) / cos (2.*ts/3.);
+ P->es = 0.;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/wag7.cpp b/src/projections/wag7.cpp
new file mode 100644
index 00000000..c8807f12
--- /dev/null
+++ b/src/projections/wag7.cpp
@@ -0,0 +1,30 @@
+#define PJ_LIB__
+
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(wag7, "Wagner VII") "\n\tMisc Sph, no inv";
+
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0, 0.0};
+ double theta, ct, D;
+
+ (void) P; /* Shut up compiler warnnings about unused P */
+
+ theta = asin (xy.y = 0.90630778703664996 * sin(lp.phi));
+ xy.x = 2.66723 * (ct = cos (theta)) * sin (lp.lam /= 3.);
+ xy.y *= 1.24104 * (D = 1/(sqrt (0.5 * (1 + ct * cos (lp.lam)))));
+ xy.x *= D;
+ return (xy);
+}
+
+
+PJ *PROJECTION(wag7) {
+ P->fwd = s_forward;
+ P->inv = nullptr;
+ P->es = 0.;
+ return P;
+}
diff --git a/src/projections/wink1.cpp b/src/projections/wink1.cpp
new file mode 100644
index 00000000..de2f55ee
--- /dev/null
+++ b/src/projections/wink1.cpp
@@ -0,0 +1,46 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(wink1, "Winkel I") "\n\tPCyl, Sph\n\tlat_ts=";
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double cosphi1;
+};
+} // anonymous namespace
+
+
+
+static XY s_forward (LP lp, PJ *P) { /* Spheroidal, forward */
+ XY xy = {0.0,0.0};
+ xy.x = .5 * lp.lam * (static_cast<struct pj_opaque*>(P->opaque)->cosphi1 + 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.phi = xy.y;
+ lp.lam = 2. * xy.x / (static_cast<struct pj_opaque*>(P->opaque)->cosphi1 + cos(lp.phi));
+ return (lp);
+}
+
+
+PJ *PROJECTION(wink1) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor(P, ENOMEM);
+ P->opaque = Q;
+
+ static_cast<struct pj_opaque*>(P->opaque)->cosphi1 = cos (pj_param(P->ctx, P->params, "rlat_ts").f);
+ P->es = 0.;
+ P->inv = s_inverse;
+ P->fwd = s_forward;
+
+ return P;
+}
diff --git a/src/projections/wink2.cpp b/src/projections/wink2.cpp
new file mode 100644
index 00000000..74a47283
--- /dev/null
+++ b/src/projections/wink2.cpp
@@ -0,0 +1,56 @@
+#define PJ_LIB__
+
+#include <errno.h>
+#include <math.h>
+
+#include "projects.h"
+
+PROJ_HEAD(wink2, "Winkel II") "\n\tPCyl, Sph, no inv\n\tlat_1=";
+
+namespace { // anonymous namespace
+struct pj_opaque {
+ double cosphi1;
+};
+} // anonymous namespace
+
+#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;
+
+ xy.y = lp.phi * M_TWO_D_PI;
+ k = M_PI * sin (lp.phi);
+ lp.phi *= 1.8;
+ for (i = MAX_ITER; i ; --i) {
+ lp.phi -= V = (lp.phi + sin (lp.phi) - k) /
+ (1. + cos (lp.phi));
+ if (fabs (V) < LOOP_TOL)
+ break;
+ }
+ if (!i)
+ lp.phi = (lp.phi < 0.) ? -M_HALFPI : M_HALFPI;
+ else
+ lp.phi *= 0.5;
+ xy.x = 0.5 * lp.lam * (cos (lp.phi) + static_cast<struct pj_opaque*>(P->opaque)->cosphi1);
+ xy.y = M_FORTPI * (sin (lp.phi) + xy.y);
+ return xy;
+}
+
+
+PJ *PROJECTION(wink2) {
+ struct pj_opaque *Q = static_cast<struct pj_opaque*>(pj_calloc (1, sizeof (struct pj_opaque)));
+ if (nullptr==Q)
+ return pj_default_destructor(P, ENOMEM);
+ P->opaque = Q;
+
+ static_cast<struct pj_opaque*>(P->opaque)->cosphi1 = cos(pj_param(P->ctx, P->params, "rlat_1").f);
+ P->es = 0.;
+ P->inv = nullptr;
+ P->fwd = s_forward;
+
+ return P;
+}