/*--------------------------------------------------------------------
 *
 *	Copyright (c) 1991-2024 by the GMT Team (https://www.generic-mapping-tools.org/team.html)
 *	See LICENSE.TXT file for copying and redistribution conditions.
 *
 *	This program is free software; you can redistribute it and/or modify
 *	it under the terms of the GNU Lesser General Public License as published by
 *	the Free Software Foundation; version 3 or any later version.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU Lesser General Public License for more details.
 *
 *	Contact info: www.generic-mapping-tools.org
 *--------------------------------------------------------------------*/
/*
 * gmt_proj.c contains the specific projection functions that convert
 * longitude and latitude to x, y.  These are used via function pointers
 * set in gmt_map.c
 *
 * Map_projections include functions that will set up the transformation
 * between xy and latlon for several map projections.
 *
 * A few of the core coordinate transformation functions are based on similar
 * FORTRAN routines written by Pat Manley, Doug Shearer, and Bill Haxby, and
 * have been rewritten in C and subsequently streamlined.  The Lambert conformal
 * was originally coded up by Bernie Coakley.  The rest is coded by Wessel/Smith
 * based on P. Snyder, "Map Projections - a working manual", USGS Prof paper 1395.
 *
 * Transformations supported (both forward and inverse):
 *
 * Non-geographic projections:
 *
 *	Linear x/y[/z] scaling
 *	Polar (theta, r) scaling
 *
 * Map projections:
 *
 *  Cylindrical:
 *	Mercator
 *	Cassini Cylindrical
 *	Cylindrical Stereographic (e.g., Gall, B.S.A.M)
 *	Miller Cylindrical
 *	Oblique Mercator
 *	TM Transverse Mercator (Ellipsoidal and Spherical)
 *	UTM Universal Transverse Mercator
 *	Cylindrical Equal-area (e.g., Gall, Behrmann)
 *	Cylindrical Equidistant (e.g., Plate Carree)
 *  Conic:
 *	Albers Equal-Area Conic
 *	Lambert Conformal Conic
 *	Equidistant Conic
 *  Azimuthal:
 *	Stereographic Conformal
 *	Lambert Azimuthal Equal-Area
 *	Orthographic
 *	Azimuthal Equidistant
 *	Gnomonic
 *  Thematic:
 *	Mollweide Equal-Area
 *	Hammer-Aitoff Equal-Area
 *	Sinusoidal Equal-Area
 *	Winkel Tripel
 *	Robinson
 *	Eckert IV
 *	Eckert IV
 *	Van der Grinten
 *
 * Author:	Paul Wessel
 * Date:	1-JAN-2010
 * Version:	5.x
 */

/*
 * B) List of exported gmtlib_* functions available to libraries via gmt_internals.h:
 *
 *	gmtlib_genper_map_clip_path
 *	gmtlib_iobl
 *	gmtlib_itranslin
 *	gmtlib_translin
 *
 * NOTE: gmt_proj.c is included directly into gmt_map.c
 */

#include "gmt_dev.h"
#include "gmt_internals.h"

#define GMT_PROJ_MAX_ITERATIONS	200
#define GMT_PROJ_CONV_LIMIT	1e-9
#define gmt_M_proj_is_zero(x) (fabs (x) < GMT_PROJ_CONV_LIMIT)

GMT_LOCAL double gmtproj_robinson_spline (struct GMT_CTRL *GMT, double xp, double *x, double *y, double *c) {
	/* Returns the interpolated value y(xp) from the Robinson coefficients */

	int j = 0, j1;
	double yp, a, b, h, ih, dx;

	if (xp < x[0] || xp > x[GMT_N_ROBINSON-1])	/* Desired point outside data range */
		return (GMT->session.d_NaN);

	while (j < GMT_N_ROBINSON && x[j] <= xp) j++;
	if (j == GMT_N_ROBINSON) j--;
	if (j > 0) j--;

	dx = xp - x[j];
	switch (GMT->current.setting.interpolant) {	/* gmtproj_vrobinson would not allow case 0 so only GMT_SPLINE_AKIMA | GMT_SPLINE_CUBIC is possible */
		case GMT_SPLINE_AKIMA:
			yp = ((c[3*j+2]*dx + c[3*j+1])*dx + c[3*j])*dx + y[j];
			break;
		case GMT_SPLINE_CUBIC:
			j1 = j + 1;
			h = x[j1] - x[j];
			ih = 1.0 / h;
			a = (x[j1] - xp) * ih;
			b = dx * ih;
			yp = a * y[j] + b * y[j1] + ((a*a*a - a) * c[j] + (b*b*b - b) * c[j1]) * (h*h) / 6.0;
			break;
		default:
			yp = 0;
	}
	return (yp);
}

GMT_LOCAL void gmtproj_check_R_J (struct GMT_CTRL *GMT, double *clon)	/* Make sure -R and -J agree for global plots; J given priority */ {
	double lon0 = 0.5 * (GMT->common.R.wesn[XLO] + GMT->common.R.wesn[XHI]);

	if (GMT->current.map.is_world && lon0 != *clon) {
		GMT->common.R.wesn[XLO] = *clon - 180.0;
		GMT->common.R.wesn[XHI] = *clon + 180.0;
		GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Central meridian set with -J (%g) implies -R%g/%g/%g/%g\n",
			*clon, GMT->common.R.wesn[XLO], GMT->common.R.wesn[XHI], GMT->common.R.wesn[YLO], GMT->common.R.wesn[YHI]);
	}
	else if (!GMT->current.map.is_world) {
		lon0 = *clon - 360.0;
		while (lon0 < GMT->common.R.wesn[XLO]) lon0 += 360.0;
		if (lon0 > GMT->common.R.wesn[XHI]) GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Central meridian outside region\n");
	}
}

GMT_LOCAL void gmtproj_iwinkel_sub (struct GMT_CTRL *GMT, double y, double *phi) {
	/* Valid only along meridian 180 degree from central meridian.  Used in left/right_winkel only */
	int n_iter = 0;
	double c, phi0, delta, sp, cp;

	c = 2.0 * y * GMT->current.proj.i_EQ_RAD;
	*phi = y * GMT->current.proj.i_EQ_RAD;
	do {
		phi0 = *phi;
		sincos (phi0, &sp, &cp);
		*phi = phi0 - (phi0 + M_PI_2 * sp - c) / (1.0 + M_PI_2 * cp);
		delta = fabs (*phi - phi0);
		n_iter++;
	}
	while (delta > GMT_PROJ_CONV_LIMIT && n_iter < GMT_PROJ_MAX_ITERATIONS);
	*phi *= R2D;
}

GMT_LOCAL void gmtproj_ipolyconic_sub (struct GMT_CTRL *GMT, double y, double lon, double *x) {
	/* Used in left/right_polyconic only */
	double E, sp, cp, phi0, phi, delta;
	int n_iter = 0;

	*x = lon;
	gmt_M_wind_lon (GMT, *x);
	y *= GMT->current.proj.i_EQ_RAD;
	y += GMT->current.proj.pole * D2R;
	if (gmt_M_proj_is_zero (y))
		*x *= GMT->current.proj.EQ_RAD * D2R;
	else {
		phi = y;
		do {
			phi0 = phi;
			sincos (phi, &sp, &cp);
			E = (*x) * sp;
			cp /= sp;	/* = cot(phi) */
			phi = y - cp * (1.0 - cosd(E));
			delta = fabs (phi - phi0);
			n_iter++;
		}
		while (delta > GMT_PROJ_CONV_LIMIT && n_iter < GMT_PROJ_MAX_ITERATIONS);
		*x = GMT->current.proj.EQ_RAD * cp * sin(E);
	}
}

/* conversion from geodetic latitude to geocentric latitude */

GMT_LOCAL double gmtproj_genper_getgeocentric (struct GMT_CTRL *GMT, double phi, double h) {
	double phig, sphi, cphi, N1;

	sincosd (phi, &sphi, &cphi);

	N1 = GMT->current.proj.EQ_RAD/sqrt(1.0 - (GMT->current.proj.ECC2*sphi*sphi));

	phig = phi - asind(N1*GMT->current.proj.ECC2*sphi*cphi/((h/GMT->current.proj.EQ_RAD+1.0)*GMT->current.proj.EQ_RAD));

	return (phig);
}

GMT_LOCAL void gmtproj_genper_toxy (struct GMT_CTRL *P, double lat, double lon, double h, double *x, double *y) {
	double angle;
	double xp, yp, rp;
	double N, C, S, K;
	double sphi, cphi;
	double sdphi, cdphi;
	double sphi1, cphi1;
	double sdlon, cdlon;

	cdphi = P->current.proj.g_cdphi;
	sdphi = P->current.proj.g_sdphi;

	cphi1 = P->current.proj.g_cphi1;
	sphi1 = P->current.proj.g_sphi1;
	h *= METERS_IN_A_KM;

	sincosd (lat, &sphi, &cphi);

	N = P->current.proj.g_R/sqrt(1.0 - (P->current.proj.ECC2*sphi*sphi));
	C = ((N+h)/P->current.proj.g_R)*cphi;
	S = ((N*P->current.proj.one_m_ECC2 + h)/P->current.proj.g_R)*sphi;

	sincosd (lon - P->current.proj.g_lon0, &sdlon, &cdlon);

	K = P->current.proj.g_H / (P->current.proj.g_P*cdphi - S*sphi1 - C*cphi1*cdlon);

	xp = K*C*sdlon;
	yp = K*(P->current.proj.g_P*sdphi + S*cphi1 - C*sphi1*cdlon);

	rp = sqrt(xp*xp + yp*yp);

	if (rp > P->current.proj.g_rmax) {
		angle = atan2(xp, yp);
		sincos (angle, &xp, &yp);
		xp *= P->current.proj.g_rmax;
		yp *= P->current.proj.g_rmax;
	}
	*x = xp;
	*y = yp;

	if (P->current.proj.g_debug > 1) {
		GMT_Report (P->parent, GMT_MSG_DEBUG, "\n");
		GMT_Report (P->parent, GMT_MSG_DEBUG, "lat  %12.3f\n", lat);
		GMT_Report (P->parent, GMT_MSG_DEBUG, "lon  %12.3f\n", lon);
		GMT_Report (P->parent, GMT_MSG_DEBUG, "h    %12.3f\n", h);
		GMT_Report (P->parent, GMT_MSG_DEBUG, "N    %12.1f\n", N);
		GMT_Report (P->parent, GMT_MSG_DEBUG, "C    %12.7f\n", C);
		GMT_Report (P->parent, GMT_MSG_DEBUG, "S    %12.7f\n", S);
		GMT_Report (P->parent, GMT_MSG_DEBUG, "K    %12.1f\n", K);
		GMT_Report (P->parent, GMT_MSG_DEBUG, "x    %12.1f\n", *x);
		GMT_Report (P->parent, GMT_MSG_DEBUG, "y    %12.1f\n", *y);
	}
}

GMT_LOCAL void gmtproj_genper_to_xtyt (struct GMT_CTRL *GMT, double angle, double x, double y, double offset, double *xt, double *yt) {
	double A, theta, xp, yp;

	theta = GMT->current.proj.g_azimuth - angle;

	A = (y * GMT->current.proj.g_cos_azimuth + x * GMT->current.proj.g_sin_azimuth) * GMT->current.proj.g_sin_tilt / GMT->current.proj.g_H + GMT->current.proj.g_cos_tilt;

	if (A > 0.0) {
		xp = (x * GMT->current.proj.g_cos_azimuth - y * GMT->current.proj.g_sin_azimuth) * GMT->current.proj.g_cos_tilt / A;
		yp = (y * GMT->current.proj.g_cos_azimuth + x * GMT->current.proj.g_sin_azimuth) / A;
		if (fabs(yp) > fabs(GMT->current.proj.g_max_yt)) {
			yp = -GMT->current.proj.g_max_yt;
			xp = -yp * tand(theta);
		}
	}
	else {
		yp = -GMT->current.proj.g_max_yt;
		xp = -yp * tand(theta);
	}

	yp -= offset;

	*xt = xp * GMT->current.proj.g_cos_twist - yp * GMT->current.proj.g_sin_twist;
	*yt = yp * GMT->current.proj.g_cos_twist + xp * GMT->current.proj.g_sin_twist;

	return;
}

GMT_LOCAL int gmtproj_genper_tolatlong (struct GMT_CTRL *GMT, double x, double y, double h, double *lat, double *lon) {
	double P, H, B, D;
	double u, v, t, Kp, X, Y;
	double E, S;
	double phi_last;
	double Kp2;
	double phi, sphi;
	double cphig;
	double e2, R, one_m_e2;
	double cphi1, sphi1;

	int niter;
	int set_exit = 0;

	h *= METERS_IN_A_KM;

	H = GMT->current.proj.g_H;
	P = GMT->current.proj.g_P;
	R = GMT->current.proj.g_R;

	one_m_e2 = GMT->current.proj.one_m_ECC2;
	e2 = GMT->current.proj.ECC2;

	cphig = GMT->current.proj.g_cphig;
	cphi1 = GMT->current.proj.g_cphi1;
	sphi1 = GMT->current.proj.g_sphi1;

	B = GMT->current.proj.g_B;
	D = GMT->current.proj.g_D;

	u = GMT->current.proj.g_BLH - GMT->current.proj.g_DG*y + GMT->current.proj.g_BJ*y + GMT->current.proj.g_DHJ;
	v = GMT->current.proj.g_LH2 + GMT->current.proj.g_G*y*y - GMT->current.proj.g_HJ*y + one_m_e2*x*x;

	if (GMT->current.proj.g_debug > 1) {
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: gmtproj_genper_tolatlong - 1\n");
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: x    %12.1f\n", x);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: y    %12.1f\n", y);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: \n");
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: P    %12.7f\n", P);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: phig %12.7f\n", GMT->current.proj.g_phig);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: \n");
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: B    %12.7f\n", B);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: D    %12.7f\n", D);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: u    %12.1f\n", u);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: v    %12.6e\n", v);
	}
	E = 1;

	t = P*P*(1.0 - e2*cphig*cphig) - E*one_m_e2;
	Kp2 = (1.0 - 4.0*(t/u)*(v/u));
	if (Kp2 < 0.0)
		Kp = -u/(2.0*t);
	else
		Kp = (-u + sqrt(u*u-4.0*t*v))/(2.0*t);
	X = R*((B-H/Kp)*cphi1 - (y/Kp - D)*sphi1);
	Y = R*x/Kp;
	S = (y/Kp-D)*cphi1 + (B-H/Kp)*sphi1;

	if (gmt_M_is_dnan(Kp) || gmt_M_is_dnan(X) || gmt_M_is_dnan(Y) || gmt_M_is_dnan(S)) set_exit++;

	if (set_exit == 1) {
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: gmtproj_genper_tolatlong - 2\n");
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: x    %12.1f\n", x);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: y    %12.1f\n", y);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: \n");
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: P    %12.7f\n", P);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: phig %12.7f\n", GMT->current.proj.g_phig);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: \n");
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: B    %12.7f\n", B);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: D    %12.7f\n", D);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: u    %12.1f\n", u);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: v    %12.6e\n", v);
	}
	if (set_exit || GMT->current.proj.g_debug > 1) {
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: t    %12.7f\n", t);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: Kp   %12.1f\n", Kp);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: Kp2  %12.1f\n", Kp2);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: X    %12.1f\n", X);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: Y    %12.1f\n", Y);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: S    %12.7f\n", S);
	}

	if (h == 0) {
		phi = atan(S/sqrt(one_m_e2*(1.0 - e2 - S*S)));
		/* if (gmt_M_is_dnan(phi)) set_exit++; */
	}
	else {
		double t1, t2;
		niter = 0;
		t2 = h*h/(R*R*one_m_e2);

		sphi = S/(one_m_e2/sqrt(1.0 - e2*S*S) + h/R);
		phi = asin(sphi);

		t1 = (1.0/sqrt(1.0 - e2*sphi*sphi) + h/R);
		E = t1 * t1 - e2*sphi*sphi*(1.0/(1.0 - e2*sphi*sphi) - t2);

		if (gmt_M_is_dnan(E)) set_exit++;

		if (set_exit == 1) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: gmtproj_genper_tolatlong - 3\n");
		if (GMT->current.proj.g_debug > 1 || set_exit) {
			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: asinS %12.7f\n", asind(S));
			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: phi   %12.7f\n", R2D*phi);
			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: E     %12.7f\n", E);
		}

		do {
			niter++;
			phi_last = phi;
			t = P*P*(1.0 - e2*cphig*cphig) - E*one_m_e2;
			Kp2 = (1.0 - 4.0*(t/u)*(v/u));
			if (Kp2 < 0.0)
				Kp = -u/(2.0*t);
			else
				Kp = (-u + sqrt(u*u-4.0*t*v))/(2.0*t);
			X = R*((B-H/Kp)*cphi1 - (y/Kp - D)*sphi1);
			Y = R*x/Kp;
			S = (y/Kp-D)*cphi1 + (B-H/Kp)*sphi1;
			sphi = S/(one_m_e2/sqrt(1.0 - e2*sphi*sphi) + h/R);
			phi = asin(sphi);
			t1 = (1.0/sqrt(1.0 - e2*sphi*sphi) + h/R);
			E = t1 * t1 - e2*sphi*sphi*(1.0/(1.0 - e2*sphi*sphi) - t2);

			if (gmt_M_is_dnan(Kp) || gmt_M_is_dnan(X) || gmt_M_is_dnan(Y) || gmt_M_is_dnan(S) || gmt_M_is_dnan(phi) || gmt_M_is_dnan(E)) set_exit++;
			if (set_exit == 1) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: gmtproj_genper_tolatlong - 4 \n");
			if (set_exit || GMT->current.proj.g_debug > 1) {
				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: iter %d\n", niter);
				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: t    %12.7f\n", t);
				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: Kp   %12.1f\n", Kp);
				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: X    %12.1f\n", X);
				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: Y    %12.1f\n", Y);
				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: S    %12.7f\n", S);
				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: phi  %12.7f\n", phi*R2D);
				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: E    %12.7f\n", E);
			}
		}
		while (fabs(phi - phi_last) > 1e-7);
	}
	if (set_exit == 1) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: gmtproj_genper_tolatlong - 5\n");
	if (set_exit || GMT->current.proj.g_debug > 1) {
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: gmtproj_genper_tolatlong phi    %12.7f\n", phi*R2D);
		return GMT_PROJECTION_ERROR;
	}
	*lat = phi * R2D;
	*lon = atan2d (Y, X) + GMT->current.proj.g_lon0;
	return (GMT_OK);
}

