/*-------------------------------------------------------------------- * * 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]); }