GMT_LOCAL void gmtproj_genper_setup (struct GMT_CTRL *GMT, double h0, double altitude, double lat, double lon0) {
/* if ellipsoid lat0 is geodetic latitude and must convert to geocentric latitude */

	double N1, phig_last;
	double R, H, P;
	double sphi1, cphi1, sphig, cphig;
	double a, e2, phig;

	int niter;

	a = GMT->current.proj.EQ_RAD;
	e2 = GMT->current.proj.ECC2;

	h0 *= METERS_IN_A_KM;

	sincosd (lat, &sphi1, &cphi1);
	sphig = sphi1; cphig = cphi1;

	N1 = a / sqrt (1.0 - (e2*sphi1*sphi1));
	niter = 0;

	if (GMT->current.proj.g_radius) {	/* Altitude given as the radial distance from the center of the Earth (in km) */
		H = fabs (altitude * METERS_IN_A_KM) - a;
		P = H/a + 1.0;
		phig = lat;
	}
	else if (GMT->current.proj.g_geosync) {	/* Select implicit altitude of geosynchronous viewpoint */
		double temp = 86164.1/TWO_PI;	/* Siderial day rotation rate */
		H = pow (3.98603e14*temp*temp, 0.3333) - a;	/* Standard gravitational parameter GM for Earth */
		P = H/a + 1.0;
		phig = lat - asind (N1*e2*sphi1*cphi1/(P*a));
		sincosd (phig, &sphig, &cphig);
		if (cphi1 != 0.0)
			H = P*a*cphig/cphi1 - N1 - h0;
		else
			H = P*a - N1 - h0;
	}
	else if (GMT->current.proj.g_earth_radius) {	/* Altitude given (in Earth radii) */
		P = altitude;
		/* need to setup H from P equation */
		phig = lat - asind (N1*e2*sphi1*cphi1/(P*a));
		sincosd (phig, &sphig, &cphig);
		if (cphi1 != 0.0)
			H = P*a*cphig/cphi1 - N1 - h0;
		else
			H = P*a - N1 - h0;
	}
	else {	/* Altitude given as normal (in km) */
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: altitude %f\n", altitude);
		H = altitude * METERS_IN_A_KM;
		/* need to setup P from iterating phig */
		phig = lat;
		do {
			niter++;
			sincosd (phig, &sphig, &cphig);
			P = (cphi1/cphig) * (H + N1 + h0)/a;
			phig_last = phig;
			phig = lat - asind(N1*e2*sphi1*cphi1/(P*a));
			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: %2d P %12.7f phig %12.7f\n", niter, P, phig);
		}
		while (fabs (phig - phig_last) > GMT_PROJ_CONV_LIMIT);
		sincosd (phig, &sphig, &cphig);
		P = (cphi1/cphig)*(H + N1 + h0)/a;
	}

	/* R = H/(P-1.0); */
	R = a;
	/* XXX Which one is it ? */

	GMT->current.proj.g_H = H;
	GMT->current.proj.g_P = P;
	GMT->current.proj.g_R = R;
	GMT->current.proj.g_lon0 = lon0;
	GMT->current.proj.g_sphi1 = sphi1;
	GMT->current.proj.g_cphi1 = cphi1;

	GMT->current.proj.g_phig = phig;
	GMT->current.proj.g_sphig = sphig;
	GMT->current.proj.g_cphig = cphig;

	sincosd (lat-phig, &(GMT->current.proj.g_sdphi), &(GMT->current.proj.g_cdphi));

	GMT->current.proj.g_L = 1.0 - e2*cphi1*cphi1;
	GMT->current.proj.g_G = 1.0 - e2*sphi1*sphi1;
	GMT->current.proj.g_J = 2.0*e2*sphi1*cphi1;
	GMT->current.proj.g_B = P*GMT->current.proj.g_cdphi;
	GMT->current.proj.g_D = P*GMT->current.proj.g_sdphi;
	GMT->current.proj.g_BLH = -2.0*GMT->current.proj.g_B*GMT->current.proj.g_L*H;
	GMT->current.proj.g_DG = 2.0*GMT->current.proj.g_D*GMT->current.proj.g_G;
	GMT->current.proj.g_BJ = GMT->current.proj.g_B*GMT->current.proj.g_J;
	GMT->current.proj.g_HJ = H*GMT->current.proj.g_J;
	GMT->current.proj.g_DHJ = GMT->current.proj.g_D*GMT->current.proj.g_HJ;
	GMT->current.proj.g_LH2 = GMT->current.proj.g_L*H*H;

	if (GMT->current.proj.g_debug > 0) {
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: a    %12.4f\n", a);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: R    %12.4f\n", R);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: e^2  %12.7f\n", e2);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: H    %12.4f\n", H);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: phi1 %12.4f\n", lat);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: lon0 %12.4f\n", lon0);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: h0   %12.4f\n", h0);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: N1   %12.1f\n", N1);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: P    %12.7f\n", P);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: phig %12.7f\n", phig);
	}

	return;
}

/* LINEAR TRANSFORMATIONS */

void gmtlib_translin (struct GMT_CTRL *GMT, double forw, double *inv)	/* Linear forward */ {
	gmt_M_unused(GMT);
	*inv = forw;
}

GMT_LOCAL void gmtproj_translind (struct GMT_CTRL *GMT, double forw, double *inv)	/* Linear forward, but with degrees*/ {
	gmt_M_wind_lon (GMT, forw)	/* Make sure forw is in -180/+180 range after removing central meridian */
	*inv = forw ;
}

GMT_LOCAL void gmtproj_itranslind (struct GMT_CTRL *GMT, double *forw, double inv)	/* Linear inverse, but with degrees*/ {
	while (inv < -GMT_180) inv += 360.0;
	while (inv > +GMT_180) inv -= 360.0;
	*forw = inv + GMT->current.proj.central_meridian;
}

void gmtlib_itranslin (struct GMT_CTRL *GMT, double *forw, double inv)	/* Linear inverse */ {
	gmt_M_unused(GMT);
	*forw = inv;
}

GMT_LOCAL void gmtproj_translog10 (struct GMT_CTRL *GMT, double forw, double *inv)	/* Log10 forward */ {
	*inv = d_log10 (GMT, forw);
}

GMT_LOCAL void gmtproj_itranslog10 (struct GMT_CTRL *GMT, double *forw, double inv) /* Log10 inverse */ {
	gmt_M_unused(GMT);
	*forw = pow (10.0, inv);
}

GMT_LOCAL void gmtproj_transpowx (struct GMT_CTRL *GMT, double x, double *x_in)	/* pow x forward */ {
	*x_in = pow (x, GMT->current.proj.xyz_pow[GMT_X]);
}

GMT_LOCAL void gmtproj_itranspowx (struct GMT_CTRL *GMT, double *x, double x_in) /* pow x inverse */ {
	*x = pow (x_in, GMT->current.proj.xyz_ipow[GMT_X]);
}

GMT_LOCAL void gmtproj_transpowy (struct GMT_CTRL *GMT, double y, double *y_in)	/* pow y forward */ {
	*y_in = pow (y, GMT->current.proj.xyz_pow[GMT_Y]);
}

GMT_LOCAL void gmtproj_itranspowy (struct GMT_CTRL *GMT, double *y, double y_in) /* pow y inverse */ {
	*y = pow (y_in, GMT->current.proj.xyz_ipow[GMT_Y]);
}

GMT_LOCAL void gmtproj_transpowz (struct GMT_CTRL *GMT, double z, double *z_in)	/* pow z forward */ {
	*z_in = pow (z, GMT->current.proj.xyz_pow[GMT_Z]);
}

GMT_LOCAL void gmtproj_itranspowz (struct GMT_CTRL *GMT, double *z, double z_in) /* pow z inverse */ {
	*z = pow (z_in, GMT->current.proj.xyz_ipow[GMT_Z]);
}

/* -JP POLAR (r-theta) PROJECTION */

GMT_LOCAL double gmtproj_planet_radius (struct GMT_CTRL *GMT, char *modifier) {
	static char *U[2] = {"m", "km"};
	unsigned int k = 0;
	double r;
	/* Set planetary radius in correct units (m or km) depending on y-range */
	r = GMT->current.setting.ref_ellipsoid[GMT->current.setting.proj_ellipsoid].eq_radius;	/* In meters */
	if ((r/ (GMT->common.R.wesn[YHI] - GMT->common.R.wesn[YLO])) >= METERS_IN_A_KM) {	/* -R seems given in km */
		r /= METERS_IN_A_KM;
		k = 1;
	}
	GMT_Report (GMT->parent, GMT_MSG_INFORMATION, "Planetary radius (%s) automatically set to %g %s\n", modifier, r, U[k]);
	return (r);
}

GMT_LOCAL void gmtproj_vpolar (struct GMT_CTRL *GMT, double lon0) {
	/* Set up a Polar (theta,r) transformation */

	GMT->current.proj.p_base_angle = lon0;
	GMT->current.proj.central_meridian = 0.5 * (GMT->common.R.wesn[XHI] + GMT->common.R.wesn[XLO]);

	if (GMT->current.proj.flip) {	/* Want radial direction inwards */
		if (GMT->current.proj.flip_radius < 0.0)	/* Flag to just flip z = north - r */
			GMT->current.proj.flip_radius = GMT->common.R.wesn[YHI];
		else if (GMT->current.proj.flip_radius == 0.0)	/* Flag to just flip z = planet_radius - r */
			GMT->current.proj.flip_radius = gmtproj_planet_radius (GMT, "+fp");
		/* else the radius was set specifically */
	}
	if (GMT->current.proj.z_down) {	/* Annotate a flavor of z = radius - r */
		if (GMT->current.proj.z_down == GMT_ZDOWN_ZP) {	/* Given z; annotate r = planet_radius - z */
			if (GMT->current.proj.flip_radius > 0.0) /* Already obtained above */
				GMT->current.proj.z_radius = GMT->current.proj.flip_radius;
			else
				GMT->current.proj.z_radius = GMT->current.proj.flip_radius = gmtproj_planet_radius (GMT, "+zp");
			GMT->current.proj.flip = true;
		}
		else if (GMT->current.proj.z_down == GMT_ZDOWN_Z)	/* z = north - r */
			GMT->current.proj.z_radius = GMT->common.R.wesn[YHI];
	}

	/* Plus pretend that it is kind of a geographic polar projection */
	GMT->current.proj.north_pole = GMT->current.proj.got_elevations;
	GMT->current.proj.pole = (GMT->current.proj.got_elevations) ? 90.0 : 0.0;
	GMT->current.proj.radial_offset /= GMT->current.proj.pars[0];	/* Convert any radial offset in inches to user units so we can use it in gmtproj_polar/ipolar */
}

GMT_LOCAL void gmtproj_polar (struct GMT_CTRL *GMT, double x, double y, double *x_i, double *y_i) {
	/* Transform x and y to polar(cylindrical) coordinates */
	if (GMT->current.proj.got_azimuths) x = 90.0 - x;		/* Azimuths, not directions given as x */
	if (GMT->current.proj.flip) y = GMT->current.proj.flip_radius - y;		/* Depth down or elevations given as y */
	sincosd (x - GMT->current.proj.p_base_angle, y_i, x_i);	/* Change base line angle */
	(*x_i) *= (y + GMT->current.proj.radial_offset);	/* Allow for inner circle radius before we start plotting */
	(*y_i) *= (y + GMT->current.proj.radial_offset);
}

GMT_LOCAL void gmtproj_ipolar (struct GMT_CTRL *GMT, double *x, double *y, double x_i, double y_i) {
	/* Inversely transform both x and y from polar(cylindrical) coordinates */
	*x = d_atan2d (y_i, x_i) + GMT->current.proj.p_base_angle;
	if (GMT->current.proj.got_azimuths) *x = 90.0 - (*x);		/* Azimuths, not directions for x */
	*y = hypot (x_i, y_i) - GMT->current.proj.radial_offset;	/* Allow for inner circle radius */
	if (GMT->current.proj.flip) *y = GMT->current.proj.flip_radius - (*y);    /* Depth down or elevations for y */
}

/* -JM MERCATOR PROJECTION */

GMT_LOCAL void gmtproj_vmerc (struct GMT_CTRL *GMT, double lon0, double lat0) {
	/* Set up a Mercator transformation with origin at (lon0, lat0) */

	double aux_lat0 = (GMT->current.proj.GMT_convert_latitudes) ? gmt_M_latg_to_latc (GMT, lat0) : lat0;

	GMT->current.proj.central_meridian = lon0;
	/* Need geodetic latitude in this expression: */
	GMT->current.proj.j_x = cosd (lat0) / d_sqrt (1.0 - GMT->current.proj.ECC2 * sind (lat0) * sind (lat0)) * GMT->current.proj.EQ_RAD;
	GMT->current.proj.j_ix = 1.0 / GMT->current.proj.j_x;
	/* Need conformal latitude in this expression (same as in gmtproj_merc_sph) */
	GMT->current.proj.j_yc = (fabs (lat0) > 0.0) ? GMT->current.proj.j_x * d_log (GMT, tand (45.0 + 0.5 * aux_lat0)) : 0.0;
}

/* Mercator projection for the sphere */

GMT_LOCAL void gmtproj_merc_sph (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to Mercator x/y (GMT->current.proj.EQ_RAD in GMT->current.proj.j_x) */

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */
	if (GMT->current.proj.GMT_convert_latitudes) lat = gmt_M_latg_to_latc (GMT, lat);

	*x = GMT->current.proj.j_x * D2R * lon;
	*y = (fabs (lat) < 90.0) ? GMT->current.proj.j_x * d_log (GMT, tand (45.0 + 0.5 * lat)) : copysign (DBL_MAX, lat);
}

GMT_LOCAL void gmtproj_imerc_sph (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert Mercator x/y to lon/lat  (GMT->current.proj.EQ_RAD in GMT->current.proj.j_ix) */

	*lon = x * GMT->current.proj.j_ix * R2D + GMT->current.proj.central_meridian;
	*lat = atand (sinh (y * GMT->current.proj.j_ix));
	if (GMT->current.proj.GMT_convert_latitudes) *lat = gmt_M_latc_to_latg (GMT, *lat);
}

/* -JY CYLINDRICAL EQUAL-AREA PROJECTION */

GMT_LOCAL void gmtproj_vcyleq (struct GMT_CTRL *GMT, double lon0, double slat) {
	/* Set up a Cylindrical equal-area transformation */

	gmtproj_check_R_J (GMT, &lon0);
	GMT->current.proj.central_meridian = lon0;
	GMT->current.proj.j_x = GMT->current.proj.EQ_RAD * D2R * cosd (slat);
	GMT->current.proj.j_y = GMT->current.proj.EQ_RAD / cosd (slat);
	GMT->current.proj.j_ix = 1.0 / GMT->current.proj.j_x;
	GMT->current.proj.j_iy = 1.0 / GMT->current.proj.j_y;
}

GMT_LOCAL void gmtproj_cyleq (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to Cylindrical equal-area x/y */

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */
	if (GMT->current.proj.GMT_convert_latitudes) lat = gmt_M_latg_to_lata (GMT, lat);

	*x = lon * GMT->current.proj.j_x;
	*y = GMT->current.proj.j_y * sind (lat);
	if (GMT->current.proj.GMT_convert_latitudes) {	/* Gotta fudge abit */
		(*x) *= GMT->current.proj.Dx;
		(*y) *= GMT->current.proj.Dy;
	}
}

GMT_LOCAL void gmtproj_icyleq (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert Cylindrical equal-area x/y to lon/lat */

	if (GMT->current.proj.GMT_convert_latitudes) {	/* Gotta fudge abit */
		x *= GMT->current.proj.iDx;
		y *= GMT->current.proj.iDy;
	}
	*lon = x * GMT->current.proj.j_ix + GMT->current.proj.central_meridian;
	*lat = d_asind (y * GMT->current.proj.j_iy);
	if (GMT->current.proj.GMT_convert_latitudes) *lat = gmt_M_lata_to_latg (GMT, *lat);
}

/* -JQ CYLINDRICAL EQUIDISTANT PROJECTION */

GMT_LOCAL void gmtproj_vcyleqdist (struct GMT_CTRL *GMT, double lon0, double slat) {
	/* Set up a Cylindrical equidistant transformation */

	gmtproj_check_R_J (GMT, &lon0);
	GMT->current.proj.central_meridian = lon0;
	GMT->current.proj.j_x = D2R * GMT->current.proj.EQ_RAD * cosd (slat);
	GMT->current.proj.j_y = D2R * GMT->current.proj.EQ_RAD;
	GMT->current.proj.j_ix = 1.0 / GMT->current.proj.j_x;
	GMT->current.proj.j_iy = 1.0 / GMT->current.proj.j_y;
}

GMT_LOCAL void gmtproj_cyleqdist (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to Cylindrical equidistant x/y */

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */
	*x = lon * GMT->current.proj.j_x;
	*y = lat * GMT->current.proj.j_y;
}

GMT_LOCAL void gmtproj_icyleqdist (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert Cylindrical equal-area x/y to lon/lat */

	*lon = x * GMT->current.proj.j_ix + GMT->current.proj.central_meridian;
	*lat = y * GMT->current.proj.j_iy;
}

/* -JJ MILLER CYLINDRICAL PROJECTION */

//#define CHRISTMAS
/* Turning on Christmas makes the Miller projection a triangular projection
 * that projects 90 degrees of longitude and latitudes 45-90 into a 45-degree
 * triangle.  Doing all for quadrants results in a square map with radial
 * meridians and lots of distortion along the boundaries.  This was used to
 * build a 3-D cube of the world with this triangle projection being used to
 * map the top (N polar to 45N) and bottom (S pole to 45S) sides, with the
 * remaining 4 sides just being -JQ maps.  I left it here since I may want
 * to mess with this in the future.  P. Wessel, Dec. 2016.
 */
#ifdef CHRISTMAS
/* Bypass Miller projection entirely and introduce a triangle projection */
GMT_LOCAL void gmtproj_vmiller (struct GMT_CTRL *GMT, double lon0, double slat) {
	/* Set up a Cylindrical equidistant transformation */
	GMT->current.proj.north_pole = (slat > 0.0);
	gmtproj_check_R_J (GMT, &lon0);
	GMT->current.proj.central_meridian = lon0;
	GMT->current.proj.j_x = 0.25 * D2R * GMT->current.proj.EQ_RAD;
	GMT->current.proj.j_y = 0.25 * D2R * GMT->current.proj.EQ_RAD;
	GMT->current.proj.j_ix = 1.0 / GMT->current.proj.j_x;
	GMT->current.proj.j_iy = 1.0 / GMT->current.proj.j_y;
}

GMT_LOCAL void gmtproj_miller (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to Cylindrical equidistant x/y */
	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */
	if (lat > 0.0) {
		*x = (0.5 + lon * (90.0 - lat) / 4050.0) * GMT->current.proj.j_x;
		*y = ((lat-45.0) / 90.0) * GMT->current.proj.j_y;
	}
	else {
		*x = (0.5 - lon * (90.0 + lat) / 4050.0) * GMT->current.proj.j_x;
		*y = -((lat+45) / 90.0) * GMT->current.proj.j_y;
	}
}

GMT_LOCAL void gmtproj_imiller (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert Cylindrical equal-area x/y to lon/lat */

	if (GMT->current.proj.north_pole) {
		*lat = 45.0 + 90.0 * y * GMT->current.proj.j_iy;
		*lon = (4050.0 * (x * GMT->current.proj.j_ix - 0.5)) / (90.0 - *lat) + GMT->current.proj.central_meridian;
	}
	else {
		*lat = -(45.0 + 90.0 * y * GMT->current.proj.j_iy);
		*lon = -(4050.0 * (x * GMT->current.proj.j_ix - 0.5)) / (90.0 + *lat) + GMT->current.proj.central_meridian;
	}
}
#else
GMT_LOCAL void gmtproj_vmiller (struct GMT_CTRL *GMT, double lon0, double unused) {
	/* Set up a Miller Cylindrical transformation */
	gmt_M_unused (unused);
	gmtproj_check_R_J (GMT, &lon0);
	GMT->current.proj.central_meridian = lon0;
	GMT->current.proj.j_x = D2R * GMT->current.proj.EQ_RAD;
	GMT->current.proj.j_y = 1.25 * GMT->current.proj.EQ_RAD;
	GMT->current.proj.j_ix = 1.0 / GMT->current.proj.j_x;
	GMT->current.proj.j_iy = 1.0 / GMT->current.proj.j_y;
}

GMT_LOCAL void gmtproj_miller (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to Miller Cylindrical x/y */

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */
	*x = lon * GMT->current.proj.j_x;
	*y = GMT->current.proj.j_y * d_log (GMT, tand (45.0 + 0.4 * lat));
}

GMT_LOCAL void gmtproj_imiller (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert Miller Cylindrical x/y to lon/lat */

	*lon = x * GMT->current.proj.j_ix + GMT->current.proj.central_meridian;
	*lat = 2.5 * atand (exp (y * GMT->current.proj.j_iy)) - 112.5;
}
#endif

/* -JCyl_stere CYLINDRICAL STEREOGRAPHIC PROJECTION */

GMT_LOCAL void gmtproj_vcylstereo (struct GMT_CTRL *GMT, double lon0, double slat) {
	/* Set up a Cylindrical Stereographic transformation */

	gmtproj_check_R_J (GMT, &lon0);
	GMT->current.proj.central_meridian = lon0;
	GMT->current.proj.j_x = GMT->current.proj.EQ_RAD * D2R * cosd (slat);
	GMT->current.proj.j_y = GMT->current.proj.EQ_RAD * (1.0 + cosd (slat));
	GMT->current.proj.j_ix = 1.0 / GMT->current.proj.j_x;
	GMT->current.proj.j_iy = 1.0 / GMT->current.proj.j_y;
}

GMT_LOCAL void gmtproj_cylstereo (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to Cylindrical Stereographic x/y */

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */
	*x = lon * GMT->current.proj.j_x;
	*y = GMT->current.proj.j_y * tand (0.5 * lat);
}

GMT_LOCAL void gmtproj_icylstereo (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert Cylindrical Stereographic x/y to lon/lat */

	*lon = x * GMT->current.proj.j_ix + GMT->current.proj.central_meridian;
	*lat = 2.0 * atand (y * GMT->current.proj.j_iy);
}

/* -JS POLAR STEREOGRAPHIC PROJECTION */

GMT_LOCAL void gmtproj_vstereo (struct GMT_CTRL *GMT, double lon0, double lat0, double horizon) {
	/* Set up a Stereographic transformation */

	double clat;
	if (GMT->current.proj.GMT_convert_latitudes) {	/* Set Conformal radius and pole latitude */
		gmtlib_scale_eqrad (GMT);
		clat = gmt_M_latg_to_latc (GMT, lat0);
	}
	else
		clat = lat0;

	GMT->current.proj.central_meridian = lon0;
	GMT->current.proj.pole = lat0;		/* This is always geodetic */
	sincosd (clat, &(GMT->current.proj.sinp), &(GMT->current.proj.cosp));	/* These may be conformal */
	GMT->current.proj.north_pole = (lat0 > 0.0);
	GMT->current.proj.s_c = 2.0 * GMT->current.proj.EQ_RAD * GMT->current.setting.proj_scale_factor;
	GMT->current.proj.s_ic = 1.0 / GMT->current.proj.s_c;
	GMT->current.proj.f_horizon = horizon;
	GMT->current.proj.rho_max = tand (0.5 * horizon) * GMT->current.proj.s_c;
}

GMT_LOCAL void gmtproj_plrs_sph (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to x/y using Spherical polar projection */
	double rho, slon, clon;

	if (GMT->current.proj.GMT_convert_latitudes) lat = gmt_M_latg_to_latc (GMT, lat);
	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */

	sincosd (lon, &slon, &clon);
	if (GMT->current.proj.north_pole) {
		rho = GMT->current.proj.s_c * tand (45.0 - 0.5 * lat);
		*y = -rho * clon;
		*x =  rho * slon;
	}
	else {
		rho = GMT->current.proj.s_c * tand (45.0 + 0.5 * lat);
		*y = rho * clon;
		*x = rho * slon;
	}
	if (GMT->current.proj.GMT_convert_latitudes) {	/* Gotta fudge abit */
		(*x) *= GMT->current.proj.Dx;
		(*y) *= GMT->current.proj.Dy;
	}
}

GMT_LOCAL void gmtproj_iplrs_sph (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert Spherical polar x/y to lon/lat */
	double c;

	if (x == 0.0 && y == 0.0) {
		*lon = GMT->current.proj.central_meridian;
		*lat = GMT->current.proj.pole;
		return;
	}

	if (GMT->current.proj.GMT_convert_latitudes) {	/* Undo effect of fudge factors */
		x *= GMT->current.proj.iDx;
		y *= GMT->current.proj.iDy;
	}
	c = 2.0 * atan (hypot (x, y) * GMT->current.proj.s_ic);

	if (GMT->current.proj.north_pole) {
		*lon = GMT->current.proj.central_meridian + d_atan2d (x, -y);
		*lat = d_asind (cos (c));
	}
	else {
		*lon = GMT->current.proj.central_meridian + d_atan2d (x, y);
		*lat = d_asind (-cos (c));
	}
	if (GMT->current.proj.GMT_convert_latitudes) *lat = gmt_M_latc_to_latg (GMT, *lat);

}

GMT_LOCAL void gmtproj_stereo1_sph (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to x/y using Spherical stereographic projection, oblique view */

	double sin_dlon, cos_dlon, s, c, cc, A;

	if (GMT->current.proj.GMT_convert_latitudes) lat = gmt_M_latg_to_latc (GMT, lat);
	sincosd (lon - GMT->current.proj.central_meridian, &sin_dlon, &cos_dlon);
	sincosd (lat, &s, &c);
	cc = c * cos_dlon;
	A = GMT->current.proj.s_c / (1.0 + GMT->current.proj.sinp * s + GMT->current.proj.cosp * cc);
	*x = A * c * sin_dlon;
	*y = A * (GMT->current.proj.cosp * s - GMT->current.proj.sinp * cc);
	if (GMT->current.proj.GMT_convert_latitudes) {	/* Gotta fudge abit */
		(*x) *= GMT->current.proj.Dx;
		(*y) *= GMT->current.proj.Dy;
	}
}

GMT_LOCAL void gmtproj_istereo_sph (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	double rho, c, sin_c, cos_c;

	if (GMT->current.proj.GMT_convert_latitudes) {	/* Undo effect of fudge factors */
		x *= GMT->current.proj.iDx;
		y *= GMT->current.proj.iDy;
	}
	rho = hypot (x, y);
	if (rho > GMT->current.proj.rho_max) {
		*lat = *lon = GMT->session.d_NaN;
		return;
	}
	c = 2.0 * atan (rho * GMT->current.proj.s_ic);
	sincos (c, &sin_c, &cos_c);
	if (rho != 0.0) sin_c /= rho;
	*lat = asind (cos_c * GMT->current.proj.sinp + y * sin_c * GMT->current.proj.cosp);
	*lon = d_atan2d (x * sin_c, cos_c * GMT->current.proj.cosp - y * sin_c * GMT->current.proj.sinp) + GMT->current.proj.central_meridian;
	if (GMT->current.proj.GMT_convert_latitudes) *lat = gmt_M_latc_to_latg (GMT, *lat);
}

/* Spherical equatorial view */

GMT_LOCAL void gmtproj_stereo2_sph (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to x/y using stereographic projection, equatorial view */

	double dlon, s, c, clon, slon, A;

	dlon = lon - GMT->current.proj.central_meridian;
	if (doubleAlmostEqual (dlon, 180.0)) {
		*x = *y = 0.0;
	}
	else {
		if (GMT->current.proj.GMT_convert_latitudes) lat = gmt_M_latg_to_latc (GMT, lat);
		sincosd (lat, &s, &c);
		sincosd (dlon, &slon, &clon);
		A = GMT->current.proj.s_c / (1.0 + c * clon);
		*x = A * c * slon;
		*y = A * s;
		if (GMT->current.proj.GMT_convert_latitudes) {	/* Gotta fudge abit */
			(*x) *= GMT->current.proj.Dx;
			(*y) *= GMT->current.proj.Dy;
		}
	}
}

/* -JL LAMBERT CONFORMAL CONIC PROJECTION */

GMT_LOCAL void gmtproj_vlamb (struct GMT_CTRL *GMT, double rlong0, double rlat0, double pha, double phb) {
	/* Set up a Lambert Conformal Conic projection (Spherical when e = 0) */

	double sin_pha, cos_pha, sin_phb, cos_phb, t_pha, m_pha, t_phb, m_phb, t_rlat0;

	gmtproj_check_R_J (GMT, &rlong0);
	GMT->current.proj.north_pole = (GMT->common.R.wesn[YHI] > 0.0 && (GMT->common.R.wesn[YLO] >= 0.0 || (-GMT->common.R.wesn[YLO]) < GMT->common.R.wesn[YHI]));
	GMT->current.proj.pole = (GMT->current.proj.north_pole) ? 90.0 : -90.0;
	sincosd (pha, &sin_pha, &cos_pha);
	sincosd (phb, &sin_phb, &cos_phb);

	t_pha = tand (45.0 - 0.5 * pha) / pow ((1.0 - GMT->current.proj.ECC *
		sin_pha) / (1.0 + GMT->current.proj.ECC * sin_pha), GMT->current.proj.half_ECC);
	m_pha = cos_pha / d_sqrt (1.0 - GMT->current.proj.ECC2 * sin_pha * sin_pha);
	t_phb = tand (45.0 - 0.5 * phb) / pow ((1.0 - GMT->current.proj.ECC *
		sin_phb) / (1.0 + GMT->current.proj.ECC * sin_phb), GMT->current.proj.half_ECC);
	m_phb = cos_phb / d_sqrt (1.0 - GMT->current.proj.ECC2 * sin_phb * sin_phb);
	t_rlat0 = tand (45.0 - 0.5 * rlat0) /
		pow ((1.0 - GMT->current.proj.ECC * sind (rlat0)) /
		(1.0 + GMT->current.proj.ECC * sind (rlat0)), GMT->current.proj.half_ECC);

	if (doubleAlmostEqualZero (pha, phb))
		GMT->current.proj.l_N = sind (pha);
	else
		GMT->current.proj.l_N = (d_log (GMT, m_pha) - d_log (GMT, m_phb))/(d_log (GMT, t_pha) - d_log (GMT, t_phb));

	GMT->current.proj.l_i_N = 1.0 / GMT->current.proj.l_N;
	GMT->current.proj.l_F = m_pha / (GMT->current.proj.l_N * pow (t_pha, GMT->current.proj.l_N));
	GMT->current.proj.central_meridian = rlong0;
	GMT->current.proj.l_rF = GMT->current.proj.EQ_RAD * GMT->current.proj.l_F;
	GMT->current.proj.l_i_rF = 1.0 / GMT->current.proj.l_rF;
	GMT->current.proj.l_rho0 = GMT->current.proj.l_rF * pow (t_rlat0, GMT->current.proj.l_N);
	GMT->current.proj.l_Nr = GMT->current.proj.l_N * D2R;
	GMT->current.proj.l_i_Nr = 1.0 / GMT->current.proj.l_Nr;
}

GMT_LOCAL void gmtproj_lamb (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	double rho, theta, hold1, hold2, hold3, es, s, c;

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */

	es = GMT->current.proj.ECC * sind (lat);
	hold2 = pow ((1.0 - es) / (1.0 + es), GMT->current.proj.half_ECC);
	hold3 = tand (45.0 - 0.5 * lat);
	hold1 = pow (hold3 / hold2, GMT->current.proj.l_N);
	rho = GMT->current.proj.l_rF * hold1;
	theta = GMT->current.proj.l_Nr * lon;
	sincos (theta, &s, &c);
	*x = rho * s;
	*y = GMT->current.proj.l_rho0 - rho * c;
}

GMT_LOCAL void gmtproj_ilamb (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	int i;
	double theta, rho, t, tphi, phi, dy, r;

	dy = GMT->current.proj.l_rho0 - y;
	theta = (GMT->current.proj.l_N < 0.0) ? d_atan2 (-x, -dy) : d_atan2 (x, dy);
	*lon = theta * GMT->current.proj.l_i_Nr + GMT->current.proj.central_meridian;

	rho = copysign (hypot (x, dy), GMT->current.proj.l_N);
	t = pow (rho * GMT->current.proj.l_i_rF, GMT->current.proj.l_i_N);
	phi = 0.0; tphi = 999.0;	/* Initialize phi = 0 */
	for (i = 0; i < GMT_PROJ_MAX_ITERATIONS && fabs (tphi - phi) > GMT_PROJ_CONV_LIMIT; i++) {
		tphi = phi;
		r = GMT->current.proj.ECC * sin (phi);
		phi = M_PI_2 - 2.0 * atan (t * pow ((1.0 - r) / (1.0 + r), GMT->current.proj.half_ECC));
	}
	*lat = phi * R2D;
}

/* Spherical cases of Lambert */

GMT_LOCAL void gmtproj_lamb_sph (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	double rho, theta, t, s, c;

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */
	if (GMT->current.proj.GMT_convert_latitudes) lat = gmt_M_latg_to_latc (GMT, lat);

	t = MAX (0.0, tand (45.0 - 0.5 * lat));	/* Guard against negative t */
	rho = GMT->current.proj.l_rF * pow (t, GMT->current.proj.l_N);
	theta = GMT->current.proj.l_Nr * lon;

	sincos (theta, &s, &c);
	*x = rho * s;
	*y = GMT->current.proj.l_rho0 - rho * c;
}

GMT_LOCAL void gmtproj_ilamb_sph (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	double theta, rho, t, dy;

	dy = GMT->current.proj.l_rho0 - y;
	theta = (GMT->current.proj.l_N < 0.0) ? d_atan2 (-x, -dy) : d_atan2 (x, dy);
	*lon = theta * GMT->current.proj.l_i_Nr + GMT->current.proj.central_meridian;

	rho = copysign (hypot (x, dy), GMT->current.proj.l_N);
	t = pow (rho * GMT->current.proj.l_i_rF, GMT->current.proj.l_i_N);
	*lat = 90.0 - 2.0 * atand (t);
	if (GMT->current.proj.GMT_convert_latitudes) *lat = gmt_M_latc_to_latg (GMT, *lat);
}

/* -JO OBLIQUE MERCATOR PROJECTION */

GMT_LOCAL void gmtproj_obl (struct GMT_CTRL *GMT, double lon, double lat, double *olon, double *olat) {
	/* Convert a longitude/latitude point to Oblique lon/lat (all in rads) */
	double p_cross_x[3], X[3];

	gmt_geo_to_cart (GMT, lat, lon, X, false);

	*olat = d_asin (gmt_dot3v (GMT, X, GMT->current.proj.o_FP));

	gmt_cross3v (GMT, GMT->current.proj.o_FP, X, p_cross_x);
	gmt_normalize3v (GMT, p_cross_x);

	*olon = copysign (d_acos (gmt_dot3v (GMT, p_cross_x, GMT->current.proj.o_FC)), gmt_dot3v (GMT, X, GMT->current.proj.o_FC));
}

void gmtlib_iobl (struct GMT_CTRL *GMT, double *lon, double *lat, double olon, double olat) {
	/* Convert a longitude/latitude point from Oblique lon/lat  (all in rads) */
	double p_cross_x[3], X[3];

	gmt_geo_to_cart (GMT, olat, olon, X, false);
	*lat = d_asin (gmt_dot3v (GMT, X, GMT->current.proj.o_IP));

	gmt_cross3v (GMT, GMT->current.proj.o_IP, X, p_cross_x);
	gmt_normalize3v (GMT, p_cross_x);

	*lon = copysign (d_acos (gmt_dot3v (GMT, p_cross_x, GMT->current.proj.o_IC)), gmt_dot3v (GMT, X, GMT->current.proj.o_IC));

	while ((*lon) < 0.0) (*lon) += TWO_PI;
	while ((*lon) >= TWO_PI) (*lon) -= TWO_PI;
}

GMT_LOCAL void gmtproj_oblmrc (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert a longitude/latitude point to Oblique Mercator coordinates
	 * by way of rotation coordinates and then using regular Mercator */
	double tlon, tlat;
	/* o_shift deals with difference between user's origin and our logical origin */

	gmtproj_obl (GMT, lon * D2R, lat * D2R, &tlon, &tlat);

	*x = GMT->current.proj.j_x * tlon;
	*y = (fabs (tlat) < M_PI_2) ? GMT->current.proj.j_x * d_log (GMT, tan (M_PI_4 + 0.5 * tlat)) - GMT->current.proj.o_shift : copysign (DBL_MAX, tlat);
	if (GMT->current.proj.obl_flip) {
		/* Let oblique Equator be y-axis, so flip x and y but must let y be negative [that change takes place in map_setxy] */
		gmt_M_double_swap (*x, *y);
		if (GMT->current.proj.pars[1] < 0.0) *x = -*x, *y = -*y;	/* S hemisphere must rotate 180 */
	}
	else if (GMT->current.proj.o_spole) {
		*x = -(*x);
		*y = -(*y);
	}
}

GMT_LOCAL void gmtproj_ioblmrc (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert a longitude/latitude point from Oblique Mercator coordinates
	 * by way of regular Mercator and then rotate coordinates */
	double tlon, tlat;
	/* o_shift deals with difference between user's origin and our logical origin */

	if (GMT->current.proj.obl_flip) {
		/* Had oblique Equator be y-axis */
		if (GMT->current.proj.pars[1] < 0.0) x = -x, y = -y;	/* S hemisphere must rotate 180 */
		gmt_M_double_swap (x, y);
	}
	else if (GMT->current.proj.o_spole) {
		x = -x;
		y = -y;
	}
	tlon = x * GMT->current.proj.j_ix;
	y += GMT->current.proj.o_shift;
	tlat = atan (sinh (y * GMT->current.proj.j_ix));

	gmtlib_iobl (GMT, lon, lat, tlon, tlat);

	(*lon) *= R2D;	(*lat) *= R2D;
}

/* -JT TRANSVERSE MERCATOR PROJECTION */

GMT_LOCAL void gmtproj_vtm (struct GMT_CTRL *GMT, double lon0, double lat0) {
	/* Set up an TM projection */
	double e1, s2, s4, s6;

	/* gmtproj_check_R_J (&lon0); */
	e1 = (1.0 - d_sqrt (GMT->current.proj.one_m_ECC2)) / (1.0 + d_sqrt (GMT->current.proj.one_m_ECC2));
	GMT->current.proj.t_e2 = GMT->current.proj.ECC2 * GMT->current.proj.i_one_m_ECC2;
	GMT->current.proj.t_c1 = 1.0 - (1.0/4.0) * GMT->current.proj.ECC2 - (3.0/64.0) * GMT->current.proj.ECC4 - (5.0/256.0) * GMT->current.proj.ECC6;
	GMT->current.proj.t_c2 = -((3.0/8.0) * GMT->current.proj.ECC2 + (3.0/32.0) * GMT->current.proj.ECC4 + (45.0/1024.0) * GMT->current.proj.ECC6);
	GMT->current.proj.t_c3 = (15.0/256.0) * GMT->current.proj.ECC4 + (45.0/1024.0) * GMT->current.proj.ECC6;
	GMT->current.proj.t_c4 = -(35.0/3072.0) * GMT->current.proj.ECC6;
	GMT->current.proj.t_i1 = 1.0 / (GMT->current.proj.EQ_RAD * GMT->current.proj.t_c1);
	GMT->current.proj.t_i2 = (3.0/2.0) * e1 - (27.0/32.0) * pow (e1, 3.0);
	GMT->current.proj.t_i3 = (21.0/16.0) * e1 * e1 - (55.0/32.0) * pow (e1, 4.0);
	GMT->current.proj.t_i4 = (151.0/96.0) * pow (e1, 3.0);
	GMT->current.proj.t_i5 = (1097.0/512.0) * pow (e1, 4.0);
	GMT->current.proj.central_meridian = lon0;	/* In degrees */
	GMT->current.proj.t_lat0 = lat0 * D2R;	/* In radians */
	s2 = sin (2.0 * GMT->current.proj.t_lat0);
	s4 = sin (4.0 * GMT->current.proj.t_lat0);
	s6 = sin (6.0 * GMT->current.proj.t_lat0);
	GMT->current.proj.t_M0 = GMT->current.proj.EQ_RAD * (GMT->current.proj.t_c1 * GMT->current.proj.t_lat0 + s2 * GMT->current.proj.t_c2 + s4 * GMT->current.proj.t_c3 + s6 * GMT->current.proj.t_c4);
	GMT->current.proj.t_r = GMT->current.proj.EQ_RAD * GMT->current.setting.proj_scale_factor;
	GMT->current.proj.t_ir = 1.0 / GMT->current.proj.t_r;
}

/* Ellipsoidal TM functions */

GMT_LOCAL void gmtproj_tm (struct GMT_CTRL *P, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to TM x/y */
	double N, T, T2, C, A, M, dlon, tan_lat, A2, A3, A4, A5, A6, s, c, s2, s4, s6;

	if (doubleAlmostEqual (fabs (lat), 90.0)) {
		M = P->current.proj.EQ_RAD * P->current.proj.t_c1 * M_PI_2;
		*x = 0.0;
		*y = P->current.setting.proj_scale_factor * M;
	}
	else {
		lat *= D2R;
		sincos (lat, &s, &c);
		tan_lat = s / c;
		s2 = sin (2.0 * lat);
		s4 = sin (4.0 * lat);
		s6 = sin (6.0 * lat);
		M = P->current.proj.EQ_RAD * (P->current.proj.t_c1 * lat + s2 * P->current.proj.t_c2 + s4 * P->current.proj.t_c3 + s6 * P->current.proj.t_c4);
		gmt_M_set_delta_lon (P->current.proj.central_meridian, lon, dlon);
		N = P->current.proj.EQ_RAD / d_sqrt (1.0 - P->current.proj.ECC2 * s * s);
		T = tan_lat * tan_lat;
		T2 = T * T;
		C = P->current.proj.t_e2 * c * c;
		A = dlon * D2R * c;
		A2 = A * A;	A3 = A2 * A;	A4 = A3 * A; A5 = A4 * A;	A6 = A5 * A;
		*x = P->current.setting.proj_scale_factor * N * (A + (1.0 - T + C) * (A3 * 0.16666666666666666667)
			+ (5.0 - 18.0 * T + T2 + 72.0 * C - 58.0 * P->current.proj.t_e2) * (A5 * 0.00833333333333333333));
		*y = P->current.setting.proj_scale_factor * (M - P->current.proj.t_M0 + N * tan_lat * (0.5 * A2 + (5.0 - T + 9.0 * C + 4.0 * C * C) * (A4 * 0.04166666666666666667)
			+ (61.0 - 58.0 * T + T2 + 600.0 * C - 330.0 * P->current.proj.t_e2) * (A6 * 0.00138888888888888889)));
	}
}

GMT_LOCAL void gmtproj_itm (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert TM x/y to lon/lat */
	double M, mu, s, c, s2, c2, s4, s6, s8, phi1, C1, C12, T1, T12, tmp, tmp2, N1, R_1, D, D2, D3, D4, D5, D6, tan_phi1;

	M = y / GMT->current.setting.proj_scale_factor + GMT->current.proj.t_M0;
	mu = M * GMT->current.proj.t_i1;

	sincos (2.0 * mu, &s2, &c2);
	s4 = sin (4.0 * mu);
	s6 = sin (6.0 * mu);
	s8 = sin (8.0 * mu);
	phi1 = mu + s2 * GMT->current.proj.t_i2 + s4 * GMT->current.proj.t_i3 + s6 * GMT->current.proj.t_i4 + s8 * GMT->current.proj.t_i5;

	sincos (phi1, &s, &c);
	tan_phi1 = s / c;
	C1 = GMT->current.proj.t_e2 * c * c;
	C12 = C1 * C1;
	T1 = tan_phi1 * tan_phi1;
	T12 = T1 * T1;
	tmp = 1.0 - GMT->current.proj.ECC2 * s * s;
	tmp2 = d_sqrt (tmp);
	N1 = GMT->current.proj.EQ_RAD / tmp2;
	R_1 = GMT->current.proj.EQ_RAD * GMT->current.proj.one_m_ECC2 / (tmp * tmp2);
	D = x / (N1 * GMT->current.setting.proj_scale_factor);
	D2 = D * D;	D3 = D2 * D;	D4 = D2 * D2; D5 = D3 * D2;	D6 = D5 * D;

	*lon = GMT->current.proj.central_meridian + R2D * (D - (1.0 + 2.0 * T1 + C1) * (D3 * 0.16666666666666666667)
		+ (5.0 - 2.0 * C1 + 28.0 * T1 - 3.0 * C12 + 8.0 * GMT->current.proj.t_e2 + 24.0 * T12)
		* (D5 * 0.00833333333333333333)) / c;
	*lat = phi1 - (N1 * tan_phi1 / R_1) * (0.5 * D2 -
		(5.0 + 3.0 * T1 + 10.0 * C1 - 4.0 * C12 - 9.0 * GMT->current.proj.t_e2) * (D4 * 0.04166666666666666667)
		+ (61.0 + 90.0 * T1 + 298 * C1 + 45.0 * T12 - 252.0 * GMT->current.proj.t_e2 - 3.0 * C12) * (D6 * 0.00138888888888888889));
	(*lat) *= R2D;
}

/* Spherical TM functions */

GMT_LOCAL void gmtproj_tm_sph (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to TM x/y by spherical formula */
	double dlon, B, clat, slat, clon, slon, xx, yy;

	gmt_M_set_delta_lon (GMT->current.proj.central_meridian, lon, dlon);
	if (fabs (lat) > 90.0) {
		/* Invalid latitude.  Treat as in gmtproj_merc_sph(), but transversely:  */
		*x = copysign (1.0e100, dlon);
		*y = 0.0;
		return;
	}

	if (GMT->current.proj.GMT_convert_latitudes) lat = gmt_M_latg_to_latc (GMT, lat);

	sincosd (lat, &slat, &clat);
	sincosd (dlon, &slon, &clon);
	B = clat * slon;
	if (fabs(B) >= 1.0) {
		/* This corresponds to the transverse "pole"; the point at x = +-infinity, y = -lat0.
			Treat as in gmtproj_merc_sph(), but transversely:  */
		*x = copysign (1.0e100, dlon);
		*y = -GMT->current.proj.t_r * GMT->current.proj.t_lat0;
		return;
	}

	xx = atanh (B);

	/* this should get us "over the pole";
	   see not Snyder's formula but his example Fig. 10 on p. 50:  */

	yy = atan2 (slat, (clat * clon)) - GMT->current.proj.t_lat0;
	if (yy < -M_PI_2) yy += TWO_PI;
	*x = GMT->current.proj.t_r * xx;
	*y = GMT->current.proj.t_r * yy;
}

GMT_LOCAL void gmtproj_itm_sph (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert TM x/y to lon/lat by spherical approximation.  */

	double xx, D, sinhxx, coshxx, sind, cosd, lambda, phi;

	xx = x * GMT->current.proj.t_ir;
	D = y * GMT->current.proj.t_ir + GMT->current.proj.t_lat0;

	sinhxx = sinh (xx);
	coshxx = cosh (xx);

	sincos (D, &sind, &cosd);
	phi = asind (sind / coshxx);
	*lat = phi;

	lambda = atan2d (sinhxx, cosd);
	*lon = lambda + GMT->current.proj.central_meridian;
	if (GMT->current.proj.GMT_convert_latitudes) *lat = gmt_M_latc_to_latg (GMT, *lat);
}

/* -JU UNIVERSAL TRANSVERSE MERCATOR PROJECTION */

/* Ellipsoidal UTM */

GMT_LOCAL void gmtproj_utm (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to UTM x/y */

	if (lon < 0.0) lon += 360.0;
	gmtproj_tm (GMT, lon, lat, x, y);
	(*x) += GMT_FALSE_EASTING;
	if (!GMT->current.proj.north_pole) (*y) += GMT_FALSE_NORTHING;	/* For S hemisphere, add 10^7 m */
}

GMT_LOCAL void gmtproj_iutm (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert UTM x/y to lon/lat */

	x -= GMT_FALSE_EASTING;
	if (!GMT->current.proj.north_pole) y -= GMT_FALSE_NORTHING;
	gmtproj_itm (GMT, lon, lat, x, y);
}

/* Spherical UTM */

GMT_LOCAL void gmtproj_utm_sph (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to UTM x/y */

	if (lon < 0.0) lon += 360.0;
	gmtproj_tm_sph (GMT, lon, lat, x, y);
	(*x) += GMT_FALSE_EASTING;
	if (!GMT->current.proj.north_pole) (*y) += GMT_FALSE_NORTHING;	/* For S hemisphere, add 10^7 m */
}

GMT_LOCAL void gmtproj_iutm_sph (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert UTM x/y to lon/lat */

	x -= GMT_FALSE_EASTING;
	if (!GMT->current.proj.north_pole) y -= GMT_FALSE_NORTHING;
	gmtproj_itm_sph (GMT, lon, lat, x, y);
}

/* -JA LAMBERT AZIMUTHAL EQUAL AREA PROJECTION */

GMT_LOCAL void gmtproj_vlambeq (struct GMT_CTRL *GMT, double lon0, double lat0, double horizon) {
	/* Set up Spherical Lambert Azimuthal Equal-Area projection */

	GMT->current.proj.central_meridian = lon0;
	GMT->current.proj.pole = lat0;
	if (GMT->current.proj.GMT_convert_latitudes) lat0 = gmt_M_latg_to_lata (GMT, lat0);
	sincosd (lat0, &(GMT->current.proj.sinp), &(GMT->current.proj.cosp));
	GMT->current.proj.f_horizon = horizon;
	GMT->current.proj.rho_max = 2.0 * sind (0.5 * horizon) * GMT->current.proj.EQ_RAD;
}

GMT_LOCAL void gmtproj_lambeq (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to Spherical Lambert Azimuthal Equal-Area x/y */
	double k, tmp, sin_lat, cos_lat, sin_lon, cos_lon, c;

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */
	if (GMT->current.proj.GMT_convert_latitudes) lat = gmt_M_latg_to_lata (GMT, lat);
	sincosd (lat, &sin_lat, &cos_lat);
	sincosd (lon, &sin_lon, &cos_lon);
	c = cos_lat * cos_lon;
	tmp = 1.0 + GMT->current.proj.sinp * sin_lat + GMT->current.proj.cosp * c;
	if (tmp > 0.0) {
		k = GMT->current.proj.EQ_RAD * d_sqrt (2.0 / tmp);
		*x = k * cos_lat * sin_lon;
		*y = k * (GMT->current.proj.cosp * sin_lat - GMT->current.proj.sinp * c);
		if (GMT->current.proj.GMT_convert_latitudes) {	/* Gotta fudge abit */
			(*x) *= GMT->current.proj.Dx;
			(*y) *= GMT->current.proj.Dy;
		}
	}
	else
		*x = *y = -DBL_MAX;
}

GMT_LOCAL void gmtproj_ilambeq (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert Lambert Azimuthal Equal-Area x/y to lon/lat */
	double rho, a, sin_c, cos_c;

	if (GMT->current.proj.GMT_convert_latitudes) {	/* Undo effect of fudge factors */
		x *= GMT->current.proj.iDx;
		y *= GMT->current.proj.iDy;
	}

	rho = hypot (x, y);
	if (rho > GMT->current.proj.rho_max) {			/* Horizon		*/
		*lat = *lon = GMT->session.d_NaN;
		return;
	}
	a = 0.5 * rho * GMT->current.proj.i_EQ_RAD;			/* a = sin(c/2)		*/
	a *= a;							/* a = sin(c/2)**2	*/
	cos_c = 1.0 - 2.0 * a;					/* cos_c = cos(c)	*/
	sin_c = sqrt (1.0 - a) * GMT->current.proj.i_EQ_RAD;		/* sin_c = sin(c)/rho	*/
	*lat = d_asind (cos_c * GMT->current.proj.sinp + y * sin_c * GMT->current.proj.cosp);
	*lon = d_atan2d (x * sin_c, GMT->current.proj.cosp * cos_c - y * GMT->current.proj.sinp * sin_c) + GMT->current.proj.central_meridian;
	if (GMT->current.proj.GMT_convert_latitudes) *lat = gmt_M_lata_to_latg (GMT, *lat);
}

/* -JG GENERAL PERSPECTIVE PROJECTION */

/* Set up General Perspective projection */

/* Convert lon/lat to General Perspective x/y */

GMT_LOCAL void gmtproj_genper (struct GMT_CTRL *GMT, double lon, double lat, double *xt, double *yt) {
	double dlon, sin_lat, cos_lat, sin_dlon, cos_dlon;
	double cosc, sinc;
	double x, y, kp;
	double angle;

	dlon = lon - GMT->current.proj.central_meridian;
	while (dlon < -GMT_180) dlon += 360.0;
	while (dlon > 180.0) dlon -= 360.0;
	dlon *= D2R;

	lat = gmtproj_genper_getgeocentric (GMT, lat, 0.0);

	sincosd (lat, &sin_lat, &cos_lat);
	sincos (dlon, &sin_dlon, &cos_dlon);

	cosc = GMT->current.proj.sinp * sin_lat + GMT->current.proj.cosp * cos_lat * cos_dlon;
	sinc = d_sqrt(1.0 - cosc * cosc);

	GMT->current.proj.g_outside = false;

	if (cosc < GMT->current.proj.g_P_inverse) { /* over the horizon */
		GMT->current.proj.g_outside = true;

		if (GMT->current.proj.polar)
			angle = M_PI - dlon;
		else if (GMT->current.proj.cosp*sinc != 0.0) {
			angle = d_acos((sin_lat - GMT->current.proj.sinp*cosc)/(GMT->current.proj.cosp*sinc));
			if (dlon < 0.0) angle = -angle;
		}
		else
			angle = 0.0;

		sincos (angle, &x, &y);
		x *= GMT->current.proj.g_rmax;
		y *= GMT->current.proj.g_rmax;
		angle *= R2D;
	}
	else if (GMT->current.proj.ECC2 != 0.0) { /* within field of view, ellipsoidal earth */
		gmtproj_genper_toxy (GMT, lat, lon, 0.0, &x, &y);
		/* angle = GMT->current.proj.g_azimuth; */
		angle = atan2d(x, y);
		/* XXX Which one is it? Forgotten R2D. Switched x and y. */
	}
	else { /* within field of view, spherical earth */
		kp = GMT->current.proj.g_R * (GMT->current.proj.g_P - 1.0) / (GMT->current.proj.g_P - cosc);
		x = kp * cos_lat * sin_dlon;
		y = kp * (GMT->current.proj.cosp * sin_lat - GMT->current.proj.sinp * cos_lat * cos_dlon);
		/* angle = GMT->current.proj.g_azimuth; */
		angle = atan2d(x, y);
		/* XXX Which one is it? Forgotten R2D. Switched x and y. */
	}

	gmtproj_genper_to_xtyt (GMT, angle, x, y, GMT->current.proj.g_yoffset, xt, yt);

	if (gmt_M_is_dnan(*yt) || gmt_M_is_dnan(*xt)) {
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: yt or xt nan\n");
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: lon %6.3f lat %6.3f\n", lon, lat);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper: xt %10.3e yt %10.3e\n", *xt, *yt);
	}
}

GMT_LOCAL void gmtproj_vgenper (struct GMT_CTRL *GMT, double lon0, double lat0, double altitude, double azimuth, double tilt, double twist, double width, double height) {
	double R, Req, Rpolar, Rlat0;
	double H, P, PP;
	double rho, rho2;
	double eca, cos_eca, sin_eca ;
	double yt_min, yt_max;
	double xt_min, xt_max;
	double rmax;
	double rmax_at_lat0, rmax_min, rmax_max;
	double t1, t2, t12, t22;
	double kp;
	double xt, yt;
	double x, y;
	double gamma, Omega, sinOmega, cosOmega, omega_max;
	double sinlatvp, coslatvp, latvp, lonvp;
	double xt_vp, yt_vp;
	double eccen;
	double max_yt;
	double sin_lat0, cos_lat0, sin_lat1, cos_lat1, dlong;
	double lat0_save;
	double vp_lat, vp_long;
	int az;

	GMT->current.proj.central_meridian = lon0;

	lat0_save = lat0;

	Req = R = GMT->current.proj.EQ_RAD;
	Rpolar = Req * sqrt(GMT->current.proj.one_m_ECC2);

	sincosd (lat0, &t2, &t1);

	t1 *= R;
	t12 = R*t1;
	t2 *= Rpolar;
	t22 = Rpolar*t2;

	Rlat0 = sqrt ((t12*t12 + t22*t22)/(t1*t1 + t2*t2));

	lat0 = gmtproj_genper_getgeocentric (GMT, lat0, 0.0);
	sincosd (lat0, &(GMT->current.proj.sinp), &(GMT->current.proj.cosp));

	if (!gmt_M_proj_is_zero (GMT->current.proj.ECC2)) {
		gmtproj_genper_setup (GMT, 0.0, altitude, lat0_save, lon0);
		GMT->current.proj.central_meridian = lon0;
		GMT->current.proj.pole = GMT->current.proj.g_phig;
	}
	else {
		GMT->current.proj.pole = lat0;

		if (GMT->current.proj.g_radius || (altitude < -10.0)) {
			/* Use altitude as the radial distance from the center of the earth */
			H = fabs (altitude * METERS_IN_A_KM) - R;
			P = H/R + 1.0;
		}
		else if (altitude <= 0.0) {	/* Compute altitude of geosynchronous viewpoint n */
			double temp = 86164.1/TWO_PI;
			H = pow(3.98603e14*temp*temp, 0.3333) - R;
			P = H/R + 1.0;
		}
		else if (altitude < 10.0) {
			P = altitude;
			H = R * (P - 1.0);
		}
		else {
			H = altitude * METERS_IN_A_KM;
			P = H/R + 1.0;
		}
		GMT->current.proj.g_R = R;
		GMT->current.proj.g_H = H;
		GMT->current.proj.g_P = P;
	}

	H = GMT->current.proj.g_H;
	P = GMT->current.proj.g_P;
	R = GMT->current.proj.g_R;

	GMT->current.proj.g_P_inverse = P > 0.0 ? 1.0/P : 1.0;

	if (GMT->current.proj.g_longlat_set) {
		double norm_long = lon0;

		vp_lat = tilt;
		vp_long = azimuth;

		if (vp_lat == lat0_save && vp_long == lon0)
			tilt = azimuth = 0.0;
		else {
			if (GMT->current.proj.g_debug > 0) {
				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: sensor point long %7.4f lat  %7.4f\n", lon0, lat0);
				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: input view point long %7.4f lat %7.4f\n", vp_long, vp_lat);
				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: input twist %7.4f\n", twist);
				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: altitude %f H %f R %f P %7.4f\n", altitude, H/1000.0, R/1000.0,P);
			}

			sincosd (90.0 - lat0, &sin_lat0, &cos_lat0);
			sincosd (90.0 - vp_lat, &sin_lat1, &cos_lat1);

			while (vp_long < 0.0) vp_long += 360.0;
			while (norm_long < 0.0) norm_long += 360.0;

			dlong  = vp_long - norm_long;
			if (dlong < -GMT_180) dlong += 360.0;

			cos_eca = cos_lat0*cos_lat1 + sin_lat0*sin_lat1*cosd(dlong);
			eca = d_acos (cos_eca);
			sin_eca = sin (eca);

			rho2 = P*P + 1.0 - 2.0*P*cos_eca;
			rho = sqrt (rho2);

			tilt = d_acosd ((rho2 + P*P - 1.0)/(2.0*rho*P));

			azimuth = d_acosd ((cos_lat1 - cos_lat0*cos_eca)/(sin_lat0*sin_eca));

			if (dlong < 0) azimuth = 360.0 - azimuth;

		}
		if (GMT->current.proj.g_debug > 0) GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: pointing at longitude %10.4f latitude %10.4f\n           with computed tilt %5.2f azimuth %6.2f\n", vp_long, vp_lat, tilt, azimuth);

	}
	else if (GMT->current.proj.g_debug > 1) {
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: sensor point long %6.3f lat  %6.3f\n", lon0, lat0);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: input azimuth   %6.3f tilt %6.3f\n", azimuth, tilt);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: input twist %6.3f\n", twist);
	}

	if (tilt < 0.0) tilt = d_asind (GMT->current.proj.g_P_inverse);

	sincosd (tilt, &(GMT->current.proj.g_sin_tilt), &(GMT->current.proj.g_cos_tilt));
	sincosd (twist, &(GMT->current.proj.g_sin_twist), &(GMT->current.proj.g_cos_twist));

	GMT->current.proj.g_box = !(fabs (width) < GMT_CONV4_LIMIT);

	if (width != 0.0 && height == 0) height = width;
	if (height != 0.0 && width == 0) width = height;
	GMT->current.proj.g_width = width/2.0;

	GMT->current.proj.g_azimuth = azimuth;
	sincosd (azimuth, &(GMT->current.proj.g_sin_azimuth), &(GMT->current.proj.g_cos_azimuth));

	PP = sqrt ((P - 1.0)/(P + 1.0));
	rmax = R*PP;
	rmax_min = Rpolar*PP;
	rmax_max = Req*PP;
	rmax_at_lat0 = Rlat0*PP;

	if (GMT->current.proj.ECC2 != 0.0) rmax = rmax_at_lat0;

	kp = R*(P - 1.0) / (P - GMT->current.proj.g_P_inverse);

	omega_max = d_acosd(GMT->current.proj.g_P_inverse);
	GMT->current.proj.g_rmax = rmax;

	/* GMT->current.proj.f_horizon = GMT->current.proj.g_P_inverse; */
	GMT->current.proj.f_horizon = omega_max;
	/* XXX Which one is it ? */

	max_yt = 2.0*R*sind (0.5*omega_max);

	eccen = sind (tilt)/sqrt (1.0 - 1.0/(P*P));

	gamma = 180.0 - d_asind (GMT->current.proj.g_sin_tilt * P);
	Omega = 180.0 - tilt - gamma;

	if (GMT->current.proj.g_debug > 0)
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: tilt %6.3f sin_tilt %10.6f P %6.4f gamma %6.4f\n   Omega %6.4f eccen %10.4f\n",
		             tilt, GMT->current.proj.g_sin_tilt, P, gamma, Omega, eccen);

	if (eccen == 1.0) {
		max_yt = MIN (max_yt, rmax * 2.0);
		if (GMT->current.proj.g_debug > 1)
			GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: Projected map is a parabola with requested tilt %6.3f\n max ECA is %6.3f degrees.\n Plot truncated for projected distances > rmax %8.2f\n", tilt, omega_max, rmax/1000.0);
	}
	else if (eccen > 1.0) {
		if (width != 0.0) {
			if (GMT->current.proj.g_debug > 1)
				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: Projected map is a hyperbola with requested tilt %6.3f\n max ECA is %6.3f degrees.\n", tilt, omega_max);
		}
		else {
			max_yt = MIN (max_yt, rmax * 2.0);
			if (GMT->current.proj.g_debug > 1)
				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: Projected map is a hyperbola with requested tilt %6.3f\n max ECA is %6.3f degrees.\n Plot truncated for projected distances > rmax %8.2f\n", tilt, omega_max, rmax/1000.0);
		}
	}
	else if (eccen > 0.5) {
		if (width != 0.0) {
			double t = sind (tilt), Pecc, maxecc = 0.5;
			Pecc = sqrt (1.0/(1.0 - (t*t/maxecc)));
			max_yt = R*sqrt ((Pecc-1.0)/(Pecc+1.0));
			if (GMT->current.proj.g_debug > 1)
				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: Projected map is an enlongated ellipse (eccentricity of %6.4f) with "
				                  "requested tilt %6.3f\nwill truncate plot at rmax %8.2f\n", eccen, tilt, max_yt);
		}
		else {
			if (max_yt > rmax *2.0) max_yt = rmax * 2.0;
			if (GMT->current.proj.g_debug > 1)
				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: Projected map is an enlongated ellipse with requested tilt %6.3f\n eccentricity %6.3f\n Plot truncated for projected distances > rmax %8.2f\n", tilt, eccen, rmax/1000.0);
		}
	}

	GMT->current.proj.g_max_yt = max_yt;

	sincosd (Omega, &sinOmega, &cosOmega);

	rho2 = P*P + 1.0 - 2.0*P*cosOmega;
	rho = sqrt (rho2);

	sinlatvp = GMT->current.proj.sinp*cosOmega + GMT->current.proj.cosp*sinOmega*GMT->current.proj.g_cos_azimuth;
	latvp = d_asind (sinlatvp);
	coslatvp = sqrt (1.0 - sinlatvp*sinlatvp);

	lonvp = acosd ((cosOmega - sinlatvp*GMT->current.proj.sinp)/(GMT->current.proj.cosp*coslatvp));
	if (azimuth > 180.0) lonvp = -lonvp;
	lonvp += lon0;

	if (GMT->current.proj.g_debug > 1)
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: pointing at longitude %10.4f latitude %10.4f\n          "
		                  "with tilt %5.2f azimuth %6.2f at distance %6.4f\n         "
		                  "with width %6.3f height %6.3f twist %6.2f\n", lonvp, latvp, tilt, azimuth, rho, width, height, twist);

	GMT->current.proj.g_yoffset = 0.0;

	if (height != 0.0) {
		GMT->current.proj.g_yoffset = GMT->current.proj.g_sin_tilt * H ;
		xt_max = R * rho * sind (0.5*width);
		xt_min = -xt_max;
		yt_max = R * rho * sind (0.5*height);
		yt_min = -yt_max;
	}
	else {
		FILE *fp = NULL;
		xt_min = 1e20;
		xt_max = -xt_min;

		yt_min = 1e20;
		yt_max = -yt_min;

		if (GMT->current.proj.g_debug > 2) {
			if ((fp = fopen("g_border.txt", "w")) == NULL) {
				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: Failed to write the g_border.txt file\n");
			}
			else {
				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: tilt %10.4f sin_tilt %10.4f cos_tilt %10.4f\n",
				             tilt, GMT->current.proj.g_sin_tilt, GMT->current.proj.g_cos_tilt);
				GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: azimuth %10.4f sin_azimuth %10.4f cos_azimuth %10.4f\n",
				             azimuth, GMT->current.proj.g_sin_azimuth, GMT->current.proj.g_cos_azimuth);
			}
		}

		for (az = 0 ; az < 360 ; az++) {
			sincosd ((double)az, &x, &y);
			x *= rmax;
			y *= rmax;
			gmtproj_genper_to_xtyt (GMT, (double)az, x, y, GMT->current.proj.g_yoffset, &xt, &yt);

			if (fp && GMT->current.proj.g_debug > 2)
				fprintf (fp,"%3d x %10.2f y %10.2f xt %10.3f yt %10.3f\n", az, x/1000, y/1000, xt/1000, yt/1000);

			xt_min = MIN (xt, xt_min);
			xt_max = MAX (xt, xt_max);
			yt_min = MIN (yt, yt_min);
			yt_max = MAX (yt, yt_max);
		}
		if (GMT->current.proj.g_debug > 2 && fp) fclose(fp);
		if (eccen > 0.5) {
			GMT->current.proj.g_width = atand (2.0*rmax/H);
			height = width = 2.0 * GMT->current.proj.g_width;
			xt_max = yt_max = R * rho * sind (GMT->current.proj.g_width);
			xt_min = yt_min = -xt_max;
		}
	}
	if (GMT->current.proj.g_debug > 1) {
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: xt max %7.1f km\n", xt_max/1000.0);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: xt min %7.1f km\n", xt_min/1000.0);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: yt max %7.1f km\n", yt_max/1000.0);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: yt min %7.1f km\n", yt_min/1000.0);
	}

	GMT->current.proj.g_xmin = xt_min;
	GMT->current.proj.g_xmax = xt_max;
	GMT->current.proj.g_ymin = yt_min;
	GMT->current.proj.g_ymax = yt_max;

	if (width != 0.0) GMT->current.proj.scale[GMT_Y] = GMT->current.proj.scale[GMT_X]/width*height;

	if (GMT->current.proj.g_debug > 0) {
		gmtproj_genper (GMT, lonvp, latvp, &xt_vp, &yt_vp);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: polar %d north %d\n", GMT->current.proj.polar, GMT->current.proj.north_pole);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: altitude H %7.1f km P %7.4f\n", H/1000.0, P);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: azimuth %5.1f tilt %5.1f\n", azimuth, tilt);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: viewpoint width %5.1f height %5.1f degrees\n", width, height);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: radius max %7.1f km\n", GMT->current.proj.g_rmax/1000.0);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: eccentricity %7.4f km\n", eccen);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: eq radius max %7.1f km\n", rmax_max/1000.0);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: polar radius max %7.1f km\n", rmax_min/1000.0);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: lat0 radius max %7.1f km\n", rmax_at_lat0/1000.0);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: kp %7.1f \n", kp/1000.0);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: y offset %7.1f km\n", GMT->current.proj.g_yoffset/1000.0);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: yt max %7.1f km\n", yt_max/1000.0);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: yt min %7.1f km\n", yt_min/1000.0);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: y max %7.1f km\n", GMT->current.proj.g_ymax/1000.0);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: y min %7.1f km\n", GMT->current.proj.g_ymin/1000.0);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: x max %7.1f km\n", GMT->current.proj.g_xmax/1000.0);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: x min %7.1f km\n", GMT->current.proj.g_xmin/1000.0);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: omega max %6.2f degrees\n", omega_max);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: gamma %6.3f Omega %6.3f \n", gamma, Omega);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: viewpoint lon %6.3f lat %6.3f \n", lonvp, latvp);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: viewpoint xt %6.3f yt %6.3f \n", xt_vp/1000.0, yt_vp/1000.0);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "vgenper: user viewpoint %d\n", GMT->current.proj.g_box);
	}

}


/* Convert General Perspective x/y to lon/lat */

GMT_LOCAL void gmtproj_igenper (struct GMT_CTRL *GMT, double *lon, double *lat, double xt, double yt) {
	double H, P, R;
	double sin_c, cos_c;
	double x, y;
	double M, Q;
	double con, com, rho;

	H = GMT->current.proj.g_H;
	R = GMT->current.proj.g_R;
	P = GMT->current.proj.g_P;

	x = xt;
	y = yt;

	xt = (x * GMT->current.proj.g_cos_twist + y * GMT->current.proj.g_sin_twist);
	yt = (y * GMT->current.proj.g_cos_twist - x * GMT->current.proj.g_sin_twist);
	yt += GMT->current.proj.g_yoffset;

	M = H * xt / (H - yt * GMT->current.proj.g_sin_tilt);
	Q = H * yt * GMT->current.proj.g_cos_tilt /(H - yt * GMT->current.proj.g_sin_tilt);
	x = M * GMT->current.proj.g_cos_azimuth + Q * GMT->current.proj.g_sin_azimuth;
	y = Q * GMT->current.proj.g_cos_azimuth - M * GMT->current.proj.g_sin_azimuth;

	rho = hypot(x, y);

	GMT->current.proj.g_outside = false;

	if (rho < GMT_CONV4_LIMIT) {
		*lat = GMT->current.proj.pole;
		*lon = GMT->current.proj.central_meridian;
		return;
	}
	if (rho > GMT->current.proj.g_rmax) {
		x *= GMT->current.proj.g_rmax/rho;
		y *= GMT->current.proj.g_rmax/rho;
		rho = GMT->current.proj.g_rmax;
		GMT->current.proj.g_outside = true;
	}

	con = P - 1.0;
	com = P + 1.0;

	if (GMT->current.proj.ECC2 != 0.0)
		gmtproj_genper_tolatlong (GMT, x, y, 0.0, lat, lon);
	else {
		sin_c = (P - d_sqrt(1.0 - (rho * rho * com)/(R*R*con))) / (R * con / rho + rho/(R*con));
		cos_c = d_sqrt(1.0 - sin_c * sin_c);
		sin_c /= rho;
		*lat = d_asind (cos_c * GMT->current.proj.sinp + y * sin_c * GMT->current.proj.cosp);
		*lon = d_atan2d (x * sin_c, cos_c * GMT->current.proj.cosp - y * sin_c * GMT->current.proj.sinp) + GMT->current.proj.central_meridian;
	}
	return;
}

int gmtlib_genper_map_clip_path (struct GMT_CTRL *GMT, uint64_t np, double *work_x, double *work_y) {
	uint64_t i;
	double da, angle;
	double x, y, xt, yt;

	if (GMT->current.proj.g_debug > 0) {
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper_map_clip_path: np %" PRIu64 "\n", np);
		GMT_Report (GMT->parent, GMT_MSG_DEBUG, "genper_map_clip_path: x_scale %e y_scale %e, x0 %e y0 %e\n", GMT->current.proj.scale[GMT_X], GMT->current.proj.scale[GMT_Y], GMT->current.proj.origin[GMT_X], GMT->current.proj.origin[GMT_Y]);
	}
	assert (np > 1);
	da = TWO_PI/(np-1);

	for (i = 0; i < np; i++) {
		angle = i * da;
		sincos (angle, &x, &y);
		x *= GMT->current.proj.g_rmax;
		y *= GMT->current.proj.g_rmax;

		/* XXX forgotten R2D */
		gmtproj_genper_to_xtyt (GMT, angle*R2D, x, y, GMT->current.proj.g_yoffset, &xt, &yt);

		if (GMT->current.proj.g_width != 0.0) {
			xt = MAX (GMT->current.proj.g_xmin, MIN (xt, GMT->current.proj.g_xmax));
			yt = MAX (GMT->current.proj.g_ymin, MIN (yt, GMT->current.proj.g_ymax));
		}
		work_x[i] = xt * GMT->current.proj.scale[GMT_X] + GMT->current.proj.origin[GMT_X];
		work_y[i] = yt * GMT->current.proj.scale[GMT_Y] + GMT->current.proj.origin[GMT_Y];
	}
	return 0;
}

/* -JG ORTHOGRAPHIC PROJECTION */

GMT_LOCAL void gmtproj_vortho (struct GMT_CTRL *GMT, double lon0, double lat0, double horizon) {
	/* Set up Orthographic projection */

	GMT->current.proj.central_meridian = lon0;
	GMT->current.proj.pole = lat0;
	sincosd (lat0, &(GMT->current.proj.sinp), &(GMT->current.proj.cosp));
	GMT->current.proj.f_horizon = horizon;
	GMT->current.proj.rho_max = sind (horizon);
}

GMT_LOCAL void gmtproj_ortho (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to Orthographic x/y */
	double sin_lat, cos_lat, sin_lon, cos_lon;

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */

	sincosd (lat, &sin_lat, &cos_lat);
	sincosd (lon, &sin_lon, &cos_lon);
	*x = GMT->current.proj.EQ_RAD * cos_lat * sin_lon;
	*y = GMT->current.proj.EQ_RAD * (GMT->current.proj.cosp * sin_lat - GMT->current.proj.sinp * cos_lat * cos_lon);
}

GMT_LOCAL void gmtproj_iortho (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert Orthographic x/y to lon/lat */
	double rho, cos_c;

	x *= GMT->current.proj.i_EQ_RAD;
	y *= GMT->current.proj.i_EQ_RAD;
	rho = hypot (x, y);
	if (rho > GMT->current.proj.rho_max) {
		*lat = *lon = GMT->session.d_NaN;
		return;
	}
	cos_c = sqrt (1.0 - rho * rho);	/* Produces NaN for rho > 1: beyond horizon */
	*lat = d_asind (cos_c * GMT->current.proj.sinp + y * GMT->current.proj.cosp);
	*lon = d_atan2d (x, cos_c * GMT->current.proj.cosp - y * GMT->current.proj.sinp) + GMT->current.proj.central_meridian;
}

/* -JF GNOMONIC PROJECTION */

GMT_LOCAL void gmtproj_vgnomonic (struct GMT_CTRL *GMT, double lon0, double lat0, double horizon) {
	/* Set up Gnomonic projection */

	GMT->current.proj.central_meridian = lon0;
	GMT->current.proj.f_horizon = horizon;
	GMT->current.proj.rho_max = tand (GMT->current.proj.f_horizon);
	GMT->current.proj.pole = lat0;
	GMT->current.proj.north_pole = (lat0 > 0.0);
	sincosd (lat0, &(GMT->current.proj.sinp), &(GMT->current.proj.cosp));
}

GMT_LOCAL void gmtproj_gnomonic (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to Gnomonic x/y */
	double k, sin_lat, cos_lat, sin_lon, cos_lon, cc;

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */
	sincosd (lat, &sin_lat, &cos_lat);
	sincosd (lon, &sin_lon, &cos_lon);

	cc = cos_lat * cos_lon;
	k =  GMT->current.proj.EQ_RAD / (GMT->current.proj.sinp * sin_lat + GMT->current.proj.cosp * cc);
	*x = k * cos_lat * sin_lon;
	*y = k * (GMT->current.proj.cosp * sin_lat - GMT->current.proj.sinp * cc);
}

GMT_LOCAL void gmtproj_ignomonic (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert Gnomonic x/y to lon/lat */
	double rho, c;

	x *= GMT->current.proj.i_EQ_RAD;
	y *= GMT->current.proj.i_EQ_RAD;
	rho = hypot (x, y);
	if (rho > GMT->current.proj.rho_max) {
		*lat = *lon = GMT->session.d_NaN;
		return;
	}
	c = atan (rho);
	*lat = d_asind (cos(c) * (GMT->current.proj.sinp + y * GMT->current.proj.cosp));
	*lon = d_atan2d (x, GMT->current.proj.cosp - y * GMT->current.proj.sinp) + GMT->current.proj.central_meridian;
}

/* -JE AZIMUTHAL EQUIDISTANT PROJECTION */

GMT_LOCAL void gmtproj_vazeqdist (struct GMT_CTRL *GMT, double lon0, double lat0, double horizon) {
	/* Set up azimuthal equidistant projection */

	GMT->current.proj.central_meridian = lon0;
	GMT->current.proj.pole = lat0;
	sincosd (lat0, &(GMT->current.proj.sinp), &(GMT->current.proj.cosp));
	GMT->current.proj.f_horizon = horizon;
	GMT->current.proj.rho_max = horizon * D2R;
}

GMT_LOCAL void gmtproj_azeqdist (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to azimuthal equidistant x/y */
	double k, cc, c, clat, slon, clon, slat, t;

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */

	if (gmt_M_proj_is_zero (lat-GMT->current.proj.pole) && gmt_M_proj_is_zero (lon)) {	/* Center of projection */
		*x = *y = 0.0;
		return;
	}
	sincosd (lat, &slat, &clat);
	sincosd (lon, &slon, &clon);

	t = clat * clon;
	cc = GMT->current.proj.sinp * slat + GMT->current.proj.cosp * t;
	if (cc <= -1.0) {	/* Antipode is a circle, so flag x,y as NaN and increase uint64_t */
		*x = *y = GMT->session.d_NaN;
		GMT->current.proj.n_antipoles++;
	}
	else {
		c = d_acos (cc);
		k = (gmt_M_proj_is_zero (c)) ? GMT->current.proj.EQ_RAD : GMT->current.proj.EQ_RAD * c / sin (c);
		*x = k * clat * slon;
		*y = k * (GMT->current.proj.cosp * slat - GMT->current.proj.sinp * t);
	}
}

GMT_LOCAL void gmtproj_iazeqdist (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert azimuthal equidistant x/y to lon/lat */
	double rho, sin_c, cos_c;

	x *= GMT->current.proj.i_EQ_RAD;
	y *= GMT->current.proj.i_EQ_RAD;
	rho = hypot (x, y);
	if (rho > GMT->current.proj.rho_max) {	/* Horizon */
		*lat = *lon = GMT->session.d_NaN;
		return;
	}
	sincos (rho, &sin_c, &cos_c);
	if (rho != 0.0) sin_c /= rho;
	/* Since in the rest we only have sin_c in combination with x or y, it is not necessary to set
	   sin_c = 1 when rho == 0. In that case x*sin_c and y*sin_c or 0 anyhow. */
	*lat = d_asind (cos_c * GMT->current.proj.sinp + y * sin_c * GMT->current.proj.cosp);
	*lon = d_atan2d (x * sin_c, cos_c * GMT->current.proj.cosp - y * sin_c * GMT->current.proj.sinp) + GMT->current.proj.central_meridian;
}

/* -JW MOLLWEIDE EQUAL AREA PROJECTION */

GMT_LOCAL void gmtproj_vmollweide (struct GMT_CTRL *GMT, double lon0, double scale) {
	/* Set up Mollweide Equal-Area projection */

	gmtproj_check_R_J (GMT, &lon0);
	GMT->current.proj.central_meridian = lon0;
	GMT->current.proj.w_x = GMT->current.proj.EQ_RAD * D2R * 2.0 * M_SQRT2 / M_PI;
	GMT->current.proj.w_y = GMT->current.proj.EQ_RAD * M_SQRT2;
	GMT->current.proj.w_iy = 1.0 / GMT->current.proj.w_y;
	GMT->current.proj.w_r = 0.25 * (scale * GMT->current.proj.M_PR_DEG * 360.0);	/* = Half the minor axis */
}

GMT_LOCAL void gmtproj_mollweide (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to Mollweide Equal-Area x/y */
	int i;
	double phi, delta, psin_lat, c, s;

	if (doubleAlmostEqual (fabs (lat), 90.0)) {	/* Special case */
		*x = 0.0;
		*y = copysign (GMT->current.proj.w_y, lat);
		return;
	}

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */
	if (GMT->current.proj.GMT_convert_latitudes) lat = gmt_M_latg_to_lata (GMT, lat);
	lat *= D2R;

	phi = lat;
	psin_lat = M_PI * sin (lat);
	i = 0;
	do {
		i++;
		sincos (phi, &s, &c);
		delta = -(phi + s - psin_lat) / (1.0 + c);
		phi += delta;
	}
	while (fabs (delta) > GMT_PROJ_CONV_LIMIT && i < GMT_PROJ_MAX_ITERATIONS);
	phi *= 0.5;
	sincos (phi, &s, &c);
	*x = GMT->current.proj.w_x * lon * c;
	*y = GMT->current.proj.w_y * s;
}

GMT_LOCAL void gmtproj_imollweide (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert Mollweide Equal-Area x/y to lon/lat */
	double phi, phi2;

	phi = asin (y * GMT->current.proj.w_iy);
	*lon = x / (GMT->current.proj.w_x * cos(phi));
	if (fabs (*lon) > 180.0) {   /* Beyond horizon by a whisker so set to 180/0 depending on signs */
		*lat = 0.0;
		*lon = copysign (180.0, *lon) + GMT->current.proj.central_meridian;
		return;
	}
	*lon += GMT->current.proj.central_meridian;
	phi2 = 2.0 * phi;
	*lat = asind ((phi2 + sin (phi2)) / M_PI);
	if (fabs (*lat) > 90.0)	/* Sanity check */
		*lat = copysign (90.0, *lat);
	else if (GMT->current.proj.GMT_convert_latitudes) *lat = gmt_M_lata_to_latg (GMT, *lat);
}

/* -JH HAMMER-AITOFF EQUAL AREA PROJECTION */

GMT_LOCAL void gmtproj_vhammer (struct GMT_CTRL *GMT, double lon0, double scale) {
	/* Set up Hammer-Aitoff Equal-Area projection */

	gmtproj_check_R_J (GMT, &lon0);
	GMT->current.proj.central_meridian = lon0;
	GMT->current.proj.w_r = 0.25 * (scale * GMT->current.proj.M_PR_DEG * 360.0);	/* = Half the minor axis */
}

GMT_LOCAL void gmtproj_hammer (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to Hammer-Aitoff Equal-Area x/y */
	double slat, clat, slon, clon, D;

	if (doubleAlmostEqual (fabs (lat), 90.0)) {	/* Save time */
		*x = 0.0;
		*y = M_SQRT2 * copysign (GMT->current.proj.EQ_RAD, lat);
		return;
	}

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */
	if (GMT->current.proj.GMT_convert_latitudes) lat = gmt_M_latg_to_lata (GMT, lat);
	sincosd (lat, &slat, &clat);
	sincosd (0.5 * lon, &slon, &clon);

	D = GMT->current.proj.EQ_RAD * sqrt (2.0 / (1.0 + clat * clon));
	*x = 2.0 * D * clat * slon;
	*y = D * slat;
}

GMT_LOCAL void gmtproj_ihammer (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert Hammer-Aitoff Equal-Area x/y to lon/lat */
	double rho, a, sin_c, cos_c;

	x *= 0.5;
	rho = hypot (x, y);
	a = 0.5 * rho * GMT->current.proj.i_EQ_RAD;			/* a = sin(c/2)		*/
	a *= a;							/* a = sin(c/2)**2	*/
	cos_c = 1.0 - 2.0 * a;					/* cos_c = cos(c)	*/
	if (cos_c < -GMT_PROJ_CONV_LIMIT) {					/* Horizon		*/
		*lat = *lon = GMT->session.d_NaN;
		return;
	}
	sin_c = sqrt (1.0 - a) * GMT->current.proj.i_EQ_RAD;		/* sin_c = sin(c)/rho	*/
	*lat = asind (y * sin_c);
	*lon = 2.0 * d_atan2d (x * sin_c, cos_c) + GMT->current.proj.central_meridian;
	if (GMT->current.proj.GMT_convert_latitudes) *lat = gmt_M_lata_to_latg (GMT, *lat);
}

/* -JV VAN DER GRINTEN PROJECTION */

GMT_LOCAL void gmtproj_vgrinten (struct GMT_CTRL *GMT, double lon0, double scale) {
	/* Set up van der Grinten projection; scale is unused for now(?) */
	gmt_M_unused(scale);
	gmtproj_check_R_J (GMT, &lon0);
	GMT->current.proj.central_meridian = lon0;
	GMT->current.proj.v_r = M_PI * GMT->current.proj.EQ_RAD;
	GMT->current.proj.v_ir = 1.0 / GMT->current.proj.v_r;
}

GMT_LOCAL void gmtproj_grinten (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to van der Grinten x/y */
	double flat, A, A2, G, P, P2, Q, P2A2, i_P2A2, GP2, c, s, theta;

	flat = fabs (lat);
	if (flat > (90.0 - GMT_PROJ_CONV_LIMIT)) {	/* Save time */
		*x = 0.0;
		*y = M_PI * copysign (GMT->current.proj.EQ_RAD, lat);
		return;
	}
	if (doubleAlmostEqualZero (lon, GMT->current.proj.central_meridian)) {	/* Save time */
		theta = d_asin (2.0 * fabs (lat) / 180.0);
		*x = 0.0;
		*y = M_PI * copysign (GMT->current.proj.EQ_RAD, lat) * tan (0.5 * theta);
		return;
	}

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */

	if (gmt_M_proj_is_zero (flat)) {	/* Save time */
		*x = GMT->current.proj.EQ_RAD * D2R * lon;
		*y = 0.0;
		return;
	}

	theta = d_asin (2.0 * fabs (lat) / 180.0);

	A = 0.5 * fabs (180.0 / lon - lon / 180.0);
	A2 = A * A;
	sincos (theta, &s, &c);
	G = c / (s + c - 1.0);
	P = G * (2.0 / s - 1.0);
	Q = A2 + G;
	P2 = P * P;
	P2A2 = P2 + A2;
	GP2 = G - P2;
	i_P2A2 = 1.0 / P2A2;

	*x = copysign (GMT->current.proj.v_r, lon) * (A * GP2 + sqrt (A2 * GP2 * GP2 - P2A2 * (G*G - P2))) * i_P2A2;
	*y = copysign (GMT->current.proj.v_r, lat) * (P * Q - A * sqrt ((A2 + 1.0) * P2A2 - Q * Q)) * i_P2A2;
}

GMT_LOCAL void gmtproj_igrinten (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert van der Grinten x/y to lon/lat */
	double x2, c1, x2y2, y2, c2, c3, d, a1, m1, theta1, p;

	x *= GMT->current.proj.v_ir;
	y *= GMT->current.proj.v_ir;
	x2 = x * x;	y2 = y * y;
	x2y2 = x2 + y2;
	if (x2y2 > 1.0) {	/* Horizon */
		*lat = *lon = GMT->session.d_NaN;
		return;
	}
	c1 = -fabs(y) * (1.0 + x2y2);
	c2 = c1 - 2 * y2 + x2;
	p = x2y2 * x2y2;
	c3 = -2.0 * c1 + 1.0 + 2.0 * y2 + p;
	d = y2 / c3 + (2 * pow (c2 / c3, 3.0) - 9.0 * c1 * c2 / (c3 * c3)) / 27.0;
	a1 = (c1 - c2 * c2 / (3.0 * c3)) / c3;
	m1 = 2.0 * sqrt (-a1 / 3.0);
	theta1 = d_acos (3.0 * d / (a1 * m1)) / 3.0;

	*lat = copysign (180.0, y) * (-m1 * cos (theta1 + M_PI/3.0) - c2 / (3.0 * c3));
	*lon = GMT->current.proj.central_meridian;
	if (x != 0.0) *lon += 90.0 * (x2y2 - 1.0 + sqrt (1.0 + 2 * (x2 - y2) + p)) / x;
}

/* -JR WINKEL-TRIPEL MODIFIED gmt_M_is_azimuthal PROJECTION */

GMT_LOCAL void gmtproj_vwinkel (struct GMT_CTRL *GMT, double lon0, double scale) {
	/* Set up Winkel Tripel projection */

	gmtproj_check_R_J (GMT, &lon0);
	GMT->current.proj.r_cosphi1 = cosd (50.0+(28.0/60.0));
	GMT->current.proj.central_meridian = lon0;
	GMT->current.proj.w_r = 0.25 * (scale * GMT->current.proj.M_PR_DEG * 360.0);	/* = Half the minor axis */
}

GMT_LOCAL void gmtproj_winkel (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to Winkel Tripel x/y */
	double X, D, x1, x2, y1, c, s;

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */
	lat *= D2R;
	lon *= (0.5 * D2R);

	/* Fist find Aitoff x/y */

	sincos (lat, &s, &c);
	D = d_acos (c * cos (lon));
	if (gmt_M_proj_is_zero (D))
		x1 = y1 = 0.0;
	else {
		X = s / sin (D);
		x1 = copysign (D * d_sqrt (1.0 - X * X), lon);
		y1 = D * X;
	}

	/* Then get equirectangular projection */

	x2 = lon * GMT->current.proj.r_cosphi1;
	/* y2 = lat; use directly in averaging below */

	/* Winkler is the average value */

	*x = GMT->current.proj.EQ_RAD * (x1 + x2);
	*y = 0.5 * GMT->current.proj.EQ_RAD * (y1 + lat);
}

GMT_LOCAL void gmtproj_iwinkel (struct GMT_CTRL *P, double *lon, double *lat, double x, double y) {
	/* Convert Winkel Tripel x/y to lon/lat */
	/* Based on iterative solution published by:
	 * Ipbuker, 2002, Cartography & Geographical Information Science, 20, 1, 37-42.
	 */

	int n_iter = 0;
	double phi0, lambda0, sp, cp, s2p, sl, cl, sl2, cl2, C, D, sq_C, C_32;
	double f1, f2, df1dp, df1dl, df2dp, df2dl, denom, delta;

	x *= P->current.proj.i_EQ_RAD;
	y *= P->current.proj.i_EQ_RAD;
	*lat = y / M_PI;	/* Initial guesses for lon and lat */
	*lon = x / M_PI;
	if (fabs (y) < GMT_PROJ_CONV_LIMIT && fabs (x) < GMT_PROJ_CONV_LIMIT) {	/* At ~origin, C is ~zero so no division */
		*lon *= R2D;
		*lon += P->current.proj.central_meridian;
		return;
	}
	do {
		phi0 = *lat;
		lambda0 = *lon;
		sincos (phi0, &sp, &cp);
		sincos (lambda0, &sl, &cl);
		sincos (0.5 * lambda0, &sl2, &cl2);
		s2p = sin (2.0 * phi0);
		D = acos (cp * cl2);
		C = 1.0 - cp * cp * cl2 * cl2;
		sq_C = sqrt (C);
		C_32 = C * sq_C;
		f1 = 0.5 * ((2.0 * D * cp * sl2) / sq_C + lambda0 * P->current.proj.r_cosphi1) - x;
		f2 = 0.5 * (D * sp / sq_C + phi0) - y;
		df1dp = (0.25 * (sl * s2p) / C) - ((D * sp * sl2) / C_32);
		df1dl = 0.5 * ((cp * cp * sl2 * sl2) / C + (D * cp * cl2 * sp * sp) / C_32 + P->current.proj.r_cosphi1);
		df2dp = 0.5 * ((sp * sp * cl2) / C + (D * (1.0 - cl2 * cl2) * cp) / C_32 + 1.0);
		df2dl = 0.125 * ((s2p * sl2) / C - (D * sp * cp * cp * sl) / C_32);
		denom = df1dp * df2dl - df2dp * df1dl;
		*lat = phi0 - (f1 * df2dl - f2 * df1dl) / denom;
		*lon = lambda0 - (f2 * df1dp - f1 * df2dp) / denom;
		delta = fabs (*lat - phi0) + fabs (*lon - lambda0);
		n_iter++;
	}
	while (delta > 1e-12 && n_iter < GMT_PROJ_MAX_ITERATIONS);
	*lon *= R2D;
	if (fabs (*lon) > 180.0) {	/* Horizon */
		*lat = *lon = P->session.d_NaN;
		return;
	}
	*lon += P->current.proj.central_meridian;
	*lat *= R2D;
}

double gmtproj_left_winkel (struct GMT_CTRL *GMT, double y) {
	double c, phi, x;

	y -= GMT->current.proj.origin[GMT_Y];
	y *= GMT->current.proj.i_scale[GMT_Y];
	gmtproj_iwinkel_sub (GMT, y, &phi);
	gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian - 180.0, phi, &x, &c);
	return (x);
}

double gmtproj_right_winkel (struct GMT_CTRL *GMT, double y) {
	double c, phi, x;

	y -= GMT->current.proj.origin[GMT_Y];
	y *= GMT->current.proj.i_scale[GMT_Y];
	gmtproj_iwinkel_sub (GMT, y, &phi);
	gmt_geo_to_xy (GMT, GMT->current.proj.central_meridian + 180.0, phi, &x, &c);
	return (x);
}

/* -JKf ECKERT IV PROJECTION */

GMT_LOCAL void gmtproj_veckert4 (struct GMT_CTRL *GMT, double lon0) {
	/* Set up Eckert IV projection */

	gmtproj_check_R_J (GMT, &lon0);
	GMT->current.proj.k4_x = 2.0 * GMT->current.proj.EQ_RAD / d_sqrt (M_PI * (4.0 + M_PI));
	GMT->current.proj.k4_y = 2.0 * GMT->current.proj.EQ_RAD * d_sqrt (M_PI / (4.0 + M_PI));
	GMT->current.proj.k4_ix = 1.0 / GMT->current.proj.k4_x;
	GMT->current.proj.k4_iy = 1.0 / GMT->current.proj.k4_y;
	GMT->current.proj.central_meridian = lon0;
}

GMT_LOCAL void gmtproj_eckert4 (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to Eckert IV x/y */
	int n_iter = 0;
	double phi, delta, s_lat, s, c;

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */
	phi = lat * D2R;
	s_lat = sin (phi);
	phi *= 0.5;
	do {
		sincos (phi, &s, &c);
		delta = -(phi + s * c + 2.0 * s - (2.0 + M_PI_2) * s_lat) / (2.0 * c * (1.0 + c));
		phi += delta;
		n_iter++;
	}
	while (fabs(delta) > GMT_PROJ_CONV_LIMIT && n_iter < GMT_PROJ_MAX_ITERATIONS);

	sincos (phi, &s, &c);
	*x = GMT->current.proj.k4_x * lon * D2R * (1.0 + c);
	*y = GMT->current.proj.k4_y * s;
}

GMT_LOCAL void gmtproj_ieckert4 (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert Eckert IV x/y to lon/lat */

	double phi, s, c;

	s = y * GMT->current.proj.k4_iy;
	phi = d_asin (s);
	c = cos (phi);
	*lon = R2D * x * GMT->current.proj.k4_ix / (1.0 + c);
	if (fabs (*lon) > 180.0) {	/* Horizon */
		*lat = *lon = GMT->session.d_NaN;
		return;
	}
	*lon += GMT->current.proj.central_meridian;
	*lat = d_asind ((phi + s * c + 2.0 * s) / (2.0 + M_PI_2));
}

double gmtproj_left_eckert4 (struct GMT_CTRL *GMT, double y) {
	double x, phi;

	y -= GMT->current.proj.origin[GMT_Y];
	y *= GMT->current.proj.i_scale[GMT_Y];
	phi = d_asin (y * GMT->current.proj.k4_iy);
	x = GMT->current.proj.k4_x * D2R * (GMT->common.R.wesn[XLO] - GMT->current.proj.central_meridian) * (1.0 + cos (phi));
	return (x * GMT->current.proj.scale[GMT_X] + GMT->current.proj.origin[GMT_X]);
}

double gmtproj_right_eckert4 (struct GMT_CTRL *GMT, double y) {
	double x, phi;

	y -= GMT->current.proj.origin[GMT_Y];
	y *= GMT->current.proj.i_scale[GMT_Y];
	phi = d_asin (y * GMT->current.proj.k4_iy);
	x = GMT->current.proj.k4_x * D2R * (GMT->common.R.wesn[XHI] - GMT->current.proj.central_meridian) * (1.0 + cos (phi));
	return (x * GMT->current.proj.scale[GMT_X] + GMT->current.proj.origin[GMT_X]);
}

/* -JKs ECKERT VI PROJECTION */

GMT_LOCAL void gmtproj_veckert6 (struct GMT_CTRL *GMT, double lon0) {
	/* Set up Eckert VI projection */

	gmtproj_check_R_J (GMT, &lon0);
	GMT->current.proj.k6_r = GMT->current.proj.EQ_RAD / sqrt (2.0 + M_PI);
	GMT->current.proj.k6_ir = 1.0 / GMT->current.proj.k6_r;
	GMT->current.proj.central_meridian = lon0;
}

GMT_LOCAL void gmtproj_eckert6 (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to Eckert VI x/y */
	int n_iter = 0;
	double phi, delta, s_lat, s, c;

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */
	if (GMT->current.proj.GMT_convert_latitudes) lat = gmt_M_latg_to_lata (GMT, lat);
	phi = lat * D2R;
	s_lat = sin (phi);
	do {
		sincos (phi, &s, &c);
		delta = -(phi + s - (1.0 + M_PI_2) * s_lat) / (1.0 + c);
		phi += delta;
		n_iter++;
	}
	while (fabs(delta) > GMT_PROJ_CONV_LIMIT && n_iter < GMT_PROJ_MAX_ITERATIONS);

	*x = GMT->current.proj.k6_r * lon * D2R * (1.0 + cos (phi));
	*y = 2.0 * GMT->current.proj.k6_r * phi;
}

GMT_LOCAL void gmtproj_ieckert6 (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert Eckert VI x/y to lon/lat */

	double phi, c, s;

	phi = 0.5 * y * GMT->current.proj.k6_ir;
	sincos (phi, &s, &c);
	*lon = R2D * x * GMT->current.proj.k6_ir / (1.0 + c);
	if (fabs (*lon) > 180.0) {	/* Horizon */
		*lat = *lon = GMT->session.d_NaN;
		return;
	}
	*lon += GMT->current.proj.central_meridian;
	*lat = d_asin ((phi + s) / (1.0 + M_PI_2)) * R2D;
	if (GMT->current.proj.GMT_convert_latitudes) *lat = gmt_M_lata_to_latg (GMT, *lat);
}

double gmtproj_left_eckert6 (struct GMT_CTRL *GMT, double y) {
	double x, phi;

	y -= GMT->current.proj.origin[GMT_Y];
	y *= GMT->current.proj.i_scale[GMT_Y];
	phi = 0.5 * y * GMT->current.proj.k6_ir;
	x = GMT->current.proj.k6_r * D2R * (GMT->common.R.wesn[XLO] - GMT->current.proj.central_meridian) * (1.0 + cos (phi));
	return (x * GMT->current.proj.scale[GMT_X] + GMT->current.proj.origin[GMT_X]);
}

double gmtproj_right_eckert6 (struct GMT_CTRL *GMT, double y) {
	double x, phi;

	y -= GMT->current.proj.origin[GMT_Y];
	y *= GMT->current.proj.i_scale[GMT_Y];
	phi = 0.5 * y * GMT->current.proj.k6_ir;
	x = GMT->current.proj.k6_r * D2R * (GMT->common.R.wesn[XHI] - GMT->current.proj.central_meridian) * (1.0 + cos (phi));
	return (x * GMT->current.proj.scale[GMT_X] + GMT->current.proj.origin[GMT_X]);
}

/* -JN ROBINSON PSEUDOCYLINDRICAL PROJECTION */

GMT_LOCAL void gmtproj_vrobinson (struct GMT_CTRL *GMT, double lon0) {
	/* Set up Robinson projection */
	int err_flag = 0;

	if (GMT->current.setting.interpolant == GMT_SPLINE_LINEAR) {	/* Must reset and warn */
		GMT_Report (GMT->parent, GMT_MSG_WARNING, "-JN requires Akima or Cubic spline interpolant, set to Akima\n");
		GMT->current.setting.interpolant = GMT_SPLINE_AKIMA;
	}

	gmtproj_check_R_J (GMT, &lon0);
	GMT->current.proj.n_cx = 0.8487 * GMT->current.proj.EQ_RAD * D2R;
	GMT->current.proj.n_cy = 1.3523 * GMT->current.proj.EQ_RAD;
	GMT->current.proj.n_i_cy = 1.0 / GMT->current.proj.n_cy;
	GMT->current.proj.central_meridian = lon0;

	GMT->current.proj.n_phi[0] = 0;	GMT->current.proj.n_X[0] = 1.0000;	GMT->current.proj.n_Y[0] = 0.0000;
	GMT->current.proj.n_phi[1] = 5;	GMT->current.proj.n_X[1] = 0.9986;	GMT->current.proj.n_Y[1] = 0.0620;
	GMT->current.proj.n_phi[2] = 10;	GMT->current.proj.n_X[2] = 0.9954;	GMT->current.proj.n_Y[2] = 0.1240;
	GMT->current.proj.n_phi[3] = 15;	GMT->current.proj.n_X[3] = 0.9900;	GMT->current.proj.n_Y[3] = 0.1860;
	GMT->current.proj.n_phi[4] = 20;	GMT->current.proj.n_X[4] = 0.9822;	GMT->current.proj.n_Y[4] = 0.2480;
	GMT->current.proj.n_phi[5] = 25;	GMT->current.proj.n_X[5] = 0.9730;	GMT->current.proj.n_Y[5] = 0.3100;
	GMT->current.proj.n_phi[6] = 30;	GMT->current.proj.n_X[6] = 0.9600;	GMT->current.proj.n_Y[6] = 0.3720;
	GMT->current.proj.n_phi[7] = 35;	GMT->current.proj.n_X[7] = 0.9427;	GMT->current.proj.n_Y[7] = 0.4340;
	GMT->current.proj.n_phi[8] = 40;	GMT->current.proj.n_X[8] = 0.9216;	GMT->current.proj.n_Y[8] = 0.4958;
	GMT->current.proj.n_phi[9] = 45;	GMT->current.proj.n_X[9] = 0.8962;	GMT->current.proj.n_Y[9] = 0.5571;
	GMT->current.proj.n_phi[10] = 50;	GMT->current.proj.n_X[10] = 0.8679;	GMT->current.proj.n_Y[10] = 0.6176;
	GMT->current.proj.n_phi[11] = 55;	GMT->current.proj.n_X[11] = 0.8350;	GMT->current.proj.n_Y[11] = 0.6769;
	GMT->current.proj.n_phi[12] = 60;	GMT->current.proj.n_X[12] = 0.7986;	GMT->current.proj.n_Y[12] = 0.7346;
	GMT->current.proj.n_phi[13] = 65;	GMT->current.proj.n_X[13] = 0.7597;	GMT->current.proj.n_Y[13] = 0.7903;
	GMT->current.proj.n_phi[14] = 70;	GMT->current.proj.n_X[14] = 0.7186;	GMT->current.proj.n_Y[14] = 0.8435;
	GMT->current.proj.n_phi[15] = 75;	GMT->current.proj.n_X[15] = 0.6732;	GMT->current.proj.n_Y[15] = 0.8936;
	GMT->current.proj.n_phi[16] = 80;	GMT->current.proj.n_X[16] = 0.6213;	GMT->current.proj.n_Y[16] = 0.9394;
	GMT->current.proj.n_phi[17] = 85;	GMT->current.proj.n_X[17] = 0.5722;	GMT->current.proj.n_Y[17] = 0.9761;
	GMT->current.proj.n_phi[18] = 90;	GMT->current.proj.n_X[18] = 0.5322;	GMT->current.proj.n_Y[18] = 1.0000;
	if (GMT->current.setting.interpolant == GMT_SPLINE_CUBIC) {	/* Natural cubic spline */
		err_flag  = gmtlib_cspline (GMT, GMT->current.proj.n_phi, GMT->current.proj.n_X,   GMT_N_ROBINSON, GMT->current.proj.n_x_coeff);
		err_flag += gmtlib_cspline (GMT, GMT->current.proj.n_phi, GMT->current.proj.n_Y,   GMT_N_ROBINSON, GMT->current.proj.n_y_coeff);
		err_flag += gmtlib_cspline (GMT, GMT->current.proj.n_Y,   GMT->current.proj.n_X,   GMT_N_ROBINSON, GMT->current.proj.n_yx_coeff);
		err_flag += gmtlib_cspline (GMT, GMT->current.proj.n_Y,   GMT->current.proj.n_phi, GMT_N_ROBINSON, GMT->current.proj.n_iy_coeff);
	}
	else {	/* Akimas spline */
		err_flag  = gmtlib_akima (GMT, GMT->current.proj.n_phi, GMT->current.proj.n_X,   GMT_N_ROBINSON, GMT->current.proj.n_x_coeff);
		err_flag += gmtlib_akima (GMT, GMT->current.proj.n_phi, GMT->current.proj.n_Y,   GMT_N_ROBINSON, GMT->current.proj.n_y_coeff);
		err_flag += gmtlib_akima (GMT, GMT->current.proj.n_Y,   GMT->current.proj.n_X,   GMT_N_ROBINSON, GMT->current.proj.n_yx_coeff);
		err_flag += gmtlib_akima (GMT, GMT->current.proj.n_Y,   GMT->current.proj.n_phi, GMT_N_ROBINSON, GMT->current.proj.n_iy_coeff);
	}
	if (err_flag) GMT_Report (GMT->parent, GMT_MSG_ERROR, "Interpolation failed in gmtproj_vrobinson?\n");
}

GMT_LOCAL void gmtproj_robinson (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
 	/* Convert lon/lat to Robinson x/y */
	double phi, X, Y;

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */
	phi = fabs (lat);

	X = gmtproj_robinson_spline (GMT, phi, GMT->current.proj.n_phi, GMT->current.proj.n_X, GMT->current.proj.n_x_coeff);
	Y = gmtproj_robinson_spline (GMT, phi, GMT->current.proj.n_phi, GMT->current.proj.n_Y, GMT->current.proj.n_y_coeff);
	*x = GMT->current.proj.n_cx * X * lon;	/* D2R is in n_cx already */
	*y = GMT->current.proj.n_cy * copysign (Y, lat);
}

GMT_LOCAL void gmtproj_irobinson (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert Robinson x/y to lon/lat */
	double X, Y;

	Y = fabs (y * GMT->current.proj.n_i_cy);
	*lat = gmtproj_robinson_spline (GMT, Y, GMT->current.proj.n_Y, GMT->current.proj.n_phi, GMT->current.proj.n_iy_coeff);
	X = gmtproj_robinson_spline (GMT, *lat, GMT->current.proj.n_phi, GMT->current.proj.n_X, GMT->current.proj.n_x_coeff);
	*lon = x / (GMT->current.proj.n_cx * X);
	if ((fabs (*lon) - GMT_PROJ_CONV_LIMIT) > 180.0) {	/* Horizon */
		*lat = *lon = GMT->session.d_NaN;
		return;
	}
	*lon += GMT->current.proj.central_meridian;
	if (y < 0.0) *lat = -(*lat);
}

double gmtproj_left_robinson (struct GMT_CTRL *GMT, double y) {
	double x, X, Y;

	y -= GMT->current.proj.origin[GMT_Y];
	y *= GMT->current.proj.i_scale[GMT_Y];
	Y = fabs (y * GMT->current.proj.n_i_cy);
	X = gmtproj_robinson_spline (GMT, Y, GMT->current.proj.n_Y, GMT->current.proj.n_X, GMT->current.proj.n_yx_coeff);
	x = GMT->current.proj.n_cx * X * (GMT->common.R.wesn[XLO] - GMT->current.proj.central_meridian);
	return (x * GMT->current.proj.scale[GMT_X] + GMT->current.proj.origin[GMT_X]);
}

double gmtproj_right_robinson (struct GMT_CTRL *GMT, double y) {
	double x, X, Y;

	y -= GMT->current.proj.origin[GMT_Y];
	y *= GMT->current.proj.i_scale[GMT_Y];
	Y = fabs (y * GMT->current.proj.n_i_cy);
	X = gmtproj_robinson_spline (GMT, Y, GMT->current.proj.n_Y, GMT->current.proj.n_X, GMT->current.proj.n_yx_coeff);
	x = GMT->current.proj.n_cx * X * (GMT->common.R.wesn[XHI] - GMT->current.proj.central_meridian);
	return (x * GMT->current.proj.scale[GMT_X] + GMT->current.proj.origin[GMT_X]);
}

#if 0
double gmtproj_left_robinson (struct GMT_CTRL *GMT, double y) {
	double x, X, Y;

	y -= GMT->current.proj.origin[GMT_Y];
	y *= GMT->current.proj.i_scale[GMT_Y];
	Y = fabs (y * GMT->current.proj.n_i_cy);
	if (gmt_intpol (GMT, GMT->current.proj.n_Y, GMT->current.proj.n_X, NULL, GMT_N_ROBINSON, 1, &Y, &X, 0.0, GMT->current.setting.interpolant)) {
		GMT_Report (GMT->parent, GMT_MSG_ERROR, "GMT Internal error in gmtproj_left_robinson!\n");
		return GMT->session.d_NaN;
	}

	x = GMT->current.proj.n_cx * X * (GMT->common.R.wesn[XLO] - GMT->current.proj.central_meridian);
	return (x * GMT->current.proj.scale[GMT_X] + GMT->current.proj.origin[GMT_X]);
}

double gmtproj_right_robinson (struct GMT_CTRL *GMT, double y) {
	double x, X, Y;

	y -= GMT->current.proj.origin[GMT_Y];
	y *= GMT->current.proj.i_scale[GMT_Y];
	Y = fabs (y * GMT->current.proj.n_i_cy);
	if (gmt_intpol (GMT, GMT->current.proj.n_Y, GMT->current.proj.n_X, NULL, GMT_N_ROBINSON, 1, &Y, &X, 0.0, GMT->current.setting.interpolant)) {
		GMT_Report (GMT->parent, GMT_MSG_ERROR, "GMT Internal error in gmtproj_right_robinson!\n");
		return GMT->session.d_NaN;
	}

	x = GMT->current.proj.n_cx * X * (GMT->common.R.wesn[XHI] - GMT->current.proj.central_meridian);
	return (x * GMT->current.proj.scale[GMT_X] + GMT->current.proj.origin[GMT_X]);
}
#endif

/* -JI SINUSOIDAL EQUAL AREA PROJECTION */

GMT_LOCAL void gmtproj_vsinusoidal (struct GMT_CTRL *GMT, double lon0) {
	/* Set up Sinusoidal projection */

	gmtproj_check_R_J (GMT, &lon0);
	GMT->current.proj.central_meridian = lon0;
}

GMT_LOCAL void gmtproj_sinusoidal (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to Sinusoidal Equal-Area x/y */

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */
	if (GMT->current.proj.GMT_convert_latitudes) lat = gmt_M_latg_to_lata (GMT, lat);

	lat *= D2R;

	*x = GMT->current.proj.EQ_RAD * lon * D2R * cos (lat);
	*y = GMT->current.proj.EQ_RAD * lat;
}

GMT_LOCAL void gmtproj_isinusoidal (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert Sinusoidal Equal-Area x/y to lon/lat */

	*lat = y * GMT->current.proj.i_EQ_RAD;
	*lon = (doubleAlmostEqual (fabs (*lat), M_PI)) ? 0.0 : R2D * x / (GMT->current.proj.EQ_RAD * cos (*lat));
	if (fabs (*lon) > 180.0) {	/* Horizon */
		*lat = *lon = GMT->session.d_NaN;
		return;
	}
	*lon += GMT->current.proj.central_meridian;
	*lat *= R2D;
	if (GMT->current.proj.GMT_convert_latitudes) *lat = gmt_M_lata_to_latg (GMT, *lat);
}

double gmtproj_left_sinusoidal (struct GMT_CTRL *GMT, double y) {
	double x, lat;
	y -= GMT->current.proj.origin[GMT_Y];
	y *= GMT->current.proj.i_scale[GMT_Y];
	lat = y * GMT->current.proj.i_EQ_RAD;
	x = GMT->current.proj.EQ_RAD * (GMT->common.R.wesn[XLO] - GMT->current.proj.central_meridian) * D2R * cos (lat);
	return (x * GMT->current.proj.scale[GMT_X] + GMT->current.proj.origin[GMT_X]);
}

double gmtproj_right_sinusoidal (struct GMT_CTRL *GMT, double y) {
	double x, lat;
	y -= GMT->current.proj.origin[GMT_Y];
	y *= GMT->current.proj.i_scale[GMT_Y];
	lat = y * GMT->current.proj.i_EQ_RAD;
	x = GMT->current.proj.EQ_RAD * (GMT->common.R.wesn[XHI] - GMT->current.proj.central_meridian) * D2R * cos (lat);
	return (x * GMT->current.proj.scale[GMT_X] + GMT->current.proj.origin[GMT_X]);
}

/* -JC CASSINI PROJECTION */

GMT_LOCAL void gmtproj_vcassini (struct GMT_CTRL *GMT, double lon0, double lat0) {
	/* Set up Cassini projection */

	double e1, s2, c2;

	gmtproj_check_R_J (GMT, &lon0);
	GMT->current.proj.central_meridian = lon0;
	GMT->current.proj.pole = lat0;
	GMT->current.proj.c_p = lat0 * D2R;
	sincos (2.0 * GMT->current.proj.c_p, &s2, &c2);

	e1 = (1.0 - d_sqrt (GMT->current.proj.one_m_ECC2)) / (1.0 + d_sqrt (GMT->current.proj.one_m_ECC2));

	GMT->current.proj.c_c1 = 1.0 - (1.0/4.0) * GMT->current.proj.ECC2 - (3.0/64.0) * GMT->current.proj.ECC4 - (5.0/256.0) * GMT->current.proj.ECC6;
	GMT->current.proj.c_c2 = -((3.0/8.0) * GMT->current.proj.ECC2 + (3.0/32.0) * GMT->current.proj.ECC4 + (25.0/768.0) * GMT->current.proj.ECC6);
	GMT->current.proj.c_c3 = (15.0/128.0) * GMT->current.proj.ECC4 + (45.0/512.0) * GMT->current.proj.ECC6;
	GMT->current.proj.c_c4 = -(35.0/768.0) * GMT->current.proj.ECC6;
	GMT->current.proj.c_M0 = GMT->current.proj.EQ_RAD * (GMT->current.proj.c_c1 * GMT->current.proj.c_p + s2 * (GMT->current.proj.c_c2 + c2 * (GMT->current.proj.c_c3 + c2 * GMT->current.proj.c_c4)));
	GMT->current.proj.c_i1 = 1.0 / (GMT->current.proj.EQ_RAD * GMT->current.proj.c_c1);
	GMT->current.proj.c_i2 = (3.0/2.0) * e1 - (29.0/12.0) * pow (e1, 3.0);
	GMT->current.proj.c_i3 = (21.0/8.0) * e1 * e1 - (1537.0/128.0) * pow (e1, 4.0);
	GMT->current.proj.c_i4 = (151.0/24.0) * pow (e1, 3.0);
	GMT->current.proj.c_i5 = (1097.0/64.0) * pow (e1, 4.0);
}

GMT_LOCAL void gmtproj_cassini (struct GMT_CTRL *P, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to Cassini x/y */

	double tany, N, T, A, C, M, s, c, s2, c2, A2, A3;

	gmt_M_wind_lon (P, lon)	/* Remove central meridian and place lon in -180/+180 range */
	lon *= D2R;

	if (gmt_M_proj_is_zero (lat)) {	/* Quick when lat is zero */
		*x = P->current.proj.EQ_RAD * lon;
		*y = -P->current.proj.c_M0;
		return;
	}

	lat *= D2R;
	sincos (lat, &s, &c);
	sincos (2.0 * lat, &s2, &c2);
	tany = s / c;
	N = P->current.proj.EQ_RAD / sqrt (1.0 - P->current.proj.ECC2 * s * s);
	T = tany * tany;
	A = lon * c;
	A2 = A * A;
	A3 = A2 * A;
	C = P->current.proj.ECC2 * c * c * P->current.proj.i_one_m_ECC2;
	M = P->current.proj.EQ_RAD * (P->current.proj.c_c1 * lat + s2 * (P->current.proj.c_c2 + c2 * (P->current.proj.c_c3 + c2 * P->current.proj.c_c4)));
	*x = N * (A - T * A3 / 6.0 - (8.0 - T + 8 * C) * T * A3 * A2 / 120.0);
	*y = M - P->current.proj.c_M0 + N * tany * (0.5 * A2 + (5.0 - T + 6.0 * C) * A2 * A2 / 24.0);
}

GMT_LOCAL void gmtproj_icassini (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert Cassini x/y to lon/lat */

	double M1, u1, u2, s, c, phi1, tany, T1, N1, R_1, D, S2, D2, D3;

	M1 = GMT->current.proj.c_M0 + y;
	u1 = M1 * GMT->current.proj.c_i1;
	u2 = 2.0 * u1;
	sincos (u2, &s, &c);
	phi1 = u1 + s * (GMT->current.proj.c_i2 + c * (GMT->current.proj.c_i3 + c * (GMT->current.proj.c_i4 + c * GMT->current.proj.c_i5)));
	if (doubleAlmostEqual (fabs (phi1), M_PI_2)) {
		*lat = copysign (M_PI_2, phi1);
		*lon = GMT->current.proj.central_meridian;
	}
	else {
		sincos (phi1, &s, &c);
		tany = s / c;
		T1 = tany * tany;
		S2 = 1.0 - GMT->current.proj.ECC2 * s * s;
		N1 = GMT->current.proj.EQ_RAD / sqrt (S2);
		R_1 = GMT->current.proj.EQ_RAD * GMT->current.proj.one_m_ECC2 / pow (S2, 1.5);
		D = x / N1;
		D2 = D * D;
		D3 = D2 * D;
		*lat = R2D * (phi1 - (N1 * tany / R_1) * (0.5 * D2 - (1.0 + 3.0 * T1) * D2 * D2 / 24.0));
		*lon = GMT->current.proj.central_meridian + R2D * (D - T1 * D3 / 3.0 + (1.0 + 3.0 * T1) * T1 * D3 * D2 / 15.0) / c;
	}
}

/* Cassini functions for the Sphere */

GMT_LOCAL void gmtproj_cassini_sph (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to Cassini x/y for spherical earth */

	double slon, clon, clat, tlat, slat;

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */

	if (gmt_M_proj_is_zero (lat)) {	/* Quick when lat is zero */
		*x = GMT->current.proj.EQ_RAD * lon * D2R;
		*y = -GMT->current.proj.EQ_RAD * GMT->current.proj.c_p;
		return;
	}

	sincosd (lon, &slon, &clon);
	sincosd (lat, &slat, &clat);
	tlat = slat / clat;
	*x = GMT->current.proj.EQ_RAD * d_asin (clat * slon);
	*y = GMT->current.proj.EQ_RAD * (atan (tlat / clon) - GMT->current.proj.c_p);
}


GMT_LOCAL void gmtproj_icassini_sph (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert Cassini x/y to lon/lat */

	double D, sD, cD, cx, tx, sx;

	x *= GMT->current.proj.i_EQ_RAD;
	D = y * GMT->current.proj.i_EQ_RAD + GMT->current.proj.c_p;
	sincos (D, &sD, &cD);
	sincos (x, &sx, &cx);
	tx = sx / cx;
	*lat = d_asind (sD * cx);
	*lon = GMT->current.proj.central_meridian + atand (tx / cD);
}

/* -JB ALBERS EQUAL-AREA CONIC PROJECTION */

GMT_LOCAL void gmtproj_valbers (struct GMT_CTRL *GMT, double lon0, double lat0, double ph1, double ph2) {
	/* Set up Albers projection */

	double s0, s1, s2, c1, c2, q0, q1, q2, m1, m2;

	gmtproj_check_R_J (GMT, &lon0);
	GMT->current.proj.central_meridian = lon0;
	GMT->current.proj.north_pole = (GMT->common.R.wesn[YHI] > 0.0 && (GMT->common.R.wesn[YLO] >= 0.0 || (-GMT->common.R.wesn[YLO]) < GMT->common.R.wesn[YHI]));
	GMT->current.proj.pole = (GMT->current.proj.north_pole) ? 90.0 : -90.0;

	s0 = sind (lat0);
	sincosd (ph1, &s1, &c1);
	sincosd (ph2, &s2, &c2);

	m1 = c1 * c1 / (1.0 - GMT->current.proj.ECC2 * s1 * s1);	/* Actually m1 and m2 squared */
	m2 = c2 * c2 / (1.0 - GMT->current.proj.ECC2 * s2 * s2);
	q0 = (gmt_M_proj_is_zero (GMT->current.proj.ECC)) ? 2.0 * s0 : GMT->current.proj.one_m_ECC2 * (s0 / (1.0 - GMT->current.proj.ECC2 * s0 * s0) - GMT->current.proj.i_half_ECC * log ((1.0 - GMT->current.proj.ECC * s0) / (1.0 + GMT->current.proj.ECC * s0)));
	q1 = (gmt_M_proj_is_zero (GMT->current.proj.ECC)) ? 2.0 * s1 : GMT->current.proj.one_m_ECC2 * (s1 / (1.0 - GMT->current.proj.ECC2 * s1 * s1) - GMT->current.proj.i_half_ECC * log ((1.0 - GMT->current.proj.ECC * s1) / (1.0 + GMT->current.proj.ECC * s1)));
	q2 = (gmt_M_proj_is_zero (GMT->current.proj.ECC)) ? 2.0 * s2 : GMT->current.proj.one_m_ECC2 * (s2 / (1.0 - GMT->current.proj.ECC2 * s2 * s2) - GMT->current.proj.i_half_ECC * log ((1.0 - GMT->current.proj.ECC * s2) / (1.0 + GMT->current.proj.ECC * s2)));

	GMT->current.proj.a_n = (doubleAlmostEqualZero (ph1, ph2)) ? s1 : (m1 - m2) / (q2 - q1);
	GMT->current.proj.a_i_n = 1.0 / GMT->current.proj.a_n;
	GMT->current.proj.a_C = m1 + GMT->current.proj.a_n * q1;
	GMT->current.proj.a_rho0 = GMT->current.proj.EQ_RAD * sqrt (GMT->current.proj.a_C - GMT->current.proj.a_n * q0) * GMT->current.proj.a_i_n;
	GMT->current.proj.a_n2ir2 = (GMT->current.proj.a_n * GMT->current.proj.a_n) / (GMT->current.proj.EQ_RAD * GMT->current.proj.EQ_RAD);
	GMT->current.proj.a_test = 1.0 - (GMT->current.proj.i_half_ECC * GMT->current.proj.one_m_ECC2) * log ((1.0 - GMT->current.proj.ECC) / (1.0 + GMT->current.proj.ECC));

}

GMT_LOCAL void gmtproj_valbers_sph (struct GMT_CTRL *GMT, double lon0, double lat0, double ph1, double ph2) {
	/* Set up Spherical Albers projection (Snyder, page 100) */

	double s1, c1;

	gmtproj_check_R_J (GMT, &lon0);
	GMT->current.proj.central_meridian = lon0;
	GMT->current.proj.north_pole = (GMT->common.R.wesn[YHI] > 0.0 && (GMT->common.R.wesn[YLO] >= 0.0 || (-GMT->common.R.wesn[YLO]) < GMT->common.R.wesn[YHI]));
	GMT->current.proj.pole = (GMT->current.proj.north_pole) ? 90.0 : -90.0;

	sincosd (ph1, &s1, &c1);

	GMT->current.proj.a_n = 0.5 * (s1 + sind (ph2));
	GMT->current.proj.a_i_n = 1.0 / GMT->current.proj.a_n;
	GMT->current.proj.a_C = c1 * c1 + 2.0 * GMT->current.proj.a_n * s1;
	GMT->current.proj.a_rho0 = GMT->current.proj.EQ_RAD * sqrt (GMT->current.proj.a_C - 2.0 * GMT->current.proj.a_n * sind (lat0)) * GMT->current.proj.a_i_n;
	GMT->current.proj.a_n2ir2 = 0.5 * GMT->current.proj.a_n / (GMT->current.proj.EQ_RAD * GMT->current.proj.EQ_RAD);
	GMT->current.proj.a_Cin = 0.5 * GMT->current.proj.a_C / GMT->current.proj.a_n;
}

GMT_LOCAL void gmtproj_albers (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to Albers x/y */

	double s, c, q, theta, rho, r;

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */

	s = sind (lat);
	if (gmt_M_proj_is_zero (GMT->current.proj.ECC))
		q = 2.0 * s;
	else {
		r = GMT->current.proj.ECC * s;
		q = GMT->current.proj.one_m_ECC2 * (s / (1.0 - GMT->current.proj.ECC2 * s * s) - GMT->current.proj.i_half_ECC * log ((1.0 - r) / (1.0 + r)));
	}
	theta = GMT->current.proj.a_n * lon * D2R;
	rho = GMT->current.proj.EQ_RAD * sqrt (GMT->current.proj.a_C - GMT->current.proj.a_n * q) * GMT->current.proj.a_i_n;

	sincos (theta, &s, &c);
	*x = rho * s;
	*y = GMT->current.proj.a_rho0 - rho * c;
}

GMT_LOCAL void gmtproj_ialbers (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert Albers x/y to lon/lat */

	int n_iter;
	double theta, rho, q, phi, phi0, s, c, s2, ex_1, delta, r;

	theta = (GMT->current.proj.a_n < 0.0) ? d_atan2 (-x, y - GMT->current.proj.a_rho0) : d_atan2 (x, GMT->current.proj.a_rho0 - y);
	rho = hypot (x, GMT->current.proj.a_rho0 - y);
	q = (GMT->current.proj.a_C - rho * rho * GMT->current.proj.a_n2ir2) * GMT->current.proj.a_i_n;

	if (doubleAlmostEqualZero (fabs (q), GMT->current.proj.a_test))
		*lat = copysign (90.0, q);
	else {
		phi = d_asin (0.5 * q);
		n_iter = 0;
		do {
			phi0 = phi;
			sincos (phi0, &s, &c);
			r = GMT->current.proj.ECC * s;
			s2 = s * s;
			ex_1 = 1.0 - GMT->current.proj.ECC2 * s2;
			phi = phi0 + 0.5 * ex_1 * ex_1 * ((q * GMT->current.proj.i_one_m_ECC2) - s / ex_1
				+ GMT->current.proj.i_half_ECC * log ((1 - r) / (1.0 + r))) / c;
			delta = fabs (phi - phi0);
			n_iter++;
		}
		while (delta > GMT_PROJ_CONV_LIMIT && n_iter < GMT_PROJ_MAX_ITERATIONS);
		*lat = R2D * phi;
	}
	*lon = GMT->current.proj.central_meridian + R2D * theta * GMT->current.proj.a_i_n;
}

/* Spherical versions of Albers */

GMT_LOCAL void gmtproj_albers_sph (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to Spherical Albers x/y */

	double s, c, theta, rho;

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */

	if (GMT->current.proj.GMT_convert_latitudes) lat = gmt_M_latg_to_lata (GMT, lat);

	theta = GMT->current.proj.a_n * lon * D2R;
	rho = GMT->current.proj.EQ_RAD * sqrt (GMT->current.proj.a_C - 2.0 * GMT->current.proj.a_n * sind (lat)) * GMT->current.proj.a_i_n;

	sincos (theta, &s, &c);
	*x = rho * s;
	*y = GMT->current.proj.a_rho0 - rho * c;
}

GMT_LOCAL void gmtproj_ialbers_sph (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert Spherical Albers x/y to lon/lat */

	double theta, A, dy;

	theta = (GMT->current.proj.a_n < 0.0) ? d_atan2 (-x, y - GMT->current.proj.a_rho0) : d_atan2 (x, GMT->current.proj.a_rho0 - y);
	dy = GMT->current.proj.a_rho0 - y;
	A = (x * x + dy * dy) * GMT->current.proj.a_n2ir2;

	*lat = d_asind (GMT->current.proj.a_Cin - A);
	*lon = GMT->current.proj.central_meridian + R2D * theta * GMT->current.proj.a_i_n;
	if (GMT->current.proj.GMT_convert_latitudes) *lat = gmt_M_lata_to_latg (GMT, *lat);
}

/* -JD EQUIDISTANT CONIC PROJECTION */

GMT_LOCAL void gmtproj_veconic (struct GMT_CTRL *GMT, double lon0, double lat0, double lat1, double lat2) {
	/* Set up Equidistant Conic projection */
	double c1;

	gmtproj_check_R_J (GMT, &lon0);
	GMT->current.proj.north_pole = (GMT->common.R.wesn[YHI] > 0.0 && (GMT->common.R.wesn[YLO] >= 0.0 || (-GMT->common.R.wesn[YLO]) < GMT->common.R.wesn[YHI]));
	c1 = cosd (lat1);
	GMT->current.proj.d_n = (doubleAlmostEqualZero (lat1, lat2)) ? sind (lat1) : (c1 - cosd (lat2)) / (D2R * (lat2 - lat1));
	GMT->current.proj.d_i_n = R2D / GMT->current.proj.d_n;	/* R2D put here instead of in lon for ieconic */
	GMT->current.proj.d_G = (c1 / GMT->current.proj.d_n) + lat1 * D2R;
	GMT->current.proj.d_rho0 = GMT->current.proj.EQ_RAD * (GMT->current.proj.d_G - lat0 * D2R);
	GMT->current.proj.central_meridian = lon0;
}

GMT_LOCAL void gmtproj_econic (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to Equidistant Conic x/y */
	double rho, theta, s, c;

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */

	rho = GMT->current.proj.EQ_RAD * (GMT->current.proj.d_G - lat * D2R);
	theta = GMT->current.proj.d_n * lon * D2R;

	sincos (theta, &s, &c);
	*x = rho * s;
	*y = GMT->current.proj.d_rho0 - rho * c;
}

GMT_LOCAL void gmtproj_ieconic (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert Equidistant Conic x/y to lon/lat */

	double rho, theta;

	rho = hypot (x, GMT->current.proj.d_rho0 - y);
	if (GMT->current.proj.d_n < 0.0) rho = -rho;
	theta = (GMT->current.proj.d_n < 0.0) ? d_atan2 (-x, y - GMT->current.proj.d_rho0) : d_atan2 (x, GMT->current.proj.d_rho0 - y);
	*lat = (GMT->current.proj.d_G - rho * GMT->current.proj.i_EQ_RAD) * R2D;
	*lon = GMT->current.proj.central_meridian + theta * GMT->current.proj.d_i_n;
}

/* -JPoly POLYCONIC PROJECTION */

GMT_LOCAL void gmtproj_vpolyconic (struct GMT_CTRL *GMT, double lon0, double lat0) {
	/* Set up Polyconic projection */

	gmtproj_check_R_J (GMT, &lon0);
	GMT->current.proj.central_meridian = lon0;
	GMT->current.proj.pole = lat0;
}

GMT_LOCAL void gmtproj_polyconic (struct GMT_CTRL *GMT, double lon, double lat, double *x, double *y) {
	/* Convert lon/lat to Polyconic x/y */
	double sE, cE, sp, cp;

	gmt_M_wind_lon (GMT, lon)	/* Remove central meridian and place lon in -180/+180 range */

	if (gmt_M_proj_is_zero(lat)) {
		*x = GMT->current.proj.EQ_RAD * lon * D2R;
		*y = GMT->current.proj.EQ_RAD * (lat - GMT->current.proj.pole) * D2R;
	}
	else {
		sincosd(lat, &sp, &cp);
		sincosd(lon * sp, &sE, &cE);
		cp /= sp;	/* = cot(phi) */
		*x = GMT->current.proj.EQ_RAD * cp * sE;
		*y = GMT->current.proj.EQ_RAD * ((lat - GMT->current.proj.pole) * D2R + cp * (1.0 - cE));
	}
}

GMT_LOCAL void gmtproj_ipolyconic (struct GMT_CTRL *GMT, double *lon, double *lat, double x, double y) {
	/* Convert Polyconic x/y to lon/lat */

	double B, phi, phi0, tanp, delta;
	int n_iter = 0;

	x *= GMT->current.proj.i_EQ_RAD;
	y *= GMT->current.proj.i_EQ_RAD;
	y += GMT->current.proj.pole * D2R;
	if (gmt_M_proj_is_zero (y)) {
		*lat = y * R2D + GMT->current.proj.pole;
		*lon = x * R2D + GMT->current.proj.central_meridian;
	}
	else {
		B = x * x + y * y;
		phi = y;
		do {
			phi0 = phi;
			tanp = tan(phi);
			phi -= (y * (phi * tanp + 1.0) - phi - 0.5 * (phi * phi + B) * tanp) /
				((phi - y) / tanp - 1.0);
			delta = fabs (phi - phi0);
			n_iter++;
		}
		while (delta > GMT_PROJ_CONV_LIMIT && n_iter < GMT_PROJ_MAX_ITERATIONS);
		*lat = R2D * phi;
		*lon = GMT->current.proj.central_meridian + asind(x * tanp) / sin(phi);
	}
}

double gmtproj_left_polyconic (struct GMT_CTRL *GMT, double y) {
	double x;
	y -= GMT->current.proj.origin[GMT_Y];
	y *= GMT->current.proj.i_scale[GMT_Y];
	gmtproj_ipolyconic_sub (GMT, y, GMT->common.R.wesn[XLO], &x);
	return (x * GMT->current.proj.scale[GMT_X] + GMT->current.proj.origin[GMT_X]);
}

double gmtproj_right_polyconic (struct GMT_CTRL *GMT, double y) {
	double x;
	y -= GMT->current.proj.origin[GMT_Y];
	y *= GMT->current.proj.i_scale[GMT_Y];
	gmtproj_ipolyconic_sub (GMT, y, GMT->common.R.wesn[XHI], &x);
	return (x * GMT->current.proj.scale[GMT_X] + GMT->current.proj.origin[GMT_X]);
}
